feat: sw offline cache

feat: detect if outdated version and force update
fix: use drag scroll everywhere
This commit is contained in:
ThaUnknown 2025-04-09 22:48:18 +02:00
parent e95aef43dc
commit 37266b88e8
No known key found for this signature in database
24 changed files with 2972 additions and 67 deletions

View file

@ -21,6 +21,7 @@
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"@types/debug": "^4.1.12",
"@types/events": "^3.0.3",
"@types/semver": "^7.7.0",
"@urql/introspection": "^1.1.0",
"autoprefixer": "^10.4.20",
"bits-ui": "^0.22.0",
@ -61,12 +62,15 @@
"js-levenshtein": "^1.1.6",
"lucide-svelte": "^0.452.0",
"p2pt": "^1.5.1",
"semver": "^7.7.1",
"simple-store-svelte": "^1.0.6",
"svelte-keybinds": "^1.0.9",
"svelte-persisted-store": "^0.12.0",
"tailwind-merge": "^2.5.4",
"tailwind-variants": "^0.2.1",
"uint8-util": "^2.2.5",
"urql": "^4.2.1"
"urql": "^4.2.1",
"workbox-core": "^7.3.0",
"workbox-precaching": "^7.3.0"
}
}

File diff suppressed because it is too large Load diff

View file

@ -190,6 +190,14 @@ details:active,
background: black !important;
}
.svelte-progress-bar {
height: 2px !important;
}
.svelte-progress-bar-leader {
height: 4px !important;
}
/* Backplate related things */
body {

1
src/app.d.ts vendored
View file

@ -67,6 +67,7 @@ export interface Native {
subtitles: (hash: string, id: number, cb: (subtitle: { text: string, time: number, duration: number }, trackNumber: number) => void) => Promise<void>
chapters: (hash: string, id: number) => Promise<Array<{ start: number, end: number, text: string }>>
isApp: boolean
version: () => string
}
declare global {

View file

@ -20,7 +20,7 @@
import { episodes as _episodes, dedupeAiring, episodeByAirDate, notes, type Media } from '$lib/modules/anilist'
import { cn, isMobile, since } from '$lib/utils'
import { list, progress } from '$lib/modules/auth'
import { click } from '$lib/modules/navigate'
import { click, dragScroll } from '$lib/modules/navigate'
export let eps: EpisodesResponse | null
export let media: Media
@ -69,7 +69,7 @@
</script>
<Pagination count={episodeCount} {perPage} bind:currentPage let:pages let:hasNext let:hasPrev let:range let:setPage siblingCount={1}>
<div class='overflow-y-auto pt-3 -mx-14 px-14 pointer-events-none -mb-3 pb-3'>
<div class='overflow-y-auto pt-3 -mx-14 px-14 pointer-events-none -mb-3 pb-3' use:dragScroll>
<div class='grid grid-cols-1 sm:grid-cols-[repeat(auto-fit,minmax(500px,1fr))] place-items-center gap-x-10 gap-y-7 justify-center align-middle pointer-events-auto'>
{#each getPage(currentPage) as { episode, image, title, summary, airingAt, airdate, filler, length } (episode)}
{@const watched = _progress >= episode}

View file

@ -13,7 +13,7 @@
import * as Dialog from '$lib/components/ui/dialog'
import { settings, videoResolutions } from '$lib/modules/settings'
import { title, type Media } from '$lib/modules/anilist'
import { click } from '$lib/modules/navigate'
import { click, dragScroll } from '$lib/modules/navigate'
import { fastPrettyBytes, since } from '$lib/utils'
const termMapping: Record<string, {text: string, color: string}> = {}
@ -178,7 +178,7 @@
Auto Select Torrent
</ProgressButton>
</div>
<div class='h-full overflow-y-auto px-4 sm:px-6 pt-2' role='menu' tabindex='-1' on:keydown={stopAnimation} on:pointerenter={stopAnimation} on:pointermove={stopAnimation}>
<div class='h-full overflow-y-auto px-4 sm:px-6 pt-2' role='menu' tabindex='-1' on:keydown={stopAnimation} on:pointerenter={stopAnimation} on:pointermove={stopAnimation} use:dragScroll>
{#await searchResult}
{#each Array.from({ length: 12 }) as _, i (i)}
<div class='p-3 h-[104px] flex cursor-pointer mb-2 relative rounded-md overflow-hidden border border-border flex-col justify-between'>

View file

@ -13,6 +13,7 @@
import * as Tree from '$lib/components/ui/tree'
import { Button } from '$lib/components/ui/button'
import { cn, toTS } from '$lib/utils'
import { dragScroll } from '$lib/modules/navigate'
export let wrapper: HTMLDivElement
@ -59,7 +60,7 @@
</Button>
</Dialog.Trigger>
<Dialog.Content class='absolute bg-transparent border-none p-0 shadow-none size-full overflow-hidden'>
<div on:pointerdown|self={close} class='size-full flex justify-center items-center flex-col overflow-y-scroll text-[6px] lg:text-xs'>
<div on:pointerdown|self={close} class='size-full flex justify-center items-center flex-col overflow-y-scroll text-[6px] lg:text-xs' use:dragScroll>
{#if showKeybinds}
<div class='bg-black py-3 px-4 rounded-md text-sm lg:text-lg font-bold mb-4'>
Drag and drop binds to change them

View file

@ -67,7 +67,8 @@ export default Object.assign<Native, Partial<Native>>({
{ start: 5, end: 15, text: 'OP' },
{ start: 1.0 * 60, end: 1.2 * 60, text: 'Chapter 1' },
{ start: 1.4 * 60, end: 88, text: 'Chapter 2 ' }
]
],
version: () => 'v0.0.2'
// @ts-expect-error idk
}, globalThis.native as Partial<Native>)

15
src/lib/modules/update.ts Normal file
View file

@ -0,0 +1,15 @@
import { compare, diff } from 'semver'
import native from './native'
import { version } from '$app/environment'
function compareVersions (): 'ui' | 'client' | undefined {
const releaseType = diff(version, native.version())
if (!releaseType) return
if (releaseType === 'patch') return
return compare(version, native.version()) === -1 ? 'ui' : 'client'
}
export const outdatedComponent = compareVersions()

View file

@ -1,5 +1,8 @@
import { redirect } from '@sveltejs/kit'
import { outdatedComponent } from '$lib/modules/update'
export function load () {
if (outdatedComponent) return redirect(307, '/update/')
redirect(307, localStorage.getItem('setup-finished') ? '/app/home/' : '/setup')
}

View file

@ -3,7 +3,6 @@
import { Sidebar } from '$lib/components/ui/sidebar'
import SearchModal from '$lib/components/SearchModal.svelte'
import Sidebarlist from '$lib/components/ui/sidebar/sidebarlist.svelte'
import { version, dev } from '$app/environment'
import { Player } from '$lib/components/ui/player'
</script>

View file

@ -1,7 +1,11 @@
import { error } from '@sveltejs/kit'
import { error, redirect } from '@sveltejs/kit'
import { dev } from '$app/environment'
import native from '$lib/modules/native'
import { outdatedComponent } from '$lib/modules/update'
export function load () {
if (!import.meta.env.DEV && !native.isApp) error(401, 'How did you get here?')
if (!dev && !native.isApp) return error(401, 'How did you get here?')
if (outdatedComponent) redirect(307, '/update/')
}

View file

@ -143,10 +143,6 @@
// }
// TODO: selects should turn into modals on mobile! like anilist
// TODO: infinite scroll
onMount(async () => {
await tick()
hideBanner.value = true
})
async function imagePicker (e: Event) {
const target = e.target as HTMLInputElement
@ -166,7 +162,7 @@
clear()
search.ids = [...new Set(res.map(r => r.anilist))]
// TODO: sort by similarity
// TODO: sort by similarity, finish
} catch (e) {
console.error(e)
}
@ -175,8 +171,7 @@
</script>
<div class='flex flex-col h-full overflow-y-auto overflow-x-clip -ml-14 pl-14 z-20 min-w-0 grow pointer-events-none' use:dragScroll>
<div class='sticky top-0 z-20 px-10 pointer-events-auto shrink-0 overflow-clip'>
<BannerImage class='-z-10 -left-14' />
<div class='sticky top-0 z-20 px-10 pointer-events-auto shrink-0 overflow-clip bg-black'>
<div class='flex flex-wrap pt-5'>
<div class='grid items-center w-1/4 p-2'>
<div class='text-xl font-bold mb-1 ml-1'>

View file

@ -1,6 +1,7 @@
<script lang='ts'>
import SettingsNav from '$lib/components/SettingsNav.svelte'
import { Separator } from '$lib/components/ui/separator'
import { dragScroll } from '$lib/modules/navigate'
const items = [
{
@ -8,8 +9,8 @@
href: '/app/settings/'
},
{
title: 'Torrent',
href: '/app/settings/torrent/'
title: 'Client',
href: '/app/settings/client/'
},
{
title: 'Interface',
@ -46,7 +47,7 @@
<aside class='lg:grow lg:max-w-60'>
<SettingsNav {items} />
</aside>
<div class='flex-1 overflow-y-scroll'>
<div class='flex-1 overflow-y-scroll' use:dragScroll>
<slot />
</div>
</div>

View file

@ -35,7 +35,7 @@
<div class='flex items-center leading-none text-sm'>
{#await promise}
<div class='w-4 h-4 relative animate-spin mr-2.5'>
<div class='w-4 h-4 border-2 rounded-[50%] border-neutral-700 border-b-border'></div>
<div class='w-4 h-4 border-2 rounded-[50%] border-neutral-700 border-b-border' />
</div>
{title} -&nbsp;<span class='text-muted-foreground text-xs'>{pending}</span>
{:then { status, text }}

View file

@ -9,6 +9,7 @@
import SettingCard from '$lib/components/SettingCard.svelte'
import { SingleCombo } from '$lib/components/ui/combobox'
import { lookupPreferences, settings } from '$lib/modules/settings'
import { dragScroll } from '$lib/modules/navigate'
function checkExtensions (svd: Record<string, ExtensionConfig>, opts: Record<string, {
options: Record<string, string | number | boolean | undefined>
@ -35,7 +36,7 @@
<Progress step={2} />
<div class='space-y-3 lg:max-w-4xl h-full overflow-y-auto w-full py-8'>
<div class='space-y-3 lg:max-w-4xl h-full overflow-y-auto w-full py-8' use:dragScroll>
<SettingCard title='Lookup Preference' description='What to prioritize when looking for and sorting results. Quality will focus on the best quality available which often means big file sizes, Size will focus on the smallest file size available, and Availability will pick results with the most peers regardless of size and quality.'>
<SingleCombo bind:value={$settings.lookupPreference} items={lookupPreferences} class='w-32 shrink-0 border-input border' />
</SettingCard>

View file

@ -37,6 +37,7 @@
import { Switch } from '$lib/components/ui/switch'
import { settings } from '$lib/modules/settings'
import native from '$lib/modules/native'
import { dragScroll } from '$lib/modules/navigate'
if (!speedTest.isRunning) speedTest.play()
@ -55,7 +56,7 @@
<Progress step={1} />
<div class='space-y-3 lg:max-w-4xl pt-5 h-full overflow-y-auto'>
<div class='space-y-3 lg:max-w-4xl pt-5 h-full overflow-y-auto' use:dragScroll>
<SettingCard class='bg-transparent' let:id title='Streamed Download' description="Only downloads the single file that's currently being watched, instead of downloading an entire batch of episodes. Saves bandwidth and reduces strain on the peer swarm.">
<Switch {id} bind:checked={$settings.torrentStreamedDownload} />
</SettingCard>

View file

@ -9,6 +9,7 @@
import { SUPPORTS, settings } from '$lib/modules/settings'
import native from '$lib/modules/native'
import { fastPrettyBytes } from '$lib/utils'
import { dragScroll } from '$lib/modules/navigate'
async function selectDownloadFolder () {
$settings.torrentPath = await native.selectDownload()
@ -26,7 +27,7 @@
<Progress />
<div class='space-y-3 lg:max-w-4xl pt-5 h-full overflow-y-auto'>
<div class='space-y-3 lg:max-w-4xl pt-5 h-full overflow-y-auto' use:dragScroll>
<SettingCard class='bg-transparent' let:id title='Torrent Download Location' description='Path to the folder used to store torrents. By default this is the TMP folder, which might lose data when your OS tries to reclaim storage. {SUPPORTS.isAndroid ? 'RESTART IS REQUIRED. /sdcard/ is internal storage, not external SD Cards. /storage/AB12-34CD/ is external storage, not internal. Thank you Android!' : ''}'>
<div class='flex'>
{#if !SUPPORTS.isAndroid}

View file

View file

@ -0,0 +1,7 @@
import { redirect } from '@sveltejs/kit'
import { outdatedComponent } from '$lib/modules/update'
export function load () {
if (!outdatedComponent) redirect(307, '/')
}

View file

@ -0,0 +1,10 @@
import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'
import { clientsClaim, skipWaiting } from 'workbox-core'
import { build, files, prerendered, version } from '$service-worker'
const precache = [...build, ...files, ...prerendered, '/'].map(url => ({ url, revision: version }))
precacheAndRoute(precache)
cleanupOutdatedCaches()
clientsClaim()
skipWaiting()

View file

@ -13,9 +13,6 @@ const config = {
preprocess: vitePreprocess({}),
kit: {
adapter: adapter({ fallback: 'index.html' }),
alias: {
'@/*': './path/to/lib/*'
},
version: {
name: process.env.npm_package_version
}

View file

@ -24,7 +24,11 @@
],
"maxNodeModuleJsDepth": 3
},
"files": [
"src/service-worker/index.ts"
],
"include": [
"src/service-worker/index.ts",
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.svelte",