Support sharing custom locations. (#7185)
Add the ability to click on the map to share a specific named location.
This commit is contained in:
parent
1262021417
commit
684b0617ae
3 changed files with 81 additions and 14 deletions
|
@ -41,7 +41,7 @@ const LocationShareTypeDropdown = ({
|
||||||
onChange,
|
onChange,
|
||||||
}: IDropdownProps) => {
|
}: IDropdownProps) => {
|
||||||
const options = [
|
const options = [
|
||||||
// <div key={LocationShareType.Custom}>{ _t("Share custom location") }</div>,
|
<div key={LocationShareType.Custom}>{ _t("Share custom location") }</div>,
|
||||||
<div key={LocationShareType.OnceOff}>{ _t("Share my current location as a once off") }</div>,
|
<div key={LocationShareType.OnceOff}>{ _t("Share my current location as a once off") }</div>,
|
||||||
// <div key={LocationShareType.OneMin}>{ _t("Share my current location for one minute") }</div>,
|
// <div key={LocationShareType.OneMin}>{ _t("Share my current location for one minute") }</div>,
|
||||||
// <div key={LocationShareType.FiveMins}>{ _t("Share my current location for five minutes") }</div>,
|
// <div key={LocationShareType.FiveMins}>{ _t("Share my current location for five minutes") }</div>,
|
||||||
|
@ -56,7 +56,9 @@ const LocationShareTypeDropdown = ({
|
||||||
return <Dropdown
|
return <Dropdown
|
||||||
id="mx_LocationShareTypeDropdown"
|
id="mx_LocationShareTypeDropdown"
|
||||||
className="mx_LocationShareTypeDropdown"
|
className="mx_LocationShareTypeDropdown"
|
||||||
onOptionChange={(key: string)=>{ onChange(LocationShareType[LocationShareType[parseInt(key)]]); }}
|
onOptionChange={(key: string) => {
|
||||||
|
onChange(LocationShareType[LocationShareType[parseInt(key)]]);
|
||||||
|
}}
|
||||||
menuWidth={width}
|
menuWidth={width}
|
||||||
label={label}
|
label={label}
|
||||||
value={value.toString()}
|
value={value.toString()}
|
||||||
|
@ -74,13 +76,14 @@ interface IState {
|
||||||
description: string;
|
description: string;
|
||||||
type: LocationShareType;
|
type: LocationShareType;
|
||||||
position?: GeolocationPosition;
|
position?: GeolocationPosition;
|
||||||
manual: boolean;
|
manualPosition?: GeolocationPosition;
|
||||||
error: Error;
|
error: Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.location.LocationPicker")
|
@replaceableComponent("views.location.LocationPicker")
|
||||||
class LocationPicker extends React.Component<IProps, IState> {
|
class LocationPicker extends React.Component<IProps, IState> {
|
||||||
private map: maplibregl.Map;
|
private map: maplibregl.Map;
|
||||||
|
private marker: maplibregl.Marker;
|
||||||
private geolocate: maplibregl.GeolocateControl;
|
private geolocate: maplibregl.GeolocateControl;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -90,7 +93,7 @@ class LocationPicker extends React.Component<IProps, IState> {
|
||||||
description: _t("My location"),
|
description: _t("My location"),
|
||||||
type: LocationShareType.OnceOff,
|
type: LocationShareType.OnceOff,
|
||||||
position: undefined,
|
position: undefined,
|
||||||
manual: false,
|
manualPosition: undefined,
|
||||||
error: undefined,
|
error: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -122,14 +125,54 @@ class LocationPicker extends React.Component<IProps, IState> {
|
||||||
this.geolocate.trigger();
|
this.geolocate.trigger();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.map.on('click', (e) => {
|
||||||
|
this.addMarker(e.lngLat);
|
||||||
|
this.storeManualPosition(e.lngLat);
|
||||||
|
this.setState({ type: LocationShareType.Custom });
|
||||||
|
});
|
||||||
|
|
||||||
this.geolocate.on('geolocate', this.onGeolocate);
|
this.geolocate.on('geolocate', this.onGeolocate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addMarker(lngLat: maplibregl.LngLat): void {
|
||||||
|
if (this.marker) return;
|
||||||
|
this.marker = new maplibregl.Marker({
|
||||||
|
draggable: true,
|
||||||
|
})
|
||||||
|
.setLngLat(lngLat)
|
||||||
|
.addTo(this.map)
|
||||||
|
.on('dragend', () => {
|
||||||
|
this.storeManualPosition(this.marker.getLngLat());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private removeMarker(): void {
|
||||||
|
if (!this.marker) return;
|
||||||
|
this.marker.remove();
|
||||||
|
this.marker = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private storeManualPosition(lngLat: maplibregl.LngLat): void {
|
||||||
|
const manualPosition: GeolocationPosition = {
|
||||||
|
coords: {
|
||||||
|
longitude: lngLat.lng,
|
||||||
|
latitude: lngLat.lat,
|
||||||
|
altitude: undefined,
|
||||||
|
accuracy: undefined,
|
||||||
|
altitudeAccuracy: undefined,
|
||||||
|
heading: undefined,
|
||||||
|
speed: undefined,
|
||||||
|
},
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
this.setState({ manualPosition });
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.geolocate.off('geolocate', this.onGeolocate);
|
this.geolocate.off('geolocate', this.onGeolocate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onGeolocate = (position) => {
|
private onGeolocate = (position: GeolocationPosition) => {
|
||||||
this.setState({ position });
|
this.setState({ position });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -146,9 +189,12 @@ class LocationPicker extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onOk = () => {
|
private onOk = () => {
|
||||||
|
const position = (this.state.type == LocationShareType.Custom) ?
|
||||||
|
this.state.manualPosition : this.state.position;
|
||||||
|
|
||||||
this.props.onChoose(
|
this.props.onChoose(
|
||||||
this.state.position ? this.getGeoUri(this.state.position) : undefined,
|
position ? this.getGeoUri(position) : undefined,
|
||||||
this.state.position ? this.state.position.timestamp : undefined,
|
position ? position.timestamp : undefined,
|
||||||
this.state.type,
|
this.state.type,
|
||||||
this.state.description,
|
this.state.description,
|
||||||
);
|
);
|
||||||
|
@ -156,6 +202,20 @@ class LocationPicker extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onTypeChange= (type: LocationShareType) => {
|
private onTypeChange= (type: LocationShareType) => {
|
||||||
|
if (type == LocationShareType.Custom) {
|
||||||
|
if (!this.state.manualPosition) {
|
||||||
|
this.setState({ manualPosition: this.state.position });
|
||||||
|
}
|
||||||
|
if (this.state.manualPosition) {
|
||||||
|
this.addMarker(new maplibregl.LngLat(
|
||||||
|
this.state.manualPosition?.coords.longitude,
|
||||||
|
this.state.manualPosition?.coords.latitude,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.removeMarker();
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({ type });
|
this.setState({ type });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -189,7 +249,7 @@ class LocationPicker extends React.Component<IProps, IState> {
|
||||||
<DialogButtons primaryButton={_t('Share')}
|
<DialogButtons primaryButton={_t('Share')}
|
||||||
onPrimaryButtonClick={this.onOk}
|
onPrimaryButtonClick={this.onOk}
|
||||||
onCancel={this.props.onFinished}
|
onCancel={this.props.onFinished}
|
||||||
disabled={!this.state.position} />
|
primaryDisabled={!this.state.position} />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -76,15 +76,22 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const config = SdkConfig.get();
|
const config = SdkConfig.get();
|
||||||
|
const coordinates = new maplibregl.LngLat(this.coords.longitude, this.coords.latitude);
|
||||||
|
|
||||||
this.map = new maplibregl.Map({
|
this.map = new maplibregl.Map({
|
||||||
container: this.getBodyId(),
|
container: this.getBodyId(),
|
||||||
style: config.map_style_url,
|
style: config.map_style_url,
|
||||||
center: [this.coords.longitude, this.coords.latitude],
|
center: coordinates,
|
||||||
zoom: 13,
|
zoom: 13,
|
||||||
});
|
});
|
||||||
|
|
||||||
new maplibregl.Marker()
|
new maplibregl.Popup({
|
||||||
.setLngLat([this.coords.longitude, this.coords.latitude])
|
closeButton: false,
|
||||||
|
closeOnClick: false,
|
||||||
|
closeOnMove: false,
|
||||||
|
})
|
||||||
|
.setLngLat(coordinates)
|
||||||
|
.setHTML(this.description)
|
||||||
.addTo(this.map);
|
.addTo(this.map);
|
||||||
|
|
||||||
this.map.on('error', (e)=>{
|
this.map.on('error', (e)=>{
|
||||||
|
@ -106,7 +113,6 @@ export default class MLocationBody extends React.Component<IBodyProps, IState> {
|
||||||
return <div className="mx_MLocationBody">
|
return <div className="mx_MLocationBody">
|
||||||
<div id={this.getBodyId()} className="mx_MLocationBody_map" />
|
<div id={this.getBodyId()} className="mx_MLocationBody_map" />
|
||||||
{ error }
|
{ error }
|
||||||
<span className="mx_EventTile_body">{ this.description }</span>
|
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2103,6 +2103,7 @@
|
||||||
"edited": "edited",
|
"edited": "edited",
|
||||||
"Submit logs": "Submit logs",
|
"Submit logs": "Submit logs",
|
||||||
"Can't load this message": "Can't load this message",
|
"Can't load this message": "Can't load this message",
|
||||||
|
"Share custom location": "Share custom location",
|
||||||
"Share my current location as a once off": "Share my current location as a once off",
|
"Share my current location as a once off": "Share my current location as a once off",
|
||||||
"My location": "My location",
|
"My location": "My location",
|
||||||
"Type of location share": "Type of location share",
|
"Type of location share": "Type of location share",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue