2021-12-06 09:45:12 +00:00
|
|
|
/*
|
|
|
|
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
|
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
|
limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import React from 'react';
|
|
|
|
|
import maplibregl from 'maplibre-gl';
|
2021-12-09 09:10:23 +00:00
|
|
|
import { logger } from "matrix-js-sdk/src/logger";
|
2021-12-15 16:54:11 +00:00
|
|
|
import { LOCATION_EVENT_TYPE } from 'matrix-js-sdk/src/@types/location';
|
2021-12-09 09:10:23 +00:00
|
|
|
|
2021-12-06 09:45:12 +00:00
|
|
|
import SdkConfig from '../../../SdkConfig';
|
|
|
|
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
|
|
|
|
import { IBodyProps } from "./IBodyProps";
|
|
|
|
|
import { _t } from '../../../languageHandler';
|
|
|
|
|
|
|
|
|
|
interface IState {
|
|
|
|
|
error: Error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@replaceableComponent("views.messages.MLocationBody")
|
|
|
|
|
export default class MLocationBody extends React.Component<IBodyProps, IState> {
|
|
|
|
|
private map: maplibregl.Map;
|
|
|
|
|
private coords: GeolocationCoordinates;
|
|
|
|
|
private description: string;
|
|
|
|
|
|
|
|
|
|
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();
|
2021-12-15 16:54:11 +00:00
|
|
|
const loc = content[LOCATION_EVENT_TYPE.name];
|
|
|
|
|
const uri = loc ? loc.uri : content['geo_uri'];
|
2021-12-06 09:45:12 +00:00
|
|
|
|
2021-12-16 14:20:55 +00:00
|
|
|
this.coords = parseGeoUri(uri);
|
2021-12-06 09:45:12 +00:00
|
|
|
this.state = {
|
|
|
|
|
error: undefined,
|
|
|
|
|
};
|
|
|
|
|
|
2021-12-15 16:54:11 +00:00
|
|
|
this.description = loc?.description ?? content['body'];
|
2021-12-06 09:45:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
const config = SdkConfig.get();
|
2021-12-17 12:26:02 +00:00
|
|
|
const coordinates = new maplibregl.LngLat(
|
|
|
|
|
this.coords.longitude, this.coords.latitude);
|
2021-12-06 10:13:06 +00:00
|
|
|
|
2021-12-06 09:45:12 +00:00
|
|
|
this.map = new maplibregl.Map({
|
|
|
|
|
container: this.getBodyId(),
|
|
|
|
|
style: config.map_style_url,
|
2021-12-06 10:13:06 +00:00
|
|
|
center: coordinates,
|
2021-12-06 09:45:12 +00:00
|
|
|
zoom: 13,
|
|
|
|
|
});
|
|
|
|
|
|
2021-12-06 10:13:06 +00:00
|
|
|
new maplibregl.Popup({
|
|
|
|
|
closeButton: false,
|
|
|
|
|
closeOnClick: false,
|
|
|
|
|
closeOnMove: false,
|
|
|
|
|
})
|
|
|
|
|
.setLngLat(coordinates)
|
|
|
|
|
.setHTML(this.description)
|
2021-12-06 09:45:12 +00:00
|
|
|
.addTo(this.map);
|
|
|
|
|
|
|
|
|
|
this.map.on('error', (e)=>{
|
2021-12-17 12:26:02 +00:00
|
|
|
logger.error(
|
|
|
|
|
"Failed to load map: check map_style_url in config.json has a "
|
|
|
|
|
+ "valid URL and API key",
|
|
|
|
|
e.error,
|
|
|
|
|
);
|
2021-12-06 09:45:12 +00:00
|
|
|
this.setState({ error: e.error });
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private getBodyId = () => {
|
|
|
|
|
return `mx_MLocationBody_${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;
|
|
|
|
|
|
|
|
|
|
return <div className="mx_MLocationBody">
|
|
|
|
|
<div id={this.getBodyId()} className="mx_MLocationBody_map" />
|
|
|
|
|
{ error }
|
|
|
|
|
</div>;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-12-16 14:20:55 +00:00
|
|
|
|
|
|
|
|
export function parseGeoUri(uri: string): GeolocationCoordinates {
|
2021-12-17 09:50:27 +00:00
|
|
|
function parse(s: string): number {
|
|
|
|
|
const ret = parseFloat(s);
|
|
|
|
|
if (Number.isNaN(ret)) {
|
|
|
|
|
return undefined;
|
|
|
|
|
} else {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-16 14:20:55 +00:00
|
|
|
const m = uri.match(/^\s*geo:(.*?)\s*$/);
|
|
|
|
|
if (!m) return;
|
|
|
|
|
const parts = m[1].split(';');
|
|
|
|
|
const coords = parts[0].split(',');
|
|
|
|
|
let uncertainty: number;
|
|
|
|
|
for (const param of parts.slice(1)) {
|
|
|
|
|
const m = param.match(/u=(.*)/);
|
2021-12-17 09:50:27 +00:00
|
|
|
if (m) uncertainty = parse(m[1]);
|
2021-12-16 14:20:55 +00:00
|
|
|
}
|
|
|
|
|
return {
|
2021-12-17 09:50:27 +00:00
|
|
|
latitude: parse(coords[0]),
|
|
|
|
|
longitude: parse(coords[1]),
|
|
|
|
|
altitude: parse(coords[2]),
|
2021-12-16 14:20:55 +00:00
|
|
|
accuracy: uncertainty,
|
|
|
|
|
altitudeAccuracy: undefined,
|
|
|
|
|
heading: undefined,
|
|
|
|
|
speed: undefined,
|
|
|
|
|
};
|
|
|
|
|
}
|