diff --git a/config/widgets.php b/config/widgets.php
index c65d3b52..df57e8f3 100644
--- a/config/widgets.php
+++ b/config/widgets.php
@@ -60,7 +60,7 @@ return [
'title' => 'Waehrungskurse',
'icon' => 'FX',
'zone' => 'sidebar',
- 'default_enabled' => false,
+ 'default_enabled' => true,
'supports_public_home' => false,
'summary' => 'Aktuelle FX-Kurse pruefen und bei Bedarf manuell aktualisieren.',
'content' => 'Ein API-Abruf wird nur ausgefuehrt, wenn der letzte Snapshot alt genug ist, ausser bei explizitem Force.',
diff --git a/partials/desktop/shell.php b/partials/desktop/shell.php
index 9ebeb87d..31aed278 100644
--- a/partials/desktop/shell.php
+++ b/partials/desktop/shell.php
@@ -158,7 +158,12 @@ $renderStartIcon = static function (array $profile): string {
-
+
diff --git a/public/assets/desktop/desktop.js b/public/assets/desktop/desktop.js
index 7f2800af..74542115 100644
--- a/public/assets/desktop/desktop.js
+++ b/public/assets/desktop/desktop.js
@@ -1736,6 +1736,18 @@ if (payloadNode) {
setAccountMenuOpen(accountMenu.hidden);
});
+ document.querySelectorAll('.tray-pill[data-tray-app-id]').forEach((button) => {
+ button.addEventListener('click', () => {
+ const appId = button.getAttribute('data-tray-app-id') || '';
+ const app = findAppById(appId);
+ if (app) {
+ openApp(app);
+ setStartMenuOpen(false);
+ setAccountMenuOpen(false);
+ }
+ });
+ });
+
debugToggleNode?.addEventListener('click', () => {
toggleDebugWindow();
});
diff --git a/src/App/App.php b/src/App/App.php
index 158010f0..6c2ceebb 100644
--- a/src/App/App.php
+++ b/src/App/App.php
@@ -56,6 +56,19 @@ final class App
$apps = AppIconResolver::decorate($enabledApps, $activeSkin);
$windows = (new WindowManager($registry))->defaultWindows();
$activeWidgetIds = array_values(array_map('strval', (array) ($userSettings['widgets']['active_ids'] ?? DesktopState::defaultWidgetIds())));
+ $trayItems = DesktopState::trayItems();
+ foreach ($enabledApps as $app) {
+ if (empty($app['supports_tray']) || (string) ($app['app_id'] ?? '') !== 'fx-rates') {
+ continue;
+ }
+
+ $trayItems[] = [
+ 'id' => 'app-' . (string) $app['app_id'],
+ 'label' => (string) ($app['icon'] ?? 'FX'),
+ 'app_id' => (string) ($app['app_id'] ?? ''),
+ 'title' => (string) ($app['title'] ?? ''),
+ ];
+ }
$displayName = (string) ($authUser['name'] ?? $authUser['username'] ?? 'Gast');
$storageScope = $isAuthenticated
? strtolower((string) ($authUser['username'] ?? $authUser['sub'] ?? 'user'))
@@ -94,7 +107,7 @@ final class App
'registry' => $widgetRegistry->all(),
'active_ids' => $activeWidgetIds,
],
- 'tray' => DesktopState::trayItems(),
+ 'tray' => $trayItems,
'session' => [
'display_name' => $displayName,
'authenticated' => $isAuthenticated,
diff --git a/src/App/UserSelfManagementService.php b/src/App/UserSelfManagementService.php
index 329338b9..bce06918 100644
--- a/src/App/UserSelfManagementService.php
+++ b/src/App/UserSelfManagementService.php
@@ -196,6 +196,10 @@ final class UserSelfManagementService
static fn (array $app): string => (string) ($app['app_id'] ?? ''),
$visibleApps
)));
+ $defaultEnabledAppIds = array_values(array_filter(array_map(
+ static fn (array $app): ?string => ($app['enabled_by_default'] ?? false) ? (string) ($app['app_id'] ?? '') : null,
+ $visibleApps
+ )));
$defaultWidgetIds = array_values(array_filter(array_map(
static fn (array $widget): ?string => ($widget['default_enabled'] ?? false) ? (string) ($widget['widget_id'] ?? '') : null,
$widgets
@@ -204,13 +208,35 @@ final class UserSelfManagementService
static fn (array $widget): string => (string) ($widget['widget_id'] ?? ''),
$widgets
)));
+ $storedEnabledAppIds = array_key_exists('enabled_ids', (array) ($settings['apps'] ?? []))
+ ? $this->sanitizeSelection($settings['apps']['enabled_ids'] ?? [], $visibleAppIds)
+ : $visibleAppIds;
+ $knownAppIds = $this->sanitizeSelection(
+ $settings['apps']['known_ids'] ?? [],
+ $visibleAppIds
+ );
+ $newDefaultAppIds = array_values(array_filter(
+ $defaultEnabledAppIds,
+ static fn (string $appId): bool => !in_array($appId, $knownAppIds, true)
+ ));
$enabledAppIds = $this->sanitizeSelection(
- $settings['apps']['enabled_ids'] ?? $visibleAppIds,
+ array_merge($storedEnabledAppIds, $newDefaultAppIds),
$visibleAppIds,
[self::APP_ID]
);
+ $storedActiveWidgetIds = array_key_exists('active_ids', (array) ($settings['widgets'] ?? []))
+ ? $this->sanitizeSelection($settings['widgets']['active_ids'] ?? [], $allWidgetIds)
+ : $defaultWidgetIds;
+ $knownWidgetIds = $this->sanitizeSelection(
+ $settings['widgets']['known_ids'] ?? [],
+ $allWidgetIds
+ );
+ $newDefaultWidgetIds = array_values(array_filter(
+ $defaultWidgetIds,
+ static fn (string $widgetId): bool => !in_array($widgetId, $knownWidgetIds, true)
+ ));
$activeWidgetIds = $this->sanitizeSelection(
- $settings['widgets']['active_ids'] ?? $defaultWidgetIds,
+ array_merge($storedActiveWidgetIds, $newDefaultWidgetIds),
$allWidgetIds
);
$profile = [];
@@ -240,9 +266,11 @@ final class UserSelfManagementService
],
'apps' => [
'enabled_ids' => $enabledAppIds,
+ 'known_ids' => $visibleAppIds,
],
'widgets' => [
'active_ids' => $activeWidgetIds,
+ 'known_ids' => $allWidgetIds,
],
'integration' => [
'ldap_write_status' => 'planned',