mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
chore(lint): no-array-index-key in app
This commit is contained in:
@@ -95,7 +95,7 @@ export function CalloutCard({
|
||||
<View style={styles.actionRow} testID={testID ? `${testID}-actions` : undefined}>
|
||||
{visibleActions.map((action, index) => (
|
||||
<CalloutActionButton
|
||||
key={`${action.label}-${index}`}
|
||||
key={action.label}
|
||||
action={action}
|
||||
testID={action.testID ?? (testID ? `${testID}-action-${index}` : undefined)}
|
||||
/>
|
||||
|
||||
@@ -1156,7 +1156,7 @@ export function Composer({
|
||||
if (attachment.kind === "image") {
|
||||
return (
|
||||
<ImageAttachmentPill
|
||||
key={`${attachment.metadata.id}-${index}`}
|
||||
key={attachment.metadata.id}
|
||||
attachment={attachment}
|
||||
index={index}
|
||||
disabled={isComposerLocked}
|
||||
|
||||
@@ -46,8 +46,12 @@ function DiffLineRow({ line }: { line: DiffLine }) {
|
||||
<Text style={line.type === "add" ? styles.addText : styles.removeText}>
|
||||
{line.content[0]}
|
||||
</Text>
|
||||
{line.segments.map((segment, segIdx) => (
|
||||
<DiffSegment key={segIdx} segment={segment} lineType={line.type} />
|
||||
{line.segments.map((segment) => (
|
||||
<DiffSegment
|
||||
key={`${segment.changed ? "c" : "u"}:${segment.text}`}
|
||||
segment={segment}
|
||||
lineType={line.type}
|
||||
/>
|
||||
))}
|
||||
</Text>
|
||||
) : (
|
||||
@@ -101,6 +105,10 @@ export function DiffViewer({
|
||||
() => [styles.linesContainer, scrollViewWidth > 0 && { minWidth: scrollViewWidth }],
|
||||
[scrollViewWidth],
|
||||
);
|
||||
const keyedDiffLines = React.useMemo(
|
||||
() => diffLines.map((line, index) => ({ key: `${index}-${line.type}-${line.content}`, line })),
|
||||
[diffLines],
|
||||
);
|
||||
|
||||
if (!diffLines.length) {
|
||||
return (
|
||||
@@ -126,8 +134,8 @@ export function DiffViewer({
|
||||
onLayout={handleInnerLayout}
|
||||
>
|
||||
<View style={linesContainerStyle}>
|
||||
{diffLines.map((line, index) => (
|
||||
<DiffLineRow key={`${line.type}-${index}`} line={line} />
|
||||
{keyedDiffLines.map(({ key, line }) => (
|
||||
<DiffLineRow key={key} line={line} />
|
||||
))}
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
@@ -105,15 +105,19 @@ const CodeLine = React.memo(function CodeLine({
|
||||
() => [codeLineStyles.gutterText, { color: baseColor }],
|
||||
[baseColor],
|
||||
);
|
||||
const keyedTokens = useMemo(
|
||||
() => tokens.map((token, index) => ({ key: `${index}-${token.text}`, token })),
|
||||
[tokens],
|
||||
);
|
||||
return (
|
||||
<View style={codeLineStyles.line}>
|
||||
<View style={gutterStyle}>
|
||||
<Text style={gutterTextStyle}>{String(lineNumber)}</Text>
|
||||
</View>
|
||||
<Text selectable style={codeLineStyles.lineText}>
|
||||
{tokens.map((token, index) => (
|
||||
{keyedTokens.map(({ key, token }) => (
|
||||
<CodeLineToken
|
||||
key={index}
|
||||
key={key}
|
||||
color={token.style ? (colorMap[token.style] ?? baseColor) : baseColor}
|
||||
text={token.text}
|
||||
/>
|
||||
@@ -238,13 +242,18 @@ function FilePreviewBody({
|
||||
}
|
||||
|
||||
const lines = highlightedLines ?? [[{ text: preview.content ?? "", style: null }]];
|
||||
const keyedLines = lines.map((tokens, index) => ({
|
||||
key: `line-${index}`,
|
||||
tokens,
|
||||
lineNumber: index + 1,
|
||||
}));
|
||||
const codeLines = (
|
||||
<View>
|
||||
{lines.map((tokens, index) => (
|
||||
{keyedLines.map(({ key, tokens, lineNumber }) => (
|
||||
<CodeLine
|
||||
key={index}
|
||||
key={key}
|
||||
tokens={tokens}
|
||||
lineNumber={index + 1}
|
||||
lineNumber={lineNumber}
|
||||
gutterWidth={gutterWidth}
|
||||
colorMap={colorMap}
|
||||
baseColor={baseColor}
|
||||
|
||||
@@ -162,11 +162,16 @@ function HighlightedText({ tokens, wrapLines = false }: HighlightedTextProps) {
|
||||
[lineHeight, wrapLines],
|
||||
);
|
||||
|
||||
const keyedTokens = useMemo(
|
||||
() => tokens.map((token, index) => ({ key: `${index}-${token.text}`, token })),
|
||||
[tokens],
|
||||
);
|
||||
|
||||
return (
|
||||
<Text style={containerStyle}>
|
||||
{tokens.map((token, index) => (
|
||||
{keyedTokens.map(({ key, token }) => (
|
||||
<HighlightedToken
|
||||
key={index}
|
||||
key={key}
|
||||
text={token.text}
|
||||
color={getTokenColor(token.style)}
|
||||
lineHeight={lineHeight}
|
||||
@@ -422,21 +427,23 @@ function SplitDiffColumn({
|
||||
[scrollWidth],
|
||||
);
|
||||
|
||||
const keyedRows = useMemo(() => rows.map((row, i) => ({ key: `row-${i}`, row })), [rows]);
|
||||
|
||||
if (wrapLines) {
|
||||
return (
|
||||
<View style={wrapCellStyle}>
|
||||
<View style={styles.linesContainer}>
|
||||
{rows.map((row, i) => {
|
||||
{keyedRows.map(({ key, row }) => {
|
||||
if (row.kind === "header") {
|
||||
return (
|
||||
<View key={`header-${i}`} style={styles.splitHeaderRow}>
|
||||
<View key={key} style={styles.splitHeaderRow}>
|
||||
<Text style={HEADER_LINE_TEXT_STYLE}>{row.content}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<SplitDiffLine
|
||||
key={`line-${i}`}
|
||||
key={key}
|
||||
line={side === "left" ? row.left : row.right}
|
||||
gutterWidth={gutterWidth}
|
||||
wrapLines={wrapLines}
|
||||
@@ -451,21 +458,16 @@ function SplitDiffColumn({
|
||||
return (
|
||||
<View style={rowCellStyle}>
|
||||
<View style={styles.gutterColumn}>
|
||||
{rows.map((row, i) => {
|
||||
{keyedRows.map(({ key, row }) => {
|
||||
if (row.kind === "header") {
|
||||
return (
|
||||
<DiffGutterCell
|
||||
key={`g-${i}`}
|
||||
lineNumber={null}
|
||||
type="header"
|
||||
gutterWidth={gutterWidth}
|
||||
/>
|
||||
<DiffGutterCell key={key} lineNumber={null} type="header" gutterWidth={gutterWidth} />
|
||||
);
|
||||
}
|
||||
const line = side === "left" ? row.left : row.right;
|
||||
return (
|
||||
<DiffGutterCell
|
||||
key={`g-${i}`}
|
||||
key={key}
|
||||
lineNumber={line?.lineNumber ?? null}
|
||||
type={line?.type}
|
||||
gutterWidth={gutterWidth}
|
||||
@@ -480,17 +482,17 @@ function SplitDiffColumn({
|
||||
contentContainerStyle={styles.diffContentInner}
|
||||
>
|
||||
<View style={linesContainerRowStyle}>
|
||||
{rows.map((row, i) => {
|
||||
{keyedRows.map(({ key, row }) => {
|
||||
if (row.kind === "header") {
|
||||
return (
|
||||
<View key={`t-${i}`} style={styles.splitHeaderRow}>
|
||||
<View key={key} style={styles.splitHeaderRow}>
|
||||
<Text style={HEADER_LINE_TEXT_STYLE}>{row.content}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<SplitTextLine
|
||||
key={`t-${i}`}
|
||||
key={key}
|
||||
line={side === "left" ? row.left : row.right}
|
||||
wrapLines={false}
|
||||
/>
|
||||
|
||||
@@ -421,8 +421,8 @@ export const UserMessage = memo(function UserMessage({
|
||||
<View style={userMessageStylesheet.bubble}>
|
||||
{hasImages ? (
|
||||
<View style={imagePreviewContainerStyle}>
|
||||
{images.map((image, index) => (
|
||||
<View key={`${image.id}-${index}`} style={userMessageStylesheet.imagePill}>
|
||||
{images.map((image) => (
|
||||
<View key={image.id} style={userMessageStylesheet.imagePill}>
|
||||
<UserMessageAttachmentThumbnail image={image} />
|
||||
</View>
|
||||
))}
|
||||
@@ -1416,6 +1416,10 @@ export const AssistantMessage = memo(function AssistantMessage({
|
||||
}, [client, handleLinkPress, markdownParser, onInlinePathPress, serverId, workspaceRoot]);
|
||||
|
||||
const blocks = useMemo(() => splitMarkdownBlocks(message), [message]);
|
||||
const keyedBlocks = useMemo(
|
||||
() => blocks.map((block, index) => ({ key: `${index}:${block.slice(0, 32)}`, block })),
|
||||
[blocks],
|
||||
);
|
||||
|
||||
const assistantContainerStyle = useMemo(
|
||||
() => [
|
||||
@@ -1431,10 +1435,10 @@ export const AssistantMessage = memo(function AssistantMessage({
|
||||
|
||||
return (
|
||||
<View testID="assistant-message" style={assistantContainerStyle}>
|
||||
{blocks.map((block, index) => (
|
||||
{keyedBlocks.map(({ key, block }, index) => (
|
||||
<AssistantMessageBlockContainer
|
||||
key={index}
|
||||
marginBottom={index < blocks.length - 1 ? theme.spacing[3] : 0}
|
||||
key={key}
|
||||
marginBottom={index < keyedBlocks.length - 1 ? theme.spacing[3] : 0}
|
||||
>
|
||||
<MemoizedMarkdownBlock
|
||||
text={block}
|
||||
@@ -1844,12 +1848,8 @@ export const TodoListCard = memo(function TodoListCard({
|
||||
{items.length === 0 ? (
|
||||
<Text style={todoListCardStylesheet.emptyText}>No tasks yet.</Text>
|
||||
) : (
|
||||
items.map((item, idx) => (
|
||||
<TodoListItemRow
|
||||
key={`${item.text}-${idx}`}
|
||||
text={item.text}
|
||||
completed={item.completed}
|
||||
/>
|
||||
items.map((item) => (
|
||||
<TodoListItemRow key={item.text} text={item.text} completed={item.completed} />
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
|
||||
@@ -67,6 +67,10 @@ export function PrPane({ data }: { data: PrPaneData }) {
|
||||
const StateIcon = getStateIcon(data.state);
|
||||
const stateLabel = getStateLabel(data.state);
|
||||
const stateLabelStyle = useMemo(() => [styles.stateLabel, { color: stateColor }], [stateColor]);
|
||||
const keyedActivity = useMemo(
|
||||
() => data.activity.map((item, idx) => ({ key: `${item.author}-${item.kind}-${idx}`, item })),
|
||||
[data.activity],
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.root}>
|
||||
@@ -116,8 +120,8 @@ export function PrPane({ data }: { data: PrPaneData }) {
|
||||
</>
|
||||
}
|
||||
>
|
||||
{data.checks.map((check, idx) => (
|
||||
<CheckRow key={`${check.name}-${idx}`} check={check} />
|
||||
{data.checks.map((check) => (
|
||||
<CheckRow key={check.name} check={check} />
|
||||
))}
|
||||
</Section>
|
||||
|
||||
@@ -147,8 +151,8 @@ export function PrPane({ data }: { data: PrPaneData }) {
|
||||
</>
|
||||
}
|
||||
>
|
||||
{data.activity.map((item, idx) => (
|
||||
<ActivityRow key={`${item.author}-${idx}`} item={item} />
|
||||
{keyedActivity.map(({ key, item }) => (
|
||||
<ActivityRow key={key} item={item} />
|
||||
))}
|
||||
</Section>
|
||||
</View>
|
||||
|
||||
@@ -356,7 +356,7 @@ export function QuestionFormCard({ permission, onRespond, isResponding }: Questi
|
||||
const otherText = otherTexts[qIndex] ?? "";
|
||||
|
||||
return (
|
||||
<View key={qIndex} style={styles.questionBlock}>
|
||||
<View key={q.question} style={styles.questionBlock}>
|
||||
<View style={styles.questionHeader}>
|
||||
<Text style={questionTextStyle}>{q.question}</Text>
|
||||
<CircleHelp size={14} color={theme.colors.foregroundMuted} />
|
||||
@@ -364,7 +364,7 @@ export function QuestionFormCard({ permission, onRespond, isResponding }: Questi
|
||||
<View style={styles.optionsWrap}>
|
||||
{q.options.map((opt, optIndex) => (
|
||||
<QuestionOptionRow
|
||||
key={optIndex}
|
||||
key={opt.label}
|
||||
qIndex={qIndex}
|
||||
optIndex={optIndex}
|
||||
option={opt}
|
||||
|
||||
@@ -3,6 +3,10 @@ import { Animated, View, type StyleProp, type ViewStyle } from "react-native";
|
||||
import { StyleSheet } from "react-native-unistyles";
|
||||
|
||||
const SECTION_OPACITIES: readonly number[] = [1, 0.7, 0.4];
|
||||
const SECTION_KEYS = SECTION_OPACITIES.map((_, i) => `skeleton-section-${i}`);
|
||||
const ROW_KEYS_BY_SECTION: readonly (readonly string[])[] = SECTION_OPACITIES.map((_, sIdx) =>
|
||||
[0, 1, 2].map((r) => `skeleton-row-${sIdx}-${r}`),
|
||||
);
|
||||
|
||||
function SkeletonPulse({ pulse, style }: { pulse: Animated.Value; style: StyleProp<ViewStyle> }) {
|
||||
const opacity = pulse.interpolate({
|
||||
@@ -37,8 +41,8 @@ function SkeletonSection({
|
||||
</View>
|
||||
|
||||
<View style={styles.rows}>
|
||||
{Array.from({ length: 3 }).map((__, rowIdx) => (
|
||||
<View key={`skeleton-row-${sectionIdx}-${rowIdx}`} style={styles.row}>
|
||||
{ROW_KEYS_BY_SECTION[sectionIdx]?.map((key) => (
|
||||
<View key={key} style={styles.row}>
|
||||
<SkeletonPulse pulse={pulse} style={styles.rowDot} />
|
||||
<SkeletonPulse pulse={pulse} style={styles.rowTitle} />
|
||||
<SkeletonPulse pulse={pulse} style={styles.rowBadge} />
|
||||
@@ -76,7 +80,7 @@ export function SidebarAgentListSkeleton() {
|
||||
<View style={styles.container}>
|
||||
{SECTION_OPACITIES.map((sectionOpacity, sectionIdx) => (
|
||||
<SkeletonSection
|
||||
key={`skeleton-section-${sectionIdx}`}
|
||||
key={SECTION_KEYS[sectionIdx]}
|
||||
pulse={pulse}
|
||||
sectionOpacity={sectionOpacity}
|
||||
sectionIdx={sectionIdx}
|
||||
|
||||
@@ -16,6 +16,7 @@ const DOT_COUNT = DOT_SEQUENCE.length;
|
||||
const GRID_COLUMNS = 2;
|
||||
const SNAKE_SEGMENT_OFFSETS = [0, -1, -2, -3, -4] as const;
|
||||
const SNAKE_OPACITIES = [1, 0.78, 0.56, 0.34, 0] as const;
|
||||
const DOT_KEYS = Array.from({ length: DOT_COUNT }, (_, i) => `dot-${i}`);
|
||||
const sharedStepProgress = makeMutable(0);
|
||||
let sharedLoopStarted = false;
|
||||
|
||||
@@ -91,7 +92,7 @@ export function SyncedLoader({ size = 10, color }: { size?: number; color: strin
|
||||
|
||||
return (
|
||||
<SpinnerDot
|
||||
key={dotIndex}
|
||||
key={DOT_KEYS[dotIndex]}
|
||||
color={color}
|
||||
dotSize={dotSize}
|
||||
sequenceIndex={sequenceIndex}
|
||||
|
||||
@@ -37,9 +37,9 @@ export function Shortcut({
|
||||
|
||||
return (
|
||||
<View style={sequenceStyle}>
|
||||
{displayChord.map(function (combo, index) {
|
||||
{displayChord.map(function (combo) {
|
||||
return (
|
||||
<View key={`${combo.join("+")}-${index}`} style={styles.badge}>
|
||||
<View key={combo.join("+")} style={styles.badge}>
|
||||
<Text style={textCombinedStyle}>{formatShortcut(combo, shortcutOs)}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user