refactor css

This commit is contained in:
pa
2026-03-08 21:01:57 +09:00
parent c55d5f0ec7
commit 2b2dbc898e
5 changed files with 733 additions and 393 deletions

View File

@@ -344,11 +344,13 @@
:unmount-on-hide="false" :unmount-on-hide="false"
@update:modelValue="avatarDialogTabClick"> @update:modelValue="avatarDialogTabClick">
<template #Info> <template #Info>
<div class="x-friend-list" style="max-height: unset"> <div class="flex flex-wrap items-start px-2.5" style="max-height: unset">
<div <div
v-if="avatarDialog.galleryImages.length || avatarDialog.ref.authorId === currentUser.id" v-if="avatarDialog.galleryImages.length || avatarDialog.ref.authorId === currentUser.id"
style="width: 100%"> style="width: 100%">
<span class="name">{{ t('dialog.avatar.info.gallery') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.avatar.info.gallery')
}}</span>
<input <input
id="AvatarGalleryUploadButton" id="AvatarGalleryUploadButton"
type="file" type="file"
@@ -384,46 +386,56 @@
</div> </div>
</div> </div>
<div v-if="avatarDialog.ref.publishedListings?.length"> <div v-if="avatarDialog.ref.publishedListings?.length">
<span class="name">{{ t('dialog.avatar.info.listings') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.avatar.info.listings')
}}</span>
<div <div
v-for="listing in avatarDialog.ref.publishedListings" v-for="listing in avatarDialog.ref.publishedListings"
:key="listing.id" :key="listing.id"
class="x-friend-item" class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
style="width: 100%; cursor: default"> <div class="relative inline-block flex-none size-9 mr-2.5">
<div class="avatar">
<img <img
class="size-full rounded-full object-cover"
:src="getImageUrlFromImageId(listing.imageId)" :src="getImageUrlFromImageId(listing.imageId)"
@click="showFullscreenImageDialog(getImageUrlFromImageId(listing.imageId))" @click="showFullscreenImageDialog(getImageUrlFromImageId(listing.imageId))"
loading="lazy" /> loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ listing.displayName }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span class="extra" style="text-decoration: underline; font-style: italic" listing.displayName
}}</span>
<span
class="block truncate text-xs"
style="text-decoration: underline; font-style: italic"
>${{ commaNumber(listing.priceTokens) }}V</span >${{ commaNumber(listing.priceTokens) }}V</span
> >
<span <span
class="extra" class="block truncate text-xs"
style="text-overflow: ellipsis; text-wrap: auto" style="text-overflow: ellipsis; text-wrap: auto"
v-text="listing.description"></span> v-text="listing.description"></span>
</div> </div>
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" style="margin-bottom: 5px">{{ t('dialog.avatar.info.memo') }}</span> <span class="block truncate font-medium leading-[18px]" style="margin-bottom: 5px">{{
t('dialog.avatar.info.memo')
}}</span>
<InputGroupTextareaField <InputGroupTextareaField
v-model="memo" v-model="memo"
class="extra" class="text-xs"
:rows="2" :rows="2"
:placeholder="t('dialog.avatar.info.memo_placeholder')" :placeholder="t('dialog.avatar.info.memo_placeholder')"
input-class="resize-none min-h-0" input-class="resize-none min-h-0"
@change="onAvatarMemoChange" /> @change="onAvatarMemoChange" />
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.avatar.info.id') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span class="extra" t('dialog.avatar.info.id')
}}</span>
<span class="block truncate text-xs"
>{{ avatarDialog.id >{{ avatarDialog.id
}}<TooltipWrapper side="top" :content="t('dialog.avatar.info.id_tooltip')"> }}<TooltipWrapper side="top" :content="t('dialog.avatar.info.id_tooltip')">
<DropdownMenu> <DropdownMenu>
@@ -449,15 +461,19 @@
> >
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.avatar.info.created_at') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span class="extra">{{ formatDateFilter(avatarDialog.ref.created_at, 'long') }}</span> t('dialog.avatar.info.created_at')
}}</span>
<span class="block truncate text-xs">{{
formatDateFilter(avatarDialog.ref.created_at, 'long')
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" style="display: inline">{{ <span class="block font-medium leading-[18px]" style="display: inline">{{
t('dialog.avatar.info.last_updated') t('dialog.avatar.info.last_updated')
}}</span> }}</span>
<TooltipWrapper <TooltipWrapper
@@ -476,32 +492,45 @@
</template> </template>
<ChevronDown class="inline-block" /> <ChevronDown class="inline-block" />
</TooltipWrapper> </TooltipWrapper>
<span class="extra">{{ formatDateFilter(avatarDialog.ref.updated_at, 'long') }}</span> <span class="block truncate text-xs">{{
formatDateFilter(avatarDialog.ref.updated_at, 'long')
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.avatar.info.version') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.avatar.info.version')
}}</span>
<span <span
v-if="avatarDialog.ref.version !== 0" v-if="avatarDialog.ref.version !== 0"
class="extra" class="block truncate text-xs"
v-text="avatarDialog.ref.version"></span> v-text="avatarDialog.ref.version"></span>
<span v-else class="extra">-</span> <span v-else class="block truncate text-xs">-</span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.avatar.info.time_spent') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.avatar.info.time_spent')
}}</span>
<span v-if="avatarDialog.timeSpent === 0" class="extra">-</span> <span v-if="avatarDialog.timeSpent === 0" class="block truncate text-xs">-</span>
<span v-else class="extra">{{ timeToText(avatarDialog.timeSpent) }}</span> <span v-else class="block truncate text-xs">{{
timeToText(avatarDialog.timeSpent)
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.avatar.info.platform') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span v-if="avatarDialogPlatform" class="extra" v-text="avatarDialogPlatform"></span> t('dialog.avatar.info.platform')
<span v-else class="extra">-</span> }}</span>
<span
v-if="avatarDialogPlatform"
class="block truncate text-xs"
v-text="avatarDialogPlatform"></span>
<span v-else class="block truncate text-xs">-</span>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -73,7 +73,7 @@
:key="avatar.id" :key="avatar.id"
:class="[ :class="[
'item-width', 'item-width',
'box-border flex items-center p-1.5 text-[13px] cursor-pointer border border-border rounded' 'box-border flex items-center p-1.5 text-[13px] cursor-pointer hover:rounded-[25px_5px_5px_25px]'
]" ]"
@click="showAvatarDialog(avatar.id)"> @click="showAvatarDialog(avatar.id)">
<div class="relative inline-block flex-none size-9 mr-2.5"> <div class="relative inline-block flex-none size-9 mr-2.5">
@@ -154,6 +154,9 @@
} }
); );
/**
*
*/
function closeSetAvatarTagsDialog() { function closeSetAvatarTagsDialog() {
emit('update:setAvatarTagsDialog', { emit('update:setAvatarTagsDialog', {
...props.setAvatarTagsDialog, ...props.setAvatarTagsDialog,
@@ -161,6 +164,9 @@
}); });
} }
/**
*
*/
function updateSelectedAvatarTags() { function updateSelectedAvatarTags() {
const D = props.setAvatarTagsDialog; const D = props.setAvatarTagsDialog;
if (D.contentHorror) { if (D.contentHorror) {
@@ -202,6 +208,11 @@
D.selectedTagsCsv = D.selectedTags.join(',').replace(/content_/g, ''); D.selectedTagsCsv = D.selectedTags.join(',').replace(/content_/g, '');
} }
/**
*
* @param avatarId
* @param checked
*/
function toggleAvatarSelection(avatarId, checked) { function toggleAvatarSelection(avatarId, checked) {
const D = props.setAvatarTagsDialog; const D = props.setAvatarTagsDialog;
const isSelected = D.selectedAvatarIds.includes(avatarId); const isSelected = D.selectedAvatarIds.includes(avatarId);
@@ -213,6 +224,9 @@
} }
} }
/**
*
*/
function updateAvatarTagsString() { function updateAvatarTagsString() {
const D = props.setAvatarTagsDialog; const D = props.setAvatarTagsDialog;
for (const ref of D.ownAvatars) { for (const ref of D.ownAvatars) {
@@ -238,6 +252,9 @@
} }
} }
/**
*
*/
function setAvatarTagsSelectToggle() { function setAvatarTagsSelectToggle() {
const D = props.setAvatarTagsDialog; const D = props.setAvatarTagsDialog;
const allSelected = D.ownAvatars.length === D.selectedAvatarIds.length; const allSelected = D.ownAvatars.length === D.selectedAvatarIds.length;
@@ -252,6 +269,9 @@
} }
} }
/**
*
*/
async function saveSetAvatarTagsDialog() { async function saveSetAvatarTagsDialog() {
const D = props.setAvatarTagsDialog; const D = props.setAvatarTagsDialog;
if (D.loading) { if (D.loading) {
@@ -287,6 +307,9 @@
} }
} }
/**
*
*/
function updateInputAvatarTags() { function updateInputAvatarTags() {
const D = props.setAvatarTagsDialog; const D = props.setAvatarTagsDialog;
D.contentHorror = false; D.contentHorror = false;

View File

@@ -353,7 +353,7 @@
@click="showFullscreenImageDialog(groupDialog.ref.bannerUrl)" @click="showFullscreenImageDialog(groupDialog.ref.bannerUrl)"
loading="lazy" /> loading="lazy" />
</div> </div>
<div class="x-friend-list" style="max-height: none"> <div class="flex flex-wrap items-start px-2.5" style="max-height: none">
<span <span
v-if="groupDialog.instances.length" v-if="groupDialog.instances.length"
style="font-size: 12px; font-weight: bold; margin: 5px"> style="font-size: 12px; font-weight: bold; margin: 5px">
@@ -373,35 +373,42 @@
</div> </div>
<div <div
v-if="room.users.length" v-if="room.users.length"
class="x-friend-list" class="flex flex-wrap items-start"
style="margin: 10px 0; padding: 0; max-height: unset"> style="margin: 10px 0; padding: 0; max-height: unset">
<div <div
v-for="user in room.users" v-for="user in room.users"
:key="user.id" :key="user.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showUserDialog(user.id)"> @click="showUserDialog(user.id)">
<div class="avatar" :class="userStatusClass(user)"> <div
<img :src="userImage(user)" loading="lazy" /> class="relative inline-block flex-none size-9 mr-2.5"
:class="userStatusClass(user)">
<img
class="size-full rounded-full object-cover"
:src="userImage(user)"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span <span
class="name" class="block truncate font-medium leading-[18px]"
:style="{ color: user.$userColour }" :style="{ color: user.$userColour }"
v-text="user.displayName" /> v-text="user.displayName" />
<span v-if="user.location === 'traveling'" class="extra"> <span v-if="user.location === 'traveling'" class="block truncate text-xs">
<Spinner class="inline-block mr-1" /> <Spinner class="inline-block mr-1" />
<Timer :epoch="user.$travelingToTime" /> <Timer :epoch="user.$travelingToTime" />
</span> </span>
<span v-else class="extra"> <span v-else class="block truncate text-xs">
<Timer :epoch="user.$location_at" /> <Timer :epoch="user.$location_at" />
</span> </span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.group.info.announcement') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.group.info.announcement')
}}</span>
<span style="display: block" v-text="groupDialog.announcement.title" /> <span style="display: block" v-text="groupDialog.announcement.title" />
<div <div
v-if="groupDialog.announcement.imageUrl" v-if="groupDialog.announcement.imageUrl"
@@ -420,7 +427,7 @@
loading="lazy" /> loading="lazy" />
</div> </div>
<pre <pre
class="extra" class="text-xs"
style=" style="
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
@@ -434,7 +441,7 @@
<br /> <br />
<div <div
v-if="groupDialog.announcement.id" v-if="groupDialog.announcement.id"
class="extra" class="text-xs"
style="float: right; margin-left: 5px"> style="float: right; margin-left: 5px">
<TooltipWrapper v-if="groupDialog.announcement.roleIds.length" side="top"> <TooltipWrapper v-if="groupDialog.announcement.roleIds.length" side="top">
<template #content> <template #content>
@@ -505,11 +512,13 @@
</div> </div>
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.group.info.rules') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.group.info.rules')
}}</span>
<pre <pre
class="extra" class="text-xs"
style=" style="
font-family: inherit; font-family: inherit;
font-size: 12px; font-size: 12px;
@@ -520,9 +529,11 @@
> >
</div> </div>
</div> </div>
<div class="x-friend-item x-friend-item-no-hover" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail" style="overflow: visible"> <div class="flex-1" style="overflow: visible">
<span class="name">{{ t('dialog.group.info.upcoming_events') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.group.info.upcoming_events')
}}</span>
<template v-if="upcomingCalenderEvents.length > 0"> <template v-if="upcomingCalenderEvents.length > 0">
<br /> <br />
<div class="grid-view events-row"> <div class="grid-view events-row">
@@ -536,12 +547,14 @@
card-class="group-dialog-grid-card" /> card-class="group-dialog-grid-card" />
</div> </div>
</template> </template>
<span v-else class="extra">-</span> <span v-else class="block truncate text-xs">-</span>
</div> </div>
</div> </div>
<div class="x-friend-item x-friend-item-no-hover" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail" style="overflow: visible"> <div class="flex-1" style="overflow: visible">
<span class="name">{{ t('dialog.group.info.past_events') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.group.info.past_events')
}}</span>
<template v-if="pastCalenderEvents.length > 0"> <template v-if="pastCalenderEvents.length > 0">
<br /> <br />
<div class="grid-view events-row"> <div class="grid-view events-row">
@@ -555,27 +568,35 @@
card-class="group-dialog-grid-card" /> card-class="group-dialog-grid-card" />
</div> </div>
</template> </template>
<span v-else class="extra">-</span> <span v-else class="block truncate text-xs">-</span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.group.info.members') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<div class="extra"> t('dialog.group.info.members')
}}</span>
<div class="block truncate text-xs">
{{ groupDialog.ref.memberCount }} ({{ groupDialog.ref.onlineMemberCount }}) {{ groupDialog.ref.memberCount }} ({{ groupDialog.ref.onlineMemberCount }})
</div> </div>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.group.info.created_at') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span class="extra">{{ formatDateFilter(groupDialog.ref.createdAt, 'long') }}</span> t('dialog.group.info.created_at')
}}</span>
<span class="block truncate text-xs">{{
formatDateFilter(groupDialog.ref.createdAt, 'long')
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" @click="showPreviousInstancesListDialog(groupDialog.ref)"> <div
<div class="detail"> class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px]"
@click="showPreviousInstancesListDialog(groupDialog.ref)">
<div class="flex-1 overflow-hidden">
<div <div
class="name" class="block truncate font-medium leading-[18px]"
style="display: flex; justify-content: space-between; align-items: center"> style="display: flex; justify-content: space-between; align-items: center">
<span> <span>
{{ t('dialog.group.info.last_visited') }} {{ t('dialog.group.info.last_visited') }}
@@ -584,12 +605,16 @@
<MoreHorizontal style="margin-right: 16px" /> <MoreHorizontal style="margin-right: 16px" />
</TooltipWrapper> </TooltipWrapper>
</div> </div>
<span class="extra">{{ formatDateFilter(groupDialog.lastVisit, 'long') }}</span> <span class="block truncate text-xs">{{
formatDateFilter(groupDialog.lastVisit, 'long')
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.group.info.links') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.group.info.links')
}}</span>
<div <div
v-if="groupDialog.ref.links && groupDialog.ref.links.length > 0" v-if="groupDialog.ref.links && groupDialog.ref.links.length > 0"
style="margin-top: 5px" style="margin-top: 5px"
@@ -613,14 +638,16 @@
</TooltipWrapper> </TooltipWrapper>
</template> </template>
</div> </div>
<div v-else class="extra">-</div> <div v-else class="block truncate text-xs">-</div>
</div> </div>
</div> </div>
<div class="inline-flex justify-between w-full"> <div class="inline-flex justify-between w-full">
<div class="x-friend-item" style="cursor: default; width: 50%"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-1/2">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.group.info.url') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span class="extra" t('dialog.group.info.url')
}}</span>
<span class="block truncate text-xs"
>{{ groupDialog.ref.$url }} >{{ groupDialog.ref.$url }}
<TooltipWrapper side="top" :content="t('dialog.group.info.url_tooltip')"> <TooltipWrapper side="top" :content="t('dialog.group.info.url_tooltip')">
<Button <Button
@@ -633,10 +660,12 @@
></span> ></span>
</div> </div>
</div> </div>
<div class="x-friend-item w-1/2" style="cursor: default; width: 50%"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-1/2">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.group.info.id') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span class="extra" t('dialog.group.info.id')
}}</span>
<span class="block truncate text-xs"
>{{ groupDialog.id }} >{{ groupDialog.id }}
<TooltipWrapper side="top" :content="t('dialog.group.info.id_tooltip')"> <TooltipWrapper side="top" :content="t('dialog.group.info.id_tooltip')">
<Button <Button
@@ -654,19 +683,27 @@
v-if="groupDialog.ref.membershipStatus === 'member'" v-if="groupDialog.ref.membershipStatus === 'member'"
style="width: 100%; margin-top: 10px; border-top: 1px solid #e4e7ed14"> style="width: 100%; margin-top: 10px; border-top: 1px solid #e4e7ed14">
<div style="width: 100%; display: flex; margin-top: 10px"> <div style="width: 100%; display: flex; margin-top: 10px">
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.group.info.joined_at') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span class="extra">{{ t('dialog.group.info.joined_at')
}}</span>
<span class="block truncate text-xs">{{
formatDateFilter(groupDialog.ref.myMember.joinedAt, 'long') formatDateFilter(groupDialog.ref.myMember.joinedAt, 'long')
}}</span> }}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.group.info.roles') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span v-if="groupDialog.memberRoles.length === 0" class="extra"> - </span> t('dialog.group.info.roles')
<span v-else class="extra"> }}</span>
<span
v-if="groupDialog.memberRoles.length === 0"
class="block truncate text-xs">
-
</span>
<span v-else class="block truncate text-xs">
<template v-for="(role, rIndex) in groupDialog.memberRoles" :key="rIndex"> <template v-for="(role, rIndex) in groupDialog.memberRoles" :key="rIndex">
<TooltipWrapper side="top"> <TooltipWrapper side="top">
<template #content> <template #content>
@@ -722,13 +759,12 @@
:placeholder="t('dialog.group.posts.search_placeholder')" :placeholder="t('dialog.group.posts.search_placeholder')"
style="width: 89%; margin-bottom: 10px" style="width: 89%; margin-bottom: 10px"
@input="updateGroupPostSearch" /> @input="updateGroupPostSearch" />
<div class="x-friend-list"> <div class="flex flex-wrap items-start">
<div <div
v-for="post in groupDialog.postsFiltered" v-for="post in groupDialog.postsFiltered"
:key="post.id" :key="post.id"
class="x-friend-item" class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
style="width: 100%; cursor: default"> <div class="flex-1 overflow-hidden">
<div class="detail">
<span style="display: block" v-text="post.title" /> <span style="display: block" v-text="post.title" />
<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
@@ -745,7 +781,7 @@
loading="lazy" /> loading="lazy" />
</div> </div>
<pre <pre
class="extra" class="text-xs"
style=" style="
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
@@ -757,7 +793,7 @@
>{{ post.text || '-' }}</pre >{{ post.text || '-' }}</pre
> >
<br /> <br />
<div v-if="post.authorId" class="extra" style="float: right; margin-left: 5px"> <div v-if="post.authorId" class="text-xs" style="float: right; margin-left: 5px">
<TooltipWrapper v-if="post.roleIds.length" side="top"> <TooltipWrapper v-if="post.roleIds.length" side="top">
<template #content> <template #content>
<span>{{ t('dialog.group.posts.visibility') }}</span> <span>{{ t('dialog.group.posts.visibility') }}</span>
@@ -911,22 +947,25 @@
</div> </div>
<div <div
v-if="groupDialog.memberSearch.length" v-if="groupDialog.memberSearch.length"
class="x-friend-list" class="flex flex-wrap items-start"
style="margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px"> style="margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px">
<div <div
v-for="user in groupDialog.memberSearchResults" v-for="user in groupDialog.memberSearchResults"
:key="user.id" :key="user.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showUserDialog(user.userId)"> @click="showUserDialog(user.userId)">
<div class="avatar"> <div class="relative inline-block flex-none size-9 mr-2.5">
<img :src="userImage(user.user)" loading="lazy" /> <img
class="size-full rounded-full object-cover"
:src="userImage(user.user)"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span <span
class="name" class="block truncate font-medium leading-[18px]"
:style="{ color: user.user?.$userColour }" :style="{ color: user.user?.$userColour }"
v-text="user.user?.displayName" /> v-text="user.user?.displayName" />
<span class="extra"> <span class="block truncate text-xs">
<template v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')"> <template v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')">
<TooltipWrapper <TooltipWrapper
v-if="user.isRepresenting" v-if="user.isRepresenting"
@@ -971,22 +1010,25 @@
</div> </div>
<ul <ul
v-else-if="groupDialog.members.length > 0" v-else-if="groupDialog.members.length > 0"
class="infinite-list x-friend-list" class="infinite-list flex flex-wrap items-start"
style="margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px"> style="margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px">
<li <li
v-for="user in groupDialog.members" v-for="user in groupDialog.members"
:key="user.id" :key="user.id"
class="infinite-list-item x-friend-item x-friend-item-border" class="infinite-list-item box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showUserDialog(user.userId)"> @click="showUserDialog(user.userId)">
<div class="avatar"> <div class="relative inline-block flex-none size-9 mr-2.5">
<img :src="userImage(user.user)" loading="lazy" /> <img
class="size-full rounded-full object-cover"
:src="userImage(user.user)"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span <span
class="name" class="block truncate font-medium leading-[18px]"
:style="{ color: user.user?.$userColour }" :style="{ color: user.user?.$userColour }"
v-text="user.user?.displayName" /> v-text="user.user?.displayName" />
<span class="extra"> <span class="block truncate text-xs">
<template v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')"> <template v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')">
<TooltipWrapper <TooltipWrapper
v-if="user.isRepresenting" v-if="user.isRepresenting"
@@ -1030,11 +1072,13 @@
</li> </li>
<div <div
v-if="!isGroupMembersDone" v-if="!isGroupMembersDone"
class="x-friend-item" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer"
style="width: 100%; height: 45px; text-align: center" style="width: 100%; height: 45px; text-align: center"
@click="loadMoreGroupMembers"> @click="loadMoreGroupMembers">
<div v-if="!isGroupMembersLoading" class="detail"> <div v-if="!isGroupMembersLoading" class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.group.members.load_more') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.group.members.load_more')
}}</span>
</div> </div>
</div> </div>
</ul> </ul>
@@ -1373,6 +1417,11 @@
} }
); );
/**
*
* @param groupId
* @param userId
*/
function showInviteGroupDialog(groupId, userId) { function showInviteGroupDialog(groupId, userId) {
if (groupId) { if (groupId) {
inviteGroupDialog.value.groupId = groupId; inviteGroupDialog.value.groupId = groupId;
@@ -1383,17 +1432,32 @@
inviteGroupDialog.value.visible = true; inviteGroupDialog.value.visible = true;
} }
/**
*
* @param groupRef
*/
function showPreviousInstancesListDialog(groupRef) { function showPreviousInstancesListDialog(groupRef) {
instanceStore.showPreviousInstancesListDialog('group', groupRef); instanceStore.showPreviousInstancesListDialog('group', groupRef);
} }
/**
*
* @param groupId
*/
function setGroupRepresentation(groupId) { function setGroupRepresentation(groupId) {
handleGroupRepresentationChange(groupId, true); handleGroupRepresentationChange(groupId, true);
} }
/**
*
* @param groupId
*/
function clearGroupRepresentation(groupId) { function clearGroupRepresentation(groupId) {
handleGroupRepresentationChange(groupId, false); handleGroupRepresentationChange(groupId, false);
} }
/**
*
*/
function groupMembersSearch() { function groupMembersSearch() {
if (groupDialog.value.memberSearch.length < 3) { if (groupDialog.value.memberSearch.length < 3) {
groupDialog.value.memberSearchResults = []; groupDialog.value.memberSearchResults = [];
@@ -1403,6 +1467,9 @@
debounce(groupMembersSearchDebounced, 200)(); debounce(groupMembersSearchDebounced, 200)();
} }
/**
*
*/
function groupMembersSearchDebounced() { function groupMembersSearchDebounced() {
const D = groupDialog.value; const D = groupDialog.value;
const search = D.memberSearch; const search = D.memberSearch;
@@ -1436,6 +1503,11 @@
}); });
} }
/**
*
* @param groupId
* @param isSet
*/
function handleGroupRepresentationChange(groupId, isSet) { function handleGroupRepresentationChange(groupId, isSet) {
groupRequest groupRequest
.setGroupRepresentation(groupId, { .setGroupRepresentation(groupId, {
@@ -1452,6 +1524,10 @@
}); });
} }
/**
*
* @param id
*/
function cancelGroupRequest(id) { function cancelGroupRequest(id) {
groupRequest groupRequest
.cancelGroupRequest({ .cancelGroupRequest({
@@ -1463,6 +1539,10 @@
} }
}); });
} }
/**
*
* @param post
*/
function confirmDeleteGroupPost(post) { function confirmDeleteGroupPost(post) {
modalStore modalStore
.confirm({ .confirm({
@@ -1504,6 +1584,10 @@
.catch(() => {}); .catch(() => {});
} }
/**
*
* @param gallery
*/
function groupGalleryStatus(gallery) { function groupGalleryStatus(gallery) {
const style = {}; const style = {};
if (!gallery.membersOnly) { if (!gallery.membersOnly) {
@@ -1516,6 +1600,10 @@
return style; return style;
} }
/**
*
* @param command
*/
function groupDialogCommand(command) { function groupDialogCommand(command) {
const D = groupDialog.value; const D = groupDialog.value;
if (D.visible === false) { if (D.visible === false) {
@@ -1565,6 +1653,10 @@
} }
} }
/**
*
* @param groupId
*/
function blockGroup(groupId) { function blockGroup(groupId) {
modalStore modalStore
.confirm({ .confirm({
@@ -1586,6 +1678,10 @@
.catch(() => {}); .catch(() => {});
} }
/**
*
* @param groupId
*/
function unblockGroup(groupId) { function unblockGroup(groupId) {
modalStore modalStore
.confirm({ .confirm({
@@ -1608,6 +1704,10 @@
.catch(() => {}); .catch(() => {});
} }
/**
*
* @param id
*/
function joinGroup(id) { function joinGroup(id) {
if (!id) { if (!id) {
return null; return null;
@@ -1634,6 +1734,10 @@
}); });
} }
/**
*
* @param tabName
*/
function handleGroupDialogTab(tabName) { function handleGroupDialogTab(tabName) {
groupDialog.value.lastActiveTab = tabName; groupDialog.value.lastActiveTab = tabName;
if (tabName === 'Members') { if (tabName === 'Members') {
@@ -1645,10 +1749,17 @@
} }
} }
/**
*
*/
function loadLastActiveTab() { function loadLastActiveTab() {
handleGroupDialogTab(groupDialog.value.lastActiveTab); handleGroupDialogTab(groupDialog.value.lastActiveTab);
} }
/**
*
* @param tabName
*/
function groupDialogTabClick(tabName) { function groupDialogTabClick(tabName) {
if (tabName === groupDialogTabCurrentName.value) { if (tabName === groupDialogTabCurrentName.value) {
if (tabName === 'JSON') { if (tabName === 'JSON') {
@@ -1660,6 +1771,11 @@
groupDialogTabCurrentName.value = tabName; groupDialogTabCurrentName.value = tabName;
} }
/**
*
* @param groupId
* @param post
*/
function showGroupPostEditDialog(groupId, post) { function showGroupPostEditDialog(groupId, post) {
const D = groupPostEditDialog; const D = groupPostEditDialog;
D.sendNotification = true; D.sendNotification = true;
@@ -1692,6 +1808,9 @@
D.visible = true; D.visible = true;
} }
/**
*
*/
async function getGroupDialogGroupMembers() { async function getGroupDialogGroupMembers() {
const D = groupDialog.value; const D = groupDialog.value;
D.members = []; D.members = [];
@@ -1730,6 +1849,9 @@
await loadMoreGroupMembers(); await loadMoreGroupMembers();
} }
/**
*
*/
async function loadMoreGroupMembers() { async function loadMoreGroupMembers() {
if (isGroupMembersDone.value || isGroupMembersLoading.value) { if (isGroupMembersDone.value || isGroupMembersLoading.value) {
return; return;
@@ -1778,6 +1900,9 @@
}); });
} }
/**
*
*/
async function getGroupGalleries() { async function getGroupGalleries() {
updateGroupDialogData({ ...groupDialog.value, galleries: {} }); updateGroupDialogData({ ...groupDialog.value, galleries: {} });
groupDialogGalleryCurrentName.value = '0'; groupDialogGalleryCurrentName.value = '0';
@@ -1788,6 +1913,11 @@
isGroupGalleryLoading.value = false; isGroupGalleryLoading.value = false;
} }
/**
*
* @param groupId
* @param galleryId
*/
async function getGroupGallery(groupId, galleryId) { async function getGroupGallery(groupId, galleryId) {
try { try {
const params = { const params = {
@@ -1819,6 +1949,9 @@
} }
} }
/**
*
*/
function refreshGroupDialogTreeData() { function refreshGroupDialogTreeData() {
const D = groupDialog.value; const D = groupDialog.value;
treeData.value = { treeData.value = {
@@ -1830,6 +1963,9 @@
}; };
} }
/**
*
*/
async function loadAllGroupMembers() { async function loadAllGroupMembers() {
if (isGroupMembersLoading.value) { if (isGroupMembersLoading.value) {
return; return;
@@ -1845,6 +1981,10 @@
} }
} }
/**
*
* @param sortOrder
*/
async function setGroupMemberSortOrder(sortOrder) { async function setGroupMemberSortOrder(sortOrder) {
const D = groupDialog.value; const D = groupDialog.value;
if (D.memberSortOrder?.value === sortOrder?.value) { if (D.memberSortOrder?.value === sortOrder?.value) {
@@ -1854,6 +1994,10 @@
await getGroupDialogGroupMembers(); await getGroupDialogGroupMembers();
} }
/**
*
* @param filter
*/
async function setGroupMemberFilter(filter) { async function setGroupMemberFilter(filter) {
const D = groupDialog.value; const D = groupDialog.value;
if (D.memberFilter === filter) { if (D.memberFilter === filter) {
@@ -1863,6 +2007,10 @@
await getGroupDialogGroupMembers(); await getGroupDialogGroupMembers();
} }
/**
*
* @param obj
*/
function updateGroupDialogData(obj) { function updateGroupDialogData(obj) {
groupDialog.value = { groupDialog.value = {
...groupDialog.value, ...groupDialog.value,
@@ -1870,6 +2018,10 @@
}; };
} }
/**
*
* @param event
*/
function updateFollowingCalendarData(event) { function updateFollowingCalendarData(event) {
const calendar = groupDialog.value.calendar; const calendar = groupDialog.value.calendar;
for (let i = 0; i < calendar.length; i++) { for (let i = 0; i < calendar.length; i++) {

View File

@@ -46,21 +46,30 @@
:location="userDialog.ref.location" :location="userDialog.ref.location"
:traveling="userDialog.ref.travelingToLocation" /> :traveling="userDialog.ref.travelingToLocation" />
</div> </div>
<div class="x-friend-list" style="flex: 1; margin-top: 10px; max-height: 150px"> <div
class="flex flex-wrap items-start"
style="flex: 1; margin-top: 10px; max-height: 150px; overflow: auto">
<div <div
v-if="userDialog.$location.userId" v-if="userDialog.$location.userId"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showUserDialog(userDialog.$location.userId)"> @click="showUserDialog(userDialog.$location.userId)">
<template v-if="userDialog.$location.user"> <template v-if="userDialog.$location.user">
<div class="avatar" :class="userStatusClass(userDialog.$location.user)"> <div
<img :src="userImage(userDialog.$location.user, true)" loading="lazy" /> class="relative inline-block flex-none size-9 mr-2.5"
:class="userStatusClass(userDialog.$location.user)">
<img
class="size-full rounded-full object-cover"
:src="userImage(userDialog.$location.user, true)"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span <span
class="name" class="block truncate font-medium leading-[18px]"
:style="{ color: userDialog.$location.user.$userColour }" :style="{ color: userDialog.$location.user.$userColour }"
v-text="userDialog.$location.user.displayName"></span> v-text="userDialog.$location.user.displayName"></span>
<span class="extra">{{ t('dialog.user.info.instance_creator') }}</span> <span class="block truncate text-xs">{{
t('dialog.user.info.instance_creator')
}}</span>
</div> </div>
</template> </template>
<span v-else v-text="userDialog.$location.userId"></span> <span v-else v-text="userDialog.$location.userId"></span>
@@ -68,21 +77,26 @@
<div <div
v-for="user in userDialog.users" v-for="user in userDialog.users"
:key="user.id" :key="user.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showUserDialog(user.id)"> @click="showUserDialog(user.id)">
<div class="avatar" :class="userStatusClass(user)"> <div
<img :src="userImage(user, true)" loading="lazy" /> class="relative inline-block flex-none size-9 mr-2.5"
:class="userStatusClass(user)">
<img
class="size-full rounded-full object-cover"
:src="userImage(user, true)"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span <span
class="name" class="block truncate font-medium leading-[18px]"
:style="{ color: user.$userColour }" :style="{ color: user.$userColour }"
v-text="user.displayName"></span> v-text="user.displayName"></span>
<span v-if="user.location === 'traveling'" class="extra"> <span v-if="user.location === 'traveling'" class="block truncate text-xs">
<Spinner class="inline-block mr-1" /> <Spinner class="inline-block mr-1" />
<Timer :epoch="user.$travelingToTime" /> <Timer :epoch="user.$travelingToTime" />
</span> </span>
<span v-else class="extra"> <span v-else class="block truncate text-xs">
<Timer :epoch="user.$location_at" /> <Timer :epoch="user.$location_at" />
</span> </span>
</div> </div>
@@ -91,15 +105,16 @@
</div> </div>
</template> </template>
<div class="x-friend-list" style="max-height: none"> <div class="flex flex-wrap items-start px-2.5" style="max-height: none">
<div <div
v-if="userDialog.note && !hideUserNotes" v-if="userDialog.note && !hideUserNotes"
class="x-friend-item" class="box-border flex items-center p-1.5 text-[13px] w-full cursor-pointer">
style="width: 100%; cursor: pointer"> <div class="flex-1 overflow-hidden" @click="isEditNoteAndMemoDialogVisible = true">
<div class="detail" @click="isEditNoteAndMemoDialogVisible = true"> <span class="block truncate font-medium leading-[18px]">{{
<span class="name">{{ t('dialog.user.info.note') }}</span> t('dialog.user.info.note')
}}</span>
<pre <pre
class="extra" class="text-xs"
style=" style="
font-family: inherit; font-family: inherit;
font-size: 12px; font-size: 12px;
@@ -114,12 +129,13 @@
</div> </div>
<div <div
v-if="userDialog.memo && !hideUserMemos" v-if="userDialog.memo && !hideUserMemos"
class="x-friend-item" class="box-border flex items-center p-1.5 text-[13px] w-full cursor-pointer">
style="width: 100%; cursor: pointer"> <div class="flex-1 overflow-hidden" @click="isEditNoteAndMemoDialogVisible = true">
<div class="detail" @click="isEditNoteAndMemoDialogVisible = true"> <span class="block truncate font-medium leading-[18px]">{{
<span class="name">{{ t('dialog.user.info.memo') }}</span> t('dialog.user.info.memo')
}}</span>
<pre <pre
class="extra" class="text-xs"
style=" style="
font-family: inherit; font-family: inherit;
font-size: 12px; font-size: 12px;
@@ -132,9 +148,9 @@
> >
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ {{
userDialog.id !== currentUser.id && userDialog.id !== currentUser.id &&
userDialog.ref.profilePicOverride && userDialog.ref.profilePicOverride &&
@@ -149,7 +165,7 @@
<Info class="inline-block" /> <Info class="inline-block" />
</TooltipWrapper> </TooltipWrapper>
</span> </span>
<div class="extra"> <div class="text-xs">
<AvatarInfo <AvatarInfo
:key="userDialog.id" :key="userDialog.id"
:imageurl="userDialog.ref.currentAvatarImageUrl" :imageurl="userDialog.ref.currentAvatarImageUrl"
@@ -159,9 +175,9 @@
</div> </div>
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" style="margin-bottom: 5px">{{ <span class="block truncate font-medium leading-[18px]" style="margin-bottom: 5px">{{
t('dialog.user.info.represented_group') t('dialog.user.info.represented_group')
}}</span> }}</span>
<div <div
@@ -169,7 +185,7 @@
userDialog.isRepresentedGroupLoading || userDialog.isRepresentedGroupLoading ||
(userDialog.representedGroup && userDialog.representedGroup.isRepresenting) (userDialog.representedGroup && userDialog.representedGroup.isRepresenting)
" "
class="extra"> class="text-xs">
<div style="display: inline-block; flex: none; margin-right: 5px"> <div style="display: inline-block; flex: none; margin-right: 5px">
<Avatar <Avatar
class="cursor-pointer size-15! rounded-lg!" class="cursor-pointer size-15! rounded-lg!"
@@ -197,14 +213,16 @@
<span>({{ userDialog.representedGroup.memberCount }})</span> <span>({{ userDialog.representedGroup.memberCount }})</span>
</span> </span>
</div> </div>
<div v-else class="extra">-</div> <div v-else class="text-xs">-</div>
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.user.info.bio') }}</span> <span class="block truncate font-medium leading-[18px]">{{
t('dialog.user.info.bio')
}}</span>
<pre <pre
class="extra truncate" class="text-xs truncate"
style=" style="
font-family: inherit; font-family: inherit;
font-size: 12px; font-size: 12px;
@@ -257,19 +275,23 @@
</div> </div>
</div> </div>
<template v-if="currentUser.id !== userDialog.id"> <template v-if="currentUser.id !== userDialog.id">
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.user.info.last_seen') }} {{ t('dialog.user.info.last_seen') }}
</span> </span>
<span class="extra">{{ formatDateFilter(userDialog.lastSeen, 'long') }}</span> <span class="block truncate text-xs">{{
formatDateFilter(userDialog.lastSeen, 'long')
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" @click="showPreviousInstancesListDialog(userDialog.ref)"> <div
<div class="detail"> class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px]"
@click="showPreviousInstancesListDialog(userDialog.ref)">
<div class="flex-1 overflow-hidden">
<div <div
class="name" class="block truncate font-medium leading-[18px]"
style="display: flex; justify-content: space-between; align-items: center"> style="display: flex; justify-content: space-between; align-items: center">
<div> <div>
{{ t('dialog.user.info.join_count') }} {{ t('dialog.user.info.join_count') }}
@@ -279,18 +301,20 @@
<MoreHorizontal style="margin-right: 16px" /> <MoreHorizontal style="margin-right: 16px" />
</TooltipWrapper> </TooltipWrapper>
</div> </div>
<span v-if="userDialog.joinCount === 0" class="extra">-</span> <span v-if="userDialog.joinCount === 0" class="block truncate text-xs">-</span>
<span v-else class="extra" v-text="userDialog.joinCount"></span> <span v-else class="block truncate text-xs" v-text="userDialog.joinCount"></span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.user.info.time_together') }} {{ t('dialog.user.info.time_together') }}
</span> </span>
<span v-if="userDialog.timeSpent === 0" class="extra">-</span> <span v-if="userDialog.timeSpent === 0" class="block truncate text-xs">-</span>
<span v-else class="extra">{{ timeToText(userDialog.timeSpent) }}</span> <span v-else class="block truncate text-xs">{{
timeToText(userDialog.timeSpent)
}}</span>
</div> </div>
</div> </div>
</template> </template>
@@ -299,36 +323,40 @@
:disabled="currentUser.id !== userDialog.id" :disabled="currentUser.id !== userDialog.id"
side="top" side="top"
:content="t('dialog.user.info.open_previous_instance')"> :content="t('dialog.user.info.open_previous_instance')">
<div class="x-friend-item" @click="showPreviousInstancesListDialog(userDialog.ref)"> <div
<div class="detail"> class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px]"
<span class="name"> @click="showPreviousInstancesListDialog(userDialog.ref)">
<div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]">
{{ t('dialog.user.info.play_time') }} {{ t('dialog.user.info.play_time') }}
</span> </span>
<span v-if="userDialog.timeSpent === 0" class="extra">-</span> <span v-if="userDialog.timeSpent === 0" class="block truncate text-xs">-</span>
<span v-else class="extra">{{ timeToText(userDialog.timeSpent) }}</span> <span v-else class="block truncate text-xs">{{
timeToText(userDialog.timeSpent)
}}</span>
</div> </div>
</div> </div>
</TooltipWrapper> </TooltipWrapper>
</template> </template>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<TooltipWrapper :side="currentUser.id !== userDialog.id ? 'bottom' : 'top'"> <TooltipWrapper :side="currentUser.id !== userDialog.id ? 'bottom' : 'top'">
<template #content> <template #content>
<span>{{ formatDateFilter(userOnlineForTimestamp(userDialog), 'short') }}</span> <span>{{ formatDateFilter(userOnlineForTimestamp(userDialog), 'short') }}</span>
</template> </template>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span <span
v-if="userDialog.ref.state === 'online' && userDialog.ref.$online_for" v-if="userDialog.ref.state === 'online' && userDialog.ref.$online_for"
class="name"> class="block truncate font-medium leading-[18px]">
{{ t('dialog.user.info.online_for') }} {{ t('dialog.user.info.online_for') }}
</span> </span>
<span v-else class="name"> <span v-else class="block truncate font-medium leading-[18px]">
{{ t('dialog.user.info.offline_for') }} {{ t('dialog.user.info.offline_for') }}
</span> </span>
<span class="extra">{{ userOnlineFor(userDialog.ref) }}</span> <span class="block truncate text-xs">{{ userOnlineFor(userDialog.ref) }}</span>
</div> </div>
</TooltipWrapper> </TooltipWrapper>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<TooltipWrapper :side="currentUser.id !== userDialog.id ? 'bottom' : 'top'"> <TooltipWrapper :side="currentUser.id !== userDialog.id ? 'bottom' : 'top'">
<template #content> <template #content>
<span <span
@@ -341,22 +369,28 @@
{{ formatDateFilter(userDialog.ref.last_activity, 'long') }}</span {{ formatDateFilter(userDialog.ref.last_activity, 'long') }}</span
> >
</template> </template>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.user.info.last_activity') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span v-if="userDialog.ref.last_activity" class="extra">{{ t('dialog.user.info.last_activity')
}}</span>
<span v-if="userDialog.ref.last_activity" class="block truncate text-xs">{{
timeToText(Date.now() - Date.parse(userDialog.ref.last_activity)) timeToText(Date.now() - Date.parse(userDialog.ref.last_activity))
}}</span> }}</span>
<span v-else class="extra">-</span> <span v-else class="block truncate text-xs">-</span>
</div> </div>
</TooltipWrapper> </TooltipWrapper>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.user.info.date_joined') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span class="extra" v-text="userDialog.ref.date_joined"></span> t('dialog.user.info.date_joined')
}}</span>
<span class="block truncate text-xs" v-text="userDialog.ref.date_joined"></span>
</div> </div>
</div> </div>
<div v-if="currentUser.id !== userDialog.id" class="x-friend-item" style="cursor: default"> <div
v-if="currentUser.id !== userDialog.id"
class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<TooltipWrapper side="top" :disabled="userDialog.dateFriendedInfo.length < 2"> <TooltipWrapper side="top" :disabled="userDialog.dateFriendedInfo.length < 2">
<template #content> <template #content>
<template v-for="ref in userDialog.dateFriendedInfo" :key="ref.type"> <template v-for="ref in userDialog.dateFriendedInfo" :key="ref.type">
@@ -364,80 +398,118 @@
><br /> ><br />
</template> </template>
</template> </template>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span v-if="userDialog.unFriended" class="name"> <span v-if="userDialog.unFriended" class="block truncate font-medium leading-[18px]">
{{ t('dialog.user.info.unfriended') }} {{ t('dialog.user.info.unfriended') }}
</span> </span>
<span v-else class="name"> <span v-else class="block truncate font-medium leading-[18px]">
{{ t('dialog.user.info.friended') }} {{ t('dialog.user.info.friended') }}
</span> </span>
<span class="extra">{{ formatDateFilter(userDialog.dateFriended, 'long') }}</span> <span class="block truncate text-xs">{{
formatDateFilter(userDialog.dateFriended, 'long')
}}</span>
</div> </div>
</TooltipWrapper> </TooltipWrapper>
</div> </div>
<template v-if="currentUser.id === userDialog.id"> <template v-if="currentUser.id === userDialog.id">
<div class="x-friend-item" @click="toggleAvatarCopying"> <div
<div class="detail"> class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px]"
<span class="name">{{ t('dialog.user.info.avatar_cloning') }}</span> @click="toggleAvatarCopying">
<span v-if="currentUser.allowAvatarCopying" class="extra">{{ <div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]">{{
t('dialog.user.info.avatar_cloning')
}}</span>
<span v-if="currentUser.allowAvatarCopying" class="block truncate text-xs">{{
t('dialog.user.info.avatar_cloning_allow') t('dialog.user.info.avatar_cloning_allow')
}}</span> }}</span>
<span v-else class="extra">{{ t('dialog.user.info.avatar_cloning_deny') }}</span> <span v-else class="block truncate text-xs">{{
t('dialog.user.info.avatar_cloning_deny')
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" @click="toggleAllowBooping"> <div
<div class="detail"> class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px]"
<span class="name">{{ t('dialog.user.info.booping') }}</span> @click="toggleAllowBooping">
<span v-if="currentUser.isBoopingEnabled" class="extra">{{ <div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]">{{
t('dialog.user.info.booping')
}}</span>
<span v-if="currentUser.isBoopingEnabled" class="block truncate text-xs">{{
t('dialog.user.info.avatar_cloning_allow') t('dialog.user.info.avatar_cloning_allow')
}}</span> }}</span>
<span v-else class="extra">{{ t('dialog.user.info.avatar_cloning_deny') }}</span> <span v-else class="block truncate text-xs">{{
t('dialog.user.info.avatar_cloning_deny')
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" @click="toggleSharedConnectionsOptOut"> <div
<div class="detail"> class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px]"
<span class="name">{{ t('dialog.user.info.show_mutual_friends') }}</span> @click="toggleSharedConnectionsOptOut">
<span v-if="!currentUser.hasSharedConnectionsOptOut" class="extra">{{ <div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]">{{
t('dialog.user.info.show_mutual_friends')
}}</span>
<span v-if="!currentUser.hasSharedConnectionsOptOut" class="block truncate text-xs">{{
t('dialog.user.info.avatar_cloning_allow') t('dialog.user.info.avatar_cloning_allow')
}}</span> }}</span>
<span v-else class="extra">{{ t('dialog.user.info.avatar_cloning_deny') }}</span> <span v-else class="block truncate text-xs">{{
t('dialog.user.info.avatar_cloning_deny')
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" @click="toggleDiscordFriendsOptOut"> <div
<div class="detail"> class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px]"
<span class="name">{{ t('dialog.user.info.show_discord_connections') }}</span> @click="toggleDiscordFriendsOptOut">
<span v-if="!currentUser.hasDiscordFriendsOptOut" class="extra">{{ <div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]">{{
t('dialog.user.info.show_discord_connections')
}}</span>
<span v-if="!currentUser.hasDiscordFriendsOptOut" class="block truncate text-xs">{{
t('dialog.user.info.avatar_cloning_allow') t('dialog.user.info.avatar_cloning_allow')
}}</span> }}</span>
<span v-else class="extra">{{ t('dialog.user.info.avatar_cloning_deny') }}</span> <span v-else class="block truncate text-xs">{{
t('dialog.user.info.avatar_cloning_deny')
}}</span>
</div> </div>
</div> </div>
</template> </template>
<template v-else> <template v-else>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.user.info.avatar_cloning') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span v-if="userDialog.ref.allowAvatarCopying" class="extra">{{ t('dialog.user.info.avatar_cloning')
}}</span>
<span v-if="userDialog.ref.allowAvatarCopying" class="block truncate text-xs">{{
t('dialog.user.info.avatar_cloning_allow') t('dialog.user.info.avatar_cloning_allow')
}}</span> }}</span>
<span v-else class="extra">{{ t('dialog.user.info.avatar_cloning_deny') }}</span> <span v-else class="block truncate text-xs">{{
t('dialog.user.info.avatar_cloning_deny')
}}</span>
</div> </div>
</div> </div>
</template> </template>
<div v-if="userDialog.ref.id === currentUser.id" class="x-friend-item" @click="getVRChatCredits()"> <div
<div class="detail"> v-if="userDialog.ref.id === currentUser.id"
<span class="name">{{ t('view.profile.profile.vrchat_credits') }}</span> class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px]"
<span class="extra">{{ vrchatCredit ?? t('view.profile.profile.refresh') }}</span> @click="getVRChatCredits()">
<div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]">{{
t('view.profile.profile.vrchat_credits')
}}</span>
<span class="block truncate text-xs">{{
vrchatCredit ?? t('view.profile.profile.refresh')
}}</span>
</div> </div>
</div> </div>
<div <div
v-if="userDialog.ref.id === currentUser.id && currentUser.homeLocation" v-if="userDialog.ref.id === currentUser.id && currentUser.homeLocation"
class="x-friend-item" class="box-border flex items-center p-1.5 text-[13px] w-full cursor-pointer"
style="width: 100%"
@click="showWorldDialog(currentUser.homeLocation)"> @click="showWorldDialog(currentUser.homeLocation)">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.user.info.home_location') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span class="extra"> t('dialog.user.info.home_location')
}}</span>
<span class="block truncate text-xs">
<span v-text="userDialog.$homeLocationName"></span> <span v-text="userDialog.$homeLocationName"></span>
<Button <Button
class="rounded-full ml-1 text-xs" class="rounded-full ml-1 text-xs"
@@ -449,10 +521,12 @@
</span> </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name">{{ t('dialog.user.info.id') }}</span> <span class="block truncate font-medium leading-[18px]">{{
<span class="extra"> t('dialog.user.info.id')
}}</span>
<span class="block truncate text-xs">
{{ userDialog.id }} {{ userDialog.id }}
<TooltipWrapper side="top" :content="t('dialog.user.info.id_tooltip')"> <TooltipWrapper side="top" :content="t('dialog.user.info.id_tooltip')">
<DropdownMenu> <DropdownMenu>
@@ -520,17 +594,22 @@
</Select> </Select>
</div> </div>
</div> </div>
<ul class="x-friend-list" style="margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px"> <ul
class="flex flex-wrap items-start"
style="margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px">
<li <li
v-for="user in userDialog.mutualFriends" v-for="user in userDialog.mutualFriends"
:key="user.id" :key="user.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showUserDialog(user.id)"> @click="showUserDialog(user.id)">
<div class="avatar"> <div class="relative inline-block flex-none size-9 mr-2.5">
<img :src="userImage(user)" loading="lazy" /> <img class="size-full rounded-full object-cover" :src="userImage(user)" loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" :style="{ color: user.$userColour }" v-text="user.displayName"></span> <span
class="block truncate font-medium leading-[18px]"
:style="{ color: user.$userColour }"
v-text="user.displayName"></span>
</div> </div>
</li> </li>
</ul> </ul>
@@ -605,7 +684,9 @@
</div> </div>
<div style="margin-top: 10px"> <div style="margin-top: 10px">
<template v-if="userDialogGroupEditMode"> <template v-if="userDialogGroupEditMode">
<div class="x-friend-list" style="margin-top: 10px; margin-bottom: 15px; max-height: unset"> <div
class="flex flex-wrap items-start"
style="margin-top: 10px; margin-bottom: 15px; max-height: unset">
<!-- Bulk actions dropdown (shown only in edit mode) --> <!-- Bulk actions dropdown (shown only in edit mode) -->
<Select :model-value="bulkGroupActionValue" @update:modelValue="handleBulkGroupAction"> <Select :model-value="bulkGroupActionValue" @update:modelValue="handleBulkGroupAction">
<SelectTrigger size="sm" style="margin-right: 5px; margin-bottom: 5px" @click.stop> <SelectTrigger size="sm" style="margin-right: 5px; margin-bottom: 5px" @click.stop>
@@ -643,8 +724,7 @@
<div <div
v-for="group in userDialogGroupEditGroups" v-for="group in userDialogGroupEditGroups"
:key="group.id" :key="group.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-full hover:rounded-[25px_5px_5px_25px]"
style="width: 100%"
@click="showGroupDialog(group.id)"> @click="showGroupDialog(group.id)">
<!-- Manual checkbox --> <!-- Manual checkbox -->
<div <div
@@ -698,12 +778,15 @@
<ArrowDown /> <ArrowDown />
</Button> </Button>
</div> </div>
<div class="avatar"> <div class="relative inline-block flex-none size-9 mr-2.5">
<img :src="group.iconUrl" loading="lazy" /> <img
class="size-full rounded-full object-cover"
:src="group.iconUrl"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" v-text="group.name"></span> <span class="block truncate font-medium leading-[18px]" v-text="group.name"></span>
<span class="extra"> <span class="block truncate text-xs">
<TooltipWrapper <TooltipWrapper
v-if="group.isRepresenting" v-if="group.isRepresenting"
side="top" side="top"
@@ -799,18 +882,25 @@
cachedConfig?.constants?.GROUPS?.MAX_OWNED cachedConfig?.constants?.GROUPS?.MAX_OWNED
}}</span }}</span
> >
<div class="x-friend-list" style="margin-top: 10px; margin-bottom: 15px; min-height: 60px"> <div
class="flex flex-wrap items-start"
style="margin-top: 10px; margin-bottom: 15px; min-height: 60px">
<div <div
v-for="group in userDialog.userGroups.ownGroups" v-for="group in userDialog.userGroups.ownGroups"
:key="group.id" :key="group.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showGroupDialog(group.id)"> @click="showGroupDialog(group.id)">
<div class="avatar"> <div class="relative inline-block flex-none size-9 mr-2.5">
<img :src="group.iconUrl" loading="lazy" /> <img
class="size-full rounded-full object-cover"
:src="group.iconUrl"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" v-text="group.name"></span> <span
<span class="extra inline-flex! items-center"> class="block truncate font-medium leading-[18px]"
v-text="group.name"></span>
<span class="block truncate text-xs inline-flex! items-center">
<TooltipWrapper <TooltipWrapper
v-if="group.isRepresenting" v-if="group.isRepresenting"
side="top" side="top"
@@ -839,18 +929,25 @@
<span style="font-size: 12px; margin-left: 5px">{{ <span style="font-size: 12px; margin-left: 5px">{{
userDialog.userGroups.mutualGroups.length userDialog.userGroups.mutualGroups.length
}}</span> }}</span>
<div class="x-friend-list" style="margin-top: 10px; margin-bottom: 15px; min-height: 60px"> <div
class="flex flex-wrap items-start"
style="margin-top: 10px; margin-bottom: 15px; min-height: 60px">
<div <div
v-for="group in userDialog.userGroups.mutualGroups" v-for="group in userDialog.userGroups.mutualGroups"
:key="group.id" :key="group.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showGroupDialog(group.id)"> @click="showGroupDialog(group.id)">
<div class="avatar"> <div class="relative inline-block flex-none size-9 mr-2.5">
<img :src="group.iconUrl" loading="lazy" /> <img
class="size-full rounded-full object-cover"
:src="group.iconUrl"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" v-text="group.name"></span> <span
<span class="extra inline-flex! items-center"> class="block truncate font-medium leading-[18px]"
v-text="group.name"></span>
<span class="block truncate text-xs inline-flex! items-center">
<TooltipWrapper <TooltipWrapper
v-if="group.isRepresenting" v-if="group.isRepresenting"
side="top" side="top"
@@ -886,18 +983,25 @@
</template> </template>
</template> </template>
</span> </span>
<div class="x-friend-list" style="margin-top: 10px; margin-bottom: 15px; min-height: 60px"> <div
class="flex flex-wrap items-start"
style="margin-top: 10px; margin-bottom: 15px; min-height: 60px">
<div <div
v-for="group in userDialog.userGroups.remainingGroups" v-for="group in userDialog.userGroups.remainingGroups"
:key="group.id" :key="group.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showGroupDialog(group.id)"> @click="showGroupDialog(group.id)">
<div class="avatar"> <div class="relative inline-block flex-none size-9 mr-2.5">
<img :src="group.iconUrl" loading="lazy" /> <img
class="size-full rounded-full object-cover"
:src="group.iconUrl"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" v-text="group.name"></span> <span
<div class="extra inline-flex! items-center"> class="block truncate font-medium leading-[18px]"
v-text="group.name"></span>
<div class="block truncate text-xs inline-flex! items-center">
<TooltipWrapper <TooltipWrapper
v-if="group.isRepresenting" v-if="group.isRepresenting"
side="top" side="top"
@@ -976,19 +1080,24 @@
</Select> </Select>
</div> </div>
</div> </div>
<div class="x-friend-list" style="margin-top: 10px; min-height: 60px"> <div class="flex flex-wrap items-start" style="margin-top: 10px; min-height: 60px">
<template v-if="userDialog.worlds.length"> <template v-if="userDialog.worlds.length">
<div <div
v-for="world in userDialog.worlds" v-for="world in userDialog.worlds"
:key="world.id" :key="world.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showWorldDialog(world.id)"> @click="showWorldDialog(world.id)">
<div class="avatar"> <div class="relative inline-block flex-none size-9 mr-2.5">
<img :src="world.thumbnailImageUrl" loading="lazy" /> <img
class="size-full rounded-full object-cover"
:src="world.thumbnailImageUrl"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" v-text="world.name"></span> <span class="block truncate font-medium leading-[18px]" v-text="world.name"></span>
<span v-if="world.occupants" class="extra">({{ world.occupants }})</span> <span v-if="world.occupants" class="block truncate text-xs"
>({{ world.occupants }})</span
>
</div> </div>
</div> </div>
</template> </template>
@@ -1049,19 +1158,26 @@
:key="`favorite-worlds-content-${index}`" :key="`favorite-worlds-content-${index}`"
v-slot:[String(index)]> v-slot:[String(index)]>
<div <div
class="x-friend-list" class="flex flex-wrap items-start"
style="margin-top: 10px; margin-bottom: 15px; min-height: 60px; max-height: none"> style="margin-top: 10px; margin-bottom: 15px; min-height: 60px; max-height: none">
<div <div
v-for="world in list[2]" v-for="world in list[2]"
:key="world.favoriteId" :key="world.favoriteId"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showWorldDialog(world.id)"> @click="showWorldDialog(world.id)">
<div class="avatar"> <div class="relative inline-block flex-none size-9 mr-2.5">
<img :src="world.thumbnailImageUrl" loading="lazy" /> <img
class="size-full rounded-full object-cover"
:src="world.thumbnailImageUrl"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" v-text="world.name"></span> <span
<span v-if="world.occupants" class="extra">({{ world.occupants }})</span> class="block truncate font-medium leading-[18px]"
v-text="world.name"></span>
<span v-if="world.occupants" class="block truncate text-xs"
>({{ world.occupants }})</span
>
</div> </div>
</div> </div>
</div> </div>
@@ -1149,29 +1265,35 @@
</template> </template>
</div> </div>
</div> </div>
<div class="x-friend-list" style="margin-top: 10px; min-height: 60px; max-height: 50vh"> <div
class="flex flex-wrap items-start"
style="margin-top: 10px; min-height: 60px; max-height: 50vh; overflow: auto">
<template v-if="filteredUserDialogAvatars.length"> <template v-if="filteredUserDialogAvatars.length">
<div <div
v-for="avatar in filteredUserDialogAvatars" v-for="avatar in filteredUserDialogAvatars"
:key="avatar.id" :key="avatar.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showAvatarDialog(avatar.id)"> @click="showAvatarDialog(avatar.id)">
<div class="avatar"> <div class="relative inline-block flex-none size-9 mr-2.5">
<img v-if="avatar.thumbnailImageUrl" :src="avatar.thumbnailImageUrl" loading="lazy" /> <img
v-if="avatar.thumbnailImageUrl"
class="size-full rounded-full object-cover"
:src="avatar.thumbnailImageUrl"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" v-text="avatar.name"></span> <span class="block truncate font-medium leading-[18px]" v-text="avatar.name"></span>
<span <span
v-if="avatar.releaseStatus === 'public'" v-if="avatar.releaseStatus === 'public'"
class="extra" class="block truncate text-xs"
v-text="avatar.releaseStatus"> v-text="avatar.releaseStatus">
</span> </span>
<span <span
v-else-if="avatar.releaseStatus === 'private'" v-else-if="avatar.releaseStatus === 'private'"
class="extra" class="block truncate text-xs"
v-text="avatar.releaseStatus"> v-text="avatar.releaseStatus">
</span> </span>
<span v-else class="extra" v-text="avatar.releaseStatus"></span> <span v-else class="block truncate text-xs" v-text="avatar.releaseStatus"></span>
</div> </div>
</div> </div>
</template> </template>
@@ -1318,11 +1440,11 @@
userRequest, userRequest,
worldRequest worldRequest
} from '../../../api'; } from '../../../api';
import { processBulk } from '../../../service/request';
import { userDialogGroupSortingOptions, userDialogMutualFriendSortingOptions } from '../../../shared/constants'; import { userDialogGroupSortingOptions, userDialogMutualFriendSortingOptions } from '../../../shared/constants';
import { userDialogWorldOrderOptions, userDialogWorldSortingOptions } from '../../../shared/constants/'; import { userDialogWorldOrderOptions, userDialogWorldSortingOptions } from '../../../shared/constants/';
import { database } from '../../../service/database'; import { database } from '../../../service/database';
import { formatJsonVars } from '../../../shared/utils/base/ui'; import { formatJsonVars } from '../../../shared/utils/base/ui';
import { processBulk } from '../../../service/request';
import InstanceActionBar from '../../InstanceActionBar.vue'; import InstanceActionBar from '../../InstanceActionBar.vue';
import SendInviteDialog from '../InviteDialog/SendInviteDialog.vue'; import SendInviteDialog from '../InviteDialog/SendInviteDialog.vue';

View File

@@ -380,22 +380,27 @@
</div> </div>
<div <div
v-if="room.$location.userId || room.users.length" v-if="room.$location.userId || room.users.length"
class="x-friend-list" class="flex flex-wrap items-start"
style="margin: 10px 0; max-height: unset"> style="margin: 10px 0; max-height: unset">
<div <div
v-if="room.$location.userId" v-if="room.$location.userId"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showUserDialog(room.$location.userId)"> @click="showUserDialog(room.$location.userId)">
<template v-if="room.$location.user"> <template v-if="room.$location.user">
<div class="avatar" :class="userStatusClass(room.$location.user)"> <div
<img :src="userImage(room.$location.user, true)" loading="lazy" /> class="relative inline-block flex-none size-9 mr-2.5"
:class="userStatusClass(room.$location.user)">
<img
class="size-full rounded-full object-cover"
:src="userImage(room.$location.user, true)"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span <span
class="name" class="block truncate font-medium leading-[18px]"
:style="{ color: room.$location.user.$userColour }" :style="{ color: room.$location.user.$userColour }"
v-text="room.$location.user.displayName" /> v-text="room.$location.user.displayName" />
<span class="extra"> <span class="block truncate text-xs">
{{ t('dialog.world.instances.instance_creator') }} {{ t('dialog.world.instances.instance_creator') }}
</span> </span>
</div> </div>
@@ -405,21 +410,26 @@
<div <div
v-for="user in room.users" v-for="user in room.users"
:key="user.id" :key="user.id"
class="x-friend-item x-friend-item-border" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px] hover:rounded-[25px_5px_5px_25px]"
@click="showUserDialog(user.id)"> @click="showUserDialog(user.id)">
<div class="avatar" :class="userStatusClass(user)"> <div
<img :src="userImage(user, true)" loading="lazy" /> class="relative inline-block flex-none size-9 mr-2.5"
:class="userStatusClass(user)">
<img
class="size-full rounded-full object-cover"
:src="userImage(user, true)"
loading="lazy" />
</div> </div>
<div class="detail"> <div class="flex-1 overflow-hidden">
<span <span
class="name" class="block truncate font-medium leading-[18px]"
:style="{ color: user.$userColour }" :style="{ color: user.$userColour }"
v-text="user.displayName" /> v-text="user.displayName" />
<span v-if="user.location === 'traveling'" class="extra"> <span v-if="user.location === 'traveling'" class="block truncate text-xs">
<Spinner class="inline-block mr-1" /> <Spinner class="inline-block mr-1" />
<Timer :epoch="user.$travelingToTime" /> <Timer :epoch="user.$travelingToTime" />
</span> </span>
<span v-else class="extra"> <span v-else class="block truncate text-xs">
<Timer :epoch="user.$location_at" /> <Timer :epoch="user.$location_at" />
</span> </span>
</div> </div>
@@ -430,15 +440,15 @@
</div> </div>
</template> </template>
<template #Info> <template #Info>
<div class="x-friend-list" style="max-height: none"> <div class="flex flex-wrap items-start px-2.5" style="max-height: none">
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.memo') }} {{ t('dialog.world.info.memo') }}
</span> </span>
<InputGroupTextareaField <InputGroupTextareaField
v-model="memo" v-model="memo"
class="extra" class="text-xs"
:rows="2" :rows="2"
:placeholder="t('dialog.world.info.memo_placeholder')" :placeholder="t('dialog.world.info.memo_placeholder')"
input-class="resize-none min-h-0" input-class="resize-none min-h-0"
@@ -446,12 +456,12 @@
</div> </div>
</div> </div>
<div style="width: 100%; display: flex"> <div style="width: 100%; display: flex">
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.id') }} {{ t('dialog.world.info.id') }}
</span> </span>
<span class="extra" style="display: inline"> <span class="block truncate text-xs" style="display: inline">
{{ worldDialog.id }} {{ worldDialog.id }}
</span> </span>
<TooltipWrapper side="top" :content="t('dialog.world.info.id_tooltip')"> <TooltipWrapper side="top" :content="t('dialog.world.info.id_tooltip')">
@@ -483,95 +493,95 @@
</div> </div>
<div <div
v-if="worldDialog.ref.previewYoutubeId" v-if="worldDialog.ref.previewYoutubeId"
class="x-friend-item" class="box-border flex items-center p-1.5 text-[13px] cursor-pointer"
style="width: 350px" style="width: 350px"
@click=" @click="
openExternalLink(`https://www.youtube.com/watch?v=${worldDialog.ref.previewYoutubeId}`) openExternalLink(`https://www.youtube.com/watch?v=${worldDialog.ref.previewYoutubeId}`)
"> ">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.youtube_preview') }} {{ t('dialog.world.info.youtube_preview') }}
</span> </span>
<span class="extra"> <span class="block truncate text-xs">
https://www.youtube.com/watch?v={{ worldDialog.ref.previewYoutubeId }} https://www.youtube.com/watch?v={{ worldDialog.ref.previewYoutubeId }}
</span> </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.author_tags') }} {{ t('dialog.world.info.author_tags') }}
</span> </span>
<span <span
v-if=" v-if="
worldDialog.ref.tags?.filter((tag) => tag.startsWith('author_tag')).length > 0 worldDialog.ref.tags?.filter((tag) => tag.startsWith('author_tag')).length > 0
" "
class="extra"> class="block truncate text-xs">
{{ worldTags }} {{ worldTags }}
</span> </span>
<span v-else class="extra"> - </span> <span v-else class="block truncate text-xs"> - </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.players') }} {{ t('dialog.world.info.players') }}
</span> </span>
<span class="extra"> <span class="block truncate text-xs">
{{ commaNumber(worldDialog.ref.occupants) }} {{ commaNumber(worldDialog.ref.occupants) }}
</span> </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.favorites') }} {{ t('dialog.world.info.favorites') }}
</span> </span>
<span class="extra"> <span class="block truncate text-xs">
{{ commaNumber(worldDialog.ref.favorites) {{ commaNumber(worldDialog.ref.favorites)
}}<span }}<span
v-if="worldDialog.ref?.favorites > 0 && worldDialog.ref?.visits > 0" v-if="worldDialog.ref?.favorites > 0 && worldDialog.ref?.visits > 0"
class="extra"> class="text-xs">
({{ favoriteRate }}%) ({{ favoriteRate }}%)
</span> </span>
</span> </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.visits') }} {{ t('dialog.world.info.visits') }}
</span> </span>
<span class="extra"> <span class="block truncate text-xs">
{{ commaNumber(worldDialog.ref.visits) }} {{ commaNumber(worldDialog.ref.visits) }}
</span> </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.capacity') }} {{ t('dialog.world.info.capacity') }}
</span> </span>
<span class="extra"> <span class="block truncate text-xs">
{{ commaNumber(worldDialog.ref.recommendedCapacity) }} ({{ {{ commaNumber(worldDialog.ref.recommendedCapacity) }} ({{
commaNumber(worldDialog.ref.capacity) commaNumber(worldDialog.ref.capacity)
}}) }})
</span> </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.created_at') }} {{ t('dialog.world.info.created_at') }}
</span> </span>
<span class="extra"> <span class="block truncate text-xs">
{{ formatDateFilter(worldDialog.ref.created_at, 'long') }} {{ formatDateFilter(worldDialog.ref.created_at, 'long') }}
</span> </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name" style="display: inline"> <span class="block truncate font-medium leading-[18px]" style="display: inline">
{{ t('dialog.world.info.last_updated') }} {{ t('dialog.world.info.last_updated') }}
</span> </span>
<TooltipWrapper <TooltipWrapper
@@ -590,30 +600,28 @@
</template> </template>
<ChevronDown class="inline-block" /> <ChevronDown class="inline-block" />
</TooltipWrapper> </TooltipWrapper>
<span class="extra"> <span class="block truncate text-xs">
{{ formatDateFilter(worldDialog.ref.updated_at, 'long') }} {{ formatDateFilter(worldDialog.ref.updated_at, 'long') }}
</span> </span>
</div> </div>
</div> </div>
<div <div
v-if="worldDialog.ref.labsPublicationDate !== 'none'" v-if="worldDialog.ref.labsPublicationDate !== 'none'"
class="x-friend-item" class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
style="cursor: default"> <div class="flex-1 overflow-hidden">
<div class="detail"> <span class="block truncate font-medium leading-[18px]">
<span class="name">
{{ t('dialog.world.info.labs_publication_date') }} {{ t('dialog.world.info.labs_publication_date') }}
</span> </span>
<span class="extra"> <span class="block truncate text-xs">
{{ formatDateFilter(worldDialog.ref.labsPublicationDate, 'long') }} {{ formatDateFilter(worldDialog.ref.labsPublicationDate, 'long') }}
</span> </span>
</div> </div>
</div> </div>
<div <div
v-if="worldDialog.ref.publicationDate !== 'none'" v-if="worldDialog.ref.publicationDate !== 'none'"
class="x-friend-item" class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
style="cursor: default"> <div class="flex-1 overflow-hidden">
<div class="detail"> <span class="block truncate font-medium leading-[18px]" style="display: inline">
<span class="name" style="display: inline">
{{ t('dialog.world.info.publication_date') }} {{ t('dialog.world.info.publication_date') }}
</span> </span>
<TooltipWrapper v-if="isTimeInLabVisible" side="top" style="margin-left: 5px"> <TooltipWrapper v-if="isTimeInLabVisible" side="top" style="margin-left: 5px">
@@ -625,62 +633,68 @@
</template> </template>
<ChevronDown class="inline-block" /> <ChevronDown class="inline-block" />
</TooltipWrapper> </TooltipWrapper>
<span class="extra"> <span class="block truncate text-xs">
{{ formatDateFilter(worldDialog.ref.publicationDate, 'long') }} {{ formatDateFilter(worldDialog.ref.publicationDate, 'long') }}
</span> </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.version') }} {{ t('dialog.world.info.version') }}
</span> </span>
<span class="extra" v-text="worldDialog.ref.version" /> <span class="block truncate text-xs" v-text="worldDialog.ref.version" />
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.heat') }} {{ t('dialog.world.info.heat') }}
</span> </span>
<span class="extra"> <span class="block truncate text-xs">
{{ commaNumber(worldDialog.ref.heat) }} {{ '🔥'.repeat(worldDialog.ref.heat) }} {{ commaNumber(worldDialog.ref.heat) }} {{ '🔥'.repeat(worldDialog.ref.heat) }}
</span> </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.popularity') }} {{ t('dialog.world.info.popularity') }}
</span> </span>
<span class="extra"> <span class="block truncate text-xs">
{{ commaNumber(worldDialog.ref.popularity) }} {{ commaNumber(worldDialog.ref.popularity) }}
{{ '💖'.repeat(worldDialog.ref.popularity) }} {{ '💖'.repeat(worldDialog.ref.popularity) }}
</span> </span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="width: 100%; cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] w-full cursor-default">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.platform') }} {{ t('dialog.world.info.platform') }}
</span> </span>
<span class="extra" style="white-space: normal">{{ worldDialogPlatform }}</span> <span class="block truncate text-xs" style="white-space: normal">{{
worldDialogPlatform
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.last_visited') }} {{ t('dialog.world.info.last_visited') }}
</span> </span>
<span class="extra">{{ formatDateFilter(worldDialog.lastVisit, 'long') }}</span> <span class="block truncate text-xs">{{
formatDateFilter(worldDialog.lastVisit, 'long')
}}</span>
</div> </div>
</div> </div>
<div class="x-friend-item" @click="showPreviousInstancesListDialog(worldDialog.ref)"> <div
<div class="detail"> class="box-border flex items-center p-1.5 text-[13px] cursor-pointer w-[167px]"
@click="showPreviousInstancesListDialog(worldDialog.ref)">
<div class="flex-1 overflow-hidden">
<div <div
class="name" class="block truncate font-medium leading-[18px]"
style="display: flex; justify-content: space-between; align-items: center"> style="display: flex; justify-content: space-between; align-items: center">
<div> <div>
{{ t('dialog.world.info.visit_count') }} {{ t('dialog.world.info.visit_count') }}
@@ -690,17 +704,17 @@
<MoreHorizontal style="margin-right: 16px" /> <MoreHorizontal style="margin-right: 16px" />
</TooltipWrapper> </TooltipWrapper>
</div> </div>
<span v-if="worldDialog.visitCount === 0" class="extra">-</span> <span v-if="worldDialog.visitCount === 0" class="block truncate text-xs">-</span>
<span v-else class="extra" v-text="worldDialog.visitCount"></span> <span v-else class="block truncate text-xs" v-text="worldDialog.visitCount"></span>
</div> </div>
</div> </div>
<div class="x-friend-item" style="cursor: default"> <div class="box-border flex items-center p-1.5 text-[13px] cursor-default w-[167px]">
<div class="detail"> <div class="flex-1 overflow-hidden">
<span class="name"> <span class="block truncate font-medium leading-[18px]">
{{ t('dialog.world.info.time_spent') }} {{ t('dialog.world.info.time_spent') }}
</span> </span>
<span class="extra"> <span class="block truncate text-xs">
{{ worldDialog.timeSpent === 0 ? ' - ' : timeSpent }} {{ worldDialog.timeSpent === 0 ? ' - ' : timeSpent }}
</span> </span>
</div> </div>