From 5b627902485b7d4c41c97c0dd76070c455b6877a Mon Sep 17 00:00:00 2001 From: Flavio Fois Date: Sun, 8 Feb 2026 22:09:32 +0100 Subject: [PATCH] Adds update checker with user preference Introduces an update checker feature that respects the user's preference, allowing them to enable or disable automatic update checks. The setting is persisted in the config file and synced to the backend. Also introduces a page dedicated to listing music that inspired the project, and makes some minor UI improvements --- app_settings.go | 30 +++ config.ini | 16 +- frontend/src/lib/components/SidebarApp.svelte | 4 +- frontend/src/lib/stores/settings.svelte.ts | 1 + frontend/src/lib/types.d.ts | 19 ++ frontend/src/routes/(app)/+layout.svelte | 24 ++- .../src/routes/(app)/inspiration/+page.svelte | 199 ++++++++++++++++++ .../src/routes/(app)/inspiration/+page.ts | 19 ++ .../src/routes/(app)/settings/+page.svelte | 51 ++++- 9 files changed, 346 insertions(+), 17 deletions(-) create mode 100644 frontend/src/routes/(app)/inspiration/+page.svelte create mode 100644 frontend/src/routes/(app)/inspiration/+page.ts diff --git a/app_settings.go b/app_settings.go index e52e700..7230b41 100644 --- a/app_settings.go +++ b/app_settings.go @@ -98,3 +98,33 @@ func (a *App) ImportSettings() (string, error) { return string(data), nil } + +// SetUpdateCheckerEnabled updates the UPDATE_CHECK_ENABLED setting in config.ini +// based on the user's preference from the GUI settings. +// +// Parameters: +// - enabled: true to enable update checking, false to disable +// +// Returns: +// - error: Error if loading or saving config fails +func (a *App) SetUpdateCheckerEnabled(enabled bool) error { + // Load current config + config := a.GetConfig() + if config == nil { + return fmt.Errorf("failed to load config") + } + + // Update the setting + if enabled { + config.EMLy.UpdateCheckEnabled = "true" + } else { + config.EMLy.UpdateCheckEnabled = "false" + } + + // Save config back to disk + if err := a.SaveConfig(config); err != nil { + return fmt.Errorf("failed to save config: %w", err) + } + + return nil +} diff --git a/config.ini b/config.ini index 07c991d..8b6f1a3 100644 --- a/config.ini +++ b/config.ini @@ -1,9 +1,9 @@ [EMLy] -SDK_DECODER_SEMVER="1.3.2" -SDK_DECODER_RELEASE_CHANNEL="beta" -GUI_SEMVER="1.4.0" -GUI_RELEASE_CHANNEL="stable" -LANGUAGE="it" -UPDATE_CHECK_ENABLED="true" -UPDATE_PATH="" -UPDATE_AUTO_CHECK="true" \ No newline at end of file +SDK_DECODER_SEMVER = 1.3.2 +SDK_DECODER_RELEASE_CHANNEL = beta +GUI_SEMVER = 1.4.0 +GUI_RELEASE_CHANNEL = stable +LANGUAGE = it +UPDATE_CHECK_ENABLED = false +UPDATE_PATH = +UPDATE_AUTO_CHECK = true diff --git a/frontend/src/lib/components/SidebarApp.svelte b/frontend/src/lib/components/SidebarApp.svelte index 460a709..693ce5e 100644 --- a/frontend/src/lib/components/SidebarApp.svelte +++ b/frontend/src/lib/components/SidebarApp.svelte @@ -3,7 +3,7 @@ import * as Sidebar from "$lib/components/ui/sidebar/index.js"; import { dangerZoneEnabled } from "$lib/stores/app"; import * as m from "$lib/paraglide/messages.js"; - import { Mail, Heart } from "@lucide/svelte/icons"; + import { Mail, Heart, Info } from "@lucide/svelte/icons"; const CLICK_WINDOW_MS = 4000; const REQUIRED_CLICKS = 10; @@ -46,7 +46,7 @@ { title: m.sidebar_credits(), url: "/credits", - icon: Heart, + icon: Info, disabled: false, id: 3, }, diff --git a/frontend/src/lib/stores/settings.svelte.ts b/frontend/src/lib/stores/settings.svelte.ts index 7294249..5d54064 100644 --- a/frontend/src/lib/stores/settings.svelte.ts +++ b/frontend/src/lib/stores/settings.svelte.ts @@ -11,6 +11,7 @@ const defaults: EMLy_GUI_Settings = { previewFileSupportedTypes: ["jpg", "jpeg", "png"], enableAttachedDebuggerProtection: true, useDarkEmailViewer: true, + enableUpdateChecker: true, }; class SettingsStore { diff --git a/frontend/src/lib/types.d.ts b/frontend/src/lib/types.d.ts index ce94a18..f6bc66a 100644 --- a/frontend/src/lib/types.d.ts +++ b/frontend/src/lib/types.d.ts @@ -9,6 +9,25 @@ interface EMLy_GUI_Settings { previewFileSupportedTypes?: SupportedFileTypePreview[]; enableAttachedDebuggerProtection?: boolean; useDarkEmailViewer?: boolean; + enableUpdateChecker?: boolean; } type SupportedLanguages = "en" | "it"; +// Plugin System Types +interface PluginFormatSupport { + extensions: string[]; + mime_types?: string[]; + priority: number; +} + +interface PluginInfo { + name: string; + version: string; + author: string; + description: string; + capabilities: string[]; + status: "unloaded" | "loading" | "active" | "error" | "disabled"; + enabled: boolean; + last_error?: string; + supported_formats?: PluginFormatSupport[]; +} \ No newline at end of file diff --git a/frontend/src/routes/(app)/+layout.svelte b/frontend/src/routes/(app)/+layout.svelte index 590b531..2a9bcec 100644 --- a/frontend/src/routes/(app)/+layout.svelte +++ b/frontend/src/routes/(app)/+layout.svelte @@ -24,6 +24,8 @@ CheckCircle, Camera, Heart, + Info, + Music } from "@lucide/svelte"; import { Separator } from "$lib/components/ui/separator/index.js"; import { toast } from "svelte-sonner"; @@ -380,7 +382,7 @@ style="cursor: pointer; opacity: 0.7;" class="hover:opacity-100 transition-opacity" /> - { if (page.url.pathname !== "/credits" && page.url.pathname !== "/credits/") @@ -389,12 +391,11 @@ style="cursor: pointer; opacity: 0.7;" class="hover:opacity-100 transition-opacity" /> - - - { - $bugReportDialogOpen = !$bugReportDialogOpen; + if (page.url.pathname !== "/inspiration" && page.url.pathname !== "/inspiration/") + goto("/inspiration"); }} style="cursor: pointer; opacity: 0.7;" class="hover:opacity-100 transition-opacity" @@ -410,6 +411,19 @@ > + + { + $bugReportDialogOpen = !$bugReportDialogOpen; + }} + > + + diff --git a/frontend/src/routes/(app)/inspiration/+page.svelte b/frontend/src/routes/(app)/inspiration/+page.svelte new file mode 100644 index 0000000..f8eaf2a --- /dev/null +++ b/frontend/src/routes/(app)/inspiration/+page.svelte @@ -0,0 +1,199 @@ + + +
+
+
+
+

+ Musical Inspiration +

+

+ This project was mainly coded to the following tracks +

+
+ +
+ + + + + + + + + FOISX's Soundtrack + + + The albums and tracks that fueled the development of EMLy + + + +
+ {#each inspirationTracks as track} +
+
+ +
+
+
+

{track.name}

+

+ {track.artist} +

+
+ +
+
+ {/each} +
+
+
+ + + + +
+ +
+

The Soundtrack

+

+ These are just a small sample of what helped inspire the project. + Although they represent a wide variety of emotions, themes and genres, some exploring deep meanings + of betrayal, personal struggles, and introspection, they provided solace and strength to the main developer + during challenging times. +
+ Music has a unique way of transforming pain into creative energy.. +

+
+
+
+
+ + +
+

+ Made with + + and + +

+

+ GUI: {config ? `v${config.GUISemver}` : "N/A"} • + SDK: {config ? `v${config.SDKDecoderSemver}` : "N/A"} +

+
+
+
+ + diff --git a/frontend/src/routes/(app)/inspiration/+page.ts b/frontend/src/routes/(app)/inspiration/+page.ts new file mode 100644 index 0000000..ed92a80 --- /dev/null +++ b/frontend/src/routes/(app)/inspiration/+page.ts @@ -0,0 +1,19 @@ +import type { PageLoad } from './$types'; +import { GetConfig } from "$lib/wailsjs/go/main/App"; +import { browser } from '$app/environment'; + +export const load = (async () => { + if (!browser) return { config: null }; + + try { + const configRoot = await GetConfig(); + return { + config: configRoot.EMLy + }; + } catch (e) { + console.error("Failed to load config for inspiration", e); + return { + config: null + }; + } +}) satisfies PageLoad; diff --git a/frontend/src/routes/(app)/settings/+page.svelte b/frontend/src/routes/(app)/settings/+page.svelte index d5d9d9c..66d808a 100644 --- a/frontend/src/routes/(app)/settings/+page.svelte +++ b/frontend/src/routes/(app)/settings/+page.svelte @@ -25,7 +25,7 @@ import { setLocale } from "$lib/paraglide/runtime"; import { mailState } from "$lib/stores/mail-state.svelte.js"; import { dev } from '$app/environment'; - import { ExportSettings, ImportSettings, CheckForUpdates, DownloadUpdate, InstallUpdate, GetUpdateStatus } from "$lib/wailsjs/go/main/App"; + import { ExportSettings, ImportSettings, CheckForUpdates, DownloadUpdate, InstallUpdate, GetUpdateStatus, SetUpdateCheckerEnabled } from "$lib/wailsjs/go/main/App"; import { EventsOn, EventsOff } from "$lib/wailsjs/runtime/runtime"; let { data } = $props(); @@ -40,6 +40,7 @@ previewFileSupportedTypes: ["jpg", "jpeg", "png"], enableAttachedDebuggerProtection: true, useDarkEmailViewer: true, + enableUpdateChecker: true, }; async function setLanguage( @@ -72,6 +73,8 @@ s.enableAttachedDebuggerProtection ?? defaults.enableAttachedDebuggerProtection ?? true, useDarkEmailViewer: s.useDarkEmailViewer ?? defaults.useDarkEmailViewer ?? true, + enableUpdateChecker: + s.enableUpdateChecker ?? defaults.enableUpdateChecker ?? true, }; } @@ -82,6 +85,7 @@ !!a.useBuiltinPDFViewer === !!b.useBuiltinPDFViewer && !!a.enableAttachedDebuggerProtection === !!b.enableAttachedDebuggerProtection && !!a.useDarkEmailViewer === !!b.useDarkEmailViewer && + !!a.enableUpdateChecker === !!b.enableUpdateChecker && JSON.stringify(a.previewFileSupportedTypes?.sort()) === JSON.stringify(b.previewFileSupportedTypes?.sort()) ); @@ -180,6 +184,23 @@ })(); }); + // Sync update checker setting to backend config.ini + let previousUpdateCheckerEnabled = form.enableUpdateChecker; + $effect(() => { + (async () => { + if (!browser) return; + if (form.enableUpdateChecker !== previousUpdateCheckerEnabled) { + try { + await SetUpdateCheckerEnabled(form.enableUpdateChecker ?? true); + LogDebug(`Update checker ${form.enableUpdateChecker ? 'enabled' : 'disabled'}`); + } catch (err) { + console.error('Failed to sync update checker setting:', err); + } + previousUpdateCheckerEnabled = form.enableUpdateChecker; + } + })(); + }); + async function exportSettings() { try { const settingsJSON = JSON.stringify(form, null, 2); @@ -568,6 +589,7 @@ + {#if form.enableUpdateChecker} Updates @@ -587,10 +609,15 @@ Update Available + {:else if updateStatus.errorMessage && updateStatus.lastCheckTime} +
+ + Check failed +
{:else if updateStatus.lastCheckTime}
- Up to date + No updates found
{/if} @@ -695,6 +722,7 @@
+ {/if} {#if $dangerZoneEnabled || dev} @@ -811,6 +839,25 @@ +
+
+ +
+ Check for application updates from network share +
+
+ +
+
+ Info: When enabled, the app will check for updates from your configured network share. Disable this if you manage updates manually or don't have network access. +
+ +
GUI: {config ? `${config.GUISemver} (${config.GUIReleaseChannel})`