mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-22 08:13:52 +02:00
fix mutuals chart by filtering out invalid user IDs
This commit is contained in:
@@ -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';
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
<template>
|
||||
<Dialog :open="isRegistryBackupDialogVisible" @update:open="(open) => !open && closeAndClearDialog()">
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{{ t('dialog.registry_backup.header') }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div style="margin-top: 10px">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; font-size: 12px">
|
||||
<span class="name" style="margin-right: 24px">{{ t('dialog.registry_backup.auto_backup') }}</span>
|
||||
<Switch :model-value="vrcRegistryAutoBackup" @update:modelValue="setVrcRegistryAutoBackup" />
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
">
|
||||
<span class="name" style="margin-right: 24px">{{
|
||||
t('dialog.registry_backup.ask_to_restore')
|
||||
}}</span>
|
||||
<Switch :model-value="vrcRegistryAskRestore" @update:modelValue="setVrcRegistryAskRestore" />
|
||||
</div>
|
||||
<DataTableLayout
|
||||
class="min-w-0 w-full"
|
||||
:table="table"
|
||||
:loading="false"
|
||||
:table-style="tableStyle"
|
||||
:show-pagination="false"
|
||||
style="margin-top: 10px" />
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 10px">
|
||||
<Button size="sm" variant="destructive" @click="deleteVrcRegistry">{{
|
||||
t('dialog.registry_backup.reset')
|
||||
}}</Button>
|
||||
<div class="flex gap-2">
|
||||
<Button size="sm" variant="outline" @click="promptVrcRegistryBackupName">{{
|
||||
t('dialog.registry_backup.backup')
|
||||
}}</Button>
|
||||
<Button size="sm" variant="outline" @click="restoreVrcRegistryFromFile">{{
|
||||
t('dialog.registry_backup.restore_from_file')
|
||||
}}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { DataTableLayout } from '@/components/ui/data-table';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAdvancedSettingsStore, useModalStore, useVrcxStore } from '../../../stores';
|
||||
import { downloadAndSaveJson, removeFromArray } from '../../../shared/utils';
|
||||
import { Switch } from '../../../components/ui/switch';
|
||||
import { createColumns } from './registryBackupColumns.jsx';
|
||||
import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable';
|
||||
|
||||
import configRepository from '../../../service/config';
|
||||
|
||||
const { backupVrcRegistry } = useVrcxStore();
|
||||
const { isRegistryBackupDialogVisible } = storeToRefs(useVrcxStore());
|
||||
const { vrcRegistryAutoBackup, vrcRegistryAskRestore } = storeToRefs(useAdvancedSettingsStore());
|
||||
const { setVrcRegistryAutoBackup, setVrcRegistryAskRestore } = useAdvancedSettingsStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const registryBackupTable = ref({
|
||||
data: [],
|
||||
layout: 'table'
|
||||
});
|
||||
|
||||
const tableStyle = { maxHeight: '320px' };
|
||||
|
||||
const rows = computed(() =>
|
||||
Array.isArray(registryBackupTable.value?.data) ? registryBackupTable.value.data.slice() : []
|
||||
);
|
||||
|
||||
const columns = computed(() =>
|
||||
createColumns({
|
||||
onRestore: restoreVrcRegistryBackup,
|
||||
onSaveToFile: saveVrcRegistryBackupToFile,
|
||||
onDelete: deleteVrcRegistryBackup
|
||||
})
|
||||
);
|
||||
|
||||
const { table } = useVrcxVueTable({
|
||||
persistKey: 'registryBackupDialog',
|
||||
get data() {
|
||||
return rows.value;
|
||||
},
|
||||
columns: columns.value,
|
||||
getRowId: (row) => String(row?.name ?? ''),
|
||||
enablePagination: false,
|
||||
initialSorting: [{ id: 'date', desc: true }]
|
||||
});
|
||||
|
||||
watch(
|
||||
() => isRegistryBackupDialogVisible.value,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
updateRegistryBackupDialog();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
async function updateRegistryBackupDialog() {
|
||||
const backupsJson = await configRepository.getString('VRCX_VRChatRegistryBackups');
|
||||
registryBackupTable.value.data = JSON.parse(backupsJson || '[]');
|
||||
}
|
||||
|
||||
function restoreVrcRegistryBackup(row) {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Restore Backup',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
const data = JSON.stringify(row.data);
|
||||
AppApi.SetVRChatRegistry(data)
|
||||
.then(() => {
|
||||
toast.success('VRC registry settings restored');
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
toast.error(`Failed to restore VRC registry settings, check console for full error: ${e}`);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function saveVrcRegistryBackupToFile(row) {
|
||||
downloadAndSaveJson(row.name, row.data);
|
||||
}
|
||||
|
||||
async function deleteVrcRegistryBackup(row) {
|
||||
const backups = registryBackupTable.value.data;
|
||||
removeFromArray(backups, row);
|
||||
await configRepository.setString('VRCX_VRChatRegistryBackups', JSON.stringify(backups));
|
||||
await updateRegistryBackupDialog();
|
||||
}
|
||||
|
||||
function deleteVrcRegistry() {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Delete VRC Registry Settings',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
AppApi.DeleteVRChatRegistryFolder().then(() => {
|
||||
toast.success('VRC registry settings deleted');
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function handleBackupVrcRegistry(name) {
|
||||
await backupVrcRegistry(name);
|
||||
await updateRegistryBackupDialog();
|
||||
}
|
||||
|
||||
function promptVrcRegistryBackupName() {
|
||||
modalStore
|
||||
.prompt({
|
||||
title: 'Backup Name',
|
||||
description: 'Enter a name for the backup',
|
||||
inputValue: 'Backup',
|
||||
pattern: /\S+/,
|
||||
errorMessage: 'Name is required'
|
||||
})
|
||||
.then(({ ok, value }) => {
|
||||
if (!ok) return;
|
||||
handleBackupVrcRegistry(value);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
async function openJsonFileSelectorDialogElectron() {
|
||||
return new Promise((resolve) => {
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.accept = '.json';
|
||||
fileInput.style.display = 'none';
|
||||
document.body.appendChild(fileInput);
|
||||
|
||||
fileInput.onchange = function (event) {
|
||||
const target = /** @type {HTMLInputElement | null} */ (event.target);
|
||||
const file = target?.files?.[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
fileInput.remove();
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
} else {
|
||||
fileInput.remove();
|
||||
resolve(null);
|
||||
}
|
||||
};
|
||||
|
||||
fileInput.click();
|
||||
});
|
||||
}
|
||||
|
||||
async function restoreVrcRegistryFromFile() {
|
||||
const filePath = await AppApi.OpenFileSelectorDialog(null, '.json', 'JSON Files (*.json)|*.json');
|
||||
if (WINDOWS) {
|
||||
if (filePath === '') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let json;
|
||||
if (LINUX) {
|
||||
json = await openJsonFileSelectorDialogElectron();
|
||||
} else {
|
||||
json = await AppApi.ReadVrcRegJsonFile(filePath);
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(json);
|
||||
if (!data || typeof data !== 'object') {
|
||||
throw new Error('Invalid JSON');
|
||||
}
|
||||
// quick check to make sure it's a valid registry backup
|
||||
for (const key in data) {
|
||||
const value = data[key];
|
||||
if (typeof value !== 'object' || typeof value.type !== 'number' || typeof value.data === 'undefined') {
|
||||
throw new Error('Invalid JSON');
|
||||
}
|
||||
}
|
||||
AppApi.SetVRChatRegistry(json)
|
||||
.then(() => {
|
||||
toast.success('VRC registry settings restored');
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
toast.error(`Failed to restore VRC registry settings, check console for full error: ${e}`);
|
||||
});
|
||||
} catch {
|
||||
toast.error('Invalid JSON');
|
||||
}
|
||||
}
|
||||
|
||||
function clearVrcRegistryDialog() {
|
||||
registryBackupTable.value.data = [];
|
||||
}
|
||||
|
||||
function closeAndClearDialog() {
|
||||
closeDialog();
|
||||
// TODO: Element Plus had a distinct @closed event after animation.
|
||||
// If you ever need exact timing, wrap DialogContent with a Transition and call clear on after-leave.
|
||||
clearVrcRegistryDialog();
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
isRegistryBackupDialogVisible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.button-pd-0 {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user