diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e956b0546..b196f00de 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,6 +25,6 @@ jobs: name: ${{ github.ref_name }} tag_name: ${{ github.ref }} files: | - main.js - manifest.json - styles.css + dist/main.js + dist/manifest.json + dist/styles.css diff --git a/.gitignore b/.gitignore index ea79101bb..ed3a3d38b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,9 +24,6 @@ main.css.map main.js main.js.map -# Ignore only the build styles.css -/styles.css - # obsidian data.json @@ -36,4 +33,4 @@ pnpm-lock.yaml *.log .DS_Store - +dist/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 38c5de545..82e4bde57 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,15 +36,25 @@ Install dependencies bun install ``` -Create a symbolic link from the cloned repository to your Obsidan vault. +Build the project. This will create a `dist` folder + +```shell +bun run build +``` + +Create a symbolic link from the cloned repository to your Obsidan vault. Be sure to link the `dist` folder Note: I recommend making a new Obsidian vault just for development. ```shell -ln -s /.obsidian/plugins +ln -s /dist /.obsidian/plugins/obsidian-dataloom ``` -e.g. `ln -s /users/trey/desktop/obsidian-dataloom /users/trey/desktop/test-vault/.obsidian/plugins` +e.g + +```shell +ln -s /users/trey/desktop/obsidian-dataloom/dist /users/trey/desktop/test-vault/.obsidian/plugins/obsidian-dataloom +``` Checkout the `dev` branch and make a child branch off of it. The branching strategy is `` -> `dev` -> `master`. diff --git a/bun.lockb b/bun.lockb index d809ebedf..cc6e8c5d6 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 9c06b6460..4a05592dc 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -1,70 +1,80 @@ import esbuild from "esbuild"; import process from "process"; import builtins from "builtin-modules"; -import * as fs from "fs"; +import fs from "fs"; +import path from "path"; + +const banner = `/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ +`; const prod = process.argv[2] === "production"; const tools = process.argv[2] === "tools"; -esbuild - .build({ - entryPoints: ["src/main.ts"], - bundle: true, - external: [ - "obsidian", - "electron", - "@codemirror/autocomplete", - "@codemirror/closebrackets", - "@codemirror/collab", - "@codemirror/commands", - "@codemirror/comment", - "@codemirror/fold", - "@codemirror/gutter", - "@codemirror/highlight", - "@codemirror/history", - "@codemirror/language", - "@codemirror/lint", - "@codemirror/matchbrackets", - "@codemirror/panel", - "@codemirror/rangeset", - "@codemirror/rectangular-selection", - "@codemirror/search", - "@codemirror/state", - "@codemirror/stream-parser", - "@codemirror/text", - "@codemirror/tooltip", - "@codemirror/view", - ...builtins, - ], - format: "cjs", - watch: prod - ? false - : { - onRebuild(error) { - if (error) console.error("watch build failed:", error); - else - fs.rename("main.css", "styles.css", (err) => { - if (err) console.error(err); - }); - }, - }, +const rebuildPlugin = { + name: "rebuild-handler", + setup(build) { + build.onEnd(async () => { + await fs.promises.rename("dist/main.css", "dist/styles.css"); + await fs.promises.copyFile( + path.join(path.resolve(), "manifest.json"), + path.join(path.resolve(), "dist", "manifest.json") + ); + }); + }, +}; + +const context = await esbuild.context({ + banner: { + js: banner, + }, + entryPoints: ["src/main.ts"], + bundle: true, + external: [ + "obsidian", + "electron", + "@codemirror/autocomplete", + "@codemirror/closebrackets", + "@codemirror/collab", + "@codemirror/commands", + "@codemirror/comment", + "@codemirror/fold", + "@codemirror/gutter", + "@codemirror/highlight", + "@codemirror/history", + "@codemirror/language", + "@codemirror/lint", + "@codemirror/matchbrackets", + "@codemirror/panel", + "@codemirror/rangeset", + "@codemirror/rectangular-selection", + "@codemirror/search", + "@codemirror/state", + "@codemirror/stream-parser", + "@codemirror/text", + "@codemirror/tooltip", + "@codemirror/view", + ...builtins, + ], + format: "cjs", + target: "es2018", + logLevel: "info", + sourcemap: prod ? false : "inline", + define: { + "process.env.ENABLE_REACT_DEVTOOLS": tools + ? JSON.stringify("true") + : JSON.stringify("false"), + }, + treeShaking: true, + outdir: "dist", + plugins: [rebuildPlugin], +}); - target: "es2016", - logLevel: "info", - sourcemap: !prod, - define: { - "process.env.ENABLE_REACT_DEVTOOLS": tools - ? JSON.stringify("true") - : JSON.stringify("false"), - }, - treeShaking: true, - outfile: "main.js", - }) - .then(() => { - if (prod) { - fs.rename("main.css", "styles.css", (err) => { - if (err) console.error(err); - }); - } - }) - .catch(() => process.exit(1)); +if (prod) { + await context.rebuild(); + process.exit(0); +} else { + await context.watch(); +} diff --git a/manifest.json b/manifest.json index bcd01fc30..8ceb088e4 100644 --- a/manifest.json +++ b/manifest.json @@ -9,5 +9,5 @@ "fundingUrl": { "Buymeacoffee": "https://www.buymeacoffee.com/treywallis" }, - "version": "8.15.10" + "version": "8.15.11" } diff --git a/package.json b/package.json index 8f0beb7eb..2e573c30c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-dataloom", - "version": "8.15.10", + "version": "8.15.11", "description": "Weave together data from diverse sources into different views. Inspired by Excel Spreadsheets and Notion.so.", "main": "main.js", "scripts": { @@ -36,7 +36,7 @@ "@typescript-eslint/parser": "^5.59.8", "babel-jest": "^29.0.2", "builtin-modules": "^3.2.0", - "esbuild": "^0.15.7", + "esbuild": "^0.19.11", "eslint": "^8.41.0", "eslint-config-react-app": "^7.0.1", "eslint-plugin-react-hooks": "^4.6.0", @@ -58,6 +58,7 @@ "dayjs": "^1.11.10", "fuzzysort": "^2.0.4", "htmlparser2": "^9.0.0", + "js-logger": "^1.6.1", "jsondiffpatch": "^0.5.0", "lucide": "^0.241.0", "markdown-it": "^13.0.1", diff --git a/src/main.ts b/src/main.ts index a39653789..cc40cc676 100644 --- a/src/main.ts +++ b/src/main.ts @@ -26,15 +26,18 @@ import { loadPreviewModeApps, purgeEmbeddedLoomApps, } from "./obsidian/embedded/embedded-app-manager"; -import { log } from "./shared/logger"; import FrontmatterCache from "./shared/frontmatter/frontmatter-cache"; import EventManager from "./shared/event/event-manager"; import { getAssignedPropertyType } from "./shared/frontmatter/obsidian-utils"; import { handleFileRename } from "./data/main-utils"; import { getBasename } from "./shared/link-and-path/file-path-utils"; +import Logger from "js-logger"; +import { formatMessageForLogger, stringToLogLevel } from "./shared/logger"; +import { LOG_LEVEL_OFF } from "./shared/logger/constants"; +import LastSavedManager from "./shared/last-saved-manager"; export interface DataLoomSettings { - shouldDebug: boolean; + logLevel: string; createAtObsidianAttachmentFolder: boolean; customFolderForNewFiles: string; removeMarkdownOnExport: boolean; @@ -48,7 +51,7 @@ export interface DataLoomSettings { } export const DEFAULT_SETTINGS: DataLoomSettings = { - shouldDebug: false, + logLevel: LOG_LEVEL_OFF, createAtObsidianAttachmentFolder: false, customFolderForNewFiles: "", removeMarkdownOnExport: true, @@ -61,6 +64,8 @@ export const DEFAULT_SETTINGS: DataLoomSettings = { pluginVersion: "", }; +const FILE_NAME = "main.ts"; + export default class DataLoomPlugin extends Plugin { settings: DataLoomSettings; displayModalsOnLoomOpen: boolean; @@ -72,6 +77,19 @@ export default class DataLoomPlugin extends Plugin { async onload() { await this.loadSettings(); + Logger.useDefaults(); + + Logger.setHandler(function (messages) { + const { message, data } = formatMessageForLogger(...messages); + console.log(message); + if (data) { + console.log(data); + } + }); + + const logLevel = stringToLogLevel(this.settings.logLevel); + Logger.setLevel(logLevel); + this.registerView( DATA_LOOM_VIEW, (leaf) => @@ -96,6 +114,9 @@ export default class DataLoomPlugin extends Plugin { this.registerDOMEvents(); this.app.workspace.onLayoutReady(async () => { + Logger.trace(FILE_NAME, "onLayoutReady", "called"); + Logger.debug(FILE_NAME, "onLayoutReady", "workspace layout is ready"); + const isDark = hasDarkTheme(); store.dispatch(setDarkMode(isDark)); @@ -112,6 +133,7 @@ export default class DataLoomPlugin extends Plugin { this.registerEvent( this.app.vault.on("create", (file: TAbstractFile) => { if (file instanceof TFile) { + Logger.trace(FILE_NAME, "registerEvent", "vault.create event called"); EventManager.getInstance().emit("file-create"); } }) @@ -142,6 +164,7 @@ export default class DataLoomPlugin extends Plugin { //TODO remove this in future versions private async migrateLoomFiles() { if (!this.settings.hasMigratedTo800) { + Logger.info(FILE_NAME, "migrateLoomFiles", "migrating to 8.0.0"); const loomFiles = this.app.vault .getFiles() .filter( @@ -200,7 +223,7 @@ export default class DataLoomPlugin extends Plugin { private registerDOMEvents() { //This event is guaranteed to fire after our React synthetic event handlers this.registerDomEvent(document, "click", () => { - log(this.settings.shouldDebug, "main handleClick"); + Logger.trace(FILE_NAME, "registerDomEvent", "click event called"); EventManager.getInstance().emit("clear-menu-trigger-focus"); EventManager.getInstance().emit("global-click"); @@ -208,7 +231,7 @@ export default class DataLoomPlugin extends Plugin { //This event is guaranteed to fire after our React synthetic event handlers this.registerDomEvent(document, "keydown", (e) => { - log(this.settings.shouldDebug, "main handleKeyDown"); + Logger.trace(FILE_NAME, "registerDomEvent", "keydown event called"); EventManager.getInstance().emit("global-keydown", e); }); } @@ -216,6 +239,7 @@ export default class DataLoomPlugin extends Plugin { private registerEvents() { this.registerEvent( this.app.workspace.on("css-change", () => { + Logger.trace(FILE_NAME, "registerEvent", "css-change event called"); const isDark = hasDarkTheme(); store.dispatch(setDarkMode(isDark)); }) @@ -225,6 +249,7 @@ export default class DataLoomPlugin extends Plugin { //or the user switches between editing and preview mode this.registerEvent( this.app.workspace.on("layout-change", () => { + Logger.trace("main.ts", "registerEvent", "layout-change event called"); const leaves = this.app.workspace.getLeavesOfType("markdown"); purgeEmbeddedLoomApps(leaves); @@ -243,6 +268,7 @@ export default class DataLoomPlugin extends Plugin { this.registerEvent( this.app.workspace.on("file-menu", (menu, file) => { + Logger.trace(FILE_NAME, "registerEvent", "file-menu event called"); if (file instanceof TFolder) { menu.addItem((item) => { item.setTitle("New loom") @@ -257,6 +283,7 @@ export default class DataLoomPlugin extends Plugin { this.registerEvent( this.app.workspace.on("file-open", async (file: TFile | null) => { + Logger.trace(FILE_NAME, "registerEvent", "file-open event called"); if (!file) return; if (!this.displayModalsOnLoomOpen) return; @@ -286,6 +313,7 @@ export default class DataLoomPlugin extends Plugin { this.app.vault.on( "rename", async (file: TAbstractFile, oldPath: string) => { + Logger.trace(FILE_NAME, "registerEvent", "rename event called"); if (file instanceof TFile) { handleFileRename( this.app, @@ -300,8 +328,18 @@ export default class DataLoomPlugin extends Plugin { this.registerEvent( this.app.vault.on("modify", async (file: TAbstractFile) => { + Logger.trace(FILE_NAME, "registerEvent", "vault.modify event called", file); if (file instanceof TFile) { if (file.extension === LOOM_EXTENSION) { + const lastSavedFile = LastSavedManager.getInstance().getLastSavedFile(); + if (lastSavedFile === file.path) { + const now = Date.now(); + const lastTime = LastSavedManager.getInstance().getLastSavedTime(); + if (now - lastTime < 5000) { + Logger.debug(FILE_NAME, "registerEvent", "vault.modify event ignored because it file was saved less than 5 seconds ago"); + return; + } + } EventManager.getInstance().emit("app-refresh-by-file", file, this.manifest.version); } } @@ -317,6 +355,7 @@ export default class DataLoomPlugin extends Plugin { private registerSourceEvents() { this.registerEvent( this.app.vault.on("rename", (file: TAbstractFile) => { + Logger.trace(FILE_NAME, "registerEvent", "vault.rename event called"); if (file instanceof TFile) { EventManager.getInstance().emit("file-rename"); } else { @@ -327,6 +366,7 @@ export default class DataLoomPlugin extends Plugin { this.registerEvent( this.app.vault.on("delete", (file: TAbstractFile) => { + Logger.trace(FILE_NAME, "registerEvent", "vault.delete event called"); if (file instanceof TFile) { EventManager.getInstance().emit("file-delete"); } else { @@ -340,6 +380,7 @@ export default class DataLoomPlugin extends Plugin { (this.app as any).metadataTypeManager.on( "changed", async (propertyName: string) => { + Logger.trace(FILE_NAME, "registerEvent", "metadataTypeManager.changed event called"); const updatedType = getAssignedPropertyType( this.app, propertyName @@ -358,6 +399,7 @@ export default class DataLoomPlugin extends Plugin { "changed", async (file: TAbstractFile) => { if (file instanceof TFile) { + Logger.trace(FILE_NAME, "registerEvent", "metadataCache.changed event called"); //Wait until metadataTypeManager has the updated properties //This is a bug. Bug #1 //TODO tell the Obsidian team @@ -522,6 +564,7 @@ export default class DataLoomPlugin extends Plugin { } async loadSettings() { + Logger.trace(FILE_NAME, "loadSettings", "called"); this.settings = Object.assign( {}, DEFAULT_SETTINGS, @@ -531,6 +574,7 @@ export default class DataLoomPlugin extends Plugin { } async saveSettings() { + Logger.trace(FILE_NAME, "saveSettings", "called"); await this.saveData(this.settings); store.dispatch(setSettings({ ...this.settings })); } diff --git a/src/obsidian/dataloom-settings-tab.ts b/src/obsidian/dataloom-settings-tab.ts index de922d619..e2ed5b32f 100644 --- a/src/obsidian/dataloom-settings-tab.ts +++ b/src/obsidian/dataloom-settings-tab.ts @@ -2,6 +2,9 @@ import { PluginSettingTab, App } from "obsidian"; import { Setting } from "obsidian"; import DataLoomPlugin from "../main"; import { renderBuyMeACoffeeBadge } from "./shared"; +import Logger from "js-logger"; +import { LOG_LEVEL_DEBUG, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_OFF, LOG_LEVEL_TRACE, LOG_LEVEL_WARN } from "src/shared/logger/constants"; +import { stringToLogLevel } from "src/shared/logger"; export default class DataLoomSettingsTab extends PluginSettingTab { plugin: DataLoomPlugin; @@ -208,17 +211,26 @@ export default class DataLoomSettingsTab extends PluginSettingTab { } private renderDebugSettings(containerEl: HTMLElement) { - new Setting(containerEl).setName("Debug").setHeading(); + new Setting(containerEl).setName("Debugging").setHeading(); new Setting(containerEl) - .setName("Debug mode") + .setName("Log level") .setDesc( - "Turns on console.log for plugin events. This is useful for troubleshooting." + "Sets the log level. Please use trace to see all log messages." ) - .addToggle((cb) => { - cb.setValue(this.plugin.settings.shouldDebug).onChange( + .addDropdown((cb) => { + cb.addOptions({ + [LOG_LEVEL_OFF]: "Off", + [LOG_LEVEL_ERROR]: "Error", + [LOG_LEVEL_WARN]: "Warn", + [LOG_LEVEL_INFO]: "Info", + [LOG_LEVEL_DEBUG]: "Debug", + [LOG_LEVEL_TRACE]: "Trace" + }) + cb.setValue(this.plugin.settings.logLevel).onChange( async (value) => { - this.plugin.settings.shouldDebug = value; + this.plugin.settings.logLevel = value; await this.plugin.saveSettings(); + Logger.setLevel(stringToLogLevel(value)); } ); }); diff --git a/src/obsidian/dataloom-view.tsx b/src/obsidian/dataloom-view.tsx index 1e29935e0..c69934937 100644 --- a/src/obsidian/dataloom-view.tsx +++ b/src/obsidian/dataloom-view.tsx @@ -10,6 +10,7 @@ import ErrorApp from "src/react/error-app"; import DeserializationError from "src/data/deserialization-error"; import { serializeFrontmatter } from "src/data/serialize-frontmatter"; import EventManager from "src/shared/event/event-manager"; +import LastSavedManager from "src/shared/last-saved-manager"; export const DATA_LOOM_VIEW = "dataloom"; @@ -105,6 +106,8 @@ export default class DataLoomView extends TextFileView { const serialized = serializeState(state); + LastSavedManager.getInstance().setLastSavedFile(this.file.path); + //We need this for when we open a new tab of the same file //so that the data is up to date this.setViewData(serialized, false); diff --git a/src/obsidian/embedded/embedded-app-manager.tsx b/src/obsidian/embedded/embedded-app-manager.tsx index 0c85db8fc..7cb5ff919 100644 --- a/src/obsidian/embedded/embedded-app-manager.tsx +++ b/src/obsidian/embedded/embedded-app-manager.tsx @@ -18,6 +18,7 @@ import ErrorApp from "src/react/error-app"; import DeserializationError from "src/data/deserialization-error"; import { serializeFrontmatter } from "src/data/serialize-frontmatter"; import EventManager from "src/shared/event/event-manager"; +import LastSavedManager from "src/shared/last-saved-manager"; interface EmbeddedApp { id: string; @@ -205,6 +206,8 @@ const handleSave = async ( state: LoomState, shouldSaveFrontmatter: boolean ) => { + LastSavedManager.getInstance().setLastSavedFile(file.path); + if (shouldSaveFrontmatter) { await serializeFrontmatter(app, state); } diff --git a/src/react/loom-app/app/hooks/use-app-events/index.tsx b/src/react/loom-app/app/hooks/use-app-events/index.tsx index 3beba4b14..d7017f90a 100644 --- a/src/react/loom-app/app/hooks/use-app-events/index.tsx +++ b/src/react/loom-app/app/hooks/use-app-events/index.tsx @@ -1,14 +1,13 @@ +import Logger from "js-logger"; import React from "react"; import { useMenuOperations } from "src/react/shared/menu-provider/hooks"; -import { useLogger } from "src/shared/logger"; export const useAppEvents = () => { const { topMenu, onRequestClose, onClearMenuTriggerFocus } = useMenuOperations(); - const logger = useLogger(); const handleClick = React.useCallback( (e: React.MouseEvent) => { - logger("App handleClick"); + Logger.trace("App handleClick"); e.stopPropagation(); if (!topMenu) { @@ -17,7 +16,7 @@ export const useAppEvents = () => { } onRequestClose(topMenu.id, "save-and-close"); }, - [topMenu, logger, onRequestClose, onClearMenuTriggerFocus] + [topMenu, onRequestClose, onClearMenuTriggerFocus] ); return { onClick: handleClick, diff --git a/src/react/loom-app/app/hooks/use-cell/index.ts b/src/react/loom-app/app/hooks/use-cell/index.ts index dfe611290..2fae9053c 100644 --- a/src/react/loom-app/app/hooks/use-cell/index.ts +++ b/src/react/loom-app/app/hooks/use-cell/index.ts @@ -1,16 +1,16 @@ -import { useLogger } from "src/shared/logger"; + import { useLoomState } from "src/react/loom-app/loom-state-provider"; import CellBodyUpdateCommand from "src/shared/loom-state/commands/cell-body-update-command"; import { Cell } from "src/shared/loom-state/types/loom-state"; import React from "react"; +import Logger from "js-logger"; export const useCell = () => { - const logger = useLogger(); const { doCommand } = useLoomState(); const handleCellChange = React.useCallback( (id: string, value: Partial, isPartial = true) => { - logger("handleCellChange", { + Logger.trace("handleCellChange", { id, value, isPartial, @@ -18,7 +18,7 @@ export const useCell = () => { doCommand(new CellBodyUpdateCommand(id, value, isPartial)); }, - [doCommand, logger] + [doCommand] ); return { diff --git a/src/react/loom-app/app/hooks/use-column-events.ts b/src/react/loom-app/app/hooks/use-column-events.ts index 14fc0aa84..df4282743 100644 --- a/src/react/loom-app/app/hooks/use-column-events.ts +++ b/src/react/loom-app/app/hooks/use-column-events.ts @@ -2,27 +2,26 @@ import React from "react"; import ColumnAddCommand from "src/shared/loom-state/commands/column-add-command"; import ColumnDeleteCommand from "src/shared/loom-state/commands/column-delete-command"; import { isEventForThisApp } from "src/shared/event/utils"; -import { useLogger } from "src/shared/logger"; import { useLoomState } from "src/react/loom-app/loom-state-provider"; import { useAppMount } from "../../app-mount-provider"; import EventManager from "src/shared/event/event-manager"; +import Logger from "js-logger"; export const useColumnEvents = () => { const { reactAppId, app } = useAppMount(); const { doCommand } = useLoomState(); - const logger = useLogger(); React.useEffect(() => { function handleColumnAddEvent() { if (isEventForThisApp(reactAppId)) { - logger("handleColumnAddEvent"); + Logger.trace("handleColumnAddEvent"); doCommand(new ColumnAddCommand()); } } function handleColumnDeleteEvent() { if (isEventForThisApp(reactAppId)) { - logger("handleColumnDeleteEvent"); + Logger.trace("handleColumnDeleteEvent"); doCommand(new ColumnDeleteCommand({ last: true })); } } @@ -36,5 +35,5 @@ export const useColumnEvents = () => { handleColumnDeleteEvent ); }; - }, [doCommand, logger, reactAppId, app]); + }, [doCommand, reactAppId, app]); }; diff --git a/src/react/loom-app/app/hooks/use-column/index.ts b/src/react/loom-app/app/hooks/use-column/index.ts index 9ba001d30..c7fec0a99 100644 --- a/src/react/loom-app/app/hooks/use-column/index.ts +++ b/src/react/loom-app/app/hooks/use-column/index.ts @@ -1,5 +1,4 @@ import { CellType, Column } from "src/shared/loom-state/types/loom-state"; -import { useLogger } from "src/shared/logger"; import { useLoomState } from "../../../loom-state-provider"; import ColumnAddCommand from "src/shared/loom-state/commands/column-add-command"; import ColumnDeleteCommand from "src/shared/loom-state/commands/column-delete-command"; @@ -7,25 +6,25 @@ import ColumnUpdateCommand from "src/shared/loom-state/commands/column-update-co import ColumnTypeUpdateCommand from "src/shared/loom-state/commands/column-type-update-command"; import ColumnReorderCommand from "src/shared/loom-state/commands/column-reorder-command"; import React from "react"; +import Logger from "js-logger"; export const useColumn = () => { - const logger = useLogger(); const { doCommand } = useLoomState(); const handleNewColumnClick = React.useCallback(() => { - logger("handleNewColumnClick"); + Logger.trace("handleNewColumnClick"); doCommand(new ColumnAddCommand()); - }, [doCommand, logger]); + }, [doCommand]); const handleColumnTypeChange = React.useCallback( (columnId: string, type: CellType) => { - logger("handleColumnTypeChange", { + Logger.trace("handleColumnTypeChange", { columnId, type, }); doCommand(new ColumnTypeUpdateCommand(columnId, type)); }, - [doCommand, logger] + [doCommand] ); const handleColumnChange = React.useCallback( @@ -37,35 +36,35 @@ export const useColumn = () => { shouldSaveFrontmatter?: boolean; } ) => { - logger("handleColumnChange", { + Logger.trace("handleColumnChange", { columnId, data, options, }); doCommand(new ColumnUpdateCommand(columnId, data, options)); }, - [doCommand, logger] + [doCommand] ); const handleColumnDeleteClick = React.useCallback( (columnId: string) => { - logger("handleColumnDeleteClick", { + Logger.trace("handleColumnDeleteClick", { columnId, }); doCommand(new ColumnDeleteCommand({ id: columnId })); }, - [doCommand, logger] + [doCommand] ); const handleColumnReorder = React.useCallback( (dragId: string, targetId: string) => { - logger("handleColumnReorder", { + Logger.trace("handleColumnReorder", { dragId, targetId, }); doCommand(new ColumnReorderCommand(dragId, targetId)); }, - [doCommand, logger] + [doCommand] ); return { diff --git a/src/react/loom-app/app/hooks/use-filter.ts b/src/react/loom-app/app/hooks/use-filter.ts index f30ca6aee..c2e2a8264 100644 --- a/src/react/loom-app/app/hooks/use-filter.ts +++ b/src/react/loom-app/app/hooks/use-filter.ts @@ -1,13 +1,12 @@ import { Filter } from "src/shared/loom-state/types/loom-state"; -import { useLogger } from "src/shared/logger"; import { filterByFilters } from "../filter-by-filters"; import { useLoomState } from "../../loom-state-provider"; import FilterUpdateCommand from "src/shared/loom-state/commands/filter-update-command"; import FilterAddCommand from "src/shared/loom-state/commands/filter-add-command"; import FilterDeleteCommand from "src/shared/loom-state/commands/filter-delete-command"; +import Logger from "js-logger"; export const useFilter = () => { - const logger = useLogger(); const { doCommand } = useLoomState(); function handleFilterUpdate( @@ -15,17 +14,17 @@ export const useFilter = () => { data: Partial, isPartial?: boolean ) { - logger("handleFilterUpdate", { id, data }); + Logger.trace("handleFilterUpdate", { id, data }); doCommand(new FilterUpdateCommand(id, data, isPartial)); } function handleFilterAdd() { - logger("handleFilterAdd"); + Logger.trace("handleFilterAdd"); doCommand(new FilterAddCommand()); } function handleFilterDelete(id: string) { - logger("handleFilterDelete", { id }); + Logger.trace("handleFilterDelete", { id }); doCommand(new FilterDeleteCommand(id)); } diff --git a/src/react/loom-app/app/hooks/use-focus/index.tsx b/src/react/loom-app/app/hooks/use-focus/index.tsx index 914559824..e7d034673 100644 --- a/src/react/loom-app/app/hooks/use-focus/index.tsx +++ b/src/react/loom-app/app/hooks/use-focus/index.tsx @@ -15,19 +15,18 @@ import { moveMenuFocusDown, moveMenuFocusUp, } from "src/react/loom-app/app/hooks/use-focus/move-focus"; -import { useLogger } from "src/shared/logger"; import { useLoomState } from "src/react/loom-app/loom-state-provider"; import { useAppMount } from "src/react/loom-app/app-mount-provider"; import { useMenuOperations } from "src/react/shared/menu-provider/hooks"; +import Logger from "js-logger"; export default function useFocus() { - const logger = useLogger(); const { reactAppId } = useAppMount(); const { loomState } = useLoomState(); const { topMenu, onClearMenuTriggerFocus } = useMenuOperations(); function handleKeyDown(e: React.KeyboardEvent) { - logger("useFocus handleKeyDown"); + Logger.trace("useFocus handleKeyDown"); if (e.key === "Tab") { onClearMenuTriggerFocus(); diff --git a/src/react/loom-app/app/hooks/use-menu-events.ts b/src/react/loom-app/app/hooks/use-menu-events.ts index 325d65fdc..d3ef09504 100644 --- a/src/react/loom-app/app/hooks/use-menu-events.ts +++ b/src/react/loom-app/app/hooks/use-menu-events.ts @@ -1,10 +1,10 @@ import React from "react"; -import { useLogger } from "src/shared/logger"; import { useAppMount } from "../../app-mount-provider"; import _ from "lodash"; import { useMenuOperations } from "src/react/shared/menu-provider/hooks"; import EventManager from "src/shared/event/event-manager"; +import Logger from "js-logger"; export const useMenuEvents = () => { useCloseOnOutsideClick(); @@ -16,7 +16,6 @@ export const useMenuEvents = () => { const useLockTableScroll = () => { const { reactAppId } = useAppMount(); const { topMenu } = useMenuOperations(); - const logger = useLogger(); const hasLockRef = React.useRef(false); React.useEffect(() => { @@ -35,16 +34,16 @@ const useLockTableScroll = () => { const { parentComponentId } = topMenu; if (!parentComponentId?.includes("cell")) return; - logger("useLockTableScroll cell menu opened. locking table scroll"); + Logger.debug("useLockTableScroll cell menu opened. locking table scroll"); tableContainerEl.style.overflow = "hidden"; } else { hasLockRef.current = false; - logger( + Logger.debug( "useLockTableScroll cell menu closed. unlocking table scroll" ); tableContainerEl.style.overflow = "auto"; } - }, [topMenu, logger, reactAppId]); + }, [topMenu, reactAppId]); }; /** @@ -98,11 +97,9 @@ const useCloseOnOutsideClick = () => { const { app } = useAppMount(); const { onCloseAll } = useMenuOperations(); - const logger = useLogger(); - React.useEffect(() => { function handleGlobalClick() { - logger("handleGlobalClick"); + Logger.trace("handleGlobalClick"); //If the user selected text and then released outside the app //we don't want to close the menu @@ -116,7 +113,7 @@ const useCloseOnOutsideClick = () => { return () => EventManager.getInstance().off("global-click", handleGlobalClick); - }, [app, logger, onCloseAll]); + }, [app, onCloseAll]); }; /** @@ -126,8 +123,6 @@ const useCloseOnObsidianModalOpen = () => { const hasCloseLock = React.useRef(false); const { topMenu, onCloseAll } = useMenuOperations(); - const logger = useLogger(); - React.useEffect(() => { if (!topMenu) hasCloseLock.current = false; }, [topMenu]); @@ -143,7 +138,7 @@ const useCloseOnObsidianModalOpen = () => { for (const entry of entries) { if (entry.target === document.body) { if (hasOpenModal()) { - logger("obsidian modal opened. closing all menus"); + Logger.info("obsidian modal opened. closing all menus"); hasCloseLock.current = true; onCloseAll(); break; @@ -156,5 +151,5 @@ const useCloseOnObsidianModalOpen = () => { observer.observe(document.body, { childList: true }); return () => observer.disconnect(); - }, [logger, onCloseAll]); + }, [onCloseAll]); }; diff --git a/src/react/loom-app/app/hooks/use-row.ts b/src/react/loom-app/app/hooks/use-row.ts index 7dcad4023..dd20228aa 100644 --- a/src/react/loom-app/app/hooks/use-row.ts +++ b/src/react/loom-app/app/hooks/use-row.ts @@ -1,4 +1,3 @@ -import { useLogger } from "src/shared/logger"; import { useLoomState } from "../../loom-state-provider"; import RowAddCommand from "src/shared/loom-state/commands/row-add-command"; import RowDeleteCommand from "src/shared/loom-state/commands/row-delete-command"; @@ -6,59 +5,59 @@ import React from "react"; import RowInsertCommand from "src/shared/loom-state/commands/row-insert-command"; import { confirmSortOrderChange } from "src/shared/sort-utils"; import RowReorderCommand from "src/shared/loom-state/commands/row-reorder-command"; +import Logger from "js-logger"; export const useRow = () => { - const logger = useLogger(); const { doCommand, loomState } = useLoomState(); const handleRowDeleteClick = React.useCallback( (rowId: string) => { - logger("handleRowDeleteClick", { + Logger.trace("handleRowDeleteClick", { rowId, }); doCommand(new RowDeleteCommand({ id: rowId })); }, - [doCommand, logger] + [doCommand] ); const handleNewRowClick = React.useCallback(() => { - logger("handleNewRowClick"); + Logger.trace("handleNewRowClick"); doCommand(new RowAddCommand()); - }, [doCommand, logger]); + }, [doCommand]); const handleRowInsertAboveClick = React.useCallback( (rowId: string) => { - logger("handleRowInsertAboveClick", { + Logger.trace("handleRowInsertAboveClick", { rowId, }); if (confirmSortOrderChange(loomState)) { doCommand(new RowInsertCommand(rowId, "above")); } }, - [doCommand, logger, loomState] + [doCommand, loomState] ); const handleRowInsertBelowClick = React.useCallback( (rowId: string) => { - logger("handleRowInsertBelowClick", { + Logger.trace("handleRowInsertBelowClick", { rowId, }); if (confirmSortOrderChange(loomState)) { doCommand(new RowInsertCommand(rowId, "below")); } }, - [doCommand, logger, loomState] + [doCommand, loomState] ); const handleRowReorder = React.useCallback( (dragId: string, targetId: string) => { - logger("handleRowReorder", { + Logger.trace("handleRowReorder", { dragId, targetId, }); doCommand(new RowReorderCommand(dragId, targetId)); }, - [doCommand, logger] + [doCommand] ); return { diff --git a/src/react/loom-app/app/hooks/use-source/index.ts b/src/react/loom-app/app/hooks/use-source/index.ts index 018d4a5c2..ba3479565 100644 --- a/src/react/loom-app/app/hooks/use-source/index.ts +++ b/src/react/loom-app/app/hooks/use-source/index.ts @@ -1,7 +1,6 @@ import React from "react"; import { Source } from "src/shared/loom-state/types/loom-state"; -import { useLogger } from "src/shared/logger"; import { useLoomState } from "src/react/loom-app/loom-state-provider"; import SourceAddCommand from "src/shared/loom-state/commands/source-add-command"; import SourceDeleteCommand from "src/shared/loom-state/commands/source-delete-command"; @@ -9,28 +8,26 @@ import updateStateFromSources from "src/shared/loom-state/update-state-from-sour import { useAppMount } from "src/react/loom-app/app-mount-provider"; import EventManager from "src/shared/event/event-manager"; import SourceUpdateCommand from "src/shared/loom-state/commands/source-update-command"; +import Logger from "js-logger"; + +const HOOK_NAME = "useSource"; export const useSource = () => { - const logger = useLogger(); const { app } = useAppMount(); const { doCommand, loomState, setLoomState } = useLoomState(); const { sources, columns } = loomState.model; - const frontmatterKeyHash = React.useMemo(() => { - return JSON.stringify(columns.map((column) => column.frontmatterKey)); - }, [columns]); + const frontmatterKeyHash = JSON.stringify(columns.map((column) => column.frontmatterKey)); + const sourcesHash = JSON.stringify(sources); - //TODO fix double update on file modify const updateRowsFromSources = React.useCallback( (fromObsidianEvent = true) => { - logger("updateRowsFromSources called"); + Logger.trace(HOOK_NAME, "updateRowsFromSources", "called"); setLoomState((prevState) => { if (fromObsidianEvent) { if (Date.now() - prevState.time < 1000) { - // console.log( - // "updateRowsFromSources called in the last 1000ms. returning..." - // ); + Logger.trace(HOOK_NAME, "updateRowsFromSource", "event ignored because it was called in the last 1000ms.") return prevState; } } @@ -62,12 +59,12 @@ export const useSource = () => { }; }); }, - [app, setLoomState, logger] + [app, setLoomState] ); React.useEffect(() => { updateRowsFromSources(false); - }, [sources, frontmatterKeyHash, updateRowsFromSources]); + }, [sourcesHash, frontmatterKeyHash, updateRowsFromSources]); React.useEffect(() => { EventManager.getInstance().on("file-create", updateRowsFromSources); @@ -117,17 +114,17 @@ export const useSource = () => { }, [updateRowsFromSources, app]); function handleSourceAdd(source: Source) { - logger("handleSourceAdd"); + Logger.trace("handleSourceAdd"); doCommand(new SourceAddCommand(source)); } function handleSourceDelete(id: string) { - logger("handleSourceDelete", { id }); + Logger.trace("handleSourceDelete", { id }); doCommand(new SourceDeleteCommand(id)); } function handleSourceUpdate(id: string, data: Partial) { - logger("handleSourceUpdate", { + Logger.trace("handleSourceUpdate", { id, data, }); diff --git a/src/react/loom-app/app/hooks/use-table-settings.ts b/src/react/loom-app/app/hooks/use-table-settings.ts index d4a798de5..e96998a78 100644 --- a/src/react/loom-app/app/hooks/use-table-settings.ts +++ b/src/react/loom-app/app/hooks/use-table-settings.ts @@ -1,24 +1,23 @@ -import { useLogger } from "src/shared/logger"; import { useLoomState } from "../../loom-state-provider"; import TableSettingsUpdateCommand from "src/shared/loom-state/commands/table-settings-update-command"; import React from "react"; +import Logger from "js-logger"; export const useTableSettings = () => { - const logger = useLogger(); const { doCommand } = useLoomState(); const handleFrozenColumnsChange = React.useCallback( (numColumns: number) => { - logger("handleFrozenColumnsChange", { numColumns }); + Logger.trace("handleFrozenColumnsChange", { numColumns }); doCommand( new TableSettingsUpdateCommand("numFrozenColumns", numColumns) ); }, - [doCommand, logger] + [doCommand] ); function handleCalculationRowToggle(value: boolean) { - logger("handleCalculationRowToggle"); + Logger.trace("handleCalculationRowToggle"); doCommand(new TableSettingsUpdateCommand("showCalculationRow", value)); } diff --git a/src/react/loom-app/app/hooks/use-tag/index.ts b/src/react/loom-app/app/hooks/use-tag/index.ts index a4fa611da..9c3cb0b37 100644 --- a/src/react/loom-app/app/hooks/use-tag/index.ts +++ b/src/react/loom-app/app/hooks/use-tag/index.ts @@ -1,4 +1,3 @@ -import { useLogger } from "src/shared/logger"; import { Color, Tag } from "src/shared/loom-state/types/loom-state"; import TagDeleteCommand from "src/shared/loom-state/commands/tag-delete-command"; import { useLoomState } from "../../../loom-state-provider"; @@ -8,14 +7,14 @@ import TagCellRemoveCommand from "src/shared/loom-state/commands/tag-cell-remove import TagCellAddCommand from "src/shared/loom-state/commands/tag-cell-add-command"; import TagCellMultipleRemoveCommand from "src/shared/loom-state/commands/tag-cell-multiple-remove-command"; import React from "react"; +import Logger from "js-logger"; export const useTag = () => { const { doCommand } = useLoomState(); - const logger = useLogger(); const handleTagAdd = React.useCallback( (cellId: string, columnId: string, markdown: string, color: Color) => { - logger("handleTagAdd", { + Logger.trace("handleTagAdd", { cellId, columnId, markdown, @@ -23,29 +22,29 @@ export const useTag = () => { }); doCommand(new TagAddCommand(cellId, columnId, markdown, color)); }, - [doCommand, logger] + [doCommand] ); const handleTagCellAdd = React.useCallback( (cellId: string, tagId: string) => { - logger("handleTagCellAdd", { + Logger.trace("handleTagCellAdd", { cellId, tagId, }); doCommand(new TagCellAddCommand(cellId, tagId)); }, - [doCommand, logger] + [doCommand] ); const handleTagCellRemove = React.useCallback( (cellId: string, tagId: string) => { - logger("handleTagCellRemove", { + Logger.trace("handleTagCellRemove", { cellId, tagId, }); doCommand(new TagCellRemoveCommand(cellId, tagId)); }, - [doCommand, logger] + [doCommand] ); const handleTagChange = React.useCallback( @@ -55,36 +54,36 @@ export const useTag = () => { data: Partial, isPartial = true ) => { - logger("handleTagChange", { + Logger.trace("handleTagChange", { columnId, tagId, data, }); doCommand(new TagUpdateCommand(columnId, tagId, data, isPartial)); }, - [doCommand, logger] + [doCommand] ); const handleTagCellMultipleRemove = React.useCallback( (cellId: string, tagIds: string[]) => { - logger("handleTagCellMultipleRemove", { + Logger.trace("handleTagCellMultipleRemove", { cellId, tagIds, }); doCommand(new TagCellMultipleRemoveCommand(cellId, tagIds)); }, - [doCommand, logger] + [doCommand] ); const handleTagDeleteClick = React.useCallback( (columnId: string, tagId: string) => { - logger("handleTagDeleteClick", { + Logger.trace("handleTagDeleteClick", { columnId, tagId, }); doCommand(new TagDeleteCommand(columnId, tagId)); }, - [doCommand, logger] + [doCommand] ); return { diff --git a/src/react/loom-app/app/index.tsx b/src/react/loom-app/app/index.tsx index 8353afadf..1fc2bd04a 100644 --- a/src/react/loom-app/app/index.tsx +++ b/src/react/loom-app/app/index.tsx @@ -26,16 +26,15 @@ import { isWindowsRedoDown, isWindowsUndoDown, } from "src/shared/keyboard-event"; -import { useLogger } from "src/shared/logger"; import { useSource } from "./hooks/use-source"; import "src/react/global.css"; import "./styles.css"; import { useAppEvents } from "./hooks/use-app-events"; import { useMenuEvents } from "./hooks/use-menu-events"; +import Logger from "js-logger"; export default function App() { - const logger = useLogger(); const { reactAppId, isMarkdownView } = useAppMount(); const { loomState, resizingColumnId, searchText, onRedo, onUndo } = @@ -98,7 +97,7 @@ export default function App() { } function handleKeyDown(e: React.KeyboardEvent) { - logger("App handleKeyDown"); + Logger.trace("App handleKeyDown"); //Stop propagation to the global event e.stopPropagation(); if (isWindowsRedoDown(e) || isMacRedoDown(e)) { diff --git a/src/react/loom-app/loom-state-provider/index.tsx b/src/react/loom-app/loom-state-provider/index.tsx index 51e84f6b5..cc0382af1 100644 --- a/src/react/loom-app/loom-state-provider/index.tsx +++ b/src/react/loom-app/loom-state-provider/index.tsx @@ -2,12 +2,12 @@ import React from "react"; import { LoomState } from "src/shared/loom-state/types/loom-state"; import LoomStateCommand from "src/shared/loom-state/commands/loom-state-command"; -import { useLogger } from "src/shared/logger"; import { sortRows } from "src/shared/loom-state/sort-rows"; import { useAppMount } from "src/react/loom-app/app-mount-provider"; import EventManager from "src/shared/event/event-manager"; import { TFile } from "obsidian"; import { deserializeState } from "src/data/serialize-state"; +import Logger from "js-logger"; interface Props { initialState: LoomState; @@ -74,7 +74,6 @@ export default function LoomStateProvider({ ]); const [position, setPosition] = React.useState(0); - const logger = useLogger(); const { reactAppId, loomFile, app } = useAppMount(); const [error, setError] = React.useState(null); @@ -97,10 +96,10 @@ export default function LoomStateProvider({ const { shouldSaveToDisk, state, shouldSaveFrontmatter } = loomState; if (shouldSaveToDisk) { - logger("LoomStateProvider saving state to disk!"); + Logger.info("LoomStateProvider saving state to disk!"); onSaveState(reactAppId, state, shouldSaveFrontmatter); } - }, [logger, reactAppId, loomState, onSaveState]); + }, [reactAppId, loomState, onSaveState]); React.useEffect(() => { function handleRefreshEvent( @@ -165,14 +164,14 @@ export default function LoomStateProvider({ const undo = React.useCallback(() => { if (position > 0) { - logger("handleUndoEvent"); + Logger.trace("handleUndoEvent"); const currentPosition = position - 1; setPosition(currentPosition); const command = history[position]; if (command !== null) { - logger(command.constructor.name + ".undo"); + Logger.trace(command.constructor.name + ".undo"); let newState = command.undo(loomState.state); if (command.shouldSortRows) { newState = sortRows(newState); @@ -185,18 +184,18 @@ export default function LoomStateProvider({ }); } } - }, [position, history, loomState, logger]); + }, [position, history, loomState]); const redo = React.useCallback(() => { if (position < history.length - 1) { - logger("handleRedoEvent"); + Logger.trace("handleRedoEvent"); const currentPosition = position + 1; setPosition(currentPosition); const command = history[currentPosition]; if (command !== null) { - logger(command.constructor.name + ".redo"); + Logger.trace(command.constructor.name + ".redo"); let newState = command.redo(loomState.state); if (command.shouldSortRows) { newState = sortRows(newState); @@ -209,7 +208,7 @@ export default function LoomStateProvider({ }); } } - }, [position, history, loomState, logger]); + }, [position, history, loomState]); const doCommand = React.useCallback( (command: LoomStateCommand) => { diff --git a/src/react/loom-app/text-cell-edit/index.tsx b/src/react/loom-app/text-cell-edit/index.tsx index c801d2112..513b6eed3 100644 --- a/src/react/loom-app/text-cell-edit/index.tsx +++ b/src/react/loom-app/text-cell-edit/index.tsx @@ -13,7 +13,6 @@ import { isSurroundedByDoubleBrackets, removeClosingBracket, } from "./utils"; -import { useLogger } from "src/shared/logger"; import "./styles.css"; import { @@ -29,6 +28,7 @@ import { isMarkdownFile, stripFileExtension, } from "src/shared/link-and-path/file-path-utils"; +import Logger from "js-logger"; interface Props { cellId: string; @@ -55,7 +55,6 @@ export default function TextCellEdit({ null ); const inputRef = React.useRef(null); - const logger = useLogger(); usePlaceCursorAtEnd(inputRef, localValue); @@ -83,15 +82,15 @@ export default function TextCellEdit({ React.useEffect(() => { if (closeRequest !== null) { - logger("TextCellEdit onClose"); + Logger.trace("TextCellEdit onClose"); if (localValue !== value) onChange(localValue); onClose(); } - }, [logger, value, localValue, closeRequest, onClose, onChange]); + }, [value, localValue, closeRequest, onClose, onChange]); function handleKeyDown(e: React.KeyboardEvent) { const el = e.target as HTMLTextAreaElement; - logger("TextCellEdit handleKeyDown"); + Logger.trace("TextCellEdit handleKeyDown"); //Prevent enter from creating a new line //unless shift or alt is pressed @@ -153,7 +152,7 @@ export default function TextCellEdit({ } function handleTextareaChange(e: React.ChangeEvent) { - logger("TextCellEdit handleTextareaChange"); + Logger.trace("TextCellEdit handleTextareaChange"); const inputValue = e.target.value; let newValue = inputValue; diff --git a/src/react/shared/base-menu/index.tsx b/src/react/shared/base-menu/index.tsx index 84ae6bcc7..7e1779b95 100644 --- a/src/react/shared/base-menu/index.tsx +++ b/src/react/shared/base-menu/index.tsx @@ -5,10 +5,10 @@ import { numToPx } from "src/shared/conversion"; import { LoomMenuPosition } from "../menu/types"; -import { useLogger } from "src/shared/logger"; import { useMenuOperations } from "../menu-provider/hooks"; import "./styles.css"; +import Logger from "js-logger"; interface Props { id: string; @@ -39,11 +39,10 @@ const BaseMenu = React.forwardRef( }: Props, ref ) => { - const logger = useLogger(); const { topMenu, onRequestClose, onClose } = useMenuOperations(); function handleClick(e: React.MouseEvent) { - logger("Menu handleClick"); + Logger.trace("Menu handleClick"); //Don't propagate to the app //it will close the menu again e.stopPropagation(); @@ -54,7 +53,7 @@ const BaseMenu = React.forwardRef( } function handleKeyDown(e: React.KeyboardEvent) { - logger("Menu handleKeyDown"); + Logger.trace("Menu handleKeyDown"); if (topMenu === null) return; if (e.key === "Enter") { diff --git a/src/react/shared/button/index.tsx b/src/react/shared/button/index.tsx index 2b8b39103..193016b1b 100644 --- a/src/react/shared/button/index.tsx +++ b/src/react/shared/button/index.tsx @@ -1,10 +1,10 @@ import React from "react"; -import { useLogger } from "src/shared/logger"; import Stack from "../stack"; import { ButtonSize, ButtonVariant } from "./types"; import "./styles.css"; +import Logger from "js-logger"; interface ButtonProps { isDisabled?: boolean; @@ -33,13 +33,12 @@ export default function Button({ onClick, onMouseDown, }: ButtonProps) { - const logger = useLogger(); function handleClick() { onClick?.(); } function handleKeyDown(e: React.KeyboardEvent) { - logger("Button handleKeyDown"); + Logger.trace("Button handleKeyDown"); if (e.key === "Enter") { //Stop click event e.preventDefault(); diff --git a/src/react/shared/menu-provider/index.tsx b/src/react/shared/menu-provider/index.tsx index 5f9eed38a..df661cc76 100644 --- a/src/react/shared/menu-provider/index.tsx +++ b/src/react/shared/menu-provider/index.tsx @@ -9,9 +9,9 @@ import { LoomMenuCloseRequestType, LoomMenuLevel, } from "./types"; -import { useLogger } from "src/shared/logger"; import { getPositionFromEl } from "./utils"; import EventManager from "src/shared/event/event-manager"; +import Logger from "js-logger"; interface ContextProps { topMenu: LoomMenu | null; @@ -60,6 +60,8 @@ interface Props { children: React.ReactNode; } +const CLASS_NAME = "MenuProvider"; + export default function MenuProvider({ children }: Props) { const [openMenus, setOpenMenus] = React.useState([]); const [closeRequests, setCloseRequests] = React.useState< @@ -68,12 +70,10 @@ export default function MenuProvider({ children }: Props) { const [focusedMenuTrigger, setFocusedMenuTrigger] = React.useState(null); - const logger = useLogger(); - const clearMenuTriggerFocus = React.useCallback(() => { - logger("MenuProvider clearMenuTriggerFocus"); + Logger.trace(CLASS_NAME, "clearMenuTriggerFocus", "called"); setFocusedMenuTrigger(null); - }, [logger]); + }, []); React.useEffect(() => { EventManager.getInstance().on( @@ -97,20 +97,33 @@ export default function MenuProvider({ children }: Props) { shouldFocusTriggerOnClose?: boolean; } ) { - logger("MenuProvider handleOpenMenu"); + Logger.trace(CLASS_NAME, "handleOpenMenu", "called"); const { name, shouldRequestOnClose, shouldFocusTriggerOnClose } = options ?? {}; if (!triggerRef.current) { - logger("No trigger ref. Cannot open menu"); + Logger.debug( + CLASS_NAME, + "handleOpenMenu", + "No trigger ref. Cannot open menu" + ); return; } if (!canOpen(level)) { - logger("Level is too low. Cannot open menu"); + Logger.debug( + CLASS_NAME, + "handleOpenMenu", + "Level is too low. Cannot open menu" + ); return; } - logger("MenuProvider opening menu", { level }); + Logger.trace( + CLASS_NAME, + "handleOpenMenu", + "MenuProvider opening menu", + { level } + ); const position = getPositionFromEl(triggerRef.current); const menu = createMenu(parentComponentId, level, position, { @@ -125,7 +138,7 @@ export default function MenuProvider({ children }: Props) { const focusMenuTrigger = React.useCallback( (parentComponentId: string, name?: string) => { - logger("MenuProvider focusMenuTrigger", { + Logger.trace(CLASS_NAME, "focusMenuTrigger", "called", { parentComponentId, name, }); @@ -134,12 +147,12 @@ export default function MenuProvider({ children }: Props) { name, }); }, - [logger] + [] ); const handleClose = React.useCallback( (id: string) => { - logger("MenuProvider onClose"); + Logger.trace(CLASS_NAME, "handleClose", "called", { id }); const menu = openMenus.find((menu) => menu.id === id); if (!menu) throw new Error("Menu not found"); @@ -154,12 +167,12 @@ export default function MenuProvider({ children }: Props) { prevRequests.filter((request) => request.menuId !== id) ); }, - [logger, openMenus, focusMenuTrigger] + [openMenus, focusMenuTrigger] ); const handleRequestClose = React.useCallback( (id: string, type: LoomMenuCloseRequestType) => { - logger("MenuProvider onRequestClose", { type }); + Logger.trace(CLASS_NAME, "onRequestClose", "called", { id, type }); const menu = openMenus.find((menu) => menu.id === id); if (!menu) return; @@ -171,12 +184,15 @@ export default function MenuProvider({ children }: Props) { handleClose(id); } }, - [logger, setCloseRequests, handleClose, openMenus] + [setCloseRequests, handleClose, openMenus] ); const handlePositionUpdate = React.useCallback( (id: string, position: LoomMenuPosition) => { - logger("MenuProvider onPositionUpdate", { id, position }); + Logger.trace(CLASS_NAME, "onPositionUpdate", "called", { + id, + position, + }); setOpenMenus((prevMenus) => { return prevMenus.map((menu) => { if (menu.id === id) { @@ -186,7 +202,7 @@ export default function MenuProvider({ children }: Props) { }); }); }, - [setOpenMenus, logger] + [setOpenMenus] ); function getMenu(parentComponentId: string, name?: string) { @@ -224,7 +240,7 @@ export default function MenuProvider({ children }: Props) { } const handleCloseAll = React.useCallback(() => { - logger("MenuProvider onCloseAll"); + Logger.trace(CLASS_NAME, "handleCloseAll", "called"); setOpenMenus((prevState) => prevState.filter((menu) => menu.shouldRequestOnClose) ); @@ -233,7 +249,7 @@ export default function MenuProvider({ children }: Props) { createCloseRequest(menu.id, "save-and-close") ); }); - }, [openMenus, setOpenMenus, logger]); + }, [openMenus, setOpenMenus]); const getTopMenu = React.useCallback(() => { return openMenus[openMenus.length - 1] ?? null; @@ -250,6 +266,7 @@ export default function MenuProvider({ children }: Props) { ); function handleCloseRequestClear(id: string) { + Logger.trace(CLASS_NAME, "handleCloseRequestClear", "called", { id }); setCloseRequests((prevRequests) => prevRequests.filter((request) => request.menuId !== id) ); diff --git a/src/react/shared/menu-trigger/index.tsx b/src/react/shared/menu-trigger/index.tsx index 16dc0b1ab..222921b06 100644 --- a/src/react/shared/menu-trigger/index.tsx +++ b/src/react/shared/menu-trigger/index.tsx @@ -6,9 +6,9 @@ import { isWindowsRedoDown, isWindowsUndoDown, } from "src/shared/keyboard-event"; -import { useLogger } from "src/shared/logger"; import { useMenuOperations } from "../menu-provider/hooks"; import { LoomMenuLevel } from "../menu-provider/types"; +import Logger from "js-logger"; interface Props { ariaLabel?: string; @@ -43,11 +43,10 @@ const MenuTrigger = React.forwardRef( }: Props, ref ) => { - const logger = useLogger(); const { topMenu, canOpen, onRequestClose } = useMenuOperations(); function handleKeyDown(e: React.KeyboardEvent) { - logger("MenuTrigger handleKeyDown"); + Logger.trace("MenuTrigger handleKeyDown"); if (e.key === "Enter") { e.stopPropagation(); @@ -90,7 +89,7 @@ const MenuTrigger = React.forwardRef( } function handleClick(e: React.MouseEvent) { - logger("MenuTrigger handleClick"); + Logger.trace("MenuTrigger handleClick"); //Don't propagate to the app or global event handlers e.stopPropagation(); onClick?.(e); diff --git a/src/react/shared/suggest-list/index.tsx b/src/react/shared/suggest-list/index.tsx index 080528d87..617622b00 100644 --- a/src/react/shared/suggest-list/index.tsx +++ b/src/react/shared/suggest-list/index.tsx @@ -11,10 +11,10 @@ import CreateButton from "./create-button"; import Divider from "../divider"; import Padding from "../padding"; -import { useLogger } from "src/shared/logger"; import { useAppMount } from "src/react/loom-app/app-mount-provider"; import "./styles.css"; +import Logger from "js-logger"; interface ContentProps { showInput?: boolean; @@ -44,7 +44,6 @@ export function SuggestList({ const [highlightIndex, setHighlightIndex] = React.useState(-1); const { app } = useAppMount(); - const logger = useLogger(); React.useEffect(() => { setLocalFilterValue(filterValue ?? ""); @@ -79,7 +78,7 @@ export function SuggestList({ } function handleKeyDown() { - logger("SuggestMenuContent handleKeyDown"); + Logger.trace("SuggestMenuContent handleKeyDown"); const focusedEl = document.activeElement; if (!focusedEl) return; diff --git a/src/react/shared/suggest-list/suggest-item/index.tsx b/src/react/shared/suggest-list/suggest-item/index.tsx index c6b290d3e..79cf0820f 100644 --- a/src/react/shared/suggest-list/suggest-item/index.tsx +++ b/src/react/shared/suggest-list/suggest-item/index.tsx @@ -4,7 +4,7 @@ import Text from "src/react/shared/text"; import { TFile } from "obsidian"; import "./styles.css"; -import { useLogger } from "src/shared/logger"; +import Logger from "js-logger"; interface Props { index: number; @@ -18,7 +18,6 @@ const SuggestItem = React.forwardRef( { index, file, isHighlighted, onItemClick }: Props, ref ) { - const logger = useLogger(); const handleClick = React.useCallback( (e: React.MouseEvent) => { //Stop propagation so the menu doesn't remove the focus class @@ -29,7 +28,7 @@ const SuggestItem = React.forwardRef( ); function handleKeyDown(e: React.KeyboardEvent) { - logger("SuggestItem handleKeyDown"); + Logger.trace("SuggestItem handleKeyDown"); if (e.key === "Enter") { //Don't insert a new line e.preventDefault(); diff --git a/src/shared/event/event-manager.ts b/src/shared/event/event-manager.ts index c43d195b8..84b0cbdbf 100644 --- a/src/shared/event/event-manager.ts +++ b/src/shared/event/event-manager.ts @@ -1,5 +1,8 @@ +import Logger from "js-logger"; import { DataLoomEvent, EventCallback } from "./types"; +const CLASS_NAME = "EventManager"; + export default class EventManager { private static instance: EventManager; private eventListeners: Record; @@ -39,7 +42,7 @@ export default class EventManager { // Method to trigger all callbacks associated with an event public emit(eventName: DataLoomEvent, ...data: any[]): void { - // console.log("[EventManager] emiting event:", eventName); + Logger.trace(CLASS_NAME, "emit", `emiting ${eventName} event`, data); if (!this.eventListeners[eventName]) { return; } diff --git a/src/shared/last-saved-manager.ts b/src/shared/last-saved-manager.ts new file mode 100644 index 000000000..0ee579f70 --- /dev/null +++ b/src/shared/last-saved-manager.ts @@ -0,0 +1,33 @@ +export default class LastSavedManager { + private static instance: LastSavedManager; + private lastSavedFile: string = ""; + private lastSavedTime: number = 0; + + + // Ensures only one instance is created + public static getInstance(): LastSavedManager { + if (!LastSavedManager.instance) { + LastSavedManager.instance = new LastSavedManager(); + } + return LastSavedManager.instance; + } + + public setLastSavedFile(fileName: string): void { + this.lastSavedFile = fileName; + this.lastSavedTime = Date.now(); + } + + public getLastSavedFile(): string { + return this.lastSavedFile; + } + + public getLastSavedTime(): number { + return this.lastSavedTime; + } + + public clearLastSavedFile(): void { + this.lastSavedFile = ""; + } + + +} diff --git a/src/shared/logger.ts b/src/shared/logger.ts deleted file mode 100644 index e9f6f3493..000000000 --- a/src/shared/logger.ts +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import { useAppSelector } from "src/redux/hooks"; - -export const log = (shouldDebug: boolean, message: string, args = {}) => { - if (shouldDebug) { - console.log(message); - if (Object.keys(args).length !== 0) console.log(args); - } -}; - -export const useLogger = () => { - const { shouldDebug } = useAppSelector((state) => state.global.settings); - - const logger = React.useCallback( - (message: string, args?: Record) => - log(shouldDebug, message, args), - [shouldDebug] - ); - - return logger; -}; diff --git a/src/shared/logger/constants.ts b/src/shared/logger/constants.ts new file mode 100644 index 000000000..fa0732d32 --- /dev/null +++ b/src/shared/logger/constants.ts @@ -0,0 +1,7 @@ +export const LOG_LEVEL_OFF = "off"; +export const LOG_LEVEL_ERROR = "error"; +export const LOG_LEVEL_WARN = "warn"; +export const LOG_LEVEL_INFO = "info"; +export const LOG_LEVEL_DEBUG = "debug"; +export const LOG_LEVEL_TRACE = "trace"; + diff --git a/src/shared/logger/index.ts b/src/shared/logger/index.ts new file mode 100644 index 000000000..d07cb8692 --- /dev/null +++ b/src/shared/logger/index.ts @@ -0,0 +1,61 @@ +import Logger, { ILogLevel } from "js-logger"; +import { LOG_LEVEL_OFF, LOG_LEVEL_ERROR, LOG_LEVEL_WARN, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_TRACE } from "./constants"; +import { FormattedLogMessage } from "./types"; + + +export const logLevelToString = (level: ILogLevel) => { + switch (level) { + case Logger.OFF: + return LOG_LEVEL_OFF; + case Logger.ERROR: + return LOG_LEVEL_ERROR; + case Logger.WARN: + return LOG_LEVEL_WARN; + case Logger.INFO: + return LOG_LEVEL_INFO; + case Logger.DEBUG: + return LOG_LEVEL_DEBUG; + case Logger.TRACE: + return LOG_LEVEL_TRACE; + default: + throw new Error("Unhandled log level"); + } +} + +export const stringToLogLevel = (value: string) => { + switch (value) { + case LOG_LEVEL_OFF: + return Logger.OFF; + case LOG_LEVEL_ERROR: + return Logger.ERROR; + case LOG_LEVEL_WARN: + return Logger.WARN; + case LOG_LEVEL_INFO: + return Logger.INFO; + case LOG_LEVEL_DEBUG: + return Logger.DEBUG; + case LOG_LEVEL_TRACE: + return Logger.TRACE; + default: + throw new Error(`Unhandled log level: ${value}`); + } +} + +export const formatMessageForLogger = (...args: string[]): FormattedLogMessage => { + if (args.length < 3) { + return { message: args[0], data: null }; + } + + const fileName = args[0]; + const functionName = args[1]; + const message = args[2]; + + if (args.length === 4) { + const data = args[3]; + if (Object.keys(data).length !== 0) { + return { message: `[${fileName}:${functionName}] ${message}`, data: data as unknown as Record }; + } + } + + return { message: `[${fileName}:${functionName}] ${message}`, data: null }; +} diff --git a/src/shared/logger/types.ts b/src/shared/logger/types.ts new file mode 100644 index 000000000..c65819620 --- /dev/null +++ b/src/shared/logger/types.ts @@ -0,0 +1,4 @@ +export interface FormattedLogMessage { + message: string; + data: Record | null; +} diff --git a/versions.json b/versions.json index d7f99cac3..2e1e6361d 100644 --- a/versions.json +++ b/versions.json @@ -159,5 +159,6 @@ "8.15.7": "1.4.0", "8.15.8": "1.4.0", "8.15.9": "1.4.0", - "8.15.10": "1.4.0" + "8.15.10": "1.4.0", + "8.15.11": "1.4.0" }