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

Commit bc18bd2

Browse files
author
Kerry Archibald
committed
publish first location immediately
Signed-off-by: Kerry Archibald <[email protected]>
1 parent cf4d6e3 commit bc18bd2

File tree

7 files changed

+111
-201
lines changed

7 files changed

+111
-201
lines changed

src/components/views/location/LiveDurationDropdown.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,12 @@ import { _t } from '../../../languageHandler';
2121
import Dropdown from '../elements/Dropdown';
2222

2323
const DURATION_MS = {
24-
// TODO kerry debug only!!
25-
oneMin: 60000,
2624
fifteenMins: 900000,
2725
oneHour: 3600000,
2826
eightHours: 28800000,
2927
};
3028

31-
export const DEFAULT_DURATION_MS = DURATION_MS.oneMin;
29+
export const DEFAULT_DURATION_MS = DURATION_MS.fifteenMins;
3230

3331
interface Props {
3432
timeout: number;

src/stores/OwnBeaconStore.ts

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ import { M_BEACON } from "matrix-js-sdk/src/@types/beacon";
2929
import defaultDispatcher from "../dispatcher/dispatcher";
3030
import { ActionPayload } from "../dispatcher/payloads";
3131
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
32-
import { arrayHasDiff } from "../utils/arrays";
32+
import { arrayDiff } from "../utils/arrays";
3333
import {
3434
ClearWatchCallback,
3535
GeolocationError,
3636
mapGeolocationPositionToTimedGeo,
3737
TimedGeoUri,
3838
watchPosition,
3939
} from "../utils/beacon";
40+
import { getCurrentPosition } from "../utils/beacon/geolocation";
4041

4142
const isOwnBeacon = (beacon: Beacon, userId: string): boolean => beacon.beaconInfoOwner === userId;
4243

@@ -75,6 +76,11 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
7576
return OwnBeaconStore.internalInstance;
7677
}
7778

79+
public get isMonitoringLiveLocation() {
80+
// we are watching location as long as this is truthy
81+
return !!this.clearPositionWatch;
82+
}
83+
7884
protected async onNotReady() {
7985
this.matrixClient.removeListener(BeaconEvent.LivenessChange, this.onBeaconLiveness);
8086
this.matrixClient.removeListener(BeaconEvent.New, this.onNewBeacon);
@@ -181,10 +187,25 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
181187
.filter(beacon => beacon.isLive)
182188
.map(beacon => beacon.identifier);
183189

184-
if (arrayHasDiff(prevLiveBeaconIds, this.liveBeaconIds)) {
190+
const diff = arrayDiff(prevLiveBeaconIds, this.liveBeaconIds);
191+
192+
if (diff.added.length || diff.removed.length) {
185193
this.emit(OwnBeaconStoreEvent.LivenessChange, this.liveBeaconIds);
186194
}
187195

196+
// publish current location immediately
197+
// when there are new live beacons
198+
// and we already have a live monitor
199+
// so first position is published quickly
200+
// even when target is stationery
201+
//
202+
// when there is no existing live monitor
203+
// it will be created below by togglePollingLocation
204+
// and publish first position quickly
205+
if (diff.added.length && this.isMonitoringLiveLocation) {
206+
this.publishCurrentLocationToBeacons();
207+
}
208+
188209
// if overall liveness changed
189210
if (!!prevLiveBeaconIds?.length !== !!this.liveBeaconIds.length) {
190211
this.togglePollingLocation();
@@ -223,14 +244,11 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
223244
if (!this.lastPublishedPosition) {
224245
return;
225246
}
226-
// TODO should this use the timestamp the position was taken,
227-
// or a new timestamp when we send it
228-
// or should we geolocation.getCurrentPosition() here?
229-
const { publishedTimestamp, position } = this.lastPublishedPosition;
247+
const { publishedTimestamp } = this.lastPublishedPosition;
230248
// if position was last updated STATIC_UPDATE_INTERVAL ms ago or more
231-
// republish our last position
249+
// get our position and publish it
232250
if (publishedTimestamp <= Date.now() - STATIC_UPDATE_INTERVAL) {
233-
this.publishLocationToBeacons(position);
251+
this.publishCurrentLocationToBeacons();
234252
}
235253
}, STATIC_UPDATE_INTERVAL);
236254
};
@@ -263,6 +281,10 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
263281
}
264282
};
265283

284+
/**
285+
* Sends m.location events to all live beacons
286+
* Sets last published beacon
287+
*/
266288
private publishLocationToBeacons = async (position: TimedGeoUri) => {
267289
this.lastPublishedPosition = { position, publishedTimestamp: Date.now() };
268290
// TODO handle failure in individual beacon without rejecting rest
@@ -273,8 +295,22 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
273295

274296
private debouncedPublishLocationToBeacons = debounce(this.publishLocationToBeacons, MOVING_UPDATE_INTERVAL);
275297

298+
/**
299+
* Sends m.location event to referencing given beacon
300+
*/
276301
private sendLocationToBeacon = async (beacon: Beacon, { geoUri, timestamp }: TimedGeoUri) => {
277302
const content = makeBeaconContent(geoUri, timestamp, beacon.beaconInfoId);
278303
await this.matrixClient.sendEvent(beacon.roomId, M_BEACON.name, content);
279304
};
305+
306+
/**
307+
* Gets the current location
308+
* (as opposed to using watching location)
309+
* and publishes it to all live beacons
310+
*/
311+
private publishCurrentLocationToBeacons = async () => {
312+
const position = await getCurrentPosition();
313+
// TODO error handling
314+
this.publishLocationToBeacons(mapGeolocationPositionToTimedGeo(position));
315+
};
280316
}

src/utils/beacon/geolocation.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,21 @@ export const mapGeolocationPositionToTimedGeo = (position: GeolocationPosition):
112112
return { timestamp: position.timestamp, geoUri: getGeoUri(genericPositionFromGeolocation(position)) };
113113
};
114114

115+
/**
116+
* Gets current position, returns a promise
117+
* @returns Promise<GeolocationPosition>
118+
*/
119+
export const getCurrentPosition = async (): Promise<GeolocationPosition> => {
120+
try {
121+
const position = await new Promise((resolve: PositionCallback, reject) => {
122+
getGeolocation().getCurrentPosition(resolve, reject, GeolocationOptions);
123+
});
124+
return position;
125+
} catch (error) {
126+
throw new Error(mapGeolocationError(error));
127+
}
128+
};
129+
115130
export type ClearWatchCallback = () => void;
116131
export const watchPosition = (
117132
onWatchPosition: PositionCallback,

test/components/views/beacon/RoomLiveShareWarning-test.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@ import {
2626
findByTestId,
2727
getMockClientWithEventEmitter,
2828
makeBeaconInfoEvent,
29+
mockGeolocation,
2930
resetAsyncStoreWithClient,
3031
setupAsyncStoreWithClient,
3132
} from '../../../test-utils';
3233

3334
jest.useFakeTimers();
35+
mockGeolocation();
3436
describe('<RoomLiveShareWarning />', () => {
3537
const aliceId = '@alice:server.org';
3638
const room1Id = '$room1:server.org';
@@ -40,6 +42,7 @@ describe('<RoomLiveShareWarning />', () => {
4042
getVisibleRooms: jest.fn().mockReturnValue([]),
4143
getUserId: jest.fn().mockReturnValue(aliceId),
4244
unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }),
45+
sendEvent: jest.fn(),
4346
});
4447

4548
// 14.03.2022 16:15
@@ -137,12 +140,16 @@ describe('<RoomLiveShareWarning />', () => {
137140

138141
it('renders correctly with one live beacon in room', () => {
139142
const component = getComponent({ roomId: room1Id });
140-
expect(component).toMatchSnapshot();
143+
// beacons have generated ids that break snapshots
144+
// assert on html
145+
expect(component.html()).toMatchSnapshot();
141146
});
142147

143148
it('renders correctly with two live beacons in room', () => {
144149
const component = getComponent({ roomId: room2Id });
145-
expect(component).toMatchSnapshot();
150+
// beacons have generated ids that break snapshots
151+
// assert on html
152+
expect(component.html()).toMatchSnapshot();
146153
// later expiry displayed
147154
expect(getExpiryText(component)).toEqual('12h left');
148155
});
Lines changed: 2 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -1,191 +1,5 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`<RoomLiveShareWarning /> when user has live beacons renders correctly with one live beacon in room 1`] = `
4-
<RoomLiveShareWarning
5-
roomId="$room1:server.org"
6-
>
7-
<div
8-
className="mx_RoomLiveShareWarning"
9-
>
10-
<StyledLiveBeaconIcon
11-
className="mx_RoomLiveShareWarning_icon"
12-
>
13-
<div
14-
className="mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon"
15-
/>
16-
</StyledLiveBeaconIcon>
17-
<span
18-
className="mx_RoomLiveShareWarning_label"
19-
>
20-
You are sharing your live location
21-
</span>
22-
<LiveTimeRemaining
23-
beacon={
24-
Beacon {
25-
"_beaconInfo": Object {
26-
"assetType": "m.self",
27-
"description": undefined,
28-
"live": true,
29-
"timeout": 3600000,
30-
"timestamp": 1647270879403,
31-
},
32-
"_events": Object {
33-
"Beacon.LivenessChange": Array [
34-
[Function],
35-
[Function],
36-
],
37-
"Beacon.new": [Function],
38-
"Beacon.update": [Function],
39-
},
40-
"_eventsCount": 3,
41-
"_isLive": true,
42-
"_maxListeners": undefined,
43-
"livenessWatchInterval": 1000000000002,
44-
"roomId": "$room1:server.org",
45-
"rootEvent": Object {
46-
"content": Object {
47-
"org.matrix.msc3488.asset": Object {
48-
"type": "m.self",
49-
},
50-
"org.matrix.msc3488.ts": 1647270879403,
51-
"org.matrix.msc3489.beacon_info": Object {
52-
"description": undefined,
53-
"live": true,
54-
"timeout": 3600000,
55-
},
56-
},
57-
"event_id": "$0",
58-
"room_id": "$room1:server.org",
59-
"state_key": "@alice:server.org",
60-
"type": "org.matrix.msc3489.beacon_info.@alice:server.org.2",
61-
},
62-
Symbol(kCapture): false,
63-
}
64-
}
65-
>
66-
<span
67-
className="mx_RoomLiveShareWarning_expiry"
68-
data-test-id="room-live-share-expiry"
69-
>
70-
1h left
71-
</span>
72-
</LiveTimeRemaining>
73-
<AccessibleButton
74-
data-test-id="room-live-share-stop-sharing"
75-
disabled={false}
76-
element="button"
77-
kind="danger"
78-
onClick={[Function]}
79-
role="button"
80-
tabIndex={0}
81-
>
82-
<button
83-
className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger"
84-
data-test-id="room-live-share-stop-sharing"
85-
onClick={[Function]}
86-
onKeyDown={[Function]}
87-
onKeyUp={[Function]}
88-
role="button"
89-
tabIndex={0}
90-
>
91-
Stop sharing
92-
</button>
93-
</AccessibleButton>
94-
</div>
95-
</RoomLiveShareWarning>
96-
`;
3+
exports[`<RoomLiveShareWarning /> when user has live beacons renders correctly with one live beacon in room 1`] = `"<div class=\\"mx_RoomLiveShareWarning\\"><div class=\\"mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon\\"></div><span class=\\"mx_RoomLiveShareWarning_label\\">You are sharing your live location</span><span data-test-id=\\"room-live-share-expiry\\" class=\\"mx_RoomLiveShareWarning_expiry\\">1h left</span><button data-test-id=\\"room-live-share-stop-sharing\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger\\">Stop sharing</button></div>"`;
974
98-
exports[`<RoomLiveShareWarning /> when user has live beacons renders correctly with two live beacons in room 1`] = `
99-
<RoomLiveShareWarning
100-
roomId="$room2:server.org"
101-
>
102-
<div
103-
className="mx_RoomLiveShareWarning"
104-
>
105-
<StyledLiveBeaconIcon
106-
className="mx_RoomLiveShareWarning_icon"
107-
>
108-
<div
109-
className="mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon"
110-
/>
111-
</StyledLiveBeaconIcon>
112-
<span
113-
className="mx_RoomLiveShareWarning_label"
114-
>
115-
You are sharing your live location
116-
</span>
117-
<LiveTimeRemaining
118-
beacon={
119-
Beacon {
120-
"_beaconInfo": Object {
121-
"assetType": "m.self",
122-
"description": undefined,
123-
"live": true,
124-
"timeout": 43200000,
125-
"timestamp": 1647270879403,
126-
},
127-
"_events": Object {
128-
"Beacon.LivenessChange": Array [
129-
[Function],
130-
[Function],
131-
],
132-
"Beacon.new": [Function],
133-
"Beacon.update": [Function],
134-
},
135-
"_eventsCount": 3,
136-
"_isLive": true,
137-
"_maxListeners": undefined,
138-
"livenessWatchInterval": 1000000000010,
139-
"roomId": "$room2:server.org",
140-
"rootEvent": Object {
141-
"content": Object {
142-
"org.matrix.msc3488.asset": Object {
143-
"type": "m.self",
144-
},
145-
"org.matrix.msc3488.ts": 1647270879403,
146-
"org.matrix.msc3489.beacon_info": Object {
147-
"description": undefined,
148-
"live": true,
149-
"timeout": 43200000,
150-
},
151-
},
152-
"event_id": "$2",
153-
"room_id": "$room2:server.org",
154-
"state_key": "@alice:server.org",
155-
"type": "org.matrix.msc3489.beacon_info.@alice:server.org.4",
156-
},
157-
Symbol(kCapture): false,
158-
}
159-
}
160-
>
161-
<span
162-
className="mx_RoomLiveShareWarning_expiry"
163-
data-test-id="room-live-share-expiry"
164-
>
165-
12h left
166-
</span>
167-
</LiveTimeRemaining>
168-
<AccessibleButton
169-
data-test-id="room-live-share-stop-sharing"
170-
disabled={false}
171-
element="button"
172-
kind="danger"
173-
onClick={[Function]}
174-
role="button"
175-
tabIndex={0}
176-
>
177-
<button
178-
className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger"
179-
data-test-id="room-live-share-stop-sharing"
180-
onClick={[Function]}
181-
onKeyDown={[Function]}
182-
onKeyUp={[Function]}
183-
role="button"
184-
tabIndex={0}
185-
>
186-
Stop sharing
187-
</button>
188-
</AccessibleButton>
189-
</div>
190-
</RoomLiveShareWarning>
191-
`;
5+
exports[`<RoomLiveShareWarning /> when user has live beacons renders correctly with two live beacons in room 1`] = `"<div class=\\"mx_RoomLiveShareWarning\\"><div class=\\"mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon\\"></div><span class=\\"mx_RoomLiveShareWarning_label\\">You are sharing your live location</span><span data-test-id=\\"room-live-share-expiry\\" class=\\"mx_RoomLiveShareWarning_expiry\\">12h left</span><button data-test-id=\\"room-live-share-stop-sharing\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger\\">Stop sharing</button></div>"`;

0 commit comments

Comments
 (0)