Combine recent raised hands into existing toast
This commit is contained in:
parent
b0b12d4224
commit
b574ba531d
4 changed files with 60 additions and 31 deletions
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
import React, { useState, useRef, useEffect, useCallback } from 'react';
|
||||||
import { isEqual, noop } from 'lodash';
|
import { isEqual, noop, sortBy } from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { VideoFrameSource } from '@signalapp/ringrtc';
|
import type { VideoFrameSource } from '@signalapp/ringrtc';
|
||||||
import type {
|
import type {
|
||||||
|
@ -520,7 +520,10 @@ export function CallScreen({
|
||||||
|
|
||||||
const renderRaisedHandsToast = React.useCallback(
|
const renderRaisedHandsToast = React.useCallback(
|
||||||
(hands: Array<number>) => {
|
(hands: Array<number>) => {
|
||||||
const names = hands.map(demuxId =>
|
// Sort "You" to the front.
|
||||||
|
const names = sortBy(hands, demuxId =>
|
||||||
|
demuxId === localDemuxId ? 0 : 1
|
||||||
|
).map(demuxId =>
|
||||||
demuxId === localDemuxId
|
demuxId === localDemuxId
|
||||||
? i18n('icu:you')
|
? i18n('icu:you')
|
||||||
: conversationsByDemuxId.get(demuxId)?.title
|
: conversationsByDemuxId.get(demuxId)?.title
|
||||||
|
|
|
@ -32,6 +32,7 @@ export type CallingToastType = {
|
||||||
content: JSX.Element | string;
|
content: JSX.Element | string;
|
||||||
autoClose: boolean;
|
autoClose: boolean;
|
||||||
dismissable?: boolean;
|
dismissable?: boolean;
|
||||||
|
lifetime?: number;
|
||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
// key must be provided if the toast is 'only-show-once'
|
// key must be provided if the toast is 'only-show-once'
|
||||||
|
@ -161,7 +162,7 @@ export function CallingToastProvider({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toast.autoClose) {
|
if (toast.autoClose) {
|
||||||
startTimer(key, lifetime);
|
startTimer(key, toast.lifetime ?? lifetime);
|
||||||
nonPersistentToasts.unshift({ ...toast, key });
|
nonPersistentToasts.unshift({ ...toast, key });
|
||||||
} else {
|
} else {
|
||||||
persistentToasts.unshift({ ...toast, key });
|
persistentToasts.unshift({ ...toast, key });
|
||||||
|
|
|
@ -8,11 +8,8 @@ import type { ConversationType } from '../state/ducks/conversations';
|
||||||
import type { LocalizerType } from '../types/Util';
|
import type { LocalizerType } from '../types/Util';
|
||||||
import { CallingToastProvider, useCallingToasts } from './CallingToast';
|
import { CallingToastProvider, useCallingToasts } from './CallingToast';
|
||||||
import { usePrevious } from '../hooks/usePrevious';
|
import { usePrevious } from '../hooks/usePrevious';
|
||||||
import {
|
import { difference as setDifference } from '../util/setUtil';
|
||||||
difference as setDifference,
|
import { isMoreRecentThan } from '../util/timestamp';
|
||||||
isEqual as setIsEqual,
|
|
||||||
} from '../util/setUtil';
|
|
||||||
import * as log from '../logging/log';
|
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
activeCall: ActiveCallType;
|
activeCall: ActiveCallType;
|
||||||
|
@ -151,7 +148,9 @@ function useRaisedHandsToast({
|
||||||
) => JSX.Element | string | undefined;
|
) => JSX.Element | string | undefined;
|
||||||
}): void {
|
}): void {
|
||||||
const RAISED_HANDS_TOAST_KEY = 'raised-hands';
|
const RAISED_HANDS_TOAST_KEY = 'raised-hands';
|
||||||
|
const RAISED_HANDS_TOAST_LIFETIME = 4000;
|
||||||
const LOAD_DELAY = 2000;
|
const LOAD_DELAY = 2000;
|
||||||
|
|
||||||
const { showToast, hideToast } = useCallingToasts();
|
const { showToast, hideToast } = useCallingToasts();
|
||||||
|
|
||||||
// Hand state is updated after a delay upon joining a call, so it can appear that
|
// Hand state is updated after a delay upon joining a call, so it can appear that
|
||||||
|
@ -179,49 +178,68 @@ function useRaisedHandsToast({
|
||||||
]
|
]
|
||||||
: [new Set(), new Set()];
|
: [new Set(), new Set()];
|
||||||
|
|
||||||
const raisedHandsInLastShownToastRef = useRef<Set<number>>(new Set());
|
const toastLastShownAt = useRef<number>(0);
|
||||||
const raisedHandsInLastShownToast = raisedHandsInLastShownToastRef.current;
|
const handsForLastShownToast = useRef<Set<number>>(new Set());
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// 1. If no hands are raised, then hide any raise hand toast.
|
if (raisedHands?.size === 0) {
|
||||||
// 2. Check if someone lowered their hand which they had recently raised. The
|
|
||||||
// previous toast saying they raised their hand would now be out of date, so we
|
|
||||||
// should hide it.
|
|
||||||
if (
|
|
||||||
raisedHands?.size === 0 ||
|
|
||||||
(raisedHandsInLastShownToast.size > 0 &&
|
|
||||||
loweredHands.size > 0 &&
|
|
||||||
setIsEqual(raisedHandsInLastShownToast, loweredHands))
|
|
||||||
) {
|
|
||||||
hideToast(RAISED_HANDS_TOAST_KEY);
|
hideToast(RAISED_HANDS_TOAST_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newHands.size === 0 || !renderRaisedHandsToast) {
|
if (
|
||||||
|
(newHands.size === 0 && loweredHands.size === 0) ||
|
||||||
|
!renderRaisedHandsToast
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = renderRaisedHandsToast([...newHands].reverse());
|
// If there's an existing raised hand toast (it hasn't faded out yet), then
|
||||||
if (!content) {
|
// group the newly raised and lowered hands into the existing toast.
|
||||||
log.warn(
|
let handsForToast: Array<number>;
|
||||||
'CallingToastManager useRaisedHandsToast: Failed to call renderRaisedHandsToast()'
|
if (
|
||||||
);
|
isMoreRecentThan(toastLastShownAt.current, RAISED_HANDS_TOAST_LIFETIME)
|
||||||
return;
|
) {
|
||||||
|
handsForToast = [
|
||||||
|
...setDifference(handsForLastShownToast.current, loweredHands),
|
||||||
|
...newHands,
|
||||||
|
];
|
||||||
|
|
||||||
|
// If someone lowered a hand which isn't present in the existing toast,
|
||||||
|
// we can ignore it.
|
||||||
|
if (
|
||||||
|
newHands.size === 0 &&
|
||||||
|
loweredHands.size > 0 &&
|
||||||
|
handsForToast.length &&
|
||||||
|
handsForToast.length === handsForLastShownToast.current.size
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handsForToast = [...newHands];
|
||||||
}
|
}
|
||||||
|
handsForLastShownToast.current = new Set([...handsForToast]);
|
||||||
|
|
||||||
hideToast(RAISED_HANDS_TOAST_KEY);
|
hideToast(RAISED_HANDS_TOAST_KEY);
|
||||||
|
|
||||||
|
const content = renderRaisedHandsToast(handsForToast.reverse());
|
||||||
|
if (!content) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Note: Don't set { dismissable: true } or else the links (Lower or View Queue)
|
// Note: Don't set { dismissable: true } or else the links (Lower or View Queue)
|
||||||
// will cause nested buttons (dismissable toasts are <button>s)
|
// will cause nested buttons (dismissable toasts are <button>s)
|
||||||
showToast({
|
showToast({
|
||||||
key: RAISED_HANDS_TOAST_KEY,
|
key: RAISED_HANDS_TOAST_KEY,
|
||||||
content,
|
content,
|
||||||
autoClose: true,
|
autoClose: true,
|
||||||
|
lifetime: RAISED_HANDS_TOAST_LIFETIME,
|
||||||
});
|
});
|
||||||
raisedHandsInLastShownToastRef.current = newHands;
|
toastLastShownAt.current = Date.now();
|
||||||
}, [
|
}, [
|
||||||
raisedHands,
|
raisedHands,
|
||||||
previousRaisedHands,
|
previousRaisedHands,
|
||||||
newHands,
|
newHands,
|
||||||
raisedHandsInLastShownToast,
|
handsForLastShownToast,
|
||||||
loweredHands,
|
loweredHands,
|
||||||
renderRaisedHandsToast,
|
renderRaisedHandsToast,
|
||||||
hideToast,
|
hideToast,
|
||||||
|
|
|
@ -3069,9 +3069,16 @@
|
||||||
{
|
{
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
"path": "ts/components/CallingToastManager.tsx",
|
"path": "ts/components/CallingToastManager.tsx",
|
||||||
"line": " const raisedHandsInLastShownToastRef = useRef<Set<number>>(new Set());",
|
"line": " const toastLastShownAt = useRef<number>(0);",
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2023-12-05T22:11:41.559Z"
|
"updated": "2024-01-12T18:56:18.138Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rule": "React-useRef",
|
||||||
|
"path": "ts/components/CallingToastManager.tsx",
|
||||||
|
"line": " const handsForLastShownToast = useRef<Set<number>>(new Set());",
|
||||||
|
"reasonCategory": "usageTrusted",
|
||||||
|
"updated": "2024-01-12T18:56:18.138Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"rule": "React-useRef",
|
"rule": "React-useRef",
|
||||||
|
|
Loading…
Reference in a new issue