From 9c00fffb89d5c883f6a3f4760403df8565551bcb Mon Sep 17 00:00:00 2001 From: MartinBraquet Date: Wed, 11 Feb 2026 16:52:27 +0100 Subject: [PATCH] Throttle toasts --- .../settings/hidden-profiles-modal.tsx | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/web/components/settings/hidden-profiles-modal.tsx b/web/components/settings/hidden-profiles-modal.tsx index fa9d97c..32453a3 100644 --- a/web/components/settings/hidden-profiles-modal.tsx +++ b/web/components/settings/hidden-profiles-modal.tsx @@ -1,4 +1,4 @@ -import {useEffect, useMemo, useState} from 'react' +import {useEffect, useMemo, useRef, useState} from 'react' import {Modal, MODAL_CLASS, SCROLLABLE_MODAL_CLASS} from 'web/components/layout/modal' import {Col} from 'web/components/layout/col' import {Row} from 'web/components/layout/row' @@ -30,6 +30,9 @@ export function HiddenProfilesModal(props: { const [error, setError] = useState(null) const [hidden, setHidden] = useState(null) const [busyIds, setBusyIds] = useState>({}) + // Throttle/batch success toasts when unhiding multiple profiles quickly + const [pendingUnhideCount, setPendingUnhideCount] = useState(0) + const toastTimerRef = useRef | null>(null) const empty = useMemo(() => (hidden ? hidden.length === 0 : false), [hidden]) @@ -56,21 +59,50 @@ export function HiddenProfilesModal(props: { .finally(() => alive && setLoading(false)) return () => { alive = false + // Reset toast batching state on close/unmount + if (toastTimerRef.current) { + clearTimeout(toastTimerRef.current as number) + toastTimerRef.current = null + } + setPendingUnhideCount(0) } }, [open]) + const flushUnhideToast = () => { + if (pendingUnhideCount <= 0) return + if (pendingUnhideCount === 1) { + toast.success( + t( + 'settings.hidden_profiles.unhidden_success', + 'Profile unhidden. You will start seeing this person again in results.' + ) + ) + } else { + toast.success( + t( + 'settings.hidden_profiles.unhidden_success_many', + 'Unhid {n} profiles.', + // Simple param replacement; if your i18n library supports interpolation, it will use it. + ).replace('{n}', String(pendingUnhideCount)) + ) + } + setPendingUnhideCount(0) + } + const unhide = async (userId: string) => { if (busyIds[userId]) return setBusyIds((b) => ({...b, [userId]: true})) try { await api('unhide-profile', {hiddenUserId: userId}) setHidden((list) => (list ? list.filter((u) => u.id !== userId) : list)) - toast.success( - t( - 'settings.hidden_profiles.unhidden_success', - 'Profile unhidden. You will start seeing this person again in results.' - ) - ) + // Batch/throttle the success toast + setPendingUnhideCount((c) => c + 1) + if (!toastTimerRef.current) { + toastTimerRef.current = setTimeout(() => { + flushUnhideToast() + toastTimerRef.current = null + }, 600) + } } catch (e) { console.error('Failed to unhide profile', e) toast.error(t('settings.hidden_profiles.unhide_failed', 'Failed to unhide'))