feat: add pagination to all searches

This commit is contained in:
ThaUnknown 2022-03-18 01:18:13 +01:00
parent bb8d8c5ad5
commit 574cc09860
5 changed files with 130 additions and 106 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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}
}
}

View file

@ -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))