mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-21 15:53:50 +02:00
1826 lines
94 KiB
Vue
1826 lines
94 KiB
Vue
<template>
|
|
<safe-dialog
|
|
ref="groupDialogRef"
|
|
:visible.sync="groupDialog.visible"
|
|
:show-close="false"
|
|
width="770px"
|
|
top="10vh"
|
|
class="x-dialog x-group-dialog">
|
|
<div class="group-banner-image">
|
|
<el-popover placement="right" width="500px" trigger="click">
|
|
<img
|
|
slot="reference"
|
|
v-lazy="groupDialog.ref.bannerUrl"
|
|
style="flex: none; width: 100%; aspect-ratio: 6/1; object-fit: cover; border-radius: 4px"
|
|
class="x-link" />
|
|
<img
|
|
v-lazy="groupDialog.ref.bannerUrl"
|
|
style="width: 854px; height: 480px"
|
|
class="x-link"
|
|
@click="showFullscreenImageDialog(groupDialog.ref.bannerUrl)" />
|
|
</el-popover>
|
|
</div>
|
|
<div v-loading="groupDialog.loading" class="group-body">
|
|
<div style="display: flex">
|
|
<el-popover placement="right" width="500px" trigger="click">
|
|
<img
|
|
slot="reference"
|
|
v-lazy="groupDialog.ref.iconUrl"
|
|
style="flex: none; width: 120px; height: 120px; border-radius: 12px"
|
|
class="x-link" />
|
|
<img
|
|
v-lazy="groupDialog.ref.iconUrl"
|
|
style="width: 500px; height: 500px"
|
|
class="x-link"
|
|
@click="showFullscreenImageDialog(groupDialog.ref.iconUrl)" />
|
|
</el-popover>
|
|
<div style="flex: 1; display: flex; align-items: center; margin-left: 15px">
|
|
<div class="group-header" style="flex: 1">
|
|
<span v-if="groupDialog.ref.ownerId === currentUser.id" style="margin-right: 5px">👑</span>
|
|
<span class="dialog-title" style="margin-right: 5px" v-text="groupDialog.ref.name"></span>
|
|
<span
|
|
class="group-discriminator x-grey"
|
|
style="font-family: monospace; font-size: 12px; margin-right: 5px">
|
|
{{ groupDialog.ref.shortCode }}.{{ groupDialog.ref.discriminator }}
|
|
</span>
|
|
<el-tooltip v-for="item in groupDialog.ref.$languages" :key="item.key" placement="top">
|
|
<template slot="content">
|
|
<span>{{ item.value }} ({{ item.key }})</span>
|
|
</template>
|
|
<span
|
|
class="flags"
|
|
:class="languageClass(item.key)"
|
|
style="display: inline-block; margin-right: 5px"></span>
|
|
</el-tooltip>
|
|
<div style="margin-top: 5px">
|
|
<span
|
|
class="x-link x-grey"
|
|
style="font-family: monospace"
|
|
@click="showUserDialog(groupDialog.ref.ownerId)"
|
|
v-text="groupDialog.ownerDisplayName"></span>
|
|
</div>
|
|
<div class="group-tags">
|
|
<el-tag
|
|
v-if="groupDialog.ref.isVerified"
|
|
type="info"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.verified') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-if="groupDialog.ref.privacy === 'private'"
|
|
type="danger"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.private') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-if="groupDialog.ref.privacy === 'default'"
|
|
type="success"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.public') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-if="groupDialog.ref.joinState === 'open'"
|
|
type="success"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.open') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-else-if="groupDialog.ref.joinState === 'request'"
|
|
type="warning"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.request') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-else-if="groupDialog.ref.joinState === 'invite'"
|
|
type="danger"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.invite') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-else-if="groupDialog.ref.joinState === 'closed'"
|
|
type="danger"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.closed') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-if="groupDialog.inGroup"
|
|
type="info"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.joined') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-if="groupDialog.ref.myMember && groupDialog.ref.myMember.bannedAt"
|
|
type="danger"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.banned') }}
|
|
</el-tag>
|
|
<template v-if="groupDialog.inGroup && groupDialog.ref.myMember">
|
|
<el-tag
|
|
v-if="groupDialog.ref.myMember.visibility === 'visible'"
|
|
type="info"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.visible') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-else-if="groupDialog.ref.myMember.visibility === 'friends'"
|
|
type="info"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.friends') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-else-if="groupDialog.ref.myMember.visibility === 'hidden'"
|
|
type="info"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.hidden') }}
|
|
</el-tag>
|
|
<el-tag
|
|
v-if="groupDialog.ref.myMember.isSubscribedToAnnouncements"
|
|
type="info"
|
|
effect="plain"
|
|
size="mini"
|
|
style="margin-right: 5px; margin-top: 5px">
|
|
{{ t('dialog.group.tags.subscribed') }}
|
|
</el-tag>
|
|
</template>
|
|
</div>
|
|
<div class="group-description" style="margin-top: 5px">
|
|
<span
|
|
v-show="groupDialog.ref.name !== groupDialog.ref.description"
|
|
style="font-size: 12px"
|
|
v-text="groupDialog.ref.description"></span>
|
|
</div>
|
|
</div>
|
|
<div style="flex: none; margin-left: 10px">
|
|
<template v-if="groupDialog.inGroup && groupDialog.ref?.myMember">
|
|
<el-tooltip
|
|
v-if="groupDialog.ref.myMember?.isRepresenting"
|
|
placement="top"
|
|
:content="t('dialog.group.actions.unrepresent_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<el-button
|
|
type="warning"
|
|
icon="el-icon-star-on"
|
|
circle
|
|
style="margin-left: 5px"
|
|
@click="clearGroupRepresentation(groupDialog.id)"></el-button>
|
|
</el-tooltip>
|
|
<el-tooltip
|
|
v-else
|
|
placement="top"
|
|
:content="t('dialog.group.actions.represent_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<span>
|
|
<el-button
|
|
type="default"
|
|
icon="el-icon-star-off"
|
|
circle
|
|
style="margin-left: 5px"
|
|
:disabled="groupDialog.ref.privacy === 'private'"
|
|
@click="setGroupRepresentation(groupDialog.id)"></el-button>
|
|
</span>
|
|
</el-tooltip>
|
|
</template>
|
|
<template v-else-if="groupDialog.ref.myMember?.membershipStatus === 'requested'">
|
|
<el-tooltip
|
|
placement="top"
|
|
:content="t('dialog.group.actions.cancel_join_request_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<span>
|
|
<el-button
|
|
type="default"
|
|
icon="el-icon-close"
|
|
circle
|
|
style="margin-left: 5px"
|
|
@click="cancelGroupRequest(groupDialog.id)"></el-button>
|
|
</span>
|
|
</el-tooltip>
|
|
</template>
|
|
<template v-else-if="groupDialog.ref.myMember?.membershipStatus === 'invited'">
|
|
<el-tooltip
|
|
placement="top"
|
|
:content="t('dialog.group.actions.pending_request_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<span>
|
|
<el-button
|
|
type="default"
|
|
icon="el-icon-check"
|
|
circle
|
|
style="margin-left: 5px"
|
|
@click="joinGroup(groupDialog.id)"></el-button>
|
|
</span>
|
|
</el-tooltip>
|
|
</template>
|
|
<template v-else>
|
|
<el-tooltip
|
|
v-if="groupDialog.ref.joinState === 'request'"
|
|
placement="top"
|
|
:content="t('dialog.group.actions.request_join_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<el-button
|
|
type="default"
|
|
icon="el-icon-message"
|
|
circle
|
|
style="margin-left: 5px"
|
|
@click="joinGroup(groupDialog.id)"></el-button>
|
|
</el-tooltip>
|
|
<el-tooltip
|
|
v-if="groupDialog.ref.joinState === 'invite'"
|
|
placement="top"
|
|
:content="t('dialog.group.actions.invite_required_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<span>
|
|
<el-button
|
|
type="default"
|
|
icon="el-icon-message"
|
|
disabled
|
|
circle
|
|
style="margin-left: 5px"></el-button>
|
|
</span>
|
|
</el-tooltip>
|
|
<el-tooltip
|
|
v-if="groupDialog.ref.joinState === 'open'"
|
|
placement="top"
|
|
:content="t('dialog.group.actions.join_group_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<el-button
|
|
type="default"
|
|
icon="el-icon-check"
|
|
circle
|
|
style="margin-left: 5px"
|
|
@click="joinGroup(groupDialog.id)"></el-button>
|
|
</el-tooltip>
|
|
</template>
|
|
<el-dropdown
|
|
trigger="click"
|
|
size="small"
|
|
style="margin-left: 5px"
|
|
@command="groupDialogCommand">
|
|
<el-button
|
|
:type="groupDialog.ref.membershipStatus === 'userblocked' ? 'danger' : 'default'"
|
|
icon="el-icon-more"
|
|
circle></el-button>
|
|
<el-dropdown-menu slot="dropdown">
|
|
<el-dropdown-item icon="el-icon-refresh" command="Refresh">
|
|
{{ t('dialog.group.actions.refresh') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item icon="el-icon-share" command="Share">
|
|
{{ t('dialog.group.actions.share') }}
|
|
</el-dropdown-item>
|
|
<template v-if="groupDialog.inGroup">
|
|
<template v-if="groupDialog.ref.myMember">
|
|
<el-dropdown-item
|
|
v-if="groupDialog.ref.myMember.isSubscribedToAnnouncements"
|
|
icon="el-icon-close"
|
|
command="Unsubscribe To Announcements"
|
|
divided>
|
|
{{ t('dialog.group.actions.unsubscribe') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item
|
|
v-else
|
|
icon="el-icon-check"
|
|
command="Subscribe To Announcements"
|
|
divided>
|
|
{{ t('dialog.group.actions.subscribe') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item
|
|
v-if="hasGroupPermission(groupDialog.ref, 'group-invites-manage')"
|
|
icon="el-icon-message"
|
|
command="Invite To Group">
|
|
{{ t('dialog.group.actions.invite_to_group') }}
|
|
</el-dropdown-item>
|
|
<template
|
|
v-if="hasGroupPermission(groupDialog.ref, 'group-announcement-manage')">
|
|
<el-dropdown-item icon="el-icon-tickets" command="Create Post">
|
|
{{ t('dialog.group.actions.create_post') }}
|
|
</el-dropdown-item>
|
|
</template>
|
|
<el-dropdown-item icon="el-icon-s-operation" command="Moderation Tools">
|
|
{{ t('dialog.group.actions.moderation_tools') }}
|
|
</el-dropdown-item>
|
|
<template
|
|
v-if="groupDialog.ref.myMember && groupDialog.ref.privacy === 'default'">
|
|
<el-dropdown-item icon="el-icon-view" command="Visibility Everyone" divided>
|
|
<i
|
|
v-if="groupDialog.ref.myMember.visibility === 'visible'"
|
|
class="el-icon-check"></i>
|
|
{{ t('dialog.group.actions.visibility_everyone') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item icon="el-icon-view" command="Visibility Friends">
|
|
<i
|
|
v-if="groupDialog.ref.myMember.visibility === 'friends'"
|
|
class="el-icon-check"></i>
|
|
{{ t('dialog.group.actions.visibility_friends') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item icon="el-icon-view" command="Visibility Hidden">
|
|
<i
|
|
v-if="groupDialog.ref.myMember.visibility === 'hidden'"
|
|
class="el-icon-check"></i>
|
|
{{ t('dialog.group.actions.visibility_hidden') }}
|
|
</el-dropdown-item>
|
|
</template>
|
|
<el-dropdown-item
|
|
icon="el-icon-delete"
|
|
command="Leave Group"
|
|
style="color: #f56c6c"
|
|
divided>
|
|
{{ t('dialog.group.actions.leave') }}
|
|
</el-dropdown-item>
|
|
</template>
|
|
</template>
|
|
<template v-else>
|
|
<el-dropdown-item
|
|
v-if="groupDialog.ref.membershipStatus === 'userblocked'"
|
|
icon="el-icon-circle-check"
|
|
command="Unblock Group"
|
|
style="color: #f56c6c"
|
|
divided>
|
|
{{ t('dialog.group.actions.unblock') }}
|
|
</el-dropdown-item>
|
|
<el-dropdown-item v-else icon="el-icon-circle-close" command="Block Group" divided>
|
|
{{ t('dialog.group.actions.block') }}
|
|
</el-dropdown-item>
|
|
</template>
|
|
</el-dropdown-menu>
|
|
</el-dropdown>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<el-tabs v-model="groupDialogTabCurrentName" @tab-click="groupDialogTabClick">
|
|
<el-tab-pane :label="t('dialog.group.info.header')">
|
|
<div class="group-banner-image-info">
|
|
<el-popover placement="right" width="500px" trigger="click">
|
|
<img
|
|
slot="reference"
|
|
v-lazy="groupDialog.ref.bannerUrl"
|
|
class="x-link"
|
|
style="
|
|
flex: none;
|
|
width: 100%;
|
|
aspect-ratio: 6/1;
|
|
object-fit: cover;
|
|
border-radius: 4px;
|
|
" />
|
|
<img
|
|
v-lazy="groupDialog.ref.bannerUrl"
|
|
class="x-link"
|
|
style="width: 854px; height: 480px"
|
|
@click="showFullscreenImageDialog(groupDialog.ref.bannerUrl)" />
|
|
</el-popover>
|
|
</div>
|
|
<div class="x-friend-list" style="max-height: none">
|
|
<span
|
|
v-if="groupDialog.instances.length"
|
|
style="font-size: 12px; font-weight: bold; margin: 5px">
|
|
{{ t('dialog.group.info.instances') }}
|
|
</span>
|
|
<div v-for="room in groupDialog.instances" :key="room.tag" style="width: 100%">
|
|
<div style="margin: 5px 0">
|
|
<Location :location="room.tag" style="display: inline-block" />
|
|
<el-tooltip placement="top" content="Invite yourself" :disabled="hideTooltips">
|
|
<InviteYourself :location="room.tag" style="margin-left: 5px" />
|
|
</el-tooltip>
|
|
<el-tooltip placement="top" content="Refresh player count" :disabled="hideTooltips">
|
|
<el-button
|
|
size="mini"
|
|
icon="el-icon-refresh"
|
|
style="margin-left: 5px"
|
|
circle
|
|
@click="refreshInstancePlayerCount(room.tag)" />
|
|
</el-tooltip>
|
|
<LastJoin :location="room.tag" :currentlocation="lastLocation.location" />
|
|
<InstanceInfo
|
|
:location="room.tag"
|
|
:instance="room.ref"
|
|
:friendcount="room.friendCount" />
|
|
</div>
|
|
<div
|
|
v-if="room.users.length"
|
|
class="x-friend-list"
|
|
style="margin: 10px 0; padding: 0; max-height: unset">
|
|
<div
|
|
v-for="user in room.users"
|
|
:key="user.id"
|
|
class="x-friend-item x-friend-item-border"
|
|
@click="showUserDialog(user.id)">
|
|
<div class="avatar" :class="userStatusClass(user)">
|
|
<img v-lazy="userImage(user)" />
|
|
</div>
|
|
<div class="detail">
|
|
<span
|
|
class="name"
|
|
:style="{ color: user.$userColour }"
|
|
v-text="user.displayName" />
|
|
<span v-if="user.location === 'traveling'" class="extra">
|
|
<i class="el-icon-loading" style="margin-right: 5px" />
|
|
<Timer :epoch="user.$travelingToTime" />
|
|
</span>
|
|
<span v-else class="extra">
|
|
<Timer :epoch="user.$location_at" />
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="x-friend-item" style="width: 100%; cursor: default">
|
|
<div class="detail">
|
|
<span class="name">{{ t('dialog.group.info.announcement') }}</span>
|
|
<span style="display: block" v-text="groupDialog.announcement.title" />
|
|
<div
|
|
v-if="groupDialog.announcement.imageUrl"
|
|
style="display: inline-block; margin-right: 5px">
|
|
<el-popover placement="right" width="500px" trigger="click">
|
|
<img
|
|
slot="reference"
|
|
v-lazy="groupDialog.announcement.imageUrl"
|
|
class="x-link"
|
|
style="
|
|
flex: none;
|
|
width: 60px;
|
|
height: 60px;
|
|
border-radius: 4px;
|
|
object-fit: cover;
|
|
" />
|
|
<img
|
|
v-lazy="groupDialog.announcement.imageUrl"
|
|
class="x-link"
|
|
style="height: 500px"
|
|
@click="showFullscreenImageDialog(groupDialog.announcement.imageUrl)" />
|
|
</el-popover>
|
|
</div>
|
|
<pre
|
|
class="extra"
|
|
style="
|
|
display: inline-block;
|
|
vertical-align: top;
|
|
font-family: inherit;
|
|
font-size: 12px;
|
|
white-space: pre-wrap;
|
|
margin: 0;
|
|
"
|
|
>{{ groupDialog.announcement.text || '-' }}</pre
|
|
>
|
|
<br />
|
|
<div
|
|
v-if="groupDialog.announcement.id"
|
|
class="extra"
|
|
style="float: right; margin-left: 5px">
|
|
<el-tooltip v-if="groupDialog.announcement.roleIds.length" placement="top">
|
|
<template #content>
|
|
<span>{{ t('dialog.group.posts.visibility') }}</span>
|
|
<br />
|
|
<template v-for="roleId in groupDialog.announcement.roleIds">
|
|
<template v-for="role in groupDialog.ref.roles"
|
|
><span
|
|
v-if="role.id === roleId"
|
|
:key="roleId + role.id"
|
|
v-text="role.name"
|
|
/></template>
|
|
<span
|
|
:key="roleId"
|
|
v-if="
|
|
groupDialog.announcement.roleIds.indexOf(roleId) <
|
|
groupDialog.announcement.roleIds.length - 1
|
|
">
|
|
,
|
|
</span>
|
|
</template>
|
|
</template>
|
|
<i class="el-icon-view" style="margin-right: 5px" />
|
|
</el-tooltip>
|
|
<DisplayName
|
|
:userid="groupDialog.announcement.authorId"
|
|
style="margin-right: 5px" />
|
|
<span v-if="groupDialog.announcement.editorId" style="margin-right: 5px">
|
|
({{ t('dialog.group.posts.edited_by') }}
|
|
<DisplayName :userid="groupDialog.announcement.editorId" />)
|
|
</span>
|
|
<el-tooltip placement="bottom">
|
|
<template #content>
|
|
<span
|
|
>{{ t('dialog.group.posts.created_at') }}
|
|
{{ formatDateFilter(groupDialog.announcement.createdAt, 'long') }}</span
|
|
>
|
|
<template
|
|
v-if="
|
|
groupDialog.announcement.updatedAt !==
|
|
groupDialog.announcement.createdAt
|
|
">
|
|
<br />
|
|
<span
|
|
>{{ t('dialog.group.posts.edited_at') }}
|
|
{{
|
|
formatDateFilter(groupDialog.announcement.updatedAt, 'long')
|
|
}}</span
|
|
>
|
|
</template>
|
|
</template>
|
|
<Timer :epoch="Date.parse(groupDialog.announcement.updatedAt)" />
|
|
</el-tooltip>
|
|
<template v-if="hasGroupPermission(groupDialog.ref, 'group-announcement-manage')">
|
|
<el-tooltip
|
|
placement="top"
|
|
:content="t('dialog.group.posts.edit_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<el-button
|
|
type="text"
|
|
icon="el-icon-edit"
|
|
size="mini"
|
|
style="margin-left: 5px"
|
|
@click="
|
|
showGroupPostEditDialog(groupDialog.id, groupDialog.announcement)
|
|
" />
|
|
</el-tooltip>
|
|
<el-tooltip
|
|
placement="top"
|
|
:content="t('dialog.group.posts.delete_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<el-button
|
|
type="text"
|
|
icon="el-icon-delete"
|
|
size="mini"
|
|
style="margin-left: 5px"
|
|
@click="confirmDeleteGroupPost(groupDialog.announcement)" />
|
|
</el-tooltip>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="x-friend-item" style="width: 100%; cursor: default">
|
|
<div class="detail">
|
|
<span class="name">{{ t('dialog.group.info.rules') }}</span>
|
|
<pre
|
|
class="extra"
|
|
style="
|
|
font-family: inherit;
|
|
font-size: 12px;
|
|
white-space: pre-wrap;
|
|
margin: 0 0.5em 0 0;
|
|
"
|
|
>{{ groupDialog.ref.rules || '-' }}</pre
|
|
>
|
|
</div>
|
|
</div>
|
|
<div class="x-friend-item" style="cursor: default">
|
|
<div class="detail">
|
|
<span class="name">{{ t('dialog.group.info.members') }}</span>
|
|
<div class="extra">
|
|
{{ groupDialog.ref.memberCount }} ({{ groupDialog.ref.onlineMemberCount }})
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="x-friend-item" style="cursor: default">
|
|
<div class="detail">
|
|
<span class="name">{{ t('dialog.group.info.created_at') }}</span>
|
|
<span class="extra">{{ formatDateFilter(groupDialog.ref.createdAt, 'long') }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="x-friend-item" style="cursor: default">
|
|
<div class="detail">
|
|
<span class="name">{{ t('dialog.group.info.links') }}</span>
|
|
<div
|
|
v-if="groupDialog.ref.links && groupDialog.ref.links.length > 0"
|
|
style="margin-top: 5px">
|
|
<el-tooltip v-for="(link, index) in groupDialog.ref.links" v-if="link" :key="index">
|
|
<template #content>
|
|
<span v-text="link" />
|
|
</template>
|
|
<img
|
|
:src="getFaviconUrl(link)"
|
|
style="
|
|
width: 16px;
|
|
height: 16px;
|
|
vertical-align: middle;
|
|
margin-right: 5px;
|
|
cursor: pointer;
|
|
"
|
|
@click.stop="openExternalLink(link)" />
|
|
</el-tooltip>
|
|
</div>
|
|
<div v-else class="extra">-</div>
|
|
</div>
|
|
</div>
|
|
<div class="x-friend-item" style="width: 350px; cursor: default">
|
|
<div class="detail">
|
|
<span class="name">{{ t('dialog.group.info.url') }}</span>
|
|
<span class="extra"
|
|
>{{ groupDialog.ref.$url }}
|
|
<el-tooltip
|
|
placement="top"
|
|
:content="t('dialog.group.info.url_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<el-button
|
|
type="default"
|
|
size="mini"
|
|
icon="el-icon-s-order"
|
|
circle
|
|
style="margin-left: 5px"
|
|
@click="copyToClipboard(groupDialog.ref.$url)" /> </el-tooltip
|
|
></span>
|
|
</div>
|
|
</div>
|
|
<div class="x-friend-item" style="width: 350px; cursor: default">
|
|
<div class="detail">
|
|
<span class="name">{{ t('dialog.group.info.id') }}</span>
|
|
<span class="extra"
|
|
>{{ groupDialog.id }}
|
|
<el-tooltip
|
|
placement="top"
|
|
:content="t('dialog.group.info.id_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<el-button
|
|
type="default"
|
|
size="mini"
|
|
icon="el-icon-s-order"
|
|
circle
|
|
style="margin-left: 5px"
|
|
@click="copyToClipboard(groupDialog.id)" /> </el-tooltip
|
|
></span>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="groupDialog.ref.membershipStatus === 'member'"
|
|
style="width: 100%; margin-top: 10px; border-top: 1px solid #e4e7ed14">
|
|
<div style="width: 100%; display: flex; margin-top: 10px">
|
|
<div class="x-friend-item" style="cursor: default">
|
|
<div class="detail">
|
|
<span class="name">{{ t('dialog.group.info.joined_at') }}</span>
|
|
<span class="extra">{{
|
|
formatDateFilter(groupDialog.ref.myMember.joinedAt, 'long')
|
|
}}</span>
|
|
</div>
|
|
</div>
|
|
<div class="x-friend-item" style="cursor: default">
|
|
<div class="detail">
|
|
<span class="name">{{ t('dialog.group.info.roles') }}</span>
|
|
<span v-if="groupDialog.memberRoles.length === 0" class="extra"> - </span>
|
|
<span v-else class="extra">
|
|
<template v-for="(role, rIndex) in groupDialog.memberRoles">
|
|
<el-tooltip :key="rIndex" placement="top">
|
|
<template #content>
|
|
<span>{{ t('dialog.group.info.role') }} {{ role.name }}</span>
|
|
<br />
|
|
<span
|
|
>{{ t('dialog.group.info.role_description') }}
|
|
{{ role.description }}</span
|
|
>
|
|
<br />
|
|
<span v-if="role.updatedAt"
|
|
>{{ t('dialog.group.info.role_updated_at') }}
|
|
{{ formatDateFilter(role.updatedAt, 'long') }}</span
|
|
>
|
|
<span v-else
|
|
>{{ t('dialog.group.info.role_created_at') }}
|
|
{{ formatDateFilter(role.createdAt, 'long') }}</span
|
|
>
|
|
<br />
|
|
<span>{{ t('dialog.group.info.role_permissions') }}</span>
|
|
<br />
|
|
<template v-for="(permission, pIndex) in role.permissions">
|
|
<span :key="pIndex">{{ permission }}</span>
|
|
<br :key="pIndex + permission" />
|
|
</template>
|
|
</template>
|
|
<span
|
|
>{{ role.name
|
|
}}{{
|
|
rIndex < groupDialog.memberRoles.length - 1 ? ', ' : ''
|
|
}}</span
|
|
>
|
|
</el-tooltip>
|
|
</template>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</el-tab-pane>
|
|
<el-tab-pane :label="t('dialog.group.posts.header')" lazy>
|
|
<template v-if="groupDialog.visible">
|
|
<span style="margin-right: 10px"
|
|
>{{ t('dialog.group.posts.posts_count') }} {{ groupDialog.posts.length }}</span
|
|
>
|
|
<el-input
|
|
v-model="groupDialog.postsSearch"
|
|
clearable
|
|
size="mini"
|
|
:placeholder="t('dialog.group.posts.search_placeholder')"
|
|
style="width: 89%; margin-bottom: 10px"
|
|
@input="updateGroupPostSearch" />
|
|
<div class="x-friend-list">
|
|
<div
|
|
v-for="post in groupDialog.postsFiltered"
|
|
:key="post.id"
|
|
class="x-friend-item"
|
|
style="width: 100%; cursor: default">
|
|
<div class="detail">
|
|
<span style="display: block" v-text="post.title" />
|
|
<div v-if="post.imageUrl" style="display: inline-block; margin-right: 5px">
|
|
<el-popover placement="right" width="500px" trigger="click">
|
|
<img
|
|
slot="reference"
|
|
v-lazy="post.imageUrl"
|
|
class="x-link"
|
|
style="
|
|
flex: none;
|
|
width: 60px;
|
|
height: 60px;
|
|
border-radius: 4px;
|
|
object-fit: cover;
|
|
" />
|
|
<img
|
|
v-lazy="post.imageUrl"
|
|
class="x-link"
|
|
style="height: 500px"
|
|
@click="showFullscreenImageDialog(post.imageUrl)" />
|
|
</el-popover>
|
|
</div>
|
|
<pre
|
|
class="extra"
|
|
style="
|
|
display: inline-block;
|
|
vertical-align: top;
|
|
font-family: inherit;
|
|
font-size: 12px;
|
|
white-space: pre-wrap;
|
|
margin: 0;
|
|
"
|
|
>{{ post.text || '-' }}</pre
|
|
>
|
|
<br />
|
|
<div v-if="post.authorId" class="extra" style="float: right; margin-left: 5px">
|
|
<el-tooltip v-if="post.roleIds.length" placement="top">
|
|
<template slot="content">
|
|
<span>{{ t('dialog.group.posts.visibility') }}</span>
|
|
<br />
|
|
<template v-for="roleId in post.roleIds">
|
|
<template v-for="role in groupDialog.ref.roles"
|
|
><span
|
|
v-if="role.id === roleId"
|
|
:key="role.id + roleId"
|
|
v-text="role.name" />
|
|
</template>
|
|
<template
|
|
v-if="post.roleIds.indexOf(roleId) < post.roleIds.length - 1"
|
|
><span :key="roleId">, </span></template
|
|
>
|
|
</template>
|
|
</template>
|
|
<i class="el-icon-view" style="margin-right: 5px" />
|
|
</el-tooltip>
|
|
<DisplayName :userid="post.authorId" style="margin-right: 5px" />
|
|
<span v-if="post.editorId" style="margin-right: 5px"
|
|
>({{ t('dialog.group.posts.edited_by') }}
|
|
<DisplayName :userid="post.editorId" />)</span
|
|
>
|
|
<el-tooltip placement="bottom">
|
|
<template slot="content">
|
|
<span
|
|
>{{ t('dialog.group.posts.created_at') }}
|
|
{{ formatDateFilter(post.createdAt, 'long') }}</span
|
|
>
|
|
<template v-if="post.updatedAt !== post.createdAt">
|
|
<br />
|
|
<span
|
|
>{{ t('dialog.group.posts.edited_at') }}
|
|
{{ formatDateFilter(post.updatedAt, 'long') }}</span
|
|
>
|
|
</template>
|
|
</template>
|
|
<Timer :epoch="Date.parse(post.updatedAt)" />
|
|
</el-tooltip>
|
|
<template
|
|
v-if="hasGroupPermission(groupDialog.ref, 'group-announcement-manage')">
|
|
<el-tooltip
|
|
placement="top"
|
|
:content="t('dialog.group.posts.edit_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<el-button
|
|
type="text"
|
|
icon="el-icon-edit"
|
|
size="mini"
|
|
style="margin-left: 5px"
|
|
@click="showGroupPostEditDialog(groupDialog.id, post)" />
|
|
</el-tooltip>
|
|
<el-tooltip
|
|
placement="top"
|
|
:content="t('dialog.group.posts.delete_tooltip')"
|
|
:disabled="hideTooltips">
|
|
<el-button
|
|
type="text"
|
|
icon="el-icon-delete"
|
|
size="mini"
|
|
style="margin-left: 5px"
|
|
@click="confirmDeleteGroupPost(post)" />
|
|
</el-tooltip>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</el-tab-pane>
|
|
<el-tab-pane :label="t('dialog.group.members.header')" lazy>
|
|
<template v-if="groupDialog.visible">
|
|
<span
|
|
v-if="hasGroupPermission(groupDialog.ref, 'group-members-viewall')"
|
|
style="font-weight: bold; font-size: 16px"
|
|
>{{ t('dialog.group.members.all_members') }}</span
|
|
>
|
|
<span v-else style="font-weight: bold; font-size: 16px">{{
|
|
t('dialog.group.members.friends_only')
|
|
}}</span>
|
|
<div style="margin-top: 10px">
|
|
<el-button
|
|
type="default"
|
|
size="mini"
|
|
icon="el-icon-refresh"
|
|
:loading="isGroupMembersLoading"
|
|
circle
|
|
@click="loadAllGroupMembers" />
|
|
<el-button
|
|
type="default"
|
|
size="mini"
|
|
icon="el-icon-download"
|
|
circle
|
|
style="margin-left: 5px"
|
|
@click="downloadAndSaveJson(`${groupDialog.id}_members`, groupDialog.members)" />
|
|
<span
|
|
v-if="groupDialog.memberSearch.length"
|
|
style="font-size: 14px; margin-left: 5px; margin-right: 5px"
|
|
>{{ groupDialog.memberSearchResults.length }}/{{ groupDialog.ref.memberCount }}</span
|
|
>
|
|
<span v-else style="font-size: 14px; margin-left: 5px; margin-right: 5px"
|
|
>{{ groupDialog.members.length }}/{{ groupDialog.ref.memberCount }}</span
|
|
>
|
|
<div
|
|
v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')"
|
|
style="float: right">
|
|
<span style="margin-right: 5px">{{ t('dialog.group.members.sort_by') }}</span>
|
|
<el-dropdown
|
|
trigger="click"
|
|
size="small"
|
|
style="margin-right: 5px"
|
|
:disabled="isGroupMembersLoading || !!groupDialog.memberSearch.length"
|
|
@click.native.stop>
|
|
<el-button size="mini">
|
|
<span
|
|
>{{ t(groupDialog.memberSortOrder.name) }}
|
|
<i class="el-icon-arrow-down el-icon--right"
|
|
/></span>
|
|
</el-button>
|
|
<el-dropdown-menu slot="dropdown">
|
|
<el-dropdown-item
|
|
v-for="item in groupDialogSortingOptions"
|
|
:key="item.name"
|
|
@click.native="setGroupMemberSortOrder(item)"
|
|
v-text="t(item.name)" />
|
|
</el-dropdown-menu>
|
|
</el-dropdown>
|
|
<span style="margin-right: 5px">{{ t('dialog.group.members.filter') }}</span>
|
|
<el-dropdown
|
|
trigger="click"
|
|
size="small"
|
|
style="margin-right: 5px"
|
|
:disabled="isGroupMembersLoading || !!groupDialog.memberSearch.length"
|
|
@click.native.stop>
|
|
<el-button size="mini">
|
|
<span
|
|
>{{ t(groupDialog.memberFilter.name) }}
|
|
<i class="el-icon-arrow-down el-icon--right"
|
|
/></span>
|
|
</el-button>
|
|
<el-dropdown-menu slot="dropdown">
|
|
<el-dropdown-item
|
|
v-for="item in groupDialogFilterOptions"
|
|
:key="item.name"
|
|
@click.native="setGroupMemberFilter(item)"
|
|
v-text="t(item.name)" />
|
|
<el-dropdown-item
|
|
v-for="item in groupDialog.ref.roles"
|
|
v-if="!item.defaultRole"
|
|
:key="item.name"
|
|
@click.native="setGroupMemberFilter(item)"
|
|
v-text="item.name" />
|
|
</el-dropdown-menu>
|
|
</el-dropdown>
|
|
</div>
|
|
<el-input
|
|
v-model="groupDialog.memberSearch"
|
|
clearable
|
|
size="mini"
|
|
:placeholder="t('dialog.group.members.search')"
|
|
style="margin-top: 10px; margin-bottom: 10px"
|
|
@input="groupMembersSearch" />
|
|
</div>
|
|
<div
|
|
v-if="groupDialog.memberSearch.length"
|
|
v-loading="isGroupMembersLoading"
|
|
class="x-friend-list"
|
|
style="margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px">
|
|
<div
|
|
v-for="user in groupDialog.memberSearchResults"
|
|
:key="user.id"
|
|
class="x-friend-item x-friend-item-border"
|
|
@click="showUserDialog(user.userId)">
|
|
<div class="avatar">
|
|
<img v-lazy="userImage(user.user)" />
|
|
</div>
|
|
<div class="detail">
|
|
<span
|
|
class="name"
|
|
:style="{ color: user.user.$userColour }"
|
|
v-text="user.user.displayName" />
|
|
<span class="extra">
|
|
<template v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')">
|
|
<el-tooltip
|
|
v-if="user.isRepresenting"
|
|
placement="top"
|
|
:content="t('dialog.group.members.representing')">
|
|
<i class="el-icon-collection-tag" style="margin-right: 5px" />
|
|
</el-tooltip>
|
|
<el-tooltip v-if="user.visibility !== 'visible'" placement="top">
|
|
<template slot="content">
|
|
<span
|
|
>{{ t('dialog.group.members.visibility') }}
|
|
{{ user.visibility }}</span
|
|
>
|
|
</template>
|
|
<i class="el-icon-view" style="margin-right: 5px" />
|
|
</el-tooltip>
|
|
<el-tooltip
|
|
v-if="!user.isSubscribedToAnnouncements"
|
|
placement="top"
|
|
:content="t('dialog.group.members.unsubscribed_announcements')">
|
|
<i class="el-icon-chat-line-square" style="margin-right: 5px" />
|
|
</el-tooltip>
|
|
<el-tooltip v-if="user.managerNotes" placement="top">
|
|
<template slot="content">
|
|
<span>{{ t('dialog.group.members.manager_notes') }}</span>
|
|
<br />
|
|
<span>{{ user.managerNotes }}</span>
|
|
</template>
|
|
<i class="el-icon-edit-outline" style="margin-right: 5px" />
|
|
</el-tooltip>
|
|
</template>
|
|
<template v-for="roleId in user.roleIds">
|
|
<template v-for="role in groupDialog.ref.roles"
|
|
><span
|
|
v-if="role.id === roleId"
|
|
:key="role.id + roleId"
|
|
v-text="role.name" /></template
|
|
><template v-if="user.roleIds.indexOf(roleId) < user.roleIds.length - 1"
|
|
><span :key="roleId">, </span></template
|
|
>
|
|
</template>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<ul
|
|
v-else-if="groupDialog.members.length > 0"
|
|
v-infinite-scroll="loadMoreGroupMembers"
|
|
class="infinite-list x-friend-list"
|
|
style="margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px">
|
|
<li
|
|
v-for="user in groupDialog.members"
|
|
:key="user.id"
|
|
class="infinite-list-item x-friend-item x-friend-item-border"
|
|
@click="showUserDialog(user.userId)">
|
|
<div class="avatar">
|
|
<img v-lazy="userImage(user.user)" />
|
|
</div>
|
|
<div class="detail">
|
|
<span
|
|
class="name"
|
|
:style="{ color: user.user.$userColour }"
|
|
v-text="user.user.displayName" />
|
|
<span class="extra">
|
|
<template v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')">
|
|
<el-tooltip
|
|
v-if="user.isRepresenting"
|
|
placement="top"
|
|
:content="t('dialog.group.members.representing')">
|
|
<i class="el-icon-collection-tag" style="margin-right: 5px" />
|
|
</el-tooltip>
|
|
<el-tooltip v-if="user.visibility !== 'visible'" placement="top">
|
|
<template slot="content">
|
|
<span
|
|
>{{ t('dialog.group.members.visibility') }}
|
|
{{ user.visibility }}</span
|
|
>
|
|
</template>
|
|
<i class="el-icon-view" style="margin-right: 5px" />
|
|
</el-tooltip>
|
|
<el-tooltip
|
|
v-if="!user.isSubscribedToAnnouncements"
|
|
placement="top"
|
|
:content="t('dialog.group.members.unsubscribed_announcements')">
|
|
<i class="el-icon-chat-line-square" style="margin-right: 5px" />
|
|
</el-tooltip>
|
|
<el-tooltip v-if="user.managerNotes" placement="top">
|
|
<template slot="content">
|
|
<span>{{ t('dialog.group.members.manager_notes') }}</span>
|
|
<br />
|
|
<span>{{ user.managerNotes }}</span>
|
|
</template>
|
|
<i class="el-icon-edit-outline" style="margin-right: 5px" />
|
|
</el-tooltip>
|
|
</template>
|
|
<template v-for="roleId in user.roleIds">
|
|
<template v-for="role in groupDialog.ref.roles"
|
|
><span
|
|
v-if="role.id === roleId"
|
|
:key="roleId + role"
|
|
v-text="role.name" /></template
|
|
><template v-if="user.roleIds.indexOf(roleId) < user.roleIds.length - 1"
|
|
><span :key="roleId"> </span></template
|
|
>
|
|
</template>
|
|
</span>
|
|
</div>
|
|
</li>
|
|
<div
|
|
v-if="!isGroupMembersDone"
|
|
v-loading="isGroupMembersLoading"
|
|
class="x-friend-item"
|
|
style="width: 100%; height: 45px; text-align: center"
|
|
@click="loadMoreGroupMembers">
|
|
<div v-if="!isGroupMembersLoading" class="detail">
|
|
<span class="name">{{ t('dialog.group.members.load_more') }}</span>
|
|
</div>
|
|
</div>
|
|
</ul>
|
|
</template>
|
|
</el-tab-pane>
|
|
<el-tab-pane :label="t('dialog.group.gallery.header')" lazy>
|
|
<el-button
|
|
type="default"
|
|
size="mini"
|
|
icon="el-icon-refresh"
|
|
:loading="isGroupGalleryLoading"
|
|
circle
|
|
@click="getGroupGalleries" />
|
|
<el-tabs
|
|
v-model="groupDialogGalleryCurrentName"
|
|
v-loading="isGroupGalleryLoading"
|
|
type="card"
|
|
style="margin-top: 10px">
|
|
<template v-for="(gallery, index) in groupDialog.ref.galleries">
|
|
<el-tab-pane :key="index">
|
|
<span slot="label">
|
|
<span style="font-weight: bold; font-size: 16px" v-text="gallery.name" />
|
|
<i
|
|
class="x-status-icon"
|
|
style="margin-left: 5px"
|
|
:class="groupGalleryStatus(gallery)" />
|
|
<span style="color: #909399; font-size: 12px; margin-left: 5px">{{
|
|
groupDialog.galleries[gallery.id] ? groupDialog.galleries[gallery.id].length : 0
|
|
}}</span>
|
|
</span>
|
|
<span style="color: #c7c7c7; padding: 10px" v-text="gallery.description" />
|
|
<el-carousel :interval="0" height="600px" style="margin-top: 10px">
|
|
<el-carousel-item
|
|
v-for="image in groupDialog.galleries[gallery.id]"
|
|
:key="image.id">
|
|
<el-popover placement="top" width="700px" trigger="click">
|
|
<img
|
|
slot="reference"
|
|
v-lazy="image.imageUrl"
|
|
class="x-link"
|
|
style="width: 100%; height: 100%; object-fit: contain" />
|
|
<img
|
|
v-lazy="image.imageUrl"
|
|
class="x-link"
|
|
style="height: 700px"
|
|
@click="showFullscreenImageDialog(image.imageUrl)" />
|
|
</el-popover>
|
|
</el-carousel-item>
|
|
</el-carousel>
|
|
</el-tab-pane>
|
|
</template>
|
|
</el-tabs>
|
|
</el-tab-pane>
|
|
<el-tab-pane :label="t('dialog.group.json.header')" lazy>
|
|
<el-button
|
|
type="default"
|
|
size="mini"
|
|
icon="el-icon-refresh"
|
|
circle
|
|
@click="refreshGroupDialogTreeData()" />
|
|
<el-button
|
|
type="default"
|
|
size="mini"
|
|
icon="el-icon-download"
|
|
circle
|
|
style="margin-left: 5px"
|
|
@click="downloadAndSaveJson(groupDialog.id, groupDialog.ref)" />
|
|
<el-tree :data="groupDialog.treeData" style="margin-top: 5px; font-size: 12px">
|
|
<template slot-scope="scope">
|
|
<span>
|
|
<span style="font-weight: bold; margin-right: 5px" v-text="scope.data.key" />
|
|
<span v-if="!scope.data.children" v-text="scope.data.value" />
|
|
</span>
|
|
</template>
|
|
</el-tree>
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
</div>
|
|
<!--Nested-->
|
|
<GroupPostEditDialog :dialog-data.sync="groupPostEditDialog" :selected-gallery-file="selectedGalleryFile" />
|
|
<GroupMemberModerationDialog
|
|
:is-group-members-loading.sync="isGroupMembersLoading"
|
|
:group-member-moderation="groupMemberModeration"
|
|
@close-dialog="closeMemberModerationDialog"
|
|
@group-members-search="groupMembersSearch"
|
|
@load-all-group-members="loadAllGroupMembers"
|
|
@set-group-member-filter="setGroupMemberFilter"
|
|
@set-group-member-sort-order="setGroupMemberSortOrder" />
|
|
<InviteGroupDialog />
|
|
</safe-dialog>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { storeToRefs } from 'pinia';
|
|
import { getCurrentInstance, nextTick, reactive, ref, watch } from 'vue';
|
|
import { useI18n } from 'vue-i18n-bridge';
|
|
import * as workerTimers from 'worker-timers';
|
|
import { groupRequest } from '../../../api';
|
|
import { $app } from '../../../app';
|
|
import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants';
|
|
import {
|
|
adjustDialogZ,
|
|
buildTreeData,
|
|
copyToClipboard,
|
|
downloadAndSaveJson,
|
|
getFaviconUrl,
|
|
hasGroupPermission,
|
|
languageClass,
|
|
openExternalLink,
|
|
refreshInstancePlayerCount,
|
|
removeFromArray,
|
|
userImage,
|
|
userStatusClass,
|
|
formatDateFilter
|
|
} from '../../../shared/utils';
|
|
import {
|
|
useAppearanceSettingsStore,
|
|
useGalleryStore,
|
|
useGroupStore,
|
|
useLocationStore,
|
|
useUserStore
|
|
} from '../../../stores';
|
|
import InviteGroupDialog from '../InviteGroupDialog.vue';
|
|
import GroupMemberModerationDialog from './GroupMemberModerationDialog.vue';
|
|
import GroupPostEditDialog from './GroupPostEditDialog.vue';
|
|
|
|
const { t } = useI18n();
|
|
|
|
const { hideTooltips } = storeToRefs(useAppearanceSettingsStore());
|
|
const { showUserDialog } = useUserStore();
|
|
const { currentUser } = storeToRefs(useUserStore());
|
|
const { groupDialog, inviteGroupDialog } = storeToRefs(useGroupStore());
|
|
const {
|
|
getGroupDialogGroup,
|
|
updateGroupPostSearch,
|
|
showGroupDialog,
|
|
leaveGroupPrompt,
|
|
setGroupVisibility,
|
|
applyGroupMember,
|
|
handleGroupMember,
|
|
handleGroupMemberProps
|
|
} = useGroupStore();
|
|
|
|
const { lastLocation } = storeToRefs(useLocationStore());
|
|
const { showFullscreenImageDialog } = useGalleryStore();
|
|
|
|
const instance = getCurrentInstance();
|
|
const $confirm = instance.proxy.$confirm;
|
|
const $message = instance.proxy.$message;
|
|
|
|
const groupDialogRef = ref(null);
|
|
const isGroupMembersDone = ref(false);
|
|
const isGroupMembersLoading = ref(false);
|
|
const groupMembersSearchTimer = ref(null);
|
|
const groupMembersSearchPending = ref(false);
|
|
const groupDialogGalleryCurrentName = ref('0');
|
|
const groupDialogTabCurrentName = ref('0');
|
|
const isGroupGalleryLoading = ref(false);
|
|
const selectedGalleryFile = ref({
|
|
selectedFileId: '',
|
|
selectedImageUrl: ''
|
|
});
|
|
const groupPostEditDialog = reactive({
|
|
visible: false,
|
|
groupRef: {},
|
|
title: '',
|
|
text: '',
|
|
sendNotification: true,
|
|
visibility: 'group',
|
|
roleIds: [],
|
|
postId: '',
|
|
groupId: ''
|
|
});
|
|
const groupMemberModeration = reactive({
|
|
visible: false,
|
|
loading: false,
|
|
id: '',
|
|
groupRef: {},
|
|
auditLogTypes: []
|
|
});
|
|
|
|
let loadMoreGroupMembersParams = ref({
|
|
n: 100,
|
|
offset: 0,
|
|
groupId: '',
|
|
sort: '',
|
|
roleId: ''
|
|
});
|
|
|
|
watch(
|
|
() => groupDialog.value.loading,
|
|
(val) => {
|
|
if (val) {
|
|
nextTick(() => adjustDialogZ(groupDialogRef.value.$el));
|
|
}
|
|
}
|
|
);
|
|
|
|
watch(
|
|
() => groupDialog.value.isGetGroupDialogGroupLoading,
|
|
(val) => {
|
|
if (val) {
|
|
getCurrentTabData();
|
|
}
|
|
}
|
|
);
|
|
|
|
function showInviteGroupDialog(groupId, userId) {
|
|
inviteGroupDialog.value.groupId = groupId;
|
|
inviteGroupDialog.value.userId = userId;
|
|
inviteGroupDialog.value.visible = true;
|
|
}
|
|
|
|
function setGroupRepresentation(groupId) {
|
|
handleGroupRepresentationChange(groupId, true);
|
|
}
|
|
function clearGroupRepresentation(groupId) {
|
|
handleGroupRepresentationChange(groupId, false);
|
|
}
|
|
|
|
function closeMemberModerationDialog() {
|
|
groupMemberModeration.visible = false;
|
|
}
|
|
|
|
function groupMembersSearch() {
|
|
if (groupMembersSearchTimer.value) {
|
|
groupMembersSearchPending.value = true;
|
|
} else {
|
|
groupMembersSearchExecute();
|
|
groupMembersSearchTimer.value = setTimeout(() => {
|
|
if (groupMembersSearchPending.value) {
|
|
groupMembersSearchExecute();
|
|
}
|
|
groupMembersSearchTimer.value = null;
|
|
}, 500);
|
|
}
|
|
}
|
|
|
|
function groupMembersSearchExecute() {
|
|
try {
|
|
groupMembersSearchDebounce();
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
groupMembersSearchTimer.value = null;
|
|
groupMembersSearchPending.value = false;
|
|
}
|
|
|
|
function groupMembersSearchDebounce() {
|
|
const D = groupDialog.value;
|
|
const search = D.memberSearch;
|
|
D.memberSearchResults = [];
|
|
if (!search || search.length < 3) {
|
|
return;
|
|
}
|
|
isGroupMembersLoading.value = false;
|
|
groupRequest
|
|
.getGroupMembersSearch({
|
|
groupId: D.id,
|
|
query: search,
|
|
n: 100,
|
|
offset: 0
|
|
})
|
|
.then((args) => {
|
|
for (const json of args.json.results) {
|
|
handleGroupMember({
|
|
json,
|
|
params: {
|
|
groupId: args.params.groupId
|
|
}
|
|
});
|
|
}
|
|
if (D.id === args.params.groupId) {
|
|
D.memberSearchResults = args.json.results;
|
|
}
|
|
})
|
|
.finally(() => {
|
|
isGroupMembersLoading.value = false;
|
|
});
|
|
}
|
|
|
|
function handleGroupRepresentationChange(groupId, isSet) {
|
|
groupRequest
|
|
.setGroupRepresentation(groupId, {
|
|
isRepresenting: isSet
|
|
})
|
|
.then((args) => {
|
|
if (groupDialog.value.visible && groupDialog.value.id === args.groupId) {
|
|
updateGroupDialogData({
|
|
...groupDialog.value,
|
|
ref: { ...groupDialog.value.ref, isRepresenting: args.params.isRepresenting }
|
|
});
|
|
getGroupDialogGroup(groupId);
|
|
}
|
|
});
|
|
}
|
|
|
|
function cancelGroupRequest(id) {
|
|
groupRequest
|
|
.cancelGroupRequest({
|
|
groupId: id
|
|
})
|
|
.then((args) => {
|
|
if (groupDialog.value.visible && groupDialog.value.id === id) {
|
|
getGroupDialogGroup(id);
|
|
}
|
|
});
|
|
}
|
|
function confirmDeleteGroupPost(post) {
|
|
$confirm('Are you sure you want to delete this post?', 'Confirm', {
|
|
confirmButtonText: 'Confirm',
|
|
cancelButtonText: 'Cancel',
|
|
type: 'info',
|
|
callback: (action) => {
|
|
if (action === 'confirm') {
|
|
groupRequest
|
|
.deleteGroupPost({
|
|
groupId: post.groupId,
|
|
postId: post.id
|
|
})
|
|
.then((args) => {
|
|
const D = groupDialog.value;
|
|
if (D.id !== args.params.groupId) {
|
|
return;
|
|
}
|
|
|
|
const postId = args.params.postId;
|
|
// remove existing post
|
|
for (const item of D.posts) {
|
|
if (item.id === postId) {
|
|
removeFromArray(D.posts, item);
|
|
break;
|
|
}
|
|
}
|
|
// remove/update announcement
|
|
if (postId === D.announcement.id) {
|
|
if (D.posts.length > 0) {
|
|
D.announcement = D.posts[0];
|
|
} else {
|
|
D.announcement = {};
|
|
}
|
|
}
|
|
updateGroupPostSearch();
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function groupGalleryStatus(gallery) {
|
|
const style = {};
|
|
if (!gallery.membersOnly) {
|
|
style.blue = true;
|
|
} else if (!gallery.roleIdsToView) {
|
|
style.green = true;
|
|
} else {
|
|
style.red = true;
|
|
}
|
|
return style;
|
|
}
|
|
|
|
function groupDialogCommand(command) {
|
|
const D = groupDialog.value;
|
|
if (D.visible === false) {
|
|
return;
|
|
}
|
|
switch (command) {
|
|
case 'Share':
|
|
copyToClipboard(groupDialog.value.ref.$url);
|
|
break;
|
|
case 'Create Post':
|
|
showGroupPostEditDialog(groupDialog.value.id, null);
|
|
break;
|
|
case 'Moderation Tools':
|
|
showGroupMemberModerationDialog(groupDialog.value.id);
|
|
break;
|
|
case 'Invite To Group':
|
|
showInviteGroupDialog(D.id, '');
|
|
break;
|
|
case 'Refresh':
|
|
showGroupDialog(D.id);
|
|
break;
|
|
case 'Leave Group':
|
|
leaveGroupPrompt(D.id);
|
|
break;
|
|
case 'Block Group':
|
|
blockGroup(D.id);
|
|
break;
|
|
case 'Unblock Group':
|
|
unblockGroup(D.id);
|
|
break;
|
|
case 'Visibility Everyone':
|
|
setGroupVisibility(D.id, 'visible');
|
|
break;
|
|
case 'Visibility Friends':
|
|
setGroupVisibility(D.id, 'friends');
|
|
break;
|
|
case 'Visibility Hidden':
|
|
setGroupVisibility(D.id, 'hidden');
|
|
break;
|
|
case 'Subscribe To Announcements':
|
|
setGroupSubscription(D.id, true);
|
|
break;
|
|
case 'Unsubscribe To Announcements':
|
|
setGroupSubscription(D.id, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
function setGroupSubscription(groupId, subscribe) {
|
|
return groupRequest
|
|
.setGroupMemberProps(currentUser.value.id, groupId, {
|
|
isSubscribedToAnnouncements: subscribe
|
|
})
|
|
.then((args) => {
|
|
handleGroupMemberProps(args);
|
|
$app.$message({
|
|
message: 'Group subscription updated',
|
|
type: 'success'
|
|
});
|
|
return args;
|
|
});
|
|
}
|
|
|
|
function blockGroup(groupId) {
|
|
$app.$confirm('Are you sure you want to block this group?', 'Confirm', {
|
|
confirmButtonText: 'Confirm',
|
|
cancelButtonText: 'Cancel',
|
|
type: 'info',
|
|
callback: (action) => {
|
|
if (action === 'confirm') {
|
|
groupRequest
|
|
.blockGroup({
|
|
groupId
|
|
})
|
|
.then((args) => {
|
|
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
|
|
showGroupDialog(args.params.groupId);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function unblockGroup(groupId) {
|
|
$app.$confirm('Are you sure you want to unblock this group?', 'Confirm', {
|
|
confirmButtonText: 'Confirm',
|
|
cancelButtonText: 'Cancel',
|
|
type: 'info',
|
|
callback: (action) => {
|
|
if (action === 'confirm') {
|
|
groupRequest
|
|
.unblockGroup({
|
|
groupId,
|
|
userId: currentUser.value.id
|
|
})
|
|
.then((args) => {
|
|
if (groupDialog.value.visible && groupDialog.value.id === args.params.groupId) {
|
|
showGroupDialog(args.params.groupId);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function showGroupMemberModerationDialog(groupId) {
|
|
if (groupId !== groupDialog.value.id) {
|
|
return;
|
|
}
|
|
const D = groupMemberModeration;
|
|
D.id = groupId;
|
|
|
|
D.groupRef = {};
|
|
D.auditLogTypes = [];
|
|
groupRequest.getCachedGroup({ groupId }).then((args) => {
|
|
D.groupRef = args.ref;
|
|
if (hasGroupPermission(D.groupRef, 'group-audit-view')) {
|
|
groupRequest.getGroupAuditLogTypes({ groupId }).then((args) => {
|
|
if (groupMemberModeration.id !== args.params.groupId) {
|
|
return;
|
|
}
|
|
groupMemberModeration.auditLogTypes = args.json;
|
|
});
|
|
}
|
|
});
|
|
D.visible = true;
|
|
}
|
|
function joinGroup(id) {
|
|
if (!id) {
|
|
return null;
|
|
}
|
|
return groupRequest
|
|
.joinGroup({
|
|
groupId: id
|
|
})
|
|
.then((args) => {
|
|
if (groupDialog.value.visible && groupDialog.value.id === id) {
|
|
updateGroupDialogData({
|
|
...groupDialog.value,
|
|
inGroup: args.json.membershipStatus === 'member'
|
|
});
|
|
// groupDialog.value.inGroup = json.membershipStatus === 'member';
|
|
getGroupDialogGroup(id);
|
|
}
|
|
if (args.json.membershipStatus === 'member') {
|
|
$message({
|
|
message: 'Group joined',
|
|
type: 'success'
|
|
});
|
|
} else if (args.json.membershipStatus === 'requested') {
|
|
$message({
|
|
message: 'Group join request sent',
|
|
type: 'success'
|
|
});
|
|
}
|
|
return args;
|
|
});
|
|
}
|
|
function groupDialogTabClick(obj) {
|
|
if (obj.label === t('dialog.group.members.header')) {
|
|
getGroupDialogGroupMembers();
|
|
} else if (obj.label === t('dialog.group.gallery.header')) {
|
|
getGroupGalleries();
|
|
} else if (obj.label === t('dialog.group.json.header')) {
|
|
refreshGroupDialogTreeData();
|
|
}
|
|
}
|
|
|
|
function showGroupPostEditDialog(groupId, post) {
|
|
const D = groupPostEditDialog;
|
|
D.sendNotification = true;
|
|
D.groupRef = {};
|
|
D.title = '';
|
|
D.text = '';
|
|
D.visibility = 'group';
|
|
D.roleIds = [];
|
|
D.postId = '';
|
|
D.groupId = groupId;
|
|
selectedGalleryFile.value = {
|
|
selectedFileId: '',
|
|
selectedImageUrl: ''
|
|
};
|
|
|
|
if (post) {
|
|
D.title = post.title;
|
|
D.text = post.text;
|
|
D.visibility = post.visibility;
|
|
D.roleIds = post.roleIds;
|
|
D.postId = post.id;
|
|
selectedGalleryFile.value = {
|
|
selectedFileId: post.imageId,
|
|
selectedImageUrl: post.imageUrl
|
|
};
|
|
}
|
|
groupRequest.getCachedGroup({ groupId }).then((args) => {
|
|
D.groupRef = args.ref;
|
|
});
|
|
D.visible = true;
|
|
}
|
|
|
|
async function getGroupDialogGroupMembers() {
|
|
const D = groupDialog.value;
|
|
D.members = [];
|
|
isGroupMembersDone.value = false;
|
|
loadMoreGroupMembersParams.value = {
|
|
n: 100,
|
|
offset: 0,
|
|
groupId: D.id,
|
|
...loadMoreGroupMembersParams.value
|
|
};
|
|
if (D.memberSortOrder.value) {
|
|
loadMoreGroupMembersParams.value.sort = D.memberSortOrder.value;
|
|
}
|
|
if (D.memberFilter.id !== null) {
|
|
loadMoreGroupMembersParams.value.roleId = D.memberFilter.id;
|
|
}
|
|
if (D.inGroup) {
|
|
await groupRequest
|
|
.getGroupMember({
|
|
groupId: D.id,
|
|
userId: currentUser.value.id
|
|
})
|
|
.then((args) => {
|
|
args.ref = applyGroupMember(args.json);
|
|
if (args.json) {
|
|
args.json.user = currentUser.value;
|
|
if (D.memberFilter.id === null) {
|
|
// when flitered by role don't include self
|
|
D.members.push(args.json);
|
|
}
|
|
}
|
|
return args;
|
|
});
|
|
}
|
|
await loadMoreGroupMembers();
|
|
}
|
|
|
|
async function loadMoreGroupMembers() {
|
|
if (isGroupMembersDone.value || isGroupMembersLoading.value) {
|
|
return;
|
|
}
|
|
const D = groupDialog.value;
|
|
const params = loadMoreGroupMembersParams.value;
|
|
D.memberSearch = '';
|
|
isGroupMembersLoading.value = true;
|
|
await groupRequest
|
|
.getGroupMembers(params)
|
|
.finally(() => {
|
|
isGroupMembersLoading.value = false;
|
|
})
|
|
.then((args) => {
|
|
for (const json of args.json) {
|
|
handleGroupMember({
|
|
json,
|
|
params: {
|
|
groupId: args.params.groupId
|
|
}
|
|
});
|
|
}
|
|
for (let i = 0; i < args.json.length; i++) {
|
|
const member = args.json[i];
|
|
if (member.userId === currentUser.value.id) {
|
|
if (D.members.length > 0 && D.members[0].userId === currentUser.value.id) {
|
|
// remove duplicate and keep sort order
|
|
D.members.splice(0, 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (args.json.length < params.n) {
|
|
isGroupMembersDone.value = true;
|
|
}
|
|
D.members = [...D.members, ...args.json];
|
|
params.offset += params.n;
|
|
return args;
|
|
})
|
|
.catch((err) => {
|
|
isGroupMembersDone.value = true;
|
|
throw err;
|
|
});
|
|
}
|
|
|
|
function getCurrentTabData() {
|
|
if (groupDialogTabCurrentName.value === '2') {
|
|
getGroupDialogGroupMembers();
|
|
} else if (groupDialogTabCurrentName.value === '3') {
|
|
getGroupGalleries();
|
|
} else if (groupDialogTabCurrentName.value === '4') {
|
|
refreshGroupDialogTreeData();
|
|
}
|
|
}
|
|
|
|
async function getGroupGalleries() {
|
|
updateGroupDialogData({ ...groupDialog.value, galleries: {} });
|
|
groupDialogGalleryCurrentName.value = '0';
|
|
isGroupGalleryLoading.value = true;
|
|
for (let i = 0; i < groupDialog.value.ref.galleries.length; i++) {
|
|
const gallery = groupDialog.value.ref.galleries[i];
|
|
await getGroupGallery(groupDialog.value.id, gallery.id);
|
|
}
|
|
isGroupGalleryLoading.value = false;
|
|
}
|
|
|
|
async function getGroupGallery(groupId, galleryId) {
|
|
try {
|
|
const params = {
|
|
groupId,
|
|
galleryId,
|
|
n: 100,
|
|
offset: 0
|
|
};
|
|
const count = 50; // 5000 max
|
|
for (let i = 0; i < count; i++) {
|
|
const args = await groupRequest.getGroupGallery(params);
|
|
if (args) {
|
|
for (const json of args.json) {
|
|
if (groupDialog.value.id === json.groupId) {
|
|
if (!groupDialog.value.galleries[json.galleryId]) {
|
|
groupDialog.value.galleries[json.galleryId] = [];
|
|
}
|
|
groupDialog.value.galleries[json.galleryId].push(json);
|
|
}
|
|
}
|
|
}
|
|
params.offset += 100;
|
|
if (args.json.length < 100) {
|
|
break;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
}
|
|
|
|
function refreshGroupDialogTreeData() {
|
|
const D = groupDialog.value;
|
|
const treeData = buildTreeData({
|
|
group: D.ref,
|
|
posts: D.posts,
|
|
instances: D.instances,
|
|
members: D.members,
|
|
galleries: D.galleries
|
|
});
|
|
updateGroupDialogData({
|
|
...groupDialog.value,
|
|
treeData
|
|
});
|
|
}
|
|
|
|
async function loadAllGroupMembers() {
|
|
if (isGroupMembersLoading.value) {
|
|
return;
|
|
}
|
|
await getGroupDialogGroupMembers();
|
|
while (groupDialog.value.visible && !isGroupMembersDone.value) {
|
|
isGroupMembersLoading.value = true;
|
|
await new Promise((resolve) => {
|
|
workerTimers.setTimeout(resolve, 1000);
|
|
});
|
|
isGroupMembersLoading.value = false;
|
|
await loadMoreGroupMembers();
|
|
}
|
|
}
|
|
|
|
async function setGroupMemberSortOrder(sortOrder) {
|
|
const D = groupDialog.value;
|
|
if (D.memberSortOrder.value === sortOrder) {
|
|
return;
|
|
}
|
|
D.memberSortOrder = sortOrder;
|
|
await getGroupDialogGroupMembers();
|
|
}
|
|
|
|
async function setGroupMemberFilter(filter) {
|
|
const D = groupDialog.value;
|
|
if (D.memberFilter === filter) {
|
|
return;
|
|
}
|
|
D.memberFilter = filter;
|
|
await getGroupDialogGroupMembers();
|
|
}
|
|
|
|
function updateGroupDialogData(obj) {
|
|
groupDialog.value = {
|
|
...groupDialog.value,
|
|
...obj
|
|
};
|
|
}
|
|
</script>
|