Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions apps/mobile/src/features/identity/IdentityAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export function IdentityAvatar(props: {
readonly name?: string | null | undefined;
readonly size?: "micro" | "sm" | "md";
readonly className?: string;
readonly highlighted?: boolean;
}) {
const model = identityAvatar({
personId: props.personId,
Expand All @@ -26,10 +27,18 @@ export function IdentityAvatar(props: {
return (
<View
accessibilityLabel={model.label}
className={cn("shrink-0 items-center justify-center rounded-full", box, props.className)}
style={{ backgroundColor: model.backgroundColor }}
className={cn(
"shrink-0 items-center justify-center rounded-full",
box,
props.highlighted && "bg-primary",
props.className,
)}
style={props.highlighted ? undefined : { backgroundColor: model.backgroundColor }}
>
<Text className={cn("font-t3-bold", text)} style={{ color: model.color }}>
<Text
className={cn("font-t3-bold", text, props.highlighted && "text-primary-foreground")}
style={props.highlighted ? undefined : { color: model.color }}
>
{model.initials}
</Text>
</View>
Expand Down
29 changes: 15 additions & 14 deletions apps/mobile/src/features/identity/ParticipantStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,29 @@ export function ParticipantStack(props: {
username={lead.username}
name={lead.name}
size="micro"
highlighted={lead.personId === claimPersonId}
/>
{extras.length > 0 ? (
<View
accessibilityElementsHidden
importantForAccessibility="no-hide-descendants"
className="size-3.5 items-center justify-center rounded-full bg-subtle"
accessibilityElementsHidden={!youParticipated}
importantForAccessibility={youParticipated ? "auto" : "no-hide-descendants"}
accessibilityLabel={youParticipated ? "You participated" : undefined}
className={cn(
"size-3.5 items-center justify-center rounded-full",
youParticipated ? "border border-primary bg-primary/15" : "bg-subtle",
)}
testID={youParticipated ? "you-participated-indicator" : undefined}
>
<Text className="text-[8px] font-t3-medium text-foreground-tertiary">
<Text
className={cn(
"text-[8px] font-t3-medium",
youParticipated ? "text-primary" : "text-foreground-tertiary",
)}
>
+{extras.length}
</Text>
</View>
) : null}
{youParticipated ? (
<View
accessibilityElementsHidden
importantForAccessibility="no-hide-descendants"
className="size-3.5 items-center justify-center rounded-full border border-primary bg-primary/15"
testID="you-participated-indicator"
>
<Text className="text-[9px] font-t3-bold text-primary">✓</Text>
</View>
) : null}
</View>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ describe("mobile participation indicator surface", () => {
const listV2 = readSource("../threads/thread-list-v2-items.tsx");
const board = readSource("../board/BoardScreen.tsx");

expect(stack).toContain('testID="you-participated-indicator"');
expect(stack).toContain('testID={youParticipated ? "you-participated-indicator"');
expect(stack).toContain("+{extras.length}");
expect(stack).toContain('"border border-primary bg-primary/15"');
expect(stack).not.toContain(">✓</Text>");
expect(stack).toContain("highlighted={lead.personId === claimPersonId}");
const avatar = readSource("./IdentityAvatar.tsx");
expect(avatar).toContain('props.highlighted && "bg-primary"');
expect(stack).toContain("isClaimedNonStarterParticipant");
expect(stack).toContain("You participated");
expect(listV1).toContain("environmentId={thread.environmentId}");
Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/components/identity/IdentityAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export function IdentityAvatar(props: {
readonly name?: string | null | undefined;
readonly size?: "micro" | "sm" | "md";
readonly className?: string;
readonly highlighted?: boolean;
/** `null` suppresses the native title when a parent owns richer tooltip content. */
readonly title?: string | null;
}) {
Expand All @@ -33,7 +34,10 @@ export function IdentityAvatar(props: {
sizeClass,
props.className,
)}
style={{ backgroundColor: model.backgroundColor, color: model.color }}
style={{
backgroundColor: props.highlighted ? "var(--primary)" : model.backgroundColor,
color: props.highlighted ? "var(--primary-foreground)" : model.color,
}}
>
{model.initials}
</span>
Expand Down
23 changes: 11 additions & 12 deletions apps/web/src/components/identity/ParticipantStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
isClaimedNonStarterParticipant,
} from "@t3tools/client-runtime/state/identity";
import type { ThreadParticipantSummary } from "@t3tools/contracts";
import { CheckIcon } from "lucide-react";
import { identityClaimPersonIdByEnvironmentAtom } from "../../state/identity";
import { IdentityAvatar } from "./IdentityAvatar";
import { participantDisplayLabel } from "./ParticipantStack.logic";
Expand Down Expand Up @@ -53,24 +52,23 @@ export function ParticipantStack(props: {
name={lead.name}
size="micro"
title={null}
highlighted={lead.personId === claimPersonId}
/>
{extras.length > 0 ? (
<span
className="inline-flex size-3.5 items-center justify-center rounded-full bg-muted text-[8px] font-medium text-muted-foreground"
aria-hidden
className={cn(
"inline-flex size-3.5 items-center justify-center rounded-full text-[8px] font-medium",
youParticipated
? "bg-primary/15 text-primary ring-1 ring-primary/35"
: "bg-muted text-muted-foreground",
)}
aria-hidden={!youParticipated}
aria-label={youParticipated ? "You participated" : undefined}
data-testid={youParticipated ? "you-participated-indicator" : undefined}
>
+{extras.length}
</span>
) : null}
{youParticipated ? (
<span
className="inline-flex size-3.5 items-center justify-center rounded-full bg-primary/15 text-primary ring-1 ring-primary/35"
aria-label="You participated"
data-testid="you-participated-indicator"
>
<CheckIcon className="size-2.5" aria-hidden />
</span>
) : null}
</span>
);

Expand All @@ -91,6 +89,7 @@ export function ParticipantStack(props: {
name={person.name}
size="micro"
title={null}
highlighted={person.personId === claimPersonId}
/>
<span className="truncate text-foreground">
{participantDisplayLabel(person)}
Expand Down
8 changes: 7 additions & 1 deletion apps/web/src/forkSurfaceExistence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,13 @@ describe("fork surface existence (anti stack-drop)", () => {
expect(stack).toContain("<TooltipPopup");
expect(stack).toContain("participantDisplayLabel");
expect(stack).toContain("title={null}");
expect(stack).toContain('data-testid="you-participated-indicator"');
expect(stack).toContain('data-testid={youParticipated ? "you-participated-indicator"');
expect(stack).toContain("+{extras.length}");
expect(stack).toContain('"bg-primary/15 text-primary ring-1 ring-primary/35"');
expect(stack).not.toContain("<CheckIcon");
expect(stack).toContain("highlighted={lead.personId === claimPersonId}");
const avatar = readSrc("components/identity/IdentityAvatar.tsx");
expect(avatar).toContain('props.highlighted ? "var(--primary)"');
expect(stack).toContain("isClaimedNonStarterParticipant");
expect(stack).toContain("· You");
const stackLogic = readSrc("components/identity/ParticipantStack.logic.ts");
Expand Down
Loading