mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 06:56:04 +02:00
feat: add notification layout setting
This commit is contained in:
@@ -162,6 +162,7 @@
|
|||||||
import { DragDropProvider } from '@dnd-kit/vue';
|
import { DragDropProvider } from '@dnd-kit/vue';
|
||||||
import { isSortable } from '@dnd-kit/vue/sortable';
|
import { isSortable } from '@dnd-kit/vue/sortable';
|
||||||
import { openExternalLink } from '@/shared/utils/common';
|
import { openExternalLink } from '@/shared/utils/common';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
@@ -172,7 +173,7 @@
|
|||||||
import { isToolNavKey } from '../../shared/constants';
|
import { isToolNavKey } from '../../shared/constants';
|
||||||
import { navDefinitions } from '../../shared/constants/ui.js';
|
import { navDefinitions } from '../../shared/constants/ui.js';
|
||||||
import { DASHBOARD_NAV_KEY_PREFIX, DEFAULT_DASHBOARD_ICON } from '../../shared/constants/dashboard';
|
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';
|
import SortableTreeNode from './SortableTreeNode.vue';
|
||||||
|
|
||||||
@@ -207,6 +208,7 @@
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
const modalStore = useModalStore();
|
const modalStore = useModalStore();
|
||||||
|
const { notificationLayout } = storeToRefs(useNotificationsSettingsStore());
|
||||||
|
|
||||||
const cloneLayout = (source) => {
|
const cloneLayout = (source) => {
|
||||||
if (!Array.isArray(source)) return [];
|
if (!Array.isArray(source)) return [];
|
||||||
@@ -270,7 +272,12 @@
|
|||||||
const map = new Map();
|
const map = new Map();
|
||||||
const source = props.definitions?.length ? props.definitions : navDefinitions;
|
const source = props.definitions?.length ? props.definitions : navDefinitions;
|
||||||
source.forEach((def) => {
|
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;
|
return map;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import {
|
|||||||
} from '../navConfigUtils';
|
} from '../navConfigUtils';
|
||||||
import { normalizeHiddenKeys, sanitizeLayout } from '../navMenuUtils';
|
import { normalizeHiddenKeys, sanitizeLayout } from '../navMenuUtils';
|
||||||
|
|
||||||
|
import { useNotificationsSettingsStore } from '../../../stores/settings/notifications';
|
||||||
|
|
||||||
export function useNavLayout({
|
export function useNavLayout({
|
||||||
t,
|
t,
|
||||||
locale,
|
locale,
|
||||||
@@ -42,15 +44,17 @@ export function useNavLayout({
|
|||||||
const navLayout = ref([]);
|
const navLayout = ref([]);
|
||||||
const navLayoutReady = ref(false);
|
const navLayoutReady = ref(false);
|
||||||
const navHiddenKeys = ref([]);
|
const navHiddenKeys = ref([]);
|
||||||
|
const notificationsSettingsStore = useNotificationsSettingsStore();
|
||||||
|
|
||||||
const allNavDefinitions = computed(() => [
|
const allNavDefinitions = computed(() => [
|
||||||
...navDefinitions,
|
...navDefinitions,
|
||||||
...dashboardStore.getDashboardNavDefinitions()
|
...dashboardStore.getDashboardNavDefinitions()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const navDefinitionMap = computed(() =>
|
const navDefinitionMap = computed(() => {
|
||||||
createNavDefinitionMap(allNavDefinitions.value)
|
const map = createNavDefinitionMap(allNavDefinitions.value);
|
||||||
);
|
return map;
|
||||||
|
});
|
||||||
|
|
||||||
// Tool nav items are add/remove only; they no longer participate in hidden state.
|
// Tool nav items are add/remove only; they no longer participate in hidden state.
|
||||||
const getDefaultHiddenKeys = (layout = []) => {
|
const getDefaultHiddenKeys = (layout = []) => {
|
||||||
@@ -60,9 +64,24 @@ export function useNavLayout({
|
|||||||
|
|
||||||
const createDefaultNavLayout = () => createBaseDefaultNavLayout(t);
|
const createDefaultNavLayout = () => createBaseDefaultNavLayout(t);
|
||||||
|
|
||||||
const menuItems = computed(() =>
|
const menuItems = computed(() => {
|
||||||
buildMenuItems(navLayout.value, navDefinitionMap.value, t)
|
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) => {
|
const getFirstNavEntryLocal = (layout) => {
|
||||||
return findFirstNavEntry(layout, navDefinitionMap.value);
|
return findFirstNavEntry(layout, navDefinitionMap.value);
|
||||||
|
|||||||
@@ -950,6 +950,9 @@
|
|||||||
"notifications": {
|
"notifications": {
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"header": "Notifications",
|
"header": "Notifications",
|
||||||
|
"layout": "Notification Layout",
|
||||||
|
"layout_notification_center": "Notification Center",
|
||||||
|
"layout_table": "Table",
|
||||||
"notification_filter": "Notification Filter",
|
"notification_filter": "Notification Filter",
|
||||||
"test_notification": "Test Notification",
|
"test_notification": "Test Notification",
|
||||||
"steamvr_notifications": {
|
"steamvr_notifications": {
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ export const useNotificationsSettingsStore = defineStore(
|
|||||||
const notificationTTSTest = ref('');
|
const notificationTTSTest = ref('');
|
||||||
const notificationPosition = ref('topCenter');
|
const notificationPosition = ref('topCenter');
|
||||||
const notificationTimeout = ref(3000);
|
const notificationTimeout = ref(3000);
|
||||||
|
const notificationLayout = ref('notification-center');
|
||||||
|
|
||||||
async function initNotificationsSettings() {
|
async function initNotificationsSettings() {
|
||||||
const [
|
const [
|
||||||
@@ -136,7 +137,8 @@ export const useNotificationsSettingsStore = defineStore(
|
|||||||
sharedFeedFiltersConfig,
|
sharedFeedFiltersConfig,
|
||||||
notificationTTSVoiceConfig,
|
notificationTTSVoiceConfig,
|
||||||
notificationPositionConfig,
|
notificationPositionConfig,
|
||||||
notificationTimeoutConfig
|
notificationTimeoutConfig,
|
||||||
|
notificationLayoutConfig
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
configRepository.getString('VRCX_overlayToast', 'Game Running'),
|
configRepository.getString('VRCX_overlayToast', 'Game Running'),
|
||||||
configRepository.getBool('VRCX_overlayNotifications', true),
|
configRepository.getBool('VRCX_overlayNotifications', true),
|
||||||
@@ -158,7 +160,11 @@ export const useNotificationsSettingsStore = defineStore(
|
|||||||
'VRCX_notificationPosition',
|
'VRCX_notificationPosition',
|
||||||
'topCenter'
|
'topCenter'
|
||||||
),
|
),
|
||||||
configRepository.getString('VRCX_notificationTimeout', '3000')
|
configRepository.getString('VRCX_notificationTimeout', '3000'),
|
||||||
|
configRepository.getString(
|
||||||
|
'VRCX_notificationLayout',
|
||||||
|
'notification-center'
|
||||||
|
)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
overlayToast.value = overlayToastConfig;
|
overlayToast.value = overlayToastConfig;
|
||||||
@@ -177,6 +183,7 @@ export const useNotificationsSettingsStore = defineStore(
|
|||||||
TTSvoices.value = speechSynthesis.getVoices();
|
TTSvoices.value = speechSynthesis.getVoices();
|
||||||
notificationPosition.value = notificationPositionConfig;
|
notificationPosition.value = notificationPositionConfig;
|
||||||
notificationTimeout.value = Number(notificationTimeoutConfig);
|
notificationTimeout.value = Number(notificationTimeoutConfig);
|
||||||
|
notificationLayout.value = notificationLayoutConfig;
|
||||||
|
|
||||||
initSharedFeedFilters();
|
initSharedFeedFilters();
|
||||||
|
|
||||||
@@ -424,6 +431,14 @@ export const useNotificationsSettingsStore = defineStore(
|
|||||||
vrStore.updateVRConfigVars();
|
vrStore.updateVRConfigVars();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} value
|
||||||
|
*/
|
||||||
|
function setNotificationLayout(value) {
|
||||||
|
notificationLayout.value = value;
|
||||||
|
configRepository.setString('VRCX_notificationLayout', value);
|
||||||
|
}
|
||||||
|
|
||||||
function promptNotificationTimeout() {
|
function promptNotificationTimeout() {
|
||||||
modalStore
|
modalStore
|
||||||
.prompt({
|
.prompt({
|
||||||
@@ -470,6 +485,7 @@ export const useNotificationsSettingsStore = defineStore(
|
|||||||
notificationTTSTest,
|
notificationTTSTest,
|
||||||
notificationPosition,
|
notificationPosition,
|
||||||
notificationTimeout,
|
notificationTimeout,
|
||||||
|
notificationLayout,
|
||||||
|
|
||||||
setOverlayToast,
|
setOverlayToast,
|
||||||
setOpenVR,
|
setOpenVR,
|
||||||
@@ -488,6 +504,7 @@ export const useNotificationsSettingsStore = defineStore(
|
|||||||
testNotificationTTS,
|
testNotificationTTS,
|
||||||
speak,
|
speak,
|
||||||
changeNotificationPosition,
|
changeNotificationPosition,
|
||||||
|
setNotificationLayout,
|
||||||
setNotificationTimeout,
|
setNotificationTimeout,
|
||||||
promptNotificationTimeout
|
promptNotificationTimeout
|
||||||
};
|
};
|
||||||
|
|||||||
+16
-1
@@ -16,6 +16,7 @@ import { showAvatarDialog } from '../coordinators/avatarCoordinator';
|
|||||||
import { showUserDialog } from '../coordinators/userCoordinator';
|
import { showUserDialog } from '../coordinators/userCoordinator';
|
||||||
import { useInstanceStore } from './instance';
|
import { useInstanceStore } from './instance';
|
||||||
import { useNotificationStore } from './notification';
|
import { useNotificationStore } from './notification';
|
||||||
|
import { useNotificationsSettingsStore } from './settings/notifications';
|
||||||
import { useSearchStore } from './search';
|
import { useSearchStore } from './search';
|
||||||
import { useUserStore } from './user';
|
import { useUserStore } from './user';
|
||||||
import { useWorldStore } from './world';
|
import { useWorldStore } from './world';
|
||||||
@@ -286,6 +287,11 @@ export const useUiStore = defineStore('Ui', () => {
|
|||||||
const name = String(routeName);
|
const name = String(routeName);
|
||||||
removeNotify(name);
|
removeNotify(name);
|
||||||
if (name === 'notification') {
|
if (name === 'notification') {
|
||||||
|
const notificationsSettingsStore = useNotificationsSettingsStore();
|
||||||
|
if (notificationsSettingsStore.notificationLayout === 'notification-center') {
|
||||||
|
router.replace({ name: 'feed' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
notificationStore.clearUnseenNotifications();
|
notificationStore.clearUnseenNotifications();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -314,10 +320,19 @@ export const useUiStore = defineStore('Ui', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateTrayIconNotify(force = false) {
|
function updateTrayIconNotify(force = false) {
|
||||||
const newState =
|
const notificationsSettingsStore = useNotificationsSettingsStore();
|
||||||
|
let newState;
|
||||||
|
if (notificationsSettingsStore.notificationLayout === 'notification-center') {
|
||||||
|
newState =
|
||||||
|
appearanceSettings.notificationIconDot &&
|
||||||
|
(notificationStore.hasUnseenNotifications ||
|
||||||
|
notifiedMenus.value.includes('friend-log'));
|
||||||
|
} else {
|
||||||
|
newState =
|
||||||
appearanceSettings.notificationIconDot &&
|
appearanceSettings.notificationIconDot &&
|
||||||
(notifiedMenus.value.includes('notification') ||
|
(notifiedMenus.value.includes('notification') ||
|
||||||
notifiedMenus.value.includes('friend-log'));
|
notifiedMenus.value.includes('friend-log'));
|
||||||
|
}
|
||||||
|
|
||||||
if (trayIconNotify.value !== newState || force) {
|
if (trayIconNotify.value !== newState || force) {
|
||||||
trayIconNotify.value = newState;
|
trayIconNotify.value = newState;
|
||||||
|
|||||||
@@ -1,6 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-10 py-2">
|
<div class="flex flex-col gap-10 py-2">
|
||||||
<SettingsGroup :title="t('view.settings.notifications.notifications.header')">
|
<SettingsGroup :title="t('view.settings.notifications.notifications.header')">
|
||||||
|
<SettingsItem :label="t('view.settings.notifications.notifications.layout')">
|
||||||
|
<Select
|
||||||
|
:model-value="notificationLayout"
|
||||||
|
@update:modelValue="setNotificationLayout">
|
||||||
|
<SelectTrigger size="sm">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="notification-center">{{
|
||||||
|
t('view.settings.notifications.notifications.layout_notification_center')
|
||||||
|
}}</SelectItem>
|
||||||
|
<SelectItem value="table">{{
|
||||||
|
t('view.settings.notifications.notifications.layout_table')
|
||||||
|
}}</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
<SettingsItem :label="t('view.settings.notifications.notifications.notification_filter')">
|
<SettingsItem :label="t('view.settings.notifications.notifications.notification_filter')">
|
||||||
<Button size="sm" variant="outline" @click="showNotyFeedFiltersDialog">{{
|
<Button size="sm" variant="outline" @click="showNotyFeedFiltersDialog">{{
|
||||||
t('view.settings.notifications.notifications.notification_filter')
|
t('view.settings.notifications.notifications.notification_filter')
|
||||||
@@ -163,7 +181,8 @@
|
|||||||
notificationTTSNickName,
|
notificationTTSNickName,
|
||||||
isTestTTSVisible,
|
isTestTTSVisible,
|
||||||
notificationTTSTest,
|
notificationTTSTest,
|
||||||
TTSvoices
|
TTSvoices,
|
||||||
|
notificationLayout
|
||||||
} = storeToRefs(notificationsSettingsStore);
|
} = storeToRefs(notificationsSettingsStore);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -173,7 +192,8 @@
|
|||||||
getTTSVoiceName,
|
getTTSVoiceName,
|
||||||
changeTTSVoice,
|
changeTTSVoice,
|
||||||
saveNotificationTTS,
|
saveNotificationTTS,
|
||||||
testNotificationTTS
|
testNotificationTTS,
|
||||||
|
setNotificationLayout
|
||||||
} = notificationsSettingsStore;
|
} = notificationsSettingsStore;
|
||||||
|
|
||||||
const { testNotification } = useNotificationStore();
|
const { testNotification } = useNotificationStore();
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
<RefreshCw v-else />
|
<RefreshCw v-else />
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipWrapper>
|
</TooltipWrapper>
|
||||||
|
<template v-if="notificationLayout !== 'table'">
|
||||||
<ContextMenu v-if="hasUnseenNotifications">
|
<ContextMenu v-if="hasUnseenNotifications">
|
||||||
<ContextMenuTrigger as-child>
|
<ContextMenuTrigger as-child>
|
||||||
<TooltipWrapper side="bottom" :content="t('side_panel.notification_center.title')">
|
<TooltipWrapper side="bottom" :content="t('side_panel.notification_center.title')">
|
||||||
@@ -55,6 +56,7 @@
|
|||||||
<Bell />
|
<Bell />
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipWrapper>
|
</TooltipWrapper>
|
||||||
|
</template>
|
||||||
<Popover v-model:open="isSettingsPopoverOpen">
|
<Popover v-model:open="isSettingsPopoverOpen">
|
||||||
<PopoverTrigger as-child>
|
<PopoverTrigger as-child>
|
||||||
<Button class="rounded-full" variant="ghost" size="icon-sm">
|
<Button class="rounded-full" variant="ghost" size="icon-sm">
|
||||||
@@ -334,7 +336,8 @@
|
|||||||
useFavoriteStore,
|
useFavoriteStore,
|
||||||
useFriendStore,
|
useFriendStore,
|
||||||
useGroupStore,
|
useGroupStore,
|
||||||
useNotificationStore
|
useNotificationStore,
|
||||||
|
useNotificationsSettingsStore
|
||||||
} from '../../stores';
|
} from '../../stores';
|
||||||
import { runRefreshFriendsListFlow } from '../../coordinators/friendSyncCoordinator';
|
import { runRefreshFriendsListFlow } from '../../coordinators/friendSyncCoordinator';
|
||||||
import { normalizeFavoriteGroupsChange, resolveFavoriteGroups } from './sidebarSettingsUtils';
|
import { normalizeFavoriteGroupsChange, resolveFavoriteGroups } from './sidebarSettingsUtils';
|
||||||
@@ -350,6 +353,7 @@
|
|||||||
const { groupInstances } = storeToRefs(useGroupStore());
|
const { groupInstances } = storeToRefs(useGroupStore());
|
||||||
const notificationStore = useNotificationStore();
|
const notificationStore = useNotificationStore();
|
||||||
const { isNotificationCenterOpen, hasUnseenNotifications } = storeToRefs(notificationStore);
|
const { isNotificationCenterOpen, hasUnseenNotifications } = storeToRefs(notificationStore);
|
||||||
|
const { notificationLayout } = storeToRefs(useNotificationsSettingsStore());
|
||||||
const quickSearchStore = useQuickSearchStore();
|
const quickSearchStore = useQuickSearchStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user