Add skeleton for file event handling

This commit is contained in:
Andras Schmelczer 2024-12-08 10:58:03 +00:00
parent 1420cf104e
commit 8a7cc65e88
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
6 changed files with 259 additions and 4 deletions

View file

@ -0,0 +1,8 @@
import { TAbstractFile } from "obsidian";
export interface FileEventHandler {
onCreate: (path: TAbstractFile) => void;
onDelete: (path: TAbstractFile) => void;
onRename: (path: TAbstractFile, oldPath: string) => void;
onModify: (path: TAbstractFile) => void;
}

View file

@ -0,0 +1,21 @@
import { TAbstractFile } from "obsidian";
import { FileEventHandler } from "./file-event-handler";
import { Logger } from "src/logger";
export class SyncEventHandler implements FileEventHandler {
onCreate(path: TAbstractFile) {
Logger.getInstance().info(`File created: ${path}`);
}
onDelete(path: TAbstractFile) {
Logger.getInstance().info(`File deleted: ${path}`);
}
onRename(path: TAbstractFile, oldPath: string) {
Logger.getInstance().info(`File renamed: ${oldPath} -> ${path}`);
}
onModify(path: TAbstractFile) {
Logger.getInstance().info(`File modified: ${path}`);
}
}

68
plugin/src/logger.ts Normal file
View file

@ -0,0 +1,68 @@
enum LogLevel {
INFO,
WARNING,
ERROR,
}
class LogLine {
constructor(public level: LogLevel, public message: string) {}
public toString(): string {
return `${this.formatLevel()}: ${this.message}`;
}
private formatLevel(): string {
switch (this.level) {
case LogLevel.INFO:
return "INFO";
case LogLevel.WARNING:
return "WARNING";
case LogLevel.ERROR:
return "ERROR";
default:
return "UNKNOWN";
}
}
}
export class Logger {
private static readonly MAX_MESSAGES = 1000;
private static instance: Logger;
private messages: LogLine[] = [];
private constructor() {}
static getInstance(): Logger {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
public info(message: string): void {
this.pushMessage(message, LogLevel.INFO);
console.log(message);
}
public warn(message: string): void {
this.pushMessage(message, LogLevel.WARNING);
console.warn(message);
}
public error(message: string): void {
this.pushMessage(message, LogLevel.ERROR);
console.error(message);
}
public getMessages(): LogLine[] {
return this.messages;
}
private pushMessage(message: string, level: LogLevel): void {
this.messages.push(new LogLine(level, message));
if (this.messages.length > Logger.MAX_MESSAGES) {
this.messages.shift();
}
}
}

119
plugin/src/plugin.ts Normal file
View file

@ -0,0 +1,119 @@
import {
App,
Editor,
MarkdownView,
Modal,
Notice,
Plugin,
PluginSettingTab,
Setting,
WorkspaceLeaf,
} from "obsidian";
import * as plugin from "../../backend/sync_wasm/pkg/sync_wasm.js";
import * as wasmBin from "../../backend/sync_wasm/pkg/sync_wasm_bg.wasm";
import { getSystemErrorName } from "util";
import { SyncSettingsTab } from "./settings/settings-tab.js";
import { SyncView } from "./views/sync-view.js";
import {
DEFAULT_SETTINGS,
SettingsContainer,
SyncSettings,
} from "./settings/settings.js";
import { Logger } from "./logger.js";
import { SyncEventHandler } from "./events/sync-event-handler.js";
export default class SyncPlugin extends Plugin {
async onload() {
Logger.getInstance().info('Starting plugin "Sample Plugin"');
await plugin.default(Promise.resolve((wasmBin as any).default));
const eventHandler = new SyncEventHandler();
[
this.app.vault.on(
"create",
eventHandler.onCreate.bind(eventHandler)
),
this.app.vault.on(
"modify",
eventHandler.onModify.bind(eventHandler)
),
this.app.vault.on(
"delete",
eventHandler.onDelete.bind(eventHandler)
),
this.app.vault.on(
"rename",
eventHandler.onRename.bind(eventHandler)
),
].forEach((event) => this.registerEvent(event));
// This creates an icon in the left ribbon.
const ribbonIconEl = this.addRibbonIcon(
"dice",
"Sample Plugin",
(evt: MouseEvent) => {
// Called when the user clicks the icon.
new Notice("This is a notice!");
}
);
// Perform additional things with the ribbon
ribbonIconEl.addClass("my-plugin-ribbon-class");
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
const statusBarItemEl = this.addStatusBarItem();
statusBarItemEl.setText("Status Bar Text");
// This adds an editor command that can perform some operation on the current editor instance
this.addCommand({
id: "sample-editor-command",
name: "Sample editor command",
editorCallback: (editor: Editor, view: MarkdownView) => {
console.log(editor.getSelection());
editor.replaceSelection("Sample Editor Command");
},
});
const settingsContainer = new SettingsContainer(
this,
await this.loadData()
);
this.addSettingTab(
new SyncSettingsTab(this.app, this, settingsContainer)
);
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
this.registerInterval(
window.setInterval(() => console.log("setInterval"), 5 * 60 * 1000)
);
this.registerView(SyncView.TYPE, (leaf) => new SyncView(leaf));
this.addRibbonIcon("dice", "Activate view", () => {
this.activateView();
});
}
onunload() {}
async activateView() {
const { workspace } = this.app;
let leaf: WorkspaceLeaf | null = null;
const leaves = workspace.getLeavesOfType(SyncView.TYPE);
if (leaves.length > 0) {
// A leaf with our view already exists, use that
leaf = leaves[0];
} else {
// Our view could not be found in the workspace, create a new leaf
// in the right sidebar for it
leaf = workspace.getRightLeaf(false);
await leaf?.setViewState({ type: SyncView.TYPE, active: true });
}
// "Reveal" the leaf in case it is in a collapsed sidebar
workspace.revealLeaf(leaf!);
}
}

View file

@ -19,10 +19,7 @@ export class SettingsContainer {
private onChangeHandlers: Array<(settings: SyncSettings) => void> = [];
public constructor(
private plugin: SyncPlugin,
private loadedSettings: any
) {
public constructor(private plugin: SyncPlugin, loadedSettings: any) {
this._settings = Object.assign({}, DEFAULT_SETTINGS, loadedSettings);
}

View file

@ -0,0 +1,42 @@
import { ItemView, WorkspaceLeaf } from "obsidian";
import { Logger } from "src/logger";
export class SyncView extends ItemView {
public static TYPE = "example-view";
constructor(leaf: WorkspaceLeaf) {
super(leaf);
}
getViewType() {
return SyncView.TYPE;
}
getDisplayText() {
return "Example view";
}
async onOpen() {
const container = this.containerEl.children[1];
container.empty();
container.createEl("h4", { text: "Example view" });
setInterval(() => this.updateView(), 1000);
}
async updateView() {
const container = this.containerEl.children[1];
container.empty();
const messages = Logger.getInstance()
.getMessages()
.map((message) => message.toString())
.join("\n");
container.createEl("pre", { text: messages });
}
async onClose() {
// Nothing to clean up.
}
}