mirror of
https://github.com/immich-app/immich
synced 2025-06-08 19:37:58 +00:00
feat(web): improve slideshow quality of life (#18778)
* Add a new setting to toggle autoplay when showing the slideshow. * Fix an issue where the slideshow would restart automatically when navigating after it was paused. * Add a keyboard shortcut 's' to start the slideshow from the asset viewer. * Add a keyboard shortcut ' ' to toggle the slideshow play/paused. * Change the timeout for hiding the slideshow controls from 10 to 2.5 seconds. * Add English translation for the 'autoplay_slideshow' setting. Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
df927dd3ce
commit
d544053c67
@ -477,6 +477,7 @@
|
|||||||
"authorized_devices": "Authorized Devices",
|
"authorized_devices": "Authorized Devices",
|
||||||
"automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere",
|
"automatic_endpoint_switching_subtitle": "Connect locally over designated Wi-Fi when available and use alternative connections elsewhere",
|
||||||
"automatic_endpoint_switching_title": "Automatic URL switching",
|
"automatic_endpoint_switching_title": "Automatic URL switching",
|
||||||
|
"autoplay_slideshow": "Autoplay slideshow",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"back_close_deselect": "Back, close, or deselect",
|
"back_close_deselect": "Back, close, or deselect",
|
||||||
"background_location_permission": "Background location permission",
|
"background_location_permission": "Background location permission",
|
||||||
|
@ -352,7 +352,9 @@
|
|||||||
const handleUpdateSelectedEditType = (type: string) => {
|
const handleUpdateSelectedEditType = (type: string) => {
|
||||||
selectedEditType = type;
|
selectedEditType = type;
|
||||||
};
|
};
|
||||||
|
|
||||||
let isFullScreen = $derived(fullscreenElement !== null);
|
let isFullScreen = $derived(fullscreenElement !== null);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (asset) {
|
if (asset) {
|
||||||
previewStackedAsset = undefined;
|
previewStackedAsset = undefined;
|
||||||
|
@ -113,6 +113,8 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onPlaySlideshow = () => ($slideshowState = SlideshowState.PlaySlideshow);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (isFaceEditMode.value && $photoZoomState.currentZoom > 1) {
|
if (isFaceEditMode.value && $photoZoomState.currentZoom > 1) {
|
||||||
zoomToggle();
|
zoomToggle();
|
||||||
@ -206,6 +208,8 @@
|
|||||||
|
|
||||||
<svelte:document
|
<svelte:document
|
||||||
use:shortcuts={[
|
use:shortcuts={[
|
||||||
|
{ shortcut: { key: 'z' }, onShortcut: zoomToggle, preventDefault: true },
|
||||||
|
{ shortcut: { key: 's' }, onShortcut: onPlaySlideshow, preventDefault: true },
|
||||||
{ shortcut: { key: 'c', ctrl: true }, onShortcut: onCopyShortcut, preventDefault: false },
|
{ shortcut: { key: 'c', ctrl: true }, onShortcut: onCopyShortcut, preventDefault: false },
|
||||||
{ shortcut: { key: 'c', meta: true }, onShortcut: onCopyShortcut, preventDefault: false },
|
{ shortcut: { key: 'c', meta: true }, onShortcut: onCopyShortcut, preventDefault: false },
|
||||||
{ shortcut: { key: 'z' }, onShortcut: zoomToggle, preventDefault: false },
|
{ shortcut: { key: 'z' }, onShortcut: zoomToggle, preventDefault: false },
|
||||||
|
@ -28,7 +28,8 @@
|
|||||||
onSetToFullScreen = () => {},
|
onSetToFullScreen = () => {},
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const { restartProgress, stopProgress, slideshowDelay, showProgressBar, slideshowNavigation } = slideshowStore;
|
const { restartProgress, stopProgress, slideshowDelay, showProgressBar, slideshowNavigation, slideshowAutoplay } =
|
||||||
|
slideshowStore;
|
||||||
|
|
||||||
let progressBarStatus: ProgressBarStatus | undefined = $state();
|
let progressBarStatus: ProgressBarStatus | undefined = $state();
|
||||||
let progressBar = $state<ReturnType<typeof ProgressBar>>();
|
let progressBar = $state<ReturnType<typeof ProgressBar>>();
|
||||||
@ -60,20 +61,20 @@
|
|||||||
showControls = false;
|
showControls = false;
|
||||||
setCursorStyle('none');
|
setCursorStyle('none');
|
||||||
}
|
}
|
||||||
}, 10_000);
|
}, 2500);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
hideControlsAfterDelay();
|
hideControlsAfterDelay();
|
||||||
unsubscribeRestart = restartProgress.subscribe((value) => {
|
unsubscribeRestart = restartProgress.subscribe((value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
progressBar?.restart(value);
|
progressBar?.restart();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
unsubscribeStop = stopProgress.subscribe((value) => {
|
unsubscribeStop = stopProgress.subscribe((value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
progressBar?.restart(false);
|
progressBar?.restart();
|
||||||
stopControlsHideTimer();
|
stopControlsHideTimer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -90,7 +91,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleDone = async () => {
|
const handleDone = async () => {
|
||||||
await progressBar?.reset();
|
await progressBar?.resetProgress();
|
||||||
|
|
||||||
if ($slideshowNavigation === SlideshowNavigation.AscendingOrder) {
|
if ($slideshowNavigation === SlideshowNavigation.AscendingOrder) {
|
||||||
onPrevious();
|
onPrevious();
|
||||||
@ -113,6 +114,17 @@
|
|||||||
{ shortcut: { key: 'Escape' }, onShortcut: onClose },
|
{ shortcut: { key: 'Escape' }, onShortcut: onClose },
|
||||||
{ shortcut: { key: 'ArrowLeft' }, onShortcut: onPrevious },
|
{ shortcut: { key: 'ArrowLeft' }, onShortcut: onPrevious },
|
||||||
{ shortcut: { key: 'ArrowRight' }, onShortcut: onNext },
|
{ shortcut: { key: 'ArrowRight' }, onShortcut: onNext },
|
||||||
|
{
|
||||||
|
shortcut: { key: ' ' },
|
||||||
|
onShortcut: () => {
|
||||||
|
if (progressBarStatus === ProgressBarStatus.Paused) {
|
||||||
|
progressBar?.play();
|
||||||
|
} else {
|
||||||
|
progressBar?.pause();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
preventDefault: true,
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -187,7 +199,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
autoplay
|
autoplay={$slideshowAutoplay}
|
||||||
hidden={!$showProgressBar}
|
hidden={!$showProgressBar}
|
||||||
duration={$slideshowDelay}
|
duration={$slideshowDelay}
|
||||||
bind:this={progressBar}
|
bind:this={progressBar}
|
||||||
|
@ -51,7 +51,11 @@
|
|||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (autoplay) {
|
if (autoplay) {
|
||||||
|
status = ProgressBarStatus.Playing;
|
||||||
await play();
|
await play();
|
||||||
|
} else {
|
||||||
|
status = ProgressBarStatus.Paused;
|
||||||
|
await progress.set(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -67,16 +71,15 @@
|
|||||||
await progress.set($progress);
|
await progress.set($progress);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const restart = async (autoplay: boolean) => {
|
export const restart = async () => {
|
||||||
await progress.set(0);
|
await progress.set(0);
|
||||||
|
|
||||||
if (autoplay) {
|
if (status !== ProgressBarStatus.Paused) {
|
||||||
await play();
|
await play();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reset = async () => {
|
export const resetProgress = async () => {
|
||||||
status = ProgressBarStatus.Paused;
|
|
||||||
await progress.set(0);
|
await progress.set(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,7 +16,14 @@
|
|||||||
import type { RenderedOption } from './elements/dropdown.svelte';
|
import type { RenderedOption } from './elements/dropdown.svelte';
|
||||||
import SettingDropdown from './shared-components/settings/setting-dropdown.svelte';
|
import SettingDropdown from './shared-components/settings/setting-dropdown.svelte';
|
||||||
|
|
||||||
const { slideshowDelay, showProgressBar, slideshowNavigation, slideshowLook, slideshowTransition } = slideshowStore;
|
const {
|
||||||
|
slideshowDelay,
|
||||||
|
showProgressBar,
|
||||||
|
slideshowNavigation,
|
||||||
|
slideshowLook,
|
||||||
|
slideshowTransition,
|
||||||
|
slideshowAutoplay,
|
||||||
|
} = slideshowStore;
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
@ -30,6 +37,7 @@
|
|||||||
let tempSlideshowNavigation = $state($slideshowNavigation);
|
let tempSlideshowNavigation = $state($slideshowNavigation);
|
||||||
let tempSlideshowLook = $state($slideshowLook);
|
let tempSlideshowLook = $state($slideshowLook);
|
||||||
let tempSlideshowTransition = $state($slideshowTransition);
|
let tempSlideshowTransition = $state($slideshowTransition);
|
||||||
|
let tempSlideshowAutoplay = $state($slideshowAutoplay);
|
||||||
|
|
||||||
const navigationOptions: Record<SlideshowNavigation, RenderedOption> = {
|
const navigationOptions: Record<SlideshowNavigation, RenderedOption> = {
|
||||||
[SlideshowNavigation.Shuffle]: { icon: mdiShuffle, title: $t('shuffle') },
|
[SlideshowNavigation.Shuffle]: { icon: mdiShuffle, title: $t('shuffle') },
|
||||||
@ -60,6 +68,7 @@
|
|||||||
$slideshowNavigation = tempSlideshowNavigation;
|
$slideshowNavigation = tempSlideshowNavigation;
|
||||||
$slideshowLook = tempSlideshowLook;
|
$slideshowLook = tempSlideshowLook;
|
||||||
$slideshowTransition = tempSlideshowTransition;
|
$slideshowTransition = tempSlideshowTransition;
|
||||||
|
$slideshowAutoplay = tempSlideshowAutoplay;
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -83,6 +92,7 @@
|
|||||||
tempSlideshowLook = handleToggle(option, lookOptions) || tempSlideshowLook;
|
tempSlideshowLook = handleToggle(option, lookOptions) || tempSlideshowLook;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<SettingSwitch title={$t('autoplay_slideshow')} bind:checked={tempSlideshowAutoplay} />
|
||||||
<SettingSwitch title={$t('show_progress_bar')} bind:checked={tempShowProgressBar} />
|
<SettingSwitch title={$t('show_progress_bar')} bind:checked={tempShowProgressBar} />
|
||||||
<SettingSwitch title={$t('show_slideshow_transition')} bind:checked={tempSlideshowTransition} />
|
<SettingSwitch title={$t('show_slideshow_transition')} bind:checked={tempSlideshowTransition} />
|
||||||
<SettingInputField
|
<SettingInputField
|
||||||
|
@ -39,6 +39,7 @@ function createSlideshowStore() {
|
|||||||
const showProgressBar = persisted<boolean>('slideshow-show-progressbar', true);
|
const showProgressBar = persisted<boolean>('slideshow-show-progressbar', true);
|
||||||
const slideshowDelay = persisted<number>('slideshow-delay', 5, {});
|
const slideshowDelay = persisted<number>('slideshow-delay', 5, {});
|
||||||
const slideshowTransition = persisted<boolean>('slideshow-transition', true);
|
const slideshowTransition = persisted<boolean>('slideshow-transition', true);
|
||||||
|
const slideshowAutoplay = persisted<boolean>('slideshow-autoplay', true, {});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
restartProgress: {
|
restartProgress: {
|
||||||
@ -69,6 +70,7 @@ function createSlideshowStore() {
|
|||||||
slideshowDelay,
|
slideshowDelay,
|
||||||
showProgressBar,
|
showProgressBar,
|
||||||
slideshowTransition,
|
slideshowTransition,
|
||||||
|
slideshowAutoplay,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user