125 lines
4.1 KiB
TypeScript
125 lines
4.1 KiB
TypeScript
import React from 'react';
|
|||
|
|
import FocusTrap from 'focus-trap-react';
|
||
|
|
import {
|
||
|
|
Box,
|
||
|
|
Button,
|
||
|
|
config,
|
||
|
|
Dialog,
|
||
|
|
Header,
|
||
|
|
Icon,
|
||
|
|
IconButton,
|
||
|
|
Icons,
|
||
|
|
Overlay,
|
||
|
|
OverlayBackdrop,
|
||
|
|
OverlayCenter,
|
||
|
|
Text,
|
||
|
|
} from 'folds';
|
||
|
|
import { useModalStyle } from '../../hooks/useModalStyle';
|
||
|
|
import { stopPropagation } from '../../utils/keyboard';
|
||
|
|
import { formatAccuracy } from '../../utils/geoShare';
|
||
|
|
|
||
|
|
export type PendingLocation = { latitude: number; longitude: number; accuracy?: number };
|
||
|
|
|
||
|
|
type ShareLocationDialogProps = {
|
||
|
|
location: PendingLocation;
|
||
|
|
roomName: string;
|
||
|
|
onSend: (approximate: boolean) => void;
|
||
|
|
requestClose: () => void;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* [Gitea #340] Confirm step before an m.location is posted: the toolbar button
|
||
|
|
* used to send the exact position from a single click. Shows what will be
|
||
|
|
* shared (coordinates + the fix's accuracy) and offers an approximate share.
|
||
|
|
*/
|
||
|
|
export function ShareLocationDialog({
|
||
|
|
location,
|
||
|
|
roomName,
|
||
|
|
onSend,
|
||
|
|
requestClose,
|
||
|
|
}: ShareLocationDialogProps) {
|
||
|
|
const modalStyle = useModalStyle(420);
|
||
|
|
const { latitude, longitude, accuracy } = location;
|
||
|
|
const accuracyLabel = formatAccuracy(accuracy);
|
||
|
|
const mapUrl = `https://www.openstreetmap.org/?mlat=${latitude}&mlon=${longitude}#map=16/${latitude}/${longitude}`;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Overlay open backdrop={<OverlayBackdrop />}>
|
||
|
|
<OverlayCenter>
|
||
|
|
<FocusTrap
|
||
|
|
focusTrapOptions={{
|
||
|
|
initialFocus: '#share-location-cancel',
|
||
|
|
onDeactivate: requestClose,
|
||
|
|
clickOutsideDeactivates: true,
|
||
|
|
escapeDeactivates: stopPropagation,
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<Dialog
|
||
|
|
role="dialog"
|
||
|
|
aria-modal="true"
|
||
|
|
aria-labelledby="share-location-title"
|
||
|
|
variant="Surface"
|
||
|
|
style={modalStyle}
|
||
|
|
>
|
||
|
|
<Header
|
||
|
|
style={{
|
||
|
|
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
||
|
|
borderBottomWidth: config.borderWidth.B300,
|
||
|
|
}}
|
||
|
|
variant="Surface"
|
||
|
|
size="500"
|
||
|
|
>
|
||
|
|
<Box grow="Yes">
|
||
|
|
<Text id="share-location-title" as="h2" size="H4">
|
||
|
|
Share your location?
|
||
|
|
</Text>
|
||
|
|
</Box>
|
||
|
|
<IconButton size="300" onClick={requestClose} radii="300" aria-label="Close">
|
||
|
|
<Icon src={Icons.Cross} />
|
||
|
|
</IconButton>
|
||
|
|
</Header>
|
||
|
|
<Box style={{ padding: config.space.S400 }} direction="Column" gap="400">
|
||
|
|
<Text>
|
||
|
|
Everyone in <b>{roomName}</b> will see this position, and it can't be taken
|
||
|
|
back from their devices once sent.
|
||
|
|
</Text>
|
||
|
|
<Box direction="Column" gap="100">
|
||
|
|
<Text size="T400">
|
||
|
|
<b>
|
||
|
|
{latitude.toFixed(5)}, {longitude.toFixed(5)}
|
||
|
|
</b>
|
||
|
|
{accuracyLabel && ` (${accuracyLabel})`}
|
||
|
|
</Text>
|
||
|
|
<Text size="T200" priority="300">
|
||
|
|
{accuracyLabel
|
||
|
|
? 'The ± is how sure your device is of this position.'
|
||
|
|
: 'Your device did not say how accurate this position is.'}{' '}
|
||
|
|
<a href={mapUrl} target="_blank" rel="noreferrer noopener">
|
||
|
|
Check on OpenStreetMap
|
||
|
|
</a>
|
||
|
|
</Text>
|
||
|
|
</Box>
|
||
|
|
<Box gap="200" justifyContent="End" wrap="Wrap">
|
||
|
|
<Button
|
||
|
|
id="share-location-cancel"
|
||
|
|
variant="Secondary"
|
||
|
|
fill="Soft"
|
||
|
|
onClick={requestClose}
|
||
|
|
>
|
||
|
|
<Text size="B400">Cancel</Text>
|
||
|
|
</Button>
|
||
|
|
<Button variant="Secondary" fill="Soft" onClick={() => onSend(true)}>
|
||
|
|
<Text size="B400">Send approximate (~1 km)</Text>
|
||
|
|
</Button>
|
||
|
|
<Button variant="Primary" onClick={() => onSend(false)}>
|
||
|
|
<Text size="B400">Send</Text>
|
||
|
|
</Button>
|
||
|
|
</Box>
|
||
|
|
</Box>
|
||
|
|
</Dialog>
|
||
|
|
</FocusTrap>
|
||
|
|
</OverlayCenter>
|
||
|
|
</Overlay>
|
||
|
|
);
|
||
|
|
}
|