Live location sharing - extract live time UI for reuse (#8283)
* extract livetimeremaining into own component Signed-off-by: Kerry Archibald <kerrya@element.io> * extract LiveTimeRemaining for reuse in beacon timeline Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
parent
e90ee38e4b
commit
4b7840bf78
5 changed files with 100 additions and 63 deletions
20
res/css/components/views/beacon/_LiveTimeRemaining.scss
Normal file
20
res/css/components/views/beacon/_LiveTimeRemaining.scss
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_LiveTimeRemaining {
|
||||||
|
color: $secondary-content;
|
||||||
|
font-size: $font-12px;
|
||||||
|
}
|
|
@ -39,12 +39,6 @@ limitations under the License.
|
||||||
font-size: $font-15px;
|
font-size: $font-15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomLiveShareWarning_expiry {
|
|
||||||
color: $secondary-content;
|
|
||||||
font-size: $font-12px;
|
|
||||||
margin-right: $spacing-16;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomLiveShareWarning_spinner {
|
.mx_RoomLiveShareWarning_spinner {
|
||||||
margin-right: $spacing-16;
|
margin-right: $spacing-16;
|
||||||
}
|
}
|
||||||
|
|
75
src/components/views/beacon/LiveTimeRemaining.tsx
Normal file
75
src/components/views/beacon/LiveTimeRemaining.tsx
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { BeaconEvent, Beacon } from 'matrix-js-sdk/src/matrix';
|
||||||
|
|
||||||
|
import { formatDuration } from '../../../DateUtils';
|
||||||
|
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
|
||||||
|
import { useInterval } from '../../../hooks/useTimeout';
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
import { getBeaconMsUntilExpiry } from '../../../utils/beacon';
|
||||||
|
|
||||||
|
const MINUTE_MS = 60000;
|
||||||
|
const HOUR_MS = MINUTE_MS * 60;
|
||||||
|
const getUpdateInterval = (ms: number) => {
|
||||||
|
// every 10 mins when more than an hour
|
||||||
|
if (ms > HOUR_MS) {
|
||||||
|
return MINUTE_MS * 10;
|
||||||
|
}
|
||||||
|
// every minute when more than a minute
|
||||||
|
if (ms > MINUTE_MS) {
|
||||||
|
return MINUTE_MS;
|
||||||
|
}
|
||||||
|
// otherwise every second
|
||||||
|
return 1000;
|
||||||
|
};
|
||||||
|
const useMsRemaining = (beacon: Beacon): number => {
|
||||||
|
const beaconInfo = useEventEmitterState(
|
||||||
|
beacon,
|
||||||
|
BeaconEvent.Update,
|
||||||
|
() => beacon.beaconInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [msRemaining, setMsRemaining] = useState(() => getBeaconMsUntilExpiry(beaconInfo));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMsRemaining(getBeaconMsUntilExpiry(beaconInfo));
|
||||||
|
}, [beaconInfo]);
|
||||||
|
|
||||||
|
const updateMsRemaining = useCallback(() => {
|
||||||
|
const ms = getBeaconMsUntilExpiry(beaconInfo);
|
||||||
|
setMsRemaining(ms);
|
||||||
|
}, [beaconInfo]);
|
||||||
|
|
||||||
|
useInterval(updateMsRemaining, getUpdateInterval(msRemaining));
|
||||||
|
|
||||||
|
return msRemaining;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LiveTimeRemaining: React.FC<{ beacon: Beacon }> = ({ beacon }) => {
|
||||||
|
const msRemaining = useMsRemaining(beacon);
|
||||||
|
|
||||||
|
const timeRemaining = formatDuration(msRemaining);
|
||||||
|
const liveTimeRemaining = _t(`%(timeRemaining)s left`, { timeRemaining });
|
||||||
|
|
||||||
|
return <span
|
||||||
|
data-test-id='room-live-share-expiry'
|
||||||
|
className="mx_LiveTimeRemaining"
|
||||||
|
>{ liveTimeRemaining }</span>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LiveTimeRemaining;
|
|
@ -14,63 +14,23 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import {
|
||||||
Room,
|
Room,
|
||||||
Beacon,
|
Beacon,
|
||||||
BeaconEvent,
|
|
||||||
BeaconIdentifier,
|
BeaconIdentifier,
|
||||||
} from 'matrix-js-sdk/src/matrix';
|
} 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 { useInterval } from '../../../hooks/useTimeout';
|
|
||||||
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore';
|
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore';
|
||||||
import { getBeaconMsUntilExpiry, sortBeaconsByLatestExpiry } from '../../../utils/beacon';
|
import { sortBeaconsByLatestExpiry } from '../../../utils/beacon';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import Spinner from '../elements/Spinner';
|
import Spinner from '../elements/Spinner';
|
||||||
import StyledLiveBeaconIcon from './StyledLiveBeaconIcon';
|
import StyledLiveBeaconIcon from './StyledLiveBeaconIcon';
|
||||||
import { Icon as CloseIcon } from '../../../../res/img/image-view/close.svg';
|
import { Icon as CloseIcon } from '../../../../res/img/image-view/close.svg';
|
||||||
|
import LiveTimeRemaining from './LiveTimeRemaining';
|
||||||
const MINUTE_MS = 60000;
|
|
||||||
const HOUR_MS = MINUTE_MS * 60;
|
|
||||||
|
|
||||||
const getUpdateInterval = (ms: number) => {
|
|
||||||
// every 10 mins when more than an hour
|
|
||||||
if (ms > HOUR_MS) {
|
|
||||||
return MINUTE_MS * 10;
|
|
||||||
}
|
|
||||||
// every minute when more than a minute
|
|
||||||
if (ms > MINUTE_MS) {
|
|
||||||
return MINUTE_MS;
|
|
||||||
}
|
|
||||||
// otherwise every second
|
|
||||||
return 1000;
|
|
||||||
};
|
|
||||||
const useMsRemaining = (beacon: Beacon): number => {
|
|
||||||
const beaconInfo = useEventEmitterState(
|
|
||||||
beacon,
|
|
||||||
BeaconEvent.Update,
|
|
||||||
() => beacon.beaconInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [msRemaining, setMsRemaining] = useState(() => getBeaconMsUntilExpiry(beaconInfo));
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setMsRemaining(getBeaconMsUntilExpiry(beaconInfo));
|
|
||||||
}, [beaconInfo]);
|
|
||||||
|
|
||||||
const updateMsRemaining = useCallback(() => {
|
|
||||||
const ms = getBeaconMsUntilExpiry(beaconInfo);
|
|
||||||
setMsRemaining(ms);
|
|
||||||
}, [beaconInfo]);
|
|
||||||
|
|
||||||
useInterval(updateMsRemaining, getUpdateInterval(msRemaining));
|
|
||||||
|
|
||||||
return msRemaining;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It's technically possible to have multiple live beacons in one room
|
* It's technically possible to have multiple live beacons in one room
|
||||||
|
@ -134,18 +94,6 @@ const useLiveBeacons = (liveBeaconIds: BeaconIdentifier[], roomId: string): Live
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const LiveTimeRemaining: React.FC<{ beacon: Beacon }> = ({ beacon }) => {
|
|
||||||
const msRemaining = useMsRemaining(beacon);
|
|
||||||
|
|
||||||
const timeRemaining = formatDuration(msRemaining);
|
|
||||||
const liveTimeRemaining = _t(`%(timeRemaining)s left`, { timeRemaining });
|
|
||||||
|
|
||||||
return <span
|
|
||||||
data-test-id='room-live-share-expiry'
|
|
||||||
className="mx_RoomLiveShareWarning_expiry"
|
|
||||||
>{ liveTimeRemaining }</span>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getLabel = (hasWireError: boolean, hasStopSharingError: boolean): string => {
|
const getLabel = (hasWireError: boolean, hasStopSharingError: boolean): string => {
|
||||||
if (hasWireError) {
|
if (hasWireError) {
|
||||||
return _t('An error occured whilst sharing your live location, please try again');
|
return _t('An error occured whilst sharing your live location, please try again');
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// 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-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 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_LiveTimeRemaining\\">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-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_LiveTimeRemaining\\">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-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 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>"`;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue