diff --git a/src/components/dialogs/CustomNavDialog.vue b/src/components/dialogs/CustomNavDialog.vue index 3ae26602..56f29c99 100644 --- a/src/components/dialogs/CustomNavDialog.vue +++ b/src/components/dialogs/CustomNavDialog.vue @@ -162,6 +162,7 @@ import { DragDropProvider } from '@dnd-kit/vue'; import { isSortable } from '@dnd-kit/vue/sortable'; import { openExternalLink } from '@/shared/utils/common'; + import { storeToRefs } from 'pinia'; import { useI18n } from 'vue-i18n'; import dayjs from 'dayjs'; @@ -172,7 +173,7 @@ import { isToolNavKey } from '../../shared/constants'; import { navDefinitions } from '../../shared/constants/ui.js'; import { DASHBOARD_NAV_KEY_PREFIX, DEFAULT_DASHBOARD_ICON } from '../../shared/constants/dashboard'; - import { useDashboardStore, useModalStore } from '../../stores'; + import { useDashboardStore, useModalStore, useNotificationsSettingsStore } from '../../stores'; import SortableTreeNode from './SortableTreeNode.vue'; @@ -207,6 +208,7 @@ const { t } = useI18n(); const dashboardStore = useDashboardStore(); const modalStore = useModalStore(); + const { notificationLayout } = storeToRefs(useNotificationsSettingsStore()); const cloneLayout = (source) => { if (!Array.isArray(source)) return []; @@ -270,7 +272,12 @@ const map = new Map(); const source = props.definitions?.length ? props.definitions : navDefinitions; source.forEach((def) => { - if (def?.key) map.set(def.key, def); + if (def?.key) { + if (def.key === 'notification' && notificationLayout.value === 'notification-center') { + return; + } + map.set(def.key, def); + } }); return map; }); diff --git a/src/components/nav-menu/composables/useNavLayout.js b/src/components/nav-menu/composables/useNavLayout.js index 1413f653..54466625 100644 --- a/src/components/nav-menu/composables/useNavLayout.js +++ b/src/components/nav-menu/composables/useNavLayout.js @@ -30,6 +30,8 @@ import { } from '../navConfigUtils'; import { normalizeHiddenKeys, sanitizeLayout } from '../navMenuUtils'; +import { useNotificationsSettingsStore } from '../../../stores/settings/notifications'; + export function useNavLayout({ t, locale, @@ -42,15 +44,17 @@ export function useNavLayout({ const navLayout = ref([]); const navLayoutReady = ref(false); const navHiddenKeys = ref([]); + const notificationsSettingsStore = useNotificationsSettingsStore(); const allNavDefinitions = computed(() => [ ...navDefinitions, ...dashboardStore.getDashboardNavDefinitions() ]); - const navDefinitionMap = computed(() => - createNavDefinitionMap(allNavDefinitions.value) - ); + const navDefinitionMap = computed(() => { + const map = createNavDefinitionMap(allNavDefinitions.value); + return map; + }); // Tool nav items are add/remove only; they no longer participate in hidden state. const getDefaultHiddenKeys = (layout = []) => { @@ -60,9 +64,24 @@ export function useNavLayout({ const createDefaultNavLayout = () => createBaseDefaultNavLayout(t); - const menuItems = computed(() => - buildMenuItems(navLayout.value, navDefinitionMap.value, t) - ); + const menuItems = computed(() => { + const items = buildMenuItems(navLayout.value, navDefinitionMap.value, t); + if (notificationsSettingsStore.notificationLayout === 'notification-center') { + return items.filter((item) => { + if (item.index === 'notification') { + return false; + } + if (item.children) { + item.children = item.children.filter( + (child) => child.index !== 'notification' + ); + return item.children.length > 0; + } + return true; + }); + } + return items; + }); const getFirstNavEntryLocal = (layout) => { return findFirstNavEntry(layout, navDefinitionMap.value); diff --git a/src/localization/en.json b/src/localization/en.json index 919bebb3..d79439a0 100644 --- a/src/localization/en.json +++ b/src/localization/en.json @@ -950,6 +950,9 @@ "notifications": { "notifications": { "header": "Notifications", + "layout": "Notification Layout", + "layout_notification_center": "Notification Center", + "layout_table": "Table", "notification_filter": "Notification Filter", "test_notification": "Test Notification", "steamvr_notifications": { diff --git a/src/stores/settings/notifications.js b/src/stores/settings/notifications.js index cbbfb991..a2292b7c 100644 --- a/src/stores/settings/notifications.js +++ b/src/stores/settings/notifications.js @@ -119,6 +119,7 @@ export const useNotificationsSettingsStore = defineStore( const notificationTTSTest = ref(''); const notificationPosition = ref('topCenter'); const notificationTimeout = ref(3000); + const notificationLayout = ref('notification-center'); async function initNotificationsSettings() { const [ @@ -136,7 +137,8 @@ export const useNotificationsSettingsStore = defineStore( sharedFeedFiltersConfig, notificationTTSVoiceConfig, notificationPositionConfig, - notificationTimeoutConfig + notificationTimeoutConfig, + notificationLayoutConfig ] = await Promise.all([ configRepository.getString('VRCX_overlayToast', 'Game Running'), configRepository.getBool('VRCX_overlayNotifications', true), @@ -158,7 +160,11 @@ export const useNotificationsSettingsStore = defineStore( 'VRCX_notificationPosition', 'topCenter' ), - configRepository.getString('VRCX_notificationTimeout', '3000') + configRepository.getString('VRCX_notificationTimeout', '3000'), + configRepository.getString( + 'VRCX_notificationLayout', + 'notification-center' + ) ]); overlayToast.value = overlayToastConfig; @@ -177,6 +183,7 @@ export const useNotificationsSettingsStore = defineStore( TTSvoices.value = speechSynthesis.getVoices(); notificationPosition.value = notificationPositionConfig; notificationTimeout.value = Number(notificationTimeoutConfig); + notificationLayout.value = notificationLayoutConfig; initSharedFeedFilters(); @@ -424,6 +431,14 @@ export const useNotificationsSettingsStore = defineStore( vrStore.updateVRConfigVars(); } + /** + * @param {string} value + */ + function setNotificationLayout(value) { + notificationLayout.value = value; + configRepository.setString('VRCX_notificationLayout', value); + } + function promptNotificationTimeout() { modalStore .prompt({ @@ -470,6 +485,7 @@ export const useNotificationsSettingsStore = defineStore( notificationTTSTest, notificationPosition, notificationTimeout, + notificationLayout, setOverlayToast, setOpenVR, @@ -488,6 +504,7 @@ export const useNotificationsSettingsStore = defineStore( testNotificationTTS, speak, changeNotificationPosition, + setNotificationLayout, setNotificationTimeout, promptNotificationTimeout }; diff --git a/src/stores/ui.js b/src/stores/ui.js index 960c436e..96d2a8e9 100644 --- a/src/stores/ui.js +++ b/src/stores/ui.js @@ -16,6 +16,7 @@ import { showAvatarDialog } from '../coordinators/avatarCoordinator'; import { showUserDialog } from '../coordinators/userCoordinator'; import { useInstanceStore } from './instance'; import { useNotificationStore } from './notification'; +import { useNotificationsSettingsStore } from './settings/notifications'; import { useSearchStore } from './search'; import { useUserStore } from './user'; import { useWorldStore } from './world'; @@ -286,6 +287,11 @@ export const useUiStore = defineStore('Ui', () => { const name = String(routeName); removeNotify(name); if (name === 'notification') { + const notificationsSettingsStore = useNotificationsSettingsStore(); + if (notificationsSettingsStore.notificationLayout === 'notification-center') { + router.replace({ name: 'feed' }); + return; + } notificationStore.clearUnseenNotifications(); } } @@ -314,10 +320,19 @@ export const useUiStore = defineStore('Ui', () => { } function updateTrayIconNotify(force = false) { - const newState = - appearanceSettings.notificationIconDot && - (notifiedMenus.value.includes('notification') || - notifiedMenus.value.includes('friend-log')); + const notificationsSettingsStore = useNotificationsSettingsStore(); + let newState; + if (notificationsSettingsStore.notificationLayout === 'notification-center') { + newState = + appearanceSettings.notificationIconDot && + (notificationStore.hasUnseenNotifications || + notifiedMenus.value.includes('friend-log')); + } else { + newState = + appearanceSettings.notificationIconDot && + (notifiedMenus.value.includes('notification') || + notifiedMenus.value.includes('friend-log')); + } if (trayIconNotify.value !== newState || force) { trayIconNotify.value = newState; diff --git a/src/views/Settings/components/Tabs/NotificationsTab.vue b/src/views/Settings/components/Tabs/NotificationsTab.vue index 7cfc5266..3d420613 100644 --- a/src/views/Settings/components/Tabs/NotificationsTab.vue +++ b/src/views/Settings/components/Tabs/NotificationsTab.vue @@ -1,6 +1,24 @@