mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-06 14:46:04 +02:00
Add recent action indicators to friend context menu
This commit is contained in:
@@ -147,12 +147,18 @@
|
|||||||
v-if="item.row.friend.state === 'online'"
|
v-if="item.row.friend.state === 'online'"
|
||||||
@click="friendRequestInvite(item.row.friend)">
|
@click="friendRequestInvite(item.row.friend)">
|
||||||
{{ t('dialog.user.actions.request_invite') }}
|
{{ t('dialog.user.actions.request_invite') }}
|
||||||
|
<ContextMenuShortcut v-if="isActionRecent(item.row.friend.id, 'Request Invite')">
|
||||||
|
<Clock class="size-3.5 text-muted-foreground" />
|
||||||
|
</ContextMenuShortcut>
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
<ContextMenuItem
|
<ContextMenuItem
|
||||||
v-if="isGameRunning"
|
v-if="isGameRunning"
|
||||||
:disabled="!canInviteToMyLocation"
|
:disabled="!canInviteToMyLocation"
|
||||||
@click="friendInvite(item.row.friend)">
|
@click="friendInvite(item.row.friend)">
|
||||||
{{ t('dialog.user.actions.invite') }}
|
{{ t('dialog.user.actions.invite') }}
|
||||||
|
<ContextMenuShortcut v-if="isActionRecent(item.row.friend.id, 'Invite')">
|
||||||
|
<Clock class="size-3.5 text-muted-foreground" />
|
||||||
|
</ContextMenuShortcut>
|
||||||
</ContextMenuItem>
|
</ContextMenuItem>
|
||||||
<ContextMenuItem
|
<ContextMenuItem
|
||||||
:disabled="!currentUser.isBoopingEnabled"
|
:disabled="!currentUser.isBoopingEnabled"
|
||||||
@@ -193,7 +199,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
|
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
|
||||||
import { ChevronDown, User } from 'lucide-vue-next';
|
import { ChevronDown, Clock, User } from 'lucide-vue-next';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { toast } from 'vue-sonner';
|
import { toast } from 'vue-sonner';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
@@ -205,6 +211,7 @@
|
|||||||
ContextMenuContent,
|
ContextMenuContent,
|
||||||
ContextMenuItem,
|
ContextMenuItem,
|
||||||
ContextMenuSeparator,
|
ContextMenuSeparator,
|
||||||
|
ContextMenuShortcut,
|
||||||
ContextMenuSub,
|
ContextMenuSub,
|
||||||
ContextMenuSubContent,
|
ContextMenuSubContent,
|
||||||
ContextMenuSubTrigger,
|
ContextMenuSubTrigger,
|
||||||
@@ -225,6 +232,7 @@
|
|||||||
import { getFriendsSortFunction, isRealInstance } from '../../../shared/utils';
|
import { getFriendsSortFunction, isRealInstance } from '../../../shared/utils';
|
||||||
import { instanceRequest, notificationRequest, queryRequest, userRequest } from '../../../api';
|
import { instanceRequest, notificationRequest, queryRequest, userRequest } from '../../../api';
|
||||||
import { useInviteChecks } from '../../../composables/useInviteChecks';
|
import { useInviteChecks } from '../../../composables/useInviteChecks';
|
||||||
|
import { isActionRecent, recordRecentAction } from '../../../composables/useRecentActions';
|
||||||
import { useUserDisplay } from '../../../composables/useUserDisplay';
|
import { useUserDisplay } from '../../../composables/useUserDisplay';
|
||||||
import { getFriendsLocations } from '../../../shared/utils/location.js';
|
import { getFriendsLocations } from '../../../shared/utils/location.js';
|
||||||
import { parseLocation } from '../../../shared/utils';
|
import { parseLocation } from '../../../shared/utils';
|
||||||
@@ -793,6 +801,7 @@
|
|||||||
*/
|
*/
|
||||||
function friendRequestInvite(friend) {
|
function friendRequestInvite(friend) {
|
||||||
notificationRequest.sendRequestInvite({ platform: 'standalonewindows' }, friend.id).then(() => {
|
notificationRequest.sendRequestInvite({ platform: 'standalonewindows' }, friend.id).then(() => {
|
||||||
|
recordRecentAction(friend.id, 'Request Invite');
|
||||||
toast.success('Request invite sent');
|
toast.success('Request invite sent');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -817,6 +826,7 @@
|
|||||||
friend.id
|
friend.id
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
recordRecentAction(friend.id, 'Invite');
|
||||||
toast.success(t('message.invite.sent'));
|
toast.success(t('message.invite.sent'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ vi.mock('../../../../components/ui/context-menu', () => ({
|
|||||||
'<button :disabled="disabled" @click="$emit(\'click\')"><slot /></button>'
|
'<button :disabled="disabled" @click="$emit(\'click\')"><slot /></button>'
|
||||||
},
|
},
|
||||||
ContextMenuSeparator: { template: '<hr />' },
|
ContextMenuSeparator: { template: '<hr />' },
|
||||||
|
ContextMenuShortcut: { template: '<span><slot /></span>' },
|
||||||
ContextMenuSub: { template: '<div><slot /></div>' },
|
ContextMenuSub: { template: '<div><slot /></div>' },
|
||||||
ContextMenuSubContent: { template: '<div><slot /></div>' },
|
ContextMenuSubContent: { template: '<div><slot /></div>' },
|
||||||
ContextMenuSubTrigger: { template: '<div><slot /></div>' },
|
ContextMenuSubTrigger: { template: '<div><slot /></div>' },
|
||||||
@@ -210,9 +211,15 @@ vi.mock('../FriendItem.vue', () => ({
|
|||||||
|
|
||||||
vi.mock('lucide-vue-next', () => ({
|
vi.mock('lucide-vue-next', () => ({
|
||||||
ChevronDown: { template: '<span data-testid="chevron" />' },
|
ChevronDown: { template: '<span data-testid="chevron" />' },
|
||||||
|
Clock: { template: '<span data-testid="clock" />' },
|
||||||
User: { template: '<i />' }
|
User: { template: '<i />' }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
vi.mock('../../../../composables/useRecentActions', () => ({
|
||||||
|
isActionRecent: vi.fn(() => false),
|
||||||
|
recordRecentAction: vi.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
import FriendsSidebar from '../FriendsSidebar.vue';
|
import FriendsSidebar from '../FriendsSidebar.vue';
|
||||||
|
|
||||||
function flushPromises() {
|
function flushPromises() {
|
||||||
|
|||||||
@@ -182,18 +182,14 @@
|
|||||||
{{ t('dialog.screenshot_metadata.section_players') }} ({{ screenshotMetadataDialog.metadata.players.length }})
|
{{ t('dialog.screenshot_metadata.section_players') }} ({{ screenshotMetadataDialog.metadata.players.length }})
|
||||||
</h4>
|
</h4>
|
||||||
<div class="flex flex-wrap gap-1 max-h-[180px] overflow-y-auto">
|
<div class="flex flex-wrap gap-1 max-h-[180px] overflow-y-auto">
|
||||||
<TooltipWrapper
|
<Badge
|
||||||
v-for="user in screenshotMetadataDialog.metadata.players"
|
v-for="user in screenshotMetadataDialog.metadata.players"
|
||||||
:key="user.id"
|
:key="user.id"
|
||||||
side="top"
|
variant="secondary"
|
||||||
:content="user.pos ? '(' + user.pos.x + ', ' + user.pos.y + ', ' + user.pos.z + ')' : ''">
|
class="cursor-pointer hover:bg-accent transition-colors"
|
||||||
<Badge
|
@click="lookupUser(user)">
|
||||||
variant="secondary"
|
{{ user.displayName }}
|
||||||
class="cursor-pointer hover:bg-accent transition-colors"
|
</Badge>
|
||||||
@click="lookupUser(user)">
|
|
||||||
{{ user.displayName }}
|
|
||||||
</Badge>
|
|
||||||
</TooltipWrapper>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -267,7 +263,7 @@
|
|||||||
import { ButtonGroup } from '@/components/ui/button-group';
|
import { ButtonGroup } from '@/components/ui/button-group';
|
||||||
import { InputGroupSearch } from '@/components/ui/input-group';
|
import { InputGroupSearch } from '@/components/ui/input-group';
|
||||||
import { Kbd } from '@/components/ui/kbd';
|
import { Kbd } from '@/components/ui/kbd';
|
||||||
import { TooltipWrapper } from '@/components/ui/tooltip';
|
|
||||||
import { formatDateFilter } from '@/shared/utils';
|
import { formatDateFilter } from '@/shared/utils';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { toast } from 'vue-sonner';
|
import { toast } from 'vue-sonner';
|
||||||
|
|||||||
Reference in New Issue
Block a user