From 8913db38f47ca2b9b15dc31a27f84241d831d82f Mon Sep 17 00:00:00 2001 From: nullptr <62841684+not-nullptr@users.noreply.github.com> Date: Fri, 15 Nov 2024 11:16:19 +0000 Subject: [PATCH] feat: more plausible analytics data (#27) * feat: testing nightly webhook * feat: add nightly label * feat: implement types for plausible * feat: add analytics for conversions --- .env.example | 3 +- src/app.d.ts | 248 +++++++++++++++++++++++++++++++- src/routes/+layout.svelte | 39 ++++- src/routes/convert/+page.svelte | 6 + 4 files changed, 289 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index d85771f..8027000 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ PUB_HOSTNAME=vert.sh # only gets used for plausible (for now) -PUB_PLAUSIBLE_URL=https://plausible.example.com # can be empty \ No newline at end of file +PUB_PLAUSIBLE_URL=https://plausible.example.com # can be empty +PUB_ENV=production # "production" or "nightly" \ No newline at end of file diff --git a/src/app.d.ts b/src/app.d.ts index da08e6d..bd1d41f 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,5 +1,242 @@ -// See https://svelte.dev/docs/kit/types#app.d.ts -// for information about these interfaces +type EventPayload = { + readonly n: string; + readonly u: Location["href"]; + readonly d: Location["hostname"]; + readonly r: Document["referrer"] | null; + readonly w: Window["innerWidth"]; + readonly h: 1 | 0; + readonly p?: string; +}; + +type CallbackArgs = { + readonly status: number; +}; + +type EventOptions = { + /** + * Callback called when the event is successfully sent. + */ + readonly callback?: (args: CallbackArgs) => void; + /** + * Properties to be bound to the event. + */ + readonly props?: { readonly [propName: string]: string | number | boolean }; +}; + +declare global { + interface Window { + plausible: TrackEvent; + } +} + +/** + * Options used when initializing the tracker. + */ +export type PlausibleInitOptions = { + /** + * If true, pageviews will be tracked when the URL hash changes. + * Enable this if you are using a frontend that uses hash-based routing. + */ + readonly hashMode?: boolean; + /** + * Set to true if you want events to be tracked when running the site locally. + */ + readonly trackLocalhost?: boolean; + /** + * The domain to bind the event to. + * Defaults to `location.hostname` + */ + readonly domain?: Location["hostname"]; + /** + * The API host where the events will be sent. + * Defaults to `'https://plausible.io'` + */ + readonly apiHost?: string; +}; + +/** + * Data passed to Plausible as events. + */ +export type PlausibleEventData = { + /** + * The URL to bind the event to. + * Defaults to `location.href`. + */ + readonly url?: Location["href"]; + /** + * The referrer to bind the event to. + * Defaults to `document.referrer` + */ + readonly referrer?: Document["referrer"] | null; + /** + * The current device's width. + * Defaults to `window.innerWidth` + */ + readonly deviceWidth?: Window["innerWidth"]; +}; + +/** + * Options used when tracking Plausible events. + */ +export type PlausibleOptions = PlausibleInitOptions & PlausibleEventData; + +/** + * Tracks a custom event. + * + * Use it to track your defined goals by providing the goal's name as `eventName`. + * + * ### Example + * ```js + * import Plausible from 'plausible-tracker' + * + * const { trackEvent } = Plausible() + * + * // Tracks the 'signup' goal + * trackEvent('signup') + * + * // Tracks the 'Download' goal passing a 'method' property. + * trackEvent('Download', { props: { method: 'HTTP' } }) + * ``` + * + * @param eventName - Name of the event to track + * @param options - Event options. + * @param eventData - Optional event data to send. Defaults to the current page's data merged with the default options provided earlier. + */ +type TrackEvent = ( + eventName: string, + options?: EventOptions, + eventData?: PlausibleOptions, +) => void; + +/** + * Manually tracks a page view. + * + * ### Example + * ```js + * import Plausible from 'plausible-tracker' + * + * const { trackPageview } = Plausible() + * + * // Track a page view + * trackPageview() + * ``` + * + * @param eventData - Optional event data to send. Defaults to the current page's data merged with the default options provided earlier. + * @param options - Event options. + */ +type TrackPageview = ( + eventData?: PlausibleOptions, + options?: EventOptions, +) => void; + +/** + * Cleans up all event listeners attached. + */ +type Cleanup = () => void; + +/** + * Tracks the current page and all further pages automatically. + * + * Call this if you don't want to manually manage pageview tracking. + * + * ### Example + * ```js + * import Plausible from 'plausible-tracker' + * + * const { enableAutoPageviews } = Plausible() + * + * // This tracks the current page view and all future ones as well + * enableAutoPageviews() + * ``` + * + * The returned value is a callback that removes the added event listeners and restores `history.pushState` + * ```js + * import Plausible from 'plausible-tracker' + * + * const { enableAutoPageviews } = Plausible() + * + * const cleanup = enableAutoPageviews() + * + * // Remove event listeners and restore `history.pushState` + * cleanup() + * ``` + */ +type EnableAutoPageviews = () => Cleanup; + +/** + * Tracks all outbound link clicks automatically + * + * Call this if you don't want to manually manage these links. + * + * It works using a **[MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)** to automagically detect link nodes throughout your application and bind `click` events to them. + * + * Optionally takes the same parameters as [`MutationObserver.observe`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe). + * + * ### Example + * ```js + * import Plausible from 'plausible-tracker' + * + * const { enableAutoOutboundTracking } = Plausible() + * + * // This tracks all the existing and future outbound links on your page. + * enableAutoOutboundTracking() + * ``` + * + * The returned value is a callback that removes the added event listeners and disconnects the observer + * ```js + * import Plausible from 'plausible-tracker' + * + * const { enableAutoOutboundTracking } = Plausible() + * + * const cleanup = enableAutoOutboundTracking() + * + * // Remove event listeners and disconnect the observer + * cleanup() + * ``` + */ +type EnableAutoOutboundTracking = ( + targetNode?: Node & ParentNode, + observerInit?: MutationObserverInit, +) => Cleanup; + +/** + * Initializes the tracker with your default values. + * + * ### Example (es module) + * ```js + * import Plausible from 'plausible-tracker' + * + * const { enableAutoPageviews, trackEvent } = Plausible({ + * domain: 'my-app-domain.com', + * hashMode: true + * }) + * + * enableAutoPageviews() + * + * function onUserRegister() { + * trackEvent('register') + * } + * ``` + * + * ### Example (commonjs) + * ```js + * var Plausible = require('plausible-tracker'); + * + * var { enableAutoPageviews, trackEvent } = Plausible({ + * domain: 'my-app-domain.com', + * hashMode: true + * }) + * + * enableAutoPageviews() + * + * function onUserRegister() { + * trackEvent('register') + * } + * ``` + * + * @param defaults - Default event parameters that will be applied to all requests. + */ + declare global { namespace App { // interface Error {} @@ -10,4 +247,11 @@ declare global { } } +declare module "svelte/elements" { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface HTMLAttributes { + [key: `event-${string}`]: string | undefined | null; + } +} + export {}; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 5c22184..e1ed614 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -6,7 +6,11 @@ import { files, theme } from "$lib/store/index.svelte"; import Logo from "$lib/components/visual/svg/Logo.svelte"; import featuredImage from "$lib/assets/VERT_Feature.webp"; - import { PUB_HOSTNAME, PUB_PLAUSIBLE_URL } from "$env/static/public"; + import { + PUB_ENV, + PUB_HOSTNAME, + PUB_PLAUSIBLE_URL, + } from "$env/static/public"; import FancyMenu from "$lib/components/functional/FancyMenu.svelte"; import { writable } from "svelte/store"; import { MoonIcon, SunIcon } from "lucide-svelte"; @@ -86,6 +90,16 @@ navbar?.addEventListener("mouseenter", mouseEnter); navbar?.addEventListener("mouseleave", mouseLeave); }); + + onMount(() => { + window.plausible = + window.plausible || + ((_, opts) => { + opts?.callback?.({ + status: 200, + }); + }); + }); @@ -96,7 +110,8 @@ {#if PUB_PLAUSIBLE_URL}{/if} @@ -110,8 +125,16 @@ href="/" class="px-4 relative h-14 mr-3 justify-center items-center bg-accent-background fill-accent-foreground rounded-xl md:hidden flex" > -
+
+ {#if PUB_ENV === "nightly"} +
+ NIGHTLY +
+ {/if}
@@ -125,8 +148,16 @@ href="/" class="px-3 relative w-full h-full mr-3 justify-center items-center bg-accent-background fill-accent-foreground rounded-xl md:flex hidden" > -
+
+ {#if PUB_ENV === "nightly"} +
+ NIGHTLY +
+ {/if}
diff --git a/src/routes/convert/+page.svelte b/src/routes/convert/+page.svelte index 667d0d0..24b19cf 100644 --- a/src/routes/convert/+page.svelte +++ b/src/routes/convert/+page.svelte @@ -73,6 +73,12 @@ promises.push( (async (i) => { await convert(files.files[i], i); + window.plausible("Convert", { + props: { + "Format from": files.files[i].from, + "Format to": files.files[i].to, + }, + }); })(i), ); }