perf(renderer): reuse theme state in activity badges

This commit is contained in:
777genius 2026-05-31 06:31:16 +03:00
parent 477d18d798
commit 61919f5aec
3 changed files with 55 additions and 6 deletions

View file

@ -24,6 +24,8 @@ interface MemberBadgeProps {
color?: string;
/** Owning team context for hover-card store lookups. */
teamName?: string;
/** Pre-resolved theme flag from callers that already read theme state. */
isLight?: boolean;
/** Avatar + badge size variant */
size?: 'xs' | 'sm' | 'md';
/** Pre-resolved avatar URL from a caller that already owns the member roster. */
@ -55,19 +57,23 @@ function getCachedMemberAvatarMap(members: readonly ResolvedTeamMember[]): Map<s
* When onClick is provided, both avatar and badge are clickable as one unit.
* Wrapped in MemberHoverCard to show member info on hover.
*/
export const MemberBadge = memo(
type MemberBadgeContentProps = Omit<MemberBadgeProps, 'isLight'> & {
isLight: boolean;
};
const MemberBadgeContent = memo(
({
name,
color,
teamName,
isLight,
size = 'sm',
avatarUrl,
hideAvatar,
onClick,
disableHoverCard,
}: MemberBadgeProps): React.JSX.Element => {
}: MemberBadgeContentProps): React.JSX.Element => {
const colors = getTeamColorSet(color ?? '');
const { isLight } = useTheme();
const effectiveAvatarTeamName = useStore((s) =>
hideAvatar || avatarUrl != null ? null : (teamName ?? s.selectedTeamName)
);
@ -140,4 +146,21 @@ export const MemberBadge = memo(
}
);
MemberBadgeContent.displayName = 'MemberBadgeContent';
const ThemedMemberBadge = memo(function ThemedMemberBadge({
isLight: _isLight,
...props
}: MemberBadgeProps): React.JSX.Element {
const { isLight } = useTheme();
return <MemberBadgeContent {...props} isLight={isLight} />;
});
export const MemberBadge = memo(function MemberBadge(props: MemberBadgeProps): React.JSX.Element {
if (typeof props.isLight === 'boolean') {
return <MemberBadgeContent {...props} isLight={props.isLight} />;
}
return <ThemedMemberBadge {...props} />;
});
MemberBadge.displayName = 'MemberBadge';

View file

@ -437,6 +437,7 @@ const PassiveIdlePeerSummaryRow = ({
teamName,
senderName,
senderColor,
isLight,
summary,
timestamp,
onMemberNameClick,
@ -444,6 +445,7 @@ const PassiveIdlePeerSummaryRow = ({
teamName: string;
senderName: string;
senderColor?: string;
isLight: boolean;
summary: string;
timestamp: string;
onMemberNameClick?: (memberName: string) => void;
@ -460,6 +462,7 @@ const PassiveIdlePeerSummaryRow = ({
name={senderName}
color={senderColor}
teamName={teamName}
isLight={isLight}
hideAvatar={false}
onClick={onMemberNameClick}
/>
@ -491,6 +494,7 @@ const TaskStallRemediationRow = ({
teamName,
recipientName,
recipientColor,
isLight,
taskRef,
timestamp,
onMemberNameClick,
@ -499,6 +503,7 @@ const TaskStallRemediationRow = ({
teamName: string;
recipientName: string;
recipientColor?: string;
isLight: boolean;
taskRef?: NonNullable<InboxMessage['taskRefs']>[number];
timestamp: string;
onMemberNameClick?: (memberName: string) => void;
@ -525,6 +530,7 @@ const TaskStallRemediationRow = ({
name={recipientName}
color={recipientColor}
teamName={teamName}
isLight={isLight}
hideAvatar
onClick={onMemberNameClick}
/>
@ -557,6 +563,7 @@ const MemberWorkSyncNudgeRow = ({
teamName,
recipientName,
recipientColor,
isLight,
taskRefs,
intent,
timestamp,
@ -566,6 +573,7 @@ const MemberWorkSyncNudgeRow = ({
teamName: string;
recipientName: string;
recipientColor?: string;
isLight: boolean;
taskRefs?: InboxMessage['taskRefs'];
intent?: InboxMessage['workSyncIntent'];
timestamp: string;
@ -599,6 +607,7 @@ const MemberWorkSyncNudgeRow = ({
name={recipientName}
color={recipientColor}
teamName={teamName}
isLight={isLight}
hideAvatar
onClick={onMemberNameClick}
/>
@ -636,6 +645,7 @@ const BootstrapSystemRow = ({
runtime,
senderColor,
recipientColor,
isLight,
timestamp,
onMemberNameClick,
}: {
@ -646,6 +656,7 @@ const BootstrapSystemRow = ({
runtime?: string;
senderColor?: string;
recipientColor?: string;
isLight: boolean;
timestamp: string;
onMemberNameClick?: (memberName: string) => void;
}): React.JSX.Element => {
@ -664,6 +675,7 @@ const BootstrapSystemRow = ({
name={senderName}
color={senderColor}
teamName={teamName}
isLight={isLight}
hideAvatar
onClick={onMemberNameClick}
/>
@ -672,6 +684,7 @@ const BootstrapSystemRow = ({
name={recipientName}
color={recipientColor}
teamName={teamName}
isLight={isLight}
hideAvatar
onClick={onMemberNameClick}
/>
@ -692,6 +705,7 @@ const BootstrapAcknowledgementRow = ({
recipientName,
senderColor,
recipientColor,
isLight,
timestamp,
onMemberNameClick,
}: {
@ -700,6 +714,7 @@ const BootstrapAcknowledgementRow = ({
recipientName: string;
senderColor?: string;
recipientColor?: string;
isLight: boolean;
timestamp: string;
onMemberNameClick?: (memberName: string) => void;
}): React.JSX.Element => {
@ -713,6 +728,7 @@ const BootstrapAcknowledgementRow = ({
name={senderName}
color={senderColor}
teamName={teamName}
isLight={isLight}
hideAvatar
onClick={onMemberNameClick}
/>
@ -721,6 +737,7 @@ const BootstrapAcknowledgementRow = ({
name={recipientName}
color={recipientColor}
teamName={teamName}
isLight={isLight}
hideAvatar
onClick={onMemberNameClick}
/>
@ -1206,6 +1223,7 @@ export const ActivityItem = memo(
teamName={teamName}
senderName={senderName}
senderColor={senderColor}
isLight={isLight}
summary={idleSemantic.peerSummary}
timestamp={timestamp}
onMemberNameClick={onMemberNameClick}
@ -1219,6 +1237,7 @@ export const ActivityItem = memo(
teamName={teamName}
recipientName={message.to ?? 'teammate'}
recipientColor={recipientColor}
isLight={isLight}
taskRef={message.taskRefs?.[0]}
timestamp={timestamp}
onMemberNameClick={onMemberNameClick}
@ -1233,6 +1252,7 @@ export const ActivityItem = memo(
teamName={teamName}
recipientName={message.to ?? 'teammate'}
recipientColor={recipientColor}
isLight={isLight}
taskRefs={message.taskRefs}
intent={message.workSyncIntent}
timestamp={timestamp}
@ -1252,6 +1272,7 @@ export const ActivityItem = memo(
runtime={bootstrapDisplay.runtime}
senderColor={senderColor}
recipientColor={recipientColor}
isLight={isLight}
timestamp={timestamp}
onMemberNameClick={onMemberNameClick}
/>
@ -1266,6 +1287,7 @@ export const ActivityItem = memo(
recipientName={message.to ?? 'lead'}
senderColor={senderColor}
recipientColor={recipientColor}
isLight={isLight}
timestamp={timestamp}
onMemberNameClick={onMemberNameClick}
/>
@ -1305,6 +1327,7 @@ export const ActivityItem = memo(
name={senderName}
color={senderColor}
teamName={teamName}
isLight={isLight}
hideAvatar={senderHideAvatar || compactHeader}
onClick={onMemberNameClick}
disableHoverCard={crossTeamOrigin != null}
@ -1383,6 +1406,7 @@ export const ActivityItem = memo(
name={crossTeamSentMemberName ?? qualifiedRecipient?.memberName ?? message.to}
color={crossTeamTarget ? undefined : recipientColor}
teamName={crossTeamTarget ? undefined : teamName}
isLight={isLight}
hideAvatar={
compactHeader ||
(crossTeamSentMemberName ?? qualifiedRecipient?.memberName ?? message.to) === 'user'

View file

@ -21,6 +21,7 @@ import {
CARD_ICON_MUTED,
} from '@renderer/constants/cssVariables';
import { getTeamColorSet } from '@renderer/constants/teamColors';
import { useTheme } from '@renderer/hooks/useTheme';
import { agentAvatarUrl } from '@renderer/utils/memberHelpers';
import {
areStringArraysEqual,
@ -567,6 +568,7 @@ const LeadThoughtsGroupRowComponent = ({
expandItemKey,
}: LeadThoughtsGroupRowProps): React.JSX.Element => {
const { t } = useAppTranslation('team');
const { isLight } = useTheme();
const ref = useRef<HTMLDivElement>(null);
const scrollRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
@ -843,7 +845,7 @@ const LeadThoughtsGroupRowComponent = ({
<div className="min-w-0">
<div className="flex min-w-0 items-start gap-3">
<div className="flex min-w-0 flex-1 items-center gap-2 overflow-hidden">
<MemberBadge name={leadName} color={memberColor} hideAvatar />
<MemberBadge name={leadName} color={memberColor} isLight={isLight} hideAvatar />
<span className="shrink-0 text-[10px]" style={{ color: CARD_ICON_MUTED }}>
{t('activity.thoughts.count', { count: thoughts.length })}
</span>
@ -916,7 +918,7 @@ const LeadThoughtsGroupRowComponent = ({
/>
</div>
) : null}
<MemberBadge name={leadName} color={memberColor} hideAvatar />
<MemberBadge name={leadName} color={memberColor} isLight={isLight} hideAvatar />
<span className="text-[10px]" style={{ color: CARD_ICON_MUTED }}>
{t('activity.thoughts.count', { count: thoughts.length })}
</span>
@ -987,7 +989,7 @@ const LeadThoughtsGroupRowComponent = ({
/>
</div>
) : null}
<MemberBadge name={leadName} color={memberColor} hideAvatar />
<MemberBadge name={leadName} color={memberColor} isLight={isLight} hideAvatar />
<span className="text-[10px]" style={{ color: CARD_ICON_MUTED }}>
{t('activity.thoughts.count', { count: thoughts.length })}
</span>