diff --git a/src/app.css b/src/app.css index 898b443..d80d4a2 100644 --- a/src/app.css +++ b/src/app.css @@ -77,7 +77,7 @@ html[data-useragent*="Tizen"] .selectable-secondary { } .peer-selectable { - @apply peer-focus-visible:outline outline-2 outline-[#f0cd6dc2] outline-offset-2; + @apply peer-focus-visible:outline outline-2 outline-primary-500 outline-offset-2; } .selectable-explicit { diff --git a/src/lib/components/Button.svelte b/src/lib/components/Button.svelte index 5045d21..55d03a4 100644 --- a/src/lib/components/Button.svelte +++ b/src/lib/components/Button.svelte @@ -3,7 +3,7 @@ import type { Readable } from 'svelte/store'; import classNames from 'classnames'; import AnimatedSelection from './AnimateScale.svelte'; - import { createEventDispatcher } from 'svelte'; + import { type ComponentType, createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher<{ clickOrSelect: null }>(); @@ -12,6 +12,9 @@ export let type: 'primary' | 'secondary' | 'primary-dark' = 'primary'; export let confirmDanger = false; export let action: (() => Promise) | null = null; + export let icon: ComponentType | undefined = undefined; + export let iconAfter: ComponentType | undefined = undefined; + export let iconAbsolute: ComponentType | undefined = undefined; let actionIsFetching = false; $: _disabled = disabled || actionIsFetching; @@ -20,6 +23,8 @@ $: if (!$hasFocus && armed) armed = false; function handleClickOrSelect() { + if (actionIsFetching || _disabled) return; + if (confirmDanger && !armed) { armed = true; return; @@ -74,17 +79,32 @@ {/if} + {#if icon} +
+ +
+ {/if} {#if $$slots['icon-after']}
{/if} + {#if iconAfter} +
+ +
+ {/if} {#if $$slots['icon-absolute']}
{/if} + {#if iconAbsolute} +
+ +
+ {/if} diff --git a/src/lib/components/Integrations/TmdbIntegrationConnect.svelte b/src/lib/components/Integrations/TmdbIntegrationConnect.svelte new file mode 100644 index 0000000..47b13d5 --- /dev/null +++ b/src/lib/components/Integrations/TmdbIntegrationConnect.svelte @@ -0,0 +1,78 @@ + + +

Connect a TMDB Account

+
+ To connect your TMDB account, log in via the link below and then click "Complete Connection". +
+ +{#if tmdbConnectQrCode} +
+{/if} + +{#if tmdbError} +
{tmdbError}
+{/if} + + + {#if !tmdbConnectRequestToken} + + {:else if tmdbConnectLink} + + + {/if} + diff --git a/src/lib/components/Integrations/TmdbIntegrationConnectDialog.svelte b/src/lib/components/Integrations/TmdbIntegrationConnectDialog.svelte new file mode 100644 index 0000000..5881978 --- /dev/null +++ b/src/lib/components/Integrations/TmdbIntegrationConnectDialog.svelte @@ -0,0 +1,9 @@ + + + + modalStack.closeTopmost()} /> + diff --git a/src/lib/components/SelectField.svelte b/src/lib/components/SelectField.svelte index 86f327f..f1e80ac 100644 --- a/src/lib/components/SelectField.svelte +++ b/src/lib/components/SelectField.svelte @@ -2,14 +2,39 @@ import Container from '../../Container.svelte'; import { ArrowRight } from 'radix-icons-svelte'; import classNames from 'classnames'; + import { createEventDispatcher } from 'svelte'; + + const dispatch = createEventDispatcher<{ clickOrSelect: null }>(); export let value: string; + export let disabled: boolean = false; + export let action: (() => Promise) | undefined = undefined; + + let actionIsFetching = false; + $: _disabled = disabled || actionIsFetching; + + function handleClickOrSelect() { + if (actionIsFetching || _disabled) return; + + if (action) { + actionIsFetching = true; + action().then(() => (actionIsFetching = false)); + } + + dispatch('clickOrSelect'); + }
@@ -20,11 +45,19 @@ {value}
- + iconClass={classNames('group-hover:text-primary-500 group-hover:scale-110', { + 'text-primary-500 scale-110': hasFocus + })} + > + +
diff --git a/src/lib/components/Toggle.svelte b/src/lib/components/Toggle.svelte index 3c316a5..f75e51c 100644 --- a/src/lib/components/Toggle.svelte +++ b/src/lib/components/Toggle.svelte @@ -10,7 +10,9 @@ let input: HTMLInputElement; const handleChange = (e: Event) => { + // @ts-ignore checked = e.target?.checked; + // @ts-ignore dispatch('change', e.target?.checked); }; @@ -30,7 +32,7 @@ on:input={handleChange} />
diff --git a/src/lib/pages/ManagePage.svelte b/src/lib/pages/ManagePage.svelte index 7d28470..4e2e77b 100644 --- a/src/lib/pages/ManagePage.svelte +++ b/src/lib/pages/ManagePage.svelte @@ -12,9 +12,13 @@ import RadarrIntegration from '../components/Integrations/RadarrIntegration.svelte'; import type { JellyfinUser } from '../apis/jellyfin/jellyfin-api'; import JellyfinIntegration from '../components/Integrations/JellyfinIntegration.svelte'; - import { createModal } from '../components/Modal/modal.store'; import JellyfinIntegrationUsersDialog from '../components/Integrations/JellyfinIntegrationUsersDialog.svelte'; import TmdbIntegration from '../components/Integrations/TmdbIntegration.svelte'; + import { tmdbApi } from '../apis/tmdb/tmdb-api'; + import SelectField from '../components/SelectField.svelte'; + import { ArrowRight, Trash } from 'radix-icons-svelte'; + import TmdbIntegrationConnectDialog from '../components/Integrations/TmdbIntegrationConnectDialog.svelte'; + import { createModal } from '../components/Modal/modal.store'; enum Tabs { Interface, @@ -40,6 +44,7 @@ let lastKeyCode = 0; let lastKey = ''; let tizenMediaKey = ''; + $: tmdbAccount = $appState.user?.settings.tmdb.userId ? tmdbApi.getAccountDetails() : undefined; // onMount(() => { // if (isTizen()) { @@ -56,6 +61,20 @@ // } // }); + async function handleDisconnectTmdb() { + return appState.updateUser((prev) => ({ + ...prev, + settings: { + ...prev.settings, + tmdb: { + ...prev.settings.tmdb, + userId: '', + sessionId: '' + } + } + })); + } + async function handleSaveJellyfin() { return appState.updateUser((prev) => ({ ...prev, @@ -108,7 +127,7 @@ }} /> - + tab.set(Tabs.Interface)} on:clickOrSelect={() => tab.set(Tabs.Interface)} let:hasFocus + focusOnClick > tab.set(Tabs.Integrations)} on:clickOrSelect={() => tab.set(Tabs.Integrations)} let:hasFocus + focusOnClick > tab.set(Tabs.About)} on:clickOrSelect={() => tab.set(Tabs.About)} let:hasFocus + focusOnClick > - -
-

Tmdb Account

- { - sonarrBaseUrl = detail.baseUrl; - sonarrApiKey = detail.apiKey; - sonarrStale = detail.stale; - }} - /> -
- -
-
- -
-

Jellyfin

- { - jellyfinBaseUrl = detail.baseUrl; - jellyfinApiKey = detail.apiKey; - jellyfinStale = detail.stale; - }} - on:click-user={({ detail }) => - createModal(JellyfinIntegrationUsersDialog, { - selectedUser: detail.user, - users: detail.users, - handleSelectUser: (u) => (jellyfinUser = u) - })} - /> -
- -
-
- -
-

Sonarr

- { - sonarrBaseUrl = detail.baseUrl; - sonarrApiKey = detail.apiKey; - sonarrStale = detail.stale; - }} - /> -
- -
-
- -
-

Radarr

- { - radarrBaseUrl = detail.baseUrl; - radarrApiKey = detail.apiKey; - radarrStale = detail.stale; - }} - /> -
- -
-
-
- - -
+ +
({ ...p, animateScrolling: detail }))} />
-
+
+ + + + +

Sonarr

+ { + sonarrBaseUrl = detail.baseUrl; + sonarrApiKey = detail.apiKey; + sonarrStale = detail.stale; + }} + /> +
+ +
+
+ + +

Radarr

+ { + radarrBaseUrl = detail.baseUrl; + radarrApiKey = detail.apiKey; + radarrStale = detail.stale; + }} + /> +
+ +
+
+
+ + + +

Tmdb Account

+ {#await tmdbAccount then tmdbAccount} + {#if tmdbAccount} + + Connected to + + + {:else} +
+ +
+ {/if} + {/await} + + + + + + + + + + + + +
+ + +

Jellyfin

+ { + jellyfinBaseUrl = detail.baseUrl; + jellyfinApiKey = detail.apiKey; + jellyfinStale = detail.stale; + }} + on:click-user={({ detail }) => + createModal(JellyfinIntegrationUsersDialog, { + selectedUser: detail.user, + users: detail.users, + handleSelectUser: (u) => (jellyfinUser = u) + })} + /> +
+ +
+
+
+
+
+ User agent: {window?.navigator?.userAgent}
Last key code: {lastKeyCode}
@@ -257,7 +308,9 @@ {#if tizenMediaKey}
Tizen media key: {tizenMediaKey}
{/if} - +
+ +