Files
orca/mobile/src/session/MobileDiffCommentLineRow.tsx
T
Kelvin Amoaba f87a746200 feat(mobile): light and dark themes with desktop's palettes (#34217)
* feat(mobile): light and dark themes with desktop's palettes

Settings → Appearance offers System, Light and Dark. The shell passes the
resolved scheme to over-the-air pages in its init frame.

Fixes #22184

* fix(mobile): draw your chat messages in desktop's user bubble

Desktop uses a faint surface with a hairline border in both themes; the inverted fill went black in light.

* test(mobile): re-measure the page script sweep after the shared theme hooks

Every route now imports the theme hooks while the entry does not, so they emit one shared script (96 for the tree). Re-swept every route prefix; the worst route step is now 15.

* test(mobile): read themed stylesheets in the text-input font-size census

The census only unwrapped StyleSheet.create({...}); inputs styled through themedStyleSheet(() => ({...})) came back unread.
2026-10-27 17:35:31 +00:00

150 lines
5.3 KiB
TypeScript

import { Pressable, Text, TextInput, View } from 'react-native'
import { MessageSquare, Plus, X } from 'lucide-react-native'
import { MobileSyntaxSegments } from '../components/MobileSyntaxSegments'
import { colors } from '../theme/mobile-theme'
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
import { styles } from './mobile-session-styles'
import type { DiffComment } from '../../../src/shared/diff-comment-types'
import type { RenderableDiffLine } from './mobile-session-route-types'
export function MobileDiffCommentLineRow({
line,
title,
index,
comments,
activeCommentLine,
commentDraft,
commentsBusy,
onStartComment,
onCancelComment,
onDraftChange,
onSubmitComment,
onDeleteComment
}: {
line: RenderableDiffLine
title: string
index: number
comments: DiffComment[]
activeCommentLine: number | null
commentDraft: string
commentsBusy: boolean
onStartComment: (lineNumber: number) => void
onCancelComment: () => void
onDraftChange: (value: string) => void
onSubmitComment: (lineNumber: number) => void
onDeleteComment: (commentId: string) => void
}) {
useMobileColorScheme()
const commentLine = line.newLineNumber
const isCommenting = commentLine !== undefined && activeCommentLine === commentLine
const canComment = commentLine !== undefined
// Why: review notes anchor to the modified side, so show that line number in the single mobile gutter.
const gutterLineNumber = line.newLineNumber ?? line.oldLineNumber ?? ''
return (
<View style={styles.diffLineBlock}>
<View
style={[
styles.diffLine,
line.kind === 'add' && styles.diffLineAdded,
line.kind === 'delete' && styles.diffLineDeleted
]}
>
<Text style={styles.diffGutter}>{gutterLineNumber}</Text>
<Text
selectable
style={styles.diffText}
accessibilityLabel={`${title} diff line ${index + 1}`}
>
<Text
style={[
styles.diffPrefix,
line.kind === 'add' && styles.diffPrefixAdded,
line.kind === 'delete' && styles.diffPrefixDeleted
]}
>
{line.kind === 'add' ? '+ ' : line.kind === 'delete' ? '- ' : ' '}
</Text>
<MobileSyntaxSegments segments={line.segments} />
</Text>
{canComment ? (
<Pressable
style={({ pressed }) => [
styles.diffCommentAddButton,
pressed && styles.diffCommentAddButtonPressed,
commentsBusy && styles.diffCommentButtonDisabled
]}
disabled={commentsBusy}
onPress={() => {
if (commentLine !== undefined) {
onStartComment(commentLine)
}
}}
accessibilityLabel={`Add note on line ${commentLine}`}
>
<Plus size={12} color={colors.textSecondary} strokeWidth={2.3} />
</Pressable>
) : null}
</View>
{comments.length > 0 ? (
<View style={styles.diffCommentList}>
{comments.map((comment) => (
<View key={comment.id} style={styles.diffCommentCard}>
<View style={styles.diffCommentHeader}>
<MessageSquare size={12} color={colors.textMuted} strokeWidth={2.2} />
<Text style={styles.diffCommentMeta}>Line {comment.lineNumber}</Text>
<Pressable
style={styles.diffCommentDeleteButton}
disabled={commentsBusy}
onPress={() => onDeleteComment(comment.id)}
accessibilityLabel={`Delete note on line ${comment.lineNumber}`}
>
<X size={12} color={colors.textMuted} strokeWidth={2.2} />
</Pressable>
</View>
<Text style={styles.diffCommentBody}>{comment.body}</Text>
</View>
))}
</View>
) : null}
{isCommenting ? (
<View style={styles.diffCommentComposer}>
<TextInput
style={[styles.textInput, styles.diffCommentInput]}
value={commentDraft}
onChangeText={onDraftChange}
placeholder="Add review note"
placeholderTextColor={colors.textMuted}
editable={!commentsBusy}
multiline
textAlignVertical="top"
autoFocus
/>
<View style={styles.diffCommentComposerActions}>
<Pressable
style={styles.diffCommentSecondaryAction}
disabled={commentsBusy}
onPress={onCancelComment}
>
<Text style={styles.diffCommentSecondaryText}>Cancel</Text>
</Pressable>
<Pressable
style={[
styles.diffCommentPrimaryAction,
(!commentDraft.trim() || commentsBusy) && styles.diffCommentButtonDisabled
]}
disabled={!commentDraft.trim() || commentsBusy}
onPress={() => {
if (commentLine !== undefined) {
onSubmitComment(commentLine)
}
}}
>
<Text style={styles.diffCommentPrimaryText}>Save note</Text>
</Pressable>
</View>
</View>
) : null}
</View>
)
}