1
0
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:
martin 2023-12-12 03:35:57 +01:00 committed by GitHub
parent fb4b4e5895
commit fba9e784fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 86 additions and 59 deletions

View File

@ -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}`);
});
};

View File

@ -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">

View File

@ -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

View File

@ -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>

View File

@ -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}`);
});
};

View File

@ -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"

View File

@ -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',

View File

@ -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);
};

View File

@ -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;
};

View File

@ -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}
&#10060;
{/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}
&#10060;
{/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}
&#10060;
{/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}
&#10060;
{/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>

View File

@ -68,8 +68,6 @@
let album = data.album;
$user = data.user;
$: album = data.album;
$: {

View File

@ -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"
>

View File

@ -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 = () => {

View File

@ -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" />

View File

@ -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,

View File

@ -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>