Open map in a dialog when it is clicked (#7465)

This commit is contained in:
Andy Balaam 2022-01-07 14:54:45 +00:00 committed by GitHub
parent 59ef2704f0
commit 707f8cd878
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 286 additions and 67 deletions

View file

@ -25,6 +25,8 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
import { IBodyProps } from "./IBodyProps";
import { _t } from '../../../languageHandler';
import MemberAvatar from '../avatars/MemberAvatar';
import Modal from '../../../Modal';
import LocationViewDialog from '../location/LocationViewDialog';
interface IState {
error: Error;
@ -32,54 +34,29 @@ interface IState {
@replaceableComponent("views.messages.MLocationBody")
export default class MLocationBody extends React.Component<IBodyProps, IState> {
private map: maplibregl.Map;
private coords: GeolocationCoordinates;
constructor(props: IBodyProps) {
super(props);
// unfortunately we're stuck supporting legacy `content.geo_uri`
// events until the end of days, or until we figure out mutable
// events - so folks can read their old chat history correctly.
// https://github.com/matrix-org/matrix-doc/issues/3516
const content = this.props.mxEvent.getContent();
const loc = content[LOCATION_EVENT_TYPE.name];
const uri = loc ? loc.uri : content['geo_uri'];
this.coords = parseGeoUri(uri);
this.coords = parseGeoUri(locationEventGeoUri(this.props.mxEvent));
this.state = {
error: undefined,
};
}
componentDidMount() {
const config = SdkConfig.get();
const coordinates = new maplibregl.LngLat(
this.coords.longitude, this.coords.latitude);
if (this.state.error) {
return;
}
this.map = new maplibregl.Map({
container: this.getBodyId(),
style: config.map_style_url,
center: coordinates,
zoom: 13,
});
new maplibregl.Marker({
element: document.getElementById(this.getMarkerId()),
anchor: 'bottom',
offset: [0, -1],
})
.setLngLat(coordinates)
.addTo(this.map);
this.map.on('error', (e)=>{
logger.error(
"Failed to load map: check map_style_url in config.json has a "
+ "valid URL and API key",
e.error,
);
this.setState({ error: e.error });
});
createMap(
this.coords,
false,
this.getBodyId(),
this.getMarkerId(),
(e: Error) => this.setState({ error: e }),
);
}
private getBodyId = () => {
@ -90,33 +67,127 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
return `mx_MLocationBody_marker_${this.props.mxEvent.getId()}`;
};
render() {
const error = this.state.error ?
<div className="mx_EventTile_tileError mx_EventTile_body">
{ _t("Failed to load map") }
</div> : null;
private onClick = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
) => {
// Don't open map if we clicked the attribution button
const target = event.target as Element;
if (target.classList.contains("maplibregl-ctrl-attrib-button")) {
return;
}
return <div className="mx_MLocationBody">
<div id={this.getBodyId()} className="mx_MLocationBody_map" />
{ error }
<div className="mx_MLocationBody_marker" id={this.getMarkerId()}>
<div className="mx_MLocationBody_markerBorder">
<MemberAvatar
member={this.props.mxEvent.sender}
width={27}
height={27}
viewUserOnClick={false}
/>
Modal.createTrackedDialog(
'Location View',
'',
LocationViewDialog,
{ mxEvent: this.props.mxEvent },
"mx_LocationViewDialog_wrapper",
false, // isPriority
true, // isStatic
);
};
render() {
return <LocationBodyContent
mxEvent={this.props.mxEvent}
bodyId={this.getBodyId()}
markerId={this.getMarkerId()}
error={this.state.error}
onClick={this.onClick}
/>;
}
}
interface ILocationBodyContentProps {
mxEvent: MatrixEvent;
bodyId: string;
markerId: string;
error: Error;
onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
}
export function LocationBodyContent(props: ILocationBodyContentProps) {
return <div className="mx_MLocationBody">
{
props.error
? <div className="mx_EventTile_tileError mx_EventTile_body">
{ _t("Failed to load map") }
</div>
<img
className="mx_MLocationBody_pointer"
src={require("../../../../res/img/location/pointer.svg")}
width="9"
height="5"
: null
}
<div
id={props.bodyId}
onClick={props.onClick}
className="mx_MLocationBody_map"
/>
<div className="mx_MLocationBody_marker" id={props.markerId}>
<div className="mx_MLocationBody_markerBorder">
<MemberAvatar
member={props.mxEvent.sender}
width={27}
height={27}
viewUserOnClick={false}
/>
</div>
</div>;
}
<img
className="mx_MLocationBody_pointer"
src={require("../../../../res/img/location/pointer.svg")}
width="9"
height="5"
/>
</div>
</div>;
}
export function createMap(
coords: GeolocationCoordinates,
interactive: boolean,
bodyId: string,
markerId: string,
onError: (error: Error) => void,
): maplibregl.Map {
const styleUrl = SdkConfig.get().map_style_url;
const coordinates = new maplibregl.LngLat(coords.longitude, coords.latitude);
const map = new maplibregl.Map({
container: bodyId,
style: styleUrl,
center: coordinates,
zoom: 13,
interactive,
});
new maplibregl.Marker({
element: document.getElementById(markerId),
anchor: 'bottom',
offset: [0, -1],
})
.setLngLat(coordinates)
.addTo(map);
map.on('error', (e) => {
logger.error(
"Failed to load map: check map_style_url in config.json has a "
+ "valid URL and API key",
e.error,
);
onError(e.error);
});
return map;
}
/**
* Find the geo-URI contained within a location event.
*/
export function locationEventGeoUri(mxEvent: MatrixEvent): string {
// unfortunately we're stuck supporting legacy `content.geo_uri`
// events until the end of days, or until we figure out mutable
// events - so folks can read their old chat history correctly.
// https://github.com/matrix-org/matrix-doc/issues/3516
const content = mxEvent.getContent();
const loc = LOCATION_EVENT_TYPE.findIn(content) as { uri?: string };
return loc ? loc.uri : content['geo_uri'];
}
export function parseGeoUri(uri: string): GeolocationCoordinates {