Skip to content

Commit

Permalink
Implemented the ideas described in #1543:
Browse files Browse the repository at this point in the history
- Semi-major rewrite of the plugin, as it turned out that a listener (for the event on globalObserver) and a callback (to the DialogWindowPlugin component) were needed.
- Also some bugfixes related to restoring the original background layer.
- Certainly, still room for improvement.
  • Loading branch information
jacobwod committed Aug 30, 2024
1 parent f8a4d52 commit b850bae
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 54 deletions.
6 changes: 6 additions & 0 deletions apps/client/src/components/Dialog/Dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default function ResponsiveDialog(props) {
children,
onAbort,
onClose,
onVisibilityChanged,
open,
options: {
abortText,
Expand Down Expand Up @@ -51,6 +52,11 @@ export default function ResponsiveDialog(props) {
onClose(promptText);
};

// This mechanism allows us to propagate the current
// visibility state of the dialog and send back, using a callback
// function, to parent component.
typeof onVisibilityChanged === "function" && onVisibilityChanged(open);

return (
<Dialog
aria-labelledby="responsive-dialog-title"
Expand Down
3 changes: 2 additions & 1 deletion apps/client/src/plugins/DialogWindowPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class DialogWindowPlugin extends React.PureComponent {
};

#onClose = () => {
typeof this.opts.onClose === "function" && this.opts.onAClose();
typeof this.opts.onClose === "function" && this.opts.onClose();
this.setState({
dialogOpen: false,
});
Expand All @@ -148,6 +148,7 @@ class DialogWindowPlugin extends React.PureComponent {
open={this.state.dialogOpen}
onClose={this.#onClose}
onAbort={this.#onAbort}
onVisibilityChanged={this.opts.onVisibilityChanged}
>
{this.props.children}
</Dialog>,
Expand Down
158 changes: 105 additions & 53 deletions apps/client/src/plugins/LayerComparer/LayerComparer.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
import React, { useEffect, useRef, useState } from "react";
import BaseLayer from "ol/layer/Base";
import { Alert, Button, Stack } from "@mui/material";
import DialogWindowPlugin from "../../plugins/DialogWindowPlugin";
import CompareIcon from "@mui/icons-material/Compare";
import { useSnackbar } from "notistack";

import DialogWindowPlugin from "../../plugins/DialogWindowPlugin";
import SelectDropdown from "./SelectDropdown.js";
import SDSControl from "./CustomOLControl.js";

const LayerComparer = (props) => {
const [layer1, setLayer1] = useState("");
const [layer2, setLayer2] = useState("");
const [layerId1, setLayerId1] = useState("");
const [layerId2, setLayerId2] = useState("");

const [layers, setLayers] = useState([]);
const [baseLayers, setBaseLayers] = useState([]);

// Prepare a ref that will hold our map control
const sds = useRef();

// Prepare a ref that will hold the ID of the original
// background layer. This makes it possible to restore
// Two more refs that will hold the OL layer objects
const l1 = useRef();
const l2 = useRef();

// Prepare a ref that will hold the original OL background
// layer object. This makes it possible to restore
// to the same background when user closes the comparer.
const oldBackgroundLayer = useRef();

// When compare mode is active, we want to show a snackbar that
// allows user to simply disable the comparer.
const { closeSnackbar, enqueueSnackbar } = useSnackbar();

// We don't want to prompt the user with more than one snack, so lets track the current one,
// so that we can close it when another one is about to open.
const helperSnack = React.useRef(null);
Expand Down Expand Up @@ -72,67 +78,111 @@ const LayerComparer = (props) => {
// If both compare layers are empty, we do the contrary and remove the control
// and restore the original background layer.
useEffect(() => {
if (layer1 === "" || layer2 === "") {
// Show previous background
oldBackgroundLayer.current?.setVisible(true);
// If layer1 or layer2 changed, it means that the Dialog is visible
// and we never want to show the snackbar simultaneously. Let's close it.
closeSnackbar(helperSnack.current);

// Remove the slider as soon as one of the compare layers is not selected
sds.current.remove();

// Close the snackbar
closeSnackbar(helperSnack.current);
if (layerId1 === "" || layerId2 === "") {
// If any of the layer dropdowns is empty, we can't compare.
resetSdsAndOl();
} else {
const l1 = props.map.getAllLayers().find((l) => l.ol_uid === layer1);
const l2 = props.map.getAllLayers().find((l) => l.ol_uid === layer2);
// If both IDs are set, we can attempt to grab the layers from the map
// and start comparing.
l1.current = props.map.getAllLayers().find((l) => l.ol_uid === layerId1);
l2.current = props.map.getAllLayers().find((l) => l.ol_uid === layerId2);

// Hide old background layers
// Let's save the original background layer, so we can restore it later
oldBackgroundLayer.current = props.map
.getAllLayers()
.find((l) => l.getVisible() === true && l.get("layerType") === "base");
// Also, let's hide it for now
oldBackgroundLayer.current?.setVisible(false);

// Activate the compare OL control
sds.current.open();
sds.current.setCompareLayers(l1, l2);

// Show the snackbar, but ensure that only one snackbar exists
closeSnackbar(helperSnack.current);
helperSnack.current = enqueueSnackbar(
"Avsluta jämföringsläget genom att trycka på knappen",
{
variant: "default",
persist: true,
anchorOrigin: { vertical: "bottom", horizontal: "center" },
sx: {
// Custom styling to follow Material Design guidelines for Snackbar.
// Placing the close button to the right of the text.
".SnackbarItem-contentRoot": {
flexWrap: "inherit !important",
},
sds.current.setCompareLayers(l1.current, l2.current);
}
}, [layerId1, layerId2, props.map, closeSnackbar]);

const resetSdsAndOl = () => {
// Remove the ref to our OL control
sds.current.remove();

// Let's hide compare layers in Map
l1.current?.setVisible(false);
l2.current?.setVisible(false);

// Show original background layer
oldBackgroundLayer.current?.setVisible(true);
};

const onVisibilityChanged = (visible) => {
// If the Dialog becomes visible, but there already is a snackbar,
// we must close it in order to avoid duplicate snackbars.
if (visible === true && helperSnack.current !== null) {
// This ugly hack is needed to avoid warnings due to a race condition
// in React's render.
setTimeout(() => {
closeSnackbar(helperSnack.current);
});
}
};

const onAbort = () => {
// Unsetting these state variables will cleanup the UI
// as well as trigger the useEffect above to run and
// take rest of remaining cleanups (once both variables
// are empty strings).
setLayerId1("");
setLayerId2("");
};

// onClose is actually the callback that runs when user
// clicks the primary action button in the Dialog, i.e. "Compare".
const onClose = () => {
// Ensure that there are real layers to compare
if (l1.current instanceof BaseLayer && l2.current instanceof BaseLayer) {
helperSnack.current = enqueueSnackbar(null, {
variant: "default",
persist: true,
anchorOrigin: { vertical: "bottom", horizontal: "center" },
sx: {
// Custom styling to follow Material Design guidelines for Snackbar.
// Placing the close button to the right of the text.
".SnackbarItem-contentRoot": {
flexWrap: "inherit !important",
},
action: (key) => (
// Since we don't have any text in the snackbar anymore, but the
// container is there, we want to remove padding from the actions container.
".SnackbarItem-action": {
paddingLeft: 0,
},
},
action: (key) => (
<>
<Button
variant="contained"
color="primary"
sx={{ mr: 1 }}
onClick={() => {
props.app.globalObserver.publish("layercomparer.showWindow");
}}
>
Välj andra lager
</Button>
<Button
variant="contained"
color="error"
onClick={() => {
onAbort();
closeSnackbar(key);
}}
>
Sluta jämföra
</Button>
),
}
);
</>
),
});
}
}, [layer1, layer2, props.map, closeSnackbar, enqueueSnackbar]);

// User can at any time abort the comparer, here's a handler
// that resets the UI.
const onAbort = () => {
sds.current.remove();
oldBackgroundLayer.current?.setVisible(true);
setLayer1("");
setLayer2("");
};

return (
Expand All @@ -150,7 +200,9 @@ const LayerComparer = (props) => {
buttonText: "Jämför",
primaryButtonVariant: "contained",
abortText: "Nollställ & stäng",
onAbort: onAbort,
onAbort: onAbort, // Called when user presses the Reset & Close button
onClose: onClose, // Called when user presses the main primary button
onVisibilityChanged: onVisibilityChanged, // Called when the dialog is shown or hidden
}}
>
<Stack spacing={2}>
Expand All @@ -159,17 +211,17 @@ const LayerComparer = (props) => {
</Alert>

<SelectDropdown
setter={setLayer1}
value={layer1}
counterValue={layer2}
setter={setLayerId1}
value={layerId1}
counterValue={layerId2}
baseLayers={baseLayers}
layers={layers}
label="Vänster sida"
/>
<SelectDropdown
setter={setLayer2}
value={layer2}
counterValue={layer1}
setter={setLayerId2}
value={layerId2}
counterValue={layerId1}
baseLayers={baseLayers}
layers={layers}
label="Höger sida"
Expand Down

0 comments on commit b850bae

Please sign in to comment.