Fix and deprecate usePrevious hook and update raised hands button

Co-authored-by: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com>
This commit is contained in:
automated-signal
2026-05-14 10:15:39 -05:00
committed by GitHub
parent 54dd1261f8
commit b16a451e1f
22 changed files with 168 additions and 88 deletions
+5 -2
View File
@@ -55,7 +55,7 @@ import { createLogger } from '../logging/log.std.ts';
import { isGroupOrAdhocActiveCall } from '../util/isGroupOrAdhocCall.std.ts';
import { CallingAdhocCallInfo } from './CallingAdhocCallInfo.dom.tsx';
import { callLinkRootKeyToUrl } from '../util/callLinkRootKeyToUrl.std.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import { copyCallLink } from '../util/copyLinksWithToast.dom.ts';
import {
redactNotificationProfileId,
@@ -264,7 +264,10 @@ function ActiveCallManager({
// For caching screenshare frames which update slowly, between Pip and CallScreen.
const imageDataCache = useRef<CallingImageDataCache>(new Map());
const previousConversationId = usePrevious(conversation.id, conversation.id);
const previousConversationId = usePreviousDeprecated(
conversation.id,
conversation.id
);
useEffect(() => {
if (conversation.id !== previousConversationId) {
imageDataCache.current.clear();
+7 -4
View File
@@ -80,7 +80,7 @@ import {
} from '../hooks/useKeyboardShortcuts.dom.tsx';
import { useValueAtFixedRate } from '../hooks/useValueAtFixedRate.std.ts';
import { isReconnecting as callingIsReconnecting } from '../util/callingIsReconnecting.std.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import {
CallingToastProvider,
PersistentCallingToast,
@@ -585,7 +585,7 @@ export function CallScreen({
}, [isSendingVideo, handleSize, isLonelyInCall, setLocalPreviewContainer]);
const { selfViewExpanded } = activeCall;
const previousSelfViewExpanded = usePrevious(
const previousSelfViewExpanded = usePreviousDeprecated(
selfViewExpanded,
selfViewExpanded
);
@@ -777,7 +777,10 @@ export function CallScreen({
const [localHandRaised, setLocalHandRaised] = useState<boolean>(
syncedLocalHandRaised
);
const previousLocalHandRaised = usePrevious(localHandRaised, localHandRaised);
const previousLocalHandRaised = usePreviousDeprecated(
localHandRaised,
localHandRaised
);
const toggleRaiseHand = useCallback(
(raise?: boolean) => {
const nextValue = raise ?? !localHandRaised;
@@ -1339,7 +1342,7 @@ function useViewModeChangedToast({
i18n: LocalizerType;
}): void {
const { viewMode } = activeCall;
const previousViewMode = usePrevious(viewMode, viewMode);
const previousViewMode = usePreviousDeprecated(viewMode, viewMode);
const presenterAci = usePresenter(activeCall.remoteParticipants);
const VIEW_MODE_CHANGED_TOAST_KEY = 'view-mode-changed';
+5 -2
View File
@@ -36,7 +36,7 @@ import type { ConversationType } from '../state/ducks/conversations.preload.ts';
import { Avatar, AvatarSize } from './Avatar.dom.tsx';
import { AvatarColors } from '../types/Colors.std.ts';
import type { SetLocalPreviewContainerType } from '../services/calling.preload.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import type { SizeCallbackType } from '../calling/VideoSupport.preload.ts';
import { MAX_FRAME_HEIGHT } from '../calling/constants.std.ts';
@@ -290,7 +290,10 @@ export function CallingPip({
};
}, []);
const previousIsWindowLarge = usePrevious(isWindowLarge, isWindowLarge);
const previousIsWindowLarge = usePreviousDeprecated(
isWindowLarge,
isWindowLarge
);
// This only runs when isWindowLarge changes, so we aggressively change height + width
useEffect(() => {
if (previousIsWindowLarge === isWindowLarge) {
+30 -16
View File
@@ -13,7 +13,7 @@ import type { ConversationType } from '../state/ducks/conversations.preload.ts';
import { ModalHost } from './ModalHost.dom.tsx';
import { drop } from '../util/drop.std.ts';
import { createLogger } from '../logging/log.std.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePrevious, usePreviousEffect } from '../hooks/usePrevious.std.ts';
import { useReducedMotion } from '../hooks/useReducedMotion.dom.ts';
const log = createLogger('CallingRaisedHandsList');
@@ -200,24 +200,20 @@ export function CallingRaisedHandsListButton({
[]
);
const prevRaisedHandsCount = usePrevious(raisedHandsCount, raisedHandsCount);
const prevSyncedLocalHandRaised = usePrevious(
const prevRaisedHandsCount = usePrevious(raisedHandsCount) ?? 0;
const prevSyncedLocalHandRaised = usePreviousEffect(
syncedLocalHandRaised,
syncedLocalHandRaised
);
const prevShownRaisedHandsCountRef = useRef<number>(raisedHandsCount);
const prevShownSyncedLocalHandRaisedRef = useRef<boolean>(
syncedLocalHandRaised
);
// Bouncy effect
useEffect(() => {
if (
raisedHandsCount > prevRaisedHandsCount ||
(raisedHandsCount > 0 && !isVisible)
) {
setIsVisible(true);
opacitySpringApi.stop();
drop(Promise.all(opacitySpringApi.start({ opacity: 1 })));
if (raisedHandsCount > prevRaisedHandsCount) {
scaleSpringApi.stop();
drop(
Promise.all(
@@ -228,14 +224,33 @@ export function CallingRaisedHandsListButton({
})
)
);
} else if (raisedHandsCount === 0) {
opacitySpringApi.stop();
}
}, [raisedHandsCount, prevRaisedHandsCount, scaleSpringApi]);
useEffect(() => {
if (raisedHandsCount === prevRaisedHandsCount) {
return;
}
opacitySpringApi.stop();
if (raisedHandsCount > 0) {
setIsVisible(true);
drop(
Promise.all(
opacitySpringApi.start({
from: { opacity: opacitySpringProps.opacity },
to: { opacity: 1 },
})
)
);
} else {
drop(
Promise.all(
opacitySpringApi.start({
from: { opacity: opacitySpringProps.opacity },
to: { opacity: 0 },
onRest: () => {
if (!raisedHandsCount) {
onResolve: ({ cancelled }) => {
if (!cancelled) {
setIsVisible(false);
}
},
@@ -244,11 +259,10 @@ export function CallingRaisedHandsListButton({
);
}
}, [
isVisible,
raisedHandsCount,
prevRaisedHandsCount,
opacitySpringApi,
scaleSpringApi,
opacitySpringProps.opacity,
setIsVisible,
]);
+2 -2
View File
@@ -21,7 +21,7 @@ import classNames from 'classnames';
import { v4 as uuid } from 'uuid';
import { useIsMounted } from '../hooks/useIsMounted.std.ts';
import type { LocalizerType } from '../types/I18N.std.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import { difference } from '../util/setUtil.std.ts';
import { useReducedMotion } from '../hooks/useReducedMotion.dom.ts';
@@ -81,7 +81,7 @@ export function CallingToastProvider({
transitionFrom?: object;
}): JSX.Element {
const [toasts, setToasts] = useState<Array<CallingToastStateType>>([]);
const previousToasts = usePrevious([], toasts);
const previousToasts = usePreviousDeprecated([], toasts);
const timeouts = useRef<Map<string, TimeoutType>>(new Map());
// All toasts are paused on hover or focus so that toasts don't disappear while a user
// is attempting to interact with them
+17 -8
View File
@@ -10,7 +10,7 @@ import { CallMode } from '../types/CallDisposition.std.ts';
import type { ConversationType } from '../state/ducks/conversations.preload.ts';
import type { LocalizerType } from '../types/Util.std.ts';
import { CallingToastProvider, useCallingToasts } from './CallingToast.dom.tsx';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import { difference as setDifference } from '../util/setUtil.std.ts';
import { isMoreRecentThan } from '../util/timestamp.std.ts';
import { isGroupOrAdhocActiveCall } from '../util/isGroupOrAdhocCall.std.ts';
@@ -58,7 +58,10 @@ export function useScreenSharingStoppedToast({
() => getCurrentPresenter(activeCall),
[activeCall]
);
const previousPresenter = usePrevious(currentPresenter, currentPresenter);
const previousPresenter = usePreviousDeprecated(
currentPresenter,
currentPresenter
);
useEffect(() => {
if (previousPresenter && !currentPresenter) {
@@ -93,7 +96,10 @@ function useMutedToast({
mutedBy: number | undefined;
i18n: LocalizerType;
}): void {
const previousHasLocalAudio = usePrevious(hasLocalAudio, hasLocalAudio);
const previousHasLocalAudio = usePreviousDeprecated(
hasLocalAudio,
hasLocalAudio
);
const { showToast, hideToast } = useCallingToasts();
const MUTED_TOAST_KEY = 'muted';
@@ -131,7 +137,10 @@ function useOutgoingRingToast({
i18n: LocalizerType;
}): void {
const { showToast, hideToast } = useCallingToasts();
const previousOutgoingRing = usePrevious(outgoingRing, outgoingRing);
const previousOutgoingRing = usePreviousDeprecated(
outgoingRing,
outgoingRing
);
const RINGING_TOAST_KEY = 'ringing';
useEffect(() => {
@@ -182,7 +191,7 @@ function useRaisedHandsToast({
return () => clearTimeout(timeout);
}, []);
const previousRaisedHands = usePrevious(raisedHands, raisedHands);
const previousRaisedHands = usePreviousDeprecated(raisedHands, raisedHands);
const [newHands, loweredHands]: [Set<number>, Set<number>] = isLoaded
? [
setDifference(
@@ -276,7 +285,7 @@ function useLowerHandSuggestionToast({
handleLowerHand: (() => void) | undefined;
isHandRaised: boolean | undefined;
}): void {
const previousSuggestLowerHand = usePrevious(
const previousSuggestLowerHand = usePreviousDeprecated(
suggestLowerHand,
suggestLowerHand
);
@@ -343,7 +352,7 @@ function useMutedByToast({
conversationsByDemuxId?: Map<number, ConversationType>;
i18n: LocalizerType;
}): void {
const previousMutedBy = usePrevious(mutedBy, mutedBy);
const previousMutedBy = usePreviousDeprecated(mutedBy, mutedBy);
const { showToast, hideToast } = useCallingToasts();
const MUTED_BY_TOAST_KEY = 'MUTED_BY_TOAST_KEY';
@@ -401,7 +410,7 @@ function useObservedRemoteMuteToast({
}): void {
const { showToast, hideToast } = useCallingToasts();
const OBSERVED_REMOTE_MUTE_TOAST_KEY = 'OBSERVED_REMOTE_MUTE_TOAST_KEY';
const previousObservedRemoteMute = usePrevious(
const previousObservedRemoteMute = usePreviousDeprecated(
observedRemoteMute,
observedRemoteMute
);
+10 -6
View File
@@ -66,7 +66,7 @@ import {
import { MediaEditor } from './MediaEditor.dom.tsx';
import { isImageTypeSupported } from '../util/GoogleChrome.std.ts';
import * as KeyboardLayout from '../services/keyboardLayout.dom.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import { PanelType } from '../types/Panels.std.ts';
import type { SmartCompositionRecordingDraftProps } from '../state/smart/CompositionRecordingDraft.preload.tsx';
import { useEscapeHandling } from '../hooks/useEscapeHandling.dom.ts';
@@ -527,7 +527,7 @@ export const CompositionArea = memo(function CompositionArea({
});
// Focus input on first mount
const previousFocusCounter = usePrevious<number | undefined>(
const previousFocusCounter = usePreviousDeprecated<number | undefined>(
focusCounter,
focusCounter
);
@@ -543,8 +543,11 @@ export const CompositionArea = memo(function CompositionArea({
}
}, [inputApiRef, focusCounter, previousFocusCounter]);
const previousSendCounter = usePrevious(sendCounter, sendCounter);
const previousConversationId = usePrevious(conversationId, conversationId);
const previousSendCounter = usePreviousDeprecated(sendCounter, sendCounter);
const previousConversationId = usePreviousDeprecated(
conversationId,
conversationId
);
useEffect(() => {
if (!inputApiRef.current) {
return;
@@ -569,9 +572,10 @@ export const CompositionArea = memo(function CompositionArea({
// - User begins editing another message.
const editHistoryLength = draftEditMessage?.editHistoryLength;
const hasEditHistoryChanged =
usePrevious(editHistoryLength, editHistoryLength) !== editHistoryLength;
usePreviousDeprecated(editHistoryLength, editHistoryLength) !==
editHistoryLength;
const hasEditedMessageChanged =
usePrevious(editedMessageId, editedMessageId) !== editedMessageId;
usePreviousDeprecated(editedMessageId, editedMessageId) !== editedMessageId;
const hasEditDraftChanged = hasEditHistoryChanged || hasEditedMessageChanged;
useEffect(() => {
+4 -4
View File
@@ -75,7 +75,7 @@ import { createLogger } from '../logging/log.std.ts';
import type { LinkPreviewForUIType } from '../types/message/LinkPreviews.std.ts';
import { StagedLinkPreview } from './conversation/StagedLinkPreview.dom.tsx';
import type { DraftEditMessageType } from '../model-types.d.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import {
matchBold,
matchItalic,
@@ -417,11 +417,11 @@ export function CompositionInput(props: Props): ReactElement {
return false;
};
const previousFormattingEnabled = usePrevious(
const previousFormattingEnabled = usePreviousDeprecated(
isFormattingEnabled,
isFormattingEnabled
);
const previousIsMouseDown = usePrevious(isMouseDown, isMouseDown);
const previousIsMouseDown = usePreviousDeprecated(isMouseDown, isMouseDown);
useEffect(() => {
const formattingChanged =
@@ -779,7 +779,7 @@ export function CompositionInput(props: Props): ReactElement {
const memberIdList = useMemo(() => {
return JSON.stringify(sortedGroupMembers?.map(mem => mem.id));
}, [sortedGroupMembers]);
const previousMemberIdList = usePrevious(undefined, memberIdList);
const previousMemberIdList = usePreviousDeprecated(undefined, memberIdList);
useEffect(() => {
memberRepositoryRef.current.updateMembers(sortedGroupMembers || []);
+2 -2
View File
@@ -5,7 +5,7 @@ import type { ReactNode, JSX } from 'react';
import { useRef, useEffect, Children } from 'react';
import classNames from 'classnames';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import { scrollToBottom } from '../util/scrollUtil.std.ts';
type PropsType = {
@@ -21,7 +21,7 @@ export function ContactPills({
// oxlint-disable-next-line no-react-children
const childCount = Children.count(children);
const previousChildCount = usePrevious(0, childCount);
const previousChildCount = usePreviousDeprecated(0, childCount);
useEffect(() => {
const hasAddedNewChild = childCount > previousChildCount;
@@ -30,7 +30,7 @@ import { MAX_FRAME_HEIGHT, MAX_FRAME_WIDTH } from '../calling/constants.std.ts';
import { useValueAtFixedRate } from '../hooks/useValueAtFixedRate.std.ts';
import { isOlderThan } from '../util/timestamp.std.ts';
import type { CallingImageDataCache } from './CallManager.dom.tsx';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import type { PropsType as SmartCallingParticipantMenuProps } from '../state/smart/CallingParticipantMenu.preload.tsx';
import type { AxoMenuBuilder } from '../axo/AxoMenuBuilder.dom.tsx';
import { AxoIconButton } from '../axo/AxoIconButton.dom.tsx';
@@ -129,8 +129,11 @@ export const GroupCallRemoteParticipant: FC<PropsType> = memo(
!props.isInPip ? props.audioLevel > 0 : false,
SPEAKING_LINGER_MS
);
const previousSharingScreen = usePrevious(sharingScreen, sharingScreen);
const prevIsActiveSpeakerInSpeakerView = usePrevious(
const previousSharingScreen = usePreviousDeprecated(
sharingScreen,
sharingScreen
);
const prevIsActiveSpeakerInSpeakerView = usePreviousDeprecated(
isActiveSpeakerInSpeakerView,
isActiveSpeakerInSpeakerView
);
+4 -4
View File
@@ -35,7 +35,7 @@ import { LeftPaneMode } from '../types/leftPane.std.ts';
import type { LocalizerType, ThemeType } from '../types/Util.std.ts';
import { ScrollBehavior } from '../types/Util.std.ts';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges.preload.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import { missingCaseError } from '../util/missingCaseError.std.ts';
import type { DurationInSeconds } from '../util/durations/index.std.ts';
import { WidthBreakpoint, getNavSidebarWidthBreakpoint } from './_util.std.ts';
@@ -312,7 +312,7 @@ export function LeftPane({
dismissBackupMediaDownloadBanner,
updateFilterByUnread,
}: PropsType): JSX.Element {
const previousModeSpecificProps = usePrevious(
const previousModeSpecificProps = usePreviousDeprecated(
modeSpecificProps,
modeSpecificProps
);
@@ -599,7 +599,7 @@ export function LeftPane({
const measureRef = useRef<HTMLDivElement>(null);
const measureSize = useSizeObserver(measureRef);
const previousMeasureSize = usePrevious(null, measureSize);
const previousMeasureSize = usePreviousDeprecated(null, measureSize);
const widthBreakpoint = getNavSidebarWidthBreakpoint(
measureSize && !measureSize.hidden
@@ -613,7 +613,7 @@ export function LeftPane({
};
// Control scroll position
const previousSelectedConversationId = usePrevious(
const previousSelectedConversationId = usePreviousDeprecated(
selectedConversationId,
selectedConversationId
);
+10 -4
View File
@@ -10,7 +10,7 @@ import type {
import type { LocalizerType } from '../types/Util.std.ts';
import { Avatar, AvatarSize } from './Avatar.dom.tsx';
import { SearchInput } from './SearchInput.dom.tsx';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import { Tooltip, TooltipPlacement } from './Tooltip.dom.tsx';
import { Theme } from '../util/theme.std.ts';
@@ -67,12 +67,18 @@ export function LeftPaneSearchInput({
}: PropsType): JSX.Element {
const inputRef = useRef<null | HTMLInputElement>(null);
const prevSearchConversationId = usePrevious(
const prevSearchConversationId = usePreviousDeprecated(
undefined,
searchConversation?.id
);
const prevSearchCounter = usePrevious(startSearchCounter, startSearchCounter);
const wasSearchingGlobally = usePrevious(false, isSearchingGlobally);
const prevSearchCounter = usePreviousDeprecated(
startSearchCounter,
startSearchCounter
);
const wasSearchingGlobally = usePreviousDeprecated(
false,
isSearchingGlobally
);
useEffect(() => {
// When user chooses to search in a given conversation we focus the field for them
+2 -2
View File
@@ -29,7 +29,7 @@ import { formatDateTimeForAttachment } from '../util/formatTimestamp.dom.ts';
import { formatDuration } from '../util/formatDuration.std.ts';
import { isGIF, isIncremental } from '../util/Attachment.std.ts';
import { useRestoreFocus } from '../hooks/useRestoreFocus.dom.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import { arrow } from '../util/keyboard.dom.ts';
import { drop } from '../util/drop.std.ts';
import { isCmdOrCtrl } from '../hooks/useKeyboardShortcuts.dom.tsx';
@@ -117,7 +117,7 @@ export function Lightbox({
}: PropsType): JSX.Element | null {
const hasThumbnails = media.length > 1;
const messageId = media.at(0)?.message.id;
const prevMessageId = usePrevious(messageId, messageId);
const prevMessageId = usePreviousDeprecated(messageId, messageId);
const needsAnimation = messageId !== prevMessageId;
const [root, setRoot] = useState<HTMLElement | undefined>();
+2 -2
View File
@@ -21,7 +21,7 @@ import { assertDev } from '../util/assert.std.ts';
import { getClassNamesFor } from '../util/getClassNamesFor.std.ts';
import { themeClassName } from '../util/theme.std.ts';
import { useEscapeHandling } from '../hooks/useEscapeHandling.dom.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import { handleOutsideClick } from '../util/handleOutsideClick.dom.ts';
import { createLogger } from '../logging/log.std.ts';
@@ -57,7 +57,7 @@ export const ModalHost = memo(function ModalHostInner({
theme,
}: PropsType) {
const containerRef = useRef<HTMLDivElement | null>(null);
const previousModalName = usePrevious(modalName, modalName);
const previousModalName = usePreviousDeprecated(modalName, modalName);
const modalContainer = useContext(ModalContainerContext) ?? document.body;
if (previousModalName !== modalName) {
+2 -2
View File
@@ -72,7 +72,7 @@ import { offsetDistanceModifier } from '../util/popperUtil.std.ts';
import { AxoButton } from '../axo/AxoButton.dom.tsx';
import { missingCaseError } from '../util/missingCaseError.std.ts';
import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser.dom.ts';
import { usePrevious } from '../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../hooks/usePrevious.std.ts';
import { tw } from '../axo/tw.dom.tsx';
const SUPPORT_URL = 'https://support.signal.org/hc/requests/new?desktop';
@@ -168,7 +168,7 @@ export function PreferencesDonateFlow({
CardFormValues | undefined
>();
const prevStep = usePrevious(step, step);
const prevStep = usePreviousDeprecated(step, step);
const hasCardFormData = useMemo(() => {
if (!cardFormValues) {
@@ -35,7 +35,7 @@ import type {
import type { LocalizerType, ThemeType } from '../../../types/Util.std.ts';
import type { PreferredBadgeSelectorType } from '../../../state/selectors/badges.preload.ts';
import type { Location } from '../../../types/Nav.std.ts';
import { usePrevious } from '../../../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../../../hooks/usePrevious.std.ts';
import type { Emoji } from '../../../axo/emoji.std.ts';
export type PropsDataType = {
@@ -148,7 +148,7 @@ export function GroupMemberLabelEditor({
// Popping the panel here after a save is far safer; we may not have re-rendered with
// the new existing values yet when the onSuccess callback down-file is called.
const previousIsSaving = usePrevious(isSaving, isSaving);
const previousIsSaving = usePreviousDeprecated(isSaving, isSaving);
useEffect(() => {
if (!isSaving && previousIsSaving !== isSaving && !isDirty) {
popPanelForConversation();
@@ -1,7 +1,7 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ForwardedRef, ReactNode, JSX } from 'react';
import { forwardRef, memo, useCallback, useMemo, useState } from 'react';
import { forwardRef, memo, useCallback, useMemo } from 'react';
import { Tabs } from 'radix-ui';
import { AnimatePresence, motion } from 'motion/react';
import type { LocalizerType } from '../../../types/I18N.std.ts';
@@ -19,6 +19,7 @@ import type { HydratedBodyRangesType } from '../../../types/BodyRange.std.ts';
import { AxoSymbol } from '../../../axo/AxoSymbol.dom.tsx';
import { missingCaseError } from '../../../util/missingCaseError.std.ts';
import { stripNewlinesForLeftPane } from '../../../util/stripNewlinesForLeftPane.std.ts';
import { usePrevious } from '../../../hooks/usePrevious.std.ts';
enum Direction {
None = 0,
@@ -26,18 +27,6 @@ enum Direction {
Forwards = 1,
}
// This `usePrevious()` hook is safe in React concurrent mode and doesn't break
// when rendered multiple times with the same values in `<StrictMode>`
function usePrevious<T>(value: T): T | null {
const [current, setCurrent] = useState<T>(value);
const [previous, setPrevious] = useState<T | null>(null);
if (current !== value) {
setCurrent(value);
setPrevious(current);
}
return previous;
}
export type PinMessageText = Readonly<{
body: string;
bodyRanges: HydratedBodyRangesType;
@@ -12,7 +12,7 @@ import type { PollWithResolvedVotersType } from '../../../state/selectors/messag
import type { LocalizerType } from '../../../types/Util.std.ts';
import { PollVotesModal } from './PollVotesModal.dom.tsx';
import { SpinnerV2 } from '../../SpinnerV2.dom.tsx';
import { usePrevious } from '../../../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../../../hooks/usePrevious.std.ts';
import { UserText } from '../../UserText.dom.tsx';
function VotedCheckmark({
@@ -170,7 +170,10 @@ export function PollMessageContents({
const [isPending, setIsPending] = useState(false);
const hasPendingVotes = poll.pendingVoteDiff && poll.pendingVoteDiff.size > 0;
const hadPendingVotesInLastRender = usePrevious(hasPendingVotes, undefined);
const hadPendingVotesInLastRender = usePreviousDeprecated(
hasPendingVotes,
undefined
);
const pendingCheckTimer = useRef<NodeJS.Timeout | null>(null);
const isIncoming = direction === 'incoming';
@@ -3,7 +3,7 @@
import { useEffect, useMemo } from 'react';
import type { AciString } from '../types/ServiceId.std.ts';
import { usePrevious } from './usePrevious.std.ts';
import { usePreviousDeprecated } from './usePrevious.std.ts';
type RemoteParticipant = {
hasRemoteVideo: boolean;
@@ -31,7 +31,7 @@ export function useActivateSpeakerViewOnPresenting({
switchFromPresentationView: () => void;
}): void {
const presenterAci = usePresenter(remoteParticipants);
const prevPresenterAci = usePrevious(presenterAci, presenterAci);
const prevPresenterAci = usePreviousDeprecated(presenterAci, presenterAci);
useEffect(() => {
if (prevPresenterAci !== presenterAci && presenterAci) {
+42 -2
View File
@@ -1,11 +1,51 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { useRef } from 'react';
import { useEffect, useRef, useState } from 'react';
export function usePrevious<T>(initialValue: T, currentValue: T): T {
/**
* This `usePrevious()` hook is safe in React concurrent mode and doesn't break
* when rendered multiple times with the same values in `<StrictMode>`
* Note: The previous value only updates when the value changes.
* If you want to do work once after a change and track that it was done:
* ```
* const [counter, setCounter] = useState(0);
* const lastAnimatedRef = useRef();
*
* useEffect(() => {
* if (counter === lastAnimatedRef.current) {
* return;
* }
* lastAnimatedRef.current = counter;
* // animate
* }, [counter]);
* ```
*/
export function usePrevious<T>(value: T): T | null {
const [current, setCurrent] = useState<T>(value);
const [previous, setPrevious] = useState<T | null>(null);
if (current !== value) {
setCurrent(value);
setPrevious(current);
}
return previous;
}
// TODO: DESKTOP-10151
/** @deprecated */
export function usePreviousDeprecated<T>(initialValue: T, currentValue: T): T {
const previousValueRef = useRef<T>(initialValue);
const result = previousValueRef.current;
previousValueRef.current = currentValue;
return result;
}
/** @deprecated */
export function usePreviousEffect<T>(initialValue: T, currentValue: T): T {
const previousValueRef = useRef<T>(initialValue);
const result = previousValueRef.current;
useEffect(() => {
previousValueRef.current = currentValue;
}, [currentValue]);
return result;
}
+2 -2
View File
@@ -4,7 +4,7 @@
import { useState, useEffect, type JSX } from 'react';
import type { LocalizerType } from '../types/Util.std.ts';
import { ResolvedSendStatus } from '../types/Stories.std.ts';
import { usePrevious } from './usePrevious.std.ts';
import { usePreviousDeprecated } from './usePrevious.std.ts';
import { AxoConfirmDialog } from '../axo/AxoConfirmDialog.dom.tsx';
export function useRetryStorySend(
@@ -19,7 +19,7 @@ export function useRetryStorySend(
const [hasSendFailedAlert, setHasSendFailedAlert] = useState(false);
const [wasManuallyRetried, setWasManuallyRetried] = useState(false);
const previousSendStatus = usePrevious(sendStatus, sendStatus);
const previousSendStatus = usePreviousDeprecated(sendStatus, sendStatus);
useEffect(() => {
if (!wasManuallyRetried) {
@@ -19,7 +19,7 @@ import { getConversations } from '../selectors/conversations.dom.ts';
import { SeenStatus } from '../../MessageSeenStatus.std.ts';
import { markViewed } from '../ducks/conversations.preload.ts';
import * as Errors from '../../types/errors.std.ts';
import { usePrevious } from '../../hooks/usePrevious.std.ts';
import { usePreviousDeprecated } from '../../hooks/usePrevious.std.ts';
const log = createLogger('VoiceNotesPlaybackProvider');
@@ -35,7 +35,10 @@ export const SmartVoiceNotesPlaybackProvider = memo(
const active = useSelector(selectAudioPlayerActive);
const conversations = useSelector(getConversations);
const previousStartPosition = usePrevious(undefined, active?.startPosition);
const previousStartPosition = usePreviousDeprecated(
undefined,
active?.startPosition
);
const content = active?.content;
let url: undefined | string;