mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-03-11 22:15:35 +00:00
feat: sw offline cache
feat: detect if outdated version and force update fix: use drag scroll everywhere
This commit is contained in:
parent
e95aef43dc
commit
37266b88e8
24 changed files with 2972 additions and 67 deletions
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
2932
pnpm-lock.yaml
2932
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -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
1
src/app.d.ts
vendored
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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'>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
15
src/lib/modules/update.ts
Normal 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()
|
||||
|
|
@ -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')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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/')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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} - <span class='text-muted-foreground text-xs'>{pending}</span>
|
||||
{:then { status, text }}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
0
src/routes/update/+page.svelte
Normal file
0
src/routes/update/+page.svelte
Normal file
7
src/routes/update/+page.ts
Normal file
7
src/routes/update/+page.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { redirect } from '@sveltejs/kit'
|
||||
|
||||
import { outdatedComponent } from '$lib/modules/update'
|
||||
|
||||
export function load () {
|
||||
if (!outdatedComponent) redirect(307, '/')
|
||||
}
|
||||
10
src/service-worker/index.ts
Normal file
10
src/service-worker/index.ts
Normal 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()
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue