feat: Enhance user color and banner management
- Updated `useUserColor` hook to support fetching and embedding user color from avatar metadata with authentication. - Introduced `useUserBanner` hook for managing user banners stored in avatar metadata, including fetching and embedding functionality. - Added `fetchAndExtractMetadata` utility to retrieve both color and banner from images. - Implemented `CustomStatusDialog` component for users to set and clear custom status messages. - Modified `SettingsTab` to include custom status functionality and improved user experience. - Enhanced image metadata utilities to support banner extraction and embedding in PNG format. - Updated sidebar settings to handle user profile changes more gracefully.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import { MatrixClient, UserEvent, UserEventHandlerMap } from 'matrix-js-sdk';
|
||||
import { useMatrixClient } from './useMatrixClient';
|
||||
import { useMediaAuthentication } from './useMediaAuthentication';
|
||||
import { mxcUrlToHttp } from '../utils/matrix';
|
||||
@@ -25,7 +25,11 @@ async function fetchAvatarData(
|
||||
if (!url) return null;
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const accessToken = mx.getAccessToken();
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: accessToken && useAuthentication ? { Authorization: `Bearer ${accessToken}` } : undefined,
|
||||
});
|
||||
if (!response.ok) return null;
|
||||
return await response.arrayBuffer();
|
||||
} catch {
|
||||
@@ -75,7 +79,8 @@ export function useUserColor(): [
|
||||
return;
|
||||
}
|
||||
|
||||
const extractedColor = await fetchAndExtractColor(httpUrl);
|
||||
const accessToken = mx.getAccessToken();
|
||||
const extractedColor = await fetchAndExtractColor(httpUrl, useAuthentication ? accessToken : null);
|
||||
setColor(extractedColor);
|
||||
} catch (e) {
|
||||
console.error('Failed to load user color from avatar:', e);
|
||||
@@ -85,6 +90,21 @@ export function useUserColor(): [
|
||||
};
|
||||
|
||||
loadColor();
|
||||
|
||||
// Listen for avatar changes and reload color
|
||||
const userId = mx.getUserId();
|
||||
if (!userId) return undefined;
|
||||
|
||||
const user = mx.getUser(userId);
|
||||
const onAvatarChange: UserEventHandlerMap[UserEvent.AvatarUrl] = () => {
|
||||
loadColor();
|
||||
};
|
||||
|
||||
user?.on(UserEvent.AvatarUrl, onAvatarChange);
|
||||
|
||||
return () => {
|
||||
user?.removeListener(UserEvent.AvatarUrl, onAvatarChange);
|
||||
};
|
||||
}, [mx, useAuthentication]);
|
||||
|
||||
const updateColor = useCallback(async (newColor: string | undefined) => {
|
||||
@@ -157,6 +177,12 @@ export function useUserColor(): [
|
||||
// Update profile with new avatar
|
||||
await mx.setAvatarUrl(uploadResponse.content_uri);
|
||||
|
||||
// Manually sync user object to ensure event listeners are triggered
|
||||
const user = mx.getUser(userId);
|
||||
if (user && user.avatarUrl !== uploadResponse.content_uri) {
|
||||
user.setAvatarUrl(uploadResponse.content_uri);
|
||||
}
|
||||
|
||||
setColor(newColor);
|
||||
console.log('User color updated in avatar:', newColor);
|
||||
} catch (e) {
|
||||
@@ -204,7 +230,8 @@ export function useOtherUserColor(userId: string, avatarMxc: string | undefined)
|
||||
return;
|
||||
}
|
||||
|
||||
const extractedColor = await fetchAndExtractColor(httpUrl);
|
||||
const accessToken = mx.getAccessToken();
|
||||
const extractedColor = await fetchAndExtractColor(httpUrl, useAuthentication ? accessToken : null);
|
||||
|
||||
// Cache the result
|
||||
userColorCache.set(cacheKey, { color: extractedColor, timestamp: Date.now() });
|
||||
|
||||
Reference in New Issue
Block a user