Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"id": "view-count",
"name": "View Count",
"version": "2.3.0",
"version": "2.3.1",
"minAppVersion": "1.4.0",
"description": "Tracks view count for each vault file.",
"description": "Track view count for each vault file.",
"author": "DecafDev",
"authorUrl": "https://github.com/decaf-dev",
"fundingUrl": "https://ko-fi.com/decaf_dev",
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "obsidian-view-count",
"version": "2.3.0",
"description": "Tracks view count for each vault file",
"version": "2.3.1",
"description": "Track view count for each vault file",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
Expand Down
4 changes: 2 additions & 2 deletions src/event/event-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export default class EventManager {

// Method to trigger all callbacks associated with an event
public emit(eventName: PluginEvent, ...data: any[]): void {
Logger.trace("EventManager emit");
Logger.debug("Emiting event", { event: eventName });
Logger.trace({ fileName: "event-manager.ts", functionName: "emit", message: "called" });
Logger.trace({ fileName: "main.ts", functionName: "emit", message: "emiting event" }, { eventName });
if (!this.eventListeners[eventName]) {
return;
}
Expand Down
34 changes: 12 additions & 22 deletions src/logger/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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";

import { FormattedLogMessage, LogMessageHeader } from "./types";

export const logLevelToString = (level: ILogLevel) => {
switch (level) {
Expand Down Expand Up @@ -41,23 +40,14 @@ export const stringToLogLevel = (value: string) => {
}
}

export const formatMessageForLogger = (...args: string[]): FormattedLogMessage => {
const data = args[1];
return { message: args[0], data: data as unknown as Record<string, unknown> };
// 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<string, unknown> };
// }
// }

// return { message: `[${fileName}:${functionName}] ${message}`, data: null };
}
export const formatMessageForLogger = (...args: unknown[]): FormattedLogMessage => {
const head: unknown = args[0];
const body = args[1] as unknown as Record<string, unknown>;
if (typeof args[0] == "object") {
const headers = head as LogMessageHeader;
const { fileName, functionName, message } = headers;
return { message: `[${fileName}:${functionName}] ${message}`, data: body };
} else {
return { message: String(head), data: body };
}
}
6 changes: 6 additions & 0 deletions src/logger/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ export interface FormattedLogMessage {
message: string;
data: Record<string, unknown> | null;
}

export interface LogMessageHeader {
fileName: string;
functionName: string;
message: string;
}
44 changes: 29 additions & 15 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,7 @@ export default class ViewCountPlugin extends Plugin {

async onload() {
await this.loadSettings();

//Setup logger
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.setupLogger();

const viewCountCache = new ViewCountCache(this.app, this.settings);
this.viewCountCache = viewCountCache;
Expand Down Expand Up @@ -132,7 +120,7 @@ export default class ViewCountPlugin extends Plugin {
const viewType = leaf.view.getViewType();

if (viewType !== "markdown" && viewType !== "image" && viewType !== "pdf" && viewType != "dataloom" && viewType != "audio" && viewType != "video") {
Logger.debug("View count not supported for view type", { viewType });
Logger.debug({ fileName: "main.ts", functionName: "active-leaf-change", message: "view count not supported for view type" }, { viewType });
this.viewCountStatusBarItem?.setText("");
return;
} else {
Expand Down Expand Up @@ -168,7 +156,7 @@ export default class ViewCountPlugin extends Plugin {


private async handleFileOpen(file: TFile) {
Logger.trace("main handleFileOpen");
Logger.trace({ fileName: "main.ts", functionName: "handleFileOpen", message: "called" });
if (this.viewCountCache === null) {
throw new Error("View count cache is null");
}
Expand All @@ -184,4 +172,30 @@ export default class ViewCountPlugin extends Plugin {
const viewName = viewCount === 1 ? "view" : "views";
this.viewCountStatusBarItem.setText(`${viewCount} ${viewName}`);
}

private setupLogger() {
Logger.useDefaults();
Logger.setHandler(function (messages, context) {
const { message, data } = formatMessageForLogger(...messages);
if (context.level === Logger.WARN) {
console.warn(message);
if (data) {
console.warn(data);
}
} else if (context.level === Logger.ERROR) {
console.error(message);
if (data) {
console.error(data);
}
} else {
console.log(message);
if (data) {
console.log(data);
}
}
});

const logLevel = stringToLogLevel(this.settings.logLevel);
Logger.setLevel(logLevel);
}
}
60 changes: 33 additions & 27 deletions src/storage/view-count-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@ export default class ViewCountCache {
}

async load() {
Logger.trace("ViewCountCache load");
Logger.trace({ fileName: "view-count-cache.ts", functionName: "load", message: "called" });
const path = getFilePath(this.app);
const exists = await this.app.vault.adapter.exists(path);

if (!exists) {
const data = stringifyEntries([]);
try {
Logger.debug("Creating new cache...");
Logger.debug({ fileName: "view-count-cache.ts", functionName: "load", message: "creating new cache..." });
await this.app.vault.create(path, data);
} catch (err) {
Logger.error("Error creating cache: ", (err as Error).message);
const error = err as Error;
Logger.error({ fileName: "view-count-cache.ts", functionName: "load", message: "error creating cache" }, { error: error.message });
new Notice("View Count: error creating cache");
}
return;
Expand All @@ -40,31 +41,33 @@ export default class ViewCountCache {
try {
const result = await this.app.vault.adapter.read(path);
this.entries = parseEntries(result);
Logger.debug("Loaded view count entries", { entries: this.entries });
Logger.debug({ fileName: "view-count-cache.ts", functionName: "load", message: "loaded view count entries" }, { entries: this.entries });
this.refresh();
} catch (err) {
Logger.error("Error loading cache: ", (err as Error).message);
const error = err as Error;
Logger.error({ fileName: "view-count-cache.ts", functionName: "load", message: "error loading cache" }, { error: error.message });
new Notice("View Count: error loading cache");
}
}

async save() {
Logger.trace("ViewCountCache save");
Logger.trace({ fileName: "view-count-cache.ts", functionName: "save", message: "called" });
try {
const path = getFilePath(this.app);
const data = stringifyEntries(this.entries);
await this.app.vault.adapter.write(path, data);
} catch (err) {
Logger.error("Error saving file cache: ", (err as Error).message);
const error = err as Error;
Logger.error({ fileName: "view-count-cache.ts", functionName: "save", message: "error saving file cache" }, { error: error.message });
new Notice("View Count: error saving file cache");
}
}

async handleFileOpen(file: TFile) {
Logger.trace("ViewCountCache handleFileOpen");
Logger.trace({ fileName: "view-count-cache.ts", functionName: "handleFileOpen", message: "called" });
const { excludedPaths, templaterDelay, saveViewCountToFrontmatter } = this.settings;
if (!shouldTrackFile(file, excludedPaths)) {
Logger.debug(`File ${file.path} is set to be excluded. Returning.`);
Logger.debug({ fileName: "view-count-cache.ts", functionName: "handeFileOpen", message: `file "${file.path}" is set to be excluded. returning...` });
return;
}

Expand All @@ -79,7 +82,7 @@ export default class ViewCountCache {
//If we're creating a new file and the templater delay is greater than 0, wait before updating the view count property in frontmatter
//This is to prevent the view count from overwriting the templater output
if (!entry && templaterDelay > 0) {
Logger.debug(`Templater delay is greater than 0. Waiting ${templaterDelay}ms before incrementing the view count.`);
Logger.debug({ fileName: "view-count-cache.ts", functionName: "handleFileOpen", message: `templater delay is greater than 0. Waiting ${templaterDelay}ms before incrementing the view count.` });
await new Promise(resolve => setTimeout(resolve, templaterDelay));
}
await this.updateViewCountProperty(file);
Expand Down Expand Up @@ -173,8 +176,9 @@ export default class ViewCountCache {
}

async renameEntry(newPath: string, oldPath: string) {
Logger.trace("ViewCountCache renameEntry");
Logger.debug("Renaming entry", { oldPath, newPath });
Logger.trace({ fileName: "view-count-cache.ts", functionName: "renameEntry", message: "called" });
Logger.debug({ fileName: "view-count-cache.ts", functionName: "renameEntry", message: "called" }, { oldPath, newPath });

this.entries = this.entries.map((entry) => {
if (entry.path === oldPath) {
entry.path = newPath;
Expand All @@ -187,8 +191,8 @@ export default class ViewCountCache {
}

async deleteEntry(file: TFile) {
Logger.trace("ViewCountCache deleteEntry");
Logger.debug("Deleting entry", { path: file.path });
Logger.trace({ fileName: "view-count-cache.ts", functionName: "deleteEntry", message: "called" });
Logger.debug({ fileName: "view-count-cache.ts", functionName: "deleteEntry", message: "deleting entry" }, { path: file.path });
this.entries = this.entries.filter((entry) => entry.path !== file.path);

this.debounceSave();
Expand All @@ -214,7 +218,7 @@ export default class ViewCountCache {
}

async syncViewCountToFrontmatter() {
Logger.trace("ViewCountCache syncViewCountToFrontmatter");
Logger.trace({ fileName: "view-count-cache.ts", functionName: "syncViewCountToFrontmatter", message: "called" });

const { saveViewCountToFrontmatter } = this.settings;
for (const entry of this.entries) {
Expand All @@ -231,7 +235,7 @@ export default class ViewCountCache {
}

private async updateViewCountProperty(file: TFile) {
Logger.trace("ViewCountCache updateViewCountPropertyInFrontmatter");
Logger.trace({ fileName: "view-count-cache.ts", functionName: "updateViewCountProperty", message: "called" });
const { viewCountPropertyName, viewCountType } = this.settings;

const entry = this.entries.find((entry) => entry.path === file.path);
Expand All @@ -241,28 +245,28 @@ export default class ViewCountCache {

await this.app.fileManager.processFrontMatter(file, (frontmatter) => {
if (viewCountType === "unique-days-opened") {
Logger.debug("Updating view count property in frontmatter", { path: file.path, viewCountPropertyName, viewCount: entry.uniqueDaysOpened, type: "unique-days-opened" });
Logger.debug({ fileName: "view-count-cache.ts", functionName: "updateViewCountProperty", message: "ipdating view count property in frontmatter" }, { path: file.path, viewCountPropertyName, viewCount: entry.uniqueDaysOpened, type: "unique-days-opened" });
frontmatter[viewCountPropertyName] = entry.uniqueDaysOpened;
} else {
Logger.debug("Updating view count property in frontmatter", { path: file.path, viewCountPropertyName, viewCount: entry.totalTimesOpened, type: "total-times-opened" });
Logger.debug({ fileName: "view-count-cache.ts", functionName: "updateViewCountProperty", message: "ipdating view count property in frontmatter" }, { path: file.path, viewCountPropertyName, viewCount: entry.totalTimesOpened, type: "total-times-opened" });
frontmatter[viewCountPropertyName] = entry.totalTimesOpened;
}
});
}

private async deleteViewCountProperty(file: TFile) {
Logger.trace("ViewCountCache deleteViewCountPropertyInFrontmatter");
Logger.trace({ fileName: "view-count-cache.ts", functionName: "deleteViewCountProperty", message: "called" });
const { viewCountPropertyName } = this.settings;

Logger.debug("Deleting view count property in frontmatter", { path: file.path, viewCountPropertyName });
Logger.debug({ fileName: "view-count-cache.ts", functionName: "deleteViewCountProperty", message: "deleting view count property in frontmatter" }, { path: file.path, viewCountPropertyName });
await this.app.fileManager.processFrontMatter(file, (frontmatter) => {
frontmatter[viewCountPropertyName] = undefined;
});
}

private async createEntry(file: TFile) {
Logger.trace("ViewCountCache createEntry");
Logger.debug("Creating new entry", { path: file.path });
Logger.trace({ fileName: "view-count-cache.ts", functionName: "createEntry", message: "called" });
Logger.debug({ fileName: "view-count-cache.ts", functionName: "createEntry", message: "creating new entry" }, { path: file.path });

const updatedEntries = [...this.entries, {
path: file.path,
Expand All @@ -274,12 +278,13 @@ export default class ViewCountCache {
}

private async addOpenLogEntry(targetPath: string) {
Logger.trace("ViewCountCache addOpenLogEntry");
Logger.debug("Adding to open log", { path: targetPath });
Logger.trace({ fileName: "view-count-cache.ts", functionName: "addOpenLogEntry", message: "called" });
Logger.trace({ fileName: "view-count-cache.ts", functionName: "addOpenLogEntry", message: "adding to open log" }, { targetPath });

const updatedEntries = this.entries.map((entry) => {
if (entry.path === targetPath) {
//Keep 31 days of logs. This will support both 30-days and month durations
//Keep 31 days of logs.
//This will support both 30-days and month durations
const start31DaysAgoMillis = getStartOf31DaysAgoMillis();
const filteredLogs = entry.openLogs.filter((log) => log.timestampMillis >= start31DaysAgoMillis);

Expand All @@ -296,8 +301,8 @@ export default class ViewCountCache {
}

private async incrementViewCount(targetPath: string) {
Logger.trace("ViewCountCache incrementViewCount");
Logger.debug("Incrementing view count", { path: targetPath });
Logger.trace({ fileName: "view-count-cache.ts", functionName: "incrementViewCount", message: "called" });
Logger.debug({ fileName: "view-count-cache.ts", functionName: "incrementViewCount", message: "incrementing view count" }, { targetPath });

const updatedEntries = this.entries.map((entry) => {
if (entry.path === targetPath) {
Expand All @@ -324,6 +329,7 @@ export default class ViewCountCache {


private refresh() {
Logger.trace({ fileName: "view-count-cache.ts", functionName: "refresh", message: "called" });
EventManager.getInstance().emit("refresh-item-view");
}
}
9 changes: 4 additions & 5 deletions src/svelte/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
updateTrendingItems();
});

//TODO add a timer to update trending items?
onMount(() => {
//TODO optimize event. Don't update all items?
function handleRefreshEvent() {
Expand Down Expand Up @@ -64,7 +63,7 @@
return { file, viewCount };
});
items = items.slice(0, listSize);
return items;
mostViewedRenderItems = items;
}

function updateTrendingItems() {
Expand All @@ -91,7 +90,7 @@
items.sort((a, b) => b.timesOpened - a.timesOpened);
items = items.filter((item) => item.timesOpened > 0);
items = items.slice(0, listSize);
return items;
trendingRenderItems = items;
}

function handleMostViewedClick() {
Expand Down Expand Up @@ -195,11 +194,11 @@
}

$: if (duration || listSize) {
trendingRenderItems = updateTrendingItems();
updateTrendingItems();
}

$: if (listSize) {
mostViewedRenderItems = updateMostViewedItems();
updateMostViewedItems();
}
</script>

Expand Down
3 changes: 2 additions & 1 deletion versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@
"2.1.0": "1.4.0",
"2.2.0": "1.4.0",
"2.2.1": "1.4.0",
"2.3.0": "1.4.0"
"2.3.0": "1.4.0",
"2.3.1": "1.4.0"
}