Add some classes to handle file events

This commit is contained in:
Silvano Cerza 2025-01-08 19:42:34 +01:00
parent 3fcd4ea56a
commit 6a73dd02e6
3 changed files with 148 additions and 0 deletions

97
src/events/handler.ts Normal file
View file

@ -0,0 +1,97 @@
import { Vault, TAbstractFile } from "obsidian";
import { Event } from "./types";
import MetadataStore from "../metadata-store";
import EventsQueue from "./queue";
/**
* Tracks changes to local sync directory and updates files metadata.
*/
export default class EventsHandler {
private eventsQueue: EventsQueue = new EventsQueue();
constructor(
private vault: Vault,
private metadataStore: MetadataStore,
private localContentDir: string,
private repoContentDir: string,
) {
this.vault.on("create", this.onCreate.bind(this));
this.vault.on("delete", this.onDelete.bind(this));
this.vault.on("modify", this.onModify.bind(this));
this.vault.on("rename", this.onRename.bind(this));
}
private async onCreate(file: TAbstractFile) {
if (!file.path.startsWith(this.localContentDir)) {
// The file has not been created in directory that we're syncing with GitHub
return;
}
this.metadataStore.data[file.path] = {
localPath: file.path,
remotePath: file.path.replace(this.localContentDir, this.repoContentDir),
sha: null,
dirty: true,
};
await this.metadataStore.save();
this.eventsQueue.enqueue({
type: "create",
filePath: file.path,
});
}
private async onDelete(file: TAbstractFile) {
if (!file.path.startsWith(this.localContentDir)) {
// The file was not in directory that we're syncing with GitHub
return;
}
delete this.metadataStore.data[file.path];
await this.metadataStore.save();
this.eventsQueue.enqueue({
type: "delete",
filePath: file.path,
});
}
private async onModify(file: TAbstractFile) {
if (!file.path.startsWith(this.localContentDir)) {
// The file has not been create in directory that we're syncing with GitHub
return;
}
this.metadataStore.data[file.path].dirty = true;
await this.metadataStore.save();
this.eventsQueue.enqueue({
type: "modify",
filePath: file.path,
});
}
private async onRename(file: TAbstractFile, oldPath: string) {
if (
!file.path.startsWith(this.localContentDir) &&
!oldPath.startsWith(this.localContentDir)
) {
// Both are not in directory that we're syncing with GitHub
return;
}
if (
file.path.startsWith(this.localContentDir) &&
oldPath.startsWith(this.localContentDir)
) {
// Both files are in the synced directory
// First create the new one
await this.onCreate(file);
// Then delete the old one
await this.onDelete(file);
return;
} else if (file.path.startsWith(this.localContentDir)) {
// Only the new file is in the local directory
await this.onCreate(file);
return;
} else if (oldPath.startsWith(this.localContentDir)) {
// Only the old file was in the local directory
await this.onDelete(file);
return;
}
}
}

47
src/events/queue.ts Normal file
View file

@ -0,0 +1,47 @@
import { Event } from "./types";
/**
* A custom queue to better handle events.
*/
export default class EventsQueue {
private eventsQueue: Map<string, Event> = new Map();
/**
* Enqueues an event in the queue.
* Handles special cases when the previous event and the new one
* would cancel themselves out.
*/
enqueue(event: Event) {
if (!this.eventsQueue.has(event.filePath)) {
// No other event exist for this file, just enqueue it
this.eventsQueue.set(event.filePath, event);
return;
}
if (
this.eventsQueue.get(event.filePath)?.type === "create" &&
event.type === "delete"
) {
// The previous event was a create and the new one is a delete.
// Just delete the previous one as they would amount to the same outcome.
this.eventsQueue.delete(event.filePath);
} else if (
this.eventsQueue.get(event.filePath)?.type === "delete" &&
event.type === "create"
) {
// The old event was a delete and the new one is a create.
// Delete the old one and enqueue a modify event as it likely
// that the content changed.
this.eventsQueue.delete(event.filePath);
this.eventsQueue.set(event.filePath, {
type: "modify",
filePath: event.filePath,
});
} else {
// Delete and enqueue the event in all other cases.
// We first delete the event to change the order of the queue
this.eventsQueue.delete(event.filePath);
this.eventsQueue.set(event.filePath, event);
}
}
}

4
src/events/types.ts Normal file
View file

@ -0,0 +1,4 @@
export type Event = {
type: "create" | "delete" | "modify";
filePath: string;
};