Avatar search

This commit is contained in:
Natsumi
2022-01-25 21:57:50 +13:00
parent e78c3291b5
commit 097d48250b
2 changed files with 298 additions and 101 deletions
+278 -95
View File
@@ -946,7 +946,11 @@ speechSynthesis.getVoices();
if (!this.imageurl) { if (!this.imageurl) {
return; return;
} }
$app.showAvatarAuthorDialog(this.userid, this.imageurl); $app.showAvatarAuthorDialog(
this.userid,
this.ownerId,
this.imageurl
);
} }
}, },
watch: { watch: {
@@ -9120,21 +9124,21 @@ speechSynthesis.getVoices();
var entry = { var entry = {
created_at: new Date().toJSON(), created_at: new Date().toJSON(),
type: 'AvatarChange', type: 'AvatarChange',
userId: user.id, userId: user.id,
displayName: user.displayName, displayName: user.displayName,
name: avatar.name, name: avatar.name,
description: avatar.description, description: avatar.description,
avatarId: avatar.id, avatarId: avatar.id,
authorId: avatar.authorId, authorId: avatar.authorId,
releaseStatus: avatar.releaseStatus, releaseStatus: avatar.releaseStatus,
imageUrl: avatar.imageUrl, imageUrl: avatar.imageUrl,
thumbnailImageUrl: avatar.thumbnailImageUrl thumbnailImageUrl: avatar.thumbnailImageUrl
}; };
this.queueGameLogNoty(entry); this.queueGameLogNoty(entry);
this.addGameLog(entry); this.addGameLog(entry);
this.addEntryPhotonEvent({ this.addEntryPhotonEvent({
photonId, photonId,
displayName: user.displayName, displayName: user.displayName,
userId: user.id, userId: user.id,
text: `ChangeAvatar ${avatar.name}`, text: `ChangeAvatar ${avatar.name}`,
created_at: gameLogDate, created_at: gameLogDate,
@@ -9730,10 +9734,14 @@ speechSynthesis.getVoices();
$app.data.searchWorldOption = ''; $app.data.searchWorldOption = '';
$app.data.searchWorldParams = {}; $app.data.searchWorldParams = {};
$app.data.searchAvatarResults = []; $app.data.searchAvatarResults = [];
$app.data.searchAvatarPage = [];
$app.data.searchAvatarPageNum = 0;
$app.data.searchAvatarFilter = ''; $app.data.searchAvatarFilter = '';
$app.data.searchAvatarSort = ''; $app.data.searchAvatarSort = '';
$app.data.searchAvatarFilterRemote = '';
$app.data.isSearchUserLoading = false; $app.data.isSearchUserLoading = false;
$app.data.isSearchWorldLoading = false; $app.data.isSearchWorldLoading = false;
$app.data.isSearchAvatarLoading = false;
API.$on('LOGIN', function () { API.$on('LOGIN', function () {
$app.searchText = ''; $app.searchText = '';
@@ -9743,10 +9751,14 @@ speechSynthesis.getVoices();
$app.searchWorldOption = ''; $app.searchWorldOption = '';
$app.searchWorldParams = {}; $app.searchWorldParams = {};
$app.searchAvatarResults = []; $app.searchAvatarResults = [];
$app.searchAvatarPage = [];
$app.searchAvatarPageNum = 0;
$app.searchAvatarFilter = ''; $app.searchAvatarFilter = '';
$app.searchAvatarSort = ''; $app.searchAvatarSort = '';
$app.searchAvatarFilterRemote = '';
$app.isSearchUserLoading = false; $app.isSearchUserLoading = false;
$app.isSearchWorldLoading = false; $app.isSearchWorldLoading = false;
$app.isSearchAvatarLoading = false;
}); });
$app.methods.clearSearch = function () { $app.methods.clearSearch = function () {
@@ -9756,6 +9768,8 @@ speechSynthesis.getVoices();
this.searchUserResults = []; this.searchUserResults = [];
this.searchWorldResults = []; this.searchWorldResults = [];
this.searchAvatarResults = []; this.searchAvatarResults = [];
this.searchAvatarPage = [];
this.searchAvatarPageNum = 0;
}; };
$app.methods.search = function () { $app.methods.search = function () {
@@ -9895,78 +9909,117 @@ speechSynthesis.getVoices();
}); });
}; };
$app.methods.searchAvatar = function () { $app.methods.searchAvatar = async function () {
this.isSearchAvatarLoading = true;
if (!this.searchAvatarFilter) { if (!this.searchAvatarFilter) {
this.searchAvatarFilter = 'all'; this.searchAvatarFilter = 'all';
} }
if (!this.searchAvatarSort) { if (!this.searchAvatarSort) {
this.searchAvatarSort = 'name'; this.searchAvatarSort = 'name';
} }
var avatars = []; if (!this.searchAvatarFilterRemote) {
this.searchAvatarFilterRemote = 'all';
}
var avatars = new Map();
var query = this.searchText.toUpperCase(); var query = this.searchText.toUpperCase();
if (!query) { if (!query) {
for (var ref of API.cachedAvatars.values()) { for (var ref of API.cachedAvatars.values()) {
if (ref.authorId === API.currentUser.id) { if (ref.authorId === API.currentUser.id) {
switch (this.searchAvatarFilter) { switch (this.searchAvatarFilter) {
case 'all': case 'all':
avatars.push(ref); avatars.set(ref.id, ref);
break; break;
case 'public': case 'public':
if (ref.releaseStatus === 'public') { if (ref.releaseStatus === 'public') {
avatars.push(ref); avatars.set(ref.id, ref);
} }
break; break;
case 'private': case 'private':
if (ref.releaseStatus === 'private') { if (ref.releaseStatus === 'private') {
avatars.push(ref); avatars.set(ref.id, ref);
} }
break; break;
} }
} }
} }
this.isSearchAvatarLoading = false;
} else { } else {
for (var ref of API.cachedAvatars.values()) { if (
var match = ref.name.toUpperCase().includes(query); this.searchAvatarFilterRemote === 'all' ||
if (!match && ref.description) { this.searchAvatarFilterRemote === 'local'
match = ref.description.toUpperCase().includes(query); ) {
} for (var ref of API.cachedAvatars.values()) {
if (!match && ref.authorName) { var match = ref.name.toUpperCase().includes(query);
match = ref.authorName.toUpperCase().includes(query); if (!match && ref.description) {
} match = ref.description.toUpperCase().includes(query);
if (match) { }
switch (this.searchAvatarFilter) { if (!match && ref.authorName) {
case 'all': match = ref.authorName.toUpperCase().includes(query);
avatars.push(ref); }
break; if (match) {
case 'public': switch (this.searchAvatarFilter) {
if (ref.releaseStatus === 'public') { case 'all':
avatars.push(ref); avatars.set(ref.id, ref);
} break;
break; case 'public':
case 'private': if (ref.releaseStatus === 'public') {
if (ref.releaseStatus === 'private') { avatars.set(ref.id, ref);
avatars.push(ref); }
} break;
break; case 'private':
if (ref.releaseStatus === 'private') {
avatars.set(ref.id, ref);
}
break;
}
} }
} }
if (avatars.length >= 1000) { }
break; if (
(this.searchAvatarFilterRemote === 'all' ||
this.searchAvatarFilterRemote === 'remote') &&
this.avatarRemoteDatabase &&
query.length >= 3
) {
var data = await this.lookupAvatars('search', query);
if (data && typeof data === 'object') {
data.forEach((avatar) => {
avatars.set(avatar.id, avatar);
});
} }
} }
this.isSearchAvatarLoading = false;
} }
var avatarsArray = Array.from(avatars.values());
switch (this.searchAvatarSort) { switch (this.searchAvatarSort) {
case 'updated': case 'updated':
avatars.sort(compareByUpdatedAt); avatarsArray.sort(compareByUpdatedAt);
break; break;
case 'created': case 'created':
avatars.sort(compareByCreatedAt); avatarsArray.sort(compareByCreatedAt);
break; break;
case 'name': case 'name':
avatars.sort(compareByName); avatarsArray.sort(compareByName);
break; break;
} }
this.searchAvatarResults = avatars; this.searchAvatarPageNum = 0;
this.searchAvatarResults = avatarsArray;
this.searchAvatarPage = avatarsArray.slice(0, 10);
};
$app.methods.moreSearchAvatar = function (n) {
if (n === -1) {
this.searchAvatarPageNum--;
var offset = this.searchAvatarPageNum * 10;
}
if (n === 1) {
this.searchAvatarPageNum++;
var offset = this.searchAvatarPageNum * 10;
}
this.searchAvatarPage = this.searchAvatarResults.slice(
offset,
offset + 10
);
}; };
// App: Favorite // App: Favorite
@@ -10963,6 +11016,12 @@ speechSynthesis.getVoices();
$app.data.nextClearVRCXCacheCheck = configRepository.getString( $app.data.nextClearVRCXCacheCheck = configRepository.getString(
'VRCX_clearVRCXCacheFrequency' 'VRCX_clearVRCXCacheFrequency'
); );
$app.data.avatarRemoteDatabase = configRepository.getBool(
'VRCX_avatarRemoteDatabase'
);
$app.data.avatarRemoteDatabaseProvider = configRepository.getString(
'VRCX_avatarRemoteDatabaseProvider'
);
$app.methods.saveOpenVROption = function () { $app.methods.saveOpenVROption = function () {
configRepository.setBool('openVR', this.openVR); configRepository.setBool('openVR', this.openVR);
configRepository.setBool('openVRAlways', this.openVRAlways); configRepository.setBool('openVRAlways', this.openVRAlways);
@@ -11024,6 +11083,10 @@ speechSynthesis.getVoices();
'VRCX_vrBackgroundEnabled', 'VRCX_vrBackgroundEnabled',
this.vrBackgroundEnabled this.vrBackgroundEnabled
); );
configRepository.setBool(
'VRCX_avatarRemoteDatabase',
this.avatarRemoteDatabase
);
this.updateSharedFeed(true); this.updateSharedFeed(true);
this.updateVRConfigVars(); this.updateVRConfigVars();
AppApi.ExecuteVrOverlayFunction('notyClear', ''); AppApi.ExecuteVrOverlayFunction('notyClear', '');
@@ -12162,6 +12225,30 @@ speechSynthesis.getVoices();
); );
}; };
$app.methods.promptSetAvatarRemoteDatabase = function () {
this.$prompt(
'Enter avatar database provider URL',
'Avatar Database Provider',
{
distinguishCancelAndClose: true,
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
inputValue: this.avatarRemoteDatabaseProvider,
inputPattern: /\S+/,
inputErrorMessage: 'Valid URL is required',
callback: (action, instance) => {
if (action === 'confirm' && instance.inputValue) {
this.avatarRemoteDatabaseProvider = instance.inputValue;
configRepository.setString(
'VRCX_avatarRemoteDatabaseProvider',
this.avatarRemoteDatabaseProvider
);
}
}
}
);
};
// App: Dialog // App: Dialog
var adjustDialogZ = (el) => { var adjustDialogZ = (el) => {
@@ -12490,15 +12577,14 @@ speechSynthesis.getVoices();
} else if (this.$refs.userDialogTabs.currentName === '3') { } else if (this.$refs.userDialogTabs.currentName === '3') {
this.userDialogLastActiveTab = 'Avatars'; this.userDialogLastActiveTab = 'Avatars';
this.setUserDialogAvatars(userId); this.setUserDialogAvatars(userId);
if (this.userDialogLastAvatar !== userId) { this.userDialogLastAvatar = userId;
this.userDialogLastAvatar = userId; if (
if ( userId === API.currentUser.id &&
userId === API.currentUser.id && D.avatars.length === 0
D.avatars.length === 0 ) {
) { this.refreshUserDialogAvatars();
this.refreshUserDialogAvatars();
}
} }
this.setUserDialogAvatarsRemote(userId);
} else if (this.$refs.userDialogTabs.currentName === '4') { } else if (this.$refs.userDialogTabs.currentName === '4') {
this.userDialogLastActiveTab = 'JSON'; this.userDialogLastActiveTab = 'JSON';
this.refreshUserDialogTreeData(); this.refreshUserDialogTreeData();
@@ -12880,13 +12966,85 @@ speechSynthesis.getVoices();
}; };
$app.methods.setUserDialogAvatars = function (userId) { $app.methods.setUserDialogAvatars = function (userId) {
var avatars = []; var avatars = new Set();
this.userDialogAvatars.forEach((avatar) => {
avatars.add(avatar.id, avatar);
});
for (var ref of API.cachedAvatars.values()) { for (var ref of API.cachedAvatars.values()) {
if (ref.authorId === userId) { if (ref.authorId === userId && !avatars.has(ref.id)) {
avatars.push(ref); this.userDialog.avatars.push(ref);
} }
} }
this.sortUserDialogAvatars(avatars); this.sortUserDialogAvatars(this.userDialog.avatars);
};
$app.methods.setUserDialogAvatarsRemote = async function (userId) {
if (this.avatarRemoteDatabase && userId !== API.currentUser.id) {
var data = await this.lookupAvatars('authorId', userId);
var avatars = new Set();
this.userDialogAvatars.forEach((avatar) => {
avatars.add(avatar.id, avatar);
});
if (data && typeof data === 'object') {
data.forEach((avatar) => {
if (avatar.id && !avatars.has(avatar.id)) {
this.userDialog.avatars.push(avatar);
}
});
}
}
this.sortUserDialogAvatars(this.userDialog.avatars);
};
$app.methods.lookupAvatars = async function (type, search) {
if (type === 'search') {
var limit = '&limit=5000';
} else {
var limit = '';
}
var avatars = new Map();
try {
var response = await webApiService.execute({
url: `${
this.avatarRemoteDatabaseProvider
}?${type}=${encodeURIComponent(search)}${limit}`,
method: 'GET',
headers: {
'User-Agent': appVersion,
Referer: 'https://vrcx.pypy.moe'
}
});
var json = JSON.parse(response.data);
if (this.debugWebRequests) {
console.log(json, response);
}
if (response.status === 200 && typeof json === 'object') {
json.forEach((avatar) => {
if (!avatars.has(avatar.Id)) {
var ref1 = {
authorId: '',
authorName: '',
name: '',
description: '',
id: '',
imageUrl: '',
// thumbnailImageUrl: '',
created_at: '0001-01-01T00:00:00.0000000Z',
updated_at: '0001-01-01T00:00:00.0000000Z',
releaseStatus: 'public',
...avatar,
thumbnailImageUrl: avatar.imageUrl
};
avatars.set(ref1.id, ref1);
}
});
} else {
throw new Error(`Error: ${response.data}`);
}
} catch {
console.error(`Avatar lookup failed for ${search}`);
}
return avatars;
}; };
$app.methods.sortUserDialogAvatars = function (array) { $app.methods.sortUserDialogAvatars = function (array) {
@@ -13154,7 +13312,11 @@ speechSynthesis.getVoices();
}); });
} else if (command === 'Show Avatar Author') { } else if (command === 'Show Avatar Author') {
var {currentAvatarImageUrl} = D.ref; var {currentAvatarImageUrl} = D.ref;
this.showAvatarAuthorDialog(D.id, currentAvatarImageUrl); this.showAvatarAuthorDialog(
D.id,
D.$avatarInfo.ownerId,
currentAvatarImageUrl
);
} else if (command === 'Show Fallback Avatar Details') { } else if (command === 'Show Fallback Avatar Details') {
var {fallbackAvatar} = D.ref; var {fallbackAvatar} = D.ref;
if (fallbackAvatar) { if (fallbackAvatar) {
@@ -13900,8 +14062,34 @@ speechSynthesis.getVoices();
} }
}; };
$app.methods.showAvatarAuthorDialog = function ( $app.methods.checkAvatarCache = function (fileId) {
var avatarId = '';
for (var ref of API.cachedAvatars.values()) {
if (extractFileId(ref.imageUrl) === fileId) {
avatarId = ref.id;
}
}
return avatarId;
};
$app.methods.checkAvatarCacheRemote = async function (fileId, ownerUserId) {
var avatarId = '';
if (this.avatarRemoteDatabase) {
var data = await this.lookupAvatars('authorId', ownerUserId);
if (data && typeof data === 'object') {
data.forEach((avatar) => {
if (extractFileId(avatar.imageUrl) === fileId) {
avatarId = avatar.id;
}
});
}
}
return avatarId;
};
$app.methods.showAvatarAuthorDialog = async function (
refUserId, refUserId,
ownerUserId,
currentAvatarImageUrl currentAvatarImageUrl
) { ) {
var fileId = extractFileId(currentAvatarImageUrl); var fileId = extractFileId(currentAvatarImageUrl);
@@ -13910,44 +14098,37 @@ speechSynthesis.getVoices();
message: 'Sorry, the author is unknown', message: 'Sorry, the author is unknown',
type: 'error' type: 'error'
}); });
return; } else if (refUserId === API.currentUser.id) {
}
if (refUserId === API.currentUser.id) {
this.showAvatarDialog(API.currentUser.currentAvatar); this.showAvatarDialog(API.currentUser.currentAvatar);
return;
}
for (var ref of API.cachedAvatars.values()) {
if (extractFileId(ref.imageUrl) === fileId) {
this.showAvatarDialog(ref.id);
return;
}
}
if (API.cachedAvatarNames.has(fileId)) {
let {ownerId} = API.cachedAvatarNames.get(fileId);
if (ownerId === API.currentUser.id) {
this.refreshUserDialogAvatars(fileId);
return;
}
if (ownerId === refUserId) {
this.$message({
message: "It's personal (own) avatar",
type: 'warning'
});
return;
}
this.showUserDialog(ownerId);
} else { } else {
API.getAvatarImages({fileId}).then((args) => { var avatarId = await this.checkAvatarCache(fileId);
let ownerId = args.json.ownerId; if (!avatarId) {
if (ownerId === refUserId) { var avatarInfo = await this.getAvatarName(
currentAvatarImageUrl
);
if (avatarInfo.ownerId === API.currentUser.id) {
this.refreshUserDialogAvatars(fileId);
}
}
if (!avatarId) {
avatarId = await this.checkAvatarCacheRemote(
fileId,
ownerUserId
);
}
if (!avatarId) {
if (avatarInfo.ownerId === refUserId) {
this.$message({ this.$message({
message: "It's personal (own) avatar", message: "It's personal (own) avatar",
type: 'warning' type: 'warning'
}); });
return; } else {
this.showUserDialog(avatarInfo.ownerId);
} }
this.showUserDialog(ownerId); }
}); if (avatarId) {
this.showAvatarDialog(avatarId);
}
} }
}; };
@@ -16866,6 +17047,8 @@ speechSynthesis.getVoices();
this.userDialog.avatars.length === 0 this.userDialog.avatars.length === 0
) { ) {
this.refreshUserDialogAvatars(); this.refreshUserDialogAvatars();
} else {
this.setUserDialogAvatarsRemote(userId);
} }
} }
} else if (obj.label === 'Worlds') { } else if (obj.label === 'Worlds') {
+20 -6
View File
@@ -410,30 +410,37 @@ html
span.extra(v-else v-text="world.authorName") span.extra(v-else v-text="world.authorName")
el-button-group(style="margin-top:15px") el-button-group(style="margin-top:15px")
el-button(v-if="searchWorldParams.offset" @click="moreSearchWorld(-1)" icon="el-icon-back" size="small") Prev el-button(v-if="searchWorldParams.offset" @click="moreSearchWorld(-1)" icon="el-icon-back" size="small") Prev
el-button(v-if="searchWorldResults.length" @click="moreSearchWorld(1)" icon="el-icon-right" size="small") Next el-button(v-if="searchWorldResults.length >= 10" @click="moreSearchWorld(1)" icon="el-icon-right" size="small") Next
el-tab-pane(label="Avatar" style="min-height:60px") el-tab-pane(label="Avatar" v-loading="isSearchAvatarLoading" style="min-height:60px")
el-tooltip(placement="bottom" content="Refresh own avatars" :disabled="hideTooltips") el-tooltip(placement="bottom" content="Refresh own avatars" :disabled="hideTooltips")
el-button(type="default" :loading="userDialog.isAvatarsLoading" @click="refreshUserDialogAvatars()" size="mini" icon="el-icon-refresh" circle) el-button(type="default" :loading="userDialog.isAvatarsLoading" @click="refreshUserDialogAvatars()" size="mini" icon="el-icon-refresh" circle)
span(style="font-size:14px;margin-left:5px") Results {{ searchAvatarResults.length }} span(style="font-size:14px;margin-left:5px") Results {{ searchAvatarResults.length }}
el-radio-group(v-model="searchAvatarSort" size="mini" style="margin-left:30px" @change="searchAvatar") el-radio-group(v-model="searchAvatarSort" size="mini" style="margin:5px;display:block" @change="searchAvatar")
el-radio(label="name") by name el-radio(label="name") by name
el-radio(label="update") by update el-radio(label="update") by update
el-radio(label="created") by created el-radio(label="created") by created
el-radio-group(v-model="searchAvatarFilter" size="mini" style="margin-left:80px" @change="searchAvatar") el-radio-group(v-model="searchAvatarFilter" size="mini" style="margin:5px;display:block" @change="searchAvatar")
el-radio(label="all") all el-radio(label="all") all
el-radio(label="public") public el-radio(label="public") public
el-radio(label="private") private el-radio(label="private") private
el-radio-group(v-model="searchAvatarFilterRemote" size="mini" style="margin:5px;display:block" @change="searchAvatar")
el-radio(label="all") all
el-radio(label="local") local
el-radio(label="remote" :disabled="!avatarRemoteDatabase") remote
.x-friend-list(style="margin-top:20px") .x-friend-list(style="margin-top:20px")
.x-friend-item(v-for="avatar in searchAvatarResults" :key="avatar.id" @click="showAvatarDialog(avatar.id)") .x-friend-item(v-for="avatar in searchAvatarPage" :key="avatar.id" @click="showAvatarDialog(avatar.id)")
template(v-once) template(v-once)
.avatar .avatar
img(v-lazy="avatar.thumbnailImageUrl") img(v-if="avatar.thumbnailImageUrl" v-lazy="avatar.thumbnailImageUrl")
.detail .detail
span.name(v-text="avatar.name") span.name(v-text="avatar.name")
span.extra(v-text="avatar.releaseStatus" v-if="avatar.releaseStatus === 'public'" style="color: #67c23a;") span.extra(v-text="avatar.releaseStatus" v-if="avatar.releaseStatus === 'public'" style="color: #67c23a;")
span.extra(v-text="avatar.releaseStatus" v-else-if="avatar.releaseStatus === 'private'" style="color: #f56c6c;") span.extra(v-text="avatar.releaseStatus" v-else-if="avatar.releaseStatus === 'private'" style="color: #f56c6c;")
span.extra(v-text="avatar.releaseStatus" v-else) span.extra(v-text="avatar.releaseStatus" v-else)
span.extra(v-text="avatar.authorName") span.extra(v-text="avatar.authorName")
el-button-group(style="margin-top:15px")
el-button(v-if="searchAvatarPageNum" @click="moreSearchAvatar(-1)" icon="el-icon-back" size="small") Prev
el-button(v-if="searchAvatarResults.length > 10 && (searchAvatarPageNum + 1) * 10 < searchAvatarResults.length" @click="moreSearchAvatar(1)" icon="el-icon-right" size="small") Next
//- favorite //- favorite
.x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'favorite'" v-if="$refs.menu && $refs.menu.activeIndex === 'favorite'") .x-container(v-show="$refs.menu && $refs.menu.activeIndex === 'favorite'" v-if="$refs.menu && $refs.menu.activeIndex === 'favorite'")
@@ -1141,6 +1148,13 @@ html
el-button-group el-button-group
el-button(size="small" icon="el-icon-s-operation" @click="showLaunchOptions()") Launch Options el-button(size="small" icon="el-icon-s-operation" @click="showLaunchOptions()") Launch Options
el-button(size="small" icon="el-icon-s-operation" @click="showVRChatConfig()") VRChat config.json el-button(size="small" icon="el-icon-s-operation" @click="showVRChatConfig()") VRChat config.json
div.options-container
span.header Remote Avatar Database
div.options-container-item
span.name Enable
el-switch(v-model="avatarRemoteDatabase" @change="saveOpenVROption")
div.options-container-item
el-button(size="small" icon="el-icon-user-solid" @click="promptSetAvatarRemoteDatabase" :disabled="!avatarRemoteDatabase") Avatar Database Provider
div.options-container div.options-container
span.header YouTube API span.header YouTube API
div.options-container-item div.options-container-item