import { Dispatch, StateUpdater, useEffect, useState } from "preact/hooks"; import appContext from "../../components/app_context"; import dialog from "../../services/dialog"; import { t } from "../../services/i18n"; import server from "../../services/server"; import toast from "../../services/toast"; import Button from "../react/Button"; import Modal from "../react/Modal"; import ReactBasicWidget from "../react/ReactBasicWidget"; import hoisted_note from "../../services/hoisted_note"; import type { RecentChangeRow } from "@triliumnext/commons"; import froca from "../../services/froca"; import { formatDateTime } from "../../utils/formatters"; import link from "../../services/link"; import RawHtml from "../react/RawHtml"; import ws from "../../services/ws"; import useTriliumEvent from "../react/hooks"; function RecentChangesDialogComponent() { const [ ancestorNoteId, setAncestorNoteId ] = useState(); const [ groupedByDate, setGroupedByDate ] = useState>(); const [ needsRefresh, setNeedsRefresh ] = useState(false); const [ shown, setShown ] = useState(false); useTriliumEvent("showRecentChanges", ({ ancestorNoteId }) => { setNeedsRefresh(true); setAncestorNoteId(ancestorNoteId ?? hoisted_note.getHoistedNoteId()); setShown(true); }); if (!groupedByDate || needsRefresh) { useEffect(() => { if (needsRefresh) { setNeedsRefresh(false); } server.get(`recent-changes/${ancestorNoteId}`) .then(async (recentChanges) => { // preload all notes into cache await froca.getNotes( recentChanges.map((r) => r.noteId), true ); const groupedByDate = groupByDate(recentChanges); setGroupedByDate(groupedByDate); }); }) } return ( { server.post("notes/erase-deleted-notes-now").then(() => { setNeedsRefresh(true); toast.showMessage(t("recent_changes.deleted_notes_message")); }); }} /> } onHidden={() => setShown(false)} show={shown} >
{groupedByDate?.size ? : <>{t("recent_changes.no_changes_message")}}
) } function RecentChangesTimeline({ groupedByDate, setShown }: { groupedByDate: Map, setShown: Dispatch> }) { return ( <> { Array.from(groupedByDate.entries()).map(([dateDay, dayChanges]) => { const formattedDate = formatDateTime(dateDay as string, "full", "none"); return (
{formattedDate}
    { dayChanges.map((change) => { const isDeleted = change.current_isDeleted; const formattedTime = formatDateTime(change.date, "none", "short"); const note = froca.getNoteFromCache(change.noteId); const notePath = note?.getBestNotePathString(); return (
  • {formattedTime} { !isDeleted ? : }
  • ); })}
); })} ); } function NoteLink({ notePath, title }: { notePath: string, title: string }) { if (!notePath || !title) { return null; } const [ noteLink, setNoteLink ] = useState | null>(null); useEffect(() => { link.createLink(notePath, { title, showNotePath: true }).then(setNoteLink); }, [notePath, title]); return ( noteLink ? : {title} ); } function DeletedNoteLink({ change, setShown }: { change: RecentChangeRow, setShown: Dispatch> }) { return ( <> {change.current_title}   ( { async () => { const text = t("recent_changes.confirm_undelete"); if (await dialog.confirm(text)) { await server.put(`notes/${change.noteId}/undelete`); setShown(false); await ws.waitForMaxKnownEntityChangeId(); const activeContext = appContext.tabManager.getActiveContext(); if (activeContext) { activeContext.setNote(change.noteId); } } } }}> {t("recent_changes.undelete_link")}) ); } export default class RecentChangesDialog extends ReactBasicWidget { get component() { return } } function groupByDate(rows: RecentChangeRow[]) { const groupedByDate = new Map(); for (const row of rows) { const dateDay = row.date.substr(0, 10); if (!groupedByDate.has(dateDay)) { groupedByDate.set(dateDay, []); } groupedByDate.get(dateDay)!.push(row); } return groupedByDate; }