From d32694596b7ed95c2fea3cdc56ca93b9f2bfe3f1 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Mon, 16 Feb 2026 14:50:20 +0200 Subject: [PATCH] Add "Renew" menu for expired mute words When a temporarily muted word expires, show a "Renew" action inline that opens a menu with duration choices (24h, 7d, 30d), letting users re-mute in one tap instead of having to remove and re-add manually. Fixes #8771 Co-Authored-By: Claude Opus 4.6 --- src/components/dialogs/MutedWords.tsx | 133 +++++++++++++++++++++----- 1 file changed, 107 insertions(+), 26 deletions(-) diff --git a/src/components/dialogs/MutedWords.tsx b/src/components/dialogs/MutedWords.tsx index eea54026a6..e674ca212b 100644 --- a/src/components/dialogs/MutedWords.tsx +++ b/src/components/dialogs/MutedWords.tsx @@ -8,6 +8,7 @@ import {logger} from '#/logger' import { usePreferencesQuery, useRemoveMutedWordMutation, + useUpdateMutedWordMutation, useUpsertMutedWordsMutation, } from '#/state/queries/preferences' import { @@ -29,6 +30,7 @@ import {PageText_Stroke2_Corner0_Rounded as PageText} from '#/components/icons/P import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' import {Loader} from '#/components/Loader' +import * as Menu from '#/components/Menu' import * as Prompt from '#/components/Prompt' import {Text} from '#/components/Typography' import {IS_NATIVE} from '#/env' @@ -421,6 +423,7 @@ function MutedWordRow({ const t = useTheme() const {_} = useLingui() const {isPending, mutateAsync: removeMutedWord} = useRemoveMutedWordMutation() + const {mutateAsync: updateMutedWord} = useUpdateMutedWordMutation() const control = Prompt.usePromptControl() const expiryDate = word.expiresAt ? new Date(word.expiresAt) : undefined const isExpired = expiryDate && expiryDate < new Date() @@ -431,6 +434,15 @@ function MutedWordRow({ removeMutedWord(word) }, [removeMutedWord, word, control]) + const renew = (days?: number) => { + updateMutedWord({ + ...word, + expiresAt: days + ? new Date(Date.now() + days * ONE_DAY).toISOString() + : undefined, + }) + } + return ( <> {(expiryDate || word.actorTarget === 'exclude-following') && ( - - - {expiryDate && ( + + {expiryDate && + (isExpired ? ( <> - {isExpired ? ( + Expired - ) : ( - - Expires{' '} - {formatDistance(expiryDate, new Date(), { - addSuffix: true, - })} - - )} + + + {' · '} + + + + {({props}) => ( + + Renew + + )} + + + + Renew duration + + + renew(1)}> + + 24 hours + + + renew(7)}> + + 7 days + + + renew(30)}> + + 30 days + + + renew()}> + + Forever + + + + + - )} - {word.actorTarget === 'exclude-following' && ( - <> - {' • '} - Excludes users you follow - - )} - + ) : ( + + + Expires{' '} + {formatDistance(expiryDate, new Date(), { + addSuffix: true, + })} + + + ))} + {word.actorTarget === 'exclude-following' && ( + + {expiryDate ? ' · ' : ''} + Excludes users you follow + + )} )}