mirror of
https://github.com/NoCrypt/migu.git
synced 2026-04-19 07:32:05 +00:00
feat: change card display mode
feat: pagination hasNextPage fix: tosho fake stats
This commit is contained in:
parent
d954314e9b
commit
51728d1ced
10 changed files with 61 additions and 32 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) }))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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')) || {}) }
|
||||
|
|
|
|||
Loading…
Reference in a new issue