feat: smart note history with manual/auto modes, delete entries, i18n fixes
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m16s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m16s
- Add noteHistoryMode setting (manual default / auto) with DB migration - Manual mode: commit button in editor toolbar creates snapshots on demand - Auto mode: smart snapshots with 20-char diff threshold + 5min cooldown, structural changes (color, pin, archive, labels) bypass cooldown - Add delete individual history entries from history modal - Fix sidebar: Notes nav no longer active on notebook pages - Fix sidebar icon: replace filled Lightbulb with outlined FileText - Fix title suggestions: change from amber to sky blue color scheme - Fix hydration mismatch: add suppressHydrationWarning on locale dates - Complete i18n: add history, sort, and AI chat translations for all 16 languages - Translate French AI assistant section (40+ keys) from English to French - Update README with new features and stack info Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ import { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import { useSearchParams, useRouter } from 'next/navigation'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { Note } from '@/lib/types'
|
||||
import { getAISettings } from '@/app/actions/ai-settings'
|
||||
import { updateAISettings } from '@/app/actions/ai-settings'
|
||||
import { getAllNotes, searchNotes } from '@/app/actions/notes'
|
||||
import { NoteInput } from '@/components/note-input'
|
||||
import { NotesMainSection, type NotesViewMode } from '@/components/notes-main-section'
|
||||
@@ -24,6 +24,7 @@ import { cn } from '@/lib/utils'
|
||||
import { LabelFilter } from '@/components/label-filter'
|
||||
import { useLanguage } from '@/lib/i18n'
|
||||
import { useHomeView } from '@/context/home-view-context'
|
||||
import { NoteHistoryModal } from '@/components/note-history-modal'
|
||||
|
||||
// Lazy-load heavy dialogs — uniquement chargés à la demande
|
||||
const NoteEditor = dynamic(
|
||||
@@ -42,6 +43,8 @@ const AutoLabelSuggestionDialog = dynamic(
|
||||
type InitialSettings = {
|
||||
showRecentNotes: boolean
|
||||
notesViewMode: 'masonry' | 'tabs'
|
||||
noteHistory: boolean
|
||||
noteHistoryMode: 'manual' | 'auto'
|
||||
}
|
||||
|
||||
interface HomeClientProps {
|
||||
@@ -59,10 +62,14 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
initialNotes.filter(n => n.isPinned)
|
||||
)
|
||||
const [notesViewMode, setNotesViewMode] = useState<NotesViewMode>(initialSettings.notesViewMode)
|
||||
const [noteHistoryEnabled, setNoteHistoryEnabled] = useState(initialSettings.noteHistory)
|
||||
const [noteHistoryMode] = useState<'manual' | 'auto'>(initialSettings.noteHistoryMode)
|
||||
const [editingNote, setEditingNote] = useState<{ note: Note; readOnly?: boolean } | null>(null)
|
||||
const [isLoading, setIsLoading] = useState(false) // false by default — data is pre-loaded
|
||||
const [notebookSuggestion, setNotebookSuggestion] = useState<{ noteId: string; content: string } | null>(null)
|
||||
const [batchOrganizationOpen, setBatchOrganizationOpen] = useState(false)
|
||||
const [historyOpen, setHistoryOpen] = useState(false)
|
||||
const [historyNote, setHistoryNote] = useState<Note | null>(null)
|
||||
const { refreshKey, triggerRefresh } = useNoteRefresh()
|
||||
const { labels } = useLabels()
|
||||
const { setControls } = useHomeView()
|
||||
@@ -133,6 +140,22 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
if (note) setEditingNote({ note, readOnly: false })
|
||||
}
|
||||
|
||||
const handleOpenHistory = useCallback((note: Note) => {
|
||||
setHistoryNote(note)
|
||||
setHistoryOpen(true)
|
||||
}, [])
|
||||
|
||||
const handleEnableHistory = useCallback(async () => {
|
||||
await updateAISettings({ noteHistory: true })
|
||||
setNoteHistoryEnabled(true)
|
||||
}, [])
|
||||
|
||||
const handleHistoryRestored = useCallback((restored: Note) => {
|
||||
setNotes((prev) => prev.map((n) => (n.id === restored.id ? { ...n, ...restored } : n)))
|
||||
setPinnedNotes((prev) => prev.map((n) => (n.id === restored.id ? { ...n, ...restored } : n)))
|
||||
setEditingNote((prev) => (prev?.note.id === restored.id ? { ...prev, note: restored } : prev))
|
||||
}, [])
|
||||
|
||||
const handleSizeChange = useCallback((noteId: string, size: 'small' | 'medium' | 'large') => {
|
||||
setNotes(prev => prev.map(n => n.id === noteId ? { ...n, size } : n))
|
||||
setPinnedNotes(prev => prev.map(n => n.id === noteId ? { ...n, size } : n))
|
||||
@@ -388,6 +411,10 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
onEdit={(note, readOnly) => setEditingNote({ note, readOnly })}
|
||||
onSizeChange={handleSizeChange}
|
||||
currentNotebookId={searchParams.get('notebook')}
|
||||
noteHistoryEnabled={noteHistoryEnabled}
|
||||
noteHistoryMode={noteHistoryMode}
|
||||
onOpenHistory={handleOpenHistory}
|
||||
onEnableHistory={handleEnableHistory}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -437,6 +464,15 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
onClose={() => setEditingNote(null)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<NoteHistoryModal
|
||||
open={historyOpen}
|
||||
onOpenChange={setHistoryOpen}
|
||||
note={historyNote}
|
||||
enabled={noteHistoryEnabled}
|
||||
onEnableHistory={handleEnableHistory}
|
||||
onRestored={handleHistoryRestored}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user