From 7e4de15ef2e62e12ae375e4d798c27bd2cfeae17 Mon Sep 17 00:00:00 2001 From: pa Date: Wed, 7 Jan 2026 19:16:31 +0900 Subject: [PATCH] replace ElMessage with Sonner --- package-lock.json | 25 +++- package.json | 9 +- src/App.vue | 2 + src/api/instance.js | 12 +- src/app.css | 1 + src/components/FullscreenImagePreview.vue | 21 +-- src/components/InstanceInfo.vue | 5 +- src/components/InviteYourself.vue | 4 +- .../dialogs/AvatarDialog/AvatarDialog.vue | 83 +++--------- .../AvatarDialog/ChangeAvatarImageDialog.vue | 12 +- .../AvatarDialog/SetAvatarStylesDialog.vue | 6 +- .../AvatarDialog/SetAvatarTagsDialog.vue | 7 +- .../GroupDialog/GallerySelectDialog.vue | 17 +-- .../dialogs/GroupDialog/GroupDialog.vue | 13 +- .../GroupMemberModerationDialog.vue | 125 ++++-------------- .../GroupDialog/GroupPostEditDialog.vue | 22 +-- .../InviteDialog/EditAndSendInviteDialog.vue | 34 ++--- .../dialogs/InviteDialog/InviteDialog.vue | 8 +- .../InviteDialog/SendInviteConfirmDialog.vue | 27 +--- src/components/dialogs/InviteGroupDialog.vue | 8 +- src/components/dialogs/LaunchDialog.vue | 18 +-- src/components/dialogs/NewInstanceDialog.vue | 7 +- .../dialogs/UserDialog/BioDialog.vue | 7 +- .../dialogs/UserDialog/PronounsDialog.vue | 7 +- .../dialogs/UserDialog/SocialStatusDialog.vue | 7 +- .../dialogs/UserDialog/UserDialog.vue | 49 ++----- .../WorldDialog/ChangeWorldImageDialog.vue | 12 +- .../WorldDialog/SetWorldTagsDialog.vue | 7 +- .../WorldDialog/WorldAllowedDomainsDialog.vue | 7 +- .../dialogs/WorldDialog/WorldDialog.vue | 93 +++---------- .../ui/data-table/DataTableLayout.vue | 6 +- src/components/ui/sonner/Sonner.vue | 62 +++++++++ src/components/ui/sonner/index.js | 1 + src/service/request.js | 8 +- src/shared/utils/base/ui.js | 8 +- src/shared/utils/common.js | 13 +- src/shared/utils/imageUpload.js | 13 +- src/stores/auth.js | 8 +- src/stores/avatar.js | 41 ++---- src/stores/charts.js | 11 +- src/stores/favorite.js | 38 +++--- src/stores/friend.js | 8 +- src/stores/game.js | 8 +- src/stores/gameLog.js | 11 +- src/stores/group.js | 18 +-- src/stores/instance.js | 55 ++++---- src/stores/invite.js | 12 +- src/stores/launch.js | 46 ++----- src/stores/photon.js | 8 +- src/stores/search.js | 8 +- src/stores/settings/advanced.js | 77 ++++------- src/stores/ui.js | 7 +- src/stores/user.js | 12 +- src/stores/vrcx.js | 52 +++----- src/stores/vrcxUpdater.js | 34 ++--- src/stores/world.js | 7 +- src/views/Charts/components/MutualFriends.vue | 16 +-- src/views/Favorites/FavoritesAvatar.vue | 23 +--- src/views/Favorites/FavoritesFriend.vue | 13 +- src/views/Favorites/FavoritesWorld.vue | 13 +- .../components/FavoritesMoveDropdown.vue | 9 +- .../Favorites/dialogs/AvatarExportDialog.vue | 13 +- .../Favorites/dialogs/AvatarImportDialog.vue | 7 +- .../Favorites/dialogs/FriendExportDialog.vue | 13 +- .../Favorites/dialogs/FriendImportDialog.vue | 7 +- .../Favorites/dialogs/WorldExportDialog.vue | 13 +- .../Favorites/dialogs/WorldImportDialog.vue | 7 +- src/views/FriendList/FriendList.vue | 7 +- src/views/Notifications/Notification.vue | 5 +- .../EditAndSendInviteResponseDialog.vue | 19 +-- .../SendInviteResponseConfirmDialog.vue | 12 +- src/views/PlayerList/PlayerList.vue | 28 +++- .../Settings/components/Tabs/AdvancedTab.vue | 32 +---- .../components/Tabs/AppearanceTab.vue | 7 +- .../Settings/dialogs/LaunchOptionsDialog.vue | 12 +- .../Settings/dialogs/RegistryBackupDialog.vue | 33 ++--- .../Settings/dialogs/TranslationApiDialog.vue | 32 +---- .../Settings/dialogs/VRChatConfigDialog.vue | 18 +-- .../Settings/dialogs/YouTubeApiDialog.vue | 17 +-- src/views/Tools/Gallery.vue | 53 ++------ src/views/Tools/Tools.vue | 22 +-- .../components/GroupCalendarEventCard.vue | 7 +- .../Tools/dialogs/EditInviteMessageDialog.vue | 9 +- .../dialogs/EditInviteMessagesDialog.vue | 7 +- .../dialogs/ScreenshotMetadataDialog.vue | 37 ++---- 85 files changed, 574 insertions(+), 1144 deletions(-) create mode 100644 src/components/ui/sonner/Sonner.vue create mode 100644 src/components/ui/sonner/index.js diff --git a/package-lock.json b/package-lock.json index 19121454..8fe8f8fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "license": "MIT", "dependencies": { "hazardous": "^0.3.0", - "node-api-dotnet": "^0.9.18" + "node-api-dotnet": "^0.9.18", + "vue-sonner": "^2.0.9" }, "devDependencies": { "@electron/rebuild": "^4.0.2", @@ -19082,6 +19083,28 @@ "node": ">=16.19.0" } }, + "node_modules/vue-sonner": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/vue-sonner/-/vue-sonner-2.0.9.tgz", + "integrity": "sha512-i6BokNlNDL93fpzNxN/LZSn6D6MzlO+i3qXt6iVZne3x1k7R46d5HlFB4P8tYydhgqOrRbIZEsnRd3kG7qGXyw==", + "license": "MIT", + "peerDependencies": { + "@nuxt/kit": "^4.0.3", + "@nuxt/schema": "^4.0.3", + "nuxt": "^4.0.3" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "@nuxt/schema": { + "optional": true + }, + "nuxt": { + "optional": true + } + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 2839d3c4..6a419ba8 100644 --- a/package.json +++ b/package.json @@ -50,10 +50,10 @@ "@vitejs/plugin-vue": "^6.0.3", "@vitejs/plugin-vue-jsx": "^5.1.3", "@vueuse/core": "^14.1.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", "animate.css": "^4.1.1", "babel-runtime": "^6.26.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "concurrently": "^9.2.1", "cross-env": "^10.1.0", "dayjs": "^1.11.19", @@ -75,8 +75,8 @@ "prettier": "^3.7.4", "reka-ui": "^2.7.0", "remixicon": "^4.8.0", - "tailwindcss": "^4.1.18", "tailwind-merge": "^3.4.0", + "tailwindcss": "^4.1.18", "tw-animate-css": "^1.4.0", "vite": "^7.3.0", "vue": "^3.5.26", @@ -172,6 +172,7 @@ }, "dependencies": { "hazardous": "^0.3.0", - "node-api-dotnet": "^0.9.18" + "node-api-dotnet": "^0.9.18", + "vue-sonner": "^2.0.9" } } diff --git a/src/App.vue b/src/App.vue index d2d825e4..b1cae1cd 100644 --- a/src/App.vue +++ b/src/App.vue @@ -12,6 +12,7 @@ ondragover="event.preventDefault()" ondrop="event.preventDefault()"> + @@ -23,6 +24,7 @@ import { computed, onBeforeMount, onMounted } from 'vue'; import { useI18n } from 'vue-i18n'; + import { Toaster } from './components/ui/sonner'; import { TooltipProvider } from './components/ui/tooltip'; import { createGlobalStores } from './stores'; import { initNoty } from './plugin/noty'; diff --git a/src/api/instance.js b/src/api/instance.js index ce5a9f15..4e996e1d 100644 --- a/src/api/instance.js +++ b/src/api/instance.js @@ -1,4 +1,4 @@ -import { ElMessage } from 'element-plus'; +import { toast } from 'vue-sonner'; import { i18n } from '../plugin/i18n'; import { request } from '../service/request'; @@ -140,16 +140,10 @@ const instanceReq = { }) .catch((err) => { if (err?.error?.message) { - ElMessage({ - message: err.error.message, - type: 'error' - }); + toast.error(err.error.message); throw err; } - ElMessage({ - message: i18n.global.t('message.instance.not_allowed'), - type: 'error' - }); + toast.error(i18n.global.t('message.instance.not_allowed')); throw err; }); } diff --git a/src/app.css b/src/app.css index ac922757..90c0f21f 100644 --- a/src/app.css +++ b/src/app.css @@ -12,6 +12,7 @@ @import 'animate.css/animate.min.css'; @import 'noty/lib/noty.css'; @import 'remixicon/fonts/remixicon.css'; +@import 'vue-sonner/style.css'; @import './styles/flags.css'; @import './styles/animated-emoji.css'; diff --git a/src/components/FullscreenImagePreview.vue b/src/components/FullscreenImagePreview.vue index 134ede8b..43ab730e 100644 --- a/src/components/FullscreenImagePreview.vue +++ b/src/components/FullscreenImagePreview.vue @@ -38,8 +38,8 @@ diff --git a/src/components/InstanceInfo.vue b/src/components/InstanceInfo.vue index 0844976a..8955e4b1 100644 --- a/src/components/InstanceInfo.vue +++ b/src/components/InstanceInfo.vue @@ -63,9 +63,10 @@ + + diff --git a/src/components/ui/sonner/index.js b/src/components/ui/sonner/index.js new file mode 100644 index 00000000..93765a16 --- /dev/null +++ b/src/components/ui/sonner/index.js @@ -0,0 +1 @@ +export { default as Toaster } from './Sonner.vue'; diff --git a/src/service/request.js b/src/service/request.js index 6389e1fb..dc0c9d4b 100644 --- a/src/service/request.js +++ b/src/service/request.js @@ -1,4 +1,5 @@ -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; +import { toast } from 'vue-sonner'; import Noty from 'noty'; @@ -198,10 +199,7 @@ export function request(endpoint, options) { status === 404 && endpoint?.startsWith('avatars/') ) { - ElMessage({ - message: t('message.api_handler.avatar_private_or_deleted'), - type: 'error' - }); + toast.error(t('message.api_handler.avatar_private_or_deleted')); avatarStore.avatarDialog.visible = false; $throw(404, data.error?.message || '', endpoint); } diff --git a/src/shared/utils/base/ui.js b/src/shared/utils/base/ui.js index c5b2ab4f..cd1cb97e 100644 --- a/src/shared/utils/base/ui.js +++ b/src/shared/utils/base/ui.js @@ -1,5 +1,5 @@ -import { ElMessage } from 'element-plus'; import { storeToRefs } from 'pinia'; +import { toast } from 'vue-sonner'; import { THEME_CONFIG } from '../../constants'; import { i18n } from '../../../plugin/i18n'; @@ -327,11 +327,7 @@ async function getThemeMode(configRepository) { function redirectToToolsTab() { router.push({ name: 'tools' }); - ElMessage({ - message: i18n.global.t('view.tools.redirect_message'), - type: 'primary', - duration: 3000 - }); + toast(i18n.global.t('view.tools.redirect_message'), { duration: 3000 }); } export { diff --git a/src/shared/utils/common.js b/src/shared/utils/common.js index 862c590b..23d9139d 100644 --- a/src/shared/utils/common.js +++ b/src/shared/utils/common.js @@ -1,5 +1,6 @@ -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; import { storeToRefs } from 'pinia'; +import { toast } from 'vue-sonner'; import Noty from 'noty'; @@ -159,17 +160,11 @@ function copyToClipboard(text, message = 'Copied successfully!') { navigator.clipboard .writeText(text) .then(() => { - ElMessage({ - message: message, - type: 'success' - }); + toast.success(message); }) .catch((err) => { console.error('Copy failed:', err); - ElMessage({ - message: 'Copy failed!', - type: 'error' - }); + toast.error('Copy failed!'); }); } diff --git a/src/shared/utils/imageUpload.js b/src/shared/utils/imageUpload.js index 594ac81a..3caeb904 100644 --- a/src/shared/utils/imageUpload.js +++ b/src/shared/utils/imageUpload.js @@ -1,5 +1,4 @@ -import { ElMessage } from 'element-plus'; - +import { toast } from 'vue-sonner'; function resolveMessage(message) { if (typeof message === 'function') { return message(); @@ -47,10 +46,7 @@ export function handleImageUploadInput(event, options = {}) { const file = files[0]; if (file.size >= maxSize) { if (tooLargeMessage) { - ElMessage({ - message: resolveMessage(tooLargeMessage), - type: 'error' - }); + toast.error(resolveMessage(tooLargeMessage)); } clearInput(); return { file: null, clearInput }; @@ -66,10 +62,7 @@ export function handleImageUploadInput(event, options = {}) { if (acceptRegex && !acceptRegex.test(file.type)) { if (invalidTypeMessage) { - ElMessage({ - message: resolveMessage(invalidTypeMessage), - type: 'error' - }); + toast.error(resolveMessage(invalidTypeMessage)); } clearInput(); return { file: null, clearInput }; diff --git a/src/stores/auth.js b/src/stores/auth.js index 5a2f83a3..6a55e5fa 100644 --- a/src/stores/auth.js +++ b/src/stores/auth.js @@ -1,6 +1,7 @@ import { reactive, ref, watch } from 'vue'; -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; import Noty from 'noty'; @@ -477,10 +478,7 @@ export const useAuthStore = defineStore('Auth', () => { }); }) .catch((_) => { - ElMessage({ - message: 'Incorrect primary password', - type: 'error' - }); + toast.error('Incorrect primary password'); reject(_); }); } else { diff --git a/src/stores/avatar.js b/src/stores/avatar.js index 32767887..69012cb6 100644 --- a/src/stores/avatar.js +++ b/src/stores/avatar.js @@ -1,6 +1,7 @@ import { nextTick, ref, watch } from 'vue'; -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { checkVRChatCache, @@ -468,10 +469,7 @@ export const useAvatarStore = defineStore('Avatar', () => { } catch (err) { const msg = `Avatar search failed for ${search} with ${avatarProviderStore.avatarRemoteDatabaseProvider}\n${err}`; console.error(msg); - ElMessage({ - message: msg, - type: 'error' - }); + toast.error(msg); } } else if (type === 'authorId') { const length = @@ -546,10 +544,7 @@ export const useAvatarStore = defineStore('Avatar', () => { } catch (err) { const msg = `Avatar lookup failed for ${authorId} with ${url}\n${err}`; console.error(msg); - ElMessage({ - message: msg, - type: 'error' - }); + toast.error(msg); } return avatars; } @@ -568,10 +563,7 @@ export const useAvatarStore = defineStore('Avatar', () => { async function selectAvatarWithoutConfirmation(id) { if (userStore.currentUser.currentAvatar === id) { - ElMessage({ - message: 'Avatar already selected', - type: 'info' - }); + toast.info('Avatar already selected'); return; } return avatarRequest @@ -579,10 +571,7 @@ export const useAvatarStore = defineStore('Avatar', () => { avatarId: id }) .then(() => { - ElMessage({ - message: 'Avatar changed', - type: 'success' - }); + toast.success('Avatar changed'); }); } @@ -614,10 +603,7 @@ export const useAvatarStore = defineStore('Avatar', () => { ) { const fileId = extractFileId(currentAvatarImageUrl); if (!fileId) { - ElMessage({ - message: 'Sorry, the author is unknown', - type: 'error' - }); + toast.error('Sorry, the author is unknown'); } else if (refUserId === userStore.currentUser.id) { showAvatarDialog(userStore.currentUser.currentAvatar); } else { @@ -637,16 +623,11 @@ export const useAvatarStore = defineStore('Avatar', () => { } if (!avatarId) { if (avatarInfo.ownerId === refUserId) { - ElMessage({ - message: - "It's personal (own) avatar or not found in avatar database", - type: 'warning' - }); + toast.warning( + "It's personal (own) avatar or not found in avatar database" + ); } else { - ElMessage({ - message: 'Avatar not found in avatar database', - type: 'warning' - }); + toast.warning('Avatar not found in avatar database'); userStore.showUserDialog(avatarInfo.ownerId); } } diff --git a/src/stores/charts.js b/src/stores/charts.js index 741b0776..933a93d2 100644 --- a/src/stores/charts.js +++ b/src/stores/charts.js @@ -1,6 +1,7 @@ import { computed, reactive, ref, watch } from 'vue'; -import { ElMessage, ElNotification } from 'element-plus'; +import { ElNotification } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; import { useFriendStore } from './friend'; @@ -38,12 +39,8 @@ export const useChartsStore = defineStore('Charts', () => { const friendCount = computed(() => friendStore.friends.size || 0); function showInfoMessage(message, type) { - ElMessage({ - message, - type, - duration: 4000, - grouping: true - }); + const toastFn = toast[type] ?? toast; + toastFn(message, { duration: 4000 }); } watch( diff --git a/src/stores/favorite.js b/src/stores/favorite.js index 590299db..b3173647 100644 --- a/src/stores/favorite.js +++ b/src/stores/favorite.js @@ -1,6 +1,6 @@ import { computed, reactive, ref, shallowReactive, watch } from 'vue'; -import { ElMessage } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; import { @@ -1071,12 +1071,11 @@ export const useFavoriteStore = defineStore('Favorite', () => { */ function renameLocalAvatarFavoriteGroup(newName, group) { if (localAvatarFavoriteGroups.value.includes(newName)) { - ElMessage({ - message: t('prompt.local_favorite_group_rename.message.error', { + toast.error( + t('prompt.local_favorite_group_rename.message.error', { name: newName - }), - type: 'error' - }); + }) + ); return; } localAvatarFavorites[newName] = localAvatarFavorites[group]; @@ -1092,12 +1091,11 @@ export const useFavoriteStore = defineStore('Favorite', () => { */ function newLocalAvatarFavoriteGroup(group) { if (localAvatarFavoriteGroups.value.includes(group)) { - ElMessage({ - message: t('prompt.new_local_favorite_group.message.error', { + toast.error( + t('prompt.new_local_favorite_group.message.error', { name: group - }), - type: 'error' - }); + }) + ); return; } if (!localAvatarFavorites[group]) { @@ -1355,12 +1353,11 @@ export const useFavoriteStore = defineStore('Favorite', () => { */ function renameLocalWorldFavoriteGroup(newName, group) { if (localWorldFavoriteGroups.value.includes(newName)) { - ElMessage({ - message: t('prompt.local_favorite_group_rename.message.error', { + toast.error( + t('prompt.local_favorite_group_rename.message.error', { name: newName - }), - type: 'error' - }); + }) + ); return; } localWorldFavorites[newName] = localWorldFavorites[group]; @@ -1474,12 +1471,11 @@ export const useFavoriteStore = defineStore('Favorite', () => { */ function newLocalWorldFavoriteGroup(group) { if (localWorldFavoriteGroups.value.includes(group)) { - ElMessage({ - message: t('prompt.new_local_favorite_group.message.error', { + toast.error( + t('prompt.new_local_favorite_group.message.error', { name: group - }), - type: 'error' - }); + }) + ); return; } if (!localWorldFavorites[group]) { diff --git a/src/stores/friend.js b/src/stores/friend.js index 33d63d89..b8045f13 100644 --- a/src/stores/friend.js +++ b/src/stores/friend.js @@ -1,6 +1,7 @@ import { computed, reactive, ref, watch } from 'vue'; -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; import { @@ -1606,10 +1607,7 @@ export const useFriendStore = defineStore('Friend', () => { } } catch (err) { if (!AppDebug.dontLogMeOut) { - ElMessage({ - message: t('message.friend.load_failed'), - type: 'error' - }); + toast.error(t('message.friend.load_failed')); authStore.handleLogoutEvent(); throw err; } diff --git a/src/stores/game.js b/src/stores/game.js index 6d69a6dd..cb8dd1f7 100644 --- a/src/stores/game.js +++ b/src/stores/game.js @@ -1,6 +1,7 @@ -import { ElMessage, ElMessageBox } from 'element-plus'; import { reactive, ref } from 'vue'; +import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { deleteVRChatCache as _deleteVRChatCache, @@ -123,10 +124,7 @@ export const useGameStore = defineStore('Game', () => { } AppApi.FocusWindow(); const message = 'VRChat crashed, attempting to rejoin last instance'; - ElMessage({ - message, - type: 'info' - }); + toast(message); const entry = { created_at: new Date().toJSON(), type: 'Event', diff --git a/src/stores/gameLog.js b/src/stores/gameLog.js index 69ce9b7a..5a78ef6a 100644 --- a/src/stores/gameLog.js +++ b/src/stores/gameLog.js @@ -1,6 +1,7 @@ import { reactive, ref, shallowReactive, watch } from 'vue'; -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import dayjs from 'dayjs'; @@ -1400,11 +1401,9 @@ export const useGameLogStore = defineStore('GameLog', () => { async function disableGameLogDialog() { if (gameStore.isGameRunning) { - ElMessage({ - message: - 'VRChat needs to be closed before this option can be changed', - type: 'error' - }); + toast.error( + 'VRChat needs to be closed before this option can be changed' + ); return; } if (!advancedSettingsStore.gameLogDisabled) { diff --git a/src/stores/group.js b/src/stores/group.js index c51de98f..48c67994 100644 --- a/src/stores/group.js +++ b/src/stores/group.js @@ -1,6 +1,7 @@ import { nextTick, reactive, ref, watch } from 'vue'; -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { groupRequest, @@ -150,10 +151,7 @@ export const useGroupStore = defineStore('Group', () => { .catch((err) => { D.loading = false; D.visible = false; - ElMessage({ - message: 'Failed to load group', - type: 'error' - }); + toast.error('Failed to load group'); throw err; }) .then((args) => { @@ -596,10 +594,7 @@ export const useGroupStore = defineStore('Group', () => { }) .then((args) => { handleGroupMemberProps(args); - ElMessage({ - message: 'Group visibility updated', - type: 'success' - }); + toast.success('Group visibility updated'); return args; }); } @@ -611,10 +606,7 @@ export const useGroupStore = defineStore('Group', () => { }) .then((args) => { handleGroupMemberProps(args); - ElMessage({ - message: 'Group subscription updated', - type: 'success' - }); + toast.success('Group subscription updated'); return args; }); } diff --git a/src/stores/instance.js b/src/stores/instance.js index fbe759d8..1cf9f641 100644 --- a/src/stores/instance.js +++ b/src/stores/instance.js @@ -1,6 +1,6 @@ -import { computed, reactive, ref, watch } from 'vue'; -import { ElMessage } from 'element-plus'; +import { reactive, ref, watch } from 'vue'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; import { @@ -16,8 +16,7 @@ import { hasGroupPermission, isRealInstance, parseLocation, - replaceBioSymbols, - replaceReactiveObject + replaceBioSymbols } from '../shared/utils'; import { instanceRequest, userRequest, worldRequest } from '../api'; import { database } from '../service/database'; @@ -72,6 +71,7 @@ export const useInstanceStore = defineStore('Instance', () => { lastUpdated: '' }); + /** @type {import('vue').Ref} */ const currentInstanceLocation = ref({}); const queuedInstances = reactive(new Map()); @@ -877,11 +877,8 @@ export const useInstanceStore = defineStore('Instance', () => { function removeAllQueuedInstances() { queuedInstances.forEach((ref) => { - ElMessage({ - message: `Removed instance ${ref.$worldName} from queue`, - type: 'info' - }); - ref.$msgBox?.close(); + toast.info(`Removed instance ${ref.$worldName} from queue`); + toast.dismiss(ref.$msgBox); }); queuedInstances.clear(); } @@ -893,7 +890,7 @@ export const useInstanceStore = defineStore('Instance', () => { function removeQueuedInstance(instanceId) { const ref = queuedInstances.get(instanceId); if (typeof ref !== 'undefined') { - ref.$msgBox.close(); + toast.dismiss(ref.$msgBox); queuedInstances.delete(instanceId); } } @@ -905,13 +902,12 @@ export const useInstanceStore = defineStore('Instance', () => { function applyQueuedInstance(instanceId) { queuedInstances.forEach((ref) => { if (ref.location !== instanceId) { - ElMessage({ - message: t('message.instance.removed_form_queue', { + toast.info( + t('message.instance.removed_form_queue', { worldName: ref.$worldName - }), - type: 'info' - }); - ref.$msgBox?.close(); + }) + ); + toast.dismiss(ref.$msgBox); queuedInstances.delete(ref.location); } }); @@ -953,7 +949,7 @@ export const useInstanceStore = defineStore('Instance', () => { function instanceQueueReady(instanceId) { const ref = queuedInstances.get(instanceId); if (typeof ref !== 'undefined') { - ref.$msgBox.close(); + toast.dismiss(ref.$msgBox); queuedInstances.delete(instanceId); } const L = parseLocation(instanceId); @@ -961,10 +957,7 @@ export const useInstanceStore = defineStore('Instance', () => { const groupName = group?.name ?? ''; const worldName = ref?.$worldName ?? ''; const location = displayLocation(instanceId, worldName, groupName); - ElMessage({ - message: `Instance ready to join ${location}`, - type: 'success' - }); + toast.success(`Instance ready to join ${location}`); const noty = { created_at: new Date().toJSON(), type: 'group.queueReady', @@ -1021,14 +1014,16 @@ export const useInstanceStore = defineStore('Instance', () => { ref.$worldName, ref.$groupName ); - ref.$msgBox?.close(); - ref.$msgBox = ElMessage({ - message: `You are in position ${ref.position} of ${ref.queueSize} in the queue for ${location} `, - type: 'info', - duration: 0, - showClose: true, - customClass: 'vrc-instance-queue-message' - }); + toast.dismiss(ref.$msgBox ?? undefined); + ref.$msgBox = toast.info( + `You are in position ${ref.position} of ${ref.queueSize} in the queue for ${location} `, + { + duration: Infinity, + position: 'bottom-right', + closeButton: true, + class: 'vrc-instance-queue-message' + } + ); queuedInstances.set(instanceId, ref); // workerTimers.setTimeout(this.instanceQueueTimeout, 3600000); } @@ -1196,7 +1191,7 @@ export const useInstanceStore = defineStore('Instance', () => { // $app.methods.instanceQueueClear = function () { // // remove all instances from queue // queuedInstances.forEach((ref) => { - // ref.$msgBox.close(); + // toast.dismiss(ref.$msgBox); // queuedInstances.delete(ref.location); // }); // }; diff --git a/src/stores/invite.js b/src/stores/invite.js index 6220f556..239a51cd 100644 --- a/src/stores/invite.js +++ b/src/stores/invite.js @@ -1,6 +1,6 @@ import { computed, ref, watch } from 'vue'; -import { ElMessage } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { instanceRequest, inviteMessagesRequest } from '../api'; import { parseLocation } from '../shared/utils'; @@ -111,10 +111,7 @@ export const useInviteStore = defineStore('Invite', () => { instanceStore.createNewInstance(worldId).then((args) => { const location = args?.json?.location; if (!location) { - ElMessage({ - message: 'Failed to create instance', - type: 'error' - }); + toast.error('Failed to create instance'); return; } // self invite @@ -134,10 +131,7 @@ export const useInviteStore = defineStore('Invite', () => { worldId: L.worldId }) .then((args) => { - ElMessage({ - message: 'Self invite sent', - type: 'success' - }); + toast.success('Self invite sent'); return args; }); }); diff --git a/src/stores/launch.js b/src/stores/launch.js index d7d29c02..f4de4063 100644 --- a/src/stores/launch.js +++ b/src/stores/launch.js @@ -1,6 +1,6 @@ import { nextTick, ref, watch } from 'vue'; -import { ElMessage } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { instanceRequest } from '../api'; import { parseLocation } from '../shared/utils'; @@ -103,11 +103,9 @@ export const useLaunchStore = defineStore('Launch', () => { } console.log('Attach Game', launchUrl, result); if (!result) { - ElMessage({ - message: - 'Failed open instance in VRChat, falling back to self invite', - type: 'warning' - }); + toast.warning( + 'Failed open instance in VRChat, falling back to self invite' + ); // self invite fallback try { const L = parseLocation(location); @@ -116,10 +114,7 @@ export const useLaunchStore = defineStore('Launch', () => { worldId: L.worldId, shortName }); - ElMessage({ - message: 'Self invite sent', - type: 'success' - }); + toast.success('Self invite sent'); } catch (e) { console.error(e); } @@ -157,38 +152,25 @@ export const useLaunchStore = defineStore('Launch', () => { args.join(' ') ); if (!result) { - ElMessage({ - message: - 'Failed to launch VRChat, invalid custom path set', - type: 'error' - }); + toast.error( + 'Failed to launch VRChat, invalid custom path set' + ); } else { - ElMessage({ - message: 'VRChat launched', - type: 'success' - }); + toast.success('VRChat launched'); } } else { const result = await AppApi.StartGame(args.join(' ')); if (!result) { - ElMessage({ - message: - 'Failed to find VRChat, set a custom path in launch options', - type: 'error' - }); + toast.error( + 'Failed to find VRChat, set a custom path in launch options' + ); } else { - ElMessage({ - message: 'VRChat launched', - type: 'success' - }); + toast.success('VRChat launched'); } } } catch (e) { console.error(e); - ElMessage({ - message: `Failed to launch VRChat: ${e.message}`, - type: 'error' - }); + toast.error(`Failed to launch VRChat: ${e.message}`); } console.log('Launch Game', args.join(' '), desktopMode); } diff --git a/src/stores/photon.js b/src/stores/photon.js index e38c6620..4248ddd4 100644 --- a/src/stores/photon.js +++ b/src/stores/photon.js @@ -1,6 +1,7 @@ import { computed, reactive, ref } from 'vue'; -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; import { @@ -433,10 +434,7 @@ export const usePhotonStore = defineStore('Photon', () => { userStore.lookupUser(ref); } } else { - ElMessage({ - message: 'No user info available', - type: 'error' - }); + toast.error('No user info available'); } } } diff --git a/src/stores/search.js b/src/stores/search.js index 4ef1259a..9afa19d1 100644 --- a/src/stores/search.js +++ b/src/stores/search.js @@ -1,6 +1,7 @@ import { computed, ref, watch } from 'vue'; -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; @@ -367,10 +368,7 @@ export const useSearchStore = defineStore('Search', () => { if (action === 'confirm' && value) { const input = value.trim(); if (!directAccessParse(input)) { - ElMessage({ - message: t('prompt.direct_access_omni.message.error'), - type: 'error' - }); + toast.error(t('prompt.direct_access_omni.message.error')); } } } catch (error) { diff --git a/src/stores/settings/advanced.js b/src/stores/settings/advanced.js index 108252c7..b9cceda7 100644 --- a/src/stores/settings/advanced.js +++ b/src/stores/settings/advanced.js @@ -1,10 +1,12 @@ import { reactive, ref, watch } from 'vue'; -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; import { AppDebug } from '../../service/appConfig'; import { database } from '../../service/database'; +import { languageCodes } from '../../localization'; import { useGameStore } from '../game'; import { useVRCXUpdaterStore } from '../vrcxUpdater'; import { useVrcxStore } from '../vrcx'; @@ -12,7 +14,6 @@ import { watchState } from '../../service/watchState'; import configRepository from '../../service/config'; import webApiService from '../../service/webapi'; -import { languageCodes } from '../../localization'; export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { const gameStore = useGameStore(); @@ -162,10 +163,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { configRepository.getString('VRCX_SentryEnabled', '') ]); - if ( - !bioLanguageConfig || - !languageCodes.includes(bioLanguageConfig) - ) { + if (!bioLanguageConfig || !languageCodes.includes(bioLanguageConfig)) { bioLanguage.value = 'en'; } else { bioLanguage.value = bioLanguageConfig; @@ -628,10 +626,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { async function translateText(text, targetLang, overrides) { if (!translationApi.value) { - ElMessage({ - message: 'Translation API disabled', - type: 'warning' - }); + toast.warning('Translation API disabled'); return null; } @@ -641,10 +636,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { if (provider === 'google') { const keyToUse = overrides?.key ?? translationApiKey.value; if (!keyToUse) { - ElMessage({ - message: 'No Translation API key configured', - type: 'warning' - }); + toast.warning('No Translation API key configured'); return null; } try { @@ -672,10 +664,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { } return data.data.translations[0].translatedText; } catch (err) { - ElMessage({ - message: `Translation failed: ${err.message}`, - type: 'error' - }); + toast.error(`Translation failed: ${err.message}`); return null; } } @@ -692,10 +681,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { `You are a translation assistant. Translate the user message into ${targetLang}. Only return the translated text.`; if (!endpoint || !model) { - ElMessage({ - message: 'Translation endpoint/model missing', - type: 'warning' - }); + toast.warning('Translation endpoint/model missing'); return null; } @@ -742,10 +728,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { const translated = data?.choices?.[0]?.message?.content; return typeof translated === 'string' ? translated.trim() : null; } catch (err) { - ElMessage({ - message: `Translation failed: ${err.message}`, - type: 'error' - }); + toast.error(`Translation failed: ${err.message}`); return null; } } @@ -769,25 +752,18 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { ) .then(async ({ action }) => { if (action === 'confirm') { - const msgBox = ElMessage({ - message: 'Batch print cropping in progress...', - type: 'warning', - duration: 0 - }); + const msgBox = toast.warning( + 'Batch print cropping in progress...', + { duration: Infinity, position: 'bottom-right' } + ); try { await AppApi.CropAllPrints(ugcFolderPath.value); - ElMessage({ - message: 'Batch print cropping complete', - type: 'success' - }); + toast.success('Batch print cropping complete'); } catch (err) { console.error(err); - ElMessage({ - message: `Batch print cropping failed: ${err}`, - type: 'error' - }); + toast.error(`Batch print cropping failed: ${err}`); } finally { - msgBox.close(); + toast.dismiss(msgBox); } } }) @@ -836,25 +812,18 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => { ) .then(async ({ action }) => { if (action === 'confirm') { - const msgBox = ElMessage({ - message: 'Batch metadata removal in progress...', - type: 'warning', - duration: 0 - }); + const msgBox = toast.warning( + 'Batch metadata removal in progress...', + { duration: Infinity, position: 'bottom-right' } + ); try { await AppApi.DeleteAllScreenshotMetadata(); - ElMessage({ - message: 'Batch metadata removal complete', - type: 'success' - }); + toast.success('Batch metadata removal complete'); } catch (err) { console.error(err); - ElMessage({ - message: `Batch metadata removal failed: ${err}`, - type: 'error' - }); + toast.error(`Batch metadata removal failed: ${err}`); } finally { - msgBox.close(); + toast.dismiss(msgBox); } } }) diff --git a/src/stores/ui.js b/src/stores/ui.js index 4e64ead3..f212454f 100644 --- a/src/stores/ui.js +++ b/src/stores/ui.js @@ -1,6 +1,6 @@ import { ref, watch } from 'vue'; -import { ElMessage } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useMagicKeys } from '@vueuse/core'; import { useRouter } from 'vue-router'; @@ -54,10 +54,7 @@ export const useUiStore = defineStore('Ui', () => { if (isPressed) { refreshCustomCss(); updateLocalizedStrings(); - ElMessage({ - message: 'Custom CSS and localization strings refreshed', - type: 'success' - }); + toast.success('Custom CSS and localization strings refreshed'); } }); diff --git a/src/stores/user.js b/src/stores/user.js index 750d4765..28265efb 100644 --- a/src/stores/user.js +++ b/src/stores/user.js @@ -1,6 +1,6 @@ import { computed, reactive, ref, shallowReactive, watch } from 'vue'; -import { ElMessage } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import Noty from 'noty'; @@ -842,10 +842,7 @@ export const useUserStore = defineStore('User', () => { .catch((err) => { D.loading = false; D.visible = false; - ElMessage({ - message: 'Failed to load user', - type: 'error' - }); + toast.error('Failed to load user'); throw err; }) .then((args) => { @@ -1218,10 +1215,7 @@ export const useUserStore = defineStore('User', () => { return; } } - ElMessage({ - message: 'Own avatar not found', - type: 'error' - }); + toast.error('Own avatar not found'); } } }); diff --git a/src/stores/vrcx.js b/src/stores/vrcx.js index 6edf2589..bb89716d 100644 --- a/src/stores/vrcx.js +++ b/src/stores/vrcx.js @@ -1,6 +1,7 @@ import { reactive, ref, watch } from 'vue'; -import { ElMessage, ElMessageBox } from 'element-plus'; +import { ElMessageBox } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; import Noty from 'noty'; @@ -160,12 +161,10 @@ export const useVrcxStore = defineStore('Vrcx', () => { let msgBox; if (state.databaseVersion < databaseVersion) { if (state.databaseVersion) { - msgBox = ElMessage({ - message: - 'DO NOT CLOSE VRCX, database upgrade in progress...', - type: 'warning', - duration: 0 - }); + msgBox = toast.warning( + 'DO NOT CLOSE VRCX, database upgrade in progress...', + { duration: Infinity, position: 'bottom-right' } + ); } console.log( `Updating database from ${state.databaseVersion} to ${databaseVersion}...` @@ -188,24 +187,19 @@ export const useVrcxStore = defineStore('Vrcx', () => { databaseVersion ); console.log('Database update complete.'); - msgBox?.close(); + toast.dismiss(msgBox); if (state.databaseVersion) { // only display when database exists - ElMessage({ - message: 'Database upgrade complete', - type: 'success' - }); + toast.success('Database upgrade complete'); } state.databaseVersion = databaseVersion; } catch (err) { console.error(err); - msgBox?.close(); - ElMessage({ - message: - 'Database upgrade failed, check console for details', - type: 'error', - duration: 120000 - }); + toast.dismiss(msgBox); + toast.error( + 'Database upgrade failed, check console for details', + { duration: 120000 } + ); AppApi.ShowDevTools(); } } @@ -365,12 +359,10 @@ export const useVrcxStore = defineStore('Vrcx', () => { } catch (e) { console.error('Failed to add screenshot metadata', e); if (e.message?.includes('UnauthorizedAccessException')) { - ElMessage({ - message: - 'Failed to add screenshot metadata, access denied. Make sure VRCX has permission to access the screenshot folder.', - type: 'error', - duration: 10000 - }); + toast.error( + 'Failed to add screenshot metadata, access denied. Make sure VRCX has permission to access the screenshot folder.', + { duration: 10000 } + ); } return; } @@ -547,10 +539,7 @@ export const useVrcxStore = defineStore('Vrcx', () => { } } - ElMessage({ - message: t('message.crash.vrcx_reload'), - type: 'success' - }); + toast.success(t('message.crash.vrcx_reload')); return; } eventLaunchCommand(command); @@ -603,10 +592,7 @@ export const useVrcxStore = defineStore('Vrcx', () => { const regexAvatarId = /avtr_[0-9A-Fa-f]{8}-([0-9A-Fa-f]{4}-){3}[0-9A-Fa-f]{12}/g; if (!avatarId.match(regexAvatarId) || avatarId.length !== 41) { - ElMessage({ - message: 'Invalid Avatar ID', - type: 'error' - }); + toast.error('Invalid Avatar ID'); break; } if (advancedSettingsStore.showConfirmationOnSwitchAvatar) { diff --git a/src/stores/vrcxUpdater.js b/src/stores/vrcxUpdater.js index 7efbee4c..073ed9a1 100644 --- a/src/stores/vrcxUpdater.js +++ b/src/stores/vrcxUpdater.js @@ -1,6 +1,6 @@ import { computed, ref } from 'vue'; -import { ElMessage } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { useI18n } from 'vue-i18n'; import { AppDebug } from '../service/appConfig'; @@ -211,12 +211,11 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => { checkingForVRCXUpdate.value = false; } if (response.status !== 200) { - ElMessage({ - message: t('message.vrcx_updater.failed', { + toast.error( + t('message.vrcx_updater.failed', { message: `${response.status} ${response.data}` - }), - type: 'error' - }); + }) + ); return; } pendingVRCXUpdate.value = false; @@ -290,12 +289,11 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => { checkingForVRCXUpdate.value = false; } if (response.status !== 200) { - ElMessage({ - message: t('message.vrcx_updater.failed', { + toast.error( + t('message.vrcx_updater.failed', { message: `${response.status} ${response.data}` - }), - type: 'error' - }); + }) + ); return; } if (AppDebug.debugWebRequests) { @@ -303,12 +301,11 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => { } const releases = []; if (typeof json !== 'object' || json.message) { - ElMessage({ - message: t('message.vrcx_updater.failed', { + toast.error( + t('message.vrcx_updater.failed', { message: json.message - }), - type: 'error' - }); + }) + ); return; } for (const release of json) { @@ -347,10 +344,7 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => { pendingVRCXInstall.value = releaseName; } catch (err) { console.error(err); - ElMessage({ - message: `${t('message.vrcx_updater.failed_install')} ${err}`, - type: 'error' - }); + toast.error(`${t('message.vrcx_updater.failed_install')} ${err}`); } finally { updateInProgress.value = false; updateProgress.value = 0; diff --git a/src/stores/world.js b/src/stores/world.js index d9aec62d..23826212 100644 --- a/src/stores/world.js +++ b/src/stores/world.js @@ -1,6 +1,6 @@ import { reactive, shallowReactive, watch } from 'vue'; -import { ElMessage } from 'element-plus'; import { defineStore } from 'pinia'; +import { toast } from 'vue-sonner'; import { checkVRChatCache, @@ -132,10 +132,7 @@ export const useWorldStore = defineStore('World', () => { .catch((err) => { D.loading = false; D.visible = false; - ElMessage({ - message: 'Failed to load world', - type: 'error' - }); + toast.error('Failed to load world'); throw err; }) .then((args) => { diff --git a/src/views/Charts/components/MutualFriends.vue b/src/views/Charts/components/MutualFriends.vue index c97f1bb3..9135b4ca 100644 --- a/src/views/Charts/components/MutualFriends.vue +++ b/src/views/Charts/components/MutualFriends.vue @@ -104,10 +104,11 @@