mirror of
https://github.com/maxdorninger/MediaManager.git
synced 2026-04-27 11:15:32 +02:00
Merge pull request #302 from maxdorninger/improve-frontend-code-quality
Improve frontend code quality
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
import { Button } from '$lib/components/ui/button/index.js';
|
import { Button } from '$lib/components/ui/button/index.js';
|
||||||
import * as Card from '$lib/components/ui/card/index.js';
|
import * as Card from '$lib/components/ui/card/index.js';
|
||||||
import { ImageOff, LoaderCircle } from 'lucide-svelte';
|
import { ImageOff, LoaderCircle } from 'lucide-svelte';
|
||||||
import { goto, invalidateAll } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import type { components } from '$lib/api/api';
|
import type { components } from '$lib/api/api';
|
||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
result,
|
result,
|
||||||
isShow = true
|
isShow = true
|
||||||
}: { result: components['schemas']['MetaDataProviderSearchResult']; isShow: boolean } = $props();
|
}: { result: components['schemas']['MetaDataProviderSearchResult']; isShow: boolean } = $props();
|
||||||
console.log('Add Show Card Result: ', result);
|
|
||||||
|
|
||||||
async function addMedia() {
|
async function addMedia() {
|
||||||
loading = true;
|
loading = true;
|
||||||
@@ -41,11 +40,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isShow) {
|
if (isShow) {
|
||||||
await goto(resolve('/dashboard/tv/[showId]', { showId: data?.id ?? '' }));
|
await goto(resolve('/dashboard/tv/[showId]', { showId: data?.id ?? '' }), {
|
||||||
|
invalidateAll: true
|
||||||
|
});
|
||||||
} else {
|
} 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;
|
loading = false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -26,15 +26,14 @@
|
|||||||
toast.error('Movie ID is missing');
|
toast.error('Movie ID is missing');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { response } = await client.DELETE('/api/v1/movies/{movie_id}', {
|
const { error } = await client.DELETE('/api/v1/movies/{movie_id}', {
|
||||||
params: {
|
params: {
|
||||||
path: { movie_id: media.id },
|
path: { movie_id: media.id },
|
||||||
query: { delete_files_on_disk: deleteFilesOnDisk, delete_torrents: deleteTorrents }
|
query: { delete_files_on_disk: deleteFilesOnDisk, delete_torrents: deleteTorrents }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (error) {
|
||||||
const errorText = await response.text();
|
toast.error('Failed to delete movie: ' + error.detail);
|
||||||
toast.error('Failed to delete movie: ' + errorText);
|
|
||||||
} else {
|
} else {
|
||||||
toast.success('Movie deleted successfully.');
|
toast.success('Movie deleted successfully.');
|
||||||
deleteDialogOpen = false;
|
deleteDialogOpen = false;
|
||||||
@@ -43,15 +42,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function delete_show() {
|
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: {
|
params: {
|
||||||
path: { show_id: media.id! },
|
path: { show_id: media.id! },
|
||||||
query: { delete_files_on_disk: deleteFilesOnDisk, delete_torrents: deleteTorrents }
|
query: { delete_files_on_disk: deleteFilesOnDisk, delete_torrents: deleteTorrents }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (error) {
|
||||||
const errorText = await response.text();
|
toast.error('Failed to delete show: ' + error.detail);
|
||||||
toast.error('Failed to delete show: ' + errorText);
|
|
||||||
} else {
|
} else {
|
||||||
toast.success('Show deleted successfully.');
|
toast.success('Show deleted successfully.');
|
||||||
deleteDialogOpen = false;
|
deleteDialogOpen = false;
|
||||||
|
|||||||
@@ -11,10 +11,11 @@
|
|||||||
import * as Table from '$lib/components/ui/table/index.js';
|
import * as Table from '$lib/components/ui/table/index.js';
|
||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
import SelectFilePathSuffixDialog from '$lib/components/select-file-path-suffix-dialog.svelte';
|
import SelectFilePathSuffixDialog from '$lib/components/select-file-path-suffix-dialog.svelte';
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
|
|
||||||
let { movie } = $props();
|
let { movie } = $props();
|
||||||
let dialogueState = $state(false);
|
let dialogueState = $state(false);
|
||||||
let torrentsPromise: any = $state();
|
let torrentsPromise: any = $state(null);
|
||||||
let tabState: string = $state('basic');
|
let tabState: string = $state('basic');
|
||||||
let isLoading: boolean = $state(false);
|
let isLoading: boolean = $state(false);
|
||||||
let advancedMode: boolean = $derived(tabState === 'advanced');
|
let advancedMode: boolean = $derived(tabState === 'advanced');
|
||||||
@@ -40,19 +41,16 @@
|
|||||||
console.warn(errorMessage);
|
console.warn(errorMessage);
|
||||||
torrentsError = errorMessage;
|
torrentsError = errorMessage;
|
||||||
if (dialogueState) toast.info(errorMessage);
|
if (dialogueState) toast.info(errorMessage);
|
||||||
return [];
|
|
||||||
} else if (!response.ok) {
|
} else if (!response.ok) {
|
||||||
const errorMessage = `Failed to download torrent for movie ${movie.id}: ${response.statusText}`;
|
const errorMessage = `Failed to download torrent for movie ${movie.id}: ${response.statusText}`;
|
||||||
console.error(errorMessage);
|
console.error(errorMessage);
|
||||||
torrentsError = errorMessage;
|
torrentsError = errorMessage;
|
||||||
toast.error(errorMessage);
|
toast.error(errorMessage);
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
console.log('Downloading torrent:', data);
|
console.log('Downloading torrent:', data);
|
||||||
toast.success('Torrent download started successfully!');
|
toast.success('Torrent download started successfully!');
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function search() {
|
async function search() {
|
||||||
@@ -75,7 +73,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</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.Trigger class={buttonVariants({ variant: 'default' })}>Download Movie</Dialog.Trigger>
|
||||||
<Dialog.Content class="max-h-[90vh] w-fit min-w-[80vw] overflow-y-auto">
|
<Dialog.Content class="max-h-[90vh] w-fit min-w-[80vw] overflow-y-auto">
|
||||||
<Dialog.Header>
|
<Dialog.Header>
|
||||||
@@ -170,9 +168,17 @@
|
|||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
{:else}
|
{:else}
|
||||||
<Table.Cell colspan={7}>
|
{#if data === null}
|
||||||
<div class="font-light text-center w-full">No torrents found.</div>
|
<Table.Cell colspan={7}>
|
||||||
</Table.Cell>
|
<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}
|
{/each}
|
||||||
</Table.Body>
|
</Table.Body>
|
||||||
</Table.Root>
|
</Table.Root>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
import type { components } from '$lib/api/api';
|
import type { components } from '$lib/api/api';
|
||||||
import SelectFilePathSuffixDialog from '$lib/components/select-file-path-suffix-dialog.svelte';
|
import SelectFilePathSuffixDialog from '$lib/components/select-file-path-suffix-dialog.svelte';
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
|
|
||||||
let { show }: { show: components['schemas']['Show'] } = $props();
|
let { show }: { show: components['schemas']['Show'] } = $props();
|
||||||
let dialogueState = $state(false);
|
let dialogueState = $state(false);
|
||||||
@@ -41,17 +42,15 @@
|
|||||||
console.warn(errorMessage);
|
console.warn(errorMessage);
|
||||||
torrentsError = errorMessage;
|
torrentsError = errorMessage;
|
||||||
if (dialogueState) toast.info(errorMessage);
|
if (dialogueState) toast.info(errorMessage);
|
||||||
return false;
|
|
||||||
} else if (!response.ok) {
|
} else if (!response.ok) {
|
||||||
const errorMessage = `Failed to download torrent for show ${show.id} and season ${selectedSeasonNumber}: ${response.statusText}`;
|
const errorMessage = `Failed to download torrent for show ${show.id} and season ${selectedSeasonNumber}: ${response.statusText}`;
|
||||||
console.error(errorMessage);
|
console.error(errorMessage);
|
||||||
torrentsError = errorMessage;
|
torrentsError = errorMessage;
|
||||||
toast.error(errorMessage);
|
toast.error(errorMessage);
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
toast.success('Torrent download started successfully!');
|
toast.success('Torrent download started successfully!');
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function search() {
|
async function search() {
|
||||||
@@ -73,7 +72,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</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.Trigger class={buttonVariants({ variant: 'default' })}>Download Seasons</Dialog.Trigger>
|
||||||
<Dialog.Content class="max-h-[90vh] w-fit min-w-[80vw] overflow-y-auto">
|
<Dialog.Content class="max-h-[90vh] w-fit min-w-[80vw] overflow-y-auto">
|
||||||
<Dialog.Header>
|
<Dialog.Header>
|
||||||
@@ -202,9 +201,17 @@
|
|||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
{:else}
|
{:else}
|
||||||
<Table.Cell colspan={7}>
|
{#if data === null}
|
||||||
<div class="font-light text-center w-full">No torrents found.</div>
|
<Table.Cell colspan={7}>
|
||||||
</Table.Cell>
|
<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}
|
{/each}
|
||||||
</Table.Body>
|
</Table.Body>
|
||||||
</Table.Root>
|
</Table.Root>
|
||||||
|
|||||||
@@ -14,11 +14,10 @@
|
|||||||
torrent: components['schemas']['MovieTorrent'] | components['schemas']['RichSeasonTorrent'];
|
torrent: components['schemas']['MovieTorrent'] | components['schemas']['RichSeasonTorrent'];
|
||||||
} = $props();
|
} = $props();
|
||||||
let dialogOpen = $state(false);
|
let dialogOpen = $state(false);
|
||||||
let importedState = $state(torrent.imported || false);
|
let importedState = $derived(torrent.imported);
|
||||||
|
|
||||||
async function closeDialog() {
|
async function closeDialog() {
|
||||||
dialogOpen = false;
|
dialogOpen = false;
|
||||||
importedState = torrent.imported || false;
|
|
||||||
}
|
}
|
||||||
async function saveTorrent() {
|
async function saveTorrent() {
|
||||||
const { error } = await client.PATCH('/api/v1/torrent/{torrent_id}/status', {
|
const { error } = await client.PATCH('/api/v1/torrent/{torrent_id}/status', {
|
||||||
@@ -32,8 +31,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(`Failed to update torrent ${torrent.torrent_id} imported state: ${error}`);
|
toast.error('Failed to update torrent.');
|
||||||
toast.error(`Failed to update torrent: ${error}`);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await invalidateAll();
|
await invalidateAll();
|
||||||
@@ -53,10 +51,8 @@
|
|||||||
</Dialog.Description>
|
</Dialog.Description>
|
||||||
</Dialog.Header>
|
</Dialog.Header>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
<Label for="imported-state">Torrent {importedState ? 'is' : 'is not'} imported.</Label>
|
||||||
<Switch bind:checked={importedState} id="imported-state" />
|
<Switch bind:checked={importedState} id="imported-state" />
|
||||||
<Label for="imported-state"
|
|
||||||
>Change Torrent import state to: {importedState ? 'is' : 'is not'} imported.</Label
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<Dialog.Footer class="mt-8 flex justify-between gap-2">
|
<Dialog.Footer class="mt-8 flex justify-between gap-2">
|
||||||
<Button onclick={() => closeDialog()} variant="secondary">Cancel</Button>
|
<Button onclick={() => closeDialog()} variant="secondary">Cancel</Button>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
import type { components } from '$lib/api/api';
|
import type { components } from '$lib/api/api';
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
media,
|
media,
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
toast.success(`Library updated to ${selectedLabel}`);
|
toast.success(`Library updated to ${selectedLabel}`);
|
||||||
media.library = selectedLabel;
|
media.library = selectedLabel;
|
||||||
}
|
}
|
||||||
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeAndFocusTrigger() {
|
function closeAndFocusTrigger() {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
import { Label } from '$lib/components/ui/label/index.js';
|
import { Label } from '$lib/components/ui/label/index.js';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { base } from '$app/paths';
|
|
||||||
import * as Alert from '$lib/components/ui/alert/index.js';
|
import * as Alert from '$lib/components/ui/alert/index.js';
|
||||||
import AlertCircleIcon from '@lucide/svelte/icons/alert-circle';
|
import AlertCircleIcon from '@lucide/svelte/icons/alert-circle';
|
||||||
import LoadingBar from '$lib/components/loading-bar.svelte';
|
import LoadingBar from '$lib/components/loading-bar.svelte';
|
||||||
@@ -49,7 +48,7 @@
|
|||||||
console.log('Received User Data: ', response);
|
console.log('Received User Data: ', response);
|
||||||
successMessage = 'Login successful! Redirecting...';
|
successMessage = 'Login successful! Redirecting...';
|
||||||
toast.success(successMessage);
|
toast.success(successMessage);
|
||||||
goto(resolve('/dashboard', {}));
|
await goto(resolve('/dashboard', {}));
|
||||||
} else {
|
} else {
|
||||||
toast.error('Login failed!');
|
toast.error('Login failed!');
|
||||||
errorMessage = `Login failed! Please check your credentials and try again.`;
|
errorMessage = `Login failed! Please check your credentials and try again.`;
|
||||||
@@ -127,7 +126,9 @@
|
|||||||
>
|
>
|
||||||
{/each}
|
{/each}
|
||||||
<div class="mt-4 text-center text-sm">
|
<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>
|
</div>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
const apiUrl = env.PUBLIC_API_URL;
|
const apiUrl = env.PUBLIC_API_URL;
|
||||||
let { media } = $props();
|
let { media } = $props();
|
||||||
console.log('got media: ', media);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<picture>
|
<picture>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import { Skeleton } from '$lib/components/ui/skeleton';
|
import { Skeleton } from '$lib/components/ui/skeleton';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import { ChevronRight } from 'lucide-svelte';
|
import { ChevronRight } from 'lucide-svelte';
|
||||||
import { base } from '$app/paths';
|
|
||||||
import type { components } from '$lib/api/api';
|
import type { components } from '$lib/api/api';
|
||||||
|
import { resolve } from '$app/paths';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
media,
|
media,
|
||||||
@@ -31,12 +31,16 @@
|
|||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
{#if isShow}
|
{#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
|
More recommendations
|
||||||
<ChevronRight />
|
<ChevronRight />
|
||||||
</Button>
|
</Button>
|
||||||
{:else}
|
{: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
|
More recommendations
|
||||||
<ChevronRight />
|
<ChevronRight />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
import type { components } from '$lib/api/api';
|
import type { components } from '$lib/api/api';
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
|
|
||||||
let { movie }: { movie: components['schemas']['PublicMovie'] } = $props();
|
let { movie }: { movie: components['schemas']['PublicMovie'] } = $props();
|
||||||
let dialogOpen = $state(false);
|
let dialogOpen = $state(false);
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
} else {
|
} else {
|
||||||
toast.error('Failed to submit request');
|
toast.error('Failed to submit request');
|
||||||
}
|
}
|
||||||
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { getContext } from 'svelte';
|
import { getContext } from 'svelte';
|
||||||
import { Button } from '$lib/components/ui/button/index.js';
|
import { Button } from '$lib/components/ui/button/index.js';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { goto } from '$app/navigation';
|
import { goto, invalidateAll } from '$app/navigation';
|
||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
|
|
||||||
@@ -56,12 +56,6 @@
|
|||||||
response = data.response;
|
response = data.response;
|
||||||
}
|
}
|
||||||
if (response.ok) {
|
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(
|
toast.success(
|
||||||
`Request ${!currentAuthorizedStatus ? 'approved' : 'unapproved'} successfully.`
|
`Request ${!currentAuthorizedStatus ? 'approved' : 'unapproved'} successfully.`
|
||||||
);
|
);
|
||||||
@@ -70,6 +64,7 @@
|
|||||||
console.error(`Failed to update request status ${response.statusText}`, errorText);
|
console.error(`Failed to update request status ${response.statusText}`, errorText);
|
||||||
toast.error(`Failed to update request status: ${response.statusText}`);
|
toast.error(`Failed to update request status: ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteRequest(requestId: string) {
|
async function deleteRequest(requestId: string) {
|
||||||
@@ -101,16 +96,12 @@
|
|||||||
response = data.response;
|
response = data.response;
|
||||||
}
|
}
|
||||||
if (response.ok) {
|
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');
|
toast.success('Request deleted successfully');
|
||||||
} else {
|
} else {
|
||||||
console.error(`Failed to delete request ${response.statusText}`, await response.text());
|
console.error(`Failed to delete request ${response.statusText}`, await response.text());
|
||||||
toast.error('Failed to delete request');
|
toast.error('Failed to delete request');
|
||||||
}
|
}
|
||||||
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
import AlertCircleIcon from '@lucide/svelte/icons/alert-circle';
|
import AlertCircleIcon from '@lucide/svelte/icons/alert-circle';
|
||||||
import LoadingBar from '$lib/components/loading-bar.svelte';
|
import LoadingBar from '$lib/components/loading-bar.svelte';
|
||||||
import CheckCircle2Icon from '@lucide/svelte/icons/check-circle-2';
|
import CheckCircle2Icon from '@lucide/svelte/icons/check-circle-2';
|
||||||
import { base } from '$app/paths';
|
|
||||||
import { handleOauth } from '$lib/utils.ts';
|
import { handleOauth } from '$lib/utils.ts';
|
||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
|
import { resolve } from '$app/paths';
|
||||||
|
|
||||||
let email = $state('');
|
let email = $state('');
|
||||||
let password = $state('');
|
let password = $state('');
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
>
|
>
|
||||||
{/each}
|
{/each}
|
||||||
<div class="mt-4 text-center text-sm">
|
<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>
|
</div>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import DeleteTorrentDialog from '$lib/components/delete-torrent-dialog.svelte';
|
import DeleteTorrentDialog from '$lib/components/delete-torrent-dialog.svelte';
|
||||||
import EditTorrentDialog from '$lib/components/edit-torrent-dialog.svelte';
|
import EditTorrentDialog from '$lib/components/edit-torrent-dialog.svelte';
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
let {
|
let {
|
||||||
torrents,
|
torrents,
|
||||||
isShow = true
|
isShow = true
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
console.log(`Successfully retried download for torrent ${torrent.torrent_title}`);
|
console.log(`Successfully retried download for torrent ${torrent.torrent_title}`);
|
||||||
toast.success('Trying to download torrent...');
|
toast.success('Trying to download torrent...');
|
||||||
}
|
}
|
||||||
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -47,8 +47,8 @@
|
|||||||
selectedUser = null;
|
selectedUser = null;
|
||||||
newPassword = '';
|
newPassword = '';
|
||||||
newEmail = '';
|
newEmail = '';
|
||||||
await invalidateAll();
|
|
||||||
}
|
}
|
||||||
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteUser() {
|
async function deleteUser() {
|
||||||
@@ -68,8 +68,8 @@
|
|||||||
toast.success(`User ${userToDelete.email} deleted successfully.`);
|
toast.success(`User ${userToDelete.email} deleted successfully.`);
|
||||||
deleteDialogOpen = false;
|
deleteDialogOpen = false;
|
||||||
userToDelete = null;
|
userToDelete = null;
|
||||||
await invalidateAll();
|
|
||||||
}
|
}
|
||||||
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import { Label } from '$lib/components/ui/label/index.js';
|
import { Label } from '$lib/components/ui/label/index.js';
|
||||||
import { Input } from '$lib/components/ui/input/index.js';
|
import { Input } from '$lib/components/ui/input/index.js';
|
||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
|
|
||||||
let newPassword: string = $state('');
|
let newPassword: string = $state('');
|
||||||
let newEmail: string = $state('');
|
let newEmail: string = $state('');
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
}
|
}
|
||||||
newPassword = '';
|
newPassword = '';
|
||||||
newEmail = '';
|
newEmail = '';
|
||||||
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { Separator } from '$lib/components/ui/separator/index.js';
|
import { Separator } from '$lib/components/ui/separator/index.js';
|
||||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||||
import * as Breadcrumb from '$lib/components/ui/breadcrumb/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 logo from '$lib/images/logo.svg';
|
||||||
import { PUBLIC_VERSION } from '$env/static/public';
|
import { PUBLIC_VERSION } from '$env/static/public';
|
||||||
</script>
|
</script>
|
||||||
@@ -22,7 +22,11 @@
|
|||||||
<Breadcrumb.Root>
|
<Breadcrumb.Root>
|
||||||
<Breadcrumb.List>
|
<Breadcrumb.List>
|
||||||
<Breadcrumb.Item class="hidden md:block">
|
<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.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
|
|||||||
@@ -12,11 +12,11 @@
|
|||||||
import DownloadMovieDialog from '$lib/components/download-movie-dialog.svelte';
|
import DownloadMovieDialog from '$lib/components/download-movie-dialog.svelte';
|
||||||
import RequestMovieDialog from '$lib/components/request-movie-dialog.svelte';
|
import RequestMovieDialog from '$lib/components/request-movie-dialog.svelte';
|
||||||
import LibraryCombobox from '$lib/components/library-combobox.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 * as Card from '$lib/components/ui/card/index.js';
|
||||||
import DeleteMediaDialog from '$lib/components/delete-media-dialog.svelte';
|
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');
|
let user: () => components['schemas']['UserRead'] = getContext('user');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -37,15 +37,15 @@
|
|||||||
<Breadcrumb.Root>
|
<Breadcrumb.Root>
|
||||||
<Breadcrumb.List>
|
<Breadcrumb.List>
|
||||||
<Breadcrumb.Item class="hidden md:block">
|
<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.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Link href="{base}/dashboard">Home</Breadcrumb.Link>
|
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Link href="{base}/dashboard/movies">Movies</Breadcrumb.Link>
|
<Breadcrumb.Link href={resolve('/dashboard/movies', {})}>Movies</Breadcrumb.Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import * as RadioGroup from '$lib/components/ui/radio-group/index.js';
|
import * as RadioGroup from '$lib/components/ui/radio-group/index.js';
|
||||||
import AddMediaCard from '$lib/components/add-media-card.svelte';
|
import AddMediaCard from '$lib/components/add-media-card.svelte';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { base } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
import type { components } from '$lib/api/api';
|
import type { components } from '$lib/api/api';
|
||||||
import { handleQueryNotificationToast } from '$lib/utils.ts';
|
import { handleQueryNotificationToast } from '$lib/utils.ts';
|
||||||
@@ -62,15 +62,15 @@
|
|||||||
<Breadcrumb.Root>
|
<Breadcrumb.Root>
|
||||||
<Breadcrumb.List>
|
<Breadcrumb.List>
|
||||||
<Breadcrumb.Item class="hidden md:block">
|
<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.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Link href="{base}/dashboard">Home</Breadcrumb.Link>
|
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Link href="{base}/dashboard/movies">Movies</Breadcrumb.Link>
|
<Breadcrumb.Link href={resolve('/dashboard/movies', {})}>Movies</Breadcrumb.Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||||
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
||||||
import RequestsTable from '$lib/components/season-requests-table.svelte';
|
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>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -21,15 +21,15 @@
|
|||||||
<Breadcrumb.Root>
|
<Breadcrumb.Root>
|
||||||
<Breadcrumb.List>
|
<Breadcrumb.List>
|
||||||
<Breadcrumb.Item class="hidden md:block">
|
<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.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Link href="{base}/dashboard">Home</Breadcrumb.Link>
|
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Link href="{base}/dashboard/movies">Movies</Breadcrumb.Link>
|
<Breadcrumb.Link href={resolve('/dashboard/movies', {})}>Movies</Breadcrumb.Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import * as Accordion from '$lib/components/ui/accordion/index.js';
|
import * as Accordion from '$lib/components/ui/accordion/index.js';
|
||||||
import * as Card from '$lib/components/ui/card/index.js';
|
import * as Card from '$lib/components/ui/card/index.js';
|
||||||
import TorrentTable from '$lib/components/torrent-table.svelte';
|
import TorrentTable from '$lib/components/torrent-table.svelte';
|
||||||
import { base } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -22,15 +22,15 @@
|
|||||||
<Breadcrumb.Root>
|
<Breadcrumb.Root>
|
||||||
<Breadcrumb.List>
|
<Breadcrumb.List>
|
||||||
<Breadcrumb.Item class="hidden md:block">
|
<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.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Link href="{base}/dashboard">Home</Breadcrumb.Link>
|
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Link href="{base}/dashboard/movies">Movies</Breadcrumb.Link>
|
<Breadcrumb.Link href={resolve('/dashboard/movies', {})}>Movies</Breadcrumb.Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||||
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
||||||
|
|
||||||
import { base } from '$app/paths';
|
|
||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
import type { components } from '$lib/api/api';
|
import type { components } from '$lib/api/api';
|
||||||
|
import { resolve } from '$app/paths';
|
||||||
|
|
||||||
let unreadNotifications: components['schemas']['Notification'][] = [];
|
let unreadNotifications: components['schemas']['Notification'][] = [];
|
||||||
let readNotifications: components['schemas']['Notification'][] = [];
|
let readNotifications: components['schemas']['Notification'][] = [];
|
||||||
@@ -99,11 +99,11 @@
|
|||||||
<Breadcrumb.Root>
|
<Breadcrumb.Root>
|
||||||
<Breadcrumb.List>
|
<Breadcrumb.List>
|
||||||
<Breadcrumb.Item class="hidden md:block">
|
<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.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Link href="{base}/dashboard">Home</Breadcrumb.Link>
|
<Breadcrumb.Link href={resolve('/dashboard', {})}>Home</Breadcrumb.Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { Separator } from '$lib/components/ui/separator';
|
import { Separator } from '$lib/components/ui/separator';
|
||||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||||
import * as Breadcrumb from '$lib/components/ui/breadcrumb/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';
|
import type { components } from '$lib/api/api';
|
||||||
|
|
||||||
let currentUser: () => components['schemas']['UserRead'] = getContext('user');
|
let currentUser: () => components['schemas']['UserRead'] = getContext('user');
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
(user: components['schemas']['UserRead']) => user.id !== currentUser().id
|
(user: components['schemas']['UserRead']) => user.id !== currentUser().id
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
console.log('Current user:', currentUser());
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -31,7 +30,7 @@
|
|||||||
<Breadcrumb.Root>
|
<Breadcrumb.Root>
|
||||||
<Breadcrumb.List>
|
<Breadcrumb.List>
|
||||||
<Breadcrumb.Item class="hidden md:block">
|
<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.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
|
|||||||
@@ -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"
|
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)}
|
{#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.Root class="col-span-full max-w-[90vw] ">
|
||||||
<Card.Header>
|
<Card.Header>
|
||||||
<Card.Title class="h-6 truncate">{getFullyQualifiedMediaName(show)}</Card.Title>
|
<Card.Title class="h-6 truncate">{getFullyQualifiedMediaName(show)}</Card.Title>
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { setContext } from 'svelte';
|
|
||||||
import type { LayoutProps } from './$types';
|
import type { LayoutProps } from './$types';
|
||||||
|
|
||||||
let { data, children }: LayoutProps = $props();
|
let { data, children }: LayoutProps = $props();
|
||||||
|
|
||||||
const showData = $derived(data.showData);
|
const showData = $derived(data.showData);
|
||||||
setContext('show', () => showData);
|
|
||||||
const fetchError = $derived((data as { error?: string }).error || null);
|
const fetchError = $derived((data as { error?: string }).error || null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ import type { LayoutLoad } from './$types';
|
|||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
|
|
||||||
export const load: LayoutLoad = async ({ params, fetch }) => {
|
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,
|
fetch: fetch,
|
||||||
params: { path: { show_id: params.showId } }
|
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,
|
fetch: fetch,
|
||||||
params: { path: { show_id: params.showId } }
|
params: { path: { show_id: params.showId } }
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
showData: show.data,
|
showData: await show.then((x) => x.data),
|
||||||
torrentsData: torrents.data
|
torrentsData: await torrents.then((x) => x.data)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,22 +23,22 @@
|
|||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import client from '$lib/api';
|
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 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() {
|
async function toggle_continuous_download() {
|
||||||
const { response } = await client.POST('/api/v1/tv/shows/{show_id}/continuousDownload', {
|
const { response } = await client.POST('/api/v1/tv/shows/{show_id}/continuousDownload', {
|
||||||
params: {
|
params: {
|
||||||
path: { show_id: show().id },
|
path: { show_id: show.id },
|
||||||
query: { continuous_download: !continuousDownloadEnabled }
|
query: { continuous_download: !continuousDownloadEnabled }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log(
|
console.log(
|
||||||
'Toggling continuous download for show',
|
'Toggling continuous download for show',
|
||||||
show().name,
|
show.name,
|
||||||
'to',
|
'to',
|
||||||
!continuousDownloadEnabled
|
!continuousDownloadEnabled
|
||||||
);
|
);
|
||||||
@@ -53,10 +53,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{getFullyQualifiedMediaName(show())} - MediaManager</title>
|
<title>{getFullyQualifiedMediaName(show)} - MediaManager</title>
|
||||||
<meta
|
<meta
|
||||||
content="View details and manage downloads for {getFullyQualifiedMediaName(
|
content="View details and manage downloads for {getFullyQualifiedMediaName(
|
||||||
show()
|
show
|
||||||
)} in MediaManager"
|
)} in MediaManager"
|
||||||
name="description"
|
name="description"
|
||||||
/>
|
/>
|
||||||
@@ -81,20 +81,20 @@
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Page>{getFullyQualifiedMediaName(show())}</Breadcrumb.Page>
|
<Breadcrumb.Page>{getFullyQualifiedMediaName(show)}</Breadcrumb.Page>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
</Breadcrumb.List>
|
</Breadcrumb.List>
|
||||||
</Breadcrumb.Root>
|
</Breadcrumb.Root>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<h1 class="scroll-m-20 text-center text-4xl font-extrabold tracking-tight lg:text-5xl">
|
<h1 class="scroll-m-20 text-center text-4xl font-extrabold tracking-tight lg:text-5xl">
|
||||||
{getFullyQualifiedMediaName(show())}
|
{getFullyQualifiedMediaName(show)}
|
||||||
</h1>
|
</h1>
|
||||||
<main class="mx-auto flex w-full flex-1 flex-col gap-4 p-4 md:max-w-[80em]">
|
<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="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">
|
<div class="w-full overflow-hidden rounded-xl bg-muted/50 md:w-1/3 md:max-w-sm">
|
||||||
{#if show().id}
|
{#if show.id}
|
||||||
<MediaPicture media={show()} />
|
<MediaPicture media={show} />
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
<div
|
||||||
class="flex aspect-9/16 h-auto w-full items-center justify-center rounded-lg bg-gray-200 text-gray-500"
|
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.Header>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<p class="leading-7 not-first:mt-6">
|
<p class="leading-7 not-first:mt-6">
|
||||||
{show().overview}
|
{show.overview}
|
||||||
</p>
|
</p>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
<Card.Title>Administrator Controls</Card.Title>
|
<Card.Title>Administrator Controls</Card.Title>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content class="flex flex-col items-center gap-4">
|
<Card.Content class="flex flex-col items-center gap-4">
|
||||||
{#if !show().ended}
|
{#if !show.ended}
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<Switch
|
<Switch
|
||||||
bind:checked={() => continuousDownloadEnabled, toggle_continuous_download}
|
bind:checked={() => continuousDownloadEnabled, toggle_continuous_download}
|
||||||
@@ -135,8 +135,8 @@
|
|||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<LibraryCombobox media={show()} mediaType="tv" />
|
<LibraryCombobox media={show} mediaType="tv" />
|
||||||
<DeleteMediaDialog isShow={true} media={show()} />
|
<DeleteMediaDialog isShow={true} media={show} />
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -146,9 +146,9 @@
|
|||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content class="flex flex-col items-center gap-4">
|
<Card.Content class="flex flex-col items-center gap-4">
|
||||||
{#if user().is_superuser}
|
{#if user().is_superuser}
|
||||||
<DownloadSeasonDialog show={show()} />
|
<DownloadSeasonDialog {show} />
|
||||||
{/if}
|
{/if}
|
||||||
<RequestSeasonDialog show={show()} />
|
<RequestSeasonDialog {show} />
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
</div>
|
</div>
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
<Card.Header>
|
<Card.Header>
|
||||||
<Card.Title>Season Details</Card.Title>
|
<Card.Title>Season Details</Card.Title>
|
||||||
<Card.Description>
|
<Card.Description>
|
||||||
A list of all seasons for {getFullyQualifiedMediaName(show())}.
|
A list of all seasons for {getFullyQualifiedMediaName(show)}.
|
||||||
</Card.Description>
|
</Card.Description>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content class="w-full overflow-x-auto">
|
<Card.Content class="w-full overflow-x-auto">
|
||||||
@@ -173,13 +173,13 @@
|
|||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Header>
|
</Table.Header>
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
{#if show().seasons.length > 0}
|
{#if show.seasons.length > 0}
|
||||||
{#each show().seasons as season (season.id)}
|
{#each show.seasons as season (season.id)}
|
||||||
<Table.Row
|
<Table.Row
|
||||||
onclick={() =>
|
onclick={() =>
|
||||||
goto(
|
goto(
|
||||||
resolve('/dashboard/tv/[showId]/[seasonId]', {
|
resolve('/dashboard/tv/[showId]/[seasonId]', {
|
||||||
showId: show().id,
|
showId: show.id,
|
||||||
seasonId: season.id
|
seasonId: season.id
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||||
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
import * as Breadcrumb from '$lib/components/ui/breadcrumb/index.js';
|
||||||
import * as Table from '$lib/components/ui/table/index.js';
|
import * as Table from '$lib/components/ui/table/index.js';
|
||||||
import { getContext } from 'svelte';
|
|
||||||
import type { components } from '$lib/api/api';
|
import type { components } from '$lib/api/api';
|
||||||
import CheckmarkX from '$lib/components/checkmark-x.svelte';
|
import CheckmarkX from '$lib/components/checkmark-x.svelte';
|
||||||
import { getFullyQualifiedMediaName, getTorrentQualityString } from '$lib/utils';
|
import { getFullyQualifiedMediaName, getTorrentQualityString } from '$lib/utils';
|
||||||
@@ -12,18 +11,16 @@
|
|||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import * as Card from '$lib/components/ui/card/index.js';
|
import * as Card from '$lib/components/ui/card/index.js';
|
||||||
|
|
||||||
let seasonFiles: components['schemas']['PublicSeasonFile'][] = $state(page.data.files);
|
let seasonFiles: components['schemas']['PublicSeasonFile'][] = $derived(page.data.files);
|
||||||
let season: components['schemas']['Season'] = $state(page.data.season);
|
let season: components['schemas']['Season'] = $derived(page.data.season);
|
||||||
let show: () => components['schemas']['Show'] = getContext('show');
|
let show: components['schemas']['Show'] = $derived(page.data.showData);
|
||||||
|
|
||||||
console.log('loaded files', seasonFiles);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{getFullyQualifiedMediaName(show())} - Season {season.number} - MediaManager</title>
|
<title>{getFullyQualifiedMediaName(show)} - Season {season.number} - MediaManager</title>
|
||||||
<meta
|
<meta
|
||||||
content="View episodes and manage downloads for {getFullyQualifiedMediaName(
|
content="View episodes and manage downloads for {getFullyQualifiedMediaName(
|
||||||
show()
|
show
|
||||||
)} Season {season.number} in MediaManager"
|
)} Season {season.number} in MediaManager"
|
||||||
name="description"
|
name="description"
|
||||||
/>
|
/>
|
||||||
@@ -48,9 +45,9 @@
|
|||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<Breadcrumb.Link href={resolve('/dashboard/tv/[showId]', { showId: show().id! })}>
|
<Breadcrumb.Link href={resolve('/dashboard/tv/[showId]', { showId: show.id! })}>
|
||||||
{show().name}
|
{show.name}
|
||||||
{show().year == null ? '' : '(' + show().year + ')'}
|
{show.year == null ? '' : '(' + show.year + ')'}
|
||||||
</Breadcrumb.Link>
|
</Breadcrumb.Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Separator class="hidden md:block" />
|
<Breadcrumb.Separator class="hidden md:block" />
|
||||||
@@ -62,12 +59,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<h1 class="scroll-m-20 text-center text-4xl font-extrabold tracking-tight lg:text-5xl">
|
<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>
|
</h1>
|
||||||
<main class="mx-auto flex w-full flex-1 flex-col gap-4 p-4 md:max-w-[80em]">
|
<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="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">
|
<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>
|
||||||
<div class="h-full w-full flex-auto rounded-xl md:w-1/4">
|
<div class="h-full w-full flex-auto rounded-xl md:w-1/4">
|
||||||
<Card.Root class="h-full w-full">
|
<Card.Root class="h-full w-full">
|
||||||
@@ -76,7 +73,7 @@
|
|||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<p class="leading-7 not-first:mt-6">
|
<p class="leading-7 not-first:mt-6">
|
||||||
{show().overview}
|
{show.overview}
|
||||||
</p>
|
</p>
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
@@ -130,7 +127,7 @@
|
|||||||
<Card.Header>
|
<Card.Header>
|
||||||
<Card.Title>Episodes</Card.Title>
|
<Card.Title>Episodes</Card.Title>
|
||||||
<Card.Description
|
<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.Description>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { PageLoad } from './$types';
|
|||||||
import client from '$lib/api';
|
import client from '$lib/api';
|
||||||
|
|
||||||
export const load: PageLoad = async ({ fetch, params }) => {
|
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,
|
fetch: fetch,
|
||||||
params: {
|
params: {
|
||||||
path: {
|
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,
|
fetch: fetch,
|
||||||
params: {
|
params: {
|
||||||
path: {
|
path: {
|
||||||
@@ -19,7 +19,7 @@ export const load: PageLoad = async ({ fetch, params }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
files: seasonFiles.data,
|
files: await seasonFiles.then((x) => x.data),
|
||||||
season: season.data
|
season: await season.then((x) => x.data)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,15 +10,15 @@
|
|||||||
import * as RadioGroup from '$lib/components/ui/radio-group/index.js';
|
import * as RadioGroup from '$lib/components/ui/radio-group/index.js';
|
||||||
import AddMediaCard from '$lib/components/add-media-card.svelte';
|
import AddMediaCard from '$lib/components/add-media-card.svelte';
|
||||||
import { onMount } from '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 searchTerm: string = $state('');
|
||||||
let metadataProvider: 'tmdb' | 'tvdb' = $state('tmdb');
|
let metadataProvider: 'tmdb' | 'tvdb' = $state('tmdb');
|
||||||
let data: components['schemas']['MetaDataProviderSearchResult'][] | null = $state(null);
|
let data: components['schemas']['MetaDataProviderSearchResult'][] | null = $state(null);
|
||||||
let isSearching: boolean = $state(false);
|
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(() => {
|
onMount(() => {
|
||||||
search('');
|
search('');
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import type { components } from '$lib/api/api';
|
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>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { LayoutLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
import client from '$lib/api';
|
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 });
|
const { data } = await client.GET('/api/v1/tv/seasons/requests', { fetch: fetch });
|
||||||
return {
|
return {
|
||||||
requestsData: data
|
requestsData: data
|
||||||
@@ -5,10 +5,7 @@
|
|||||||
import background from '$lib/images/pawel-czerwinski-NTYYL9Eb9y8-unsplash.jpg?enhanced';
|
import background from '$lib/images/pawel-czerwinski-NTYYL9Eb9y8-unsplash.jpg?enhanced';
|
||||||
import { PUBLIC_VERSION } from '$env/static/public';
|
import { PUBLIC_VERSION } from '$env/static/public';
|
||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import { page } from '$app/state';
|
|
||||||
import { setContext } from 'svelte';
|
|
||||||
|
|
||||||
setContext('oauthProviders', () => page.data.oauthProviders.oauth_providers);
|
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import LoginCard from '$lib/components/login-card.svelte';
|
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>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -14,5 +14,5 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<LoginCard oauthProviderNames={oauthProvider()} />
|
<LoginCard oauthProviderNames={oauthProviders} />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SignupCard from '$lib/components/signup-card.svelte';
|
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>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -10,4 +10,4 @@
|
|||||||
<meta content="Signup - MediaManager" name="description" />
|
<meta content="Signup - MediaManager" name="description" />
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<SignupCard oauthProviderNames={oauthProvider()} />
|
<SignupCard oauthProviderNames={oauthProviders} />
|
||||||
|
|||||||
Reference in New Issue
Block a user