mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-04-20 10:42:15 +00:00
feat: add pagination to all searches
This commit is contained in:
parent
bb8d8c5ad5
commit
574cc09860
5 changed files with 130 additions and 106 deletions
|
|
@ -10,17 +10,45 @@
|
|||
import { getContext } from 'svelte'
|
||||
|
||||
let media = getContext('gallery')
|
||||
let search
|
||||
let search = {}
|
||||
let current = null
|
||||
let page = 1
|
||||
|
||||
$: if (!$media) {
|
||||
current = null
|
||||
canScroll = true
|
||||
}
|
||||
|
||||
let canScroll = true
|
||||
let hasNext = true
|
||||
async function infiniteScroll() {
|
||||
if (current && canScroll && hasNext && this.scrollTop + this.clientHeight > this.scrollHeight - 800) {
|
||||
canScroll = false
|
||||
const res = await sections[current].load(++page)
|
||||
$media = $media.then(old => {
|
||||
return old.concat(res)
|
||||
})
|
||||
canScroll = hasNext
|
||||
}
|
||||
}
|
||||
|
||||
$: load(current)
|
||||
function load(current) {
|
||||
if (sections[current]) {
|
||||
page = 1
|
||||
$media = sections[current].load(1)
|
||||
} else {
|
||||
$media = null
|
||||
canScroll = true
|
||||
}
|
||||
}
|
||||
|
||||
function processMedia(res) {
|
||||
hasNext = res.data.Page.pageInfo.hasNextPage
|
||||
return res.data.Page.media.map(media => {
|
||||
return { media }
|
||||
})
|
||||
}
|
||||
setInterval(async () => {
|
||||
const media = await releasesCards(5)
|
||||
if (media) sections[1].cards = media
|
||||
}, 30000)
|
||||
|
||||
let lastRSSDate = 0
|
||||
async function releasesCards(limit, force) {
|
||||
|
|
@ -41,78 +69,84 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
const sections = [
|
||||
{
|
||||
const sections = {
|
||||
continue: {
|
||||
title: 'Continue Watching',
|
||||
click: () => {
|
||||
$media = alRequest({ method: 'UserLists', status_in: 'CURRENT' }).then(res => {
|
||||
return res.data.Page.mediaList.filter(i => {
|
||||
return i.media.status !== 'RELEASING' || i.media.mediaListEntry?.progress < i.media.nextAiringEpisode?.episode - 1
|
||||
})
|
||||
})
|
||||
},
|
||||
cards:
|
||||
alToken &&
|
||||
alRequest({ method: 'UserLists', status_in: 'CURRENT' }).then(res => {
|
||||
load: (page = 1, perPage = 50) => {
|
||||
return alRequest({ method: 'UserLists', status_in: 'CURRENT', page, perPage }).then(res => {
|
||||
hasNext = res.data.Page.pageInfo.hasNextPage
|
||||
return res.data.Page.mediaList
|
||||
.filter(i => {
|
||||
return i.media.status !== 'RELEASING' || i.media.mediaListEntry?.progress < i.media.nextAiringEpisode?.episode - 1
|
||||
})
|
||||
.slice(0, 5)
|
||||
}),
|
||||
.slice(0, perPage)
|
||||
})
|
||||
},
|
||||
hide: !alToken
|
||||
},
|
||||
{
|
||||
releases: {
|
||||
title: 'New Releases',
|
||||
click: () => {
|
||||
$media = releasesCards(200, true)
|
||||
},
|
||||
cards: releasesCards(5)
|
||||
releases: true,
|
||||
load: (force, perPage = 50) => {
|
||||
hasNext = false
|
||||
return releasesCards(perPage, force)
|
||||
}
|
||||
},
|
||||
{
|
||||
planning: {
|
||||
title: 'Your List',
|
||||
click: () => {
|
||||
$media = alRequest({ method: 'UserLists', status_in: 'PLANNING' }).then(res => res.data.Page.mediaList)
|
||||
load: (page = 1, perPage = 50) => {
|
||||
return alRequest({ method: 'UserLists', page, perPage, status_in: 'PLANNING' }).then(res => {
|
||||
hasNext = res.data.Page.pageInfo.hasNextPage
|
||||
return res.data.Page.mediaList
|
||||
})
|
||||
},
|
||||
cards: alToken && alRequest({ method: 'UserLists', status_in: 'PLANNING', perPage: 5 }).then(res => res.data.Page.mediaList),
|
||||
hide: !alToken
|
||||
},
|
||||
{
|
||||
trending: {
|
||||
title: 'Trending Now',
|
||||
click: () => {
|
||||
search.sort = 'TRENDING_DESC'
|
||||
},
|
||||
cards: alRequest({ method: 'Search', perPage: 5, sort: 'TRENDING_DESC' }).then(res => processMedia(res))
|
||||
load: (page = 1, perPage = 50) => {
|
||||
return alRequest({ method: 'Search', page, perPage, sort: 'TRENDING_DESC' }).then(res => processMedia(res))
|
||||
}
|
||||
},
|
||||
{
|
||||
romance: {
|
||||
title: 'Romance',
|
||||
click: () => {
|
||||
search.sort = 'TRENDING_DESC'
|
||||
search.genre = 'romance'
|
||||
},
|
||||
cards: alRequest({ method: 'Search', perPage: 5, genre: 'Romance', sort: 'TRENDING_DESC' }).then(res => processMedia(res))
|
||||
load: (page = 1, perPage = 50) => {
|
||||
return alRequest({ method: 'Search', page, perPage, genre: 'Romance', sort: 'TRENDING_DESC' }).then(res => processMedia(res))
|
||||
}
|
||||
},
|
||||
{
|
||||
action: {
|
||||
title: 'Action',
|
||||
click: () => {
|
||||
search.sort = 'TRENDING_DESC'
|
||||
search.genre = 'action'
|
||||
},
|
||||
cards: alRequest({ method: 'Search', perPage: 5, genre: 'Action', sort: 'TRENDING_DESC' }).then(res => processMedia(res))
|
||||
load: (page = 1, perPage = 50) => {
|
||||
return alRequest({ method: 'Search', page, perPage, genre: 'Action', sort: 'TRENDING_DESC' }).then(res => processMedia(res))
|
||||
}
|
||||
},
|
||||
search: {
|
||||
hide: true,
|
||||
load: (page = 1, perPage = 50) => {
|
||||
const opts = {
|
||||
method: 'Search',
|
||||
page,
|
||||
perPage
|
||||
}
|
||||
for (const [key, value] of Object.entries(search)) {
|
||||
if (value) opts[key] = value
|
||||
}
|
||||
return alRequest(opts).then(res => processMedia(res))
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="d-flex h-full flex-column overflow-y-scroll root">
|
||||
<div class="d-flex h-full flex-column overflow-y-scroll root" on:scroll={infiniteScroll}>
|
||||
<div class="h-full py-10">
|
||||
<Search bind:media={$media} bind:search />
|
||||
<Search bind:media={$media} bind:search bind:current />
|
||||
{#if $media}
|
||||
<Gallery media={$media} />
|
||||
{:else}
|
||||
<div>
|
||||
{#each sections as opts (opts.title)}
|
||||
{#each Object.entries(sections) as [key, opts] (opts.title)}
|
||||
{#if !opts.hide}
|
||||
<Section {opts} />
|
||||
<Section opts={{ ...opts, onclick: () => (current = key) }} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,9 @@
|
|||
<script>
|
||||
import { alRequest } from '@/modules/anilist.js'
|
||||
export let search = {}
|
||||
export let search
|
||||
export let current
|
||||
export let media = null
|
||||
let searchTimeout = null
|
||||
|
||||
function processMedia(res) {
|
||||
return res.data.Page.media.map(media => {
|
||||
return { media }
|
||||
})
|
||||
}
|
||||
function searchClear() {
|
||||
search = {
|
||||
format: '',
|
||||
|
|
@ -17,9 +12,8 @@
|
|||
sort: '',
|
||||
status: ''
|
||||
}
|
||||
media = null
|
||||
current = null
|
||||
}
|
||||
$: input(search)
|
||||
function input() {
|
||||
if (!searchTimeout) {
|
||||
if (Object.values(search).filter(v => v).length) media = new Promise(() => {})
|
||||
|
|
@ -27,31 +21,14 @@
|
|||
clearTimeout(searchTimeout)
|
||||
}
|
||||
searchTimeout = setTimeout(() => {
|
||||
handleSearch()
|
||||
current = null
|
||||
current = 'search'
|
||||
searchTimeout = null
|
||||
}, 500)
|
||||
}
|
||||
export async function handleSearch(page) {
|
||||
const defaults = {
|
||||
method: 'Search',
|
||||
page: page || 1
|
||||
}
|
||||
const opts = {}
|
||||
Object.assign(opts, defaults)
|
||||
for (const [key, value] of Object.entries(search)) {
|
||||
if (value) opts[key] = value
|
||||
}
|
||||
if (Object.keys(defaults).length !== Object.keys(opts).length) {
|
||||
media = alRequest(opts).then(res => {
|
||||
return processMedia(res)
|
||||
})
|
||||
} else {
|
||||
media = null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container-fluid row p-20">
|
||||
<div class="container-fluid row p-20" on:change={input}>
|
||||
<div class="col-lg col-4 p-10 d-flex flex-column justify-content-end">
|
||||
<div class="pb-10 font-size-24 font-weight-semi-bold d-flex">
|
||||
<div class="material-icons mr-10 font-size-30">title</div>
|
||||
|
|
@ -61,7 +38,14 @@
|
|||
<div class="input-group-prepend">
|
||||
<span class="input-group-text d-flex material-icons bg-dark pr-0 font-size-18">search</span>
|
||||
</div>
|
||||
<!-- svelte-ignore missing-declaration -->
|
||||
<input
|
||||
on:input={({ target }) => {
|
||||
queueMicrotask(() => {
|
||||
search.search = target.value
|
||||
input()
|
||||
})
|
||||
}}
|
||||
type="search"
|
||||
class="form-control bg-dark border-left-0 shadow-none text-capitalize"
|
||||
autocomplete="off"
|
||||
|
|
@ -154,7 +138,8 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="col-auto p-10 d-flex">
|
||||
<button class="btn bg-dark material-icons font-size-18 px-5 align-self-end shadow-lg border-0" type="button" on:click={searchClear} class:text-primary={!!media}>delete</button>
|
||||
<button class="btn bg-dark material-icons font-size-18 px-5 align-self-end shadow-lg border-0" type="button" on:click={searchClear} class:text-primary={!!current}
|
||||
>delete</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,27 @@
|
|||
<script>
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
import Cards from './Cards.svelte'
|
||||
export let opts = {}
|
||||
export let opts
|
||||
let cards = opts.load(1, 5)
|
||||
let interval = null
|
||||
if (opts.releases) {
|
||||
interval = setInterval(async () => {
|
||||
const media = await opts.load(false, 5)
|
||||
if (media) cards = media
|
||||
}, 30000)
|
||||
}
|
||||
onDestroy(() => {
|
||||
if (interval) clearInterval(interval)
|
||||
})
|
||||
</script>
|
||||
|
||||
<span class="d-flex px-20 align-items-end pointer text-decoration-none text-muted" on:click={opts.click}>
|
||||
<span class="d-flex px-20 align-items-end pointer text-decoration-none text-muted" on:click={opts.onclick}>
|
||||
<div class="pl-10 font-size-24 font-weight-semi-bold">{opts.title}</div>
|
||||
<div class="pr-10 ml-auto font-size-12">View More</div>
|
||||
</span>
|
||||
<div class="gallery pt-10 pb-20 w-full overflow-x-hidden position-relative">
|
||||
<Cards cards={opts.cards} />
|
||||
<Cards {cards} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* global halfmoon */
|
||||
import { alToken } from '@/lib/pages/Settings.svelte'
|
||||
|
||||
export const alID =
|
||||
|
|
@ -162,7 +161,7 @@ query ($page: Int, $perPage: Int, $sort: [MediaSort], $type: MediaType, $search:
|
|||
pageInfo {
|
||||
hasNextPage
|
||||
},
|
||||
media(type: $type, search: $search, sort: $sort, status_in: $status) {
|
||||
media(type: $type, search: $search, sort: $sort, status_in: $status, isAdult: false) {
|
||||
${queryObjects}
|
||||
}
|
||||
}
|
||||
|
|
@ -249,7 +248,7 @@ query ($page: Int, $perPage: Int, $from: Int, $to: Int) {
|
|||
episode,
|
||||
timeUntilAiring,
|
||||
airingAt,
|
||||
media{
|
||||
media(isAdult: false){
|
||||
${queryObjects}
|
||||
}
|
||||
}
|
||||
|
|
@ -265,12 +264,12 @@ query ($page: Int, $perPage: Int, $from: Int, $to: Int) {
|
|||
variables.status = opts.status
|
||||
variables.sort = opts.sort || 'SEARCH_MATCH'
|
||||
query = `
|
||||
query ($page: Int, $perPage: Int, $sort: [MediaSort], $type: MediaType, $search: String, $status: MediaStatus, $season: MediaSeason, $year: Int, $genre: String, $format: MediaFormat, $startDate: FuzzyDateInt) {
|
||||
query ($page: Int, $perPage: Int, $sort: [MediaSort], $type: MediaType, $search: String, $status: MediaStatus, $season: MediaSeason, $year: Int, $genre: String, $format: MediaFormat) {
|
||||
Page (page: $page, perPage: $perPage) {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
},
|
||||
media(type: $type, search: $search, sort: $sort, status: $status, season: $season, seasonYear: $year, genre: $genre, format: $format, startDate_greater: $startDate) {
|
||||
media(type: $type, search: $search, sort: $sort, status: $status, season: $season, seasonYear: $year, genre: $genre, format: $format, isAdult: false) {
|
||||
${queryObjects}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,30 +45,23 @@ export async function add (torrentID) {
|
|||
page.set('player')
|
||||
if (typeof torrentID === 'string' && !torrentID.startsWith('magnet:')) {
|
||||
const res = await fetch(torrentID)
|
||||
const buffer = await res.arrayBuffer()
|
||||
const file = new File([buffer], 'file.torrent', {
|
||||
torrentID = new File([await res.arrayBuffer()], 'file.torrent', {
|
||||
type: 'application/x-bittorrent'
|
||||
})
|
||||
addTorrent(file)
|
||||
} else {
|
||||
addTorrent(torrentID)
|
||||
}
|
||||
client.add(torrentID, {
|
||||
private: set.torrentPeX,
|
||||
path: set.torrentPath,
|
||||
destroyStoreOnDestroy: !set.torrentPersist,
|
||||
announce: [
|
||||
'wss://tracker.openwebtorrent.com',
|
||||
'wss://spacetradersapi-chatbox.herokuapp.com:443/announce',
|
||||
'wss://peertube.cpy.re:443/tracker/socket'
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function addTorrent (id) {
|
||||
client.add(id, {
|
||||
private: set.torrentPeX,
|
||||
path: set.torrentPath,
|
||||
destroyStoreOnDestroy: !set.torrentPersist,
|
||||
announce: [
|
||||
'wss://tracker.openwebtorrent.com',
|
||||
'wss://spacetradersapi-chatbox.herokuapp.com:443/announce',
|
||||
'wss://peertube.cpy.re:443/tracker/socket'
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
client.on('torrent', torrent => {
|
||||
console.log('ready', torrent.name)
|
||||
const string = JSON.stringify(Array.from(torrent.torrentFile))
|
||||
|
|
|
|||
Loading…
Reference in a new issue