Merge pull request #921 from texchi2/fix/calendar-save-in-flight

fix(calendar): show in-flight state on Save, and surface the silent length-guard rejection
This commit is contained in:
Linus Rath
2026-09-30 14:23:09 +02:00
2 changed files with 12 additions and 2 deletions
+11 -2
View File
@@ -2,6 +2,7 @@
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
import { useTranslations, useLocale } from "next-intl";
import { toast } from "@/stores/toast-store";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { LinkifiedText } from "@/components/ui/linkified-text";
@@ -493,7 +494,13 @@ export function EventModal({
const handleSave = useCallback(async () => {
const trimmedTitle = title.trim();
if (!trimmedTitle || isSaving) return;
if (trimmedTitle.length > 500 || description.trim().length > 10000 || location.trim().length > 500) return;
if (trimmedTitle.length > 500 || description.trim().length > 10000 || location.trim().length > 500) {
// This guard returned silently, so a rejected save was indistinguishable from a dead
// button. The fields also enforce their limits via maxLength, so this is a backstop —
// but if it ever fires, say so.
toast.error(t("notifications.event_error"));
return;
}
const pendingAttendee = participantInputRef.current?.flush() ?? null;
const effectiveAttendees = pendingAttendee ? [...attendees, pendingAttendee] : attendees;
@@ -1444,7 +1451,9 @@ export function EventModal({
disabled={!title.trim() || isSaving}
className="w-full"
>
{t("form.save")}
{/* While a save is in flight the button was disabled with no visible change, so a
slow request (mobile, VPN) looked like a dead button for the whole timeout. */}
{isSaving ? t("form.saving") : t("form.save")}
</Button>
</div>
)}
+1
View File
@@ -2945,6 +2945,7 @@
"all_day_event": "All-day event",
"calendar_select": "Calendar",
"save": "Save",
"saving": "Saving…",
"cancel": "Cancel",
"delete_confirm": "Are you sure you want to delete this event?",
"color": "Color"