feat: implement fallback to unauthenticated requests for media fetching on 401 errors
This commit is contained in:
@@ -41,13 +41,19 @@ export const useAuthenticatedMediaUrl = (
|
||||
const fetchMedia = async () => {
|
||||
try {
|
||||
const accessToken = mx.getAccessToken();
|
||||
const response = await fetch(src, {
|
||||
let response = await fetch(src, {
|
||||
method: 'GET',
|
||||
headers: accessToken
|
||||
? { Authorization: `Bearer ${accessToken}` }
|
||||
: undefined,
|
||||
});
|
||||
|
||||
// If we got a 401 and we tried with auth, fallback to unauthenticated request
|
||||
if (!response.ok && response.status === 401 && accessToken) {
|
||||
console.warn('[useAuthenticatedMediaUrl] Auth failed (401), attempting unauthenticated fallback for:', src);
|
||||
response = await fetch(src, { method: 'GET' });
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn(`Failed to fetch authenticated media: ${response.status}`);
|
||||
// Fall back to original URL in case server doesn't require auth
|
||||
@@ -99,13 +105,19 @@ export const useAuthenticatedMediaFetch = () => {
|
||||
|
||||
try {
|
||||
const accessToken = mx.getAccessToken();
|
||||
const response = await fetch(src, {
|
||||
let response = await fetch(src, {
|
||||
method: 'GET',
|
||||
headers: accessToken
|
||||
? { Authorization: `Bearer ${accessToken}` }
|
||||
: undefined,
|
||||
});
|
||||
|
||||
// If we got a 401 and we tried with auth, fallback to unauthenticated request
|
||||
if (!response.ok && response.status === 401 && accessToken) {
|
||||
console.warn('[useAuthenticatedMediaFetch] Auth failed (401), attempting unauthenticated fallback');
|
||||
response = await fetch(src, { method: 'GET' });
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn(`Failed to fetch authenticated media: ${response.status}`);
|
||||
return src;
|
||||
|
||||
@@ -26,10 +26,16 @@ async function fetchAvatarData(
|
||||
|
||||
try {
|
||||
const accessToken = mx.getAccessToken();
|
||||
const response = await fetch(url, {
|
||||
let response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: accessToken && useAuthentication ? { Authorization: `Bearer ${accessToken}` } : undefined,
|
||||
});
|
||||
|
||||
// If we got a 401 and we tried with auth, fallback to unauthenticated request
|
||||
if (!response.ok && response.status === 401 && accessToken && useAuthentication) {
|
||||
response = await fetch(url, { method: 'GET' });
|
||||
}
|
||||
|
||||
if (!response.ok) return null;
|
||||
return await response.arrayBuffer();
|
||||
} catch {
|
||||
@@ -112,8 +118,6 @@ export function useUserBanner(): [
|
||||
throw new Error('No user ID');
|
||||
}
|
||||
|
||||
console.log('[useUserBanner] Starting banner update:', newBanner);
|
||||
|
||||
const profile = await mx.getProfileInfo(userId);
|
||||
const avatarUrl = profile.avatar_url;
|
||||
|
||||
@@ -121,19 +125,14 @@ export function useUserBanner(): [
|
||||
throw new Error('No avatar set. Please upload an avatar first.');
|
||||
}
|
||||
|
||||
console.log('[useUserBanner] Current avatar URL:', avatarUrl);
|
||||
|
||||
// Fetch current avatar
|
||||
const avatarData = await fetchAvatarData(mx, avatarUrl, useAuthentication);
|
||||
if (!avatarData) {
|
||||
throw new Error('Failed to fetch current avatar');
|
||||
}
|
||||
|
||||
console.log('[useUserBanner] Fetched avatar data, size:', avatarData.byteLength);
|
||||
|
||||
// Detect image format
|
||||
const format = detectImageFormat(avatarData);
|
||||
console.log('[useUserBanner] Detected format:', format);
|
||||
|
||||
if (format === 'unknown') {
|
||||
throw new Error('Unsupported avatar image format');
|
||||
@@ -141,7 +140,6 @@ export function useUserBanner(): [
|
||||
|
||||
// Get existing metadata to preserve
|
||||
const existingMetadata = extractMetadataFromImage(avatarData);
|
||||
console.log('[useUserBanner] Existing metadata:', existingMetadata);
|
||||
|
||||
// Modify image metadata with new banner, preserving color
|
||||
const newMetadata: ImageMetadata = {
|
||||
@@ -149,18 +147,14 @@ export function useUserBanner(): [
|
||||
banner: newBanner,
|
||||
};
|
||||
|
||||
console.log('[useUserBanner] Embedding metadata:', newMetadata);
|
||||
const newAvatarData = embedMetadataInImage(avatarData, newMetadata);
|
||||
|
||||
if (!newAvatarData) {
|
||||
throw new Error('Failed to embed banner in avatar metadata');
|
||||
}
|
||||
|
||||
console.log('[useUserBanner] New avatar data size:', newAvatarData.byteLength);
|
||||
|
||||
// Verify the banner was embedded correctly before uploading
|
||||
const verifyMetadata = extractMetadataFromImage(newAvatarData);
|
||||
console.log('[useUserBanner] Verification metadata:', verifyMetadata);
|
||||
|
||||
if (newBanner && verifyMetadata.banner !== newBanner) {
|
||||
throw new Error('Banner verification failed');
|
||||
@@ -171,22 +165,19 @@ export function useUserBanner(): [
|
||||
const extension = getExtension(format);
|
||||
const blob = new Blob([newAvatarData], { type: mimeType });
|
||||
|
||||
console.log('[useUserBanner] Uploading avatar blob:', blob.size, 'bytes, type:', mimeType);
|
||||
const uploadResponse = await mx.uploadContent(blob, {
|
||||
name: `avatar.${extension}`,
|
||||
type: mimeType,
|
||||
});
|
||||
|
||||
console.log('[useUserBanner] Upload response:', uploadResponse.content_uri);
|
||||
|
||||
// Update profile with new avatar
|
||||
console.log('[useUserBanner] Calling setAvatarUrl...');
|
||||
try {
|
||||
await Promise.race([
|
||||
mx.setAvatarUrl(uploadResponse.content_uri),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('setAvatarUrl timeout')), 30000))
|
||||
new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error('setAvatarUrl timeout')), 30000);
|
||||
})
|
||||
]);
|
||||
console.log('[useUserBanner] Avatar URL updated successfully');
|
||||
|
||||
// Manually sync user object to ensure event listeners are triggered
|
||||
const user = mx.getUser(userId);
|
||||
@@ -194,7 +185,6 @@ export function useUserBanner(): [
|
||||
user.setAvatarUrl(uploadResponse.content_uri);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[useUserBanner] setAvatarUrl failed:', error);
|
||||
throw new Error(`Failed to update avatar URL: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
|
||||
@@ -225,7 +215,7 @@ export function useOtherUserBanner(userId: string, avatarMxc: string | undefined
|
||||
|
||||
if (!avatarMxc) {
|
||||
setBannerBlobUrl(undefined);
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const loadBanner = async () => {
|
||||
@@ -275,7 +265,6 @@ export function useOtherUserBanner(userId: string, avatarMxc: string | undefined
|
||||
|
||||
const response = await fetch(bannerHttpUrl, { headers });
|
||||
if (!response.ok) {
|
||||
console.warn('Failed to fetch banner image:', response.status);
|
||||
setBannerBlobUrl(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -26,10 +26,17 @@ async function fetchAvatarData(
|
||||
|
||||
try {
|
||||
const accessToken = mx.getAccessToken();
|
||||
const response = await fetch(url, {
|
||||
let response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: accessToken && useAuthentication ? { Authorization: `Bearer ${accessToken}` } : undefined,
|
||||
});
|
||||
|
||||
// If we got a 401 and we tried with auth, fallback to unauthenticated request
|
||||
if (!response.ok && response.status === 401 && accessToken && useAuthentication) {
|
||||
console.warn('[fetchAvatarData] Auth failed (401), attempting unauthenticated fallback');
|
||||
response = await fetch(url, { method: 'GET' });
|
||||
}
|
||||
|
||||
if (!response.ok) return null;
|
||||
return await response.arrayBuffer();
|
||||
} catch {
|
||||
|
||||
@@ -27,10 +27,16 @@ async function fetchAvatarData(
|
||||
|
||||
try {
|
||||
const accessToken = mx.getAccessToken();
|
||||
const response = await fetch(url, {
|
||||
let response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: accessToken && useAuthentication ? { Authorization: `Bearer ${accessToken}` } : undefined,
|
||||
});
|
||||
|
||||
// If we got a 401 and we tried with auth, fallback to unauthenticated request
|
||||
if (!response.ok && response.status === 401 && accessToken && useAuthentication) {
|
||||
response = await fetch(url, { method: 'GET' });
|
||||
}
|
||||
|
||||
if (!response.ok) return null;
|
||||
return await response.arrayBuffer();
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user