Repository navigation
[Map layers]: add readOnly option to MapLayerOptions - #1831
romradchenko wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
An in-flight source request can continue after switching to read-only mode, violating the offline-loading guarantee.
4 open findings
What changed in this PR
Adds an optional read-only mode to keep Map Layers usable offline while preventing network-dependent configuration changes.
Changes:
- Hides or disables attach, detach, sign-in, reorder, settings, and provider controls.
- Preserves visibility, transparency, zoom, color, and sub-layer controls.
- Skips source loading and adds read-only tests and documentation.
| File | Description |
|---|---|
MapLayersList.tsx |
Hides attach controls. |
MapLayerSettingsMenu.tsx |
Hides detach action. |
MapLayerManager.tsx |
Propagates read-only state. |
MapLayerItem.tsx |
Hides selection and authentication controls. |
MapLayerDroppable.tsx |
Disables reordering and attachment. |
MapLayerActionButtons.tsx |
Hides selection and bulk detach. |
BasemapPanel.tsx |
Disables provider selection. |
Interfaces.ts |
Defines the public option. |
useMapLayerSources.ts |
Skips source loading. |
MapLayerManager.test.tsx |
Tests read-only behavior. |
| Changeset | Documents the new option. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| }, [mapLayerOptions?.baseMapLayers]); | ||
| const hideExternalMapLayersSection = mapLayerOptions?.hideExternalMapLayers ?? false; | ||
| const fetchPublicMapLayerSources = mapLayerOptions?.fetchPublicMapLayerSources ?? false; | ||
| const readOnly = mapLayerOptions?.readOnly ?? false; |
There was a problem hiding this comment.
We work out readOnly here, and then again in 4 other components (MapLayerActionButtons, MapLayerDroppable, MapLayerItem, MapLayersList). Could we add readOnly: boolean to MapLayerListContext and set it once here? Then the others can just read context.readOnly.
| export function MapLayerItem(props: MapLayerItemProps) { | ||
| const context = useMapLayerListContext(); | ||
| const readOnly = context.mapLayerOptions?.readOnly ?? false; | ||
| const signInOpenRef = React.useRef(false); |
There was a problem hiding this comment.
This ref gets set or cleared in 5 places, plus there's an effect that watches readOnly. Most of that comes from the sign-in button block being copied twice in this file (same ~36 lines at the two RequireAuth spots).
Could we move it into a small MapLayerSignInButton component that closes its dialog when it unmounts, if it opened one? Then hiding the button in read-only mode closes the dialog on its own, and the effect isn't needed. AttachLayerPanel already closes its modal on unmount, so there's a precedent.
Or: do we need to close it at all? If you go offline in the middle of signing in, I'd expect the dialog to just fail to validate.
Nambot 🤖 (powered by Claude Opus 5.5)
There was a problem hiding this comment.
Done in 14f6c97. Moved to MapLayerSignInButton. It closes its dialog on unmount, so the effect is gone.
| } | ||
|
|
||
| if (!isMounted.current) { | ||
| if (!isMounted.current || cancelled) { |
There was a problem hiding this comment.
cancelled is also set when the component unmounts, because the cleanup runs then. So I think we can drop the isMounted checks in this effect and keep only cancelled.
Nambot 🤖 (powered by Claude Opus 5.5)
| const isMounted = useIsMountedRef(); | ||
|
|
||
| React.useEffect(() => { | ||
| if (skipLoading) { |
There was a problem hiding this comment.
Small bug: if read-only turns on while sources are loading, cancelled skips setLoadingSources(false), and this early return never resets it either. So loadingSources stays true. You can't see it in read-only mode, but the state is wrong. Maybe call setLoadingSources(false) before the return?
Also, skipLoading could be required. There's only one caller, and the other args are required?
Nambot 🤖 (powered by Claude Opus 5.5)
There was a problem hiding this comment.
Fixed in 14f6c97, with a test. skipLoading is required now.
| disabled={props.disabled || props.readOnly} | ||
| // Use key to reset internal state of Select when disabled changes, ensuring the dropdown menu is responsive. | ||
| key={String(props.disabled)} | ||
| key={String(props.disabled || props.readOnly)} |
There was a problem hiding this comment.
nit: The String() is fine, since key can't be a boolean. But props.disabled || props.readOnly is written twice. Could we put it in one const and use that for both disabled and key?
Nambot 🤖 (powered by Claude Opus 5.5)
| <MenuItem key={1} onClick={() => handleRemoveLayer()}> | ||
| {labelDetach} | ||
| </MenuItem>, | ||
| ...(readOnly |
There was a problem hiding this comment.
nit: the spread with an empty array is a bit hard to read. Maybe put each menu item in a named const first, then build the list as [zoomItem, ...(readOnly ? [] : [detachItem]), sliderItem]?
| "@itwin/map-layers": minor | ||
| --- | ||
|
|
||
| Added `MapLayerOptions.readOnly` to show the Map Layers widget in read-only mode, e.g. while the application is offline. Attached layers can still be shown, hidden and made transparent, but layers cannot be attached, detached, reordered or signed in to, and the base map provider and map settings cannot be changed. Base map visibility, transparency and color stay editable. Map layer sources are not loaded. Defaults to `false`, so existing consumers are not affected. |
There was a problem hiding this comment.
nit: this repeats most of the doc comment from Interfaces.ts. One sentence plus the example is probably enough
… fix loading state; review nits


Summary
Adds an optional
MapLayerOptions.readOnlyflag so desktop apps can keep the Map Layers widget visible while offline instead of hiding it.Today iTwin Studio hides the widget when offline, because attach, base map provider change, sign-in and map settings need the network. With
readOnly: true:Map layer sources (
MapLayerSources.create,MapLayerPreferences.getSources) are not loaded in read-only mode; they load again when the flag switches back tofalse.Compatibility
readOnlyis optional and defaults tofalse; no existing behavior, props or exports change. A test covers that all actions are still shown when it is not set.Usage
Open question
Should read-only allow switching the base map to "Color fill" (works offline)? This PR disables the provider select entirely to keep the change minimal.
Testing
MapLayerManager.test.tsx(read-only mode: hidden actions, layer menu, visibility controls, no source loading, default unchanged).pnpm build,pnpm test,pnpm lint,pnpm format,cspellpass locally.