fix: R3 bugs - TipTap focus leak, Quick Capture palette, Calendar day view

This commit is contained in:
Hermes
2026-08-01 12:26:43 +00:00
parent 118617c892
commit 3a0df07296
3 changed files with 14 additions and 20 deletions
+5 -11
View File
@@ -162,7 +162,6 @@ function EventForm({ event, onClose }: { event?: CalendarEvent; onClose: () => v
function CalendarPage() { function CalendarPage() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [date, setDate] = useState(new Date()); const [date, setDate] = useState(new Date());
const [view, setView] = useState<string>("month");
const [createOpen, setCreateOpen] = useState(false); const [createOpen, setCreateOpen] = useState(false);
const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(null); const [selectedEvent, setSelectedEvent] = useState<CalendarEvent | null>(null);
const [eventDetailOpen, setEventDetailOpen] = useState(false); const [eventDetailOpen, setEventDetailOpen] = useState(false);
@@ -177,12 +176,7 @@ function CalendarPage() {
return () => window.removeEventListener("resize", check); return () => window.removeEventListener("resize", check);
}, []); }, []);
// Auto-switch to agenda on mobile // Mobile: defaultView is set to agenda via isMobile check
useEffect(() => {
if (isMobile && view === "month") {
setView("agenda");
}
}, [isMobile]);
const { data: eventsData } = useApiQuery<{ items: CalendarEvent[]; totalItems: number }>( const { data: eventsData } = useApiQuery<{ items: CalendarEvent[]; totalItems: number }>(
["calendar-events", date.toISOString()], ["calendar-events", date.toISOString()],
@@ -241,8 +235,9 @@ function CalendarPage() {
setDate(newDate); setDate(newDate);
}, []); }, []);
const viewRef = useRef<string>("month");
const handleViewChange = useCallback((newView: string) => { const handleViewChange = useCallback((newView: string) => {
setView(newView); viewRef.current = newView;
}, []); }, []);
return ( return (
@@ -267,8 +262,7 @@ function CalendarPage() {
startAccessor="start" startAccessor="start"
endAccessor="end" endAccessor="end"
date={date} date={date}
view={view} defaultView={isMobile ? Views.AGENDA : Views.MONTH}
defaultView={Views.MONTH}
onNavigate={handleNavigate} onNavigate={handleNavigate}
onView={handleViewChange} onView={handleViewChange}
onSelectEvent={handleSelectEvent} onSelectEvent={handleSelectEvent}
@@ -281,7 +275,7 @@ function CalendarPage() {
showMultiDayTimes showMultiDayTimes
components={{ components={{
event: EventComponent, event: EventComponent,
toolbar: (props: any) => <CustomToolbar {...props} currentView={view} />, toolbar: (props: any) => <CustomToolbar {...props} />,
}} }}
views={["month", "week", "work_week", "day", "agenda"]} views={["month", "week", "work_week", "day", "agenda"]}
step={30} step={30}
+2 -1
View File
@@ -205,6 +205,7 @@ function QuickCaptureWidget() {
}); });
const handleSubmit = (e: React.FormEvent) => { const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation();
if (!text.trim()) return; if (!text.trim()) return;
if (type === "task") createTask.mutate(text.trim()); if (type === "task") createTask.mutate(text.trim());
else createNote.mutate(text.trim()); else createNote.mutate(text.trim());
@@ -221,7 +222,7 @@ function QuickCaptureWidget() {
</Select> </Select>
<Input placeholder="Quick capture..." value={text} onChange={(e) => setText(e.target.value)} className="h-9" /> <Input placeholder="Quick capture..." value={text} onChange={(e) => setText(e.target.value)} className="h-9" />
</div> </div>
<Button type="submit" size="sm" className="h-9" disabled={!text.trim() || createTask.isPending || createNote.isPending}>Add</Button> <Button type="button" size="sm" className="h-9" disabled={!text.trim() || createTask.isPending || createNote.isPending} onClick={handleSubmit}>Add</Button>
</form> </form>
); );
} }
+7 -8
View File
@@ -1,4 +1,4 @@
import { useState, useCallback, useRef, useEffect } from "react"; import { useState, useCallback, useRef, useEffect, memo } from "react";
import { createRoute } from "@tanstack/react-router"; import { createRoute } from "@tanstack/react-router";
import { Route as appRoute } from "../_app"; import { Route as appRoute } from "../_app";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
@@ -17,7 +17,7 @@ import { cn } from "@/lib/utils";
import type { Note, PaginatedResponse } from "@/lib/types"; import type { Note, PaginatedResponse } from "@/lib/types";
// Simple TipTap-like editor using contentEditable // Simple TipTap-like editor using contentEditable
function NoteEditor({ content, onChange, placeholder = "Start writing..." }: { content: string; onChange: (html: string) => void; placeholder?: string }) { const NoteEditor = memo(function NoteEditor({ content, onChange, placeholder = "Start writing..." }: { content: string; onChange: (html: string) => void; placeholder?: string }) {
const editorRef = useRef<HTMLDivElement>(null); const editorRef = useRef<HTMLDivElement>(null);
const [isPlaceholder, setIsPlaceholder] = useState(!content); const [isPlaceholder, setIsPlaceholder] = useState(!content);
@@ -47,14 +47,14 @@ function NoteEditor({ content, onChange, placeholder = "Start writing..." }: { c
/> />
</div> </div>
); );
} });
function NotesPage() { function NotesPage() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [selectedNote, setSelectedNote] = useState<Note | null>(null); const [selectedNote, setSelectedNote] = useState<Note | null>(null);
const [editorContent, setEditorContent] = useState(""); const [editorContent, setEditorContent] = useState("");
const [saveTimer, setSaveTimer] = useState<ReturnType<typeof setTimeout> | null>(null); const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const [showBacklinks, setShowBacklinks] = useState(false); const [showBacklinks, setShowBacklinks] = useState(false);
const [showVersions, setShowVersions] = useState(false); const [showVersions, setShowVersions] = useState(false);
@@ -104,14 +104,13 @@ function NotesPage() {
const handleContentChange = useCallback((html: string) => { const handleContentChange = useCallback((html: string) => {
setEditorContent(html); setEditorContent(html);
if (saveTimer) clearTimeout(saveTimer); if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
const timer = setTimeout(() => { saveTimerRef.current = setTimeout(() => {
if (selectedNote) { if (selectedNote) {
updateMutation.mutate({ id: selectedNote.id, data: { content: html } }); updateMutation.mutate({ id: selectedNote.id, data: { content: html } });
} }
}, 500); }, 500);
setSaveTimer(timer); }, [selectedNote, updateMutation]);
}, [selectedNote, updateMutation, saveTimer]);
const handleTitleChange = (title: string) => { const handleTitleChange = (title: string) => {
if (selectedNote) { if (selectedNote) {