feat: add user color customization and metadata handling
- Implemented `useUserColor` hook to manage user profile color stored in avatar PNG metadata. - Added `useOtherUserColor` hook to fetch and cache colors from other users' avatars. - Created utility functions for reading and writing PNG metadata, enabling color embedding and extraction. - Refactored `SearchResultGroup`, `RoomInput`, `Message`, `RoomPinMenu`, and `Notifications` components to utilize user color for display. - Introduced `UserColorPicker` component in settings for users to select and save their profile color. - Enhanced notification and message rendering to prioritize custom user colors over legacy colors.
This commit is contained in:
@@ -117,6 +117,8 @@ import { useTheme } from '../../hooks/useTheme';
|
||||
import { useRoomCreatorsTag } from '../../hooks/useRoomCreatorsTag';
|
||||
import { usePowerLevelTags } from '../../hooks/usePowerLevelTags';
|
||||
import { useComposingCheck } from '../../hooks/useComposingCheck';
|
||||
import { useOtherUserColor } from '../../hooks/useUserColor';
|
||||
import { getMemberAvatarMxc } from '../../utils/room';
|
||||
|
||||
interface RoomInputProps {
|
||||
editor: Editor;
|
||||
@@ -142,6 +144,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
const [msgDraft, setMsgDraft] = useAtom(roomIdToMsgDraftAtomFamily(roomId));
|
||||
const [replyDraft, setReplyDraft] = useAtom(roomIdToReplyDraftAtomFamily(roomId));
|
||||
const replyUserID = replyDraft?.userId;
|
||||
const replyAvatarMxc = replyUserID ? getMemberAvatarMxc(room, replyUserID) : undefined;
|
||||
|
||||
const powerLevelTags = usePowerLevelTags(room, powerLevels);
|
||||
const creatorsTag = useRoomCreatorsTag();
|
||||
@@ -153,12 +156,16 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
powerLevelTags
|
||||
);
|
||||
|
||||
// Get custom user color from avatar metadata
|
||||
const replyCustomColor = useOtherUserColor(replyUserID ?? '', replyAvatarMxc);
|
||||
|
||||
const replyPowerTag = replyUserID ? getMemberPowerTag(replyUserID) : undefined;
|
||||
const replyPowerColor = replyPowerTag?.color
|
||||
? accessibleTagColors.get(replyPowerTag.color)
|
||||
: undefined;
|
||||
// Priority: custom user color > legacy/direct colorMXID > tag color
|
||||
const replyUsernameColor =
|
||||
legacyUsernameColor || direct ? colorMXID(replyUserID ?? '') : replyPowerColor;
|
||||
replyCustomColor ?? (legacyUsernameColor || direct ? colorMXID(replyUserID ?? '') : replyPowerColor);
|
||||
|
||||
const [uploadBoard, setUploadBoard] = useState(true);
|
||||
const [selectedFiles, setSelectedFiles] = useAtom(roomIdToUploadItemsAtomFamily(roomId));
|
||||
|
||||
@@ -80,6 +80,7 @@ import { PowerIcon } from '../../../components/power';
|
||||
import colorMXID from '../../../../util/colorMXID';
|
||||
import { getPowerTagIconSrc } from '../../../hooks/useMemberPowerTag';
|
||||
import { Presence, useUserPresence } from '../../../hooks/useUserPresence';
|
||||
import { useOtherUserColor } from '../../../hooks/useUserColor';
|
||||
|
||||
export type ReactionHandler = (keyOrMxc: string, shortcode: string) => void;
|
||||
|
||||
@@ -736,6 +737,9 @@ export const Message = as<'div', MessageProps>(
|
||||
getMemberDisplayName(room, senderId) ?? getMxIdLocalPart(senderId) ?? senderId;
|
||||
const senderAvatarMxc = getMemberAvatarMxc(room, senderId);
|
||||
|
||||
// Get custom user color from avatar metadata (takes priority)
|
||||
const customUserColor = useOtherUserColor(senderId, senderAvatarMxc);
|
||||
|
||||
const tagColor = memberPowerTag?.color
|
||||
? accessibleTagColors?.get(memberPowerTag.color)
|
||||
: undefined;
|
||||
@@ -743,7 +747,8 @@ export const Message = as<'div', MessageProps>(
|
||||
? getPowerTagIconSrc(mx, useAuthentication, memberPowerTag.icon)
|
||||
: undefined;
|
||||
|
||||
const usernameColor = legacyUsernameColor ? colorMXID(senderId) : tagColor;
|
||||
// Priority: custom user color > tag color (non-legacy) > colorMXID (legacy)
|
||||
const usernameColor = customUserColor ?? (legacyUsernameColor ? colorMXID(senderId) : tagColor);
|
||||
|
||||
const headerJSX = !collapse && (
|
||||
<Box
|
||||
|
||||
@@ -86,6 +86,7 @@ import {
|
||||
useGetMemberPowerTag,
|
||||
} from '../../../hooks/useMemberPowerTag';
|
||||
import { useRoomCreatorsTag } from '../../../hooks/useRoomCreatorsTag';
|
||||
import { useOtherUserColor } from '../../../hooks/useUserColor';
|
||||
|
||||
type PinnedMessageProps = {
|
||||
room: Room;
|
||||
@@ -179,6 +180,9 @@ function PinnedMessage({
|
||||
const senderAvatarMxc = getMemberAvatarMxc(room, sender);
|
||||
const getContent = (() => pinnedEvent.getContent()) as GetContentCallback;
|
||||
|
||||
// Get custom user color from avatar metadata
|
||||
const customUserColor = useOtherUserColor(sender, senderAvatarMxc);
|
||||
|
||||
const memberPowerTag = getMemberPowerTag(sender);
|
||||
const tagColor = memberPowerTag?.color
|
||||
? accessibleTagColors?.get(memberPowerTag.color)
|
||||
@@ -187,7 +191,8 @@ function PinnedMessage({
|
||||
? getPowerTagIconSrc(mx, useAuthentication, memberPowerTag.icon)
|
||||
: undefined;
|
||||
|
||||
const usernameColor = legacyUsernameColor ? colorMXID(sender) : tagColor;
|
||||
// Priority: custom user color > tag color (non-legacy) > colorMXID (legacy)
|
||||
const usernameColor = customUserColor ?? (legacyUsernameColor ? colorMXID(sender) : tagColor);
|
||||
|
||||
return (
|
||||
<ModernLayout
|
||||
|
||||
Reference in New Issue
Block a user