fix: Update mutuals friends error handling

This commit is contained in:
pa
2025-12-06 04:56:11 +09:00
committed by Natsumi
parent 978154266c
commit 0d6ed02355
7 changed files with 65 additions and 23 deletions
@@ -363,7 +363,7 @@
<el-icon style="margin-left: 3px"><Warning /></el-icon> <el-icon style="margin-left: 3px"><Warning /></el-icon>
</el-tooltip> </el-tooltip>
</span> </span>
<span class="extra">{{ userOnlineFor(userDialog) }}</span> <span class="extra">{{ userOnlineFor(userDialog.ref) }}</span>
</div> </div>
</el-tooltip> </el-tooltip>
</div> </div>
@@ -538,7 +538,7 @@
<el-tab-pane <el-tab-pane
name="Mutual Friends" name="Mutual Friends"
v-if="userDialog.id !== currentUser.id" v-if="userDialog.id !== currentUser.id && !currentUser.hasSharedConnectionsOptOut"
:label="t('dialog.user.mutual_friends.header')" :label="t('dialog.user.mutual_friends.header')"
lazy> lazy>
<div style="display: flex; align-items: center; justify-content: space-between"> <div style="display: flex; align-items: center; justify-content: space-between">
+2 -1
View File
@@ -2350,7 +2350,8 @@
"vpn_in_use": "VRChat currently blocks most VPNs. Please disable any connected VPNs and try again.", "vpn_in_use": "VRChat currently blocks most VPNs. Please disable any connected VPNs and try again.",
"login_error": "Login Error", "login_error": "Login Error",
"invalid_json_response": "Invalid JSON response", "invalid_json_response": "Invalid JSON response",
"403_404_bailing_request": "Bailing request due to recent 404/403" "403_404_bailing_request": "Bailing request due to recent 404/403",
"unavailable": "Service may be unavailable due to VRChat internal issues"
} }
} }
} }
+2 -1
View File
@@ -2223,7 +2223,8 @@
"vpn_in_use": "VRChatは現在ほとんどのVPNをブロックしています。接続中のVPNを無効にして、もう一度お試しください。", "vpn_in_use": "VRChatは現在ほとんどのVPNをブロックしています。接続中のVPNを無効にして、もう一度お試しください。",
"login_error": "ログインエラー", "login_error": "ログインエラー",
"invalid_json_response": "無効なJSONレスポンス", "invalid_json_response": "無効なJSONレスポンス",
"403_404_bailing_request": "最近の404/403エラーのため、リクエストを中止しました" "403_404_bailing_request": "最近の404/403エラーのため、リクエストを中止しました",
"unavailable": "サービスはVRChatの内部問題により利用できない可能性があります"
} }
} }
} }
+2 -1
View File
@@ -2349,7 +2349,8 @@
"vpn_in_use": "VRChat 目前限制了大多数的 VPN 服务。请断开所有已连接的 VPN 后重试。", "vpn_in_use": "VRChat 目前限制了大多数的 VPN 服务。请断开所有已连接的 VPN 后重试。",
"login_error": "登录失败", "login_error": "登录失败",
"invalid_json_response": "无效的 JSON 响应", "invalid_json_response": "无效的 JSON 响应",
"403_404_bailing_request": "由于最近出现的 403/404 错误,请求已中止" "403_404_bailing_request": "由于最近出现的 403/404 错误,请求已中止",
"unavailable": "服务可能由于 VRChat 内部问题而不可用"
} }
} }
} }
+9 -6
View File
@@ -284,7 +284,6 @@ export function $throw(code, error, endpoint) {
`${t('api.error.message.endpoint')}: "${typeof endpoint === 'string' ? endpoint : JSON.stringify(endpoint)}"` `${t('api.error.message.endpoint')}: "${typeof endpoint === 'string' ? endpoint : JSON.stringify(endpoint)}"`
); );
} }
const text = message.map((s) => escapeTag(s)).join('<br>');
let ignoreError = false; let ignoreError = false;
if ( if (
(code === 404 || code === -1) && (code === 404 || code === -1) &&
@@ -298,11 +297,13 @@ export function $throw(code, error, endpoint) {
) { ) {
ignoreError = true; ignoreError = true;
} }
if ( if (code === 403 || code === 404 || code === -1) {
(code === 403 || code === 404 || code === -1) && if (endpoint?.startsWith('instances/')) {
endpoint?.startsWith('instances/') ignoreError = true;
) { }
ignoreError = true; if (endpoint?.includes('/mutuals/friends')) {
message[1] = `${t('api.error.message.error_message')}: "${t('api.error.message.unavailable')}"`;
}
} }
if (endpoint?.startsWith('analysis/')) { if (endpoint?.startsWith('analysis/')) {
ignoreError = true; ignoreError = true;
@@ -310,6 +311,8 @@ export function $throw(code, error, endpoint) {
if (endpoint.endsWith('/mutuals') && (code === 403 || code === -1)) { if (endpoint.endsWith('/mutuals') && (code === 403 || code === -1)) {
ignoreError = true; ignoreError = true;
} }
const text = message.map((s) => escapeTag(s)).join('<br>');
if (text.length && !ignoreError) { if (text.length && !ignoreError) {
if (AppDebug.errorNoty) { if (AppDebug.errorNoty) {
AppDebug.errorNoty.close(); AppDebug.errorNoty.close();
+8 -8
View File
@@ -259,16 +259,16 @@ function parseUserUrl(user) {
/** /**
* *
* @param {object} ctx * @param {object} ref
* @returns {string} * @returns {string}
*/ */
function userOnlineFor(ctx) { function userOnlineFor(ref) {
if (ctx.ref.state === 'online' && ctx.ref.$online_for) { if (ref.state === 'online' && ref.$online_for) {
return timeToText(Date.now() - ctx.ref.$online_for); return timeToText(Date.now() - ref.$online_for);
} else if (ctx.ref.state === 'active' && ctx.ref.$active_for) { } else if (ref.state === 'active' && ref.$active_for) {
return timeToText(Date.now() - ctx.ref.$active_for); return timeToText(Date.now() - ref.$active_for);
} else if (ctx.ref.$offline_for) { } else if (ref.$offline_for) {
return timeToText(Date.now() - ctx.ref.$offline_for); return timeToText(Date.now() - ref.$offline_for);
} }
return '-'; return '-';
} }
+40 -4
View File
@@ -268,6 +268,8 @@
} }
} }
const isCancelled = () => status.cancelRequested === true;
async function startFetch() { async function startFetch() {
const rateLimiter = createRateLimiter({ const rateLimiter = createRateLimiter({
limitPerInterval: 5, limitPerInterval: 5,
@@ -278,12 +280,34 @@
const collected = []; const collected = [];
let offset = 0; let offset = 0;
while (true) { while (true) {
if (isCancelled()) {
break;
}
await rateLimiter.wait(); await rateLimiter.wait();
const args = await executeWithBackoff(() => userRequest.getMutualFriends({ userId, offset, n: 100 }), { if (isCancelled()) {
maxRetries: 4, break;
baseDelay: 500, }
shouldRetry: (err) => err?.status === 429 || (err?.message || '').includes('429') const args = await executeWithBackoff(
() => {
if (isCancelled()) {
throw new Error('cancelled');
}
return userRequest.getMutualFriends({ userId, offset, n: 100 });
},
{
maxRetries: 4,
baseDelay: 500,
shouldRetry: (err) => err?.status === 429 || (err?.message || '').includes('429')
}
).catch((err) => {
if ((err?.message || '') === 'cancelled') {
return null;
}
throw err;
}); });
if (!args || isCancelled()) {
break;
}
collected.push(...args.json); collected.push(...args.json);
if (args.json.length < 100) { if (args.json.length < 100) {
break; break;
@@ -320,10 +344,22 @@
if (!friend?.id) { if (!friend?.id) {
continue; continue;
} }
if (isCancelled()) {
cancelled = true;
break;
}
try { try {
const mutuals = await fetchMutualFriends(friend.id); const mutuals = await fetchMutualFriends(friend.id);
if (isCancelled()) {
cancelled = true;
break;
}
mutualMap.set(friend.id, { friend, mutuals }); mutualMap.set(friend.id, { friend, mutuals });
} catch (err) { } catch (err) {
if ((err?.message || '') === 'cancelled' || isCancelled()) {
cancelled = true;
break;
}
console.warn('[MutualNetworkGraph] Skipping friend due to fetch error', friend.id, err); console.warn('[MutualNetworkGraph] Skipping friend due to fetch error', friend.id, err);
continue; continue;
} }