feat: add umami analytics

This commit is contained in:
Samuel 2025-01-22 16:56:43 +01:00
parent 448dde62f7
commit 533866a1f2
7 changed files with 53 additions and 20 deletions

View file

@ -6,6 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<!-- <link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" /> --> <!-- <link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" /> -->
<script defer src="https://umami.duskflower.dev/script.js" data-website-id="90772569-37b0-47af-9702-2ec2cfe0fe5f"
data-domains="signalstats.duskflower.dev"></script>
</head> </head>
<body> <body>

View file

@ -40,6 +40,7 @@
"@solidjs/router": "^0.15.3", "@solidjs/router": "^0.15.3",
"@sqlite.org/sqlite-wasm": "3.48.0-build2", "@sqlite.org/sqlite-wasm": "3.48.0-build2",
"@tanstack/solid-table": "^8.20.5", "@tanstack/solid-table": "^8.20.5",
"@types/umami": "^2.10.0",
"chart.js": "^4.4.7", "chart.js": "^4.4.7",
"chartjs-chart-wordcloud": "^4.4.4", "chartjs-chart-wordcloud": "^4.4.4",
"chartjs-plugin-deferred": "^2.0.0", "chartjs-plugin-deferred": "^2.0.0",

8
pnpm-lock.yaml generated
View file

@ -41,6 +41,9 @@ importers:
'@tanstack/solid-table': '@tanstack/solid-table':
specifier: ^8.20.5 specifier: ^8.20.5
version: 8.20.5(solid-js@1.9.4) version: 8.20.5(solid-js@1.9.4)
'@types/umami':
specifier: ^2.10.0
version: 2.10.0
chart.js: chart.js:
specifier: ^4.4.7 specifier: ^4.4.7
version: 4.4.7 version: 4.4.7
@ -800,6 +803,9 @@ packages:
'@types/node@22.10.7': '@types/node@22.10.7':
resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==} resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==}
'@types/umami@2.10.0':
resolution: {integrity: sha512-iWcs1KkcO3ooIi2rR9M5drmpQzlsT+sFiyWElIGmVwjdGlp+vQmy/VYIChYnF5ETqx7KrL80JfSkroS6dm37Hg==}
JSONStream@1.3.5: JSONStream@1.3.5:
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
hasBin: true hasBin: true
@ -2739,6 +2745,8 @@ snapshots:
dependencies: dependencies:
undici-types: 6.20.0 undici-types: 6.20.0
'@types/umami@2.10.0': {}
JSONStream@1.3.5: JSONStream@1.3.5:
dependencies: dependencies:
jsonparse: 1.3.1 jsonparse: 1.3.1

View file

@ -42,7 +42,14 @@ if (root) {
There is currently no backup database loaded, but you can watch statistics that have been There is currently no backup database loaded, but you can watch statistics that have been
cached, meaning only chats you already opened or chats that were preloaded. cached, meaning only chats you already opened or chats that were preloaded.
<br /> <br />
<A href="/overview">Watch cached statistics</A> <A
href="/overview"
onClick={() => {
umami.track("Watch cached statistics");
}}
>
Watch cached statistics
</A>
</Callout> </Callout>
</Show> </Show>
} }

View file

@ -37,7 +37,7 @@ export const Home: Component<RouteSectionProps> = () => {
const [loadingProgress, setLoadingProgress] = createSignal<number>(); const [loadingProgress, setLoadingProgress] = createSignal<number>();
// const [isLoadingDatabase, setIsLoadingDatabase] = createSignal(false); // const [isLoadingDatabase, setIsLoadingDatabase] = createSignal(false);
const onSubmit: JSX.EventHandler<HTMLFormElement, SubmitEvent> = async (event) => { const onSubmit: JSX.EventHandler<HTMLFormElement, SubmitEvent> = (event) => {
event.preventDefault(); event.preventDefault();
const currentBackupFile = backupFile(); const currentBackupFile = backupFile();
@ -53,22 +53,24 @@ export const Home: Component<RouteSectionProps> = () => {
// setDbHash(hash); // setDbHash(hash);
try { decryptBackup(currentBackupFile, currentPassphrase, setDecryptionProgress)
const decrypted = await decryptBackup(currentBackupFile, currentPassphrase, setDecryptionProgress); .then(async (decrypted) => {
umami.track("Decrypt backup");
setDecryptionProgress(undefined); setDecryptionProgress(undefined);
// setIsLoadingDatabase(true); // setIsLoadingDatabase(true);
setLoadingProgress(0); setLoadingProgress(0);
await loadDb(decrypted.database_statements, setLoadingProgress); await loadDb(decrypted.database_statements, setLoadingProgress);
umami.track("Load database");
// setIsLoadingDatabase(false); // setIsLoadingDatabase(false);
setLoadingProgress(undefined); setLoadingProgress(undefined);
navigate("/overview"); navigate("/overview");
} catch (error) { })
.catch((error) => {
console.error("Decryption failed:", error); console.error("Decryption failed:", error);
} });
} }
}; };

View file

@ -94,6 +94,7 @@ export const columns = [
<Button <Button
variant="ghost" variant="ghost"
onClick={() => { onClick={() => {
umami.track("Sort overview table");
props.column.toggleSorting(); props.column.toggleSorting();
}} }}
> >
@ -144,6 +145,7 @@ export const columns = [
<Button <Button
variant="ghost" variant="ghost"
onClick={() => { onClick={() => {
umami.track("Sort overview table");
props.column.toggleSorting(); props.column.toggleSorting();
}} }}
> >
@ -162,6 +164,7 @@ export const columns = [
<Button <Button
variant="ghost" variant="ghost"
onClick={() => { onClick={() => {
umami.track("Sort overview table");
props.column.toggleSorting(); props.column.toggleSorting();
}} }}
> >
@ -269,7 +272,10 @@ export const OverviewTable = (props: OverviewTableProps) => {
<div class="flex items-center py-4"> <div class="flex items-center py-4">
<TextField <TextField
value={(table.getColumn("name")?.getFilterValue() as string | undefined) ?? ""} value={(table.getColumn("name")?.getFilterValue() as string | undefined) ?? ""}
onChange={(value) => table.getColumn("name")?.setFilterValue(value)} onChange={(value) => {
umami.track("Filter overview table");
table.getColumn("name")?.setFilterValue(value);
}}
> >
<TextFieldInput placeholder="Filter by name..." class="max-w-sm" /> <TextFieldInput placeholder="Filter by name..." class="max-w-sm" />
</TextField> </TextField>
@ -278,7 +284,10 @@ export const OverviewTable = (props: OverviewTableProps) => {
<Checkbox <Checkbox
id="show-archived" id="show-archived"
checked={(table.getColumn("archived")?.getFilterValue() as boolean | undefined) ?? false} checked={(table.getColumn("archived")?.getFilterValue() as boolean | undefined) ?? false}
onChange={(value) => table.getColumn("archived")?.setFilterValue(value)} onChange={(value) => {
umami.track("Filter overview table");
table.getColumn("archived")?.setFilterValue(value);
}}
/> />
<div class="grid gap-1.5 leading-none"> <div class="grid gap-1.5 leading-none">
<Label for="show-archived">Show archived chats</Label> <Label for="show-archived">Show archived chats</Label>
@ -288,7 +297,10 @@ export const OverviewTable = (props: OverviewTableProps) => {
<Checkbox <Checkbox
id="show-groups" id="show-groups"
checked={(table.getColumn("isGroup")?.getFilterValue() as boolean | undefined) ?? false} checked={(table.getColumn("isGroup")?.getFilterValue() as boolean | undefined) ?? false}
onChange={(value) => table.getColumn("isGroup")?.setFilterValue(value)} onChange={(value) => {
umami.track("Filter overview table");
table.getColumn("isGroup")?.setFilterValue(value);
}}
/> />
<div class="grid gap-1.5 leading-none"> <div class="grid gap-1.5 leading-none">
<Label for="show-groups">Show group chats (detailed analysis not implemented)</Label> <Label for="show-groups">Show group chats (detailed analysis not implemented)</Label>
@ -347,7 +359,7 @@ export const OverviewTable = (props: OverviewTableProps) => {
preload(`/${isGroup ? "group" : "dm"}/${threadId.toString()}`, { preload(`/${isGroup ? "group" : "dm"}/${threadId.toString()}`, {
preloadData: true, preloadData: true,
}); });
}, 20); }, 50);
event.currentTarget.addEventListener( event.currentTarget.addEventListener(
"pointerout", "pointerout",
@ -365,6 +377,7 @@ export const OverviewTable = (props: OverviewTableProps) => {
const isGroup = row.original.isGroup; const isGroup = row.original.isGroup;
if (rowIsAvailable(threadId)) { if (rowIsAvailable(threadId)) {
umami.track("Load chat statistics");
navigate(`/${isGroup ? "group" : "dm"}/${threadId.toString()}`); navigate(`/${isGroup ? "group" : "dm"}/${threadId.toString()}`);
} }
}} }}

View file

@ -8,7 +8,7 @@
"esModuleInterop": true, "esModuleInterop": true,
"jsx": "preserve", "jsx": "preserve",
"jsxImportSource": "solid-js", "jsxImportSource": "solid-js",
"types": ["vite/client"], "types": ["vite/client", "@types/umami"],
"noEmit": true, "noEmit": true,
"isolatedModules": true, "isolatedModules": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,