fix(app): make session rename reachable in the mobile sessions dropdown (#2924)
* fix(app): make session rename reachable in the mobile sessions dropdown The per-session actions menu in the mobile tab switcher (a gorhom bottom sheet) opened as an anchored popover, which on native renders a second RN Modal stacked on the sheet — the fragile nested-overlay case docs/floating-panels.md warns about. The actions menu (incl. Rename) never appeared on native, so sessions could not be renamed from the dropdown. Give the menu compactMode="sheet" like every other mobile menu in the file, so it stacks as a gorhom sheet on a gorhom sheet (the supported path). Extract the accessory into workspace-tab-trailing-accessory.tsx and add a reachability test that fails if the sheet presentation regresses. * test(app): cover compact session rename via the tab switcher e2e Adds a Playwright spec that drives the mobile-viewport sessions dropdown -> per-session actions sheet -> Rename, exercising the real gorhom sheet interaction the fix restores (the desktop rename specs only cover the right-click context menu, and the unit test mocks the menu engine). --------- Co-authored-by: Klaude <klaude@Klaudes-MacBook-Pro.local> Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
This commit is contained in:
co-authored by
Klaude
Mohamed Boudra
parent
1e0ce69c54
commit
49b257c55a
@@ -20,21 +20,15 @@ import * as Clipboard from "expo-clipboard";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { DiffStat } from "@/components/diff-stat";
|
||||
import {
|
||||
CopyX,
|
||||
ArrowLeftToLine,
|
||||
ArrowRightToLine,
|
||||
ChevronDown,
|
||||
Copy,
|
||||
Ellipsis,
|
||||
Globe,
|
||||
Import as ImportIcon,
|
||||
PanelRight,
|
||||
Pencil,
|
||||
RotateCw,
|
||||
Settings,
|
||||
SquarePen,
|
||||
SquareTerminal,
|
||||
X,
|
||||
} from "lucide-react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
||||
@@ -128,13 +122,13 @@ import {
|
||||
useWorkspaceTabRename,
|
||||
WorkspaceTabRenameModal,
|
||||
} from "@/screens/workspace/use-workspace-tab-rename";
|
||||
import { MobileTabTrailingAccessory } from "@/screens/workspace/workspace-tab-trailing-accessory";
|
||||
import {
|
||||
WorkspaceDesktopTabsRow,
|
||||
type WorkspaceDesktopTabRowItem,
|
||||
} from "@/screens/workspace/workspace-desktop-tabs-row";
|
||||
import {
|
||||
buildWorkspaceTabMenuEntries,
|
||||
type WorkspaceTabMenuEntry,
|
||||
type WorkspaceTabMenuLabels,
|
||||
} from "@/screens/workspace/workspace-tab-menu";
|
||||
import { useDesktopBrowserNewTabRequests } from "@/desktop/browser/new-tab-requests";
|
||||
@@ -249,12 +243,6 @@ const ThemedLoadingSpinner = withUnistyles(LoadingSpinner);
|
||||
const ThemedEllipsis = withUnistyles(Ellipsis);
|
||||
const ThemedChevronDown = withUnistyles(ChevronDown);
|
||||
const ThemedCopy = withUnistyles(Copy);
|
||||
const ThemedRotateCw = withUnistyles(RotateCw);
|
||||
const ThemedArrowLeftToLine = withUnistyles(ArrowLeftToLine);
|
||||
const ThemedArrowRightToLine = withUnistyles(ArrowRightToLine);
|
||||
const ThemedCopyX = withUnistyles(CopyX);
|
||||
const ThemedPencil = withUnistyles(Pencil);
|
||||
const ThemedX = withUnistyles(X);
|
||||
const ThemedSquarePen = withUnistyles(SquarePen);
|
||||
const ThemedSquareTerminal = withUnistyles(SquareTerminal);
|
||||
const ThemedGlobe = withUnistyles(Globe);
|
||||
@@ -516,95 +504,10 @@ function WorkspaceDocumentTitleEffect({
|
||||
|
||||
function noop() {}
|
||||
|
||||
function mobileTabMenuTriggerStyle({ open, pressed }: { open?: boolean; pressed?: boolean }) {
|
||||
return [
|
||||
styles.mobileTabMenuTrigger,
|
||||
(Boolean(open) || Boolean(pressed)) && styles.mobileTabMenuTriggerActive,
|
||||
];
|
||||
}
|
||||
|
||||
function switcherTriggerStyle({ pressed }: { pressed?: boolean }) {
|
||||
return [styles.switcherTrigger, Boolean(pressed) && styles.switcherTriggerPressed];
|
||||
}
|
||||
|
||||
function MobileTabTrailingAccessory({
|
||||
menuTestIDBase,
|
||||
presentationLabel,
|
||||
menuEntries,
|
||||
}: {
|
||||
menuTestIDBase: string;
|
||||
presentationLabel: string;
|
||||
menuEntries: WorkspaceTabMenuEntry[];
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
testID={`${menuTestIDBase}-trigger`}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t("workspace.tabs.menu.openFor", { label: presentationLabel })}
|
||||
hitSlop={8}
|
||||
style={mobileTabMenuTriggerStyle}
|
||||
>
|
||||
<ThemedEllipsis size={14} uniProps={mutedColorMapping} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent side="bottom" align="end" width={220} testID={menuTestIDBase}>
|
||||
{menuEntries.map((entry) =>
|
||||
entry.kind === "separator" ? (
|
||||
<DropdownMenuSeparator key={entry.key} />
|
||||
) : (
|
||||
<MobileTabDropdownMenuItem key={entry.key} entry={entry} />
|
||||
),
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileTabDropdownMenuItem({
|
||||
entry,
|
||||
}: {
|
||||
entry: Extract<WorkspaceTabMenuEntry, { kind: "item" }>;
|
||||
}) {
|
||||
const leading = useMemo(() => {
|
||||
switch (entry.icon) {
|
||||
case "copy":
|
||||
return <ThemedCopy size={16} uniProps={mutedColorMapping} />;
|
||||
case "rotate-cw":
|
||||
return <ThemedRotateCw size={16} uniProps={mutedColorMapping} />;
|
||||
case "arrow-left-to-line":
|
||||
return <ThemedArrowLeftToLine size={16} uniProps={mutedColorMapping} />;
|
||||
case "arrow-right-to-line":
|
||||
return <ThemedArrowRightToLine size={16} uniProps={mutedColorMapping} />;
|
||||
case "copy-x":
|
||||
return <ThemedCopyX size={16} uniProps={mutedColorMapping} />;
|
||||
case "pencil":
|
||||
return <ThemedPencil size={16} uniProps={mutedColorMapping} />;
|
||||
case "x":
|
||||
return <ThemedX size={16} uniProps={mutedColorMapping} />;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}, [entry.icon]);
|
||||
const trailing = useMemo(
|
||||
() => (entry.hint ? <Text style={styles.menuItemHint}>{entry.hint}</Text> : undefined),
|
||||
[entry.hint],
|
||||
);
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
testID={entry.testID}
|
||||
disabled={entry.disabled}
|
||||
destructive={entry.destructive}
|
||||
onSelect={entry.onSelect}
|
||||
tooltip={entry.tooltip}
|
||||
leading={leading}
|
||||
trailing={trailing}
|
||||
>
|
||||
{entry.label}
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileWorkspaceTabOption({
|
||||
tab,
|
||||
tabIndex,
|
||||
@@ -4170,20 +4073,6 @@ const styles = StyleSheet.create((theme) => ({
|
||||
color: theme.colors.foreground,
|
||||
fontSize: theme.fontSize.sm,
|
||||
},
|
||||
mobileTabMenuTrigger: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: theme.borderRadius.md,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
mobileTabMenuTriggerActive: {
|
||||
backgroundColor: theme.colors.surface2,
|
||||
},
|
||||
menuItemHint: {
|
||||
color: theme.colors.foregroundMuted,
|
||||
fontSize: theme.fontSize.xs,
|
||||
},
|
||||
headerMenuProfileIconWrapper: {
|
||||
width: 16,
|
||||
height: 16,
|
||||
|
||||
Reference in New Issue
Block a user