$urls[] = $url;
}
- $url = new SitemapUrl();
- $url->path = '/map';
- $url->lastmod = TechniqueMap::latest()->first()->created_at;
- $url->changefreq = 'monthly';
- $url->priority = 0.9;
- $urls[] = $url;
+ foreach (['lw', 'dw', 'sp', 'uw'] as $map) {
+ $tech = TechniqueMap::where('map', '=', $map)->latest()->first();
+ $url = new SitemapUrl();
+ $url->path = '/map/'.$map;
+ if ($tech) {
+ $url->lastmod = $tech->created_at;
+ }
+ $url->changefreq = 'monthly';
+ $url->priority = 0.9;
+ $urls[] = $url;
+ }
$url = new SitemapUrl();
$url->path = '/tech';
{t('menu.tech')}
</Nav.Link>
</LinkContainer>
- <LinkContainer to="/map">
- <Nav.Link href="/map">
+ <LinkContainer to="/map/lw">
+ <Nav.Link href="/map/lw">
{t('menu.map')}
</Nav.Link>
</LinkContainer>
path="locations/:name"
element={<Technique namespace="locations" type="location" />}
/>
- <Route path="map" element={<Map />} />
+ <Route path="map">
+ <Route index element={<Navigate replace to="lw" />} />
+ <Route path=":activeMap" element={<Map />} />
+ </Route>
<Route
path="modes"
element={<Techniques namespace="modes" type="mode" />}
import React from 'react';
import { Button } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
-import { Link } from 'react-router-dom';
+import { Link, useSearchParams } from 'react-router-dom';
import { useOpenSeadragon } from './OpenSeadragon';
import Icon from '../common/Icon';
const Item = ({ pin }) => {
const { viewer } = useOpenSeadragon();
+ const [, setSearchParams] = useSearchParams();
const { t } = useTranslation();
const goToLocation = React.useCallback(pin => {
- if (viewer && viewer.viewport) {
- viewer.viewport.panTo(new OpenSeadragon.Point(pin.x, pin.y));
- viewer.viewport.zoomTo(4);
+ setSearchParams({ x: pin.x, y: pin.y, z: 4 });
+ if (viewer && viewer.element) {
viewer.element.scrollIntoView();
}
}, [viewer]);
import OpenSeadragon from 'openseadragon';
import PropTypes from 'prop-types';
import React from 'react';
+import { useNavigate, useParams } from 'react-router';
+import { createSearchParams, useSearchParams } from 'react-router-dom';
export const Context = React.createContext({});
export const useOpenSeadragon = () => React.useContext(Context);
export const Provider = React.forwardRef(({ children }, ref) => {
- const [activeMap, setActiveMap] = React.useState('lw');
+ const { activeMap } = useParams();
+ const navigate = useNavigate();
+ const [searchParams, setSearchParams] = useSearchParams();
const [pins, setPins] = React.useState([]);
const [viewer, setViewer] = React.useState(null);
+ const storePosition = React.useCallback(() => {
+ if (!viewer || !viewer.viewport) return;
+ const center = viewer.viewport.getCenter();
+ const zoom = viewer.viewport.getZoom();
+ setSearchParams({ x: center.x, y: center.y, z: zoom }, { replace: true });
+ }, [setSearchParams, viewer]);
+
+ const setActiveMap = React.useCallback(map => {
+ if (viewer && viewer.viewport) {
+ const center = viewer.viewport.getCenter();
+ const zoom = viewer.viewport.getZoom();
+ const params = { x: center.x, y: center.y, z: zoom };
+ navigate(
+ { pathname: `../${map}`, search: `?${createSearchParams(params)}` },
+ { replace: true },
+ );
+ } else {
+ navigate(`../${map}`, { replace: true });
+ }
+ }, [navigate, viewer]);
+
+ React.useEffect(() => {
+ if (!viewer || !viewer.viewport) return;
+ if (searchParams.has('x') && searchParams.has('y')) {
+ viewer.viewport.panTo(new OpenSeadragon.Point(
+ parseFloat(searchParams.get('x')),
+ parseFloat(searchParams.get('y')),
+ ));
+ }
+ if (searchParams.has('z')) {
+ viewer.viewport.zoomTo(parseFloat(searchParams.get('z')));
+ }
+ }, [searchParams, viewer]);
+
React.useEffect(() => {
if (!ref.current) return;
};
}, [activeMap, viewer]);
- return <Context.Provider value={{ activeMap, setActiveMap, pins, viewer }}>
+ return <Context.Provider value={{ activeMap, pins, setActiveMap, storePosition, viewer }}>
{children}
</Context.Provider>;
});
import React from 'react';
import { Link, useNavigate } from 'react-router-dom';
+import { useOpenSeadragon } from './OpenSeadragon';
import Overlay from './Overlay';
import Popover from './Popover';
import Icon from '../common/Icon';
import i18n from '../../i18n';
const Pin = ({ pin }) => {
+ const { storePosition } = useOpenSeadragon();
const [showPopover, setShowPopover] = React.useState(false);
const ref = React.useRef();
const onClick = React.useCallback((e) => {
if (ref.current && ref.current.contains(e.originalTarget)) {
if (e.originalTarget.tagName === 'A') {
+ storePosition();
navigate(new URL(e.originalTarget.href).pathname);
}
} else {
if (pin.technique.type === 'location') {
setShowPopover(s => !s);
} else {
+ storePosition();
navigate(getLink(pin.technique));
}
}
import { Container } from 'react-bootstrap';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';
+import { useParams } from 'react-router';
import CanonicalLinks from '../common/CanonicalLinks';
import Buttons from '../map/Buttons';
const Map = () => {
const [uwOverlay, setUWOverlay] = React.useState(false);
+ const { activeMap } = useParams();
const container = React.useRef();
const { t } = useTranslation();
<title>{t('map.heading')}</title>
<meta name="description" content={t('map.description')} />
</Helmet>
- <CanonicalLinks base="/map" />
+ <CanonicalLinks base={`/map/${activeMap}`} />
<OpenSeadragon ref={container}>
<div className="d-flex align-items-start justify-content-between">
<h1>{t('map.heading')}</h1>
@foreach ($urls as $url)
<url>
<loc>{{ url($url->path) }}</loc>
- <lastmod>{{ $url->lastmod->tz('UTC')->toAtomString() }}</lastmod>
+ @if ($url->lastmod)
+ <lastmod>{{ $url->lastmod->tz('UTC')->toAtomString() }}</lastmod>
+ @endif
<changefreq>{{ $url->changefreq }}</changefreq>
<priority>{{ $url->priority }}</priority>
</url>