feat: Add detailed logging for user profile components and image metadata extraction; enhance color handling utilities

This commit is contained in:
2026-04-30 20:23:44 +10:00
parent cad4852878
commit 5d4d518af8
9 changed files with 299 additions and 48 deletions

View File

@@ -73,6 +73,7 @@ export function useUserBanner(): [
const profile = await mx.getProfileInfo(userId);
const avatarUrl = profile.avatar_url;
console.log('[useUserBanner.loadBanner] Loading banner from avatar:', avatarUrl);
if (!avatarUrl) {
setBanner(undefined);
@@ -90,6 +91,7 @@ export function useUserBanner(): [
// Always use current session's token to avoid stale tokens during account switches
const accessToken = getCurrentAccessToken();
const metadata = await fetchAndExtractMetadata(httpUrl, useAuthentication ? accessToken : null);
console.log('[useUserBanner.loadBanner] Extracted banner:', metadata.banner);
setBanner(metadata.banner);
} catch {
setBanner(undefined);
@@ -104,7 +106,8 @@ export function useUserBanner(): [
if (!userId) return undefined;
const user = mx.getUser(userId);
const onAvatarChange: UserEventHandlerMap[UserEvent.AvatarUrl] = () => {
const onAvatarChange: UserEventHandlerMap[UserEvent.AvatarUrl] = (newAvatarUrl) => {
console.log('[useUserBanner] Avatar changed event fired, new URL:', newAvatarUrl);
loadBanner();
};
@@ -143,12 +146,16 @@ export function useUserBanner(): [
// Get existing metadata to preserve
const existingMetadata = extractMetadataFromImage(avatarData);
console.log('[updateBanner] Existing metadata:', existingMetadata);
// Modify image metadata with new banner, preserving color
const newMetadata: ImageMetadata = {
color: existingMetadata.color,
banner: newBanner,
avatarBorderColor: existingMetadata.avatarBorderColor,
gradient: existingMetadata.gradient,
};
console.log('[updateBanner] New metadata to embed:', newMetadata);
const newAvatarData = embedMetadataInImage(avatarData, newMetadata);
@@ -158,10 +165,13 @@ export function useUserBanner(): [
// Verify the banner was embedded correctly before uploading
const verifyMetadata = extractMetadataFromImage(newAvatarData);
console.log('[updateBanner] Verification - extracted metadata from new avatar:', verifyMetadata);
if (newBanner && verifyMetadata.banner !== newBanner) {
console.error('[updateBanner] Banner verification FAILED!', 'Expected:', newBanner, 'Got:', verifyMetadata.banner);
throw new Error('Banner verification failed');
}
console.log('[updateBanner] Banner verification passed');
// Upload modified avatar with correct MIME type
const mimeType = getMimeType(format);
@@ -172,21 +182,26 @@ export function useUserBanner(): [
name: `avatar.${extension}`,
type: mimeType,
});
console.log('[updateBanner] Avatar uploaded successfully, MXC:', uploadResponse.content_uri);
// Update profile with new avatar
try {
console.log('[updateBanner] Setting avatar URL to:', uploadResponse.content_uri);
await Promise.race([
mx.setAvatarUrl(uploadResponse.content_uri),
new Promise((_, reject) => {
setTimeout(() => reject(new Error('setAvatarUrl timeout')), 30000);
})
]);
console.log('[updateBanner] setAvatarUrl completed');
// Manually sync user object to ensure event listeners are triggered
const user = mx.getUser(userId);
if (user && user.avatarUrl !== uploadResponse.content_uri) {
console.log('[updateBanner] Manually syncing user avatar URL');
user.setAvatarUrl(uploadResponse.content_uri);
}
console.log('[updateBanner] Banner update complete');
} catch (error) {
throw new Error(`Failed to update avatar URL: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
@@ -217,11 +232,14 @@ export function useOtherUserBanner(userId: string, avatarMxc: string | undefined
let blobUrl: string | undefined;
if (!avatarMxc) {
console.log('[useOtherUserBanner] No avatarMxc provided for', userId);
setBannerBlobUrl(undefined);
return undefined;
}
const loadBanner = async () => {
console.log('[useOtherUserBanner] Loading banner for', userId, 'avatarMxc:', avatarMxc);
// Check cache first
const cacheKey = `${userId}:${avatarMxc}`;
const cached = userBannerCache.get(cacheKey);
@@ -230,11 +248,15 @@ export function useOtherUserBanner(userId: string, avatarMxc: string | undefined
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
// Use cached banner MXC
console.log('[useOtherUserBanner] Using cached bannerMxc:', cached.bannerMxc);
bannerMxc = cached.bannerMxc;
} else {
// Fetch banner MXC from avatar metadata
const httpUrl = mxcUrlToHttp(mx, avatarMxc, useAuthentication);
console.log('[useOtherUserBanner] Fetching metadata from:', httpUrl);
if (!httpUrl) {
console.log('[useOtherUserBanner] Failed to get HTTP URL');
setBannerBlobUrl(undefined);
return;
}
@@ -243,6 +265,8 @@ export function useOtherUserBanner(userId: string, avatarMxc: string | undefined
const accessToken = getCurrentAccessToken();
const metadata = await fetchAndExtractMetadata(httpUrl, useAuthentication ? accessToken : null);
console.log('[useOtherUserBanner] Extracted metadata:', metadata);
bannerMxc = metadata.banner;
// Cache the banner MXC (not the blob URL)
@@ -250,17 +274,23 @@ export function useOtherUserBanner(userId: string, avatarMxc: string | undefined
}
if (!bannerMxc) {
console.log('[useOtherUserBanner] No banner MXC found');
setBannerBlobUrl(undefined);
return;
}
console.log('[useOtherUserBanner] Fetching banner image from MXC:', bannerMxc);
// Fetch the banner image data with authentication
const bannerHttpUrl = mxcUrlToHttp(mx, bannerMxc, useAuthentication);
if (!bannerHttpUrl) {
console.log('[useOtherUserBanner] Failed to convert banner MXC to HTTP URL');
setBannerBlobUrl(undefined);
return;
}
console.log('[useOtherUserBanner] Banner HTTP URL:', bannerHttpUrl);
// Always use current session's token to avoid stale tokens during account switches
const accessToken = getCurrentAccessToken();
const headers: HeadersInit = {};

View File

@@ -260,10 +260,19 @@ const CACHE_TTL_MS = 30 * 60 * 1000; // 30 minutes
export function useOtherUserColor(userId: string, avatarMxc: string | undefined): string | undefined {
const mx = useMatrixClient();
const useAuthentication = useMediaAuthentication();
const [color, setColor] = useState<string | undefined>();
// Check cache synchronously BEFORE initializing state to avoid flash
const cacheKey = avatarMxc ? `${userId}:${avatarMxc}` : '';
const cachedEntry = cacheKey ? userColorCache.get(cacheKey) : undefined;
const cachedColor = cachedEntry && Date.now() - cachedEntry.timestamp < CACHE_TTL_MS
? cachedEntry.color
: undefined;
const [color, setColor] = useState<string | undefined>(cachedColor);
useEffect(() => {
if (!avatarMxc) {
console.log('[useOtherUserColor] No avatarMxc provided for', userId);
setColor(undefined);
return;
}
@@ -272,13 +281,16 @@ export function useOtherUserColor(userId: string, avatarMxc: string | undefined)
const cacheKey = `${userId}:${avatarMxc}`;
const cached = userColorCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
console.log('[useOtherUserColor] Using cached color for', userId, ':', cached.color);
setColor(cached.color);
return;
}
const loadColor = async () => {
const httpUrl = mxcUrlToHttp(mx, avatarMxc, useAuthentication);
console.log('[useOtherUserColor] Loading color for', userId, 'from', httpUrl);
if (!httpUrl) {
console.log('[useOtherUserColor] Failed to get HTTP URL for', avatarMxc);
setColor(undefined);
return;
}
@@ -287,6 +299,8 @@ export function useOtherUserColor(userId: string, avatarMxc: string | undefined)
const accessToken = getCurrentAccessToken();
const extractedColor = await fetchAndExtractColor(httpUrl, useAuthentication ? accessToken : null);
console.log('[useOtherUserColor] Extracted color for', userId, ':', extractedColor);
// Cache the result
userColorCache.set(cacheKey, { color: extractedColor, timestamp: Date.now() });

View File

@@ -212,22 +212,29 @@ export function useOtherUserProfileStyle(userId: string, avatarMxc: string | und
useEffect(() => {
if (!avatarMxc) {
console.log('[useOtherUserProfileStyle] No avatarMxc provided for', userId);
setStyle({});
return;
}
const loadStyle = async () => {
console.log('[useOtherUserProfileStyle] Loading style for', userId, 'avatarMxc:', avatarMxc);
// Check cache first
const cacheKey = `${userId}:${avatarMxc}`;
const cached = userStyleCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
console.log('[useOtherUserProfileStyle] Using cached style:', cached.style);
setStyle(cached.style);
return;
}
const httpUrl = mxcUrlToHttp(mx, avatarMxc, useAuthentication);
console.log('[useOtherUserProfileStyle] Fetching metadata from:', httpUrl);
if (!httpUrl) {
console.log('[useOtherUserProfileStyle] Failed to get HTTP URL');
setStyle({});
return;
}
@@ -236,11 +243,15 @@ export function useOtherUserProfileStyle(userId: string, avatarMxc: string | und
const accessToken = getCurrentAccessToken();
const metadata = await fetchAndExtractMetadata(httpUrl, useAuthentication ? accessToken : null);
console.log('[useOtherUserProfileStyle] Extracted metadata:', metadata);
const styleData: ProfileStyleData = {
avatarBorderColor: metadata.avatarBorderColor,
gradient: metadata.gradient,
};
console.log('[useOtherUserProfileStyle] Final styleData:', styleData);
// Cache the result
userStyleCache.set(cacheKey, { style: styleData, timestamp: Date.now() });
setStyle(styleData);