refactor: pretty much the whole backend module hierarchy

This commit is contained in:
Aleksi Lassila
2025-02-11 02:40:41 +02:00
parent 6969525464
commit fa27f19975
96 changed files with 1786 additions and 2033 deletions

View File

@@ -49,7 +49,7 @@
<AnimatedSelection
hasFocus={$hasFocus || $hasSecondaryFocus}
enabled={!disabled}
class={classNames('flex items-center font-medium tracking-wide ', {}, $$restProps.class)}
class={classNames('inline-flex items-center font-medium tracking-wide ', {}, $$restProps.class)}
>
<Container
bind:hasFocus
@@ -83,9 +83,7 @@
'!bg-red-500': confirmDanger && armed
})}
>
<div
class="flex-1 text-center text-nowrap flex items-center justify-center relative *:flex-1"
>
<div class="flex-1 text-center text-nowrap flex items-center justify-center relative">
{#if $$slots.icon}
<div class="mr-2">
<slot name="icon" />

View File

@@ -81,7 +81,7 @@
{#if backdropUrl}
<LazyImg src={backdropUrl} class="absolute inset-0" />
{:else}
<div class="absolute inset-0 bg-secondary-700 header4 flex items-center justify-center">
<div class="absolute inset-0 bg-secondary-700 h1 flex items-center justify-center">
{title}
</div>
{/if}

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import IconButton from '../IconButton.svelte';
import IconButton from '../FloatingIconButton.svelte';
import { ChevronLeft, ChevronRight } from 'radix-icons-svelte';
import classNames from 'classnames';
import Container from '../Container.svelte';
@@ -26,7 +26,7 @@
<div class={classNames('flex flex-col group/carousel', $$restProps.class)}>
<div class={'flex justify-between items-center mb-2 ' + scrollClass}>
<div class="header2">
<div class="h3">
<slot name="header" />
</div>
<div

View File

@@ -4,6 +4,9 @@
import { createEventDispatcher, onMount } from 'svelte';
import { Selectable, type EnterEvent, type NavigateEvent, type KeyEvent } from '../selectable';
import classNames from 'classnames';
import type { ContainerProps } from './Container.type';
type $$Props = ContainerProps;
/**
* The basis for d-pad navigation. It's a mess, but it works™
@@ -20,14 +23,14 @@
playPause: KeyEvent;
}>();
export let name: string = '';
export let direction: 'vertical' | 'horizontal' | 'grid' = 'vertical';
export let gridCols: number = 0;
export let focusOnMount = false;
export let trapFocus = false;
export let debugOutline = false;
export let focusOnClick = false;
export let focusedChild = false;
export let name: Required<ContainerProps>['name'] = '';
export let direction: Required<ContainerProps>['direction'] = 'vertical';
export let gridCols: Required<ContainerProps>['gridCols'] = 0;
export let focusOnMount: Required<ContainerProps>['focusOnMount'] = false;
export let trapFocus: Required<ContainerProps>['trapFocus'] = false;
export let debugOutline: Required<ContainerProps>['debugOutline'] = false;
export let focusOnClick: Required<ContainerProps>['focusOnClick'] = false;
export let focusedChild: Required<ContainerProps>['focusedChild'] = false;
export let disabled = false;
@@ -95,7 +98,7 @@
export const focusIndex = rest.focusIndex;
export const activeChild = rest.activeChild;
export let tag = 'div';
export let tag: Required<ContainerProps>['tag'] = 'div';
$: selectable.setIsDisabled(disabled);
$: selectable.setGridColumns(gridCols);

View File

@@ -0,0 +1,25 @@
// export let name: string = '';
// export let direction: 'vertical' | 'horizontal' | 'grid' = 'vertical';
// export let gridCols: number = 0;
// export let focusOnMount = false;
// export let trapFocus = false;
// export let debugOutline = false;
// export let focusOnClick = false;
// export let focusedChild = false;
import type { SvelteHTMLElements } from "svelte/elements";
// export let disabled = false;
export type ContainerProps = SvelteHTMLElements['div'] & {
name?: string;
direction?: 'vertical' | 'horizontal' | 'grid';
gridCols?: number;
focusOnMount?: boolean;
trapFocus?: boolean;
debugOutline?: boolean;
focusOnClick?: boolean;
focusedChild?: boolean;
disabled?: boolean;
tag?: string;
};

View File

@@ -4,6 +4,12 @@
import { get } from 'svelte/store';
import Sidebar from '../Sidebar/Sidebar.svelte';
import classNames from 'classnames';
import type { ContainerProps } from '../Container.type';
interface $$Props extends ContainerProps {
topmost?: boolean;
sidebar?: boolean;
}
export let topmost = true;
export let sidebar = true;
@@ -45,7 +51,7 @@
{#if sidebar}
<Sidebar />
{/if}
<Container on:back={handleGoToTop} focusOnMount class={classNames($$restProps.class)}>
<Container {...$$restProps} on:back={handleGoToTop} focusOnMount>
<slot {handleGoBack} registrar={topSelectable.registrar} />
</Container>
</Container>

View File

@@ -30,7 +30,7 @@
</script>
<Dialog>
<div class="header2 mb-4">
<div class="h3 mb-4">
{header}
</div>
<div class="font-medium text-secondary-300 mb-8">

View File

@@ -165,7 +165,7 @@
<Dialog class="grid" size={'dynamic'}>
<Tab {...tab} tab={Tabs.EditProfile} class="space-y-4 max-w-lg">
<h1 class="header2">
<h1 class="h3">
{createNew ? 'Create Account' : 'Edit Profile'}
</h1>
<TextField bind:value={name}>name</TextField>
@@ -236,7 +236,7 @@
detail.stopPropagation();
}}
>
<h1 class="header2 mb-6">Select Profile Picture</h1>
<h1 class="h3 mb-6">Select Profile Picture</h1>
<Container direction="grid" gridCols={3} class="grid grid-cols-3 gap-4 w-max">
<ProfileIcon
url={profilePictures.ana}

View File

@@ -21,7 +21,7 @@
/* For a11y*/
}}
>
<Panel {size} class={$$restProps.class}>
<Panel {size} class={$$restProps.class} onClose={handleClose}>
<slot close={handleClose} />
</Panel>
</div>

View File

@@ -4,6 +4,7 @@
import SelectItem from '../SelectItem.svelte';
import { modalStack } from '../Modal/modal.store';
// TODO: Add labels to the options
export let title: string = 'Select';
export let subtitle: string = '';
export let options: string[];
@@ -21,7 +22,7 @@
<Dialog>
<div class="mb-4">
<slot>
<h1 class="header1">{title}</h1>
<h1 class="h4">{title}</h1>
<p class="text-secondary-300">{subtitle}</p>
</slot>
</div>

View File

@@ -24,7 +24,7 @@
<div class="flex items-center justify-center text-secondary-500 mb-4">
<InfoCircled size={64} />
</div>
<h1 class="header2 text-center">Update Available</h1>
<h1 class="h3 text-center">Update Available</h1>
<div class="body mb-8 text-center">Reiverr {version} is now available.</div>
<Container class="space-y-4">
<Button type="primary-dark" on:clickOrSelect={dismiss}>Dismiss</Button>

View File

@@ -0,0 +1,52 @@
<script lang="ts">
import classNames from 'classnames';
import Container from './Container.svelte';
export let disabled = false;
export let type: 'floating' | 'solid' = 'floating';
export let container = false;
// const getClass = (hasFocus?: boolean) => type === 'floating' ? classNames(
// 'text-zinc-300 hover:text-zinc-50 p-1 flex items-center justify-center rounded-sm flex-shrink-0 bg-transparent',
// {
// 'opacity-30 cursor-not-allowed pointer-events-none': disabled,
// 'cursor-pointer': !disabled
// },
// $$restProps.class
// ) : classNames(
// 'group-hover:bg-primary-500 group-hover:text-secondary-800 rounded-full p-3',
// {
// 'bg-primary-500 text-secondary-800': hasFocus
// },
// $$restProps.class
// )
</script>
{#if !container}
<button
class={classNames(
'text-secondary-400 hover:text-secondary-200 p-1 flex items-center justify-center rounded-sm flex-shrink-0 bg-transparent',
{
'opacity-30 cursor-not-allowed pointer-events-none': disabled,
'cursor-pointer': !disabled
},
$$restProps.class
)}
on:click
>
<slot />
</button>
{:else}
<Container on:clickOrSelect let:hasFocus class="cursor-pointer group">
<div
class={classNames(
'group-hover:bg-primary-500 group-hover:text-secondary-800 rounded-full p-3',
{
'bg-primary-500 text-secondary-800': hasFocus
}
)}
>
<slot />
</div>
</Container>
{/if}

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import Container from '../Container.svelte';
import HeroShowcaseBackground from './HeroBackground.svelte';
import IconButton from '../IconButton.svelte';
import IconButton from '../FloatingIconButton.svelte';
import { ChevronRight } from 'radix-icons-svelte';
import PageDots from '../HeroShowcase/PageDots.svelte';
import type { Readable, Writable } from 'svelte/store';

View File

@@ -1,19 +0,0 @@
<script lang="ts">
import classNames from 'classnames';
export let disabled = false;
</script>
<button
class={classNames(
'text-zinc-300 hover:text-zinc-50 p-1 flex items-center justify-center rounded-sm flex-shrink-0 bg-transparent',
{
'opacity-30 cursor-not-allowed pointer-events-none': disabled,
'cursor-pointer': !disabled
},
$$restProps.class
)}
on:click
>
<slot />
</button>

View File

@@ -15,7 +15,7 @@
</script>
<Dialog>
<h1 class="header1 mb-2">Users</h1>
<h1 class="h4 mb-2">Users</h1>
<div class="space-y-4">
{#each users as user}
<SelectItem

View File

@@ -51,7 +51,7 @@
}
</script>
<h1 class="header2 mb-2">Connect a TMDB Account</h1>
<h1 class="h3 mb-2">Connect a TMDB Account</h1>
<div class="body mb-8">
To connect your TMDB account, log in via the link below and then click "Complete Connection".
</div>

View File

@@ -40,7 +40,7 @@
</script>
<Container class="flex flex-col" focusOnMount>
<h1 class="header2 w-full mb-2">Login to Reiverr</h1>
<h1 class="h3 w-full mb-2">Login to Reiverr</h1>
<div class="body mb-4">
If this is your first time logging in, a new account will be created based on your credentials.
</div>

View File

@@ -126,7 +126,7 @@
>
<div class="z-10 mb-8">
<div class="h-24" />
<h1 class="header2">Add {title} to Sonarr?</h1>
<h1 class="h3">Add {title} to Sonarr?</h1>
<div class="font-medium text-secondary-300 mb-8">
Before you can fetch episodes, you need to add this series to Sonarr.
</div>

View File

@@ -127,7 +127,7 @@
>
<div class="z-10 mb-8">
<div class="h-24" />
<h1 class="header2">Add {title} to Sonarr?</h1>
<h1 class="h3">Add {title} to Sonarr?</h1>
<div class="font-medium text-secondary-300 mb-8">
Before you can fetch episodes, you need to add this series to Sonarr.
</div>

View File

@@ -1,8 +1,8 @@
<div {...$$restProps}>
<div class="header4">
<div class="h1">
<slot name="title" />
</div>
<div class="header1">
<div class="h4">
<slot name="subtitle" />
</div>
</div>

View File

@@ -64,10 +64,10 @@
</script>
<Container trapFocus class="py-8 h-full flex flex-col">
<h1 class="header4 mx-12">
<h1 class="h1 mx-12">
<slot name="title" />
</h1>
<h2 class="header1 mx-12 mb-8">
<h2 class="h4 mx-12 mb-8">
<slot name="subtitle" />
</h2>

View File

@@ -1,12 +1,15 @@
<script lang="ts">
import classNames from 'classnames';
import IconButton from './FloatingIconButton.svelte';
import { Cross1 } from 'radix-icons-svelte';
export let size: 'sm' | 'full' | 'lg' | 'dynamic' = 'sm';
export let onClose: (() => void) | undefined = undefined;
</script>
<div
class={classNames(
'bg-primary-800 rounded-2xl p-12 relative shadow-xl flex flex-col transition-[max-width]',
'bg-primary-800 rounded-2xl px-8 py-8 relative shadow-xl flex flex-col transition-[max-width]',
{
'flex-1 max-h-full max-w-lg min-h-0 overflow-y-auto scrollbar-hide': size === 'sm',
'flex-1 h-full overflow-hidden': size === 'full',
@@ -16,5 +19,12 @@
$$restProps.class
)}
>
{#if onClose}
<div class="absolute top-4 right-4">
<IconButton on:click={onClose}>
<Cross1 size={20} />
</IconButton>
</div>
{/if}
<slot />
</div>

View File

@@ -6,6 +6,7 @@
const dispatch = createEventDispatcher<{ clickOrSelect: null }>();
export let color: 'secondary' | 'primary' = 'secondary';
export let value: string;
export let disabled: boolean = false;
export let action: (() => Promise<any>) | undefined = undefined;
@@ -27,11 +28,13 @@
<Container
class={classNames(
'flex items-center justify-between bg-primary-900 rounded-xl px-6 py-2.5 font-medium',
'border-2 border-transparent focus:border-primary-500 hover:border-primary-500 group',
'flex items-center justify-between rounded-xl px-6 py-2.5 font-medium',
'border-2 border-transparent focus:border-primary-500 hover:border-primary-500 group',
{
'cursor-pointer': !_disabled,
'cursor-not-allowed pointer-events-none opacity-40': _disabled
'cursor-not-allowed pointer-events-none opacity-40': _disabled,
'bg-primary-900': color === 'secondary',
'bg-secondary-800': color === 'primary'
},
$$restProps.class
)}
@@ -39,12 +42,16 @@
let:hasFocus
>
<div>
<h1 class="text-secondary-300 font-semibold tracking-wide text-sm">
<slot />
</h1>
<span>
{value}
</span>
{#if !$$slots.content}
<h1 class="text-secondary-300 font-semibold tracking-wide text-sm">
<slot />
</h1>
<span>
{value}
</span>
{:else}
<slot name="content" />
{/if}
</div>
<slot
name="icon"

View File

@@ -8,6 +8,7 @@
export let index: number = tab;
export let openTab: Writable<number>;
export let size: 'hug' | 'stretch' = 'hug';
export let direction: 'horizontal' | 'vertical' = 'horizontal';
let selectable: Selectable;
@@ -46,11 +47,22 @@
disabled={!active}
>
<div
class={classNames($$restProps.class, 'transition-[transform,opacity]', {
'opacity-0 pointer-events-none': !active,
'-translate-x-10': !active && $openTab >= index,
'translate-x-10': !active && $openTab < index
})}
class={classNames(
$$restProps.class,
'transition-[transform,opacity]',
{
'opacity-0 pointer-events-none': !active
},
direction === 'horizontal'
? {
'-translate-x-10': !active && $openTab >= index,
'translate-x-10': !active && $openTab < index
}
: {
'-translate-y-10': !active && $openTab >= index,
'translate-y-10': !active && $openTab < index
}
)}
>
<slot />
</div>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { Cross2 } from 'radix-icons-svelte';
import IconButton from './IconButton.svelte';
import IconButton from './FloatingIconButton.svelte';
import axios from 'axios';
import Button from './Button.svelte';
import { skippedVersion } from '../stores/localstorage.store';

View File

@@ -17,7 +17,7 @@
<Dialog {modalId}>
<div>
<h1 class="header2 mb-4 flex items-center space-x-4">
<h1 class="h3 mb-4 flex items-center space-x-4">
<span>Audio</span>
<ChatBubble size={32} />
</h1>

View File

@@ -15,7 +15,7 @@
</script>
<Dialog {modalId}>
<h1 class="header2 mb-4 flex items-center space-x-4">
<h1 class="h3 mb-4 flex items-center space-x-4">
<span>Subtitles</span>
<TextAlignLeft size={32} />
</h1>

View File

@@ -198,7 +198,7 @@
<div class="text-secondary-300 font-medium text-wider text-xl mb-1 tracking-wide">
{subtitle}
</div>
<h1 class="header4">{title}</h1>
<h1 class="h1">{title}</h1>
</div>
<div class="flex space-x-2">
{#if subtitleInfo?.availableSubtitles?.length}