From 1f1a2b019bb1093f1cc1a798269ea43134570890 Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Thu, 9 Mar 2023 16:37:57 +0800 Subject: [PATCH] [ENG-227] Desktop app and landing page telemetry using Plausible Analytics (#583) * add Plausible analytics to landing page * proxy plausible through vercel * fix typo & add other options * add plausible to `sd/client` * add telemetry sharing option into library config * add telemetry config option to lib creation dialog * revert error message change but keep the typo fix * add telemetry sharing & error handling to client context * add important note about requiring the tracker component in root/base layouts * add the `PlausibleTracker` * grammatical tweaks * some TS cleanup * disable analytics in debug mode * further component improvements and use custom event props * more cleanup * remove tracking from onboarding (no telemetry sharing config option) * update comment * add fancy new plausible hooks/tracking * add `pageview` monitoring hook to `$libraryId` layout * add library creation events to onboarding and creation dialog * revert `useCurrentLibraryId()` error handling & add important comment * minor comment tweaks * replace `usage` with `telemetry` * add missing newline * add location create & delete events * add tag create & delete events * add/update library create & delete events * add fn for getting telemetry settings for library by uuid * add more events + fix a few bugs * update generics * add `telemetryState` `valtio` store * use new telemetry state * remove old artifacts from `ClientContext` * Revert "add telemetry sharing option into library config" This reverts commit afb9f892aba0054e1bd719003d4150a76c0ded64. * update events, docs & generics * add `tagAssign` event * light comment updates * const names, comments, etc * add additional info to props and update comment * add telemetry sharing to debug state (for sharing telemetry in debug mode) * update `debugState` item name * change how `Switch` updates the store in privacy settings * remove `getTelemetryState` from `telemetryState` * cleanup library creation event handling/telemetry config updating * add `DebugPopover` to onboarding in debug mode * improve code quality/comments * remove useless comment * rename `ob_store` and `shareTelemetryDataWithDevelopers` * fix typo * add `telemetryLogger` and prevent multiple of the same events from firing consecutively * add more unique path matching and fix an issue with events * rename `telemetryLogger` -> `telemetryLogging` --------- Co-authored-by: brxken128 <77554505+brxken128@users.noreply.github.com> --- interface/ErrorFallback.tsx | 2 +- .../$libraryId/Explorer/File/ContextMenu.tsx | 12 +- .../Layout/Sidebar/AddLocationButton.tsx | 14 +- .../Layout/Sidebar/DebugPopover.tsx | 34 +- interface/app/$libraryId/Layout/index.tsx | 15 +- .../$libraryId/settings/client/privacy.tsx | 13 +- .../library/locations/DeleteDialog.tsx | 10 +- .../settings/library/tags/CreateDialog.tsx | 8 +- .../settings/library/tags/DeleteDialog.tsx | 10 +- .../settings/node/libraries/CreateDialog.tsx | 33 +- .../settings/node/libraries/DeleteDialog.tsx | 12 +- interface/app/index.tsx | 5 + interface/app/onboarding/Layout.tsx | 6 +- interface/app/onboarding/Progress.tsx | 4 +- interface/app/onboarding/creating-library.tsx | 29 +- interface/app/onboarding/master-password.tsx | 4 +- interface/app/onboarding/new-library.tsx | 4 +- interface/app/onboarding/privacy.tsx | 3 +- packages/client/package.json | 1 + packages/client/src/hooks/index.ts | 1 + packages/client/src/hooks/usePlausible.tsx | 380 ++++++++++++++++++ packages/client/src/stores/debugState.ts | 17 +- packages/client/src/stores/index.ts | 1 + packages/client/src/stores/onboardingStore.ts | 2 +- packages/client/src/stores/telemetryState.tsx | 10 + pnpm-lock.yaml | Bin 781238 -> 781498 bytes 26 files changed, 582 insertions(+), 48 deletions(-) create mode 100644 packages/client/src/hooks/usePlausible.tsx create mode 100644 packages/client/src/stores/telemetryState.tsx diff --git a/interface/ErrorFallback.tsx b/interface/ErrorFallback.tsx index a6be2fad9..4692d9a05 100644 --- a/interface/ErrorFallback.tsx +++ b/interface/ErrorFallback.tsx @@ -36,7 +36,7 @@ export function ErrorPage({
Error: {message}
{debug.enabled && (
-					Check the console (CMD/CRTL + OPTION + i) for stack trace.
+					Check the console (CMD/CTRL + OPTION + i) for stack trace.
 				
)}
diff --git a/interface/app/$libraryId/Explorer/File/ContextMenu.tsx b/interface/app/$libraryId/Explorer/File/ContextMenu.tsx index 330f423ee..f191038a8 100644 --- a/interface/app/$libraryId/Explorer/File/ContextMenu.tsx +++ b/interface/app/$libraryId/Explorer/File/ContextMenu.tsx @@ -18,7 +18,8 @@ import { isObject, useLibraryContext, useLibraryMutation, - useLibraryQuery + useLibraryQuery, + usePlausibleEvent } from '@sd/client'; import { ContextMenu, dialogManager } from '@sd/ui'; import { useExplorerParams } from '~/app/$libraryId/location/$id'; @@ -248,9 +249,16 @@ export default ({ data, ...props }: Props) => { }; const AssignTagMenuItems = (props: { objectId: number }) => { + const platform = usePlatform(); + const submitPlausibleEvent = usePlausibleEvent({ platformType: platform.platform }); + const tags = useLibraryQuery(['tags.list'], { suspense: true }); const tagsForObject = useLibraryQuery(['tags.getForObject', props.objectId], { suspense: true }); - const assignTag = useLibraryMutation('tags.assign'); + const assignTag = useLibraryMutation('tags.assign', { + onSuccess: () => { + submitPlausibleEvent({ event: { type: 'tagAssign' } }); + } + }); return ( <> diff --git a/interface/app/$libraryId/Layout/Sidebar/AddLocationButton.tsx b/interface/app/$libraryId/Layout/Sidebar/AddLocationButton.tsx index 27ed29c77..6c0e4ec68 100644 --- a/interface/app/$libraryId/Layout/Sidebar/AddLocationButton.tsx +++ b/interface/app/$libraryId/Layout/Sidebar/AddLocationButton.tsx @@ -1,12 +1,20 @@ -import { useLibraryMutation } from '@sd/client'; +import { useLibraryMutation, usePlausibleEvent } from '@sd/client'; import { dialogManager } from '@sd/ui'; import { usePlatform } from '~/util/Platform'; import AddLocationDialog from '../../settings/library/locations/AddDialog'; export default () => { const platform = usePlatform(); - - const createLocation = useLibraryMutation('locations.create'); + const submitPlausibleEvent = usePlausibleEvent({ platformType: platform.platform }); + const createLocation = useLibraryMutation('locations.create', { + onSuccess: () => { + submitPlausibleEvent({ + event: { + type: 'locationCreate' + } + }); + } + }); return (
+
+
+ +
+ Share anonymous usage + + + +
+ {/* TODO: Proper UI for this. Maybe checkbox for encrypted or not and then reveal these fields. Select encrypted by default. */} {/* Make the secret key field empty to skip key setup. */} diff --git a/interface/app/$libraryId/settings/node/libraries/DeleteDialog.tsx b/interface/app/$libraryId/settings/node/libraries/DeleteDialog.tsx index ae5454f1a..660424080 100644 --- a/interface/app/$libraryId/settings/node/libraries/DeleteDialog.tsx +++ b/interface/app/$libraryId/settings/node/libraries/DeleteDialog.tsx @@ -1,7 +1,8 @@ import { useQueryClient } from '@tanstack/react-query'; -import { useBridgeMutation } from '@sd/client'; +import { useBridgeMutation, usePlausibleEvent, useTelemetryState } from '@sd/client'; import { Dialog, UseDialogProps, useDialog } from '@sd/ui'; import { forms } from '@sd/ui'; +import { usePlatform } from '~/util/Platform'; const { useZodForm, z } = forms; @@ -11,11 +12,20 @@ interface Props extends UseDialogProps { export default function DeleteLibraryDialog(props: Props) { const dialog = useDialog(props); + const platform = usePlatform(); + const submitPlausibleEvent = usePlausibleEvent({ platformType: platform.platform }); + const shareTelemetry = useTelemetryState().shareTelemetry; const queryClient = useQueryClient(); const deleteLib = useBridgeMutation('library.delete', { onSuccess: () => { queryClient.invalidateQueries(['library.list']); + + submitPlausibleEvent({ + event: { + type: 'libraryDelete' + } + }); } }); diff --git a/interface/app/index.tsx b/interface/app/index.tsx index 3ab562b58..3c4d010f5 100644 --- a/interface/app/index.tsx +++ b/interface/app/index.tsx @@ -20,6 +20,11 @@ const Index = () => { return ; }; + +// NOTE: all route `Layout`s below should contain +// the `usePlausiblePageViewMonitor` hook, as early as possible (ideally within the layout itself). +// the hook should only be included if there's a valid `ClientContext` (so not onboarding) + const routes = [ { index: true, diff --git a/interface/app/onboarding/Layout.tsx b/interface/app/onboarding/Layout.tsx index 9b1d39fb3..b594218da 100644 --- a/interface/app/onboarding/Layout.tsx +++ b/interface/app/onboarding/Layout.tsx @@ -2,10 +2,11 @@ import BloomOne from '@sd/assets/images/bloom-one.png'; import clsx from 'clsx'; import { useEffect } from 'react'; import { Outlet, useNavigate } from 'react-router'; -import { getOnboardingStore } from '@sd/client'; +import { getOnboardingStore, useDebugState } from '@sd/client'; import { tw } from '@sd/ui'; import DragRegion from '~/components/DragRegion'; import { useOperatingSystem } from '~/hooks/useOperatingSystem'; +import DebugPopover from '../$libraryId/Layout/Sidebar/DebugPopover'; import Progress from './Progress'; export const OnboardingContainer = tw.div`flex flex-col items-center`; @@ -15,6 +16,7 @@ export const OnboardingImg = tw.img`w-20 h-20 mb-2`; export default () => { const os = useOperatingSystem(); + const debugState = useDebugState(); const navigate = useNavigate(); useEffect( @@ -39,7 +41,6 @@ export default () => { )} > -
@@ -55,6 +56,7 @@ export default () => { {/* */}
+ {debugState.enabled && } ); }; diff --git a/interface/app/onboarding/Progress.tsx b/interface/app/onboarding/Progress.tsx index 0d7a1be65..fbda0a396 100644 --- a/interface/app/onboarding/Progress.tsx +++ b/interface/app/onboarding/Progress.tsx @@ -24,7 +24,7 @@ export function useUnlockOnboardingScreen() { } export default function OnboardingProgress() { - const ob_store = useOnboardingStore(); + const obStore = useOnboardingStore(); const navigate = useNavigate(); const currentScreenKey = useCurrentOnboardingScreenKey(); @@ -37,7 +37,7 @@ export default function OnboardingProgress() { return (