Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 9ba55d1

Browse files
author
Kerry
authored
Live location sharing - consolidate maps (#8236)
* extract location markers into generic Marker Signed-off-by: Kerry Archibald <[email protected]> * wrap marker in smartmarker Signed-off-by: Kerry Archibald <[email protected]> * test smartmarker Signed-off-by: Kerry Archibald <[email protected]> * working map in location body Signed-off-by: Kerry Archibald <[email protected]> * test Map Signed-off-by: Kerry Archibald <[email protected]> * remove skinned sdk Signed-off-by: Kerry Archibald <[email protected]> * update snaps with new mocks Signed-off-by: Kerry Archibald <[email protected]> * use new ZoomButtons in MLocationBody Signed-off-by: Kerry Archibald <[email protected]> * make LocationViewDialog map interactive Signed-off-by: Kerry Archibald <[email protected]> * test MLocationBody Signed-off-by: Kerry Archibald <[email protected]> * test LocationViewDialog Signed-off-by: Kerry Archibald <[email protected]> * add copyrights, shrink snapshot Signed-off-by: Kerry Archibald <[email protected]> * update comment Signed-off-by: Kerry Archibald <[email protected]> * lint Signed-off-by: Kerry Archibald <[email protected]> * lint Signed-off-by: Kerry Archibald <[email protected]>
1 parent 944e11d commit 9ba55d1

File tree

16 files changed

+889
-234
lines changed

16 files changed

+889
-234
lines changed

__mocks__/maplibre-gl.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ class MockMap extends EventEmitter {
66
removeControl = jest.fn();
77
zoomIn = jest.fn();
88
zoomOut = jest.fn();
9+
setCenter = jest.fn();
10+
setStyle = jest.fn();
911
}
1012
const MockMapInstance = new MockMap();
1113

res/css/views/dialogs/_LocationViewDialog.scss

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -48,49 +48,10 @@ limitations under the License.
4848
background-color: $dialog-close-external-color;
4949
}
5050
}
51+
}
5152

52-
.mx_MLocationBody {
53-
position: absolute;
54-
55-
.mx_MLocationBody_map {
56-
width: 80vw;
57-
height: 80vh;
58-
}
59-
60-
.mx_MLocationBody_zoomButtons {
61-
position: absolute;
62-
display: grid;
63-
grid-template-columns: auto;
64-
grid-row-gap: 8px;
65-
66-
right: 24px;
67-
bottom: 48px;
68-
69-
.mx_AccessibleButton {
70-
background-color: $background;
71-
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.25);
72-
border-radius: 4px;
73-
width: 24px;
74-
height: 24px;
75-
76-
.mx_MLocationBody_zoomButton {
77-
background-color: $primary-content;
78-
margin: 4px;
79-
width: 16px;
80-
height: 16px;
81-
mask-repeat: no-repeat;
82-
mask-size: contain;
83-
mask-position: center;
84-
}
85-
86-
.mx_MLocationBody_plusButton {
87-
mask-image: url('$(res)/img/element-icons/plus-button.svg');
88-
}
89-
90-
.mx_MLocationBody_minusButton {
91-
mask-image: url('$(res)/img/element-icons/minus-button.svg');
92-
}
93-
}
94-
}
95-
}
53+
.mx_LocationViewDialog_map {
54+
width: 80vw;
55+
height: 80vh;
56+
border-radius: 8px;
9657
}

src/components/views/location/LocationPicker.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
225225
return (
226226
<div className="mx_LocationPicker">
227227
<div id="mx_LocationPicker_map" />
228+
228229
{ this.props.shareType === LocationShareType.Pin && <div className="mx_LocationPicker_pinText">
229230
<span>
230231
{ this.state.position ? _t("Click to move the pin") : _t("Click to drop a pin") }

src/components/views/location/LocationViewDialog.tsx

Lines changed: 32 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ limitations under the License.
1616

1717
import React from 'react';
1818
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
19-
import { ClientEvent, IClientWellKnown, MatrixClient } from 'matrix-js-sdk/src/client';
19+
import { MatrixClient } from 'matrix-js-sdk/src/client';
2020

2121
import BaseDialog from "../dialogs/BaseDialog";
2222
import { IDialogProps } from "../dialogs/IDialogProps";
23-
import { LocationBodyContent } from '../messages/MLocationBody';
24-
import { tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
25-
import { parseGeoUri, locationEventGeoUri, createMapWithCoords } from '../../../utils/location';
23+
import { locationEventGeoUri, isSelfLocation } from '../../../utils/location';
24+
import Map from './Map';
25+
import SmartMarker from './SmartMarker';
26+
import ZoomButtons from './ZoomButtons';
2627

2728
interface IProps extends IDialogProps {
2829
matrixClient: MatrixClient;
@@ -34,78 +35,54 @@ interface IState {
3435
}
3536

3637
export default class LocationViewDialog extends React.Component<IProps, IState> {
37-
private coords: GeolocationCoordinates;
38-
private map?: maplibregl.Map;
39-
4038
constructor(props: IProps) {
4139
super(props);
4240

43-
this.coords = parseGeoUri(locationEventGeoUri(this.props.mxEvent));
44-
this.map = null;
4541
this.state = {
4642
error: undefined,
4743
};
4844
}
4945

50-
componentDidMount() {
51-
if (this.state.error) {
52-
return;
53-
}
54-
55-
this.props.matrixClient.on(ClientEvent.ClientWellKnown, this.updateStyleUrl);
56-
57-
this.map = createMapWithCoords(
58-
this.coords,
59-
true,
60-
this.getBodyId(),
61-
this.getMarkerId(),
62-
(e: Error) => this.setState({ error: e }),
63-
);
64-
}
65-
66-
componentWillUnmount() {
67-
this.props.matrixClient.off(ClientEvent.ClientWellKnown, this.updateStyleUrl);
68-
}
69-
70-
private updateStyleUrl = (clientWellKnown: IClientWellKnown) => {
71-
const style = tileServerFromWellKnown(clientWellKnown)?.["map_style_url"];
72-
if (style) {
73-
this.map?.setStyle(style);
74-
}
75-
};
76-
7746
private getBodyId = () => {
7847
return `mx_LocationViewDialog_${this.props.mxEvent.getId()}`;
7948
};
8049

81-
private getMarkerId = () => {
82-
return `mx_MLocationViewDialog_marker_${this.props.mxEvent.getId()}`;
83-
};
84-
85-
private onZoomIn = () => {
86-
this.map?.zoomIn();
87-
};
88-
89-
private onZoomOut = () => {
90-
this.map?.zoomOut();
50+
private onError = (error) => {
51+
this.setState({ error });
9152
};
9253

9354
render() {
55+
const { mxEvent } = this.props;
56+
57+
// only pass member to marker when should render avatar marker
58+
const markerRoomMember = isSelfLocation(mxEvent.getContent()) ? mxEvent.sender : undefined;
59+
const geoUri = locationEventGeoUri(mxEvent);
9460
return (
9561
<BaseDialog
9662
className='mx_LocationViewDialog'
9763
onFinished={this.props.onFinished}
9864
fixedWidth={false}
9965
>
100-
<LocationBodyContent
101-
mxEvent={this.props.mxEvent}
102-
bodyId={this.getBodyId()}
103-
markerId={this.getMarkerId()}
104-
error={this.state.error}
105-
zoomButtons={true}
106-
onZoomIn={this.onZoomIn}
107-
onZoomOut={this.onZoomOut}
108-
/>
66+
<Map
67+
id={this.getBodyId()}
68+
centerGeoUri={geoUri}
69+
onError={this.onError}
70+
interactive
71+
className="mx_LocationViewDialog_map"
72+
>
73+
{
74+
({ map }) =>
75+
<>
76+
<SmartMarker
77+
map={map}
78+
id={`${this.getBodyId()}-marker`}
79+
geoUri={geoUri}
80+
roomMember={markerRoomMember}
81+
/>
82+
<ZoomButtons map={map} />
83+
</>
84+
}
85+
</Map>
10986
</BaseDialog>
11087
);
11188
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import React, { ReactNode, useContext, useEffect } from 'react';
18+
import classNames from 'classnames';
19+
import { ClientEvent, IClientWellKnown } from 'matrix-js-sdk/src/matrix';
20+
import { logger } from 'matrix-js-sdk/src/logger';
21+
22+
import MatrixClientContext from '../../../contexts/MatrixClientContext';
23+
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
24+
import { parseGeoUri } from '../../../utils/location';
25+
import { tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
26+
import { useMap } from '../../../utils/location/useMap';
27+
28+
const useMapWithStyle = ({ id, centerGeoUri, onError, interactive }) => {
29+
const bodyId = `mx_Map_${id}`;
30+
31+
// style config
32+
const context = useContext(MatrixClientContext);
33+
const mapStyleUrl = useEventEmitterState(
34+
context,
35+
ClientEvent.ClientWellKnown,
36+
(clientWellKnown: IClientWellKnown) => tileServerFromWellKnown(clientWellKnown)?.["map_style_url"],
37+
);
38+
39+
const map = useMap({ interactive, bodyId, onError });
40+
41+
useEffect(() => {
42+
if (mapStyleUrl && map) {
43+
map.setStyle(mapStyleUrl);
44+
}
45+
}, [mapStyleUrl, map]);
46+
47+
useEffect(() => {
48+
if (map && centerGeoUri) {
49+
try {
50+
const coords = parseGeoUri(centerGeoUri);
51+
map.setCenter({ lon: coords.longitude, lat: coords.latitude });
52+
} catch (error) {
53+
logger.error('Could not set map center', centerGeoUri);
54+
}
55+
}
56+
}, [map, centerGeoUri]);
57+
58+
return {
59+
map,
60+
bodyId,
61+
};
62+
};
63+
64+
interface MapProps {
65+
id: string;
66+
interactive?: boolean;
67+
centerGeoUri?: string;
68+
className?: string;
69+
onClick?: () => void;
70+
onError?: (error: Error) => void;
71+
children?: (renderProps: {
72+
map: maplibregl.Map;
73+
}) => ReactNode;
74+
}
75+
76+
const Map: React.FC<MapProps> = ({
77+
centerGeoUri, className, id, onError, onClick, children, interactive,
78+
}) => {
79+
const { map, bodyId } = useMapWithStyle({ centerGeoUri, onError, id, interactive });
80+
81+
const onMapClick = (
82+
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
83+
) => {
84+
// Eat click events when clicking the attribution button
85+
const target = event.target as Element;
86+
if (target.classList.contains("maplibregl-ctrl-attrib-button")) {
87+
return;
88+
}
89+
90+
onClick && onClick();
91+
};
92+
93+
return <div className={classNames('mx_Map', className)}
94+
id={bodyId}
95+
onClick={onMapClick}
96+
>
97+
{ !!children && !!map && children({ map }) }
98+
</div>;
99+
};
100+
101+
export default Map;

0 commit comments

Comments
 (0)