Add Kavita setting

This commit is contained in:
2025-10-16 00:08:47 +02:00
parent d987dd0ebb
commit 35ca5b9550
4 changed files with 126 additions and 32 deletions

View File

@@ -14,15 +14,37 @@
} }
@keyframes shake { @keyframes shake {
0% { transform: translate(1px, 1px) rotate(0deg); } 0% {
10% { transform: translate(-1px, -2px) rotate(-1deg); } transform: translate(1px, 1px) rotate(0deg);
20% { transform: translate(-3px, 0px) rotate(1deg); } }
30% { transform: translate(3px, 2px) rotate(0deg); } 10% {
40% { transform: translate(1px, -1px) rotate(1deg); } transform: translate(-1px, -2px) rotate(-1deg);
50% { transform: translate(-1px, 2px) rotate(-1deg); } }
60% { transform: translate(-3px, 1px) rotate(0deg); } 20% {
70% { transform: translate(3px, 1px) rotate(-1deg); } transform: translate(-3px, 0px) rotate(1deg);
80% { transform: translate(-1px, -1px) rotate(1deg); } }
90% { transform: translate(1px, 2px) rotate(0deg); } 30% {
100% { transform: translate(1px, -2px) rotate(-1deg); } transform: translate(3px, 2px) rotate(0deg);
} }
40% {
transform: translate(1px, -1px) rotate(1deg);
}
50% {
transform: translate(-1px, 2px) rotate(-1deg);
}
60% {
transform: translate(-3px, 1px) rotate(0deg);
}
70% {
transform: translate(3px, 1px) rotate(-1deg);
}
80% {
transform: translate(-1px, -1px) rotate(1deg);
}
90% {
transform: translate(1px, 2px) rotate(0deg);
}
100% {
transform: translate(1px, -2px) rotate(-1deg);
}
}

View File

@@ -0,0 +1,51 @@
<template>
<UModal v-bind="$props" title="Connect Kavita">
<template #body>
<UFormField label="URL">
<UInput v-model="requestData.url" placeholder="https://" class="w-full" :disabled="busy" />
</UFormField>
<UFormField label="Username">
<UInput v-model="requestData.username" class="w-full" :disabled="busy" />
</UFormField>
<UFormField label="Password">
<UInput v-model="requestData.password" type="password" class="w-full" :disabled="busy" />
</UFormField>
<UButton
icon="i-lucide-link"
:class="['mt-2 float-right', success == false ? 'animate-[shake_0.2s] bg-error' : '']"
:loading="busy"
:disabled="busy || !allowSend"
@click="connect"
>Connect</UButton
>
</template>
</UModal>
</template>
<script setup lang="ts">
import type { components } from '#open-fetch-schemas/api';
type CreateLibraryConnectorRecord = components['schemas']['CreateLibraryConnectorRecord'];
const { $api } = useNuxtApp();
const requestData = ref<CreateLibraryConnectorRecord>({ libraryType: 'Kavita', url: '', username: '', password: '' });
const allowSend = computed(() => requestData.value.url && requestData.value.username && requestData.value.password);
const busy = ref<boolean>(false);
const success = ref<boolean | undefined>(undefined);
const emit = defineEmits<{ close: [boolean] }>();
const connect = async () => {
busy.value = true;
try {
await $api('/v2/LibraryConnector', { method: 'PUT', body: requestData.value });
await refreshNuxtData(FetchKeys.Libraries.All);
emit('close', false);
success.value = true;
} catch {
success.value = false;
setTimeout(() => (success.value = undefined), 200);
} finally {
busy.value = false;
}
};
</script>

View File

@@ -10,7 +10,14 @@
<UFormField label="Password"> <UFormField label="Password">
<UInput v-model="requestData.password" type="password" class="w-full" :disabled="busy" /> <UInput v-model="requestData.password" type="password" class="w-full" :disabled="busy" />
</UFormField> </UFormField>
<UButton icon="i-lucide-link" :class="['mt-2 float-right', success == false ? 'animate-[shake_0.2s] bg-error' : '' ]" :loading="busy" :disabled="busy || !allowSend" @click="connect">Connect</UButton> <UButton
icon="i-lucide-link"
:class="['mt-2 float-right', success == false ? 'animate-[shake_0.2s] bg-error' : '']"
:loading="busy"
:disabled="busy || !allowSend"
@click="connect"
>Connect</UButton
>
</template> </template>
</UModal> </UModal>
</template> </template>
@@ -20,30 +27,25 @@ import type { components } from '#open-fetch-schemas/api';
type CreateLibraryConnectorRecord = components['schemas']['CreateLibraryConnectorRecord']; type CreateLibraryConnectorRecord = components['schemas']['CreateLibraryConnectorRecord'];
const { $api } = useNuxtApp(); const { $api } = useNuxtApp();
const requestData = ref<CreateLibraryConnectorRecord>({ const requestData = ref<CreateLibraryConnectorRecord>({ libraryType: 'Komga', url: '', username: '', password: '' });
libraryType: 'Komga',
url: '',
username: '',
password: ''
});
const allowSend = computed(() => requestData.value.url && requestData.value.username && requestData.value.password); const allowSend = computed(() => requestData.value.url && requestData.value.username && requestData.value.password);
const busy = ref<boolean>(false); const busy = ref<boolean>(false);
const success = ref<boolean | undefined>(undefined); const success = ref<boolean | undefined>(undefined);
const emit = defineEmits<{ close: [boolean] }>() const emit = defineEmits<{ close: [boolean] }>();
const connect = async () => { const connect = async () => {
busy.value = true; busy.value = true;
try { try {
await $api("/v2/LibraryConnector", { method: "PUT", body: requestData.value }); await $api('/v2/LibraryConnector', { method: 'PUT', body: requestData.value });
await refreshNuxtData(FetchKeys.Libraries.All); await refreshNuxtData(FetchKeys.Libraries.All);
emit('close', false); emit('close', false);
success.value = true; success.value = true;
}catch { } catch {
success.value = false; success.value = false;
setTimeout(() => success.value = undefined, 200); setTimeout(() => (success.value = undefined), 200);
}finally { } finally {
busy.value = false; busy.value = false;
} }
} };
</script> </script>

View File

@@ -9,9 +9,18 @@
<div class="flex flex-row gap-2"> <div class="flex flex-row gap-2">
<UButton icon="i-lucide-plus" class="w-fit" @click="addLibraryModal.open()">Add</UButton> <UButton icon="i-lucide-plus" class="w-fit" @click="addLibraryModal.open()">Add</UButton>
<UTooltip :text="komgaConnected ? 'Disconnect Komga' : 'Connect Komga'"> <UTooltip :text="komgaConnected ? 'Disconnect Komga' : 'Connect Komga'">
<UButton :icon="komgaConnected ? 'i-lucide-unlink' : 'i-lucide-link'" class="w-fit" @click="onKomgaClick" <UButton
>Komga</UButton :icon="komgaConnected ? 'i-lucide-unlink' : 'i-lucide-link'"
> class="w-fit"
label="Komga"
@click="onKomgaClick" />
</UTooltip>
<UTooltip :text="kavitaConnected ? 'Disconnect Kavita' : 'Connect Kavita'">
<UButton
:icon="kavitaConnected ? 'i-lucide-unlink' : 'i-lucide-link'"
class="w-fit"
label="Kavita"
@click="onKavitaClick" />
</UTooltip> </UTooltip>
</div> </div>
</template> </template>
@@ -34,7 +43,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { LazyAddLibraryModal, LazyKomgaModal } from '#components'; import { LazyAddLibraryModal, LazyKavitaModal, LazyKomgaModal } from '#components';
import FileLibraries from '~/components/FileLibraries.vue'; import FileLibraries from '~/components/FileLibraries.vue';
import { refreshNuxtData } from '#app'; import { refreshNuxtData } from '#app';
const overlay = useOverlay(); const overlay = useOverlay();
@@ -42,6 +51,7 @@ const { $api } = useNuxtApp();
const addLibraryModal = overlay.create(LazyAddLibraryModal); const addLibraryModal = overlay.create(LazyAddLibraryModal);
const komgaModal = overlay.create(LazyKomgaModal); const komgaModal = overlay.create(LazyKomgaModal);
const kavitaModal = overlay.create(LazyKavitaModal);
const config = useRuntimeConfig(); const config = useRuntimeConfig();
const apiUrl = ref(config.public.openFetch.api.baseURL); const apiUrl = ref(config.public.openFetch.api.baseURL);
@@ -62,12 +72,21 @@ const cleanUpDatabase = async () => {
}; };
const { data: libraries } = useApi('/v2/LibraryConnector', { key: FetchKeys.Libraries.All }); const { data: libraries } = useApi('/v2/LibraryConnector', { key: FetchKeys.Libraries.All });
const komgaConnected = computed(() => libraries.value?.find((l) => l.type === "Komga")?.key); const komgaConnected = computed(() => libraries.value?.find((l) => l.type === 'Komga')?.key);
const onKomgaClick = async () => { const onKomgaClick = async () => {
if (!komgaConnected.value) { if (!komgaConnected.value) {
komgaModal.open(); komgaModal.open();
}else{ } else {
await $api("/v2/LibraryConnector/{LibraryConnectorId}", { method: "DELETE", path: { LibraryConnectorId: komgaConnected.value } }); await $api('/v2/LibraryConnector/{LibraryConnectorId}', { method: 'DELETE', path: { LibraryConnectorId: komgaConnected.value } });
await refreshNuxtData(FetchKeys.Libraries.All);
}
};
const kavitaConnected = computed(() => libraries.value?.find((l) => l.type === 'Kavita')?.key);
const onKavitaClick = async () => {
if (!kavitaConnected.value) {
kavitaModal.open();
} else {
await $api('/v2/LibraryConnector/{LibraryConnectorId}', { method: 'DELETE', path: { LibraryConnectorId: kavitaConnected.value } });
await refreshNuxtData(FetchKeys.Libraries.All); await refreshNuxtData(FetchKeys.Libraries.All);
} }
}; };