feat: add expand/collapse all button to git diff pane

This commit is contained in:
Mohamed Boudra
2026-02-04 08:22:27 +07:00
parent 469a25c724
commit f08efd2da6
+71 -25
View File
@@ -16,7 +16,7 @@ import { ScrollView, type ScrollView as ScrollViewType } from "react-native-gest
import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { StyleSheet, useUnistyles } from "react-native-unistyles";
import AsyncStorage from "@react-native-async-storage/async-storage"; import AsyncStorage from "@react-native-async-storage/async-storage";
import * as Linking from "expo-linking"; import * as Linking from "expo-linking";
import { ChevronDown, ChevronRight, GitBranch, MoreVertical, ArrowLeftRight } from "lucide-react-native"; import { ChevronDown, ChevronRight, GitBranch, MoreVertical, ArrowLeftRight, ListChevronsDownUp, ListChevronsUpDown } from "lucide-react-native";
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useSessionStore } from "@/stores/session-store"; import { useSessionStore } from "@/stores/session-store";
import { import {
@@ -583,6 +583,23 @@ export function GitDiffPane({ serverId, agentId, cwd }: GitDiffPaneProps) {
})); }));
}, []); }, []);
const allExpanded = useMemo(() => {
if (files.length === 0) return false;
return files.every((file) => expandedByPath[file.path]);
}, [files, expandedByPath]);
const handleToggleExpandAll = useCallback(() => {
if (allExpanded) {
setExpandedByPath({});
} else {
const newExpanded: Record<string, boolean> = {};
for (const file of files) {
newExpanded[file.path] = true;
}
setExpandedByPath(newExpanded);
}
}, [allExpanded, files]);
// Reset manual refresh flag when fetch completes // Reset manual refresh flag when fetch completes
useEffect(() => { useEffect(() => {
if (!(isDiffFetching || isStatusFetching) && isManualRefresh) { if (!(isDiffFetching || isStatusFetching) && isManualRefresh) {
@@ -1157,27 +1174,42 @@ export function GitDiffPane({ serverId, agentId, cwd }: GitDiffPaneProps) {
{isGit && (hasUncommittedChanges || aheadCount > 0) ? ( {isGit && (hasUncommittedChanges || aheadCount > 0) ? (
<View style={styles.diffStatusContainer}> <View style={styles.diffStatusContainer}>
<Pressable <View style={styles.diffStatusInner}>
style={({ hovered, pressed }) => [ <Pressable
styles.diffStatusRow, style={({ hovered, pressed }) => [
(hovered || pressed) && styles.diffStatusRowHovered, styles.diffStatusRow,
]} (hovered || pressed) && styles.diffStatusRowHovered,
testID="changes-diff-status" ]}
onPress={() => setDiffModeOverride(diffMode === "uncommitted" ? "base" : "uncommitted")} testID="changes-diff-status"
> onPress={() => setDiffModeOverride(diffMode === "uncommitted" ? "base" : "uncommitted")}
{({ hovered, pressed }) => ( >
<> {({ hovered, pressed }) => (
<Text style={styles.diffStatusText}> <>
{diffMode === "uncommitted" ? "Uncommitted" : "Committed"} <Text style={styles.diffStatusText}>
</Text> {diffMode === "uncommitted" ? "Uncommitted" : "Committed"}
<ArrowLeftRight </Text>
size={12} {(hovered || pressed) ? (
color={theme.colors.foregroundMuted} <ArrowLeftRight size={12} color={theme.colors.foregroundMuted} />
style={hovered || pressed ? undefined : styles.diffStatusIconHidden} ) : null}
/> </>
</>
)} )}
</Pressable> </Pressable>
{files.length > 0 ? (
<Pressable
style={({ hovered, pressed }) => [
styles.expandAllButton,
(hovered || pressed) && styles.diffStatusRowHovered,
]}
onPress={handleToggleExpandAll}
>
{allExpanded ? (
<ListChevronsDownUp size={14} color={theme.colors.foregroundMuted} />
) : (
<ListChevronsUpDown size={14} color={theme.colors.foregroundMuted} />
)}
</Pressable>
) : null}
</View>
</View> </View>
) : null} ) : null}
@@ -1229,15 +1261,20 @@ const styles = StyleSheet.create((theme) => ({
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: theme.colors.border, borderBottomColor: theme.colors.border,
}, },
diffStatusInner: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
paddingRight: theme.spacing[3],
},
diffStatusRow: { diffStatusRow: {
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
alignSelf: "flex-start",
gap: theme.spacing[1], gap: theme.spacing[1],
// Align text with chevron/branch icons (at spacing[2] from edge) // Align text with header branch icon (at spacing[3] from edge, minus our horizontal padding)
marginLeft: theme.spacing[3] - theme.spacing[1],
marginVertical: theme.spacing[2], marginVertical: theme.spacing[2],
paddingLeft: theme.spacing[2], paddingHorizontal: theme.spacing[1],
paddingRight: theme.spacing[2],
paddingVertical: theme.spacing[1], paddingVertical: theme.spacing[1],
borderRadius: theme.borderRadius.base, borderRadius: theme.borderRadius.base,
}, },
@@ -1251,6 +1288,15 @@ const styles = StyleSheet.create((theme) => ({
diffStatusIconHidden: { diffStatusIconHidden: {
opacity: 0, opacity: 0,
}, },
expandAllButton: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[1],
marginVertical: theme.spacing[2],
paddingHorizontal: theme.spacing[1],
paddingVertical: theme.spacing[1],
borderRadius: theme.borderRadius.base,
},
splitButton: { splitButton: {
flexDirection: "row", flexDirection: "row",
alignItems: "stretch", alignItems: "stretch",