1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-25 15:59:38 +02:00

feat: add Immich integration view and API documentation, enhance error handling, and include SVG asset

This commit is contained in:
Sean Morley 2025-01-01 16:24:44 -05:00
parent 15fd15ba40
commit 67f6af8ca3
8 changed files with 209 additions and 35 deletions

View file

@ -13,7 +13,7 @@
import { addToast } from '$lib/toasts';
import { deserialize } from '$app/forms';
import { t } from 'svelte-i18n';
import ImmichLogo from '$lib/assets/immich.svg';
export let longitude: number | null = null;
export let latitude: number | null = null;
export let collection: Collection | null = null;
@ -180,31 +180,74 @@
}
async function fetchImage() {
let res = await fetch(url);
let data = await res.blob();
if (!data) {
imageError = $t('adventures.no_image_url');
return;
}
let file = new File([data], 'image.jpg', { type: 'image/jpeg' });
let formData = new FormData();
formData.append('image', file);
formData.append('adventure', adventure.id);
let res2 = await fetch(`/adventures?/image`, {
method: 'POST',
body: formData
});
let data2 = await res2.json();
console.log(data2);
if (data2.type === 'success') {
images = [...images, data2];
adventure.images = images;
addToast('success', $t('adventures.image_upload_success'));
} else {
try {
let res = await fetch(url);
let data = await res.blob();
if (!data) {
imageError = $t('adventures.no_image_url');
return;
}
let file = new File([data], 'image.jpg', { type: 'image/jpeg' });
let formData = new FormData();
formData.append('image', file);
formData.append('adventure', adventure.id);
let res2 = await fetch(`/adventures?/image`, {
method: 'POST',
body: formData
});
let data2 = await res2.json();
if (data2.type === 'success') {
console.log('Response Data:', data2);
// Deserialize the nested data
let rawData = JSON.parse(data2.data); // Parse the data field
console.log('Deserialized Data:', rawData);
// Assuming the first object in the array is the new image
let newImage = {
id: rawData[0].id,
image: rawData[2] // This is the URL for the image
};
console.log('New Image:', newImage);
// Update images and adventure
images = [...images, newImage];
adventure.images = images;
addToast('success', $t('adventures.image_upload_success'));
} else {
addToast('error', $t('adventures.image_upload_error'));
}
} catch (error) {
console.error('Error in fetchImage:', error);
addToast('error', $t('adventures.image_upload_error'));
}
}
let immichSearchValue: string = '';
let immichError: string = '';
async function searchImmich() {
let res = await fetch(`/api/integrations/immich/search/?query=${immichSearchValue}`);
if (!res.ok) {
let data = await res.json();
let errorMessage = data.message;
console.log(errorMessage);
immichError = $t(data.code);
} else {
let data = await res.json();
console.log(data);
immichError = '';
if (data.results && data.results.length > 0) {
immichImages = data.results;
} else {
immichError = $t('immich.no_items_found');
}
}
}
async function fetchWikiImage() {
let res = await fetch(`/api/generate/img/?name=${imageSearch}`);
let data = await res.json();
@ -337,6 +380,9 @@
const dispatch = createEventDispatcher();
let modal: HTMLDialogElement;
let immichIntegration: boolean = false;
let immichImages: any[] = [];
onMount(async () => {
modal = document.getElementById('my_modal_1') as HTMLDialogElement;
modal.showModal();
@ -347,6 +393,16 @@
} else {
addToast('error', $t('adventures.category_fetch_error'));
}
// Check for Immich Integration
let res = await fetch('/api/integrations');
if (!res.ok) {
addToast('error', $t('immich.integration_fetch_error'));
} else {
let data = await res.json();
if (data.immich) {
immichIntegration = true;
}
}
});
function close() {
@ -915,10 +971,10 @@ it would also work to just use on:click on the MapLibre component itself. -->
</form>
</div>
{:else}
<p class="text-lg text-gray-600">{$t('adventures.upload_images_here')}</p>
<p class="text-lg">{$t('adventures.upload_images_here')}</p>
<div class="mb-4">
<label for="image" class="block font-medium text-gray-700 mb-2">
<label for="image" class="block font-medium mb-2">
{$t('adventures.image')}
</label>
<form
@ -944,7 +1000,7 @@ it would also work to just use on:click on the MapLibre component itself. -->
</div>
<div class="mb-4">
<label for="url" class="block font-medium text-gray-700 mb-2">
<label for="url" class="block font-medium mb-2">
{$t('adventures.url')}
</label>
<div class="flex gap-2">
@ -963,7 +1019,7 @@ it would also work to just use on:click on the MapLibre component itself. -->
</div>
<div class="mb-4">
<label for="name" class="block font-medium text-gray-700 mb-2">
<label for="name" class="block font-medium mb-2">
{$t('adventures.wikipedia')}
</label>
<div class="flex gap-2">
@ -981,6 +1037,49 @@ it would also work to just use on:click on the MapLibre component itself. -->
</div>
</div>
{#if immichIntegration}
<div class="mb-4">
<label for="immich" class="block font-medium mb-2">
{$t('immich.immich')}
<img src={ImmichLogo} alt="Immich Logo" class="h-6 w-6 inline-block -mt-1" />
</label>
<!-- search bar -->
<div>
<input
type="text"
placeholder="Type here"
bind:value={immichSearchValue}
class="input input-bordered w-full max-w-xs"
/>
<button on:click={searchImmich} class="btn btn-neutral mt-2">Search</button>
</div>
<p class="text-red-500">{immichError}</p>
<div class="flex flex-wrap gap-4 mr-4 mt-2">
{#each immichImages as image}
<div class="flex flex-col items-center gap-2">
<img
src={`/immich/${image.id}`}
alt="Image from Immich"
class="h-24 w-24 object-cover rounded-md"
/>
<button
type="button"
class="btn btn-sm btn-primary"
on:click={() => {
let currentDomain = window.location.origin;
let fullUrl = `${currentDomain}/immich/${image.id}`;
url = fullUrl;
fetchImage();
}}
>
{$t('adventures.upload_image')}
</button>
</div>
{/each}
</div>
</div>
{/if}
<div class="divider"></div>
{#if images.length > 0}