Fix avatar upload failing on JPEG/WebP/GIF without a profile color.

embedMetadataInImage returned null for non-PNG when there was nothing to embed, which blocked plain avatar saves after format/library churn.
This commit is contained in:
2026-07-25 20:09:22 +10:00
parent 99a294791e
commit 2603ca8e5c
5 changed files with 158 additions and 36 deletions

View File

@@ -12,6 +12,8 @@ import {
getMimeType,
getExtension,
ImageMetadata,
convertImageDataToPng,
uint8ArrayToBlob,
} from '../utils/imageMetadata';
/**
@@ -137,27 +139,39 @@ export function useUserBanner(): [
throw new Error('Failed to fetch current avatar');
}
// Detect image format
const format = detectImageFormat(avatarData);
// Detect image format — banner lives in PNG tEXt, so convert other formats first
let workingData: ArrayBuffer | Uint8Array = avatarData;
let format = detectImageFormat(workingData);
if (format === 'unknown') {
throw new Error('Unsupported avatar image format');
}
if (format !== 'png') {
console.log('[updateBanner] Converting', format, 'avatar to PNG for banner metadata');
const pngData = await convertImageDataToPng(workingData);
if (!pngData) {
throw new Error('Failed to convert avatar to PNG for banner metadata');
}
workingData = pngData;
format = 'png';
}
// Get existing metadata to preserve
const existingMetadata = extractMetadataFromImage(avatarData);
const existingMetadata = extractMetadataFromImage(workingData);
console.log('[updateBanner] Existing metadata:', existingMetadata);
// Modify image metadata with new banner, preserving color
// Modify image metadata with new banner, preserving color.
// Empty string clears banner (PNG embed drops falsy fields after stripping old chunks).
const newMetadata: ImageMetadata = {
color: existingMetadata.color,
banner: newBanner,
banner: newBanner ?? '',
avatarBorderColor: existingMetadata.avatarBorderColor,
gradient: existingMetadata.gradient,
};
console.log('[updateBanner] New metadata to embed:', newMetadata);
const newAvatarData = embedMetadataInImage(avatarData, newMetadata);
const newAvatarData = embedMetadataInImage(workingData, newMetadata);
if (!newAvatarData) {
throw new Error('Failed to embed banner in avatar metadata');
@@ -166,8 +180,8 @@ 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) {
if ((newBanner || undefined) !== verifyMetadata.banner) {
console.error('[updateBanner] Banner verification FAILED!', 'Expected:', newBanner, 'Got:', verifyMetadata.banner);
throw new Error('Banner verification failed');
}
@@ -176,8 +190,8 @@ export function useUserBanner(): [
// Upload modified avatar with correct MIME type
const mimeType = getMimeType(format);
const extension = getExtension(format);
const blob = new Blob([newAvatarData], { type: mimeType });
const blob = uint8ArrayToBlob(newAvatarData, mimeType);
const uploadResponse = await mx.uploadContent(blob, {
name: `avatar.${extension}`,
type: mimeType,