mirror of https://github.com/vector-im/riot-web
Live location sharing - display wire error in room (#8198)
* expose wire errors in more useful way * add wire error state to room live share warning bar Signed-off-by: Kerry Archibald <kerrya@element.io> * stylelint Signed-off-by: Kerry Archibald <kerrya@element.io> * add types to getLabel helper Signed-off-by: Kerry Archibald <kerrya@element.io>pull/21833/head
parent
60ca8996d3
commit
1175226bcb
|
@ -48,3 +48,13 @@ limitations under the License.
|
||||||
.mx_RoomLiveShareWarning_spinner {
|
.mx_RoomLiveShareWarning_spinner {
|
||||||
margin-right: $spacing-16;
|
margin-right: $spacing-16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomLiveShareWarning_closeButton {
|
||||||
|
@mixin ButtonResetDefault;
|
||||||
|
margin-left: $spacing-16;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomLiveShareWarning_closeButtonIcon {
|
||||||
|
height: $font-18px;
|
||||||
|
padding: $spacing-4;
|
||||||
|
}
|
||||||
|
|
|
@ -18,19 +18,16 @@ import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Room, Beacon } from 'matrix-js-sdk/src/matrix';
|
import { Room, Beacon } from 'matrix-js-sdk/src/matrix';
|
||||||
|
|
||||||
|
import { formatDuration } from '../../../DateUtils';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
|
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
|
||||||
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore';
|
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
|
||||||
import StyledLiveBeaconIcon from './StyledLiveBeaconIcon';
|
|
||||||
import { formatDuration } from '../../../DateUtils';
|
|
||||||
import { getBeaconMsUntilExpiry, sortBeaconsByLatestExpiry } from '../../../utils/beacon';
|
|
||||||
import Spinner from '../elements/Spinner';
|
|
||||||
import { useInterval } from '../../../hooks/useTimeout';
|
import { useInterval } from '../../../hooks/useTimeout';
|
||||||
|
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore';
|
||||||
interface Props {
|
import { getBeaconMsUntilExpiry, sortBeaconsByLatestExpiry } from '../../../utils/beacon';
|
||||||
roomId: Room['roomId'];
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
}
|
import Spinner from '../elements/Spinner';
|
||||||
|
import StyledLiveBeaconIcon from './StyledLiveBeaconIcon';
|
||||||
|
import { Icon as CloseIcon } from '../../../../res/img/image-view/close.svg';
|
||||||
|
|
||||||
const MINUTE_MS = 60000;
|
const MINUTE_MS = 60000;
|
||||||
const HOUR_MS = MINUTE_MS * 60;
|
const HOUR_MS = MINUTE_MS * 60;
|
||||||
|
@ -72,24 +69,20 @@ const useMsRemaining = (beacon: Beacon): number => {
|
||||||
type LiveBeaconsState = {
|
type LiveBeaconsState = {
|
||||||
beacon?: Beacon;
|
beacon?: Beacon;
|
||||||
onStopSharing?: () => void;
|
onStopSharing?: () => void;
|
||||||
|
onResetWireError?: () => void;
|
||||||
stoppingInProgress?: boolean;
|
stoppingInProgress?: boolean;
|
||||||
hasStopSharingError?: boolean;
|
hasStopSharingError?: boolean;
|
||||||
|
hasWireError?: boolean;
|
||||||
};
|
};
|
||||||
const useLiveBeacons = (roomId: Room['roomId']): LiveBeaconsState => {
|
const useLiveBeacons = (liveBeaconIds: string[], roomId: string): LiveBeaconsState => {
|
||||||
const [stoppingInProgress, setStoppingInProgress] = useState(false);
|
const [stoppingInProgress, setStoppingInProgress] = useState(false);
|
||||||
const [error, setError] = useState<Error>();
|
const [error, setError] = useState<Error>();
|
||||||
|
|
||||||
// do we have an active geolocation.watchPosition
|
const hasWireError = useEventEmitterState(
|
||||||
const isMonitoringLiveLocation = useEventEmitterState(
|
|
||||||
OwnBeaconStore.instance,
|
OwnBeaconStore.instance,
|
||||||
OwnBeaconStoreEvent.MonitoringLivePosition,
|
OwnBeaconStoreEvent.WireError,
|
||||||
() => OwnBeaconStore.instance.isMonitoringLiveLocation,
|
() =>
|
||||||
);
|
OwnBeaconStore.instance.hasWireErrors(roomId),
|
||||||
|
|
||||||
const liveBeaconIds = useEventEmitterState(
|
|
||||||
OwnBeaconStore.instance,
|
|
||||||
OwnBeaconStoreEvent.LivenessChange,
|
|
||||||
() => OwnBeaconStore.instance.getLiveBeaconIds(roomId),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// reset stopping in progress on change in live ids
|
// reset stopping in progress on change in live ids
|
||||||
|
@ -98,10 +91,6 @@ const useLiveBeacons = (roomId: Room['roomId']): LiveBeaconsState => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
}, [liveBeaconIds]);
|
}, [liveBeaconIds]);
|
||||||
|
|
||||||
if (!isMonitoringLiveLocation || !liveBeaconIds?.length) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// select the beacon with latest expiry to display expiry time
|
// select the beacon with latest expiry to display expiry time
|
||||||
const beacon = liveBeaconIds.map(beaconId => OwnBeaconStore.instance.getBeaconById(beaconId))
|
const beacon = liveBeaconIds.map(beaconId => OwnBeaconStore.instance.getBeaconById(beaconId))
|
||||||
.sort(sortBeaconsByLatestExpiry)
|
.sort(sortBeaconsByLatestExpiry)
|
||||||
|
@ -120,7 +109,18 @@ const useLiveBeacons = (roomId: Room['roomId']): LiveBeaconsState => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { onStopSharing, beacon, stoppingInProgress, hasStopSharingError: !!error };
|
const onResetWireError = () => {
|
||||||
|
liveBeaconIds.map(beaconId => OwnBeaconStore.instance.resetWireError(beaconId));
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onStopSharing,
|
||||||
|
onResetWireError,
|
||||||
|
beacon,
|
||||||
|
stoppingInProgress,
|
||||||
|
hasWireError,
|
||||||
|
hasStopSharingError: !!error,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const LiveTimeRemaining: React.FC<{ beacon: Beacon }> = ({ beacon }) => {
|
const LiveTimeRemaining: React.FC<{ beacon: Beacon }> = ({ beacon }) => {
|
||||||
|
@ -135,44 +135,103 @@ const LiveTimeRemaining: React.FC<{ beacon: Beacon }> = ({ beacon }) => {
|
||||||
>{ liveTimeRemaining }</span>;
|
>{ liveTimeRemaining }</span>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const RoomLiveShareWarning: React.FC<Props> = ({ roomId }) => {
|
const getLabel = (hasWireError: boolean, hasStopSharingError: boolean): string => {
|
||||||
|
if (hasWireError) {
|
||||||
|
return _t('An error occured whilst sharing your live location, please try again');
|
||||||
|
}
|
||||||
|
if (hasStopSharingError) {
|
||||||
|
return _t('An error occurred while stopping your live location, please try again');
|
||||||
|
}
|
||||||
|
return _t('You are sharing your live location');
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RoomLiveShareWarningInnerProps {
|
||||||
|
liveBeaconIds: string[];
|
||||||
|
roomId: Room['roomId'];
|
||||||
|
}
|
||||||
|
const RoomLiveShareWarningInner: React.FC<RoomLiveShareWarningInnerProps> = ({ liveBeaconIds, roomId }) => {
|
||||||
const {
|
const {
|
||||||
onStopSharing,
|
onStopSharing,
|
||||||
|
onResetWireError,
|
||||||
beacon,
|
beacon,
|
||||||
stoppingInProgress,
|
stoppingInProgress,
|
||||||
hasStopSharingError,
|
hasStopSharingError,
|
||||||
} = useLiveBeacons(roomId);
|
hasWireError,
|
||||||
|
} = useLiveBeacons(liveBeaconIds, roomId);
|
||||||
|
|
||||||
if (!beacon) {
|
if (!beacon) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasError = hasStopSharingError || hasWireError;
|
||||||
|
|
||||||
|
const onButtonClick = () => {
|
||||||
|
if (hasWireError) {
|
||||||
|
onResetWireError();
|
||||||
|
} else {
|
||||||
|
onStopSharing();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return <div
|
return <div
|
||||||
className={classNames('mx_RoomLiveShareWarning')}
|
className={classNames('mx_RoomLiveShareWarning')}
|
||||||
>
|
>
|
||||||
<StyledLiveBeaconIcon className="mx_RoomLiveShareWarning_icon" withError={hasStopSharingError} />
|
<StyledLiveBeaconIcon className="mx_RoomLiveShareWarning_icon" withError={hasError} />
|
||||||
|
|
||||||
<span className="mx_RoomLiveShareWarning_label">
|
<span className="mx_RoomLiveShareWarning_label">
|
||||||
{ hasStopSharingError ?
|
{ getLabel(hasWireError, hasStopSharingError) }
|
||||||
_t('An error occurred while stopping your live location, please try again') :
|
|
||||||
_t('You are sharing your live location')
|
|
||||||
}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{ stoppingInProgress &&
|
{ stoppingInProgress &&
|
||||||
<span className='mx_RoomLiveShareWarning_spinner'><Spinner h={16} w={16} /></span>
|
<span className='mx_RoomLiveShareWarning_spinner'><Spinner h={16} w={16} /></span>
|
||||||
}
|
}
|
||||||
{ !stoppingInProgress && !hasStopSharingError && <LiveTimeRemaining beacon={beacon} /> }
|
{ !stoppingInProgress && !hasError && <LiveTimeRemaining beacon={beacon} /> }
|
||||||
|
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
data-test-id='room-live-share-stop-sharing'
|
data-test-id='room-live-share-primary-button'
|
||||||
onClick={onStopSharing}
|
onClick={onButtonClick}
|
||||||
kind='danger'
|
kind='danger'
|
||||||
element='button'
|
element='button'
|
||||||
disabled={stoppingInProgress}
|
disabled={stoppingInProgress}
|
||||||
>
|
>
|
||||||
{ hasStopSharingError ? _t('Retry') : _t('Stop sharing') }
|
{ hasError ? _t('Retry') : _t('Stop sharing') }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
{ hasWireError && <AccessibleButton
|
||||||
|
data-test-id='room-live-share-wire-error-close-button'
|
||||||
|
title={_t('Stop sharing and close')}
|
||||||
|
element='button'
|
||||||
|
className='mx_RoomLiveShareWarning_closeButton'
|
||||||
|
onClick={onStopSharing}
|
||||||
|
>
|
||||||
|
<CloseIcon className='mx_RoomLiveShareWarning_closeButtonIcon' />
|
||||||
|
</AccessibleButton> }
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
roomId: Room['roomId'];
|
||||||
|
}
|
||||||
|
const RoomLiveShareWarning: React.FC<Props> = ({ roomId }) => {
|
||||||
|
// do we have an active geolocation.watchPosition
|
||||||
|
const isMonitoringLiveLocation = useEventEmitterState(
|
||||||
|
OwnBeaconStore.instance,
|
||||||
|
OwnBeaconStoreEvent.MonitoringLivePosition,
|
||||||
|
() => OwnBeaconStore.instance.isMonitoringLiveLocation,
|
||||||
|
);
|
||||||
|
|
||||||
|
const liveBeaconIds = useEventEmitterState(
|
||||||
|
OwnBeaconStore.instance,
|
||||||
|
OwnBeaconStoreEvent.LivenessChange,
|
||||||
|
() => OwnBeaconStore.instance.getLiveBeaconIds(roomId),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isMonitoringLiveLocation || !liveBeaconIds.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split into outer/inner to avoid watching various parts of live beacon state
|
||||||
|
// when there are none
|
||||||
|
return <RoomLiveShareWarningInner liveBeaconIds={liveBeaconIds} roomId={roomId} />;
|
||||||
|
};
|
||||||
|
|
||||||
export default RoomLiveShareWarning;
|
export default RoomLiveShareWarning;
|
||||||
|
|
|
@ -2898,8 +2898,10 @@
|
||||||
"Join the beta": "Join the beta",
|
"Join the beta": "Join the beta",
|
||||||
"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 occurred while stopping your live location, please try again": "An error occurred while stopping your live location, please try again",
|
"An error occurred while stopping your live location, please try again": "An error occurred while stopping your live location, please try again",
|
||||||
"Stop sharing": "Stop sharing",
|
"Stop sharing": "Stop sharing",
|
||||||
|
"Stop sharing and close": "Stop sharing and close",
|
||||||
"Avatar": "Avatar",
|
"Avatar": "Avatar",
|
||||||
"This room is public": "This room is public",
|
"This room is public": "This room is public",
|
||||||
"Away": "Away",
|
"Away": "Away",
|
||||||
|
|
|
@ -130,16 +130,24 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||||
return !!this.getLiveBeaconIds(roomId).length;
|
return !!this.getLiveBeaconIds(roomId).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some live beacon has a wire error
|
||||||
|
* Optionally filter by room
|
||||||
|
*/
|
||||||
|
public hasWireErrors(roomId?: string): boolean {
|
||||||
|
return this.getLiveBeaconIds(roomId).some(this.beaconHasWireError);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a beacon has failed to publish position
|
* If a beacon has failed to publish position
|
||||||
* past the allowed consecutive failure count (BAIL_AFTER_CONSECUTIVE_ERROR_COUNT)
|
* past the allowed consecutive failure count (BAIL_AFTER_CONSECUTIVE_ERROR_COUNT)
|
||||||
* Then consider it to have an error
|
* Then consider it to have an error
|
||||||
*/
|
*/
|
||||||
public hasWireError(beaconId: string): boolean {
|
public beaconHasWireError = (beaconId: string): boolean => {
|
||||||
return this.beaconWireErrorCounts.get(beaconId) >= BAIL_AFTER_CONSECUTIVE_ERROR_COUNT;
|
return this.beaconWireErrorCounts.get(beaconId) >= BAIL_AFTER_CONSECUTIVE_ERROR_COUNT;
|
||||||
}
|
};
|
||||||
|
|
||||||
public resetWireError(beaconId: string): void {
|
public resetWireError = (beaconId: string): void => {
|
||||||
this.incrementBeaconWireErrorCount(beaconId, false);
|
this.incrementBeaconWireErrorCount(beaconId, false);
|
||||||
|
|
||||||
// always publish to all live beacons together
|
// always publish to all live beacons together
|
||||||
|
@ -147,7 +155,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||||
// to keep lastPublishedTimestamp simple
|
// to keep lastPublishedTimestamp simple
|
||||||
// and extra published locations don't hurt
|
// and extra published locations don't hurt
|
||||||
this.publishCurrentLocationToBeacons();
|
this.publishCurrentLocationToBeacons();
|
||||||
}
|
};
|
||||||
|
|
||||||
public getLiveBeaconIds(roomId?: string): string[] {
|
public getLiveBeaconIds(roomId?: string): string[] {
|
||||||
if (!roomId) {
|
if (!roomId) {
|
||||||
|
@ -230,7 +238,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||||
* Live beacon ids that do not have wire errors
|
* Live beacon ids that do not have wire errors
|
||||||
*/
|
*/
|
||||||
private get healthyLiveBeaconIds() {
|
private get healthyLiveBeaconIds() {
|
||||||
return this.liveBeaconIds.filter(beaconId => !this.hasWireError(beaconId));
|
return this.liveBeaconIds.filter(beaconId => !this.beaconHasWireError(beaconId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private initialiseBeaconState = () => {
|
private initialiseBeaconState = () => {
|
||||||
|
@ -458,7 +466,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||||
* - emit if beacon error count crossed threshold
|
* - emit if beacon error count crossed threshold
|
||||||
*/
|
*/
|
||||||
private incrementBeaconWireErrorCount = (beaconId: string, isError: boolean): void => {
|
private incrementBeaconWireErrorCount = (beaconId: string, isError: boolean): void => {
|
||||||
const hadError = this.hasWireError(beaconId);
|
const hadError = this.beaconHasWireError(beaconId);
|
||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
// increment error count
|
// increment error count
|
||||||
|
@ -471,7 +479,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||||
this.beaconWireErrorCounts.delete(beaconId);
|
this.beaconWireErrorCounts.delete(beaconId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasWireError(beaconId) !== hadError) {
|
if (this.beaconHasWireError(beaconId) !== hadError) {
|
||||||
this.emit(OwnBeaconStoreEvent.WireError, beaconId);
|
this.emit(OwnBeaconStoreEvent.WireError, beaconId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -101,6 +101,7 @@ describe('<RoomLiveShareWarning />', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
jest.spyOn(OwnBeaconStore.instance, 'hasWireErrors').mockRestore();
|
||||||
await resetAsyncStoreWithClient(OwnBeaconStore.instance);
|
await resetAsyncStoreWithClient(OwnBeaconStore.instance);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -238,13 +239,13 @@ describe('<RoomLiveShareWarning />', () => {
|
||||||
const component = getComponent({ roomId: room2Id });
|
const component = getComponent({ roomId: room2Id });
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
findByTestId(component, 'room-live-share-stop-sharing').at(0).simulate('click');
|
findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click');
|
||||||
component.setProps({});
|
component.setProps({});
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledTimes(2);
|
expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledTimes(2);
|
||||||
expect(component.find('Spinner').length).toBeTruthy();
|
expect(component.find('Spinner').length).toBeTruthy();
|
||||||
expect(findByTestId(component, 'room-live-share-stop-sharing').at(0).props().disabled).toBeTruthy();
|
expect(findByTestId(component, 'room-live-share-primary-button').at(0).props().disabled).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays error when stop sharing fails', async () => {
|
it('displays error when stop sharing fails', async () => {
|
||||||
|
@ -256,7 +257,7 @@ describe('<RoomLiveShareWarning />', () => {
|
||||||
.mockResolvedValue(({ event_id: '1' }));
|
.mockResolvedValue(({ event_id: '1' }));
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
findByTestId(component, 'room-live-share-stop-sharing').at(0).simulate('click');
|
findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click');
|
||||||
await flushPromisesWithFakeTimers();
|
await flushPromisesWithFakeTimers();
|
||||||
});
|
});
|
||||||
component.setProps({});
|
component.setProps({});
|
||||||
|
@ -264,7 +265,7 @@ describe('<RoomLiveShareWarning />', () => {
|
||||||
expect(component.html()).toMatchSnapshot();
|
expect(component.html()).toMatchSnapshot();
|
||||||
|
|
||||||
act(() => {
|
act(() => {
|
||||||
findByTestId(component, 'room-live-share-stop-sharing').at(0).simulate('click');
|
findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click');
|
||||||
component.setProps({});
|
component.setProps({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -277,7 +278,7 @@ describe('<RoomLiveShareWarning />', () => {
|
||||||
|
|
||||||
// stop the beacon
|
// stop the beacon
|
||||||
act(() => {
|
act(() => {
|
||||||
findByTestId(component, 'room-live-share-stop-sharing').at(0).simulate('click');
|
findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click');
|
||||||
});
|
});
|
||||||
// time travel until room1Beacon1 is expired
|
// time travel until room1Beacon1 is expired
|
||||||
act(() => {
|
act(() => {
|
||||||
|
@ -293,9 +294,83 @@ describe('<RoomLiveShareWarning />', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// button not disabled and expiry time shown
|
// button not disabled and expiry time shown
|
||||||
expect(findByTestId(component, 'room-live-share-stop-sharing').at(0).props().disabled).toBeFalsy();
|
expect(findByTestId(component, 'room-live-share-primary-button').at(0).props().disabled).toBeFalsy();
|
||||||
expect(findByTestId(component, 'room-live-share-expiry').text()).toEqual('1h left');
|
expect(findByTestId(component, 'room-live-share-expiry').text()).toEqual('1h left');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('with wire errors', () => {
|
||||||
|
it('displays wire error when mounted with wire errors', async () => {
|
||||||
|
const hasWireErrorsSpy = jest.spyOn(OwnBeaconStore.instance, 'hasWireErrors').mockReturnValue(true);
|
||||||
|
const component = getComponent({ roomId: room2Id });
|
||||||
|
|
||||||
|
expect(component).toMatchSnapshot();
|
||||||
|
expect(hasWireErrorsSpy).toHaveBeenCalledWith(room2Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays wire error when wireError event is emitted and beacons have errors', async () => {
|
||||||
|
const hasWireErrorsSpy = jest.spyOn(OwnBeaconStore.instance, 'hasWireErrors').mockReturnValue(false);
|
||||||
|
const component = getComponent({ roomId: room2Id });
|
||||||
|
|
||||||
|
// update mock and emit event
|
||||||
|
act(() => {
|
||||||
|
hasWireErrorsSpy.mockReturnValue(true);
|
||||||
|
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.WireError, room2Beacon1.getType());
|
||||||
|
});
|
||||||
|
component.setProps({});
|
||||||
|
|
||||||
|
// renders wire error ui
|
||||||
|
expect(component.find('.mx_RoomLiveShareWarning_label').text()).toEqual(
|
||||||
|
'An error occured whilst sharing your live location, please try again',
|
||||||
|
);
|
||||||
|
expect(findByTestId(component, 'room-live-share-wire-error-close-button').length).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('stops displaying wire error when errors are cleared', async () => {
|
||||||
|
const hasWireErrorsSpy = jest.spyOn(OwnBeaconStore.instance, 'hasWireErrors').mockReturnValue(true);
|
||||||
|
const component = getComponent({ roomId: room2Id });
|
||||||
|
|
||||||
|
// update mock and emit event
|
||||||
|
act(() => {
|
||||||
|
hasWireErrorsSpy.mockReturnValue(false);
|
||||||
|
OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.WireError, room2Beacon1.getType());
|
||||||
|
});
|
||||||
|
component.setProps({});
|
||||||
|
|
||||||
|
// renders error-free ui
|
||||||
|
expect(component.find('.mx_RoomLiveShareWarning_label').text()).toEqual(
|
||||||
|
'You are sharing your live location',
|
||||||
|
);
|
||||||
|
expect(findByTestId(component, 'room-live-share-wire-error-close-button').length).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicking retry button resets wire errors', async () => {
|
||||||
|
jest.spyOn(OwnBeaconStore.instance, 'hasWireErrors').mockReturnValue(true);
|
||||||
|
const resetErrorSpy = jest.spyOn(OwnBeaconStore.instance, 'resetWireError');
|
||||||
|
|
||||||
|
const component = getComponent({ roomId: room2Id });
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resetErrorSpy).toHaveBeenCalledWith(room2Beacon1.getType());
|
||||||
|
expect(resetErrorSpy).toHaveBeenCalledWith(room2Beacon2.getType());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clicking close button stops beacons', async () => {
|
||||||
|
jest.spyOn(OwnBeaconStore.instance, 'hasWireErrors').mockReturnValue(true);
|
||||||
|
const stopBeaconSpy = jest.spyOn(OwnBeaconStore.instance, 'stopBeacon');
|
||||||
|
|
||||||
|
const component = getComponent({ roomId: room2Id });
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
findByTestId(component, 'room-live-share-wire-error-close-button').at(0).simulate('click');
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(stopBeaconSpy).toHaveBeenCalledWith(room2Beacon1.getType());
|
||||||
|
expect(stopBeaconSpy).toHaveBeenCalledWith(room2Beacon2.getType());
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,86 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available 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>"`;
|
exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available 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-primary-button\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger\\">Stop sharing</button></div>"`;
|
||||||
|
|
||||||
exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available 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>"`;
|
exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available 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-primary-button\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger\\">Stop sharing</button></div>"`;
|
||||||
|
|
||||||
exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available stopping beacons displays error when stop sharing fails 1`] = `"<div class=\\"mx_RoomLiveShareWarning\\"><div class=\\"mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon mx_StyledLiveBeaconIcon_error\\"></div><span class=\\"mx_RoomLiveShareWarning_label\\">An error occurred while stopping your live location, please try again</span><button data-test-id=\\"room-live-share-stop-sharing\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger\\">Retry</button></div>"`;
|
exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available stopping beacons displays error when stop sharing fails 1`] = `"<div class=\\"mx_RoomLiveShareWarning\\"><div class=\\"mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon mx_StyledLiveBeaconIcon_error\\"></div><span class=\\"mx_RoomLiveShareWarning_label\\">An error occurred while stopping your live location, please try again</span><button data-test-id=\\"room-live-share-primary-button\\" role=\\"button\\" tabindex=\\"0\\" class=\\"mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger\\">Retry</button></div>"`;
|
||||||
|
|
||||||
|
exports[`<RoomLiveShareWarning /> when user has live beacons and geolocation is available with wire errors displays wire error when mounted with wire errors 1`] = `
|
||||||
|
<RoomLiveShareWarning
|
||||||
|
roomId="$room2:server.org"
|
||||||
|
>
|
||||||
|
<RoomLiveShareWarningInner
|
||||||
|
liveBeaconIds={
|
||||||
|
Array [
|
||||||
|
"org.matrix.msc3489.beacon_info.@alice:server.org.3",
|
||||||
|
"org.matrix.msc3489.beacon_info.@alice:server.org.4",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
roomId="$room2:server.org"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="mx_RoomLiveShareWarning"
|
||||||
|
>
|
||||||
|
<StyledLiveBeaconIcon
|
||||||
|
className="mx_RoomLiveShareWarning_icon"
|
||||||
|
withError={true}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="mx_StyledLiveBeaconIcon mx_RoomLiveShareWarning_icon mx_StyledLiveBeaconIcon_error"
|
||||||
|
/>
|
||||||
|
</StyledLiveBeaconIcon>
|
||||||
|
<span
|
||||||
|
className="mx_RoomLiveShareWarning_label"
|
||||||
|
>
|
||||||
|
An error occured whilst sharing your live location, please try again
|
||||||
|
</span>
|
||||||
|
<AccessibleButton
|
||||||
|
data-test-id="room-live-share-primary-button"
|
||||||
|
disabled={false}
|
||||||
|
element="button"
|
||||||
|
kind="danger"
|
||||||
|
onClick={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_danger"
|
||||||
|
data-test-id="room-live-share-primary-button"
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
onKeyUp={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
Retry
|
||||||
|
</button>
|
||||||
|
</AccessibleButton>
|
||||||
|
<AccessibleButton
|
||||||
|
className="mx_RoomLiveShareWarning_closeButton"
|
||||||
|
data-test-id="room-live-share-wire-error-close-button"
|
||||||
|
element="button"
|
||||||
|
onClick={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
title="Stop sharing and close"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="mx_AccessibleButton mx_RoomLiveShareWarning_closeButton"
|
||||||
|
data-test-id="room-live-share-wire-error-close-button"
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
onKeyUp={[Function]}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
title="Stop sharing and close"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="mx_RoomLiveShareWarning_closeButtonIcon"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>
|
||||||
|
</RoomLiveShareWarningInner>
|
||||||
|
</RoomLiveShareWarning>
|
||||||
|
`;
|
||||||
|
|
|
@ -863,7 +863,8 @@ describe('OwnBeaconStore', () => {
|
||||||
|
|
||||||
// called for each position from watchPosition
|
// called for each position from watchPosition
|
||||||
expect(mockClient.sendEvent).toHaveBeenCalledTimes(5);
|
expect(mockClient.sendEvent).toHaveBeenCalledTimes(5);
|
||||||
expect(store.hasWireError(alicesRoom1BeaconInfo.getType())).toBe(false);
|
expect(store.beaconHasWireError(alicesRoom1BeaconInfo.getType())).toBe(false);
|
||||||
|
expect(store.hasWireErrors()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('continues publishing positions when a beacon fails intermittently', async () => {
|
it('continues publishing positions when a beacon fails intermittently', async () => {
|
||||||
|
@ -889,7 +890,8 @@ describe('OwnBeaconStore', () => {
|
||||||
|
|
||||||
// called for each position from watchPosition
|
// called for each position from watchPosition
|
||||||
expect(mockClient.sendEvent).toHaveBeenCalledTimes(5);
|
expect(mockClient.sendEvent).toHaveBeenCalledTimes(5);
|
||||||
expect(store.hasWireError(alicesRoom1BeaconInfo.getType())).toBe(false);
|
expect(store.beaconHasWireError(alicesRoom1BeaconInfo.getType())).toBe(false);
|
||||||
|
expect(store.hasWireErrors()).toBe(false);
|
||||||
expect(emitSpy).not.toHaveBeenCalledWith(
|
expect(emitSpy).not.toHaveBeenCalledWith(
|
||||||
OwnBeaconStoreEvent.WireError, alicesRoom1BeaconInfo.getType(),
|
OwnBeaconStoreEvent.WireError, alicesRoom1BeaconInfo.getType(),
|
||||||
);
|
);
|
||||||
|
@ -911,7 +913,8 @@ describe('OwnBeaconStore', () => {
|
||||||
|
|
||||||
// only two allowed failures
|
// only two allowed failures
|
||||||
expect(mockClient.sendEvent).toHaveBeenCalledTimes(2);
|
expect(mockClient.sendEvent).toHaveBeenCalledTimes(2);
|
||||||
expect(store.hasWireError(alicesRoom1BeaconInfo.getType())).toBe(true);
|
expect(store.beaconHasWireError(alicesRoom1BeaconInfo.getType())).toBe(true);
|
||||||
|
expect(store.hasWireErrors()).toBe(true);
|
||||||
expect(emitSpy).toHaveBeenCalledWith(
|
expect(emitSpy).toHaveBeenCalledWith(
|
||||||
OwnBeaconStoreEvent.WireError, alicesRoom1BeaconInfo.getType(),
|
OwnBeaconStoreEvent.WireError, alicesRoom1BeaconInfo.getType(),
|
||||||
);
|
);
|
||||||
|
@ -933,7 +936,9 @@ describe('OwnBeaconStore', () => {
|
||||||
|
|
||||||
// only two allowed failures
|
// only two allowed failures
|
||||||
expect(mockClient.sendEvent).toHaveBeenCalledTimes(2);
|
expect(mockClient.sendEvent).toHaveBeenCalledTimes(2);
|
||||||
expect(store.hasWireError(alicesRoom1BeaconInfo.getType())).toBe(true);
|
expect(store.beaconHasWireError(alicesRoom1BeaconInfo.getType())).toBe(true);
|
||||||
|
expect(store.hasWireErrors()).toBe(true);
|
||||||
|
expect(store.hasWireErrors(room1Id)).toBe(true);
|
||||||
expect(emitSpy).toHaveBeenCalledWith(
|
expect(emitSpy).toHaveBeenCalledWith(
|
||||||
OwnBeaconStoreEvent.WireError, alicesRoom1BeaconInfo.getType(),
|
OwnBeaconStoreEvent.WireError, alicesRoom1BeaconInfo.getType(),
|
||||||
);
|
);
|
||||||
|
@ -942,7 +947,7 @@ describe('OwnBeaconStore', () => {
|
||||||
emitSpy.mockClear();
|
emitSpy.mockClear();
|
||||||
store.resetWireError(alicesRoom1BeaconInfo.getType());
|
store.resetWireError(alicesRoom1BeaconInfo.getType());
|
||||||
|
|
||||||
expect(store.hasWireError(alicesRoom1BeaconInfo.getType())).toBe(false);
|
expect(store.beaconHasWireError(alicesRoom1BeaconInfo.getType())).toBe(false);
|
||||||
|
|
||||||
// 2 more positions from watchPosition in this period
|
// 2 more positions from watchPosition in this period
|
||||||
await advanceAndFlushPromises(10000);
|
await advanceAndFlushPromises(10000);
|
||||||
|
|
Loading…
Reference in New Issue