authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-07-09 07:00:51-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-09 14:00:51+00:00
logf61fa9499d96fee825492aba204593c37b27e0cb
treee81d10ce7ecce748dc0793dfecc17bdf23054732
parent8619ef22e9a173678b1ed2d00eae99b329a7c6b6
signature Signed by PGP key B5690EEEBB952194

Expose mobile PR indicator labels to accessibility (#3828)

Co-authored-by: codex <codex@users.noreply.github.com>

3 files changed, 9 insertions(+), 20 deletions(-)

apps/mobile/src/features/threads/thread-list-items.tsx+4-15
...@@ -441,10 +441,8 @@ export const ThreadListRow = memo(function ThreadListRow(props: {...@@ -441,10 +441,8 @@ export const ThreadListRow = memo(function ThreadListRow(props: {
441 const iconSubtleColor = useThemeColor("--color-icon-subtle");441 const iconSubtleColor = useThemeColor("--color-icon-subtle");
442 const screenColor = useThemeColor("--color-screen");442 const screenColor = useThemeColor("--color-screen");
443 const drawerColor = useThemeColor("--color-drawer");443 const drawerColor = useThemeColor("--color-drawer");
444 const mutedColor = useThemeColor("--color-foreground-muted");
445 const pressedBackgroundColor = useThemeColor("--color-subtle");444 const pressedBackgroundColor = useThemeColor("--color-subtle");
446 const selectedBackgroundColor = useThemeColor("--color-user-bubble");445 const selectedBackgroundColor = useThemeColor("--color-user-bubble");
447 const selectedMutedColor = useThemeColor("--color-user-bubble-foreground-muted");
448446
449 const { thread, onSelectThread, onArchiveThread, onDeleteThread } = props;447 const { thread, onSelectThread, onArchiveThread, onDeleteThread } = props;
450 const status = resolveThreadStatus(thread);448 const status = resolveThreadStatus(thread);
...@@ -452,12 +450,12 @@ export const ThreadListRow = memo(function ThreadListRow(props: {...@@ -452,12 +450,12 @@ export const ThreadListRow = memo(function ThreadListRow(props: {
452 const timestamp = relativeTime(450 const timestamp = relativeTime(
453 thread.latestUserMessageAt ?? thread.updatedAt ?? thread.createdAt,451 thread.latestUserMessageAt ?? thread.updatedAt ?? thread.createdAt,
454 );452 );
453 const threadAccessibilityLabel = pr ? `${thread.title}, ${pr.accessibilityLabel}` : thread.title;
455 const subtitleParts = [props.environmentLabel, thread.branch].filter((part): part is string =>454 const subtitleParts = [props.environmentLabel, thread.branch].filter((part): part is string =>
456 Boolean(part),455 Boolean(part),
457 );456 );
458457
459 const backgroundColor = compact ? screenColor : drawerColor;458 const backgroundColor = compact ? screenColor : drawerColor;
460 const effectiveMuted = selected ? selectedMutedColor : mutedColor;
461 const effectivePressedBackground = selected ? "rgba(255,255,255,0.16)" : pressedBackgroundColor;459 const effectivePressedBackground = selected ? "rgba(255,255,255,0.16)" : pressedBackgroundColor;
462 const effectiveStatus =460 const effectiveStatus =
463 selected && status461 selected && status
...@@ -496,12 +494,6 @@ export const ThreadListRow = memo(function ThreadListRow(props: {...@@ -496,12 +494,6 @@ export const ThreadListRow = memo(function ThreadListRow(props: {
496 <View className="mt-px flex-row items-center gap-1.5">494 <View className="mt-px flex-row items-center gap-1.5">
497 {subtitleParts.length > 0 ? (495 {subtitleParts.length > 0 ? (
498 <>496 <>
499 <SymbolView
500 name="arrow.triangle.branch"
501 size={10}
502 tintColor={compact ? iconSubtleColor : effectiveMuted}
503 type="monochrome"
504 />
505 <Text497 <Text
506 className={cn(498 className={cn(
507 "shrink",499 "shrink",
...@@ -516,10 +508,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: {...@@ -516,10 +508,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: {
516 </>508 </>
517 ) : null}509 ) : null}
518 {pr !== null ? (510 {pr !== null ? (
519 <View511 <View className="flex-row items-center gap-0.5">
520 accessibilityLabel={pr.accessibilityLabel}
521 className="flex-row items-center gap-0.5"
522 >
523 <PullRequestIcon512 <PullRequestIcon
524 size={compact ? 13 : 11}513 size={compact ? 13 : 11}
525 color={selected ? "#ffffff" : pullRequestTintColor(pr.state, colorScheme)}514 color={selected ? "#ffffff" : pullRequestTintColor(pr.state, colorScheme)}
...@@ -540,7 +529,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: {...@@ -540,7 +529,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: {
540 compact ? (529 compact ? (
541 <Pressable530 <Pressable
542 accessibilityHint="Swipe left for archive and delete actions"531 accessibilityHint="Swipe left for archive and delete actions"
543 accessibilityLabel={thread.title}532 accessibilityLabel={threadAccessibilityLabel}
544 accessibilityRole="button"533 accessibilityRole="button"
545 className="bg-screen"534 className="bg-screen"
546 onPress={() => {535 onPress={() => {
...@@ -586,7 +575,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: {...@@ -586,7 +575,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: {
586 ) : (575 ) : (
587 <Pressable576 <Pressable
588 accessibilityHint="Opens the thread"577 accessibilityHint="Opens the thread"
589 accessibilityLabel={thread.title}578 accessibilityLabel={threadAccessibilityLabel}
590 accessibilityRole="button"579 accessibilityRole="button"
591 accessibilityState={{ selected }}580 accessibilityState={{ selected }}
592 onHoverIn={() => setHovered(true)}581 onHoverIn={() => setHovered(true)}
apps/mobile/src/state/thread-pr-presentation.ts+2-2
...@@ -7,7 +7,7 @@ export interface ThreadPrPresentation {...@@ -7,7 +7,7 @@ export interface ThreadPrPresentation {
7 readonly number: number;7 readonly number: number;
8 readonly state: ThreadPr["state"];8 readonly state: ThreadPr["state"];
9 readonly url: string;9 readonly url: string;
10 /** Compact desktop-style label, e.g. "#3774". */10 /** Compact pull request number label, e.g. "3774". */
11 readonly label: string;11 readonly label: string;
12 /** Full, provider-aware label for assistive technologies. */12 /** Full, provider-aware label for assistive technologies. */
13 readonly accessibilityLabel: string;13 readonly accessibilityLabel: string;
...@@ -29,7 +29,7 @@ export function presentThreadPr(...@@ -29,7 +29,7 @@ export function presentThreadPr(
29 number: pr.number,29 number: pr.number,
30 state: pr.state,30 state: pr.state,
31 url: pr.url,31 url: pr.url,
32 label: `#${pr.number}`,32 label: String(pr.number),
33 accessibilityLabel: `#${pr.number} ${presentation.longName} ${pr.state}`,33 accessibilityLabel: `#${pr.number} ${presentation.longName} ${pr.state}`,
34 textClassName: PR_STATE_TEXT_CLASS[pr.state],34 textClassName: PR_STATE_TEXT_CLASS[pr.state],
35 };35 };
apps/mobile/src/state/use-thread-pr.test.ts+3-3
...@@ -13,9 +13,9 @@ const pullRequest: NonNullable<VcsStatusResult["pr"]> = {...@@ -13,9 +13,9 @@ const pullRequest: NonNullable<VcsStatusResult["pr"]> = {
13};13};
1414
15describe("presentThreadPr", () => {15describe("presentThreadPr", () => {
16 it("uses the compact desktop-style pull request number label", () => {16 it("uses the compact pull request number label without a hash prefix", () => {
17 expect(presentThreadPr(pullRequest, undefined)).toMatchObject({17 expect(presentThreadPr(pullRequest, undefined)).toMatchObject({
18 label: "#3774",18 label: "3774",
19 accessibilityLabel: "#3774 pull request merged",19 accessibilityLabel: "#3774 pull request merged",
20 textClassName: "text-violet-600 dark:text-violet-400",20 textClassName: "text-violet-600 dark:text-violet-400",
21 });21 });
...@@ -29,7 +29,7 @@ describe("presentThreadPr", () => {...@@ -29,7 +29,7 @@ describe("presentThreadPr", () => {
29 baseUrl: "https://gitlab.com",29 baseUrl: "https://gitlab.com",
30 }),30 }),
31 ).toMatchObject({31 ).toMatchObject({
32 label: "#3774",32 label: "3774",
33 accessibilityLabel: "#3774 merge request merged",33 accessibilityLabel: "#3774 merge request merged",
34 });34 });
35 });35 });