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

Commit

Permalink
Live location share - redact related locations on beacon redaction (P…
Browse files Browse the repository at this point in the history
…SF-1151) (#8926)

* redact beacon locations on redaction

* redact beacon locations on beacon info redaction

* fussy import ordering

* use real fake redaction in beaconbody test
  • Loading branch information
Kerry authored Jun 29, 2022
1 parent 1304ff1 commit 4486509
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 4 deletions.
48 changes: 44 additions & 4 deletions src/components/views/messages/MBeaconBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,18 @@ 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 React, { useCallback, useContext, useEffect, useState } from 'react';
import {
Beacon,
BeaconEvent,
MatrixEvent,
MatrixEventEvent,
MatrixClient,
RelationType,
} from 'matrix-js-sdk/src/matrix';
import { BeaconLocationState } from 'matrix-js-sdk/src/content-helpers';
import { randomString } from 'matrix-js-sdk/src/randomstring';
import { M_BEACON } from 'matrix-js-sdk/src/@types/beacon';

import MatrixClientContext from '../../../contexts/MatrixClientContext';
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
Expand All @@ -27,10 +35,11 @@ import { isBeaconWaitingToStart, useBeacon } from '../../../utils/beacon';
import { isSelfLocation } from '../../../utils/location';
import { BeaconDisplayStatus, getBeaconDisplayStatus } from '../beacon/displayStatus';
import BeaconStatus from '../beacon/BeaconStatus';
import OwnBeaconStatus from '../beacon/OwnBeaconStatus';
import Map from '../location/Map';
import MapFallback from '../location/MapFallback';
import SmartMarker from '../location/SmartMarker';
import OwnBeaconStatus from '../beacon/OwnBeaconStatus';
import { GetRelationsForEvent } from '../rooms/EventTile';
import BeaconViewDialog from '../beacon/BeaconViewDialog';
import { IBodyProps } from "./IBodyProps";

Expand Down Expand Up @@ -87,7 +96,36 @@ const useUniqueId = (eventId: string): string => {
return id;
};

const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent }, ref) => {
// remove related beacon locations on beacon redaction
const useHandleBeaconRedaction = (
event: MatrixEvent,
getRelationsForEvent: GetRelationsForEvent,
cli: MatrixClient,
): void => {
const onBeforeBeaconInfoRedaction = useCallback((_event: MatrixEvent, redactionEvent: MatrixEvent) => {
const relations = getRelationsForEvent ?
getRelationsForEvent(event.getId(), RelationType.Reference, M_BEACON.name) :
undefined;

relations?.getRelations()?.forEach(locationEvent => {
cli.redactEvent(
locationEvent.getRoomId(),
locationEvent.getId(),
undefined,
redactionEvent.getContent(),
);
});
}, [event, getRelationsForEvent, cli]);

useEffect(() => {
event.addListener(MatrixEventEvent.BeforeRedaction, onBeforeBeaconInfoRedaction);
return () => {
event.removeListener(MatrixEventEvent.BeforeRedaction, onBeforeBeaconInfoRedaction);
};
}, [event, onBeforeBeaconInfoRedaction]);
};

const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelationsForEvent }, ref) => {
const {
beacon,
isLive,
Expand All @@ -102,6 +140,8 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent }, ref) =>
const markerRoomMember = isSelfLocation(mxEvent.getContent()) ? mxEvent.sender : undefined;
const isOwnBeacon = beacon?.beaconInfoOwner === matrixClient.getUserId();

useHandleBeaconRedaction(mxEvent, getRelationsForEvent, matrixClient);

const onClick = () => {
if (displayStatus !== BeaconDisplayStatus.Active) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ exports[`<BeaconMarker /> renders marker when beacon has location 1`] = `
},
"org.matrix.msc3488.ts": 1647270879404,
},
"room_id": undefined,
"sender": "@alice:server",
"type": "org.matrix.msc3672.beacon",
},
Expand Down
125 changes: 125 additions & 0 deletions test/components/views/messages/MBeaconBody-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ import maplibregl from 'maplibre-gl';
import {
BeaconEvent,
getBeaconInfoIdentifier,
RelationType,
MatrixEvent,
EventType,
} from 'matrix-js-sdk/src/matrix';
import { Relations } from 'matrix-js-sdk/src/models/relations';
import { M_BEACON } from 'matrix-js-sdk/src/@types/beacon';

import MBeaconBody from '../../../../src/components/views/messages/MBeaconBody';
import {
Expand Down Expand Up @@ -53,6 +58,7 @@ describe('<MBeaconBody />', () => {
}),
getUserId: jest.fn().mockReturnValue(aliceId),
getRoom: jest.fn(),
redactEvent: jest.fn(),
});

const defaultEvent = makeBeaconInfoEvent(aliceId,
Expand Down Expand Up @@ -333,4 +339,123 @@ describe('<MBeaconBody />', () => {
expect(mockMarker.setLngLat).toHaveBeenCalledWith({ lat: 52, lon: 42 });
});
});

describe('redaction', () => {
const makeEvents = (): {
beaconInfoEvent: MatrixEvent;
location1: MatrixEvent;
location2: MatrixEvent;
} => {
const beaconInfoEvent = makeBeaconInfoEvent(
aliceId,
roomId,
{ isLive: true },
'$alice-room1-1',
);

const location1 = makeBeaconEvent(
aliceId, { beaconInfoId: beaconInfoEvent.getId(), geoUri: 'geo:51,41', timestamp: now + 1 },
roomId,
);
location1.event.event_id = '1';
const location2 = makeBeaconEvent(
aliceId, { beaconInfoId: beaconInfoEvent.getId(), geoUri: 'geo:52,42', timestamp: now + 10000 },
roomId,
);
location2.event.event_id = '2';
return { beaconInfoEvent, location1, location2 };
};

const redactionEvent = new MatrixEvent({ type: EventType.RoomRedaction, content: { reason: 'test reason' } });

const setupRoomWithBeacon = (beaconInfoEvent, locationEvents: MatrixEvent[] = []) => {
const room = makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
const beaconInstance = room.currentState.beacons.get(getBeaconInfoIdentifier(beaconInfoEvent));
beaconInstance.addLocations(locationEvents);
};
const mockGetRelationsForEvent = (locationEvents: MatrixEvent[] = []) => {
const relations = new Relations(RelationType.Reference, M_BEACON.name, mockClient);
jest.spyOn(relations, 'getRelations').mockReturnValue(locationEvents);

const getRelationsForEvent = jest.fn().mockReturnValue(relations);

return getRelationsForEvent;
};

it('does nothing when getRelationsForEvent is falsy', () => {
const { beaconInfoEvent, location1, location2 } = makeEvents();
setupRoomWithBeacon(beaconInfoEvent, [location1, location2]);

getComponent({ mxEvent: beaconInfoEvent });

act(() => {
beaconInfoEvent.makeRedacted(redactionEvent);
});

// no error, no redactions
expect(mockClient.redactEvent).not.toHaveBeenCalled();
});

it('cleans up redaction listener on unmount', () => {
const { beaconInfoEvent, location1, location2 } = makeEvents();
setupRoomWithBeacon(beaconInfoEvent, [location1, location2]);
const removeListenerSpy = jest.spyOn(beaconInfoEvent, 'removeListener');

const component = getComponent({ mxEvent: beaconInfoEvent });

act(() => {
component.unmount();
});

expect(removeListenerSpy).toHaveBeenCalled();
});

it('does nothing when beacon has no related locations', async () => {
const { beaconInfoEvent } = makeEvents();
// no locations
setupRoomWithBeacon(beaconInfoEvent, []);
const getRelationsForEvent = await mockGetRelationsForEvent();

getComponent({ mxEvent: beaconInfoEvent, getRelationsForEvent });

act(() => {
beaconInfoEvent.makeRedacted(redactionEvent);
});

expect(getRelationsForEvent).toHaveBeenCalledWith(
beaconInfoEvent.getId(), RelationType.Reference, M_BEACON.name,
);
expect(mockClient.redactEvent).not.toHaveBeenCalled();
});

it('redacts related locations on beacon redaction', async () => {
const { beaconInfoEvent, location1, location2 } = makeEvents();
setupRoomWithBeacon(beaconInfoEvent, [location1, location2]);

const getRelationsForEvent = await mockGetRelationsForEvent([location1, location2]);

getComponent({ mxEvent: beaconInfoEvent, getRelationsForEvent });

act(() => {
beaconInfoEvent.makeRedacted(redactionEvent);
});

expect(getRelationsForEvent).toHaveBeenCalledWith(
beaconInfoEvent.getId(), RelationType.Reference, M_BEACON.name,
);
expect(mockClient.redactEvent).toHaveBeenCalledTimes(2);
expect(mockClient.redactEvent).toHaveBeenCalledWith(
roomId,
location1.getId(),
undefined,
{ reason: 'test reason' },
);
expect(mockClient.redactEvent).toHaveBeenCalledWith(
roomId,
location2.getId(),
undefined,
{ reason: 'test reason' },
);
});
});
});
2 changes: 2 additions & 0 deletions test/test-utils/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const DEFAULT_CONTENT_PROPS: ContentProps = {
export const makeBeaconEvent = (
sender: string,
contentProps: Partial<ContentProps> = {},
roomId?: string,
): MatrixEvent => {
const { geoUri, timestamp, beaconInfoId, description } = {
...DEFAULT_CONTENT_PROPS,
Expand All @@ -105,6 +106,7 @@ export const makeBeaconEvent = (

return new MatrixEvent({
type: M_BEACON.name,
room_id: roomId,
sender,
content: makeBeaconContent(geoUri, timestamp, beaconInfoId, description),
});
Expand Down

0 comments on commit 4486509

Please sign in to comment.