diff --git a/apps/roam/src/components/settings/HomePersonalSettings.tsx b/apps/roam/src/components/settings/HomePersonalSettings.tsx index 407e38667..726c630dc 100644 --- a/apps/roam/src/components/settings/HomePersonalSettings.tsx +++ b/apps/roam/src/components/settings/HomePersonalSettings.tsx @@ -20,7 +20,10 @@ import { DISCOURSE_CONTEXT_OVERLAY_IN_CANVAS_KEY, DISCOURSE_TOOL_SHORTCUT_KEY, STREAMLINE_STYLING_KEY, + DISALLOW_TRACKING, } from "~/data/userSettings"; +import { enablePostHog, disablePostHog } from "~/utils/posthog"; +import internalError from "~/utils/internalError"; import KeyboardShortcutInput from "./KeyboardShortcutInput"; import { getSetting, setSetting } from "~/utils/extensionSettings"; import streamlineStyling from "~/styles/streamlineStyling"; @@ -263,6 +266,38 @@ const HomePersonalSettings = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => { } /> + { + const target = e.target as HTMLInputElement; + const disallow = target.checked; + void setSetting(DISALLOW_TRACKING, disallow) + .then(() => { + if (disallow) { + disablePostHog(); + } else { + enablePostHog(); + } + }) + .catch((error) => { + target.checked = !disallow; + internalError({ + error, + userMessage: "Could not change settings", + }); + }); + }} + labelElement={ + <> + Disable tracking + + + } + /> ); }; diff --git a/apps/roam/src/data/userSettings.ts b/apps/roam/src/data/userSettings.ts index 24ea7e0ce..e67c9d93a 100644 --- a/apps/roam/src/data/userSettings.ts +++ b/apps/roam/src/data/userSettings.ts @@ -8,3 +8,4 @@ export const DISCOURSE_TOOL_SHORTCUT_KEY = "discourse-tool-shortcut"; export const DISCOURSE_CONTEXT_OVERLAY_IN_CANVAS_KEY = "discourse-context-overlay-in-canvas"; export const STREAMLINE_STYLING_KEY = "streamline-styling"; +export const DISALLOW_TRACKING = "disallow-tracking"; diff --git a/apps/roam/src/index.ts b/apps/roam/src/index.ts index dfe82b2ae..992c2718e 100644 --- a/apps/roam/src/index.ts +++ b/apps/roam/src/index.ts @@ -1,6 +1,5 @@ import { addStyle } from "roamjs-components/dom"; import { render as renderToast } from "roamjs-components/components/Toast"; -import getCurrentUserUid from "roamjs-components/queries/getCurrentUserUid"; import { runExtension } from "roamjs-components/util"; import { queryBuilderLoadedToast } from "./data/toastMessages"; import runQuery from "./utils/runQuery"; @@ -21,7 +20,6 @@ import discourseFloatingMenuStyles from "./styles/discourseFloatingMenuStyles.cs import settingsStyles from "./styles/settingsStyles.css"; import discourseGraphStyles from "./styles/discourseGraphStyles.css"; import streamlineStyling from "./styles/streamlineStyling"; -import posthog from "posthog-js"; import getDiscourseNodes from "./utils/getDiscourseNodes"; import { initFeedbackWidget } from "./components/BirdEatsBugs"; import { @@ -38,53 +36,16 @@ import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByPar import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle"; import { DISCOURSE_CONFIG_PAGE_TITLE } from "./utils/renderNodeConfigPage"; import { getSetting } from "./utils/extensionSettings"; -import { STREAMLINE_STYLING_KEY } from "./data/userSettings"; -import { getVersionWithDate } from "~/utils/getVersion"; - -const initPostHog = () => { - posthog.init("phc_SNMmBqwNfcEpNduQ41dBUjtGNEUEKAy6jTn63Fzsrax", { - api_host: "https://us.i.posthog.com", - person_profiles: "identified_only", - capture_pageview: false, - autocapture: false, - loaded: (posthog) => { - const { version, buildDate } = getVersionWithDate(); - const userUid = getCurrentUserUid(); - const graphName = window.roamAlphaAPI.graph.name; - posthog.identify(userUid, { - graphName, - }); - posthog.register({ - version: version || "-", - buildDate: buildDate || "-", - graphName, - }); - posthog.capture("Extension Loaded"); - }, - property_denylist: [ - "$ip", // Still seeing ip in the event - "$device_id", - "$geoip_city_name", - "$geoip_latitude", - "$geoip_longitude", - "$geoip_postal_code", - "$geoip_subdivision_1_name", - "$raw_user_agent", - "$current_url", - "$referrer", - "$referring_domain", - "$initial_current_url", - "$pathname", - ], - }); -}; +import { initPostHog } from "./utils/posthog"; +import { STREAMLINE_STYLING_KEY, DISALLOW_TRACKING } from "./data/userSettings"; export const DEFAULT_CANVAS_PAGE_FORMAT = "Canvas/*"; export default runExtension(async (onloadArgs) => { const isEncrypted = window.roamAlphaAPI.graph.isEncrypted; const isOffline = window.roamAlphaAPI.graph.type === "offline"; - if (!isEncrypted && !isOffline) { + const disallowTracking = getSetting(DISALLOW_TRACKING, false); + if (!isEncrypted && !isOffline && !disallowTracking) { initPostHog(); } diff --git a/apps/roam/src/utils/internalError.ts b/apps/roam/src/utils/internalError.ts index f74216d5b..c9e77920d 100644 --- a/apps/roam/src/utils/internalError.ts +++ b/apps/roam/src/utils/internalError.ts @@ -2,6 +2,8 @@ import posthog from "posthog-js"; import type { Properties } from "posthog-js"; import renderToast from "roamjs-components/components/Toast"; import sendErrorEmail from "~/utils/sendErrorEmail"; +import { getSetting } from "~/utils/extensionSettings"; +import { DISALLOW_TRACKING } from "~/data/userSettings"; const NON_WORD = /\W+/g; @@ -20,7 +22,10 @@ const internalError = ({ sendEmail?: boolean; forceSendInDev?: boolean; }): void => { - if (process.env.NODE_ENV === "development" && forceSendInDev !== true) { + if ( + getSetting(DISALLOW_TRACKING, false) || + (process.env.NODE_ENV === "development" && forceSendInDev !== true) + ) { console.error(error, context); } else { type = type || "Internal Error"; diff --git a/apps/roam/src/utils/posthog.ts b/apps/roam/src/utils/posthog.ts new file mode 100644 index 000000000..1e88e2c14 --- /dev/null +++ b/apps/roam/src/utils/posthog.ts @@ -0,0 +1,77 @@ +import getCurrentUserUid from "roamjs-components/queries/getCurrentUserUid"; +import { getVersionWithDate } from "./getVersion"; +import posthog from "posthog-js"; +import type { CaptureResult } from "posthog-js"; +import { getSetting } from "./extensionSettings"; +import { DISALLOW_TRACKING } from "~/data/userSettings"; + +let initialized = false; + +const doInitPostHog = (): void => { + if (initialized) return; + const propertyDenylist = new Set([ + "$ip", + "$device_id", + "$geoip_city_name", + "$geoip_latitude", + "$geoip_longitude", + "$geoip_postal_code", + "$geoip_subdivision_1_name", + "$raw_user_agent", + "$current_url", + "$referrer", + "$referring_domain", + "$initial_current_url", + "$pathname", + ]); + posthog.init("phc_SNMmBqwNfcEpNduQ41dBUjtGNEUEKAy6jTn63Fzsrax", { + /* eslint-disable @typescript-eslint/naming-convention */ + api_host: "https://us.i.posthog.com", + person_profiles: "identified_only", + capture_pageview: false, + property_denylist: [...propertyDenylist], + before_send: (result: CaptureResult | null) => { + if (result !== null) { + result.properties = Object.fromEntries( + Object.entries(result.properties).filter( + ([k]) => !propertyDenylist.has(k), + ), + ); + } + return result; + }, + /* eslint-enable @typescript-eslint/naming-convention */ + autocapture: false, + loaded: (posthog) => { + const { version, buildDate } = getVersionWithDate(); + const userUid = getCurrentUserUid(); + const graphName = window.roamAlphaAPI.graph.name; + posthog.identify(userUid, { + graphName, + }); + posthog.register({ + version: version || "-", + buildDate: buildDate || "-", + graphName, + }); + posthog.capture("Extension Loaded"); + initialized = true; + }, + }); +}; + +export const enablePostHog = (): void => { + doInitPostHog(); + posthog.opt_in_capturing(); +}; + +export const disablePostHog = (): void => { + if (initialized) posthog.opt_out_capturing(); +}; + +export const initPostHog = (): void => { + const disabled = getSetting(DISALLOW_TRACKING, false); + if (!disabled) { + doInitPostHog(); + } +};