Live location sharing - update beacon tile with latest location (#8265)

* add useBeacon hook

Signed-off-by: Kerry Archibald <kerrya@element.io>

* update message tile types to work with function comp with ref

Signed-off-by: Kerry Archibald <kerrya@element.io>

* use beacon hook in beacon body

Signed-off-by: Kerry Archibald <kerrya@element.io>

* update beacon body with (textual) latest locations, test

Signed-off-by: Kerry Archibald <kerrya@element.io>

* language in comment

Signed-off-by: Kerry Archibald <kerrya@element.io>

* comments

Signed-off-by: Kerry Archibald <kerrya@element.io>

* copyright

Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
Kerry 2022-04-11 11:16:32 +02:00 committed by GitHub
parent 7ba991cd8c
commit aecd71ad15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 424 additions and 29 deletions

View file

@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { LegacyRef } from "react";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Relations } from "matrix-js-sdk/src/models/relations";
@ -52,4 +53,6 @@ export interface IBodyProps {
// helper function to access relations for this event
getRelationsForEvent?: (eventId: string, relationType: string, eventType: string) => Relations;
ref?: React.RefObject<any> | LegacyRef<any>;
}

View file

@ -15,41 +15,71 @@ limitations under the License.
*/
import React from 'react';
import { Beacon, getBeaconInfoIdentifier } from 'matrix-js-sdk/src/matrix';
import { BeaconEvent, MatrixEvent } from 'matrix-js-sdk/src/matrix';
import { BeaconLocationState } from 'matrix-js-sdk/src/content-helpers';
import MatrixClientContext from '../../../contexts/MatrixClientContext';
import { IBodyProps } from "./IBodyProps";
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
import { useBeacon } from '../../../utils/beacon';
export default class MLocationBody extends React.Component<IBodyProps> {
public static contextType = MatrixClientContext;
public context!: React.ContextType<typeof MatrixClientContext>;
private beacon: Beacon | undefined;
private roomId: string;
private beaconIdentifier: string;
const useBeaconState = (beaconInfoEvent: MatrixEvent): {
hasBeacon: boolean;
description?: string;
latestLocationState?: BeaconLocationState;
isLive?: boolean;
} => {
const beacon = useBeacon(beaconInfoEvent);
constructor(props: IBodyProps) {
super(props);
const isLive = useEventEmitterState(
beacon,
BeaconEvent.LivenessChange,
() => beacon?.isLive);
this.roomId = props.mxEvent.getRoomId();
const latestLocationState = useEventEmitterState(
beacon,
BeaconEvent.LocationUpdate,
() => beacon?.latestLocationState);
this.beaconIdentifier = getBeaconInfoIdentifier(props.mxEvent);
if (!beacon) {
return {
hasBeacon: false,
};
}
componentDidMount() {
const roomState = this.context.getRoom(this.roomId)?.currentState;
const { description } = beacon.beaconInfo;
const beacon = roomState?.beacons.get(this.beaconIdentifier);
return {
hasBeacon: true,
description,
isLive,
latestLocationState,
};
};
this.beacon = beacon;
const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, ...rest }, ref) => {
const {
hasBeacon,
isLive,
description,
latestLocationState,
} = useBeaconState(mxEvent);
if (!hasBeacon || !isLive) {
// TODO stopped, error states
return <span ref={ref}>Beacon stopped or replaced</span>;
}
render(): React.ReactElement<HTMLDivElement> {
if (!this.beacon) {
// TODO loading and error states
return null;
}
// TODO everything else :~)
const description = this.beacon.beaconInfo.description;
return <div>{ description }</div>;
}
}
return (
// TODO nice map
<div className='mx_MBeaconBody' ref={ref}>
<code>{ mxEvent.getId() }</code>&nbsp;
<span>Beacon "{ description }" </span>
{ latestLocationState ?
<span>{ `${latestLocationState.uri} at ${latestLocationState.timestamp}` }</span> :
<span data-test-id='beacon-waiting-for-location'>Waiting for location</span> }
</div>
);
});
export default MBeaconBody;

View file

@ -94,7 +94,7 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
};
}
private get evTypes(): Record<string, typeof React.Component> {
private get evTypes(): Record<string, React.ComponentType<Partial<IBodyProps>>> {
return {
[EventType.Sticker]: MStickerBody,
[M_POLL_START.name]: MPollBody,
@ -122,7 +122,7 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
const content = this.props.mxEvent.getContent();
const type = this.props.mxEvent.getType();
const msgtype = content.msgtype;
let BodyType: typeof React.Component | ReactAnyComponent = RedactedBody;
let BodyType: React.ComponentType<Partial<IBodyProps>> | ReactAnyComponent = RedactedBody;
if (!this.props.mxEvent.isRedacted()) {
// only resolve BodyType if event is not redacted
if (type && this.evTypes[type]) {