Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit b51ef24

Browse files
author
Kerry
authored
Live location share - forward latest location (PSF-1044) (#8860)
* handle beacon location events in ForwardDialog * add transformer for forwarded events in MessageContextMenu * remove canForward * update snapshots for beacon model change * add comments * fix bad copy pasted test * add test for beacon locations
1 parent 0a90674 commit b51ef24

File tree

12 files changed

+292
-81
lines changed

12 files changed

+292
-81
lines changed

__mocks__/maplibre-gl.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
117
const EventEmitter = require("events");
2-
const { LngLat, NavigationControl, LngLatBounds } = require('maplibre-gl');
18+
const { LngLat, NavigationControl, LngLatBounds, AttributionControl } = require('maplibre-gl');
319

420
class MockMap extends EventEmitter {
521
addControl = jest.fn();
@@ -27,4 +43,5 @@ module.exports = {
2743
LngLat,
2844
LngLatBounds,
2945
NavigationControl,
46+
AttributionControl,
3047
};

src/components/views/context_menus/MessageContextMenu.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import Modal from '../../../Modal';
3030
import Resend from '../../../Resend';
3131
import SettingsStore from '../../../settings/SettingsStore';
3232
import { isUrlPermitted } from '../../../HtmlUtils';
33-
import { canEditContent, canForward, editEvent, isContentActionable, isLocationEvent } from '../../../utils/EventUtils';
33+
import { canEditContent, editEvent, isContentActionable, isLocationEvent } from '../../../utils/EventUtils';
3434
import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from './IconizedContextMenu';
3535
import { ReadPinsEventId } from "../right_panel/types";
3636
import { Action } from "../../../dispatcher/actions";
@@ -51,6 +51,7 @@ import { GetRelationsForEvent, IEventTileOps } from "../rooms/EventTile";
5151
import { OpenForwardDialogPayload } from "../../../dispatcher/payloads/OpenForwardDialogPayload";
5252
import { OpenReportEventDialogPayload } from "../../../dispatcher/payloads/OpenReportEventDialogPayload";
5353
import { createMapSiteLinkFromEvent } from '../../../utils/location';
54+
import { getForwardableEvent } from '../../../events/forward/getForwardableEvent';
5455

5556
interface IProps extends IPosition {
5657
chevronFace: ChevronFace;
@@ -188,10 +189,10 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
188189
this.closeMenu();
189190
};
190191

191-
private onForwardClick = (): void => {
192+
private onForwardClick = (forwardableEvent: MatrixEvent) => (): void => {
192193
dis.dispatch<OpenForwardDialogPayload>({
193194
action: Action.OpenForwardDialog,
194-
event: this.props.mxEvent,
195+
event: forwardableEvent,
195196
permalinkCreator: this.props.permalinkCreator,
196197
});
197198
this.closeMenu();
@@ -379,12 +380,13 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
379380
}
380381

381382
let forwardButton: JSX.Element;
382-
if (contentActionable && canForward(mxEvent)) {
383+
const forwardableEvent = getForwardableEvent(mxEvent, cli);
384+
if (contentActionable && forwardableEvent) {
383385
forwardButton = (
384386
<IconizedContextMenuOption
385387
iconClassName="mx_MessageContextMenu_iconForward"
386388
label={_t("Forward")}
387-
onClick={this.onForwardClick}
389+
onClick={this.onForwardClick(forwardableEvent)}
388390
/>
389391
);
390392
}

src/components/views/dialogs/ForwardDialog.tsx

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member";
2323
import { EventType } from "matrix-js-sdk/src/@types/event";
2424
import { ILocationContent, LocationAssetType, M_TIMESTAMP } from "matrix-js-sdk/src/@types/location";
2525
import { makeLocationContent } from "matrix-js-sdk/src/content-helpers";
26+
import { M_BEACON } from "matrix-js-sdk/src/@types/beacon";
2627

2728
import { _t } from "../../../languageHandler";
2829
import dis from "../../../dispatcher/dispatcher";
@@ -158,32 +159,42 @@ const Entry: React.FC<IEntryProps> = ({ room, type, content, matrixClient: cli,
158159
</div>;
159160
};
160161

161-
const getStrippedEventContent = (event: MatrixEvent): IContent => {
162+
const transformEvent = (event: MatrixEvent): {type: string, content: IContent } => {
162163
const {
163164
// eslint-disable-next-line @typescript-eslint/no-unused-vars
164165
"m.relates_to": _, // strip relations - in future we will attach a relation pointing at the original event
165166
// We're taking a shallow copy here to avoid https://github.com/vector-im/element-web/issues/10924
166167
...content
167168
} = event.getContent();
168169

170+
// beacon pulses get transformed into static locations on forward
171+
const type = M_BEACON.matches(event.getType()) ? EventType.RoomMessage : event.getType();
172+
169173
// self location shares should have their description removed
170174
// and become 'pin' share type
171-
if (isLocationEvent(event) && isSelfLocation(content as ILocationContent)) {
175+
if (
176+
(isLocationEvent(event) && isSelfLocation(content as ILocationContent)) ||
177+
// beacon pulses get transformed into static locations on forward
178+
M_BEACON.matches(event.getType())
179+
) {
172180
const timestamp = M_TIMESTAMP.findIn<number>(content);
173181
const geoUri = locationEventGeoUri(event);
174182
return {
175-
...content,
176-
...makeLocationContent(
177-
undefined, // text
178-
geoUri,
179-
timestamp || Date.now(),
180-
undefined, // description
181-
LocationAssetType.Pin,
182-
),
183+
type,
184+
content: {
185+
...content,
186+
...makeLocationContent(
187+
undefined, // text
188+
geoUri,
189+
timestamp || Date.now(),
190+
undefined, // description
191+
LocationAssetType.Pin,
192+
),
193+
},
183194
};
184195
}
185196

186-
return content;
197+
return { type, content };
187198
};
188199

189200
const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCreator, onFinished }) => {
@@ -193,7 +204,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
193204
cli.getProfileInfo(userId).then(info => setProfileInfo(info));
194205
}, [cli, userId]);
195206

196-
const content = getStrippedEventContent(event);
207+
const { type, content } = transformEvent(event);
197208

198209
// For the message preview we fake the sender as ourselves
199210
const mockEvent = new MatrixEvent({
@@ -293,7 +304,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
293304
<Entry
294305
key={room.roomId}
295306
room={room}
296-
type={event.getType()}
307+
type={type}
297308
content={content}
298309
matrixClient={cli}
299310
onFinished={onFinished}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { getBeaconInfoIdentifier } from "matrix-js-sdk/src/matrix";
18+
19+
import { ForwardableEventTransformFunction } from "./types";
20+
21+
/**
22+
* Live location beacons should forward their latest location as a static pin location
23+
* If the beacon is not live, or doesn't have a location forwarding is not allowed
24+
*/
25+
export const getForwardableBeaconEvent: ForwardableEventTransformFunction = (event, cli) => {
26+
const room = cli.getRoom(event.getRoomId());
27+
const beacon = room.currentState.beacons?.get(getBeaconInfoIdentifier(event));
28+
const latestLocationEvent = beacon.latestLocationEvent;
29+
30+
if (beacon.isLive && latestLocationEvent) {
31+
return latestLocationEvent;
32+
}
33+
return null;
34+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { M_POLL_START } from "matrix-events-sdk";
18+
import { M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon";
19+
import { MatrixEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
20+
21+
import { getForwardableBeaconEvent } from "./getForwardableBeacon";
22+
23+
/**
24+
* Get forwardable event for a given event
25+
* If an event is not forwardable return null
26+
*/
27+
export const getForwardableEvent = (event: MatrixEvent, cli: MatrixClient): MatrixEvent | null => {
28+
if (M_POLL_START.matches(event.getType())) {
29+
return null;
30+
}
31+
if (M_BEACON_INFO.matches(event.getType())) {
32+
return getForwardableBeaconEvent(event, cli);
33+
}
34+
return event;
35+
};
36+

src/events/forward/types.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
18+
19+
export type ForwardableEventTransformFunction = (event: MatrixEvent, cli: MatrixClient) => MatrixEvent | null;

src/utils/EventUtils.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,6 @@ export const isLocationEvent = (event: MatrixEvent): boolean => {
281281
);
282282
};
283283

284-
export function canForward(event: MatrixEvent): boolean {
285-
return !(
286-
M_POLL_START.matches(event.getType()) ||
287-
// disallow forwarding until psf-1044
288-
M_BEACON_INFO.matches(event.getType())
289-
);
290-
}
291-
292284
export function hasThreadSummary(event: MatrixEvent): boolean {
293285
return event.isThreadRoot && event.getThread()?.length && !!event.getThread().replyToEvent;
294286
}

test/components/views/beacon/__snapshots__/BeaconMarker-test.tsx.snap

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,20 @@ exports[`<BeaconMarker /> renders marker when beacon has location 1`] = `
2626
},
2727
"_eventsCount": 5,
2828
"_isLive": true,
29-
"_latestLocationState": Object {
30-
"description": undefined,
31-
"timestamp": 1647270879404,
32-
"uri": "geo:51,41",
29+
"_latestLocationEvent": Object {
30+
"content": Object {
31+
"m.relates_to": Object {
32+
"event_id": "$alice-room1-1",
33+
"rel_type": "m.reference",
34+
},
35+
"org.matrix.msc3488.location": Object {
36+
"description": undefined,
37+
"uri": "geo:51,41",
38+
},
39+
"org.matrix.msc3488.ts": 1647270879404,
40+
},
41+
"sender": "@alice:server",
42+
"type": "org.matrix.msc3672.beacon",
3343
},
3444
"_maxListeners": undefined,
3545
"clearLatestLocation": [Function],

test/components/views/beacon/__snapshots__/BeaconStatus-test.tsx.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ exports[`<BeaconStatus /> active state renders without children 1`] = `
1616
"_events": Object {},
1717
"_eventsCount": 0,
1818
"_isLive": undefined,
19-
"_latestLocationState": undefined,
19+
"_latestLocationEvent": undefined,
2020
"_maxListeners": undefined,
2121
"clearLatestLocation": [Function],
2222
"livenessWatchTimeout": undefined,
@@ -78,7 +78,7 @@ exports[`<BeaconStatus /> active state renders without children 1`] = `
7878
"_events": Object {},
7979
"_eventsCount": 0,
8080
"_isLive": undefined,
81-
"_latestLocationState": undefined,
81+
"_latestLocationEvent": undefined,
8282
"_maxListeners": undefined,
8383
"clearLatestLocation": [Function],
8484
"livenessWatchTimeout": undefined,

0 commit comments

Comments
 (0)