From fe3250587390e98a8463c0ecd7c014c3bbcf2093 Mon Sep 17 00:00:00 2001 From: MassiveBox Date: Wed, 16 Apr 2025 23:56:24 +0200 Subject: [PATCH] Implement analytics --- src/analytics.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/config.ts | 17 +++++++++++++---- src/index.ts | 36 ++++++++++++++++++++++++++++++++---- 3 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 src/analytics.ts diff --git a/src/analytics.ts b/src/analytics.ts new file mode 100644 index 0000000..13d37be --- /dev/null +++ b/src/analytics.ts @@ -0,0 +1,46 @@ +import {getBackend, getFrontend} from "siyuan"; +import {JSON_MIME} from "@/const"; +import packageJson from '../package.json' assert { type: 'json' }; + +export class Analytics { + + private readonly enabled: boolean; + + private static readonly ENDPOINT = 'https://stats.massive.box/api/send_noua'; + private static readonly WEBSITE_ID = '0a1ebbc1-d702-4f64-86ed-f62dcde9b522'; + + constructor(enabled: boolean) { + this.enabled = enabled; + } + + async sendEvent(name: string) { + + if(!this.enabled) return; + + const sendData = (name == 'load' || name == 'install') ? + { + 'appVersion': window.navigator.userAgent.split(' ')[0], + 'pluginVersion': packageJson.version, + 'frontend': getFrontend(), + 'backend': getBackend(), + 'language': navigator.language, + } : {}; + + await fetch(Analytics.ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': JSON_MIME, + }, + body: JSON.stringify({ + type: 'event', + payload: { + website: Analytics.WEBSITE_ID, + name: name, + data: sendData, + }, + }) + }) + + } + +} \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index 8b293db..b744fb4 100644 --- a/src/config.ts +++ b/src/config.ts @@ -5,7 +5,7 @@ import {SettingUtils} from "@/libs/setting-utils"; import {validateColor} from "@/helper"; type Options = { - autoResize: boolean, + autoResize: boolean background: string analytics: boolean }; @@ -15,12 +15,16 @@ export class PluginConfig { private file: PluginFile; options: Options; + private firstRun: boolean; + + getFirstRun() { return this.firstRun } constructor() { this.file = new PluginFile(STORAGE_PATH, CONFIG_FILENAME, JSON_MIME); } async load() { + this.firstRun = false; await this.file.loadFromSiYuanFS(); this.options = JSON.parse(this.file.getContent()); if(this.options == null) { @@ -32,8 +36,9 @@ export class PluginConfig { this.options = { autoResize: true, background: "#000000", - analytics: true + analytics: true, }; + this.firstRun = true; } async save() { @@ -69,7 +74,11 @@ export class PluginConfigViewer { this.settingUtils = new SettingUtils({ plugin: this.plugin, callback: async (data) => { - this.config.setConfig(data); + this.config.setConfig({ + analytics: data.analytics, + autoResize: data.autoResize, + background: data.background, + }); await this.config.save(); } }); @@ -95,7 +104,7 @@ export class PluginConfigViewer { title: "Analytics", description: ` Enable to send anonymous usage data to the developer. - Privacy + Privacy `, value: this.config.options.analytics, type: 'checkbox' diff --git a/src/index.ts b/src/index.ts index 78735ce..2bd05c8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,10 +9,12 @@ import { import {migrate} from "@/migration"; import {EditorManager, PluginEditor} from "@/editor"; import {PluginConfig, PluginConfigViewer} from "@/config"; +import {Analytics} from "@/analytics"; export default class DrawJSPlugin extends Plugin { config: PluginConfig; + analytics: Analytics; async onload() { @@ -20,16 +22,15 @@ export default class DrawJSPlugin extends Plugin { EditorManager.registerTab(this); migrate() - this.config = new PluginConfig(); - await this.config.load(); - let configViewer = new PluginConfigViewer(this.config, this); - await configViewer.load(); + await this.startConfig(); + await this.startAnalytics(); this.protyleSlash = [{ id: "insert-drawing", filter: ["Insert Drawing", "Add drawing", "whiteboard", "freehand", "graphics", "jsdraw"], html: getMenuHTML("iconDraw", this.i18n.insertDrawing), callback: (protyle: Protyle) => { + void this.analytics.sendEvent('create'); const fileID = generateRandomString(); const syncID = generateTimeString() + '-' + generateRandomString(); protyle.insert(getMarkdownBlock(fileID, syncID), true, false); @@ -44,6 +45,7 @@ export default class DrawJSPlugin extends Plugin { icon: "iconDraw", label: "Edit with js-draw", click: () => { + void this.analytics.sendEvent('edit'); new EditorManager(new PluginEditor(ids.fileID, ids.syncID)).open(this) } }) @@ -51,4 +53,30 @@ export default class DrawJSPlugin extends Plugin { } + onunload() { + void this.analytics.sendEvent("unload"); + } + + uninstall() { + void this.analytics.sendEvent("uninstall"); + } + + private async startConfig() { + this.config = new PluginConfig(); + await this.config.load(); + let configViewer = new PluginConfigViewer(this.config, this); + await configViewer.load(); + } + + private async startAnalytics() { + this.analytics = new Analytics(this.config.options.analytics); + if(this.config.getFirstRun()) { + await this.config.save(); + void this.analytics.sendEvent('install'); + }else{ + void this.analytics.sendEvent('load'); + } + } + + } \ No newline at end of file