Skip to content

Commit

Permalink
fix(matomo): envoyer les events de changement de pages
Browse files Browse the repository at this point in the history
  • Loading branch information
m-maillot committed Dec 16, 2024
1 parent 1055bae commit 3b705e0
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 8 deletions.
44 changes: 44 additions & 0 deletions packages/code-du-travail-frontend/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// / <reference types="next" />
// / <reference types="next/types/global" />

interface Dimensions {
dimension1?: string;
dimension2?: string;
dimension3?: string;
dimension4?: string;
dimension5?: string;
dimension6?: string;
dimension7?: string;
dimension8?: string;
dimension9?: string;
dimension10?: string;
}

interface Window {
_paq?:
| (
| Dimensions
| number[]
| string[]
| number
| string
| null
| undefined
)[][]
| null;
}
declare namespace NodeJS {
interface Global {
_paq?:
| (
| Dimensions
| number[]
| string[]
| number
| string
| null
| undefined
)[][]
| null;
}
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,80 @@
"use client";

import { init, push } from "@socialgouv/matomo-next";
import { usePathname } from "next/navigation";
import { useEffect } from "react";
import { usePathname, useSearchParams } from "next/navigation";
import { Suspense, useEffect, useState } from "react";
import { PIWIK_SITE_ID, PIWIK_URL, SITE_URL, WIDGETS_PATH } from "../../config";
import { getSourceUrlFromPath } from "../../lib";
import init, { push } from "./MatomoNext";

export function MatomoAnalytics() {
const pathname = usePathname();
function MatomoComponent() {
const searchParams = useSearchParams();
const searchParamsString = searchParams?.toString();
const path = usePathname();
const [previousPath, setPreviousPath] = useState<string | null>(null);

useEffect(() => {
console.log("[MATOMO] Init");
init({
siteId: PIWIK_SITE_ID,
url: PIWIK_URL,
onInitialization: () => {
const referrerUrl =
document?.referrer || getSourceUrlFromPath(SITE_URL + pathname);
document?.referrer || getSourceUrlFromPath(SITE_URL + path);
if (referrerUrl) {
push(["setReferrerUrl", referrerUrl]);
}
if (pathname && pathname.match(WIDGETS_PATH)) {
if (path && path.match(WIDGETS_PATH)) {
push(["setCookieSameSite", "None"]);
}
},
excludeUrlsPatterns: [WIDGETS_PATH],
});
}, []);

useEffect(() => {
if (path && !isExcludedUrl(path, [WIDGETS_PATH])) {
console.log("[MATOMO] Log path (", path, ")");
let [pathname] = path.split("?");
pathname = pathname.replace(/#.*/, "");

console.log("Set custom URL", pathname);

if (previousPath) {
push(["setReferrerUrl", `${previousPath}`]);
}

push(["setCustomUrl", pathname]);
push(["deleteCustomVariables", "page"]);
setPreviousPath(pathname);

const query = searchParams?.get("q");
push(["setDocumentTitle", document.title]);
if (startsWith(path, "/recherche") || startsWith(path, "/search")) {
push(["trackSiteSearch", query ?? ""]);
} else {
push(["trackPageView"]);
}
}
}, [path, searchParamsString]);
return null;
}

export const MatomoAnalytics = () => (
<Suspense>
<MatomoComponent />
</Suspense>
);

const startsWith = (str: string, needle: string) => {
return str.substring(0, needle.length) === needle;
};

const isExcludedUrl = (url: string, patterns: RegExp[]): boolean => {
let excluded = false;
patterns.forEach((pattern) => {
if (pattern.exec(url) !== null) {
excluded = true;
}
});
return excluded;
};
97 changes: 97 additions & 0 deletions packages/code-du-travail-frontend/src/modules/config/MatomoNext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
interface HTMLTrustedScriptElement extends Omit<HTMLScriptElement, "src"> {
src: TrustedScriptURL | string;
}

interface InitSettings {
url: string;
siteId: string;
jsTrackerFile?: string;
phpTrackerFile?: string;
excludeUrlsPatterns?: RegExp[];
disableCookies?: boolean;
onRouteChangeStart?: (path: string) => void;
onRouteChangeComplete?: (path: string) => void;
onInitialization?: () => void;
onScriptLoadingError?: () => void;
nonce?: string;
trustedPolicyName?: string;
logExcludedTracks?: boolean;
}

// to push custom events
export function push(
args: (
| Dimensions
| number[]
| string[]
| number
| string
| null
| undefined
)[]
): void {
if (!window._paq) {
window._paq = [];
}
window._paq.push(args);
}

const trustedPolicyHooks: TrustedTypePolicyOptions = {
createScript: (s) => s,
createScriptURL: (s) => s,
};

// initialize the tracker
export function init({
url,
siteId,
jsTrackerFile = "matomo.js",
phpTrackerFile = "matomo.php",
disableCookies = false,
onInitialization = undefined,
onScriptLoadingError = undefined,
nonce,
trustedPolicyName = "matomo-next",
}: InitSettings): void {
window._paq = window._paq !== null ? window._paq : [];
if (!url) {
console.warn("Matomo disabled, please provide matomo url");
return;
}

const sanitizer =
window.trustedTypes?.createPolicy(trustedPolicyName, trustedPolicyHooks) ??
trustedPolicyHooks;

if (onInitialization) onInitialization();

if (disableCookies) {
push(["disableCookies"]);
}

push(["enableLinkTracking"]);
push(["setTrackerUrl", `${url}/${phpTrackerFile}`]);
push(["setSiteId", siteId]);

const scriptElement: HTMLTrustedScriptElement =
document.createElement("script");
const refElement = document.getElementsByTagName("script")[0];
if (nonce) {
scriptElement.setAttribute("nonce", nonce);
}
scriptElement.type = "text/javascript";
scriptElement.async = true;
scriptElement.defer = true;
const fullUrl = `${url}/${jsTrackerFile}`;
scriptElement.src = sanitizer.createScriptURL?.(fullUrl) ?? fullUrl;
if (onScriptLoadingError) {
scriptElement.onerror = () => {
onScriptLoadingError();
};
}
if (refElement.parentNode) {
refElement.parentNode.insertBefore(scriptElement, refElement);
}
}

export default init;
8 changes: 7 additions & 1 deletion packages/code-du-travail-frontend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
"@styled-system/*": ["./src/styled-system/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"./global.d.ts"
],
"exclude": ["node_modules", "cypress"]
}

0 comments on commit 3b705e0

Please sign in to comment.