fix: And refactor onboarding integration components

This commit is contained in:
Aleksi Lassila
2024-06-18 00:58:03 +03:00
parent 1c2fbf74eb
commit 355c93e9e8
10 changed files with 372 additions and 599 deletions

View File

@@ -7,55 +7,59 @@
import { derived, get } from 'svelte/store';
const dispatch = createEventDispatcher<{
change: { baseUrl: string; apiKey: string; stale: boolean };
'click-user': { user: JellyfinUser | undefined; users: JellyfinUser[] };
'click-user': {
user: JellyfinUser | undefined;
users: JellyfinUser[];
setJellyfinUser: typeof setJellyfinUser;
};
}>();
export let baseUrl = get(user)?.settings.jellyfin.baseUrl || '';
export let apiKey = get(user)?.settings.jellyfin.apiKey || '';
let baseUrl = get(user)?.settings.jellyfin.baseUrl || '';
let apiKey = get(user)?.settings.jellyfin.apiKey || '';
export let jellyfinUser: JellyfinUser | undefined = undefined;
const originalBaseUrl = derived(user, (user) => user?.settings.jellyfin.baseUrl || '');
const originalApiKey = derived(user, (user) => user?.settings.jellyfin.apiKey || '');
const originalUserId = derived(user, (user) => user?.settings.jellyfin.userId || undefined);
let timeout: ReturnType<typeof setTimeout>;
export let jellyfinUsers: Promise<JellyfinUser[]> | undefined = undefined;
let stale = false;
let error = '';
let jellyfinUsers: Promise<JellyfinUser[]> | undefined = undefined;
export let jellyfinUser: JellyfinUser | undefined;
$: {
if ($originalBaseUrl !== baseUrl && $originalApiKey !== apiKey) handleChange();
else dispatch('change', { baseUrl, apiKey, stale: false });
jellyfinUser;
$originalBaseUrl;
$originalApiKey;
$originalUserId;
stale = getIsStale();
}
$: if (jellyfinUser)
dispatch('change', {
baseUrl,
apiKey,
stale:
baseUrl && apiKey ? jellyfinUser?.Id !== get(user)?.settings.jellyfin.userId : !jellyfinUser
});
handleChange();
function getIsStale() {
return (
(!!jellyfinUser?.Id || (!baseUrl && !apiKey && !jellyfinUser)) &&
($originalBaseUrl !== baseUrl ||
$originalApiKey !== apiKey ||
$originalUserId !== jellyfinUser?.Id)
);
}
function handleChange() {
clearTimeout(timeout);
stale = false;
error = '';
jellyfinUsers = undefined;
jellyfinUser = undefined;
if (baseUrl === '' || apiKey === '') {
stale = getIsStale();
return;
}
const baseUrlCopy = baseUrl;
const apiKeyCopy = apiKey;
dispatch('change', {
baseUrl: '',
apiKey: '',
stale:
baseUrl === '' &&
apiKey === '' &&
jellyfinUser === undefined &&
(baseUrl !== $originalBaseUrl || apiKey !== $originalApiKey)
});
if (baseUrlCopy === '' || apiKeyCopy === '') return;
timeout = setTimeout(async () => {
jellyfinUsers = jellyfinApi.getJellyfinUsers(baseUrl, apiKey);
@@ -64,28 +68,38 @@
if (users.length) {
jellyfinUser = users.find((u) => u.Id === get(user)?.settings.jellyfin.userId);
const stale =
(baseUrlCopy !== $originalBaseUrl || apiKeyCopy !== $originalApiKey) &&
jellyfinUser !== undefined;
dispatch('change', { baseUrl: baseUrlCopy, apiKey: apiKeyCopy, stale });
// stale = !!jellyfinUser?.Id && getIsStale();
} else {
error = 'Could not connect';
stale = false;
}
// if (res.status !== 200) {
// error =
// res.status === 404
// ? 'Server not found'
// : res.status === 401
// ? 'Invalid api key'
// : 'Could not connect';
//
// return; // TODO add notification
// } else {
// dispatch('change', { baseUrl: baseUrlCopy, apiKey: apiKeyCopy, stale });
// }
}, 1000);
}
const setJellyfinUser = (u: JellyfinUser) => (jellyfinUser = u);
async function handleSave() {
if (!stale) return;
return user.updateUser((prev) => ({
...prev,
settings: {
...prev.settings,
jellyfin: {
...prev.settings.jellyfin,
baseUrl,
apiKey,
userId: jellyfinUser?.Id || ''
}
}
}));
}
$: empty = !baseUrl && !apiKey && !jellyfinUser;
$: unchanged =
$originalBaseUrl === baseUrl &&
$originalApiKey === apiKey &&
$originalUserId === jellyfinUser?.Id;
</script>
<div class="space-y-4 mb-4">
@@ -109,7 +123,8 @@
{#if users?.length}
<SelectField
value={jellyfinUser?.Name || 'Select User'}
on:clickOrSelect={() => dispatch('click-user', { user: jellyfinUser, users })}
on:clickOrSelect={() =>
dispatch('click-user', { user: jellyfinUser, users, setJellyfinUser })}
class="mb-4"
>
User
@@ -120,3 +135,5 @@
{#if error}
<div class="text-red-500 mb-4">{error}</div>
{/if}
<slot {handleSave} {stale} {empty} {unchanged} />

View File

@@ -5,48 +5,51 @@
import { user } from '../../stores/user.store';
import { derived, get } from 'svelte/store';
const dispatch = createEventDispatcher<{
change: { baseUrl: string; apiKey: string; stale: boolean };
}>();
export let baseUrl = get(user)?.settings.radarr.baseUrl || '';
export let apiKey = get(user)?.settings.radarr.apiKey || '';
let baseUrl = get(user)?.settings.radarr.baseUrl || '';
let apiKey = get(user)?.settings.radarr.apiKey || '';
const originalBaseUrl = derived(user, (user) => user?.settings.radarr.baseUrl || '');
const originalApiKey = derived(user, (user) => user?.settings.radarr.apiKey || '');
let timeout: ReturnType<typeof setTimeout>;
let stale = false;
let error = '';
let timeout: ReturnType<typeof setTimeout>;
let healthCheck: Promise<boolean> | undefined;
$: {
if ($originalBaseUrl !== baseUrl && $originalApiKey !== apiKey) handleChange();
else dispatch('change', { baseUrl, apiKey, stale: false });
$originalBaseUrl;
$originalApiKey;
stale = getIsStale();
}
handleChange();
function getIsStale() {
return (
(!!healthCheck || (!baseUrl && !apiKey)) &&
($originalBaseUrl !== baseUrl || $originalApiKey !== apiKey)
);
}
function handleChange() {
clearTimeout(timeout);
stale = false;
error = '';
healthCheck = undefined;
if (baseUrl === '' || apiKey === '') {
stale = getIsStale();
return;
}
const baseUrlCopy = baseUrl;
const apiKeyCopy = apiKey;
const stale = baseUrlCopy !== $originalBaseUrl || apiKeyCopy !== $originalApiKey;
dispatch('change', {
baseUrl: '',
apiKey: '',
stale: baseUrl === '' && apiKey === '' && stale
});
if (baseUrlCopy === '' || apiKeyCopy === '') return;
timeout = setTimeout(async () => {
const p = radarrApi.getHealth(baseUrlCopy, apiKeyCopy);
healthCheck = p.then((res) => res.status === 200);
const res = await p;
if (baseUrlCopy !== baseUrl || apiKeyCopy !== apiKey) return;
if (res.status !== 200) {
error =
res.status === 404
@@ -55,12 +58,29 @@
? 'Invalid api key'
: 'Could not connect';
return; // TODO add notification
stale = false; // TODO add notification
} else {
dispatch('change', { baseUrl: baseUrlCopy, apiKey: apiKeyCopy, stale });
stale = getIsStale();
}
}, 1000);
}
async function handleSave() {
return user.updateUser((prev) => ({
...prev,
settings: {
...prev.settings,
radarr: {
...prev.settings.radarr,
baseUrl,
apiKey
}
}
}));
}
$: empty = !baseUrl && !apiKey;
$: unchanged = baseUrl === $originalBaseUrl && apiKey === $originalApiKey;
</script>
<div class="space-y-4 mb-4">
@@ -73,3 +93,5 @@
{#if error}
<div class="text-red-500 mb-4">{error}</div>
{/if}
<slot {handleSave} {stale} {empty} {unchanged} />

View File

@@ -5,50 +5,51 @@
import { user } from '../../stores/user.store';
import { derived, get } from 'svelte/store';
const dispatch = createEventDispatcher<{
change: { baseUrl: string; apiKey: string; stale: boolean };
}>();
export let baseUrl = get(user)?.settings.sonarr.baseUrl || '';
export let apiKey = get(user)?.settings.sonarr.apiKey || '';
let baseUrl = get(user)?.settings.sonarr.baseUrl || '';
let apiKey = get(user)?.settings.sonarr.apiKey || '';
const originalBaseUrl = derived(user, (u) => u?.settings.sonarr.baseUrl || '');
const originalApiKey = derived(user, (u) => u?.settings.sonarr.apiKey || '');
let timeout: ReturnType<typeof setTimeout>;
let stale = false;
let error = '';
let timeout: ReturnType<typeof setTimeout>;
let healthCheck: Promise<boolean> | undefined;
$: {
if ($originalBaseUrl !== baseUrl && $originalApiKey !== apiKey) handleChange();
else dispatch('change', { baseUrl, apiKey, stale: false });
$originalBaseUrl;
$originalApiKey;
stale = getIsStale();
}
handleChange();
function handleChange() {
console.log('handleChange', $originalBaseUrl, baseUrl, $originalApiKey, apiKey);
function getIsStale() {
return (
(!!healthCheck || (!baseUrl && !apiKey)) &&
($originalBaseUrl !== baseUrl || $originalApiKey !== apiKey)
);
}
function handleChange() {
clearTimeout(timeout);
stale = false;
error = '';
healthCheck = undefined;
if (baseUrl === '' || apiKey === '') {
stale = getIsStale();
return;
}
const baseUrlCopy = baseUrl;
const apiKeyCopy = apiKey;
const stale = baseUrlCopy !== $originalBaseUrl || apiKeyCopy !== $originalApiKey;
dispatch('change', {
baseUrl: '',
apiKey: '',
stale: baseUrl === '' && apiKey === '' && stale
});
if (baseUrlCopy === '' || apiKeyCopy === '') return;
timeout = setTimeout(async () => {
const p = sonarrApi.getHealth(baseUrlCopy, apiKeyCopy);
healthCheck = p.then((res) => res.status === 200);
const res = await p;
if (baseUrlCopy !== baseUrl || apiKeyCopy !== apiKey) return;
if (res.status !== 200) {
error =
res.status === 404
@@ -57,12 +58,29 @@
? 'Invalid api key'
: 'Could not connect';
return; // TODO add notification
stale = false; // TODO add notification
} else {
dispatch('change', { baseUrl: baseUrlCopy, apiKey: apiKeyCopy, stale });
stale = getIsStale();
}
}, 1000);
}
async function handleSave() {
return user.updateUser((prev) => ({
...prev,
settings: {
...prev.settings,
sonarr: {
...prev.settings.sonarr,
baseUrl,
apiKey
}
}
}));
}
$: empty = !baseUrl && !apiKey;
$: unchanged = baseUrl === $originalBaseUrl && apiKey === $originalApiKey;
</script>
<div class="space-y-4 mb-4">
@@ -75,3 +93,5 @@
{#if error}
<div class="text-red-500 mb-4">{error}</div>
{/if}
<slot {handleSave} {stale} {empty} {unchanged} />

View File

@@ -1,76 +1,48 @@
<script lang="ts">
import TextField from '../TextField.svelte';
import { sonarrApi } from '../../apis/sonarr/sonarr-api';
import { createEventDispatcher } from 'svelte';
import { tmdbApi } from '../../apis/tmdb/tmdb-api';
import { user } from '../../stores/user.store';
import SelectField from '../SelectField.svelte';
import { createEventDispatcher } from 'svelte';
import Button from '../Button.svelte';
import { ArrowRight, Trash } from 'radix-icons-svelte';
import { derived } from 'svelte/store';
import classNames from 'classnames';
const dispatch = createEventDispatcher<{
change: { baseUrl: string; apiKey: string; stale: boolean };
}>();
export let handleConnectTmdb: () => void;
export let baseUrl = '';
export let apiKey = '';
let originalBaseUrl: string | undefined;
let originalApiKey: string | undefined;
let timeout: ReturnType<typeof setTimeout>;
let error = '';
let healthCheck: Promise<boolean> | undefined;
const dispatch = createEventDispatcher<{ 'click-user': null }>();
const userId = derived(user, (user) => user?.settings.tmdb.userId);
user.subscribe((user) => {
baseUrl = baseUrl || user?.settings.sonarr.baseUrl || '';
apiKey = apiKey || user?.settings.sonarr.apiKey || '';
$: connectedTmdbAccount = !!$userId && tmdbApi.getAccountDetails();
originalBaseUrl = baseUrl;
originalApiKey = apiKey;
handleChange();
});
function handleChange() {
clearTimeout(timeout);
error = '';
healthCheck = undefined;
const baseUrlCopy = baseUrl;
const apiKeyCopy = apiKey;
const stale = baseUrlCopy !== originalBaseUrl || apiKeyCopy !== originalApiKey;
dispatch('change', {
baseUrl: '',
apiKey: '',
stale: baseUrl === '' && apiKey === ''
});
if (baseUrlCopy === '' || apiKeyCopy === '') return;
timeout = setTimeout(async () => {
const p = sonarrApi.getHealth(baseUrlCopy, apiKeyCopy);
healthCheck = p.then((res) => res.status === 200);
const res = await p;
if (baseUrlCopy !== baseUrl || apiKeyCopy !== apiKey) return;
if (res.status !== 200) {
error =
res.status === 404
? 'Server not found'
: res.status === 401
? 'Invalid api key'
: 'Could not connect';
return; // TODO add notification
} else {
dispatch('change', { baseUrl: baseUrlCopy, apiKey: apiKeyCopy, stale });
async function handleDisconnectTmdb() {
return user.updateUser((prev) => ({
...prev,
settings: {
...prev.settings,
tmdb: {
...prev.settings.tmdb,
userId: '',
sessionId: ''
}
}
}, 1000);
}));
}
</script>
<div class="space-y-4 mb-4">
<TextField bind:value={baseUrl} isValid={healthCheck} on:change={handleChange}>Base Url</TextField
>
<TextField bind:value={apiKey} isValid={healthCheck} on:change={handleChange}>API Key</TextField>
</div>
{#if error}
<div class="text-red-500 mb-4">{error}</div>
{/if}
{#await connectedTmdbAccount then tmdbAccount}
{#if tmdbAccount}
<SelectField value={tmdbAccount.username || ''} action={handleDisconnectTmdb} class="mb-4">
Connected to
<Trash slot="icon" let:size let:iconClass {size} class={classNames(iconClass, '')} />
</SelectField>
{:else}
<slot>
<div class="flex space-x-4">
<Button type="primary-dark" iconAfter={ArrowRight} on:clickOrSelect={handleConnectTmdb}>
Connect
</Button>
</div>
</slot>
{/if}
{/await}

View File

@@ -2,7 +2,7 @@
import Container from '../../../Container.svelte';
import { tmdbApi } from '../../apis/tmdb/tmdb-api';
import Button from '../Button.svelte';
import { createEventDispatcher } from 'svelte';
import { createEventDispatcher, onMount } from 'svelte';
import { ExternalLink } from 'radix-icons-svelte';
import { user } from '../../stores/user.store';
@@ -13,6 +13,8 @@
let tmdbConnectQrCode: string | undefined = undefined;
let tmdbError: string = '';
handleGenerateTMDBLink();
async function handleGenerateTMDBLink() {
return tmdbApi.getConnectAccountLink().then((res) => {
if (res?.status_code !== 1) return; // TODO add notification
@@ -66,9 +68,9 @@
{/if}
<Container direction="horizontal" class="flex space-x-4 *:flex-1">
{#if !tmdbConnectRequestToken}
<Button type="primary-dark" action={handleGenerateTMDBLink}>Generate Link</Button>
{:else if tmdbConnectLink}
<!--{#if !tmdbConnectRequestToken}-->
<!-- <Button type="primary-dark" action={handleGenerateTMDBLink}>Generate Link</Button>-->
{#if tmdbConnectLink}
<Button type="primary-dark" action={completeTMDBConnect}>Complete Connection</Button>
<Button type="primary-dark" on:clickOrSelect={() => window.open(tmdbConnectLink)}>
Open Link