refactor(notifications): sync toast position with legacy settings

This change ensures that Nuxt UI notifications respect the display position configured in the legacy webGUI settings.

Backend:
- Added `NotificationSettings` to the GraphQL model.
- Exposed `settings` field on the `Notifications` resolver.
- Implemented `getSettings` in `NotificationsService` to read `notify.position` from the Dynamix store.

Frontend:
- Added `getNotificationSettings` GraphQL query.
- Updated `mount-engine.ts` to fetch settings before mounting.
- Mapped legacy position values (e.g., 'center') to Nuxt UI compatible values (e.g., 'top-center').
This commit is contained in:
Ajit Mehrotra
2025-12-05 15:31:17 -05:00
parent cd201626b7
commit 1587d589b6
6 changed files with 2786 additions and 1794 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -99,6 +99,14 @@ export class NotificationCounts {
total!: number;
}
@ObjectType('NotificationSettings')
export class NotificationSettings {
@Field()
@IsString()
@IsNotEmpty()
position!: string;
}
@ObjectType('NotificationOverview')
export class NotificationOverview {
@Field(() => NotificationCounts)
@@ -170,4 +178,8 @@ export class Notifications extends Node {
})
@IsNotEmpty()
warningsAndAlerts!: Notification[];
@Field(() => NotificationSettings)
@IsNotEmpty()
settings!: NotificationSettings;
}

View File

@@ -13,6 +13,7 @@ import {
NotificationImportance,
NotificationOverview,
Notifications,
NotificationSettings,
NotificationType,
} from '@app/unraid-api/graph/resolvers/notifications/notifications.model.js';
import { NotificationsService } from '@app/unraid-api/graph/resolvers/notifications/notifications.service.js';
@@ -41,6 +42,11 @@ export class NotificationsResolver {
return this.notificationsService.getOverview();
}
@ResolveField(() => NotificationSettings)
public settings(): NotificationSettings {
return this.notificationsService.getSettings();
}
@ResolveField(() => [Notification])
public async list(
@Args('filter', { type: () => NotificationFilter })

View File

@@ -25,6 +25,7 @@ import {
NotificationFilter,
NotificationImportance,
NotificationOverview,
NotificationSettings,
NotificationType,
} from '@app/unraid-api/graph/resolvers/notifications/notifications.model.js';
import { validateObject } from '@app/unraid-api/graph/resolvers/validation.utils.js';
@@ -174,6 +175,13 @@ export class NotificationsService {
return structuredClone(NotificationsService.overview);
}
public getSettings(): NotificationSettings {
const { notify } = getters.dynamix();
return {
position: notify?.position ?? 'top-right',
};
}
private publishOverview(overview = NotificationsService.overview) {
return pubsub.publish(PUBSUB_CHANNEL.NOTIFICATION_OVERVIEW, {
notificationsOverview: overview,

View File

@@ -1,3 +1,5 @@
import gql from 'graphql-tag';
import { graphql } from '~/composables/gql/gql';
export const NOTIFICATION_FRAGMENT = graphql(/* GraphQL */ `
@@ -34,6 +36,17 @@ export const getNotifications = graphql(/* GraphQL */ `
}
`);
export const getNotificationSettings = gql`
query GetNotificationSettings {
notifications {
id
settings {
position
}
}
}
`;
export const warningsAndAlerts = graphql(/* GraphQL */ `
query WarningAndAlertNotifications {
notifications {

View File

@@ -8,6 +8,7 @@ import { componentMappings } from '@/components/Wrapper/component-registry';
import { client } from '~/helpers/create-apollo-client';
import { createI18nInstance, ensureLocale, getWindowLocale } from '~/helpers/i18n-loader';
import { getNotificationSettings } from '~/components/Notifications/graphql/notification.query';
// Import Pinia for use in Vue apps
import { globalPinia } from '~/store/globalPinia';
import { useThemeStore } from '~/store/theme';
@@ -122,6 +123,44 @@ export async function mountUnifiedApp() {
const themeStore = useThemeStore();
// Fetch notification settings
type ToastPosition =
| 'top-left'
| 'top-right'
| 'bottom-left'
| 'bottom-right'
| 'top-center'
| 'bottom-center';
interface NotificationSettingsResponse {
notifications?: {
settings?: {
position?: string;
};
};
}
let toasterPosition: ToastPosition = appConfig.ui.toaster.position as ToastPosition;
try {
const { data } = await apolloClient.query<NotificationSettingsResponse>({
query: getNotificationSettings,
fetchPolicy: 'network-only',
});
const legacyPosition = data?.notifications?.settings?.position;
console.log('[UnifiedMount] Legacy position:', legacyPosition);
if (legacyPosition) {
const map: Record<string, ToastPosition> = {
'top-left': 'top-left',
'top-right': 'top-right',
'bottom-left': 'bottom-left',
'bottom-right': 'bottom-right',
center: 'top-center',
};
toasterPosition = map[legacyPosition] || toasterPosition;
}
} catch (e) {
console.error('[UnifiedMount] Failed to fetch notification settings', e);
}
// Mount the app to establish context
let rootElement = document.getElementById('unraid-unified-root');
if (!rootElement) {
@@ -225,7 +264,10 @@ export async function mountUnifiedApp() {
UApp,
{
portal: portalTarget,
toaster: appConfig.ui.toaster,
toaster: {
...appConfig.ui.toaster,
position: toasterPosition,
},
},
{
default: () => h(component, props),