Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(web): keyboard shortcut handling #7946

Merged
merged 5 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions web/src/lib/components/album-page/album-description.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { autoGrowHeight } from '$lib/utils/autogrow';
import { updateAlbumInfo } from '@immich/sdk';
import { handleError } from '$lib/utils/handle-error';
import { shortcut } from '$lib/utils/shortcut';

export let id: string;
export let description: string;
Expand Down Expand Up @@ -37,6 +38,10 @@
on:focusout={handleUpdateDescription}
use:autoGrowHeight
placeholder="Add description"
use:shortcut={{
shortcut: { key: 'Enter', ctrl: true },
onShortcut: (e) => e.currentTarget.blur(),
}}
/>
{:else if description}
<p class="break-words whitespace-pre-line w-full text-black dark:text-white text-base">
Expand Down
3 changes: 2 additions & 1 deletion web/src/lib/components/album-page/album-title.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import { updateAlbumInfo } from '@immich/sdk';
import { handleError } from '$lib/utils/handle-error';
import { shortcut } from '$lib/utils/shortcut';

export let id: string;
export let albumName: string;
Expand Down Expand Up @@ -29,7 +30,7 @@
</script>

<input
on:keydown={(e) => e.key === 'Enter' && e.currentTarget.blur()}
use:shortcut={{ shortcut: { key: 'Enter' }, onShortcut: (e) => e.currentTarget.blur() }}
on:blur={handleUpdateName}
class="w-[99%] mb-2 border-b-2 border-transparent text-6xl text-immich-primary outline-none transition-all dark:text-immich-dark-primary {isOwned
? 'hover:border-gray-400'
Expand Down
43 changes: 12 additions & 31 deletions web/src/lib/components/album-page/album-viewer.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<script lang="ts">
import { browser } from '$app/environment';
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store';
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
import type { AlbumResponseDto, SharedLinkResponseDto, UserResponseDto } from '@immich/sdk';
import { onDestroy, onMount } from 'svelte';
import { createAssetInteractionStore } from '../../stores/asset-interaction.store';
import { AssetStore } from '../../stores/assets.store';
import { downloadArchive } from '../../utils/asset-utils';
Expand All @@ -16,7 +14,7 @@
import ControlAppBar from '../shared-components/control-app-bar.svelte';
import ImmichLogo from '../shared-components/immich-logo.svelte';
import ThemeButton from '../shared-components/theme-button.svelte';
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
import { shortcut } from '$lib/utils/shortcut';
import { mdiFileImagePlusOutline, mdiFolderDownloadOutline } from '@mdi/js';
import { handlePromiseError } from '$lib/utils';
import AlbumSummary from './album-summary.svelte';
Expand All @@ -39,39 +37,22 @@
}
});

const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);

onMount(() => {
document.addEventListener('keydown', onKeyboardPress);
});

onDestroy(() => {
if (browser) {
document.removeEventListener('keydown', onKeyboardPress);
}
});

const handleKeyboardPress = (event: KeyboardEvent) => {
if (shouldIgnoreShortcut(event)) {
return;
}
if (!$showAssetViewer) {
switch (event.key) {
case 'Escape': {
if ($isMultiSelectState) {
assetInteractionStore.clearMultiselect();
}
return;
}
}
}
};

const downloadAlbum = async () => {
await downloadArchive(`${album.albumName}.zip`, { albumId: album.id });
};
</script>

<svelte:window
use:shortcut={{
shortcut: { key: 'Escape' },
onShortcut: () => {
if (!$showAssetViewer && $isMultiSelectState) {
assetInteractionStore.clearMultiselect();
}
},
}}
/>

<header>
{#if $isMultiSelectState}
<AssetSelectControlBar
Expand Down
14 changes: 5 additions & 9 deletions web/src/lib/components/asset-viewer/activity-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import { NotificationType, notificationController } from '../shared-components/notification/notification';
import UserAvatar from '../shared-components/user-avatar.svelte';
import { locale } from '$lib/stores/preferences.store';
import { shortcut } from '$lib/utils/shortcut';

const units: Intl.RelativeTimeFormatUnit[] = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'];

Expand Down Expand Up @@ -95,14 +96,6 @@
}
};

const handleEnter = async (event: KeyboardEvent) => {
if (event.key === 'Enter') {
event.preventDefault();
await handleSendComment();
return;
}
};

const timeOptions = {
year: 'numeric',
month: '2-digit',
Expand Down Expand Up @@ -295,7 +288,10 @@
use:autoGrowHeight={'5px'}
placeholder={disabled ? 'Comments are disabled' : 'Say something'}
on:input={() => autoGrowHeight(textArea, '5px')}
on:keypress={handleEnter}
use:shortcut={{
shortcut: { key: 'Enter' },
onShortcut: () => handleSendComment(),
}}
class="h-[18px] {disabled
? 'cursor-not-allowed'
: ''} w-full max-h-56 pr-2 items-center overflow-y-auto leading-4 outline-none resize-none bg-gray-200"
Expand Down
81 changes: 17 additions & 64 deletions web/src/lib/components/asset-viewer/asset-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import { getAssetJobMessage, isSharedLink, handlePromiseError } from '$lib/utils';
import { addAssetsToAlbum, downloadFile } from '$lib/utils/asset-utils';
import { handleError } from '$lib/utils/handle-error';
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
import { shortcuts } from '$lib/utils/shortcut';
import { SlideshowHistory } from '$lib/utils/slideshow-history';
import {
AssetJobName,
Expand Down Expand Up @@ -250,68 +250,9 @@
isShowActivity = !isShowActivity;
};

const handleKeypress = async (event: KeyboardEvent) => {
if (shouldIgnoreShortcut(event)) {
return;
}

const key = event.key;
const shiftKey = event.shiftKey;
const ctrlKey = event.ctrlKey;

if (ctrlKey) {
return;
}

switch (key) {
case 'a':
case 'A': {
if (shiftKey) {
await toggleArchive();
}
return;
}
case 'ArrowLeft': {
await navigateAsset('previous');
return;
}
case 'ArrowRight': {
await navigateAsset('next');
return;
}
case 'd':
case 'D': {
if (shiftKey) {
await downloadFile(asset);
}
return;
}
case 'Delete': {
await trashOrDelete(shiftKey);
return;
}
case 'Escape': {
if (isShowDeleteConfirmation) {
isShowDeleteConfirmation = false;
return;
}
if (isShowShareModal) {
isShowShareModal = false;
return;
}
closeViewer();
return;
}
case 'f': {
await toggleFavorite();
return;
}
case 'i': {
isShowActivity = false;
$isShowDetail = !$isShowDetail;
return;
}
}
const toggleDetailPanel = () => {
isShowActivity = false;
$isShowDetail = !$isShowDetail;
};

const handleCloseViewer = () => {
Expand Down Expand Up @@ -551,7 +492,19 @@
};
</script>

<svelte:window on:keydown={handleKeypress} />
<svelte:window
use:shortcuts={[
{ shortcut: { key: 'a', shift: true }, onShortcut: toggleArchive },
{ shortcut: { key: 'ArrowLeft' }, onShortcut: () => navigateAsset('previous') },
{ shortcut: { key: 'ArrowRight' }, onShortcut: () => navigateAsset('next') },
{ shortcut: { key: 'd', shift: true }, onShortcut: () => downloadFile(asset) },
{ shortcut: { key: 'Delete' }, onShortcut: () => trashOrDelete(false) },
{ shortcut: { key: 'Delete', shift: true }, onShortcut: () => trashOrDelete(true) },
{ shortcut: { key: 'Escape' }, onShortcut: closeViewer },
{ shortcut: { key: 'f' }, onShortcut: toggleFavorite },
{ shortcut: { key: 'i' }, onShortcut: toggleDetailPanel },
]}
/>

<section
id="immich-asset-viewer"
Expand Down
21 changes: 5 additions & 16 deletions web/src/lib/components/asset-viewer/detail-panel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import UserAvatar from '../shared-components/user-avatar.svelte';
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
import { NotificationType, notificationController } from '../shared-components/notification/notification';
import { shortcut } from '$lib/utils/shortcut';

export let asset: AssetResponseDto;
export let albums: AlbumResponseDto[] = [];
Expand Down Expand Up @@ -105,20 +106,6 @@
closeViewer: void;
}>();

const handleKeypress = async (event: KeyboardEvent) => {
if (event.target !== textArea) {
return;
}
const ctrl = event.ctrlKey;
switch (event.key) {
case 'Enter': {
if (ctrl && event.target === textArea) {
await handleFocusOut();
}
}
}
};

const getMegapixel = (width: number, height: number): number | undefined => {
const megapixel = Math.round((height * width) / 1_000_000);

Expand Down Expand Up @@ -180,8 +167,6 @@
}
</script>

<svelte:window on:keydown={handleKeypress} />

<section class="relative p-2 dark:bg-immich-dark-bg dark:text-immich-dark-fg">
<div class="flex place-items-center gap-2">
<button
Expand Down Expand Up @@ -223,6 +208,10 @@
use:autoGrowHeight
use:clickOutside
on:outclick={handleFocusOut}
use:shortcut={{
shortcut: { key: 'Enter', ctrl: true },
onShortcut: () => handlePromiseError(handleFocusOut()),
}}
/>
{/key}
</section>
Expand Down
30 changes: 16 additions & 14 deletions web/src/lib/components/asset-viewer/photo-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { downloadRequest, getAssetFileUrl, handlePromiseError } from '$lib/utils';
import { isWebCompatibleImage } from '$lib/utils/asset-utils';
import { getBoundingBox } from '$lib/utils/people-utils';
import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
import { shortcuts } from '$lib/utils/shortcut';
import { type AssetResponseDto } from '@immich/sdk';
import { useZoomImageWheel } from '@zoom-image/svelte';
import { onDestroy, onMount } from 'svelte';
Expand Down Expand Up @@ -65,18 +65,6 @@
}
};

const handleKeypress = async (event: KeyboardEvent) => {
if (shouldIgnoreShortcut(event)) {
return;
}
if (window.getSelection()?.type === 'Range') {
return;
}
if ((event.metaKey || event.ctrlKey) && event.key === 'c') {
await doCopy();
}
};

const doCopy = async () => {
if (!canCopyImagesToClipboard()) {
return;
Expand Down Expand Up @@ -119,9 +107,23 @@
handlePromiseError(loadAssetData({ loadOriginal: true }));
}
});

const onCopyShortcut = () => {
if (window.getSelection()?.type === 'Range') {
return;
}
handlePromiseError(doCopy());
};
</script>

<svelte:window on:keydown={handleKeypress} on:copyImage={doCopy} on:zoomImage={doZoomImage} />
<svelte:window
on:copyImage={doCopy}
on:zoomImage={doZoomImage}
use:shortcuts={[
{ shortcut: { key: 'c', ctrl: true }, onShortcut: onCopyShortcut },
{ shortcut: { key: 'c', meta: true }, onShortcut: onCopyShortcut },
]}
/>

<div
bind:this={element}
Expand Down
22 changes: 8 additions & 14 deletions web/src/lib/components/memory-page/memory-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import type { Viewport } from '$lib/stores/assets.store';
import { memoryStore } from '$lib/stores/memory.store';
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
import { shortcuts } from '$lib/utils/shortcut';
import { fromLocalDateTime } from '$lib/utils/timeline-util';
import { ThumbnailFormat, getMemoryLane } from '@immich/sdk';
import { mdiChevronDown, mdiChevronLeft, mdiChevronRight, mdiChevronUp, mdiPause, mdiPlay } from '@mdi/js';
Expand Down Expand Up @@ -73,19 +74,6 @@
// Progress should be reset when the current memory or asset changes.
$: memoryIndex, assetIndex, handlePromiseError(reset());

const handleKeyDown = async (e: KeyboardEvent) => {
if (e.key === 'ArrowRight' && canGoForward) {
e.preventDefault();
await toNext();
} else if (e.key === 'ArrowLeft' && canGoBack) {
e.preventDefault();
await toPrevious();
} else if (e.key === 'Escape') {
e.preventDefault();
await goto(AppRoute.PHOTOS);
}
};

onMount(async () => {
if (!$memoryStore) {
const localTime = new Date();
Expand All @@ -101,7 +89,13 @@
let galleryInView = false;
</script>

<svelte:window on:keydown={handleKeyDown} />
<svelte:window
use:shortcuts={[
{ shortcut: { key: 'ArrowRight' }, onShortcut: () => canGoForward && toNext() },
{ shortcut: { key: 'ArrowLeft' }, onShortcut: () => canGoBack && toPrevious() },
{ shortcut: { key: 'Escape' }, onShortcut: () => goto(AppRoute.PHOTOS) },
]}
/>

<section id="memory-viewer" class="w-full bg-immich-dark-gray" bind:this={memoryWrapper}>
{#if currentMemory}
Expand Down