mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-18 06:13:52 +02:00
add destructive variant to alert dialogs for destructive actions
This commit is contained in:
@@ -287,7 +287,8 @@ export function useAvatarDialogCommands(
|
||||
title: t('confirm.title'),
|
||||
description: t('confirm.command_question', {
|
||||
command: t('dialog.avatar.actions.block')
|
||||
})
|
||||
}),
|
||||
destructive: true
|
||||
}),
|
||||
handler: (id) => {
|
||||
avatarModerationRequest
|
||||
@@ -369,7 +370,8 @@ export function useAvatarDialogCommands(
|
||||
title: t('confirm.title'),
|
||||
description: t('confirm.command_question', {
|
||||
command: t('dialog.avatar.actions.delete')
|
||||
})
|
||||
}),
|
||||
destructive: true
|
||||
}),
|
||||
handler: (id) => {
|
||||
avatarRequest
|
||||
@@ -399,7 +401,8 @@ export function useAvatarDialogCommands(
|
||||
title: t('confirm.title'),
|
||||
description: t('confirm.command_question', {
|
||||
command: t('dialog.avatar.actions.delete_impostor')
|
||||
})
|
||||
}),
|
||||
destructive: true
|
||||
}),
|
||||
handler: (id) => {
|
||||
avatarRequest
|
||||
@@ -432,7 +435,8 @@ export function useAvatarDialogCommands(
|
||||
title: t('confirm.title'),
|
||||
description: t('confirm.command_question', {
|
||||
command: t('dialog.avatar.actions.regenerate_impostor')
|
||||
})
|
||||
}),
|
||||
destructive: true
|
||||
}),
|
||||
handler: (id) => {
|
||||
avatarRequest
|
||||
|
||||
@@ -770,7 +770,8 @@
|
||||
const dashboardId = String(dashboardKey || '').replace(DASHBOARD_NAV_KEY_PREFIX, '');
|
||||
const { ok } = await modalStore.confirm({
|
||||
title: t('dashboard.confirmations.delete_title'),
|
||||
description: t('dashboard.confirmations.delete_description')
|
||||
description: t('dashboard.confirmations.delete_description'),
|
||||
destructive: true
|
||||
});
|
||||
if (!ok) {
|
||||
return;
|
||||
|
||||
@@ -561,7 +561,8 @@
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.delete_post'),
|
||||
title: t('confirm.title')
|
||||
title: t('confirm.title'),
|
||||
destructive: true
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
|
||||
@@ -84,7 +84,8 @@ export function useGroupDialogCommands(
|
||||
'Block Group': {
|
||||
confirm: () => ({
|
||||
title: t('confirm.title'),
|
||||
description: t('confirm.block_group')
|
||||
description: t('confirm.block_group'),
|
||||
destructive: true
|
||||
}),
|
||||
handler: (id) => {
|
||||
groupRequest.blockGroup({ groupId: id }).then((args) => {
|
||||
|
||||
@@ -486,7 +486,8 @@ export function useUserDialogCommands(
|
||||
title: t('confirm.title'),
|
||||
description: t('confirm.command_question', {
|
||||
command: t('dialog.user.actions.moderation_block')
|
||||
})
|
||||
}),
|
||||
destructive: true
|
||||
}),
|
||||
handler: async (userId) => {
|
||||
const args =
|
||||
@@ -518,7 +519,8 @@ export function useUserDialogCommands(
|
||||
title: t('confirm.title'),
|
||||
description: t('confirm.command_question', {
|
||||
command: t('dialog.user.actions.moderation_mute')
|
||||
})
|
||||
}),
|
||||
destructive: true
|
||||
}),
|
||||
handler: async (userId) => {
|
||||
const args =
|
||||
@@ -554,7 +556,8 @@ export function useUserDialogCommands(
|
||||
command: t(
|
||||
'dialog.user.actions.moderation_disable_avatar_interaction'
|
||||
)
|
||||
})
|
||||
}),
|
||||
destructive: true
|
||||
}),
|
||||
handler: async (userId) => {
|
||||
const args =
|
||||
@@ -590,7 +593,8 @@ export function useUserDialogCommands(
|
||||
command: t(
|
||||
'dialog.user.actions.moderation_disable_chatbox'
|
||||
)
|
||||
})
|
||||
}),
|
||||
destructive: true
|
||||
}),
|
||||
handler: async (userId) => {
|
||||
const args =
|
||||
@@ -622,7 +626,8 @@ export function useUserDialogCommands(
|
||||
title: t('confirm.title'),
|
||||
description: t('confirm.command_question', {
|
||||
command: t('dialog.user.actions.unfriend')
|
||||
})
|
||||
}),
|
||||
destructive: true
|
||||
}),
|
||||
handler: async (userId) => {
|
||||
const args = await friendRequest.deleteFriend(
|
||||
|
||||
@@ -528,7 +528,8 @@ export function useWorldDialogCommands(
|
||||
title: t('confirm.title'),
|
||||
description: t('confirm.command_question', {
|
||||
command: t('dialog.world.actions.delete')
|
||||
})
|
||||
}),
|
||||
destructive: true
|
||||
}),
|
||||
handler: (id) => {
|
||||
worldRequest.deleteWorld({ worldId: id }).then((args) => {
|
||||
|
||||
@@ -50,11 +50,11 @@
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem
|
||||
:disabled="!hasNotifications"
|
||||
v-if="hasNotifications"
|
||||
@click="clearAllNotifications">
|
||||
{{ t('nav_menu.mark_all_read') }}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuSeparator v-if="hasNotifications" />
|
||||
<template v-if="isDashboardItem(item)">
|
||||
<ContextMenuItem @click="handleEditDashboard(item)">
|
||||
{{ t('nav_menu.edit_dashboard') }}
|
||||
@@ -101,10 +101,10 @@
|
||||
</SidebarContent>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem :disabled="!hasNotifications" @click="clearAllNotifications">
|
||||
<ContextMenuItem v-if="hasNotifications" @click="clearAllNotifications">
|
||||
{{ t('nav_menu.mark_all_read') }}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuSeparator v-if="hasNotifications" />
|
||||
<ContextMenuItem @click="handleQuickCreateDashboard">
|
||||
{{ t('dashboard.new_dashboard') }}
|
||||
</ContextMenuItem>
|
||||
@@ -361,7 +361,8 @@
|
||||
}
|
||||
const { ok } = await modalStore.confirm({
|
||||
title: t('dashboard.confirmations.delete_title'),
|
||||
description: t('dashboard.confirmations.delete_description')
|
||||
description: t('dashboard.confirmations.delete_description'),
|
||||
destructive: true
|
||||
});
|
||||
if (!ok) {
|
||||
return;
|
||||
|
||||
@@ -94,11 +94,11 @@
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem
|
||||
:disabled="!hasNotifications"
|
||||
v-if="hasNotifications"
|
||||
@click="emit('clear-notifications')">
|
||||
{{ t('nav_menu.mark_all_read') }}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuSeparator v-if="hasNotifications" />
|
||||
<template v-if="isDashboardItem(entry)">
|
||||
<ContextMenuItem @click="emit('edit-dashboard', entry)">
|
||||
{{ t('nav_menu.edit_dashboard') }}
|
||||
@@ -126,10 +126,10 @@
|
||||
</div>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem :disabled="!hasNotifications" @click="emit('clear-notifications')">
|
||||
<ContextMenuItem v-if="hasNotifications" @click="emit('clear-notifications')">
|
||||
{{ t('nav_menu.mark_all_read') }}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuSeparator v-if="hasNotifications" />
|
||||
<ContextMenuItem @click="emit('create-dashboard')">
|
||||
{{ t('dashboard.new_dashboard') }}
|
||||
</ContextMenuItem>
|
||||
|
||||
@@ -7,14 +7,15 @@
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
variant: { type: String, required: false },
|
||||
class: { type: null, required: false }
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
const delegatedProps = reactiveOmit(props, 'class', 'variant');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
|
||||
<AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants({ variant: props.variant }), props.class)">
|
||||
<slot />
|
||||
</AlertDialogAction>
|
||||
</template>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
const modalStore = useModalStore();
|
||||
|
||||
const { alertOpen, alertMode, alertTitle, alertDescription, alertOkText, alertCancelText, alertDismissible } =
|
||||
const { alertOpen, alertMode, alertTitle, alertDescription, alertOkText, alertCancelText, alertDismissible, alertDestructive } =
|
||||
storeToRefs(modalStore);
|
||||
|
||||
function onEscapeKeyDown(event) {
|
||||
@@ -60,7 +60,7 @@
|
||||
{{ alertCancelText }}
|
||||
</AlertDialogCancel>
|
||||
|
||||
<AlertDialogAction @click="modalStore.handleOk">
|
||||
<AlertDialogAction :variant="alertDestructive ? 'destructive' : undefined" @click="modalStore.handleOk">
|
||||
{{ alertOkText }}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
|
||||
@@ -232,7 +232,8 @@ export function promptClearAvatarHistory() {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.clear_avatar_history'),
|
||||
title: 'Confirm'
|
||||
title: t('confirm.title'),
|
||||
destructive: true
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
@@ -472,7 +473,7 @@ export function selectAvatarWithConfirmation(id) {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.select_avatar'),
|
||||
title: 'Confirm'
|
||||
title: t('confirm.title')
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
|
||||
@@ -746,7 +746,8 @@ export function leaveGroupPrompt(groupId) {
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.leave_group'),
|
||||
title: t('confirm.title')
|
||||
title: t('confirm.title'),
|
||||
destructive: true
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (!ok) return;
|
||||
|
||||
@@ -187,7 +187,8 @@
|
||||
"tab_group": "Group",
|
||||
"tab_other": "Other",
|
||||
"past_notifications": "Last 24 hours",
|
||||
"no_new_notifications": "No new notifications in the past 24 hours"
|
||||
"no_new_notifications": "No new notifications in the past 24 hours",
|
||||
"no_unseen_notifications": "No unread notifications"
|
||||
}
|
||||
},
|
||||
"view": {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { useI18n } from 'vue-i18n';
|
||||
* @property {string=} confirmText
|
||||
* @property {string=} cancelText
|
||||
* @property {boolean=} dismissible // true: allow esc/outside, false: block
|
||||
* @property {boolean=} destructive // true: use destructive variant for confirm button
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -23,6 +24,7 @@ import { useI18n } from 'vue-i18n';
|
||||
* @property {string} description
|
||||
* @property {string=} confirmText
|
||||
* @property {boolean=} dismissible
|
||||
* @property {boolean=} destructive // true: use destructive variant for confirm button
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -65,6 +67,7 @@ export const useModalStore = defineStore('Modal', () => {
|
||||
const alertOkText = ref('');
|
||||
const alertCancelText = ref('');
|
||||
const alertDismissible = ref(true);
|
||||
const alertDestructive = ref(false);
|
||||
|
||||
const promptOpen = ref(false);
|
||||
const promptTitle = ref('');
|
||||
@@ -155,6 +158,7 @@ export const useModalStore = defineStore('Modal', () => {
|
||||
alertTitle.value = options.title;
|
||||
alertDescription.value = options.description;
|
||||
alertDismissible.value = options.dismissible !== false;
|
||||
alertDestructive.value = options.destructive === true;
|
||||
|
||||
if (mode === 'alert') {
|
||||
alertOkText.value =
|
||||
@@ -381,6 +385,7 @@ export const useModalStore = defineStore('Modal', () => {
|
||||
alertOkText,
|
||||
alertCancelText,
|
||||
alertDismissible,
|
||||
alertDestructive,
|
||||
promptOpen,
|
||||
promptTitle,
|
||||
promptDescription,
|
||||
|
||||
@@ -212,7 +212,8 @@
|
||||
const handleDelete = async () => {
|
||||
const { ok } = await modalStore.confirm({
|
||||
title: t('dashboard.confirmations.delete_title'),
|
||||
description: t('dashboard.confirmations.delete_description')
|
||||
description: t('dashboard.confirmations.delete_description'),
|
||||
destructive: true
|
||||
});
|
||||
if (!ok) {
|
||||
return;
|
||||
|
||||
@@ -880,7 +880,8 @@
|
||||
invalidIdsText,
|
||||
title: t('view.favorite.avatars.confirm_delete_invalid'),
|
||||
confirmText: t('confirm.confirm_button'),
|
||||
cancelText: t('view.favorite.avatars.copy_removed_ids')
|
||||
cancelText: t('view.favorite.avatars.copy_removed_ids'),
|
||||
destructive: true
|
||||
});
|
||||
|
||||
if (!confirmDeleteResult.ok) {
|
||||
@@ -1000,7 +1001,8 @@
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.clear_group'),
|
||||
title: t('confirm.title')
|
||||
title: t('confirm.title'),
|
||||
destructive: true
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (ok) {
|
||||
@@ -1050,7 +1052,8 @@
|
||||
modalStore
|
||||
.confirm({
|
||||
description: t('confirm.delete_group', { name: group }),
|
||||
title: t('confirm.title')
|
||||
title: t('confirm.title'),
|
||||
destructive: true
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (ok) {
|
||||
@@ -1223,9 +1226,9 @@
|
||||
const total = selectedFavoriteAvatars.value.length;
|
||||
modalStore
|
||||
.confirm({
|
||||
description: `Are you sure you want to unfavorite ${total} favorites?
|
||||
This action cannot be undone.`,
|
||||
title: `Delete ${total} favorites?`
|
||||
description: t('confirm.bulk_unfavorite', { count: total }),
|
||||
title: t('confirm.bulk_unfavorite_title', { count: total }),
|
||||
destructive: true
|
||||
})
|
||||
.then(({ ok }) => {
|
||||
if (ok) {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<RefreshCw v-else />
|
||||
</Button>
|
||||
</TooltipWrapper>
|
||||
<ContextMenu>
|
||||
<ContextMenu v-if="hasUnseenNotifications">
|
||||
<ContextMenuTrigger as-child>
|
||||
<TooltipWrapper side="bottom" :content="t('side_panel.notification_center.title')">
|
||||
<Button
|
||||
@@ -36,17 +36,26 @@
|
||||
@click="isNotificationCenterOpen = !isNotificationCenterOpen">
|
||||
<Bell />
|
||||
<span
|
||||
v-if="hasUnseenNotifications"
|
||||
class="absolute top-1 right-1.25 size-1.5 rounded-full bg-red-500" />
|
||||
</Button>
|
||||
</TooltipWrapper>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem :disabled="!hasUnseenNotifications" @click="markNotificationsRead">
|
||||
<ContextMenuItem @click="markNotificationsRead">
|
||||
{{ t('nav_menu.mark_all_read') }}
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
<TooltipWrapper v-else side="bottom" :content="t('side_panel.notification_center.title')">
|
||||
<Button
|
||||
class="rounded-full relative"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
@click="isNotificationCenterOpen = !isNotificationCenterOpen"
|
||||
@contextmenu.prevent="toast.info(t('side_panel.notification_center.no_unseen_notifications'))">
|
||||
<Bell />
|
||||
</Button>
|
||||
</TooltipWrapper>
|
||||
<Popover v-model:open="isSettingsPopoverOpen">
|
||||
<PopoverTrigger as-child>
|
||||
<Button class="rounded-full" variant="ghost" size="icon-sm">
|
||||
@@ -246,6 +255,7 @@
|
||||
SelectValue
|
||||
} from '@/components/ui/select';
|
||||
import { Bell, RefreshCw, Search, Settings } from 'lucide-vue-next';
|
||||
import { toast } from 'vue-sonner';
|
||||
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from '@/components/ui/context-menu';
|
||||
import { Field, FieldContent, FieldLabel } from '@/components/ui/field';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
|
||||
Reference in New Issue
Block a user