mirror of
https://github.com/aleksilassila/reiverr.git
synced 2026-04-21 00:05:13 +02:00
feat: Manage page tabs
This commit is contained in:
118
src/lib/components/Integrations/JellyfinIntegration.svelte
Normal file
118
src/lib/components/Integrations/JellyfinIntegration.svelte
Normal file
@@ -0,0 +1,118 @@
|
||||
<script lang="ts">
|
||||
import TextField from '../TextField.svelte';
|
||||
import { appState } from '../../stores/app-state.store';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import SelectField from '../SelectField.svelte';
|
||||
import { jellyfinApi, type JellyfinUser } from '../../apis/jellyfin/jellyfin-api';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: { baseUrl: string; apiKey: string; stale: boolean };
|
||||
'click-user': { user: JellyfinUser | undefined; users: JellyfinUser[] };
|
||||
}>();
|
||||
|
||||
export let baseUrl = '';
|
||||
export let apiKey = '';
|
||||
let originalBaseUrl: string | undefined;
|
||||
let originalApiKey: string | undefined;
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
let error = '';
|
||||
let jellyfinUsers: Promise<JellyfinUser[]> | undefined = undefined;
|
||||
export let jellyfinUser: JellyfinUser | undefined;
|
||||
|
||||
appState.subscribe((appState) => {
|
||||
baseUrl = baseUrl || appState.user?.settings.jellyfin.baseUrl || '';
|
||||
apiKey = apiKey || appState.user?.settings.jellyfin.apiKey || '';
|
||||
|
||||
originalBaseUrl = baseUrl;
|
||||
originalApiKey = apiKey;
|
||||
|
||||
handleChange();
|
||||
});
|
||||
|
||||
$: if (jellyfinUser)
|
||||
dispatch('change', {
|
||||
baseUrl,
|
||||
apiKey,
|
||||
stale:
|
||||
baseUrl && apiKey
|
||||
? jellyfinUser?.Id !== get(appState).user?.settings.jellyfin.userId
|
||||
: !jellyfinUser
|
||||
});
|
||||
|
||||
function handleChange() {
|
||||
clearTimeout(timeout);
|
||||
error = '';
|
||||
jellyfinUsers = undefined;
|
||||
jellyfinUser = undefined;
|
||||
|
||||
const baseUrlCopy = baseUrl;
|
||||
const apiKeyCopy = apiKey;
|
||||
|
||||
dispatch('change', {
|
||||
baseUrl: '',
|
||||
apiKey: '',
|
||||
stale: baseUrl === '' && apiKey === '' && jellyfinUser === undefined
|
||||
});
|
||||
|
||||
if (baseUrlCopy === '' || apiKeyCopy === '') return;
|
||||
|
||||
timeout = setTimeout(async () => {
|
||||
jellyfinUsers = jellyfinApi.getJellyfinUsers(baseUrl, apiKey);
|
||||
|
||||
const users = await jellyfinUsers;
|
||||
if (baseUrlCopy !== baseUrl || apiKeyCopy !== apiKey) return;
|
||||
|
||||
if (users.length) {
|
||||
jellyfinUser = users.find((u) => u.Id === get(appState).user?.settings.jellyfin.userId);
|
||||
const stale =
|
||||
(baseUrlCopy !== originalBaseUrl || apiKeyCopy !== originalApiKey) &&
|
||||
jellyfinUser !== undefined;
|
||||
dispatch('change', { baseUrl: baseUrlCopy, apiKey: apiKeyCopy, stale });
|
||||
} else {
|
||||
error = 'Could not connect';
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-4 mb-4">
|
||||
<TextField
|
||||
bind:value={baseUrl}
|
||||
isValid={jellyfinUsers?.then((u) => !!u?.length)}
|
||||
on:change={handleChange}>Base Url</TextField
|
||||
>
|
||||
<TextField
|
||||
bind:value={apiKey}
|
||||
isValid={jellyfinUsers?.then((u) => !!u?.length)}
|
||||
on:change={handleChange}>API Key</TextField
|
||||
>
|
||||
</div>
|
||||
|
||||
{#await jellyfinUsers then users}
|
||||
{#if users?.length}
|
||||
<SelectField
|
||||
value={jellyfinUser?.Name || 'Select User'}
|
||||
on:clickOrSelect={() => dispatch('click-user', { user: jellyfinUser, users })}
|
||||
>
|
||||
User
|
||||
</SelectField>
|
||||
{/if}
|
||||
{/await}
|
||||
|
||||
{#if error}
|
||||
<div class="text-red-500 mb-4">{error}</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,23 @@
|
||||
<script lang="ts">
|
||||
import Dialog from '../Dialog/Dialog.svelte';
|
||||
import type { JellyfinUser } from '../../apis/jellyfin/jellyfin-api';
|
||||
import SelectItem from '../SelectItem.svelte';
|
||||
import { modalStack } from '../Modal/modal.store';
|
||||
|
||||
export let users: JellyfinUser[];
|
||||
export let selectedUser: JellyfinUser | undefined;
|
||||
export let handleSelectUser: (user: JellyfinUser) => void;
|
||||
|
||||
function handleSelect(user: JellyfinUser) {
|
||||
handleSelectUser(user);
|
||||
modalStack.closeTopmost();
|
||||
}
|
||||
</script>
|
||||
|
||||
<Dialog>
|
||||
{#each users as user}
|
||||
<SelectItem selected={user.Id === selectedUser?.Id} on:clickOrSelect={() => handleSelect(user)}>
|
||||
{user.Name}
|
||||
</SelectItem>
|
||||
{/each}
|
||||
</Dialog>
|
||||
76
src/lib/components/Integrations/RadarrIntegration.svelte
Normal file
76
src/lib/components/Integrations/RadarrIntegration.svelte
Normal file
@@ -0,0 +1,76 @@
|
||||
<script lang="ts">
|
||||
import TextField from '../TextField.svelte';
|
||||
import { appState } from '../../stores/app-state.store';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { radarrApi } from '../../apis/radarr/radarr-api';
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: { baseUrl: string; apiKey: string; stale: boolean };
|
||||
}>();
|
||||
|
||||
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;
|
||||
|
||||
appState.subscribe((appState) => {
|
||||
baseUrl = baseUrl || appState.user?.settings.radarr.baseUrl || '';
|
||||
apiKey = apiKey || appState.user?.settings.radarr.apiKey || '';
|
||||
|
||||
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 = 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
|
||||
? '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);
|
||||
}
|
||||
</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}
|
||||
76
src/lib/components/Integrations/SonarrIntegration.svelte
Normal file
76
src/lib/components/Integrations/SonarrIntegration.svelte
Normal file
@@ -0,0 +1,76 @@
|
||||
<script lang="ts">
|
||||
import TextField from '../TextField.svelte';
|
||||
import { appState } from '../../stores/app-state.store';
|
||||
import { sonarrApi } from '../../apis/sonarr/sonarr-api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: { baseUrl: string; apiKey: string; stale: boolean };
|
||||
}>();
|
||||
|
||||
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;
|
||||
|
||||
appState.subscribe((appState) => {
|
||||
baseUrl = baseUrl || appState.user?.settings.sonarr.baseUrl || '';
|
||||
apiKey = apiKey || appState.user?.settings.sonarr.apiKey || '';
|
||||
|
||||
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 });
|
||||
}
|
||||
}, 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}
|
||||
76
src/lib/components/Integrations/TmdbIntegration.svelte
Normal file
76
src/lib/components/Integrations/TmdbIntegration.svelte
Normal file
@@ -0,0 +1,76 @@
|
||||
<script lang="ts">
|
||||
import TextField from '../TextField.svelte';
|
||||
import { appState } from '../../stores/app-state.store';
|
||||
import { sonarrApi } from '../../apis/sonarr/sonarr-api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: { baseUrl: string; apiKey: string; stale: boolean };
|
||||
}>();
|
||||
|
||||
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;
|
||||
|
||||
appState.subscribe((appState) => {
|
||||
baseUrl = baseUrl || appState.user?.settings.sonarr.baseUrl || '';
|
||||
apiKey = apiKey || appState.user?.settings.sonarr.apiKey || '';
|
||||
|
||||
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 });
|
||||
}
|
||||
}, 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}
|
||||
Reference in New Issue
Block a user