@@ -14,61 +14,109 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
+ import React , { useEffect , useState } from 'react' ;
17
18
import classNames from 'classnames' ;
18
- import React from 'react' ;
19
19
import { Room } from 'matrix-js-sdk/src/matrix' ;
20
20
21
- import { useEventEmitterState } from '../../../hooks/useEventEmitter' ;
22
21
import { _t } from '../../../languageHandler' ;
22
+ import { useEventEmitterState } from '../../../hooks/useEventEmitter' ;
23
23
import { OwnBeaconStore , OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore' ;
24
- import { Icon as LiveLocationIcon } from '../../../../res/img/location/live-location.svg' ;
25
24
import AccessibleButton from '../elements/AccessibleButton' ;
25
+ import StyledLiveBeaconIcon from './StyledLiveBeaconIcon' ;
26
+ import { formatDuration } from '../../../DateUtils' ;
27
+ import { getBeaconMsUntilExpiry , sortBeaconsByLatestExpiry } from '../../../utils/beacon' ;
28
+ import Spinner from '../elements/Spinner' ;
26
29
27
30
interface Props {
28
31
roomId : Room [ 'roomId' ] ;
29
32
}
30
33
31
- const RoomLiveShareWarning : React . FC < Props > = ( { roomId } ) => {
34
+ /**
35
+ * It's technically possible to have multiple live beacons in one room
36
+ * Select the latest expiry to display,
37
+ * and kill all beacons on stop sharing
38
+ */
39
+ type LiveBeaconsState = {
40
+ liveBeaconIds : string [ ] ;
41
+ msRemaining ?: number ;
42
+ onStopSharing ?: ( ) => void ;
43
+ stoppingInProgress ?: boolean ;
44
+ } ;
45
+ const useLiveBeacons = ( roomId : Room [ 'roomId' ] ) : LiveBeaconsState => {
46
+ const [ stoppingInProgress , setStoppingInProgress ] = useState ( false ) ;
32
47
const liveBeaconIds = useEventEmitterState (
33
48
OwnBeaconStore . instance ,
34
49
OwnBeaconStoreEvent . LivenessChange ,
35
50
( ) => OwnBeaconStore . instance . getLiveBeaconIds ( roomId ) ,
36
51
) ;
37
52
53
+ // reset stopping in progress on change in live ids
54
+ useEffect ( ( ) => {
55
+ setStoppingInProgress ( false ) ;
56
+ } , [ liveBeaconIds , setStoppingInProgress ] ) ;
57
+
38
58
if ( ! liveBeaconIds ?. length ) {
39
- return null ;
59
+ return { liveBeaconIds } ;
40
60
}
41
61
42
- if ( liveBeaconIds . length > 1 ) {
43
- throw new Error ( 'not handled yet' ) ;
44
- }
62
+ // select the beacon with latest expiry to display expiry time
63
+ const beacon = liveBeaconIds . map ( beaconId => OwnBeaconStore . instance . getBeaconById ( beaconId ) )
64
+ . sort ( sortBeaconsByLatestExpiry )
65
+ . shift ( ) ;
45
66
46
- const beaconId = liveBeaconIds [ 0 ] ;
67
+ const onStopSharing = async ( ) => {
68
+ setStoppingInProgress ( true ) ;
69
+ try {
70
+ await Promise . all ( liveBeaconIds . map ( beaconId => OwnBeaconStore . instance . stopBeacon ( beaconId ) ) ) ;
71
+ } catch ( error ) {
72
+ // only clear loading in case of error
73
+ // to avoid flash of not-loading state
74
+ // after beacons have been stopped but we wait for sync
75
+ setStoppingInProgress ( false ) ;
76
+ }
77
+ } ;
47
78
48
- const beacon = OwnBeaconStore . instance . getBeaconById ( liveBeaconIds [ 0 ] ) ;
49
- const liveTimeRemaining = `${ beacon . beaconInfo . timeout } ` ;
79
+ const msRemaining = getBeaconMsUntilExpiry ( beacon ) ;
50
80
51
- const onStopSharing = ( ) => {
52
- OwnBeaconStore . instance . stopBeacon ( beaconId ) ;
53
- } ;
81
+ return { liveBeaconIds, onStopSharing, msRemaining, stoppingInProgress } ;
82
+ } ;
83
+
84
+ const RoomLiveShareWarning : React . FC < Props > = ( { roomId } ) => {
85
+ const {
86
+ liveBeaconIds,
87
+ onStopSharing,
88
+ msRemaining,
89
+ stoppingInProgress,
90
+ } = useLiveBeacons ( roomId ) ;
91
+
92
+ if ( ! liveBeaconIds ?. length ) {
93
+ return null ;
94
+ }
95
+
96
+ const timeRemaining = formatDuration ( msRemaining ) ;
97
+ const liveTimeRemaining = _t ( `%(timeRemaining)s left` , { timeRemaining } ) ;
54
98
55
99
return < div
56
100
className = { classNames ( 'mx_RoomLiveShareWarning' ) }
57
101
>
58
- < LiveLocationIcon />
102
+ < StyledLiveBeaconIcon className = "mx_RoomLiveShareWarning_icon" />
59
103
< span className = "mx_RoomLiveShareWarning_label" >
60
-
61
- { _t ( 'You are sharing your live location' ) }
104
+ { _t ( 'You are sharing %(count)s live locations' , { count : liveBeaconIds . length } ) }
62
105
</ span >
63
- < span
64
- data-test-id = 'room-live-share-expiry'
65
- className = "mx_RoomLiveShareWarning_expiry"
66
- > { liveTimeRemaining } </ span >
106
+
107
+ { stoppingInProgress ?
108
+ < span className = 'mx_RoomLiveShareWarning_spinner' > < Spinner h = { 16 } w = { 16 } /> </ span > :
109
+ < span
110
+ data-test-id = 'room-live-share-expiry'
111
+ className = "mx_RoomLiveShareWarning_expiry"
112
+ > { liveTimeRemaining } </ span >
113
+ }
67
114
< AccessibleButton
68
115
data-test-id = 'room-live-share-stop-sharing'
69
- onClick = { onStopSharing }
116
+ onClick = { onStopSharing }
70
117
kind = 'danger'
71
118
element = 'button'
119
+ disabled = { stoppingInProgress }
72
120
>
73
121
{ _t ( 'Stop sharing' ) }
74
122
</ AccessibleButton >
0 commit comments