mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-03-11 22:15:35 +00:00
feat: greatly improve setup screen
This commit is contained in:
parent
c4ee54beba
commit
0037dc2231
12 changed files with 59 additions and 39 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ui",
|
||||
"version": "6.3.26",
|
||||
"version": "6.3.27",
|
||||
"license": "BUSL-1.1",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.14.4",
|
||||
|
|
|
|||
10
src/app.css
10
src/app.css
|
|
@ -316,6 +316,16 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
@keyframes bg-grid-animate {
|
||||
from {
|
||||
background-position: 0 100%;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position: 100% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-marquee {
|
||||
animation: marquee 80s infinite linear;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@
|
|||
|
||||
let importPromise = Promise.resolve()
|
||||
|
||||
function importExtension () {
|
||||
export function importExtension (ext = extensionInput) {
|
||||
importPromise = (async () => {
|
||||
try {
|
||||
await storage.import(extensionInput)
|
||||
await storage.import(ext)
|
||||
} catch (err) {
|
||||
const error = err as Error
|
||||
toast.error(error.cause as string, { description: error.message })
|
||||
|
|
@ -132,7 +132,7 @@
|
|||
Importing extensions....
|
||||
</Button>
|
||||
{:then _}
|
||||
<Button class='font-bold flex items-center justify-center w-full sm:w-56 max-w-full shrink-0' size='default' on:click={importExtension}>
|
||||
<Button class='font-bold flex items-center justify-center w-full sm:w-56 max-w-full shrink-0' size='default' on:click={() => importExtension()}>
|
||||
<Plus size={iconSizes.lg} class='mr-2' />
|
||||
Import Extensions
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@
|
|||
function seek (time: number) {
|
||||
// WARN: this causes all subscriptions to video to re-run!!!
|
||||
video.currentTime = currentTime = currentTime + time
|
||||
currentTime = currentTime + time
|
||||
// currentTime = currentTime + time
|
||||
playAnimation(time > 0 ? 'seekforw' : 'seekback')
|
||||
}
|
||||
function seekTo (time: number) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// TODO: update these
|
||||
import { writable } from 'simple-store-svelte'
|
||||
|
||||
import type { Media } from './modules/anilist'
|
||||
|
||||
// TODO: update these
|
||||
export const CHANGELOG_URL = 'https://api.github.com/repos/ThaUnknown/miru/releases'
|
||||
export const WEB_URL = 'https://miru.watch'
|
||||
export const DEFAULT_EXTENSIONS = 'gh:hayase-app/extensions'
|
||||
export const SETUP_VERSION = 2
|
||||
export const SETUP_VERSION = 3
|
||||
|
||||
// episode is optional here, but is actually always defined
|
||||
export const searchStore = writable<{episode?: number, media?: Media}>({})
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ export default Object.assign<Native, Partial<Native>>({
|
|||
setPlayBackState: async e => { navigator.mediaSession.playbackState = e },
|
||||
setActionHandler: async (...args) => navigator.mediaSession.setActionHandler(...args as [action: MediaSessionAction, handler: MediaSessionActionHandler | null]),
|
||||
checkAvailableSpace: () => new Promise(resolve => setTimeout(() => resolve(Math.floor(Math.random() * (1e10 - 1e8 + 1) + 1e8)), 1000)),
|
||||
checkIncomingConnections: () => new Promise(resolve => setTimeout(() => resolve(Math.random() > 0.5), 5000)),
|
||||
checkIncomingConnections: () => new Promise(resolve => setTimeout(() => resolve(false), 1000)),
|
||||
updatePeerCounts: async () => [],
|
||||
isApp: false,
|
||||
playTorrent: async () => dummyFiles,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<div class='w-full h-full flex items-center justify-center md:py-20 md:px-4 lg:px-10'>
|
||||
<div class='md:bg-neutral-950 w-full md:max-w-[1140px] h-full md:max-h-[720px] rounded-lg flex-col md:border border-border flex items-center justify-center overflow-clip'>
|
||||
<div class='absolute opacity-25 w-full h-[130%] -z-[1] bg-cover bg-no-repeat bg-[url(/bg_grid.jpg)] animate-[bg-grid-animate_60s_linear_infinite_alternate]' />
|
||||
<div class='w-full md:max-w-[1140px] h-full md:max-h-[720px] flex-col flex items-center justify-center overflow-clip'>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,18 +5,20 @@
|
|||
import { Checkbox } from '$lib/components/ui/checkbox'
|
||||
import { Label } from '$lib/components/ui/label'
|
||||
import native from '$lib/modules/native'
|
||||
import { click } from '$lib/modules/navigate'
|
||||
import { click, dragScroll } from '$lib/modules/navigate'
|
||||
|
||||
let checked = false
|
||||
</script>
|
||||
|
||||
<Logo class='w-52 h-52 object-contain mb-14' />
|
||||
<div class='font-bold text-5xl'>Welcome to Hayase</div>
|
||||
<div class='text-muted-foreground pt-6'>Let's set up your perfect streaming environment.</div>
|
||||
<div class='flex items-center space-x-2 pt-12'>
|
||||
<Checkbox id='terms' bind:checked />
|
||||
<Label for='terms' class='text-md font-medium leading-none text-muted-foreground'>
|
||||
I agree to the <a use:click={() => native.openURL(`${WEB_URL}/terms`)} class='text-primary underline'>Terms of Service</a> and <a use:click={() => native.openURL(`${WEB_URL}/privacy`)} class='text-primary underline'>Privacy Policy</a>
|
||||
</Label>
|
||||
<div class='space-y-3 lg:max-w-4xl h-full overflow-y-auto w-full py-8 flex flex-col items-center justify-center' use:dragScroll>
|
||||
<Logo class='w-52 h-52 object-contain mb-14 shrink-0' />
|
||||
<div class='font-bold text-5xl'>Welcome to Hayase</div>
|
||||
<div class='text-muted-foreground pt-3'>Let's set up your perfect streaming environment.</div>
|
||||
<div class='flex items-center space-x-2 pt-12 pb-3'>
|
||||
<Checkbox id='terms' bind:checked />
|
||||
<Label for='terms' class='text-md font-medium leading-none text-muted-foreground'>
|
||||
I agree to the <a use:click={() => native.openURL(`${WEB_URL}/terms`)} class='text-primary underline'>Terms of Service</a> and <a use:click={() => native.openURL(`${WEB_URL}/privacy`)} class='text-primary underline'>Privacy Policy</a>
|
||||
</Label>
|
||||
</div>
|
||||
<Button class='text-lg font-bold shrink-0' disabled={!checked} size='lg' href={checked ? './storage' : undefined}>Start Setup</Button>
|
||||
</div>
|
||||
<Button class='mt-8 text-lg font-bold' disabled={!checked} size='lg' href={checked ? './storage' : undefined}>Start Setup</Button>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
promise: Promise<{
|
||||
status: 'warning' | 'success' | 'error'
|
||||
text: string
|
||||
slot?: string
|
||||
}>
|
||||
title: string
|
||||
pending: string
|
||||
|
|
@ -41,7 +42,7 @@
|
|||
<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 text-wrap'>{pending}</span>
|
||||
{:then { status, text }}
|
||||
{:then { status, text, slot }}
|
||||
<Badge variant={status} class='w-4 h-4 rounded-[50%] p-[3px] justify-center items-center mr-2.5'>
|
||||
{#if status === 'success'}
|
||||
<Check strokeWidth='4px' />
|
||||
|
|
@ -54,13 +55,18 @@
|
|||
<X strokeWidth='4px' />
|
||||
{/if}
|
||||
</Badge>
|
||||
{title} - <span class='text-muted-foreground text-xs text-wrap'>{text}</span>
|
||||
{title} - <span class='text-muted-foreground text-xs text-wrap flex'>
|
||||
{text}
|
||||
{#if slot}
|
||||
<slot />
|
||||
{/if}
|
||||
</span>
|
||||
{/await}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class='flex flex-row items-center justify-between w-full bg-neutral-950 border-t border-border py-4 px-8'>
|
||||
<div class='flex flex-row items-center justify-between w-full bg-neutral-950 border-t md:border md:rounded-lg border-border py-4 px-8'>
|
||||
<Button variant='secondary' class='w-24' href='../{PREV[step]}'>Prev</Button>
|
||||
{#await settled}
|
||||
<Tooltip.Root>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang='ts'>
|
||||
import { onMount } from 'svelte'
|
||||
import { persisted } from 'svelte-persisted-store'
|
||||
import { toast } from 'svelte-sonner'
|
||||
|
||||
import Footer, { type Checks } from '../Footer.svelte'
|
||||
import Progress from '../Progress.svelte'
|
||||
|
|
@ -11,7 +11,8 @@
|
|||
import SettingCard from '$lib/components/SettingCard.svelte'
|
||||
import { SingleCombo } from '$lib/components/ui/combobox'
|
||||
import { Extensions } from '$lib/components/ui/extensions'
|
||||
import { saved, options, storage } from '$lib/modules/extensions'
|
||||
import { saved, options } from '$lib/modules/extensions'
|
||||
import { dragScroll } from '$lib/modules/navigate'
|
||||
import { lookupPreferences, settings, SUPPORTS } from '$lib/modules/settings'
|
||||
|
||||
function checkExtensions (svd: Record<string, ExtensionConfig>, opts: Record<string, {
|
||||
|
|
@ -36,29 +37,24 @@
|
|||
pending: 'Waiting for at least one extension to be installed...'
|
||||
}]
|
||||
|
||||
async function importDefault () {
|
||||
try {
|
||||
await storage.import(DEFAULT_EXTENSIONS)
|
||||
} catch (err) {
|
||||
const error = err as Error
|
||||
toast.error(error.cause as string, { description: error.message })
|
||||
}
|
||||
}
|
||||
|
||||
if (!SUPPORTS.isAndroid && !Object.keys($saved).length) importDefault()
|
||||
onMount(() => {
|
||||
if (!SUPPORTS.isAndroid && !Object.keys($saved).length) importExtension(DEFAULT_EXTENSIONS)
|
||||
})
|
||||
|
||||
const hasForwarding = persisted('torrent-port-forwarding', false)
|
||||
$settings.lookupPreference = $hasForwarding ? 'quality' : 'seeders'
|
||||
|
||||
let importExtension: ((ext?: string) => void)
|
||||
</script>
|
||||
|
||||
<Progress step={2} />
|
||||
|
||||
<div class='space-y-3 lg:max-w-4xl h-full overflow-y-auto w-full py-8'>
|
||||
<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.'>
|
||||
<div class='space-y-3 lg:max-w-4xl h-full overflow-y-auto w-full py-8' use:dragScroll>
|
||||
<SettingCard class='bg-transparent' 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>
|
||||
<div class='px-6'>
|
||||
<Extensions />
|
||||
<Extensions bind:importExtension />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
</script>
|
||||
|
||||
<script lang='ts'>
|
||||
import CircleHelp from 'lucide-svelte/icons/circle-help'
|
||||
import { persisted } from 'svelte-persisted-store'
|
||||
|
||||
import Footer from '../Footer.svelte'
|
||||
|
|
@ -49,7 +50,7 @@
|
|||
const res = await native.checkIncomingConnections(port)
|
||||
$hasForwarding = res
|
||||
if (res) return { status: 'success', text: 'Port forwarding is available.' }
|
||||
return { status: 'error', text: 'Not available. Peer discovery will suffer. Streaming old, poorly seeded anime might be impossible.' }
|
||||
return { status: 'error', text: 'Not available. Peer discovery will suffer. Streaming old, poorly seeded anime might be impossible.', slot: 'port' }
|
||||
}
|
||||
|
||||
$: port = $settings.torrentPort
|
||||
|
|
@ -81,4 +82,8 @@
|
|||
</SettingCard>
|
||||
</div>
|
||||
|
||||
<Footer step={1} {checks} />
|
||||
<Footer step={1} {checks}>
|
||||
<div class='contents' on:click={() => native.openURL('https://thewiki.moe/getting-started/torrenting/#port-forwarding')}>
|
||||
<CircleHelp class='size-4 ml-2 shrink-0 inline cursor-pointer' />
|
||||
</div>
|
||||
</Footer>
|
||||
|
|
|
|||
BIN
static/bg_grid.jpg
Normal file
BIN
static/bg_grid.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 261 KiB |
Loading…
Reference in a new issue