fix styles

This commit is contained in:
pa
2026-01-18 17:06:16 +09:00
committed by Natsumi
parent c326e4fd3e
commit 1de16dc699
40 changed files with 370 additions and 601 deletions
+5 -1
View File
@@ -21,11 +21,15 @@ html {
box-sizing: border-box; box-sizing: border-box;
background: var(--background); background: var(--background);
height: calc(100vh - 20px); height: calc(100vh - 20px);
margin: 10px 10px 10px 0; margin: 10px 0 10px 0;
border-radius: var(--radius); border-radius: var(--radius);
border: 1px solid var(--border); border: 1px solid var(--border);
} }
.aside-collapsed .x-container {
margin-right: 10px;
}
html.dark .x-container { html.dark .x-container {
background: var(--sidebar); background: var(--sidebar);
} }
+7 -1
View File
@@ -3,7 +3,12 @@
<span class="flex items-center" <span class="flex items-center"
>{{ avatarName }} <Lock v-if="avatarType && avatarType === '(own)'" class="h-4 w-4 mx-1" >{{ avatarName }} <Lock v-if="avatarType && avatarType === '(own)'" class="h-4 w-4 mx-1"
/></span> /></span>
<span v-if="avatarTags" style="font-size: 12px">{{ avatarTags }}</span> <TooltipWrapper v-if="avatarTags">
<template #content>
<span>{{ avatarTags }}</span>
</template>
<span v-if="avatarTags" style="font-size: 12px" class="truncate">{{ avatarTags }}</span>
</TooltipWrapper>
</div> </div>
</template> </template>
@@ -11,6 +16,7 @@
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { Lock } from 'lucide-vue-next'; import { Lock } from 'lucide-vue-next';
import { TooltipWrapper } from './ui/tooltip';
import { useAvatarStore } from '../stores'; import { useAvatarStore } from '../stores';
const avatarStore = useAvatarStore(); const avatarStore = useAvatarStore();
+1 -1
View File
@@ -1,5 +1,5 @@
<template> <template>
<span @click="showUserDialog" class="x-link">{{ username }}</span> <span @click="showUserDialog" class="cursor-pointer">{{ username }}</span>
</template> </template>
<script setup> <script setup>
+48 -38
View File
@@ -3,32 +3,9 @@
<div v-if="!text" class="transparent">-</div> <div v-if="!text" class="transparent">-</div>
<div v-show="text" class="flex items-center"> <div v-show="text" class="flex items-center">
<div v-if="region" :class="['flags', 'mr-1.5', region]"></div> <div v-if="region" :class="['flags', 'mr-1.5', region]"></div>
<template v-if="disableTooltip"> <TooltipWrapper :content="tooltipContent" :disabled="tooltipDisabled" :delay-duration="300" side="top">
<div <div
:class="['x-location', { 'x-link': link && location !== 'private' && location !== 'offline' }]" :class="locationClasses"
class="inline-flex min-w-0 flex-nowrap items-center overflow-hidden"
@click="handleShowWorldDialog">
<Spinner v-if="isTraveling" class="mr-1" />
<span class="min-w-0 truncate">{{ text }}</span>
<span v-if="showInstanceIdInLocation && instanceName" class="ml-1 whitespace-nowrap">{{
` · #${instanceName}`
}}</span>
<span v-if="groupName" class="ml-0.5 whitespace-nowrap x-link" @click.stop="handleShowGroupDialog">
({{ groupName }})
</span>
</div>
<AlertTriangle v-if="isClosed" :class="['inline-block', 'ml-5']" style="color: lightcoral" />
</template>
<template v-else>
<TooltipWrapper
:content="`${t('dialog.new_instance.instance_id')}: #${instanceName}`"
:disabled="!instanceName || showInstanceIdInLocation"
:delay-duration="300"
side="top">
<div
:class="['x-location', { 'x-link': link && location !== 'private' && location !== 'offline' }]"
class="inline-flex min-w-0 flex-nowrap items-center overflow-hidden" class="inline-flex min-w-0 flex-nowrap items-center overflow-hidden"
@click="handleShowWorldDialog"> @click="handleShowWorldDialog">
<Spinner v-if="isTraveling" class="mr-1" /> <Spinner v-if="isTraveling" class="mr-1" />
@@ -38,23 +15,23 @@
}}</span> }}</span>
<span <span
v-if="groupName" v-if="groupName"
class="ml-0.5 whitespace-nowrap x-link" class="ml-0.5 whitespace-nowrap cursor-pointer"
@click.stop="handleShowGroupDialog"> @click.stop="handleShowGroupDialog">
({{ groupName }}) ({{ groupName }})
</span> </span>
</div> </div>
</TooltipWrapper> </TooltipWrapper>
<TooltipWrapper v-if="isClosed" :content="t('dialog.user.info.instance_closed')">
<AlertTriangle :class="['inline-block', 'ml-5']" style="color: lightcoral" /> <TooltipWrapper v-if="isClosed" :content="closedTooltip" :disabled="disableTooltip">
<AlertTriangle class="inline-block ml-2 text-muted-foreground" />
</TooltipWrapper> </TooltipWrapper>
</template> <Lock v-if="strict" class="inline-block ml-2 text-muted-foreground" />
<Lock v-if="strict" :class="['inline-block', 'ml-5']" />
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onBeforeUnmount, ref, watch } from 'vue'; import { computed, onBeforeUnmount, ref, watch } from 'vue';
import { AlertTriangle, Lock } from 'lucide-vue-next'; import { AlertTriangle, Lock } from 'lucide-vue-next';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@@ -113,6 +90,19 @@
const isClosed = ref(false); const isClosed = ref(false);
const instanceName = ref(''); const instanceName = ref('');
const isLocationLink = computed(() => props.link && props.location !== 'private' && props.location !== 'offline');
const locationClasses = computed(() => [
'x-location',
{
'cursor-pointer': isLocationLink.value
}
]);
const tooltipContent = computed(() => `${t('dialog.new_instance.instance_id')}: #${instanceName.value}`);
const tooltipDisabled = computed(
() => props.disableTooltip || !instanceName.value || showInstanceIdInLocation.value
);
const closedTooltip = computed(() => t('dialog.user.info.instance_closed'));
let isDisposed = false; let isDisposed = false;
onBeforeUnmount(() => { onBeforeUnmount(() => {
isDisposed = true; isDisposed = true;
@@ -136,16 +126,21 @@
return props.location; return props.location;
} }
function parse() { function resetState() {
if (isDisposed) {
return;
}
text.value = ''; text.value = '';
region.value = ''; region.value = '';
strict.value = false; strict.value = false;
isTraveling.value = false; isTraveling.value = false;
groupName.value = ''; groupName.value = '';
isClosed.value = false; isClosed.value = false;
instanceName.value = '';
}
function parse() {
if (isDisposed) {
return;
}
resetState();
let instanceId = props.location; let instanceId = props.location;
if (typeof props.traveling !== 'undefined' && props.location === 'traveling') { if (typeof props.traveling !== 'undefined' && props.location === 'traveling') {
@@ -159,8 +154,17 @@
return; return;
} }
applyInstanceRef(L);
updateGroupName(L, instanceId);
updateRegion(L);
strict.value = L.strict;
}
function applyInstanceRef(L) {
const instanceRef = cachedInstances.get(L.tag); const instanceRef = cachedInstances.get(L.tag);
if (typeof instanceRef !== 'undefined') { if (typeof instanceRef === 'undefined') {
return;
}
if (instanceRef.displayName) { if (instanceRef.displayName) {
setText(L); setText(L);
instanceName.value = instanceRef.displayName; instanceName.value = instanceRef.displayName;
@@ -170,9 +174,14 @@
} }
} }
function updateGroupName(L, instanceId) {
if (props.grouphint) { if (props.grouphint) {
groupName.value = props.grouphint; groupName.value = props.grouphint;
} else if (L.groupId) { return;
}
if (!L.groupId) {
return;
}
groupName.value = L.groupId; groupName.value = L.groupId;
getGroupName(instanceId).then((name) => { getGroupName(instanceId).then((name) => {
if (!isDisposed && name && currentInstanceId() === L.tag) { if (!isDisposed && name && currentInstanceId() === L.tag) {
@@ -180,6 +189,8 @@
} }
}); });
} }
function updateRegion(L) {
region.value = ''; region.value = '';
if (!L.isOffline && !L.isPrivate && !L.isTraveling) { if (!L.isOffline && !L.isPrivate && !L.isTraveling) {
region.value = L.region; region.value = L.region;
@@ -187,7 +198,6 @@
region.value = 'us'; region.value = 'us';
} }
} }
strict.value = L.strict;
} }
function setText(L) { function setText(L) {
+2 -2
View File
@@ -1,11 +1,11 @@
<template> <template>
<span class="x-location-world"> <span class="x-location-world">
<span v-if="region" :class="['flags', 'inline-block', 'mr-1.25', region]"></span> <span v-if="region" :class="['flags', 'inline-block', 'mr-1.25', region]"></span>
<span @click="showLaunchDialog" class="x-link"> <span @click="showLaunchDialog" class="cursor-pointer">
<Unlock v-if="isUnlocked" :class="['inline-block', 'mr-1.25']" /> <Unlock v-if="isUnlocked" :class="['inline-block', 'mr-1.25']" />
<span> {{ accessTypeName }} #{{ instanceName }}</span> <span> {{ accessTypeName }} #{{ instanceName }}</span>
</span> </span>
<span v-if="groupName" @click="showGroupDialog" class="x-link">({{ groupName }})</span> <span v-if="groupName" @click="showGroupDialog" class="cursor-pointer">({{ groupName }})</span>
<TooltipWrapper v-if="isClosed" :content="t('dialog.user.info.instance_closed')"> <TooltipWrapper v-if="isClosed" :content="t('dialog.user.info.instance_closed')">
<AlertTriangle :class="['inline-block', 'ml-5']" style="color: lightcoral" /> <AlertTriangle :class="['inline-block', 'ml-5']" style="color: lightcoral" />
</TooltipWrapper> </TooltipWrapper>
@@ -8,7 +8,7 @@
<div style="display: flex"> <div style="display: flex">
<img <img
:src="avatarDialog.ref.thumbnailImageUrl" :src="avatarDialog.ref.thumbnailImageUrl"
class="x-link" class="cursor-pointer"
@click="showFullscreenImageDialog(avatarDialog.ref.imageUrl)" @click="showFullscreenImageDialog(avatarDialog.ref.imageUrl)"
style="flex: none; width: 160px; height: 120px; border-radius: 12px" style="flex: none; width: 160px; height: 120px; border-radius: 12px"
loading="lazy" /> loading="lazy" />
@@ -23,7 +23,7 @@
</div> </div>
<div style="margin-top: 5px"> <div style="margin-top: 5px">
<span <span
class="x-link x-grey" class="cursor-pointer x-grey"
style="font-family: monospace" style="font-family: monospace"
@click="showUserDialog(avatarDialog.ref.authorId)" @click="showUserDialog(avatarDialog.ref.authorId)"
v-text="avatarDialog.ref.authorName"></span> v-text="avatarDialog.ref.authorName"></span>
@@ -105,7 +105,7 @@
<Badge <Badge
v-if="avatarDialog.inCache" v-if="avatarDialog.inCache"
variant="outline" variant="outline"
class="x-link" class="cursor-pointer"
style="margin-right: 5px; margin-top: 5px" style="margin-right: 5px; margin-top: 5px"
@click="openFolderGeneric(avatarDialog.cachePath)"> @click="openFolderGeneric(avatarDialog.cachePath)">
<span v-text="avatarDialog.cacheSize"></span> <span v-text="avatarDialog.cacheSize"></span>
+1 -1
View File
@@ -154,7 +154,7 @@
</div> </div>
<div class="mt-2"> <div class="mt-2">
<a <a
class="x-link" class="cursor-pointer"
@click.prevent="openExternalLink('https://remixicon.com/')"> @click.prevent="openExternalLink('https://remixicon.com/')">
https://remixicon.com/ https://remixicon.com/
</a> </a>
@@ -15,7 +15,7 @@
<img <img
:src="groupDialog.ref.iconUrl" :src="groupDialog.ref.iconUrl"
style="flex: none; width: 120px; height: 120px; border-radius: 12px" style="flex: none; width: 120px; height: 120px; border-radius: 12px"
class="x-link" class="cursor-pointer"
@click="showFullscreenImageDialog(groupDialog.ref.iconUrl)" @click="showFullscreenImageDialog(groupDialog.ref.iconUrl)"
loading="lazy" /> loading="lazy" />
<div style="flex: 1; display: flex; align-items: center; margin-left: 15px"> <div style="flex: 1; display: flex; align-items: center; margin-left: 15px">
@@ -42,7 +42,7 @@
</TooltipWrapper> </TooltipWrapper>
<div style="margin-top: 5px"> <div style="margin-top: 5px">
<span <span
class="x-link x-grey" class="cursor-pointer x-grey"
style="font-family: monospace" style="font-family: monospace"
@click="showUserDialog(groupDialog.ref.ownerId)" @click="showUserDialog(groupDialog.ref.ownerId)"
v-text="groupDialog.ownerDisplayName"></span> v-text="groupDialog.ownerDisplayName"></span>
@@ -357,7 +357,7 @@
<div class="group-banner-image-info"> <div class="group-banner-image-info">
<img <img
:src="groupDialog.ref.bannerUrl" :src="groupDialog.ref.bannerUrl"
class="x-link" class="cursor-pointer"
style=" style="
flex: none; flex: none;
width: 100%; width: 100%;
@@ -424,7 +424,7 @@
style="display: inline-block; margin-right: 5px"> style="display: inline-block; margin-right: 5px">
<img <img
:src="groupDialog.announcement.imageUrl" :src="groupDialog.announcement.imageUrl"
class="x-link" class="cursor-pointer"
style=" style="
flex: none; flex: none;
width: 60px; width: 60px;
@@ -760,7 +760,7 @@
<div v-if="post.imageUrl" style="display: inline-block; margin-right: 5px"> <div v-if="post.imageUrl" style="display: inline-block; margin-right: 5px">
<img <img
:src="post.imageUrl" :src="post.imageUrl"
class="x-link" class="cursor-pointer"
style=" style="
flex: none; flex: none;
width: 60px; width: 60px;
@@ -1123,7 +1123,7 @@
class="p-0 overflow-hidden transition-shadow hover:shadow-md"> class="p-0 overflow-hidden transition-shadow hover:shadow-md">
<img <img
:src="image.imageUrl" :src="image.imageUrl"
:class="['x-link', 'max-w-full', 'max-h-full']" :class="[' cursor-pointer', 'max-w-full', 'max-h-full']"
@click="showFullscreenImageDialog(image.imageUrl)" @click="showFullscreenImageDialog(image.imageUrl)"
loading="lazy" /> loading="lazy" />
</Card> </Card>
@@ -12,7 +12,7 @@
</DialogHeader> </DialogHeader>
<div v-if="inviteDialog.visible"> <div v-if="inviteDialog.visible">
<Location :location="inviteDialog.worldId" :link="false" /> <Location :location="inviteDialog.worldId" :link="false" class="cursor-default" />
<br /> <br />
<Button size="sm" class="mr-2" variant="outline" style="margin-top: 10px" @click="addSelfToInvite">{{ <Button size="sm" class="mr-2" variant="outline" style="margin-top: 10px" @click="addSelfToInvite">{{
t('dialog.invite.add_self') t('dialog.invite.add_self')
+2 -2
View File
@@ -29,7 +29,7 @@
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<span>{{ t('dialog.launch.short_url') }}</span> <span>{{ t('dialog.launch.short_url') }}</span>
<TooltipWrapper side="top" :content="t('dialog.launch.short_url_notice')"> <TooltipWrapper side="top" :content="t('dialog.launch.short_url_notice')">
<AlertTriangle /> <Info class="text-muted-foreground" />
</TooltipWrapper> </TooltipWrapper>
</span> </span>
</FieldLabel> </FieldLabel>
@@ -145,7 +145,7 @@
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { Field, FieldContent, FieldGroup, FieldLabel } from '@/components/ui/field'; import { Field, FieldContent, FieldGroup, FieldLabel } from '@/components/ui/field';
import { computed, onBeforeUnmount, ref, watch } from 'vue'; import { computed, onBeforeUnmount, ref, watch } from 'vue';
import { AlertTriangle, Copy, MoreHorizontal } from 'lucide-vue-next'; import { Copy, Info, MoreHorizontal } from 'lucide-vue-next';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ButtonGroup } from '@/components/ui/button-group'; import { ButtonGroup } from '@/components/ui/button-group';
import { InputGroupField } from '@/components/ui/input-group'; import { InputGroupField } from '@/components/ui/input-group';
@@ -76,7 +76,7 @@ export const createColumns = ({ onLookupUser }) => [
const original = row.original; const original = row.original;
return ( return (
<span <span
class="x-link" class=" cursor-pointer"
onClick={(event) => { onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
onLookupUser?.(original); onLookupUser?.(original);
@@ -66,10 +66,6 @@
{{ t('dialog.user.actions.edit_pronouns') }} {{ t('dialog.user.actions.edit_pronouns') }}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem @click="onCommand('Logout')">
<Power class="size-4" />
{{ t('dialog.user.actions.logout') }}
</DropdownMenuItem>
</template> </template>
<template v-else> <template v-else>
<template v-if="userDialog.isFriend"> <template v-if="userDialog.isFriend">
@@ -178,7 +178,7 @@
class="extra"> class="extra">
<div style="display: inline-block; flex: none; margin-right: 5px"> <div style="display: inline-block; flex: none; margin-right: 5px">
<Avatar <Avatar
class="x-link size-15! rounded-lg!" class="cursor-pointer size-15! rounded-lg!"
:style="{ :style="{
background: userDialog.isRepresentedGroupLoading ? '#f5f7fa' : '' background: userDialog.isRepresentedGroupLoading ? '#f5f7fa' : ''
}" }"
@@ -1442,7 +1442,6 @@
const { getFriendRequest, handleFriendDelete } = useFriendStore(); const { getFriendRequest, handleFriendDelete } = useFriendStore();
const { clearInviteImageUpload, showFullscreenImageDialog, showGalleryPage } = useGalleryStore(); const { clearInviteImageUpload, showFullscreenImageDialog, showGalleryPage } = useGalleryStore();
const { logout } = useAuthStore();
const { cachedConfig } = storeToRefs(useAuthStore()); const { cachedConfig } = storeToRefs(useAuthStore());
const { applyPlayerModeration, handlePlayerModerationDelete } = useModerationStore(); const { applyPlayerModeration, handlePlayerModerationDelete } = useModerationStore();
const { shiftHeld } = storeToRefs(useUiStore()); const { shiftHeld } = storeToRefs(useUiStore());
@@ -1802,8 +1801,6 @@
showBioDialog(); showBioDialog();
} else if (command === 'Pencil Pronouns') { } else if (command === 'Pencil Pronouns') {
showPronounsDialog(); showPronounsDialog();
} else if (command === 'Logout') {
logout();
} else if (command === 'Request Invite') { } else if (command === 'Request Invite') {
notificationRequest notificationRequest
.sendRequestInvite( .sendRequestInvite(
@@ -4,14 +4,14 @@
v-if=" v-if="
!userDialog.loading && (userDialog.ref.profilePicOverrideThumbnail || userDialog.ref.profilePicOverride) !userDialog.loading && (userDialog.ref.profilePicOverrideThumbnail || userDialog.ref.profilePicOverride)
" "
class="x-link" class="cursor-pointer"
:src="userDialog.ref.profilePicOverrideThumbnail || userDialog.ref.profilePicOverride" :src="userDialog.ref.profilePicOverrideThumbnail || userDialog.ref.profilePicOverride"
style="flex: none; height: 120px; width: 213.33px; border-radius: 12px; object-fit: cover" style="flex: none; height: 120px; width: 213.33px; border-radius: 12px; object-fit: cover"
@click="showFullscreenImageDialog(userDialog.ref.profilePicOverride)" @click="showFullscreenImageDialog(userDialog.ref.profilePicOverride)"
loading="lazy" /> loading="lazy" />
<img <img
v-else-if="!userDialog.loading" v-else-if="!userDialog.loading"
class="x-link" class="cursor-pointer"
:src="userDialog.ref.currentAvatarThumbnailImageUrl" :src="userDialog.ref.currentAvatarThumbnailImageUrl"
style="flex: none; height: 120px; width: 160px; border-radius: 12px; object-fit: cover" style="flex: none; height: 120px; width: 160px; border-radius: 12px; object-fit: cover"
@click="showFullscreenImageDialog(userDialog.ref.currentAvatarImageUrl)" @click="showFullscreenImageDialog(userDialog.ref.currentAvatarImageUrl)"
@@ -191,7 +191,7 @@
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<img <img
class="x-link hover:grayscale-0" class="cursor-pointer hover:grayscale-0"
:src="badge.badgeImageUrl" :src="badge.badgeImageUrl"
style=" style="
flex: none; flex: none;
@@ -208,7 +208,7 @@
<PopoverContent side="bottom" class="w-75"> <PopoverContent side="bottom" class="w-75">
<img <img
:src="badge.badgeImageUrl" :src="badge.badgeImageUrl"
:class="['x-link', 'max-w-full', 'max-h-full']" :class="['cursor-pointer', 'max-w-full', 'max-h-full']"
@click="showFullscreenImageDialog(badge.badgeImageUrl)" @click="showFullscreenImageDialog(badge.badgeImageUrl)"
loading="lazy" /> loading="lazy" />
<br /> <br />
@@ -253,7 +253,7 @@
<div v-if="userDialog.ref.userIcon" style="flex: none; margin-right: 10px"> <div v-if="userDialog.ref.userIcon" style="flex: none; margin-right: 10px">
<img <img
class="x-link" class="cursor-pointer"
:src="userImage(userDialog.ref, true, '256', true)" :src="userImage(userDialog.ref, true, '256', true)"
style="flex: none; width: 120px; height: 120px; border-radius: 12px; object-fit: cover" style="flex: none; width: 120px; height: 120px; border-radius: 12px; object-fit: cover"
@click="showFullscreenImageDialog(userDialog.ref.userIcon)" @click="showFullscreenImageDialog(userDialog.ref.userIcon)"
@@ -14,7 +14,7 @@
<div style="display: flex"> <div style="display: flex">
<img <img
:src="worldDialog.ref.thumbnailImageUrl" :src="worldDialog.ref.thumbnailImageUrl"
class="x-link" class="cursor-pointer"
style="flex: none; width: 160px; height: 120px; border-radius: 12px" style="flex: none; width: 160px; height: 120px; border-radius: 12px"
@click="showFullscreenImageDialog(worldDialog.ref.imageUrl)" @click="showFullscreenImageDialog(worldDialog.ref.imageUrl)"
loading="lazy" /> loading="lazy" />
@@ -36,7 +36,7 @@
</div> </div>
<div style="margin-top: 5px"> <div style="margin-top: 5px">
<span <span
class="x-link x-grey" class="cursor-pointer x-grey"
style="font-family: monospace" style="font-family: monospace"
@click="showUserDialog(worldDialog.ref.authorId)" @click="showUserDialog(worldDialog.ref.authorId)"
v-text="worldDialog.ref.authorName" /> v-text="worldDialog.ref.authorName" />
@@ -125,7 +125,7 @@
<Badge <Badge
v-if="worldDialog.inCache" v-if="worldDialog.inCache"
variant="outline" variant="outline"
class="x-link" class="cursor-pointer"
style="margin-right: 5px; margin-top: 5px" style="margin-right: 5px; margin-top: 5px"
@click="openFolderGeneric(worldDialog.cachePath)"> @click="openFolderGeneric(worldDialog.cachePath)">
<span v-text="worldDialog.cacheSize" /> <span v-text="worldDialog.cacheSize" />
+2 -1
View File
@@ -348,7 +348,8 @@
"stop_fetching": "Stop fetching" "stop_fetching": "Stop fetching"
}, },
"status": { "status": {
"no_friends_to_process": "You have no friends to process" "no_friends_to_process": "You have no friends to process",
"loading_cache": "Loading cached mutual friends from database"
}, },
"progress": { "progress": {
"friends_processed": "Friends processed", "friends_processed": "Friends processed",
+14 -2
View File
@@ -215,7 +215,11 @@
const isOptOut = computed(() => Boolean(currentUser.value?.hasSharedConnectionsOptOut)); const isOptOut = computed(() => Boolean(currentUser.value?.hasSharedConnectionsOptOut));
// @ts-ignore // @ts-ignore
const graphReady = computed(() => Array.isArray(graphPayload.value?.nodes) && graphPayload.value.nodes.length > 0); const graphReady = computed(() => Array.isArray(graphPayload.value?.nodes) && graphPayload.value.nodes.length > 0);
const fetchButtonDisabled = computed(() => isFetching.value || isOptOut.value || totalFriends.value === 0); const isLoadingSnapshot = ref(false);
const loadingToastId = ref(null);
const fetchButtonDisabled = computed(
() => isFetching.value || isOptOut.value || totalFriends.value === 0 || isLoadingSnapshot.value
);
const fetchButtonLabel = computed(() => const fetchButtonLabel = computed(() =>
hasFetched.value hasFetched.value
? t('view.charts.mutual_friend.actions.fetch_again') ? t('view.charts.mutual_friend.actions.fetch_again')
@@ -361,9 +365,11 @@
} }
async function loadGraphFromDatabase() { async function loadGraphFromDatabase() {
if (hasFetched.value || isFetching.value) { if (hasFetched.value || isFetching.value || isLoadingSnapshot.value) {
return; return;
} }
isLoadingSnapshot.value = true;
loadingToastId.value = toast.loading(t('view.charts.mutual_friend.status.loading_cache'));
try { try {
const snapshot = await database.getMutualGraphSnapshot(); const snapshot = await database.getMutualGraphSnapshot();
if (!snapshot || snapshot.size === 0) { if (!snapshot || snapshot.size === 0) {
@@ -399,6 +405,12 @@
status.needsRefetch = false; status.needsRefetch = false;
} catch (err) { } catch (err) {
console.error('[MutualNetworkGraph] Failed to load cached mutual graph', err); console.error('[MutualNetworkGraph] Failed to load cached mutual graph', err);
} finally {
isLoadingSnapshot.value = false;
if (loadingToastId.value !== null && loadingToastId.value !== undefined) {
toast.dismiss(loadingToastId.value);
loadingToastId.value = null;
}
} }
} }
+23 -122
View File
@@ -129,57 +129,30 @@
<MoreHorizontal /> <MoreHorizontal />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent side="right" class="w-55 p-1 rounded-lg"> <DropdownMenuContent side="right" class="w-55">
<div class="favorites-group-menu"> <DropdownMenuItem @click="handleRemoteRename(group)">
<button
type="button"
class="favorites-group-menu__item"
@click="handleRemoteRename(group)">
<span>{{ t('view.favorite.rename_tooltip') }}</span> <span>{{ t('view.favorite.rename_tooltip') }}</span>
</button> </DropdownMenuItem>
<DropdownMenuSub> <DropdownMenuSub>
<DropdownMenuSubTrigger <DropdownMenuSubTrigger>
class="favorites-group-menu__item favorites-group-menu__item--submenu">
<span>{{ t('view.favorite.visibility_tooltip') }}</span> <span>{{ t('view.favorite.visibility_tooltip') }}</span>
</DropdownMenuSubTrigger> </DropdownMenuSubTrigger>
<DropdownMenuPortal> <DropdownMenuPortal>
<DropdownMenuSubContent <DropdownMenuSubContent side="right" align="start" class="w-45">
side="right" <DropdownMenuCheckboxItem
align="start"
class="w-45 p-1 rounded-lg">
<div class="group-visibility-menu">
<button
v-for="visibility in avatarGroupVisibilityOptions" v-for="visibility in avatarGroupVisibilityOptions"
:key="visibility" :key="visibility"
type="button" :model-value="group.visibility === visibility"
:class="[ indicator-position="right"
'group-visibility-menu__item', @select="handleVisibilitySelection(group, visibility)">
{
'is-active':
group.visibility === visibility
}
]"
@click="
handleVisibilitySelection(group, visibility)
">
<span>{{ formatVisibility(visibility) }}</span> <span>{{ formatVisibility(visibility) }}</span>
<span </DropdownMenuCheckboxItem>
v-if="group.visibility === visibility"
class="group-visibility-menu__check">
<Check class="h-3 w-3" />
</span>
</button>
</div>
</DropdownMenuSubContent> </DropdownMenuSubContent>
</DropdownMenuPortal> </DropdownMenuPortal>
</DropdownMenuSub> </DropdownMenuSub>
<button <DropdownMenuItem variant="destructive" @click="handleRemoteClear(group)">
type="button"
class="favorites-group-menu__item favorites-group-menu__item--danger"
@click="handleRemoteClear(group)">
<span>{{ t('view.favorite.clear') }}</span> <span>{{ t('view.favorite.clear') }}</span>
</button> </DropdownMenuItem>
</div>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
@@ -251,27 +224,16 @@
><Ellipsis ><Ellipsis
/></Button> /></Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent side="right" class="w-50 p-1 rounded-lg"> <DropdownMenuContent side="right" class="w-50">
<div class="favorites-group-menu"> <DropdownMenuItem @click="handleLocalRename(group)">
<button
type="button"
class="favorites-group-menu__item"
@click="handleLocalRename(group)">
<span>{{ t('view.favorite.rename_tooltip') }}</span> <span>{{ t('view.favorite.rename_tooltip') }}</span>
</button> </DropdownMenuItem>
<button <DropdownMenuItem @click="handleCheckInvalidAvatars(group)">
type="button"
class="favorites-group-menu__item"
@click="handleCheckInvalidAvatars(group)">
<span>{{ t('view.favorite.avatars.check_invalid') }}</span> <span>{{ t('view.favorite.avatars.check_invalid') }}</span>
</button> </DropdownMenuItem>
<button <DropdownMenuItem variant="destructive" @click="handleLocalDelete(group)">
type="button"
class="favorites-group-menu__item favorites-group-menu__item--danger"
@click="handleLocalDelete(group)">
<span>{{ t('view.favorite.delete_tooltip') }}</span> <span>{{ t('view.favorite.delete_tooltip') }}</span>
</button> </DropdownMenuItem>
</div>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
@@ -317,15 +279,10 @@
><Ellipsis ><Ellipsis
/></Button> /></Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent side="right" class="w-45 p-1 rounded-lg"> <DropdownMenuContent side="right" class="w-45">
<div class="favorites-group-menu"> <DropdownMenuItem variant="destructive" @click="handleHistoryClear">
<button
type="button"
class="favorites-group-menu__item favorites-group-menu__item--danger"
@click="handleHistoryClear">
<span>{{ t('view.favorite.clear_tooltip') }}</span> <span>{{ t('view.favorite.clear_tooltip') }}</span>
</button> </DropdownMenuItem>
</div>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
@@ -537,6 +494,7 @@
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuPortal, DropdownMenuPortal,
@@ -1736,63 +1694,6 @@
padding: 12px 0; padding: 12px 0;
} }
.favorites-group-menu {
display: flex;
flex-direction: column;
gap: 4px;
}
.favorites-group-menu__item {
display: flex;
align-items: center;
justify-content: space-between;
border: none;
background: transparent;
border-radius: 8px;
padding: 6px 12px;
font-size: 13px;
cursor: pointer;
color: inherit;
transition: background-color 0.15s ease;
min-height: 32px;
align-self: stretch;
}
.favorites-group-menu__item--submenu {
padding-right: 8px;
}
.favorites-group-menu__arrow {
margin-left: auto;
font-size: 12px;
}
.group-visibility-menu {
display: flex;
flex-direction: column;
gap: 4px;
}
.group-visibility-menu__item {
display: flex;
align-items: center;
justify-content: space-between;
border: none;
background: transparent;
padding: 6px 10px;
border-radius: 8px;
cursor: pointer;
color: inherit;
font-size: 13px;
transition: background-color 0.15s ease;
min-height: 32px;
align-self: stretch;
}
.group-visibility-menu__check {
font-size: 12px;
}
.favorites-content { .favorites-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
+14 -102
View File
@@ -129,57 +129,35 @@
<MoreHorizontal /> <MoreHorizontal />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent side="right" class="w-55 p-1 rounded-lg"> <DropdownMenuContent side="right" class="w-55">
<div class="favorites-group-menu"> <DropdownMenuItem @click="handleRemoteRename(group)">
<button
type="button"
class="favorites-group-menu__item"
@click="handleRemoteRename(group)">
<span>{{ t('view.favorite.rename_tooltip') }}</span> <span>{{ t('view.favorite.rename_tooltip') }}</span>
</button> </DropdownMenuItem>
<DropdownMenuSub> <DropdownMenuSub>
<DropdownMenuSubTrigger <DropdownMenuSubTrigger>
class="favorites-group-menu__item favorites-group-menu__item--submenu">
<span>{{ t('view.favorite.visibility_tooltip') }}</span> <span>{{ t('view.favorite.visibility_tooltip') }}</span>
</DropdownMenuSubTrigger> </DropdownMenuSubTrigger>
<DropdownMenuPortal> <DropdownMenuPortal>
<DropdownMenuSubContent <DropdownMenuSubContent
side="right" side="right"
align="start" align="start"
class="w-[180px] p-1 rounded-lg"> class="w-[180px]">
<div class="group-visibility-menu"> <DropdownMenuCheckboxItem
<button
v-for="visibility in friendGroupVisibilityOptions" v-for="visibility in friendGroupVisibilityOptions"
:key="visibility" :key="visibility"
type="button" :model-value="group.visibility === visibility"
:class="[ indicator-position="right"
'group-visibility-menu__item', @select="handleVisibilitySelection(group, visibility)">
{
'is-active':
group.visibility === visibility
}
]"
@click="
handleVisibilitySelection(group, visibility)
">
<span>{{ formatVisibility(visibility) }}</span> <span>{{ formatVisibility(visibility) }}</span>
<span </DropdownMenuCheckboxItem>
v-if="group.visibility === visibility"
class="group-visibility-menu__check">
<Check class="h-3 w-3" />
</span>
</button>
</div>
</DropdownMenuSubContent> </DropdownMenuSubContent>
</DropdownMenuPortal> </DropdownMenuPortal>
</DropdownMenuSub> </DropdownMenuSub>
<button <DropdownMenuItem
type="button" variant="destructive"
class="favorites-group-menu__item favorites-group-menu__item--danger"
@click="handleRemoteClear(group)"> @click="handleRemoteClear(group)">
<span>{{ t('view.favorite.clear') }}</span> <span>{{ t('view.favorite.clear') }}</span>
</button> </DropdownMenuItem>
</div>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
@@ -320,6 +298,7 @@
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuPortal, DropdownMenuPortal,
@@ -993,73 +972,6 @@
padding: 12px 0; padding: 12px 0;
} }
.favorites-group-menu {
display: flex;
flex-direction: column;
gap: 4px;
}
.favorites-group-menu__item {
display: flex;
align-items: center;
justify-content: space-between;
border: none;
background: transparent;
border-radius: 8px;
padding: 6px 12px;
font-size: 13px;
cursor: pointer;
color: inherit;
transition: background-color 0.15s ease;
min-height: 32px;
align-self: stretch;
}
.favorites-group-menu__item:hover {
}
.favorites-group-menu__item--danger {
}
.favorites-group-menu__item--submenu {
padding-right: 8px;
}
.favorites-group-menu__arrow {
margin-left: auto;
font-size: 12px;
}
.group-visibility-menu {
display: flex;
flex-direction: column;
gap: 4px;
}
.group-visibility-menu__item {
display: flex;
align-items: center;
justify-content: space-between;
border: none;
background: transparent;
padding: 6px 10px;
border-radius: 8px;
cursor: pointer;
color: inherit;
font-size: 13px;
transition: background-color 0.15s ease;
min-height: 32px;
align-self: stretch;
}
.group-visibility-menu__item:hover,
.group-visibility-menu__item.is-active {
}
.group-visibility-menu__check {
font-size: 12px;
}
.favorites-content { .favorites-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
+18 -115
View File
@@ -129,54 +129,30 @@
<MoreHorizontal /> <MoreHorizontal />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent side="right" class="w-50 p-1 rounded-lg"> <DropdownMenuContent side="right" class="w-50">
<div class="favorites-group-menu"> <DropdownMenuItem @click="handleRemoteRename(group)">
<button
type="button"
class="favorites-group-menu__item"
@click="handleRemoteRename(group)">
<span>{{ t('view.favorite.rename_tooltip') }}</span> <span>{{ t('view.favorite.rename_tooltip') }}</span>
</button> </DropdownMenuItem>
<DropdownMenuSub> <DropdownMenuSub>
<DropdownMenuSubTrigger <DropdownMenuSubTrigger>
class="favorites-group-menu__item favorites-group-menu__item--submenu">
<span>{{ t('view.favorite.visibility_tooltip') }}</span> <span>{{ t('view.favorite.visibility_tooltip') }}</span>
</DropdownMenuSubTrigger> </DropdownMenuSubTrigger>
<DropdownMenuPortal> <DropdownMenuPortal>
<DropdownMenuSubContent <DropdownMenuSubContent side="right" align="start" class="w-[200px]">
side="right" <DropdownMenuCheckboxItem
align="start"
class="w-[200px] p-1 rounded-lg">
<div class="group-visibility-menu">
<button
v-for="visibility in worldGroupVisibilityOptions" v-for="visibility in worldGroupVisibilityOptions"
:key="visibility" :key="visibility"
type="button" :model-value="group.visibility === visibility"
class="group-visibility-menu__item" indicator-position="right"
:class="{ @select="handleVisibilitySelection(group, visibility)">
'is-active': group.visibility === visibility
}"
@click="
handleVisibilitySelection(group, visibility)
">
<span>{{ formatVisibility(visibility) }}</span> <span>{{ formatVisibility(visibility) }}</span>
<span </DropdownMenuCheckboxItem>
v-if="group.visibility === visibility"
class="group-visibility-menu__check"
></span
>
</button>
</div>
</DropdownMenuSubContent> </DropdownMenuSubContent>
</DropdownMenuPortal> </DropdownMenuPortal>
</DropdownMenuSub> </DropdownMenuSub>
<button <DropdownMenuItem variant="destructive" @click="handleRemoteClear(group)">
type="button"
class="favorites-group-menu__item favorites-group-menu__item--danger"
@click="handleRemoteClear(group)">
<span>{{ t('view.favorite.clear') }}</span> <span>{{ t('view.favorite.clear') }}</span>
</button> </DropdownMenuItem>
</div>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
@@ -247,21 +223,13 @@
><Ellipsis ><Ellipsis
/></Button> /></Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent side="right" class="w-50 p-1 rounded-lg"> <DropdownMenuContent side="right" class="w-50">
<div class="favorites-group-menu"> <DropdownMenuItem @click="handleLocalRename(group)">
<button
type="button"
class="favorites-group-menu__item"
@click="handleLocalRename(group)">
<span>{{ t('view.favorite.rename_tooltip') }}</span> <span>{{ t('view.favorite.rename_tooltip') }}</span>
</button> </DropdownMenuItem>
<button <DropdownMenuItem variant="destructive" @click="handleLocalDelete(group)">
type="button"
class="favorites-group-menu__item favorites-group-menu__item--danger"
@click="handleLocalDelete(group)">
<span>{{ t('view.favorite.delete_tooltip') }}</span> <span>{{ t('view.favorite.delete_tooltip') }}</span>
</button> </DropdownMenuItem>
</div>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
@@ -448,6 +416,7 @@
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuPortal, DropdownMenuPortal,
@@ -1496,72 +1465,6 @@
padding: 12px 0; padding: 12px 0;
} }
.favorites-group-menu {
display: flex;
flex-direction: column;
gap: 4px;
}
.favorites-group-menu__item {
display: flex;
align-items: center;
justify-content: space-between;
border: none;
background: transparent;
border-radius: 8px;
padding: 6px 12px;
font-size: 13px;
cursor: pointer;
color: inherit;
transition: background-color 0.15s ease;
min-height: 32px;
align-self: stretch;
}
.favorites-group-menu__item:hover {
}
.favorites-group-menu__item--danger {
}
.favorites-group-menu__item--submenu {
padding-right: 8px;
}
.favorites-group-menu__arrow {
margin-left: auto;
font-size: 12px;
}
.group-visibility-menu {
display: flex;
flex-direction: column;
gap: 4px;
}
.group-visibility-menu__item {
display: flex;
align-items: center;
justify-content: space-between;
border: none;
background: transparent;
padding: 6px 10px;
border-radius: 8px;
cursor: pointer;
color: inherit;
font-size: 13px;
transition: background-color 0.15s ease;
min-height: 32px;
align-self: stretch;
}
.group-visibility-menu__item:hover,
.group-visibility-menu__item.is-active {
}
.group-visibility-menu__check {
font-size: 12px;
}
.favorites-content { .favorites-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -42,7 +42,7 @@ export const createColumns = ({ onShowAvatar, onShowUser, onDelete, onShowFullsc
const original = row.original; const original = row.original;
return ( return (
<span <span
class="x-link" class=" cursor-pointer"
title={original?.name} title={original?.name}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -63,7 +63,7 @@ export const createColumns = ({ onShowAvatar, onShowUser, onDelete, onShowFullsc
const original = row.original; const original = row.original;
return ( return (
<span <span
class="x-link" class=" cursor-pointer"
title={original?.authorName} title={original?.authorName}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -42,7 +42,7 @@ export const createColumns = ({ userImage, userImageFull, onShowFullscreenImage,
const original = row.original; const original = row.original;
return ( return (
<span <span
class="x-link" class="cursor-pointer"
title={original?.displayName} title={original?.displayName}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -42,7 +42,7 @@ export const createColumns = ({ onShowWorld, onShowUser, onDelete, onShowFullscr
const original = row.original; const original = row.original;
return ( return (
<span <span
class="x-link" class="cursor-pointer"
title={original?.name} title={original?.name}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -63,7 +63,7 @@ export const createColumns = ({ onShowWorld, onShowUser, onDelete, onShowFullscr
const original = row.original; const original = row.original;
return ( return (
<span <span
class="x-link" class="cursor-pointer"
title={original?.authorName} title={original?.authorName}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
+3 -3
View File
@@ -92,7 +92,7 @@ const expandedRow = ({ row }) => {
src={ src={
original.previousCurrentAvatarThumbnailImageUrl original.previousCurrentAvatarThumbnailImageUrl
} }
class="x-link h-30 w-40 rounded pointer" class="cursor-pointer h-30 w-40 rounded pointer"
loading="lazy" loading="lazy"
onClick={() => onClick={() =>
showFullscreenImageDialog( showFullscreenImageDialog(
@@ -125,7 +125,7 @@ const expandedRow = ({ row }) => {
src={ src={
original.currentAvatarThumbnailImageUrl original.currentAvatarThumbnailImageUrl
} }
class="x-link h-30 w-40 rounded pointer" class="cursor-pointer h-30 w-40 rounded pointer"
loading="lazy" loading="lazy"
onClick={() => onClick={() =>
showFullscreenImageDialog( showFullscreenImageDialog(
@@ -280,7 +280,7 @@ export const columns = [
const original = row.original; const original = row.original;
return ( return (
<span <span
class="x-link pr-2.5 cursor-pointer" class="cursor-pointer pr-2.5 cursor-pointer"
onClick={() => showUserDialog(original.userId)} onClick={() => showUserDialog(original.userId)}
> >
{original.displayName} {original.displayName}
+2 -2
View File
@@ -89,7 +89,7 @@ export const createColumns = ({ onDelete, onDeletePrompt }) => {
const displayName = const displayName =
original.displayName || original.userId || ''; original.displayName || original.userId || '';
return ( return (
<span class="block w-full whitespace-normal break-words"> <span class="block w-full whitespace-normal wrap-break-word cursor-pointer">
{original.type === 'DisplayName' ? ( {original.type === 'DisplayName' ? (
<span class="mr-1"> <span class="mr-1">
{original.previousDisplayName} {original.previousDisplayName}
@@ -97,7 +97,7 @@ export const createColumns = ({ onDelete, onDeletePrompt }) => {
</span> </span>
) : null} ) : null}
<span <span
class="x-link pr-2.5" class="cursor-pointer pr-2.5"
onClick={() => showUserDialog(original.userId)} onClick={() => showUserDialog(original.userId)}
> >
{displayName} {displayName}
+38 -17
View File
@@ -10,27 +10,33 @@
</Tabs> </Tabs>
<div class="friend-view__actions"> <div class="friend-view__actions">
<InputGroupSearch v-model="searchTerm" class="friend-view__search" placeholder="Search Friend" /> <InputGroupSearch v-model="searchTerm" class="friend-view__search" placeholder="Search Friend" />
<TooltipWrapper :content="t('view.charts.instance_activity.settings.header')" side="top">
<div>
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<div>
<TooltipWrapper :content="t('view.charts.instance_activity.settings.header')" side="top">
<Button class="rounded-full mr-2" size="icon" variant="outline"> <Button class="rounded-full mr-2" size="icon" variant="outline">
<Settings /> <Settings />
</Button> </Button>
</TooltipWrapper>
</div>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent side="bottom" class="w-87.5"> <PopoverContent side="bottom" class="w-87.5">
<div style="display: flex; justify-content: space-between; align-items: center"> <div class="friend-view__settings">
<span class="friend-view__settings-label">{{ <Field orientation="horizontal" class="friend-view__settings-row">
<FieldLabel class="friend-view__settings-label">{{
t('view.friends_locations.separate_same_instance_friends') t('view.friends_locations.separate_same_instance_friends')
}}</span> }}</FieldLabel>
<FieldContent>
<Switch v-model="showSameInstance" /> <Switch v-model="showSameInstance" />
</div> </FieldContent>
<div class="friend-view__settings-row"> </Field>
<span class="friend-view__settings-label">{{ t('view.friends_locations.scale') }}</span> <Field orientation="horizontal" class="friend-view__settings-row">
<FieldLabel class="friend-view__settings-label">
{{ t('view.friends_locations.scale') }}
</FieldLabel>
<FieldContent>
<div class="friend-view__scale-control"> <div class="friend-view__scale-control">
<span class="friend-view__scale-value">{{ cardScalePercentLabel }}&nbsp;</span> <span class="friend-view__scale-value"
>{{ cardScalePercentLabel }}&nbsp;</span
>
<Slider <Slider
v-model="cardScaleValue" v-model="cardScaleValue"
class="friend-view__slider" class="friend-view__slider"
@@ -38,11 +44,17 @@
:max="1.0" :max="1.0"
:step="0.01" /> :step="0.01" />
</div> </div>
</div> </FieldContent>
<div class="friend-view__settings-row"> </Field>
<span class="friend-view__settings-label">{{ t('view.friends_locations.spacing') }}</span> <Field orientation="horizontal" class="friend-view__settings-row">
<FieldLabel class="friend-view__settings-label">
{{ t('view.friends_locations.spacing') }}
</FieldLabel>
<FieldContent>
<div class="friend-view__scale-control"> <div class="friend-view__scale-control">
<span class="friend-view__scale-value">{{ cardSpacingPercentLabel }}&nbsp;</span> <span class="friend-view__scale-value"
>{{ cardSpacingPercentLabel }}&nbsp;</span
>
<Slider <Slider
v-model="cardSpacingValue" v-model="cardSpacingValue"
class="friend-view__slider" class="friend-view__slider"
@@ -50,10 +62,14 @@
:max="1.0" :max="1.0"
:step="0.05" /> :step="0.05" />
</div> </div>
</FieldContent>
</Field>
</div> </div>
</PopoverContent> </PopoverContent>
</Popover> </Popover>
</div> </div>
</TooltipWrapper>
</div>
</div> </div>
<div v-else class="friend-view__toolbar friend-view__toolbar--loading"> <div v-else class="friend-view__toolbar friend-view__toolbar--loading">
<span class="friend-view__loading-text">{{ t('view.friends_locations.loading_more') }}</span> <span class="friend-view__loading-text">{{ t('view.friends_locations.loading_more') }}</span>
@@ -64,7 +80,7 @@
<template v-if="row.type === 'header'"> <template v-if="row.type === 'header'">
<header class="friend-view__instance-header"> <header class="friend-view__instance-header">
<Location class="text-xs" :location="row.instanceId" style="display: inline" /> <Location class="text-xs" :location="row.instanceId" style="display: inline" />
<span class="friend-view__instance-count">{{ row.count }}</span> <span class="friend-view__instance-count">({{ row.count }})</span>
</header> </header>
</template> </template>
@@ -95,6 +111,7 @@
<script setup> <script setup>
import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'; import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
import { Field, FieldContent, FieldLabel } from '@/components/ui/field';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Loader2, Settings } from 'lucide-vue-next'; import { Loader2, Settings } from 'lucide-vue-next';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@@ -714,6 +731,11 @@
font-weight: 500; font-weight: 500;
} }
.friend-view__settings {
display: grid;
gap: 12px;
}
.friend-view__loading-text { .friend-view__loading-text {
padding-right: 12px; padding-right: 12px;
} }
@@ -828,7 +850,6 @@
.friend-view__instance-header { .friend-view__instance-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
padding: 4px 2px; padding: 4px 2px;
margin: 5px 10px; margin: 5px 10px;
font-weight: 600; font-weight: 600;
+8 -8
View File
@@ -91,7 +91,7 @@ export const createColumns = ({ getCreatedAt, onDelete, onDeletePrompt }) => {
return ( return (
<Badge variant="outline" class="text-muted-foreground"> <Badge variant="outline" class="text-muted-foreground">
<span <span
class={isLink ? 'x-link' : undefined} class={isLink ? 'cursor-pointer' : undefined}
onClick={() => onClick={() =>
isLink && showWorldDialog(original.location) isLink && showWorldDialog(original.location)
} }
@@ -111,10 +111,10 @@ export const createColumns = ({ getCreatedAt, onDelete, onDeletePrompt }) => {
const isFriend = original.isFriend; const isFriend = original.isFriend;
const isFavorite = original.isFavorite; const isFavorite = original.isFavorite;
return ( return (
<span> <span class="cursor-pointer">
{original.displayName ? ( {original.displayName ? (
<span <span
class="x-link table-user mr-1" class="cursor-pointer table-user mr-1"
onClick={() => lookupUser(original)} onClick={() => lookupUser(original)}
> >
{original.displayName} {original.displayName}
@@ -183,13 +183,13 @@ export const createColumns = ({ getCreatedAt, onDelete, onDeletePrompt }) => {
original.videoId !== 'PopcornPalace'; original.videoId !== 'PopcornPalace';
const label = original.videoName || original.videoUrl; const label = original.videoName || original.videoUrl;
return ( return (
<span class="block w-full min-w-0 truncate"> <span class="block w-full min-w-0 truncate cursor-pointer">
{original.videoId ? ( {original.videoId ? (
<span class="mr-1.5">{original.videoId}:</span> <span class="mr-1.5">{original.videoId}:</span>
) : null} ) : null}
{showLink ? ( {showLink ? (
<span <span
class="x-link" class="cursor-pointer"
onClick={() => onClick={() =>
openExternalLink(original.videoUrl) openExternalLink(original.videoUrl)
} }
@@ -208,9 +208,9 @@ export const createColumns = ({ getCreatedAt, onDelete, onDeletePrompt }) => {
original.type === 'StringLoad' original.type === 'StringLoad'
) { ) {
return ( return (
<span class="block w-full min-w-0 truncate"> <span class="block w-full min-w-0 truncate cursor-pointer">
<span <span
class="x-link" class="cursor-pointer"
onClick={() => onClick={() =>
openExternalLink(original.resourceUrl) openExternalLink(original.resourceUrl)
} }
@@ -230,7 +230,7 @@ export const createColumns = ({ getCreatedAt, onDelete, onDeletePrompt }) => {
} }
return ( return (
<span class="x-link block w-full min-w-0 truncate"> <span class="block w-full min-w-0 truncate">
{original.data} {original.data}
</span> </span>
); );
+9 -2
View File
@@ -18,7 +18,10 @@
<ResizablePanelGroup <ResizablePanelGroup
ref="panelGroupRef" ref="panelGroupRef"
direction="horizontal" direction="horizontal"
class="group/main-layout flex-1 h-full min-w-0" :class="[
'group/main-layout flex-1 h-full min-w-0',
{ 'aside-collapsed': isAsideCollapsedStatic }
]"
@layout="handleLayout"> @layout="handleLayout">
<template #default="{ layout }"> <template #default="{ layout }">
<ResizablePanel :default-size="mainDefaultSize" :order="1"> <ResizablePanel :default-size="mainDefaultSize" :order="1">
@@ -126,7 +129,7 @@
const router = useRouter(); const router = useRouter();
const appearanceSettingsStore = useAppearanceSettingsStore(); const appearanceSettingsStore = useAppearanceSettingsStore();
const { navWidth, isNavCollapsed } = storeToRefs(appearanceSettingsStore); const { navWidth, isNavCollapsed, asideWidth } = storeToRefs(appearanceSettingsStore);
const sidebarOpen = computed(() => !isNavCollapsed.value); const sidebarOpen = computed(() => !isNavCollapsed.value);
@@ -184,6 +187,10 @@
isSideBarTabShow isSideBarTabShow
} = useAuthenticatedLayoutResizable(); } = useAuthenticatedLayoutResizable();
const isAsideCollapsedStatic = computed(
() => !isSideBarTabShow.value || asideWidth.value === 0
);
watch( watch(
() => watchState.isLoggedIn, () => watchState.isLoggedIn,
(isLoggedIn) => { (isLoggedIn) => {
+6 -3
View File
@@ -158,14 +158,17 @@
<div class="x-legal-notice-container"> <div class="x-legal-notice-container">
<div style="text-align: center; font-size: 12px"> <div style="text-align: center; font-size: 12px">
<p> <p>
<a class="x-link" @click="openExternalLink('https://vrchat.com/home/password')">{{ <a class="cursor-pointer" @click="openExternalLink('https://vrchat.com/home/password')">{{
t('view.login.forgotPassword') t('view.login.forgotPassword')
}}</a> }}</a>
</p> </p>
<p> <p>
&copy; 2019-2026 &copy; 2019-2026
<a class="x-link" @click="openExternalLink('https://github.com/pypy-vrc')">pypy</a> &amp; <a class="cursor-pointer" @click="openExternalLink('https://github.com/pypy-vrc')">pypy</a>
<a class="x-link" @click="openExternalLink('https://github.com/Natsumi-sama')">Natsumi</a> &amp;
<a class="cursor-pointer" @click="openExternalLink('https://github.com/Natsumi-sama')"
>Natsumi</a
>
</p> </p>
<p>{{ t('view.settings.general.legal_notice.info') }}</p> <p>{{ t('view.settings.general.legal_notice.info') }}</p>
<p>{{ t('view.settings.general.legal_notice.disclaimer1') }}</p> <p>{{ t('view.settings.general.legal_notice.disclaimer1') }}</p>
+2 -2
View File
@@ -87,7 +87,7 @@ export const createColumns = ({ onDelete, onDeletePrompt }) => {
const original = row.original; const original = row.original;
return ( return (
<span <span
class="x-link block w-full min-w-0 truncate pr-2.5" class="cursor-pointer block w-full min-w-0 truncate pr-2.5 cursor-pointer"
onClick={() => showUserDialog(original.sourceUserId)} onClick={() => showUserDialog(original.sourceUserId)}
> >
{original.sourceDisplayName} {original.sourceDisplayName}
@@ -107,7 +107,7 @@ export const createColumns = ({ onDelete, onDeletePrompt }) => {
const original = row.original; const original = row.original;
return ( return (
<span <span
class="x-link block w-full whitespace-normal break-words pr-2.5" class="cursor-pointer block w-full whitespace-normal wrap-break-word pr-2.5 cursor-pointer"
onClick={() => showUserDialog(original.targetUserId)} onClick={() => showUserDialog(original.targetUserId)}
> >
{original.targetDisplayName} {original.targetDisplayName}
+10 -10
View File
@@ -167,7 +167,7 @@ export const createColumns = ({
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<span <span
class="x-link" class="cursor-pointer"
onClick={() => onClick={() =>
showWorldDialog( showWorldDialog(
original.location original.location
@@ -200,7 +200,7 @@ export const createColumns = ({
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<span <span
class="x-link" class="cursor-pointer"
onClick={() => onClick={() =>
openNotificationLink( openNotificationLink(
original.link original.link
@@ -242,7 +242,7 @@ export const createColumns = ({
return ( return (
<span class="table-user-text block w-full min-w-0 truncate"> <span class="table-user-text block w-full min-w-0 truncate">
<span <span
class="x-link block w-full min-w-0 truncate" class="cursor-pointer block w-full min-w-0 truncate"
onClick={() => onClick={() =>
showUserDialog(original.senderUserId) showUserDialog(original.senderUserId)
} }
@@ -257,7 +257,7 @@ export const createColumns = ({
return ( return (
<span class="table-user-text block w-full min-w-0 truncate"> <span class="table-user-text block w-full min-w-0 truncate">
<span <span
class="x-link block w-full min-w-0 truncate" class="cursor-pointer block w-full min-w-0 truncate"
onClick={() => onClick={() =>
openNotificationLink(original.link) openNotificationLink(original.link)
} }
@@ -306,7 +306,7 @@ export const createColumns = ({
return ( return (
<span class="table-user-text block w-full min-w-0 truncate"> <span class="table-user-text block w-full min-w-0 truncate">
<span <span
class="x-link block w-full min-w-0 truncate" class="cursor-pointer block w-full min-w-0 truncate"
onClick={() => onClick={() =>
showGroupDialog(original.senderUserId) showGroupDialog(original.senderUserId)
} }
@@ -332,7 +332,7 @@ export const createColumns = ({
return ( return (
<span class="table-user-text block w-full min-w-0 truncate"> <span class="table-user-text block w-full min-w-0 truncate">
<span <span
class="x-link block w-full min-w-0 truncate" class="cursor-pointer block w-full min-w-0 truncate"
onClick={() => onClick={() =>
openNotificationLink(original.link) openNotificationLink(original.link)
} }
@@ -347,7 +347,7 @@ export const createColumns = ({
return ( return (
<span class="table-user-text block w-full min-w-0 truncate"> <span class="table-user-text block w-full min-w-0 truncate">
<span <span
class="x-link block w-full min-w-0 truncate" class="cursor-pointer block w-full min-w-0 truncate"
onClick={() => onClick={() =>
openNotificationLink(original.link) openNotificationLink(original.link)
} }
@@ -400,7 +400,7 @@ export const createColumns = ({
} }
return ( return (
<Emoji <Emoji
class="x-link h-7.5 w-7.5 rounded object-cover" class="cursor-pointer h-7.5 w-7.5 rounded object-cover"
onClick={() => showFullscreenImageDialog(imageUrl)} onClick={() => showFullscreenImageDialog(imageUrl)}
imageUrl={imageUrl} imageUrl={imageUrl}
size={30} size={30}
@@ -411,7 +411,7 @@ export const createColumns = ({
if (original.details?.imageUrl) { if (original.details?.imageUrl) {
return ( return (
<img <img
class="x-link h-7.5 w-7.5 rounded object-cover" class="cursor-pointer h-7.5 w-7.5 rounded object-cover"
src={getSmallThumbnailUrl( src={getSmallThumbnailUrl(
original.details.imageUrl original.details.imageUrl
)} )}
@@ -428,7 +428,7 @@ export const createColumns = ({
if (original.imageUrl) { if (original.imageUrl) {
return ( return (
<img <img
class="x-link h-7.5 w-7.5 rounded object-cover" class="cursor-pointer h-7.5 w-7.5 rounded object-cover"
src={getSmallThumbnailUrl(original.imageUrl)} src={getSmallThumbnailUrl(original.imageUrl)}
onClick={() => onClick={() =>
showFullscreenImageDialog(original.imageUrl) showFullscreenImageDialog(original.imageUrl)
+4 -3
View File
@@ -8,14 +8,14 @@
class="mb-7"> class="mb-7">
<img <img
:src="currentInstanceWorld.ref.thumbnailImageUrl" :src="currentInstanceWorld.ref.thumbnailImageUrl"
class="x-link" class="cursor-pointer"
style="flex: none; width: 160px; height: 120px; border-radius: 4px" style="flex: none; width: 160px; height: 120px; border-radius: 4px"
@click="showFullscreenImageDialog(currentInstanceWorld.ref.imageUrl)" @click="showFullscreenImageDialog(currentInstanceWorld.ref.imageUrl)"
loading="lazy" /> loading="lazy" />
<div style="margin-left: 10px; display: flex; flex-direction: column; min-width: 320px; width: 100%"> <div style="margin-left: 10px; display: flex; flex-direction: column; min-width: 320px; width: 100%">
<div> <div>
<span <span
class="x-link" class="cursor-pointer"
style=" style="
font-weight: bold; font-weight: bold;
overflow: hidden; overflow: hidden;
@@ -36,7 +36,7 @@
</div> </div>
<div> <div>
<span <span
class="x-link x-grey" class="cursor-pointer x-grey"
style="font-family: monospace" style="font-family: monospace"
@click="showUserDialog(currentInstanceWorld.ref.authorId)" @click="showUserDialog(currentInstanceWorld.ref.authorId)"
v-text="currentInstanceWorld.ref.authorName"></span> v-text="currentInstanceWorld.ref.authorName"></span>
@@ -268,6 +268,7 @@
persistKey: 'playerList', persistKey: 'playerList',
data: currentInstanceUsersData, data: currentInstanceUsersData,
columns: playerListColumns, columns: playerListColumns,
enablePagination: false,
getRowId: (row) => `${row?.ref?.id ?? ''}:${row?.displayName ?? ''}` getRowId: (row) => `${row?.ref?.id ?? ''}:${row?.displayName ?? ''}`
}); });
@@ -35,7 +35,7 @@ function DetailCell({ row, isPrevious, onShowAvatar, onShowGroup, onShowWorld, o
return ( return (
<> <>
<span <span
class="x-link" class="cursor-pointer"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
onShowAvatar?.(r.avatar?.id); onShowAvatar?.(r.avatar?.id);
@@ -116,7 +116,7 @@ function DetailCell({ row, isPrevious, onShowAvatar, onShowGroup, onShowWorld, o
return ( return (
<> <>
<span <span
class="x-link" class="cursor-pointer"
style="margin-right: 5px" style="margin-right: 5px"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -129,7 +129,7 @@ function DetailCell({ row, isPrevious, onShowAvatar, onShowGroup, onShowWorld, o
<ArrowRight /> <ArrowRight />
</span> </span>
<span <span
class="x-link" class="cursor-pointer"
style="margin-left: 5px" style="margin-left: 5px"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -145,7 +145,7 @@ function DetailCell({ row, isPrevious, onShowAvatar, onShowGroup, onShowWorld, o
if (r.type === 'PortalSpawn') { if (r.type === 'PortalSpawn') {
return ( return (
<span <span
class="x-link" class="cursor-pointer"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
onShowWorld?.(r.location, r.shortName); onShowWorld?.(r.location, r.shortName);
@@ -176,7 +176,7 @@ function DetailCell({ row, isPrevious, onShowAvatar, onShowGroup, onShowWorld, o
<span>Android&nbsp;</span> <span>Android&nbsp;</span>
) : null} ) : null}
<span <span
class="x-link" class="cursor-pointer"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
onShowAvatar?.(r.avatar?.id); onShowAvatar?.(r.avatar?.id);
@@ -233,7 +233,14 @@ function DetailCell({ row, isPrevious, onShowAvatar, onShowGroup, onShowWorld, o
return <span>{r.text}</span>; return <span>{r.text}</span>;
} }
export const createColumns = ({ isPrevious, onShowUser, onShowAvatar, onShowGroup, onShowWorld, onShowImage }) => [ export const createColumns = ({
isPrevious,
onShowUser,
onShowAvatar,
onShowGroup,
onShowWorld,
onShowImage
}) => [
{ {
id: 'created_at', id: 'created_at',
accessorFn: (row) => (row?.created_at ? Date.parse(row.created_at) : 0), accessorFn: (row) => (row?.created_at ? Date.parse(row.created_at) : 0),
@@ -250,7 +257,9 @@ export const createColumns = ({ isPrevious, onShowUser, onShowAvatar, onShowGrou
) )
}} }}
> >
<span>{formatDateFilter(row.original?.created_at, 'short')}</span> <span>
{formatDateFilter(row.original?.created_at, 'short')}
</span>
</TooltipWrapper> </TooltipWrapper>
) )
}, },
@@ -261,7 +270,7 @@ export const createColumns = ({ isPrevious, onShowUser, onShowAvatar, onShowGrou
enableSorting: false, enableSorting: false,
cell: ({ row }) => ( cell: ({ row }) => (
<span <span
class="x-link" class="cursor-pointer"
style="padding-right: 10px" style="padding-right: 10px"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@@ -283,8 +283,8 @@
<div class="options-container-item" style="display: block"> <div class="options-container-item" style="display: block">
<p> <p>
&copy; 2019-2026 &copy; 2019-2026
<a class="x-link" @click="openExternalLink('https://github.com/pypy-vrc')">pypy</a> &amp; <a class="cursor-pointer" @click="openExternalLink('https://github.com/pypy-vrc')">pypy</a> &amp;
<a class="x-link" @click="openExternalLink('https://github.com/Natsumi-sama')">Natsumi</a> <a class="cursor-pointer" @click="openExternalLink('https://github.com/Natsumi-sama')">Natsumi</a>
</p> </p>
<p>{{ t('view.settings.general.legal_notice.info') }}</p> <p>{{ t('view.settings.general.legal_notice.info') }}</p>
<p>{{ t('view.settings.general.legal_notice.disclaimer1') }}</p> <p>{{ t('view.settings.general.legal_notice.disclaimer1') }}</p>
@@ -8,8 +8,9 @@
<h2 v-text="changeLogDialog.buildName"></h2> <h2 v-text="changeLogDialog.buildName"></h2>
<span v-show="changeLogDialog.buildName"> <span v-show="changeLogDialog.buildName">
{{ t('dialog.change_log.description') }} {{ t('dialog.change_log.description') }}
<a class="x-link" @click="openExternalLink('https://www.patreon.com/Natsumi_VRCX')">Patreon</a>, <a class="cursor-pointer" @click="openExternalLink('https://www.patreon.com/Natsumi_VRCX')"
<a class="x-link" @click="openExternalLink('https://ko-fi.com/natsumi_sama')">Ko-fi</a>. >Patreon</a
>, <a class="cursor-pointer" @click="openExternalLink('https://ko-fi.com/natsumi_sama')">Ko-fi</a>.
</span> </span>
<VueShowdown <VueShowdown
:markdown="changeLogDialog.changeLog" :markdown="changeLogDialog.changeLog"
+1
View File
@@ -190,6 +190,7 @@
order: 99; order: 99;
height: 100%; height: 100%;
box-sizing: border-box; box-sizing: border-box;
padding-left: 10px;
} }
.sidebar-tab-count { .sidebar-tab-count {
@@ -1,7 +1,7 @@
<template> <template>
<div class="x-friend-list" style="padding: 10px 5px"> <div class="x-friend-list" style="padding: 10px 5px">
<div <div
class="x-friend-group x-link flex items-center" class="x-friend-group cursor-pointer flex items-center"
style="padding: 0 0 5px" style="padding: 0 0 5px"
@click=" @click="
isFriendsGroupMe = !isFriendsGroupMe; isFriendsGroupMe = !isFriendsGroupMe;
@@ -38,7 +38,7 @@
</div> </div>
<div <div
v-show="vipFriendsDisplayNumber" v-show="vipFriendsDisplayNumber"
class="x-friend-group x-link flex items-center" class="x-friend-group cursor-pointer flex items-center"
@click=" @click="
isVIPFriends = !isVIPFriends; isVIPFriends = !isVIPFriends;
saveFriendsGroupStates(); saveFriendsGroupStates();
@@ -78,7 +78,7 @@
</div> </div>
<template v-if="isSidebarGroupByInstance && friendsInSameInstance.length"> <template v-if="isSidebarGroupByInstance && friendsInSameInstance.length">
<div class="x-friend-group x-link flex items-center" @click="toggleSwitchGroupByInstanceCollapsed"> <div class="x-friend-group cursor-pointer flex items-center" @click="toggleSwitchGroupByInstanceCollapsed">
<ChevronDown class="rotation-transition" :class="{ 'is-rotated': isSidebarGroupByInstanceCollapsed }" /> <ChevronDown class="rotation-transition" :class="{ 'is-rotated': isSidebarGroupByInstanceCollapsed }" />
<span style="margin-left: 5px" <span style="margin-left: 5px"
>{{ t('side_panel.same_instance') }} &horbar; {{ friendsInSameInstance.length }}</span >{{ t('side_panel.same_instance') }} &horbar; {{ friendsInSameInstance.length }}</span
@@ -109,7 +109,7 @@
</template> </template>
<div <div
v-show="onlineFriendsByGroupStatus.length" v-show="onlineFriendsByGroupStatus.length"
class="x-friend-group x-link flex items-center" class="x-friend-group cursor-pointer flex items-center"
@click=" @click="
isOnlineFriends = !isOnlineFriends; isOnlineFriends = !isOnlineFriends;
saveFriendsGroupStates(); saveFriendsGroupStates();
@@ -128,7 +128,7 @@
</div> </div>
<div <div
v-show="activeFriends.length" v-show="activeFriends.length"
class="x-friend-group x-link flex items-center" class="x-friend-group cursor-pointer flex items-center"
@click=" @click="
isActiveFriends = !isActiveFriends; isActiveFriends = !isActiveFriends;
saveFriendsGroupStates(); saveFriendsGroupStates();
@@ -145,7 +145,7 @@
</div> </div>
<div <div
v-show="offlineFriends.length" v-show="offlineFriends.length"
class="x-friend-group x-link flex items-center" class="x-friend-group cursor-pointer flex items-center"
@click=" @click="
isOfflineFriends = !isOfflineFriends; isOfflineFriends = !isOfflineFriends;
saveFriendsGroupStates(); saveFriendsGroupStates();
@@ -310,9 +310,6 @@
</script> </script>
<style scoped> <style scoped>
.x-link:hover {
text-decoration: none;
}
.is-rotated { .is-rotated {
transform: rotate(-90deg); transform: rotate(-90deg);
} }
@@ -1,7 +1,7 @@
<template> <template>
<div class="x-friend-list" style="padding: 10px 5px"> <div class="x-friend-list" style="padding: 10px 5px">
<template v-for="(group, index) in groupedGroupInstances" :key="getGroupId(group)"> <template v-for="(group, index) in groupedGroupInstances" :key="getGroupId(group)">
<div class="x-friend-group x-link" :style="{ paddingTop: index === 0 ? '0px' : '10px' }"> <div class="x-friend-group cursor-pointer" :style="{ paddingTop: index === 0 ? '0px' : '10px' }">
<div @click="toggleGroupSidebarCollapse(getGroupId(group))" style="display: flex; align-items: center"> <div @click="toggleGroupSidebarCollapse(getGroupId(group))" style="display: flex; align-items: center">
<ChevronDown <ChevronDown
class="rotation-transition" class="rotation-transition"
@@ -93,12 +93,6 @@
</script> </script>
<style scoped> <style scoped>
.x-link:hover {
text-decoration: none;
}
.x-link:hover span {
text-decoration: underline;
}
.is-rotated { .is-rotated {
transform: rotate(-90deg); transform: rotate(-90deg);
} }
+5 -12
View File
@@ -7,9 +7,7 @@
<span class="header">{{ t('dialog.screenshot_metadata.header') }}</span> <span class="header">{{ t('dialog.screenshot_metadata.header') }}</span>
</div> </div>
<div @dragover.prevent @dragenter.prevent @drop="handleDrop"> <div @dragover.prevent @dragenter.prevent @drop="handleDrop">
<span>{{ <span>{{ t('dialog.screenshot_metadata.drag') }}</span>
t('dialog.screenshot_metadata.drag')
}}</span>
<br /> <br />
<br /> <br />
<Button size="sm" variant="outline" class="mr-2" @click="getAndDisplayScreenshotFromFile">{{ <Button size="sm" variant="outline" class="mr-2" @click="getAndDisplayScreenshotFromFile">{{
@@ -100,8 +98,7 @@
<DisplayName <DisplayName
v-if="screenshotMetadataDialog.metadata.author" v-if="screenshotMetadataDialog.metadata.author"
:userid="screenshotMetadataDialog.metadata.author.id" :userid="screenshotMetadataDialog.metadata.author.id"
:hint="screenshotMetadataDialog.metadata.author.displayName" :hint="screenshotMetadataDialog.metadata.author.displayName" />
/>
<br /> <br />
<div class="my-2 w-[90%] ml-17"> <div class="my-2 w-[90%] ml-17">
<Carousel :opts="{ loop: false }" @init-api="handleScreenshotMetadataCarouselInit"> <Carousel :opts="{ loop: false }" @init-api="handleScreenshotMetadataCarouselInit">
@@ -109,7 +106,6 @@
<CarouselItem> <CarouselItem>
<div class="h-150 w-full"> <div class="h-150 w-full">
<img <img
class="x-link"
:src="screenshotMetadataDialog.metadata.previousFilePath" :src="screenshotMetadataDialog.metadata.previousFilePath"
style="width: 100%; height: 100%; object-fit: contain" /> style="width: 100%; height: 100%; object-fit: contain" />
</div> </div>
@@ -117,7 +113,7 @@
<CarouselItem> <CarouselItem>
<div class="h-150 w-full"> <div class="h-150 w-full">
<img <img
class="x-link" class="cursor-pointer"
:src="screenshotMetadataDialog.metadata.filePath" :src="screenshotMetadataDialog.metadata.filePath"
style="width: 100%; height: 100%; object-fit: contain" style="width: 100%; height: 100%; object-fit: contain"
@click="showFullscreenImageDialog(screenshotMetadataDialog.metadata.filePath)" /> @click="showFullscreenImageDialog(screenshotMetadataDialog.metadata.filePath)" />
@@ -126,7 +122,6 @@
<CarouselItem> <CarouselItem>
<div class="h-150 w-full"> <div class="h-150 w-full">
<img <img
class="x-link"
:src="screenshotMetadataDialog.metadata.nextFilePath" :src="screenshotMetadataDialog.metadata.nextFilePath"
style="width: 100%; height: 100%; object-fit: contain" /> style="width: 100%; height: 100%; object-fit: contain" />
</div> </div>
@@ -143,10 +138,8 @@
<br /> <br />
</template> </template>
<span v-for="user in screenshotMetadataDialog.metadata.players" :key="user.id" style="margin-top: 5px"> <span v-for="user in screenshotMetadataDialog.metadata.players" :key="user.id" style="margin-top: 5px">
<span class="x-link" @click="lookupUser(user)" v-text="user.displayName"></span> <span class="cursor-pointer" @click="lookupUser(user)" v-text="user.displayName"></span>
<span <span v-if="user.pos" v-text="'(' + user.pos.x + ', ' + user.pos.y + ', ' + user.pos.z + ')'"></span>
v-if="user.pos"
v-text="'(' + user.pos.x + ', ' + user.pos.y + ', ' + user.pos.z + ')'"></span>
<br /> <br />
</span> </span>
</div> </div>
@@ -41,7 +41,7 @@ export const createColumns = ({ userImage, userImageFull, onShowFullscreenImage,
const original = row.original; const original = row.original;
return ( return (
<span <span
class="x-link" class="cursor-pointer"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
onShowUser?.(original?.id); onShowUser?.(original?.id);