From d3e44523bd2ea00dd1f83fb2e689bdec971736fe Mon Sep 17 00:00:00 2001 From: pa Date: Thu, 5 Feb 2026 20:54:21 +0900 Subject: [PATCH] fix mutuals chart by filtering out invalid user IDs --- src/stores/charts.js | 32 ++++++++++++------- .../Settings/components/Tabs/AdvancedTab.vue | 2 +- src/views/Tools/Tools.vue | 20 ++++++++++-- .../dialogs/RegistryBackupDialog.vue | 2 +- 4 files changed, 40 insertions(+), 16 deletions(-) rename src/views/{Settings => Tools}/dialogs/RegistryBackupDialog.vue (99%) diff --git a/src/stores/charts.js b/src/stores/charts.js index 1bcb2cd0..450e67b4 100644 --- a/src/stores/charts.js +++ b/src/stores/charts.js @@ -16,6 +16,19 @@ function createDefaultFetchState() { }; } +const EMPTY_USER_ID = 'usr_00000000-0000-0000-0000-000000000000'; + +function normalizeIdentifier(value) { + if (typeof value === 'string') return value; + if (value === undefined || value === null) return ''; + return String(value); +} + +function isValidMutualIdentifier(value) { + const identifier = normalizeIdentifier(value); + return Boolean(identifier && identifier !== EMPTY_USER_ID); +} + export const useChartsStore = defineStore('Charts', () => { const friendStore = useFriendStore(); const userStore = useUserStore(); @@ -176,7 +189,11 @@ export const useChartsStore = defineStore('Charts', () => { if (!args || isCancelled()) break; - collected.push(...args.json); + collected.push( + ...args.json.filter((entry) => + isValidMutualIdentifier(entry?.id) + ) + ); if (args.json.length < 100) break; offset += args.json.length; @@ -259,17 +276,8 @@ export const useChartsStore = defineStore('Charts', () => { const ids = []; for (const entry of collection) { - const identifier = - typeof entry?.id === 'string' - ? entry.id - : entry?.id !== undefined && entry?.id !== null - ? String(entry.id) - : ''; - if ( - identifier && - identifier !== - 'usr_00000000-0000-0000-0000-000000000000' - ) + const identifier = normalizeIdentifier(entry?.id); + if (isValidMutualIdentifier(identifier)) ids.push(identifier); } diff --git a/src/views/Settings/components/Tabs/AdvancedTab.vue b/src/views/Settings/components/Tabs/AdvancedTab.vue index 4f83280d..aa332c6c 100644 --- a/src/views/Settings/components/Tabs/AdvancedTab.vue +++ b/src/views/Settings/components/Tabs/AdvancedTab.vue @@ -381,7 +381,7 @@ import AvatarProviderDialog from '../../dialogs/AvatarProviderDialog.vue'; import PhotonSettings from '../PhotonSettings.vue'; - import RegistryBackupDialog from '../../dialogs/RegistryBackupDialog.vue'; + import RegistryBackupDialog from '../../../Tools/dialogs/RegistryBackupDialog.vue'; import SimpleSwitch from '../SimpleSwitch.vue'; import TranslationApiDialog from '../../dialogs/TranslationApiDialog.vue'; import YouTubeApiDialog from '../../dialogs/YouTubeApiDialog.vue'; diff --git a/src/views/Tools/Tools.vue b/src/views/Tools/Tools.vue index a33f4cac..26808236 100644 --- a/src/views/Tools/Tools.vue +++ b/src/views/Tools/Tools.vue @@ -337,7 +337,7 @@ SquarePen, UserCheck } from 'lucide-vue-next'; - import { computed, defineAsyncComponent, ref } from 'vue'; + import { computed, defineAsyncComponent, onMounted, ref } from 'vue'; import { useRoute, useRouter } from 'vue-router'; import { Card } from '@/components/ui/card'; import { storeToRefs } from 'pinia'; @@ -350,6 +350,7 @@ import { useVrcxStore } from '../../stores/vrcx'; import AutoChangeStatusDialog from './dialogs/AutoChangeStatusDialog.vue'; + import configRepository from '../../service/config.js'; const GroupCalendarDialog = defineAsyncComponent(() => import('./dialogs/GroupCalendarDialog.vue')); const NoteExportDialog = defineAsyncComponent(() => import('./dialogs/NoteExportDialog.vue')); @@ -357,7 +358,7 @@ const ExportDiscordNamesDialog = defineAsyncComponent(() => import('./dialogs/ExportDiscordNamesDialog.vue')); const ExportFriendsListDialog = defineAsyncComponent(() => import('./dialogs/ExportFriendsListDialog.vue')); const ExportAvatarsListDialog = defineAsyncComponent(() => import('./dialogs/ExportAvatarsListDialog.vue')); - const RegistryBackupDialog = defineAsyncComponent(() => import('../Settings/dialogs/RegistryBackupDialog.vue')); + const RegistryBackupDialog = defineAsyncComponent(() => import('./dialogs/RegistryBackupDialog.vue')); const { t } = useI18n(); const router = useRouter(); @@ -368,6 +369,7 @@ const { showVRChatConfig } = useAdvancedSettingsStore(); const { showLaunchOptions } = useLaunchStore(); const { showRegistryBackupDialog } = useVrcxStore(); + const toolsCategoryCollapsedConfigKey = 'VRCX_toolsCategoryCollapsed'; const categoryCollapsed = ref({ group: false, @@ -401,8 +403,22 @@ const toggleCategory = (category) => { categoryCollapsed.value[category] = !categoryCollapsed.value[category]; + configRepository.setString(toolsCategoryCollapsedConfigKey, JSON.stringify(categoryCollapsed.value)); }; + onMounted(async () => { + const storedValue = await configRepository.getString(toolsCategoryCollapsedConfigKey, '{}'); + try { + const parsed = JSON.parse(storedValue); + categoryCollapsed.value = { + ...categoryCollapsed.value, + ...parsed + }; + } catch { + // ignore invalid stored value and keep defaults + } + }); + const showEditInviteMessageDialog = () => { isEditInviteMessagesDialogVisible.value = true; }; diff --git a/src/views/Settings/dialogs/RegistryBackupDialog.vue b/src/views/Tools/dialogs/RegistryBackupDialog.vue similarity index 99% rename from src/views/Settings/dialogs/RegistryBackupDialog.vue rename to src/views/Tools/dialogs/RegistryBackupDialog.vue index 5317795a..1aa29897 100644 --- a/src/views/Settings/dialogs/RegistryBackupDialog.vue +++ b/src/views/Tools/dialogs/RegistryBackupDialog.vue @@ -59,7 +59,7 @@ import { useAdvancedSettingsStore, useModalStore, useVrcxStore } from '../../../stores'; import { downloadAndSaveJson, removeFromArray } from '../../../shared/utils'; import { Switch } from '../../../components/ui/switch'; - import { createColumns } from './registryBackupColumns.jsx'; + import { createColumns } from '../../Settings/dialogs/registryBackupColumns.jsx'; import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable'; import configRepository from '../../../service/config';