-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added support for manga w/ searches, caching etc.
- Loading branch information
Showing
7 changed files
with
388 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<template> | ||
<transition | ||
appear | ||
enter-active-class="animated fadeIn" | ||
leave-active-class="animated fadeOut" | ||
> | ||
<q-card | ||
:class="['q-ma-md', $q.screen.width > 400 ? 'col-2' : 'col-10']" | ||
v-ripple | ||
@click="goto('/manga/' + manga.mal_id)" | ||
> | ||
<q-img :src="manga.image_url"> | ||
<div class="absolute-bottom"> | ||
<div class="text-h6">{{ manga.title }}</div> | ||
<div class="text-subtitle2"> | ||
{{ manga.type }}, {{ Math.round(manga.score) }} stars | ||
</div> | ||
</div> | ||
</q-img> | ||
</q-card> | ||
</transition> | ||
</template> | ||
|
||
<script> | ||
/* eslint-disable @typescript-eslint/no-unsafe-call */ | ||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ | ||
export default { | ||
name: 'AnimeCard', | ||
props: { | ||
manga: { | ||
type: Object, | ||
required: true | ||
} | ||
}, | ||
methods: { | ||
goto(e) { | ||
this.$router.push(e); | ||
} | ||
} | ||
}; | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
<template> | ||
<q-page class="items-center justify-evenly"> | ||
<div class="post"> | ||
<div v-if="error" class="error"> | ||
{{ error }} | ||
</div> | ||
|
||
<div v-if="manga.mal_id" class="content"> | ||
<div | ||
:class="[ | ||
'q-mx-lg', | ||
'items-center', | ||
'justify-evenly', | ||
$q.screen.width > 800 ? 'row' : 'col' | ||
]" | ||
> | ||
<div class="col-5"> | ||
<q-img :src="manga.image_url" /> | ||
<q-btn | ||
class="q-ma-lg" | ||
color="primary" | ||
clickable | ||
type="a" | ||
target="_blank" | ||
:href="manga.url" | ||
> | ||
Open in MAL</q-btn | ||
> | ||
</div> | ||
<div class="col-5"> | ||
<h2>{{ manga.title }}</h2> | ||
|
||
<h6 class="disabled">{{ manga.title_english }}</h6> | ||
<p class="text-justify">{{ manga.synopsis }}</p> | ||
<h5> | ||
<q-icon name="stars" /> | ||
{{ manga.score ? manga.score.toPrecision(3) : '-' }} / 10 | ||
</h5> | ||
<h5> | ||
<q-icon name="visibility" /> {{ norm(manga.members) || '0' }} | ||
</h5> | ||
<h5><q-icon name="star" /> {{ norm(manga.favorites) || '0' }}</h5> | ||
<h5># {{ norm(manga.rank) || '-' }}</h5> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</q-page> | ||
</template> | ||
|
||
<script lang="ts"> | ||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ | ||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ | ||
import axios from 'axios'; | ||
import Vue from 'vue'; | ||
export default Vue.extend({ | ||
name: 'PageManga', | ||
data() { | ||
return { | ||
player: null, | ||
manga: { | ||
title: '', | ||
mal_id: '' | ||
}, | ||
error: '' | ||
}; | ||
}, | ||
created() { | ||
// fetch the data when the view is created and the data is | ||
// already being observed | ||
this.fetchData(); | ||
}, | ||
watch: { | ||
// call again the method if the route changes | ||
$route: 'fetchData' | ||
}, | ||
methods: { | ||
norm(x: number) { | ||
if (!x) return; | ||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); | ||
}, | ||
fetchData() { | ||
this.error = ''; | ||
this.$q.loading.show({ | ||
delay: 400 // ms | ||
}); | ||
const currentManga = this.$route.params.id; | ||
if (!navigator.onLine) { | ||
console.log('checking for cached manga'); | ||
let cache = this.$q.localStorage.getItem('cache'); | ||
if (!cache) { | ||
this.$q.localStorage.set('cache', {}); | ||
cache = this.$q.localStorage.getItem('cache'); | ||
} | ||
/* @ts-ignore */ | ||
if (!cache.manga) cache.manga = {}; | ||
/* @ts-ignore */ | ||
if (cache.manga[this.$route.params.id]) { | ||
if (currentManga != this.$route.params.id) return; | ||
/* @ts-ignore */ | ||
console.log('found some'); | ||
/* @ts-ignore */ | ||
this.manga = cache.manga[this.$route.params.id]; | ||
this.$q.loading.hide(); | ||
return; | ||
} else { | ||
console.log('none found'); | ||
if (currentManga != this.$route.params.id) return; | ||
this.$q.notify( | ||
"This manga hasn't been cached, so we can't show you anything. Connect to the internet and try again." | ||
); | ||
this.$q.loading.hide(); | ||
return; | ||
} | ||
} | ||
// replace `getPost` with your data fetching util / API wrapper | ||
axios | ||
.get(`https://api.jikan.moe/v3/manga/${this.$route.params.id}`) | ||
.then(data => { | ||
this.$q.loading.hide(); | ||
this.manga = data.data; | ||
document.title = `${this.manga.title} | Mirai`; | ||
/* @ts-ignore */ | ||
let cache = this.$q.localStorage.getItem('cache'); | ||
/* @ts-ignore */ | ||
if (!cache) { | ||
this.$q.localStorage.set('cache', {}); | ||
cache = this.$q.localStorage.getItem('cache'); | ||
} | ||
/* @ts-ignore */ | ||
if (!cache.manga) cache.manga = {}; | ||
/* @ts-ignore */ | ||
cache.manga[this.$route.params.id] = this.manga; | ||
/* @ts-ignore */ | ||
cache.manga[this.$route.params.id].date = new Date(); | ||
if (currentManga != this.$route.params.id) return; | ||
this.$q.localStorage.set('cache', cache); | ||
}) | ||
.catch((e: string) => { | ||
console.log(e); | ||
this.$q.loading.hide(); | ||
this.error = e; | ||
}); | ||
} | ||
} | ||
}); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
<template> | ||
<q-page class="row items-center justify-evenly"> | ||
<div class="row col-12 items-center justify-evenly"> | ||
<q-input | ||
:class="['q-my-xl', $q.screen.width > 400 ? 'col-8' : 'col-10']" | ||
v-model="searchmanga" | ||
debounce="300" | ||
filled | ||
label="Enter a manga..." | ||
:rules="[ | ||
val => | ||
!val || | ||
val.trim().length >= 3 || | ||
val.trim() == '' || | ||
'Minimum 3 characters.' | ||
]" | ||
@keypress="load" | ||
:loading="loading" | ||
clearable | ||
autofocus | ||
type="search" | ||
> | ||
<template v-slot:append> | ||
<q-icon name="search" /> | ||
</template> | ||
</q-input> | ||
</div> | ||
|
||
<q-space /> | ||
<div class="row items-center justify-evenly"> | ||
<MangaCard v-for="manga in results" :key="manga.mal_id" :manga="manga" /> | ||
</div> | ||
</q-page> | ||
</template> | ||
|
||
<script lang="ts"> | ||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ | ||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ | ||
import Vue from 'vue'; | ||
import MangaCard from 'components/MangaCard.vue'; | ||
import axios from 'axios'; | ||
export default Vue.extend({ | ||
name: 'PageSearchManga', | ||
components: { MangaCard }, | ||
mounted() { | ||
if ( | ||
this.$route.params.query && | ||
this.$route.params.query != this.searchmanga | ||
) { | ||
this.searchmanga = this.$route.params.query; | ||
} | ||
}, | ||
watch: { | ||
searchmanga() { | ||
document.title = | ||
this.searchmanga && this.searchmanga.trim() | ||
? `"${this.searchmanga}" | Search Mirai` | ||
: 'Search Mirai'; | ||
if ( | ||
!this.$route.params.query || | ||
this.$route.params.query != this.searchmanga | ||
) { | ||
this.$router | ||
.replace('/searchmanga/' + encodeURIComponent(this.searchmanga || '')) | ||
.catch(e => console.log(e)); | ||
} | ||
const currentSearch = this.searchmanga.trim(); | ||
this.loading = true; | ||
if (!this.searchmanga || this.searchmanga.trim() == '') { | ||
this.loading = false; | ||
this.results = []; | ||
return; | ||
} | ||
console.log('checking for cached searches'); | ||
let cache = this.$q.localStorage.getItem('cache'); | ||
if (!cache) { | ||
this.$q.localStorage.set('cache', {}); | ||
cache = this.$q.localStorage.getItem('cache'); | ||
} | ||
/* @ts-ignore */ | ||
if (!cache.searchmanga) cache.searchmanga = {}; | ||
/* @ts-ignore */ | ||
if (cache.searchmanga[this.searchmanga.trim()]) { | ||
if (currentSearch != this.searchmanga.trim()) return; | ||
/* @ts-ignore */ | ||
console.log('found some'); | ||
/* @ts-ignore */ | ||
this.results = cache.searchmanga[this.searchmanga.trim()]; | ||
this.loading = false; | ||
if (!navigator.onLine) return; | ||
} else { | ||
console.log('none found'); | ||
if (!navigator.onLine) { | ||
if (currentSearch != this.searchmanga.trim()) return; | ||
this.$q.notify( | ||
"This search hasn't been cached, so we can't show you anything. Connect to the internet and try again." | ||
); | ||
this.loading = false; | ||
this.results = []; | ||
return; | ||
} | ||
} | ||
axios | ||
.get( | ||
`https://api.jikan.moe/v3/search/manga?q=${encodeURIComponent( | ||
this.searchmanga.trim() | ||
)}&page=1` | ||
) | ||
.then(e => { | ||
/* @ts-ignore */ | ||
let cache = this.$q.localStorage.getItem('cache'); | ||
/* @ts-ignore */ | ||
if (!cache) { | ||
this.$q.localStorage.set('cache', {}); | ||
cache = this.$q.localStorage.getItem('cache'); | ||
} | ||
/* @ts-ignore */ | ||
if (!cache.searchmanga) cache.searchmanga = {}; | ||
/* @ts-ignore */ | ||
cache.searchmanga[this.searchmanga.trim()] = e.data.results; | ||
/* @ts-ignore */ | ||
cache.searchmanga[this.searchmanga.trim()].date = new Date(); | ||
if (currentSearch.trim() != this.searchmanga.trim()) return; | ||
this.$q.localStorage.set('cache', cache); | ||
this.results = e.data.results; | ||
this.loading = false; | ||
}) | ||
.catch(e => console.log(e)); | ||
} | ||
}, | ||
methods: { | ||
load() { | ||
this.loading = true; | ||
} | ||
}, | ||
data() { | ||
return { | ||
searchmanga: '', | ||
results: [], | ||
loading: false, | ||
online: false | ||
}; | ||
} | ||
}); | ||
</script> |
Oops, something went wrong.