authorgravatar for 35393046+lnieuwenhuis@users.noreply.github.comLars Nieuwenhuis <35393046+lnieuwenhuis@users.noreply.github.com> 2026-09-07 09:18:17+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-07 00:18:17-07:00
logbc3dc2694b5d15613ef610d693d18ebd904b0d56
tree676bcc577c92608f2a14b00a2b8b5cb9749cd0d0
parentd8bc6831cd822b294d875364cf3aa0a17c999258
signature Signed by PGP key B5690EEEBB952194

fix(mobile): hide changed-files navigator and restore refresh in raw diff fallback (#9828)

Co-authored-by: James <jamescollard2005@gmail.com>

1 files changed, 50 insertions(+), 6 deletions(-)

apps/mobile/src/features/review/ReviewSheet.tsx+50-6
...@@ -23,6 +23,7 @@ import {...@@ -23,6 +23,7 @@ import {
23 FlatList,23 FlatList,
24 Platform,24 Platform,
25 Pressable,25 Pressable,
26 RefreshControl,
26 ScrollView,27 ScrollView,
27 type NativeSyntheticEvent,28 type NativeSyntheticEvent,
28 StyleSheet,29 StyleSheet,
...@@ -399,7 +400,11 @@ export function ReviewSheet(props: ReviewSheetProps) {...@@ -399,7 +400,11 @@ export function ReviewSheet(props: ReviewSheetProps) {
399 selectedSection,400 selectedSection,
400 draftMessage,401 draftMessage,
401 });402 });
402 const NativeReviewDiffView = resolveNativeReviewDiffView()!;403 // Resolution returns null while Expo registers the native view (or forever
404 // when the binary lacks it). Rendering a null component type crashes the
405 // app, so callers must fall back — ThreadFeed's ReviewCommentCard does the
406 // same check.
407 const NativeReviewDiffView = resolveNativeReviewDiffView();
403 const nativeReviewDiffViewRef = useRef<NativeReviewDiffViewHandle>(null);408 const nativeReviewDiffViewRef = useRef<NativeReviewDiffViewHandle>(null);
404 const showcasedReviewDrawRef = useRef<string | null>(null);409 const showcasedReviewDrawRef = useRef<string | null>(null);
405 // Native pull-to-refresh on the diff surface (replaces the old Refresh menu item).410 // Native pull-to-refresh on the diff surface (replaces the old Refresh menu item).
...@@ -597,11 +602,27 @@ export function ReviewSheet(props: ReviewSheetProps) {...@@ -597,11 +602,27 @@ export function ReviewSheet(props: ReviewSheetProps) {
597 .filter((part): part is string => Boolean(part))602 .filter((part): part is string => Boolean(part))
598 .join(" · ");603 .join(" · ");
599604
600 // The changed-files navigator lives in the workspace inspector column —605 // The changed-files navigator drives the native diff surface via
601 // the single right-hand pane per route — instead of an in-screen panel.606 // scrollToFile, so it is only useful when that surface resolved. In raw
607 // fallback mode the ref is necessarily null and the raw patch neither
608 // scrolls nor filters — registering the navigator would present working
609 // controls that cannot navigate.
602 const showChangedFilesPane =610 const showChangedFilesPane =
603 !showConnectionNotice && selectedSection !== null && parsedDiff.kind === "files";611 !showConnectionNotice &&
612 selectedSection !== null &&
613 parsedDiff.kind === "files" &&
614 NativeReviewDiffView !== null;
604 useRegisterWorkspaceInspector(showChangedFilesPane ? renderInspector : undefined);615 useRegisterWorkspaceInspector(showChangedFilesPane ? renderInspector : undefined);
616 // Raw fallback renders the patch inline with no inspector content, so the
617 // pane toggle would open an empty column — hide it in exactly that case.
618 const showChangedFilesToggle =
619 panes.supportsAuxiliaryPane &&
620 !(
621 !showConnectionNotice &&
622 selectedSection !== null &&
623 parsedDiff.kind === "files" &&
624 NativeReviewDiffView === null
625 );
605626
606 const listHeader = useMemo(() => {627 const listHeader = useMemo(() => {
607 const children: ReactElement[] = [];628 const children: ReactElement[] = [];
...@@ -688,7 +709,7 @@ export function ReviewSheet(props: ReviewSheetProps) {...@@ -688,7 +709,7 @@ export function ReviewSheet(props: ReviewSheetProps) {
688709
689 {!isAndroid && (showSectionToolbar || panes.supportsAuxiliaryPane || gitMenuAvailable) ? (710 {!isAndroid && (showSectionToolbar || panes.supportsAuxiliaryPane || gitMenuAvailable) ? (
690 <NativeHeaderToolbar placement="right">711 <NativeHeaderToolbar placement="right">
691 {panes.supportsAuxiliaryPane ? (712 {showChangedFilesToggle ? (
692 <NativeHeaderToolbar.Button713 <NativeHeaderToolbar.Button
693 accessibilityLabel={714 accessibilityLabel={
694 panes.auxiliaryPaneVisible ? "Hide changed files" : "Show changed files"715 panes.auxiliaryPaneVisible ? "Hide changed files" : "Show changed files"
...@@ -781,7 +802,7 @@ export function ReviewSheet(props: ReviewSheetProps) {...@@ -781,7 +802,7 @@ export function ReviewSheet(props: ReviewSheetProps) {
781 onRetry={handleRetryEnvironment}802 onRetry={handleRetryEnvironment}
782 />803 />
783 </View>804 </View>
784 ) : selectedSection && parsedDiff.kind === "files" ? (805 ) : selectedSection && parsedDiff.kind === "files" && NativeReviewDiffView ? (
785 <View806 <View
786 className="flex-1"807 className="flex-1"
787 style={{808 style={{
...@@ -835,6 +856,16 @@ export function ReviewSheet(props: ReviewSheetProps) {...@@ -835,6 +856,16 @@ export function ReviewSheet(props: ReviewSheetProps) {
835 }}856 }}
836 showsVerticalScrollIndicator={false}857 showsVerticalScrollIndicator={false}
837 className="flex-1"858 className="flex-1"
859 refreshControl={
860 // The native diff surface owns pull-to-refresh via onPullToRefresh;
861 // the raw fallback (and empty states) need an explicit control —
862 // iOS has no other refresh affordance here (the explicit
863 // "Refresh current diff" menu is Android-only).
864 <RefreshControl
865 refreshing={isPullRefreshing}
866 onRefresh={() => void handlePullToRefresh()}
867 />
868 }
838 >869 >
839 {listHeader}870 {listHeader}
840 {!selectedSection ? (871 {!selectedSection ? (
...@@ -867,6 +898,19 @@ export function ReviewSheet(props: ReviewSheetProps) {...@@ -867,6 +898,19 @@ export function ReviewSheet(props: ReviewSheetProps) {
867 </Text>898 </Text>
868 </ScrollView>899 </ScrollView>
869 </View>900 </View>
901 ) : parsedDiff.kind === "files" ? (
902 // The native diff surface could not be resolved on this binary;
903 // degrade to the raw patch instead of crashing the app.
904 <View className="gap-3 border-b border-border bg-card px-4 py-4">
905 <Text className="text-xs leading-normal text-foreground-muted">
906 Native diff view unavailable. Showing the raw patch.
907 </Text>
908 <ScrollView horizontal showsHorizontalScrollIndicator={false} bounces={false}>
909 <Text selectable className="font-mono text-xs leading-relaxed text-foreground">
910 {selectedSection?.diff ?? ""}
911 </Text>
912 </ScrollView>
913 </View>
870 ) : null}914 ) : null}
871 </ScrollView>915 </ScrollView>
872 )}916 )}