Apply prettier formatting

This commit is contained in:
Michael Weimann 2022-12-12 12:24:14 +01:00
parent 1cac306093
commit 526645c791
No known key found for this signature in database
GPG key ID: 53F535A266BB9584
1576 changed files with 65385 additions and 62478 deletions

View file

@ -14,49 +14,49 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { useState } from 'react';
import React, { useState } from "react";
import { _t } from '../../../languageHandler';
import StyledLiveBeaconIcon from '../beacon/StyledLiveBeaconIcon';
import AccessibleButton from '../elements/AccessibleButton';
import LabelledToggleSwitch from '../elements/LabelledToggleSwitch';
import Heading from '../typography/Heading';
import { _t } from "../../../languageHandler";
import StyledLiveBeaconIcon from "../beacon/StyledLiveBeaconIcon";
import AccessibleButton from "../elements/AccessibleButton";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
import Heading from "../typography/Heading";
interface Props {
onSubmit: () => void;
}
export const EnableLiveShare: React.FC<Props> = ({
onSubmit,
}) => {
export const EnableLiveShare: React.FC<Props> = ({ onSubmit }) => {
const [isEnabled, setEnabled] = useState(false);
return (
<div data-test-id='location-picker-enable-live-share' className='mx_EnableLiveShare'>
<StyledLiveBeaconIcon className='mx_EnableLiveShare_icon' />
<Heading className='mx_EnableLiveShare_heading' size='h3'>{ _t('Live location sharing') }</Heading>
<p className='mx_EnableLiveShare_description'>
{ _t(
'Please note: this is a labs feature using a temporary implementation. ' +
'This means you will not be able to delete your location history, ' +
'and advanced users will be able to see your location history ' +
'even after you stop sharing your live location with this room.',
) }
<div data-test-id="location-picker-enable-live-share" className="mx_EnableLiveShare">
<StyledLiveBeaconIcon className="mx_EnableLiveShare_icon" />
<Heading className="mx_EnableLiveShare_heading" size="h3">
{_t("Live location sharing")}
</Heading>
<p className="mx_EnableLiveShare_description">
{_t(
"Please note: this is a labs feature using a temporary implementation. " +
"This means you will not be able to delete your location history, " +
"and advanced users will be able to see your location history " +
"even after you stop sharing your live location with this room.",
)}
</p>
<LabelledToggleSwitch
data-test-id='enable-live-share-toggle'
data-test-id="enable-live-share-toggle"
value={isEnabled}
onChange={setEnabled}
label={_t('Enable live location sharing')}
label={_t("Enable live location sharing")}
/>
<AccessibleButton
data-test-id='enable-live-share-submit'
className='mx_EnableLiveShare_button'
element='button'
kind='primary'
data-test-id="enable-live-share-submit"
className="mx_EnableLiveShare_button"
element="button"
kind="primary"
onClick={onSubmit}
disabled={!isEnabled}
>
{ _t('OK') }
{_t("OK")}
</AccessibleButton>
</div>
);

View file

@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import { formatDuration } from '../../../DateUtils';
import { _t } from '../../../languageHandler';
import Dropdown from '../elements/Dropdown';
import { formatDuration } from "../../../DateUtils";
import { _t } from "../../../languageHandler";
import Dropdown from "../elements/Dropdown";
const DURATION_MS = {
fifteenMins: 900000,
@ -34,19 +34,23 @@ interface Props {
}
const getLabel = (durationMs: number) => {
return _t('Share for %(duration)s', { duration: formatDuration(durationMs) });
return _t("Share for %(duration)s", { duration: formatDuration(durationMs) });
};
const LiveDurationDropdown: React.FC<Props> = ({ timeout, onChange }) => {
const options = Object.values(DURATION_MS).map((duration) =>
({ key: duration.toString(), duration, label: getLabel(duration) }),
);
const options = Object.values(DURATION_MS).map((duration) => ({
key: duration.toString(),
duration,
label: getLabel(duration),
}));
// timeout is not one of our default values
// eg it was set by another client
if (!Object.values(DURATION_MS).includes(timeout)) {
options.push({
key: timeout.toString(), duration: timeout, label: getLabel(timeout),
key: timeout.toString(),
duration: timeout,
label: getLabel(timeout),
});
}
@ -55,18 +59,22 @@ const LiveDurationDropdown: React.FC<Props> = ({ timeout, onChange }) => {
onChange(+key);
};
return <Dropdown
id='live-duration'
data-test-id='live-duration-dropdown'
label={getLabel(timeout)}
value={timeout.toString()}
onOptionChange={onOptionChange}
className='mx_LiveDurationDropdown'
>
{ options.map(({ key, label }) =>
<div data-test-id={`live-duration-option-${key}`} key={key}>{ label }</div>,
) }
</Dropdown>;
return (
<Dropdown
id="live-duration"
data-test-id="live-duration-dropdown"
label={getLabel(timeout)}
value={timeout.toString()}
onOptionChange={onOptionChange}
className="mx_LiveDurationDropdown"
>
{options.map(({ key, label }) => (
<div data-test-id={`live-duration-option-${key}`} key={key}>
{label}
</div>
))}
</Dropdown>
);
};
export default LiveDurationDropdown;

View file

@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { ReactElement, SyntheticEvent, useContext } from 'react';
import classNames from 'classnames';
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
import { IEventRelation } from 'matrix-js-sdk/src/models/event';
import React, { ReactElement, SyntheticEvent, useContext } from "react";
import classNames from "classnames";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { IEventRelation } from "matrix-js-sdk/src/models/event";
import { _t } from '../../../languageHandler';
import { CollapsibleButton } from '../rooms/CollapsibleButton';
import { _t } from "../../../languageHandler";
import { CollapsibleButton } from "../rooms/CollapsibleButton";
import { aboveLeftOf, useContextMenu, AboveLeftOf } from "../../structures/ContextMenu";
import { OverflowMenuContext } from "../rooms/MessageComposerButtons";
import LocationShareMenu from './LocationShareMenu';
import LocationShareMenu from "./LocationShareMenu";
interface IProps {
roomId: string;
@ -43,37 +43,37 @@ export const LocationButton: React.FC<IProps> = ({ roomId, sender, menuPosition,
let contextMenu: ReactElement;
if (menuDisplayed) {
const position = menuPosition ?? aboveLeftOf(
button.current.getBoundingClientRect());
const position = menuPosition ?? aboveLeftOf(button.current.getBoundingClientRect());
contextMenu = <LocationShareMenu
menuPosition={position}
onFinished={_onFinished}
sender={sender}
roomId={roomId}
openMenu={openMenu}
relation={relation}
/>;
contextMenu = (
<LocationShareMenu
menuPosition={position}
onFinished={_onFinished}
sender={sender}
roomId={roomId}
openMenu={openMenu}
relation={relation}
/>
);
}
const className = classNames(
"mx_MessageComposer_button",
{
"mx_MessageComposer_button_highlight": menuDisplayed,
},
const className = classNames("mx_MessageComposer_button", {
mx_MessageComposer_button_highlight: menuDisplayed,
});
return (
<React.Fragment>
<CollapsibleButton
className={className}
iconClassName="mx_MessageComposer_location"
onClick={openMenu}
title={_t("Location")}
inputRef={button}
/>
{contextMenu}
</React.Fragment>
);
return <React.Fragment>
<CollapsibleButton
className={className}
iconClassName="mx_MessageComposer_location"
onClick={openMenu}
title={_t("Location")}
inputRef={button}
/>
{ contextMenu }
</React.Fragment>;
};
export default LocationButton;

View file

@ -14,25 +14,25 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { SyntheticEvent } from 'react';
import maplibregl, { MapMouseEvent } from 'maplibre-gl';
import React, { SyntheticEvent } from "react";
import maplibregl, { MapMouseEvent } from "maplibre-gl";
import { logger } from "matrix-js-sdk/src/logger";
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
import { ClientEvent, IClientWellKnown } from 'matrix-js-sdk/src/client';
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { ClientEvent, IClientWellKnown } from "matrix-js-sdk/src/client";
import { _t } from '../../../languageHandler';
import MatrixClientContext from '../../../contexts/MatrixClientContext';
import Modal from '../../../Modal';
import SdkConfig from '../../../SdkConfig';
import { tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
import { GenericPosition, genericPositionFromGeolocation, getGeoUri } from '../../../utils/beacon';
import { LocationShareError, findMapStyleUrl } from '../../../utils/location';
import ErrorDialog from '../dialogs/ErrorDialog';
import AccessibleButton from '../elements/AccessibleButton';
import { MapError } from './MapError';
import LiveDurationDropdown, { DEFAULT_DURATION_MS } from './LiveDurationDropdown';
import { LocationShareType, ShareLocationFn } from './shareLocation';
import Marker from './Marker';
import { _t } from "../../../languageHandler";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import Modal from "../../../Modal";
import SdkConfig from "../../../SdkConfig";
import { tileServerFromWellKnown } from "../../../utils/WellKnownUtils";
import { GenericPosition, genericPositionFromGeolocation, getGeoUri } from "../../../utils/beacon";
import { LocationShareError, findMapStyleUrl } from "../../../utils/location";
import ErrorDialog from "../dialogs/ErrorDialog";
import AccessibleButton from "../elements/AccessibleButton";
import { MapError } from "./MapError";
import LiveDurationDropdown, { DEFAULT_DURATION_MS } from "./LiveDurationDropdown";
import { LocationShareType, ShareLocationFn } from "./shareLocation";
import Marker from "./Marker";
export interface ILocationPickerProps {
sender: RoomMember;
@ -76,7 +76,7 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
try {
this.map = new maplibregl.Map({
container: 'mx_LocationPicker_map',
container: "mx_LocationPicker_map",
style: findMapStyleUrl(),
center: [0, 0],
zoom: 1,
@ -92,54 +92,56 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
this.map.addControl(this.geolocate);
this.map.on('error', (e) => {
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",
"Failed to load map: check map_style_url in config.json " + "has a valid URL and API key",
e.error,
);
this.setState({ error: LocationShareError.MapStyleUrlNotReachable });
});
this.map.on('load', () => {
this.map.on("load", () => {
this.geolocate.trigger();
});
this.geolocate.on('error', this.onGeolocateError);
this.geolocate.on("error", this.onGeolocateError);
if (isSharingOwnLocation(this.props.shareType)) {
this.geolocate.on('geolocate', this.onGeolocate);
this.geolocate.on("geolocate", this.onGeolocate);
}
if (this.props.shareType === LocationShareType.Pin) {
const navigationControl = new maplibregl.NavigationControl({
showCompass: false, showZoom: true,
showCompass: false,
showZoom: true,
});
this.map.addControl(navigationControl, 'bottom-right');
this.map.on('click', this.onClick);
this.map.addControl(navigationControl, "bottom-right");
this.map.on("click", this.onClick);
}
} catch (e) {
logger.error("Failed to render map", e);
const errorType = e?.message === LocationShareError.MapStyleUrlNotConfigured ?
LocationShareError.MapStyleUrlNotConfigured :
LocationShareError.Default;
const errorType =
e?.message === LocationShareError.MapStyleUrlNotConfigured
? LocationShareError.MapStyleUrlNotConfigured
: LocationShareError.Default;
this.setState({ error: errorType });
}
}
componentWillUnmount() {
this.geolocate?.off('error', this.onGeolocateError);
this.geolocate?.off('geolocate', this.onGeolocate);
this.map?.off('click', this.onClick);
this.geolocate?.off("error", this.onGeolocateError);
this.geolocate?.off("geolocate", this.onGeolocate);
this.map?.off("click", this.onClick);
this.context.off(ClientEvent.ClientWellKnown, this.updateStyleUrl);
}
private addMarkerToMap = () => {
this.marker = new maplibregl.Marker({
element: document.getElementById(this.getMarkerId()),
anchor: 'bottom',
anchor: "bottom",
offset: [0, -1],
}).setLngLat(new maplibregl.LngLat(0, 0))
})
.setLngLat(new maplibregl.LngLat(0, 0))
.addTo(this.map);
};
@ -155,12 +157,7 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
this.addMarkerToMap();
}
this.setState({ position: genericPositionFromGeolocation(position) });
this.marker?.setLngLat(
new maplibregl.LngLat(
position.coords.longitude,
position.coords.latitude,
),
);
this.marker?.setLngLat(new maplibregl.LngLat(position.coords.longitude, position.coords.latitude));
};
private onClick = (event: MapMouseEvent) => {
@ -202,65 +199,66 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
const { timeout, position } = this.state;
this.props.onChoose(
position ? { uri: getGeoUri(position), timestamp: position.timestamp, timeout } : {
timeout,
});
position
? { uri: getGeoUri(position), timestamp: position.timestamp, timeout }
: {
timeout,
},
);
this.props.onFinished();
};
render() {
if (this.state.error) {
return <div className="mx_LocationPicker mx_LocationPicker_hasError">
<MapError
error={this.state.error}
onFinished={this.props.onFinished} />
</div>;
return (
<div className="mx_LocationPicker mx_LocationPicker_hasError">
<MapError error={this.state.error} onFinished={this.props.onFinished} />
</div>
);
}
return (
<div className="mx_LocationPicker">
<div id="mx_LocationPicker_map" />
{ this.props.shareType === LocationShareType.Pin && <div className="mx_LocationPicker_pinText">
<span>
{ this.state.position ? _t("Click to move the pin") : _t("Click to drop a pin") }
</span>
</div>
}
{this.props.shareType === LocationShareType.Pin && (
<div className="mx_LocationPicker_pinText">
<span>{this.state.position ? _t("Click to move the pin") : _t("Click to drop a pin")}</span>
</div>
)}
<div className="mx_LocationPicker_footer">
<form onSubmit={this.onOk}>
{ this.props.shareType === LocationShareType.Live &&
<LiveDurationDropdown
onChange={this.onTimeoutChange}
timeout={this.state.timeout}
/>
}
{this.props.shareType === LocationShareType.Live && (
<LiveDurationDropdown onChange={this.onTimeoutChange} timeout={this.state.timeout} />
)}
<AccessibleButton
data-test-id="location-picker-submit-button"
type="submit"
element='button'
kind='primary'
className='mx_LocationPicker_submitButton'
element="button"
kind="primary"
className="mx_LocationPicker_submitButton"
disabled={!this.state.position}
onClick={this.onOk}>
{ _t('Share location') }
onClick={this.onOk}
>
{_t("Share location")}
</AccessibleButton>
</form>
</div>
<div id={this.getMarkerId()}>
{ /*
{/*
maplibregl hijacks the div above to style the marker
it must be in the dom when the map is initialised
and keep a consistent class
we want to hide the marker until it is set in the case of pin drop
so hide the internal visible elements
*/ }
*/}
{ !!this.marker && <Marker
roomMember={isSharingOwnLocation(this.props.shareType) ? this.props.sender : undefined}
useMemberColor={this.props.shareType === LocationShareType.Live}
/>
}
{!!this.marker && (
<Marker
roomMember={isSharingOwnLocation(this.props.shareType) ? this.props.sender : undefined}
useMemberColor={this.props.shareType === LocationShareType.Live}
/>
)}
</div>
</div>
);
@ -272,18 +270,17 @@ export default LocationPicker;
function positionFailureMessage(code: number): string {
const brand = SdkConfig.get().brand;
switch (code) {
case 1: return _t(
"%(brand)s was denied permission to fetch your location. " +
"Please allow location access in your browser settings.", { brand },
);
case 2: return _t(
"Failed to fetch your location. Please try again later.",
);
case 3: return _t(
"Timed out trying to fetch your location. Please try again later.",
);
case 4: return _t(
"Unknown error fetching location. Please try again later.",
);
case 1:
return _t(
"%(brand)s was denied permission to fetch your location. " +
"Please allow location access in your browser settings.",
{ brand },
);
case 2:
return _t("Failed to fetch your location. Please try again later.");
case 3:
return _t("Timed out trying to fetch your location. Please try again later.");
case 4:
return _t("Unknown error fetching location. Please try again later.");
}
}

View file

@ -14,23 +14,23 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { SyntheticEvent, useContext, useState } from 'react';
import { Room } from 'matrix-js-sdk/src/models/room';
import { IEventRelation } from 'matrix-js-sdk/src/models/event';
import React, { SyntheticEvent, useContext, useState } from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import { IEventRelation } from "matrix-js-sdk/src/models/event";
import MatrixClientContext from '../../../contexts/MatrixClientContext';
import ContextMenu, { AboveLeftOf } from '../../structures/ContextMenu';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import ContextMenu, { AboveLeftOf } from "../../structures/ContextMenu";
import LocationPicker, { ILocationPickerProps } from "./LocationPicker";
import { shareLiveLocation, shareLocation, LocationShareType } from './shareLocation';
import SettingsStore from '../../../settings/SettingsStore';
import ShareDialogButtons from './ShareDialogButtons';
import ShareType from './ShareType';
import { OwnProfileStore } from '../../../stores/OwnProfileStore';
import { EnableLiveShare } from './EnableLiveShare';
import { useFeatureEnabled } from '../../../hooks/useSettings';
import { SettingLevel } from '../../../settings/SettingLevel';
import { shareLiveLocation, shareLocation, LocationShareType } from "./shareLocation";
import SettingsStore from "../../../settings/SettingsStore";
import ShareDialogButtons from "./ShareDialogButtons";
import ShareType from "./ShareType";
import { OwnProfileStore } from "../../../stores/OwnProfileStore";
import { EnableLiveShare } from "./EnableLiveShare";
import { useFeatureEnabled } from "../../../hooks/useSettings";
import { SettingLevel } from "../../../settings/SettingLevel";
type Props = Omit<ILocationPickerProps, 'onChoose' | 'shareType'> & {
type Props = Omit<ILocationPickerProps, "onChoose" | "shareType"> & {
onFinished: (ev?: SyntheticEvent) => void;
menuPosition: AboveLeftOf;
openMenu: () => void;
@ -39,9 +39,7 @@ type Props = Omit<ILocationPickerProps, 'onChoose' | 'shareType'> & {
};
const getEnabledShareTypes = (relation): LocationShareType[] => {
const enabledShareTypes = [
LocationShareType.Own,
];
const enabledShareTypes = [LocationShareType.Own];
// live locations cannot have a relation
// hide the option when composer has a relation
@ -54,14 +52,7 @@ const getEnabledShareTypes = (relation): LocationShareType[] => {
return enabledShareTypes;
};
const LocationShareMenu: React.FC<Props> = ({
menuPosition,
onFinished,
sender,
roomId,
openMenu,
relation,
}) => {
const LocationShareMenu: React.FC<Props> = ({ menuPosition, onFinished, sender, roomId, openMenu, relation }) => {
const matrixClient = useContext(MatrixClientContext);
const enabledShareTypes = getEnabledShareTypes(relation);
const isLiveShareEnabled = useFeatureEnabled("feature_location_share_live");
@ -74,9 +65,10 @@ const LocationShareMenu: React.FC<Props> = ({
const displayName = OwnProfileStore.instance.displayName;
const onLocationSubmit = shareType === LocationShareType.Live ?
shareLiveLocation(matrixClient, roomId, displayName, openMenu) :
shareLocation(matrixClient, roomId, shareType, relation, openMenu);
const onLocationSubmit =
shareType === LocationShareType.Live
? shareLiveLocation(matrixClient, roomId, displayName, openMenu)
: shareLocation(matrixClient, roomId, shareType, relation, openMenu);
const onLiveShareEnableSubmit = () => {
SettingsStore.setValue("feature_location_share_live", undefined, SettingLevel.DEVICE, true);
@ -84,31 +76,27 @@ const LocationShareMenu: React.FC<Props> = ({
const shouldAdvertiseLiveLabsFlag = shareType === LocationShareType.Live && !isLiveShareEnabled;
return <ContextMenu
{...menuPosition}
onFinished={onFinished}
managed={false}
>
<div className="mx_LocationShareMenu">
{ shouldAdvertiseLiveLabsFlag &&
<EnableLiveShare
onSubmit={onLiveShareEnableSubmit}
return (
<ContextMenu {...menuPosition} onFinished={onFinished} managed={false}>
<div className="mx_LocationShareMenu">
{shouldAdvertiseLiveLabsFlag && <EnableLiveShare onSubmit={onLiveShareEnableSubmit} />}
{!shouldAdvertiseLiveLabsFlag && !!shareType && (
<LocationPicker
sender={sender}
shareType={shareType}
onChoose={onLocationSubmit}
onFinished={onFinished}
/>
)}
{!shareType && <ShareType setShareType={setShareType} enabledShareTypes={enabledShareTypes} />}
<ShareDialogButtons
displayBack={!!shareType && multipleShareTypesEnabled}
onBack={() => setShareType(undefined)}
onCancel={onFinished}
/>
}
{ !shouldAdvertiseLiveLabsFlag && !!shareType &&
<LocationPicker
sender={sender}
shareType={shareType}
onChoose={onLocationSubmit}
onFinished={onFinished}
/>
}
{ !shareType &&
<ShareType setShareType={setShareType} enabledShareTypes={enabledShareTypes} />
}
<ShareDialogButtons displayBack={!!shareType && multipleShareTypesEnabled} onBack={() => setShareType(undefined)} onCancel={onFinished} />
</div>
</ContextMenu>;
</div>
</ContextMenu>
);
};
export default LocationShareMenu;

View file

@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import { MatrixClient } from 'matrix-js-sdk/src/client';
import React from "react";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { MatrixClient } from "matrix-js-sdk/src/client";
import BaseDialog from "../dialogs/BaseDialog";
import { IDialogProps } from "../dialogs/IDialogProps";
import { locationEventGeoUri, isSelfLocation } from '../../../utils/location';
import Map from './Map';
import SmartMarker from './SmartMarker';
import ZoomButtons from './ZoomButtons';
import { locationEventGeoUri, isSelfLocation } from "../../../utils/location";
import Map from "./Map";
import SmartMarker from "./SmartMarker";
import ZoomButtons from "./ZoomButtons";
interface IProps extends IDialogProps {
matrixClient: MatrixClient;
@ -61,11 +61,7 @@ export default class LocationViewDialog extends React.Component<IProps, IState>
const markerRoomMember = isSelfLocation(mxEvent.getContent()) ? mxEvent.sender : undefined;
const geoUri = locationEventGeoUri(mxEvent);
return (
<BaseDialog
className='mx_LocationViewDialog'
onFinished={this.props.onFinished}
fixedWidth={false}
>
<BaseDialog className="mx_LocationViewDialog" onFinished={this.props.onFinished} fixedWidth={false}>
<Map
id={this.getBodyId()}
centerGeoUri={geoUri}
@ -73,18 +69,17 @@ export default class LocationViewDialog extends React.Component<IProps, IState>
interactive
className="mx_LocationViewDialog_map"
>
{
({ map }) =>
<>
<SmartMarker
map={map}
id={`${this.getBodyId()}-marker`}
geoUri={geoUri}
roomMember={markerRoomMember}
/>
<ZoomButtons map={map} />
</>
}
{({ map }) => (
<>
<SmartMarker
map={map}
id={`${this.getBodyId()}-marker`}
geoUri={geoUri}
roomMember={markerRoomMember}
/>
<ZoomButtons map={map} />
</>
)}
</Map>
</BaseDialog>
);

View file

@ -14,18 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { ReactNode, useContext, useEffect } from 'react';
import classNames from 'classnames';
import maplibregl from 'maplibre-gl';
import { ClientEvent, IClientWellKnown } from 'matrix-js-sdk/src/matrix';
import { logger } from 'matrix-js-sdk/src/logger';
import React, { ReactNode, useContext, useEffect } from "react";
import classNames from "classnames";
import maplibregl from "maplibre-gl";
import { ClientEvent, IClientWellKnown } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import MatrixClientContext from '../../../contexts/MatrixClientContext';
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
import { parseGeoUri } from '../../../utils/location';
import { tileServerFromWellKnown } from '../../../utils/WellKnownUtils';
import { useMap } from '../../../utils/location/useMap';
import { Bounds } from '../../../utils/beacon/bounds';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
import { parseGeoUri } from "../../../utils/location";
import { tileServerFromWellKnown } from "../../../utils/WellKnownUtils";
import { useMap } from "../../../utils/location/useMap";
import { Bounds } from "../../../utils/beacon/bounds";
const useMapWithStyle = ({ id, centerGeoUri, onError, interactive, bounds }) => {
const bodyId = `mx_Map_${id}`;
@ -52,7 +52,7 @@ const useMapWithStyle = ({ id, centerGeoUri, onError, interactive, bounds }) =>
const coords = parseGeoUri(centerGeoUri);
map.setCenter({ lon: coords.longitude, lat: coords.latitude });
} catch (_error) {
logger.error('Could not set map center');
logger.error("Could not set map center");
}
}
}, [map, centerGeoUri]);
@ -66,7 +66,7 @@ const useMapWithStyle = ({ id, centerGeoUri, onError, interactive, bounds }) =>
);
map.fitBounds(lngLatBounds, { padding: 100, maxZoom: 15 });
} catch (_error) {
logger.error('Invalid map bounds');
logger.error("Invalid map bounds");
}
}
}, [map, bounds]);
@ -92,25 +92,13 @@ interface MapProps {
className?: string;
onClick?: () => void;
onError?: (error: Error) => void;
children?: (renderProps: {
map: maplibregl.Map;
}) => ReactNode;
children?: (renderProps: { map: maplibregl.Map }) => ReactNode;
}
const Map: React.FC<MapProps> = ({
bounds,
centerGeoUri,
children,
className,
id,
interactive,
onError, onClick,
}) => {
const Map: React.FC<MapProps> = ({ bounds, centerGeoUri, children, className, id, interactive, onError, onClick }) => {
const { map, bodyId } = useMapWithStyle({ centerGeoUri, onError, id, interactive, bounds });
const onMapClick = (
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
) => {
const onMapClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
// Eat click events when clicking the attribution button
const target = event.target as Element;
if (target.classList.contains("maplibregl-ctrl-attrib-button")) {
@ -120,12 +108,11 @@ const Map: React.FC<MapProps> = ({
onClick && onClick();
};
return <div className={classNames('mx_Map', className)}
id={bodyId}
onClick={onMapClick}
>
{ !!children && !!map && children({ map }) }
</div>;
return (
<div className={classNames("mx_Map", className)} id={bodyId} onClick={onMapClick}>
{!!children && !!map && children({ map })}
</div>
);
};
export default Map;

View file

@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import classNames from 'classnames';
import React from "react";
import classNames from "classnames";
import { Icon as WarningBadge } from '../../../../res/img/element-icons/warning-badge.svg';
import { _t } from '../../../languageHandler';
import { getLocationShareErrorMessage, LocationShareError } from '../../../utils/location';
import AccessibleButton from '../elements/AccessibleButton';
import Heading from '../typography/Heading';
import { Icon as WarningBadge } from "../../../../res/img/element-icons/warning-badge.svg";
import { _t } from "../../../languageHandler";
import { getLocationShareErrorMessage, LocationShareError } from "../../../utils/location";
import AccessibleButton from "../elements/AccessibleButton";
import Heading from "../typography/Heading";
export interface MapErrorProps {
error: LocationShareError;
@ -31,30 +31,21 @@ export interface MapErrorProps {
onClick?: () => void;
}
export const MapError: React.FC<MapErrorProps> = ({
error,
isMinimised,
className,
onFinished,
onClick,
}) => (
<div data-test-id='map-rendering-error'
className={classNames('mx_MapError', className, { 'mx_MapError_isMinimised': isMinimised })}
export const MapError: React.FC<MapErrorProps> = ({ error, isMinimised, className, onFinished, onClick }) => (
<div
data-test-id="map-rendering-error"
className={classNames("mx_MapError", className, { mx_MapError_isMinimised: isMinimised })}
onClick={onClick}
>
<WarningBadge className='mx_MapError_icon' />
<Heading className='mx_MapError_heading' size='h3'>{ _t('Unable to load map') }</Heading>
<p className='mx_MapError_message'>
{ getLocationShareErrorMessage(error) }
</p>
{ onFinished &&
<AccessibleButton
element='button'
kind='primary'
onClick={onFinished}
>
{ _t('OK') }
<WarningBadge className="mx_MapError_icon" />
<Heading className="mx_MapError_heading" size="h3">
{_t("Unable to load map")}
</Heading>
<p className="mx_MapError_message">{getLocationShareErrorMessage(error)}</p>
{onFinished && (
<AccessibleButton element="button" kind="primary" onClick={onFinished}>
{_t("OK")}
</AccessibleButton>
}
)}
</div>
);

View file

@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import classNames from 'classnames';
import React from "react";
import classNames from "classnames";
import { Icon as LocationMarkerIcon } from '../../../../res/img/element-icons/location.svg';
import { Icon as MapFallbackImage } from '../../../../res/img/location/map.svg';
import Spinner from '../elements/Spinner';
import { Icon as LocationMarkerIcon } from "../../../../res/img/element-icons/location.svg";
import { Icon as MapFallbackImage } from "../../../../res/img/location/map.svg";
import Spinner from "../elements/Spinner";
interface Props extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
@ -28,11 +28,13 @@ interface Props extends React.HTMLAttributes<HTMLDivElement> {
}
const MapFallback: React.FC<Props> = ({ className, isLoading, children, ...rest }) => {
return <div className={classNames('mx_MapFallback', className)} {...rest}>
<MapFallbackImage className='mx_MapFallback_bg' />
{ isLoading ? <Spinner h={32} w={32} /> : <LocationMarkerIcon className='mx_MapFallback_icon' /> }
{ children }
</div>;
return (
<div className={classNames("mx_MapFallback", className)} {...rest}>
<MapFallbackImage className="mx_MapFallback_bg" />
{isLoading ? <Spinner h={32} w={32} /> : <LocationMarkerIcon className="mx_MapFallback_icon" />}
{children}
</div>
);
};
export default MapFallback;

View file

@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { ReactNode, useState } from 'react';
import classNames from 'classnames';
import { RoomMember } from 'matrix-js-sdk/src/matrix';
import React, { ReactNode, useState } from "react";
import classNames from "classnames";
import { RoomMember } from "matrix-js-sdk/src/matrix";
import { Icon as LocationIcon } from '../../../../res/img/element-icons/location.svg';
import { getUserNameColorClass } from '../../../utils/FormattingUtils';
import MemberAvatar from '../avatars/MemberAvatar';
import { Icon as LocationIcon } from "../../../../res/img/element-icons/location.svg";
import { getUserNameColorClass } from "../../../utils/FormattingUtils";
import MemberAvatar from "../avatars/MemberAvatar";
interface Props {
id?: string;
@ -36,11 +36,12 @@ interface Props {
* tooltip is truthy
*/
const OptionalTooltip: React.FC<{
tooltip?: ReactNode; children: ReactNode;
tooltip?: ReactNode;
children: ReactNode;
}> = ({ tooltip, children }) => {
const [isVisible, setIsVisible] = useState(false);
if (!tooltip) {
return <>{ children }</>;
return <>{children}</>;
}
const show = () => setIsVisible(true);
@ -51,40 +52,45 @@ const OptionalTooltip: React.FC<{
setIsVisible(!isVisible);
};
return <div onMouseEnter={show} onClick={toggleVisibility} onMouseLeave={hide}>
{ children }
{ isVisible && tooltip }
</div>;
return (
<div onMouseEnter={show} onClick={toggleVisibility} onMouseLeave={hide}>
{children}
{isVisible && tooltip}
</div>
);
};
/**
* Generic location marker
*/
const Marker = React.forwardRef<HTMLDivElement, Props>(({ id, roomMember, useMemberColor, tooltip }, ref) => {
const memberColorClass = useMemberColor && roomMember ? getUserNameColorClass(roomMember.userId) : '';
return <div
ref={ref}
id={id}
className={classNames("mx_Marker", memberColorClass, {
"mx_Marker_defaultColor": !memberColorClass,
})}
>
<OptionalTooltip tooltip={tooltip}>
<div className="mx_Marker_border">
{ roomMember ?
<MemberAvatar
member={roomMember}
width={36}
height={36}
viewUserOnClick={false}
// no mxid on hover when marker has tooltip
hideTitle={!!tooltip}
/>
: <LocationIcon className="mx_Marker_icon" />
}
</div>
</OptionalTooltip>
</div>;
const memberColorClass = useMemberColor && roomMember ? getUserNameColorClass(roomMember.userId) : "";
return (
<div
ref={ref}
id={id}
className={classNames("mx_Marker", memberColorClass, {
mx_Marker_defaultColor: !memberColorClass,
})}
>
<OptionalTooltip tooltip={tooltip}>
<div className="mx_Marker_border">
{roomMember ? (
<MemberAvatar
member={roomMember}
width={36}
height={36}
viewUserOnClick={false}
// no mxid on hover when marker has tooltip
hideTitle={!!tooltip}
/>
) : (
<LocationIcon className="mx_Marker_icon" />
)}
</div>
</OptionalTooltip>
</div>
);
});
export default Marker;

View file

@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import AccessibleButton from '../elements/AccessibleButton';
import { Icon as BackIcon } from '../../../../res/img/element-icons/caret-left.svg';
import { Icon as CloseIcon } from '../../../../res/img/element-icons/cancel-rounded.svg';
import AccessibleButton from "../elements/AccessibleButton";
import { Icon as BackIcon } from "../../../../res/img/element-icons/caret-left.svg";
import { Icon as CloseIcon } from "../../../../res/img/element-icons/cancel-rounded.svg";
interface Props {
onCancel: () => void;
@ -27,26 +27,28 @@ interface Props {
}
const ShareDialogButtons: React.FC<Props> = ({ onBack, onCancel, displayBack }) => {
return <div className='mx_ShareDialogButtons'>
{ displayBack &&
return (
<div className="mx_ShareDialogButtons">
{displayBack && (
<AccessibleButton
className="mx_ShareDialogButtons_button left"
data-test-id="share-dialog-buttons-back"
onClick={onBack}
element="button"
>
<BackIcon className="mx_ShareDialogButtons_button-icon" />
</AccessibleButton>
)}
<AccessibleButton
className='mx_ShareDialogButtons_button left'
data-test-id='share-dialog-buttons-back'
onClick={onBack}
element='button'
className="mx_ShareDialogButtons_button right"
data-test-id="share-dialog-buttons-cancel"
onClick={onCancel}
element="button"
>
<BackIcon className='mx_ShareDialogButtons_button-icon' />
<CloseIcon className="mx_ShareDialogButtons_button-icon" />
</AccessibleButton>
}
<AccessibleButton
className='mx_ShareDialogButtons_button right'
data-test-id='share-dialog-buttons-cancel'
onClick={onCancel}
element='button'
>
<CloseIcon className='mx_ShareDialogButtons_button-icon' />
</AccessibleButton>
</div>;
</div>
);
};
export default ShareDialogButtons;

View file

@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { HTMLAttributes, useContext } from 'react';
import React, { HTMLAttributes, useContext } from "react";
import MatrixClientContext from '../../../contexts/MatrixClientContext';
import { _t } from '../../../languageHandler';
import { OwnProfileStore } from '../../../stores/OwnProfileStore';
import BaseAvatar from '../avatars/BaseAvatar';
import AccessibleButton from '../elements/AccessibleButton';
import Heading from '../typography/Heading';
import { Icon as LocationIcon } from '../../../../res/img/element-icons/location.svg';
import { LocationShareType } from './shareLocation';
import StyledLiveBeaconIcon from '../beacon/StyledLiveBeaconIcon';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { _t } from "../../../languageHandler";
import { OwnProfileStore } from "../../../stores/OwnProfileStore";
import BaseAvatar from "../avatars/BaseAvatar";
import AccessibleButton from "../elements/AccessibleButton";
import Heading from "../typography/Heading";
import { Icon as LocationIcon } from "../../../../res/img/element-icons/location.svg";
import { LocationShareType } from "./shareLocation";
import StyledLiveBeaconIcon from "../beacon/StyledLiveBeaconIcon";
const UserAvatar = () => {
const matrixClient = useContext(MatrixClientContext);
@ -34,63 +34,65 @@ const UserAvatar = () => {
const avatarSize = 36;
const avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize);
return <div className={`mx_ShareType_option-icon ${LocationShareType.Own}`}>
<BaseAvatar
idName={userId}
name={displayName}
url={avatarUrl}
width={avatarSize}
height={avatarSize}
resizeMethod="crop"
className="mx_UserMenu_userAvatar_BaseAvatar"
/>
</div>;
return (
<div className={`mx_ShareType_option-icon ${LocationShareType.Own}`}>
<BaseAvatar
idName={userId}
name={displayName}
url={avatarUrl}
width={avatarSize}
height={avatarSize}
resizeMethod="crop"
className="mx_UserMenu_userAvatar_BaseAvatar"
/>
</div>
);
};
type ShareTypeOptionProps = HTMLAttributes<Element> & { label: string, shareType: LocationShareType };
const ShareTypeOption: React.FC<ShareTypeOptionProps> = ({
onClick, label, shareType, ...rest
}) => <AccessibleButton
element='button'
className='mx_ShareType_option'
onClick={onClick}
{...rest}>
{ shareType === LocationShareType.Own && <UserAvatar /> }
{ shareType === LocationShareType.Pin &&
<LocationIcon className={`mx_ShareType_option-icon ${LocationShareType.Pin}`} /> }
{ shareType === LocationShareType.Live &&
<StyledLiveBeaconIcon className={`mx_ShareType_option-icon ${LocationShareType.Live}`} /> }
type ShareTypeOptionProps = HTMLAttributes<Element> & { label: string; shareType: LocationShareType };
const ShareTypeOption: React.FC<ShareTypeOptionProps> = ({ onClick, label, shareType, ...rest }) => (
<AccessibleButton element="button" className="mx_ShareType_option" onClick={onClick} {...rest}>
{shareType === LocationShareType.Own && <UserAvatar />}
{shareType === LocationShareType.Pin && (
<LocationIcon className={`mx_ShareType_option-icon ${LocationShareType.Pin}`} />
)}
{shareType === LocationShareType.Live && (
<StyledLiveBeaconIcon className={`mx_ShareType_option-icon ${LocationShareType.Live}`} />
)}
{ label }
</AccessibleButton>;
{label}
</AccessibleButton>
);
interface Props {
setShareType: (shareType: LocationShareType) => void;
enabledShareTypes: LocationShareType[];
}
const ShareType: React.FC<Props> = ({
setShareType, enabledShareTypes,
}) => {
const ShareType: React.FC<Props> = ({ setShareType, enabledShareTypes }) => {
const labels = {
[LocationShareType.Own]: _t('My current location'),
[LocationShareType.Live]: _t('My live location'),
[LocationShareType.Pin]: _t('Drop a Pin'),
[LocationShareType.Own]: _t("My current location"),
[LocationShareType.Live]: _t("My live location"),
[LocationShareType.Pin]: _t("Drop a Pin"),
};
return <div className='mx_ShareType'>
<LocationIcon className='mx_ShareType_badge' />
<Heading className='mx_ShareType_heading' size='h3'>{ _t("What location type do you want to share?") }</Heading>
<div className='mx_ShareType_wrapper_options'>
{ enabledShareTypes.map((type) =>
<ShareTypeOption
key={type}
onClick={() => setShareType(type)}
label={labels[type]}
shareType={type}
data-test-id={`share-location-option-${type}`}
/>,
) }
return (
<div className="mx_ShareType">
<LocationIcon className="mx_ShareType_badge" />
<Heading className="mx_ShareType_heading" size="h3">
{_t("What location type do you want to share?")}
</Heading>
<div className="mx_ShareType_wrapper_options">
{enabledShareTypes.map((type) => (
<ShareTypeOption
key={type}
onClick={() => setShareType(type)}
label={labels[type]}
shareType={type}
data-test-id={`share-location-option-${type}`}
/>
))}
</div>
</div>
</div>;
);
};
export default ShareType;

View file

@ -14,28 +14,31 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { ReactNode, useCallback, useEffect, useState } from 'react';
import maplibregl from 'maplibre-gl';
import { RoomMember } from 'matrix-js-sdk/src/matrix';
import React, { ReactNode, useCallback, useEffect, useState } from "react";
import maplibregl from "maplibre-gl";
import { RoomMember } from "matrix-js-sdk/src/matrix";
import { createMarker, parseGeoUri } from '../../../utils/location';
import Marker from './Marker';
import { createMarker, parseGeoUri } from "../../../utils/location";
import Marker from "./Marker";
const useMapMarker = (
map: maplibregl.Map,
geoUri: string,
): { marker?: maplibregl.Marker, onElementRef: (el: HTMLDivElement) => void } => {
): { marker?: maplibregl.Marker; onElementRef: (el: HTMLDivElement) => void } => {
const [marker, setMarker] = useState<maplibregl.Marker>();
const onElementRef = useCallback((element: HTMLDivElement) => {
if (marker || !element) {
return;
}
const coords = parseGeoUri(geoUri);
const newMarker = createMarker(coords, element);
newMarker.addTo(map);
setMarker(newMarker);
}, [marker, geoUri, map]);
const onElementRef = useCallback(
(element: HTMLDivElement) => {
if (marker || !element) {
return;
}
const coords = parseGeoUri(geoUri);
const newMarker = createMarker(coords, element);
newMarker.addTo(map);
setMarker(newMarker);
},
[marker, geoUri, map],
);
useEffect(() => {
if (marker) {
@ -44,11 +47,14 @@ const useMapMarker = (
}
}, [marker, geoUri]);
useEffect(() => () => {
if (marker) {
marker.remove();
}
}, [marker]);
useEffect(
() => () => {
if (marker) {
marker.remove();
}
},
[marker],
);
return {
marker,

View file

@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import maplibregl from 'maplibre-gl';
import React from "react";
import maplibregl from "maplibre-gl";
import { _t } from '../../../languageHandler';
import AccessibleButton from '../elements/AccessibleButton';
import { Icon as PlusIcon } from '../../../../res/img/element-icons/plus-button.svg';
import { Icon as MinusIcon } from '../../../../res/img/element-icons/minus-button.svg';
import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
import { Icon as PlusIcon } from "../../../../res/img/element-icons/plus-button.svg";
import { Icon as MinusIcon } from "../../../../res/img/element-icons/minus-button.svg";
interface Props {
map: maplibregl.Map;
@ -35,24 +35,26 @@ const ZoomButtons: React.FC<Props> = ({ map }) => {
map.zoomOut();
};
return <div className="mx_ZoomButtons">
<AccessibleButton
onClick={onZoomIn}
data-test-id='map-zoom-in-button'
title={_t("Zoom in")}
className='mx_ZoomButtons_button'
>
<PlusIcon className='mx_ZoomButtons_icon' />
</AccessibleButton>
<AccessibleButton
onClick={onZoomOut}
data-test-id='map-zoom-out-button'
title={_t("Zoom out")}
className='mx_ZoomButtons_button'
>
<MinusIcon className='mx_ZoomButtons_icon' />
</AccessibleButton>
</div>;
return (
<div className="mx_ZoomButtons">
<AccessibleButton
onClick={onZoomIn}
data-test-id="map-zoom-in-button"
title={_t("Zoom in")}
className="mx_ZoomButtons_button"
>
<PlusIcon className="mx_ZoomButtons_icon" />
</AccessibleButton>
<AccessibleButton
onClick={onZoomOut}
data-test-id="map-zoom-out-button"
title={_t("Zoom out")}
className="mx_ZoomButtons_button"
>
<MinusIcon className="mx_ZoomButtons_icon" />
</AccessibleButton>
</div>
);
};
export default ZoomButtons;

View file

@ -30,9 +30,9 @@ import { OwnBeaconStore } from "../../../stores/OwnBeaconStore";
import { doMaybeLocalRoomAction } from "../../../utils/local-room";
export enum LocationShareType {
Own = 'Own',
Pin = 'Pin',
Live = 'Live'
Own = "Own",
Pin = "Pin",
Live = "Live",
}
export type LocationShareProps = {
@ -46,13 +46,16 @@ const DEFAULT_LIVE_DURATION = 300000;
export type ShareLocationFn = (props: LocationShareProps) => Promise<void>;
const getPermissionsErrorParams = (shareType: LocationShareType): {
const getPermissionsErrorParams = (
shareType: LocationShareType,
): {
errorMessage: string;
modalParams: IQuestionDialogProps;
} => {
const errorMessage = shareType === LocationShareType.Live
? "Insufficient permissions to start sharing your live location"
: "Insufficient permissions to send your location";
const errorMessage =
shareType === LocationShareType.Live
? "Insufficient permissions to start sharing your live location"
: "Insufficient permissions to send your location";
const modalParams = {
title: _t("You don't have permission to share locations"),
@ -64,20 +67,24 @@ const getPermissionsErrorParams = (shareType: LocationShareType): {
return { modalParams, errorMessage };
};
const getDefaultErrorParams = (shareType: LocationShareType, openMenu: () => void): {
const getDefaultErrorParams = (
shareType: LocationShareType,
openMenu: () => void,
): {
errorMessage: string;
modalParams: IQuestionDialogProps;
} => {
const errorMessage = shareType === LocationShareType.Live
? "We couldn't start sharing your live location"
: "We couldn't send your location";
const errorMessage =
shareType === LocationShareType.Live
? "We couldn't start sharing your live location"
: "We couldn't send your location";
const modalParams = {
title: _t("We couldn't send your location"),
description: _t("%(brand)s could not send your location. Please try again later.", {
brand: SdkConfig.get().brand,
}),
button: _t('Try again'),
cancelButton: _t('Cancel'),
button: _t("Try again"),
cancelButton: _t("Cancel"),
onFinished: (tryAgain: boolean) => {
if (tryAgain) {
openMenu();
@ -88,52 +95,55 @@ const getDefaultErrorParams = (shareType: LocationShareType, openMenu: () => voi
};
const handleShareError = (error: Error, openMenu: () => void, shareType: LocationShareType): void => {
const { modalParams, errorMessage } = (error as MatrixError).errcode === 'M_FORBIDDEN' ?
getPermissionsErrorParams(shareType) :
getDefaultErrorParams(shareType, openMenu);
const { modalParams, errorMessage } =
(error as MatrixError).errcode === "M_FORBIDDEN"
? getPermissionsErrorParams(shareType)
: getDefaultErrorParams(shareType, openMenu);
logger.error(errorMessage, error);
Modal.createDialog(QuestionDialog, modalParams);
};
export const shareLiveLocation = (
client: MatrixClient, roomId: string, displayName: string, openMenu: () => void,
): ShareLocationFn => async ({ timeout }) => {
const description = _t(`%(displayName)s's live location`, { displayName });
try {
await OwnBeaconStore.instance.createLiveBeacon(
roomId,
makeBeaconInfoContent(
timeout ?? DEFAULT_LIVE_DURATION,
true, /* isLive */
description,
LocationAssetType.Self,
),
);
} catch (error) {
handleShareError(error, openMenu, LocationShareType.Live);
}
};
export const shareLiveLocation =
(client: MatrixClient, roomId: string, displayName: string, openMenu: () => void): ShareLocationFn =>
async ({ timeout }) => {
const description = _t(`%(displayName)s's live location`, { displayName });
try {
await OwnBeaconStore.instance.createLiveBeacon(
roomId,
makeBeaconInfoContent(
timeout ?? DEFAULT_LIVE_DURATION,
true /* isLive */,
description,
LocationAssetType.Self,
),
);
} catch (error) {
handleShareError(error, openMenu, LocationShareType.Live);
}
};
export const shareLocation = (
client: MatrixClient,
roomId: string,
shareType: LocationShareType,
relation: IEventRelation | undefined,
openMenu: () => void,
): ShareLocationFn => async ({ uri, timestamp }) => {
if (!uri) return;
try {
const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name ? relation.event_id : null;
const assetType = shareType === LocationShareType.Pin ? LocationAssetType.Pin : LocationAssetType.Self;
const content = makeLocationContent(undefined, uri, timestamp, undefined, assetType);
await doMaybeLocalRoomAction(
roomId,
(actualRoomId: string) => client.sendMessage(actualRoomId, threadId, content),
client,
);
} catch (error) {
handleShareError(error, openMenu, shareType);
}
};
export const shareLocation =
(
client: MatrixClient,
roomId: string,
shareType: LocationShareType,
relation: IEventRelation | undefined,
openMenu: () => void,
): ShareLocationFn =>
async ({ uri, timestamp }) => {
if (!uri) return;
try {
const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name ? relation.event_id : null;
const assetType = shareType === LocationShareType.Pin ? LocationAssetType.Pin : LocationAssetType.Self;
const content = makeLocationContent(undefined, uri, timestamp, undefined, assetType);
await doMaybeLocalRoomAction(
roomId,
(actualRoomId: string) => client.sendMessage(actualRoomId, threadId, content),
client,
);
} catch (error) {
handleShareError(error, openMenu, shareType);
}
};