Combine recent raised hands into existing toast

This commit is contained in:
ayumi-signal 2024-01-12 19:44:44 -08:00 committed by GitHub
parent b0b12d4224
commit b574ba531d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 31 deletions

View file

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

View file

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

View file

@ -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,

View file

@ -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",