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

Auto-Reload on Web #141

Merged
merged 2 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docker/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ services:
- 5173:5173
volumes:
- ~/immichFrame_Config:/app/Config
- ../immichFrame.Web/src:/app/src
# - PATH/TO/CONFIG:/app/Config
13 changes: 12 additions & 1 deletion immichFrame.Web/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ts from 'typescript-eslint';
import svelte from 'eslint-plugin-svelte';
import prettier from 'eslint-config-prettier';
import globals from 'globals';
import parser from 'svelte-eslint-parser';

/** @type {import('eslint').Linter.Config[]} */
export default [
Expand All @@ -22,12 +23,22 @@ export default [
{
files: ['**/*.svelte'],
languageOptions: {
parser: parser,
ecmaVersion: 5,
sourceType: 'script',

parserOptions: {
parser: ts.parser
parser: '@typescript-eslint/parser'
}
}
},
{
ignores: ['build/', '.svelte-kit/', 'dist/']
},
{
files: ['*.ts'],
rules: {
'no-undef': 'off'
}
}
];
5 changes: 1 addition & 4 deletions immichFrame.Web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write .",
"api": "oazapfts https://localhost:7018/swagger/v1/swagger.json src/lib/immichFrameApi.ts",
"dev-up": "docker compose -f ../docker/docker-compose.dev.yml up --remove-orphans || make dev-down",
"dev-down": "docker compose -f ../docker/docker-compose.dev.yml down --remove-orphans",
"dev-update": "docker compose -f ../docker/docker-compose.dev.yml up --build -V --remove-orphans"
"api": "oazapfts https://localhost:7018/swagger/v1/swagger.json src/lib/immichFrameApi.ts"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
Expand Down
14 changes: 7 additions & 7 deletions immichFrame.Web/src/lib/components/elements/imageOverlay.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

function clickNext() {
dispatch('next');
dispatch('back');
dispatch('settings');
}
function clickBack() {
dispatch('back');
Expand All @@ -23,23 +21,23 @@

<div id="image-overlay" class="grid grid-cols-3 gap-2 content-stretch">
<div class="overlay-item group">
<button on:click={clickBack} class="opacity-0 group-hover:opacity-100"
<button on:click={clickBack} class="opacity-0 group-hover:opacity-100 frame-color"
><Icon title="Text" path={mdiChevronLeft} size="20em" /></button
>
</div>

<div class="overlay-item grid grid-rows-3 gap-2 content-stretch">
<button on:click={clickSettings} class="opacity-0 hover:opacity-100"
<button on:click={clickSettings} class="opacity-0 hover:opacity-100 frame-color"
><Icon title="Text" path={mdiCog} size="20em" /></button
>
<button on:click={clickPause} class="opacity-0 hover:opacity-100"
<button on:click={clickPause} class="opacity-0 hover:opacity-100 frame-color"
><Icon title="Text" path={mdiPause} size="20em" /></button
>
<div></div>
</div>

<div class="overlay-item group">
<button on:click={clickNext} class="opacity-0 group-hover:opacity-100"
<button on:click={clickNext} class="opacity-0 group-hover:opacity-100 frame-color"
><Icon title="Text" path={mdiChevronRight} size="20em" />
</button>
</div>
Expand All @@ -62,8 +60,10 @@
justify-content: center; /* Centers the SVG horizontally */
align-items: center; /* Optionally, centers the SVG vertically as well */
/* background-color: rgba(75, 75, 75, 0.4); */
color: wheat;
height: 100%;
width: 100%;
}
.frame-color {
color: wheat;
}
</style>
101 changes: 101 additions & 0 deletions immichFrame.Web/src/lib/components/elements/progress-bar.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script context="module" lang="ts">
export enum ProgressBarStatus {
Playing = 'playing',
Paused = 'paused'
}
</script>

<script lang="ts">
import { handlePromiseError } from '$lib/utils';

import { createEventDispatcher, onMount } from 'svelte';
import { tweened } from 'svelte/motion';

/**
* Autoplay on mount
* @default false
*/
export let autoplay = false;

/**
* Progress bar status
*/
export let status: ProgressBarStatus = ProgressBarStatus.Paused;

export let hidden = false;

export let duration = 5;

const onChange = async () => {
progress = setDuration(duration);
await play();
};

let progress = setDuration(duration);

// svelte 5, again....
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
$: duration, handlePromiseError(onChange());

$: {
if ($progress === 1) {
dispatch('done');
}
}

const dispatch = createEventDispatcher<{
done: void;
playing: void;
paused: void;
}>();

onMount(async () => {
if (autoplay) {
await play();
}
});

export const play = async () => {
status = ProgressBarStatus.Playing;
dispatch('playing');
await progress.set(1);
};

export const pause = async () => {
status = ProgressBarStatus.Paused;
dispatch('paused');
await progress.set($progress);
};

export const restart = async (autoplay: boolean) => {
await progress.set(0);

if (autoplay) {
await play();
}
};

export const reset = async () => {
status = ProgressBarStatus.Paused;
await progress.set(0);
};

function setDuration(newDuration: number) {
return tweened<number>(0, {
duration: (from: number, to: number) => (to ? newDuration * 1000 * (to - from) : 0)
});
}
</script>

{#if !hidden}
<span
class="absolute left-0 bottom-0 h-[3px] framebg-color"
style:width={`${$progress * 100}%`}
/>
{/if}

<style>
.framebg-color {
background-color: wheat;
}
</style>
62 changes: 59 additions & 3 deletions immichFrame.Web/src/lib/components/home-page/home-page.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
<script lang="ts">
import * as api from '$lib/immichFrameApi';
import Image from '../elements/image.svelte';
import { onMount } from 'svelte';
import ThumbHashImage from '../elements/thumbHashImage.svelte';
import ImageOverlay from '../elements/imageOverlay.svelte';
import Clock from '../elements/clock.svelte';
import ProgressBar, { ProgressBarStatus } from '$lib/components/elements/progress-bar.svelte';
import { slideshowStore } from '$lib/stores/slideshow.store';
import { onDestroy, onMount } from 'svelte';
api.defaults.baseUrl = 'http://localhost:8080/'; // TODO: replace configurable settings

let imageData: Blob | null;
let assetData: api.AssetResponseDto | null;

const { restartProgress, stopProgress } = slideshowStore;

let progressBarStatus: ProgressBarStatus;
let progressBar: ProgressBar;

let unsubscribeRestart: () => void;
let unsubscribeStop: () => void;

async function loadImage() {
let assetRequest = await api.getAsset();

Expand All @@ -28,15 +38,61 @@
imageData = imageRequest.data;
}

onMount(async () => loadImage());
onMount(async () => {
unsubscribeRestart = restartProgress.subscribe((value) => {
if (value) {
progressBar.restart(value);
}
});

unsubscribeStop = stopProgress.subscribe((value) => {
if (value) {
progressBar.restart(false);
}
});

await loadImage();
});

onDestroy(() => {
if (unsubscribeRestart) {
unsubscribeRestart();
}

if (unsubscribeStop) {
unsubscribeStop();
}
});

const handleDone = async () => {
await loadImage();
progressBar.restart(true);
};
</script>

<section id="home-page" class="fixed grid h-screen w-screen bg-black">
{#if imageData && assetData}
<Clock />
<ImageOverlay on:next={async () => loadImage()} />
<ImageOverlay
on:next={async () => {
await loadImage();
progressBar.restart(true);
}}
on:back={async () => {
await loadImage();
progressBar.restart(true);
}}
/>
<ThumbHashImage thumbHash={assetData.thumbhash ?? ''} />
<Image data={imageData} />
<ProgressBar
autoplay
hidden={false}
duration={5}
bind:this={progressBar}
bind:status={progressBarStatus}
on:done={handleDone}
/>
{:else}
<!-- maybe show immich logo?-->
<p class="text-white">LOADING ...</p>
Expand Down
48 changes: 48 additions & 0 deletions immichFrame.Web/src/lib/stores/slideshow.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { writable } from 'svelte/store';

export enum SlideshowState {
PlaySlideshow = 'play-slideshow',
StopSlideshow = 'stop-slideshow',
None = 'none',
}


function createSlideshowStore() {
const restartState = writable<boolean>(false);
const stopState = writable<boolean>(false);

const slideshowState = writable<SlideshowState>(SlideshowState.None);

return {
restartProgress: {
subscribe: restartState.subscribe,
set: (value: boolean) => {

console.log('restartProgress value is: ')
console.log(value)
// Trigger an action whenever the restartProgress is set to true. Automatically
// reset the restart state after that
if (value) {
restartState.set(true);
restartState.set(false);
}
},
},
stopProgress: {
subscribe: stopState.subscribe,
set: (value: boolean) => {
console.log('stopProgress value is: ')
console.log(value)
// Trigger an action whenever the stopProgress is set to true. Automatically
// reset the stop state after that
if (value) {
stopState.set(true);
stopState.set(false);
}
},
},
slideshowState
};
}

export const slideshowStore = createSlideshowStore();
4 changes: 4 additions & 0 deletions immichFrame.Web/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export const decodeBase64 = (data: string) => Uint8Array.from(atob(data), (c) => c.charCodeAt(0));

export const handlePromiseError = <T>(promise: Promise<T>): void => {
promise.catch((error) => console.error(`[utils.ts]:handlePromiseError ${error}`, error));
};
Loading