refactor: Group Dialog (#1207)

This commit is contained in:
pa
2025-04-08 19:49:25 +09:00
committed by GitHub
parent dc5f48808f
commit 63cd5421e2
28 changed files with 5553 additions and 4465 deletions

View File

@@ -1,6 +1,6 @@
{
"root": true,
"extends": ["eslint:all", "plugin:vue/recommended", "prettier"],
"extends": ["eslint:recommended", "plugin:vue/recommended", "prettier"],
"env": {
"browser": true,
"commonjs": true,
@@ -36,28 +36,7 @@
"AssetBundleManager": "readonly"
},
"rules": {
"arrow-body-style": 0,
"block-scoped-var": 0,
"camelcase": 0,
"capitalized-comments": 0,
"class-methods-use-this": 0,
"complexity": 0,
"default-case": 0,
"func-names": 0,
"func-style": 0,
"guard-for-in": 0,
"id-length": 0,
"line-comment-position": 0,
"max-depth": 0,
"max-lines": 0,
"max-lines-per-function": 0,
"max-params": 0,
"max-statements": 0,
"multiline-comment-style": 0,
"new-cap": 0,
"no-await-in-loop": 0,
"no-console": 0,
"no-continue": 0,
"no-control-regex": 0,
"no-empty": [
"error",
@@ -65,29 +44,9 @@
"allowEmptyCatch": true
}
],
"no-inline-comments": 0,
"no-invalid-this": 0,
"no-var": "warn",
"prefer-const": "warn",
"no-loop-func": 0,
"no-magic-numbers": 0,
"no-negated-condition": 0,
"no-plusplus": 0,
"no-redeclare": 0,
"no-ternary": 0,
"no-throw-literal": 0,
"no-underscore-dangle": 0,
"no-var": 0,
"no-void": 0,
"no-warning-comments": 0,
"one-var": 0,
"prefer-arrow-callback": 0,
"prefer-const": 0,
"prefer-destructuring": 0,
"prefer-named-capture-group": 0,
"require-unicode-regexp": 0,
"sort-imports": 0,
"sort-keys": 0,
"sort-vars": 0,
"strict": 0,
"vars-on-top": 0,
"object-curly-spacing": ["error", "always"],
"require-atomic-updates": 0

100
package-lock.json generated
View File

@@ -47,6 +47,7 @@
"vue-data-tables": "^3.4.5",
"vue-demi": "^0.14.10",
"vue-i18n": "^8.28.2",
"vue-i18n-bridge": "^9.14.1",
"vue-lazyload": "^1.3.4",
"vue-loader": "^15.11.1",
"vue-markdown": "^2.2.4",
@@ -1382,6 +1383,70 @@
"integrity": "sha512-B6kpvqeD0ukTR7sydGKpktvO3VhZkOwQxAdLLGPdSHxQxREa2+sH6B9ODop6quPGjhmsZkJ/hL01rQ8At5xDew==",
"dev": true
},
"node_modules/@intlify/core-base": {
"version": "9.14.1",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.1.tgz",
"integrity": "sha512-rG5/hlNW6Qfve41go37szEf0mVLcfhYuOu83JcY0jZKasnwsrcZYYWDzebCcuO5I/6Sy1JFWo9p+nvkQS1Dy+w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@intlify/message-compiler": "9.14.1",
"@intlify/shared": "9.14.1"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/message-compiler": {
"version": "9.14.1",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.1.tgz",
"integrity": "sha512-MY8hwukJBnXvGAncVKlHsqKDQ5ZcQx4peqEmI8wBUTXn4pezrtTGYXNoz81cLyEEHB+L/zlKWVBSh5TiX4gYoQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@intlify/shared": "9.14.1",
"source-map-js": "^1.0.2"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/shared": {
"version": "9.14.1",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.1.tgz",
"integrity": "sha512-XjHu6PEQup9MnP1x0W9y0nXXfq9jFftAYSfV11hryjtH4XqXP8HrzMvXI+ZVifF+jZLszaTzIhvukllplxTQTg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@intlify/vue-devtools": {
"version": "9.14.1",
"resolved": "https://registry.npmjs.org/@intlify/vue-devtools/-/vue-devtools-9.14.1.tgz",
"integrity": "sha512-twkyipsHAVXesM0edbQ8oaePUaQdgjlftX6udQvNsgItXf3qK+gLM6NFEduvoruexOWi7iipLXL5Luk7B9yf9Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@intlify/core-base": "9.14.1",
"@intlify/shared": "9.14.1"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -2289,6 +2354,13 @@
"dev": true,
"license": "ISC"
},
"node_modules/@vue/devtools-api": {
"version": "6.6.4",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/ast": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
@@ -9695,6 +9767,34 @@
"integrity": "sha512-C5GZjs1tYlAqjwymaaCPDjCyGo10ajUphiwA922jKt9n7KPpqR7oM1PCwYzhB/E7+nT3wfdG3oRre5raIT1rKA==",
"dev": true
},
"node_modules/vue-i18n-bridge": {
"version": "9.14.1",
"resolved": "https://registry.npmjs.org/vue-i18n-bridge/-/vue-i18n-bridge-9.14.1.tgz",
"integrity": "sha512-KmCO/Pk5RwqtHqT1k/njhaGuSQmO6RKjtf4QK7aQVoUKlWBkRBwI4PWQRUjne09E9txBncw6dSZzRdiVew/oFw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@intlify/core-base": "9.14.1",
"@intlify/shared": "9.14.1",
"@intlify/vue-devtools": "9.14.1",
"@vue/devtools-api": "^6.5.0",
"vue-demi": "^0.14.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/kazupon"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/vue-lazyload": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/vue-lazyload/-/vue-lazyload-1.3.4.tgz",

View File

@@ -63,6 +63,7 @@
"vue-data-tables": "^3.4.5",
"vue-demi": "^0.14.10",
"vue-i18n": "^8.28.2",
"vue-i18n-bridge": "^9.14.1",
"vue-lazyload": "^1.3.4",
"vue-loader": "^15.11.1",
"vue-markdown": "^2.2.4",

View File

@@ -14,6 +14,7 @@ import Noty from 'noty';
import Vue from 'vue';
import VueLazyload from 'vue-lazyload';
import VueI18n from 'vue-i18n';
import { createI18n } from 'vue-i18n-bridge';
import { DataTables } from 'vue-data-tables';
import ElementUI from 'element-ui';
import dayjs from 'dayjs';
@@ -49,7 +50,8 @@ import {
inviteMessagesRequest,
miscRequest,
imageRequest,
vrcPlusImageRequest
vrcPlusImageRequest,
groupRequest
} from './classes/request';
// tabs
@@ -75,6 +77,8 @@ import PreviousInstancesUserDialog from './views/dialogs/previousInstances/Previ
import FavoriteDialog from './views/dialogs/favoritesDialog/FavoriteDialog.vue';
import ExportFriendsListDialog from './views/dialogs/favoritesDialog/ExportFriendsListDialog.vue';
import ExportAvatarsListDialog from './views/dialogs/favoritesDialog/ExportAvatarsListDialog.vue';
import GroupDialog from './views/dialogs/groupDialog/GroupDialog.vue';
import InviteGroupDialog from './views/dialogs/groupDialog/InviteGroupDialog.vue';
// main app classes
import _sharedFeed from './classes/sharedFeed.js';
@@ -136,16 +140,24 @@ console.log(`isLinux: ${LINUX}`);
}
// #region | localization
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: 'en',
fallbackLocale: 'en',
messages: localizedStrings,
silentTranslationWarn: true
});
const $t = i18n.t.bind(i18n);
Vue.use(VueI18n, { bridge: true });
const i18n = createI18n(
{
locale: 'en',
fallbackLocale: 'en',
messages: localizedStrings,
legacy: false,
globalInjection: true,
missingWarn: false,
warnHtmlMessage: false,
fallbackWarn: false
},
VueI18n
);
const $t = i18n.global.t;
Vue.use(i18n);
Vue.use(ElementUI, {
i18n: (key, value) => i18n.t(key, value)
i18n: (key, value) => i18n.global.t(key, value)
});
// #endregion
@@ -225,6 +237,9 @@ console.log(`isLinux: ${LINUX}`);
PreviousInstancesUserDialog,
// - world
WorldDialog,
// - group
GroupDialog,
InviteGroupDialog,
// - favorites
FriendImportDialog,
WorldImportDialog,
@@ -248,7 +263,6 @@ console.log(`isLinux: ${LINUX}`);
userImageFull: this.userImageFull,
showFullscreenImageDialog: this.showFullscreenImageDialog,
statusClass: this.statusClass,
getFaviconUrl: this.getFaviconUrl,
openExternalLink: this.openExternalLink,
beforeDialogClose: this.beforeDialogClose,
dialogMouseDown: this.dialogMouseDown,
@@ -260,7 +274,10 @@ console.log(`isLinux: ${LINUX}`);
showInviteDialog: this.showInviteDialog,
showLaunchDialog: this.showLaunchDialog,
showFavoriteDialog: this.showFavoriteDialog,
displayPreviousImages: this.displayPreviousImages
displayPreviousImages: this.displayPreviousImages,
languageClass: this.languageClass,
showGroupDialog: this.showGroupDialog,
showGallerySelectDialog: this.showGallerySelectDialog
};
},
el: '#root',
@@ -287,15 +304,9 @@ console.log(`isLinux: ${LINUX}`);
this.enableAppLauncher,
this.enableAppLauncherAutoClose
);
API.$on('SHOW_USER_DIALOG', (userId) =>
this.showUserDialog(userId)
);
API.$on('SHOW_WORLD_DIALOG_SHORTNAME', (tag) =>
this.verifyShortName('', tag)
);
API.$on('SHOW_GROUP_DIALOG', (groupId) =>
this.showGroupDialog(groupId)
);
this.updateLoop();
this.getGameLogTable();
this.refreshCustomCss();
@@ -4169,13 +4180,6 @@ console.log(`isLinux: ${LINUX}`);
$app.data.gameLogTable.pageSize = $app.data.tablePageSize;
$app.data.feedTable.pageSize = $app.data.tablePageSize;
$app.data.groupMemberModerationTable.pageSize = $app.data.tablePageSize;
$app.data.groupBansModerationTable.pageSize = $app.data.tablePageSize;
$app.data.groupLogsModerationTable.pageSize = $app.data.tablePageSize;
$app.data.groupInvitesModerationTable.pageSize = $app.data.tablePageSize;
$app.data.groupJoinRequestsModerationTable.pageSize =
$app.data.tablePageSize;
$app.data.groupBlockedModerationTable.pageSize = $app.data.tablePageSize;
$app.data.dontLogMeOut = false;
@@ -5559,11 +5563,22 @@ console.log(`isLinux: ${LINUX}`);
}
}
this.isSearchGroupLoading = true;
await API.groupSearch(params)
await groupRequest
.groupSearch(params)
.finally(() => {
this.isSearchGroupLoading = false;
})
.then((args) => {
// API.$on('GROUP:SEARCH', function (args) {
for (const json of args.json) {
API.$emit('GROUP', {
json,
params: {
groupId: json.id
}
});
}
// });
var map = new Map();
for (var json of args.json) {
var ref = API.cachedGroups.get(json.id);
@@ -8263,8 +8278,18 @@ console.log(`isLinux: ${LINUX}`);
};
$app.methods.showGroupDialogShortCode = function (shortCode) {
API.groupStrictsearch({ query: shortCode }).then((args) => {
for (var group of args.json) {
groupRequest.groupStrictsearch({ query: shortCode }).then((args) => {
for (const group of args.json) {
// API.$on('GROUP:STRICTSEARCH', function (args) {
// for (var json of args.json) {
API.$emit('GROUP', {
group,
params: {
groupId: group.id
}
});
// }
// });
if (`${group.shortCode}.${group.discriminator}` === shortCode) {
this.showGroupDialog(group.id);
}
@@ -8461,15 +8486,6 @@ console.log(`isLinux: ${LINUX}`);
await this.sortCurrentUserGroups();
};
$app.methods.getFaviconUrl = function (resource) {
try {
var url = new URL(resource);
return `https://icons.duckduckgo.com/ip2/${url.host}.ico`;
} catch (err) {
return '';
}
};
API.$on('LOGOUT', function () {
$app.userDialog.visible = false;
});
@@ -8912,14 +8928,18 @@ console.log(`isLinux: ${LINUX}`);
}
});
}
API.getRepresentedGroup({ userId }).then((args1) => {
D.representedGroup = args1.json;
D.representedGroup.$thumbnailUrl =
this.getSmallThumbnailUrl(args1.json.iconUrl);
if (!args1.json || !args1.json.isRepresenting) {
D.isRepresentedGroupLoading = false;
}
});
groupRequest
.getRepresentedGroup({ userId })
.then((args1) => {
D.representedGroup = args1.json;
D.representedGroup.$thumbnailUrl =
this.getSmallThumbnailUrl(
args1.json.iconUrl
);
if (!args1.json || !args1.json.isRepresenting) {
D.isRepresentedGroupLoading = false;
}
});
D.loading = false;
});
}
@@ -10943,7 +10963,7 @@ console.log(`isLinux: ${LINUX}`);
this.showSetAvatarTagsDialog(D.id);
break;
case 'Download Unity Package':
$utils.openExternalLink(
this.openExternalLink(
this.replaceVrcPackageUrl(
this.avatarDialog.ref.unityPackageUrl
)
@@ -11639,7 +11659,7 @@ console.log(`isLinux: ${LINUX}`);
if (
D.ageGate &&
type === 'group' &&
this.hasGroupPermission(
$utils.hasGroupPermission(
D.groupRef,
'group-instance-age-gated-create'
)
@@ -12196,22 +12216,6 @@ console.log(`isLinux: ${LINUX}`);
this.copyToClipboard(displayName);
};
$app.methods.copyGroupId = function (groupId) {
this.$message({
message: 'Group ID copied to clipboard',
type: 'success'
});
this.copyToClipboard(groupId);
};
$app.methods.copyGroupUrl = function (groupUrl) {
this.$message({
message: 'Group URL copied to clipboard',
type: 'success'
});
this.copyToClipboard(groupUrl);
};
$app.methods.copyImageUrl = function (imageUrl) {
this.$message({
message: 'ImageUrl copied to clipboard',
@@ -15134,6 +15138,7 @@ console.log(`isLinux: ${LINUX}`);
return style;
};
// TODO: launch, invite, refresh, etc. buttons, better to split into one component
$app.methods.refreshInstancePlayerCount = function (instance) {
var L = $utils.parseLocation(instance);
if (L.isRealInstance) {
@@ -15161,7 +15166,7 @@ console.log(`isLinux: ${LINUX}`);
mutualGroups: [],
remainingGroups: []
};
var args = await API.getGroups({ userId });
var args = await groupRequest.getGroups({ userId });
if (userId !== this.userDialog.id) {
this.userDialog.isGroupsLoading = false;
return;
@@ -18649,6 +18654,8 @@ console.log(`isLinux: ${LINUX}`);
}
};
// use in avatar, user, group dialog
// TODO: better in utils
$app.methods.downloadAndSaveJson = function (fileName, data) {
if (!fileName || !data) {
return;
@@ -18752,6 +18759,9 @@ console.log(`isLinux: ${LINUX}`);
};
};
$app.data.groupDialogSortingOptions = {};
$app.data.groupDialogFilterOptions = {};
$app.methods.applyGroupDialogSortingStrings = function () {
this.groupDialogSortingOptions = {
joinedAtDesc: {
@@ -19299,7 +19309,6 @@ console.log(`isLinux: ${LINUX}`);
hideTooltips: this.hideTooltips,
randomUserColours: this.randomUserColours,
sortStatus: this.sortStatus,
languageClass: this.languageClass,
confirmDeleteFriend: this.confirmDeleteFriend,
friendsListSearch: this.friendsListSearch,
stringComparer: this.stringComparer
@@ -19626,6 +19635,54 @@ console.log(`isLinux: ${LINUX}`);
};
};
$app.computed.groupDialogBind = function () {
return {
'group-dialog': this.groupDialog,
'hide-tooltips': this.hideTooltips,
'last-location': this.lastLocation,
'update-instance-info': this.updateInstanceInfo,
'group-dialog-sorting-options': this.groupDialogSortingOptions,
'group-dialog-filter-options': this.groupDialogFilterOptions,
'is-group-gallery-loading': this.isGroupGalleryLoading,
'gallery-select-dialog': this.gallerySelectDialog,
'random-user-colours': this.randomUserColours
};
};
$app.computed.groupDialogEvent = function () {
return {
'refresh-instance-player-count': this.refreshInstancePlayerCount,
'update-group-post-search': this.updateGroupPostSearch,
'download-and-save-json': this.downloadAndSaveJson,
'group-dialog-command': this.groupDialogCommand,
'get-group-dialog-group': this.getGroupDialogGroup,
'clear-image-gallery-select': this.clearImageGallerySelect,
'update:gallery-select-dialog': (val) =>
(this.gallerySelectDialog = val),
'update:group-dialog': (val) => {
this.groupDialog = val;
}
};
};
$app.computed.inviteGroupDialogBind = function () {
return {
'dialog-data': this.inviteGroupDialog,
'vip-friends': this.vipFriends,
'online-friends': this.onlineFriends,
'offline-friends': this.offlineFriends,
'active-friends': this.activeFriends
};
};
$app.computed.inviteGroupDialogEvent = function () {
return {
'update:dialog-data': (val) => {
this.inviteGroupDialog = val;
}
};
};
// #endregion
// #region | Electron

View File

@@ -80,9 +80,6 @@ doctype html
include ./mixins/dialogs/avatarDialog.pug
+avatarDialog
include ./mixins/dialogs/groupDialog.pug
+groupDialog
include ./mixins/dialogs/images.pug
+images
@@ -92,9 +89,6 @@ doctype html
include ./mixins/dialogs/openSourceSoftwareNotice.pug
+openSourceSoftwareNotice
include ./mixins/dialogs/groups.pug
+groups
include ./mixins/dialogs/currentUser.pug
+currentUser
@@ -144,4 +138,9 @@ doctype html
//- world
world-dialog(v-bind='worldDialogBind' v-on='worldDialogEvent')
//- group
GroupDialog(v-bind='groupDialogBind' v-on='groupDialogEvent')
InviteGroupDialog(v-bind='inviteGroupDialogBind' v-on='inviteGroupDialogEvent')
//- el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="templateDialog" :visible.sync="templateDialog.visible" :title="$t('dialog.template_dialog.header')" width="450px")

View File

@@ -560,7 +560,6 @@ img.friends-list-avatar {
.x-dialog > .el-dialog {
max-width: 100%;
margin-bottom: 10px;
}
.x-user-dialog > .el-dialog > .el-dialog__header,

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,773 @@
const groupReq = {
/**
* @param {string} groupId
* @param {{isRepresenting: bool}} params
* @returns
*/
setGroupRepresentation(groupId, params) {
return window.API.call(`groups/${groupId}/representation`, {
method: 'PUT',
params
}).then((json) => {
const args = {
json,
groupId,
params
};
return args;
});
},
/**
* @param {{ groupId: string }} params
* @return { Promise<{json: any, params}> }
*/
cancelGroupRequest(params) {
return window.API.call(`groups/${params.groupId}/requests`, {
method: 'DELETE'
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ groupId: string, postId: string }} params
* @return { Promise<{json: any, params}> }
*/
deleteGroupPost(params) {
return window.API.call(
`groups/${params.groupId}/posts/${params.postId}`,
{
method: 'DELETE'
}
).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ groupId: string }} params
*/
getGroup(params) {
return window.API.call(`groups/${params.groupId}`, {
method: 'GET',
params: {
includeRoles: params.includeRoles || false
}
}).then((json) => {
const args = {
json,
params
};
window.API.$emit('GROUP', args);
return args;
});
},
/**
* @param {{ userId: string }} params
* @return { Promise<{json: any, params}> }
*/
getRepresentedGroup(params) {
return window.API.call(`users/${params.userId}/groups/represented`, {
method: 'GET'
}).then((json) => {
const args = {
json,
params
};
window.API.$emit('GROUP:REPRESENTED', args);
return args;
});
},
/**
* @param {{ userId: string }} params
* @return { Promise<{json: any, params}> }
*/
getGroups(params) {
return window.API.call(`users/${params.userId}/groups`, {
method: 'GET'
}).then((json) => {
const args = {
json,
params
};
window.API.$emit('GROUP:LIST', args);
return args;
});
},
/**
* @param {{ groupId: string }} params
* @return { Promise<{json: any, params}> }
*/
joinGroup(params) {
return window.API.call(`groups/${params.groupId}/join`, {
method: 'POST'
}).then((json) => {
const args = {
json,
params
};
window.API.$emit('GROUP:JOIN', args);
return args;
});
},
/**
* @param {{ groupId: string }} params
* @return { Promise<{json: any, params}> }
*/
leaveGroup(params) {
return window.API.call(`groups/${params.groupId}/leave`, {
method: 'POST'
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ query: string }} params
* @return { Promise<{json: any, params}> }
*/
groupStrictsearch(params) {
return window.API.call(`groups/strictsearch`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
userId: string,
groupId: string,
params: {
visibility: string,
isSubscribedToAnnouncements: bool,
managerNotes: string
}
*/
setGroupMemberProps(userId, groupId, params) {
return window.API.call(`groups/${groupId}/members/${userId}`, {
method: 'PUT',
params
}).then((json) => {
const args = {
json,
userId,
groupId,
params
};
window.API.$emit('GROUP:MEMBER:PROPS', args);
return args;
});
},
/**
* @param {{
* userId: string,
* groupId: string,
* roleId: string
* }} params
* @return { Promise<{json: any, params}> }
*/
addGroupMemberRole(params) {
return window.API.call(
`groups/${params.groupId}/members/${params.userId}/roles/${params.roleId}`,
{
method: 'PUT'
}
).then((json) => {
const args = {
json,
params
};
// window.API.$emit('GROUP:MEMBER:ROLE:CHANGE', args);
return args;
});
},
/**
* @param {{
* userId: string,
* groupId: string,
* roleId: string
* }} params
* @return { Promise<{json: any, params}> }
*/
removeGroupMemberRole(params) {
return window.API.call(
`groups/${params.groupId}/members/${params.userId}/roles/${params.roleId}`,
{
method: 'DELETE'
}
).then((json) => {
const args = {
json,
params
};
// window.API.$emit('GROUP:MEMBER:ROLE:CHANGE', args);
return args;
});
},
getGroupPermissions(params) {
return window.API.call(`users/${params.userId}/groups/permissions`, {
method: 'GET'
}).then((json) => {
const args = {
json,
params
};
window.API.$emit('GROUP:PERMISSIONS', args);
return args;
});
},
/**
* @param {{
groupId: string,
n: number,
offset: number
}} params
* @return { Promise<{json: any, params}> }
*/
getGroupPosts(params) {
return window.API.call(`groups/${params.groupId}/posts`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
window.API.$emit('GROUP:POSTS', args);
return args;
});
},
editGroupPost(params) {
return window.API.call(
`groups/${params.groupId}/posts/${params.postId}`,
{
method: 'PUT',
params
}
).then((json) => {
const args = {
json,
params
};
window.API.$emit('GROUP:POST', args);
return args;
});
},
createGroupPost(params) {
return window.API.call(`groups/${params.groupId}/posts`, {
method: 'POST',
params
}).then((json) => {
const args = {
json,
params
};
window.API.$emit('GROUP:POST', args);
return args;
});
},
/**
* @param {{
* groupId: string,
* userId: string
* }} params
* @return { Promise<{json: any, params}> }
*/
getGroupMember(params) {
return window.API.call(
`groups/${params.groupId}/members/${params.userId}`,
{
method: 'GET'
}
).then((json) => {
const args = {
json,
params
};
// window.API.$emit('GROUP:MEMBER', args);
return args;
});
},
/**
* @param {{
* groupId: string,
* n: number,
* offset: number
* }} params
* @return { Promise<{json: any, params}> }
*/
getGroupMembers(params) {
return window.API.call(`groups/${params.groupId}/members`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
window.API.$emit('GROUP:MEMBERS', args);
return args;
});
},
/**
* @param {{
* groupId: string,
* query: string,
* n: number,
* offset: number
* }} params
* @return { Promise<{json: any, params}> }
*/
getGroupMembersSearch(params) {
return window.API.call(`groups/${params.groupId}/members/search`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{
* groupId: string
* }} params
* @return { Promise<{json: any, params}> }
*/
blockGroup(params) {
return window.API.call(`groups/${params.groupId}/block`, {
method: 'POST'
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{
* groupId: string,
* userId: string
* }} params
* @return { Promise<{json: any, params}> }
*/
unblockGroup(params) {
return window.API.call(
`groups/${params.groupId}/members/${params.userId}`,
{
method: 'DELETE'
}
).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{
* groupId: string,
* userId: string
* }} params
* @return { Promise<{json: any, params}> }
*/
sendGroupInvite(params) {
return window.API.call(`groups/${params.groupId}/invites`, {
method: 'POST',
params: {
userId: params.userId
}
}).then((json) => {
const args = {
json,
params
};
window.API.$emit('GROUP:INVITE', args);
return args;
});
},
/**
* @param {{
* groupId: string,
* userId: string
* }} params
* @return { Promise<{json: any, params}> }
*/
kickGroupMember(params) {
return window.API.call(
`groups/${params.groupId}/members/${params.userId}`,
{
method: 'DELETE'
}
).then((json) => {
const args = {
json,
params
};
// useless code
// window.API.$emit('GROUP:MEMBER:KICK', args);
return args;
});
},
/**
* @param {{ groupId: string, userId: string }} params
* @return { Promise<{json: any, params}> }
*/
banGroupMember(params) {
return window.API.call(`groups/${params.groupId}/bans`, {
method: 'POST',
params: {
userId: params.userId
}
}).then((json) => {
const args = {
json,
params
};
// useless code
// window.API.$emit('GROUP:MEMBER:BAN', args);
return args;
});
},
unbanGroupMember(params) {
return window.API.call(
`groups/${params.groupId}/bans/${params.userId}`,
{
method: 'DELETE'
}
).then((json) => {
const args = {
json,
params
};
// useless code
// window.API.$emit('GROUP:MEMBER:UNBAN', args);
return args;
});
},
/**
* @param {{ groupId: string, userId: string }} params
* @return { Promise<{json: any, params}> }
*/
deleteSentGroupInvite(params) {
return window.API.call(
`groups/${params.groupId}/invites/${params.userId}`,
{
method: 'DELETE'
}
).then((json) => {
const args = {
json,
params
};
// useless code
// window.API.$emit('GROUP:INVITE:DELETE', args);
return args;
});
},
deleteBlockedGroupRequest(params) {
return window.API.call(
`groups/${params.groupId}/members/${params.userId}`,
{
method: 'DELETE'
}
).then((json) => {
const args = {
json,
params
};
// useless code
// window.API.$emit('GROUP:BLOCKED:DELETE', args);
return args;
});
},
acceptGroupInviteRequest(params) {
return window.API.call(
`groups/${params.groupId}/requests/${params.userId}`,
{
method: 'PUT',
params: {
action: 'accept'
}
}
).then((json) => {
const args = {
json,
params
};
// useless code
// window.API.$emit('GROUP:INVITE:ACCEPT', args);
return args;
});
},
rejectGroupInviteRequest(params) {
return window.API.call(
`groups/${params.groupId}/requests/${params.userId}`,
{
method: 'PUT',
params: {
action: 'reject'
}
}
).then((json) => {
const args = {
json,
params
};
// useless code
// window.API.$emit('GROUP:INVITE:REJECT', args);
return args;
});
},
blockGroupInviteRequest(params) {
return window.API.call(
`groups/${params.groupId}/requests/${params.userId}`,
{
method: 'PUT',
params: {
action: 'reject',
block: true
}
}
).then((json) => {
const args = {
json,
params
};
// useless code
// window.API.$emit('GROUP:INVITE:BLOCK', args);
return args;
});
},
getGroupBans(params) {
return window.API.call(`groups/${params.groupId}/bans`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ groupId: string }} params
* @return { Promise<{json: any, params}> }
*/
getGroupAuditLogTypes(params) {
return window.API.call(`groups/${params.groupId}/auditLogTypes`, {
method: 'GET'
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ groupId: string, eventTypes: array }} params
* @return { Promise<{json: any, params}> }
*/
getGroupLogs(params) {
return window.API.call(`groups/${params.groupId}/auditLogs`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ groupId: string }} params
* @return { Promise<{json: any, params}> }
*/
getGroupInvites(params) {
return window.API.call(`groups/${params.groupId}/invites`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ groupId: string }} params
* @return { Promise<{json: any, params}> }
*/
getGroupJoinRequests(params) {
return window.API.call(`groups/${params.groupId}/requests`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
// window.API.$emit('GROUP:JOINREQUESTS', args);
return args;
});
},
/**
* @param {{ groupId: string }} params
* @return { Promise<{json: any, params}> }
*/
getGroupInstances(params) {
return window.API.call(
`users/${window.API.currentUser.id}/instances/groups/${params.groupId}`,
{
method: 'GET'
}
).then((json) => {
const args = {
json,
params
};
return args;
});
},
/**
* @param {{ groupId: string }} params
* @return { Promise<{json: any, params}> }
*/
getGroupRoles(params) {
return window.API.call(`groups/${params.groupId}/roles`, {
method: 'GET',
params
}).then((json) => {
const args = {
json,
params
};
// useless code
// this.$emit('GROUP:ROLES', args);
return args;
});
},
getUsersGroupInstances() {
return window.API.call(
`users/${window.API.currentUser.id}/instances/groups`,
{
method: 'GET'
}
).then((json) => {
const args = {
json
};
window.API.$emit('GROUP:USER:INSTANCES', args);
return args;
});
},
/**
* @param {{
query: string,
n: number,
offset: number,
order: string,
sortBy: string
}} params
* @return { Promise<{json: any, params}> }
*/
groupSearch(params) {
return window.API.call(`groups`, {
method: 'GET',
params
}).then((json) => {
var args = {
json,
params
};
return args;
});
},
/**
* @param {{
groupId: string,
galleryId: string,
n: number,
offset: number
}} params
* @return { Promise<{json: any, params}> }
*/
getGroupGallery(params) {
return window.API.call(
`groups/${params.groupId}/galleries/${params.galleryId}`,
{
method: 'GET',
params: {
n: params.n,
offset: params.offset
}
}
).then((json) => {
const args = {
json,
params
};
return args;
});
}
// no place to use this
// getRequestedGroups() {
// return window.API.call(
// `users/${window.API.currentUser.id}/groups/requested`,
// {
// method: 'GET'
// }
// ).then((json) => {
// const args = {
// json
// };
// window.API.$emit('GROUP:REQUESTED', args);
// return args;
// });
// }
// ----------------- left over code -----------------
// /**
// * @param {{ groupId: string }} params
// * @return { Promise<{json: any, params}> }
// */
// API.getGroupAnnouncement = function (params) {
// return this.call(`groups/${params.groupId}/announcement`, {
// method: 'GET'
// }).then((json) => {
// var args = {
// json,
// params
// };
// this.$emit('GROUP:ANNOUNCEMENT', args);
// return args;
// });
// };
};
export default groupReq;

View File

@@ -20,6 +20,7 @@ import vrcPlusImageRequest from './vrcPlusImage';
import inviteMessagesRequest from './inviteMessages';
import imageRequest from './image';
import miscRequest from './misc';
import groupRequest from './group';
window.request = {
userRequest,
@@ -35,7 +36,8 @@ window.request = {
vrcPlusImageRequest,
inviteMessagesRequest,
imageRequest,
miscRequest
miscRequest,
groupRequest
};
export {
@@ -52,5 +54,6 @@ export {
vrcPlusImageRequest,
inviteMessagesRequest,
imageRequest,
miscRequest
miscRequest,
groupRequest
};

View File

@@ -1,7 +1,7 @@
import * as workerTimers from 'worker-timers';
import configRepository from '../repository/config.js';
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
import { worldRequest } from './request';
import { baseClass, $app, API } from './baseClass.js';
import { worldRequest, groupRequest } from './request';
export default class extends baseClass {
constructor(_app, _API, _t) {
@@ -121,13 +121,15 @@ export default class extends baseClass {
groupName = groupRef.name;
} else {
// no group cache, fetch group and try again
API.getGroup({
groupId: ref.$location.groupId
})
groupRequest
.getGroup({
groupId: ref.$location.groupId
})
.then((args) => {
workerTimers.setTimeout(() => {
// delay to allow for group cache to update
$app.sharedFeed.pendingUpdate = true;
$app.sharedFeed.pendingUpdate =
true;
$app.updateSharedFeed(false);
}, 100);
return args;

View File

@@ -169,7 +169,7 @@ export default class extends baseClass {
if (!L.groupId) {
return;
}
API.$emit('SHOW_GROUP_DIALOG', L.groupId);
this.showGroupDialog(L.groupId);
}
},
watch: {
@@ -320,7 +320,7 @@ export default class extends baseClass {
// check group perms
var groupId = this.instance.ownerId;
var group = API.cachedGroups.get(groupId);
this.canCloseInstance = $app.hasGroupPermission(
this.canCloseInstance = utils.hasGroupPermission(
group,
'group-instance-moderate'
);
@@ -339,7 +339,7 @@ export default class extends baseClass {
}
},
showUserDialog(userId) {
API.$emit('SHOW_USER_DIALOG', userId);
this.showUserDialog(userId);
}
},
watch: {

View File

@@ -1,5 +1,6 @@
import * as workerTimers from 'worker-timers';
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
import { baseClass, $app, API } from './baseClass.js';
import { groupRequest } from './request/index.js';
export default class extends baseClass {
constructor(_app, _API, _t) {
@@ -47,7 +48,7 @@ export default class extends baseClass {
if (--this.nextGroupInstanceRefresh <= 0) {
if (this.friendLogInitStatus) {
this.nextGroupInstanceRefresh = 300; // 5min
API.getUsersGroupInstances();
groupRequest.getUsersGroupInstances();
}
AppApi.CheckGameRunning();
}

View File

@@ -448,5 +448,46 @@ export default {
return `https://vrchat.com/home/launch?worldId=${encodeURIComponent(
L.worldId
)}`;
},
getFaviconUrl(resource) {
try {
const url = new URL(resource);
return `https://icons.duckduckgo.com/ip2/${url.host}.ico`;
} catch (err) {
return '';
}
},
copyToClipboard(text) {
navigator.clipboard
.writeText(text)
.then(() => {
window.$app.$message({
message: 'Copied successfully!',
type: 'success'
});
})
.catch((err) => {
console.error('Copy failed:', err);
this.$message.error('Copy failed!');
});
},
hasGroupPermission(ref, permission) {
if (
ref &&
ref.myMember &&
ref.myMember.permissions &&
(ref.myMember.permissions.includes('*') ||
ref.myMember.permissions.includes(permission))
) {
return true;
}
return false;
},
getAuditLogTypeName(auditLogType) {
if (!auditLogType) return '';
return auditLogType
.replace('group.', '')
.replace(/\./g, ' ')
.replace(/\b\w/g, (l) => l.toUpperCase());
}
};

View File

@@ -1,6 +1,7 @@
import * as workerTimers from 'worker-timers';
import Noty from 'noty';
import { baseClass, $app, API, $t, $utils } from './baseClass.js';
import { baseClass, $app, API, $utils } from './baseClass.js';
import { groupRequest } from './request';
export default class extends baseClass {
constructor(_app, _API, _t) {
@@ -450,7 +451,7 @@ export default class extends baseClass {
case 'group-role-updated':
var groupId = content.role.groupId;
API.getGroup({ groupId, includeRoles: true });
groupRequest.getGroup({ groupId, includeRoles: true });
console.log('group-role-updated', content);
// content {

View File

@@ -153,7 +153,7 @@
if (!L.groupId) {
return;
}
this.API.$emit('SHOW_GROUP_DIALOG', L.groupId);
this.showGroupDialog(L.groupId);
}
}
};

View File

@@ -353,7 +353,7 @@
type: 'info',
callback: (action) => {
if (action === 'confirm') {
this.deleteLocalWorldFavoriteGroup(group);
this.$emit('delete-local-world-favorite-group', group);
}
}
});

View File

@@ -0,0 +1,164 @@
import { reactive, ref } from 'vue';
import configRepository from '../../repository/config';
function useModerationTable() {
const groupInvitesModerationTable = reactive({
data: [],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 25, 50, 100]
}
});
const groupJoinRequestsModerationTable = reactive({
data: [],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 25, 50, 100]
}
});
const groupBlockedModerationTable = reactive({
data: [],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 25, 50, 100]
}
});
const groupLogsModerationTable = reactive({
data: [],
filters: [{ prop: ['description'], value: '' }],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 25, 50, 100]
}
});
const groupBansModerationTable = reactive({
data: [],
filters: [{ prop: ['$displayName'], value: '' }],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 25, 50, 100]
}
});
const groupMemberModerationTable = reactive({
data: [],
tableProps: { stripe: true, size: 'mini' },
pageSize: 15,
paginationProps: {
small: true,
layout: 'sizes,prev,pager,next,total',
pageSizes: [10, 15, 25, 50, 100]
}
});
async function initializePageSize() {
try {
const tablePageSize = await configRepository.getInt(
'VRCX_tablePageSize',
15
);
groupMemberModerationTable.pageSize = tablePageSize;
groupBansModerationTable.pageSize = tablePageSize;
groupLogsModerationTable.pageSize = tablePageSize;
groupInvitesModerationTable.pageSize = tablePageSize;
groupJoinRequestsModerationTable.pageSize = tablePageSize;
groupBlockedModerationTable.pageSize = tablePageSize;
} catch (error) {
console.error('Failed to initialize table page size:', error);
}
}
function deselectGroupMember(userId) {
const deselectInTable = (tableData) => {
if (userId) {
const row = tableData.find((item) => item.userId === userId);
if (row) {
row.$selected = false;
}
} else {
tableData.forEach((row) => {
if (row.$selected) {
row.$selected = false;
}
});
}
};
deselectInTable(groupMemberModerationTable.data);
deselectInTable(groupBansModerationTable.data);
deselectInTable(groupInvitesModerationTable.data);
deselectInTable(groupJoinRequestsModerationTable.data);
deselectInTable(groupBlockedModerationTable.data);
}
return {
groupInvitesModerationTable,
groupJoinRequestsModerationTable,
groupBlockedModerationTable,
groupLogsModerationTable,
groupBansModerationTable,
groupMemberModerationTable,
initializePageSize,
deselectGroupMember
};
}
function useSelectedUsers() {
const selectedUsers = reactive({});
// computed not working here hmm
const selectedUsersArray = ref([]);
function groupMemberModerationTableSelectionChange(row) {
if (row.$selected && !selectedUsers[row.userId]) {
setSelectedUsers(row.userId, row);
} else if (!row.$selected && selectedUsers[row.userId]) {
deselectedUsers(row.userId);
}
}
function deselectedUsers(userId, isAll = false) {
if (isAll) {
for (const id in selectedUsers) {
if (Object.prototype.hasOwnProperty.call(selectedUsers, id)) {
delete selectedUsers[id];
}
}
} else {
if (Object.prototype.hasOwnProperty.call(selectedUsers, userId)) {
delete selectedUsers[userId];
}
}
selectedUsersArray.value = Object.values(selectedUsers);
}
function setSelectedUsers(usersId, user) {
if (!user) {
return;
}
selectedUsers[usersId] = user;
selectedUsersArray.value = Object.values(selectedUsers);
}
return {
selectedUsers,
selectedUsersArray,
groupMemberModerationTableSelectionChange,
deselectedUsers,
setSelectedUsers
};
}
export { useModerationTable, useSelectedUsers };

View File

@@ -1,758 +0,0 @@
mixin groupDialog
el-dialog.x-dialog.x-group-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
ref='groupDialog'
:visible.sync='groupDialog.visible'
:show-close='false'
width='770px'
top='10vh')
.group-banner-image
el-popover(placement='right' width='500px' trigger='click')
img.x-link(
slot='reference'
v-lazy='groupDialog.ref.bannerUrl'
style='flex: none; width: 100%; aspect-ratio: 6/1; object-fit: cover; border-radius: 4px')
img.x-link(
v-lazy='groupDialog.ref.bannerUrl'
style='width: 854px; height: 480px'
@click='showFullscreenImageDialog(groupDialog.ref.bannerUrl)')
.group-body(v-loading='groupDialog.loading')
div(style='display: flex')
el-popover(placement='right' width='500px' trigger='click')
img.x-link(
slot='reference'
v-lazy='groupDialog.ref.iconUrl'
style='flex: none; width: 120px; height: 120px; border-radius: 12px')
img.x-link(
v-lazy='groupDialog.ref.iconUrl'
style='width: 500px; height: 500px'
@click='showFullscreenImageDialog(groupDialog.ref.iconUrl)')
div(style='flex: 1; display: flex; align-items: center; margin-left: 15px')
.group-header(style='flex: 1')
span(v-if='groupDialog.ref.ownerId === API.currentUser.id' style='margin-right: 5px') 👑
span.dialog-title(v-text='groupDialog.ref.name' style='margin-right: 5px')
span.group-discriminator.x-grey(
style='font-family: monospace; font-size: 12px; margin-right: 5px') {{ groupDialog.ref.shortCode }}.{{ groupDialog.ref.discriminator }}
el-tooltip(v-for='item in groupDialog.ref.$languages' :key='item.key' placement='top')
template(#content)
span {{ item.value }} ({{ item.key }})
span.flags(
:class='languageClass(item.key)'
style='display: inline-block; margin-right: 5px')
div(style='margin-top: 5px')
span.x-link.x-grey(
v-text='groupDialog.ownerDisplayName'
@click='showUserDialog(groupDialog.ref.ownerId)'
style='font-family: monospace')
.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(
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(
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(
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(
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(
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(
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(
v-if='groupDialog.inGroup'
type='info'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') {{ $t('dialog.group.tags.joined') }}
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') }}
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(
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(
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(
v-if='groupDialog.ref.myMember.isSubscribedToAnnouncements'
type='info'
effect='plain'
size='mini'
style='margin-right: 5px; margin-top: 5px') {{ $t('dialog.group.tags.subscribed') }}
.group-description(style='margin-top: 5px')
span(
v-show='groupDialog.ref.name !== groupDialog.ref.description'
v-text='groupDialog.ref.description'
style='font-size: 12px')
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
@click='clearGroupRepresentation(groupDialog.id)'
style='margin-left: 5px')
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
@click='setGroupRepresentation(groupDialog.id)'
style='margin-left: 5px'
:disabled='groupDialog.ref.privacy === "private"')
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
@click='cancelGroupRequest(groupDialog.id)'
style='margin-left: 5px')
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
@click='joinGroup(groupDialog.id)'
style='margin-left: 5px')
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
@click='joinGroup(groupDialog.id)'
style='margin-left: 5px')
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-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
@click='joinGroup(groupDialog.id)'
style='margin-left: 5px')
el-dropdown(
trigger='click'
@command='groupDialogCommand'
size='small'
style='margin-left: 5px')
el-button(
:type='groupDialog.ref.membershipStatus === "userblocked" ? "danger" : "default"'
icon='el-icon-more'
circle)
el-dropdown-menu(#default='dropdown')
el-dropdown-item(icon='el-icon-refresh' command='Refresh') {{ $t('dialog.group.actions.refresh') }}
el-dropdown-item(icon='el-icon-share' command='Share') {{ $t('dialog.group.actions.share') }}
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(
v-else
icon='el-icon-check'
command='Subscribe To Announcements'
divided) {{ $t('dialog.group.actions.subscribe') }}
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') }}
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') }}
//- template(v-if="hasGroupPermission(groupDialog.ref, 'group-members-manage')")
el-dropdown-item(icon='el-icon-s-operation' command='Moderation Tools') {{ $t('dialog.group.actions.moderation_tools') }}
template(v-if='groupDialog.ref.myMember && groupDialog.ref.privacy === "default"')
el-dropdown-item(icon='el-icon-view' command='Visibility Everyone' divided) #[i.el-icon-check(v-if='groupDialog.ref.myMember.visibility === "visible"')] {{ $t('dialog.group.actions.visibility_everyone') }}
el-dropdown-item(icon='el-icon-view' command='Visibility Friends') #[i.el-icon-check(v-if='groupDialog.ref.myMember.visibility === "friends"')] {{ $t('dialog.group.actions.visibility_friends') }}
el-dropdown-item(icon='el-icon-view' command='Visibility Hidden') #[i.el-icon-check(v-if='groupDialog.ref.myMember.visibility === "hidden"')] {{ $t('dialog.group.actions.visibility_hidden') }}
el-dropdown-item(
icon='el-icon-delete'
command='Leave Group'
style='color: #f56c6c'
divided) {{ $t('dialog.group.actions.leave') }}
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(v-else icon='el-icon-circle-close' command='Block Group' divided) {{ $t('dialog.group.actions.block') }}
el-tabs(ref='groupDialogTabs' @tab-click='groupDialogTabClick')
el-tab-pane(:label='$t("dialog.group.info.header")')
.group-banner-image-info
el-popover(placement='right' width='500px' trigger='click')
img.x-link(
slot='reference'
v-lazy='groupDialog.ref.bannerUrl'
style='flex: none; width: 100%; aspect-ratio: 6/1; object-fit: cover; border-radius: 4px')
img.x-link(
v-lazy='groupDialog.ref.bannerUrl'
style='width: 854px; height: 480px'
@click='showFullscreenImageDialog(groupDialog.ref.bannerUrl)')
.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') }}
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')
invite-yourself(:location='room.tag' style='margin-left: 5px')
el-tooltip(placement='top' content='Refresh player count' :disabled='hideTooltips')
el-button(
@click='refreshInstancePlayerCount(room.tag)'
size='mini'
icon='el-icon-refresh'
style='margin-left: 5px'
circle)
last-join(:location='room.tag' :currentlocation='lastLocation.location')
instance-info(
:location='room.tag'
:instance='room.ref'
:friendcount='room.friendCount'
:updateelement='updateInstanceInfo')
.x-friend-list(
style='margin: 10px 0; padding: 0; max-height: unset'
v-if='room.users.length')
.x-friend-item.x-friend-item-border(
v-for='user in room.users'
:key='user.id'
@click='showUserDialog(user.id)')
.avatar(:class='userStatusClass(user)')
img(v-lazy='userImage(user)')
.detail
span.name(v-text='user.displayName' :style='{ color: user.$userColour }')
span.extra(v-if='user.location === "traveling"')
i.el-icon-loading(style='margin-right: 5px')
timer(:epoch='user.$travelingToTime')
span.extra(v-else)
timer(:epoch='user.$location_at')
.x-friend-item(style='width: 100%; cursor: default')
.detail
span.name {{ $t('dialog.group.info.announcement') }}
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.x-link(
slot='reference'
v-lazy='groupDialog.announcement.imageUrl'
style='flex: none; width: 60px; height: 60px; border-radius: 4px; object-fit: cover')
img.x-link(
v-lazy='groupDialog.announcement.imageUrl'
style='height: 500px'
@click='showFullscreenImageDialog(groupDialog.announcement.imageUrl)')
pre.extra(
style='display: inline-block; vertical-align: top; font-family: inherit; font-size: 12px; white-space: pre-wrap; margin: 0') {{ groupDialog.announcement.text || '-' }}
br
.extra(v-if='groupDialog.announcement.id' style='float: right; margin-left: 5px')
el-tooltip(v-if='groupDialog.announcement.roleIds.length' placement='top')
template(#content)
span {{ $t('dialog.group.posts.visibility') }}
br
template(v-for='roleId in groupDialog.announcement.roleIds')
span(
v-for='(role, rIndex) in groupDialog.ref.roles'
:key='rIndex'
v-if='role.id === roleId'
v-text='role.name')
span(
v-if='groupDialog.announcement.roleIds.indexOf(roleId) < groupDialog.announcement.roleIds.length - 1') ,&nbsp;
i.el-icon-view(style='margin-right: 5px')
display-name(:userid='groupDialog.announcement.authorId' style='margin-right: 5px')
span(v-if='groupDialog.announcement.editorId' style='margin-right: 5px') ({{ $t('dialog.group.posts.edited_by') }} #[display-name(:userid='groupDialog.announcement.editorId')])
el-tooltip(placement='bottom')
template(#content)
span {{ $t('dialog.group.posts.created_at') }} {{ groupDialog.announcement.createdAt | formatDate('long') }}
template(
v-if='groupDialog.announcement.updatedAt !== groupDialog.announcement.createdAt')
br
span {{ $t('dialog.group.posts.edited_at') }} {{ groupDialog.announcement.updatedAt | formatDate('long') }}
timer(:epoch='Date.parse(groupDialog.announcement.updatedAt)')
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(
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)')
.x-friend-item(style='width: 100%; cursor: default')
.detail
span.name {{ $t('dialog.group.info.rules') }}
pre.extra(
style='font-family: inherit; font-size: 12px; white-space: pre-wrap; margin: 0 0.5em 0 0') {{ groupDialog.ref.rules || '-' }}
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.group.info.members') }}
.extra {{ groupDialog.ref.memberCount }} ({{ groupDialog.ref.onlineMemberCount }})
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.group.info.created_at') }}
span.extra {{ groupDialog.ref.createdAt | formatDate('long') }}
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.group.info.links') }}
div(
v-if='groupDialog.ref.links && groupDialog.ref.links.length > 0'
style='margin-top: 5px')
el-tooltip(v-if='link' v-for='(link, index) in groupDialog.ref.links' :key='index')
template(#content)
span(v-text='link')
img(
:src='getFaviconUrl(link)'
style='width: 16px; height: 16px; vertical-align: middle; margin-right: 5px; cursor: pointer'
@click.stop='openExternalLink(link)')
.extra(v-else) -
.x-friend-item(style='width: 350px; cursor: default')
.detail
span.name {{ $t('dialog.group.info.url') }}
span.extra {{ groupDialog.ref.$url }}
el-tooltip(
placement='top'
:content='$t("dialog.group.info.url_tooltip")'
:disabled='hideTooltips')
el-button(
type='default'
@click='copyGroupUrl(groupDialog.ref.$url)'
size='mini'
icon='el-icon-s-order'
circle
style='margin-left: 5px')
.x-friend-item(style='width: 350px; cursor: default')
.detail
span.name {{ $t('dialog.group.info.id') }}
span.extra {{ groupDialog.id }}
el-tooltip(
placement='top'
:content='$t("dialog.group.info.id_tooltip")'
:disabled='hideTooltips')
el-button(
type='default'
@click='copyGroupId(groupDialog.id)'
size='mini'
icon='el-icon-s-order'
circle
style='margin-left: 5px')
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')
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.group.info.joined_at') }}
span.extra {{ groupDialog.ref.myMember.joinedAt | formatDate('long') }}
.x-friend-item(style='cursor: default')
.detail
span.name {{ $t('dialog.group.info.roles') }}
span.extra(v-if='groupDialog.memberRoles.length === 0') -
span.extra(v-else)
template(v-for='(role, rIndex) in groupDialog.memberRoles')
el-tooltip(:key='rIndex' placement='top')
template(#content)
span {{ $t('dialog.group.info.role') }} {{ role.name }}
br
span {{ $t('dialog.group.info.role_description') }} {{ role.description }}
br
span(v-if='role.updatedAt') {{ $t('dialog.group.info.role_updated_at') }} {{ role.updatedAt | formatDate('long') }}
span(v-else) {{ $t('dialog.group.info.role_created_at') }} {{ role.createdAt | formatDate('long') }}
br
span {{ $t('dialog.group.info.role_permissions') }}
br
template(v-for='(permission, pIndex) in role.permissions')
span(:key='pIndex') {{ permission }}
br
span {{ role.name }}{{ rIndex < groupDialog.memberRoles.length - 1 ? ', ' : '' }}
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 }}
el-input(
v-model='groupDialog.postsSearch'
@input='updateGroupPostSearch'
clearable
size='mini'
:placeholder='$t("dialog.group.posts.search_placeholder")'
style='width: 89%; margin-bottom: 10px')
.x-friend-list
.x-friend-item(
v-for='post in groupDialog.postsFiltered'
:key='post.id'
style='width: 100%; cursor: default')
.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.x-link(
slot='reference'
v-lazy='post.imageUrl'
style='flex: none; width: 60px; height: 60px; border-radius: 4px; object-fit: cover')
img.x-link(
v-lazy='post.imageUrl'
style='height: 500px'
@click='showFullscreenImageDialog(post.imageUrl)')
pre.extra(
style='display: inline-block; vertical-align: top; font-family: inherit; font-size: 12px; white-space: pre-wrap; margin: 0') {{ post.text || '-' }}
br
.extra(v-if='post.authorId' style='float: right; margin-left: 5px')
el-tooltip(v-if='post.roleIds.length' placement='top')
template(#content)
span {{ $t('dialog.group.posts.visibility') }}
br
template(v-for='roleId in post.roleIds')
span(
v-for='(role, rIndex) in groupDialog.ref.roles'
:key='rIndex'
v-if='role.id === roleId'
v-text='role.name')
span(v-if='post.roleIds.indexOf(roleId) < post.roleIds.length - 1') ,&nbsp;
i.el-icon-view(style='margin-right: 5px')
display-name(:userid='post.authorId' style='margin-right: 5px')
span(v-if='post.editorId' style='margin-right: 5px') ({{ $t('dialog.group.posts.edited_by') }} #[display-name(:userid='post.editorId')])
el-tooltip(placement='bottom')
template(#content)
span {{ $t('dialog.group.posts.created_at') }} {{ post.createdAt | formatDate('long') }}
template(v-if='post.updatedAt !== post.createdAt')
br
span {{ $t('dialog.group.posts.edited_at') }} {{ post.updatedAt | formatDate('long') }}
timer(:epoch='Date.parse(post.updatedAt)')
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(
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-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(v-else style='font-weight: bold; font-size: 16px') {{ $t('dialog.group.members.friends_only') }}
div(style='margin-top: 10px')
el-button(
type='default'
@click='loadAllGroupMembers'
size='mini'
icon='el-icon-refresh'
:loading='isGroupMembersLoading'
circle)
el-button(
type='default'
@click='downloadAndSaveJson(`${groupDialog.id}_members`, groupDialog.members)'
size='mini'
icon='el-icon-download'
circle
style='margin-left: 5px')
span(
v-if='groupDialog.memberSearch.length'
style='font-size: 14px; margin-left: 5px; margin-right: 5px') {{ groupDialog.memberSearchResults.length }}/{{ groupDialog.ref.memberCount }}
span(v-else style='font-size: 14px; margin-left: 5px; margin-right: 5px') {{ groupDialog.members.length }}/{{ groupDialog.ref.memberCount }}
div(
v-if='hasGroupPermission(groupDialog.ref, "group-members-manage")'
style='float: right')
span(style='margin-right: 5px') {{ $t('dialog.group.members.sort_by') }}
el-dropdown(
@click.native.stop
trigger='click'
size='small'
style='margin-right: 5px'
:disabled='isGroupMembersLoading || groupDialog.memberSearch.length')
el-button(size='mini')
span {{ groupDialog.memberSortOrder.name }} #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default='dropdown')
el-dropdown-item(
v-for='item in groupDialogSortingOptions'
:key='item.name'
v-text='item.name'
@click.native='setGroupMemberSortOrder(item)')
span(style='margin-right: 5px') {{ $t('dialog.group.members.filter') }}
el-dropdown(
@click.native.stop
trigger='click'
size='small'
style='margin-right: 5px'
:disabled='isGroupMembersLoading || groupDialog.memberSearch.length')
el-button(size='mini')
span {{ groupDialog.memberFilter.name }} #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default='dropdown')
el-dropdown-item(
v-for='item in groupDialogFilterOptions'
:key='item.name'
v-text='item.name'
@click.native='setGroupMemberFilter(item)')
el-dropdown-item(
v-for='item in groupDialog.ref.roles'
:key='item.name'
v-if='!item.defaultRole'
v-text='item.name'
@click.native='setGroupMemberFilter(item)')
el-input(
v-model='groupDialog.memberSearch'
@input='groupMembersSearch'
clearable
size='mini'
:placeholder='$t("dialog.group.members.search")'
style='margin-top: 10px; margin-bottom: 10px')
.x-friend-list(
v-if='groupDialog.memberSearch.length'
v-loading='isGroupMembersLoading'
style='margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px')
.x-friend-item.x-friend-item-border(
v-for='user in groupDialog.memberSearchResults'
:key='user.id'
@click='showUserDialog(user.userId)')
.avatar
img(v-lazy='userImage(user.user)')
.detail
span.name(v-text='user.user.displayName' :style='{ color: user.user.$userColour }')
span.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.el-icon-collection-tag(style='margin-right: 5px')
el-tooltip(v-if='user.visibility !== "visible"' placement='top')
template(#content)
span {{ $t('dialog.group.members.visibility') }} {{ user.visibility }}
i.el-icon-view(style='margin-right: 5px')
el-tooltip(
v-if='!user.isSubscribedToAnnouncements'
placement='top'
:content='$t("dialog.group.members.unsubscribed_announcements")')
i.el-icon-chat-line-square(style='margin-right: 5px')
el-tooltip(v-if='user.managerNotes' placement='top')
template(#content)
span {{ $t('dialog.group.members.manager_notes') }}
br
span {{ user.managerNotes }}
i.el-icon-edit-outline(style='margin-right: 5px')
template(v-for='roleId in user.roleIds')
span(
v-for='(role, rIndex) in groupDialog.ref.roles'
:key='rIndex'
v-if='role.id === roleId'
v-text='role.name')
span(v-if='user.roleIds.indexOf(roleId) < user.roleIds.length - 1') ,&nbsp;
ul.infinite-list.x-friend-list(
v-else-if='groupDialog.members.length > 0'
v-infinite-scroll='loadMoreGroupMembers'
style='margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px')
li.infinite-list-item.x-friend-item.x-friend-item-border(
v-for='user in groupDialog.members'
:key='user.id'
@click='showUserDialog(user.userId)')
.avatar
img(v-lazy='userImage(user.user)')
.detail
span.name(v-text='user.user.displayName' :style='{ color: user.user.$userColour }')
span.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.el-icon-collection-tag(style='margin-right: 5px')
el-tooltip(v-if='user.visibility !== "visible"' placement='top')
template(#content)
span {{ $t('dialog.group.members.visibility') }} {{ user.visibility }}
i.el-icon-view(style='margin-right: 5px')
el-tooltip(
v-if='!user.isSubscribedToAnnouncements'
placement='top'
:content='$t("dialog.group.members.unsubscribed_announcements")')
i.el-icon-chat-line-square(style='margin-right: 5px')
el-tooltip(v-if='user.managerNotes' placement='top')
template(#content)
span {{ $t('dialog.group.members.manager_notes') }}
br
span {{ user.managerNotes }}
i.el-icon-edit-outline(style='margin-right: 5px')
template(v-for='roleId in user.roleIds')
span(
v-for='(role, rIndex) in groupDialog.ref.roles'
:key='rIndex'
v-if='role.id === roleId'
v-text='role.name')
span(v-if='user.roleIds.indexOf(roleId) < user.roleIds.length - 1') ,&nbsp;
.x-friend-item(
v-if='!isGroupMembersDone'
v-loading='isGroupMembersLoading'
style='width: 100%; height: 45px; text-align: center'
@click='loadMoreGroupMembers')
.detail(v-if='!isGroupMembersLoading')
span.name {{ $t('dialog.group.members.load_more') }}
el-tab-pane(:label='$t("dialog.group.gallery.header")' lazy)
el-button(
type='default'
size='mini'
icon='el-icon-refresh'
@click='getGroupGalleries'
:loading='isGroupGalleryLoading'
circle)
el-tabs(
type='card'
v-loading='isGroupGalleryLoading'
ref='groupDialogGallery'
style='margin-top: 10px')
template(v-for='(gallery, index) in groupDialog.ref.galleries')
el-tab-pane
span(slot='label')
span(v-text='gallery.name' style='font-weight: bold; font-size: 16px')
i.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(v-text='gallery.description' style='color: #c7c7c7; padding: 10px')
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.x-link(
slot='reference'
v-lazy='image.imageUrl'
style='width: 100%; height: 100%; object-fit: contain')
img.x-link(
v-lazy='image.imageUrl'
style='height: 700px'
@click='showFullscreenImageDialog(image.imageUrl)')
el-tab-pane(:label='$t("dialog.group.json.header")' lazy)
el-button(
type='default'
@click='refreshGroupDialogTreeData()'
size='mini'
icon='el-icon-refresh'
circle)
el-button(
type='default'
@click='downloadAndSaveJson(groupDialog.id, groupDialog.ref)'
size='mini'
icon='el-icon-download'
circle
style='margin-left: 5px')
el-tree(:data='groupDialog.treeData' style='margin-top: 5px; font-size: 12px')
template(#default='scope')
span
span(v-text='scope.data.key' style='font-weight: bold; margin-right: 5px')
span(v-if='!scope.data.children' v-text='scope.data.value')

View File

@@ -1,743 +0,0 @@
mixin groups
//- dialog: invite group
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
ref='inviteGroupDialog'
:visible.sync='inviteGroupDialog.visible'
:title='$t("dialog.invite_to_group.header")'
width='450px')
div(v-if='inviteGroupDialog.visible' v-loading='inviteGroupDialog.loading')
span {{ $t('dialog.invite_to_group.description') }}
br
el-select(
v-model='inviteGroupDialog.groupId'
clearable
:placeholder='$t("dialog.invite_to_group.choose_group_placeholder")'
filterable
:disabled='inviteGroupDialog.loading'
@change='isAllowedToInviteToGroup'
style='margin-top: 15px')
el-option-group(
v-if='API.currentUserGroups.size'
:label='$t("dialog.invite_to_group.groups")'
style='width: 410px')
el-option.x-friend-item(
v-for='group in API.currentUserGroups.values()'
:key='group.id'
:label='group.name'
:value='group.id'
style='height: auto')
.avatar
img(v-lazy='group.iconUrl')
.detail
span.name(v-text='group.name')
el-select(
v-model='inviteGroupDialog.userIds'
multiple
clearable
:placeholder='$t("dialog.invite_to_group.choose_friends_placeholder")'
filterable
:disabled='inviteGroupDialog.loading'
style='width: 100%; margin-top: 15px')
el-option-group(v-if='inviteGroupDialog.userId' :label='$t("dialog.invite_to_group.selected_users")')
el-option.x-friend-item(
:key='inviteGroupDialog.userObject.id'
:label='inviteGroupDialog.userObject.displayName'
:value='inviteGroupDialog.userObject.id'
style='height: auto')
template(v-if='inviteGroupDialog.userObject.id')
.avatar(:class='userStatusClass(inviteGroupDialog.userObject)')
img(v-lazy='userImage(inviteGroupDialog.userObject)')
.detail
span.name(
v-text='inviteGroupDialog.userObject.displayName'
:style='{ color: inviteGroupDialog.userObject.$userColour }')
span(v-else v-text='inviteGroupDialog.userId')
el-option-group(v-if='vipFriends.length' :label='$t("side_panel.favorite")')
el-option.x-friend-item(
v-for='friend in vipFriends'
:key='friend.id'
:label='friend.name'
:value='friend.id'
style='height: auto')
template(v-if='friend.ref')
.avatar(:class='userStatusClass(friend.ref)')
img(v-lazy='userImage(friend.ref)')
.detail
span.name(v-text='friend.ref.displayName' :style='{ color: friend.ref.$userColour }')
span(v-else v-text='friend.id')
el-option-group(v-if='onlineFriends.length' :label='$t("side_panel.online")')
el-option.x-friend-item(
v-for='friend in onlineFriends'
:key='friend.id'
:label='friend.name'
:value='friend.id'
style='height: auto')
template(v-if='friend.ref')
.avatar(:class='userStatusClass(friend.ref)')
img(v-lazy='userImage(friend.ref)')
.detail
span.name(v-text='friend.ref.displayName' :style='{ color: friend.ref.$userColour }')
span(v-else v-text='friend.id')
el-option-group(v-if='activeFriends.length' :label='$t("side_panel.active")')
el-option.x-friend-item(
v-for='friend in activeFriends'
:key='friend.id'
:label='friend.name'
:value='friend.id'
style='height: auto')
template(v-if='friend.ref')
.avatar
img(v-lazy='userImage(friend.ref)')
.detail
span.name(v-text='friend.ref.displayName' :style='{ color: friend.ref.$userColour }')
span(v-else v-text='friend.id')
el-option-group(v-if='offlineFriends.length' :label='$t("side_panel.offline")')
el-option.x-friend-item(
v-for='friend in offlineFriends'
:key='friend.id'
:label='friend.name'
:value='friend.id'
style='height: auto')
template(v-if='friend.ref')
.avatar
img(v-lazy='userImage(friend.ref)')
.detail
span.name(v-text='friend.ref.displayName' :style='{ color: friend.ref.$userColour }')
span(v-else v-text='friend.id')
template(#footer)
el-button(
type='primary'
size='small'
:disabled='inviteGroupDialog.loading || !inviteGroupDialog.userIds.length'
@click='sendGroupInvite()') Invite
//- dialog: group moderation
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
ref='groupMemberModeration'
:visible.sync='groupMemberModeration.visible'
:title='$t("dialog.group_member_moderation.header")'
width='90vw')
div(v-if='groupMemberModeration.visible')
h3(v-text='groupMemberModeration.groupRef.name')
el-tabs(type='card' style='height: 100%')
el-tab-pane(:label='$t("dialog.group_member_moderation.members")')
div(style='margin-top: 10px')
el-button(
type='default'
@click='loadAllGroupMembers'
size='mini'
icon='el-icon-refresh'
:loading='isGroupMembersLoading'
circle)
span(style='font-size: 14px; margin-left: 5px; margin-right: 5px') {{ groupMemberModerationTable.data.length }}/{{ groupMemberModeration.groupRef.memberCount }}
div(style='float: right; margin-top: 5px')
span(style='margin-right: 5px') {{ $t('dialog.group.members.sort_by') }}
el-dropdown(
@click.native.stop
trigger='click'
size='small'
style='margin-right: 5px'
:disabled='isGroupMembersLoading || groupDialog.memberSearch.length || !hasGroupPermission(groupDialog.ref, "group-bans-manage")')
el-button(size='mini')
span {{ groupDialog.memberSortOrder.name }} #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default='dropdown')
el-dropdown-item(
v-for='item in groupDialogSortingOptions'
:key='item.name'
v-text='item.name'
@click.native='setGroupMemberSortOrder(item)')
span(style='margin-right: 5px') {{ $t('dialog.group.members.filter') }}
el-dropdown(
@click.native.stop
trigger='click'
size='small'
style='margin-right: 5px'
:disabled='isGroupMembersLoading || groupDialog.memberSearch.length || !hasGroupPermission(groupDialog.ref, "group-bans-manage")')
el-button(size='mini')
span {{ groupDialog.memberFilter.name }} #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default='dropdown')
el-dropdown-item(
v-for='item in groupDialogFilterOptions'
:key='item.name'
v-text='item.name'
@click.native='setGroupMemberFilter(item)')
el-dropdown-item(
v-for='item in groupDialog.ref.roles'
:key='item.name'
v-if='!item.defaultRole'
v-text='item.name'
@click.native='setGroupMemberFilter(item)')
el-input(
v-model='groupDialog.memberSearch'
:disabled='!hasGroupPermission(groupDialog.ref, "group-bans-manage")'
@input='groupMembersSearch'
clearable
size='mini'
:placeholder='$t("dialog.group.members.search")'
style='margin-top: 10px; margin-bottom: 10px')
br
el-button(size='small' @click='selectAllGroupMembers') {{ $t('dialog.group_member_moderation.select_all') }}
data-tables(v-bind='groupMemberModerationTable' style='margin-top: 10px')
el-table-column(width='55' prop='$selected' :key='groupMemberModerationTableForceUpdate')
template(#default='scope')
el-button(type='text' size='mini' @click.stop)
el-checkbox(
v-model='scope.row.$selected'
@change='groupMemberModerationTableSelectionChange(scope.row)')
el-table-column(:label='$t("dialog.group_member_moderation.avatar")' width='70' prop='photo')
template(#default='scope')
el-popover(placement='right' height='500px' trigger='hover')
img.friends-list-avatar(slot='reference' v-lazy='userImage(scope.row.user)')
img.friends-list-avatar(
v-lazy='userImageFull(scope.row.user)'
style='height: 500px; cursor: pointer'
@click='showFullscreenImageDialog(userImageFull(scope.row.user))')
el-table-column(
:label='$t("dialog.group_member_moderation.display_name")'
width='160'
prop='$displayName'
sortable)
template(#default='scope')
span(style='cursor: pointer' @click='showUserDialog(scope.row.userId)')
span(
v-if='randomUserColours'
v-text='scope.row.user.displayName'
:style='{ color: scope.row.user.$userColour }')
span(v-else v-text='scope.row.user.displayName')
el-table-column(:label='$t("dialog.group_member_moderation.roles")' prop='roleIds' sortable)
template(#default='scope')
template(v-for='roleId in scope.row.roleIds')
span(
v-for='(role, rIndex) in groupMemberModeration.groupRef.roles'
:key='rIndex'
v-if='role.id === roleId'
v-text='role.name')
span(v-if='scope.row.roleIds.indexOf(roleId) < scope.row.roleIds.length - 1') ,&nbsp;
el-table-column(
:label='$t("dialog.group_member_moderation.notes")'
prop='managerNotes'
sortable)
template(#default='scope')
span(v-text='scope.row.managerNotes' @click.stop)
el-table-column(
:label='$t("dialog.group_member_moderation.joined_at")'
width='170'
prop='joinedAt'
sortable)
template(#default='scope')
span {{ scope.row.joinedAt | formatDate('long') }}
el-table-column(
:label='$t("dialog.group_member_moderation.visibility")'
width='120'
prop='visibility'
sortable)
template(#default='scope')
span(v-text='scope.row.visibility')
el-tab-pane(
:label='$t("dialog.group_member_moderation.bans")'
:disabled='!hasGroupPermission(groupDialog.ref, "group-bans-manage")')
div(style='margin-top: 10px')
el-button(
type='default'
@click='getAllGroupBans(groupMemberModeration.id)'
size='mini'
icon='el-icon-refresh'
:loading='isGroupMembersLoading'
circle)
span(style='font-size: 14px; margin-left: 5px; margin-right: 5px') {{ groupBansModerationTable.data.length }}
br
el-input(
v-model='groupBansModerationTable.filters[0].value'
clearable
size='mini'
:placeholder='$t("dialog.group.members.search")'
style='margin-top: 10px; margin-bottom: 10px')
br
el-button(size='small' @click='selectAllGroupBans') {{ $t('dialog.group_member_moderation.select_all') }}
data-tables(v-bind='groupBansModerationTable' style='margin-top: 10px')
el-table-column(width='55' prop='$selected' :key='groupMemberModerationTableForceUpdate')
template(#default='scope')
el-button(type='text' size='mini' @click.stop)
el-checkbox(
v-model='scope.row.$selected'
@change='groupMemberModerationTableSelectionChange(scope.row)')
el-table-column(:label='$t("dialog.group_member_moderation.avatar")' width='70' prop='photo')
template(#default='scope')
el-popover(placement='right' height='500px' trigger='hover')
img.friends-list-avatar(slot='reference' v-lazy='userImage(scope.row.user)')
img.friends-list-avatar(
v-lazy='userImageFull(scope.row.user)'
style='height: 500px; cursor: pointer'
@click='showFullscreenImageDialog(userImageFull(scope.row.user))')
el-table-column(
:label='$t("dialog.group_member_moderation.display_name")'
width='160'
prop='$displayName'
sortable)
template(#default='scope')
span(style='cursor: pointer' @click='showUserDialog(scope.row.userId)')
span(
v-if='randomUserColours'
v-text='scope.row.user.displayName'
:style='{ color: scope.row.user.$userColour }')
span(v-else v-text='scope.row.user.displayName')
el-table-column(:label='$t("dialog.group_member_moderation.roles")' prop='roleIds' sortable)
template(#default='scope')
template(v-for='roleId in scope.row.roleIds')
span(
v-for='(role, rIndex) in groupMemberModeration.groupRef.roles'
:key='rIndex'
v-if='role.id === roleId'
v-text='role.name')
span(v-if='scope.row.roleIds.indexOf(roleId) < scope.row.roleIds.length - 1') ,&nbsp;
el-table-column(
:label='$t("dialog.group_member_moderation.notes")'
prop='managerNotes'
sortable)
template(#default='scope')
span(v-text='scope.row.managerNotes' @click.stop)
el-table-column(
:label='$t("dialog.group_member_moderation.joined_at")'
width='170'
prop='joinedAt'
sortable)
template(#default='scope')
span {{ scope.row.joinedAt | formatDate('long') }}
el-table-column(
:label='$t("dialog.group_member_moderation.banned_at")'
width='170'
prop='joinedAt'
sortable)
template(#default='scope')
span {{ scope.row.bannedAt | formatDate('long') }}
el-tab-pane(
:label='$t("dialog.group_member_moderation.invites")'
:disabled='!hasGroupPermission(groupDialog.ref, "group-invites-manage")')
div(style='margin-top: 10px')
el-button(
type='default'
@click='getAllGroupInvitesAndJoinRequests(groupMemberModeration.id)'
size='mini'
icon='el-icon-refresh'
:loading='isGroupMembersLoading'
circle)
br
el-tabs
el-tab-pane
span(slot='label')
span(
v-text='$t("dialog.group_member_moderation.sent_invites")'
style='font-weight: bold; font-size: 16px')
span(style='color: #909399; font-size: 12px; margin-left: 5px') {{ groupInvitesModerationTable.data.length }}
el-button(size='small' @click='selectAllGroupInvites') {{ $t('dialog.group_member_moderation.select_all') }}
data-tables(v-bind='groupInvitesModerationTable' style='margin-top: 10px')
el-table-column(
width='55'
prop='$selected'
:key='groupMemberModerationTableForceUpdate')
template(#default='scope')
el-button(type='text' size='mini' @click.stop)
el-checkbox(
v-model='scope.row.$selected'
@change='groupMemberModerationTableSelectionChange(scope.row)')
el-table-column(
:label='$t("dialog.group_member_moderation.avatar")'
width='70'
prop='photo')
template(#default='scope')
el-popover(placement='right' height='500px' trigger='hover')
img.friends-list-avatar(
slot='reference'
v-lazy='userImage(scope.row.user)')
img.friends-list-avatar(
v-lazy='userImageFull(scope.row.user)'
style='height: 500px; cursor: pointer'
@click='showFullscreenImageDialog(userImageFull(scope.row.user))')
el-table-column(
:label='$t("dialog.group_member_moderation.display_name")'
width='160'
prop='$displayName'
sortable)
template(#default='scope')
span(style='cursor: pointer' @click='showUserDialog(scope.row.userId)')
span(
v-if='randomUserColours'
v-text='scope.row.user.displayName'
:style='{ color: scope.row.user.$userColour }')
span(v-else v-text='scope.row.user.displayName')
el-table-column(
:label='$t("dialog.group_member_moderation.notes")'
prop='managerNotes'
sortable)
template(#default='scope')
span(v-text='scope.row.managerNotes' @click.stop)
br
el-button(
@click='groupMembersDeleteSentInvite'
:disabled='groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-invites-manage")') {{ $t('dialog.group_member_moderation.delete_sent_invite') }}
el-tab-pane
span(slot='label')
span(
v-text='$t("dialog.group_member_moderation.join_requests")'
style='font-weight: bold; font-size: 16px')
span(style='color: #909399; font-size: 12px; margin-left: 5px') {{ groupJoinRequestsModerationTable.data.length }}
el-button(size='small' @click='selectAllGroupJoinRequests') {{ $t('dialog.group_member_moderation.select_all') }}
data-tables(v-bind='groupJoinRequestsModerationTable' style='margin-top: 10px')
el-table-column(
width='55'
prop='$selected'
:key='groupMemberModerationTableForceUpdate')
template(#default='scope')
el-button(type='text' size='mini' @click.stop)
el-checkbox(
v-model='scope.row.$selected'
@change='groupMemberModerationTableSelectionChange(scope.row)')
el-table-column(
:label='$t("dialog.group_member_moderation.avatar")'
width='70'
prop='photo')
template(#default='scope')
el-popover(placement='right' height='500px' trigger='hover')
img.friends-list-avatar(
slot='reference'
v-lazy='userImage(scope.row.user)')
img.friends-list-avatar(
v-lazy='userImageFull(scope.row.user)'
style='height: 500px; cursor: pointer'
@click='showFullscreenImageDialog(userImageFull(scope.row.user))')
el-table-column(
:label='$t("dialog.group_member_moderation.display_name")'
width='160'
prop='$displayName'
sortable)
template(#default='scope')
span(style='cursor: pointer' @click='showUserDialog(scope.row.userId)')
span(
v-if='randomUserColours'
v-text='scope.row.user.displayName'
:style='{ color: scope.row.user.$userColour }')
span(v-else v-text='scope.row.user.displayName')
el-table-column(
:label='$t("dialog.group_member_moderation.notes")'
prop='managerNotes'
sortable)
template(#default='scope')
span(v-text='scope.row.managerNotes' @click.stop)
br
el-button(
@click='groupMembersAcceptInviteRequest'
:disabled='groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-invites-manage")') {{ $t('dialog.group_member_moderation.accept_join_requests') }}
el-button(
@click='groupMembersRejectInviteRequest'
:disabled='groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-invites-manage")') {{ $t('dialog.group_member_moderation.reject_join_requests') }}
el-button(
@click='groupMembersBlockJoinRequest'
:disabled='groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-invites-manage")') {{ $t('dialog.group_member_moderation.block_join_requests') }}
el-tab-pane
span(slot='label')
span(
v-text='$t("dialog.group_member_moderation.blocked_requests")'
style='font-weight: bold; font-size: 16px')
span(style='color: #909399; font-size: 12px; margin-left: 5px') {{ groupBlockedModerationTable.data.length }}
el-button(size='small' @click='selectAllGroupBlocked') {{ $t('dialog.group_member_moderation.select_all') }}
data-tables(v-bind='groupBlockedModerationTable' style='margin-top: 10px')
el-table-column(
width='55'
prop='$selected'
:key='groupMemberModerationTableForceUpdate')
template(#default='scope')
el-button(type='text' size='mini' @click.stop)
el-checkbox(
v-model='scope.row.$selected'
@change='groupMemberModerationTableSelectionChange(scope.row)')
el-table-column(
:label='$t("dialog.group_member_moderation.avatar")'
width='70'
prop='photo')
template(#default='scope')
el-popover(placement='right' height='500px' trigger='hover')
img.friends-list-avatar(
slot='reference'
v-lazy='userImage(scope.row.user)')
img.friends-list-avatar(
v-lazy='userImageFull(scope.row.user)'
style='height: 500px; cursor: pointer'
@click='showFullscreenImageDialog(userImageFull(scope.row.user))')
el-table-column(
:label='$t("dialog.group_member_moderation.display_name")'
width='160'
prop='$displayName'
sortable)
template(#default='scope')
span(style='cursor: pointer' @click='showUserDialog(scope.row.userId)')
span(
v-if='randomUserColours'
v-text='scope.row.user.displayName'
:style='{ color: scope.row.user.$userColour }')
span(v-else v-text='scope.row.user.displayName')
el-table-column(
:label='$t("dialog.group_member_moderation.notes")'
prop='managerNotes'
sortable)
template(#default='scope')
span(v-text='scope.row.managerNotes' @click.stop)
br
el-button(
@click='groupMembersDeleteBlockedRequest'
:disabled='groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-invites-manage")') {{ $t('dialog.group_member_moderation.delete_blocked_requests') }}
el-tab-pane(
:label='$t("dialog.group_member_moderation.logs")'
:disabled='!hasGroupPermission(groupDialog.ref, "group-audit-view")')
div(style='margin-top: 10px')
el-button(
type='default'
@click='getAllGroupLogs(groupMemberModeration.id)'
size='mini'
icon='el-icon-refresh'
:loading='isGroupMembersLoading'
circle)
span(style='font-size: 14px; margin-left: 5px; margin-right: 5px') {{ groupLogsModerationTable.data.length }}
br
div(style='display: flex; justify-content: space-between; align-items: center')
div
el-select(
v-model='groupMemberModeration.selectedAuditLogTypes'
multiple
collapse-tags
:placeholder='$t("dialog.group_member_moderation.filter_type")')
el-option-group(:label='$t("dialog.group_member_moderation.select_type")')
el-option.x-friend-item(
v-for='type in groupMemberModeration.auditLogTypes'
:key='type'
:label='getAuditLogTypeName(type)'
:value='type')
.detail
span.name(v-text='getAuditLogTypeName(type)')
el-input(
v-model='groupLogsModerationTable.filters[0].value'
:placeholder='$t("dialog.group_member_moderation.search_placeholder")'
style='display: inline-block; width: 150px; margin: 10px')
div
el-button(@click='showGroupLogsExportDialog') {{ $t('dialog.group_member_moderation.export_logs') }}
br
data-tables(v-bind='groupLogsModerationTable' style='margin-top: 10px')
el-table-column(
:label='$t("dialog.group_member_moderation.created_at")'
width='170'
prop='created_at'
sortable)
template(#default='scope')
span {{ scope.row.created_at | formatDate('long') }}
el-table-column(
:label='$t("dialog.group_member_moderation.type")'
width='190'
prop='eventType'
sortable)
template(#default='scope')
span(v-text='scope.row.eventType')
el-table-column(
:label='$t("dialog.group_member_moderation.display_name")'
width='160'
prop='actorDisplayName'
sortable)
template(#default='scope')
span(style='cursor: pointer' @click='showUserDialog(scope.row.actorId)')
span(v-text='scope.row.actorDisplayName')
el-table-column(:label='$t("dialog.group_member_moderation.description")' prop='description')
template(#default='scope')
span(v-text='scope.row.description')
el-table-column(:label='$t("dialog.group_member_moderation.data")' prop='data')
template(#default='scope')
span(v-if='Object.keys(scope.row.data).length' v-text='JSON.stringify(scope.row.data)')
br
br
span.name {{ $t('dialog.group_member_moderation.user_id') }}
br
el-input(
v-model='groupMemberModeration.selectUserId'
size='mini'
style='margin-top: 5px; width: 340px'
:placeholder='$t("dialog.group_member_moderation.user_id_placeholder")'
clearable)
el-button(size='small' @click='selectGroupMemberUserId' :disabled='!groupMemberModeration.selectUserId') {{ $t('dialog.group_member_moderation.select_user') }}
br
br
span.name {{ $t('dialog.group_member_moderation.selected_users') }}
el-button(
type='default'
@click='clearSelectedGroupMembers'
size='mini'
icon='el-icon-delete'
circle
style='margin-left: 5px')
br
el-tag(
v-for='user in groupMemberModeration.selectedUsersArray'
type='info'
disable-transitions='true'
:key='user.id'
style='margin-right: 5px; margin-top: 5px'
closable
@close='deleteSelectedGroupMember(user)')
span {{ user.user?.displayName }} #[i.el-icon-warning(v-if='user.membershipStatus !== "member"' style='margin-left: 5px')]
br
br
span.name {{ $t('dialog.group_member_moderation.notes') }}
el-input.extra(
v-model='groupMemberModeration.note'
type='textarea'
:rows='2'
:autosize='{ minRows: 1, maxRows: 20 }'
:placeholder='$t("dialog.group_member_moderation.note_placeholder")'
size='mini'
resize='none'
style='margin-top: 5px')
br
br
span.name {{ $t('dialog.group_member_moderation.selected_roles') }}
br
el-select(
v-model='groupMemberModeration.selectedRoles'
clearable
multiple
:placeholder='$t("dialog.group_member_moderation.choose_roles_placeholder")'
filterable
style='margin-top: 5px')
el-option-group(:label='$t("dialog.group_member_moderation.roles")')
el-option.x-friend-item(
v-for='role in groupMemberModeration.groupRef.roles'
:key='role.id'
:label='role.name'
:value='role.id'
style='height: auto')
.detail
span.name(v-text='role.name')
br
br
span.name {{ $t('dialog.group_member_moderation.actions') }}
br
el-button(
@click='groupMembersAddRoles'
:disabled='!groupMemberModeration.selectedRoles.length || groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-roles-assign")') {{ $t('dialog.group_member_moderation.add_roles') }}
el-button(
@click='groupMembersRemoveRoles'
:disabled='!groupMemberModeration.selectedRoles.length || groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-roles-assign")') {{ $t('dialog.group_member_moderation.remove_roles') }}
el-button(
@click='groupMembersSaveNote'
:disabled='groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-members-manage")') {{ $t('dialog.group_member_moderation.save_note') }}
el-button(
@click='groupMembersKick'
:disabled='groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-members-remove")') {{ $t('dialog.group_member_moderation.kick') }}
el-button(
@click='groupMembersBan'
:disabled='groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-bans-manage")') {{ $t('dialog.group_member_moderation.ban') }}
el-button(
@click='groupMembersUnban'
:disabled='groupMemberModeration.progressCurrent || !hasGroupPermission(groupDialog.ref, "group-bans-manage")') {{ $t('dialog.group_member_moderation.unban') }}
span(v-if='groupMemberModeration.progressCurrent' style='margin-top: 10px') #[i.el-icon-loading(style='margin-left: 5px; margin-right: 5px')] {{ $t('dialog.group_member_moderation.progress') }} {{ groupMemberModeration.progressCurrent }}/{{ groupMemberModeration.progressTotal }}
el-button(
v-if='groupMemberModeration.progressCurrent'
@click='groupMemberModeration.progressTotal = 0'
style='margin-left: 5px') {{ $t('dialog.group_member_moderation.cancel') }}
//- dialog: group posts
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
ref='groupPostEditDialog'
:visible.sync='groupPostEditDialog.visible'
:title='$t("dialog.group_post_edit.header")'
width='650px')
div(v-if='groupPostEditDialog.visible')
h3(v-text='groupPostEditDialog.groupRef.name')
el-form(:model='groupPostEditDialog' label-width='150px')
el-form-item(:label='$t("dialog.group_post_edit.title")')
el-input(v-model='groupPostEditDialog.title' size='mini')
el-form-item(:label='$t("dialog.group_post_edit.message")')
el-input(
v-model='groupPostEditDialog.text'
type='textarea'
:rows='4'
:autosize='{ minRows: 4, maxRows: 20 }'
style='margin-top: 10px'
resize='none')
el-form-item
el-checkbox(
v-if='!groupPostEditDialog.postId'
v-model='groupPostEditDialog.sendNotification'
size='small') {{ $t('dialog.group_post_edit.send_notification') }}
el-form-item(:label='$t("dialog.group_post_edit.post_visibility")')
el-radio-group(v-model='groupPostEditDialog.visibility' size='small')
el-radio(label='public') {{ $t('dialog.group_post_edit.visibility_public') }}
el-radio(label='group') {{ $t('dialog.group_post_edit.visibility_group') }}
el-form-item(
v-if='groupPostEditDialog.visibility === "group"'
:label='$t("dialog.new_instance.roles")')
el-select(
v-model='groupPostEditDialog.roleIds'
multiple
clearable
:placeholder='$t("dialog.new_instance.role_placeholder")'
style='width: 100%')
el-option-group(:label='$t("dialog.new_instance.role_placeholder")')
el-option.x-friend-item(
v-for='role in groupPostEditDialog.groupRef?.roles'
:key='role.id'
:label='role.name'
:value='role.id'
style='height: auto; width: 478px')
.detail
span.name(v-text='role.name')
el-form-item(:label='$t("dialog.group_post_edit.image")')
template(v-if='gallerySelectDialog.selectedFileId')
div(style='display: inline-block; flex: none; margin-right: 5px')
el-popover(placement='right' width='500px' trigger='click')
img.x-link(
slot='reference'
v-lazy='gallerySelectDialog.selectedImageUrl'
style='flex: none; width: 60px; height: 60px; border-radius: 4px; object-fit: cover')
img.x-link(
v-lazy='gallerySelectDialog.selectedImageUrl'
style='height: 500px'
@click='showFullscreenImageDialog(gallerySelectDialog.selectedImageUrl)')
el-button(size='mini' @click='clearImageGallerySelect' style='vertical-align: top') {{ $t('dialog.invite_message.clear_selected_image') }}
template(v-else)
el-button(size='mini' @click='showGallerySelectDialog' style='margin-right: 5px') {{ $t('dialog.invite_message.select_image') }}
template(#footer)
el-button(size='small' @click='groupPostEditDialog.visible = false') {{ $t('dialog.group_post_edit.cancel') }}
el-button(v-if='groupPostEditDialog.postId' size='small' @click='editGroupPost') {{ $t('dialog.group_post_edit.edit_post') }}
el-button(v-else size='small' @click='createGroupPost') {{ $t('dialog.group_post_edit.create_post') }}
//- dialog: export logs
el-dialog.x-dialog(
:before-close='beforeDialogClose'
@mousedown.native='dialogMouseDown'
@mouseup.native='dialogMouseUp'
:visible.sync='isGroupLogsExportDialogVisible'
:title='$t("dialog.group_member_moderation.export_logs")'
width='650px'
ref='groupLogsExportDialogRef')
el-checkbox-group(
v-model='checkedGroupLogsExportLogsOptions'
@change='updateGrouptLogsExporContent()'
style='margin-bottom: 10px')
template(v-for='option in checkGroupsLogsExportLogsOptions')
el-checkbox(:key='option.label' :label='option.label') {{ $t(option.text) }}
br
el-input(
type='textarea'
v-model='groupLogsExportContent'
size='mini'
rows='15'
resize='none'
readonly
style='margin-top: 15px'
@click.native='handleCopyGroupLogsExportContent')

View File

@@ -106,9 +106,13 @@ mixin notificationsTab
:grouphint='scope.row.details.groupName'
:link='true')
br
span(
el-tooltip(
v-if='scope.row.message && scope.row.message !== `This is a generated invite to ${scope.row.details?.worldName}`'
v-text='scope.row.message')
placement='top')
template(#content)
pre.extra(
style='display: inline-block; vertical-align: top; font-family: inherit; font-size: 12px; white-space: pre-wrap; margin: 0') {{ scope.row.message || '-' }}
div(v-text='scope.row.message')
span(
v-else-if='scope.row.details && scope.row.details.inviteMessage'
v-text='scope.row.details.inviteMessage')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
<template>
<el-dialog
class="x-dialog"
:before-close="beforeDialogClose"
:visible="isGroupLogsExportDialogVisible"
:title="t('dialog.group_member_moderation.export_logs')"
width="650px"
append-to-body
@close="setIsGroupLogsExportDialogVisible"
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<el-checkbox-group
v-model="checkedGroupLogsExportLogsOptions"
style="margin-bottom: 10px"
@change="updateGroupLogsExportContent">
<template v-for="option in checkGroupsLogsExportLogsOptions">
<el-checkbox :key="option.label" :label="option.label">
{{ t(option.text) }}
</el-checkbox>
</template>
</el-checkbox-group>
<br />
<el-input
v-model="groupLogsExportContent"
type="textarea"
size="mini"
rows="15"
resize="none"
readonly
style="margin-top: 15px"
@click.native="handleCopyGroupLogsExportContent" />
</el-dialog>
</template>
<script setup>
import { inject, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n-bridge';
import utils from '../../../classes/utils';
const beforeDialogClose = inject('beforeDialogClose');
const dialogMouseDown = inject('dialogMouseDown');
const dialogMouseUp = inject('dialogMouseUp');
const { t } = useI18n();
const props = defineProps({
isGroupLogsExportDialogVisible: {
type: Boolean,
default: false
},
groupLogsModerationTable: {
type: Object,
default: () => {}
}
});
const emit = defineEmits(['update:isGroupLogsExportDialogVisible']);
watch(
() => props.isGroupLogsExportDialogVisible,
(newVal) => {
if (newVal) {
updateGroupLogsExportContent();
}
}
);
const groupLogsExportContent = ref('');
const checkGroupsLogsExportLogsOptions = [
{ label: 'created_at', text: 'dialog.group_member_moderation.created_at' },
{ label: 'eventType', text: 'dialog.group_member_moderation.type' },
{ label: 'actorDisplayName', text: 'dialog.group_member_moderation.display_name' },
{ label: 'description', text: 'dialog.group_member_moderation.description' },
{ label: 'data', text: 'dialog.group_member_moderation.data' }
];
const checkedGroupLogsExportLogsOptions = ref([
'created_at',
'eventType',
'actorDisplayName',
'description',
'data'
]);
function updateGroupLogsExportContent() {
const formatter = (str) => (/[\x00-\x1f,"]/.test(str) ? `"${str.replace(/"/g, '""')}"` : str);
const sortedCheckedOptions = checkGroupsLogsExportLogsOptions
.filter((option) => checkedGroupLogsExportLogsOptions.value.includes(option.label))
.map((option) => option.label);
const header = `${sortedCheckedOptions.join(',')}\n`;
const content = props.groupLogsModerationTable.data
.map((item) =>
sortedCheckedOptions
.map((key) => formatter(key === 'data' ? JSON.stringify(item[key]) : item[key]))
.join(',')
)
.join('\n');
groupLogsExportContent.value = header + content; // Update ref
}
function handleCopyGroupLogsExportContent() {
utils.copyToClipboard(groupLogsExportContent.value);
}
function setIsGroupLogsExportDialogVisible() {
emit('update:isGroupLogsExportDialogVisible', false);
}
</script>

View File

@@ -0,0 +1,200 @@
<template>
<el-dialog
ref="groupPostEditDialog"
:before-close="beforeDialogClose"
:visible.sync="groupPostEditDialog.visible"
:title="$t('dialog.group_post_edit.header')"
width="650px"
append-to-body
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<div v-if="groupPostEditDialog.visible">
<h3 v-text="groupPostEditDialog.groupRef.name"></h3>
<el-form :model="groupPostEditDialog" label-width="150px">
<el-form-item :label="$t('dialog.group_post_edit.title')">
<el-input v-model="groupPostEditDialog.title" size="mini"></el-input>
</el-form-item>
<el-form-item :label="$t('dialog.group_post_edit.message')">
<el-input
v-model="groupPostEditDialog.text"
type="textarea"
:rows="4"
:autosize="{ minRows: 4, maxRows: 20 }"
style="margin-top: 10px"
resize="none"></el-input>
</el-form-item>
<el-form-item>
<el-checkbox
v-if="!groupPostEditDialog.postId"
v-model="groupPostEditDialog.sendNotification"
size="small">
{{ $t('dialog.group_post_edit.send_notification') }}
</el-checkbox>
</el-form-item>
<el-form-item :label="$t('dialog.group_post_edit.post_visibility')">
<el-radio-group v-model="groupPostEditDialog.visibility" size="small">
<el-radio label="public">
{{ $t('dialog.group_post_edit.visibility_public') }}
</el-radio>
<el-radio label="group">
{{ $t('dialog.group_post_edit.visibility_group') }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="groupPostEditDialog.visibility === 'group'"
:label="$t('dialog.new_instance.roles')">
<el-select
v-model="groupPostEditDialog.roleIds"
multiple
clearable
:placeholder="$t('dialog.new_instance.role_placeholder')"
style="width: 100%">
<el-option-group :label="$t('dialog.new_instance.role_placeholder')">
<el-option
v-for="role in groupPostEditDialog.groupRef?.roles"
:key="role.id"
:label="role.name"
:value="role.id"
style="height: auto; width: 478px">
<div class="detail">
<span class="name" v-text="role.name"></span>
</div>
</el-option>
</el-option-group>
</el-select>
</el-form-item>
<el-form-item :label="$t('dialog.group_post_edit.image')">
<template v-if="gallerySelectDialog.selectedFileId">
<div style="display: inline-block; flex: none; margin-right: 5px">
<el-popover placement="right" width="500px" trigger="click">
<img
slot="reference"
v-lazy="gallerySelectDialog.selectedImageUrl"
style="
flex: none;
width: 60px;
height: 60px;
border-radius: 4px;
object-fit: cover;
" />
<img
v-lazy="gallerySelectDialog.selectedImageUrl"
style="height: 500px"
@click="showFullscreenImageDialog(gallerySelectDialog.selectedImageUrl)" />
</el-popover>
<el-button size="mini" style="vertical-align: top" @click="clearImageGallerySelect">
{{ $t('dialog.invite_message.clear_selected_image') }}
</el-button>
</div>
</template>
<template v-else>
<el-button size="mini" style="margin-right: 5px" @click="showGallerySelectDialog">
{{ $t('dialog.invite_message.select_image') }}
</el-button>
</template>
</el-form-item>
</el-form>
</div>
<template #footer>
<el-button size="small" @click="groupPostEditDialog.visible = false">
{{ $t('dialog.group_post_edit.cancel') }}
</el-button>
<el-button v-if="groupPostEditDialog.postId" size="small" @click="editGroupPost">
{{ $t('dialog.group_post_edit.edit_post') }}
</el-button>
<el-button v-else size="small" @click="createGroupPost">
{{ $t('dialog.group_post_edit.create_post') }}
</el-button>
</template>
</el-dialog>
</template>
<script>
import { groupRequest } from '../../../classes/request';
export default {
name: 'GroupPostEditDialog',
inject: [
'beforeDialogClose',
'showFullscreenImageDialog',
'dialogMouseDown',
'dialogMouseUp',
'showGallerySelectDialog'
],
props: {
dialogData: {
type: Object,
required: true
},
gallerySelectDialog: {
type: Object,
required: true
}
},
computed: {
groupPostEditDialog: {
get() {
return this.dialogData;
},
set(value) {
this.$emit('update:dialog-data', value);
}
}
},
methods: {
editGroupPost() {
const D = this.groupPostEditDialog;
if (!D.groupId || !D.postId) {
return;
}
const params = {
groupId: D.groupId,
postId: D.postId,
title: D.title,
text: D.text,
roleIds: D.roleIds,
visibility: D.visibility,
imageId: null
};
if (this.gallerySelectDialog.selectedFileId) {
params.imageId = this.gallerySelectDialog.selectedFileId;
}
groupRequest.editGroupPost(params).then((args) => {
this.$message({
message: 'Group post edited',
type: 'success'
});
return args;
});
D.visible = false;
},
createGroupPost() {
const D = this.groupPostEditDialog;
const params = {
groupId: D.groupId,
title: D.title,
text: D.text,
roleIds: D.roleIds,
visibility: D.visibility,
sendNotification: D.sendNotification,
imageId: null
};
if (this.gallerySelectDialog.selectedFileId) {
params.imageId = this.gallerySelectDialog.selectedFileId;
}
groupRequest.createGroupPost(params).then((args) => {
this.$message({
message: 'Group post created',
type: 'success'
});
return args;
});
D.visible = false;
},
clearImageGallerySelect() {
this.$emit('clear-image-gallery-select');
}
}
};
</script>

View File

@@ -0,0 +1,307 @@
<template>
<el-dialog
ref="inviteGroupDialog"
:visible.sync="inviteGroupDialog.visible"
:before-close="beforeDialogClose"
:title="$t('dialog.invite_to_group.header')"
width="450px"
@mousedown.native="dialogMouseDown"
@mouseup.native="dialogMouseUp">
<div v-if="inviteGroupDialog.visible" v-loading="inviteGroupDialog.loading">
<span>{{ $t('dialog.invite_to_group.description') }}</span>
<br />
<el-select
v-model="inviteGroupDialog.groupId"
clearable
:placeholder="$t('dialog.invite_to_group.choose_group_placeholder')"
filterable
:disabled="inviteGroupDialog.loading"
style="margin-top: 15px"
@change="isAllowedToInviteToGroup">
<el-option-group
v-if="API.currentUserGroups.size"
:label="$t('dialog.invite_to_group.groups')"
style="width: 410px">
<el-option
v-for="group in API.currentUserGroups.values()"
:key="group.id"
:label="group.name"
:value="group.id"
style="height: auto"
class="x-friend-item">
<div class="avatar">
<img v-lazy="group.iconUrl" />
</div>
<div class="detail">
<span class="name" v-text="group.name"></span>
</div>
</el-option>
</el-option-group>
</el-select>
<el-select
v-model="inviteGroupDialog.userIds"
multiple
clearable
:placeholder="$t('dialog.invite_to_group.choose_friends_placeholder')"
filterable
:disabled="inviteGroupDialog.loading"
style="width: 100%; margin-top: 15px">
<el-option-group v-if="inviteGroupDialog.userId" :label="$t('dialog.invite_to_group.selected_users')">
<el-option
:key="inviteGroupDialog.userObject.id"
:label="inviteGroupDialog.userObject.displayName"
:value="inviteGroupDialog.userObject.id"
class="x-friend-item">
<template v-if="inviteGroupDialog.userObject.id">
<div class="avatar" :class="userStatusClass(inviteGroupDialog.userObject)">
<img v-lazy="userImage(inviteGroupDialog.userObject)" />
</div>
<div class="detail">
<span
class="name"
:style="{ color: inviteGroupDialog.userObject.$userColour }"
v-text="inviteGroupDialog.userObject.displayName"></span>
</div>
</template>
<span v-else v-text="inviteGroupDialog.userId"></span>
</el-option>
</el-option-group>
<el-option-group v-if="vipFriends.length" :label="$t('side_panel.favorite')">
<el-option
v-for="friend in vipFriends"
:key="friend.id"
:label="friend.name"
:value="friend.id"
style="height: auto"
class="x-friend-item">
<template v-if="friend.ref">
<div class="avatar" :class="userStatusClass(friend.ref)">
<img v-lazy="userImage(friend.ref)" />
</div>
<div class="detail">
<span
class="name"
:style="{ color: friend.ref.$userColour }"
v-text="friend.ref.displayName"></span>
</div>
</template>
<span v-else v-text="friend.id"></span>
</el-option>
</el-option-group>
<el-option-group v-if="onlineFriends.length" :label="$t('side_panel.online')">
<el-option
v-for="friend in onlineFriends"
:key="friend.id"
:label="friend.name"
:value="friend.id"
style="height: auto"
class="x-friend-item">
<template v-if="friend.ref">
<div class="avatar" :class="userStatusClass(friend.ref)">
<img v-lazy="userImage(friend.ref)" />
</div>
<div class="detail">
<span
class="name"
:style="{ color: friend.ref.$userColour }"
v-text="friend.ref.displayName"></span>
</div>
</template>
<span v-else v-text="friend.id"></span>
</el-option>
</el-option-group>
<el-option-group v-if="activeFriends.length" :label="$t('side_panel.active')">
<el-option
v-for="friend in activeFriends"
:key="friend.id"
:label="friend.name"
:value="friend.id"
style="height: auto"
class="x-friend-item">
<template v-if="friend.ref">
<div class="avatar">
<img v-lazy="userImage(friend.ref)" />
</div>
<div class="detail">
<span
class="name"
:style="{ color: friend.ref.$userColour }"
v-text="friend.ref.displayName"></span>
</div>
</template>
<span v-else v-text="friend.id"></span>
</el-option>
</el-option-group>
<el-option-group v-if="offlineFriends.length" :label="$t('side_panel.offline')">
<el-option
v-for="friend in offlineFriends"
:key="friend.id"
:label="friend.name"
:value="friend.id"
style="height: auto"
class="x-friend-item">
<template v-if="friend.ref">
<div class="avatar">
<img v-lazy="userImage(friend.ref)" />
</div>
<div class="detail">
<span
class="name"
:style="{ color: friend.ref.$userColour }"
v-text="friend.ref.displayName"></span>
</div>
</template>
<span v-else v-text="friend.id"></span>
</el-option>
</el-option-group>
</el-select>
</div>
<template #footer>
<el-button
type="primary"
size="small"
:disabled="inviteGroupDialog.loading || !inviteGroupDialog.userIds.length"
@click="sendGroupInvite">
Invite
</el-button>
</template>
</el-dialog>
</template>
<script>
import { groupRequest, userRequest } from '../../../classes/request';
import utils from '../../../classes/utils';
export default {
name: 'InviteGroupDialog',
inject: [
'API',
'dialogMouseDown',
'dialogMouseUp',
'beforeDialogClose',
'userStatusClass',
'userImage',
'adjustDialogZ'
],
props: {
dialogData: {
type: Object,
required: true
},
vipFriends: {
type: Array,
required: true
},
onlineFriends: {
type: Array,
required: true
},
activeFriends: {
type: Array,
required: true
},
offlineFriends: {
type: Array,
required: true
}
},
computed: {
inviteGroupDialog: {
get() {
return this.dialogData;
},
set(value) {
this.$emit('update:dialog-data', value);
}
}
},
watch: {
'dialogData.visible'(value) {
if (value) {
this.initDialog();
}
}
},
methods: {
initDialog() {
this.$nextTick(() => this.adjustDialogZ(this.$refs.inviteGroupDialog.$el));
const D = this.inviteGroupDialog;
if (D.groupId) {
this.API.getCachedGroup({
groupId: D.groupId
})
.then((args) => {
D.groupName = args.ref.name;
})
.catch(() => {
D.groupId = '';
});
this.isAllowedToInviteToGroup();
}
if (D.userId) {
userRequest.getCachedUser({ userId: D.userId }).then((args) => {
D.userObject = args.ref;
D.userIds = [D.userId];
});
}
},
isAllowedToInviteToGroup() {
const D = this.inviteGroupDialog;
const groupId = D.groupId;
if (!groupId) {
return;
}
this.inviteGroupDialog.loading = true;
groupRequest
.getGroup({ groupId })
.then((args) => {
if (utils.hasGroupPermission(args.ref, 'group-invites-manage')) {
return args;
}
// not allowed to invite
this.inviteGroupDialog.groupId = '';
this.$message({
type: 'error',
message: 'You are not allowed to invite to this group'
});
return args;
})
.finally(() => {
this.inviteGroupDialog.loading = false;
});
},
sendGroupInvite() {
this.$confirm('Continue? Invite User(s) To Group', 'Confirm', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'info',
callback: (action) => {
const D = this.inviteGroupDialog;
if (action !== 'confirm' || D.loading === true) {
return;
}
D.loading = true;
const inviteLoop = () => {
if (D.userIds.length === 0) {
D.loading = false;
return;
}
const receiverUserId = D.userIds.shift();
groupRequest
.sendGroupInvite({
groupId: D.groupId,
userId: receiverUserId
})
.then(inviteLoop)
.catch(() => {
D.loading = false;
});
};
inviteLoop();
}
});
}
}
};
</script>

View File

@@ -490,7 +490,7 @@
</template>
<script>
import { instanceRequest } from '../../../classes/request';
import { groupRequest, instanceRequest } from '../../../classes/request';
import utils from '../../../classes/utils';
import configRepository from '../../../repository/config';
@@ -508,10 +508,6 @@
'adjustDialogZ'
],
props: {
hasGroupPermission: {
type: Function,
required: true
},
vipFriends: {
type: Array,
required: true
@@ -603,7 +599,7 @@
D.strict = false;
D.shortName = '';
D.secureOrShortName = '';
this.API.getGroupPermissions({ userId: this.API.currentUser.id });
groupRequest.getGroupPermissions({ userId: this.API.currentUser.id });
this.buildInstance();
this.buildLegacyInstance();
this.updateNewInstanceDialog();
@@ -738,13 +734,15 @@
if (typeof ref !== 'undefined') {
D.groupRef = ref;
D.selectedGroupRoles = ref.roles;
this.API.getGroupRoles({
groupId: D.groupId
}).then((args) => {
D.lastSelectedGroupId = D.groupId;
D.selectedGroupRoles = args.json;
ref.roles = args.json;
});
groupRequest
.getGroupRoles({
groupId: D.groupId
})
.then((args) => {
D.lastSelectedGroupId = D.groupId;
D.selectedGroupRoles = args.json;
ref.roles = args.json;
});
}
}
if (!D.groupId) {
@@ -811,13 +809,15 @@
if (typeof ref !== 'undefined') {
D.groupRef = ref;
D.selectedGroupRoles = ref.roles;
this.API.getGroupRoles({
groupId: D.groupId
}).then((args) => {
D.lastSelectedGroupId = D.groupId;
D.selectedGroupRoles = args.json;
ref.roles = args.json;
});
groupRequest
.getGroupRoles({
groupId: D.groupId
})
.then((args) => {
D.lastSelectedGroupId = D.groupId;
D.selectedGroupRoles = args.json;
ref.roles = args.json;
});
}
}
if (!D.groupId) {
@@ -868,6 +868,9 @@
});
console.error(error.message);
}
},
hasGroupPermission(ref, permission) {
return utils.hasGroupPermission(ref, permission);
}
}
};

View File

@@ -756,8 +756,7 @@
:offline-friends="offlineFriends"
:active-friends="activeFriends"
:online-friends="onlineFriends"
:vip-friends="vipFriends"
:has-group-permission="hasGroupPermission" />
:vip-friends="vipFriends" />
</el-dialog>
</template>
@@ -787,7 +786,8 @@
'dialogMouseUp',
'displayPreviousImages',
'showWorldDialog',
'showFavoriteDialog'
'showFavoriteDialog',
'openExternalLink'
],
props: {
worldDialog: Object,
@@ -804,7 +804,6 @@
activeFriends: Array,
onlineFriends: Array,
vipFriends: Array,
hasGroupPermission: Function,
// TODO: Remove
updateInstanceInfo: Number
@@ -1034,7 +1033,7 @@
this.isSetWorldTagsDialogVisible = true;
break;
case 'Download Unity Package':
utils.openExternalLink(this.replaceVrcPackageUrl(this.worldDialog.ref.unityPackageUrl));
this.openExternalLink(this.replaceVrcPackageUrl(this.worldDialog.ref.unityPackageUrl));
break;
case 'Change Image':
this.displayPreviousImages('World', 'Change');

View File

@@ -279,11 +279,11 @@
inject: [
'userImage',
'userImageFull',
'getFaviconUrl',
'showFullscreenImageDialog',
'showUserDialog',
'statusClass',
'openExternalLink'
'openExternalLink',
'languageClass'
],
props: {
friends: {
@@ -293,7 +293,6 @@
hideTooltips: Boolean,
randomUserColours: Boolean,
sortStatus: Function,
languageClass: Function,
confirmDeleteFriend: Function,
friendsListSearch: String,
menuActiveIndex: String,
@@ -504,6 +503,9 @@
},
timeToText(val) {
return utils.timeToText(val);
},
getFaviconUrl(link) {
return utils.getFaviconUrl(link);
}
}
};