From 2ed0ed7d628b4caea2a3089bb9179537e4a35311 Mon Sep 17 00:00:00 2001 From: Natsumi Date: Fri, 23 Jun 2023 21:06:36 +1200 Subject: [PATCH] Instance info tooltip and random fixes --- html/src/app.js | 535 ++++++++++++++++------ html/src/index.pug | 45 +- html/src/localization/localizedStrings.js | 7 +- html/src/localization/strings/en.json | 8 +- html/src/mixins/tabs/playerList.pug | 6 +- html/src/vr.pug | 2 +- 6 files changed, 424 insertions(+), 179 deletions(-) diff --git a/html/src/app.js b/html/src/app.js index 57ad2b8c..48b5d08f 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -1064,6 +1064,101 @@ speechSynthesis.getVoices(); } }); + Vue.component('instance-info', { + template: + '
' + + '' + + '
' + + 'PC: {{ platforms.standalonewindows }}
' + + 'Quest: {{ platforms.android }}
' + + '{{ $t("dialog.user.info.instance_game_version") }} {{ gameServerVersion }}
' + + '{{ $t("dialog.user.info.instance_queuing_enabled") }}
' + + '{{ $t("dialog.user.info.instance_users") }}
' + + '' + + '
' + + '' + + '
' + + '{{ occupants }}/{{ capacity }}' + + '({{ friendcount }})' + + '{{ $t("dialog.user.info.instance_full") }}' + + '{{ $t("dialog.user.info.instance_queue") }} {{ queueSize }}' + + '
', + props: { + location: String, + instance: Object, + friendcount: Number, + updateelement: Number + }, + data() { + return { + isValidInstance: this.isValidInstance, + isFull: this.isFull, + occupants: this.occupants, + capacity: this.capacity, + queueSize: this.queueSize, + queueEnabled: this.queueEnabled, + platforms: this.platforms, + userList: this.userList, + gameServerVersion: this.gameServerVersion + }; + }, + methods: { + parse() { + this.isValidInstance = false; + this.isFull = false; + this.occupants = 0; + this.capacity = 0; + this.queueSize = 0; + this.queueEnabled = false; + this.platforms = []; + this.userList = []; + this.gameServerVersion = ''; + if ( + !this.location || + !this.instance || + Object.keys(this.instance).length === 0 + ) { + return; + } + this.isValidInstance = true; + this.isFull = + typeof this.instance.hasCapacityForYou !== 'undefined' && + !this.instance.hasCapacityForYou; + this.occupants = this.instance.n_users; + if (this.location === $app.lastLocation.location) { + // use gameLog for occupants when in same location + this.occupants = $app.lastLocation.playerList.size; + } + this.capacity = this.instance.capacity; + this.gameServerVersion = this.instance.gameServerVersion; + this.queueSize = this.instance.queueSize; + if (this.instance.platforms) { + this.platforms = this.instance.platforms; + } + if (this.instance.users) { + this.userList = this.instance.users; + } + }, + showUserDialog(userId) { + API.$emit('SHOW_USER_DIALOG', userId); + } + }, + watch: { + updateelement() { + this.parse(); + }, + location() { + this.parse(); + }, + friendcount() { + this.parse(); + } + }, + created() { + this.parse(); + } + }); + Vue.component('avatar-info', { template: '
{{ avatarName }}{{ avatarType }}
', @@ -1281,6 +1376,7 @@ speechSynthesis.getVoices(); // set VRCX online/offline timers $online_for: this.currentUser.$online_for, $offline_for: this.currentUser.$offline_for, + $location_at: this.currentUser.$location_at, $travelingToTime: this.currentUser.$travelingToTime }); }); @@ -1616,6 +1712,7 @@ speechSynthesis.getVoices(); // VRCX $online_for: Date.now(), $offline_for: '', + $location_at: Date.now(), $travelingToTime: Date.now(), $homeLocation: {}, $isVRCPlus: false, @@ -2065,19 +2162,16 @@ speechSynthesis.getVoices(); return this.currentUser?.presence?.world; }; - // TODO: traveling to world checks API.actuallyGetCurrentLocation = async function () { let gameLogLocation = $app.lastLocation.location; if (gameLogLocation === 'traveling') { gameLogLocation = $app.lastLocationDestination; } - console.log('PWI: gameLog Location:', gameLogLocation); let presenceLocation = this.currentUser.$locationTag; if (presenceLocation === 'traveling') { presenceLocation = this.currentUser.$travelingToLocation; } - console.log('PWI: presence Location:', presenceLocation); // We want to use presence if it's valid to avoid extra API calls, but its prone to being outdated when this function is called. // So we check if the presence location is the same as the gameLog location; If it is, the presence is (probably) valid and we can use it. @@ -2085,7 +2179,6 @@ speechSynthesis.getVoices(); // If the user happens to be offline or the api is just being dumb, we assume that the user logged into VRCX is different than the one in-game and return the gameLog location. // This is really dumb. if (presenceLocation === gameLogLocation) { - console.log('PWI: locations match:', presenceLocation); const L = this.parseLocation(presenceLocation); return L.worldId; } @@ -2320,6 +2413,11 @@ speechSynthesis.getVoices(); }); }; + // #endregion + // #region | API: Instance + + API.cachedInstances = new Map(); + /* params: { worldId: string, @@ -2447,59 +2545,88 @@ speechSynthesis.getVoices(); }); }; - API.$on('INSTANCE', function (args) { - var { json } = args; - if (!json) { - return; + API.applyInstance = function (json) { + var ref = this.cachedInstances.get(json.id); + if (typeof ref === 'undefined') { + ref = { + id: '', + location: '', + instanceId: '', + name: '', + worldId: '', + type: '', + ownerId: '', + tags: [], + active: false, + full: false, + n_users: 0, + hasCapacityForYou: true, // not present depending on endpoint + capacity: 0, + recommendedCapacity: 0, + userCount: 0, // PC only? + queueEnabled: false, // only present with group instance type + queueSize: 0, // only present when queuing is enabled + platforms: [], + gameServerVersion: 0, + secureName: '', + shortName: '', + world: {}, + nonce: '', + users: [], // only present when you're the owner + clientNumber: '', + photonRegion: '', + region: '', + canRequestInvite: false, + permanent: false, + private: '', + strict: false, + // VRCX + $fetchedAt: '', + ...json + }; + this.cachedInstances.set(ref.id, ref); + } else { + Object.assign(ref, json); } - var D = $app.userDialog; - if ($app.userDialog.visible && D.ref.$location.tag === json.id) { - D.instance.occupants = json.n_users; - D.instance.full = - typeof json.hasCapacityForYou !== 'undefined' && - !json.hasCapacityForYou; - D.instance.json = json; + ref.$location = this.parseLocation(ref.location); + if (json.world?.id) { + ref.world = this.applyWorld(json.world); } - }); + if (!json.$fetchedAt) { + ref.$fetchedAt = new Date().toJSON(); + } + return ref; + }; API.$on('INSTANCE', function (args) { var { json } = args; if (!json) { return; } - var D = $app.worldDialog; - if ($app.worldDialog.visible && $app.worldDialog.id === json.worldId) { - for (var instance of D.rooms) { - if (instance.id === json.instanceId) { - instance.occupants = json.n_users; - instance.full = - typeof json.hasCapacityForYou !== 'undefined' && - !json.hasCapacityForYou; - instance.json = json; - break; - } - } - } + args.ref = this.applyInstance(args.json); }); API.$on('INSTANCE', function (args) { - var { json } = args; - if (!json) { - return; + if ( + $app.userDialog.visible && + $app.userDialog.ref.$location.tag === args.json.id + ) { + $app.applyUserDialogLocation(); } - var D = $app.groupDialog; - if ($app.groupDialog.visible && $app.groupDialog.id === json.ownerId) { - for (var instance of D.instances) { - if (instance.id === json.instanceId) { - instance.occupants = json.n_users; - instance.full = - typeof json.hasCapacityForYou !== 'undefined' && - !json.hasCapacityForYou; - instance.json = json; - break; - } - } + if ( + $app.worldDialog.visible && + $app.worldDialog.id === args.json.worldId + ) { + $app.applyWorldDialogInstances(); } + if ( + $app.groupDialog.visible && + $app.groupDialog.id === args.json.ownerId + ) { + $app.applyGroupDialogInstances(); + } + // hacky workaround to force update instance info + $app.updateInstanceInfo++; }); // #endregion @@ -5098,11 +5225,16 @@ speechSynthesis.getVoices(); this.enableAppLauncher, this.enableAppLauncherAutoClose ); + API.$on('SHOW_USER_DIALOG', (userId) => + this.showUserDialog(userId) + ); API.$on('SHOW_WORLD_DIALOG', (tag) => this.showWorldDialog(tag)); API.$on('SHOW_WORLD_DIALOG_SHORTNAME', (tag) => this.verifyShortName('', tag) ); - API.$on('SHOW_GROUP_DIALOG', (tag) => this.showGroupDialog(tag)); + API.$on('SHOW_GROUP_DIALOG', (groupId) => + this.showGroupDialog(groupId) + ); API.$on('SHOW_LAUNCH_DIALOG', (tag, shortName) => this.showLaunchDialog(tag, shortName) ); @@ -9616,8 +9748,12 @@ speechSynthesis.getVoices(); location: gameLog.location, worldId: L.worldId, worldName, + groupName: '', time: 0 }; + this.getGroupName(gameLog.location).then((groupName) => { + entry.groupName = groupName; + }); this.addGamelogLocationToDatabase(entry); break; case 'player-joined': @@ -9978,6 +10114,66 @@ speechSynthesis.getVoices(); 'CallUdonMethod' ]; + $app.data.oldPhotonEmojis = [ + 'Angry', + 'Blushing', + 'Crying', + 'Frown', + 'Hand Wave', + 'Hang Ten', + 'In Love', + 'Jack O Lantern', + 'Kiss', + 'Laugh', + 'Skull', + 'Smile', + 'Spooky Ghost', + 'Stoic', + 'Sunglasses', + 'Thinking', + 'Thumbs Down', + 'Thumbs Up', + 'Tongue Out', + 'Wow', + 'Bats', + 'Cloud', + 'Fire', + 'Snow Fall', + 'Snowball', + 'Splash', + 'Web', + 'Beer', + 'Candy', + 'Candy Cane', + 'Candy Corn', + 'Champagne', + 'Drink', + 'Gingerbread', + 'Ice Cream', + 'Pineapple', + 'Pizza', + 'Tomato', + 'Beachball', + 'Coal', + 'Confetti', + 'Gift', + 'Gifts', + 'Life Ring', + 'Mistletoe', + 'Money', + 'Neon Shades', + 'Sun Lotion', + 'Boo', + 'Broken Heart', + 'Exclamation', + 'Go', + 'Heart', + 'Music Note', + 'Question', + 'Stop', + 'Zzz' + ]; + $app.data.photonEmojis = [ 'Angry', 'Blushing', @@ -9999,6 +10195,14 @@ speechSynthesis.getVoices(); 'Thumbs Up', 'Tongue Out', 'Wow', + 'Arrow Point', + "Can't see", + 'Hourglass', + 'Keyboard', + 'No Headphones', + 'No Mic', + 'Portal', + 'Shush', 'Bats', 'Cloud', 'Fire', @@ -10230,20 +10434,30 @@ speechSynthesis.getVoices(); if (input.photonId === this.photonLobbyMaster) { isMaster = true; } - var userId = this.getUserIdFromPhotonId(input.photonId); + var joinTimeRef = this.photonLobbyJointime.get(input.photonId); + var isModerator = joinTimeRef?.canModerateInstance; + var photonUserRef = this.photonLobby.get(input.photonId); + var displayName = ''; + var userId = ''; + var isFriend = false; + if (typeof photonUserRef !== 'undefined') { + displayName = photonUserRef.displayName; + userId = photonUserRef.id; + isFriend = photonUserRef.isFriend; + } var isFavorite = API.cachedFavoritesByObjectId.has(userId); - var isFriend = this.friends.has(userId); var colour = ''; var tagRef = this.customUserTags.get(userId); if (typeof tagRef !== 'undefined' && tagRef.colour) { colour = tagRef.colour; } var feed = { - displayName: this.getDisplayNameFromPhotonId(input.photonId), + displayName, userId, isFavorite, isFriend, isMaster, + isModerator, colour, ...input }; @@ -10747,6 +10961,25 @@ speechSynthesis.getVoices(); }); } break; + case 71: + // Spawn Emoji + var photonId = data.Parameters[254]; + var type = data.Parameters[245][0]; + var emojiName = ''; + if (type === 0) { + var emojiId = data.Parameters[245][2]; + emojiName = this.photonEmojis[emojiId]; + } else if (type === 1) { + // file_id + emojiName = data.Parameters[245][1]; + } + this.addEntryPhotonEvent({ + photonId, + text: emojiName, + type: 'SpawnEmoji', + created_at: gameLogDate + }); + break; } }; @@ -10791,7 +11024,7 @@ speechSynthesis.getVoices(); } else if (eventData.EventName === 'ReleaseBones') { var text = 'ResetPhysBones'; } else if (eventData.EventName === 'SpawnEmojiRPC') { - var text = this.photonEmojis[eventData.Data]; + var text = this.oldPhotonEmojis[eventData.Data]; type = 'SpawnEmoji'; } else { var eventVrc = ''; @@ -11720,6 +11953,9 @@ speechSynthesis.getVoices(); var videoName = data[4]; var videoUrl = videoName; var videoId = 'Movie&Chill'; + if (!videoName) { + return; + } if (videoUrl === this.nowPlaying.url) { var entry = { created_at: gameLog.dt, @@ -12455,16 +12691,18 @@ speechSynthesis.getVoices(); this.isSearchAvatarLoading = false; } var avatarsArray = Array.from(avatars.values()); - switch (this.searchAvatarSort) { - case 'updated': - avatarsArray.sort(compareByUpdatedAt); - break; - case 'created': - avatarsArray.sort(compareByCreatedAt); - break; - case 'name': - avatarsArray.sort(compareByName); - break; + if (this.searchAvatarFilterRemote === 'local') { + switch (this.searchAvatarSort) { + case 'updated': + avatarsArray.sort(compareByUpdatedAt); + break; + case 'created': + avatarsArray.sort(compareByCreatedAt); + break; + case 'name': + avatarsArray.sort(compareByName); + break; + } } this.searchAvatarPageNum = 0; this.searchAvatarResults = avatarsArray; @@ -14280,7 +14518,7 @@ speechSynthesis.getVoices(); this.updateVRConfigVars(); }; - $app.data.youTubeApi = configRepository.getBool('VRCX_youtubeAPI', true); + $app.data.youTubeApi = configRepository.getBool('VRCX_youtubeAPI', false); $app.data.youTubeApiKey = configRepository.getString( 'VRCX_youtubeAPIKey', '' @@ -15515,12 +15753,10 @@ speechSynthesis.getVoices(); id: '', tag: '', $location: {}, - occupants: 0, friendCount: 0, - full: false, users: [], shortName: '', - json: {} + ref: {} }; D.representedGroup = { bannerUrl: '', @@ -15726,14 +15962,11 @@ speechSynthesis.getVoices(); return; } var L = API.parseLocation(D.ref.$location.tag); - if (L.tag !== this.lastLocation.location && updateInstanceOccupants) { - this.userDialog.instance.occupants = 0; - if (this.isRealInstance(L.tag)) { - API.getInstance({ - worldId: L.worldId, - instanceId: L.instanceId - }); - } + if (updateInstanceOccupants && this.isRealInstance(L.tag)) { + API.getInstance({ + worldId: L.worldId, + instanceId: L.instanceId + }); } D.$location = L; if (L.userId) { @@ -15819,12 +16052,10 @@ speechSynthesis.getVoices(); id: L.instanceId, tag: L.tag, $location: L, - occupants: this.lastLocation.playerList.size, friendCount: 0, - full: false, users: [], shortName: '', - json: {} + ref: {} }; } if (!this.isRealInstance(L.tag)) { @@ -15832,13 +16063,16 @@ speechSynthesis.getVoices(); id: L.instanceId, tag: L.tag, $location: L, - occupants: 0, - full: false, + friendCount: 0, users: [], shortName: '', - json: {} + ref: {} }; } + var instanceRef = API.cachedInstances.get(L.tag); + if (typeof instanceRef !== 'undefined') { + D.instance.ref = instanceRef; + } D.instance.friendCount = friendCount; this.updateTimers(); }; @@ -15929,12 +16163,14 @@ speechSynthesis.getVoices(); ) { isMaster = true; } + var isModerator = false; var lobbyJointime = $app.photonLobbyJointime.get(photonId); var inVRMode = null; var groupOnNameplate = ''; if (typeof lobbyJointime !== 'undefined') { inVRMode = lobbyJointime.inVRMode; groupOnNameplate = lobbyJointime.groupOnNameplate; + isModerator = lobbyJointime.canModerateInstance; } // if (groupOnNameplate) { // API.getCachedGroup({ @@ -15945,7 +16181,7 @@ speechSynthesis.getVoices(); // } var timeoutTime = 0; if (typeof ref.id !== 'undefined') { - isFriend = $app.friends.has(ref.id); + isFriend = ref.isFriend; if ( $app.timeoutHudOverlayFilter === 'VIP' || $app.timeoutHudOverlayFilter === 'Friends' @@ -15970,6 +16206,7 @@ speechSynthesis.getVoices(); $trustSortNum: ref.$trustSortNum ?? 0, photonId, isMaster, + isModerator, inVRMode, groupOnNameplate, isFriend, @@ -16021,8 +16258,11 @@ speechSynthesis.getVoices(); this.updateTimers(); }; + $app.data.updateInstanceInfo = 0; + $app.data.currentInstanceWorld = { ref: {}, + instance: {}, isPC: false, isQuest: false, isIos: false, @@ -16041,6 +16281,7 @@ speechSynthesis.getVoices(); if (!instanceId) { this.currentInstanceWorld = { ref: {}, + instance: {}, isPC: false, isQuest: false, isIos: false, @@ -16053,6 +16294,7 @@ speechSynthesis.getVoices(); } else if (instanceId !== this.currentInstanceLocation.tag) { this.currentInstanceWorld = { ref: {}, + instance: {}, isPC: false, isQuest: false, isIos: false, @@ -16110,6 +16352,20 @@ speechSynthesis.getVoices(); }); }); } + if (this.isRealInstance(instanceId)) { + var ref = API.cachedInstances.get(instanceId); + if (typeof ref !== 'undefined') { + this.currentInstanceWorld.instance = ref; + } else { + var L = API.parseLocation(instanceId); + API.getInstance({ + worldId: L.worldId, + instanceId: L.instanceId + }).then((args) => { + this.currentInstanceWorld.instance = args.ref; + }); + } + } }; $app.methods.getAvailablePlatforms = function (unityPackages) { @@ -16708,11 +16964,7 @@ speechSynthesis.getVoices(); D.ref = ref; $app.applyWorldDialogInstances(); for (var room of D.rooms) { - if ( - $app.isRealInstance(room.tag) && - room.tag !== $app.lastLocation.location && - (room.$location.accessType !== 'public' || room.occupants === 0) - ) { + if ($app.isRealInstance(room.tag)) { API.getInstance({ worldId: D.id, instanceId: room.id @@ -16892,13 +17144,14 @@ speechSynthesis.getVoices(); } var instances = {}; if (D.ref.instances) { - for (var [id, occupants] of D.ref.instances) { - instances[id] = { - id, - tag: `${D.id}:${id}`, - occupants, + for (var instance of D.ref.instances) { + // instance = [ instanceId, occupants ] + var instanceId = instance[0]; + instances[instanceId] = { + id: instanceId, + tag: `${D.id}:${instanceId}`, + $location: {}, friendCount: 0, - full: false, users: [], shortName: '', json: {} @@ -16911,9 +17164,7 @@ speechSynthesis.getVoices(); id: instanceId, tag: `${D.id}:${instanceId}`, $location: {}, - occupants: 0, friendCount: 0, - full: false, users: [], shortName, json: {} @@ -16923,17 +17174,16 @@ speechSynthesis.getVoices(); var lastLocation$ = cachedCurrentUser.$location; var playersInInstance = this.lastLocation.playerList; if (lastLocation$.worldId === D.id && playersInInstance.size > 0) { + // pull instance json from cache var friendsInInstance = this.lastLocation.friendList; var instance = { id: lastLocation$.instanceId, tag: lastLocation$.tag, $location: {}, - occupants: playersInInstance.size, friendCount: friendsInInstance.size, - full: false, users: [], shortName: '', - json: {} + ref: {} }; instances[instance.id] = instance; for (var friend of friendsInInstance.values()) { @@ -16967,12 +17217,10 @@ speechSynthesis.getVoices(); id: instanceId, tag: `${D.id}:${instanceId}`, $location: {}, - occupants: 0, friendCount: 0, - full: false, users: [], shortName: '', - json: {} + ref: {} }; instances[instanceId] = instance; } @@ -16987,12 +17235,10 @@ speechSynthesis.getVoices(); id: instanceId, tag: `${D.id}:${instanceId}`, $location: {}, - occupants: 0, friendCount: 0, - full: false, users: [], shortName: '', - json: {} + ref: {} }; instances[instanceId] = instance; } @@ -17031,21 +17277,11 @@ speechSynthesis.getVoices(); } rooms.push(instance); } - // reuse instance occupants from getInstance + // get instance from cache for (var room of rooms) { - if ( - $app.isRealInstance(room.tag) && - room.tag !== $app.lastLocation.location && - (room.$location.accessType !== 'public' || room.occupants === 0) - ) { - for (var instance of D.rooms) { - if (instance.id === room.id) { - room.occupants = instance.occupants; - room.full = instance.full; - room.json = instance.json; - break; - } - } + var ref = API.cachedInstances.get(room.tag); + if (typeof ref !== 'undefined') { + room.ref = ref; } } // sort by more friends, occupants @@ -17075,12 +17311,10 @@ speechSynthesis.getVoices(); id: instance.instanceId, tag: instance.location, $location: {}, - occupants: instance.userCount, friendCount: 0, - full: false, users: [], shortName: instance.shortName, - json: instance + ref: instance }; } } @@ -17094,12 +17328,10 @@ speechSynthesis.getVoices(); id: lastLocation$.instanceId, tag: currentLocation, $location: {}, - occupants: playersInInstance.size, friendCount: friendsInInstance.size, - full: false, users: [], shortName: '', - json: {} + ref: {} }; instances[currentLocation] = instance; for (var friend of friendsInInstance.values()) { @@ -17133,12 +17365,10 @@ speechSynthesis.getVoices(); id: instanceId, tag, $location: {}, - occupants: 0, friendCount: 0, - full: false, users: [], shortName: '', - json: {} + ref: {} }; instances[tag] = instance; } @@ -17153,12 +17383,10 @@ speechSynthesis.getVoices(); id: instanceId, tag, $location: {}, - occupants: 0, friendCount: 0, - full: false, users: [], shortName: '', - json: {} + ref: {} }; instances[tag] = instance; } @@ -17181,6 +17409,18 @@ speechSynthesis.getVoices(); } rooms.push(instance); } + // get instance + for (var room of rooms) { + var ref = API.cachedInstances.get(room.tag); + if (typeof ref !== 'undefined') { + room.ref = ref; + } else if ($app.isRealInstance(room.tag)) { + API.getInstance({ + worldId: room.$location.worldId, + instanceId: room.$location.instanceId + }); + } + } // sort by more friends, occupants rooms.sort(function (a, b) { return b.users.length - a.users.length || b.occupants - a.occupants; @@ -21941,7 +22181,7 @@ speechSynthesis.getVoices(); $app.methods.refreshInstancePlayerCount = function (instance) { var L = API.parseLocation(instance); - if (L.worldId && L.tag !== this.lastLocation.location) { + if (L.worldId && L.instanceId) { API.getInstance({ worldId: L.worldId, instanceId: L.instanceId @@ -22613,7 +22853,14 @@ speechSynthesis.getVoices(); API.cachedGroups.delete(id); } }); + API.cachedInstances.forEach((ref, id) => { + // delete instances over an hour old + if (Date.parse(ref.$fetchedAt) < Date.now() - 3600000) { + API.cachedInstances.delete(id); + } + }); API.cachedAvatarNames = new Map(); + this.updateInstanceInfo = 0; }; $app.data.sqliteTableSizes = {}; @@ -25032,10 +25279,13 @@ speechSynthesis.getVoices(); }); API.$on('GROUP', function (args) { - if ($app.groupDialog.visible && $app.groupDialog.id === args.ref.id) { - // update group dialog - $app.groupDialog.inGroup = args.ref.membershipStatus === 'member'; + var { ref } = args; + var D = $app.groupDialog; + if (D.visible === false || D.id !== ref.id) { + return; } + D.inGroup = ref.membershipStatus === 'member'; + D.ref = ref; }); /* @@ -25507,6 +25757,10 @@ speechSynthesis.getVoices(); API.$on('GROUP:USER:INSTANCES', function (args) { $app.groupInstances = []; for (var json of args.json.instances) { + if (args.json.fetchedAt) { + // tack on fetchedAt + json.$fetchedAt = args.json.fetchedAt; + } this.$emit('INSTANCE', { json, params: { @@ -25519,7 +25773,6 @@ speechSynthesis.getVoices(); worldId: json.world.id } }); - json.world = this.applyWorld(json.world); var ref = this.cachedGroups.get(json.ownerId); if (typeof ref === 'undefined') { @@ -25529,28 +25782,12 @@ speechSynthesis.getVoices(); return; } $app.groupInstances.push({ - $group: ref, - ...json + group: ref, + instance: this.applyInstance(json) }); } }); - API.$on('INSTANCE', function (args) { - var { json } = args; - if (!json) { - return; - } - for (var instance of $app.groupInstances) { - if (instance.id === json.id) { - instance = { - ...instance, - ...json - }; - break; - } - } - }); - /* params: { query: string, diff --git a/html/src/index.pug b/html/src/index.pug index 3bdbef76..71abd717 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -97,8 +97,9 @@ html template(v-if="item.ref") .detail span.name(v-text="item.ref.displayName" :style="{'color':item.ref.$userColour}") - span.extra(v-if="item.ref.state === 'offline'") {{ $t('side_panel.search_result_offline') }} - span.extra(v-else-if="item.ref.state === 'active'") {{ $t('side_panel.search_result_active') }} + span.extra(v-if="!item.ref.isFriend") + span.extra(v-else-if="item.ref.state === 'offline'") {{ $t('side_panel.search_result_active') }} + span.extra(v-else-if="item.ref.state === 'active'") {{ $t('side_panel.search_result_offline') }} location.extra(v-else :location="item.ref.location" :traveling="item.ref.travelingToLocation" :link="false") img.avatar(v-lazy="userImage(item.ref)") span(v-else) {{ $t('side_panel.search_result_more') }} #[span(v-text="item.label" style="font-weight:bold")] @@ -189,14 +190,14 @@ html template(#label) span {{ $t('side_panel.groups') }} ({{ groupInstances.length }}) .x-friend-list(style="padding:10px 5px") - .x-friend-item(v-for="instance in groupInstances" :key="instance.id" @click="showGroupDialog(instance.ownerId)") + .x-friend-item(v-for="ref in groupInstances" :key="ref.instance.id" @click="showGroupDialog(ref.instance.ownerId)") .avatar - img(v-lazy="instance.$group.iconUrl") + img(v-lazy="ref.group.iconUrl") .detail span.name - span(v-text="instance.$group.name") - span(style="font-weight:normal;margin-left:5px") ({{ instance.userCount }}/{{ instance.capacity }}) - location.extra(:location="instance.location" :link="false") + span(v-text="ref.group.name") + span(style="font-weight:normal;margin-left:5px") ({{ ref.instance.userCount }}/{{ ref.instance.capacity }}) + location.extra(:location="ref.instance.location" :link="false") //- ## Dialogs ## -\\ @@ -314,16 +315,16 @@ html template(v-if="isFriendOnline(userDialog.friend) || API.currentUser.id === userDialog.id") div(v-if="userDialog.ref.location" style="display:flex;flex-direction:column;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #e4e7ed14") div(style="flex:none") - location(:location="userDialog.ref.location" :traveling="userDialog.ref.travelingToLocation") template(v-if="isRealInstance(userDialog.$location.tag)") el-tooltip(placement="top" :content="$t('dialog.user.info.launch_invite_tooltip')" :disabled="hideTooltips") - launch(:location="userDialog.$location.tag" style="margin-left:5px") + launch(:location="userDialog.$location.tag") el-tooltip(placement="top" :content="$t('dialog.user.info.self_invite_tooltip')" :disabled="hideTooltips") - invite-yourself(:location="userDialog.$location.tag" :shortname="userDialog.$location.shortName" style="margin-left:5px" :disabled="typeof userDialog.instance.json.canRequestInvite !== 'undefined' && !userDialog.instance.json.canRequestInvite") - el-tooltip(placement="top" :content="$t('dialog.user.info.refresh_user_count_tooltip')" :disabled="hideTooltips") - el-button(v-if="userDialog.$location.tag !== lastLocation.location" @click="refreshInstancePlayerCount(userDialog.$location.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle) - span(v-if="userDialog.instance.occupants" style="margin-left:5px") {{ userDialog.instance.occupants }} #[template(v-if="userDialog.instance.friendCount > 0") ({{ userDialog.instance.friendCount }})] - span(v-if="userDialog.instance.full" style="margin-left:5px;color:lightcoral") {{ $t('dialog.user.info.instance_full') }} + invite-yourself(:location="userDialog.$location.tag" :shortname="userDialog.$location.shortName" style="margin-left:5px" :disabled="typeof userDialog.instance.ref?.canRequestInvite !== 'undefined' && !userDialog.instance.ref.canRequestInvite") + el-tooltip(placement="top" :content="$t('dialog.user.info.refresh_instance_info')" :disabled="hideTooltips") + el-button(@click="refreshInstancePlayerCount(userDialog.$location.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle) + instance-info(:location="userDialog.$location.tag" :instance="userDialog.instance.ref" :friendcount="userDialog.instance.friendCount" :updateelement="updateInstanceInfo") + br + location(:location="userDialog.ref.location" :traveling="userDialog.ref.travelingToLocation") .x-friend-list(style="flex:1;margin-top:10px;max-height:150px") .x-friend-item(v-if="userDialog.$location.userId" @click="showUserDialog(userDialog.$location.userId)" class="x-friend-item-border") template(v-if="userDialog.$location.user") @@ -614,11 +615,10 @@ html div(style="margin:5px 0") location-world(:locationobject="room.$location" :currentuserid="API.currentUser.id" :worlddialogshortname="worldDialog.$location.shortName") el-tooltip(placement="top" :content="$t('dialog.world.instances.self_invite_tooltip')" :disabled="hideTooltips") - invite-yourself(:location="room.$location.tag" :shortname="room.$location.shortName" style="margin-left:5px" :disabled="typeof room.json.canRequestInvite !== 'undefined' && !room.json.canRequestInvite") - el-tooltip(placement="top" :content="$t('dialog.world.instances.refresh_user_count_tooltip')" :disabled="hideTooltips") - el-button(v-if="room.tag !== lastLocation.location" @click="refreshInstancePlayerCount(room.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle) - span(v-if="room.occupants" style="margin-left:5px") {{ room.occupants }} #[template(v-if="room.friendCount > 0") ({{ room.friendCount }})] - span(v-if="room.full" style="margin-left:5px;color:lightcoral") {{ $t('dialog.world.instances.instance_full') }} + invite-yourself(:location="room.$location.tag" :shortname="room.$location.shortName" style="margin-left:5px" :disabled="typeof room.ref?.canRequestInvite !== 'undefined' && !room.ref.canRequestInvite") + el-tooltip(placement="top" :content="$t('dialog.world.instances.refresh_instance_info')" :disabled="hideTooltips") + el-button(@click="refreshInstancePlayerCount(room.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle) + instance-info(:location="room.tag" :instance="room.ref" :friendcount="room.friendCount" :updateelement="updateInstanceInfo") .x-friend-list(style="margin:10px 0;max-height:unset" v-if="room.$location.userId || room.users.length") .x-friend-item(v-if="room.$location.userId" @click="showUserDialog(room.$location.userId)" class="x-friend-item-border") template(v-if="room.$location.user") @@ -912,11 +912,10 @@ html div(style="margin:5px 0") location(:location="room.tag") el-tooltip(placement="top" content="Invite yourself" :disabled="hideTooltips") - invite-yourself(:location="room.tag" style="margin-left:5px" :disabled="typeof room.json.canRequestInvite !== 'undefined' && !room.json.canRequestInvite") + invite-yourself(:location="room.tag" style="margin-left:5px" :disabled="typeof room.ref?.canRequestInvite !== 'undefined' && !room.ref.canRequestInvite") el-tooltip(placement="top" content="Refresh player count" :disabled="hideTooltips") - el-button(v-if="room.tag !== lastLocation.location" @click="refreshInstancePlayerCount(room.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle) - span(v-if="room.occupants" style="margin-left:5px") {{ room.occupants }} #[template(v-if="room.friendCount > 0") ({{ room.friendCount }})] - span(v-if="room.full" style="margin-left:5px;color:lightcoral") {{ $t('dialog.group.info.instance_full') }} + el-button(@click="refreshInstancePlayerCount(room.tag)" size="mini" icon="el-icon-refresh" style="margin-left:5px" circle) + 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(v-for="user in room.users" :key="user.id" @click="showUserDialog(user.id)" class="x-friend-item-border") .avatar(:class="userStatusClass(user)") diff --git a/html/src/localization/localizedStrings.js b/html/src/localization/localizedStrings.js index d7386981..9f5406c6 100644 --- a/html/src/localization/localizedStrings.js +++ b/html/src/localization/localizedStrings.js @@ -13,16 +13,21 @@ import elements_ko from 'element-ui/lib/locale/lang/ko'; import zh_CN from './strings/zh_CN.json' assert { type: 'JSON' }; import elements_zh_CN from 'element-ui/lib/locale/lang/zh-CN'; +import fr from './strings/fr.json' assert { type: 'JSON' }; +import elements_fr from 'element-ui/lib/locale/lang/fr'; + const localized_en = { ...en, ...elements_en }; const localized_zh_TW = { ...zh_TW, ...elements_zh_TW }; const localized_zh_CN = { ...zh_CN, ...elements_zh_CN }; const localized_ko = { ...ko, ...elements_ko }; const localized_ja = { ...ja, ...elements_ja }; +const localized_fr = { ...fr, ...elements_fr }; export { localized_en as en, localized_zh_TW as zh_TW, localized_ko as ko, localized_zh_CN as zh_CN, - localized_ja as ja_JP + localized_ja as ja_JP, + localized_fr as fr }; diff --git a/html/src/localization/strings/en.json b/html/src/localization/strings/en.json index d45b5c82..0a5817bd 100644 --- a/html/src/localization/strings/en.json +++ b/html/src/localization/strings/en.json @@ -544,7 +544,11 @@ "header": "Info", "launch_invite_tooltip": "Launch/Invite", "self_invite_tooltip": "Invite Yourself", - "refresh_user_count_tooltip": "Refresh User Count", + "refresh_instance_info": "Refresh Instance Info", + "instance_queue": "Queue:", + "instance_users": "Users:", + "instance_game_version": "Game Version:", + "instance_queuing_enabled": "Queuing Enabled", "instance_creator": "Instance Creator", "note": "Note", "note_placeholder": "Click to add a note", @@ -633,7 +637,7 @@ "private_count": "Private {count}", "capacity_count": "Capacity {count} ({max})", "self_invite_tooltip": "Invite Yourself", - "refresh_user_count_tooltip": "Refresh User Count", + "refresh_instance_info": "Refresh Instance Info", "instance_full": "full", "instance_creator": "Instance Creator" }, diff --git a/html/src/mixins/tabs/playerList.pug b/html/src/mixins/tabs/playerList.pug index 846382db..bf8a7aec 100644 --- a/html/src/mixins/tabs/playerList.pug +++ b/html/src/mixins/tabs/playerList.pug @@ -24,9 +24,7 @@ mixin playerListTab() | {{ $t('dialog.world.tags.cache') }} br location-world(:locationobject="currentInstanceLocation" :currentuserid="API.currentUser.id") - span(v-if="lastLocation.playerList.size > 0" style="margin-left:5px") - | {{ lastLocation.playerList.size }} - | #[template(v-if="lastLocation.friendList.size > 0") ({{ lastLocation.friendList.size }})] + instance-info(:location="currentInstanceLocation.tag" :instance="currentInstanceWorld.instance" :friendcount="lastLocation.friendList.size" :updateelement="updateInstanceInfo") |  #[timer(v-if="lastLocation.date" :epoch="lastLocation.date")] div(style="margin-top:5px") span(v-show="currentInstanceWorld.ref.name !== currentInstanceWorld.ref.description" v-text="currentInstanceWorld.ref.description" style="font-size:12px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2") @@ -207,6 +205,8 @@ mixin playerListTab() template(v-once #default="scope") el-tooltip(v-if="scope.row.isMaster" placement="left" content="Instance Master") span 👑 + el-tooltip(v-if="scope.row.isModerator" placement="left" content="Moderator") + span ⚔️ el-tooltip(v-if="scope.row.isFriend" placement="left" content="Friend") span 💚 el-tooltip(v-if="scope.row.timeoutTime" placement="left" content="Timeout") diff --git a/html/src/vr.pug b/html/src/vr.pug index e9ecb19d..76ce5c85 100644 --- a/html/src/vr.pug +++ b/html/src/vr.pug @@ -489,7 +489,7 @@ html circle(class="np-progress-circle-stroke" cx="60" cy="60" stroke="white" r="30" fill="transparent" stroke-width="60") .hud-feed div(v-for="feed in hudFeed") - .item(:class="{ friend: feed.isFriend, favorite: feed.isFavorite }") #[span(v-if="feed.isMaster") 👑]#[strong.name(v-text="feed.displayName" :style="{'color':feed.colour}")] + .item(:class="{ friend: feed.isFriend, favorite: feed.isFavorite }") #[span(v-if="feed.isMaster") 👑]#[span(v-if="feed.isModerator") ⚔️]#[strong.name(v-text="feed.displayName" :style="{'color':feed.colour}")] template(v-if="feed.type === 'ChangeAvatar'") span(style="margin-left:10px;color:#a3a3a3") ChangeAvatar span(v-if="!feed.inCache" style="color:#aaa;margin-left:10px") #[i.el-icon-download]