perf(renderer): reuse theme state in activity badges
This commit is contained in:
parent
477d18d798
commit
61919f5aec
3 changed files with 55 additions and 6 deletions
|
|
@ -24,6 +24,8 @@ interface MemberBadgeProps {
|
||||||
color?: string;
|
color?: string;
|
||||||
/** Owning team context for hover-card store lookups. */
|
/** Owning team context for hover-card store lookups. */
|
||||||
teamName?: string;
|
teamName?: string;
|
||||||
|
/** Pre-resolved theme flag from callers that already read theme state. */
|
||||||
|
isLight?: boolean;
|
||||||
/** Avatar + badge size variant */
|
/** Avatar + badge size variant */
|
||||||
size?: 'xs' | 'sm' | 'md';
|
size?: 'xs' | 'sm' | 'md';
|
||||||
/** Pre-resolved avatar URL from a caller that already owns the member roster. */
|
/** 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.
|
* When onClick is provided, both avatar and badge are clickable as one unit.
|
||||||
* Wrapped in MemberHoverCard to show member info on hover.
|
* Wrapped in MemberHoverCard to show member info on hover.
|
||||||
*/
|
*/
|
||||||
export const MemberBadge = memo(
|
type MemberBadgeContentProps = Omit<MemberBadgeProps, 'isLight'> & {
|
||||||
|
isLight: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MemberBadgeContent = memo(
|
||||||
({
|
({
|
||||||
name,
|
name,
|
||||||
color,
|
color,
|
||||||
teamName,
|
teamName,
|
||||||
|
isLight,
|
||||||
size = 'sm',
|
size = 'sm',
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
hideAvatar,
|
hideAvatar,
|
||||||
onClick,
|
onClick,
|
||||||
disableHoverCard,
|
disableHoverCard,
|
||||||
}: MemberBadgeProps): React.JSX.Element => {
|
}: MemberBadgeContentProps): React.JSX.Element => {
|
||||||
const colors = getTeamColorSet(color ?? '');
|
const colors = getTeamColorSet(color ?? '');
|
||||||
const { isLight } = useTheme();
|
|
||||||
const effectiveAvatarTeamName = useStore((s) =>
|
const effectiveAvatarTeamName = useStore((s) =>
|
||||||
hideAvatar || avatarUrl != null ? null : (teamName ?? s.selectedTeamName)
|
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';
|
MemberBadge.displayName = 'MemberBadge';
|
||||||
|
|
|
||||||
|
|
@ -437,6 +437,7 @@ const PassiveIdlePeerSummaryRow = ({
|
||||||
teamName,
|
teamName,
|
||||||
senderName,
|
senderName,
|
||||||
senderColor,
|
senderColor,
|
||||||
|
isLight,
|
||||||
summary,
|
summary,
|
||||||
timestamp,
|
timestamp,
|
||||||
onMemberNameClick,
|
onMemberNameClick,
|
||||||
|
|
@ -444,6 +445,7 @@ const PassiveIdlePeerSummaryRow = ({
|
||||||
teamName: string;
|
teamName: string;
|
||||||
senderName: string;
|
senderName: string;
|
||||||
senderColor?: string;
|
senderColor?: string;
|
||||||
|
isLight: boolean;
|
||||||
summary: string;
|
summary: string;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
onMemberNameClick?: (memberName: string) => void;
|
onMemberNameClick?: (memberName: string) => void;
|
||||||
|
|
@ -460,6 +462,7 @@ const PassiveIdlePeerSummaryRow = ({
|
||||||
name={senderName}
|
name={senderName}
|
||||||
color={senderColor}
|
color={senderColor}
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
|
isLight={isLight}
|
||||||
hideAvatar={false}
|
hideAvatar={false}
|
||||||
onClick={onMemberNameClick}
|
onClick={onMemberNameClick}
|
||||||
/>
|
/>
|
||||||
|
|
@ -491,6 +494,7 @@ const TaskStallRemediationRow = ({
|
||||||
teamName,
|
teamName,
|
||||||
recipientName,
|
recipientName,
|
||||||
recipientColor,
|
recipientColor,
|
||||||
|
isLight,
|
||||||
taskRef,
|
taskRef,
|
||||||
timestamp,
|
timestamp,
|
||||||
onMemberNameClick,
|
onMemberNameClick,
|
||||||
|
|
@ -499,6 +503,7 @@ const TaskStallRemediationRow = ({
|
||||||
teamName: string;
|
teamName: string;
|
||||||
recipientName: string;
|
recipientName: string;
|
||||||
recipientColor?: string;
|
recipientColor?: string;
|
||||||
|
isLight: boolean;
|
||||||
taskRef?: NonNullable<InboxMessage['taskRefs']>[number];
|
taskRef?: NonNullable<InboxMessage['taskRefs']>[number];
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
onMemberNameClick?: (memberName: string) => void;
|
onMemberNameClick?: (memberName: string) => void;
|
||||||
|
|
@ -525,6 +530,7 @@ const TaskStallRemediationRow = ({
|
||||||
name={recipientName}
|
name={recipientName}
|
||||||
color={recipientColor}
|
color={recipientColor}
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
|
isLight={isLight}
|
||||||
hideAvatar
|
hideAvatar
|
||||||
onClick={onMemberNameClick}
|
onClick={onMemberNameClick}
|
||||||
/>
|
/>
|
||||||
|
|
@ -557,6 +563,7 @@ const MemberWorkSyncNudgeRow = ({
|
||||||
teamName,
|
teamName,
|
||||||
recipientName,
|
recipientName,
|
||||||
recipientColor,
|
recipientColor,
|
||||||
|
isLight,
|
||||||
taskRefs,
|
taskRefs,
|
||||||
intent,
|
intent,
|
||||||
timestamp,
|
timestamp,
|
||||||
|
|
@ -566,6 +573,7 @@ const MemberWorkSyncNudgeRow = ({
|
||||||
teamName: string;
|
teamName: string;
|
||||||
recipientName: string;
|
recipientName: string;
|
||||||
recipientColor?: string;
|
recipientColor?: string;
|
||||||
|
isLight: boolean;
|
||||||
taskRefs?: InboxMessage['taskRefs'];
|
taskRefs?: InboxMessage['taskRefs'];
|
||||||
intent?: InboxMessage['workSyncIntent'];
|
intent?: InboxMessage['workSyncIntent'];
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
|
|
@ -599,6 +607,7 @@ const MemberWorkSyncNudgeRow = ({
|
||||||
name={recipientName}
|
name={recipientName}
|
||||||
color={recipientColor}
|
color={recipientColor}
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
|
isLight={isLight}
|
||||||
hideAvatar
|
hideAvatar
|
||||||
onClick={onMemberNameClick}
|
onClick={onMemberNameClick}
|
||||||
/>
|
/>
|
||||||
|
|
@ -636,6 +645,7 @@ const BootstrapSystemRow = ({
|
||||||
runtime,
|
runtime,
|
||||||
senderColor,
|
senderColor,
|
||||||
recipientColor,
|
recipientColor,
|
||||||
|
isLight,
|
||||||
timestamp,
|
timestamp,
|
||||||
onMemberNameClick,
|
onMemberNameClick,
|
||||||
}: {
|
}: {
|
||||||
|
|
@ -646,6 +656,7 @@ const BootstrapSystemRow = ({
|
||||||
runtime?: string;
|
runtime?: string;
|
||||||
senderColor?: string;
|
senderColor?: string;
|
||||||
recipientColor?: string;
|
recipientColor?: string;
|
||||||
|
isLight: boolean;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
onMemberNameClick?: (memberName: string) => void;
|
onMemberNameClick?: (memberName: string) => void;
|
||||||
}): React.JSX.Element => {
|
}): React.JSX.Element => {
|
||||||
|
|
@ -664,6 +675,7 @@ const BootstrapSystemRow = ({
|
||||||
name={senderName}
|
name={senderName}
|
||||||
color={senderColor}
|
color={senderColor}
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
|
isLight={isLight}
|
||||||
hideAvatar
|
hideAvatar
|
||||||
onClick={onMemberNameClick}
|
onClick={onMemberNameClick}
|
||||||
/>
|
/>
|
||||||
|
|
@ -672,6 +684,7 @@ const BootstrapSystemRow = ({
|
||||||
name={recipientName}
|
name={recipientName}
|
||||||
color={recipientColor}
|
color={recipientColor}
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
|
isLight={isLight}
|
||||||
hideAvatar
|
hideAvatar
|
||||||
onClick={onMemberNameClick}
|
onClick={onMemberNameClick}
|
||||||
/>
|
/>
|
||||||
|
|
@ -692,6 +705,7 @@ const BootstrapAcknowledgementRow = ({
|
||||||
recipientName,
|
recipientName,
|
||||||
senderColor,
|
senderColor,
|
||||||
recipientColor,
|
recipientColor,
|
||||||
|
isLight,
|
||||||
timestamp,
|
timestamp,
|
||||||
onMemberNameClick,
|
onMemberNameClick,
|
||||||
}: {
|
}: {
|
||||||
|
|
@ -700,6 +714,7 @@ const BootstrapAcknowledgementRow = ({
|
||||||
recipientName: string;
|
recipientName: string;
|
||||||
senderColor?: string;
|
senderColor?: string;
|
||||||
recipientColor?: string;
|
recipientColor?: string;
|
||||||
|
isLight: boolean;
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
onMemberNameClick?: (memberName: string) => void;
|
onMemberNameClick?: (memberName: string) => void;
|
||||||
}): React.JSX.Element => {
|
}): React.JSX.Element => {
|
||||||
|
|
@ -713,6 +728,7 @@ const BootstrapAcknowledgementRow = ({
|
||||||
name={senderName}
|
name={senderName}
|
||||||
color={senderColor}
|
color={senderColor}
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
|
isLight={isLight}
|
||||||
hideAvatar
|
hideAvatar
|
||||||
onClick={onMemberNameClick}
|
onClick={onMemberNameClick}
|
||||||
/>
|
/>
|
||||||
|
|
@ -721,6 +737,7 @@ const BootstrapAcknowledgementRow = ({
|
||||||
name={recipientName}
|
name={recipientName}
|
||||||
color={recipientColor}
|
color={recipientColor}
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
|
isLight={isLight}
|
||||||
hideAvatar
|
hideAvatar
|
||||||
onClick={onMemberNameClick}
|
onClick={onMemberNameClick}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1206,6 +1223,7 @@ export const ActivityItem = memo(
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
senderName={senderName}
|
senderName={senderName}
|
||||||
senderColor={senderColor}
|
senderColor={senderColor}
|
||||||
|
isLight={isLight}
|
||||||
summary={idleSemantic.peerSummary}
|
summary={idleSemantic.peerSummary}
|
||||||
timestamp={timestamp}
|
timestamp={timestamp}
|
||||||
onMemberNameClick={onMemberNameClick}
|
onMemberNameClick={onMemberNameClick}
|
||||||
|
|
@ -1219,6 +1237,7 @@ export const ActivityItem = memo(
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
recipientName={message.to ?? 'teammate'}
|
recipientName={message.to ?? 'teammate'}
|
||||||
recipientColor={recipientColor}
|
recipientColor={recipientColor}
|
||||||
|
isLight={isLight}
|
||||||
taskRef={message.taskRefs?.[0]}
|
taskRef={message.taskRefs?.[0]}
|
||||||
timestamp={timestamp}
|
timestamp={timestamp}
|
||||||
onMemberNameClick={onMemberNameClick}
|
onMemberNameClick={onMemberNameClick}
|
||||||
|
|
@ -1233,6 +1252,7 @@ export const ActivityItem = memo(
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
recipientName={message.to ?? 'teammate'}
|
recipientName={message.to ?? 'teammate'}
|
||||||
recipientColor={recipientColor}
|
recipientColor={recipientColor}
|
||||||
|
isLight={isLight}
|
||||||
taskRefs={message.taskRefs}
|
taskRefs={message.taskRefs}
|
||||||
intent={message.workSyncIntent}
|
intent={message.workSyncIntent}
|
||||||
timestamp={timestamp}
|
timestamp={timestamp}
|
||||||
|
|
@ -1252,6 +1272,7 @@ export const ActivityItem = memo(
|
||||||
runtime={bootstrapDisplay.runtime}
|
runtime={bootstrapDisplay.runtime}
|
||||||
senderColor={senderColor}
|
senderColor={senderColor}
|
||||||
recipientColor={recipientColor}
|
recipientColor={recipientColor}
|
||||||
|
isLight={isLight}
|
||||||
timestamp={timestamp}
|
timestamp={timestamp}
|
||||||
onMemberNameClick={onMemberNameClick}
|
onMemberNameClick={onMemberNameClick}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1266,6 +1287,7 @@ export const ActivityItem = memo(
|
||||||
recipientName={message.to ?? 'lead'}
|
recipientName={message.to ?? 'lead'}
|
||||||
senderColor={senderColor}
|
senderColor={senderColor}
|
||||||
recipientColor={recipientColor}
|
recipientColor={recipientColor}
|
||||||
|
isLight={isLight}
|
||||||
timestamp={timestamp}
|
timestamp={timestamp}
|
||||||
onMemberNameClick={onMemberNameClick}
|
onMemberNameClick={onMemberNameClick}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1305,6 +1327,7 @@ export const ActivityItem = memo(
|
||||||
name={senderName}
|
name={senderName}
|
||||||
color={senderColor}
|
color={senderColor}
|
||||||
teamName={teamName}
|
teamName={teamName}
|
||||||
|
isLight={isLight}
|
||||||
hideAvatar={senderHideAvatar || compactHeader}
|
hideAvatar={senderHideAvatar || compactHeader}
|
||||||
onClick={onMemberNameClick}
|
onClick={onMemberNameClick}
|
||||||
disableHoverCard={crossTeamOrigin != null}
|
disableHoverCard={crossTeamOrigin != null}
|
||||||
|
|
@ -1383,6 +1406,7 @@ export const ActivityItem = memo(
|
||||||
name={crossTeamSentMemberName ?? qualifiedRecipient?.memberName ?? message.to}
|
name={crossTeamSentMemberName ?? qualifiedRecipient?.memberName ?? message.to}
|
||||||
color={crossTeamTarget ? undefined : recipientColor}
|
color={crossTeamTarget ? undefined : recipientColor}
|
||||||
teamName={crossTeamTarget ? undefined : teamName}
|
teamName={crossTeamTarget ? undefined : teamName}
|
||||||
|
isLight={isLight}
|
||||||
hideAvatar={
|
hideAvatar={
|
||||||
compactHeader ||
|
compactHeader ||
|
||||||
(crossTeamSentMemberName ?? qualifiedRecipient?.memberName ?? message.to) === 'user'
|
(crossTeamSentMemberName ?? qualifiedRecipient?.memberName ?? message.to) === 'user'
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import {
|
||||||
CARD_ICON_MUTED,
|
CARD_ICON_MUTED,
|
||||||
} from '@renderer/constants/cssVariables';
|
} from '@renderer/constants/cssVariables';
|
||||||
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
import { getTeamColorSet } from '@renderer/constants/teamColors';
|
||||||
|
import { useTheme } from '@renderer/hooks/useTheme';
|
||||||
import { agentAvatarUrl } from '@renderer/utils/memberHelpers';
|
import { agentAvatarUrl } from '@renderer/utils/memberHelpers';
|
||||||
import {
|
import {
|
||||||
areStringArraysEqual,
|
areStringArraysEqual,
|
||||||
|
|
@ -567,6 +568,7 @@ const LeadThoughtsGroupRowComponent = ({
|
||||||
expandItemKey,
|
expandItemKey,
|
||||||
}: LeadThoughtsGroupRowProps): React.JSX.Element => {
|
}: LeadThoughtsGroupRowProps): React.JSX.Element => {
|
||||||
const { t } = useAppTranslation('team');
|
const { t } = useAppTranslation('team');
|
||||||
|
const { isLight } = useTheme();
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
const contentRef = useRef<HTMLDivElement>(null);
|
const contentRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
@ -843,7 +845,7 @@ const LeadThoughtsGroupRowComponent = ({
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="flex min-w-0 items-start gap-3">
|
<div className="flex min-w-0 items-start gap-3">
|
||||||
<div className="flex min-w-0 flex-1 items-center gap-2 overflow-hidden">
|
<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 }}>
|
<span className="shrink-0 text-[10px]" style={{ color: CARD_ICON_MUTED }}>
|
||||||
{t('activity.thoughts.count', { count: thoughts.length })}
|
{t('activity.thoughts.count', { count: thoughts.length })}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -916,7 +918,7 @@ const LeadThoughtsGroupRowComponent = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<MemberBadge name={leadName} color={memberColor} hideAvatar />
|
<MemberBadge name={leadName} color={memberColor} isLight={isLight} hideAvatar />
|
||||||
<span className="text-[10px]" style={{ color: CARD_ICON_MUTED }}>
|
<span className="text-[10px]" style={{ color: CARD_ICON_MUTED }}>
|
||||||
{t('activity.thoughts.count', { count: thoughts.length })}
|
{t('activity.thoughts.count', { count: thoughts.length })}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -987,7 +989,7 @@ const LeadThoughtsGroupRowComponent = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<MemberBadge name={leadName} color={memberColor} hideAvatar />
|
<MemberBadge name={leadName} color={memberColor} isLight={isLight} hideAvatar />
|
||||||
<span className="text-[10px]" style={{ color: CARD_ICON_MUTED }}>
|
<span className="text-[10px]" style={{ color: CARD_ICON_MUTED }}>
|
||||||
{t('activity.thoughts.count', { count: thoughts.length })}
|
{t('activity.thoughts.count', { count: thoughts.length })}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue