fix(web): remove Google Analytics and point site links at the fork (#35)
Drops the gtag scripts and the upstream measurement ID, rewrites the privacy copy to match, adds a test that no analytics ID appears under web/, and repoints footer links at the fork with one upstream credit link.
This commit was merged in pull request #35.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
// Ticket #16: the site loads no analytics. Everything the static export is
|
||||
// built from (web/src, web/public and the next.config files) is scanned byte
|
||||
// for byte, so a measurement tag cannot return through a component, an asset
|
||||
// or a config file. Binary assets are read as latin1 so every byte is covered.
|
||||
|
||||
const WEB_ROOT = process.cwd();
|
||||
const THIS_FILE = path.join(WEB_ROOT, 'src', 'app', '__tests__', 'no-analytics.test.ts');
|
||||
const TREE_ROOTS = [path.join(WEB_ROOT, 'src'), path.join(WEB_ROOT, 'public')];
|
||||
|
||||
const MARKERS: [string, RegExp][] = [
|
||||
['googletagmanager', /googletagmanager/],
|
||||
['google-analytics', /google-analytics/],
|
||||
['gtag(', /gtag\(/],
|
||||
['G- measurement ID', /G-[A-Z0-9]{8,}/],
|
||||
];
|
||||
|
||||
function scanTargets(): string[] {
|
||||
const targets: string[] = [];
|
||||
for (const root of TREE_ROOTS) {
|
||||
for (const rel of fs.readdirSync(root, { encoding: 'utf8', recursive: true })) {
|
||||
const full = path.join(root, rel);
|
||||
if (fs.statSync(full).isFile()) targets.push(full);
|
||||
}
|
||||
}
|
||||
for (const name of fs.readdirSync(WEB_ROOT)) {
|
||||
if (/^next\.config\./.test(name)) targets.push(path.join(WEB_ROOT, name));
|
||||
}
|
||||
return targets.filter((file) => file !== THIS_FILE);
|
||||
}
|
||||
|
||||
const sources = scanTargets().map((file) => ({
|
||||
file: path.relative(WEB_ROOT, file),
|
||||
text: fs.readFileSync(file, 'latin1'),
|
||||
}));
|
||||
|
||||
describe('no analytics on the site (ticket #16)', () => {
|
||||
it('scans the source, public assets and next.config files', () => {
|
||||
const names = sources.map((source) => source.file);
|
||||
expect(names).toEqual(
|
||||
expect.arrayContaining(['src/app/layout.tsx', 'next.config.js', 'public/_headers']),
|
||||
);
|
||||
});
|
||||
|
||||
it.each(MARKERS)('contains no %s marker', (_label, marker) => {
|
||||
const offenders = sources.filter((source) => marker.test(source.text)).map((source) => source.file);
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,6 @@
|
||||
import "@/styles/globals.css";
|
||||
import React from "react";
|
||||
import { type Metadata } from "next";
|
||||
import Script from "next/script";
|
||||
import {
|
||||
absoluteUrl,
|
||||
DEFAULT_OG_IMAGE_ALT,
|
||||
@@ -173,18 +172,6 @@ export default function RootLayout({
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<Script
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-7NBBNJNNQ7"
|
||||
strategy="lazyOnload"
|
||||
/>
|
||||
<Script id="google-analytics" strategy="lazyOnload">
|
||||
{`
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-7NBBNJNNQ7');
|
||||
`}
|
||||
</Script>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -141,17 +141,10 @@ export default function OpenSourcePage() {
|
||||
Free & Open Source Voice Dictation
|
||||
</h1>
|
||||
<p className="mb-8 max-w-4xl text-lg text-muted-foreground">
|
||||
Voculinux is 100% open source under the AGPL-3.0 license, a hard fork of{" "}
|
||||
<a
|
||||
href="https://github.com/VocaHQ/vocalinux"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-medium text-primary hover:underline"
|
||||
>
|
||||
Vocalinux (upstream, by VocaHQ)
|
||||
</a>
|
||||
. No subscriptions, no vendor lock-in, no hidden costs. Local engines keep
|
||||
audio on the machine. Remote API is optional.
|
||||
Voculinux is 100% open source under the AGPL-3.0 license, a hard fork of
|
||||
Vocalinux (upstream, by VocaHQ). No subscriptions, no vendor lock-in, no
|
||||
hidden costs. Local engines keep audio on the machine. Remote API is
|
||||
optional.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -986,8 +986,8 @@ export default function HomePage() {
|
||||
<p className="section-lede">
|
||||
Voculinux is a hard fork of Vocalinux for Void Linux (glibc,
|
||||
Wayland-primary under niri). The internal Python module keeps the
|
||||
upstream name so fixes stay cheap to port by hand; upstream links
|
||||
below are retained as provenance.
|
||||
upstream name so fixes stay cheap to port by hand. Upstream is
|
||||
credited in the footer.
|
||||
</p>
|
||||
</div>
|
||||
<div className="family-grid">
|
||||
@@ -1010,11 +1010,7 @@ export default function HomePage() {
|
||||
Linux.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
className="family-card"
|
||||
href={UPSTREAM_REPO_URL}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div className="family-card">
|
||||
<div className="family-card-top">
|
||||
<span className="family-platform">
|
||||
<img
|
||||
@@ -1032,7 +1028,7 @@ export default function HomePage() {
|
||||
The upstream Linux dictation app this fork is ported from. Fixes
|
||||
are taken by hand, one at a time.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
className="family-card"
|
||||
href="https://bongbetic.com"
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function PrivacyPage() {
|
||||
headline: "Voculinux Privacy Policy",
|
||||
description:
|
||||
"How Voculinux handles voice data: local engines on your machine, optional Remote API to a server you configure.",
|
||||
dateModified: "2026-09-23",
|
||||
dateModified: "2026-11-06",
|
||||
author: {
|
||||
"@type": "Person",
|
||||
name: "Jatin K Malik",
|
||||
@@ -158,14 +158,13 @@ export default function PrivacyPage() {
|
||||
</h2>
|
||||
<div className="space-y-4 text-muted-foreground">
|
||||
<p>
|
||||
voculinux.com loads Google Analytics via the googletagmanager gtag
|
||||
script for measurement ID{" "}
|
||||
<code className="rounded bg-muted px-1.5 py-0.5">G-7NBBNJNNQ7</code>{" "}
|
||||
and runs gtag config with no IP anonymization and no cookie flags.
|
||||
That sends page views and a Google Analytics client identifier (a
|
||||
cookie) to Google. Google processes IP addresses and device and
|
||||
browser data. The installed Voculinux app does not send usage
|
||||
telemetry.
|
||||
voculinux.com has no analytics, measurement or tracking scripts,
|
||||
and its code sets no cookies.
|
||||
</p>
|
||||
<p>
|
||||
The Bongbetic logos in the header and footer are images served from
|
||||
bongbetic.com. Loading any page therefore makes a request to that
|
||||
host.
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
Reference in New Issue
Block a user