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:
Kerry 2022-04-12 09:24:17 +02:00 committed by GitHub
parent e90ee38e4b
commit 4b7840bf78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 100 additions and 63 deletions

View 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;
}

View file

@ -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;
} }

View 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;

View file

@ -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');

View file

@ -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>"`;