diff --git a/html/src/app.js b/html/src/app.js
index 30c061ff..949f19a0 100644
--- a/html/src/app.js
+++ b/html/src/app.js
@@ -22692,7 +22692,7 @@ speechSynthesis.getVoices();
API.$on('LOGIN', async function () {
$app.avatarHistory = new Set();
- var historyArray = await database.getAvatarHistory();
+ var historyArray = await database.getAvatarHistory(API.currentUser.id);
$app.avatarHistoryArray = historyArray;
for (var i = 0; i < historyArray.length; i++) {
$app.avatarHistory.add(historyArray[i].id);
@@ -24641,6 +24641,32 @@ speechSynthesis.getVoices();
}
};
+ $app.data.groupDialogSortingOptions = {
+ joinedAtDesc: {
+ name: $t('dialog.group.members.sorting.joined_at_desc'),
+ value: 'joinedAt:desc'
+ },
+ joinedAtAsc: {
+ name: $t('dialog.group.members.sorting.joined_at_asc'),
+ value: 'joinedAt:asc'
+ },
+ userId: {
+ name: $t('dialog.group.members.sorting.user_id'),
+ value: ''
+ }
+ };
+
+ $app.data.groupDialogFilterOptions = {
+ everyone: {
+ name: $t('dialog.group.members.filters.everyone'),
+ id: null
+ },
+ usersWithNoRole: {
+ name: $t('dialog.group.members.filters.users_with_no_role'),
+ id: ''
+ }
+ };
+
$app.data.groupDialog = {
visible: false,
loading: false,
@@ -24654,6 +24680,8 @@ speechSynthesis.getVoices();
members: [],
instances: [],
memberRoles: [],
+ memberFilter: $app.data.groupDialogFilterOptions.everyone,
+ memberSortOrder: $app.data.groupDialogSortingOptions.joinedAtDesc,
galleries: {}
};
@@ -24678,6 +24706,7 @@ speechSynthesis.getVoices();
}
if (this.groupDialogLastMembers !== groupId) {
D.members = [];
+ D.memberFilter = this.groupDialogFilterOptions.everyone;
}
API.getCachedGroup({
groupId
@@ -24950,6 +24979,12 @@ speechSynthesis.getVoices();
offset: 0,
groupId: D.id
};
+ if (D.memberSortOrder.value) {
+ this.loadMoreGroupMembersParams.sort = D.memberSortOrder.value;
+ }
+ if (D.memberFilter.id !== null) {
+ this.loadMoreGroupMembersParams.roleId = D.memberFilter.id;
+ }
if (D.inGroup) {
await API.getGroupMember({
groupId: D.id,
@@ -24957,7 +24992,10 @@ speechSynthesis.getVoices();
}).then((args) => {
if (args.json) {
args.json.user = API.currentUser;
- D.members.push(args.json);
+ if (D.memberFilter.id === null) {
+ // when flitered by role don't include self
+ D.members.push(args.json);
+ }
}
return args;
});
@@ -24969,6 +25007,7 @@ speechSynthesis.getVoices();
if (this.isGroupMembersDone || this.isGroupMembersLoading) {
return;
}
+ var D = this.groupDialog;
var params = this.loadMoreGroupMembersParams;
this.isGroupMembersLoading = true;
await API.getGroupMembers(params)
@@ -24979,19 +25018,20 @@ speechSynthesis.getVoices();
for (var i = 0; i < args.json.length; i++) {
var member = args.json[i];
if (member.userId === API.currentUser.id) {
- // remove self from array
- // when fetching only friends self is included
- args.json.splice(i, 1);
+ if (
+ D.members.length > 0 &&
+ D.members[0].userId === API.currentUser.id
+ ) {
+ // remove duplicate and keep sort order
+ D.members.splice(0, 1);
+ }
break;
}
}
if (args.json.length < params.n) {
this.isGroupMembersDone = true;
}
- this.groupDialog.members = [
- ...this.groupDialog.members,
- ...args.json
- ];
+ D.members = [...D.members, ...args.json];
params.offset += params.n;
return args;
})
@@ -25011,6 +25051,24 @@ speechSynthesis.getVoices();
}
};
+ $app.methods.setGroupMemberSortOrder = async function (sortOrder) {
+ var D = this.groupDialog;
+ if (D.memberSortOrder === sortOrder) {
+ return;
+ }
+ D.memberSortOrder = sortOrder;
+ await this.getGroupDialogGroupMembers();
+ };
+
+ $app.methods.setGroupMemberFilter = async function (filter) {
+ var D = this.groupDialog;
+ if (D.memberFilter === filter) {
+ return;
+ }
+ D.memberFilter = filter;
+ await this.getGroupDialogGroupMembers();
+ };
+
$app.methods.hasGroupPermission = function (ref, permission) {
if (
ref &&
diff --git a/html/src/index.pug b/html/src/index.pug
index 4751b6e8..c1b98a2c 100644
--- a/html/src/index.pug
+++ b/html/src/index.pug
@@ -188,9 +188,9 @@ html
location(:location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName" :link="false")
span(v-else-if="scope.row.type === 'ChatBoxMessage'" v-text="scope.row.text")
span(v-else-if="scope.row.type === 'OnPlayerJoined'")
- span(v-if="scope.row.platform === 'Desktop'" style="color:#409eff") PC
+ span(v-if="scope.row.platform === 'Desktop'" style="color:#409eff") Desktop
span(v-else-if="scope.row.platform === 'VR'" style="color:#409eff") VR
- span(v-else-if="scope.row.platform === 'Quest'" style="color:#67c23a") Q
+ span(v-else-if="scope.row.platform === 'Quest'" style="color:#67c23a") Quest
span.x-link(v-text="scope.row.avatar.name" @click="showAvatarDialog(scope.row.avatar.id)")
|
span(v-if="!scope.row.inCache" style="color:#aaa") #[i.el-icon-download]
@@ -252,9 +252,9 @@ html
location(:location="scope.row.location" :hint="scope.row.worldName" :grouphint="scope.row.groupName" :link="false")
span(v-else-if="scope.row.type === 'ChatBoxMessage'" v-text="scope.row.text")
span(v-else-if="scope.row.type === 'OnPlayerJoined'")
- span(v-if="scope.row.platform === 'Desktop'" style="color:#409eff") PC
+ span(v-if="scope.row.platform === 'Desktop'" style="color:#409eff") Desktop
span(v-else-if="scope.row.platform === 'VR'" style="color:#409eff") VR
- span(v-else-if="scope.row.platform === 'Quest'" style="color:#67c23a") Q
+ span(v-else-if="scope.row.platform === 'Quest'" style="color:#67c23a") Quest
span.x-link(v-text="scope.row.avatar.name" @click="showAvatarDialog(scope.row.avatar.id)")
|
span(v-if="!scope.row.inCache" style="color:#aaa") #[i.el-icon-download]
@@ -1721,7 +1721,7 @@ html
el-tooltip(v-else placement="top" :content="$t('dialog.user.actions.favorite_tooltip')" :disabled="hideTooltips")
el-button(type="default" @click="userDialogCommand('Add Favorite')" icon="el-icon-star-off" circle)
el-dropdown(trigger="click" @command="userDialogCommand" size="small")
- el-button(:type="(userDialog.incomingRequest || userDialog.outgoingRequest || userDialog.isShowAvatar) ? 'success' : (userDialog.isBlock || userDialog.isMute || userDialog.isHideAvatar) ? 'danger' : 'default'" icon="el-icon-more" circle style="margin-left:5px")
+ el-button(:type="(userDialog.incomingRequest || userDialog.outgoingRequest) ? 'success' : (userDialog.isBlock || userDialog.isMute) ? 'danger' : 'default'" icon="el-icon-more" circle style="margin-left:5px")
el-dropdown-menu(#default="dropdown")
el-dropdown-item(icon="el-icon-refresh" command="Refresh") {{ $t('dialog.user.actions.refresh') }}
el-dropdown-item(icon="el-icon-s-order" command="Copy User") {{ $t('dialog.user.actions.copy_url') }}
@@ -2465,7 +2465,21 @@ html
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(style="font-size:14px;margin-left:5px;margin-right:5px") {{ groupDialog.members.length }}/{{ groupDialog.ref.memberCount }}
- ul.infinite-list.x-friend-list(v-if="groupDialog.members.length > 0" v-infinite-scroll="loadMoreGroupMembers" style="margin-top:10px;overflow:auto;max-height:250px")
+ 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")
+ 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" 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")
+ 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" v-text="item.name" @click.native="setGroupMemberFilter(item)")
+ el-dropdown-item(v-for="(item) in groupDialog.memberRoles" v-text="item.name" @click.native="setGroupMemberFilter(item)")
+ ul.infinite-list.x-friend-list(v-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(v-for="user in groupDialog.members" :key="user.id" @click="showUserDialog(user.userId)" class="x-friend-item-border")
.avatar
img(v-lazy="userImage(user.user)")
diff --git a/html/src/localization/strings/en.json b/html/src/localization/strings/en.json
index 0263533c..3615d625 100644
--- a/html/src/localization/strings/en.json
+++ b/html/src/localization/strings/en.json
@@ -746,7 +746,18 @@
"header": "Members",
"all_members": "All Members",
"friends_only": "Friends Only",
- "load_more": "Load more..."
+ "load_more": "Load more...",
+ "sort_by": "Sort By:",
+ "sorting": {
+ "user_id": "User ID (Ascending)",
+ "joined_at_asc": "Joined At (Ascending)",
+ "joined_at_desc": "Joined At (Descending)"
+ },
+ "filter": "Filter:",
+ "filters": {
+ "everyone": "Everyone",
+ "users_with_no_role": "Users With No Role"
+ }
},
"gallery": {
"header": "Gallery"
diff --git a/html/src/repository/database.js b/html/src/repository/database.js
index 144abcf4..2493a25d 100644
--- a/html/src/repository/database.js
+++ b/html/src/repository/database.js
@@ -1693,7 +1693,7 @@ class Database {
);
}
- async getAvatarHistory() {
+ async getAvatarHistory(currentUserId) {
var data = [];
await sqliteService.execute((dbRow) => {
var row = {
@@ -1710,7 +1710,7 @@ class Database {
version: dbRow[13]
};
data.push(row);
- }, `SELECT * FROM ${Database.userPrefix}_avatar_history INNER JOIN cache_avatar ON cache_avatar.id = ${Database.userPrefix}_avatar_history.avatar_id ORDER BY ${Database.userPrefix}_avatar_history.created_at DESC LIMIT 100`);
+ }, `SELECT * FROM ${Database.userPrefix}_avatar_history INNER JOIN cache_avatar ON cache_avatar.id = ${Database.userPrefix}_avatar_history.avatar_id WHERE author_id != "${currentUserId}" ORDER BY ${Database.userPrefix}_avatar_history.created_at DESC LIMIT 100`);
return data;
}
diff --git a/html/src/vr.pug b/html/src/vr.pug
index 91c93829..f8a22a1b 100644
--- a/html/src/vr.pug
+++ b/html/src/vr.pug
@@ -505,9 +505,9 @@ html
location(:location="feed.location" :hint="feed.worldName" :grouphint="feed.groupName" :link="false" style="margin-left:10px")
template(v-else-if="feed.type === 'OnPlayerJoined'")
span(style="margin-left:10px;color:#a3a3a3") has joined
- span(v-if="feed.platform === 'Desktop'" style="color:#409eff;margin-left:10px") PC
+ span(v-if="feed.platform === 'Desktop'" style="color:#409eff;margin-left:10px") Desktop
span(v-else-if="feed.platform === 'VR'" style="color:#409eff;margin-left:10px") VR
- span(v-else-if="feed.platform === 'Quest'" style="color:#67c23a;margin-left:10px") Q
+ span(v-else-if="feed.platform === 'Quest'" style="color:#67c23a;margin-left:10px") Quest
span(v-if="!feed.inCache" style="color:#aaa;margin-left:10px") #[i.el-icon-download]
span(v-text="feed.avatar.name" style="margin-left:10px")
template(v-else-if="feed.type === 'SpawnEmoji'")