mirror of
https://github.com/immich-app/immich.git
synced 2026-07-16 05:34:30 +03:00
refactor(web): move keyboard state into a store (#29181)
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
brokenAssetClass?: ClassValue;
|
||||
dimmed?: boolean;
|
||||
albumUsers?: UserResponseDto[];
|
||||
onClick?: (asset: TimelineAsset, event?: MouseEvent) => void;
|
||||
onClick?: (asset: TimelineAsset) => void;
|
||||
onPreview?: (asset: TimelineAsset) => void;
|
||||
onSelect?: (asset: TimelineAsset) => void;
|
||||
onMouseEvent?: (event: { isMouseOver: boolean; selectedGroupIndex: number }) => void;
|
||||
@@ -93,12 +93,12 @@
|
||||
}
|
||||
};
|
||||
|
||||
const callClickHandlers = (e?: MouseEvent) => {
|
||||
const callClickHandlers = () => {
|
||||
if (selected) {
|
||||
onIconClickedHandler(e);
|
||||
onIconClickedHandler();
|
||||
return;
|
||||
}
|
||||
onClick?.($state.snapshot(asset), e);
|
||||
onClick?.($state.snapshot(asset));
|
||||
};
|
||||
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
@@ -109,7 +109,7 @@
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
callClickHandlers(e);
|
||||
callClickHandlers();
|
||||
};
|
||||
|
||||
const onMouseEnter = () => {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import AssetDeleteConfirmModal from '$lib/modals/AssetDeleteConfirmModal.svelte';
|
||||
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
|
||||
import { showDeleteModal } from '$lib/stores/preferences.store';
|
||||
import { handlePromiseError } from '$lib/utils';
|
||||
import { deleteAssets } from '$lib/utils/actions';
|
||||
@@ -85,7 +86,6 @@
|
||||
return top + pageHeaderOffset < window.bottom && top + geometry.getHeight(index) > window.top;
|
||||
};
|
||||
|
||||
let shiftKeyIsDown = $state(false);
|
||||
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
|
||||
let scrollTop = $state(0);
|
||||
|
||||
@@ -122,21 +122,6 @@
|
||||
assetInteraction.selectAssets(assets.map((a) => toTimelineAsset(a)));
|
||||
};
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Shift') {
|
||||
event.preventDefault();
|
||||
|
||||
shiftKeyIsDown = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyUp = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Shift') {
|
||||
event.preventDefault();
|
||||
shiftKeyIsDown = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectAssets = (asset: TimelineAsset) => {
|
||||
if (!asset) {
|
||||
return;
|
||||
@@ -168,7 +153,7 @@
|
||||
};
|
||||
|
||||
const selectAssetCandidates = (endAsset: TimelineAsset) => {
|
||||
if (!shiftKeyIsDown) {
|
||||
if (!keyboardManager.shift) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -188,7 +173,7 @@
|
||||
};
|
||||
|
||||
const onSelectStart = (event: Event) => {
|
||||
if (assetInteraction.selectionActive && shiftKeyIsDown) {
|
||||
if (assetInteraction.selectionActive && keyboardManager.shift) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
@@ -333,13 +318,13 @@
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (!shiftKeyIsDown) {
|
||||
if (!keyboardManager.shift) {
|
||||
assetInteraction.clearCandidates();
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (shiftKeyIsDown && lastAssetMouseEvent) {
|
||||
if (keyboardManager.shift && lastAssetMouseEvent) {
|
||||
selectAssetCandidates(lastAssetMouseEvent);
|
||||
}
|
||||
});
|
||||
@@ -351,13 +336,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:document
|
||||
onkeydown={onKeyDown}
|
||||
onkeyup={onKeyUp}
|
||||
onselectstart={onSelectStart}
|
||||
use:shortcuts={shortcutList}
|
||||
onscroll={() => updateSlidingWindow()}
|
||||
/>
|
||||
<svelte:document onselectstart={onSelectStart} use:shortcuts={shortcutList} onscroll={() => updateSlidingWindow()} />
|
||||
|
||||
{#if assets.length > 0}
|
||||
<div
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||
import type { TimelineAsset, TimelineManagerOptions, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
|
||||
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
|
||||
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
|
||||
import { mediaQueryManager } from '$lib/stores/media-query-manager.svelte';
|
||||
import { isAssetViewerRoute, navigate } from '$lib/utils/navigation';
|
||||
import { getTimes, type ScrubberListener } from '$lib/utils/timeline-util';
|
||||
@@ -59,7 +60,6 @@
|
||||
groupTitle: string,
|
||||
asset: TimelineAsset,
|
||||
) => void,
|
||||
event?: MouseEvent,
|
||||
) => void;
|
||||
}
|
||||
|
||||
@@ -371,21 +371,6 @@
|
||||
|
||||
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
|
||||
|
||||
let shiftKeyIsDown = $state(false);
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Shift') {
|
||||
event.preventDefault();
|
||||
shiftKeyIsDown = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyUp = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Shift') {
|
||||
event.preventDefault();
|
||||
shiftKeyIsDown = false;
|
||||
}
|
||||
};
|
||||
const handleSelectAssetCandidates = (asset: TimelineAsset | null) => {
|
||||
if (asset) {
|
||||
void selectAssetCandidates(asset);
|
||||
@@ -486,7 +471,7 @@
|
||||
};
|
||||
|
||||
const selectAssetCandidates = async (endAsset: TimelineAsset) => {
|
||||
if (!shiftKeyIsDown) {
|
||||
if (!keyboardManager.shift) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -506,13 +491,13 @@
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (!shiftKeyIsDown) {
|
||||
if (!keyboardManager.shift) {
|
||||
assetInteraction.clearCandidates();
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (shiftKeyIsDown && lastAssetMouseEvent) {
|
||||
if (keyboardManager.shift && lastAssetMouseEvent) {
|
||||
void selectAssetCandidates(lastAssetMouseEvent);
|
||||
}
|
||||
});
|
||||
@@ -561,8 +546,6 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:document onkeydown={onKeyDown} onkeyup={onKeyUp} />
|
||||
|
||||
<HotModuleReload
|
||||
onAfterUpdate={() => {
|
||||
const asset = page.url.searchParams.get('at');
|
||||
@@ -600,12 +583,12 @@
|
||||
onScrubKeyDown={(evt) => {
|
||||
evt.preventDefault();
|
||||
let amount = 50;
|
||||
if (shiftKeyIsDown) {
|
||||
if (keyboardManager.shift) {
|
||||
amount = 500;
|
||||
}
|
||||
if (evt.key === 'ArrowUp') {
|
||||
amount = -amount;
|
||||
if (shiftKeyIsDown) {
|
||||
if (keyboardManager.shift) {
|
||||
scrollableElement?.scrollBy({ top: amount, behavior: 'smooth' });
|
||||
}
|
||||
} else if (evt.key === 'ArrowDown') {
|
||||
@@ -686,9 +669,9 @@
|
||||
{asset}
|
||||
{albumUsers}
|
||||
{groupIndex}
|
||||
onClick={(asset, event) => {
|
||||
onClick={(asset) => {
|
||||
if (typeof onThumbnailClick === 'function') {
|
||||
onThumbnailClick(asset, timelineManager, timelineDay, _onClick, event);
|
||||
onThumbnailClick(asset, timelineManager, timelineDay, _onClick);
|
||||
} else {
|
||||
_onClick(timelineManager, timelineDay.getAssets(), timelineDay.groupTitle, asset);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import NavigateToDateModal from '$lib/modals/NavigateToDateModal.svelte';
|
||||
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
|
||||
import { showDeleteModal } from '$lib/stores/preferences.store';
|
||||
import { searchStore } from '$lib/stores/search.svelte';
|
||||
import { handlePromiseError } from '$lib/utils';
|
||||
@@ -73,32 +74,8 @@
|
||||
assetInteraction.clear();
|
||||
};
|
||||
|
||||
let shiftKeyIsDown = $state(false);
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (searchStore.isSearchEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'Shift') {
|
||||
event.preventDefault();
|
||||
shiftKeyIsDown = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyUp = (event: KeyboardEvent) => {
|
||||
if (searchStore.isSearchEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'Shift') {
|
||||
event.preventDefault();
|
||||
shiftKeyIsDown = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onSelectStart = (e: Event) => {
|
||||
if (assetInteraction.selectionActive && shiftKeyIsDown) {
|
||||
if (!searchStore.isSearchEnabled && assetInteraction.selectionActive && keyboardManager.shift) {
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
@@ -171,4 +148,4 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:document onkeydown={onKeyDown} onkeyup={onKeyUp} onselectstart={onSelectStart} use:shortcuts={shortcutList} />
|
||||
<svelte:document onselectstart={onSelectStart} use:shortcuts={shortcutList} />
|
||||
|
||||
47
web/src/lib/stores/keyboard-manager.svelte.ts
Normal file
47
web/src/lib/stores/keyboard-manager.svelte.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
class KeyboardManager {
|
||||
#shift = $state(false);
|
||||
#ctrl = $state(false);
|
||||
#meta = $state(false);
|
||||
#alt = $state(false);
|
||||
|
||||
constructor() {
|
||||
if (globalThis.window === undefined) {
|
||||
return;
|
||||
}
|
||||
globalThis.addEventListener('keydown', this.#update);
|
||||
globalThis.addEventListener('keyup', this.#update);
|
||||
globalThis.addEventListener('blur', this.#clear);
|
||||
}
|
||||
|
||||
get shift() {
|
||||
return this.#shift;
|
||||
}
|
||||
|
||||
get ctrl() {
|
||||
return this.#ctrl;
|
||||
}
|
||||
|
||||
get meta() {
|
||||
return this.#meta;
|
||||
}
|
||||
|
||||
get alt() {
|
||||
return this.#alt;
|
||||
}
|
||||
|
||||
#update = (event: KeyboardEvent) => {
|
||||
this.#shift = event.shiftKey;
|
||||
this.#ctrl = event.ctrlKey;
|
||||
this.#meta = event.metaKey;
|
||||
this.#alt = event.altKey;
|
||||
};
|
||||
|
||||
#clear = () => {
|
||||
this.#shift = false;
|
||||
this.#ctrl = false;
|
||||
this.#meta = false;
|
||||
this.#alt = false;
|
||||
};
|
||||
}
|
||||
|
||||
export const keyboardManager = new KeyboardManager();
|
||||
@@ -11,6 +11,7 @@
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import GeolocationPointPickerModal from '$lib/modals/GeolocationPointPickerModal.svelte';
|
||||
import GeolocationUpdateConfirmModal from '$lib/modals/GeolocationUpdateConfirmModal.svelte';
|
||||
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
|
||||
import type { LatLng } from '$lib/types';
|
||||
import { setQueryValue } from '$lib/utils/navigation';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
@@ -118,13 +119,10 @@
|
||||
groupTitle: string,
|
||||
asset: TimelineAsset,
|
||||
) => void,
|
||||
event?: MouseEvent,
|
||||
) => {
|
||||
if (event?.shiftKey) {
|
||||
if (keyboardManager.shift) {
|
||||
onClick(timelineManager, timelineDay.getAssets(), timelineDay.groupTitle, asset);
|
||||
return;
|
||||
}
|
||||
if (hasGps(asset)) {
|
||||
} else if (hasGps(asset)) {
|
||||
locationUpdated = true;
|
||||
setTimeout(() => {
|
||||
locationUpdated = false;
|
||||
|
||||
Reference in New Issue
Block a user