feat: Limit bio display height(#1158), Toggle for display of age gated instances(#1188), fix: (#1189)(#1192) (#1195)

This commit is contained in:
pa
2025-03-24 11:13:35 +09:00
committed by GitHub
parent 249dc7fc1e
commit 71d8cc22bf
12 changed files with 183 additions and 136 deletions

View File

@@ -6908,6 +6908,21 @@ console.log(`isLinux: ${LINUX}`);
); );
} }
$app.data.pendingOfflineDelay = 180000; $app.data.pendingOfflineDelay = 180000;
// It's a mess, but it'll be fine afterward with the state manager
$app.data.isAgeGatedInstancesVisible = await configRepository.getBool(
'VRCX_isAgeGatedInstancesVisible',
true
);
$app.methods.toggleIsAgeGatedInstancesVisible = function () {
this.isAgeGatedInstancesVisible = !this.isAgeGatedInstancesVisible;
configRepository.setBool(
'VRCX_isAgeGatedInstancesVisible',
this.isAgeGatedInstancesVisible
);
};
if (await configRepository.getString('VRCX_avatarRemoteDatabaseProvider')) { if (await configRepository.getString('VRCX_avatarRemoteDatabaseProvider')) {
// move existing provider to new list // move existing provider to new list
var avatarRemoteDatabaseProvider = await configRepository.getString( var avatarRemoteDatabaseProvider = await configRepository.getString(
@@ -16694,25 +16709,6 @@ console.log(`isLinux: ${LINUX}`);
resolution = '128', resolution = '128',
isUserDialogIcon = false isUserDialogIcon = false
) { ) {
function convertFileUrlToImageUrl(url) {
/**
* possible patterns?
* /file/file_fileId/version
* /file/file_fileId/version/
* /file/file_fileId/version/file
* /file/file_fileId/version/file/
*/
const pattern = /file\/file_([a-f0-9-]+)\/(\d+)(\/file)?\/?$/;
const match = url.match(pattern);
if (match) {
const fileId = match[1];
const version = match[2];
return `https://api.vrchat.cloud/api/1/image/file_${fileId}/${version}/${resolution}`;
}
// return /image/file_fileId url?
return url;
}
if (!user) { if (!user) {
return ''; return '';
} }
@@ -16721,7 +16717,7 @@ console.log(`isLinux: ${LINUX}`);
(this.displayVRCPlusIconsAsAvatar && user.userIcon) (this.displayVRCPlusIconsAsAvatar && user.userIcon)
) { ) {
if (isIcon) { if (isIcon) {
return convertFileUrlToImageUrl(user.userIcon); return $utils.convertFileUrlToImageUrl(user.userIcon);
} }
return user.userIcon; return user.userIcon;
} }
@@ -16752,7 +16748,9 @@ console.log(`isLinux: ${LINUX}`);
} }
if (user.currentAvatarImageUrl) { if (user.currentAvatarImageUrl) {
if (isIcon) { if (isIcon) {
return convertFileUrlToImageUrl(user.currentAvatarImageUrl); return $utils.convertFileUrlToImageUrl(
user.currentAvatarImageUrl
);
} }
return user.currentAvatarImageUrl; return user.currentAvatarImageUrl;
} }
@@ -19908,12 +19906,8 @@ console.log(`isLinux: ${LINUX}`);
); );
}; };
$app.methods.getSmallThumbnailUrl = function (url, resolution = 128) { $app.methods.getSmallThumbnailUrl = function (url) {
return ( return $utils.convertFileUrlToImageUrl(url);
url
?.replace('/file/', '/image/')
.replace('/1/file', `/1/${resolution}`) || url
);
}; };
// #endregion // #endregion
@@ -19975,7 +19969,8 @@ console.log(`isLinux: ${LINUX}`);
groupInstances: this.groupInstances, groupInstances: this.groupInstances,
inGameGroupOrder: this.inGameGroupOrder, inGameGroupOrder: this.inGameGroupOrder,
groupedByGroupKeyFavoriteFriends: groupedByGroupKeyFavoriteFriends:
this.groupedByGroupKeyFavoriteFriends this.groupedByGroupKeyFavoriteFriends,
isAgeGatedInstancesVisible: this.isAgeGatedInstancesVisible
}; };
}; };

View File

@@ -4,6 +4,7 @@ import configRepository from '../repository/config.js';
import database from '../repository/database.js'; import database from '../repository/database.js';
import { baseClass, $app, API, $t, $utils } from './baseClass.js'; import { baseClass, $app, API, $t, $utils } from './baseClass.js';
import { userRequest } from './request'; import { userRequest } from './request';
import dayjs from 'dayjs';
export default class extends baseClass { export default class extends baseClass {
constructor(_app, _API, _t) { constructor(_app, _API, _t) {
@@ -168,7 +169,8 @@ export default class extends baseClass {
if (typeof friendRef?.ref !== 'undefined') { if (typeof friendRef?.ref !== 'undefined') {
friendRef.ref.$joinCount++; friendRef.ref.$joinCount++;
friendRef.ref.$lastSeen = new Date().toJSON(); friendRef.ref.$lastSeen = new Date().toJSON();
friendRef.ref.$timeSpent += Date.now() - ref.joinTime; friendRef.ref.$timeSpent +=
dayjs(gameLog.dt) - ref.joinTime;
if ( if (
this.sidebarSortMethods.includes( this.sidebarSortMethods.includes(
'Sort by Last Seen' 'Sort by Last Seen'
@@ -178,7 +180,7 @@ export default class extends baseClass {
this.sortOnlineFriends = true; this.sortOnlineFriends = true;
} }
} }
var time = Date.now() - ref.joinTime; var time = dayjs(gameLog.dt) - ref.joinTime;
this.lastLocation.playerList.delete(userId); this.lastLocation.playerList.delete(userId);
this.lastLocation.friendList.delete(userId); this.lastLocation.friendList.delete(userId);
this.photonLobbyAvatars.delete(userId); this.photonLobbyAvatars.delete(userId);

View File

@@ -402,5 +402,24 @@ export default {
} }
} }
return false; return false;
},
convertFileUrlToImageUrl(url, resolution = 128) {
/**
* possible patterns?
* /file/file_fileId/version
* /file/file_fileId/version/
* /file/file_fileId/version/file
* /file/file_fileId/version/file/
*/
const pattern = /file\/file_([a-f0-9-]+)\/(\d+)(\/file)?\/?$/;
const match = url.match(pattern);
if (match) {
const fileId = match[1];
const version = match[2];
return `https://api.vrchat.cloud/api/1/image/file_${fileId}/${version}/${resolution}`;
}
// no match return origin url
return url;
} }
}; };

View File

@@ -22,18 +22,20 @@
:key="ref.instance.id" :key="ref.instance.id"
class="x-friend-item" class="x-friend-item"
@click="$emit('show-group-dialog', ref.instance.ownerId)"> @click="$emit('show-group-dialog', ref.instance.ownerId)">
<div class="avatar"> <template v-if="isAgeGatedInstancesVisible || !(ref.ageGate || ref.location?.includes('~ageGate'))">
<img v-lazy="ref.group.iconUrl" /> <div class="avatar">
</div> <img v-lazy="getSmallGroupIconUrl(ref.group.iconUrl)" />
<div class="detail"> </div>
<span class="name"> <div class="detail">
<span v-text="ref.group.name"></span> <span class="name">
<span style="font-weight: normal; margin-left: 5px" <span v-text="ref.group.name"></span>
>({{ ref.instance.userCount }}/{{ ref.instance.capacity }})</span <span style="font-weight: normal; margin-left: 5px"
> >({{ ref.instance.userCount }}/{{ ref.instance.capacity }})</span
</span> >
<location class="extra" :location="ref.instance.location" :link="false" /> </span>
</div> <location class="extra" :location="ref.instance.location" :link="false" />
</div>
</template>
</div> </div>
</template> </template>
</template> </template>
@@ -42,6 +44,7 @@
<script> <script>
import Location from '../common/Location.vue'; import Location from '../common/Location.vue';
import utils from '../../classes/utils';
export default { export default {
name: 'GroupsSidebar', name: 'GroupsSidebar',
@@ -56,6 +59,10 @@
groupOrder: { groupOrder: {
type: Array, type: Array,
default: () => [] default: () => []
},
isAgeGatedInstancesVisible: {
type: Boolean,
default: false
} }
}, },
data() { data() {
@@ -89,6 +96,9 @@
} }
}, },
methods: { methods: {
getSmallGroupIconUrl(url) {
return utils.convertFileUrlToImageUrl(url);
},
toggleGroupSidebarCollapse(groupId) { toggleGroupSidebarCollapse(groupId) {
this.groupInstancesCfg[groupId].isCollapsed = !this.groupInstancesCfg[groupId].isCollapsed; this.groupInstancesCfg[groupId].isCollapsed = !this.groupInstancesCfg[groupId].isCollapsed;
}, },

View File

@@ -381,6 +381,7 @@
"zoom": "Zoom", "zoom": "Zoom",
"vrcplus_profile_icons": "VRCPlus Profile Icons", "vrcplus_profile_icons": "VRCPlus Profile Icons",
"tooltips": "Tooltips", "tooltips": "Tooltips",
"age_gated_instances": "Age Gated Instances",
"nicknames": "Memo Nicknames", "nicknames": "Memo Nicknames",
"sort_favorite_by": "Sort Favorites by", "sort_favorite_by": "Sort Favorites by",
"sort_favorite_by_name": "name", "sort_favorite_by_name": "name",

View File

@@ -477,7 +477,7 @@ mixin userDialog
.detail .detail
span.name {{ $t('dialog.user.info.bio') }} span.name {{ $t('dialog.user.info.bio') }}
pre.extra( pre.extra(
style='font-family: inherit; font-size: 12px; white-space: pre-wrap; margin: 0 0.5em 0 0') {{ userDialog.ref.bio || '-' }} style='font-family: inherit; font-size: 12px; white-space: pre-wrap; margin: 0 0.5em 0 0; max-height: 40vh; overflow-y: auto') {{ userDialog.ref.bio || '-' }}
div(v-if='userDialog.id === API.currentUser.id' style='float: right') div(v-if='userDialog.id === API.currentUser.id' style='float: right')
el-button( el-button(
type='text' type='text'

View File

@@ -6,6 +6,7 @@ mixin worldDialog
:last-location='lastLocation' :last-location='lastLocation'
:instance-join-history='instanceJoinHistory' :instance-join-history='instanceJoinHistory'
:update-instance-info='updateInstanceInfo' :update-instance-info='updateInstanceInfo'
:is-age-gated-instances-visible='isAgeGatedInstancesVisible'
@open-folder-generic='openFolderGeneric' @open-folder-generic='openFolderGeneric'
@delete-vrchat-cache='deleteVRChatCache' @delete-vrchat-cache='deleteVRChatCache'
@world-dialog-command='worldDialogCommand' @world-dialog-command='worldDialogCommand'

View File

@@ -36,11 +36,14 @@ mixin gameLogTab
span {{ scope.row.created_at | formatDate('short') }} span {{ scope.row.created_at | formatDate('short') }}
el-table-column(:label='$t("table.gameLog.type")' prop='type' width='120') el-table-column(:label='$t("table.gameLog.type")' prop='type' width='120')
template(#default='scope') template(#default='scope')
span.x-link( el-tooltip(placement='right' :open-delay='500' :disabled='hideTooltips')
v-if='scope.row.location && scope.row.type !== "Location"' template(#content)
v-text='$t("view.game_log.filters." + scope.row.type)' span {{ $t("view.game_log.filters." + scope.row.type) }}
@click='showWorldDialog(scope.row.location)') span.x-link(
span(v-else v-text='$t("view.game_log.filters." + scope.row.type)') v-if='scope.row.location && scope.row.type !== "Location"'
v-text='$t("view.game_log.filters." + scope.row.type)'
@click='showWorldDialog(scope.row.location)')
span(v-else v-text='$t("view.game_log.filters." + scope.row.type)')
el-table-column(:label='$t("table.gameLog.icon")' prop='isFriend' width='70' align='center') el-table-column(:label='$t("table.gameLog.icon")' prop='isFriend' width='70' align='center')
template(#default='scope') template(#default='scope')
template(v-if='gameLogIsFriend(scope.row)') template(v-if='gameLogIsFriend(scope.row)')

View File

@@ -273,6 +273,10 @@ mixin settingsTab
:label='$t("view.settings.appearance.appearance.tooltips")' :label='$t("view.settings.appearance.appearance.tooltips")'
:value='!hideTooltips' :value='!hideTooltips'
@change='saveOpenVROption("VRCX_hideTooltips")') @change='saveOpenVROption("VRCX_hideTooltips")')
simple-switch(
:label='$t("view.settings.appearance.appearance.age_gated_instances")'
:value='isAgeGatedInstancesVisible'
@change='toggleIsAgeGatedInstancesVisible')
.options-container-item .options-container-item
span.name {{ $t('view.settings.appearance.appearance.sort_favorite_by') }} span.name {{ $t('view.settings.appearance.appearance.sort_favorite_by') }}
el-radio-group(v-model='sortFavorites' @change='saveSortFavoritesOption') el-radio-group(v-model='sortFavorites' @change='saveSortFavoritesOption')

View File

@@ -2061,4 +2061,10 @@ i.x-user-status {
background: linear-gradient(90deg, #302E34 25%, #423F46 37%, #302E34 63%); background: linear-gradient(90deg, #302E34 25%, #423F46 37%, #302E34 63%);
background-size: 400% 100%; background-size: 400% 100%;
animation: el-skeleton-loading 1.4s ease infinite; animation: el-skeleton-loading 1.4s ease infinite;
}
.el-table .el-table__body-wrapper .el-table__row .cell > span {
display: block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
} }

View File

@@ -95,6 +95,7 @@
<groups-sidebar <groups-sidebar
:group-instances="groupInstances" :group-instances="groupInstances"
:group-order="inGameGroupOrder" :group-order="inGameGroupOrder"
:is-age-gated-instances-visible="isAgeGatedInstancesVisible"
@show-group-dialog="$emit('show-group-dialog', $event)"></groups-sidebar> @show-group-dialog="$emit('show-group-dialog', $event)"></groups-sidebar>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@@ -108,12 +109,12 @@
export default { export default {
name: 'SideBar', name: 'SideBar',
inject: ['API', 'userImage'],
components: { components: {
FriendsSidebar, FriendsSidebar,
GroupsSidebar, GroupsSidebar,
Location Location
}, },
inject: ['API', 'userImage'],
props: { props: {
// settings // settings
// remove these props when have a state manager. // remove these props when have a state manager.
@@ -124,6 +125,7 @@
gameLogDisabled: Boolean, gameLogDisabled: Boolean,
hideNicknames: Boolean, hideNicknames: Boolean,
isHideFriendsInSameInstance: Boolean, isHideFriendsInSameInstance: Boolean,
isAgeGatedInstancesVisible: Boolean,
isSideBarTabShow: Boolean, isSideBarTabShow: Boolean,

View File

@@ -325,103 +325,106 @@
}} }}
</div> </div>
<div v-for="room in worldDialog.rooms" :key="room.id"> <div v-for="room in worldDialog.rooms" :key="room.id">
<div style="margin: 5px 0"> <template
<location-world v-if="isAgeGatedInstancesVisible || !(room.ageGate || room.location?.includes('~ageGate'))">
:locationobject="room.$location" <div style="margin: 5px 0">
:currentuserid="API.currentUser.id" <location-world
:worlddialogshortname="worldDialog.$location.shortName" :locationobject="room.$location"
@show-launch-dialog="showLaunchDialog" /> :currentuserid="API.currentUser.id"
<launch :worlddialogshortname="worldDialog.$location.shortName"
:location="room.tag" @show-launch-dialog="showLaunchDialog" />
style="margin-left: 5px" <launch
@show-launch-dialog="showLaunchDialog" /> :location="room.tag"
<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" />
</el-tooltip>
<el-tooltip
placement="top"
:content="$t('dialog.world.instances.refresh_instance_info')"
:disabled="hideTooltips">
<el-button
size="mini"
icon="el-icon-refresh"
style="margin-left: 5px" style="margin-left: 5px"
circle @show-launch-dialog="showLaunchDialog" />
@click="refreshInstancePlayerCount(room.tag)" /> <el-tooltip
</el-tooltip> placement="top"
<el-tooltip :content="$t('dialog.world.instances.self_invite_tooltip')"
v-if="instanceJoinHistory.get(room.$location.tag)" :disabled="hideTooltips">
placement="top" <invite-yourself
:content="$t('dialog.previous_instances.info')" :location="room.$location.tag"
:disabled="hideTooltips"> :shortname="room.$location.shortName"
<el-button style="margin-left: 5px" />
size="mini" </el-tooltip>
icon="el-icon-s-data" <el-tooltip
style="margin-left: 5px" placement="top"
plain :content="$t('dialog.world.instances.refresh_instance_info')"
circle :disabled="hideTooltips">
@click="showPreviousInstanceInfoDialog(room.location)" /> <el-button
</el-tooltip> size="mini"
<last-join :location="room.$location.tag" :currentlocation="lastLocation.location" /> icon="el-icon-refresh"
<instance-info style="margin-left: 5px"
:location="room.tag" circle
:instance="room.ref" @click="refreshInstancePlayerCount(room.tag)" />
:friendcount="room.friendCount" </el-tooltip>
:updateelement="updateInstanceInfo" /> <el-tooltip
<div v-if="instanceJoinHistory.get(room.$location.tag)"
v-if="room.$location.userId || room.users.length" placement="top"
class="x-friend-list" :content="$t('dialog.previous_instances.info')"
style="margin: 10px 0; max-height: unset"> :disabled="hideTooltips">
<el-button
size="mini"
icon="el-icon-s-data"
style="margin-left: 5px"
plain
circle
@click="showPreviousInstanceInfoDialog(room.location)" />
</el-tooltip>
<last-join :location="room.$location.tag" :currentlocation="lastLocation.location" />
<instance-info
:location="room.tag"
:instance="room.ref"
:friendcount="room.friendCount"
:updateelement="updateInstanceInfo" />
<div <div
v-if="room.$location.userId" v-if="room.$location.userId || room.users.length"
class="x-friend-item x-friend-item-border" class="x-friend-list"
@click="showUserDialog(room.$location.userId)"> style="margin: 10px 0; max-height: unset">
<template v-if="room.$location.user"> <div
<div class="avatar" :class="userStatusClass(room.$location.user)"> v-if="room.$location.userId"
<img v-lazy="userImage(room.$location.user, true)" /> class="x-friend-item x-friend-item-border"
@click="showUserDialog(room.$location.userId)">
<template v-if="room.$location.user">
<div class="avatar" :class="userStatusClass(room.$location.user)">
<img v-lazy="userImage(room.$location.user, true)" />
</div>
<div class="detail">
<span
class="name"
:style="{ color: room.$location.user.$userColour }"
v-text="room.$location.user.displayName" />
<span class="extra">
{{ $t('dialog.world.instances.instance_creator') }}
</span>
</div>
</template>
<span v-else v-text="room.$location.userId" />
</div>
<div
v-for="user in room.users"
:key="user.id"
class="x-friend-item x-friend-item-border"
@click="showUserDialog(user.id)">
<div class="avatar" :class="userStatusClass(user)">
<img v-lazy="userImage(user, true)" />
</div> </div>
<div class="detail"> <div class="detail">
<span <span
class="name" class="name"
:style="{ color: room.$location.user.$userColour }" :style="{ color: user.$userColour }"
v-text="room.$location.user.displayName" /> v-text="user.displayName" />
<span class="extra"> <span v-if="user.location === 'traveling'" class="extra">
{{ $t('dialog.world.instances.instance_creator') }} <i class="el-icon-loading" style="margin-right: 5px" />
<timer :epoch="user.$travelingToTime" />
</span>
<span v-else class="extra">
<timer :epoch="user.$location_at" />
</span> </span>
</div> </div>
</template>
<span v-else v-text="room.$location.userId" />
</div>
<div
v-for="user in room.users"
:key="user.id"
class="x-friend-item x-friend-item-border"
@click="showUserDialog(user.id)">
<div class="avatar" :class="userStatusClass(user)">
<img v-lazy="userImage(user, true)" />
</div>
<div class="detail">
<span
class="name"
:style="{ color: user.$userColour }"
v-text="user.displayName" />
<span v-if="user.location === 'traveling'" class="extra">
<i class="el-icon-loading" style="margin-right: 5px" />
<timer :epoch="user.$travelingToTime" />
</span>
<span v-else class="extra">
<timer :epoch="user.$location_at" />
</span>
</div> </div>
</div> </div>
</div> </div>
</div> </template>
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('dialog.world.info.header')" lazy> <el-tab-pane :label="$t('dialog.world.info.header')" lazy>
@@ -756,8 +759,9 @@
isGameRunning: Boolean, isGameRunning: Boolean,
lastLocation: Object, lastLocation: Object,
instanceJoinHistory: Map, instanceJoinHistory: Map,
isAgeGatedInstancesVisible: Boolean,
// ok // TODO: Remove
updateInstanceInfo: Number updateInstanceInfo: Number
}, },
data() { data() {