mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-06 00:32:01 +02:00
replace ElMessageBox(alert, confirm) with alert dialog
This commit is contained in:
78
package-lock.json
generated
78
package-lock.json
generated
@@ -21,12 +21,12 @@
|
||||
"@fontsource-variable/noto-sans-tc": "^5.2.9",
|
||||
"@kamiya4047/eslint-plugin-pretty-import": "^0.1.6",
|
||||
"@sentry/vite-plugin": "^4.6.1",
|
||||
"@sentry/vue": "^10.32.1",
|
||||
"@sentry/vue": "^10.33.0",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@tanstack/vue-table": "^8.21.3",
|
||||
"@tanstack/vue-virtual": "^3.13.18",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^25.0.6",
|
||||
"@types/node": "^25.0.7",
|
||||
"@vee-validate/zod": "^4.15.1",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
||||
@@ -4372,54 +4372,54 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@sentry-internal/browser-utils": {
|
||||
"version": "10.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.32.1.tgz",
|
||||
"integrity": "sha512-sjLLep1es3rTkbtAdTtdpc/a6g7v7bK5YJiZJsUigoJ4NTiFeMI5uIDCxbH/tjJ1q23YE1LzVn7T96I+qBRjHA==",
|
||||
"version": "10.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-10.33.0.tgz",
|
||||
"integrity": "sha512-nDJFHAfiFifBfJB0OF6DV6BIsIV5uah4lDsV4UBAgPBf+YAHclO10y1gi2U/JMh58c+s4lXi9p+PI1TFXZ0c6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "10.32.1"
|
||||
"@sentry/core": "10.33.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/feedback": {
|
||||
"version": "10.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.32.1.tgz",
|
||||
"integrity": "sha512-O24G8jxbfBY1RE/v2qFikPJISVMOrd/zk8FKyl+oUVYdOxU2Ucjk2cR3EQruBFlc7irnL6rT3GPfRZ/kBgLkmQ==",
|
||||
"version": "10.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-10.33.0.tgz",
|
||||
"integrity": "sha512-sN/VLWtEf0BeV6w6wldIpTxUQxNVc9o9tjLRQa8je1ZV2FCgXA124Iff/zsowsz82dLqtg7qp6GA5zYXVq+JMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "10.32.1"
|
||||
"@sentry/core": "10.33.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/replay": {
|
||||
"version": "10.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.32.1.tgz",
|
||||
"integrity": "sha512-KKmLUgIaLRM0VjrMA1ByQTawZyRDYSkG2evvEOVpEtR9F0sumidAQdi7UY71QEKE1RYe/Jcp/3WoaqsMh8tbnQ==",
|
||||
"version": "10.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-10.33.0.tgz",
|
||||
"integrity": "sha512-UOU9PYxuXnPop3HoQ3l4Q7SZUXJC3Vmfm0Adgad8U03UcrThWIHYc5CxECSrVzfDFNOT7w9o7HQgRAgWxBPMXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/browser-utils": "10.32.1",
|
||||
"@sentry/core": "10.32.1"
|
||||
"@sentry-internal/browser-utils": "10.33.0",
|
||||
"@sentry/core": "10.33.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/replay-canvas": {
|
||||
"version": "10.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.32.1.tgz",
|
||||
"integrity": "sha512-/XGTzWNWVc+B691fIVekV2KeoHFEDA5KftrLFAhEAW7uWOwk/xy3aQX4TYM0LcPm2PBKvoumlAD+Sd/aXk63oA==",
|
||||
"version": "10.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-10.33.0.tgz",
|
||||
"integrity": "sha512-MTmP6uoAVzw4CCPeqCgCLsRSiOfGLxgyMFjGTCW3E7t62MJ9S0H5sLsQ34sHxXUa1gFU9UNAjEvRRpZ0JvWrPw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/replay": "10.32.1",
|
||||
"@sentry/core": "10.32.1"
|
||||
"@sentry-internal/replay": "10.33.0",
|
||||
"@sentry/core": "10.33.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -4436,17 +4436,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "10.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.32.1.tgz",
|
||||
"integrity": "sha512-NPNCXTZ05ZGTFyJdKNqjykpFm+urem0ebosILQiw3C4BxNVNGH4vfYZexyl6prRhmg91oB6GjVNiVDuJiap1gg==",
|
||||
"version": "10.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.33.0.tgz",
|
||||
"integrity": "sha512-iWiPjik9zetM84jKfk01UveW1J0+X7w8XmJ8+IrhTyNDBVUWCRJWD8FrksiN1dRSg5mFWgfMRzKMz27hAScRwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/browser-utils": "10.32.1",
|
||||
"@sentry-internal/feedback": "10.32.1",
|
||||
"@sentry-internal/replay": "10.32.1",
|
||||
"@sentry-internal/replay-canvas": "10.32.1",
|
||||
"@sentry/core": "10.32.1"
|
||||
"@sentry-internal/browser-utils": "10.33.0",
|
||||
"@sentry-internal/feedback": "10.33.0",
|
||||
"@sentry-internal/replay": "10.33.0",
|
||||
"@sentry-internal/replay-canvas": "10.33.0",
|
||||
"@sentry/core": "10.33.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -4647,9 +4647,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "10.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.32.1.tgz",
|
||||
"integrity": "sha512-PH2ldpSJlhqsMj2vCTyU0BI2Fx1oIDhm7Izo5xFALvjVCS0gmlqHt1udu6YlKn8BtpGH6bGzssvv5APrk+OdPQ==",
|
||||
"version": "10.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.33.0.tgz",
|
||||
"integrity": "sha512-ehH1VSUclIHZKEZVdv+klofsFIh8FFzqA6AAV23RtLepptzA8wqQzUGraEuSN25sYcNmYJ0jti5U0Ys+WZv5Dw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -4671,14 +4671,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/vue": {
|
||||
"version": "10.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/vue/-/vue-10.32.1.tgz",
|
||||
"integrity": "sha512-3KVvjkBw18FgbYar87CevQNPRATtBrzi+xIRZf6uJG2Wnd9w+WH3+CQsjKwDvQiYyChiW4CYuFL2DuQ/VqOxfQ==",
|
||||
"version": "10.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/vue/-/vue-10.33.0.tgz",
|
||||
"integrity": "sha512-CUtoBl62DG8mkoYfgpkw2WdB187XA2CfPj7OJdIzt3lavhpSAPmsY4jUarK2RUJvcowr5zYbEfv50Y0tsQxuGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/browser": "10.32.1",
|
||||
"@sentry/core": "10.32.1"
|
||||
"@sentry/browser": "10.33.0",
|
||||
"@sentry/core": "10.33.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -5313,9 +5313,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.6.tgz",
|
||||
"integrity": "sha512-NNu0sjyNxpoiW3YuVFfNz7mxSQ+S4X2G28uqg2s+CzoqoQjLPsWSbsFFyztIAqt2vb8kfEAsJNepMGPTxFDx3Q==",
|
||||
"version": "25.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.7.tgz",
|
||||
"integrity": "sha512-C/er7DlIZgRJO7WtTdYovjIFzGsz0I95UlMyR9anTb4aCpBSRWe5Jc1/RvLKUfzmOxHPGjSE5+63HgLtndxU4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -42,12 +42,12 @@
|
||||
"@fontsource-variable/noto-sans-tc": "^5.2.9",
|
||||
"@kamiya4047/eslint-plugin-pretty-import": "^0.1.6",
|
||||
"@sentry/vite-plugin": "^4.6.1",
|
||||
"@sentry/vue": "^10.32.1",
|
||||
"@sentry/vue": "^10.33.0",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@tanstack/vue-table": "^8.21.3",
|
||||
"@tanstack/vue-virtual": "^3.13.18",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^25.0.6",
|
||||
"@types/node": "^25.0.7",
|
||||
"@vee-validate/zod": "^4.15.1",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
<RouterView></RouterView>
|
||||
<Toaster position="top-center"></Toaster>
|
||||
|
||||
<AlertDialogModal></AlertDialogModal>
|
||||
|
||||
<VRCXUpdateDialog></VRCXUpdateDialog>
|
||||
</div>
|
||||
</el-config-provider>
|
||||
@@ -29,6 +31,7 @@
|
||||
import { createGlobalStores } from './stores';
|
||||
import { initNoty } from './plugin/noty';
|
||||
|
||||
import AlertDialogModal from './components/ui/alert-dialog/AlertDialogModal.vue';
|
||||
import MacOSTitleBar from './components/MacOSTitleBar.vue';
|
||||
import VRCXUpdateDialog from './components/dialogs/VRCXUpdateDialog.vue';
|
||||
|
||||
|
||||
@@ -67,11 +67,10 @@
|
||||
import { reactive, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CaretBottom } from '@element-plus/icons-vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useGroupStore, useInstanceStore, useLocationStore, useUserStore } from '../stores';
|
||||
import { useGroupStore, useInstanceStore, useLocationStore, useModalStore, useUserStore } from '../stores';
|
||||
import { formatDateFilter, hasGroupPermission } from '../shared/utils';
|
||||
import { miscRequest } from '../api';
|
||||
|
||||
@@ -81,6 +80,7 @@
|
||||
const userStore = useUserStore();
|
||||
const groupStore = useGroupStore();
|
||||
const instanceStore = useInstanceStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const props = defineProps({
|
||||
location: String,
|
||||
@@ -126,13 +126,13 @@
|
||||
}
|
||||
|
||||
function closeInstance(location) {
|
||||
ElMessageBox.confirm('Continue? Close Instance, nobody will be able to join', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning'
|
||||
})
|
||||
.then(async (action) => {
|
||||
if (action !== 'confirm') return;
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Close Instance, nobody will be able to join',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(async ({ ok }) => {
|
||||
if (!ok) return;
|
||||
const args = await miscRequest.closeInstance({ location, hardClose: false });
|
||||
if (args.json) {
|
||||
toast.success(t('message.instance.closed'));
|
||||
|
||||
@@ -246,8 +246,8 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, defineAsyncComponent, onMounted, ref, watch } from 'vue';
|
||||
import { ElMessageBox, dayjs } from 'element-plus';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { dayjs } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
@@ -255,6 +255,7 @@
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useAuthStore,
|
||||
useModalStore,
|
||||
useSearchStore,
|
||||
useUiStore,
|
||||
useVRCXUpdaterStore
|
||||
@@ -269,6 +270,7 @@
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const router = useRouter();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const createDefaultNavLayout = () => [
|
||||
{ type: 'item', key: 'feed' },
|
||||
@@ -559,12 +561,15 @@
|
||||
};
|
||||
|
||||
const handleCustomNavReset = () => {
|
||||
ElMessageBox.confirm(t('nav_menu.custom_nav.restore_default_confirm'), {
|
||||
type: 'warning',
|
||||
confirmButtonText: t('nav_menu.custom_nav.restore_default'),
|
||||
cancelButtonText: t('nav_menu.custom_nav.cancel')
|
||||
})
|
||||
.then(async () => {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('nav_menu.custom_nav.restore_default_confirm'),
|
||||
title: t('confirm.title'),
|
||||
confirmText: t('nav_menu.custom_nav.restore_default'),
|
||||
cancelText: t('nav_menu.custom_nav.cancel')
|
||||
})
|
||||
.then(async ({ ok }) => {
|
||||
if (!ok) return;
|
||||
const defaults = sanitizeLayout(createDefaultNavLayout());
|
||||
navLayout.value = defaults;
|
||||
await saveNavLayout(defaults);
|
||||
|
||||
@@ -559,6 +559,7 @@
|
||||
useFavoriteStore,
|
||||
useGalleryStore,
|
||||
useGameStore,
|
||||
useModalStore,
|
||||
useUserStore
|
||||
} from '../../../stores';
|
||||
import {
|
||||
@@ -591,6 +592,7 @@
|
||||
const { deleteVRChatCache } = useGameStore();
|
||||
const { showFullscreenImageDialog } = useGalleryStore();
|
||||
const { isDarkMode } = storeToRefs(useAppearanceSettingsStore());
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -752,15 +754,13 @@
|
||||
showFavoriteDialog('avatar', D.id);
|
||||
break;
|
||||
default:
|
||||
ElMessageBox.confirm(`Continue? ${command}`, 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action !== 'confirm') {
|
||||
return;
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
title: 'Confirm',
|
||||
description: `Continue? ${command}`
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
switch (command) {
|
||||
case 'Delete Favorite':
|
||||
favoriteRequest.deleteFavorite({
|
||||
@@ -897,8 +897,7 @@
|
||||
});
|
||||
break;
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1146,11 +1146,10 @@
|
||||
View,
|
||||
Warning
|
||||
} from '@element-plus/icons-vue';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { RefreshCcw } from 'lucide-vue-next';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
@@ -1176,6 +1175,14 @@
|
||||
userImage,
|
||||
userStatusClass
|
||||
} from '../../../shared/utils';
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useGalleryStore,
|
||||
useGroupStore,
|
||||
useLocationStore,
|
||||
useModalStore,
|
||||
useUserStore
|
||||
} from '../../../stores';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -1183,13 +1190,6 @@
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from '../../ui/dropdown-menu';
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useGalleryStore,
|
||||
useGroupStore,
|
||||
useLocationStore,
|
||||
useUserStore
|
||||
} from '../../../stores';
|
||||
import { formatJsonVars, getNextDialogIndex } from '../../../shared/utils/base/ui';
|
||||
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
|
||||
import { Badge } from '../../ui/badge';
|
||||
@@ -1203,6 +1203,8 @@
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { showUserDialog } = useUserStore();
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
const { groupDialog, inviteGroupDialog } = storeToRefs(useGroupStore());
|
||||
@@ -1469,43 +1471,42 @@
|
||||
});
|
||||
}
|
||||
function confirmDeleteGroupPost(post) {
|
||||
ElMessageBox.confirm('Are you sure you want to delete this post?', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
groupRequest
|
||||
.deleteGroupPost({
|
||||
groupId: post.groupId,
|
||||
postId: post.id
|
||||
})
|
||||
.then((args) => {
|
||||
const D = groupDialog.value;
|
||||
if (D.id !== args.params.groupId) {
|
||||
return;
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Are you sure you want to delete this post?',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
groupRequest
|
||||
.deleteGroupPost({
|
||||
groupId: post.groupId,
|
||||
postId: post.id
|
||||
})
|
||||
.then((args) => {
|
||||
const D = groupDialog.value;
|
||||
if (D.id !== args.params.groupId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const postId = args.params.postId;
|
||||
// remove existing post
|
||||
for (const item of D.posts) {
|
||||
if (item.id === postId) {
|
||||
removeFromArray(D.posts, item);
|
||||
break;
|
||||
}
|
||||
const postId = args.params.postId;
|
||||
// remove existing post
|
||||
for (const item of D.posts) {
|
||||
if (item.id === postId) {
|
||||
removeFromArray(D.posts, item);
|
||||
break;
|
||||
}
|
||||
// remove/update announcement
|
||||
if (postId === D.announcement.id) {
|
||||
if (D.posts.length > 0) {
|
||||
D.announcement = D.posts[0];
|
||||
} else {
|
||||
D.announcement = {};
|
||||
}
|
||||
}
|
||||
// remove/update announcement
|
||||
if (postId === D.announcement.id) {
|
||||
if (D.posts.length > 0) {
|
||||
D.announcement = D.posts[0];
|
||||
} else {
|
||||
D.announcement = {};
|
||||
}
|
||||
updateGroupPostSearch();
|
||||
});
|
||||
}
|
||||
}
|
||||
updateGroupPostSearch();
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
@@ -1571,46 +1572,44 @@
|
||||
}
|
||||
|
||||
function blockGroup(groupId) {
|
||||
ElMessageBox.confirm('Are you sure you want to block this group?', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
groupRequest
|
||||
.blockGroup({
|
||||
groupId
|
||||
})
|
||||
.then((args) => {
|
||||
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
|
||||
showGroupDialog(args.params.groupId);
|
||||
}
|
||||
});
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Are you sure you want to block this group?',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
groupRequest
|
||||
.blockGroup({
|
||||
groupId
|
||||
})
|
||||
.then((args) => {
|
||||
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
|
||||
showGroupDialog(args.params.groupId);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function unblockGroup(groupId) {
|
||||
ElMessageBox.confirm('Are you sure you want to unblock this group?', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
groupRequest
|
||||
.unblockGroup({
|
||||
groupId,
|
||||
userId: currentUser.value.id
|
||||
})
|
||||
.then((args) => {
|
||||
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
|
||||
showGroupDialog(args.params.groupId);
|
||||
}
|
||||
});
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Are you sure you want to unblock this group?',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
groupRequest
|
||||
.unblockGroup({
|
||||
groupId,
|
||||
userId: currentUser.value.id
|
||||
})
|
||||
.then((args) => {
|
||||
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
|
||||
showGroupDialog(args.params.groupId);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -87,12 +87,11 @@
|
||||
import { computed, ref } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Check as CheckIcon } from 'lucide-vue-next';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useFriendStore, useGalleryStore, useInviteStore, useUserStore } from '../../../stores';
|
||||
import { useFriendStore, useGalleryStore, useInviteStore, useModalStore, useUserStore } from '../../../stores';
|
||||
import { parseLocation, userImage, userStatusClass } from '../../../shared/utils';
|
||||
import { instanceRequest, notificationRequest } from '../../../api';
|
||||
import { VirtualCombobox } from '../../ui/virtual-combobox';
|
||||
@@ -104,6 +103,8 @@
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
const { clearInviteImageUpload } = useGalleryStore();
|
||||
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
inviteDialog: {
|
||||
@@ -233,14 +234,15 @@
|
||||
}
|
||||
|
||||
function sendInvite() {
|
||||
ElMessageBox.confirm('Continue? Invite', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Invite',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
const D = props.inviteDialog;
|
||||
if (action !== 'confirm' || D.loading === true) {
|
||||
if (D.loading === true) {
|
||||
return;
|
||||
}
|
||||
D.loading = true;
|
||||
@@ -275,7 +277,8 @@
|
||||
}
|
||||
};
|
||||
inviteLoop();
|
||||
})
|
||||
.catch(() => {});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
}) .catch(() => {});
|
||||
|
||||
@@ -79,14 +79,13 @@
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Check as CheckIcon } from 'lucide-vue-next';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { hasGroupPermission, userImage, userStatusClass } from '../../shared/utils';
|
||||
import { useFriendStore, useGroupStore, useModalStore } from '../../stores';
|
||||
import { groupRequest, userRequest } from '../../api';
|
||||
import { useFriendStore, useGroupStore } from '../../stores';
|
||||
import { VirtualCombobox } from '../ui/virtual-combobox';
|
||||
import { getNextDialogIndex } from '../../shared/utils/base/ui';
|
||||
|
||||
@@ -96,6 +95,7 @@
|
||||
const { currentUserGroups, inviteGroupDialog } = storeToRefs(useGroupStore());
|
||||
const { applyGroup } = useGroupStore();
|
||||
const { t } = useI18n();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
watch(
|
||||
() => inviteGroupDialog.value.visible,
|
||||
@@ -272,14 +272,15 @@
|
||||
});
|
||||
}
|
||||
function sendGroupInvite() {
|
||||
ElMessageBox.confirm('Continue? Invite User(s) To Group', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Invite User(s) To Group',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
const D = inviteGroupDialog.value;
|
||||
if (action !== 'confirm' || D.loading === true) {
|
||||
if (D.loading === true) {
|
||||
return;
|
||||
}
|
||||
D.loading = true;
|
||||
|
||||
@@ -135,7 +135,6 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ButtonGroup } from '@/components/ui/button-group';
|
||||
import { Copy } from 'lucide-vue-next';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { MoreHorizontal } from 'lucide-vue-next';
|
||||
import { Warning } from '@element-plus/icons-vue';
|
||||
@@ -143,7 +142,14 @@
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useFriendStore, useGameStore, useInviteStore, useLaunchStore, useLocationStore } from '../../stores';
|
||||
import {
|
||||
useFriendStore,
|
||||
useGameStore,
|
||||
useInviteStore,
|
||||
useLaunchStore,
|
||||
useLocationStore,
|
||||
useModalStore
|
||||
} from '../../stores';
|
||||
import { checkCanInvite, getLaunchURL, isRealInstance, parseLocation } from '../../shared/utils';
|
||||
import { instanceRequest, worldRequest } from '../../api';
|
||||
import { getNextDialogIndex } from '../../shared/utils/base/ui';
|
||||
@@ -153,6 +159,8 @@
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { friends } = storeToRefs(useFriendStore());
|
||||
const { lastLocation } = storeToRefs(useLocationStore());
|
||||
const { launchGame, tryOpenInstanceInVrc } = useLaunchStore();
|
||||
@@ -245,16 +253,17 @@
|
||||
}
|
||||
function handleLaunchGame(location, shortName, desktop) {
|
||||
if (isGameRunning.value) {
|
||||
ElMessageBox.confirm(t('dialog.launch.game_running_warning'), t('dialog.launch.header'), {
|
||||
confirmButtonText: t('dialog.launch.confirm_yes'),
|
||||
cancelButtonText: t('dialog.launch.confirm_no'),
|
||||
type: 'warning'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
launchGame(location, shortName, desktop);
|
||||
isVisible.value = false;
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('dialog.launch.game_running_warning'),
|
||||
title: t('dialog.launch.header'),
|
||||
confirmText: t('dialog.launch.confirm_yes'),
|
||||
cancelText: t('dialog.launch.confirm_no')
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
launchGame(location, shortName, desktop);
|
||||
isVisible.value = false;
|
||||
})
|
||||
.catch(() => {});
|
||||
return;
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
@@ -41,7 +40,7 @@
|
||||
removeFromArray,
|
||||
timeToText
|
||||
} from '../../../shared/utils';
|
||||
import { useInstanceStore, useSearchStore, useUiStore, useVrcxStore } from '../../../stores';
|
||||
import { useInstanceStore, useModalStore, useSearchStore, useUiStore, useVrcxStore } from '../../../stores';
|
||||
import { DataTableLayout } from '../../ui/data-table';
|
||||
import { createColumns } from './previousInstancesGroupColumns.jsx';
|
||||
import { database } from '../../../service/database';
|
||||
@@ -56,6 +55,8 @@
|
||||
const previousInstancesGroupDialogIndex = ref(2000);
|
||||
const loading = ref(false);
|
||||
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const vrcxStore = useVrcxStore();
|
||||
const rawRows = ref([]);
|
||||
const search = ref('');
|
||||
@@ -169,15 +170,14 @@
|
||||
}
|
||||
|
||||
function deleteGameLogGroupInstancePrompt(row) {
|
||||
ElMessageBox.confirm('Continue? Delete GameLog Instance', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
deleteGameLogGroupInstance(row);
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Delete GameLog Instance',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
deleteGameLogGroupInstance(row);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -30,11 +30,18 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import {
|
||||
useInstanceStore,
|
||||
useModalStore,
|
||||
useSearchStore,
|
||||
useUiStore,
|
||||
useUserStore,
|
||||
useVrcxStore
|
||||
} from '../../../stores';
|
||||
import {
|
||||
compareByCreatedAt,
|
||||
localeIncludes,
|
||||
@@ -42,7 +49,6 @@
|
||||
removeFromArray,
|
||||
timeToText
|
||||
} from '../../../shared/utils';
|
||||
import { useInstanceStore, useSearchStore, useUiStore, useUserStore, useVrcxStore } from '../../../stores';
|
||||
import { DataTableLayout } from '../../ui/data-table';
|
||||
import { createColumns } from './previousInstancesWorldColumns.jsx';
|
||||
import { database } from '../../../service/database';
|
||||
@@ -51,6 +57,8 @@
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const props = defineProps({
|
||||
previousInstancesWorldDialog: {
|
||||
type: Object,
|
||||
@@ -164,15 +172,14 @@
|
||||
}
|
||||
|
||||
function deleteGameLogWorldInstancePrompt(row) {
|
||||
ElMessageBox.confirm('Continue? Delete GameLog Instance', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
deleteGameLogWorldInstance(row);
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Delete GameLog Instance',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
deleteGameLogWorldInstance(row);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -30,11 +30,18 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import {
|
||||
useInstanceStore,
|
||||
useLaunchStore,
|
||||
useModalStore,
|
||||
useSearchStore,
|
||||
useUiStore,
|
||||
useVrcxStore
|
||||
} from '../../../stores';
|
||||
import {
|
||||
compareByCreatedAt,
|
||||
localeIncludes,
|
||||
@@ -42,7 +49,6 @@
|
||||
removeFromArray,
|
||||
timeToText
|
||||
} from '../../../shared/utils';
|
||||
import { useInstanceStore, useLaunchStore, useSearchStore, useUiStore, useVrcxStore } from '../../../stores';
|
||||
import { DataTableLayout } from '../../ui/data-table';
|
||||
import { createColumns } from './previousInstancesUserColumns.jsx';
|
||||
import { database } from '../../../service/database';
|
||||
@@ -68,6 +74,8 @@
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:previous-instances-user-dialog']);
|
||||
|
||||
const modalStore = useModalStore();
|
||||
const loading = ref(false);
|
||||
const rawRows = ref([]);
|
||||
const search = ref('');
|
||||
@@ -189,13 +197,14 @@
|
||||
}
|
||||
|
||||
function deleteGameLogUserInstancePrompt(row) {
|
||||
ElMessageBox.confirm('Continue? Delete User From GameLog Instance', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') deleteGameLogUserInstance(row);
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Delete User From GameLog Instance',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
deleteGameLogUserInstance(row);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -1318,7 +1318,6 @@
|
||||
import { Download, LogOut, RefreshCcw } from 'lucide-vue-next';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
@@ -1358,6 +1357,7 @@
|
||||
useGroupStore,
|
||||
useInviteStore,
|
||||
useLocationStore,
|
||||
useModalStore,
|
||||
useModerationStore,
|
||||
useUiStore,
|
||||
useUserStore,
|
||||
@@ -1393,6 +1393,8 @@
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { hideUserNotes, hideUserMemos, isDarkMode } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { bioLanguage, avatarRemoteDatabase, translationApi, translationApiType } =
|
||||
storeToRefs(useAdvancedSettingsStore());
|
||||
@@ -1894,19 +1896,17 @@
|
||||
? command
|
||||
: t(`${i18nPreFix}${formattedCommand}`);
|
||||
|
||||
ElMessageBox.confirm(
|
||||
t('confirm.message', {
|
||||
command: displayCommandText
|
||||
}),
|
||||
t('confirm.title'),
|
||||
{
|
||||
confirmButtonText: t('confirm.confirm_button'),
|
||||
cancelButtonText: t('confirm.cancel_button'),
|
||||
type: 'info'
|
||||
}
|
||||
)
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.message', {
|
||||
command: displayCommandText
|
||||
}),
|
||||
title: t('confirm.title'),
|
||||
confirmText: t('confirm.confirm_button'),
|
||||
cancelText: t('confirm.cancel_button')
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (ok) {
|
||||
performUserDialogCommand(command, D.id);
|
||||
}
|
||||
})
|
||||
@@ -2422,22 +2422,21 @@
|
||||
}
|
||||
|
||||
function resetHome() {
|
||||
ElMessageBox.confirm('Continue? Reset Home', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
userRequest
|
||||
.saveCurrentUser({
|
||||
homeLocation: ''
|
||||
})
|
||||
.then((args) => {
|
||||
toast.success('Home world has been reset');
|
||||
return args;
|
||||
});
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Reset Home',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
userRequest
|
||||
.saveCurrentUser({
|
||||
homeLocation: ''
|
||||
})
|
||||
.then((args) => {
|
||||
toast.success('Home world has been reset');
|
||||
return args;
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -795,6 +795,7 @@
|
||||
useInstanceStore,
|
||||
useInviteStore,
|
||||
useLocationStore,
|
||||
useModalStore,
|
||||
useUserStore,
|
||||
useWorldStore
|
||||
} from '../../../stores';
|
||||
@@ -810,6 +811,8 @@
|
||||
import { Badge } from '../../ui/badge';
|
||||
import { database } from '../../../service/database.js';
|
||||
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const NewInstanceDialog = defineAsyncComponent(() => import('../NewInstanceDialog.vue'));
|
||||
const PreviousInstancesWorldDialog = defineAsyncComponent(
|
||||
() => import('../PreviousInstancesDialog/PreviousInstancesWorldDialog.vue')
|
||||
@@ -988,15 +991,13 @@
|
||||
case 'Unpublish':
|
||||
case 'Delete Persistent Data':
|
||||
case 'Delete':
|
||||
ElMessageBox.confirm(`Continue? ${command}`, 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action !== 'confirm') {
|
||||
return;
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: `Continue? ${command}`,
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
switch (command) {
|
||||
case 'Delete Favorite':
|
||||
favoriteRequest.deleteFavorite({
|
||||
|
||||
17
src/components/ui/alert-dialog/AlertDialog.vue
Normal file
17
src/components/ui/alert-dialog/AlertDialog.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup>
|
||||
import { AlertDialogRoot, useForwardPropsEmits } from 'reka-ui';
|
||||
|
||||
const props = defineProps({
|
||||
open: { type: Boolean, required: false },
|
||||
defaultOpen: { type: Boolean, required: false }
|
||||
});
|
||||
const emits = defineEmits(['update:open']);
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogRoot v-slot="slotProps" data-slot="alert-dialog" v-bind="forwarded">
|
||||
<slot v-bind="slotProps" />
|
||||
</AlertDialogRoot>
|
||||
</template>
|
||||
20
src/components/ui/alert-dialog/AlertDialogAction.vue
Normal file
20
src/components/ui/alert-dialog/AlertDialogAction.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
import { AlertDialogAction } from 'reka-ui';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
|
||||
<slot />
|
||||
</AlertDialogAction>
|
||||
</template>
|
||||
22
src/components/ui/alert-dialog/AlertDialogCancel.vue
Normal file
22
src/components/ui/alert-dialog/AlertDialogCancel.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup>
|
||||
import { AlertDialogCancel } from 'reka-ui';
|
||||
import { buttonVariants } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogCancel
|
||||
v-bind="delegatedProps"
|
||||
:class="cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', props.class)">
|
||||
<slot />
|
||||
</AlertDialogCancel>
|
||||
</template>
|
||||
48
src/components/ui/alert-dialog/AlertDialogContent.vue
Normal file
48
src/components/ui/alert-dialog/AlertDialogContent.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<script setup>
|
||||
import { AlertDialogContent, AlertDialogOverlay, AlertDialogPortal, useForwardPropsEmits } from 'reka-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
forceMount: { type: Boolean, required: false },
|
||||
disableOutsidePointerEvents: { type: Boolean, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
const emits = defineEmits([
|
||||
'escapeKeyDown',
|
||||
'pointerDownOutside',
|
||||
'focusOutside',
|
||||
'interactOutside',
|
||||
'openAutoFocus',
|
||||
'closeAutoFocus'
|
||||
]);
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay
|
||||
data-slot="alert-dialog-overlay"
|
||||
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-10000 bg-black/80" />
|
||||
<AlertDialogContent
|
||||
data-slot="alert-dialog-content"
|
||||
v-bind="{ ...$attrs, ...forwarded }"
|
||||
:class="
|
||||
cn(
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-10000 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
||||
props.class
|
||||
)
|
||||
">
|
||||
<slot />
|
||||
</AlertDialogContent>
|
||||
</AlertDialogPortal>
|
||||
</template>
|
||||
22
src/components/ui/alert-dialog/AlertDialogDescription.vue
Normal file
22
src/components/ui/alert-dialog/AlertDialogDescription.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup>
|
||||
import { AlertDialogDescription } from 'reka-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogDescription
|
||||
data-slot="alert-dialog-description"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('text-muted-foreground text-sm', props.class)">
|
||||
<slot />
|
||||
</AlertDialogDescription>
|
||||
</template>
|
||||
15
src/components/ui/alert-dialog/AlertDialogFooter.vue
Normal file
15
src/components/ui/alert-dialog/AlertDialogFooter.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script setup>
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const props = defineProps({
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-slot="alert-dialog-footer"
|
||||
:class="cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
13
src/components/ui/alert-dialog/AlertDialogHeader.vue
Normal file
13
src/components/ui/alert-dialog/AlertDialogHeader.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup>
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const props = defineProps({
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-slot="alert-dialog-header" :class="cn('flex flex-col gap-2 text-center sm:text-left', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
67
src/components/ui/alert-dialog/AlertDialogModal.vue
Normal file
67
src/components/ui/alert-dialog/AlertDialogModal.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<script setup>
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useModalStore } from '@/stores';
|
||||
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { alertOpen, alertMode, alertTitle, alertDescription, alertOkText, alertCancelText, alertDismissible } =
|
||||
storeToRefs(modalStore);
|
||||
|
||||
function onEscapeKeyDown(event) {
|
||||
if (!alertDismissible.value) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
modalStore.handleDismiss();
|
||||
}
|
||||
|
||||
function onPointerDownOutside(event) {
|
||||
if (!alertDismissible.value) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
modalStore.handleDismiss();
|
||||
}
|
||||
|
||||
function onInteractOutside(event) {
|
||||
if (!alertDismissible.value) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
modalStore.handleDismiss();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialog :open="alertOpen" @update:open="modalStore.setAlertOpen">
|
||||
<AlertDialogContent
|
||||
@escapeKeyDown="onEscapeKeyDown"
|
||||
@pointerDownOutside="onPointerDownOutside"
|
||||
@interactOutside="onInteractOutside">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{{ alertTitle }}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{{ alertDescription }}</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel v-if="alertMode === 'confirm'" @click="modalStore.handleCancel">
|
||||
{{ alertCancelText }}
|
||||
</AlertDialogCancel>
|
||||
|
||||
<AlertDialogAction @click="modalStore.handleOk">
|
||||
{{ alertOkText }}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</template>
|
||||
22
src/components/ui/alert-dialog/AlertDialogTitle.vue
Normal file
22
src/components/ui/alert-dialog/AlertDialogTitle.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup>
|
||||
import { AlertDialogTitle } from 'reka-ui';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { reactiveOmit } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogTitle
|
||||
data-slot="alert-dialog-title"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('text-lg font-semibold', props.class)">
|
||||
<slot />
|
||||
</AlertDialogTitle>
|
||||
</template>
|
||||
14
src/components/ui/alert-dialog/AlertDialogTrigger.vue
Normal file
14
src/components/ui/alert-dialog/AlertDialogTrigger.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup>
|
||||
import { AlertDialogTrigger } from 'reka-ui';
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogTrigger data-slot="alert-dialog-trigger" v-bind="props">
|
||||
<slot />
|
||||
</AlertDialogTrigger>
|
||||
</template>
|
||||
9
src/components/ui/alert-dialog/index.js
Normal file
9
src/components/ui/alert-dialog/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export { default as AlertDialog } from './AlertDialog.vue';
|
||||
export { default as AlertDialogAction } from './AlertDialogAction.vue';
|
||||
export { default as AlertDialogCancel } from './AlertDialogCancel.vue';
|
||||
export { default as AlertDialogContent } from './AlertDialogContent.vue';
|
||||
export { default as AlertDialogDescription } from './AlertDialogDescription.vue';
|
||||
export { default as AlertDialogFooter } from './AlertDialogFooter.vue';
|
||||
export { default as AlertDialogHeader } from './AlertDialogHeader.vue';
|
||||
export { default as AlertDialogTitle } from './AlertDialogTitle.vue';
|
||||
export { default as AlertDialogTrigger } from './AlertDialogTrigger.vue';
|
||||
@@ -911,6 +911,11 @@
|
||||
"pending_offline": "Pending Offline"
|
||||
},
|
||||
"dialog": {
|
||||
"alertdialog": {
|
||||
"ok": "OK",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm"
|
||||
},
|
||||
"user": {
|
||||
"status": {
|
||||
"active": "Active",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
import Noty from 'noty';
|
||||
@@ -6,6 +5,7 @@ import Noty from 'noty';
|
||||
import {
|
||||
useAuthStore,
|
||||
useAvatarStore,
|
||||
useModalStore,
|
||||
useNotificationStore,
|
||||
useUpdateLoopStore,
|
||||
useUserStore
|
||||
@@ -33,6 +33,7 @@ export function request(endpoint, options) {
|
||||
const userStore = useUserStore();
|
||||
const avatarStore = useAvatarStore();
|
||||
const authStore = useAuthStore();
|
||||
const modalStore = useModalStore();
|
||||
const notificationStore = useNotificationStore();
|
||||
const updateLoopStore = useUpdateLoopStore();
|
||||
if (
|
||||
@@ -187,10 +188,10 @@ export function request(endpoint, options) {
|
||||
}
|
||||
}
|
||||
if (status === 403 && endpoint === 'config') {
|
||||
ElMessageBox.alert(
|
||||
t('api.error.message.vpn_in_use'),
|
||||
`403 ${t('api.error.message.login_error')}`
|
||||
).catch(() => {});
|
||||
modalStore.alert({
|
||||
description: t('api.error.message.vpn_in_use'),
|
||||
title: `403 ${t('api.error.message.login_error')}`
|
||||
});
|
||||
authStore.handleLogoutEvent();
|
||||
$throw(403, endpoint);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
|
||||
import { openExternalLink } from '../shared/utils';
|
||||
import { useModalStore } from '../stores';
|
||||
|
||||
// requires binding of SQLite
|
||||
class SQLiteService {
|
||||
handleSQLiteError(e) {
|
||||
if (typeof e.message === 'string') {
|
||||
const modalStore = useModalStore();
|
||||
if (e.message.includes('database disk image is malformed')) {
|
||||
ElMessageBox.confirm(
|
||||
'Please repair or delete your database file by following these instructions.',
|
||||
'Your database is corrupted',
|
||||
{
|
||||
confirmButtonText: 'Confirm',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
.then(async (action) => {
|
||||
if (action !== 'confirm') return;
|
||||
modalStore
|
||||
.confirm({
|
||||
description:
|
||||
'Please repair or delete your database file by following these instructions.',
|
||||
title: 'Your database is corrupted'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
openExternalLink(
|
||||
'https://github.com/vrcx-team/VRCX/wiki#how-to-repair-vrcx-database'
|
||||
);
|
||||
@@ -24,37 +22,26 @@ class SQLiteService {
|
||||
.catch(() => {});
|
||||
}
|
||||
if (e.message.includes('database or disk is full')) {
|
||||
ElMessageBox.alert(
|
||||
'Please free up some disk space.',
|
||||
'Disk containing database is full',
|
||||
{
|
||||
confirmButtonText: 'OK',
|
||||
type: 'warning'
|
||||
}
|
||||
).catch(() => {});
|
||||
modalStore.alert({
|
||||
description: 'Please free up some disk space.',
|
||||
title: 'Disk containing database is full'
|
||||
});
|
||||
}
|
||||
if (
|
||||
e.message.includes('database is locked') ||
|
||||
e.message.includes('attempt to write a readonly database')
|
||||
) {
|
||||
ElMessageBox.alert(
|
||||
'Please close other applications that might be using the database file.',
|
||||
'Database is locked',
|
||||
{
|
||||
confirmButtonText: 'OK',
|
||||
type: 'warning'
|
||||
}
|
||||
).catch(() => {});
|
||||
modalStore.alert({
|
||||
description:
|
||||
'Please close other applications that might be using the database file.',
|
||||
title: 'Database is locked'
|
||||
});
|
||||
}
|
||||
if (e.message.includes('disk I/O error')) {
|
||||
ElMessageBox.alert(
|
||||
'Please check your disk for errors.',
|
||||
'Disk I/O error',
|
||||
{
|
||||
confirmButtonText: 'OK',
|
||||
type: 'warning'
|
||||
}
|
||||
).catch(() => {});
|
||||
modalStore.alert({
|
||||
description: 'Please check your disk for errors.',
|
||||
title: 'Disk I/O error'
|
||||
});
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
@@ -7,6 +6,7 @@ import Noty from 'noty';
|
||||
import {
|
||||
useAvatarStore,
|
||||
useInstanceStore,
|
||||
useModalStore,
|
||||
useSearchStore,
|
||||
useWorldStore
|
||||
} from '../../stores';
|
||||
@@ -395,24 +395,22 @@ function openExternalLink(link) {
|
||||
return;
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(`${link}`, 'Open External Link', {
|
||||
distinguishCancelAndClose: true,
|
||||
confirmButtonText: 'Open',
|
||||
cancelButtonText: 'Copy',
|
||||
type: 'info',
|
||||
beforeClose: (action, instance, done) => {
|
||||
if (action === 'cancel') {
|
||||
copyToClipboard(link);
|
||||
}
|
||||
done();
|
||||
}
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
AppApi.OpenLink(link);
|
||||
}
|
||||
const modalStore = useModalStore();
|
||||
modalStore
|
||||
.confirm({
|
||||
description: `${link}`,
|
||||
title: 'Open External Link',
|
||||
confirmText: 'Open',
|
||||
cancelText: 'Copy'
|
||||
})
|
||||
.catch(() => {});
|
||||
// TODO: beforeClose alert dialog
|
||||
.then(({ ok }) => {
|
||||
if (!ok) {
|
||||
copyToClipboard(link, 'Link copied to clipboard!');
|
||||
return;
|
||||
}
|
||||
AppApi.OpenLink(link);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@ import { database } from '../service/database';
|
||||
import { escapeTag } from '../shared/utils';
|
||||
import { request } from '../service/request';
|
||||
import { useAdvancedSettingsStore } from './settings/advanced';
|
||||
import { useModalStore } from './modal';
|
||||
import { useNotificationStore } from './notification';
|
||||
import { useUpdateLoopStore } from './updateLoop';
|
||||
import { useUserStore } from './user';
|
||||
@@ -27,6 +28,7 @@ export const useAuthStore = defineStore('Auth', () => {
|
||||
const notificationStore = useNotificationStore();
|
||||
const userStore = useUserStore();
|
||||
const updateLoopStore = useUpdateLoopStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
const state = reactive({
|
||||
@@ -405,21 +407,20 @@ export const useAuthStore = defineStore('Auth', () => {
|
||||
}
|
||||
|
||||
function logout() {
|
||||
ElMessageBox.confirm('Continue? Logout', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
const existingStyle = document.getElementById(
|
||||
'login-container-style'
|
||||
);
|
||||
if (existingStyle) {
|
||||
existingStyle.parentNode.removeChild(existingStyle);
|
||||
}
|
||||
handleLogoutEvent();
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Logout',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
const existingStyle = document.getElementById(
|
||||
'login-container-style'
|
||||
);
|
||||
if (existingStyle) {
|
||||
existingStyle.parentNode.removeChild(existingStyle);
|
||||
}
|
||||
handleLogoutEvent();
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { defineStore } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
@@ -18,6 +17,7 @@ import { database } from '../service/database';
|
||||
import { useAdvancedSettingsStore } from './settings/advanced';
|
||||
import { useAvatarProviderStore } from './avatarProvider';
|
||||
import { useFavoriteStore } from './favorite';
|
||||
import { useModalStore } from './modal';
|
||||
import { useUserStore } from './user';
|
||||
import { useVRCXUpdaterStore } from './vrcxUpdater';
|
||||
import { watchState } from '../service/watchState';
|
||||
@@ -30,6 +30,7 @@ export const useAvatarStore = defineStore('Avatar', () => {
|
||||
const vrcxUpdaterStore = useVRCXUpdaterStore();
|
||||
const advancedSettingsStore = useAdvancedSettingsStore();
|
||||
const userStore = useUserStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
let cachedAvatarModerations = new Map();
|
||||
let cachedAvatars = new Map();
|
||||
@@ -389,12 +390,13 @@ export const useAvatarStore = defineStore('Avatar', () => {
|
||||
}
|
||||
|
||||
function promptClearAvatarHistory() {
|
||||
ElMessageBox.confirm('Continue? Clear Avatar History', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then(() => {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Clear Avatar History',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
clearAvatarHistory();
|
||||
})
|
||||
.catch(() => {});
|
||||
@@ -552,12 +554,13 @@ export const useAvatarStore = defineStore('Avatar', () => {
|
||||
}
|
||||
|
||||
function selectAvatarWithConfirmation(id) {
|
||||
ElMessageBox.confirm(`Continue? Select Avatar`, 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then(() => {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Select Avatar',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
selectAvatarWithoutConfirmation(id);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { defineStore } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
@@ -27,6 +26,7 @@ import { useFeedStore } from './feed';
|
||||
import { useGeneralSettingsStore } from './settings/general';
|
||||
import { useGroupStore } from './group';
|
||||
import { useLocationStore } from './location';
|
||||
import { useModalStore } from './modal';
|
||||
import { useNotificationStore } from './notification';
|
||||
import { useSharedFeedStore } from './sharedFeed';
|
||||
import { useUiStore } from './ui';
|
||||
@@ -51,6 +51,7 @@ export const useFriendStore = defineStore('Friend', () => {
|
||||
const authStore = useAuthStore();
|
||||
const locationStore = useLocationStore();
|
||||
const favoriteStore = useFavoriteStore();
|
||||
const modalStore = useModalStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const state = reactive({
|
||||
@@ -1578,12 +1579,13 @@ export const useFriendStore = defineStore('Friend', () => {
|
||||
}
|
||||
|
||||
function confirmDeleteFriend(id) {
|
||||
ElMessageBox.confirm('Continue? Unfriend', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then(async () => {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Unfriend',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(async ({ ok }) => {
|
||||
if (!ok) return;
|
||||
const args = await friendRequest.deleteFriend({
|
||||
userId: id
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { reactive, ref, shallowReactive, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -19,9 +18,10 @@ import {
|
||||
} from '../api';
|
||||
import { AppDebug } from '../service/appConfig';
|
||||
import { handleImageUploadInput } from '../shared/utils/imageUpload';
|
||||
import { useAdvancedSettingsStore } from './settings/advanced';
|
||||
import { watchState } from '../service/watchState';
|
||||
import { router } from '../plugin/router';
|
||||
import { useAdvancedSettingsStore } from './settings/advanced';
|
||||
import { useModalStore } from './modal';
|
||||
import { watchState } from '../service/watchState';
|
||||
|
||||
import miscReq from '../api/misc';
|
||||
|
||||
@@ -30,6 +30,7 @@ import * as workerTimers from 'worker-timers';
|
||||
export const useGalleryStore = defineStore('Gallery', () => {
|
||||
const advancedSettingsStore = useAdvancedSettingsStore();
|
||||
const { t } = useI18n();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const state = reactive({
|
||||
printCache: [],
|
||||
@@ -515,17 +516,15 @@ export const useGalleryStore = defineStore('Gallery', () => {
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.message.includes('Could not find file')) {
|
||||
ElMessageBox.confirm(
|
||||
'Windows has blocked VRCX from creating files on your system. Please allow VRCX to create files to save emojis, would you like to see instructions on how to fix this?',
|
||||
'Failed to create emoji folder',
|
||||
{
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Ignore',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
.then(async (action) => {
|
||||
if (action !== 'confirm') return;
|
||||
modalStore
|
||||
.confirm({
|
||||
description:
|
||||
'Windows has blocked VRCX from creating files on your system. Please allow VRCX to create files to save emojis, would you like to see instructions on how to fix this?',
|
||||
title: 'Failed to create emoji folder',
|
||||
cancelText: 'Ignore'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
openExternalLink(
|
||||
'https://www.youtube.com/watch?v=1mwmmCdA4D8&t=213s'
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { reactive, ref } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { defineStore } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
@@ -14,6 +13,7 @@ import { useGameLogStore } from './gameLog';
|
||||
import { useInstanceStore } from './instance';
|
||||
import { useLaunchStore } from './launch';
|
||||
import { useLocationStore } from './location';
|
||||
import { useModalStore } from './modal';
|
||||
import { useNotificationStore } from './notification';
|
||||
import { useUpdateLoopStore } from './updateLoop';
|
||||
import { useUserStore } from './user';
|
||||
@@ -36,6 +36,7 @@ export const useGameStore = defineStore('Game', () => {
|
||||
const vrStore = useVrStore();
|
||||
const userStore = useUserStore();
|
||||
const updateLoopStore = useUpdateLoopStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const state = reactive({
|
||||
lastCrashedTime: null
|
||||
@@ -218,17 +219,19 @@ export const useGameStore = defineStore('Game', () => {
|
||||
);
|
||||
if (!result) {
|
||||
// failed to set key
|
||||
ElMessageBox.alert(
|
||||
'VRCX has noticed VRChat debug logging is disabled. VRCX requires debug logging in order to function correctly. Please enable debug logging in VRChat quick menu settings > debug > enable debug logging, then rejoin the instance or restart VRChat.',
|
||||
'Enable debug logging'
|
||||
).catch(() => {});
|
||||
modalStore.alert({
|
||||
description:
|
||||
'VRCX has noticed VRChat debug logging is disabled. VRCX requires debug logging in order to function correctly. Please enable debug logging in VRChat quick menu settings > debug > enable debug logging, then rejoin the instance or restart VRChat.',
|
||||
title: 'Enable debug logging'
|
||||
});
|
||||
console.error('Failed to enable debug logging', result);
|
||||
return;
|
||||
}
|
||||
ElMessageBox.alert(
|
||||
'VRCX has noticed VRChat debug logging is disabled and automatically re-enabled it. VRCX requires debug logging in order to function correctly.',
|
||||
'Enabled debug logging'
|
||||
).catch(() => {});
|
||||
modalStore.alert({
|
||||
description:
|
||||
'VRCX has noticed VRChat debug logging is disabled and automatically re-enabled it. VRCX requires debug logging in order to function correctly.',
|
||||
title: 'Enabled debug logging'
|
||||
});
|
||||
console.log('Enabled debug logging');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { reactive, ref, shallowReactive, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { defineStore } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
@@ -23,6 +22,7 @@ import { useGameStore } from './game';
|
||||
import { useGeneralSettingsStore } from './settings/general';
|
||||
import { useInstanceStore } from './instance';
|
||||
import { useLocationStore } from './location';
|
||||
import { useModalStore } from './modal';
|
||||
import { useNotificationStore } from './notification';
|
||||
import { usePhotonStore } from './photon';
|
||||
import { useSharedFeedStore } from './sharedFeed';
|
||||
@@ -54,6 +54,7 @@ export const useGameLogStore = defineStore('GameLog', () => {
|
||||
const galleryStore = useGalleryStore();
|
||||
const photonStore = usePhotonStore();
|
||||
const sharedFeedStore = useSharedFeedStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const state = reactive({
|
||||
lastLocationAvatarList: new Map()
|
||||
@@ -1414,15 +1415,14 @@ export const useGameLogStore = defineStore('GameLog', () => {
|
||||
return;
|
||||
}
|
||||
if (!advancedSettingsStore.gameLogDisabled) {
|
||||
ElMessageBox.confirm('Continue? Disable GameLog', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then(({ action }) => {
|
||||
if (action === 'confirm') {
|
||||
advancedSettingsStore.setGameLogDisabled();
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Disable GameLog',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
advancedSettingsStore.setGameLogDisabled();
|
||||
})
|
||||
.catch(() => {});
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { nextTick, reactive, ref, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { defineStore } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
@@ -18,6 +17,7 @@ import { database } from '../service/database.js';
|
||||
import { groupDialogFilterOptions } from '../shared/constants/';
|
||||
import { useGameStore } from './game';
|
||||
import { useInstanceStore } from './instance';
|
||||
import { useModalStore } from './modal';
|
||||
import { useNotificationStore } from './notification';
|
||||
import { useUserStore } from './user';
|
||||
import { watchState } from '../service/watchState';
|
||||
@@ -31,6 +31,7 @@ export const useGroupStore = defineStore('Group', () => {
|
||||
const gameStore = useGameStore();
|
||||
const userStore = useUserStore();
|
||||
const notificationStore = useNotificationStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
let cachedGroups = new Map();
|
||||
|
||||
@@ -555,16 +556,13 @@ export const useGroupStore = defineStore('Group', () => {
|
||||
}
|
||||
|
||||
function leaveGroupPrompt(groupId) {
|
||||
ElMessageBox.confirm(
|
||||
'Are you sure you want to leave this group?',
|
||||
'Confirm',
|
||||
{
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Are you sure you want to leave this group?',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
leaveGroup(groupId);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
@@ -21,6 +21,7 @@ import { useInstanceStore } from './instance';
|
||||
import { useInviteStore } from './invite';
|
||||
import { useLaunchStore } from './launch';
|
||||
import { useLocationStore } from './location';
|
||||
import { useModalStore } from './modal';
|
||||
import { useModerationStore } from './moderation';
|
||||
import { useNotificationStore } from './notification';
|
||||
import { useNotificationsSettingsStore } from './settings/notifications';
|
||||
@@ -162,7 +163,8 @@ export function createGlobalStores() {
|
||||
updateLoop: useUpdateLoopStore(),
|
||||
auth: useAuthStore(),
|
||||
vrcStatus: useVrcStatusStore(),
|
||||
charts: useChartsStore()
|
||||
charts: useChartsStore(),
|
||||
modal: useModalStore()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -200,5 +202,6 @@ export {
|
||||
useWorldStore,
|
||||
useSharedFeedStore,
|
||||
useUpdateLoopStore,
|
||||
useVrcStatusStore
|
||||
useVrcStatusStore,
|
||||
useModalStore
|
||||
};
|
||||
|
||||
178
src/stores/modal.js
Normal file
178
src/stores/modal.js
Normal file
@@ -0,0 +1,178 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { i18n } from '@/plugin';
|
||||
import { ref } from 'vue';
|
||||
|
||||
function translate(key, fallback) {
|
||||
try {
|
||||
return i18n.global.t(key);
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ConfirmResult
|
||||
* @property {boolean} ok
|
||||
* @property {'ok' | 'cancel' | 'dismiss' | 'replaced'} reason
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ConfirmOptions
|
||||
* @property {string} title
|
||||
* @property {string} description
|
||||
* @property {string=} confirmText
|
||||
* @property {string=} cancelText
|
||||
* @property {boolean=} dismissible // true: allow esc/outside, false: block
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} AlertOptions
|
||||
* @property {string} title
|
||||
* @property {string} description
|
||||
* @property {string=} confirmText
|
||||
* @property {boolean=} dismissible
|
||||
*/
|
||||
|
||||
// TODO: Method chains for confirm
|
||||
|
||||
export const useModalStore = defineStore('Modal', () => {
|
||||
const alertOpen = ref(false);
|
||||
const alertMode = ref('confirm'); // 'confirm' | 'alert'
|
||||
const alertTitle = ref('');
|
||||
const alertDescription = ref('');
|
||||
const alertOkText = ref('');
|
||||
const alertCancelText = ref('');
|
||||
const alertDismissible = ref(true);
|
||||
|
||||
/** @type {{ resolve: ((result: ConfirmResult) => void) | null } | null} */
|
||||
let pending = null;
|
||||
|
||||
function closeDialog() {
|
||||
alertOpen.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {'ok' | 'cancel' | 'dismiss' | 'replaced'} reason
|
||||
*/
|
||||
function finish(reason) {
|
||||
const resolve = pending?.resolve;
|
||||
pending = null;
|
||||
closeDialog();
|
||||
if (resolve) resolve({ ok: reason === 'ok', reason });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {'ok' | 'cancel' | 'dismiss' | 'replaced'} reason
|
||||
*/
|
||||
function finishWithoutClosing(reason) {
|
||||
const resolve = pending?.resolve;
|
||||
pending = null;
|
||||
if (resolve) resolve({ ok: reason === 'ok', reason });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {'confirm' | 'alert'} mode
|
||||
* @param {any} options
|
||||
*/
|
||||
function openBase(mode, options) {
|
||||
if (pending) {
|
||||
// old dialog is force-finished
|
||||
// do not call closeDialog here because we are about to open a new one
|
||||
finishWithoutClosing('replaced');
|
||||
}
|
||||
|
||||
alertMode.value = mode;
|
||||
alertTitle.value = options.title;
|
||||
alertDescription.value = options.description;
|
||||
alertDismissible.value = options.dismissible !== false;
|
||||
|
||||
if (mode === 'alert') {
|
||||
alertOkText.value =
|
||||
options.confirmText || translate('dialog.alertdialog.ok', 'OK');
|
||||
alertCancelText.value = '';
|
||||
} else {
|
||||
alertOkText.value =
|
||||
options.confirmText ||
|
||||
translate('dialog.alertdialog.confirm', 'Confirm');
|
||||
alertCancelText.value =
|
||||
options.cancelText ||
|
||||
translate('dialog.alertdialog.cancel', 'Cancel');
|
||||
}
|
||||
|
||||
alertOpen.value = true;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
pending = { resolve };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* confirm: always resolve({ok, reason})
|
||||
* @param {ConfirmOptions} options
|
||||
* @returns {Promise<ConfirmResult>}
|
||||
*/
|
||||
function confirm(options) {
|
||||
return openBase('confirm', options);
|
||||
}
|
||||
|
||||
/**
|
||||
* alert: always resolve({ok:true, reason:'ok'}) when closed
|
||||
* @param {AlertOptions} options
|
||||
* @returns {Promise<ConfirmResult>}
|
||||
*/
|
||||
function alert(options) {
|
||||
return openBase('alert', options);
|
||||
}
|
||||
|
||||
function handleOk() {
|
||||
if (!pending) return;
|
||||
finish('ok');
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
if (!pending) return;
|
||||
|
||||
// alert has no cancel semantics
|
||||
if (alertMode.value === 'alert') {
|
||||
finish('ok');
|
||||
return;
|
||||
}
|
||||
|
||||
finish('cancel');
|
||||
}
|
||||
|
||||
function handleDismiss() {
|
||||
if (!pending) return;
|
||||
if (!alertDismissible.value) return;
|
||||
|
||||
// alert: dismiss also means done
|
||||
if (alertMode.value === 'alert') {
|
||||
finish('ok');
|
||||
return;
|
||||
}
|
||||
|
||||
finish('dismiss');
|
||||
}
|
||||
|
||||
function setAlertOpen(open) {
|
||||
alertOpen.value = !!open;
|
||||
}
|
||||
|
||||
return {
|
||||
alertOpen,
|
||||
alertMode,
|
||||
alertTitle,
|
||||
alertDescription,
|
||||
alertOkText,
|
||||
alertCancelText,
|
||||
alertDismissible,
|
||||
|
||||
confirm,
|
||||
alert,
|
||||
|
||||
handleOk,
|
||||
handleCancel,
|
||||
handleDismiss,
|
||||
setAlertOpen
|
||||
};
|
||||
});
|
||||
@@ -8,6 +8,7 @@ import { AppDebug } from '../../service/appConfig';
|
||||
import { database } from '../../service/database';
|
||||
import { languageCodes } from '../../localization';
|
||||
import { useGameStore } from '../game';
|
||||
import { useModalStore } from '../modal';
|
||||
import { useVRCXUpdaterStore } from '../vrcxUpdater';
|
||||
import { useVrcxStore } from '../vrcx';
|
||||
import { watchState } from '../../service/watchState';
|
||||
@@ -19,6 +20,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
const gameStore = useGameStore();
|
||||
const vrcxStore = useVrcxStore();
|
||||
const VRCXUpdaterStore = useVRCXUpdaterStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -466,68 +468,58 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
}
|
||||
|
||||
async function checkSentryConsent() {
|
||||
const { action: consentAction } = await ElMessageBox.confirm(
|
||||
'Help improve VRCX by allowing anonymous error reporting?</br></br>' +
|
||||
'• Only collects crash and error information.</br>' +
|
||||
'• No personal data or VRChat information is collected.</br>' +
|
||||
'• Only enabled in nightly builds.</br>' +
|
||||
'• Can be disabled at anytime in Advanced Settings.',
|
||||
'Anonymous Error Reporting',
|
||||
{
|
||||
type: 'warning',
|
||||
center: true,
|
||||
dangerouslyUseHTMLString: true,
|
||||
closeOnClickModal: false,
|
||||
closeOnPressEscape: false,
|
||||
distinguishCancelAndClose: true
|
||||
}
|
||||
).catch(() => ({ action: 'cancel' }));
|
||||
modalStore
|
||||
.confirm({
|
||||
description:
|
||||
'Help improve VRCX by allowing anonymous error reporting?</br></br>' +
|
||||
'• Only collects crash and error information.</br>' +
|
||||
'• No personal data or VRChat information is collected.</br>' +
|
||||
'• Only enabled in nightly builds.</br>' +
|
||||
'• Can be disabled at anytime in Advanced Settings.',
|
||||
title: 'Anonymous Error Reporting'
|
||||
})
|
||||
.then(async ({ ok }) => {
|
||||
if (!ok) return;
|
||||
modalStore
|
||||
.confirm({
|
||||
description:
|
||||
'Error reporting setting has been enabled. Would you like to restart VRCX now for the change to take effect?',
|
||||
title: 'Restart Required',
|
||||
confirmText: 'Restart Now',
|
||||
cancelText: 'Later'
|
||||
})
|
||||
.then(async ({ ok }) => {
|
||||
if (!ok) return;
|
||||
|
||||
if (consentAction === 'cancel') return;
|
||||
sentryErrorReporting.value = true;
|
||||
configRepository.setBool('VRCX_SentryEnabled', true);
|
||||
|
||||
const { action: restartAction } = await ElMessageBox.confirm(
|
||||
'Error reporting setting has been enabled. Would you like to restart VRCX now for the change to take effect?',
|
||||
'Restart Required',
|
||||
{
|
||||
confirmButtonText: 'Restart Now',
|
||||
cancelButtonText: 'Later',
|
||||
type: 'warning',
|
||||
center: true,
|
||||
closeOnClickModal: false,
|
||||
closeOnPressEscape: false
|
||||
}
|
||||
).catch(() => ({ action: 'cancel' }));
|
||||
|
||||
if (restartAction === 'cancel') return;
|
||||
|
||||
sentryErrorReporting.value = true;
|
||||
configRepository.setBool('VRCX_SentryEnabled', true);
|
||||
|
||||
VRCXUpdaterStore.restartVRCX(false);
|
||||
VRCXUpdaterStore.restartVRCX(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function setSentryErrorReporting() {
|
||||
if (VRCXUpdaterStore.branch !== 'Nightly') return;
|
||||
|
||||
const { action: restartAction } = await ElMessageBox.confirm(
|
||||
'Error reporting setting has been disabled. Would you like to restart VRCX now for the change to take effect?',
|
||||
'Restart Required',
|
||||
{
|
||||
confirmButtonText: 'Restart Now',
|
||||
cancelButtonText: 'Later',
|
||||
type: 'info',
|
||||
center: true
|
||||
}
|
||||
).catch(() => ({ action: 'cancel' }));
|
||||
modalStore
|
||||
.confirm({
|
||||
description:
|
||||
'Error reporting setting has been disabled. Would you like to restart VRCX now for the change to take effect?',
|
||||
title: 'Restart Required',
|
||||
confirmText: 'Restart Now',
|
||||
cancelText: 'Later'
|
||||
})
|
||||
.then(async ({ ok }) => {
|
||||
if (!ok) return;
|
||||
|
||||
if (restartAction === 'cancel') return;
|
||||
|
||||
sentryErrorReporting.value = !sentryErrorReporting.value;
|
||||
await configRepository.setBool(
|
||||
'VRCX_SentryEnabled',
|
||||
sentryErrorReporting.value
|
||||
);
|
||||
VRCXUpdaterStore.restartVRCX(false);
|
||||
sentryErrorReporting.value = !sentryErrorReporting.value;
|
||||
await configRepository.setBool(
|
||||
'VRCX_SentryEnabled',
|
||||
sentryErrorReporting.value
|
||||
);
|
||||
VRCXUpdaterStore.restartVRCX(false);
|
||||
});
|
||||
}
|
||||
|
||||
async function getSqliteTableSizes() {
|
||||
@@ -735,96 +727,87 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
|
||||
|
||||
function cropPrintsChanged() {
|
||||
if (!cropInstancePrints.value) return;
|
||||
ElMessageBox.confirm(
|
||||
t(
|
||||
'view.settings.advanced.advanced.save_instance_prints_to_file.crop_convert_old'
|
||||
),
|
||||
{
|
||||
confirmButtonText: t(
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t(
|
||||
'view.settings.advanced.advanced.save_instance_prints_to_file.crop_convert_old'
|
||||
),
|
||||
title: '',
|
||||
confirmText: t(
|
||||
'view.settings.advanced.advanced.save_instance_prints_to_file.crop_convert_old_confirm'
|
||||
),
|
||||
cancelButtonText: t(
|
||||
cancelText: t(
|
||||
'view.settings.advanced.advanced.save_instance_prints_to_file.crop_convert_old_cancel'
|
||||
),
|
||||
type: 'info',
|
||||
showInput: false
|
||||
}
|
||||
)
|
||||
.then(async ({ action }) => {
|
||||
if (action === 'confirm') {
|
||||
const msgBox = toast.warning(
|
||||
'Batch print cropping in progress...',
|
||||
{ duration: Infinity, position: 'bottom-right' }
|
||||
);
|
||||
try {
|
||||
await AppApi.CropAllPrints(ugcFolderPath.value);
|
||||
toast.success('Batch print cropping complete');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error(`Batch print cropping failed: ${err}`);
|
||||
} finally {
|
||||
toast.dismiss(msgBox);
|
||||
}
|
||||
)
|
||||
})
|
||||
.then(async ({ ok }) => {
|
||||
if (!ok) return;
|
||||
const msgBox = toast.warning(
|
||||
'Batch print cropping in progress...',
|
||||
{ duration: Infinity, position: 'bottom-right' }
|
||||
);
|
||||
try {
|
||||
await AppApi.CropAllPrints(ugcFolderPath.value);
|
||||
toast.success('Batch print cropping complete');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error(`Batch print cropping failed: ${err}`);
|
||||
} finally {
|
||||
toast.dismiss(msgBox);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function askDeleteAllScreenshotMetadata() {
|
||||
ElMessageBox.confirm(
|
||||
t(
|
||||
'view.settings.advanced.advanced.delete_all_screenshot_metadata.ask'
|
||||
),
|
||||
{
|
||||
confirmButtonText: t(
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t(
|
||||
'view.settings.advanced.advanced.delete_all_screenshot_metadata.ask'
|
||||
),
|
||||
title: '',
|
||||
confirmText: t(
|
||||
'view.settings.advanced.advanced.delete_all_screenshot_metadata.confirm_yes'
|
||||
),
|
||||
cancelButtonText: t(
|
||||
cancelText: t(
|
||||
'view.settings.advanced.advanced.delete_all_screenshot_metadata.confirm_no'
|
||||
),
|
||||
type: 'warning',
|
||||
showInput: false
|
||||
}
|
||||
)
|
||||
.then(({ action }) => {
|
||||
if (action === 'confirm') {
|
||||
deleteAllScreenshotMetadata();
|
||||
}
|
||||
)
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
deleteAllScreenshotMetadata();
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function deleteAllScreenshotMetadata() {
|
||||
ElMessageBox.confirm(
|
||||
t(
|
||||
'view.settings.advanced.advanced.delete_all_screenshot_metadata.confirm'
|
||||
),
|
||||
{
|
||||
confirmButtonText: t(
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t(
|
||||
'view.settings.advanced.advanced.delete_all_screenshot_metadata.confirm'
|
||||
),
|
||||
title: '',
|
||||
confirmText: t(
|
||||
'view.settings.advanced.advanced.save_instance_prints_to_file.crop_convert_old_confirm'
|
||||
),
|
||||
cancelButtonText: t(
|
||||
cancelText: t(
|
||||
'view.settings.advanced.advanced.save_instance_prints_to_file.crop_convert_old_cancel'
|
||||
),
|
||||
type: 'warning',
|
||||
showInput: false
|
||||
}
|
||||
)
|
||||
.then(async ({ action }) => {
|
||||
if (action === 'confirm') {
|
||||
const msgBox = toast.warning(
|
||||
'Batch metadata removal in progress...',
|
||||
{ duration: Infinity, position: 'bottom-right' }
|
||||
);
|
||||
try {
|
||||
await AppApi.DeleteAllScreenshotMetadata();
|
||||
toast.success('Batch metadata removal complete');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error(`Batch metadata removal failed: ${err}`);
|
||||
} finally {
|
||||
toast.dismiss(msgBox);
|
||||
}
|
||||
)
|
||||
})
|
||||
.then(async ({ ok }) => {
|
||||
if (!ok) return;
|
||||
const msgBox = toast.warning(
|
||||
'Batch metadata removal in progress...',
|
||||
{ duration: Infinity, position: 'bottom-right' }
|
||||
);
|
||||
try {
|
||||
await AppApi.DeleteAllScreenshotMetadata();
|
||||
toast.success('Batch metadata removal complete');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
toast.error(`Batch metadata removal failed: ${err}`);
|
||||
} finally {
|
||||
toast.dismiss(msgBox);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { defineStore } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
@@ -26,6 +25,7 @@ import { useGameStore } from './game';
|
||||
import { useGroupStore } from './group';
|
||||
import { useInstanceStore } from './instance';
|
||||
import { useLocationStore } from './location';
|
||||
import { useModalStore } from './modal';
|
||||
import { useNotificationStore } from './notification';
|
||||
import { usePhotonStore } from './photon';
|
||||
import { useSearchStore } from './search';
|
||||
@@ -58,6 +58,7 @@ export const useVrcxStore = defineStore('Vrcx', () => {
|
||||
const vrcStatusStore = useVrcStatusStore();
|
||||
const galleryStore = useGalleryStore();
|
||||
const { t } = useI18n();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const state = reactive({
|
||||
databaseVersion: 0,
|
||||
@@ -711,10 +712,10 @@ export const useVrcxStore = defineStore('Vrcx', () => {
|
||||
return;
|
||||
}
|
||||
// popup message about auto restore
|
||||
ElMessageBox.alert(
|
||||
t('dialog.registry_backup.restore_prompt'),
|
||||
t('dialog.registry_backup.header')
|
||||
).catch(() => {});
|
||||
modalStore.alert({
|
||||
description: t('dialog.registry_backup.restore_prompt'),
|
||||
title: t('dialog.registry_backup.header')
|
||||
});
|
||||
showRegistryBackupDialog();
|
||||
await AppApi.FocusWindow();
|
||||
await configRepository.setString(
|
||||
|
||||
@@ -142,16 +142,21 @@
|
||||
import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel } from '@/components/ui/field';
|
||||
import { NumberField, NumberFieldContent, NumberFieldInput } from '@/components/ui/number-field';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Settings } from 'lucide-vue-next';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { Settings } from 'lucide-vue-next';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { onBeforeRouteLeave } from 'vue-router';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAppearanceSettingsStore, useChartsStore, useFriendStore, useUserStore } from '../../../stores';
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useChartsStore,
|
||||
useFriendStore,
|
||||
useModalStore,
|
||||
useUserStore
|
||||
} from '../../../stores';
|
||||
import { applyForceOverrides, computeForceOptions, useMutualGraphChart } from '../composables/useMutualGraphChart';
|
||||
import { createRateLimiter, executeWithBackoff } from '../../../shared/utils';
|
||||
import { database } from '../../../service/database';
|
||||
@@ -164,6 +169,7 @@
|
||||
const { t } = useI18n();
|
||||
const friendStore = useFriendStore();
|
||||
const userStore = useUserStore();
|
||||
const modalStore = useModalStore();
|
||||
const chartsStore = useChartsStore();
|
||||
const appearanceStore = useAppearanceSettingsStore();
|
||||
const { friends } = storeToRefs(friendStore);
|
||||
@@ -396,39 +402,35 @@
|
||||
if (isFetching.value || hasFetched.value || !totalFriends.value) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
t('view.charts.mutual_friend.prompt.message'),
|
||||
t('view.charts.mutual_friend.prompt.title'),
|
||||
{
|
||||
confirmButtonText: t('view.charts.mutual_friend.prompt.confirm'),
|
||||
cancelButtonText: t('view.charts.mutual_friend.prompt.cancel'),
|
||||
type: 'warning'
|
||||
}
|
||||
);
|
||||
await startFetch();
|
||||
} catch {
|
||||
// cancelled
|
||||
}
|
||||
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('view.charts.mutual_friend.prompt.message'),
|
||||
title: t('view.charts.mutual_friend.prompt.title'),
|
||||
confirmText: t('view.charts.mutual_friend.prompt.confirm'),
|
||||
cancelText: t('view.charts.mutual_friend.prompt.cancel')
|
||||
})
|
||||
.then(async ({ ok }) => {
|
||||
if (!ok) return;
|
||||
|
||||
await startFetch();
|
||||
});
|
||||
}
|
||||
|
||||
function promptEnableMutualFriendsSharing() {
|
||||
ElMessageBox.confirm(
|
||||
t('view.charts.mutual_friend.enable_sharing_prompt.message'),
|
||||
t('view.charts.mutual_friend.enable_sharing_prompt.title'),
|
||||
{
|
||||
confirmButtonText: t('view.charts.mutual_friend.enable_sharing_prompt.confirm'),
|
||||
cancelButtonText: t('view.charts.mutual_friend.enable_sharing_prompt.cancel'),
|
||||
type: 'info'
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('view.charts.mutual_friend.enable_sharing_prompt.message'),
|
||||
title: t('view.charts.mutual_friend.enable_sharing_prompt.title'),
|
||||
confirmText: t('view.charts.mutual_friend.enable_sharing_prompt.confirm'),
|
||||
cancelText: t('view.charts.mutual_friend.enable_sharing_prompt.cancel')
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
userStore.toggleSharedConnectionsOptOut();
|
||||
promptInitialFetch();
|
||||
})
|
||||
.catch(() => {
|
||||
// cancelled
|
||||
});
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function cancelFetch() {
|
||||
|
||||
@@ -521,18 +521,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
computed,
|
||||
h,
|
||||
markRaw,
|
||||
nextTick,
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
reactive,
|
||||
ref,
|
||||
watch
|
||||
} from 'vue';
|
||||
import { computed, markRaw, nextTick, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { Ellipsis, Loader, RefreshCcw } from 'lucide-vue-next';
|
||||
import { MoreFilled, Plus, Refresh } from '@element-plus/icons-vue';
|
||||
import { InputGroupField, InputGroupSearch } from '@/components/ui/input-group';
|
||||
@@ -558,7 +547,13 @@
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from '../../components/ui/dropdown-menu';
|
||||
import { useAppearanceSettingsStore, useAvatarStore, useFavoriteStore, useUserStore } from '../../stores';
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useAvatarStore,
|
||||
useFavoriteStore,
|
||||
useModalStore,
|
||||
useUserStore
|
||||
} from '../../stores';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../../components/ui/popover';
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '../../components/ui/resizable';
|
||||
import { avatarRequest, favoriteRequest } from '../../api';
|
||||
@@ -597,6 +592,7 @@
|
||||
const { sortFavorites } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { setSortFavorites } = useAppearanceSettingsStore();
|
||||
const favoriteStore = useFavoriteStore();
|
||||
const modalStore = useModalStore();
|
||||
const {
|
||||
favoriteAvatars,
|
||||
favoriteAvatarGroups,
|
||||
@@ -1183,26 +1179,22 @@
|
||||
showAvatarImportDialog();
|
||||
}
|
||||
|
||||
function showAvatarBulkUnfavoriteSelectionConfirm() {
|
||||
async function showAvatarBulkUnfavoriteSelectionConfirm() {
|
||||
if (!selectedFavoriteAvatars.value.length) {
|
||||
return;
|
||||
}
|
||||
const total = selectedFavoriteAvatars.value.length;
|
||||
ElMessageBox.confirm(
|
||||
`Are you sure you want to unfavorite ${total} favorites?\n This action cannot be undone.`,
|
||||
`Delete ${total} favorites?`,
|
||||
{
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
}
|
||||
)
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
bulkUnfavoriteSelectedAvatars([...selectedFavoriteAvatars.value]);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
const result = await modalStore.confirm({
|
||||
description: `Are you sure you want to unfavorite ${total} favorites?\nThis action cannot be undone.`,
|
||||
title: `Delete ${total} favorites?`
|
||||
});
|
||||
|
||||
if (!result.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
bulkUnfavoriteSelectedAvatars([...selectedFavoriteAvatars.value]);
|
||||
}
|
||||
|
||||
function bulkUnfavoriteSelectedAvatars(ids) {
|
||||
@@ -1252,17 +1244,14 @@
|
||||
async function handleCheckInvalidAvatars(groupName) {
|
||||
handleGroupMenuVisible(localGroupMenuKey(groupName), false);
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
t('view.favorite.avatars.check_description'),
|
||||
t('view.favorite.avatars.check_invalid'),
|
||||
{
|
||||
confirmButtonText: t('confirm.confirm_button'),
|
||||
cancelButtonText: t('confirm.cancel_button'),
|
||||
type: 'info'
|
||||
}
|
||||
);
|
||||
} catch {
|
||||
const startCheckResult = await modalStore.confirm({
|
||||
description: t('view.favorite.avatars.check_description'),
|
||||
title: t('view.favorite.avatars.check_invalid'),
|
||||
confirmText: t('confirm.confirm_button'),
|
||||
cancelText: t('confirm.cancel_button')
|
||||
});
|
||||
|
||||
if (!startCheckResult.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1299,54 +1288,29 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmDelete = await ElMessageBox.confirm(
|
||||
h('div', [
|
||||
h(
|
||||
'p',
|
||||
{ style: 'margin-bottom: 12px;' },
|
||||
t('view.favorite.avatars.confirm_delete_description', { count: result.invalid })
|
||||
),
|
||||
h(
|
||||
'div',
|
||||
{ style: 'margin-top: 12px; margin-bottom: 8px; font-weight: 600;' },
|
||||
t('view.favorite.avatars.removed_list_header')
|
||||
),
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
style: 'max-height: 200px; overflow-y: auto; background: var(--el-fill-color-lighter); padding: 8px; border-radius: 4px;'
|
||||
},
|
||||
result.invalidIds.map((id) =>
|
||||
h('div', { style: 'font-family: monospace; font-size: 12px; padding: 2px 0;' }, id)
|
||||
)
|
||||
)
|
||||
]),
|
||||
t('view.favorite.avatars.confirm_delete_invalid'),
|
||||
{
|
||||
confirmButtonText: t('confirm.confirm_button'),
|
||||
cancelButtonText: t('view.favorite.avatars.copy_removed_ids'),
|
||||
distinguishCancelAndClose: true,
|
||||
type: 'warning',
|
||||
beforeClose: (action, instance, done) => {
|
||||
if (action === 'cancel') {
|
||||
navigator.clipboard
|
||||
.writeText(result.invalidIds.join('\n'))
|
||||
.then(() => {
|
||||
toast.success(t('view.favorite.avatars.copied_ids'));
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error(t('view.favorite.avatars.copy_failed'));
|
||||
});
|
||||
return;
|
||||
}
|
||||
done();
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
const invalidIdsText = result.invalidIds.join('\n');
|
||||
|
||||
if (!confirmDelete) {
|
||||
const confirmDeleteResult = await modalStore.confirm({
|
||||
description:
|
||||
`${t('view.favorite.avatars.confirm_delete_description', { count: result.invalid })}` +
|
||||
`\n\n${t('view.favorite.avatars.removed_list_header')}\n` +
|
||||
invalidIdsText,
|
||||
title: t('view.favorite.avatars.confirm_delete_invalid'),
|
||||
confirmText: t('confirm.confirm_button'),
|
||||
cancelText: t('view.favorite.avatars.copy_removed_ids')
|
||||
});
|
||||
|
||||
if (!confirmDeleteResult.ok) {
|
||||
if (confirmDeleteResult.reason === 'cancel') {
|
||||
navigator.clipboard
|
||||
.writeText(invalidIdsText)
|
||||
.then(() => {
|
||||
toast.success(t('view.favorite.avatars.copied_ids'));
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error(t('view.favorite.avatars.copy_failed'));
|
||||
});
|
||||
}
|
||||
toast.info(t('view.favorite.avatars.delete_cancelled'));
|
||||
return;
|
||||
}
|
||||
@@ -1434,18 +1398,16 @@
|
||||
}
|
||||
|
||||
function clearFavoriteGroup(ctx) {
|
||||
ElMessageBox.confirm('Continue? Clear Group', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
favoriteRequest.clearFavoriteGroup({
|
||||
type: ctx.type,
|
||||
group: ctx.name
|
||||
});
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Clear Group',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(() => {
|
||||
favoriteRequest.clearFavoriteGroup({
|
||||
type: ctx.type,
|
||||
group: ctx.name
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
@@ -1477,16 +1439,12 @@
|
||||
}
|
||||
|
||||
function promptLocalAvatarFavoriteGroupDelete(group) {
|
||||
ElMessageBox.confirm(`Delete Group? ${group}`, 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
deleteLocalAvatarFavoriteGroup(group);
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: `Delete Group? ${group}`,
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(() => deleteLocalAvatarFavoriteGroup(group))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
|
||||
@@ -331,9 +331,9 @@
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from '../../components/ui/dropdown-menu';
|
||||
import { useAppearanceSettingsStore, useFavoriteStore, useModalStore, useUserStore } from '../../stores';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../../components/ui/popover';
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '../../components/ui/resizable';
|
||||
import { useAppearanceSettingsStore, useFavoriteStore, useUserStore } from '../../stores';
|
||||
import { Badge } from '../../components/ui/badge';
|
||||
import { Slider } from '../../components/ui/slider';
|
||||
import { Switch } from '../../components/ui/switch';
|
||||
@@ -358,6 +358,7 @@
|
||||
const { sortFavorites } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { setSortFavorites } = useAppearanceSettingsStore();
|
||||
const favoriteStore = useFavoriteStore();
|
||||
const modalStore = useModalStore();
|
||||
const {
|
||||
favoriteFriends,
|
||||
favoriteFriendGroups,
|
||||
@@ -751,20 +752,12 @@
|
||||
return;
|
||||
}
|
||||
const total = selectedFavoriteFriends.value.length;
|
||||
ElMessageBox.confirm(
|
||||
`Are you sure you want to unfavorite ${total} favorites?\n This action cannot be undone.`,
|
||||
`Delete ${total} favorites?`,
|
||||
{
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
}
|
||||
)
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
bulkUnfavoriteSelectedFriends([...selectedFavoriteFriends.value]);
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: `Are you sure you want to unfavorite ${total} favorites?\n This action cannot be undone.`,
|
||||
title: `Delete ${total} favorites?`
|
||||
})
|
||||
.then(({ ok }) => ok && bulkUnfavoriteSelectedFriends([...selectedFavoriteFriends.value]))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -779,18 +772,16 @@
|
||||
}
|
||||
|
||||
function clearFavoriteGroup(ctx) {
|
||||
ElMessageBox.confirm('Continue? Clear Group', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
favoriteRequest.clearFavoriteGroup({
|
||||
type: ctx.type,
|
||||
group: ctx.name
|
||||
});
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Clear Group',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(() => {
|
||||
favoriteRequest.clearFavoriteGroup({
|
||||
type: ctx.type,
|
||||
group: ctx.name
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -461,9 +461,9 @@
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from '../../components/ui/dropdown-menu';
|
||||
import { useAppearanceSettingsStore, useFavoriteStore, useModalStore, useWorldStore } from '../../stores';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '../../components/ui/popover';
|
||||
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '../../components/ui/resizable';
|
||||
import { useAppearanceSettingsStore, useFavoriteStore, useWorldStore } from '../../stores';
|
||||
import { favoriteRequest, worldRequest } from '../../api';
|
||||
import { Badge } from '../../components/ui/badge';
|
||||
import { Slider } from '../../components/ui/slider';
|
||||
@@ -490,6 +490,7 @@
|
||||
const { sortFavorites } = storeToRefs(useAppearanceSettingsStore());
|
||||
const { setSortFavorites } = useAppearanceSettingsStore();
|
||||
const favoriteStore = useFavoriteStore();
|
||||
const modalStore = useModalStore();
|
||||
const {
|
||||
favoriteWorlds,
|
||||
favoriteWorldGroups,
|
||||
@@ -1054,21 +1055,13 @@
|
||||
return;
|
||||
}
|
||||
const total = selectedFavoriteWorlds.value.length;
|
||||
ElMessageBox.confirm(
|
||||
`Are you sure you want to unfavorite ${total} favorites?
|
||||
modalStore
|
||||
.confirm({
|
||||
description: `Are you sure you want to unfavorite ${total} favorites?
|
||||
This action cannot be undone.`,
|
||||
`Delete ${total} favorites?`,
|
||||
{
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
}
|
||||
)
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
bulkUnfavoriteSelectedWorlds([...selectedFavoriteWorlds.value]);
|
||||
}
|
||||
title: `Delete ${total} favorites?`
|
||||
})
|
||||
.then(() => bulkUnfavoriteSelectedWorlds([...selectedFavoriteWorlds.value]))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -1175,32 +1168,26 @@
|
||||
}
|
||||
|
||||
function promptLocalWorldFavoriteGroupDelete(group) {
|
||||
ElMessageBox.confirm(`Delete Group? ${group}`, 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
deleteLocalWorldFavoriteGroup(group);
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: `Delete Group? ${group}`,
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(() => deleteLocalWorldFavoriteGroup(group))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function clearFavoriteGroup(ctx) {
|
||||
ElMessageBox.confirm('Continue? Clear Group', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
favoriteRequest.clearFavoriteGroup({
|
||||
type: ctx.type,
|
||||
group: ctx.name
|
||||
});
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Clear Group',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(() => {
|
||||
favoriteRequest.clearFavoriteGroup({
|
||||
type: ctx.type,
|
||||
group: ctx.name
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -108,7 +108,6 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { storeToRefs } from 'pinia';
|
||||
@@ -119,6 +118,7 @@
|
||||
import {
|
||||
useAppearanceSettingsStore,
|
||||
useFriendStore,
|
||||
useModalStore,
|
||||
useSearchStore,
|
||||
useUserStore,
|
||||
useVrcxStore
|
||||
@@ -138,6 +138,7 @@
|
||||
const emit = defineEmits(['lookup-user']);
|
||||
|
||||
const { friends } = storeToRefs(useFriendStore());
|
||||
const modalStore = useModalStore();
|
||||
const { getAllUserStats, getAllUserMutualCount, confirmDeleteFriend, handleFriendDelete } = useFriendStore();
|
||||
const { randomUserColours } = storeToRefs(useAppearanceSettingsStore());
|
||||
const vrcxStore = useVrcxStore();
|
||||
@@ -324,25 +325,18 @@
|
||||
.filter((item) => selectedFriends.value.has(item.id))
|
||||
.map((item) => item.displayName);
|
||||
if (!pending.length) return;
|
||||
ElMessageBox.confirm(
|
||||
`Are you sure you want to delete ${pending.length} friends?
|
||||
This can negatively affect your trust rank,
|
||||
This action cannot be undone.`,
|
||||
`Delete ${pending.length} friends?`,
|
||||
{
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info',
|
||||
showInput: true,
|
||||
inputType: 'textarea',
|
||||
inputValue: pending.join('\r\n')
|
||||
}
|
||||
)
|
||||
.then(({ action }) => {
|
||||
if (action === 'confirm') {
|
||||
bulkUnfriendSelection();
|
||||
}
|
||||
const description =
|
||||
`Are you sure you want to delete ${pending.length} friends?\n` +
|
||||
'This can negatively affect your trust rank,\n' +
|
||||
'This action cannot be undone.\n\n' +
|
||||
pending.join('\n');
|
||||
|
||||
modalStore
|
||||
.confirm({
|
||||
description,
|
||||
title: `Delete ${pending.length} friends?`
|
||||
})
|
||||
.then(({ ok }) => ok && bulkUnfriendSelection())
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -355,9 +349,9 @@
|
||||
selectedFriends.value.delete(item.id);
|
||||
}
|
||||
}
|
||||
ElMessageBox.alert(`Unfriended ${selectedFriends.value.size} friends.`, 'Bulk Unfriend Complete', {
|
||||
confirmButtonText: 'OK',
|
||||
type: 'success'
|
||||
modalStore.alert({
|
||||
description: `Unfriended ${selectedFriends.value.size} friends.`,
|
||||
title: 'Bulk Unfriend Complete'
|
||||
});
|
||||
selectedFriends.value.clear();
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -61,7 +60,7 @@
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '../../components/ui/select';
|
||||
import { useAppearanceSettingsStore, useFriendStore, useVrcxStore } from '../../stores';
|
||||
import { useAppearanceSettingsStore, useFriendStore, useModalStore, useVrcxStore } from '../../stores';
|
||||
import { DataTableLayout } from '../../components/ui/data-table';
|
||||
import { InputGroupField } from '../../components/ui/input-group';
|
||||
import { createColumns } from './columns.jsx';
|
||||
@@ -74,6 +73,7 @@
|
||||
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
const vrcxStore = useVrcxStore();
|
||||
const modalStore = useModalStore();
|
||||
const { hideUnfriends } = storeToRefs(appearanceSettingsStore);
|
||||
const { friendLogTable } = storeToRefs(useFriendStore());
|
||||
|
||||
@@ -149,16 +149,12 @@
|
||||
saveTableFilters();
|
||||
}
|
||||
function deleteFriendLogPrompt(row) {
|
||||
ElMessageBox.confirm('Continue? Delete Log', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
deleteFriendLog(row);
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Delete Log',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => ok && deleteFriendLog(row))
|
||||
.catch(() => {});
|
||||
}
|
||||
function deleteFriendLog(row) {
|
||||
|
||||
@@ -57,14 +57,13 @@
|
||||
<script setup>
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { useAppearanceSettingsStore, useGameLogStore, useVrcxStore } from '../../stores';
|
||||
import { useAppearanceSettingsStore, useGameLogStore, useModalStore, useVrcxStore } from '../../stores';
|
||||
import { DataTableLayout } from '../../components/ui/data-table';
|
||||
import { InputGroupField } from '../../components/ui/input-group';
|
||||
import { createColumns } from './columns.jsx';
|
||||
@@ -77,6 +76,7 @@
|
||||
const { gameLogTable } = storeToRefs(useGameLogStore());
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
const vrcxStore = useVrcxStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
function getGameLogCreatedAt(row) {
|
||||
if (typeof row?.created_at === 'string' && row.created_at.length > 0) {
|
||||
@@ -134,16 +134,12 @@
|
||||
});
|
||||
|
||||
function deleteGameLogEntryPrompt(row) {
|
||||
ElMessageBox.confirm('Continue? Delete Log', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
deleteGameLogEntry(row);
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Delete Log',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => ok && deleteGameLogEntry(row))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
|
||||
@@ -51,14 +51,13 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAppearanceSettingsStore, useModerationStore, useVrcxStore } from '../../stores';
|
||||
import { useAppearanceSettingsStore, useModalStore, useModerationStore, useVrcxStore } from '../../stores';
|
||||
import { DataTableLayout } from '../../components/ui/data-table';
|
||||
import { createColumns } from './columns.jsx';
|
||||
import { moderationTypes } from '../../shared/constants';
|
||||
@@ -73,6 +72,7 @@
|
||||
const { refreshPlayerModerations, handlePlayerModerationDelete } = useModerationStore();
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
const vrcxStore = useVrcxStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const moderationRef = ref(null);
|
||||
const { tableStyle: tableHeightStyle } = useDataTableScrollHeight(moderationRef, {
|
||||
@@ -110,16 +110,12 @@
|
||||
}
|
||||
|
||||
function deletePlayerModerationPrompt(row) {
|
||||
ElMessageBox.confirm(`Continue? Delete Moderation ${row.type}`, 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
deletePlayerModeration(row);
|
||||
}
|
||||
modalStore
|
||||
.confirm({
|
||||
description: `Continue? Delete Moderation ${row.type}`,
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => ok && deletePlayerModeration(row))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,6 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupField } from '@/components/ui/input-group';
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
@@ -102,6 +101,7 @@
|
||||
useGroupStore,
|
||||
useInviteStore,
|
||||
useLocationStore,
|
||||
useModalStore,
|
||||
useNotificationStore,
|
||||
useUserStore,
|
||||
useVrcxStore
|
||||
@@ -129,6 +129,7 @@
|
||||
const { currentUser } = storeToRefs(useUserStore());
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
const vrcxStore = useVrcxStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -321,17 +322,18 @@
|
||||
|
||||
function acceptFriendRequestNotification(row) {
|
||||
// FIXME: 메시지 수정
|
||||
ElMessageBox.confirm('Continue? Accept Friend Request', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
notificationRequest.acceptFriendRequestNotification({
|
||||
notificationId: row.id
|
||||
});
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Accept Friend Request',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
notificationRequest.acceptFriendRequestNotification({
|
||||
notificationId: row.id
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
@@ -345,46 +347,47 @@
|
||||
}
|
||||
|
||||
function acceptRequestInvite(row) {
|
||||
ElMessageBox.confirm('Continue? Send Invite', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
let currentLocation = lastLocation.value.location;
|
||||
if (lastLocation.value.location === 'traveling') {
|
||||
currentLocation = lastLocationDestination.value;
|
||||
}
|
||||
if (!currentLocation) {
|
||||
// game log disabled, use API location
|
||||
currentLocation = currentUser.$locationTag;
|
||||
}
|
||||
const L = parseLocation(currentLocation);
|
||||
worldRequest
|
||||
.getCachedWorld({
|
||||
worldId: L.worldId
|
||||
})
|
||||
.then((args) => {
|
||||
notificationRequest
|
||||
.sendInvite(
|
||||
{
|
||||
instanceId: L.tag,
|
||||
worldId: L.tag,
|
||||
worldName: args.ref.name,
|
||||
rsvp: true
|
||||
},
|
||||
row.senderUserId
|
||||
)
|
||||
.then((_args) => {
|
||||
toast('Invite sent');
|
||||
notificationRequest.hideNotification({
|
||||
notificationId: row.id
|
||||
});
|
||||
return _args;
|
||||
});
|
||||
});
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Send Invite',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
let currentLocation = lastLocation.value.location;
|
||||
if (lastLocation.value.location === 'traveling') {
|
||||
currentLocation = lastLocationDestination.value;
|
||||
}
|
||||
if (!currentLocation) {
|
||||
// game log disabled, use API location
|
||||
currentLocation = currentUser.$locationTag;
|
||||
}
|
||||
const L = parseLocation(currentLocation);
|
||||
worldRequest
|
||||
.getCachedWorld({
|
||||
worldId: L.worldId
|
||||
})
|
||||
.then((args) => {
|
||||
notificationRequest
|
||||
.sendInvite(
|
||||
{
|
||||
instanceId: L.tag,
|
||||
worldId: L.tag,
|
||||
worldName: args.ref.name,
|
||||
rsvp: true
|
||||
},
|
||||
row.senderUserId
|
||||
)
|
||||
.then((_args) => {
|
||||
toast('Invite sent');
|
||||
notificationRequest.hideNotification({
|
||||
notificationId: row.id
|
||||
});
|
||||
return _args;
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
@@ -454,13 +457,13 @@
|
||||
}
|
||||
|
||||
function hideNotificationPrompt(row) {
|
||||
ElMessageBox.confirm(`Continue? Decline ${row.type}`, 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: `Continue? Decline ${row.type}`,
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (ok) {
|
||||
hideNotification(row);
|
||||
}
|
||||
})
|
||||
@@ -478,13 +481,13 @@
|
||||
}
|
||||
|
||||
function deleteNotificationLogPrompt(row) {
|
||||
ElMessageBox.confirm(`Continue? Delete ${row.type}`, 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: `Continue? Delete ${row.type}`,
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (ok) {
|
||||
deleteNotificationLog(row);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAdvancedSettingsStore, useModalStore, useVrcxStore } from '../../../stores';
|
||||
import { downloadAndSaveJson, removeFromArray } from '../../../shared/utils';
|
||||
import { useAdvancedSettingsStore, useVrcxStore } from '../../../stores';
|
||||
import { Switch } from '../../../components/ui/switch';
|
||||
import { createColumns } from './registryBackupColumns.jsx';
|
||||
import { useVrcxVueTable } from '../../../lib/table/useVrcxVueTable';
|
||||
@@ -67,6 +67,7 @@
|
||||
const { isRegistryBackupDialogVisible } = storeToRefs(useVrcxStore());
|
||||
const { vrcRegistryAutoBackup, vrcRegistryAskRestore } = storeToRefs(useAdvancedSettingsStore());
|
||||
const { setVrcRegistryAutoBackup, setVrcRegistryAskRestore } = useAdvancedSettingsStore();
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -121,13 +122,13 @@
|
||||
}
|
||||
|
||||
function restoreVrcRegistryBackup(row) {
|
||||
ElMessageBox.confirm('Continue? Restore Backup', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action !== 'confirm') {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Restore Backup',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
const data = JSON.stringify(row.data);
|
||||
@@ -155,13 +156,13 @@
|
||||
}
|
||||
|
||||
function deleteVrcRegistry() {
|
||||
ElMessageBox.confirm('Continue? Delete VRC Registry Settings', 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action !== 'confirm') {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Delete VRC Registry Settings',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
AppApi.DeleteVRChatRegistryFolder().then(() => {
|
||||
@@ -178,8 +179,6 @@
|
||||
|
||||
function promptVrcRegistryBackupName() {
|
||||
ElMessageBox.prompt('Enter a name for the backup', 'Backup Name', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
inputPattern: /\S+/,
|
||||
inputErrorMessage: 'Name is required',
|
||||
inputValue: 'Backup'
|
||||
|
||||
@@ -173,23 +173,23 @@
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { InputGroupAction } from '@/components/ui/input-group';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { InputGroupAction } from '@/components/ui/input-group';
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAdvancedSettingsStore, useGameStore, useModalStore } from '../../../stores';
|
||||
import { VRChatCameraResolutions, VRChatScreenshotResolutions } from '../../../shared/constants';
|
||||
import { getVRChatResolution, openExternalLink } from '../../../shared/utils';
|
||||
import { useAdvancedSettingsStore, useGameStore } from '../../../stores';
|
||||
|
||||
const { VRChatUsedCacheSize, VRChatTotalCacheSize, VRChatCacheSizeLoading } = storeToRefs(useGameStore());
|
||||
const { sweepVRChatCache, getVRChatCacheSize } = useGameStore();
|
||||
const { folderSelectorDialog } = useAdvancedSettingsStore();
|
||||
const { isVRChatConfigDialogVisible } = storeToRefs(useAdvancedSettingsStore());
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -322,13 +322,13 @@
|
||||
});
|
||||
|
||||
function showDeleteAllVRChatCacheConfirm() {
|
||||
ElMessageBox.confirm(`Continue? Delete all VRChat cache`, 'Confirm', {
|
||||
confirmButtonText: 'Confirm',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'info'
|
||||
})
|
||||
.then((action) => {
|
||||
if (action === 'confirm') {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: 'Continue? Delete all VRChat cache',
|
||||
title: 'Confirm'
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (ok) {
|
||||
deleteAllVRChatCache();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
<div v-for="friendArr in friendsInSameInstance" :key="friendArr[0].ref.$location.tag">
|
||||
<div class="mb-1 flex items-center">
|
||||
<Location
|
||||
class="extra text-neutral-300!"
|
||||
class="extra text-muted-foreground!"
|
||||
:location="getFriendsLocations(friendArr)"
|
||||
style="display: inline" />
|
||||
<span class="extra" style="margin-left: 5px">{{ `(${friendArr.length})` }}</span>
|
||||
|
||||
Reference in New Issue
Block a user