Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions app/routes/_app+/recipients+/$recipientId.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ function MessageForms({
const deleteSafeDelayMs = 150
const [confirmDelete, setConfirmDelete] = useState(false)
const [canDelete, setCanDelete] = useState(false)
const [currentContent, setCurrentContent] = useState(message.content)
const formRef = useRef<HTMLFormElement | null>(null)
const textareaRef = useRef<HTMLTextAreaElement | null>(null)
const [updateContentForm, updateContentFields] = useForm({
Expand All @@ -408,6 +409,8 @@ function MessageForms({
const sendIsPending = sendNowFetcher.state !== 'idle'
const deleteIsPending = deleteFetcher.state !== 'idle'
const textareaProps = getTextareaProps(updateContentFields.content)
const hasEdits = currentContent !== message.content
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

State not reset when save succeeds with modified content

Low Severity

The currentContent state is initialized once via useState(message.content) and only updates through onInput. After a successful save, if message.content from the server differs from what the user typed (e.g., server-side trimming or normalization), hasEdits remains true and the save button incorrectly stays visible. Additionally, if external updates change message.content while the form is open, the save button may appear even though the user hasn't made any edits, potentially leading to unintentional data overwrites.

Fix in Cursor Fix in Web

const showSaveButton = hasEdits || updateIsPending

useEffect(() => {
if (confirmDelete) {
Expand Down Expand Up @@ -461,19 +464,21 @@ function MessageForms({
<span>{headerText}</span>
</div>
<div className="flex items-center gap-2">
<StatusButton
form={updateContentForm.id}
status={updateIsPending ? 'pending' : 'idle'}
className="h-11 w-11 gap-0 text-[hsl(var(--palette-cream))] hover:bg-[hsl(var(--palette-cream))/0.15] sm:h-10 sm:w-10"
size="icon"
variant="ghost"
type="submit"
name="intent"
value={updateMessageContentActionIntent}
>
<Icon name="check" size="sm" />
<span className="sr-only">Save</span>
</StatusButton>
{showSaveButton ? (
<StatusButton
form={updateContentForm.id}
status={updateIsPending ? 'pending' : 'idle'}
className="h-11 w-11 gap-0 text-[hsl(var(--palette-cream))] hover:bg-[hsl(var(--palette-cream))/0.15] sm:h-10 sm:w-10"
size="icon"
variant="ghost"
type="submit"
name="intent"
value={updateMessageContentActionIntent}
>
<Icon name="check" size="sm" />
<span className="sr-only">Save</span>
</StatusButton>
) : null}
<DropdownMenu
onOpenChange={(open) => {
if (!open) setConfirmDelete(false)
Expand Down Expand Up @@ -532,6 +537,9 @@ function MessageForms({
</label>
<textarea
{...textareaProps}
onInput={(event) => {
setCurrentContent(event.currentTarget.value)
}}
ref={textareaRef}
className="mt-4 w-full resize-none bg-transparent text-sm leading-relaxed text-[hsl(var(--palette-cream))] placeholder:text-[hsl(var(--palette-cream))]/80 focus-visible:outline-none"
rows={4}
Expand Down
Loading