diff --git a/html/src/app.js b/html/src/app.js index f3f5f37f..d6274eec 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -1689,6 +1689,7 @@ speechSynthesis.getVoices(); imageUrl: '', thumbnailImageUrl: '', releaseStatus: '', + styles: [], version: 0, unityPackages: [], unityPackageUrl: '', @@ -6866,19 +6867,22 @@ speechSynthesis.getVoices(); }; $app.methods.deleteFavorite = function (objectId) { - // FIXME: 메시지 수정 - this.$confirm('Continue? Delete Favorite', 'Confirm', { - confirmButtonText: 'Confirm', - cancelButtonText: 'Cancel', - type: 'info', - callback: (action) => { - if (action === 'confirm') { - API.deleteFavorite({ - objectId - }); - } - } + API.deleteFavorite({ + objectId }); + // FIXME: 메시지 수정 + // this.$confirm('Continue? Delete Favorite', 'Confirm', { + // confirmButtonText: 'Confirm', + // cancelButtonText: 'Cancel', + // type: 'info', + // callback: (action) => { + // if (action === 'confirm') { + // API.deleteFavorite({ + // objectId + // }); + // } + // } + // }); }; $app.methods.deleteFavoriteNoConfirm = function (objectId) { @@ -7964,7 +7968,7 @@ speechSynthesis.getVoices(); '[ "https://avtr.just-h.party/vrcx_search.php" ]' ) ); - $app.data.pendingOfflineDelay = 110000; + $app.data.pendingOfflineDelay = 130000; if (await configRepository.getString('VRCX_avatarRemoteDatabaseProvider')) { // move existing provider to new list var avatarRemoteDatabaseProvider = await configRepository.getString( @@ -9696,7 +9700,7 @@ speechSynthesis.getVoices(); .getUserStats(D.ref, inCurrentWorld) .then((ref1) => { if (ref1.userId === D.id) { - D.lastSeen = ref1.created_at; + D.lastSeen = ref1.lastSeen; D.joinCount = ref1.joinCount; D.timeSpent = ref1.timeSpent; } @@ -9757,7 +9761,7 @@ speechSynthesis.getVoices(); .getUserStats(D.ref, inCurrentWorld) .then((ref1) => { if (ref1.userId === D.id) { - D.lastSeen = ref1.created_at; + D.lastSeen = ref1.lastSeen; D.joinCount = ref1.joinCount; D.timeSpent = ref1.timeSpent; } @@ -10688,6 +10692,8 @@ speechSynthesis.getVoices(); } if (command === 'Refresh') { this.showUserDialog(D.id); + } else if (command === 'Share') { + this.copyUserURL(D.id); } else if (command === 'Add Favorite') { this.showFavoriteDialog('friend', D.id); } else if (command === 'Edit Social Status') { @@ -11482,21 +11488,14 @@ speechSynthesis.getVoices(); case 'Refresh': this.showWorldDialog(D.id); break; + case 'Share': + this.copyWorldUrl(D.id); + break; case 'New Instance': this.showNewInstanceDialog(D.$location.tag); break; case 'New Instance and Self Invite': - this.newInstanceDialog.worldId = D.id; - this.createNewInstance().then((args) => { - if (!args?.json?.location) { - this.$message({ - message: 'Failed to create instance', - type: 'error' - }); - return; - } - this.selfInvite(args.json.location); - }); + this.newInstanceSelfInvite(D.id); break; case 'Add Favorite': this.showFavoriteDialog('world', D.id); @@ -11628,6 +11627,20 @@ speechSynthesis.getVoices(); } }; + $app.methods.newInstanceSelfInvite = function (worldId) { + this.newInstanceDialog.worldId = worldId; + this.createNewInstance().then((args) => { + if (!args?.json?.location) { + this.$message({ + message: 'Failed to create instance', + type: 'error' + }); + return; + } + this.selfInvite(args.json.location); + }); + }; + $app.methods.refreshWorldDialogTreeData = function () { var D = this.worldDialog; D.treeData = $utils.buildTreeData(D.ref); @@ -14856,7 +14869,7 @@ speechSynthesis.getVoices(); var ref = await database.getUserStats(ctx); /* eslint-disable require-atomic-updates */ ctx.$joinCount = ref.joinCount; - ctx.$lastSeen = ref.created_at; + ctx.$lastSeen = ref.lastSeen; ctx.$timeSpent = ref.timeSpent; /* eslint-enable require-atomic-updates */ }; @@ -17135,11 +17148,11 @@ speechSynthesis.getVoices(); $app.methods.userFavoriteWorldsStatus = function (visibility) { var style = {}; if (visibility === 'public') { - style.online = true; + style.green = true; } else if (visibility === 'friends') { - style.joinme = true; + style.blue = true; } else { - style.busy = true; + style.red = true; } return style; }; diff --git a/html/src/app.scss b/html/src/app.scss index dd622db0..43e2c61d 100644 --- a/html/src/app.scss +++ b/html/src/app.scss @@ -593,7 +593,8 @@ img.friends-list-avatar { text-align: center; } -i.x-user-status { +i.x-user-status, +i.x-status-icon { display: inline-block; width: 10px; height: 10px; @@ -624,6 +625,22 @@ i.x-user-status.busy { mask-image: url(masks/busy.svg); } +i.x-status-icon.green { + background: #67c23a; +} + +i.x-status-icon.blue { + background: #409eff; +} + +i.x-status-icon.orange { + background: #ff9500; +} + +i.x-status-icon.red { + background: #ff2c2c; +} + .x-tag-friend { color: rgb(255, 208, 0) !important; border-color: rgb(255, 208, 0) !important; diff --git a/html/src/classes/gameLog.js b/html/src/classes/gameLog.js index a48a5baf..fd839dd5 100644 --- a/html/src/classes/gameLog.js +++ b/html/src/classes/gameLog.js @@ -166,7 +166,7 @@ export default class extends baseClass { var friendRef = this.friends.get(userId); if (typeof friendRef?.ref !== 'undefined') { friendRef.ref.$joinCount++; - friendRef.ref.$lastSeen = Date.now(); + friendRef.ref.$lastSeen = new Date().toJSON(); friendRef.ref.$timeSpent += Date.now() - ref.joinTime; if ( this.sidebarSortMethods.includes( diff --git a/html/src/classes/groups.js b/html/src/classes/groups.js index 7d31cbeb..8596d026 100644 --- a/html/src/classes/groups.js +++ b/html/src/classes/groups.js @@ -2222,6 +2222,9 @@ export default class extends baseClass { case 'Refresh': this.showGroupDialog(D.id); break; + case 'Share': + this.copyGroupUrl(D.ref.$url); + break; case 'Moderation Tools': this.showGroupMemberModerationDialog(D.id); break; @@ -2645,11 +2648,11 @@ export default class extends baseClass { groupGalleryStatus(gallery) { var style = {}; if (!gallery.membersOnly) { - style.joinme = true; + style.blue = true; } else if (!gallery.roleIdsToView) { - style.online = true; + style.green = true; } else { - style.busy = true; + style.red = true; } return style; }, diff --git a/html/src/localization/en/en.json b/html/src/localization/en/en.json index 7a87bf9d..f51b8773 100644 --- a/html/src/localization/en/en.json +++ b/html/src/localization/en/en.json @@ -112,6 +112,7 @@ "import": "Import", "move_tooltip": "Move", "copy_tooltip": "Copy", + "self_invite_tooltip": "Self Invite", "unfavorite_tooltip": "Unfavorite", "visibility_tooltip": "Change Visibility", "rename_tooltip": "Rename", @@ -584,6 +585,7 @@ "favorite_tooltip": "Add to favorites", "unfavorite_tooltip": "Remove from favorites", "refresh": "Refresh", + "share": "Share", "invite": "Invite", "invite_with_message": "Invite With Message", "request_invite": "Request Invite", @@ -733,6 +735,7 @@ "delete_cache_tooltip": "Delete world from cache", "favorites_tooltip": "Favorites", "refresh": "Refresh", + "share": "Share", "new_instance": "New Instance", "new_instance_and_self_invite": "New Instance and Self Invite", "make_home": "Make Home", @@ -879,6 +882,7 @@ "invite_required_tooltip": "Invite required", "join_group_tooltip": "Join Group", "refresh": "Refresh", + "share": "Share", "unsubscribe": "Unsubscribe From Announcements", "subscribe": "Subscribe To Announcements", "invite_to_group": "Invite To Group", diff --git a/html/src/mixins/dialogs/groupDialog.pug b/html/src/mixins/dialogs/groupDialog.pug index a8701416..a644c252 100644 --- a/html/src/mixins/dialogs/groupDialog.pug +++ b/html/src/mixins/dialogs/groupDialog.pug @@ -70,6 +70,7 @@ mixin groupDialog() 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') }} @@ -329,7 +330,7 @@ mixin groupDialog() el-tab-pane span(slot="label") span(v-text="gallery.name" style="font-weight:bold;font-size:16px") - i.x-user-status(style="margin-left:5px" :class="groupGalleryStatus(gallery)") + 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") diff --git a/html/src/mixins/dialogs/userDialog.pug b/html/src/mixins/dialogs/userDialog.pug index 3c987799..84471217 100644 --- a/html/src/mixins/dialogs/userDialog.pug +++ b/html/src/mixins/dialogs/userDialog.pug @@ -93,6 +93,7 @@ mixin userDialog() 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-share" command="Share") {{ $t('dialog.user.actions.share') }} template(v-if="userDialog.ref.id === API.currentUser.id") el-dropdown-item(icon="el-icon-picture-outline" command="Manage Gallery" divided) {{ $t('dialog.user.actions.manage_gallery_icon') }} el-dropdown-item(icon="el-icon-s-custom" command="Show Avatar Author") {{ $t('dialog.user.actions.show_avatar_author') }} @@ -467,7 +468,7 @@ mixin userDialog() el-tab-pane span(slot="label") span(v-text="list[0]" style="font-weight:bold;font-size:16px") - i.x-user-status(style="margin-left:5px" :class="userFavoriteWorldsStatus(list[1])") + i.x-status-icon(style="margin-left:5px" :class="userFavoriteWorldsStatus(list[1])") span(style="color:#909399;font-size:12px;margin-left:5px") {{ list[2].length }}/{{ API.favoriteLimits.maxFavoritesPerGroup.world }} .x-friend-list(style="margin-top:10px;margin-bottom:15px;min-height:60px") .x-friend-item(v-for="world in list[2]" :key="world.id" @click="showWorldDialog(world.id)" class="x-friend-item-border") diff --git a/html/src/mixins/dialogs/worldDialog.pug b/html/src/mixins/dialogs/worldDialog.pug index eaab6451..8e94ff2c 100644 --- a/html/src/mixins/dialogs/worldDialog.pug +++ b/html/src/mixins/dialogs/worldDialog.pug @@ -52,6 +52,7 @@ mixin worldDialog() el-button(type="default" icon="el-icon-more" circle) el-dropdown-menu(#default="dropdown") el-dropdown-item(icon="el-icon-refresh" command="Refresh") {{ $t('dialog.world.actions.refresh') }} + el-dropdown-item(icon="el-icon-share" command="Share") {{ $t('dialog.world.actions.share') }} el-dropdown-item(icon="el-icon-s-flag" command="New Instance" divided) {{ $t('dialog.world.actions.new_instance') }} el-dropdown-item(icon="el-icon-message" command="New Instance and Self Invite") {{ $t('dialog.world.actions.new_instance_and_self_invite') }} el-dropdown-item(v-if="API.currentUser.$homeLocation && API.currentUser.$homeLocation.worldId === worldDialog.id" icon="el-icon-magic-stick" command="Reset Home" divided) {{ $t('dialog.world.actions.reset_home') }} diff --git a/html/src/mixins/tabs/favorites.pug b/html/src/mixins/tabs/favorites.pug index 253588e2..69a526d3 100644 --- a/html/src/mixins/tabs/favorites.pug +++ b/html/src/mixins/tabs/favorites.pug @@ -47,7 +47,8 @@ mixin favoritesTab() el-checkbox(v-model="favorite.$selected") template(v-else) el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") - el-button(@click.stop="deleteFavorite(favorite.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + el-button(v-if="shiftHeld" @click.stop="deleteFavorite(favorite.id)" size="mini" icon="el-icon-close" circle style="color:#f56c6c;margin-left:5px") + el-button(v-else @click.stop="showFavoriteDialog('friend', favorite.id)" type="default" icon="el-icon-star-on" size="mini" circle style="margin-left:5px") template(v-else) .avatar .detail @@ -81,7 +82,7 @@ mixin favoritesTab() el-collapse-item(v-for="group in API.favoriteWorldGroups" :key="group.name") template(slot="title") span(v-text="group.displayName ? group.displayName : group.name" style="font-weight:bold;font-size:14px;margin-left:10px") - i.x-user-status(style="margin-left:5px" :class="userFavoriteWorldsStatus(group.visibility)") + i.x-status-icon(style="margin-left:5px" :class="userFavoriteWorldsStatus(group.visibility)") span(style="color:#909399;font-size:12px;margin-left:10px") {{ group.count }}/{{ group.capacity }} el-tooltip(placement="top" :content="$t('view.favorite.visibility_tooltip')" :disabled="hideTooltips") el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:10px") @@ -116,8 +117,11 @@ mixin favoritesTab() i.el-icon-warning(style="color:#f56c6c;margin-left:5px") el-tooltip(v-if="favorite.ref.releaseStatus === 'private'" placement="left" :content="$t('view.favorite.private')") i.el-icon-warning(style="color:#e6a23c;margin-left:5px") + el-tooltip(placement="left" :content="$t('view.favorite.self_invite_tooltip')" :disabled="hideTooltips") + el-button(@click.stop="newInstanceSelfInvite(favorite.id)" size="mini" icon="el-icon-message" circle style="margin-left:5px") el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") - el-button(@click.stop="deleteFavorite(favorite.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + el-button(v-if="shiftHeld" @click.stop="deleteFavorite(favorite.id)" size="mini" icon="el-icon-close" circle style="color:#f56c6c;margin-left:5px") + el-button(v-else @click.stop="showFavoriteDialog('world', favorite.id)" type="default" icon="el-icon-star-on" size="mini" circle style="margin-left:5px") template(v-else) .avatar .detail @@ -157,8 +161,12 @@ mixin favoritesTab() el-dropdown-menu(#default="dropdown") template(v-for="groupAPI in API.favoriteWorldGroups" :key="groupAPI.name") el-dropdown-item(style="display:block;margin:10px 0" @click.native="addFavoriteWorld(favorite, groupAPI, true)" :disabled="groupAPI.count >= groupAPI.capacity") {{ groupAPI.displayName }} ({{ groupAPI.count }} / {{ groupAPI.capacity }}) + template(v-else) + el-tooltip(placement="left" :content="$t('view.favorite.self_invite_tooltip')" :disabled="hideTooltips") + el-button(@click.stop="newInstanceSelfInvite(favorite.id)" size="mini" icon="el-icon-message" circle style="margin-left:5px") el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") - el-button(@click.stop="removeLocalWorldFavorite(favorite.id, group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + el-button(v-if="shiftHeld" @click.stop="removeLocalWorldFavorite(favorite.id, group)" size="mini" icon="el-icon-close" circle style="color:#f56c6c;margin-left:5px") + el-button(v-else @click.stop="showFavoriteDialog('world', favorite.id)" type="default" icon="el-icon-star-on" size="mini" circle style="margin-left:5px") template(v-else) .avatar .detail @@ -222,7 +230,8 @@ mixin favoritesTab() el-tooltip(v-if="favorite.ref.releaseStatus !== 'private' && !favorite.deleted" placement="left" :content="$t('view.favorite.select_avatar_tooltip')" :disabled="hideTooltips") el-button(@click.stop="selectAvatarWithConfirmation(favorite.id)" :disabled="API.currentUser.currentAvatar === favorite.id" size="mini" icon="el-icon-check" circle style="margin-left:5px") el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") - el-button(@click.stop="deleteFavorite(favorite.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + el-button(v-if="shiftHeld" @click.stop="deleteFavorite(favorite.id)" size="mini" icon="el-icon-close" circle style="color:#f56c6c;margin-left:5px") + el-button(v-else @click.stop="showFavoriteDialog('avatar', favorite.id)" type="default" icon="el-icon-star-on" size="mini" circle style="margin-left:5px") template(v-else) .avatar .detail @@ -246,7 +255,7 @@ mixin favoritesTab() el-button(@click.stop="selectAvatarWithConfirmation(favorite.id)" :disabled="API.currentUser.currentAvatar === favorite.id" size="mini" icon="el-icon-check" circle style="margin-left:5px") template(v-if="API.cachedFavoritesByObjectId.has(favorite.id)") el-tooltip(placement="right" content="Unfavorite" :disabled="hideTooltips") - el-button(@click.stop="deleteFavorite(favorite.id)" type="default" icon="el-icon-star-on" size="mini" circle style="margin-left:5px") + el-button(@click.stop="showFavoriteDialog('avatar', favorite.id)" type="default" icon="el-icon-star-on" size="mini" circle style="margin-left:5px") template(v-else) el-tooltip(placement="right" content="Favorite" :disabled="hideTooltips") el-button(@click.stop="showFavoriteDialog('avatar', favorite.id)" type="default" icon="el-icon-star-off" size="mini" circle style="margin-left:5px") @@ -285,7 +294,8 @@ mixin favoritesTab() el-tooltip(placement="left" :content="$t('view.favorite.select_avatar_tooltip')" :disabled="hideTooltips") el-button(@click.stop="selectAvatarWithConfirmation(favorite.id)" :disabled="API.currentUser.currentAvatar === favorite.id" size="mini" icon="el-icon-check" circle style="margin-left:5px") el-tooltip(placement="right" :content="$t('view.favorite.unfavorite_tooltip')" :disabled="hideTooltips") - el-button(@click.stop="removeLocalAvatarFavorite(favorite.id, group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px") + el-button(v-if="shiftHeld" @click.stop="removeLocalAvatarFavorite(favorite.id, group)" size="mini" icon="el-icon-close" circle style="color:#f56c6c;margin-left:5px") + el-button(v-else @click.stop="showFavoriteDialog('avatar', favorite.id)" type="default" icon="el-icon-star-on" size="mini" circle style="margin-left:5px") template(v-else) .avatar .detail diff --git a/html/src/repository/database.js b/html/src/repository/database.js index 9b011c11..35f38c55 100644 --- a/html/src/repository/database.js +++ b/html/src/repository/database.js @@ -1147,7 +1147,7 @@ class Database { var instances = new Set(); var ref = { timeSpent: 0, - created_at: '', + lastSeen: '', joinCount: 0, userId: input.id, previousDisplayNames: new Map() @@ -1159,7 +1159,7 @@ class Database { } i++; if (i === 1 || (inCurrentWorld && i === 2)) { - ref.created_at = row[0]; + ref.lastSeen = row[0]; } instances.add(row[3]); if (input.displayName !== row[4]) {