fix(app): retain Explorer tab state
This commit is contained in:
@@ -17,13 +17,19 @@ import { formatPrTabLabel, PullRequestTabIcon } from "@/git/pull-request-panel";
|
|||||||
import { usePanelStore, selectIsFileExplorerOpen, type ExplorerTab } from "@/stores/panel-store";
|
import { usePanelStore, selectIsFileExplorerOpen, type ExplorerTab } from "@/stores/panel-store";
|
||||||
import { useCloseFileExplorerGesture } from "@/mobile-panels/gestures";
|
import { useCloseFileExplorerGesture } from "@/mobile-panels/gestures";
|
||||||
import { MobilePanelOverlay } from "@/mobile-panels/presentation";
|
import { MobilePanelOverlay } from "@/mobile-panels/presentation";
|
||||||
import { HEADER_INNER_HEIGHT } from "@/constants/layout";
|
import {
|
||||||
|
HEADER_INNER_HEIGHT,
|
||||||
|
HEADER_INNER_HEIGHT_MOBILE,
|
||||||
|
HEADER_TOP_PADDING_MOBILE,
|
||||||
|
} from "@/constants/layout";
|
||||||
import { ChangesSurface } from "@/git/diff-pane";
|
import { ChangesSurface } from "@/git/diff-pane";
|
||||||
import { FileExplorerPane } from "./file-explorer-pane";
|
import { FileExplorerPane } from "./file-explorer-pane";
|
||||||
import { useKeyboardShiftStyle } from "@/hooks/use-keyboard-shift-style";
|
import { useKeyboardShiftStyle } from "@/hooks/use-keyboard-shift-style";
|
||||||
|
import { shouldUseCompactExplorerKeyboardPadding } from "@/hooks/keyboard-shift-policy";
|
||||||
import { useHasOwnedWindowChromeObstruction, WindowChromeSafeArea } from "@/utils/desktop-window";
|
import { useHasOwnedWindowChromeObstruction, WindowChromeSafeArea } from "@/utils/desktop-window";
|
||||||
import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region";
|
import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region";
|
||||||
import { RetainedPanelActivity } from "@/components/retained-panel";
|
import { RetainedPanel, RetainedPanelActivity } from "@/components/retained-panel";
|
||||||
|
import { useMountedTabSet } from "@/screens/workspace/use-mounted-tab-set";
|
||||||
import { SidebarResizeHandle } from "@/components/sidebar-resize-handle";
|
import { SidebarResizeHandle } from "@/components/sidebar-resize-handle";
|
||||||
import { resolveDesktopExplorerWidth } from "@/components/desktop-sidebar-layout";
|
import { resolveDesktopExplorerWidth } from "@/components/desktop-sidebar-layout";
|
||||||
import {
|
import {
|
||||||
@@ -82,9 +88,10 @@ export function CompactExplorerSidebar({
|
|||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
isGit,
|
isGit,
|
||||||
});
|
});
|
||||||
|
const usePanelKeyboardPadding = shouldUseCompactExplorerKeyboardPadding({ isGit, explorerTab });
|
||||||
const { style: mobileKeyboardInsetStyle } = useKeyboardShiftStyle({
|
const { style: mobileKeyboardInsetStyle } = useKeyboardShiftStyle({
|
||||||
mode: "padding",
|
mode: "padding",
|
||||||
enabled: true,
|
enabled: usePanelKeyboardPadding,
|
||||||
});
|
});
|
||||||
const { gesture: closeGesture } = useCloseFileExplorerGesture();
|
const { gesture: closeGesture } = useCloseFileExplorerGesture();
|
||||||
|
|
||||||
@@ -104,12 +111,19 @@ export function CompactExplorerSidebar({
|
|||||||
const mobileSidebarStyle = useMemo(
|
const mobileSidebarStyle = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
paddingTop: insets.top,
|
paddingTop: insets.top + HEADER_TOP_PADDING_MOBILE,
|
||||||
|
paddingBottom: usePanelKeyboardPadding ? 0 : insets.bottom,
|
||||||
backgroundColor: theme.colors.surfaceSidebar,
|
backgroundColor: theme.colors.surfaceSidebar,
|
||||||
},
|
},
|
||||||
mobileKeyboardInsetStyle,
|
mobileKeyboardInsetStyle,
|
||||||
],
|
],
|
||||||
[insets.top, theme.colors.surfaceSidebar, mobileKeyboardInsetStyle],
|
[
|
||||||
|
insets.bottom,
|
||||||
|
insets.top,
|
||||||
|
mobileKeyboardInsetStyle,
|
||||||
|
theme.colors.surfaceSidebar,
|
||||||
|
usePanelKeyboardPadding,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -318,6 +332,16 @@ function ExplorerSidebarContent({
|
|||||||
!isGit && (activeTab === "changes" || activeTab === "pr") ? "files" : activeTab;
|
!isGit && (activeTab === "changes" || activeTab === "pr") ? "files" : activeTab;
|
||||||
const resolvedTab: ExplorerTab = requestedTab === "pr" && !showPrTab ? "changes" : requestedTab;
|
const resolvedTab: ExplorerTab = requestedTab === "pr" && !showPrTab ? "changes" : requestedTab;
|
||||||
const prTabLabel = formatPrTabLabel(prPane.prNumber);
|
const prTabLabel = formatPrTabLabel(prPane.prNumber);
|
||||||
|
const availableTabs = useMemo<ExplorerTab[]>(() => {
|
||||||
|
const tabs: ExplorerTab[] = isGit ? ["changes", "files"] : ["files"];
|
||||||
|
if (isGit && showPrTab) tabs.push("pr");
|
||||||
|
return tabs;
|
||||||
|
}, [isGit, showPrTab]);
|
||||||
|
const { mountedTabIds } = useMountedTabSet({
|
||||||
|
activeTabId: resolvedTab,
|
||||||
|
allTabIds: availableTabs,
|
||||||
|
cap: availableTabs.length,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.sidebarContent} pointerEvents="auto">
|
<View style={styles.sidebarContent} pointerEvents="auto">
|
||||||
@@ -391,31 +415,37 @@ function ExplorerSidebarContent({
|
|||||||
|
|
||||||
{/* Content based on active tab */}
|
{/* Content based on active tab */}
|
||||||
<View style={styles.contentArea} testID="explorer-content-area">
|
<View style={styles.contentArea} testID="explorer-content-area">
|
||||||
{resolvedTab === "changes" && (
|
{mountedTabIds.has("changes") ? (
|
||||||
<ChangedFilesPane
|
<RetainedPanel active={resolvedTab === "changes"}>
|
||||||
serverId={serverId}
|
<ChangedFilesPane
|
||||||
workspaceId={workspaceId}
|
serverId={serverId}
|
||||||
workspaceRoot={workspaceRoot}
|
workspaceId={workspaceId}
|
||||||
isOpen={isOpen}
|
workspaceRoot={workspaceRoot}
|
||||||
onOpenFile={onOpenFile}
|
isOpen={isOpen}
|
||||||
/>
|
onOpenFile={onOpenFile}
|
||||||
)}
|
/>
|
||||||
{resolvedTab === "files" && (
|
</RetainedPanel>
|
||||||
<FilesPane
|
) : null}
|
||||||
serverId={serverId}
|
{mountedTabIds.has("files") ? (
|
||||||
workspaceId={workspaceId}
|
<RetainedPanel active={resolvedTab === "files"}>
|
||||||
workspaceRoot={workspaceRoot}
|
<FilesPane
|
||||||
onOpenFile={onOpenFile}
|
serverId={serverId}
|
||||||
/>
|
workspaceId={workspaceId}
|
||||||
)}
|
workspaceRoot={workspaceRoot}
|
||||||
{resolvedTab === "pr" && (
|
onOpenFile={onOpenFile}
|
||||||
<PrTabContent
|
/>
|
||||||
serverId={serverId}
|
</RetainedPanel>
|
||||||
workspaceId={workspaceId}
|
) : null}
|
||||||
cwd={workspaceRoot}
|
{mountedTabIds.has("pr") ? (
|
||||||
prPane={prPane}
|
<RetainedPanel active={resolvedTab === "pr"}>
|
||||||
/>
|
<PrTabContent
|
||||||
)}
|
serverId={serverId}
|
||||||
|
workspaceId={workspaceId}
|
||||||
|
cwd={workspaceRoot}
|
||||||
|
prPane={prPane}
|
||||||
|
/>
|
||||||
|
</RetainedPanel>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
@@ -487,7 +517,10 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
position: "relative",
|
position: "relative",
|
||||||
height: HEADER_INNER_HEIGHT,
|
height: {
|
||||||
|
xs: HEADER_INNER_HEIGHT_MOBILE,
|
||||||
|
md: HEADER_INNER_HEIGHT,
|
||||||
|
},
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
export const DEFAULT_IOS_KEYBOARD_INSET_MIN_HEIGHT = 120;
|
export const DEFAULT_IOS_KEYBOARD_INSET_MIN_HEIGHT = 120;
|
||||||
|
|
||||||
|
export function shouldUseCompactExplorerKeyboardPadding(input: {
|
||||||
|
isGit: boolean;
|
||||||
|
explorerTab: "changes" | "files" | "pr";
|
||||||
|
}): boolean {
|
||||||
|
return !input.isGit || input.explorerTab !== "changes";
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveKeyboardShift(input: {
|
export function resolveKeyboardShift(input: {
|
||||||
rawKeyboardHeight: number;
|
rawKeyboardHeight: number;
|
||||||
keyboardProgress: number;
|
keyboardProgress: number;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { resolveKeyboardShift } from "./keyboard-shift-policy";
|
import {
|
||||||
|
resolveKeyboardShift,
|
||||||
|
shouldUseCompactExplorerKeyboardPadding,
|
||||||
|
} from "./keyboard-shift-policy";
|
||||||
|
|
||||||
describe("resolveKeyboardShift", () => {
|
describe("resolveKeyboardShift", () => {
|
||||||
it("keeps the existing open-keyboard offset behavior", () => {
|
it("keeps the existing open-keyboard offset behavior", () => {
|
||||||
@@ -38,3 +41,17 @@ describe("resolveKeyboardShift", () => {
|
|||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("shouldUseCompactExplorerKeyboardPadding", () => {
|
||||||
|
it("keeps the changes viewport stable while preserving padding for other tabs", () => {
|
||||||
|
expect(shouldUseCompactExplorerKeyboardPadding({ isGit: true, explorerTab: "changes" })).toBe(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
expect(shouldUseCompactExplorerKeyboardPadding({ isGit: true, explorerTab: "files" })).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(shouldUseCompactExplorerKeyboardPadding({ isGit: false, explorerTab: "changes" })).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user