Avatar lookup loading toast

This commit is contained in:
Natsumi
2026-02-03 13:52:55 +13:00
parent bbbb79eaca
commit 5a27e6fb51
7 changed files with 126 additions and 58 deletions

View File

@@ -384,7 +384,7 @@
"fetch_cancelled_graph_not_updated": "Fetch cancelled"
},
"notifications": {
"start_fetching": "Start fetching",
"start_fetching": "Started fetching",
"mutual_friend_graph_ready_title": "Mutual Friend Network Graph",
"mutual_friend_graph_ready_message": "Mutual friend network graph is ready",
"friend_list_changed_fetch_again": "Friend list changed. Please fetch the mutual friend network again."
@@ -2082,6 +2082,12 @@
"image_changed": "Avatar image changed",
"image_invalid": "Current avatar image invalid"
},
"avatar_lookup": {
"not_found": "Avatar not found in search providers",
"failed": "Avatar lookup failed",
"private_or_not_found": "Users avatar is private or not found in search providers",
"loading": "Searching for avatar using search providers"
},
"database": {
"upgrade_complete": "Database upgrade complete"
},
@@ -2091,7 +2097,7 @@
"file": {
"not_image": "File isn't an image",
"too_large": "File size too large",
"folder_missing": "Folder dosn't exist"
"folder_missing": "Folder doesn't exist"
},
"group": {
"load_failed": "Failed to load group"

View File

@@ -69,6 +69,7 @@ export const useAvatarStore = defineStore('Avatar', () => {
timeSpent: 0
});
const avatarHistory = ref([]);
const loadingToastId = ref(null);
watch(
() => watchState.isLoggedIn,
@@ -463,10 +464,11 @@ export const useAvatarStore = defineStore('Avatar', () => {
const avatars = new Map();
if (type === 'search') {
try {
const url = `${
avatarProviderStore.avatarRemoteDatabaseProvider
}?${type}=${encodeURIComponent(search)}&n=5000`;
const response = await webApiService.execute({
url: `${
avatarProviderStore.avatarRemoteDatabaseProvider
}?${type}=${encodeURIComponent(search)}&n=5000`,
url,
method: 'GET',
headers: {
Referer: 'https://vrcx.app',
@@ -475,7 +477,7 @@ export const useAvatarStore = defineStore('Avatar', () => {
});
const json = JSON.parse(response.data);
if (AppDebug.debugWebRequests) {
console.log(json, response);
console.log(url, json, response);
}
if (response.status === 200 && typeof json === 'object') {
json.forEach((avatar) => {
@@ -522,11 +524,18 @@ export const useAvatarStore = defineStore('Avatar', () => {
}
async function lookupAvatarByImageFileId(authorId, fileId) {
const length =
avatarProviderStore.avatarRemoteDatabaseProviderList.length;
for (let i = 0; i < length; ++i) {
const url = avatarProviderStore.avatarRemoteDatabaseProviderList[i];
const avatarArray = await lookupAvatarsByAuthor(url, authorId);
for (const providerUrl of avatarProviderStore.avatarRemoteDatabaseProviderList) {
const avatar = await lookupAvatarByFileId(providerUrl, fileId);
if (avatar?.id) {
return avatar.id;
}
}
for (const providerUrl of avatarProviderStore.avatarRemoteDatabaseProviderList) {
const avatarArray = await lookupAvatarsByAuthor(
providerUrl,
authorId
);
for (const avatar of avatarArray) {
if (extractFileId(avatar.imageUrl) === fileId) {
return avatar.id;
@@ -536,14 +545,11 @@ export const useAvatarStore = defineStore('Avatar', () => {
return null;
}
async function lookupAvatarsByAuthor(url, authorId) {
const avatars = [];
if (!url) {
return avatars;
}
async function lookupAvatarByFileId(providerUrl, fileId) {
try {
const url = `${providerUrl}?fileId=${encodeURIComponent(fileId)}`;
const response = await webApiService.execute({
url: `${url}?authorId=${encodeURIComponent(authorId)}`,
url,
method: 'GET',
headers: {
Referer: 'https://vrcx.app',
@@ -552,7 +558,50 @@ export const useAvatarStore = defineStore('Avatar', () => {
});
const json = JSON.parse(response.data);
if (AppDebug.debugWebRequests) {
console.log(json, response);
console.log(url, json, response);
}
if (response.status === 200 && typeof json === 'object') {
const ref = {
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',
...json
};
return ref;
} else {
return null;
}
} catch (err) {
// ignore errors for now, not all providers support this lookup type
return null;
}
}
async function lookupAvatarsByAuthor(providerUrl, authorId) {
const avatars = [];
if (!providerUrl) {
return avatars;
}
try {
const url = `${providerUrl}?authorId=${encodeURIComponent(authorId)}`;
const response = await webApiService.execute({
url,
method: 'GET',
headers: {
Referer: 'https://vrcx.app',
'VRCX-ID': vrcxUpdaterStore.vrcxId
}
});
const json = JSON.parse(response.data);
if (AppDebug.debugWebRequests) {
console.log(url, json, response);
}
if (response.status === 200 && typeof json === 'object') {
json.forEach((avatar) => {
@@ -621,11 +670,21 @@ export const useAvatarStore = defineStore('Avatar', () => {
async function checkAvatarCacheRemote(fileId, ownerUserId) {
if (advancedSettingsStore.avatarRemoteDatabase) {
const avatarId = await lookupAvatarByImageFileId(
ownerUserId,
fileId
);
return avatarId;
try {
toast.dismiss(loadingToastId.value);
loadingToastId.value = toast.loading(
t('message.avatar_lookup.loading')
);
const avatarId = await lookupAvatarByImageFileId(
ownerUserId,
fileId
);
return avatarId;
} catch (err) {
console.error('Failed to lookup avatar by image file id:', err);
} finally {
toast.dismiss(loadingToastId.value);
}
}
return null;
}
@@ -637,7 +696,7 @@ export const useAvatarStore = defineStore('Avatar', () => {
) {
const fileId = extractFileId(currentAvatarImageUrl);
if (!fileId) {
toast.error('Sorry, the author is unknown');
toast.error(t('message.avatar_lookup.failed'));
} else if (refUserId === userStore.currentUser.id) {
showAvatarDialog(userStore.currentUser.currentAvatar);
} else {
@@ -646,22 +705,20 @@ export const useAvatarStore = defineStore('Avatar', () => {
if (!avatarId) {
avatarInfo = await getAvatarName(currentAvatarImageUrl);
if (avatarInfo.ownerId === userStore.currentUser.id) {
userStore.refreshUserDialogAvatars(fileId);
await userStore.refreshUserDialogAvatars(fileId);
return;
}
}
if (!avatarId) {
avatarId = await checkAvatarCacheRemote(
fileId,
avatarInfo.ownerId
);
avatarId = await checkAvatarCacheRemote(fileId, ownerUserId);
}
if (!avatarId) {
if (avatarInfo.ownerId === refUserId) {
if (ownerUserId === refUserId) {
toast.warning(
"It's personal (own) avatar or not found in avatar database"
t('message.avatar_lookup.private_or_not_found')
);
} else {
toast.warning('Avatar not found in avatar database');
toast.warning(t('message.avatar_lookup.not_found'));
userStore.showUserDialog(avatarInfo.ownerId);
}
}

View File

@@ -15,7 +15,7 @@ export const useAvatarProviderStore = defineStore('AvatarProvider', () => {
const avatarRemoteDatabaseProvider = ref('');
const avatarRemoteDatabaseProviderList = ref([
'https://api.avtrdb.com/v2/avatar/search/vrcx'
'https://api.avtrdb.com/v3/avatar/search/vrcx'
]);
watch(
() => watchState.isLoggedIn,
@@ -29,21 +29,26 @@ export const useAvatarProviderStore = defineStore('AvatarProvider', () => {
avatarRemoteDatabaseProviderList.value = JSON.parse(
await configRepository.getString(
'VRCX_avatarRemoteDatabaseProviderList',
'[ "https://api.avtrdb.com/v2/avatar/search/vrcx" ]'
'[ "https://api.avtrdb.com/v3/avatar/search/vrcx" ]'
)
);
const deprecated = 'https://avtr.just-h.party/vrcx_search.php';
const v1 = 'https://api.avtrdb.com/v1/avatar/search/vrcx';
const v2 = 'https://api.avtrdb.com/v2/avatar/search/vrcx';
const v3 = 'https://api.avtrdb.com/v3/avatar/search/vrcx';
const newList = avatarRemoteDatabaseProviderList.value
.filter((u) => u !== deprecated)
.map((u) => (u === v1 || u === v2 ? v3 : u));
if (
avatarRemoteDatabaseProviderList.value.includes(
'https://avtr.just-h.party/vrcx_search.php'
)
JSON.stringify(newList) !==
JSON.stringify(avatarRemoteDatabaseProviderList.value)
) {
removeFromArray(
avatarRemoteDatabaseProviderList.value,
'https://avtr.just-h.party/vrcx_search.php'
);
avatarRemoteDatabaseProviderList.value = newList;
await configRepository.setString(
'VRCX_avatarRemoteDatabaseProviderList',
JSON.stringify(avatarRemoteDatabaseProviderList.value)
JSON.stringify(newList)
);
}

View File

@@ -458,7 +458,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
const data = JSON.parse(response.data);
if (AppDebug.debugWebRequests) {
console.log('Models API response:', data);
console.log(modelsURL, data, response);
}
if (data.data && Array.isArray(data.data)) {
@@ -685,10 +685,11 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
apiKey = youTubeApiKey.value;
}
try {
const url = `https://www.googleapis.com/youtube/v3/videos?id=${encodeURIComponent(
videoId
)}&part=snippet,contentDetails&key=${apiKey}`;
const response = await webApiService.execute({
url: `https://www.googleapis.com/youtube/v3/videos?id=${encodeURIComponent(
videoId
)}&part=snippet,contentDetails&key=${apiKey}`,
url,
method: 'GET',
headers: {
Referer: 'https://vrcx.app'
@@ -696,7 +697,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
});
const json = JSON.parse(response.data);
if (AppDebug.debugWebRequests) {
console.log(json, response);
console.log(url, json, response);
}
if (response.status === 200) {
data = json;
@@ -725,8 +726,9 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
return null;
}
try {
const url = `https://translation.googleapis.com/language/translate/v2?key=${keyToUse}`;
const response = await webApiService.execute({
url: `https://translation.googleapis.com/language/translate/v2?key=${keyToUse}`,
url,
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -745,7 +747,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
}
const data = JSON.parse(response.data);
if (AppDebug.debugWebRequests) {
console.log(data, response);
console.log(url, data, response);
}
return data.data.translations[0].translatedText;
} catch (err) {
@@ -809,7 +811,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
const data = JSON.parse(response.data);
if (AppDebug.debugWebRequests) {
console.log(data, response);
console.log(endpoint, data, response);
}
const translated = data?.choices?.[0]?.message?.content;

View File

@@ -1208,7 +1208,7 @@ export const useUserStore = defineStore('User', () => {
D.avatars = array;
}
function refreshUserDialogAvatars(fileId) {
async function refreshUserDialogAvatars(fileId) {
const D = userDialog.value;
if (D.isAvatarsLoading) {
return;
@@ -1233,7 +1233,7 @@ export const useUserStore = defineStore('User', () => {
}
}
const map = new Map();
processBulk({
await processBulk({
fn: avatarRequest.getAvatars,
N: -1,
params,

View File

@@ -221,7 +221,7 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
}
pendingVRCXUpdate.value = false;
if (AppDebug.debugWebRequests) {
console.log(json, response);
console.log(url, json, response);
}
if (json === Object(json) && json.name && json.published_at) {
changeLogDialog.value.buildName = json.name;
@@ -308,7 +308,7 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
return;
}
if (AppDebug.debugWebRequests) {
console.log(json, response);
console.log(url, json, response);
}
const releases = [];
if (typeof json !== 'object' || json.message) {

View File

@@ -515,6 +515,7 @@
if (hasFetched.value && !status.needsRefetch) return;
isLoadingSnapshot.value = true;
toast.dismiss(loadingToastId.value);
loadingToastId.value = toast.loading(t('view.charts.mutual_friend.status.loading_cache'));
try {
@@ -561,10 +562,7 @@
console.error('[MutualNetworkGraph] Failed to load cached mutual graph', err);
} finally {
isLoadingSnapshot.value = false;
if (loadingToastId.value !== null && loadingToastId.value !== undefined) {
toast.dismiss(loadingToastId.value);
loadingToastId.value = null;
}
toast.dismiss(loadingToastId.value);
}
}