apparent-capacitor 0.0.0-stage → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +88 -2
- package/dist/index.d.ts +189 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +961 -0
- package/dist/index.js.map +1 -0
- package/dist/screens.d.ts +23 -0
- package/dist/screens.d.ts.map +1 -0
- package/dist/screens.js +76 -0
- package/dist/screens.js.map +1 -0
- package/package.json +52 -4
- package/src/index.ts +932 -0
- package/src/screens.ts +81 -0
package/src/screens.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screens from any router (Ionic, Vue Router, React Router, Angular, or none): watches the address the WebView shows
|
|
3
|
+
* (history.pushState / replaceState, back and forward, #/hash routes) and sends a screen_view for each new one.
|
|
4
|
+
*
|
|
5
|
+
* import { trackScreens } from "apparent-capacitor";
|
|
6
|
+
* trackScreens();
|
|
7
|
+
*
|
|
8
|
+
* The name is the route, not the address: "/practice/17" and "/practice/18" are both "practice/:id" (the real path
|
|
9
|
+
* goes in `path`), so screens group. "/" is "home". Pass `name` to choose names yourself (return null to skip one).
|
|
10
|
+
*/
|
|
11
|
+
import { Apparent, type ApparentClient } from "./index";
|
|
12
|
+
|
|
13
|
+
/** "/orders/8f14e45f-ceea-467a-9575-6e0b3ad3cfa1/items/42?x=1" → "orders/:id/items/:id". */
|
|
14
|
+
export function screenName(path: string): string {
|
|
15
|
+
const clean = String(path).split(/[?#]/)[0]!.replace(/\/{2,}/g, "/").replace(/^\/|\/$/g, "");
|
|
16
|
+
if (!clean) return "home";
|
|
17
|
+
return clean.split("/").map((seg) => {
|
|
18
|
+
let s = seg;
|
|
19
|
+
try { s = decodeURIComponent(seg); } catch { /* keep as is */ }
|
|
20
|
+
const id = /^\d+$/.test(s) // 42
|
|
21
|
+
|| /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s) // uuid
|
|
22
|
+
|| /^[0-9a-f]{12,}$/i.test(s) // hashes, Mongo ids
|
|
23
|
+
|| (s.length >= 16 && /\d/.test(s) && /^[A-Za-z0-9_-]+$/.test(s) && !/^[a-z]+(?:[-_][a-z]+)*$/.test(s)); // Firebase-style ids
|
|
24
|
+
return id ? ":id" : seg;
|
|
25
|
+
}).join("/").slice(0, 80);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type Options = {
|
|
29
|
+
/** Your own name for a path; return null to send nothing for it. Default: screenName(path). */
|
|
30
|
+
name?: (path: string) => string | null | undefined;
|
|
31
|
+
/** The client, for tests. Default: Apparent. */
|
|
32
|
+
client?: Pick<ApparentClient, "screen">;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** Start sending screen_view on every route change. Returns a function that stops it. */
|
|
36
|
+
export function trackScreens(options: Options = {}): () => void {
|
|
37
|
+
const w = globalThis as unknown as {
|
|
38
|
+
location?: { pathname: string; hash: string; search: string };
|
|
39
|
+
history?: History;
|
|
40
|
+
addEventListener?: (t: string, f: () => void) => void;
|
|
41
|
+
removeEventListener?: (t: string, f: () => void) => void;
|
|
42
|
+
};
|
|
43
|
+
const loc = w.location;
|
|
44
|
+
const history = w.history;
|
|
45
|
+
if (!loc || !history || typeof w.addEventListener !== "function") return () => {};
|
|
46
|
+
const client = options.client ?? Apparent;
|
|
47
|
+
let last: string | null = null;
|
|
48
|
+
const seen = () => {
|
|
49
|
+
try {
|
|
50
|
+
// Hash routers (#/settings) keep the page at "/".
|
|
51
|
+
const path = loc.hash.startsWith("#/") ? loc.hash.slice(1) : loc.pathname;
|
|
52
|
+
const bare = path.split(/[?#]/)[0] || "/";
|
|
53
|
+
if (bare === last) return;
|
|
54
|
+
last = bare;
|
|
55
|
+
const name = options.name ? options.name(bare) : screenName(bare);
|
|
56
|
+
if (name) client.screen(name, { path: bare.slice(0, 200) });
|
|
57
|
+
} catch { /* never break navigation */ }
|
|
58
|
+
};
|
|
59
|
+
const push = history.pushState;
|
|
60
|
+
const replace = history.replaceState;
|
|
61
|
+
// Routers call these after changing the address; ours runs after theirs, so location is already the new one.
|
|
62
|
+
history.pushState = function (this: History, ...args: Parameters<History["pushState"]>) {
|
|
63
|
+
const out = push.apply(this, args);
|
|
64
|
+
seen();
|
|
65
|
+
return out;
|
|
66
|
+
};
|
|
67
|
+
history.replaceState = function (this: History, ...args: Parameters<History["replaceState"]>) {
|
|
68
|
+
const out = replace.apply(this, args);
|
|
69
|
+
seen();
|
|
70
|
+
return out;
|
|
71
|
+
};
|
|
72
|
+
w.addEventListener("popstate", seen);
|
|
73
|
+
w.addEventListener("hashchange", seen);
|
|
74
|
+
seen();
|
|
75
|
+
return () => {
|
|
76
|
+
history.pushState = push;
|
|
77
|
+
history.replaceState = replace;
|
|
78
|
+
w.removeEventListener?.("popstate", seen);
|
|
79
|
+
w.removeEventListener?.("hashchange", seen);
|
|
80
|
+
};
|
|
81
|
+
}
|