Moderations in playerList

This commit is contained in:
Natsumi
2025-08-21 05:07:06 +12:00
parent 7d06966fef
commit 887f59d2b4
9 changed files with 138 additions and 39 deletions
+1
View File
@@ -847,6 +847,7 @@ i.x-status-icon.red {
.el-dialog, .el-dialog,
.el-message-box { .el-message-box {
border-radius: 28px; border-radius: 28px;
word-break: break-word;
} }
.el-tabs__nav-wrap::after { .el-tabs__nav-wrap::after {
@@ -2392,8 +2392,6 @@
D.isBlock = true; D.isBlock = true;
} else if (ref.type === 'mute') { } else if (ref.type === 'mute') {
D.isMute = true; D.isMute = true;
} else if (ref.type === 'hideAvatar') {
D.isHideAvatar = true;
} else if (ref.type === 'interactOff') { } else if (ref.type === 'interactOff') {
D.isInteractOff = true; D.isInteractOff = true;
} else if (ref.type === 'muteChat') { } else if (ref.type === 'muteChat') {
+28 -14
View File
@@ -440,6 +440,34 @@ export const useFriendStore = defineStore('Friend', () => {
if (typeof ref !== 'undefined') { if (typeof ref !== 'undefined') {
location = ref.location; location = ref.location;
$location_at = ref.$location_at; $location_at = ref.$location_at;
// wtf, fetch user if offline in an instance
if (
ctx.state !== 'online' &&
isRealInstance(ref.location) &&
ref.$lastFetch < Date.now() - 10000 // 10 seconds
) {
console.log(
`Fetching offline friend in an instance ${ctx.name}`
);
userRequest.getUser({
userId: id
});
}
// wtf, fetch user if online in an offline location
if (
ctx.state === 'online' &&
ref.location === 'offline' &&
ref.$lastFetch < Date.now() - 10000 // 10 seconds
) {
console.log(
`Fetching online friend in an offline location ${ctx.name}`
);
userRequest.getUser({
userId: id
});
return;
}
} }
if (typeof stateInput === 'undefined' || ctx.state === stateInput) { if (typeof stateInput === 'undefined' || ctx.state === stateInput) {
// this is should be: undefined -> user // this is should be: undefined -> user
@@ -489,20 +517,6 @@ export const useFriendStore = defineStore('Friend', () => {
state.sortOfflineFriends = true; state.sortOfflineFriends = true;
} }
} }
// wtf, fetch user if offline in an instance
if (
ctx.state !== 'online' &&
typeof ref !== 'undefined' &&
isRealInstance(ref.location) &&
ref.$lastFetch < Date.now() - 10000 // 10 seconds
) {
console.log(
`Fetching offline friend in an instance ${ctx.name}`
);
userRequest.getUser({
userId: id
});
}
} else if ( } else if (
ctx.state === 'online' && ctx.state === 'online' &&
(stateInput === 'active' || stateInput === 'offline') (stateInput === 'active' || stateInput === 'offline')
+10 -1
View File
@@ -1080,8 +1080,12 @@ export const useInstanceStore = defineStore('Instance', () => {
function updatePlayerListDebounce() { function updatePlayerListDebounce() {
const users = []; const users = [];
const pushUser = function (ref) { const pushUser = function (ref) {
let photonId = ''; let photonId = -1;
let isFriend = false; let isFriend = false;
let isBlocked = false;
let isMuted = false;
let isAvatarInteractionDisabled = false;
let isChatBoxMuted = false;
photonStore.photonLobbyCurrent.forEach((ref1, id) => { photonStore.photonLobbyCurrent.forEach((ref1, id) => {
if (typeof ref1 !== 'undefined') { if (typeof ref1 !== 'undefined') {
if ( if (
@@ -1138,6 +1142,11 @@ export const useInstanceStore = defineStore('Instance', () => {
} }
}); });
} }
isBlocked = ref.$moderations.isBlocked;
isMuted = ref.$moderations.isMuted;
isAvatarInteractionDisabled =
ref.$moderations.isAvatarInteractionDisabled;
isChatBoxMuted = ref.$moderations.isChatBoxMuted;
} }
users.push({ users.push({
ref, ref,
+64 -7
View File
@@ -1,16 +1,13 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import Vue, { computed, reactive, watch } from 'vue'; import { computed, reactive, watch } from 'vue';
import { avatarModerationRequest, playerModerationRequest } from '../api'; import { avatarModerationRequest, playerModerationRequest } from '../api';
import { $app } from '../app';
import { watchState } from '../service/watchState'; import { watchState } from '../service/watchState';
import { useAvatarStore } from './avatar'; import { useAvatarStore } from './avatar';
import { useUserStore } from './user'; import { useUserStore } from './user';
import { useI18n } from 'vue-i18n-bridge';
export const useModerationStore = defineStore('Moderation', () => { export const useModerationStore = defineStore('Moderation', () => {
const avatarStore = useAvatarStore(); const avatarStore = useAvatarStore();
const userStore = useUserStore(); const userStore = useUserStore();
const { t } = useI18n();
const state = reactive({ const state = reactive({
cachedPlayerModerations: new Map(), cachedPlayerModerations: new Map(),
@@ -66,6 +63,23 @@ export const useModerationStore = defineStore('Moderation', () => {
function handlePlayerModerationAtDelete(args) { function handlePlayerModerationAtDelete(args) {
const { ref } = args; const { ref } = args;
let hasModeration = false;
for (const ref of state.cachedPlayerModerations.values()) {
if (ref.targetUserId === ref.targetUserId) {
hasModeration = true;
break;
}
}
if (!hasModeration) {
state.cachedPlayerModerationsUserIds.delete(ref.targetUserId);
}
const userRef = userStore.cachedUsers.get(ref.targetUserId);
if (typeof userRef !== 'undefined') {
userRef.$moderations = getUserModerations(ref.targetUserId);
}
const D = userStore.userDialog; const D = userStore.userDialog;
if ( if (
D.visible === false || D.visible === false ||
@@ -91,7 +105,7 @@ export const useModerationStore = defineStore('Moderation', () => {
for (let i = 0; i < length; ++i) { for (let i = 0; i < length; ++i) {
if (array[i].id === ref.id) { if (array[i].id === ref.id) {
array.splice(i, 1); array.splice(i, 1);
return; break;
} }
} }
} }
@@ -112,9 +126,9 @@ export const useModerationStore = defineStore('Moderation', () => {
playerModerationId: ref.id playerModerationId: ref.id
} }
}); });
break;
} }
} }
state.cachedPlayerModerationsUserIds.delete(moderated);
} }
/** /**
@@ -153,6 +167,10 @@ export const useModerationStore = defineStore('Moderation', () => {
} else { } else {
array.push(ref); array.push(ref);
} }
const userRef = userStore.cachedUsers.get(ref.targetUserId);
if (typeof userRef !== 'undefined') {
userRef.$moderations = getUserModerations(ref.targetUserId);
}
return ref; return ref;
} }
@@ -213,6 +231,44 @@ export const useModerationStore = defineStore('Moderation', () => {
}); });
} }
/**
* Get user moderations
* @param {string} userId
* @return {object} moderations
* @property {boolean} isBlocked
* @property {boolean} isMuted
* @property {boolean} isAvatarInteractionDisabled
* @property {boolean} isChatBoxMuted
*/
function getUserModerations(userId) {
let moderations = {
isBlocked: false,
isMuted: false,
isAvatarInteractionDisabled: false,
isChatBoxMuted: false
};
for (let ref of state.cachedPlayerModerations.values()) {
if (ref.targetUserId !== userId) {
continue;
}
switch (ref.type) {
case 'block':
moderations.isBlocked = true;
break;
case 'mute':
moderations.isMuted = true;
break;
case 'interactOff':
moderations.isAvatarInteractionDisabled = true;
break;
case 'muteChat':
moderations.isChatBoxMuted = true;
break;
}
}
return moderations;
}
return { return {
state, state,
cachedPlayerModerations, cachedPlayerModerations,
@@ -222,6 +278,7 @@ export const useModerationStore = defineStore('Moderation', () => {
refreshPlayerModerations, refreshPlayerModerations,
applyPlayerModeration, applyPlayerModeration,
handlePlayerModerationDelete handlePlayerModerationDelete,
getUserModerations
}; };
}); });
+2 -2
View File
@@ -524,6 +524,7 @@ export const useUserStore = defineStore('User', () => {
$customTagColour: '', $customTagColour: '',
$friendNumber: 0, $friendNumber: 0,
$platform: '', $platform: '',
$moderations: {},
// //
...json ...json
}; };
@@ -594,6 +595,7 @@ export const useUserStore = defineStore('User', () => {
} }
Object.assign(ref, json); Object.assign(ref, json);
} }
ref.$moderations = moderationStore.getUserModerations(ref.id);
ref.$isVRCPlus = ref.tags.includes('system_supporter'); ref.$isVRCPlus = ref.tags.includes('system_supporter');
appearanceSettingsStore.applyUserTrustLevel(ref); appearanceSettingsStore.applyUserTrustLevel(ref);
applyUserLanguage(ref); applyUserLanguage(ref);
@@ -846,8 +848,6 @@ export const useUserStore = defineStore('User', () => {
D.isBlock = true; D.isBlock = true;
} else if (ref.type === 'mute') { } else if (ref.type === 'mute') {
D.isMute = true; D.isMute = true;
} else if (ref.type === 'hideAvatar') {
D.isHideAvatar = true;
} else if (ref.type === 'interactOff') { } else if (ref.type === 'interactOff') {
D.isInteractOff = true; D.isInteractOff = true;
} else if (ref.type === 'muteChat') { } else if (ref.type === 'muteChat') {
+3 -3
View File
@@ -14,9 +14,9 @@ export type CreateInstance = (params: {
type: string; type: string;
region: string; region: string;
ownerId: string; ownerId: string;
roleIds: string[]; roleIds?: string[];
groupAccessType: string; groupAccessType?: string;
queueEnabled: boolean; queueEnabled?: boolean;
}) => Promise<{ }) => Promise<{
json: any; json: any;
params: any; params: any;
+8
View File
@@ -61,6 +61,14 @@ export interface VrcxUser extends GetUserResponse {
$customTagColour: string; $customTagColour: string;
$friendNumber: number; $friendNumber: number;
$lastFetch: number; $lastFetch: number;
$moderations: moderations;
}
export interface moderations {
isBlocked: boolean;
isMuted: boolean;
isAvatarInteractionDisabled: boolean;
isChatBoxMuted: boolean;
} }
export interface VrcxCurrentUser extends GetCurrentUserResponse { export interface VrcxCurrentUser extends GetCurrentUserResponse {
+21 -9
View File
@@ -708,6 +708,22 @@
<el-tooltip v-if="scope.row.isFriend" placement="left" content="Friend"> <el-tooltip v-if="scope.row.isFriend" placement="left" content="Friend">
<span>💚</span> <span>💚</span>
</el-tooltip> </el-tooltip>
<el-tooltip v-if="scope.row.isBlocked" placement="left" content="Blocked">
<i class="el-icon el-icon-circle-close" style="color: red"></i>
</el-tooltip>
<el-tooltip v-if="scope.row.isMuted" placement="left" content="Muted">
<i class="el-icon el-icon-turn-off-microphone" style="color: orange"></i>
</el-tooltip>
<el-tooltip
v-if="scope.row.isAvatarInteractionDisabled"
placement="left"
content="Avatar Interaction Disabled
">
<i class="el-icon el-icon-thumb" style="color: orange"></i>
</el-tooltip>
<el-tooltip v-if="scope.row.isChatBoxMuted" placement="left" content="Chatbox Muted">
<i class="el-icon el-icon-chat-line-round" style="color: orange"></i>
</el-tooltip>
<el-tooltip v-if="scope.row.timeoutTime" placement="left" content="Timeout"> <el-tooltip v-if="scope.row.timeoutTime" placement="left" content="Timeout">
<span style="color: red">🔴{{ scope.row.timeoutTime }}s</span> <span style="color: red">🔴{{ scope.row.timeoutTime }}s</span>
</el-tooltip> </el-tooltip>
@@ -715,17 +731,13 @@
</el-table-column> </el-table-column>
<el-table-column :label="t('table.playerList.platform')" prop="inVRMode" width="80"> <el-table-column :label="t('table.playerList.platform')" prop="inVRMode" width="80">
<template #default="scope"> <template #default="scope">
<template v-if="scope.row.ref.last_platform"> <template v-if="scope.row.ref.$platform">
<span v-if="scope.row.ref.last_platform === 'standalonewindows'" style="color: #409eff" <span v-if="scope.row.ref.$platform === 'standalonewindows'" style="color: #409eff"
>PC</span >PC</span
> >
<span v-else-if="scope.row.ref.last_platform === 'android'" style="color: #67c23a" <span v-else-if="scope.row.ref.$platform === 'android'" style="color: #67c23a">A</span>
>A</span <span v-else-if="scope.row.ref.$platform === 'ios'" style="color: #c7c7ce">iOS</span>
> <span v-else>{{ scope.row.ref.$platform }}</span>
<span v-else-if="scope.row.ref.last_platform === 'ios'" style="color: #c7c7ce"
>iOS</span
>
<span v-else>{{ scope.row.ref.last_platform }}</span>
</template> </template>
<template v-if="scope.row.inVRMode !== null"> <template v-if="scope.row.inVRMode !== null">
<span v-if="scope.row.inVRMode">VR</span> <span v-if="scope.row.inVRMode">VR</span>