resolve merge conflict
@@ -86,6 +86,8 @@ export default {
|
||||
|
||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||
moduleNameMapper: {
|
||||
'\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||
'identity-obj-proxy',
|
||||
'^\\$lib(.*)$': '<rootDir>/src/lib$1',
|
||||
'^\\@api(.*)$': '<rootDir>/src/api$1',
|
||||
'^\\@test-data(.*)$': '<rootDir>/src/test-data$1'
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
"lodash-es": "^4.17.21",
|
||||
"luxon": "^3.1.1",
|
||||
"socket.io-client": "^4.5.1",
|
||||
"svelte-keydown": "^0.5.0",
|
||||
"svelte-material-icons": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -45,6 +44,7 @@
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"factory.ts": "^1.2.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.0.2",
|
||||
"jest-environment-jsdom": "^29.0.2",
|
||||
"postcss": "^8.4.13",
|
||||
@@ -53,7 +53,6 @@
|
||||
"svelte": "^3.44.0",
|
||||
"svelte-check": "^2.7.1",
|
||||
"svelte-jester": "^2.3.2",
|
||||
"svelte-keydown": "^0.5.0",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"tailwindcss": "^3.0.24",
|
||||
"tslib": "^2.3.1",
|
||||
@@ -6202,6 +6201,12 @@
|
||||
"uglify-js": "^3.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/harmony-reflect": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
|
||||
"integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
@@ -6337,6 +6342,18 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/identity-obj-proxy": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
|
||||
"integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"harmony-reflect": "^1.4.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
|
||||
@@ -10573,12 +10590,6 @@
|
||||
"svelte": ">= 3"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-keydown": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-keydown/-/svelte-keydown-0.5.0.tgz",
|
||||
"integrity": "sha512-DgY6AYlKbBocSvjC3kUeNPcStJQOTOCxAGG9ymVHzJdsQ1hRJuB8pcnB4UFH8uH3bAPdYyXXa3LwenLDL41eqQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/svelte-material-icons": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/svelte-material-icons/-/svelte-material-icons-2.0.4.tgz",
|
||||
@@ -15822,6 +15833,12 @@
|
||||
"wordwrap": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"harmony-reflect": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
|
||||
"integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==",
|
||||
"dev": true
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
@@ -15918,6 +15935,15 @@
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
}
|
||||
},
|
||||
"identity-obj-proxy": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
|
||||
"integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"harmony-reflect": "^1.4.6"
|
||||
}
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
|
||||
@@ -18997,12 +19023,6 @@
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"svelte-keydown": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte-keydown/-/svelte-keydown-0.5.0.tgz",
|
||||
"integrity": "sha512-DgY6AYlKbBocSvjC3kUeNPcStJQOTOCxAGG9ymVHzJdsQ1hRJuB8pcnB4UFH8uH3bAPdYyXXa3LwenLDL41eqQ==",
|
||||
"dev": true
|
||||
},
|
||||
"svelte-material-icons": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/svelte-material-icons/-/svelte-material-icons-2.0.4.tgz",
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"factory.ts": "^1.2.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.0.2",
|
||||
"jest-environment-jsdom": "^29.0.2",
|
||||
"postcss": "^8.4.13",
|
||||
@@ -51,7 +52,6 @@
|
||||
"svelte": "^3.44.0",
|
||||
"svelte-check": "^2.7.1",
|
||||
"svelte-jester": "^2.3.2",
|
||||
"svelte-keydown": "^0.5.0",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"tailwindcss": "^3.0.24",
|
||||
"tslib": "^2.3.1",
|
||||
@@ -69,7 +69,6 @@
|
||||
"lodash-es": "^4.17.21",
|
||||
"luxon": "^3.1.1",
|
||||
"socket.io-client": "^4.5.1",
|
||||
"svelte-keydown": "^0.5.0",
|
||||
"svelte-material-icons": "^2.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
src: url('/fonts/WorkSans-VariableFont_wght.ttf') format('truetype-variations');
|
||||
src: url('$lib/assets/fonts/WorkSans-VariableFont_wght.ttf') format('truetype-variations');
|
||||
font-weight: 1 999;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Snowburst One';
|
||||
src: url('/fonts/SnowburstOne-Regular.ttf') format('truetype');
|
||||
src: url('$lib/assets/fonts/SnowburstOne-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
:root {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 584 B |
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type Icon from 'svelte-material-icons/AbTesting.svelte';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export let logo: any;
|
||||
export let logo: typeof Icon;
|
||||
export let title: string;
|
||||
export let value: string;
|
||||
export let unit: string | undefined = undefined;
|
||||
@@ -13,13 +13,10 @@
|
||||
}
|
||||
|
||||
const maxLength = 13;
|
||||
let result = '';
|
||||
const valueLength = parseInt(value).toString().length;
|
||||
const zeroLength = maxLength - valueLength;
|
||||
for (let i = 0; i < zeroLength; i++) {
|
||||
result += '0';
|
||||
}
|
||||
return result;
|
||||
|
||||
return '0'.repeat(zeroLength);
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -44,10 +44,10 @@ describe('AlbumCard component', () => {
|
||||
const albumDetailsElement = sut.getByTestId('album-details');
|
||||
const detailsText = `${count} items` + (shared ? ' . Shared' : '');
|
||||
|
||||
expect(albumImgElement).toHaveAttribute('src', 'no-thumbnail.png');
|
||||
expect(albumImgElement).toHaveAttribute('src');
|
||||
expect(albumImgElement).toHaveAttribute('alt', album.id);
|
||||
|
||||
await waitFor(() => expect(albumImgElement).toHaveAttribute('src', 'no-thumbnail.png'));
|
||||
await waitFor(() => expect(albumImgElement).toHaveAttribute('src'));
|
||||
|
||||
expect(albumImgElement).toHaveAttribute('alt', album.id);
|
||||
expect(apiMock.assetApi.getAssetThumbnail).not.toHaveBeenCalled();
|
||||
@@ -108,7 +108,7 @@ describe('AlbumCard component', () => {
|
||||
sut = render(AlbumCard, { album });
|
||||
|
||||
const albumImgElement = sut.getByTestId('album-image');
|
||||
await waitFor(() => expect(albumImgElement).toHaveAttribute('src', 'no-thumbnail.png'));
|
||||
await waitFor(() => expect(albumImgElement).toHaveAttribute('src'));
|
||||
});
|
||||
|
||||
it('dispatches custom "click" event with the album in context', async () => {
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
|
||||
import CircleIconButton from '../shared-components/circle-icon-button.svelte';
|
||||
import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
|
||||
|
||||
export let album: AlbumResponseDto;
|
||||
|
||||
const NO_THUMBNAIL = 'no-thumbnail.png';
|
||||
|
||||
let imageData = `/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=${ThumbnailFormat.Webp}`;
|
||||
if (!album.albumThumbnailAssetId) {
|
||||
imageData = NO_THUMBNAIL;
|
||||
imageData = noThumbnailUrl;
|
||||
}
|
||||
|
||||
const dispatchClick = createEventDispatcher<OnClick>();
|
||||
@@ -51,7 +50,7 @@
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || NO_THUMBNAIL;
|
||||
imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;
|
||||
});
|
||||
|
||||
const locale = navigator.language;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||
import { bulkDownload } from '$lib/utils/asset-utils';
|
||||
import GalleryViewer from '../shared-components/gallery-viewer/gallery-viewer.svelte';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
|
||||
export let album: AlbumResponseDto;
|
||||
export let sharedLink: SharedLinkResponseDto | undefined = undefined;
|
||||
@@ -329,12 +330,8 @@
|
||||
}
|
||||
};
|
||||
|
||||
const showAlbumOptionsMenu = (event: CustomEvent) => {
|
||||
contextMenuPosition = {
|
||||
x: event.detail.mouseEvent.x,
|
||||
y: event.detail.mouseEvent.y
|
||||
};
|
||||
|
||||
const showAlbumOptionsMenu = ({ x, y }: MouseEvent) => {
|
||||
contextMenuPosition = { x, y };
|
||||
isShowAlbumOptions = !isShowAlbumOptions;
|
||||
};
|
||||
|
||||
@@ -419,13 +416,7 @@
|
||||
class="flex gap-2 place-items-center hover:cursor-pointer ml-6"
|
||||
href="https://immich.app"
|
||||
>
|
||||
<img
|
||||
src="/immich-logo.svg"
|
||||
alt="immich logo"
|
||||
height="30"
|
||||
width="30"
|
||||
draggable="false"
|
||||
/>
|
||||
<ImmichLogo height={30} width={30} />
|
||||
<h1 class="font-immich-title text-lg text-immich-primary dark:text-immich-dark-primary">
|
||||
IMMICH
|
||||
</h1>
|
||||
@@ -472,7 +463,7 @@
|
||||
{#if !isPublicShared}
|
||||
<CircleIconButton
|
||||
title="Album options"
|
||||
on:click={(event) => showAlbumOptionsMenu(event)}
|
||||
on:click={showAlbumOptionsMenu}
|
||||
logo={DotsVertical}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import Link from 'svelte-material-icons/Link.svelte';
|
||||
import ShareCircle from 'svelte-material-icons/ShareCircle.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
|
||||
export let album: AlbumResponseDto;
|
||||
export let sharedUsersInAlbum: Set<UserResponseDto>;
|
||||
@@ -53,7 +54,7 @@
|
||||
<BaseModal on:close={() => dispatch('close')}>
|
||||
<svelte:fragment slot="title">
|
||||
<span class="flex gap-2 place-items-center">
|
||||
<img src="/immich-logo.svg" width="24" alt="Immich" draggable="false" />
|
||||
<ImmichLogo width={24} />
|
||||
<p class="font-medium">Invite to album</p>
|
||||
</span>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -31,12 +31,8 @@
|
||||
let contextMenuPosition = { x: 0, y: 0 };
|
||||
let isShowAssetOptions = false;
|
||||
|
||||
const showOptionsMenu = (event: CustomEvent) => {
|
||||
contextMenuPosition = {
|
||||
x: event.detail.mouseEvent.x,
|
||||
y: event.detail.mouseEvent.y
|
||||
};
|
||||
|
||||
const showOptionsMenu = ({ x, y }: MouseEvent) => {
|
||||
contextMenuPosition = { x, y };
|
||||
isShowAssetOptions = !isShowAssetOptions;
|
||||
};
|
||||
|
||||
@@ -101,11 +97,7 @@
|
||||
|
||||
{#if isOwner}
|
||||
<CircleIconButton logo={DeleteOutline} on:click={() => dispatch('delete')} title="Delete" />
|
||||
<CircleIconButton
|
||||
logo={DotsVertical}
|
||||
on:click={(event) => showOptionsMenu(event)}
|
||||
title="More"
|
||||
/>
|
||||
<CircleIconButton logo={DotsVertical} on:click={showOptionsMenu} title="More" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -304,7 +304,7 @@
|
||||
on:onVideoEnded={() => (shouldPlayMotionPhoto = false)}
|
||||
/>
|
||||
{:else}
|
||||
<PhotoViewer {publicSharedKey} assetId={asset.id} on:close={closeViewer} />
|
||||
<PhotoViewer {publicSharedKey} {asset} on:close={closeViewer} />
|
||||
{/if}
|
||||
{:else}
|
||||
<VideoViewer {publicSharedKey} assetId={asset.id} on:close={closeViewer} />
|
||||
|
||||
@@ -1,39 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { api, AssetResponseDto } from '@api';
|
||||
import Keydown from 'svelte-keydown';
|
||||
import { copyImageToClipboard } from 'copy-image-clipboard';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType
|
||||
} from '../shared-components/notification/notification';
|
||||
|
||||
export let assetId: string;
|
||||
export let asset: AssetResponseDto;
|
||||
export let publicSharedKey = '';
|
||||
|
||||
let assetInfo: AssetResponseDto;
|
||||
let assetData: string;
|
||||
|
||||
let copyImageToClipboard: (src: string) => Promise<Blob>;
|
||||
|
||||
onMount(async () => {
|
||||
const { data } = await api.assetApi.getAssetById(assetId, {
|
||||
params: {
|
||||
key: publicSharedKey
|
||||
}
|
||||
});
|
||||
assetInfo = data;
|
||||
|
||||
//Import hack :( see https://github.com/vadimkorr/svelte-carousel/issues/27#issuecomment-851022295
|
||||
const module = await import('copy-image-clipboard');
|
||||
copyImageToClipboard = module.copyImageToClipboard;
|
||||
});
|
||||
|
||||
const loadAssetData = async () => {
|
||||
try {
|
||||
const { data } = await api.assetApi.serveFile(assetInfo.id, false, true, {
|
||||
const { data } = await api.assetApi.serveFile(asset.id, false, true, {
|
||||
params: {
|
||||
key: publicSharedKey
|
||||
},
|
||||
@@ -51,42 +33,51 @@
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeypress = async (keyEvent: CustomEvent<string>) => {
|
||||
if (keyEvent.detail == 'Control-c' || keyEvent.detail == 'Meta-c') {
|
||||
const handleKeypress = async ({ metaKey, ctrlKey, key }: KeyboardEvent) => {
|
||||
if ((metaKey || ctrlKey) && key === 'c') {
|
||||
await doCopy();
|
||||
}
|
||||
};
|
||||
|
||||
export const doCopy = async () => {
|
||||
await copyImageToClipboard(assetData);
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
message: 'Copied image to clipboard.',
|
||||
timeout: 3000
|
||||
});
|
||||
try {
|
||||
await copyImageToClipboard(assetData);
|
||||
notificationController.show({
|
||||
type: NotificationType.Info,
|
||||
message: 'Copied image to clipboard.',
|
||||
timeout: 3000
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
notificationController.show({
|
||||
type: NotificationType.Error,
|
||||
message: 'Copying image to clipboard failed. Click here to learn more.',
|
||||
timeout: 5000,
|
||||
action: {
|
||||
type: 'link',
|
||||
target:
|
||||
'https://github.com/LuanEdCosta/copy-image-clipboard#enable-clipboard-api-features-in-firefox'
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<Keydown on:combo={handleKeypress} />
|
||||
|
||||
<svelte:window on:copyImage={async () => await doCopy()} />
|
||||
<svelte:window on:keydown={handleKeypress} on:copyImage={doCopy} />
|
||||
|
||||
<div
|
||||
transition:fade={{ duration: 150 }}
|
||||
class="flex place-items-center place-content-center h-full select-none"
|
||||
>
|
||||
{#if assetInfo}
|
||||
{#await loadAssetData()}
|
||||
<LoadingSpinner />
|
||||
{:then assetData}
|
||||
<img
|
||||
transition:fade={{ duration: 150 }}
|
||||
src={assetData}
|
||||
alt={assetId}
|
||||
class="object-contain h-full transition-all"
|
||||
loading="lazy"
|
||||
draggable="false"
|
||||
/>
|
||||
{/await}
|
||||
{/if}
|
||||
{#await loadAssetData()}
|
||||
<LoadingSpinner />
|
||||
{:then assetData}
|
||||
<img
|
||||
transition:fade={{ duration: 150 }}
|
||||
src={assetData}
|
||||
alt={asset.id}
|
||||
class="object-contain h-full transition-all"
|
||||
draggable="false"
|
||||
/>
|
||||
{/await}
|
||||
</div>
|
||||
|
||||
@@ -1,40 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
|
||||
import { api, AssetResponseDto, getFileUrl } from '@api';
|
||||
import { getFileUrl } from '@api';
|
||||
|
||||
export let assetId: string;
|
||||
export let publicSharedKey = '';
|
||||
let asset: AssetResponseDto;
|
||||
|
||||
let isVideoLoading = true;
|
||||
let videoUrl: string;
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
onMount(async () => {
|
||||
const { data: assetInfo } = await api.assetApi.getAssetById(assetId, {
|
||||
params: {
|
||||
key: publicSharedKey
|
||||
}
|
||||
});
|
||||
|
||||
await loadVideoData(assetInfo);
|
||||
|
||||
asset = assetInfo;
|
||||
});
|
||||
|
||||
const loadVideoData = async (assetInfo: AssetResponseDto) => {
|
||||
isVideoLoading = true;
|
||||
|
||||
videoUrl = getFileUrl(assetInfo.id, false, true, publicSharedKey);
|
||||
|
||||
return assetInfo;
|
||||
};
|
||||
|
||||
const handleCanPlay = (ev: Event) => {
|
||||
const playerNode = ev.target as HTMLVideoElement;
|
||||
const handleCanPlay = (ev: Event & { currentTarget: HTMLVideoElement }) => {
|
||||
const playerNode = ev.currentTarget;
|
||||
|
||||
playerNode.muted = true;
|
||||
playerNode.play();
|
||||
@@ -48,21 +25,19 @@
|
||||
transition:fade={{ duration: 150 }}
|
||||
class="flex place-items-center place-content-center h-full select-none"
|
||||
>
|
||||
{#if asset}
|
||||
<video
|
||||
controls
|
||||
class="h-full object-contain"
|
||||
on:canplay={handleCanPlay}
|
||||
on:ended={() => dispatch('onVideoEnded')}
|
||||
>
|
||||
<source src={videoUrl} type="video/mp4" />
|
||||
<track kind="captions" />
|
||||
</video>
|
||||
<video
|
||||
controls
|
||||
class="h-full object-contain"
|
||||
src={getFileUrl(assetId, false, true, publicSharedKey)}
|
||||
on:canplay={handleCanPlay}
|
||||
on:ended={() => dispatch('onVideoEnded')}
|
||||
>
|
||||
<track kind="captions" />
|
||||
</video>
|
||||
|
||||
{#if isVideoLoading}
|
||||
<div class="absolute flex place-items-center place-content-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
{/if}
|
||||
{#if isVideoLoading}
|
||||
<div class="absolute flex place-items-center place-content-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
import { api } from '@api';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
|
||||
let error: string;
|
||||
let success: string;
|
||||
|
||||
@@ -55,14 +56,7 @@
|
||||
class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
|
||||
>
|
||||
<div class="flex flex-col place-items-center place-content-center gap-4 px-4">
|
||||
<img
|
||||
class="text-center"
|
||||
src="/immich-logo.svg"
|
||||
height="100"
|
||||
width="100"
|
||||
alt="immich-logo"
|
||||
draggable="false"
|
||||
/>
|
||||
<ImmichLogo class="text-center" height="100" width="100" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
Admin Registration
|
||||
</h1>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { api, UserResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
|
||||
export let user: UserResponseDto;
|
||||
let error: string;
|
||||
@@ -47,14 +48,7 @@
|
||||
class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
|
||||
>
|
||||
<div class="flex flex-col place-items-center place-content-center gap-4 px-4">
|
||||
<img
|
||||
class="text-center"
|
||||
src="/immich-logo.svg"
|
||||
height="100"
|
||||
width="100"
|
||||
alt="immich-logo"
|
||||
draggable="false"
|
||||
/>
|
||||
<ImmichLogo class="text-center" height="100" width="100" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
Change Password
|
||||
</h1>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { api } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType
|
||||
@@ -80,14 +81,7 @@
|
||||
class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
|
||||
>
|
||||
<div class="flex flex-col place-items-center place-content-center gap-4 px-4">
|
||||
<img
|
||||
class="text-center"
|
||||
src="/immich-logo.svg"
|
||||
height="100"
|
||||
width="100"
|
||||
alt="immich-logo"
|
||||
draggable="false"
|
||||
/>
|
||||
<ImmichLogo class="text-center" height="100" width="100" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
|
||||
Create new user
|
||||
</h1>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { api, oauth, OAuthConfigResponseDto } from '@api';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
|
||||
let error: string;
|
||||
let email = '';
|
||||
@@ -77,14 +78,7 @@
|
||||
class="border bg-white dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-md py-8"
|
||||
>
|
||||
<div class="flex flex-col place-items-center place-content-center gap-4 px-4">
|
||||
<img
|
||||
class="text-center"
|
||||
src="/immich-logo.svg"
|
||||
height="100"
|
||||
width="100"
|
||||
alt="immich-logo"
|
||||
draggable="false"
|
||||
/>
|
||||
<ImmichLogo class="text-center" height="100" width="100" />
|
||||
<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">Login</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
notificationController,
|
||||
NotificationType
|
||||
} from '../shared-components/notification/notification';
|
||||
import ImmichLogo from '../shared-components/immich-logo.svelte';
|
||||
|
||||
export let sharedLink: SharedLinkResponseDto;
|
||||
export let isOwned: boolean;
|
||||
@@ -122,7 +123,7 @@
|
||||
class="flex gap-2 place-items-center hover:cursor-pointer ml-6"
|
||||
href="https://immich.app"
|
||||
>
|
||||
<img src="/immich-logo.svg" alt="immich logo" height="30" width="30" draggable="false" />
|
||||
<ImmichLogo height="30" width="30" />
|
||||
<h1 class="font-immich-title text-lg text-immich-primary dark:text-immich-dark-primary">
|
||||
IMMICH
|
||||
</h1>
|
||||
|
||||
@@ -2,31 +2,21 @@
|
||||
/**
|
||||
* This is the circle icon component.
|
||||
*/
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import type Icon from 'svelte-material-icons/AbTesting.svelte';
|
||||
|
||||
// TODO: why any here?
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export let logo: any;
|
||||
export let logo: typeof Icon;
|
||||
export let backgroundColor = 'transparent';
|
||||
export let hoverColor = '#e2e7e9';
|
||||
export let size = '24';
|
||||
export let title = '';
|
||||
let iconButton: HTMLButtonElement;
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
$: {
|
||||
if (iconButton) {
|
||||
iconButton.style.backgroundColor = backgroundColor;
|
||||
iconButton.style.setProperty('--immich-icon-button-hover-color', hoverColor);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
{title}
|
||||
bind:this={iconButton}
|
||||
style:backgroundColor
|
||||
style:--immich-icon-button-hover-color={hoverColor}
|
||||
class={`immich-circle-icon-button dark:text-immich-dark-fg hover:dark:text-immich-dark-gray rounded-full p-3 flex place-items-center place-content-center transition-all`}
|
||||
on:click={(mouseEvent) => dispatch('click', { mouseEvent })}
|
||||
on:click
|
||||
>
|
||||
<svelte:component this={logo} {size} />
|
||||
</button>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
import ImmichLogo from './immich-logo.svelte';
|
||||
|
||||
export let dropHandler: (event: DragEvent) => void;
|
||||
export let dragOverHandler: (event: DragEvent) => void;
|
||||
@@ -14,13 +15,6 @@
|
||||
on:dragleave={dragLeaveHandler}
|
||||
class="fixed inset-0 w-full h-full z-[1000] flex flex-col items-center justify-center bg-gray-100/90 dark:bg-immich-dark-bg/90 text-immich-dark-gray dark:text-immich-gray"
|
||||
>
|
||||
<img
|
||||
src="/immich-logo.svg"
|
||||
alt="immich logo"
|
||||
height="200"
|
||||
width="200"
|
||||
class="animate-bounce pb-16"
|
||||
draggable="false"
|
||||
/>
|
||||
<ImmichLogo height="200" width="200" class="animate-bounce pb-16" />
|
||||
<div class="text-2xl">Drop files anywhere to upload</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import immichLogoUrl from '$lib/assets/immich-logo.svg';
|
||||
|
||||
export let draggable = false;
|
||||
</script>
|
||||
|
||||
<img src={immichLogoUrl} alt="Immich Logo" {draggable} {...$$restProps} />
|
||||
@@ -8,6 +8,7 @@
|
||||
import ThemeButton from '../theme-button.svelte';
|
||||
import { AppRoute } from '../../../constants';
|
||||
import AccountInfoPanel from './account-info-panel.svelte';
|
||||
import ImmichLogo from '../immich-logo.svelte';
|
||||
export let user: UserResponseDto;
|
||||
export let shouldShowUploadButton = true;
|
||||
|
||||
@@ -50,7 +51,7 @@
|
||||
class="flex gap-2 place-items-center hover:cursor-pointer"
|
||||
href="/photos"
|
||||
>
|
||||
<img src="/immich-logo.svg" alt="immich logo" height="35" width="35" draggable="false" />
|
||||
<ImmichLogo height="35" width="35" />
|
||||
<h1 class="font-immich-title text-2xl text-immich-primary dark:text-immich-dark-primary">
|
||||
IMMICH
|
||||
</h1>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type Icon from 'svelte-material-icons/AbTesting.svelte';
|
||||
|
||||
export let title: string;
|
||||
// TODO: why `any` here? There should be a expected type for this
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export let logo: any;
|
||||
export let logo: typeof Icon;
|
||||
export let isSelected: boolean;
|
||||
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { fade } from 'svelte/transition';
|
||||
import { asByteUnitString } from '$lib/utils/byte-units';
|
||||
import { UploadAsset } from '$lib/models/upload-asset';
|
||||
import ImmichLogo from './immich-logo.svelte';
|
||||
|
||||
export let uploadAsset: UploadAsset;
|
||||
|
||||
@@ -16,13 +17,9 @@
|
||||
>
|
||||
<div class="relative">
|
||||
{#if showFallbackImage}
|
||||
<img
|
||||
in:fade={{ duration: 250 }}
|
||||
src="immich-logo.svg"
|
||||
alt="Immich Logo"
|
||||
class="h-[70px] w-[70px] object-cover rounded-tl-lg rounded-bl-lg"
|
||||
draggable="false"
|
||||
/>
|
||||
<div in:fade={{ duration: 250 }}>
|
||||
<ImmichLogo class="h-[70px] w-[70px] object-cover rounded-tl-lg rounded-bl-lg" />
|
||||
</div>
|
||||
{:else}
|
||||
<img
|
||||
in:fade={{ duration: 250 }}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { AlbumResponseDto, api, ThumbnailFormat, UserResponseDto } from '@api';
|
||||
import { fade } from 'svelte/transition';
|
||||
import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
|
||||
|
||||
export let album: AlbumResponseDto;
|
||||
export let user: UserResponseDto;
|
||||
|
||||
const loadImageData = async (thubmnailId: string | null) => {
|
||||
if (thubmnailId == null) {
|
||||
return '/no-thumbnail.png';
|
||||
return noThumbnailUrl;
|
||||
}
|
||||
|
||||
const { data } = await api.assetApi.getAssetThumbnail(thubmnailId, ThumbnailFormat.Webp, {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
NotificationType
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
|
||||
|
||||
const handleCopy = async () => {
|
||||
//
|
||||
@@ -33,7 +34,7 @@
|
||||
<section class="bg-immich-bg dark:bg-immich-dark-bg">
|
||||
<div class="flex border-b dark:border-b-immich-dark-gray place-items-center px-6 py-4">
|
||||
<a class="flex gap-2 place-items-center hover:cursor-pointer" href="/photos">
|
||||
<img src="/immich-logo.svg" alt="immich logo" height="35" width="35" draggable="false" />
|
||||
<ImmichLogo height="35" width="35" />
|
||||
<h1 class="font-immich-title text-2xl text-immich-primary dark:text-immich-dark-primary">
|
||||
IMMICH
|
||||
</h1>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import NavigationLoadingBar from '$lib/components/shared-components/navigation-loading-bar.svelte';
|
||||
import NotificationList from '$lib/components/shared-components/notification/notification-list.svelte';
|
||||
import { fileUploadHandler } from '$lib/utils/file-uploader';
|
||||
import faviconUrl from '$lib/assets/favicon.png';
|
||||
|
||||
let shouldShowAnnouncement: boolean;
|
||||
let localVersion: string;
|
||||
@@ -80,6 +81,7 @@
|
||||
<svelte:head>
|
||||
<title>{$page.data.meta?.title || 'Web'} - Immich</title>
|
||||
{#if $page.data.meta}
|
||||
<link rel="icon" href={faviconUrl} />
|
||||
<meta name="description" content={$page.data.meta.description} />
|
||||
|
||||
<!-- Facebook Meta Tags -->
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
|
||||
</script>
|
||||
|
||||
<section class="h-screen w-screen flex place-items-center place-content-center">
|
||||
<div class="flex flex-col place-items-center gap-8 text-center max-w-[350px]">
|
||||
<div class="flex place-items-center place-content-center ">
|
||||
<img
|
||||
class="text-center"
|
||||
src="immich-logo.svg"
|
||||
height="200"
|
||||
width="200"
|
||||
alt="immich-logo"
|
||||
draggable="false"
|
||||
/>
|
||||
<ImmichLogo class="text-center" height="200" width="200" />
|
||||
</div>
|
||||
<h1
|
||||
class="text-4xl text-immich-primary dark:text-immich-dark-primary font-bold font-immich-title"
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
|
||||
import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
|
||||
import { useAlbums } from './albums.bloc';
|
||||
import empty1Url from '$lib/assets/empty-1.svg';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -93,7 +94,7 @@
|
||||
on:keydown={handleCreateAlbum}
|
||||
class="border dark:border-immich-dark-gray hover:bg-immich-primary/5 dark:hover:bg-immich-dark-primary/25 hover:cursor-pointer p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center"
|
||||
>
|
||||
<img src="/empty-1.svg" alt="Empty shared album" width="500" draggable="false" />
|
||||
<img src={empty1Url} alt="Empty shared album" width="500" draggable="false" />
|
||||
|
||||
<p class="text-center text-immich-text-gray-500 dark:text-immich-dark-fg">
|
||||
Create an album to organize your photos and videos
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import StarMinusOutline from 'svelte-material-icons/StarMinusOutline.svelte';
|
||||
import Error from '../+error.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import empty1Url from '$lib/assets/empty-1.svg';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -126,7 +127,7 @@
|
||||
<div
|
||||
class="border dark:border-immich-dark-gray hover:bg-immich-primary/5 dark:hover:bg-immich-dark-primary/25 hover:cursor-pointer p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center"
|
||||
>
|
||||
<img src="/empty-1.svg" alt="Empty shared album" width="500" draggable="false" />
|
||||
<img src={empty1Url} alt="Empty shared album" width="500" draggable="false" />
|
||||
|
||||
<p class="text-center text-immich-text-gray-500 dark:text-immich-dark-fg">
|
||||
Add favorites to quickly find your best pictures and videos
|
||||
|
||||
@@ -64,12 +64,8 @@
|
||||
let isShowAlbumPicker = false;
|
||||
let addToSharedAlbum = false;
|
||||
|
||||
const handleShowMenu = (event: CustomEvent) => {
|
||||
contextMenuPosition = {
|
||||
x: event.detail.mouseEvent.x,
|
||||
y: event.detail.mouseEvent.y
|
||||
};
|
||||
|
||||
const handleShowMenu = ({ x, y }: MouseEvent) => {
|
||||
contextMenuPosition = { x, y };
|
||||
isShowAddMenu = !isShowAddMenu;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { error } from '@sveltejs/kit';
|
||||
import { getThumbnailUrl } from '$lib/utils/asset-utils';
|
||||
import { serverApi, ThumbnailFormat } from '@api';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import featurePanelUrl from '$lib/assets/feature-panel.png';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, parent }) => {
|
||||
const { user } = await parent();
|
||||
@@ -23,7 +24,7 @@ export const load: PageServerLoad = async ({ params, parent }) => {
|
||||
description: sharedLink.description || `${assetCount} shared photos & videos.`,
|
||||
imageUrl: assetId
|
||||
? getThumbnailUrl(assetId, ThumbnailFormat.Webp, sharedLink.key)
|
||||
: 'feature-panel.png'
|
||||
: featurePanelUrl
|
||||
},
|
||||
user
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
notificationController,
|
||||
NotificationType
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import empty2Url from '$lib/assets/empty-2.svg';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@@ -94,7 +95,7 @@
|
||||
<div
|
||||
class="border dark:border-immich-dark-gray p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center dark:text-immich-dark-fg"
|
||||
>
|
||||
<img src="/empty-2.svg" alt="Empty shared album" width="500" draggable="false" />
|
||||
<img src={empty2Url} alt="Empty shared album" width="500" draggable="false" />
|
||||
<p class="text-center text-immich-text-gray-500">
|
||||
Create a shared album to share photos and videos with people in your network
|
||||
</p>
|
||||
|
||||
|
Before Width: | Height: | Size: 352 KiB |
|
Before Width: | Height: | Size: 113 KiB |