mirror of
https://github.com/immich-app/immich.git
synced 2024-11-28 09:33:27 +02:00
feat: use <a> tag for albums in list view (#5645)
* fix: multiple improvements * pr feedback * optimize
This commit is contained in:
parent
fb4b4e5895
commit
fba9e784fb
@ -19,7 +19,7 @@
|
||||
import PhotoViewer from './photo-viewer.svelte';
|
||||
import VideoViewer from './video-viewer.svelte';
|
||||
import PanoramaViewer from './panorama-viewer.svelte';
|
||||
import { AssetAction, ProjectionType } from '$lib/constants';
|
||||
import { AppRoute, AssetAction, ProjectionType } from '$lib/constants';
|
||||
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
|
||||
import ProfileImageCropper from '../shared-components/profile-image-cropper.svelte';
|
||||
import { isShowDetail } from '$lib/stores/preferences.store';
|
||||
@ -430,7 +430,7 @@
|
||||
const { albumName }: { albumName: string } = event.detail;
|
||||
api.albumApi.createAlbum({ createAlbumDto: { albumName, assetIds: [asset.id] } }).then((response) => {
|
||||
const album = response.data;
|
||||
goto('/albums/' + album.id);
|
||||
goto(`${AppRoute.ALBUMS}/${album.id}`);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -238,7 +238,9 @@
|
||||
on:mouseleave={() => ($boundingBoxesArray = [])}
|
||||
>
|
||||
<a
|
||||
href="/people/{person.id}?previousRoute={albumId ? `${AppRoute.ALBUMS}/${albumId}` : AppRoute.PHOTOS}"
|
||||
href="{AppRoute.PEOPLE}/{person.id}?previousRoute={albumId
|
||||
? `${AppRoute.ALBUMS}/${albumId}`
|
||||
: AppRoute.PHOTOS}"
|
||||
on:click={() => dispatch('close-viewer')}
|
||||
>
|
||||
<div class="relative">
|
||||
|
@ -45,7 +45,7 @@
|
||||
on:mouseleave={() => (showVerticalDots = false)}
|
||||
role="group"
|
||||
>
|
||||
<a href="/people/{person.id}?previousRoute={AppRoute.PEOPLE}" draggable="false">
|
||||
<a href="{AppRoute.PEOPLE}/{person.id}?previousRoute={AppRoute.PEOPLE}" draggable="false">
|
||||
<div class="h-48 w-48 rounded-xl brightness-95 filter">
|
||||
<ImageThumbnail
|
||||
shadow
|
||||
|
@ -7,6 +7,7 @@
|
||||
import { handleError } from '../../utils/handle-error';
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { mdiAccountEditOutline } from '@mdi/js';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
|
||||
export let user: UserResponseDto;
|
||||
export let canResetPassword = true;
|
||||
@ -99,7 +100,7 @@
|
||||
|
||||
<p>
|
||||
Note: To apply the Storage Label to previously uploaded assets, run the
|
||||
<a href="/admin/jobs-status" class="text-immich-primary dark:text-immich-dark-primary">
|
||||
<a href={AppRoute.ADMIN_JOBS} class="text-immich-primary dark:text-immich-dark-primary">
|
||||
Storage Migration Job</a
|
||||
>
|
||||
</p>
|
||||
|
@ -10,6 +10,7 @@
|
||||
import { AlbumResponseDto, api } from '@api';
|
||||
import { getMenuContext } from '../asset-select-context-menu.svelte';
|
||||
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
|
||||
export let shared = false;
|
||||
let showAlbumPicker = false;
|
||||
@ -37,7 +38,7 @@
|
||||
|
||||
clearSelect();
|
||||
|
||||
goto('/albums/' + id);
|
||||
goto(`${AppRoute.ALBUMS}/${id}`);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
import { notificationController, NotificationType } from '../shared-components/notification/notification';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { mdiArrowLeft, mdiFileImagePlusOutline, mdiFolderDownloadOutline, mdiSelectAll } from '@mdi/js';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
|
||||
export let sharedLink: SharedLinkResponseDto;
|
||||
export let isOwned: boolean;
|
||||
@ -78,7 +79,7 @@
|
||||
{/if}
|
||||
</AssetSelectControlBar>
|
||||
{:else}
|
||||
<ControlAppBar on:close-button-click={() => goto('/photos')} backIcon={mdiArrowLeft} showBackButton={false}>
|
||||
<ControlAppBar on:close-button-click={() => goto(AppRoute.PHOTOS)} backIcon={mdiArrowLeft} showBackButton={false}>
|
||||
<svelte:fragment slot="leading">
|
||||
<a
|
||||
data-sveltekit-preload-data="hover"
|
||||
|
@ -30,6 +30,7 @@ export enum AppRoute {
|
||||
USER_SETTINGS = '/user-settings',
|
||||
MEMORY = '/memory',
|
||||
TRASH = '/trash',
|
||||
PARTNERS = '/partners',
|
||||
|
||||
AUTH_LOGIN = '/auth/login',
|
||||
AUTH_LOGOUT = '/auth/logout',
|
||||
|
@ -1,4 +1,12 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import { get, writable } from 'svelte/store';
|
||||
import type { UserResponseDto } from '@api';
|
||||
|
||||
export const user = writable<UserResponseDto | null>(null);
|
||||
|
||||
export const setUser = (value: UserResponseDto | null) => {
|
||||
user.set(value);
|
||||
};
|
||||
|
||||
export const getSavedUser = () => {
|
||||
return get(user);
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { api } from '@api';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { AppRoute } from '../constants';
|
||||
import { getSavedUser, setUser } from '$lib/stores/user.store';
|
||||
|
||||
export interface AuthOptions {
|
||||
admin?: true;
|
||||
@ -19,7 +20,9 @@ export const getAuthUser = async () => {
|
||||
export const authenticate = async (options?: AuthOptions) => {
|
||||
options = options || {};
|
||||
|
||||
const user = await getAuthUser();
|
||||
const savedUser = getSavedUser();
|
||||
const user = savedUser || (await getAuthUser());
|
||||
|
||||
if (!user) {
|
||||
throw redirect(302, AppRoute.AUTH_LOGIN);
|
||||
}
|
||||
@ -28,6 +31,10 @@ export const authenticate = async (options?: AuthOptions) => {
|
||||
throw redirect(302, AppRoute.PHOTOS);
|
||||
}
|
||||
|
||||
if (!savedUser) {
|
||||
setUser(user);
|
||||
}
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
import { flip } from 'svelte/animate';
|
||||
import Dropdown from '$lib/components/elements/dropdown.svelte';
|
||||
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
|
||||
import { dateFormats } from '$lib/constants';
|
||||
import { AppRoute, dateFormats } from '$lib/constants';
|
||||
import { locale, AlbumViewMode } from '$lib/stores/preferences.store';
|
||||
import {
|
||||
notificationController,
|
||||
@ -46,6 +46,7 @@
|
||||
} from '@mdi/js';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let shouldShowEditUserForm = false;
|
||||
let selectedAlbum: AlbumResponseDto;
|
||||
|
||||
@ -192,7 +193,7 @@
|
||||
const handleCreateAlbum = async () => {
|
||||
const newAlbum = await createAlbum();
|
||||
if (newAlbum) {
|
||||
goto('/albums/' + newAlbum.id);
|
||||
goto(`${AppRoute.ALBUMS}/${newAlbum.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
@ -289,7 +290,7 @@
|
||||
{#if $albumViewSettings.view === AlbumViewMode.Cover}
|
||||
<div class="grid grid-cols-[repeat(auto-fill,minmax(14rem,1fr))]">
|
||||
{#each $albums as album (album.id)}
|
||||
<a data-sveltekit-preload-data="hover" href={`albums/${album.id}`} animate:flip={{ duration: 200 }}>
|
||||
<a data-sveltekit-preload-data="hover" href="{AppRoute.ALBUMS}/{album.id}" animate:flip={{ duration: 200 }}>
|
||||
<AlbumCard
|
||||
{album}
|
||||
on:showalbumcontextmenu={(e) => showAlbumContextMenu(e.detail, album)}
|
||||
@ -316,47 +317,49 @@
|
||||
{#each $albums as album (album.id)}
|
||||
<tr
|
||||
class="flex h-[50px] w-full place-items-center border-[3px] border-transparent p-2 text-center odd:bg-immich-gray even:bg-immich-bg hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5"
|
||||
on:click={() => goto(`albums/${album.id}`)}
|
||||
on:keydown={(event) => event.key === 'Enter' && goto(`albums/${album.id}`)}
|
||||
on:click={() => goto(`${AppRoute.ALBUMS}/${album.id}`)}
|
||||
on:keydown={(event) => event.key === 'Enter' && goto(`${AppRoute.ALBUMS}/${album.id}`)}
|
||||
tabindex="0"
|
||||
>
|
||||
<td class="text-md text-ellipsis text-left w-8/12 sm:w-4/12 md:w-4/12 xl:w-[30%] 2xl:w-[40%]"
|
||||
>{album.albumName}</td
|
||||
>
|
||||
<td class="text-md text-ellipsis text-center sm:w-2/12 md:w-2/12 xl:w-[15%] 2xl:w-[12%]">
|
||||
{album.assetCount}
|
||||
{album.assetCount > 1 ? `items` : `item`}
|
||||
</td>
|
||||
<td class="text-md hidden text-ellipsis text-center sm:block w-3/12 xl:w-[15%] 2xl:w-[12%]"
|
||||
>{dateLocaleString(album.updatedAt)}
|
||||
</td>
|
||||
<td class="text-md hidden text-ellipsis text-center sm:block w-3/12 xl:w-[15%] 2xl:w-[12%]"
|
||||
>{dateLocaleString(album.createdAt)}</td
|
||||
>
|
||||
<td class="text-md text-ellipsis text-center hidden xl:block xl:w-[15%] 2xl:w-[12%]">
|
||||
{#if album.endDate}
|
||||
{dateLocaleString(album.endDate)}
|
||||
{:else}
|
||||
❌
|
||||
{/if}</td
|
||||
>
|
||||
<td class="text-md text-ellipsis text-center hidden xl:block xl:w-[15%] 2xl:w-[12%]"
|
||||
>{#if album.startDate}
|
||||
{dateLocaleString(album.startDate)}
|
||||
{:else}
|
||||
❌
|
||||
{/if}</td
|
||||
>
|
||||
<a data-sveltekit-preload-data="hover" class="flex w-full" href="{AppRoute.ALBUMS}/{album.id}">
|
||||
<td class="text-md text-ellipsis text-left w-8/12 sm:w-4/12 md:w-4/12 xl:w-[30%] 2xl:w-[40%]"
|
||||
>{album.albumName}</td
|
||||
>
|
||||
<td class="text-md text-ellipsis text-center sm:w-2/12 md:w-2/12 xl:w-[15%] 2xl:w-[12%]">
|
||||
{album.assetCount}
|
||||
{album.assetCount > 1 ? `items` : `item`}
|
||||
</td>
|
||||
<td class="text-md hidden text-ellipsis text-center sm:block w-3/12 xl:w-[15%] 2xl:w-[12%]"
|
||||
>{dateLocaleString(album.updatedAt)}
|
||||
</td>
|
||||
<td class="text-md hidden text-ellipsis text-center sm:block w-3/12 xl:w-[15%] 2xl:w-[12%]"
|
||||
>{dateLocaleString(album.createdAt)}</td
|
||||
>
|
||||
<td class="text-md text-ellipsis text-center hidden xl:block xl:w-[15%] 2xl:w-[12%]">
|
||||
{#if album.endDate}
|
||||
{dateLocaleString(album.endDate)}
|
||||
{:else}
|
||||
❌
|
||||
{/if}</td
|
||||
>
|
||||
<td class="text-md text-ellipsis text-center hidden xl:block xl:w-[15%] 2xl:w-[12%]"
|
||||
>{#if album.startDate}
|
||||
{dateLocaleString(album.startDate)}
|
||||
{:else}
|
||||
❌
|
||||
{/if}</td
|
||||
>
|
||||
</a>
|
||||
<td class="text-md hidden text-ellipsis text-center 2xl:block xl:w-[15%] 2xl:w-[12%]">
|
||||
<button
|
||||
on:click|stopPropagation={() => handleEdit(album)}
|
||||
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
|
||||
class="rounded-full z-1 bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
|
||||
>
|
||||
<Icon path={mdiPencilOutline} size="16" />
|
||||
</button>
|
||||
<button
|
||||
on:click|stopPropagation={() => chooseAlbumToDelete(album)}
|
||||
class="rounded-full bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
|
||||
class="rounded-full z-1 bg-immich-primary p-3 text-gray-100 transition-all duration-150 hover:bg-immich-primary/75 dark:bg-immich-dark-primary dark:text-gray-700"
|
||||
>
|
||||
<Icon path={mdiTrashCanOutline} size="16" />
|
||||
</button>
|
||||
|
@ -68,8 +68,6 @@
|
||||
|
||||
let album = data.album;
|
||||
|
||||
$user = data.user;
|
||||
|
||||
$: album = data.album;
|
||||
|
||||
$: {
|
||||
|
@ -50,7 +50,7 @@
|
||||
<div class="flex flex-row {MAX_ITEMS < 5 ? 'justify-center' : ''} flex-wrap gap-4" bind:offsetWidth={innerWidth}>
|
||||
{#if MAX_ITEMS}
|
||||
{#each people as person (person.id)}
|
||||
<a href="/people/{person.id}" class="w-20 md:w-24 text-center">
|
||||
<a href="{AppRoute.PEOPLE}/{person.id}" class="w-20 md:w-24 text-center">
|
||||
<ImageThumbnail
|
||||
circle
|
||||
shadow
|
||||
@ -73,7 +73,7 @@
|
||||
</div>
|
||||
<div class="flex flex-row flex-wrap gap-4">
|
||||
{#each places as item (item.data.id)}
|
||||
<a class="relative" href="/search?{Field.CITY}={item.value}" draggable="false">
|
||||
<a class="relative" href="{AppRoute.SEARCH}?{Field.CITY}={item.value}" draggable="false">
|
||||
<div
|
||||
class="flex w-[calc((100vw-(72px+5rem))/2)] max-w-[156px] justify-center overflow-hidden rounded-xl brightness-75 filter"
|
||||
>
|
||||
@ -97,7 +97,7 @@
|
||||
</div>
|
||||
<div class="flex flex-row flex-wrap gap-4">
|
||||
{#each things as item}
|
||||
<a class="relative" href="/search?{Field.OBJECTS}={item.value}" draggable="false">
|
||||
<a class="relative" href="{AppRoute.SEARCH}?{Field.OBJECTS}={item.value}" draggable="false">
|
||||
<div
|
||||
class="flex w-[calc((100vw-(72px+5rem))/2)] max-w-[156px] justify-center overflow-hidden rounded-xl brightness-75 filter"
|
||||
>
|
||||
|
@ -24,7 +24,6 @@
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { mdiDotsVertical, mdiPlus } from '@mdi/js';
|
||||
import UpdatePanel from '$lib/components/shared-components/update-panel.svelte';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@ -34,8 +33,6 @@
|
||||
const assetInteractionStore = createAssetInteractionStore();
|
||||
const { isMultiSelectState, selectedAssets } = assetInteractionStore;
|
||||
|
||||
$user = data.user;
|
||||
|
||||
$: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite);
|
||||
|
||||
const handleEscape = () => {
|
||||
|
@ -27,7 +27,7 @@
|
||||
},
|
||||
});
|
||||
|
||||
goto('/albums/' + newAlbum.id);
|
||||
goto(`${AppRoute.ALBUMS}/${newAlbum.id}`);
|
||||
} catch (e) {
|
||||
notificationController.show({
|
||||
message: 'Error creating album, check console for more details',
|
||||
@ -66,7 +66,7 @@
|
||||
<div class="flex flex-row flex-wrap gap-4">
|
||||
{#each data.partners as partner (partner.id)}
|
||||
<a
|
||||
href="/partners/{partner.id}"
|
||||
href="{AppRoute.PARTNERS}/{partner.id}"
|
||||
class="flex gap-4 rounded-lg px-5 py-4 transition-all hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
>
|
||||
<UserAvatar user={partner} size="lg" />
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { api } from '../api';
|
||||
import { api } from '@api';
|
||||
import type { LayoutLoad } from './$types';
|
||||
import { getSavedUser, setUser } from '$lib/stores/user.store';
|
||||
|
||||
const getUser = async () => {
|
||||
try {
|
||||
@ -14,7 +15,12 @@ export const ssr = false;
|
||||
export const csr = true;
|
||||
|
||||
export const load = (async () => {
|
||||
const user = await getUser();
|
||||
const savedUser = getSavedUser();
|
||||
const user = savedUser || (await getUser());
|
||||
|
||||
if (!savedUser) {
|
||||
setUser(user);
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
</script>
|
||||
|
||||
<section class="flex h-screen w-screen place-content-center place-items-center">
|
||||
@ -12,8 +12,10 @@
|
||||
<h1 class="font-immich-title text-4xl font-bold text-immich-primary dark:text-immich-dark-primary">
|
||||
Welcome to IMMICH Web
|
||||
</h1>
|
||||
<Button size="lg" rounded="lg" on:click={() => goto('/auth/register')}>
|
||||
<span class="px-2 font-bold">Getting Started</span>
|
||||
</Button>
|
||||
<a href={AppRoute.AUTH_REGISTER}>
|
||||
<Button size="lg" rounded="lg">
|
||||
<span class="px-2 font-bold">Getting Started</span>
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
Loading…
Reference in New Issue
Block a user