mirror of
https://github.com/immich-app/immich.git
synced 2025-11-22 22:20:43 +09:00
fix(web): keyboard shortcut handling (#7946)
* fix(web): keyboard shortcut handling * drop executeShortcuts in favor of action * fix merge --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
@@ -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'];
|
||||
|
||||
@@ -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',
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
@@ -256,68 +256,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 = () => {
|
||||
@@ -557,7 +498,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"
|
||||
|
||||
@@ -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[] = [];
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -223,6 +208,10 @@
|
||||
use:autoGrowHeight
|
||||
use:clickOutside
|
||||
on:outclick={handleFocusOut}
|
||||
use:shortcut={{
|
||||
shortcut: { key: 'Enter', ctrl: true },
|
||||
onShortcut: () => handlePromiseError(handleFocusOut()),
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
</section>
|
||||
|
||||
@@ -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, AssetTypeEnum } from '@immich/sdk';
|
||||
import { useZoomImageWheel } from '@zoom-image/svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
@@ -84,18 +84,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;
|
||||
@@ -138,9 +126,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}
|
||||
|
||||
Reference in New Issue
Block a user