diff --git a/html/src/app.js b/html/src/app.js
index 21cc024b..325e1325 100644
--- a/html/src/app.js
+++ b/html/src/app.js
@@ -19659,7 +19659,7 @@ speechSynthesis.getVoices();
});
}
});
- }
+ };
$app.methods.avatarDialogCommand = function (command) {
var D = this.avatarDialog;
@@ -27185,6 +27185,7 @@ speechSynthesis.getVoices();
avatarIdList: new Set(),
errors: '',
avatarImportFavoriteGroup: null,
+ avatarImportLocalFavoriteGroup: null,
importProgress: 0,
importProgressTotal: 0
};
@@ -27269,9 +27270,16 @@ speechSynthesis.getVoices();
$app.methods.selectAvatarImportGroup = function (group) {
var D = this.avatarImportDialog;
+ D.avatarImportLocalFavoriteGroup = null;
D.avatarImportFavoriteGroup = group;
};
+ $app.methods.selectAvatarImportLocalGroup = function (group) {
+ var D = this.avatarImportDialog;
+ D.avatarImportFavoriteGroup = null;
+ D.avatarImportLocalFavoriteGroup = group;
+ };
+
$app.methods.cancelAvatarImport = function () {
var D = this.avatarImportDialog;
D.loading = false;
@@ -27279,10 +27287,10 @@ speechSynthesis.getVoices();
$app.methods.importAvatarImportTable = async function () {
var D = this.avatarImportDialog;
- D.loading = true;
- if (!D.avatarImportFavoriteGroup) {
+ if (!D.avatarImportFavoriteGroup && !D.avatarImportLocalFavoriteGroup) {
return;
}
+ D.loading = true;
var data = [...this.avatarImportTable.data].reverse();
D.importProgressTotal = data.length;
try {
@@ -27291,7 +27299,17 @@ speechSynthesis.getVoices();
break;
}
var ref = data[i];
- await this.addFavoriteAvatar(ref, D.avatarImportFavoriteGroup);
+ if (D.avatarImportFavoriteGroup) {
+ await this.addFavoriteAvatar(
+ ref,
+ D.avatarImportFavoriteGroup
+ );
+ } else if (D.avatarImportLocalFavoriteGroup) {
+ this.addLocalAvatarFavorite(
+ ref.id,
+ D.avatarImportLocalFavoriteGroup
+ );
+ }
removeFromArray(this.avatarImportTable.data, ref);
D.avatarIdList.delete(ref.id);
D.importProgress++;
@@ -27310,9 +27328,11 @@ speechSynthesis.getVoices();
$app.resetAvatarImport();
$app.avatarImportDialog.visible = false;
$app.avatarImportFavoriteGroup = null;
+ $app.avatarImportLocalFavoriteGroup = null;
$app.avatarExportDialogVisible = false;
$app.avatarExportFavoriteGroup = null;
+ $app.avatarExportLocalFavoriteGroup = null;
});
// #endregion
@@ -27322,12 +27342,14 @@ speechSynthesis.getVoices();
$app.data.avatarExportDialogVisible = false;
$app.data.avatarExportContent = '';
$app.data.avatarExportFavoriteGroup = null;
+ $app.data.avatarExportLocalFavoriteGroup = null;
$app.methods.showAvatarExportDialog = function () {
this.$nextTick(() =>
adjustDialogZ(this.$refs.avatarExportDialogRef.$el)
);
this.avatarExportFavoriteGroup = null;
+ this.avatarExportLocalFavoriteGroup = null;
this.updateAvatarExportDialog();
this.avatarExportDialogVisible = true;
};
@@ -27340,23 +27362,54 @@ speechSynthesis.getVoices();
return str;
};
var lines = ['AvatarID,Name'];
- API.favoriteAvatarGroups.forEach((group) => {
- if (
- !this.avatarExportFavoriteGroup ||
- this.avatarExportFavoriteGroup === group
- ) {
- $app.favoriteAvatars.forEach((ref) => {
- if (group.key === ref.groupKey) {
- lines.push(`${_(ref.id)},${_(ref.name)}`);
- }
- });
+ if (this.avatarExportFavoriteGroup) {
+ API.favoriteAvatarGroups.forEach((group) => {
+ if (
+ !this.avatarExportFavoriteGroup ||
+ this.avatarExportFavoriteGroup === group
+ ) {
+ $app.favoriteAvatars.forEach((ref) => {
+ if (group.key === ref.groupKey) {
+ lines.push(`${_(ref.id)},${_(ref.name)}`);
+ }
+ });
+ }
+ });
+ } else if (this.avatarExportLocalFavoriteGroup) {
+ var favoriteGroup =
+ this.localAvatarFavorites[this.avatarExportLocalFavoriteGroup];
+ if (!favoriteGroup) {
+ return;
}
- });
+ for (var i = 0; i < favoriteGroup.length; ++i) {
+ var ref = favoriteGroup[i];
+ lines.push(`${_(ref.id)},${_(ref.name)}`);
+ }
+ } else {
+ // export all
+ this.favoriteAvatars.forEach((ref1) => {
+ lines.push(`${_(ref1.id)},${_(ref1.name)}`);
+ });
+ for (var i = 0; i < this.localAvatarFavoritesList.length; ++i) {
+ var avatarId = this.localAvatarFavoritesList[i];
+ var ref2 = API.cachedAvatars.get(avatarId);
+ if (typeof ref2 !== 'undefined') {
+ lines.push(`${_(ref2.id)},${_(ref2.name)}`);
+ }
+ }
+ }
this.avatarExportContent = lines.join('\n');
};
$app.methods.selectAvatarExportGroup = function (group) {
this.avatarExportFavoriteGroup = group;
+ this.avatarExportLocalFavoriteGroup = null;
+ this.updateAvatarExportDialog();
+ };
+
+ $app.methods.selectAvatarExportLocalGroup = function (group) {
+ this.avatarExportLocalFavoriteGroup = group;
+ this.avatarExportFavoriteGroup = null;
this.updateAvatarExportDialog();
};
@@ -28180,7 +28233,7 @@ speechSynthesis.getVoices();
$app.methods.isLocalUserVrcplusSupporter = function () {
return API.currentUser.$isVRCPlus;
- }
+ };
$app.data.localAvatarFavoriteGroups = [];
$app.data.localAvatarFavoritesList = [];
@@ -28485,6 +28538,52 @@ speechSynthesis.getVoices();
});
};
+ $app.data.avatarFavoriteSearch = '';
+ $app.data.avatarFavoriteSearchResults = [];
+
+ $app.methods.searchAvatarFavorites = function () {
+ var search = this.avatarFavoriteSearch.toLowerCase();
+ if (search.length < 3) {
+ this.avatarFavoriteSearchResults = [];
+ return;
+ }
+
+ var results = [];
+ for (var i = 0; i < this.localAvatarFavoriteGroups.length; ++i) {
+ var group = this.localAvatarFavoriteGroups[i];
+ if (!this.localAvatarFavorites[group]) {
+ continue;
+ }
+ for (var j = 0; j < this.localAvatarFavorites[group].length; ++j) {
+ var ref = this.localAvatarFavorites[group][j];
+ if (!ref || !ref.id) {
+ continue;
+ }
+ if (
+ ref.name.toLowerCase().includes(search) ||
+ ref.authorName.toLowerCase().includes(search)
+ ) {
+ results.push(ref);
+ }
+ }
+ }
+
+ for (var i = 0; i < this.favoriteAvatars.length; ++i) {
+ var ref = this.favoriteAvatars[i].ref;
+ if (!ref) {
+ continue;
+ }
+ if (
+ ref.name.toLowerCase().includes(search) ||
+ ref.authorName.toLowerCase().includes(search)
+ ) {
+ results.push(ref);
+ }
+ }
+
+ this.avatarFavoriteSearchResults = results;
+ };
+
// #endregion
// #region | Local Favorite Friends
diff --git a/html/src/index.pug b/html/src/index.pug
index 6fc68106..33cbe6f9 100644
--- a/html/src/index.pug
+++ b/html/src/index.pug
@@ -1316,7 +1316,7 @@ html
span(style="display:block;text-align:center") {{ $t('dialog.favorite.local_avatar_favorites') }}
template(v-for="group in localAvatarFavoriteGroups" :key="group")
el-button(v-if="hasLocalAvatarFavorite(favoriteDialog.objectId, group)" style="display:block;width:100%;margin:10px 0" @click="removeLocalAvatarFavorite(favoriteDialog.objectId, group)") #[i.el-icon-check] {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
- el-button(v-else-if="isLocalUserVrcplusSupporter()" style="display:block;width:100%;margin:10px 0" @click="addLocalAvatarFavorite(favoriteDialog.objectId, group)") {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
+ el-button(v-else style="display:block;width:100%;margin:10px 0" :disabled="!isLocalUserVrcplusSupporter()" @click="addLocalAvatarFavorite(favoriteDialog.objectId, group)") {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
//- dialog: invite
el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="inviteDialog" :visible.sync="inviteDialog.visible" :title="$t('dialog.invite.header')" width="500px")
@@ -2627,7 +2627,7 @@ html
el-dropdown(@click.native.stop trigger="click" size="small")
el-button(size="mini")
span(v-if="worldExportFavoriteGroup") {{ worldExportFavoriteGroup.displayName }} ({{ worldExportFavoriteGroup.count }}/{{ worldExportFavoriteGroup.capacity }}) #[i.el-icon-arrow-down.el-icon--right]
- span(v-else) Select Group #[i.el-icon-arrow-down.el-icon--right]
+ span(v-else) All Favorites #[i.el-icon-arrow-down.el-icon--right]
el-dropdown-menu(#default="dropdown")
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectWorldExportGroup(null)") None
template(v-for="groupAPI in API.favoriteWorldGroups" :key="groupAPI.name")
@@ -2706,6 +2706,14 @@ html
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarExportGroup(null)") All Favorites
template(v-for="groupAPI in API.favoriteAvatarGroups" :key="groupAPI.name")
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarExportGroup(groupAPI)") {{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
+ el-dropdown(@click.native.stop trigger="click" size="small" style="margin-left:10px")
+ el-button(size="mini")
+ span(v-if="avatarExportLocalFavoriteGroup") {{ avatarExportLocalFavoriteGroup }} ({{ getLocalAvatarFavoriteGroupLength(avatarExportLocalFavoriteGroup) }}) #[i.el-icon-arrow-down.el-icon--right]
+ span(v-else) Select Group #[i.el-icon-arrow-down.el-icon--right]
+ el-dropdown-menu(#default="dropdown")
+ el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarExportLocalGroup(null)") None
+ template(v-for="group in localAvatarFavoriteGroups" :key="group")
+ el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarExportLocalGroup(group)" ) {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
br
el-input(type="textarea" v-if="avatarExportDialogVisible" v-model="avatarExportContent" size="mini" rows="15" resize="none" readonly style="margin-top:15px" @click.native="$event.target.tagName === 'TEXTAREA' && $event.target.select()")
@@ -2724,7 +2732,14 @@ html
el-dropdown-menu(#default="dropdown")
template(v-for="groupAPI in API.favoriteAvatarGroups" :key="groupAPI.name")
el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarImportGroup(groupAPI)" :disabled="groupAPI.count >= groupAPI.capacity") {{ groupAPI.displayName }} ({{ groupAPI.count }}/{{ groupAPI.capacity }})
- el-button(size="small" @click="importAvatarImportTable" style="margin:5px" :disabled="avatarImportTable.data.length === 0 || !avatarImportDialog.avatarImportFavoriteGroup") {{ $t('dialog.avatar_import.import') }}
+ el-dropdown(@click.native.stop trigger="click" size="small" style="margin:5px")
+ el-button(size="mini")
+ span(v-if="avatarImportDialog.avatarImportLocalFavoriteGroup") {{ avatarImportDialog.avatarImportLocalFavoriteGroup }} ({{ getLocalAvatarFavoriteGroupLength(avatarImportDialog.avatarImportLocalFavoriteGroup) }}) #[i.el-icon-arrow-down.el-icon--right]
+ span(v-else) {{ $t('dialog.avatar_import.select_group_placeholder') }} #[i.el-icon-arrow-down.el-icon--right]
+ el-dropdown-menu(#default="dropdown")
+ template(v-for="group in localAvatarFavoriteGroups" :key="group")
+ el-dropdown-item(style="display:block;margin:10px 0" @click.native="selectAvatarImportLocalGroup(group)" ) {{ group }} ({{ getLocalAvatarFavoriteGroupLength(group) }})
+ el-button(size="small" @click="importAvatarImportTable" style="margin:5px" :disabled="avatarImportTable.data.length === 0 || (!avatarImportDialog.avatarImportFavoriteGroup && !avatarImportDialog.avatarImportLocalFavoriteGroup)") {{ $t('dialog.avatar_import.import') }}
el-button(v-if="avatarImportDialog.loading" size="small" @click="cancelAvatarImport" style="margin-top:10px") {{ $t('dialog.avatar_import.cancel') }}
span(v-if="avatarImportDialog.avatarImportFavoriteGroup") {{ avatarImportTable.data.length }} / {{ avatarImportDialog.avatarImportFavoriteGroup.capacity - avatarImportDialog.avatarImportFavoriteGroup.count }}
span(v-if="avatarImportDialog.importProgress" style="margin:10px") #[i.el-icon-loading(style="margin-right:5px")] {{ $t('dialog.avatar_import.import_progress') }} {{ avatarImportDialog.importProgress }}/{{ avatarImportDialog.importProgressTotal }}
diff --git a/html/src/localization/en/en.json b/html/src/localization/en/en.json
index 5dcb79fd..3c2160f0 100644
--- a/html/src/localization/en/en.json
+++ b/html/src/localization/en/en.json
@@ -94,7 +94,9 @@
},
"avatars": {
"header": "Avatars",
- "local_favorites": "Local Favorites (VRC+ exclusive)",
+ "search": "Search",
+ "vrchat_favorites": "VRChat Favorites",
+ "local_favorites": "Local Favorites (Requires VRC+)",
"new_group": "New Group"
},
"bulk_unfavorite_mode": "Bulk Unfavorite Mode",
@@ -916,7 +918,7 @@
"header": "Choose Group",
"vrchat_favorites": "VRChat Favorites",
"local_favorites": "Local Favorites",
- "local_avatar_favorites": "Local Favorites (VRC+ exclusive)"
+ "local_avatar_favorites": "Local Favorites (Requires VRC+)"
},
"invite": {
"header": "Invite",
diff --git a/html/src/mixins/tabs/favorites.pug b/html/src/mixins/tabs/favorites.pug
index 92df51b7..78e4a2d4 100644
--- a/html/src/mixins/tabs/favorites.pug
+++ b/html/src/mixins/tabs/favorites.pug
@@ -157,6 +157,23 @@ mixin favoritesTab()
div(style="display:inline-block;float:right;font-size:13px;margin-right:10px")
span.name(style="margin-right:5px") {{ $t('view.favorite.sort_by') }}
el-switch(v-model="sortFavorites" :inactive-text="$t('view.settings.appearance.appearance.sort_favorite_by_name')" :active-text="$t('view.settings.appearance.appearance.sort_favorite_by_date')" @change="saveSortFavoritesOption")
+ span(style="display:block;margin-top:20px") {{ $t('view.favorite.avatars.search') }}
+ el-input(v-model="avatarFavoriteSearch" @input="searchAvatarFavorites" clearable size="mini" :placeholder="$t('view.favorite.avatars.search')" style="width:300px;margin-top:10px")
+ .x-friend-list(style="margin-top:10px")
+ div(style="display:inline-block;width:300px;margin-right:15px" v-for="favorite in avatarFavoriteSearchResults" :key="favorite.id" @click="showAvatarDialog(favorite.id)")
+ .x-friend-item
+ template(v-if="favorite.name")
+ .avatar
+ img(v-lazy="favorite.thumbnailImageUrl")
+ .detail
+ span.name(v-text="favorite.name")
+ span.extra(v-text="favorite.authorName")
+ template(v-else)
+ .avatar
+ .detail
+ span.name(v-text="favorite.id")
+
+ span(style="display:block;margin-top:20px") {{ $t('view.favorite.avatars.vrchat_favorites') }}
el-collapse-item(v-for="group in API.favoriteAvatarGroups" :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")
@@ -182,9 +199,9 @@ 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(v-if="favorite.ref.releaseStatus !== 'private' && !favorite.deleted" placement="right" :content="$t('view.favorite.select_avatar_tooltip')" :disabled="hideTooltips")
+ 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="left" :content="$t('view.favorite.move_tooltip')" :disabled="hideTooltips")
+ el-tooltip(placement="top" :content="$t('view.favorite.move_tooltip')" :disabled="hideTooltips")
el-dropdown(trigger="click" @click.native.stop size="mini" style="margin-left:5px")
el-button(type="default" icon="el-icon-back" size="mini" circle)
el-dropdown-menu(#default="dropdown")
@@ -211,14 +228,14 @@ mixin favoritesTab()
.detail
span.name(v-text="favorite.name")
span.extra(v-text="favorite.authorName")
- el-tooltip(placement="right" :content="$t('view.favorite.select_avatar_tooltip')" :disabled="hideTooltips")
+ 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")
template(v-if="API.cachedFavoritesByObjectId.has(favorite.id)")
- el-tooltip(placement="left" content="Unfavorite" :disabled="hideTooltips")
- el-button(@click.stop="deleteFavorite(favorite.id)" type="default" icon="el-icon-star-on" size="mini" circle)
+ 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")
template(v-else)
- el-tooltip(placement="left" content="Favorite" :disabled="hideTooltips")
- el-button(@click.stop="showFavoriteDialog('avatar', favorite.id)" type="default" icon="el-icon-star-off" size="mini" circle)
+ 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")
span(style="display:block;margin-top:20px") {{ $t('view.favorite.avatars.local_favorites') }}
el-button(size="small" :disabled="!isLocalUserVrcplusSupporter()" @click="promptNewLocalAvatarFavoriteGroup" style="display:block;margin-top:10px") {{ $t('view.favorite.avatars.new_group') }}
el-collapse-item(v-for="group in localAvatarFavoriteGroups" v-if="localAvatarFavorites[group]" :key="group")
@@ -226,7 +243,7 @@ mixin favoritesTab()
span(v-text="group" style="font-weight:bold;font-size:14px;margin-left:10px")
span(style="color:#909399;font-size:12px;margin-left:10px") {{ getLocalAvatarFavoriteGroupLength(group) }}
el-tooltip(placement="top" :content="$t('view.favorite.rename_tooltip')" :disabled="hideTooltips")
- el-button(@click.stop="promptLocalAvatarFavoriteGroupRename(group)" size="mini" icon="el-icon-edit" circle style="margin-left:10px")
+ el-button(@click.stop="promptLocalAvatarFavoriteGroupRename(group)" size="mini" icon="el-icon-edit" circle style="margin-left:5px")
el-tooltip(placement="right" :content="$t('view.favorite.delete_tooltip')" :disabled="hideTooltips")
el-button(@click.stop="promptLocalAvatarFavoriteGroupDelete(group)" size="mini" icon="el-icon-delete" circle style="margin-left:5px")
.x-friend-list(style="margin-top:10px")
@@ -238,7 +255,7 @@ mixin favoritesTab()
.detail
span.name(v-text="favorite.name")
span.extra(v-text="favorite.authorName")
- el-tooltip(placement="right" :content="$t('view.favorite.select_avatar_tooltip')" :disabled="hideTooltips")
+ 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")