diff --git a/src/components/dialogs/AvatarDialog/AvatarDialog.vue b/src/components/dialogs/AvatarDialog/AvatarDialog.vue index b1886012..acd90936 100644 --- a/src/components/dialogs/AvatarDialog/AvatarDialog.vue +++ b/src/components/dialogs/AvatarDialog/AvatarDialog.vue @@ -545,7 +545,6 @@ import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from '@/components/ui/carousel'; import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue'; import { Button } from '@/components/ui/button'; - import { ElMessageBox } from 'element-plus'; import { InputGroupTextareaField } from '@/components/ui/input-group'; import { TabsUnderline } from '@/components/ui/tabs'; import { storeToRefs } from 'pinia'; @@ -925,18 +924,17 @@ } function promptChangeAvatarDescription(avatar) { - ElMessageBox.prompt( - t('prompt.change_avatar_description.description'), - t('prompt.change_avatar_description.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.change_avatar_description.ok'), - cancelButtonText: t('prompt.change_avatar_description.cancel'), + modalStore + .prompt({ + title: t('prompt.change_avatar_description.header'), + description: t('prompt.change_avatar_description.description'), + confirmText: t('prompt.change_avatar_description.ok'), + cancelText: t('prompt.change_avatar_description.cancel'), inputValue: avatar.ref.description, - inputErrorMessage: t('prompt.change_avatar_description.input_error') - } - ) - .then(({ value }) => { + errorMessage: t('prompt.change_avatar_description.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; if (value && value !== avatar.ref.description) { avatarRequest .saveAvatar({ @@ -954,14 +952,17 @@ } function promptRenameAvatar(avatar) { - ElMessageBox.prompt(t('prompt.rename_avatar.description'), t('prompt.rename_avatar.header'), { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.rename_avatar.ok'), - cancelButtonText: t('prompt.rename_avatar.cancel'), - inputValue: avatar.ref.name, - inputErrorMessage: t('prompt.rename_avatar.input_error') - }) - .then(({ value }) => { + modalStore + .prompt({ + title: t('prompt.rename_avatar.header'), + description: t('prompt.rename_avatar.description'), + confirmText: t('prompt.rename_avatar.ok'), + cancelText: t('prompt.rename_avatar.cancel'), + inputValue: avatar.ref.name, + errorMessage: t('prompt.rename_avatar.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; if (value && value !== avatar.ref.name) { avatarRequest .saveAvatar({ diff --git a/src/components/dialogs/UserDialog/UserDialog.vue b/src/components/dialogs/UserDialog/UserDialog.vue index e9942a5c..030028ac 100644 --- a/src/components/dialogs/UserDialog/UserDialog.vue +++ b/src/components/dialogs/UserDialog/UserDialog.vue @@ -1304,6 +1304,7 @@ ArrowUp, Copy, Download, + DownloadIcon, Eye, Languages, Loader2, diff --git a/src/components/dialogs/WorldDialog/WorldDialog.vue b/src/components/dialogs/WorldDialog/WorldDialog.vue index a0c7fb7e..7d12dd05 100644 --- a/src/components/dialogs/WorldDialog/WorldDialog.vue +++ b/src/components/dialogs/WorldDialog/WorldDialog.vue @@ -746,7 +746,6 @@ } from 'lucide-vue-next'; import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue'; import { Button } from '@/components/ui/button'; - import { ElMessageBox } from 'element-plus'; import { InputGroupTextareaField } from '@/components/ui/input-group'; import { TabsUnderline } from '@/components/ui/tabs'; import { storeToRefs } from 'pinia'; @@ -1120,14 +1119,17 @@ } function promptRenameWorld(world) { - ElMessageBox.prompt(t('prompt.rename_world.description'), t('prompt.rename_world.header'), { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.rename_world.ok'), - cancelButtonText: t('prompt.rename_world.cancel'), - inputValue: world.ref.name, - inputErrorMessage: t('prompt.rename_world.input_error') - }) - .then(({ value }) => { + modalStore + .prompt({ + title: t('prompt.rename_world.header'), + description: t('prompt.rename_world.description'), + confirmText: t('prompt.rename_world.ok'), + cancelText: t('prompt.rename_world.cancel'), + inputValue: world.ref.name, + errorMessage: t('prompt.rename_world.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; if (value && value !== world.ref.name) { worldRequest .saveWorld({ @@ -1143,18 +1145,17 @@ .catch(() => {}); } function promptChangeWorldDescription(world) { - ElMessageBox.prompt( - t('prompt.change_world_description.description'), - t('prompt.change_world_description.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.change_world_description.ok'), - cancelButtonText: t('prompt.change_world_description.cancel'), + modalStore + .prompt({ + title: t('prompt.change_world_description.header'), + description: t('prompt.change_world_description.description'), + confirmText: t('prompt.change_world_description.ok'), + cancelText: t('prompt.change_world_description.cancel'), inputValue: world.ref.description, - inputErrorMessage: t('prompt.change_world_description.input_error') - } - ) - .then(({ value }) => { + errorMessage: t('prompt.change_world_description.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; if (value && value !== world.ref.description) { worldRequest .saveWorld({ @@ -1171,15 +1172,18 @@ } function promptChangeWorldCapacity(world) { - ElMessageBox.prompt(t('prompt.change_world_capacity.description'), t('prompt.change_world_capacity.header'), { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.change_world_capacity.ok'), - cancelButtonText: t('prompt.change_world_capacity.cancel'), - inputValue: world.ref.capacity, - inputPattern: /\d+$/, - inputErrorMessage: t('prompt.change_world_capacity.input_error') - }) - .then(({ value }) => { + modalStore + .prompt({ + title: t('prompt.change_world_capacity.header'), + description: t('prompt.change_world_capacity.description'), + confirmText: t('prompt.change_world_capacity.ok'), + cancelText: t('prompt.change_world_capacity.cancel'), + inputValue: world.ref.capacity, + pattern: /\d+$/, + errorMessage: t('prompt.change_world_capacity.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; if (value && value !== world.ref.capacity) { worldRequest .saveWorld({ @@ -1196,19 +1200,18 @@ } function promptChangeWorldRecommendedCapacity(world) { - ElMessageBox.prompt( - t('prompt.change_world_recommended_capacity.description'), - t('prompt.change_world_recommended_capacity.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.change_world_capacity.ok'), - cancelButtonText: t('prompt.change_world_capacity.cancel'), + modalStore + .prompt({ + title: t('prompt.change_world_recommended_capacity.header'), + description: t('prompt.change_world_recommended_capacity.description'), + confirmText: t('prompt.change_world_capacity.ok'), + cancelText: t('prompt.change_world_capacity.cancel'), inputValue: world.ref.recommendedCapacity, - inputPattern: /\d+$/, - inputErrorMessage: t('prompt.change_world_recommended_capacity.input_error') - } - ) - .then(({ value }) => { + pattern: /\d+$/, + errorMessage: t('prompt.change_world_recommended_capacity.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; if (value && value !== world.ref.recommendedCapacity) { worldRequest .saveWorld({ @@ -1225,14 +1228,17 @@ } function promptChangeWorldYouTubePreview(world) { - ElMessageBox.prompt(t('prompt.change_world_preview.description'), t('prompt.change_world_preview.header'), { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.change_world_preview.ok'), - cancelButtonText: t('prompt.change_world_preview.cancel'), - inputValue: world.ref.previewYoutubeId, - inputErrorMessage: t('prompt.change_world_preview.input_error') - }) - .then(({ value }) => { + modalStore + .prompt({ + title: t('prompt.change_world_preview.header'), + description: t('prompt.change_world_preview.description'), + confirmText: t('prompt.change_world_preview.ok'), + cancelText: t('prompt.change_world_preview.cancel'), + inputValue: world.ref.previewYoutubeId, + errorMessage: t('prompt.change_world_preview.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; if (value && value !== world.ref.previewYoutubeId) { let processedValue = value; if (value.length > 11) { diff --git a/src/components/ui/dialog/DialogContent.vue b/src/components/ui/dialog/DialogContent.vue index e4bc487f..16527a95 100644 --- a/src/components/ui/dialog/DialogContent.vue +++ b/src/components/ui/dialog/DialogContent.vue @@ -40,7 +40,7 @@ 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-50 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', + '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 ) "> diff --git a/src/components/ui/dialog/DialogOverlay.vue b/src/components/ui/dialog/DialogOverlay.vue index f271b7af..426340d1 100644 --- a/src/components/ui/dialog/DialogOverlay.vue +++ b/src/components/ui/dialog/DialogOverlay.vue @@ -19,7 +19,7 @@ v-bind="delegatedProps" :class=" cn( - '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-50 bg-black/80', + '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', props.class ) "> diff --git a/src/components/ui/dialog/DialogScrollContent.vue b/src/components/ui/dialog/DialogScrollContent.vue index eef37eff..8878e641 100644 --- a/src/components/ui/dialog/DialogScrollContent.vue +++ b/src/components/ui/dialog/DialogScrollContent.vue @@ -32,11 +32,11 @@ diff --git a/src/components/ui/hover-card/HoverCardContent.vue b/src/components/ui/hover-card/HoverCardContent.vue index 42152301..436936ab 100644 --- a/src/components/ui/hover-card/HoverCardContent.vue +++ b/src/components/ui/hover-card/HoverCardContent.vue @@ -43,7 +43,7 @@ v-bind="{ ...$attrs, ...forwardedProps }" :class=" cn( - 'bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 rounded-md border p-4 shadow-md outline-hidden', + 'bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-10000 w-64 rounded-md border p-4 shadow-md outline-hidden', props.class ) "> diff --git a/src/components/ui/input/Input.vue b/src/components/ui/input/Input.vue index 86f30b92..675e242c 100644 --- a/src/components/ui/input/Input.vue +++ b/src/components/ui/input/Input.vue @@ -11,7 +11,6 @@ const emits = defineEmits(['update:modelValue']); const modelValue = useVModel(props, 'modelValue', emits, { - passive: true, defaultValue: props.defaultValue }); diff --git a/src/stores/auth.js b/src/stores/auth.js index 9aa30ddd..534abfae 100644 --- a/src/stores/auth.js +++ b/src/stores/auth.js @@ -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'; @@ -255,15 +254,21 @@ export const useAuthStore = defineStore('Auth', () => { if (advancedSettingsStore.enablePrimaryPassword) { enablePrimaryPasswordDialog.value.visible = true; } else { - ElMessageBox.prompt( - t('prompt.primary_password.description'), - t('prompt.primary_password.header'), - { - inputType: 'password', - inputPattern: /[\s\S]{1,32}/ - } - ) - .then(async ({ value }) => { + modalStore + .prompt({ + title: t('prompt.primary_password.header'), + description: t('prompt.primary_password.description'), + pattern: /[\s\S]{1,32}/ + }) + .then(async ({ ok, value }) => { + if (!ok) { + advancedSettingsStore.enablePrimaryPassword = true; + advancedSettingsStore.setEnablePrimaryPasswordConfigRepository( + true + ); + return; + } + const savedCredentials = JSON.parse( await configRepository.getString( 'savedCredentials', @@ -299,7 +304,8 @@ export const useAuthStore = defineStore('Auth', () => { }); } }) - .catch(async () => { + .catch((err) => { + console.error(err); advancedSettingsStore.enablePrimaryPassword = true; advancedSettingsStore.setEnablePrimaryPasswordConfigRepository( true @@ -378,16 +384,19 @@ export const useAuthStore = defineStore('Auth', () => { return new Promise((resolve, reject) => { if (!advancedSettingsStore.enablePrimaryPassword) { resolve(args.password); + return; } - ElMessageBox.prompt( - t('prompt.primary_password.description'), - t('prompt.primary_password.header'), - { - inputType: 'password', - inputPattern: /[\s\S]{1,32}/ - } - ) - .then(({ value }) => { + modalStore + .prompt({ + title: t('prompt.primary_password.header'), + description: t('prompt.primary_password.description'), + pattern: /[\s\S]{1,32}/ + }) + .then(({ ok, value }) => { + if (!ok) { + reject(new Error('primary password prompt cancelled')); + return; + } security .decrypt(args.password, value) .then(resolve) @@ -536,15 +545,16 @@ export const useAuthStore = defineStore('Auth', () => { loginForm.value.saveCredentials && advancedSettingsStore.enablePrimaryPassword ) { - ElMessageBox.prompt( - t('prompt.primary_password.description'), - t('prompt.primary_password.header'), - { - inputType: 'password', - inputPattern: /[\s\S]{1,32}/ - } - ) - .then(async ({ value }) => { + modalStore + .prompt({ + title: t('prompt.primary_password.header'), + description: t( + 'prompt.primary_password.description' + ), + pattern: /[\s\S]{1,32}/ + }) + .then(async ({ ok, value }) => { + if (!ok) return; const savedCredentials = JSON.parse( await configRepository.getString( 'savedCredentials' @@ -613,41 +623,39 @@ export const useAuthStore = defineStore('Auth', () => { } AppApi.FlashWindow(); twoFactorAuthDialogVisible.value = true; - ElMessageBox.prompt( - t('prompt.totp.description'), - t('prompt.totp.header'), - { - distinguishCancelAndClose: true, - cancelButtonText: t('prompt.totp.use_otp'), - confirmButtonText: t('prompt.totp.verify'), - inputPlaceholder: t('prompt.totp.input_placeholder'), - inputPattern: /^[0-9]{6}$/, - inputErrorMessage: t('prompt.totp.input_error'), - beforeClose: (action, instance, done) => { - twoFactorAuthDialogVisible.value = false; - if (action === 'cancel') { - promptOTP(); - } - done(); - } - } - ) - .then(({ value, action }) => { - if (action === 'confirm') { - authRequest - .verifyTOTP({ - code: value.trim() - }) - .catch((err) => { - console.error(err); - clearCookiesTryLogin(); - }) - .then(() => { - userStore.getCurrentUser(); - }); - } + modalStore + .prompt({ + title: t('prompt.totp.header'), + description: t('prompt.totp.description'), + cancelText: t('prompt.totp.use_otp'), + confirmText: t('prompt.totp.verify'), + pattern: /^[0-9]{6}$/, + errorMessage: t('prompt.totp.input_error') }) - .catch(() => {}); + .then(({ ok, reason, value }) => { + twoFactorAuthDialogVisible.value = false; + + if (reason === 'cancel') { + promptOTP(); + return; + } + if (!ok) return; + + authRequest + .verifyTOTP({ + code: value.trim() + }) + .catch((err) => { + console.error(err); + clearCookiesTryLogin(); + }) + .then(() => { + userStore.getCurrentUser(); + }); + }) + .catch(() => { + twoFactorAuthDialogVisible.value = false; + }); } function promptOTP() { @@ -655,41 +663,39 @@ export const useAuthStore = defineStore('Auth', () => { return; } twoFactorAuthDialogVisible.value = true; - ElMessageBox.prompt( - t('prompt.otp.description'), - t('prompt.otp.header'), - { - distinguishCancelAndClose: true, - cancelButtonText: t('prompt.otp.use_totp'), - confirmButtonText: t('prompt.otp.verify'), - inputPlaceholder: t('prompt.otp.input_placeholder'), - inputPattern: /^[a-z0-9]{4}-[a-z0-9]{4}$/, - inputErrorMessage: t('prompt.otp.input_error'), - beforeClose: (action, instance, done) => { - twoFactorAuthDialogVisible.value = false; - if (action === 'cancel') { - promptTOTP(); - } - done(); - } - } - ) - .then(({ value, action }) => { - if (action === 'confirm') { - authRequest - .verifyOTP({ - code: value.trim() - }) - .catch((err) => { - console.error(err); - clearCookiesTryLogin(); - }) - .then(() => { - userStore.getCurrentUser(); - }); - } + modalStore + .prompt({ + title: t('prompt.otp.header'), + description: t('prompt.otp.description'), + cancelText: t('prompt.otp.use_totp'), + confirmText: t('prompt.otp.verify'), + pattern: /^[a-z0-9]{4}-[a-z0-9]{4}$/, + errorMessage: t('prompt.otp.input_error') }) - .catch(() => {}); + .then(({ ok, reason, value }) => { + twoFactorAuthDialogVisible.value = false; + + if (reason === 'cancel') { + promptTOTP(); + return; + } + if (!ok) return; + + authRequest + .verifyOTP({ + code: value.trim() + }) + .catch((err) => { + console.error(err); + clearCookiesTryLogin(); + }) + .then(() => { + userStore.getCurrentUser(); + }); + }) + .catch(() => { + twoFactorAuthDialogVisible.value = false; + }); } function promptEmailOTP() { @@ -698,42 +704,39 @@ export const useAuthStore = defineStore('Auth', () => { } AppApi.FlashWindow(); twoFactorAuthDialogVisible.value = true; - ElMessageBox.prompt( - t('prompt.email_otp.description'), - t('prompt.email_otp.header'), - { - distinguishCancelAndClose: true, - cancelButtonText: t('prompt.email_otp.resend'), - confirmButtonText: t('prompt.email_otp.verify'), - inputPlaceholder: t('prompt.email_otp.input_placeholder'), - inputPattern: /^[0-9]{6}$/, - inputErrorMessage: t('prompt.email_otp.input_error'), - beforeClose: (action, instance, done) => { - twoFactorAuthDialogVisible.value = false; - if (action === 'cancel') { - resendEmail2fa(); - return; - } - done(); - } - } - ) - .then(({ value, action }) => { - if (action === 'confirm') { - authRequest - .verifyEmailOTP({ - code: value.trim() - }) - .catch((err) => { - console.error(err); - promptEmailOTP(); - }) - .then(() => { - userStore.getCurrentUser(); - }); - } + modalStore + .prompt({ + title: t('prompt.email_otp.header'), + description: t('prompt.email_otp.description'), + cancelText: t('prompt.email_otp.resend'), + confirmText: t('prompt.email_otp.verify'), + pattern: /^[0-9]{6}$/, + errorMessage: t('prompt.email_otp.input_error') }) - .catch(() => {}); + .then(({ ok, reason, value }) => { + twoFactorAuthDialogVisible.value = false; + + if (reason === 'cancel') { + resendEmail2fa(); + return; + } + if (!ok) return; + + authRequest + .verifyEmailOTP({ + code: value.trim() + }) + .catch((err) => { + console.error(err); + promptEmailOTP(); + }) + .then(() => { + userStore.getCurrentUser(); + }); + }) + .catch(() => { + twoFactorAuthDialogVisible.value = false; + }); } /** diff --git a/src/stores/photon.js b/src/stores/photon.js index 4248ddd4..5395f4dc 100644 --- a/src/stores/photon.js +++ b/src/stores/photon.js @@ -1,5 +1,4 @@ import { computed, reactive, ref } from 'vue'; -import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; @@ -24,6 +23,7 @@ import { useFriendStore } from './friend'; import { useGameLogStore } from './gameLog'; import { useInstanceStore } from './instance'; import { useLocationStore } from './location'; +import { useModalStore } from './modal'; import { useNotificationStore } from './notification'; import { useSharedFeedStore } from './sharedFeed'; import { useUserStore } from './user'; @@ -39,6 +39,7 @@ export const usePhotonStore = defineStore('Photon', () => { const friendStore = useFriendStore(); const instanceStore = useInstanceStore(); const gameLogStore = useGameLogStore(); + const modalStore = useModalStore(); const notificationStore = useNotificationStore(); const locationStore = useLocationStore(); const sharedFeedStore = useSharedFeedStore(); @@ -440,24 +441,21 @@ export const usePhotonStore = defineStore('Photon', () => { } function promptPhotonOverlayMessageTimeout() { - ElMessageBox.prompt( - t('prompt.overlay_message_timeout.description'), - t('prompt.overlay_message_timeout.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.overlay_message_timeout.ok'), - cancelButtonText: t('prompt.overlay_message_timeout.cancel'), + modalStore + .prompt({ + title: t('prompt.overlay_message_timeout.header'), + description: t('prompt.overlay_message_timeout.description'), + confirmText: t('prompt.overlay_message_timeout.ok'), + cancelText: t('prompt.overlay_message_timeout.cancel'), inputValue: ( state.photonOverlayMessageTimeout / 1000 ).toString(), - inputPattern: /\d+$/, - inputErrorMessage: t( - 'prompt.overlay_message_timeout.input_error' - ) - } - ) - .then(({ value, action }) => { - if (action === 'confirm' && value && !isNaN(Number(value))) { + pattern: /\d+$/, + errorMessage: t('prompt.overlay_message_timeout.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; + if (value && !isNaN(Number(value))) { state.photonOverlayMessageTimeout = Math.trunc( Number(value) * 1000 ); @@ -468,22 +466,21 @@ export const usePhotonStore = defineStore('Photon', () => { } function promptPhotonLobbyTimeoutThreshold() { - ElMessageBox.prompt( - t('prompt.photon_lobby_timeout.description'), - t('prompt.photon_lobby_timeout.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.photon_lobby_timeout.ok'), - cancelButtonText: t('prompt.photon_lobby_timeout.cancel'), + modalStore + .prompt({ + title: t('prompt.photon_lobby_timeout.header'), + description: t('prompt.photon_lobby_timeout.description'), + confirmText: t('prompt.photon_lobby_timeout.ok'), + cancelText: t('prompt.photon_lobby_timeout.cancel'), inputValue: ( state.photonLobbyTimeoutThreshold / 1000 ).toString(), - inputPattern: /\d+$/, - inputErrorMessage: t('prompt.photon_lobby_timeout.input_error') - } - ) - .then(({ value, action }) => { - if (action === 'confirm' && value && !isNaN(Number(value))) { + pattern: /\d+$/, + errorMessage: t('prompt.photon_lobby_timeout.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; + if (value && !isNaN(Number(value))) { state.photonLobbyTimeoutThreshold = Math.trunc( Number(value) * 1000 ); diff --git a/src/stores/search.js b/src/stores/search.js index 9afa19d1..5d4bf76e 100644 --- a/src/stores/search.js +++ b/src/stores/search.js @@ -1,5 +1,4 @@ import { computed, ref, watch } from 'vue'; -import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; @@ -13,6 +12,7 @@ import { useAppearanceSettingsStore } from './settings/appearance'; import { useAvatarStore } from './avatar'; import { useFriendStore } from './friend'; import { useGroupStore } from './group'; +import { useModalStore } from './modal'; import { useUserStore } from './user'; import { useWorldStore } from './world'; import { watchState } from '../service/watchState'; @@ -25,6 +25,7 @@ export const useSearchStore = defineStore('Search', () => { const worldStore = useWorldStore(); const avatarStore = useAvatarStore(); const groupStore = useGroupStore(); + const modalStore = useModalStore(); const { t } = useI18n(); const searchText = ref(''); @@ -350,22 +351,20 @@ export const useSearchStore = defineStore('Search', () => { async function promptOmniDirectDialog() { if (directAccessPrompt.value) return; - directAccessPrompt.value = ElMessageBox.prompt( - t('prompt.direct_access_omni.description'), - t('prompt.direct_access_omni.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.direct_access_omni.ok'), - cancelButtonText: t('prompt.direct_access_omni.cancel'), - inputPattern: /\S+/, - inputErrorMessage: t('prompt.direct_access_omni.input_error') - } - ); + // Element Plus: prompt(message, title, options) + directAccessPrompt.value = modalStore.prompt({ + title: t('prompt.direct_access_omni.header'), + description: t('prompt.direct_access_omni.description'), + confirmText: t('prompt.direct_access_omni.ok'), + cancelText: t('prompt.direct_access_omni.cancel'), + pattern: /\S+/, + errorMessage: t('prompt.direct_access_omni.input_error') + }); try { - const { value, action } = await directAccessPrompt.value; + const { ok, value } = await directAccessPrompt.value; - if (action === 'confirm' && value) { + if (ok && value) { const input = value.trim(); if (!directAccessParse(input)) { toast.error(t('prompt.direct_access_omni.message.error')); diff --git a/src/stores/settings/advanced.js b/src/stores/settings/advanced.js index 1839050b..f06b9bf4 100644 --- a/src/stores/settings/advanced.js +++ b/src/stores/settings/advanced.js @@ -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'; @@ -855,23 +854,22 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { } function promptAutoClearVRCXCacheFrequency() { - ElMessageBox.prompt( - t('prompt.auto_clear_cache.description'), - t('prompt.auto_clear_cache.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.auto_clear_cache.ok'), - cancelButtonText: t('prompt.auto_clear_cache.cancel'), + modalStore + .prompt({ + title: t('prompt.auto_clear_cache.header'), + description: t('prompt.auto_clear_cache.description'), + confirmText: t('prompt.auto_clear_cache.ok'), + cancelText: t('prompt.auto_clear_cache.cancel'), inputValue: ( vrcxStore.clearVRCXCacheFrequency / 3600 / 2 ).toString(), - inputPattern: /\d+$/, - inputErrorMessage: t('prompt.auto_clear_cache.input_error') - } - ) - .then(async ({ value }) => { + pattern: /\d+$/, + errorMessage: t('prompt.auto_clear_cache.input_error') + }) + .then(async ({ ok, value }) => { + if (!ok) return; if (value && !isNaN(parseInt(value, 10))) { vrcxStore.clearVRCXCacheFrequency = Math.trunc( parseInt(value, 10) * 3600 * 2 diff --git a/src/stores/settings/appearance.js b/src/stores/settings/appearance.js index 3d9939eb..31784ce8 100644 --- a/src/stores/settings/appearance.js +++ b/src/stores/settings/appearance.js @@ -1,5 +1,4 @@ import { computed, ref, watch } from 'vue'; -import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; @@ -17,6 +16,7 @@ import { loadLocalizedStrings } from '../../plugin'; import { useElementTheme } from '../../composables/useElementTheme'; import { useFeedStore } from '../feed'; import { useGameLogStore } from '../gameLog'; +import { useModalStore } from '../modal'; import { useUiStore } from '../ui'; import { useUserStore } from '../user'; import { useVrStore } from '../vr'; @@ -36,6 +36,7 @@ export const useAppearanceSettingsStore = defineStore( const userStore = useUserStore(); const router = useRouter(); const uiStore = useUiStore(); + const modalStore = useModalStore(); const { t, locale } = useI18n(); @@ -746,19 +747,18 @@ export const useAppearanceSettingsStore = defineStore( } function promptMaxTableSizeDialog() { - ElMessageBox.prompt( - t('prompt.change_table_size.description'), - t('prompt.change_table_size.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.change_table_size.save'), - cancelButtonText: t('prompt.change_table_size.cancel'), + modalStore + .prompt({ + title: t('prompt.change_table_size.header'), + description: t('prompt.change_table_size.description'), + confirmText: t('prompt.change_table_size.save'), + cancelText: t('prompt.change_table_size.cancel'), inputValue: vrcxStore.maxTableSize.toString(), - inputPattern: /\d+$/, - inputErrorMessage: t('prompt.change_table_size.input_error') - } - ) - .then(async ({ value }) => { + pattern: /\d+$/, + errorMessage: t('prompt.change_table_size.input_error') + }) + .then(async ({ ok, value }) => { + if (!ok) return; if (value) { let processedValue = Number(value); if (processedValue > 10000) { diff --git a/src/stores/settings/general.js b/src/stores/settings/general.js index b28d42ad..ae934b67 100644 --- a/src/stores/settings/general.js +++ b/src/stores/settings/general.js @@ -1,9 +1,9 @@ -import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { useFriendStore } from '../friend'; +import { useModalStore } from '../modal'; import { useVRCXUpdaterStore } from '../vrcxUpdater'; import { useVrcxStore } from '../vrcx'; @@ -15,6 +15,7 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => { const vrcxStore = useVrcxStore(); const VRCXUpdaterStore = useVRCXUpdaterStore(); const friendStore = useFriendStore(); + const modalStore = useModalStore(); const { t } = useI18n(); @@ -231,32 +232,32 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => { } function promptProxySettings() { - ElMessageBox.prompt( - t('prompt.proxy_settings.description'), - t('prompt.proxy_settings.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.proxy_settings.restart'), - cancelButtonText: t('prompt.proxy_settings.close'), - inputValue: vrcxStore.proxyServer, - inputPlaceholder: t('prompt.proxy_settings.placeholder') - } - ) - .then(async ({ value }) => { - vrcxStore.proxyServer = value; - await VRCXStorage.Set( - 'VRCX_ProxyServer', - vrcxStore.proxyServer - ); - await VRCXStorage.Save(); - await new Promise((resolve) => { - workerTimers.setTimeout(resolve, 100); - }); - const { restartVRCX } = VRCXUpdaterStore; - const isUpgrade = false; - restartVRCX(isUpgrade); + // Element Plus: prompt(message, title, options) + modalStore + .prompt({ + title: t('prompt.proxy_settings.header'), + description: t('prompt.proxy_settings.description'), + confirmText: t('prompt.proxy_settings.restart'), + cancelText: t('prompt.proxy_settings.close'), + inputValue: vrcxStore.proxyServer }) - .catch(async () => { + .then(async ({ ok, value }) => { + if (ok) { + vrcxStore.proxyServer = value; + await VRCXStorage.Set( + 'VRCX_ProxyServer', + vrcxStore.proxyServer + ); + await VRCXStorage.Save(); + await new Promise((resolve) => { + workerTimers.setTimeout(resolve, 100); + }); + const { restartVRCX } = VRCXUpdaterStore; + const isUpgrade = false; + restartVRCX(isUpgrade); + return; + } + // User clicked close/cancel, still save the value but don't restart if (vrcxStore.proxyServer !== undefined) { await VRCXStorage.Set( @@ -268,6 +269,9 @@ export const useGeneralSettingsStore = defineStore('GeneralSettings', () => { workerTimers.setTimeout(resolve, 100); }); } + }) + .catch((err) => { + console.error(err); }); } diff --git a/src/stores/settings/notifications.js b/src/stores/settings/notifications.js index 0e3ba05b..5acd0fe0 100644 --- a/src/stores/settings/notifications.js +++ b/src/stores/settings/notifications.js @@ -1,9 +1,9 @@ -import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { sharedFeedFiltersDefaults } from '../../shared/constants'; +import { useModalStore } from '../modal'; import { useVrStore } from '../vr'; import configRepository from '../../service/config'; @@ -12,6 +12,7 @@ export const useNotificationsSettingsStore = defineStore( 'NotificationsSettings', () => { const vrStore = useVrStore(); + const modalStore = useModalStore(); const { t } = useI18n(); @@ -410,21 +411,18 @@ export const useNotificationsSettingsStore = defineStore( } function promptNotificationTimeout() { - ElMessageBox.prompt( - t('prompt.notification_timeout.description'), - t('prompt.notification_timeout.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.notification_timeout.ok'), - cancelButtonText: t('prompt.notification_timeout.cancel'), + modalStore + .prompt({ + title: t('prompt.notification_timeout.header'), + description: t('prompt.notification_timeout.description'), + confirmText: t('prompt.notification_timeout.ok'), + cancelText: t('prompt.notification_timeout.cancel'), inputValue: notificationTimeout.value / 1000, - inputPattern: /\d+$/, - inputErrorMessage: t( - 'prompt.notification_timeout.input_error' - ) - } - ) - .then(async ({ value }) => { + pattern: /\d+$/, + errorMessage: t('prompt.notification_timeout.input_error') + }) + .then(async ({ ok, value }) => { + if (!ok) return; if (value && !isNaN(value)) { notificationTimeout.value = Math.trunc( Number(value) * 1000 diff --git a/src/views/Favorites/FavoritesAvatar.vue b/src/views/Favorites/FavoritesAvatar.vue index 6dd306f0..cd63f54d 100644 --- a/src/views/Favorites/FavoritesAvatar.vue +++ b/src/views/Favorites/FavoritesAvatar.vue @@ -529,7 +529,6 @@ import { ArrowUpDown, Check, Ellipsis, Loader, MoreHorizontal, Plus, RefreshCcw, RefreshCw } from 'lucide-vue-next'; import { InputGroupField, InputGroupSearch } from '@/components/ui/input-group'; import { Button } from '@/components/ui/button'; - import { ElMessageBox } from 'element-plus'; import { Spinner } from '@/components/ui/spinner'; import { storeToRefs } from 'pinia'; import { toast } from 'vue-sonner'; @@ -1344,19 +1343,18 @@ function changeFavoriteGroupName(group) { const currentName = group.displayName || group.name; - ElMessageBox.prompt( - t('prompt.change_favorite_group_name.description'), - t('prompt.change_favorite_group_name.header'), - { - confirmButtonText: t('prompt.change_favorite_group_name.change'), - cancelButtonText: t('prompt.change_favorite_group_name.cancel'), - inputPlaceholder: t('prompt.change_favorite_group_name.input_placeholder'), - inputPattern: /\S+/, + modalStore + .prompt({ + title: t('prompt.change_favorite_group_name.header'), + description: t('prompt.change_favorite_group_name.description'), + confirmText: t('prompt.change_favorite_group_name.change'), + cancelText: t('prompt.change_favorite_group_name.cancel'), + pattern: /\S+/, inputValue: currentName, - inputErrorMessage: t('prompt.change_favorite_group_name.input_error') - } - ) - .then(({ value }) => { + errorMessage: t('prompt.change_favorite_group_name.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; const newName = value.trim(); if (!newName || newName === currentName) { return; @@ -1419,19 +1417,18 @@ } function promptLocalAvatarFavoriteGroupRename(group) { - ElMessageBox.prompt( - t('prompt.local_favorite_group_rename.description'), - t('prompt.local_favorite_group_rename.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.local_favorite_group_rename.save'), - cancelButtonText: t('prompt.local_favorite_group_rename.cancel'), - inputPattern: /\S+/, - inputErrorMessage: t('prompt.local_favorite_group_rename.input_error'), + modalStore + .prompt({ + title: t('prompt.local_favorite_group_rename.header'), + description: t('prompt.local_favorite_group_rename.description'), + confirmText: t('prompt.local_favorite_group_rename.save'), + cancelText: t('prompt.local_favorite_group_rename.cancel'), + pattern: /\S+/, + errorMessage: t('prompt.local_favorite_group_rename.input_error'), inputValue: group - } - ) - .then(({ value }) => { + }) + .then(({ ok, value }) => { + if (!ok) return; if (value) { renameLocalAvatarFavoriteGroup(value, group); nextTick(() => { diff --git a/src/views/Favorites/FavoritesFriend.vue b/src/views/Favorites/FavoritesFriend.vue index 2cfc6dd8..205cc063 100644 --- a/src/views/Favorites/FavoritesFriend.vue +++ b/src/views/Favorites/FavoritesFriend.vue @@ -312,7 +312,6 @@ import { computed, nextTick, onBeforeMount, onMounted, onUnmounted, ref, watch } from 'vue'; import { ArrowUpDown, Check, Ellipsis, MoreHorizontal, RefreshCw } from 'lucide-vue-next'; import { Button } from '@/components/ui/button'; - import { ElMessageBox } from 'element-plus'; import { InputGroupSearch } from '@/components/ui/input-group'; import { Spinner } from '@/components/ui/spinner'; import { storeToRefs } from 'pinia'; @@ -809,19 +808,18 @@ function changeFavoriteGroupName(group) { const currentName = group.displayName || group.name; - ElMessageBox.prompt( - t('prompt.change_favorite_group_name.description'), - t('prompt.change_favorite_group_name.header'), - { - confirmButtonText: t('prompt.change_favorite_group_name.change'), - cancelButtonText: t('prompt.change_favorite_group_name.cancel'), - inputPlaceholder: t('prompt.change_favorite_group_name.input_placeholder'), - inputPattern: /\S+/, + modalStore + .prompt({ + title: t('prompt.change_favorite_group_name.header'), + description: t('prompt.change_favorite_group_name.description'), + confirmText: t('prompt.change_favorite_group_name.change'), + cancelText: t('prompt.change_favorite_group_name.cancel'), + pattern: /\S+/, inputValue: currentName, - inputErrorMessage: t('prompt.change_favorite_group_name.input_error') - } - ) - .then(({ value }) => { + errorMessage: t('prompt.change_favorite_group_name.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; const newName = value.trim(); if (!newName || newName === currentName) { return; diff --git a/src/views/Favorites/FavoritesWorld.vue b/src/views/Favorites/FavoritesWorld.vue index cf76745b..f62f2419 100644 --- a/src/views/Favorites/FavoritesWorld.vue +++ b/src/views/Favorites/FavoritesWorld.vue @@ -440,7 +440,6 @@ import { ArrowUpDown, Ellipsis, MoreHorizontal, Plus, RefreshCcw, RefreshCw } from 'lucide-vue-next'; import { InputGroupField, InputGroupSearch } from '@/components/ui/input-group'; import { Button } from '@/components/ui/button'; - import { ElMessageBox } from 'element-plus'; import { Spinner } from '@/components/ui/spinner'; import { storeToRefs } from 'pinia'; import { toast } from 'vue-sonner'; @@ -1145,19 +1144,18 @@ } function promptLocalWorldFavoriteGroupRename(group) { - ElMessageBox.prompt( - t('prompt.local_favorite_group_rename.description'), - t('prompt.local_favorite_group_rename.header'), - { - distinguishCancelAndClose: true, - confirmButtonText: t('prompt.local_favorite_group_rename.save'), - cancelButtonText: t('prompt.local_favorite_group_rename.cancel'), - inputPattern: /\S+/, - inputErrorMessage: t('prompt.local_favorite_group_rename.input_error'), + modalStore + .prompt({ + title: t('prompt.local_favorite_group_rename.header'), + description: t('prompt.local_favorite_group_rename.description'), + confirmText: t('prompt.local_favorite_group_rename.save'), + cancelText: t('prompt.local_favorite_group_rename.cancel'), + pattern: /\S+/, + errorMessage: t('prompt.local_favorite_group_rename.input_error'), inputValue: group - } - ) - .then(({ value }) => { + }) + .then(({ ok, value }) => { + if (!ok) return; if (value) { renameLocalWorldFavoriteGroup(value, group); nextTick(() => { @@ -1238,19 +1236,18 @@ function changeFavoriteGroupName(group) { const currentName = group.displayName || group.name; - ElMessageBox.prompt( - t('prompt.change_favorite_group_name.description'), - t('prompt.change_favorite_group_name.header'), - { - confirmButtonText: t('prompt.change_favorite_group_name.change'), - cancelButtonText: t('prompt.change_favorite_group_name.cancel'), - inputPlaceholder: t('prompt.change_favorite_group_name.input_placeholder'), - inputPattern: /\S+/, + modalStore + .prompt({ + title: t('prompt.change_favorite_group_name.header'), + description: t('prompt.change_favorite_group_name.description'), + confirmText: t('prompt.change_favorite_group_name.change'), + cancelText: t('prompt.change_favorite_group_name.cancel'), + pattern: /\S+/, inputValue: currentName, - inputErrorMessage: t('prompt.change_favorite_group_name.input_error') - } - ) - .then(({ value }) => { + errorMessage: t('prompt.change_favorite_group_name.input_error') + }) + .then(({ ok, value }) => { + if (!ok) return; const newName = value.trim(); if (!newName || newName === currentName) { return; diff --git a/src/views/PlayerList/components/photonEventColumns.jsx b/src/views/PlayerList/components/photonEventColumns.jsx index 55068ebb..302c1d6d 100644 --- a/src/views/PlayerList/components/photonEventColumns.jsx +++ b/src/views/PlayerList/components/photonEventColumns.jsx @@ -88,7 +88,7 @@ function DetailCell({ row, isPrevious, onShowAvatar, onShowGroup, onShowWorld, o > - + - + ) : null} - { @@ -192,7 +191,7 @@ function DetailCell({ row, isPrevious, onShowAvatar, onShowGroup, onShowWorld, o   {!r.inCache ? ( - +   ) : null} diff --git a/src/views/Settings/dialogs/RegistryBackupDialog.vue b/src/views/Settings/dialogs/RegistryBackupDialog.vue index 58416fd0..359e9c2f 100644 --- a/src/views/Settings/dialogs/RegistryBackupDialog.vue +++ b/src/views/Settings/dialogs/RegistryBackupDialog.vue @@ -50,7 +50,6 @@ import { computed, ref, watch } from 'vue'; import { Button } from '@/components/ui/button'; import { DataTableLayout } from '@/components/ui/data-table'; - import { ElMessageBox } from 'element-plus'; import { storeToRefs } from 'pinia'; import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; @@ -178,12 +177,16 @@ } function promptVrcRegistryBackupName() { - ElMessageBox.prompt('Enter a name for the backup', 'Backup Name', { - inputPattern: /\S+/, - inputErrorMessage: 'Name is required', - inputValue: 'Backup' - }) - .then(({ value }) => { + modalStore + .prompt({ + title: 'Backup Name', + description: 'Enter a name for the backup', + inputValue: 'Backup', + pattern: /\S+/, + errorMessage: 'Name is required' + }) + .then(({ ok, value }) => { + if (!ok) return; handleBackupVrcRegistry(value); }) .catch(() => {}); diff --git a/src/views/Tools/Gallery.vue b/src/views/Tools/Gallery.vue index d9918410..da03eaa8 100644 --- a/src/views/Tools/Gallery.vue +++ b/src/views/Tools/Gallery.vue @@ -580,7 +580,6 @@ import { Button } from '@/components/ui/button'; import { ButtonGroup } from '@/components/ui/button-group'; import { Checkbox } from '@/components/ui/checkbox'; - import { ElMessageBox } from 'element-plus'; import { InputGroupTextareaField } from '@/components/ui/input-group'; import { TabsUnderline } from '@/components/ui/tabs'; import { VirtualCombobox } from '@/components/ui/virtual-combobox'; @@ -597,7 +596,7 @@ openExternalLink } from '../../shared/utils'; import { inventoryRequest, miscRequest, userRequest, vrcPlusIconRequest, vrcPlusImageRequest } from '../../api'; - import { useAdvancedSettingsStore, useAuthStore, useGalleryStore, useUserStore } from '../../stores'; + import { useAdvancedSettingsStore, useAuthStore, useGalleryStore, useModalStore, useUserStore } from '../../stores'; import { emojiAnimationStyleList, emojiAnimationStyleUrl } from '../../shared/constants'; import { AppDebug } from '../../service/appConfig'; import { handleImageUploadInput } from '../../shared/utils/imageUpload'; @@ -606,6 +605,7 @@ const { t } = useI18n(); const router = useRouter(); + const modalStore = useModalStore(); const { galleryTable, @@ -1161,11 +1161,15 @@ } async function redeemReward() { - ElMessageBox.prompt(t('prompt.redeem.description'), t('prompt.redeem.header'), { - confirmButtonText: t('prompt.redeem.redeem'), - cancelButtonText: t('prompt.redeem.cancel') - }) - .then(({ value }) => { + modalStore + .prompt({ + title: t('prompt.redeem.header'), + description: t('prompt.redeem.description'), + confirmText: t('prompt.redeem.redeem'), + cancelText: t('prompt.redeem.cancel') + }) + .then(({ ok, value }) => { + if (!ok) return; if (value) { inventoryRequest .redeemReward({