Merge pull request #302 from maxdorninger/improve-frontend-code-quality

Improve frontend code quality
This commit is contained in:
Maximilian Dorninger
2025-12-21 19:11:02 +01:00
committed by GitHub
34 changed files with 155 additions and 148 deletions

View File

@@ -2,7 +2,7 @@
import { Button } from '$lib/components/ui/button/index.js';
import * as Card from '$lib/components/ui/card/index.js';
import { ImageOff, LoaderCircle } from 'lucide-svelte';
import { goto, invalidateAll } from '$app/navigation';
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import type { components } from '$lib/api/api';
import client from '$lib/api';
@@ -13,7 +13,6 @@
result,
isShow = true
}: { result: components['schemas']['MetaDataProviderSearchResult']; isShow: boolean } = $props();
console.log('Add Show Card Result: ', result);
async function addMedia() {
loading = true;
@@ -41,11 +40,14 @@
}
if (isShow) {
await goto(resolve('/dashboard/tv/[showId]', { showId: data?.id ?? '' }));
await goto(resolve('/dashboard/tv/[showId]', { showId: data?.id ?? '' }), {
invalidateAll: true
});
} else {
await goto(resolve('/dashboard/movies/[movieId]', { movieId: data?.id ?? '' }));
await goto(resolve('/dashboard/movies/[movieId]', { movieId: data?.id ?? '' }), {
invalidateAll: true
});
}
await invalidateAll();
loading = false;
}
</script>

View File

@@ -26,15 +26,14 @@
toast.error('Movie ID is missing');
return;
}
const { response } = await client.DELETE('/api/v1/movies/{movie_id}', {
const { error } = await client.DELETE('/api/v1/movies/{movie_id}', {
params: {
path: { movie_id: media.id },
query: { delete_files_on_disk: deleteFilesOnDisk, delete_torrents: deleteTorrents }
}
});
if (!response.ok) {
const errorText = await response.text();
toast.error('Failed to delete movie: ' + errorText);
if (error) {
toast.error('Failed to delete movie: ' + error.detail);
} else {
toast.success('Movie deleted successfully.');
deleteDialogOpen = false;
@@ -43,15 +42,14 @@
}
async function delete_show() {
const { response } = await client.DELETE('/api/v1/tv/shows/{show_id}', {
const { error } = await client.DELETE('/api/v1/tv/shows/{show_id}', {
params: {
path: { show_id: media.id! },
query: { delete_files_on_disk: deleteFilesOnDisk, delete_torrents: deleteTorrents }
}
});
if (!response.ok) {
const errorText = await response.text();
toast.error('Failed to delete show: ' + errorText);
if (error) {
toast.error('Failed to delete show: ' + error.detail);
} else {
toast.success('Show deleted successfully.');
deleteDialogOpen = false;

View File

@@ -11,10 +11,11 @@
import * as Table from '$lib/components/ui/table/index.js';
import client from '$lib/api';
import SelectFilePathSuffixDialog from '$lib/components/select-file-path-suffix-dialog.svelte';
import { invalidateAll } from '$app/navigation';
let { movie } = $props();
let dialogueState = $state(false);
let torrentsPromise: any = $state();
let torrentsPromise: any = $state(null);
let tabState: string = $state('basic');
let isLoading: boolean = $state(false);
let advancedMode: boolean = $derived(tabState === 'advanced');
@@ -40,19 +41,16 @@
console.warn(errorMessage);
torrentsError = errorMessage;
if (dialogueState) toast.info(errorMessage);
return [];
} else if (!response.ok) {
const errorMessage = `Failed to download torrent for movie ${movie.id}: ${response.statusText}`;
console.error(errorMessage);
torrentsError = errorMessage;
toast.error(errorMessage);
return false;
} else {
console.log('Downloading torrent:', data);
toast.success('Torrent download started successfully!');
return true;
}
await invalidateAll();
}
async function search() {
@@ -75,7 +73,7 @@
}
</script>
<Dialog.Root bind:open={dialogueState} onOpenChange={() => (dialogueState ? search() : null)}>
<Dialog.Root bind:open={dialogueState}>
<Dialog.Trigger class={buttonVariants({ variant: 'default' })}>Download Movie</Dialog.Trigger>
<Dialog.Content class="max-h-[90vh] w-fit min-w-[80vw] overflow-y-auto">
<Dialog.Header>
@@ -170,9 +168,17 @@
</Table.Cell>
</Table.Row>
{:else}
<Table.Cell colspan={7}>
<div class="font-light text-center w-full">No torrents found.</div>
</Table.Cell>
{#if data === null}
<Table.Cell colspan={7}>
<div class="font-light text-center w-full">
Start searching by clicking the search button!
</div>
</Table.Cell>
{:else}
<Table.Cell colspan={7}>
<div class="font-light text-center w-full">No torrents found.</div>
</Table.Cell>
{/if}
{/each}
</Table.Body>
</Table.Root>

View File

@@ -12,6 +12,7 @@
import client from '$lib/api';
import type { components } from '$lib/api/api';
import SelectFilePathSuffixDialog from '$lib/components/select-file-path-suffix-dialog.svelte';
import { invalidateAll } from '$app/navigation';
let { show }: { show: components['schemas']['Show'] } = $props();
let dialogueState = $state(false);
@@ -41,17 +42,15 @@
console.warn(errorMessage);
torrentsError = errorMessage;
if (dialogueState) toast.info(errorMessage);
return false;
} else if (!response.ok) {
const errorMessage = `Failed to download torrent for show ${show.id} and season ${selectedSeasonNumber}: ${response.statusText}`;
console.error(errorMessage);
torrentsError = errorMessage;
toast.error(errorMessage);
return false;
} else {
toast.success('Torrent download started successfully!');
return true;
}
await invalidateAll();
}
async function search() {
@@ -73,7 +72,7 @@
}
</script>
<Dialog.Root bind:open={dialogueState} onOpenChange={() => (dialogueState ? search() : null)}>
<Dialog.Root bind:open={dialogueState}>
<Dialog.Trigger class={buttonVariants({ variant: 'default' })}>Download Seasons</Dialog.Trigger>
<Dialog.Content class="max-h-[90vh] w-fit min-w-[80vw] overflow-y-auto">
<Dialog.Header>
@@ -202,9 +201,17 @@
</Table.Cell>
</Table.Row>
{:else}
<Table.Cell colspan={7}>
<div class="font-light text-center w-full">No torrents found.</div>
</Table.Cell>
{#if data === null}
<Table.Cell colspan={7}>
<div class="font-light text-center w-full">
Start searching by clicking the search button!
</div>
</Table.Cell>
{:else}
<Table.Cell colspan={7}>
<div class="font-light text-center w-full">No torrents found.</div>
</Table.Cell>
{/if}
{/each}
</Table.Body>
</Table.Root>

View File

@@ -14,11 +14,10 @@
torrent: components['schemas']['MovieTorrent'] | components['schemas']['RichSeasonTorrent'];
} = $props();
let dialogOpen = $state(false);
let importedState = $state(torrent.imported || false);
let importedState = $derived(torrent.imported);
async function closeDialog() {
dialogOpen = false;
importedState = torrent.imported || false;
}
async function saveTorrent() {
const { error } = await client.PATCH('/api/v1/torrent/{torrent_id}/status', {
@@ -32,8 +31,7 @@
}
});
if (error) {
console.error(`Failed to update torrent ${torrent.torrent_id} imported state: ${error}`);
toast.error(`Failed to update torrent: ${error}`);
toast.error('Failed to update torrent.');
return;
}
await invalidateAll();
@@ -53,10 +51,8 @@
</Dialog.Description>
</Dialog.Header>
<div class="flex gap-2">
<Label for="imported-state">Torrent {importedState ? 'is' : 'is not'} imported.</Label>
<Switch bind:checked={importedState} id="imported-state" />
<Label for="imported-state"
>Change Torrent import state to: {importedState ? 'is' : 'is not'} imported.</Label
>
</div>
<Dialog.Footer class="mt-8 flex justify-between gap-2">
<Button onclick={() => closeDialog()} variant="secondary">Cancel</Button>

View File

@@ -9,6 +9,7 @@
import { toast } from 'svelte-sonner';
import client from '$lib/api';
import type { components } from '$lib/api/api';
import { invalidateAll } from '$app/navigation';
let {
media,
@@ -70,6 +71,7 @@
toast.success(`Library updated to ${selectedLabel}`);
media.library = selectedLabel;
}
await invalidateAll();
}
function closeAndFocusTrigger() {

View File

@@ -5,7 +5,6 @@
import { Label } from '$lib/components/ui/label/index.js';
import { goto } from '$app/navigation';
import { toast } from 'svelte-sonner';
import { base } from '$app/paths';
import * as Alert from '$lib/components/ui/alert/index.js';
import AlertCircleIcon from '@lucide/svelte/icons/alert-circle';
import LoadingBar from '$lib/components/loading-bar.svelte';
@@ -49,7 +48,7 @@
console.log('Received User Data: ', response);
successMessage = 'Login successful! Redirecting...';
toast.success(successMessage);
goto(resolve('/dashboard', {}));
await goto(resolve('/dashboard', {}));
} else {
toast.error('Login failed!');
errorMessage = `Login failed! Please check your credentials and try again.`;
@@ -127,7 +126,9 @@
>
{/each}
<div class="mt-4 text-center text-sm">
<Button href="{base}/login/signup/" variant="link">Don't have an account? Sign up</Button>
<Button href={resolve('/login/signup/', {})} variant="link"
>Don't have an account? Sign up</Button
>
</div>
</Card.Content>
</Card.Root>

View File

@@ -4,7 +4,6 @@
const apiUrl = env.PUBLIC_API_URL;
let { media } = $props();
console.log('got media: ', media);
</script>
<picture>

View File

@@ -3,8 +3,8 @@
import { Skeleton } from '$lib/components/ui/skeleton';
import { Button } from '$lib/components/ui/button';
import { ChevronRight } from 'lucide-svelte';
import { base } from '$app/paths';
import type { components } from '$lib/api/api';
import { resolve } from '$app/paths';
let {
media,
@@ -31,12 +31,16 @@
{/each}
{/if}
{#if isShow}
<Button class="md:col-start-2" variant="secondary" href="{base}/dashboard/tv/add-show">
<Button class="md:col-start-2" variant="secondary" href={resolve('/dashboard/tv/add-show', {})}>
More recommendations
<ChevronRight />
</Button>
{:else}
<Button class="md:col-start-2" variant="secondary" href="{base}/dashboard/movies/add-movie">
<Button
class="md:col-start-2"
variant="secondary"
href={resolve('/dashboard/movies/add-movie', {})}
>
More recommendations
<ChevronRight />
</Button>

View File

@@ -8,6 +8,7 @@
import { toast } from 'svelte-sonner';
import client from '$lib/api';
import type { components } from '$lib/api/api';
import { invalidateAll } from '$app/navigation';
let { movie }: { movie: components['schemas']['PublicMovie'] } = $props();
let dialogOpen = $state(false);
@@ -44,6 +45,7 @@
} else {
toast.error('Failed to submit request');
}
await invalidateAll();
}
</script>

View File

@@ -7,7 +7,7 @@
import { getContext } from 'svelte';
import { Button } from '$lib/components/ui/button/index.js';
import { toast } from 'svelte-sonner';
import { goto } from '$app/navigation';
import { goto, invalidateAll } from '$app/navigation';
import { resolve } from '$app/paths';
import client from '$lib/api';
@@ -56,12 +56,6 @@
response = data.response;
}
if (response.ok) {
const requestIndex = requests.findIndex((r) => r.id === requestId);
if (requestIndex !== -1) {
let newAuthorizedStatus = !currentAuthorizedStatus;
requests[requestIndex]!.authorized = newAuthorizedStatus;
requests[requestIndex]!.authorized_by = newAuthorizedStatus ? user() : undefined;
}
toast.success(
`Request ${!currentAuthorizedStatus ? 'approved' : 'unapproved'} successfully.`
);
@@ -70,6 +64,7 @@
console.error(`Failed to update request status ${response.statusText}`, errorText);
toast.error(`Failed to update request status: ${response.statusText}`);
}
await invalidateAll();
}
async function deleteRequest(requestId: string) {
@@ -101,16 +96,12 @@
response = data.response;
}
if (response.ok) {
// remove the request from the list
const index = requests.findIndex((r) => r.id === requestId);
if (index > -1) {
requests.splice(index, 1);
}
toast.success('Request deleted successfully');
} else {
console.error(`Failed to delete request ${response.statusText}`, await response.text());
toast.error('Failed to delete request');
}
await invalidateAll();
}
</script>

View File

@@ -8,9 +8,9 @@
import AlertCircleIcon from '@lucide/svelte/icons/alert-circle';
import LoadingBar from '$lib/components/loading-bar.svelte';
import CheckCircle2Icon from '@lucide/svelte/icons/check-circle-2';
import { base } from '$app/paths';
import { handleOauth } from '$lib/utils.ts';
import client from '$lib/api';
import { resolve } from '$app/paths';
let email = $state('');
let password = $state('');
@@ -125,7 +125,7 @@
>
{/each}
<div class="mt-4 text-center text-sm">
<Button href="{base}/login/" variant="link">Already have an account? Login</Button>
<Button href={resolve('/login/', {})} variant="link">Already have an account? Login</Button>
</div>
</Card.Content>
</Card.Root>

View File

@@ -13,6 +13,7 @@
import { toast } from 'svelte-sonner';
import DeleteTorrentDialog from '$lib/components/delete-torrent-dialog.svelte';
import EditTorrentDialog from '$lib/components/edit-torrent-dialog.svelte';
import { invalidateAll } from '$app/navigation';
let {
torrents,
isShow = true
@@ -42,6 +43,7 @@
console.log(`Successfully retried download for torrent ${torrent.torrent_title}`);
toast.success('Trying to download torrent...');
}
await invalidateAll();
}
</script>

View File

@@ -47,8 +47,8 @@
selectedUser = null;
newPassword = '';
newEmail = '';
await invalidateAll();
}
await invalidateAll();
}
async function deleteUser() {
@@ -68,8 +68,8 @@
toast.success(`User ${userToDelete.email} deleted successfully.`);
deleteDialogOpen = false;
userToDelete = null;
await invalidateAll();
}
await invalidateAll();
}
</script>

View File

@@ -5,6 +5,7 @@
import { Label } from '$lib/components/ui/label/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import client from '$lib/api';
import { invalidateAll } from '$app/navigation';
let newPassword: string = $state('');
let newEmail: string = $state('');
@@ -25,6 +26,7 @@
}
newPassword = '';
newEmail = '';
await invalidateAll();
}
</script>

View File

@@ -2,7 +2,7 @@
import { Separator } from '$lib/components/ui/separator/index.js';
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
import { base } from '$app/paths';
import { resolve } from '$app/paths';
import logo from '$lib/images/logo.svg';
import { PUBLIC_VERSION } from '$env/static/public';
</script>
@@ -22,7 +22,11 @@
<Breadcrumb.Root>
<Breadcrumb.List>
<Breadcrumb.Item class="hidden md:block">
<Breadcrumb.Link href="{base}/dashboard">MediaManager</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>MediaManager</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>

View File

@@ -12,11 +12,11 @@
import DownloadMovieDialog from '$lib/components/download-movie-dialog.svelte';
import RequestMovieDialog from '$lib/components/request-movie-dialog.svelte';
import LibraryCombobox from '$lib/components/library-combobox.svelte';
import { base } from '$app/paths';
import { resolve } from '$app/paths';
import * as Card from '$lib/components/ui/card/index.js';
import DeleteMediaDialog from '$lib/components/delete-media-dialog.svelte';
let movie: components['schemas']['PublicMovie'] = page.data.movie;
let movie: components['schemas']['PublicMovie'] = $derived(page.data.movie);
let user: () => components['schemas']['UserRead'] = getContext('user');
</script>
@@ -37,15 +37,15 @@
<Breadcrumb.Root>
<Breadcrumb.List>
<Breadcrumb.Item class="hidden md:block">
<Breadcrumb.Link href="{base}/dashboard">MediaManager</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>MediaManager</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href="{base}/dashboard">Home</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href="{base}/dashboard/movies">Movies</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard/movies', {})}>Movies</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>

View File

@@ -10,7 +10,7 @@
import * as RadioGroup from '$lib/components/ui/radio-group/index.js';
import AddMediaCard from '$lib/components/add-media-card.svelte';
import { onMount } from 'svelte';
import { base } from '$app/paths';
import { resolve } from '$app/paths';
import client from '$lib/api';
import type { components } from '$lib/api/api';
import { handleQueryNotificationToast } from '$lib/utils.ts';
@@ -62,15 +62,15 @@
<Breadcrumb.Root>
<Breadcrumb.List>
<Breadcrumb.Item class="hidden md:block">
<Breadcrumb.Link href="{base}/dashboard">MediaManager</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>MediaManager</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href="{base}/dashboard">Home</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href="{base}/dashboard/movies">Movies</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard/movies', {})}>Movies</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>

View File

@@ -4,9 +4,9 @@
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
import RequestsTable from '$lib/components/season-requests-table.svelte';
import { base } from '$app/paths';
import { resolve } from '$app/paths';
let requests = page.data.requestsData;
let requests = $derived(page.data.requestsData);
</script>
<svelte:head>
@@ -21,15 +21,15 @@
<Breadcrumb.Root>
<Breadcrumb.List>
<Breadcrumb.Item class="hidden md:block">
<Breadcrumb.Link href="{base}/dashboard">MediaManager</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>MediaManager</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href="{base}/dashboard">Home</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href="{base}/dashboard/movies">Movies</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard/movies', {})}>Movies</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>

View File

@@ -6,7 +6,7 @@
import * as Accordion from '$lib/components/ui/accordion/index.js';
import * as Card from '$lib/components/ui/card/index.js';
import TorrentTable from '$lib/components/torrent-table.svelte';
import { base } from '$app/paths';
import { resolve } from '$app/paths';
import { page } from '$app/state';
</script>
@@ -22,15 +22,15 @@
<Breadcrumb.Root>
<Breadcrumb.List>
<Breadcrumb.Item class="hidden md:block">
<Breadcrumb.Link href="{base}/dashboard">MediaManager</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>MediaManager</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href="{base}/dashboard">Home</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href="{base}/dashboard/movies">Movies</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard/movies', {})}>Movies</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>

View File

@@ -5,9 +5,9 @@
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
import { base } from '$app/paths';
import client from '$lib/api';
import type { components } from '$lib/api/api';
import { resolve } from '$app/paths';
let unreadNotifications: components['schemas']['Notification'][] = [];
let readNotifications: components['schemas']['Notification'][] = [];
@@ -99,11 +99,11 @@
<Breadcrumb.Root>
<Breadcrumb.List>
<Breadcrumb.Item class="hidden md:block">
<Breadcrumb.Link href="{base}/dashboard">MediaManager</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>MediaManager</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href="{base}/dashboard">Home</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>

View File

@@ -7,7 +7,7 @@
import { Separator } from '$lib/components/ui/separator';
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
import { base } from '$app/paths';
import { resolve } from '$app/paths';
import type { components } from '$lib/api/api';
let currentUser: () => components['schemas']['UserRead'] = getContext('user');
@@ -16,7 +16,6 @@
(user: components['schemas']['UserRead']) => user.id !== currentUser().id
)
);
console.log('Current user:', currentUser());
</script>
<svelte:head>
@@ -31,7 +30,7 @@
<Breadcrumb.Root>
<Breadcrumb.List>
<Breadcrumb.Item class="hidden md:block">
<Breadcrumb.Link href="{base}/dashboard">MediaManager</Breadcrumb.Link>
<Breadcrumb.Link href={resolve('/dashboard', {})}>MediaManager</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>

View File

@@ -72,7 +72,7 @@
class="grid w-full auto-rows-min gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5"
>
{#each tvShows as show (show.id)}
<a href={resolve('/dashboard/tv/[showId]', { showId: show.id })}>
<a href={resolve('/dashboard/tv/[showId]', { showId: show.id! })}>
<Card.Root class="col-span-full max-w-[90vw] ">
<Card.Header>
<Card.Title class="h-6 truncate">{getFullyQualifiedMediaName(show)}</Card.Title>

View File

@@ -1,11 +1,9 @@
<script lang="ts">
import { setContext } from 'svelte';
import type { LayoutProps } from './$types';
let { data, children }: LayoutProps = $props();
const showData = $derived(data.showData);
setContext('show', () => showData);
const fetchError = $derived((data as { error?: string }).error || null);
</script>

View File

@@ -2,17 +2,17 @@ import type { LayoutLoad } from './$types';
import client from '$lib/api';
export const load: LayoutLoad = async ({ params, fetch }) => {
const show = await client.GET('/api/v1/tv/shows/{show_id}', {
const show = client.GET('/api/v1/tv/shows/{show_id}', {
fetch: fetch,
params: { path: { show_id: params.showId } }
});
const torrents = await client.GET('/api/v1/tv/shows/{show_id}/torrents', {
const torrents = client.GET('/api/v1/tv/shows/{show_id}/torrents', {
fetch: fetch,
params: { path: { show_id: params.showId } }
});
return {
showData: show.data,
torrentsData: torrents.data
showData: await show.then((x) => x.data),
torrentsData: await torrents.then((x) => x.data)
};
};

View File

@@ -23,22 +23,22 @@
import { resolve } from '$app/paths';
import client from '$lib/api';
let show: () => components['schemas']['PublicShow'] = getContext('show');
let show: components['schemas']['PublicShow'] = $derived(page.data.showData);
let torrents: components['schemas']['RichShowTorrent'] = $derived(page.data.torrentsData);
let user: () => components['schemas']['UserRead'] = getContext('user');
let torrents: components['schemas']['RichShowTorrent'] = page.data.torrentsData;
let continuousDownloadEnabled = $state(show().continuous_download);
let continuousDownloadEnabled = $derived(show.continuous_download);
async function toggle_continuous_download() {
const { response } = await client.POST('/api/v1/tv/shows/{show_id}/continuousDownload', {
params: {
path: { show_id: show().id },
path: { show_id: show.id },
query: { continuous_download: !continuousDownloadEnabled }
}
});
console.log(
'Toggling continuous download for show',
show().name,
show.name,
'to',
!continuousDownloadEnabled
);
@@ -53,10 +53,10 @@
</script>
<svelte:head>
<title>{getFullyQualifiedMediaName(show())} - MediaManager</title>
<title>{getFullyQualifiedMediaName(show)} - MediaManager</title>
<meta
content="View details and manage downloads for {getFullyQualifiedMediaName(
show()
show
)} in MediaManager"
name="description"
/>
@@ -81,20 +81,20 @@
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Page>{getFullyQualifiedMediaName(show())}</Breadcrumb.Page>
<Breadcrumb.Page>{getFullyQualifiedMediaName(show)}</Breadcrumb.Page>
</Breadcrumb.Item>
</Breadcrumb.List>
</Breadcrumb.Root>
</div>
</header>
<h1 class="scroll-m-20 text-center text-4xl font-extrabold tracking-tight lg:text-5xl">
{getFullyQualifiedMediaName(show())}
{getFullyQualifiedMediaName(show)}
</h1>
<main class="mx-auto flex w-full flex-1 flex-col gap-4 p-4 md:max-w-[80em]">
<div class="flex flex-col gap-4 md:flex-row md:items-stretch">
<div class="w-full overflow-hidden rounded-xl bg-muted/50 md:w-1/3 md:max-w-sm">
{#if show().id}
<MediaPicture media={show()} />
{#if show.id}
<MediaPicture media={show} />
{:else}
<div
class="flex aspect-9/16 h-auto w-full items-center justify-center rounded-lg bg-gray-200 text-gray-500"
@@ -110,7 +110,7 @@
</Card.Header>
<Card.Content>
<p class="leading-7 not-first:mt-6">
{show().overview}
{show.overview}
</p>
</Card.Content>
</Card.Root>
@@ -124,7 +124,7 @@
<Card.Title>Administrator Controls</Card.Title>
</Card.Header>
<Card.Content class="flex flex-col items-center gap-4">
{#if !show().ended}
{#if !show.ended}
<div class="flex items-center gap-3">
<Switch
bind:checked={() => continuousDownloadEnabled, toggle_continuous_download}
@@ -135,8 +135,8 @@
</Label>
</div>
{/if}
<LibraryCombobox media={show()} mediaType="tv" />
<DeleteMediaDialog isShow={true} media={show()} />
<LibraryCombobox media={show} mediaType="tv" />
<DeleteMediaDialog isShow={true} media={show} />
</Card.Content>
</Card.Root>
{/if}
@@ -146,9 +146,9 @@
</Card.Header>
<Card.Content class="flex flex-col items-center gap-4">
{#if user().is_superuser}
<DownloadSeasonDialog show={show()} />
<DownloadSeasonDialog {show} />
{/if}
<RequestSeasonDialog show={show()} />
<RequestSeasonDialog {show} />
</Card.Content>
</Card.Root>
</div>
@@ -158,7 +158,7 @@
<Card.Header>
<Card.Title>Season Details</Card.Title>
<Card.Description>
A list of all seasons for {getFullyQualifiedMediaName(show())}.
A list of all seasons for {getFullyQualifiedMediaName(show)}.
</Card.Description>
</Card.Header>
<Card.Content class="w-full overflow-x-auto">
@@ -173,13 +173,13 @@
</Table.Row>
</Table.Header>
<Table.Body>
{#if show().seasons.length > 0}
{#each show().seasons as season (season.id)}
{#if show.seasons.length > 0}
{#each show.seasons as season (season.id)}
<Table.Row
onclick={() =>
goto(
resolve('/dashboard/tv/[showId]/[seasonId]', {
showId: show().id,
showId: show.id,
seasonId: season.id
})
)}

View File

@@ -4,7 +4,6 @@
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
import * as Table from '$lib/components/ui/table/index.js';
import { getContext } from 'svelte';
import type { components } from '$lib/api/api';
import CheckmarkX from '$lib/components/checkmark-x.svelte';
import { getFullyQualifiedMediaName, getTorrentQualityString } from '$lib/utils';
@@ -12,18 +11,16 @@
import { resolve } from '$app/paths';
import * as Card from '$lib/components/ui/card/index.js';
let seasonFiles: components['schemas']['PublicSeasonFile'][] = $state(page.data.files);
let season: components['schemas']['Season'] = $state(page.data.season);
let show: () => components['schemas']['Show'] = getContext('show');
console.log('loaded files', seasonFiles);
let seasonFiles: components['schemas']['PublicSeasonFile'][] = $derived(page.data.files);
let season: components['schemas']['Season'] = $derived(page.data.season);
let show: components['schemas']['Show'] = $derived(page.data.showData);
</script>
<svelte:head>
<title>{getFullyQualifiedMediaName(show())} - Season {season.number} - MediaManager</title>
<title>{getFullyQualifiedMediaName(show)} - Season {season.number} - MediaManager</title>
<meta
content="View episodes and manage downloads for {getFullyQualifiedMediaName(
show()
show
)} Season {season.number} in MediaManager"
name="description"
/>
@@ -48,9 +45,9 @@
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
<Breadcrumb.Item>
<Breadcrumb.Link href={resolve('/dashboard/tv/[showId]', { showId: show().id! })}>
{show().name}
{show().year == null ? '' : '(' + show().year + ')'}
<Breadcrumb.Link href={resolve('/dashboard/tv/[showId]', { showId: show.id! })}>
{show.name}
{show.year == null ? '' : '(' + show.year + ')'}
</Breadcrumb.Link>
</Breadcrumb.Item>
<Breadcrumb.Separator class="hidden md:block" />
@@ -62,12 +59,12 @@
</div>
</header>
<h1 class="scroll-m-20 text-center text-4xl font-extrabold tracking-tight lg:text-5xl">
{getFullyQualifiedMediaName(show())} Season {season.number}
{getFullyQualifiedMediaName(show)} Season {season.number}
</h1>
<main class="mx-auto flex w-full flex-1 flex-col gap-4 p-4 md:max-w-[80em]">
<div class="flex flex-col gap-4 md:flex-row md:items-stretch">
<div class="w-full overflow-hidden rounded-xl bg-muted/50 md:w-1/3 md:max-w-sm">
<MediaPicture media={show()} />
<MediaPicture media={show} />
</div>
<div class="h-full w-full flex-auto rounded-xl md:w-1/4">
<Card.Root class="h-full w-full">
@@ -76,7 +73,7 @@
</Card.Header>
<Card.Content>
<p class="leading-7 not-first:mt-6">
{show().overview}
{show.overview}
</p>
</Card.Content>
</Card.Root>
@@ -130,7 +127,7 @@
<Card.Header>
<Card.Title>Episodes</Card.Title>
<Card.Description
>A list of all episodes for {getFullyQualifiedMediaName(show())} Season {season.number}
>A list of all episodes for {getFullyQualifiedMediaName(show)} Season {season.number}
.
</Card.Description>
</Card.Header>

View File

@@ -2,7 +2,7 @@ import type { PageLoad } from './$types';
import client from '$lib/api';
export const load: PageLoad = async ({ fetch, params }) => {
const season = await client.GET('/api/v1/tv/seasons/{season_id}', {
const season = client.GET('/api/v1/tv/seasons/{season_id}', {
fetch: fetch,
params: {
path: {
@@ -10,7 +10,7 @@ export const load: PageLoad = async ({ fetch, params }) => {
}
}
});
const seasonFiles = await client.GET('/api/v1/tv/seasons/{season_id}/files', {
const seasonFiles = client.GET('/api/v1/tv/seasons/{season_id}/files', {
fetch: fetch,
params: {
path: {
@@ -19,7 +19,7 @@ export const load: PageLoad = async ({ fetch, params }) => {
}
});
return {
files: seasonFiles.data,
season: season.data
files: await seasonFiles.then((x) => x.data),
season: await season.then((x) => x.data)
};
};

View File

@@ -10,15 +10,15 @@
import * as RadioGroup from '$lib/components/ui/radio-group/index.js';
import AddMediaCard from '$lib/components/add-media-card.svelte';
import { onMount } from 'svelte';
import { resolve } from '$app/paths';
import client from '$lib/api';
import type { components } from '$lib/api/api';
import { handleQueryNotificationToast } from '$lib/utils.ts';
let searchTerm: string = $state('');
let metadataProvider: 'tmdb' | 'tvdb' = $state('tmdb');
let data: components['schemas']['MetaDataProviderSearchResult'][] | null = $state(null);
let isSearching: boolean = $state(false);
import { resolve } from '$app/paths';
import client from '$lib/api';
import type { components } from '$lib/api/api';
import { handleQueryNotificationToast } from '$lib/utils.ts';
onMount(() => {
search('');

View File

@@ -7,7 +7,7 @@
import { resolve } from '$app/paths';
import type { components } from '$lib/api/api';
let requests: components['schemas']['RichSeasonRequest'][] = $state(page.data.requestsData);
let requests: components['schemas']['RichSeasonRequest'][] = $derived(page.data.requestsData);
</script>
<svelte:head>

View File

@@ -1,7 +1,7 @@
import type { LayoutLoad } from './$types';
import type { PageLoad } from './$types';
import client from '$lib/api';
export const load: LayoutLoad = async ({ fetch }) => {
export const load: PageLoad = async ({ fetch }) => {
const { data } = await client.GET('/api/v1/tv/seasons/requests', { fetch: fetch });
return {
requestsData: data

View File

@@ -5,10 +5,7 @@
import background from '$lib/images/pawel-czerwinski-NTYYL9Eb9y8-unsplash.jpg?enhanced';
import { PUBLIC_VERSION } from '$env/static/public';
import { resolve } from '$app/paths';
import { page } from '$app/state';
import { setContext } from 'svelte';
setContext('oauthProviders', () => page.data.oauthProviders.oauth_providers);
let { children } = $props();
</script>

View File

@@ -1,8 +1,8 @@
<script lang="ts">
import LoginCard from '$lib/components/login-card.svelte';
import { getContext } from 'svelte';
import { page } from '$app/state';
let oauthProvider: () => string[] = getContext('oauthProviders');
let oauthProviders: string[] = $derived(page.data.oauthProviders);
</script>
<svelte:head>
@@ -14,5 +14,5 @@
</svelte:head>
<main>
<LoginCard oauthProviderNames={oauthProvider()} />
<LoginCard oauthProviderNames={oauthProviders} />
</main>

View File

@@ -1,8 +1,8 @@
<script lang="ts">
import SignupCard from '$lib/components/signup-card.svelte';
import { getContext } from 'svelte';
import { page } from '$app/state';
let oauthProvider: () => string[] = getContext('oauthProviders');
let oauthProviders: string[] = $derived(page.data.oauthProviders);
</script>
<svelte:head>
@@ -10,4 +10,4 @@
<meta content="Signup - MediaManager" name="description" />
</svelte:head>
<SignupCard oauthProviderNames={oauthProvider()} />
<SignupCard oauthProviderNames={oauthProviders} />