Live Location Sharing - left panel warning with error (#8201)
* add error style to left panel beacon warning Signed-off-by: Kerry Archibald <kerrya@element.io> * test Signed-off-by: Kerry Archibald <kerrya@element.io> * add beacon sort util * link to latest beacon room from left panel warning Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
parent
1175226bcb
commit
4922e19b5a
8 changed files with 289 additions and 24 deletions
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_LeftPanelLiveShareWarning {
|
.mx_LeftPanelLiveShareWarning {
|
||||||
|
@mixin ButtonResetDefault;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
@ -29,3 +30,7 @@ limitations under the License.
|
||||||
// go above to get hover for title
|
// go above to get hover for title
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_LeftPanelLiveShareWarning__error {
|
||||||
|
background-color: $alert;
|
||||||
|
}
|
||||||
|
|
|
@ -21,11 +21,31 @@ import { useEventEmitterState } from '../../../hooks/useEventEmitter';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore';
|
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore';
|
||||||
import { Icon as LiveLocationIcon } from '../../../../res/img/location/live-location.svg';
|
import { Icon as LiveLocationIcon } from '../../../../res/img/location/live-location.svg';
|
||||||
|
import { ViewRoomPayload } from '../../../dispatcher/payloads/ViewRoomPayload';
|
||||||
|
import { Action } from '../../../dispatcher/actions';
|
||||||
|
import dispatcher from '../../../dispatcher/dispatcher';
|
||||||
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isMinimized?: boolean;
|
isMinimized?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Choose the most relevant beacon
|
||||||
|
* and get its roomId
|
||||||
|
*/
|
||||||
|
const chooseBestBeaconRoomId = (liveBeaconIds, errorBeaconIds): string | undefined => {
|
||||||
|
// both lists are ordered by creation timestamp in store
|
||||||
|
// so select latest beacon
|
||||||
|
const beaconId = errorBeaconIds?.[0] ?? liveBeaconIds?.[0];
|
||||||
|
if (!beaconId) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const beacon = OwnBeaconStore.instance.getBeaconById(beaconId);
|
||||||
|
|
||||||
|
return beacon?.roomId;
|
||||||
|
};
|
||||||
|
|
||||||
const LeftPanelLiveShareWarning: React.FC<Props> = ({ isMinimized }) => {
|
const LeftPanelLiveShareWarning: React.FC<Props> = ({ isMinimized }) => {
|
||||||
const isMonitoringLiveLocation = useEventEmitterState(
|
const isMonitoringLiveLocation = useEventEmitterState(
|
||||||
OwnBeaconStore.instance,
|
OwnBeaconStore.instance,
|
||||||
|
@ -33,18 +53,48 @@ const LeftPanelLiveShareWarning: React.FC<Props> = ({ isMinimized }) => {
|
||||||
() => OwnBeaconStore.instance.isMonitoringLiveLocation,
|
() => OwnBeaconStore.instance.isMonitoringLiveLocation,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const beaconIdsWithWireError = useEventEmitterState(
|
||||||
|
OwnBeaconStore.instance,
|
||||||
|
OwnBeaconStoreEvent.WireError,
|
||||||
|
() => OwnBeaconStore.instance.getLiveBeaconIdsWithWireError(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const liveBeaconIds = useEventEmitterState(
|
||||||
|
OwnBeaconStore.instance,
|
||||||
|
OwnBeaconStoreEvent.LivenessChange,
|
||||||
|
() => OwnBeaconStore.instance.getLiveBeaconIds(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasWireErrors = !!beaconIdsWithWireError.length;
|
||||||
|
|
||||||
if (!isMonitoringLiveLocation) {
|
if (!isMonitoringLiveLocation) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div
|
const relevantBeaconRoomId = chooseBestBeaconRoomId(liveBeaconIds, beaconIdsWithWireError);
|
||||||
|
|
||||||
|
const onWarningClick = relevantBeaconRoomId ? () => {
|
||||||
|
dispatcher.dispatch<ViewRoomPayload>({
|
||||||
|
action: Action.ViewRoom,
|
||||||
|
room_id: relevantBeaconRoomId,
|
||||||
|
metricsTrigger: undefined,
|
||||||
|
});
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
const label = hasWireErrors ?
|
||||||
|
_t('An error occured whilst sharing your live location') :
|
||||||
|
_t('You are sharing your live location');
|
||||||
|
|
||||||
|
return <AccessibleButton
|
||||||
className={classNames('mx_LeftPanelLiveShareWarning', {
|
className={classNames('mx_LeftPanelLiveShareWarning', {
|
||||||
'mx_LeftPanelLiveShareWarning__minimized': isMinimized,
|
'mx_LeftPanelLiveShareWarning__minimized': isMinimized,
|
||||||
|
'mx_LeftPanelLiveShareWarning__error': hasWireErrors,
|
||||||
})}
|
})}
|
||||||
title={isMinimized ? _t('You are sharing your live location') : undefined}
|
title={isMinimized ? label : undefined}
|
||||||
|
onClick={onWarningClick}
|
||||||
>
|
>
|
||||||
{ isMinimized ? <LiveLocationIcon height={10} /> : _t('You are sharing your live location') }
|
{ isMinimized ? <LiveLocationIcon height={10} /> : label }
|
||||||
</div>;
|
</AccessibleButton>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LeftPanelLiveShareWarning;
|
export default LeftPanelLiveShareWarning;
|
||||||
|
|
|
@ -2896,6 +2896,7 @@
|
||||||
"Beta": "Beta",
|
"Beta": "Beta",
|
||||||
"Leave the beta": "Leave the beta",
|
"Leave the beta": "Leave the beta",
|
||||||
"Join the beta": "Join the beta",
|
"Join the beta": "Join the beta",
|
||||||
|
"An error occured whilst sharing your live location": "An error occured whilst sharing your live location",
|
||||||
"You are sharing your live location": "You are sharing your live location",
|
"You are sharing your live location": "You are sharing your live location",
|
||||||
"%(timeRemaining)s left": "%(timeRemaining)s left",
|
"%(timeRemaining)s left": "%(timeRemaining)s left",
|
||||||
"An error occured whilst sharing your live location, please try again": "An error occured whilst sharing your live location, please try again",
|
"An error occured whilst sharing your live location, please try again": "An error occured whilst sharing your live location, please try again",
|
||||||
|
|
|
@ -38,6 +38,7 @@ import {
|
||||||
ClearWatchCallback,
|
ClearWatchCallback,
|
||||||
GeolocationError,
|
GeolocationError,
|
||||||
mapGeolocationPositionToTimedGeo,
|
mapGeolocationPositionToTimedGeo,
|
||||||
|
sortBeaconsByLatestCreation,
|
||||||
TimedGeoUri,
|
TimedGeoUri,
|
||||||
watchPosition,
|
watchPosition,
|
||||||
} from "../utils/beacon";
|
} from "../utils/beacon";
|
||||||
|
@ -73,6 +74,10 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||||
* Reset on successful publish of location
|
* Reset on successful publish of location
|
||||||
*/
|
*/
|
||||||
public readonly beaconWireErrorCounts = new Map<string, number>();
|
public readonly beaconWireErrorCounts = new Map<string, number>();
|
||||||
|
/**
|
||||||
|
* ids of live beacons
|
||||||
|
* ordered by creation time descending
|
||||||
|
*/
|
||||||
private liveBeaconIds = [];
|
private liveBeaconIds = [];
|
||||||
private locationInterval: number;
|
private locationInterval: number;
|
||||||
private geolocationError: GeolocationError | undefined;
|
private geolocationError: GeolocationError | undefined;
|
||||||
|
@ -126,17 +131,17 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||||
// we don't actually do anything here
|
// we don't actually do anything here
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasLiveBeacons(roomId?: string): boolean {
|
public hasLiveBeacons = (roomId?: string): boolean => {
|
||||||
return !!this.getLiveBeaconIds(roomId).length;
|
return !!this.getLiveBeaconIds(roomId).length;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some live beacon has a wire error
|
* Some live beacon has a wire error
|
||||||
* Optionally filter by room
|
* Optionally filter by room
|
||||||
*/
|
*/
|
||||||
public hasWireErrors(roomId?: string): boolean {
|
public hasWireErrors = (roomId?: string): boolean => {
|
||||||
return this.getLiveBeaconIds(roomId).some(this.beaconHasWireError);
|
return this.getLiveBeaconIds(roomId).some(this.beaconHasWireError);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a beacon has failed to publish position
|
* If a beacon has failed to publish position
|
||||||
|
@ -157,16 +162,20 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||||
this.publishCurrentLocationToBeacons();
|
this.publishCurrentLocationToBeacons();
|
||||||
};
|
};
|
||||||
|
|
||||||
public getLiveBeaconIds(roomId?: string): string[] {
|
public getLiveBeaconIds = (roomId?: string): string[] => {
|
||||||
if (!roomId) {
|
if (!roomId) {
|
||||||
return this.liveBeaconIds;
|
return this.liveBeaconIds;
|
||||||
}
|
}
|
||||||
return this.liveBeaconIds.filter(beaconId => this.beaconsByRoomId.get(roomId)?.has(beaconId));
|
return this.liveBeaconIds.filter(beaconId => this.beaconsByRoomId.get(roomId)?.has(beaconId));
|
||||||
}
|
};
|
||||||
|
|
||||||
public getBeaconById(beaconId: string): Beacon | undefined {
|
public getLiveBeaconIdsWithWireError = (roomId?: string): string[] => {
|
||||||
|
return this.getLiveBeaconIds(roomId).filter(this.beaconHasWireError);
|
||||||
|
};
|
||||||
|
|
||||||
|
public getBeaconById = (beaconId: string): Beacon | undefined => {
|
||||||
return this.beacons.get(beaconId);
|
return this.beacons.get(beaconId);
|
||||||
}
|
};
|
||||||
|
|
||||||
public stopBeacon = async (beaconInfoType: string): Promise<void> => {
|
public stopBeacon = async (beaconInfoType: string): Promise<void> => {
|
||||||
const beacon = this.beacons.get(beaconInfoType);
|
const beacon = this.beacons.get(beaconInfoType);
|
||||||
|
@ -287,6 +296,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||||
const prevLiveBeaconIds = this.getLiveBeaconIds();
|
const prevLiveBeaconIds = this.getLiveBeaconIds();
|
||||||
this.liveBeaconIds = [...this.beacons.values()]
|
this.liveBeaconIds = [...this.beacons.values()]
|
||||||
.filter(beacon => beacon.isLive)
|
.filter(beacon => beacon.isLive)
|
||||||
|
.sort(sortBeaconsByLatestCreation)
|
||||||
.map(beacon => beacon.identifier);
|
.map(beacon => beacon.identifier);
|
||||||
|
|
||||||
const diff = arrayDiff(prevLiveBeaconIds, this.liveBeaconIds);
|
const diff = arrayDiff(prevLiveBeaconIds, this.liveBeaconIds);
|
||||||
|
|
|
@ -34,3 +34,7 @@ export const getBeaconExpiryTimestamp = (beacon: Beacon): number =>
|
||||||
|
|
||||||
export const sortBeaconsByLatestExpiry = (left: Beacon, right: Beacon): number =>
|
export const sortBeaconsByLatestExpiry = (left: Beacon, right: Beacon): number =>
|
||||||
getBeaconExpiryTimestamp(right) - getBeaconExpiryTimestamp(left);
|
getBeaconExpiryTimestamp(right) - getBeaconExpiryTimestamp(left);
|
||||||
|
|
||||||
|
// aka sort by timestamp descending
|
||||||
|
export const sortBeaconsByLatestCreation = (left: Beacon, right: Beacon): number =>
|
||||||
|
right.beaconInfo.timestamp - left.beaconInfo.timestamp;
|
||||||
|
|
|
@ -17,17 +17,23 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mocked } from 'jest-mock';
|
import { mocked } from 'jest-mock';
|
||||||
import { mount } from 'enzyme';
|
import { mount } from 'enzyme';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { Beacon } from 'matrix-js-sdk/src/matrix';
|
||||||
|
|
||||||
import '../../../skinned-sdk';
|
import '../../../skinned-sdk';
|
||||||
import LeftPanelLiveShareWarning from '../../../../src/components/views/beacon/LeftPanelLiveShareWarning';
|
import LeftPanelLiveShareWarning from '../../../../src/components/views/beacon/LeftPanelLiveShareWarning';
|
||||||
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../../src/stores/OwnBeaconStore';
|
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../../src/stores/OwnBeaconStore';
|
||||||
import { flushPromises } from '../../../test-utils';
|
import { flushPromises, makeBeaconInfoEvent } from '../../../test-utils';
|
||||||
|
import dispatcher from '../../../../src/dispatcher/dispatcher';
|
||||||
|
import { Action } from '../../../../src/dispatcher/actions';
|
||||||
|
|
||||||
jest.mock('../../../../src/stores/OwnBeaconStore', () => {
|
jest.mock('../../../../src/stores/OwnBeaconStore', () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
const EventEmitter = require("events");
|
const EventEmitter = require("events");
|
||||||
class MockOwnBeaconStore extends EventEmitter {
|
class MockOwnBeaconStore extends EventEmitter {
|
||||||
public hasLiveBeacons = jest.fn().mockReturnValue(false);
|
public getLiveBeaconIdsWithWireError = jest.fn().mockReturnValue([]);
|
||||||
|
public getBeaconById = jest.fn();
|
||||||
|
public getLiveBeaconIds = jest.fn().mockReturnValue([]);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -44,32 +50,136 @@ describe('<LeftPanelLiveShareWarning />', () => {
|
||||||
const getComponent = (props = {}) =>
|
const getComponent = (props = {}) =>
|
||||||
mount(<LeftPanelLiveShareWarning {...defaultProps} {...props} />);
|
mount(<LeftPanelLiveShareWarning {...defaultProps} {...props} />);
|
||||||
|
|
||||||
|
const roomId1 = '!room1:server';
|
||||||
|
const roomId2 = '!room2:server';
|
||||||
|
const aliceId = '@alive:server';
|
||||||
|
|
||||||
|
const now = 1647270879403;
|
||||||
|
const HOUR_MS = 3600000;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.spyOn(global.Date, 'now').mockReturnValue(now);
|
||||||
|
jest.spyOn(dispatcher, 'dispatch').mockClear().mockImplementation(() => { });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
jest.spyOn(global.Date, 'now').mockRestore();
|
||||||
|
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
// 12h old, 12h left
|
||||||
|
const beacon1 = new Beacon(makeBeaconInfoEvent(aliceId,
|
||||||
|
roomId1,
|
||||||
|
{ timeout: HOUR_MS * 24, timestamp: now - 12 * HOUR_MS },
|
||||||
|
'$1',
|
||||||
|
));
|
||||||
|
// 10h left
|
||||||
|
const beacon2 = new Beacon(makeBeaconInfoEvent(aliceId,
|
||||||
|
roomId2,
|
||||||
|
{ timeout: HOUR_MS * 10, timestamp: now },
|
||||||
|
'$2',
|
||||||
|
));
|
||||||
|
|
||||||
it('renders nothing when user has no live beacons', () => {
|
it('renders nothing when user has no live beacons', () => {
|
||||||
const component = getComponent();
|
const component = getComponent();
|
||||||
expect(component.html()).toBe(null);
|
expect(component.html()).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when user has live location monitor', () => {
|
describe('when user has live location monitor', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
mocked(OwnBeaconStore.instance).getBeaconById.mockImplementation(beaconId => {
|
||||||
|
if (beaconId === beacon1.identifier) {
|
||||||
|
return beacon1;
|
||||||
|
}
|
||||||
|
return beacon2;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocked(OwnBeaconStore.instance).isMonitoringLiveLocation = true;
|
mocked(OwnBeaconStore.instance).isMonitoringLiveLocation = true;
|
||||||
|
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithWireError.mockReturnValue([]);
|
||||||
|
mocked(OwnBeaconStore.instance).getLiveBeaconIds.mockReturnValue([beacon2.identifier, beacon1.identifier]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly when not minimized', () => {
|
it('renders correctly when not minimized', () => {
|
||||||
const component = getComponent();
|
const component = getComponent();
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('goes to room of latest beacon when clicked', () => {
|
||||||
|
const component = getComponent();
|
||||||
|
const dispatchSpy = jest.spyOn(dispatcher, 'dispatch');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
component.simulate('click');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dispatchSpy).toHaveBeenCalledWith({
|
||||||
|
action: Action.ViewRoom,
|
||||||
|
metricsTrigger: undefined,
|
||||||
|
// latest beacon's room
|
||||||
|
room_id: roomId2,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('renders correctly when minimized', () => {
|
it('renders correctly when minimized', () => {
|
||||||
const component = getComponent({ isMinimized: true });
|
const component = getComponent({ isMinimized: true });
|
||||||
expect(component).toMatchSnapshot();
|
expect(component).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders wire error', () => {
|
||||||
|
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithWireError.mockReturnValue([beacon1.identifier]);
|
||||||
|
const component = getComponent();
|
||||||
|
expect(component).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('goes to room of latest beacon with wire error when clicked', () => {
|
||||||
|
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithWireError.mockReturnValue([beacon1.identifier]);
|
||||||
|
const component = getComponent();
|
||||||
|
const dispatchSpy = jest.spyOn(dispatcher, 'dispatch');
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
component.simulate('click');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(dispatchSpy).toHaveBeenCalledWith({
|
||||||
|
action: Action.ViewRoom,
|
||||||
|
metricsTrigger: undefined,
|
||||||
|
// error beacon's room
|
||||||
|
room_id: roomId1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('goes back to default style when wire errors are cleared', () => {
|
||||||
|
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithWireError.mockReturnValue([beacon1.identifier]);
|
||||||
|
const component = getComponent();
|
||||||
|
// error mode
|
||||||
|
expect(component.find('.mx_LeftPanelLiveShareWarning').at(0).text()).toEqual(
|
||||||
|
'An error occured whilst sharing your live location',
|
||||||
|
);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
mocked(OwnBeaconStore.instance).getLiveBeaconIdsWithWireError.mockReturnValue([]);
|
||||||
|
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.WireError, 'abc');
|
||||||
|
});
|
||||||
|
|
||||||
|
component.setProps({});
|
||||||
|
|
||||||
|
// default mode
|
||||||
|
expect(component.find('.mx_LeftPanelLiveShareWarning').at(0).text()).toEqual(
|
||||||
|
'You are sharing your live location',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('removes itself when user stops having live beacons', async () => {
|
it('removes itself when user stops having live beacons', async () => {
|
||||||
const component = getComponent({ isMinimized: true });
|
const component = getComponent({ isMinimized: true });
|
||||||
// started out rendered
|
// started out rendered
|
||||||
expect(component.html()).toBeTruthy();
|
expect(component.html()).toBeTruthy();
|
||||||
|
|
||||||
mocked(OwnBeaconStore.instance).isMonitoringLiveLocation = false;
|
act(() => {
|
||||||
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.MonitoringLivePosition);
|
mocked(OwnBeaconStore.instance).isMonitoringLiveLocation = false;
|
||||||
|
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.MonitoringLivePosition);
|
||||||
|
});
|
||||||
|
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
component.setProps({});
|
component.setProps({});
|
||||||
|
|
|
@ -4,23 +4,73 @@ exports[`<LeftPanelLiveShareWarning /> when user has live location monitor rende
|
||||||
<LeftPanelLiveShareWarning
|
<LeftPanelLiveShareWarning
|
||||||
isMinimized={true}
|
isMinimized={true}
|
||||||
>
|
>
|
||||||
<div
|
<AccessibleButton
|
||||||
className="mx_LeftPanelLiveShareWarning mx_LeftPanelLiveShareWarning__minimized"
|
className="mx_LeftPanelLiveShareWarning mx_LeftPanelLiveShareWarning__minimized"
|
||||||
|
element="div"
|
||||||
|
onClick={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
title="You are sharing your live location"
|
title="You are sharing your live location"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
height={10}
|
className="mx_AccessibleButton mx_LeftPanelLiveShareWarning mx_LeftPanelLiveShareWarning__minimized"
|
||||||
/>
|
onClick={[Function]}
|
||||||
</div>
|
onKeyDown={[Function]}
|
||||||
|
onKeyUp={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
title="You are sharing your live location"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
height={10}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</AccessibleButton>
|
||||||
</LeftPanelLiveShareWarning>
|
</LeftPanelLiveShareWarning>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`<LeftPanelLiveShareWarning /> when user has live location monitor renders correctly when not minimized 1`] = `
|
exports[`<LeftPanelLiveShareWarning /> when user has live location monitor renders correctly when not minimized 1`] = `
|
||||||
<LeftPanelLiveShareWarning>
|
<LeftPanelLiveShareWarning>
|
||||||
<div
|
<AccessibleButton
|
||||||
className="mx_LeftPanelLiveShareWarning"
|
className="mx_LeftPanelLiveShareWarning"
|
||||||
|
element="div"
|
||||||
|
onClick={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
You are sharing your live location
|
<div
|
||||||
</div>
|
className="mx_AccessibleButton mx_LeftPanelLiveShareWarning"
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
onKeyUp={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
You are sharing your live location
|
||||||
|
</div>
|
||||||
|
</AccessibleButton>
|
||||||
|
</LeftPanelLiveShareWarning>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`<LeftPanelLiveShareWarning /> when user has live location monitor renders wire error 1`] = `
|
||||||
|
<LeftPanelLiveShareWarning>
|
||||||
|
<AccessibleButton
|
||||||
|
className="mx_LeftPanelLiveShareWarning mx_LeftPanelLiveShareWarning__error"
|
||||||
|
element="div"
|
||||||
|
onClick={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="mx_AccessibleButton mx_LeftPanelLiveShareWarning mx_LeftPanelLiveShareWarning__error"
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
onKeyUp={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
An error occured whilst sharing your live location
|
||||||
|
</div>
|
||||||
|
</AccessibleButton>
|
||||||
</LeftPanelLiveShareWarning>
|
</LeftPanelLiveShareWarning>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -16,7 +16,11 @@ limitations under the License.
|
||||||
|
|
||||||
import { Beacon } from "matrix-js-sdk/src/matrix";
|
import { Beacon } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { msUntilExpiry, sortBeaconsByLatestExpiry } from "../../../src/utils/beacon";
|
import {
|
||||||
|
msUntilExpiry,
|
||||||
|
sortBeaconsByLatestExpiry,
|
||||||
|
sortBeaconsByLatestCreation,
|
||||||
|
} from "../../../src/utils/beacon";
|
||||||
import { makeBeaconInfoEvent } from "../../test-utils";
|
import { makeBeaconInfoEvent } from "../../test-utils";
|
||||||
|
|
||||||
describe('beacon utils', () => {
|
describe('beacon utils', () => {
|
||||||
|
@ -80,4 +84,35 @@ describe('beacon utils', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('sortBeaconsByLatestCreation()', () => {
|
||||||
|
const roomId = '!room:server';
|
||||||
|
const aliceId = '@alive:server';
|
||||||
|
|
||||||
|
// 12h old, 12h left
|
||||||
|
const beacon1 = new Beacon(makeBeaconInfoEvent(aliceId,
|
||||||
|
roomId,
|
||||||
|
{ timeout: HOUR_MS * 24, timestamp: now - 12 * HOUR_MS },
|
||||||
|
'$1',
|
||||||
|
));
|
||||||
|
// 10h left
|
||||||
|
const beacon2 = new Beacon(makeBeaconInfoEvent(aliceId,
|
||||||
|
roomId,
|
||||||
|
{ timeout: HOUR_MS * 10, timestamp: now },
|
||||||
|
'$2',
|
||||||
|
));
|
||||||
|
|
||||||
|
// 1ms left
|
||||||
|
const beacon3 = new Beacon(makeBeaconInfoEvent(aliceId,
|
||||||
|
roomId,
|
||||||
|
{ timeout: HOUR_MS + 1, timestamp: now - HOUR_MS },
|
||||||
|
'$3',
|
||||||
|
));
|
||||||
|
|
||||||
|
it('sorts beacons by descending creation time', () => {
|
||||||
|
expect([beacon1, beacon2, beacon3].sort(sortBeaconsByLatestCreation)).toEqual([
|
||||||
|
beacon2, beacon3, beacon1,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue