diff --git a/mobile/src/source-control/MobileCommitDetailPage.tsx b/mobile/src/source-control/MobileCommitDetailPage.tsx index 4f2ee5d511c..152d6d4aed7 100644 --- a/mobile/src/source-control/MobileCommitDetailPage.tsx +++ b/mobile/src/source-control/MobileCommitDetailPage.tsx @@ -34,6 +34,8 @@ type Props = { commit: MobileGitHistoryItem bottomInset: number onBack: () => void + // Opens a file in the shared diff screen; rows stay inert when the host offers none. + onOpenFile?: (filePath: string) => void } function splitPath(path: string): { name: string; dir: string } { @@ -57,7 +59,8 @@ export function MobileCommitDetailPage({ worktreeId, commit, bottomInset, - onBack + onBack, + onOpenFile }: Props) { useMobileColorScheme() const t = useMobileTranslation() @@ -271,19 +274,33 @@ export function MobileCommitDetailPage({ {t('sourceControl.gitHistoryList.noFileChanges', 'No file changes')} ) : ( - loadedFiles.map((file) => ) + loadedFiles.map((file) => ( + + )) )} ) } -function CommitFileRow({ file }: { file: MobileGitChangedFile }) { +function CommitFileRow({ + file, + onOpen +}: { + file: MobileGitChangedFile + onOpen?: (filePath: string) => void +}) { useMobileColorScheme() const status = file.status ?? 'modified' const { name, dir } = splitPath(file.path) return ( - + [styles.fileRow, pressed && onOpen && styles.fileRowPressed]} + disabled={!onOpen} + onPress={() => onOpen?.(file.path)} + accessibilityRole={onOpen ? 'button' : undefined} + accessibilityLabel={file.path} + > {STATUS_LABELS[status]} @@ -301,7 +318,7 @@ function CommitFileRow({ file }: { file: MobileGitChangedFile }) { ) : null} - + ) } @@ -358,6 +375,7 @@ const styles = themedStyleSheet(() => ({ fontFamily: typography.monoFamily, fontWeight: '600' }, + fileRowPressed: { backgroundColor: colors.bgRaised }, fileMain: { flex: 1, minWidth: 0 }, fileName: { color: colors.textPrimary, fontSize: typography.bodySize }, fileNameDeleted: { textDecorationLine: 'line-through', color: colors.textMuted }, diff --git a/mobile/src/source-control/MobileCommitsScreen.test.tsx b/mobile/src/source-control/MobileCommitsScreen.test.tsx index 8bb0a4193b3..bb4c359916f 100644 --- a/mobile/src/source-control/MobileCommitsScreen.test.tsx +++ b/mobile/src/source-control/MobileCommitsScreen.test.tsx @@ -297,6 +297,35 @@ describe('MobileCommitsScreen', () => { expect(list?.props.refreshControl.props.refreshing).toBe(false) }) + it("opens a commit's file in the shared diff screen against that commit", async () => { + const openDiffForFile = vi.fn() + const client = createFakeRpcClient() + client.sendRequest = routedSend(() => Promise.resolve(historyResponse('expandable'))) + await act(async () => { + renderer = create( + createElement(MobileCommitsScreen, { + client, + connState: 'connected', + worktreeId: 'wt-1', + hostId: 'host-1', + bottomInset: 0, + openDiffForFile + }) + ) + await settle() + }) + await openFirstCommit() + const fileRow = renderer?.root.find( + (node) => String(node.type) === 'Pressable' && node.props.accessibilityLabel === 'src/app.ts' + ) + await act(async () => fileRow?.props.onPress()) + expect(openDiffForFile).toHaveBeenCalledWith({ + kind: 'commit', + filePath: 'src/app.ts', + commitOid: 'commit-1' + }) + }) + it('wipes commits when the worktree identity changes', async () => { let historyCalls = 0 const sendRequest = routedSend(() => { diff --git a/mobile/src/source-control/MobileCommitsScreen.tsx b/mobile/src/source-control/MobileCommitsScreen.tsx index 100c78fe9ee..4f508f3db89 100644 --- a/mobile/src/source-control/MobileCommitsScreen.tsx +++ b/mobile/src/source-control/MobileCommitsScreen.tsx @@ -8,6 +8,7 @@ import type { RpcClient } from '../transport/rpc-client' import type { ConnectionState } from '../transport/types' import type { MobileGitHistoryItem } from './git-history-reply-schema' import { MobileCommitDetailPage } from './MobileCommitDetailPage' +import type { MobileDiffFileTarget } from './use-mobile-source-control-openers' import { MobileGitHistoryList, type MobileGitHistoryListProps } from './MobileGitHistoryList' type OpenCommit = (commit: MobileGitHistoryItem) => void @@ -17,12 +18,14 @@ type Props = Omit & { embedded?: boolean // Set by a parent whose own commit page covers more than this list, e.g. the whole Source Control screen. onOpenCommit?: OpenCommit + openDiffForFile?: (target: MobileDiffFileTarget) => void } // The branch's commits; a commit opens over them unless the parent hosts the commit page itself. export const MobileCommitsScreen = memo(function MobileCommitsScreen({ embedded = false, onOpenCommit, + openDiffForFile, ...props }: Props) { const list = (open: OpenCommit) => @@ -37,6 +40,7 @@ export const MobileCommitsScreen = memo(function MobileCommitsScreen({ worktreeId={props.worktreeId} bottomInset={props.bottomInset} padTop={false} + openDiffForFile={openDiffForFile} > {list} @@ -58,6 +62,7 @@ export function MobileCommitPageHost({ worktreeId, bottomInset, padTop, + openDiffForFile, children }: { client: RpcClient | null @@ -66,6 +71,7 @@ export function MobileCommitPageHost({ bottomInset: number // The page covers a screen-top header, so it pads under the status bar unless an ancestor already did. padTop: boolean + openDiffForFile?: (target: MobileDiffFileTarget) => void children: (openCommit: OpenCommit) => ReactNode }) { const pager = useOptionPager<'commit'>() @@ -97,6 +103,11 @@ export function MobileCommitPageHost({ commit={commit} bottomInset={bottomInset} onBack={close} + onOpenFile={ + openDiffForFile + ? (filePath) => openDiffForFile({ kind: 'commit', filePath, commitOid: commit.id }) + : undefined + } /> ) : null return ( diff --git a/mobile/src/source-control/MobileSourceControlPanel.tsx b/mobile/src/source-control/MobileSourceControlPanel.tsx index 91b66d76f70..0346e035393 100644 --- a/mobile/src/source-control/MobileSourceControlPanel.tsx +++ b/mobile/src/source-control/MobileSourceControlPanel.tsx @@ -317,6 +317,7 @@ export function MobileSourceControlPanel({ worktreeId={worktreeId} bottomInset={insets.bottom} padTop={!embedded} + openDiffForFile={state.openDiffForFile} > {(openCommit) => (