diff --git a/src/components/calendar/TimeGridScrollView.tsx b/src/components/calendar/TimeGridScrollView.tsx
index 65f7503e..be6da07a 100644
--- a/src/components/calendar/TimeGridScrollView.tsx
+++ b/src/components/calendar/TimeGridScrollView.tsx
@@ -89,6 +89,8 @@ interface TimeGridScrollViewProps {
onSelectDate?: (date: Date) => void;
onSelectEvent?: (event: CalendarEvent) => void;
onCreateAtTime?: (date: Date) => void;
+ /** A change takes the "New event" block away (the editor closed, the tab was left). */
+ draftResetKey?: number;
}
/**
@@ -119,6 +121,7 @@ function TimeGridScrollViewInner({
onSelectDate,
onSelectEvent,
onCreateAtTime,
+ draftResetKey = 0,
}: TimeGridScrollViewProps) {
const c = useColors();
// "Light event colors" (webmail calendarTintedEvents).
@@ -314,6 +317,9 @@ function TimeGridScrollViewInner({
// A tap on an empty slot marks it with a "New event" block; tapping that
// block creates the event. Two taps, so a stray tap never opens the editor.
const [draft, setDraft] = React.useState<{ key: string; hour: number } | null>(null);
+ React.useEffect(() => {
+ setDraft(null);
+ }, [draftResetKey]);
const handleTapAt = React.useCallback((day: Date, hour: number) => {
const key = dayKey(day);
setDraft((cur) => (cur && cur.key === key && cur.hour === hour ? null : { key, hour }));
diff --git a/src/components/calendar/__tests__/slot-draft-reset.test.ts b/src/components/calendar/__tests__/slot-draft-reset.test.ts
new file mode 100644
index 00000000..542e774d
--- /dev/null
+++ b/src/components/calendar/__tests__/slot-draft-reset.test.ts
@@ -0,0 +1,21 @@
+import { describe, expect, it } from 'vitest';
+import fs from 'node:fs';
+import path from 'node:path';
+
+// Checked on the source: the suite has no React Native renderer.
+const grid = fs.readFileSync(path.resolve(__dirname, '../TimeGridScrollView.tsx'), 'utf8');
+const screen = fs.readFileSync(path.resolve(__dirname, '../../../screens/CalendarScreen.tsx'), 'utf8');
+
+describe('the "New event" block a tap on an empty slot leaves', () => {
+ it('goes when the grid is told to drop it', () => {
+ expect(grid).toMatch(/React\.useEffect\(\(\) => \{\s*setDraft\(null\);\s*\}, \[draftResetKey\]\);/);
+ });
+
+ it('is dropped when the editor opens or closes and when the user leaves the tab', () => {
+ const grids = screen.slice(screen.indexOf('', screen.indexOf(' \{\s*setSlotDraftReset\(\(n\) => n \+ 1\);\s*\}, \[modalVisible\]\);/);
+ // The focus effect's cleanup runs on blur.
+ expect(screen).toMatch(/useFocusEffect\(\s*React\.useCallback\(\(\) => \(\) => setSlotDraftReset\(\(n\) => n \+ 1\), \[\]\),?\s*\)/);
+ });
+});
diff --git a/src/screens/CalendarScreen.tsx b/src/screens/CalendarScreen.tsx
index 38eb0432..3c36e138 100644
--- a/src/screens/CalendarScreen.tsx
+++ b/src/screens/CalendarScreen.tsx
@@ -11,6 +11,7 @@ import {
useWindowDimensions,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
+import { useFocusEffect } from '@react-navigation/native';
import { TASK_CHIP_ID_PREFIX, multiDayCount } from '../lib/calendar-time-grid';
import {
Calendar as CalendarIcon,
@@ -239,6 +240,15 @@ export default function CalendarScreen() {
const [modalEvent, setModalEvent] = React.useState(null);
const [modalDate, setModalDate] = React.useState(undefined);
const [modalVisible, setModalVisible] = React.useState(false);
+ // The "New event" block a tap on an empty slot leaves: gone once the
+ // editor opens or closes, or the user leaves the tab.
+ const [slotDraftReset, setSlotDraftReset] = React.useState(0);
+ React.useEffect(() => {
+ setSlotDraftReset((n) => n + 1);
+ }, [modalVisible]);
+ useFocusEffect(
+ React.useCallback(() => () => setSlotDraftReset((n) => n + 1), []),
+ );
const [pendingAction, setPendingAction] = React.useState(null);
const [sidebarVisible, setSidebarVisible] = React.useState(false);
const [tasksVisible, setTasksVisible] = React.useState(false);
@@ -1177,6 +1187,7 @@ export default function CalendarScreen() {
onSelectDate={viewMode === 'day' ? handleSelectDate : openDay}
onSelectEvent={handleSelectEvent}
onCreateAtTime={openCreate}
+ draftResetKey={slotDraftReset}
/>
)}
{viewMode === 'week' && !freeScroll && (