fix offline mode and layout bugs
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 4m32s
Traceability Validation / Check Requirement Traces (push) Successful in 22s
Build & Release / Run Tests (push) Successful in 5m21s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 4m25s
Build & Release / Build Linux (push) Successful in 17m31s
Build & Release / Build Android (push) Successful in 22m5s
Build & Release / Create Release (push) Successful in 16s
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 4m32s
Traceability Validation / Check Requirement Traces (push) Successful in 22s
Build & Release / Run Tests (push) Successful in 5m21s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 4m25s
Build & Release / Build Linux (push) Successful in 17m31s
Build & Release / Build Android (push) Successful in 22m5s
Build & Release / Create Release (push) Successful in 16s
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { get } from "svelte/store";
|
||||
import { page } from "$app/stores";
|
||||
import { goto } from "$app/navigation";
|
||||
import { platform } from "@tauri-apps/plugin-os";
|
||||
@@ -7,8 +8,9 @@
|
||||
import { auth, needsReauth, isAuthenticated } from "$lib/stores/auth";
|
||||
import { connectivity, isConnected } from "$lib/stores/connectivity";
|
||||
import { initPlayerEvents, cleanupPlayerEvents } from "$lib/services/playerEvents";
|
||||
import { initDownloadEvents, cleanupDownloadEvents } from "$lib/stores/downloads";
|
||||
import { downloads, initDownloadEvents, cleanupDownloadEvents } from "$lib/stores/downloads";
|
||||
import { syncService } from "$lib/services/syncService";
|
||||
import { onReconnected as onCatalogReconnected, syncCatalog, refreshSyncStatus, showServerCatalog, lastCatalogSync } from "$lib/services/offlineCatalog";
|
||||
import { playbackMode } from "$lib/stores/playbackMode";
|
||||
import { sessions } from "$lib/stores/sessions";
|
||||
import { currentMedia, isPlaying, playbackPosition, playbackDuration } from "$lib/stores/player";
|
||||
@@ -43,6 +45,20 @@
|
||||
($isAndroid || !pathname.startsWith('/library'))
|
||||
);
|
||||
|
||||
// The library and settings routes own their own full-height layout (their own
|
||||
// scroll container + bottom-space reservation), so the root must leave their
|
||||
// wrapper as a plain non-scrolling box. Every other top-level page (search,
|
||||
// downloads, sessions, home) renders straight into the root, so the root
|
||||
// wrapper has to scroll AND reserve the fixed bottom UI's height — otherwise
|
||||
// the mini player / bottom nav overlay the last rows of content.
|
||||
const routeOwnsLayout = $derived(
|
||||
pathname.startsWith('/library') ||
|
||||
pathname.startsWith('/settings') ||
|
||||
pathname.startsWith('/player/') ||
|
||||
pathname.startsWith('/login')
|
||||
);
|
||||
const showBottomUi = $derived(showBottomNav || showGlobalMiniPlayer);
|
||||
|
||||
$effect(() => {
|
||||
const el = bottomUiEl;
|
||||
if (!el) {
|
||||
@@ -83,9 +99,26 @@
|
||||
// Initialize download event listener
|
||||
await initDownloadEvents();
|
||||
|
||||
// Prime the downloads store from the DB. The store starts empty each launch,
|
||||
// and download badges (e.g. AlbumDownloadButton) derive purely from it, so
|
||||
// without an initial refresh a previously-downloaded album shows as
|
||||
// not-downloaded until the user opens the Downloads page.
|
||||
const userId = get(auth).user?.id;
|
||||
if (userId) {
|
||||
downloads.refresh(userId).catch((err) =>
|
||||
console.error("Initial downloads refresh failed:", err)
|
||||
);
|
||||
}
|
||||
|
||||
// Start sync service for offline mutation queue
|
||||
syncService.start();
|
||||
|
||||
// Kick off a best-effort full-catalog pre-sync so the whole server catalog
|
||||
// is browsable (greyed out) offline, and load the last-sync hint for the
|
||||
// offline banner. Non-blocking — no-ops when not connected.
|
||||
void syncCatalog();
|
||||
void refreshSyncStatus();
|
||||
|
||||
// Initialize playback mode and session monitoring
|
||||
playbackMode.initializeSessionMonitoring();
|
||||
await playbackMode.refresh();
|
||||
@@ -114,6 +147,8 @@
|
||||
onServerReconnected: () => {
|
||||
// Retry session verification when server becomes reachable
|
||||
auth.retryVerification();
|
||||
// Resume offline-queued downloads and refresh the catalog.
|
||||
void onCatalogReconnected();
|
||||
},
|
||||
}).catch((monitorError) => {
|
||||
console.error("[Layout] Failed to start connectivity monitoring:", monitorError);
|
||||
@@ -153,13 +188,35 @@
|
||||
{$pendingSyncCount} pending sync{$pendingSyncCount !== 1 ? 's' : ''}
|
||||
</span>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => showServerCatalog.update((v) => !v)}
|
||||
class="ml-1 bg-white/20 hover:bg-white/30 px-2 py-0.5 rounded-full text-xs transition-colors"
|
||||
aria-pressed={$showServerCatalog}
|
||||
title={$lastCatalogSync ? `Catalog last synced ${new Date($lastCatalogSync).toLocaleString()}` : 'Catalog not yet synced'}
|
||||
>
|
||||
{$showServerCatalog ? 'Hide server media' : 'Show all server media'}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Main content -->
|
||||
<div class="flex-1 overflow-hidden">
|
||||
{@render children()}
|
||||
</div>
|
||||
<!-- Main content. Routes that own their full-height layout (library,
|
||||
settings, player, login) get a plain clipped box and manage their own
|
||||
scrolling internally. All other top-level pages render directly here, so
|
||||
this wrapper must scroll and reserve the fixed bottom UI's measured
|
||||
height so the mini player / bottom nav never overlap the last rows. -->
|
||||
{#if routeOwnsLayout}
|
||||
<div class="flex-1 overflow-hidden">
|
||||
{@render children()}
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="flex-1 overflow-y-auto min-h-0"
|
||||
style="padding-bottom: {showBottomUi ? `${$bottomUiHeight}px` : '0'}; overscroll-behavior: contain"
|
||||
>
|
||||
{@render children()}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Re-authentication modal -->
|
||||
<ReauthModal isOpen={$needsReauth} />
|
||||
|
||||
Reference in New Issue
Block a user