* extract location markers into generic Marker Signed-off-by: Kerry Archibald <kerrya@element.io> * wrap marker in smartmarker Signed-off-by: Kerry Archibald <kerrya@element.io> * test smartmarker Signed-off-by: Kerry Archibald <kerrya@element.io> * working map in location body Signed-off-by: Kerry Archibald <kerrya@element.io> * remove skinned sdk Signed-off-by: Kerry Archibald <kerrya@element.io> * use new ZoomButtons in MLocationBody Signed-off-by: Kerry Archibald <kerrya@element.io> * test LocationViewDialog Signed-off-by: Kerry Archibald <kerrya@element.io> * update commentt Signed-off-by: Kerry Archibald <kerrya@element.io> * lint Signed-off-by: Kerry Archibald <kerrya@element.io> * lint Signed-off-by: Kerry Archibald <kerrya@element.io> * extract livetimeremaining into own component Signed-off-by: Kerry Archibald <kerrya@element.io> * extract more beacon state utils Signed-off-by: Kerry Archibald <kerrya@element.io> * update tests for roomlivesharewarning Signed-off-by: Kerry Archibald <kerrya@element.io> * add beacon map and status chin Signed-off-by: Kerry Archibald <kerrya@element.io> * add handling for bubbles Signed-off-by: Kerry Archibald <kerrya@element.io> * tests for BeaconBody Signed-off-by: Kerry Archibald <kerrya@element.io> * move displaystatus check up to mbeaconbody Signed-off-by: Kerry Archibald <kerrya@element.io> * test BeaconStatus Signed-off-by: Kerry Archibald <kerrya@element.io> * rename BeaconStatusChin -> BeaconStatus Signed-off-by: Kerry Archibald <kerrya@element.io> * make BeaconStatus generic Signed-off-by: Kerry Archibald <kerrya@element.io> * lint Signed-off-by: Kerry Archibald <kerrya@element.io> * adjust spinner size Signed-off-by: Kerry Archibald <kerrya@element.io> * lint Signed-off-by: Kerry Archibald <kerrya@element.io> * add static time remaining option to beacon status Signed-off-by: Kerry Archibald <kerrya@element.io> * render time differently for own beacon Signed-off-by: Kerry Archibald <kerrya@element.io> * use children to add actions to BeaconStatus Signed-off-by: Kerry Archibald <kerrya@element.io> * add OwnBeaconStatus wrapper with stop button Signed-off-by: Kerry Archibald <kerrya@element.io> * add error states for own beacon Signed-off-by: Kerry Archibald <kerrya@element.io> * test OwnBeaconStatus Signed-off-by: Kerry Archibald <kerrya@element.io> * move ownbeaconstatus to write dir Signed-off-by: Kerry Archibald <kerrya@element.io> * improve button styling Signed-off-by: Kerry Archibald <kerrya@element.io> * i18n Signed-off-by: Kerry Archibald <kerrya@element.io> * lint Signed-off-by: Kerry Archibald <kerrya@element.io>
140 lines
4.9 KiB
TypeScript
140 lines
4.9 KiB
TypeScript
/*
|
|
Copyright 2022 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, { useContext, useEffect, useState } from 'react';
|
|
import { Beacon, BeaconEvent, MatrixEvent } from 'matrix-js-sdk/src/matrix';
|
|
import { BeaconLocationState } from 'matrix-js-sdk/src/content-helpers';
|
|
import { randomString } from 'matrix-js-sdk/src/randomstring';
|
|
|
|
import { Icon as LocationMarkerIcon } from '../../../../res/img/element-icons/location.svg';
|
|
import MatrixClientContext from '../../../contexts/MatrixClientContext';
|
|
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
|
|
import { _t } from '../../../languageHandler';
|
|
import { useBeacon } from '../../../utils/beacon';
|
|
import { isSelfLocation } from '../../../utils/location';
|
|
import { BeaconDisplayStatus, getBeaconDisplayStatus } from '../beacon/displayStatus';
|
|
import BeaconStatus from '../beacon/BeaconStatus';
|
|
import Spinner from '../elements/Spinner';
|
|
import Map from '../location/Map';
|
|
import SmartMarker from '../location/SmartMarker';
|
|
import OwnBeaconStatus from '../beacon/OwnBeaconStatus';
|
|
import { IBodyProps } from "./IBodyProps";
|
|
|
|
const useBeaconState = (beaconInfoEvent: MatrixEvent): {
|
|
beacon?: Beacon;
|
|
description?: string;
|
|
latestLocationState?: BeaconLocationState;
|
|
isLive?: boolean;
|
|
} => {
|
|
const beacon = useBeacon(beaconInfoEvent);
|
|
|
|
const isLive = useEventEmitterState(
|
|
beacon,
|
|
BeaconEvent.LivenessChange,
|
|
() => beacon?.isLive);
|
|
|
|
const latestLocationState = useEventEmitterState(
|
|
beacon,
|
|
BeaconEvent.LocationUpdate,
|
|
() => beacon?.latestLocationState);
|
|
|
|
if (!beacon) {
|
|
return {};
|
|
}
|
|
|
|
const { description } = beacon.beaconInfo;
|
|
|
|
return {
|
|
beacon,
|
|
description,
|
|
isLive,
|
|
latestLocationState,
|
|
};
|
|
};
|
|
|
|
// multiple instances of same map might be in document
|
|
// eg thread and main timeline, reply
|
|
// maplibregl needs a unique id to attach the map instance to
|
|
const useUniqueId = (eventId: string): string => {
|
|
const [id, setId] = useState(`${eventId}_${randomString(8)}`);
|
|
|
|
useEffect(() => {
|
|
setId(`${eventId}_${randomString(8)}`);
|
|
}, [eventId]);
|
|
|
|
return id;
|
|
};
|
|
|
|
const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent }, ref) => {
|
|
const {
|
|
beacon,
|
|
isLive,
|
|
latestLocationState,
|
|
} = useBeaconState(mxEvent);
|
|
const mapId = useUniqueId(mxEvent.getId());
|
|
const [error, setError] = useState<Error>();
|
|
const matrixClient = useContext(MatrixClientContext);
|
|
const displayStatus = getBeaconDisplayStatus(isLive, latestLocationState, error);
|
|
const markerRoomMember = isSelfLocation(mxEvent.getContent()) ? mxEvent.sender : undefined;
|
|
|
|
const isOwnBeacon = beacon?.beaconInfoOwner === matrixClient.getUserId();
|
|
|
|
return (
|
|
<div className='mx_MBeaconBody' ref={ref}>
|
|
{ displayStatus === BeaconDisplayStatus.Active ?
|
|
<Map
|
|
id={mapId}
|
|
centerGeoUri={latestLocationState.uri}
|
|
onError={setError}
|
|
className="mx_MBeaconBody_map"
|
|
>
|
|
{
|
|
({ map }) =>
|
|
<SmartMarker
|
|
map={map}
|
|
id={`${mapId}-marker`}
|
|
geoUri={latestLocationState.uri}
|
|
roomMember={markerRoomMember}
|
|
useMemberColor
|
|
/>
|
|
}
|
|
</Map>
|
|
: <div className='mx_MBeaconBody_map mx_MBeaconBody_mapFallback'>
|
|
{ displayStatus === BeaconDisplayStatus.Loading ?
|
|
<Spinner h={32} w={32} /> :
|
|
<LocationMarkerIcon className='mx_MBeaconBody_mapFallbackIcon' />
|
|
}
|
|
</div>
|
|
}
|
|
{ isOwnBeacon ?
|
|
<OwnBeaconStatus
|
|
className='mx_MBeaconBody_chin'
|
|
beacon={beacon}
|
|
displayStatus={displayStatus}
|
|
/> :
|
|
<BeaconStatus
|
|
className='mx_MBeaconBody_chin'
|
|
beacon={beacon}
|
|
displayStatus={displayStatus}
|
|
label={_t('View live location')}
|
|
/>
|
|
}
|
|
</div>
|
|
);
|
|
});
|
|
|
|
export default MBeaconBody;
|
|
|