From 468d0ac8cf9d13b0c43444299544036c8382e375 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Sun, 23 Mar 2025 15:00:20 +0000 Subject: [PATCH] Fix history view --- frontend/obsidian-plugin/src/styles.scss | 2 +- .../obsidian-plugin/src/views/history-view.ts | 40 ++++++---- frontend/sync-client/src/sync-client.ts | 4 +- .../sync-client/src/tracing/sync-history.ts | 76 ++++++++++++------- 4 files changed, 77 insertions(+), 45 deletions(-) diff --git a/frontend/obsidian-plugin/src/styles.scss b/frontend/obsidian-plugin/src/styles.scss index 9ffc5caa..110e7152 100644 --- a/frontend/obsidian-plugin/src/styles.scss +++ b/frontend/obsidian-plugin/src/styles.scss @@ -70,7 +70,7 @@ textarea { resize: none; - height: 60px; + height: 75px; } } diff --git a/frontend/obsidian-plugin/src/views/history-view.ts b/frontend/obsidian-plugin/src/views/history-view.ts index 2169a425..2a4d7e1e 100644 --- a/frontend/obsidian-plugin/src/views/history-view.ts +++ b/frontend/obsidian-plugin/src/views/history-view.ts @@ -23,11 +23,14 @@ export class HistoryView extends ItemView { super(leaf); this.icon = HistoryView.ICON; - this.client.addSyncHistoryUpdateListener(() => { - this.updateView().catch((_error: unknown) => { - this.client.logger.error("Failed to update history view"); - }); - }); + this.client.addSyncHistoryUpdateListener( + () => + void this.updateView().catch((error: unknown) => { + this.client.logger.error( + `Failed to update history view: ${error}` + ); + }) + ); } private static getSyncTypeIcon(type: SyncType | undefined): IconName { @@ -58,6 +61,21 @@ export class HistoryView extends ItemView { }); } + private static updateTimeSince( + element: HTMLElement, + entry: HistoryEntry + ): void { + const timestampElement = element.querySelector( + ".history-card-timestamp" + ); + if (timestampElement != null) { + timestampElement.textContent = intlFormatDistance( + entry.timestamp, + new Date() + ); + } + } + public getViewType(): string { return HistoryView.TYPE; } @@ -88,7 +106,7 @@ export class HistoryView extends ItemView { return; } - const entries = this.client.getHistoryEntries().reverse(); + const entries = this.client.getHistoryEntries(); if (this.historyEntryToElement.size === 0 && entries.length > 0) { // Clear the "No update has happened yet" message @@ -98,15 +116,7 @@ export class HistoryView extends ItemView { entries.forEach((entry) => { const element = this.historyEntryToElement.get(entry); if (element !== undefined) { - const timestampElement = element.querySelector( - ".history-card-timestamp" - ); - if (timestampElement != null) { - timestampElement.textContent = intlFormatDistance( - entry.timestamp, - new Date() - ); - } + HistoryView.updateTimeSince(element, entry); return; } diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index ceadd8cf..0e0df2ff 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -149,8 +149,8 @@ export class SyncClient { return this.syncService.checkConnection(); } - public getHistoryEntries(): HistoryEntry[] { - return this.history.getEntries(); + public getHistoryEntries(): readonly HistoryEntry[] { + return this.history.entries; } public addSyncHistoryUpdateListener( diff --git a/frontend/sync-client/src/tracing/sync-history.ts b/frontend/sync-client/src/tracing/sync-history.ts index 0980d809..fe782e9b 100644 --- a/frontend/sync-client/src/tracing/sync-history.ts +++ b/frontend/sync-client/src/tracing/sync-history.ts @@ -28,8 +28,9 @@ export interface HistoryStats { export class SyncHistory { private static readonly MAX_ENTRIES = 500; + private static readonly TIMEOUT_FOR_MERGING_ENTRIES_IN_SECONDS = 15; - private entries: HistoryEntry[] = []; + private _entries: HistoryEntry[] = []; private readonly syncHistoryUpdateListeners: (( status: HistoryStats @@ -42,19 +43,35 @@ export class SyncHistory { public constructor(private readonly logger: Logger) {} - public getEntries(): HistoryEntry[] { - return [...this.entries]; + public get entries(): readonly HistoryEntry[] { + return this._entries; } - public reset(): void { - this.entries.length = 0; - this.status = { - success: 0, - error: 0 + /** + * Insert the entry at the beginning of the history list. If the entry + * already in the list, it will get moved to the beginning and updated. + * + * If the entry list is too long, the oldest entry will be removed. + */ + public addHistoryEntry(entry: CommonHistoryEntry): void { + const historyEntry = { + ...entry, + timestamp: new Date() }; - this.syncHistoryUpdateListeners.forEach((listener) => { - listener(this.status); - }); + + const candidate = this.findSimilarRecentEntry(historyEntry); + if (candidate !== undefined) { + this._entries = this._entries.filter((e) => e !== candidate); + } + + // Insert the entry at the beginning + this._entries.unshift(historyEntry); + + if (this._entries.length > SyncHistory.MAX_ENTRIES) { + this._entries.pop(); + } + + this.updateSuccessCount(historyEntry); } public addSyncHistoryUpdateListener( @@ -64,25 +81,35 @@ export class SyncHistory { listener({ ...this.status }); } - public addHistoryEntry(entry: CommonHistoryEntry): void { - const historyEntry = { - ...entry, - timestamp: new Date() + public reset(): void { + this._entries.length = 0; + this.status = { + success: 0, + error: 0 }; + this.syncHistoryUpdateListeners.forEach((listener) => { + listener(this.status); + }); + } - const candidate = this.entries.find( - (e) => e.relativePath === historyEntry.relativePath + private findSimilarRecentEntry( + entry: HistoryEntry + ): HistoryEntry | undefined { + const candidate = this._entries.find( + (e) => e.relativePath === entry.relativePath ); if ( candidate !== undefined && - (this.entries.slice(-1)[0] === candidate || - candidate.timestamp.getTime() + 10 * 1000 > - historyEntry.timestamp.getTime()) + (this._entries[0] === candidate || + candidate.timestamp.getTime() + + SyncHistory.TIMEOUT_FOR_MERGING_ENTRIES_IN_SECONDS * 1000 > + entry.timestamp.getTime()) ) { - this.entries = this.entries.filter((e) => e !== candidate); + return candidate; } - this.entries.push(historyEntry); + } + private updateSuccessCount(entry: HistoryEntry): void { if (entry.status === SyncStatus.SUCCESS) { this.status.success++; this.logger.info( @@ -94,13 +121,8 @@ export class SyncHistory { `Cannot sync file: ${entry.relativePath} - ${entry.message}` ); } - this.syncHistoryUpdateListeners.forEach((listener) => { listener(this.status); }); - - if (this.entries.length > SyncHistory.MAX_ENTRIES) { - this.entries.shift(); - } } }