Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 70 additions & 37 deletions apps/geolibre-desktop/src/components/layout/DesktopShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,14 @@
el.className = "contents";
return el;
});
const activePanelId = useRightPanelState().activeId;
const rightPanelState = useRightPanelState();
const activePanelId = rightPanelState.activeId;
const replaceStylePanelIds = rightPanelState.visibleIds.filter(
(id) => rightPanelState.panelDocks[id] === "replace-style",
);
const replaceLayersPanelIds = rightPanelState.visibleIds.filter(
(id) => rightPanelState.panelDocks[id] === "replace-layers",
);
const activePanel = activePanelId ? getRightPanel(activePanelId) : undefined;
// The plugins in VIEWER_BLOCKED_PLUGIN_IDS paint drawing and editing controls
// onto the map, which the read-only viewer preset cannot hide the way it
Expand Down Expand Up @@ -1374,7 +1381,7 @@
}
});
return () => setKmlFileImportHandler(null);
}, [addImportedVectorLayers, confirmLargeVectorDataset, t]);

Check warning on line 1384 in apps/geolibre-desktop/src/components/layout/DesktopShell.tsx

View workflow job for this annotation

GitHub Actions / Build and test

React Hook useEffect has an unnecessary dependency: 'confirmLargeVectorDataset'. Either exclude it or remove the dependency array. Outer scope values like 'confirmLargeVectorDataset' aren't valid dependencies because mutating them doesn't re-render the component

const addDroppedPhotos = useCallback(
(result: GeotaggedPhotoResult | null): number => {
Expand Down Expand Up @@ -1650,7 +1657,7 @@
disposed = true;
unlisten?.();
};
}, [

Check warning on line 1660 in apps/geolibre-desktop/src/components/layout/DesktopShell.tsx

View workflow job for this annotation

GitHub Actions / Build and test

React Hook useEffect has a missing dependency: 't'. Either include it or remove the dependency array
clearDropMessageLater,
finishDrop,
addDroppedRasters,
Expand Down Expand Up @@ -1794,7 +1801,7 @@
clearDropMessageLater();
}
},
[

Check warning on line 1804 in apps/geolibre-desktop/src/components/layout/DesktopShell.tsx

View workflow job for this annotation

GitHub Actions / Build and test

React Hook useCallback has a missing dependency: 't'. Either include it or remove the dependency array
clearDropMessageLater,
finishDrop,
addDroppedRasters,
Expand Down Expand Up @@ -2087,6 +2094,7 @@
key={replaceLayersPanelId}
side="layers"
pluginId={replaceLayersPanelId}
additionalPanelIds={replaceLayersPanelIds}
Comment thread
giswqs marked this conversation as resolved.
Outdated
pluginContentEl={dockContentEl}
pluginWidth={pluginPanelWidth}
onPluginWidthChange={setPluginPanelWidth}
Expand Down Expand Up @@ -2312,42 +2320,67 @@
{/* Same as the left dock: a map-only / hidden-panels embed skips the
entire right side-dock (Style, plugin panels, and their shared rail). */}
{layoutOptions.panelsHidden || layoutOptions.viewer ? null : replaceStylePanelId ? (
// Shared-rail mode (issue #765): the plugin panel shares the Style
// sidebar surface, so a single rail lists both the workbench and Style
// instead of the two positional plugin slots flanking the Style panel.
<SectionErrorBoundary
label="Shared right sidebar"
displayName={t("shell.section.sharedRightSidebar")}
>
<SharedSidebar
// Key by the active panel id so switching between two replace-style
// plugins remounts the sidebar, resetting its per-panel local state
// (the Style opt-in) rather than carrying the previous plugin over.
key={replaceStylePanelId}
side="style"
pluginId={replaceStylePanelId}
pluginContentEl={dockContentEl}
pluginWidth={pluginPanelWidth}
onPluginWidthChange={setPluginPanelWidth}
builtinVisible={layoutOptions.stylePanelVisible}
builtinTitle={t("sharedRail.style")}
builtinIcon={<SlidersHorizontal className="h-4 w-4" />}
// Mirror the standalone Style panel's autoCollapse triggers so the
// notebook / story-map presentation collapses Style here too.
// `autoCollapsedPanel` is omitted because it is always null in a
// shared-rail mode (the panel is the sole active one).
forceBuiltinCollapsed={notebookOpen || storymapPresenting}
renderBuiltin={({ collapsed, onCollapsedChange }) => (
<StylePanel
mapControllerRef={mapControllerRef}
onResizeStart={startStylePanelResize}
collapsed={collapsed}
onCollapsedChange={onCollapsedChange}
hideOwnRail
/>
)}
/>
</SectionErrorBoundary>
<>
{/* Shared-rail panels such as Comments must not remove the ordinary
positional docks: enabled Web Services panels still live in
left/right-of-style and need their vertical rail entries. */}
<SectionErrorBoundary
label="Plugin panel (left of Style)"
displayName={t("shell.section.pluginPanelLeftOfStyle")}
>
<PluginRightPanel
dock="left-of-style"
contentEl={dockContentEl}
width={pluginPanelWidth}
onWidthChange={setPluginPanelWidth}
/>
</SectionErrorBoundary>
<SectionErrorBoundary
label="Shared right sidebar"
displayName={t("shell.section.sharedRightSidebar")}
>
<SharedSidebar
// Key by the active panel id so switching between two replace-style
// plugins remounts the sidebar, resetting its per-panel local state
// (the Style opt-in) rather than carrying the previous plugin over.
key={replaceStylePanelId}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that multiple panels can be simultaneously visible in the shared Style rail (Comments plus any of the many plugins now defaulting to replace-style), replaceStylePanelId — and therefore this key — changes on every click between two already-visible rail entries, not just on the rare merge/detach action the original comment describes. Each switch fully remounts SharedSidebar, including the wrapped <StylePanel> and SharedSidebar's own builtinOptedIn state.

That interacts badly with the new auto-expand effect in StylePanel.tsx (previousSelectedLayerId = useRef(selectedLayerId) + effect gating on a change from that ref): the ref reinitializes to the current selectedLayerId on every remount, so if a layer is already selected when the remount happens (the common case — a user had a layer selected, then merely clicked to a different panel in the shared rail), the effect sees no "change" and never expands Style. The panel lands collapsed even though a real layer is selected, defeating the "selecting a layer re-expands the Style panel" behavior this PR adds.

Confidence: medium — the remount mechanics and the ref's mount-time initialization are clear from the code; the perceived severity depends on how often users actually have two replace-style panels open at once (Comments + a Web Services plugin).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not changing this one — the remount is deliberate and the outcome it produces is the intended layout. Clicking a second shared-rail entry makes that panel the expanded surface, so pluginExpanded is true and Style is collapsed regardless of builtinOptedIn; the reset ref only matters once the user collapses the plugin again, and at that point Style being on the rail is the documented "both collapsed" resting state rather than a defeated auto-expand. Selecting any layer after that still expands Style, because the selection is a real change against the ref. Re-selecting the same layer is a no-op by design (the effect fires on change, not on click). Happy to revisit if the collapse-on-switch reads wrong in use.

side="style"
pluginId={replaceStylePanelId}
additionalPanelIds={replaceStylePanelIds}
pluginContentEl={dockContentEl}
pluginWidth={pluginPanelWidth}
onPluginWidthChange={setPluginPanelWidth}
builtinVisible={layoutOptions.stylePanelVisible}
builtinTitle={t("sharedRail.style")}
builtinIcon={<SlidersHorizontal className="h-4 w-4" />}
// Mirror the standalone Style panel's autoCollapse triggers so the
// notebook / story-map presentation collapses Style here too.
// `autoCollapsedPanel` is omitted because it is always null in a
// shared-rail mode (the panel is the sole active one).
forceBuiltinCollapsed={notebookOpen || storymapPresenting}
renderBuiltin={({ collapsed, onCollapsedChange }) => (
<StylePanel
mapControllerRef={mapControllerRef}
onResizeStart={startStylePanelResize}
collapsed={collapsed}
onCollapsedChange={onCollapsedChange}
hideOwnRail
/>
)}
/>
</SectionErrorBoundary>
<SectionErrorBoundary
label="Plugin panel (right of Style)"
displayName={t("shell.section.pluginPanelRightOfStyle")}
>
<PluginRightPanel
dock="right-of-style"
contentEl={dockContentEl}
width={pluginPanelWidth}
onWidthChange={setPluginPanelWidth}
/>
</SectionErrorBoundary>
</>
Comment thread
giswqs marked this conversation as resolved.
Outdated
) : (
<>
<SectionErrorBoundary
Expand Down
31 changes: 30 additions & 1 deletion apps/geolibre-desktop/src/components/layout/SettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
Locate,
MapPinned,
LayoutPanelTop,
MessageSquare,
Moon,
Palette,
PanelLeft,
Expand Down Expand Up @@ -84,6 +85,7 @@ import {
} from "../../hooks/useDesktopSettings";
import { useLanguage } from "../../hooks/useLanguage";
import { BROWSER_PANEL_ID } from "../../hooks/useRegisterBrowserPanel";
import { COMMENTS_PANEL_ID } from "../../hooks/useRegisterCommentsPanel";
import { useRightPanelState } from "../../hooks/useRightPanels";
import type { ThemeMode } from "../../hooks/useThemeMode";
import { isTauri } from "../../lib/is-tauri";
Expand Down Expand Up @@ -390,7 +392,9 @@ export function SettingsDialog({
// The Browser is a dockable right panel (open/close via the registry), not a
// persisted layout preference, so its Layout toggle acts on the live registry
// state directly rather than through the draft settings.
const browserPanelOpen = useRightPanelState().activeId === BROWSER_PANEL_ID;
const rightPanelState = useRightPanelState();
const browserPanelOpen = rightPanelState.visibleIds.includes(BROWSER_PANEL_ID);
const commentsPanelOpen = rightPanelState.visibleIds.includes(COMMENTS_PANEL_ID);
// Show it collapsed on the shared Layers rail, matching its default state, so
// re-enabling from Settings doesn't jump to an expanded panel that buries the
// Layers panel.
Expand All @@ -402,6 +406,14 @@ export function SettingsDialog({
closeRightPanel(BROWSER_PANEL_ID);
}
};
const toggleCommentsPanel = (show: boolean) => {
if (show) {
openRightPanel(COMMENTS_PANEL_ID);
collapseRightPanel(COMMENTS_PANEL_ID);
} else {
closeRightPanel(COMMENTS_PANEL_ID);
}
};
Comment thread
giswqs marked this conversation as resolved.
// A field a deep-link asked us to focus once its section renders; cleared
// after the focus lands so a later open without a focus request stays put.
const [pendingFocus, setPendingFocus] = useState<SettingsFocusTarget | null>(null);
Expand Down Expand Up @@ -1264,6 +1276,13 @@ export function SettingsDialog({
>
{t("settings.layout.showBrowserPanel")}
</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem
checked={commentsPanelOpen}
onCheckedChange={(checked: boolean) => toggleCommentsPanel(checked === true)}
onSelect={(event: Event) => event.preventDefault()}
>
{t("settings.layout.showCommentsPanel")}
</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onSelect={() => {
Expand Down Expand Up @@ -1738,6 +1757,16 @@ export function SettingsDialog({
<FolderTree className="h-4 w-4 text-muted-foreground" />
<span>{t("settings.layout.showBrowserPanel")}</span>
</label>
<label className="flex items-center gap-3 rounded-md border p-3 text-sm">
<input
className="h-4 w-4"
type="checkbox"
checked={commentsPanelOpen}
onChange={(event) => toggleCommentsPanel(event.target.checked)}
/>
<MessageSquare className="h-4 w-4 text-muted-foreground" />
<span>{t("settings.layout.showCommentsPanel")}</span>
</label>
</div>
{showsAdvancedNotices(desktopSettings.uiProfile) ? (
<div className="rounded-md border bg-muted/40 p-3 text-xs text-muted-foreground">
Expand Down
Loading
Loading