Fix history view

This commit is contained in:
Andras Schmelczer 2025-03-23 15:00:20 +00:00
commit 468d0ac8cf
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
4 changed files with 77 additions and 45 deletions

View file

@ -70,7 +70,7 @@
textarea { textarea {
resize: none; resize: none;
height: 60px; height: 75px;
} }
} }

View file

@ -23,11 +23,14 @@ export class HistoryView extends ItemView {
super(leaf); super(leaf);
this.icon = HistoryView.ICON; this.icon = HistoryView.ICON;
this.client.addSyncHistoryUpdateListener(() => { this.client.addSyncHistoryUpdateListener(
this.updateView().catch((_error: unknown) => { () =>
this.client.logger.error("Failed to update history view"); void this.updateView().catch((error: unknown) => {
}); this.client.logger.error(
}); `Failed to update history view: ${error}`
);
})
);
} }
private static getSyncTypeIcon(type: SyncType | undefined): IconName { 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 { public getViewType(): string {
return HistoryView.TYPE; return HistoryView.TYPE;
} }
@ -88,7 +106,7 @@ export class HistoryView extends ItemView {
return; return;
} }
const entries = this.client.getHistoryEntries().reverse(); const entries = this.client.getHistoryEntries();
if (this.historyEntryToElement.size === 0 && entries.length > 0) { if (this.historyEntryToElement.size === 0 && entries.length > 0) {
// Clear the "No update has happened yet" message // Clear the "No update has happened yet" message
@ -98,15 +116,7 @@ export class HistoryView extends ItemView {
entries.forEach((entry) => { entries.forEach((entry) => {
const element = this.historyEntryToElement.get(entry); const element = this.historyEntryToElement.get(entry);
if (element !== undefined) { if (element !== undefined) {
const timestampElement = element.querySelector( HistoryView.updateTimeSince(element, entry);
".history-card-timestamp"
);
if (timestampElement != null) {
timestampElement.textContent = intlFormatDistance(
entry.timestamp,
new Date()
);
}
return; return;
} }

View file

@ -149,8 +149,8 @@ export class SyncClient {
return this.syncService.checkConnection(); return this.syncService.checkConnection();
} }
public getHistoryEntries(): HistoryEntry[] { public getHistoryEntries(): readonly HistoryEntry[] {
return this.history.getEntries(); return this.history.entries;
} }
public addSyncHistoryUpdateListener( public addSyncHistoryUpdateListener(

View file

@ -28,8 +28,9 @@ export interface HistoryStats {
export class SyncHistory { export class SyncHistory {
private static readonly MAX_ENTRIES = 500; 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: (( private readonly syncHistoryUpdateListeners: ((
status: HistoryStats status: HistoryStats
@ -42,19 +43,35 @@ export class SyncHistory {
public constructor(private readonly logger: Logger) {} public constructor(private readonly logger: Logger) {}
public getEntries(): HistoryEntry[] { public get entries(): readonly HistoryEntry[] {
return [...this.entries]; return this._entries;
} }
public reset(): void { /**
this.entries.length = 0; * Insert the entry at the beginning of the history list. If the entry
this.status = { * already in the list, it will get moved to the beginning and updated.
success: 0, *
error: 0 * 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( public addSyncHistoryUpdateListener(
@ -64,25 +81,35 @@ export class SyncHistory {
listener({ ...this.status }); listener({ ...this.status });
} }
public addHistoryEntry(entry: CommonHistoryEntry): void { public reset(): void {
const historyEntry = { this._entries.length = 0;
...entry, this.status = {
timestamp: new Date() success: 0,
error: 0
}; };
this.syncHistoryUpdateListeners.forEach((listener) => {
listener(this.status);
});
}
const candidate = this.entries.find( private findSimilarRecentEntry(
(e) => e.relativePath === historyEntry.relativePath entry: HistoryEntry
): HistoryEntry | undefined {
const candidate = this._entries.find(
(e) => e.relativePath === entry.relativePath
); );
if ( if (
candidate !== undefined && candidate !== undefined &&
(this.entries.slice(-1)[0] === candidate || (this._entries[0] === candidate ||
candidate.timestamp.getTime() + 10 * 1000 > candidate.timestamp.getTime() +
historyEntry.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) { if (entry.status === SyncStatus.SUCCESS) {
this.status.success++; this.status.success++;
this.logger.info( this.logger.info(
@ -94,13 +121,8 @@ export class SyncHistory {
`Cannot sync file: ${entry.relativePath} - ${entry.message}` `Cannot sync file: ${entry.relativePath} - ${entry.message}`
); );
} }
this.syncHistoryUpdateListeners.forEach((listener) => { this.syncHistoryUpdateListeners.forEach((listener) => {
listener(this.status); listener(this.status);
}); });
if (this.entries.length > SyncHistory.MAX_ENTRIES) {
this.entries.shift();
}
} }
} }