feat: change card display mode

feat: pagination hasNextPage
fix: tosho fake stats
This commit is contained in:
ThaUnknown 2023-07-06 22:42:12 +02:00
parent d954314e9b
commit 51728d1ced
10 changed files with 61 additions and 32 deletions

View file

@ -1,6 +1,6 @@
{
"name": "Miru",
"version": "4.1.3",
"version": "4.1.4",
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
"description": "Stream anime torrents, real-time with no waiting for downloads.",
"main": "build/main.js",

View file

@ -20,7 +20,7 @@
import RSSView from './views/RSSView.svelte'
import Menubar from './components/Menubar.svelte'
import Toasts from './components/Toasts.svelte'
import CatBlock from './views/CatBlock.svelte'
import CatBlock from './views/CatBlock.svelte' // TODO: deprecate
setContext('view', view)
@ -31,7 +31,7 @@
<Toasts />
<div class='page-wrapper with-sidebar with-transitions bg-dark' data-sidebar-type='overlayed-all'>
<div class='sticky-alerts' />
<!-- <CatBlock /> -->
<CatBlock />
<Menubar bind:page={$page} />
<ViewAnime />
<ViewTrailer />

View file

@ -10,6 +10,7 @@
<script>
import { traceAnime } from '@/modules/anime.js'
import { set } from '../views/Settings.svelte'
export let search
let searchTextInput
@ -38,6 +39,10 @@
target.value = null
}
}
function changeCardMode (type) {
set.cards = type
form.dispatchEvent(new Event('input', { bubbles: true }))
}
</script>
<form class='container-fluid py-20 px-10 pb-0 position-sticky top-0 search-container z-40 bg-dark' on:input bind:this={form}>
@ -187,8 +192,8 @@
<span class='badge bg-light border-0 py-5 px-10 text-capitalize mr-20 text-white text-nowrap'>{('' + badge).replace(/_/g, ' ').toLowerCase()}</span>
{/each}
{/if}
<span class='material-symbols-outlined font-size-24 mr-10 filled ml-auto text-dark-light'>grid_on</span>
<span class='material-symbols-outlined font-size-24 filled text-dark-light'>grid_view</span>
<span class='material-symbols-outlined font-size-24 mr-10 filled ml-auto text-dark-light pointer' class:text-muted={set.cards === 'small'} on:click={() => changeCardMode('small')}>grid_on</span>
<span class='material-symbols-outlined font-size-24 filled text-dark-light pointer' class:text-muted={set.cards === 'full'} on:click={() => changeCardMode('full')}>grid_view</span>
</div>
</form>
@ -197,11 +202,11 @@
color: var(--gray-color-light);
}
.input-group,
.container-fluid button {
.container-fluid button, .pointer {
transition: scale 0.2s ease;
}
.input-group:hover {
.input-group:hover, .pointer:hover {
scale: 1.08;
}

View file

@ -5,11 +5,14 @@
import FullCard from './FullCard.svelte'
import EpisodeCard from './EpisodeCard.svelte'
import FullSkeletonCard from './FullSkeletonCard.svelte'
import { set } from '../../views/Settings.svelte'
export let card
const type = card.type || set.cards
</script>
{#if card.type === 'episode'}
{#if type === 'episode'}
{#await card.data}
<EpisodeSkeletonCard />
@ -19,7 +22,7 @@
{/if}
{/await}
{:else if card.type === 'full'}
{:else if type === 'full'}
{#await card.data}
<FullSkeletonCard />
@ -29,7 +32,7 @@
{/if}
{/await}
{:else} <!-- card.type === 'small' -->
{:else} <!-- type === 'small' -->
{#await card.data}
<SkeletonCard />

View file

@ -211,8 +211,8 @@ function mapTosho2dDeDupedEntry (entries) {
const dupe = deduped[entry.info_hash]
dupe.title ??= entry.title || entry.torrent_name
dupe.id ||= entry.nyaa_id
dupe.seeders ||= entry.seeders >= 100000 ? entry.leechers * 3 : entry.seeders
dupe.leechers ||= entry.leechers ?? 0
dupe.seeders ||= entry.seeders >= 100000 ? 0 : entry.seeders
dupe.leechers ||= entry.leechers >= 100000 ? 0 : entry.leechers
dupe.downloads ||= entry.torrent_downloaded_count
dupe.size ||= entry.total_size && fastPrettyBytes(entry.total_size)
dupe.date ||= entry.timestamp && new Date(entry.timestamp * 1000)
@ -221,8 +221,8 @@ function mapTosho2dDeDupedEntry (entries) {
title: entry.title || entry.torrent_name,
link: entry.magnet_uri,
id: entry.nyaa_id,
seeders: entry.seeders >= 100000 ? entry.leechers * 3 : entry.seeders, // this is a REALLY bad assumption to make, but its a decent guess
leechers: entry.leechers,
seeders: entry.seeders >= 100000 ? 0 : entry.seeders,
leechers: entry.leechers >= 100000 ? 0 : entry.leechers,
downloads: entry.torrent_downloaded_count,
size: entry.total_size && fastPrettyBytes(entry.total_size),
date: entry.timestamp && new Date(entry.timestamp * 1000)

View file

@ -3,6 +3,7 @@ import { set } from '@/views/Settings.svelte'
import { addToast } from '@/components/Toasts.svelte'
import { add } from '@/modules/torrent.js'
import { resolveFileMedia, getEpisodeMetadataForMedia } from './anime.js'
import { hasNextPage } from '@/modules/sections.js'
export const exclusions = ['DTS']
const isDev = location.hostname === 'localhost'
@ -81,6 +82,7 @@ class RSSMediaManager {
const index = (page - 1) * perPage
const targetPage = [...content.querySelectorAll('item')].slice(index, index + perPage)
const items = parseRSSNodes(targetPage)
hasNextPage.value = items.length === perPage
const result = items.map(item => this.resolveAnimeFromRSSItem(item))
this.resultMap[url] = {
date: pubDate,

View file

@ -1,4 +1,7 @@
import { alRequest } from '@/modules/anilist.js'
import { writable } from 'simple-store-svelte'
export const hasNextPage = writable(true)
export default class Sections {
constructor (data = []) {
@ -22,7 +25,7 @@ export default class Sections {
static wrapResponse (res, length, type) {
res.then(res => {
this.hasNext = res?.data?.Page.pageInfo.hasNextPage
hasNextPage.value = res?.data?.Page.pageInfo.hasNextPage
})
return Array.from({ length }, (_, i) => ({ type, data: Sections.fromPending(res, i) }))
}

View file

@ -17,7 +17,7 @@
manager.add([
{
title,
load: (page = 1, perPage = 12) => RSSManager.getMediaForRSS(page, perPage, url),
load: (page = 1, perPage = 6) => RSSManager.getMediaForRSS(page, perPage, url),
preview: RSSManager.getMediaForRSS(1, 6, url),
variables: { disableSearch: true }
}

View file

@ -10,37 +10,52 @@
<script>
import Search, { searchCleanup } from '../components/Search.svelte'
import Card from '../components/cards/Card.svelte'
import { hasNextPage } from '@/modules/sections.js'
import smoothScroll from '@/modules/scroll.js'
import { debounce } from '@/modules/util.js'
let page = 1
function loadSearchData (search) {
const load = search.load || Sections.createFallbackLoad()
$items = load(page, undefined, searchCleanup(search))
items.value = []
hasNextPage.value = true
function loadSearchData () {
const load = $search.load || Sections.createFallbackLoad()
const nextData = load(page, undefined, searchCleanup($search))
$items = [...$items, ...nextData]
return nextData[nextData.length - 1].data
}
loadSearchData($search)
const update = debounce(loadSearchData, 150)
const update = debounce(() => {
page = 1
items.value = []
loadSearchData()
}, 150)
let canScroll = true
const hasNextPage = true
async function loadTillFull (element) {
while (hasNextPage.value && element.scrollHeight <= element.clientHeight) {
await loadSearchData()
}
}
async function infiniteScroll () {
if (canScroll && hasNextPage && this.scrollTop + this.clientHeight > this.scrollHeight - 800) {
if (canScroll && $hasNextPage && this.scrollTop + this.clientHeight > this.scrollHeight - 800) {
canScroll = false
const load = search.load || Sections.createFallbackLoad()
const nextData = load(++page, undefined, searchCleanup(search))
$items = [...$items, ...nextData]
nextData[nextData.length - 1].data.then(() => { canScroll = true })
page++
await loadSearchData()
canScroll = true
}
}
</script>
<div class='h-full w-full overflow-y-scroll d-flex flex-wrap flex-row root overflow-x-hidden px-50 justify-content-center align-content-start' use:smoothScroll on:scroll={infiniteScroll}>
<div class='h-full w-full overflow-y-scroll d-flex flex-wrap flex-row root overflow-x-hidden px-50 justify-content-center align-content-start' use:smoothScroll use:loadTillFull on:scroll={infiniteScroll}>
<Search bind:search={$search} on:input={() => update($search)} />
{#each $items as card}
<Card {card} />
{/each}
{#key $items}
{#each $items as card}
<Card {card} />
{/each}
{/key}
</div>
<style>

View file

@ -20,7 +20,8 @@
doHURL: 'https://cloudflare-dns.com/dns-query',
disableSubtitleBlur: false,
catURL: decodeURIComponent(atob('aHR0cHMlM0ElMkYlMkZueWFhLnNp')),
showDetailsInRPC: true
showDetailsInRPC: true,
cards: 'small'
}
localStorage.removeItem('relations') // TODO: remove
export const set = { ...defaults, ...(JSON.parse(localStorage.getItem('settings')) || {}) }