Skip to content

Commit 90b3d73

Browse files
authored
chore: update privacy policy to mention sentry (@Miodec) (monkeytypegame#6558)
Also refactors cookie modal and cokie local storage a little bit. closes monkeytypegame#6557
1 parent 98f1273 commit 90b3d73

File tree

8 files changed

+149
-85
lines changed

8 files changed

+149
-85
lines changed

frontend/src/html/popups.html

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,19 @@
166166
<label class="checkbox">
167167
<div class="title">analytics</div>
168168
<div class="description">
169-
We use Google Analytics cookies to check how users interact with our
170-
website and use this data to improve our site design.
169+
We use Google Analytics to track the overall traffic and
170+
demographics of our site.
171+
</div>
172+
<input type="checkbox" />
173+
</label>
174+
</div>
175+
<div class="cookie sentry">
176+
<label class="checkbox">
177+
<div class="title">sentry</div>
178+
<div class="description">
179+
We use Sentry to track errors and performance issues on our site, as
180+
well as record annonymized user sessions to help us debug issues and
181+
improve our product.
171182
</div>
172183
<input type="checkbox" />
173184
</label>

frontend/src/privacy-policy.html

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@
111111
</p>
112112

113113
<p>Effective date: September 8, 2021</p>
114-
<p>Last updated: May 20, 2024</p>
114+
<p>Last updated: May 12, 2025</p>
115115
<p>
116116
Thanks for trusting Monkeytype ('Monkeytype', 'we', 'us', 'our') with
117117
your personal information! We take our responsibility to you very
@@ -274,21 +274,50 @@ <h1 id="Data_Protection_Rights">
274274
</ul>
275275
<!-- <p>If you make a request, we have one month to respond to you. If you would like to exercise any of these rights, please contact us at our email: [email protected]</p> -->
276276

277-
<h1 id="Log_Data">What log data do we collect?</h1>
277+
<h1 id="Log_Data">Analytics</h1>
278278
<p>
279-
Like most websites, Monkeytype collects information that your browser
280-
sends whenever you visit the website. This data may include internet
281-
protocol (IP) addresses, browser type, Internet Service Provider
282-
(ISP), date and time stamp, referring/exit pages, and time spent on
283-
each page.
279+
Like most websites, we use Google Analytics, which collects
280+
information that your browser sends whenever you visit the website.
281+
This data may include internet protocol (IP) addresses, browser type,
282+
Internet Service Provider (ISP), date and time stamp, referring/exit
283+
pages, and time spent on each page.
284284
<b>
285285
THIS DATA DOES NOT CONTAIN ANY PERSONALLY IDENTIFIABLE INFORMATION.
286286
</b>
287287
We use this information for analyzing trends, administering the site,
288288
tracking users' movement on the website, and gathering demographic
289289
information.
290290
</p>
291-
<p>In our case, this service is provided by Google Analytics.</p>
291+
<p>
292+
For more information on Google Analytics' privacy policy, please
293+
visit:
294+
<a
295+
href="https://support.google.com/analytics/answer/6004245?hl=en"
296+
target="_blank"
297+
rel="noopener noreferrer"
298+
>
299+
https://support.google.com/analytics/answer/6004245?hl=en
300+
</a>
301+
</p>
302+
303+
<h1 id="Sentry">Sentry</h1>
304+
<p>
305+
Sentry is a crash reporting service that helps us track errors and
306+
crashes on the website. It collects information about your device,
307+
browser, and the error that occurred. Sometimes it might also include
308+
an annonymized replay of your session. This information is used to
309+
track down bugs faster and improve our website.
310+
</p>
311+
<p>
312+
For more information on Sentry's privacy policy, please visit:
313+
<a
314+
href="https://sentry.io/privacy/"
315+
target="_blank"
316+
rel="noopener noreferrer"
317+
>
318+
https://sentry.io/privacy/
319+
</a>
320+
</p>
292321

293322
<h1 id="Advertisements">Advertisements</h1>
294323
<p>

frontend/src/ts/controllers/analytics-controller.ts

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,9 @@ import {
66
} from "firebase/analytics";
77
import { app as firebaseApp } from "../firebase";
88
import { createErrorMessage } from "../utils/misc";
9-
import { parseWithSchema as parseJsonWithSchema } from "@monkeytype/util/json";
10-
import { z } from "zod";
119

1210
let analytics: AnalyticsType;
1311

14-
const AcceptedCookiesSchema = z.object({
15-
security: z.boolean(),
16-
analytics: z.boolean(),
17-
});
18-
19-
type AcceptedCookies = z.infer<typeof AcceptedCookiesSchema>;
20-
2112
export async function log(
2213
eventName: string,
2314
params?: Record<string, string>
@@ -29,26 +20,12 @@ export async function log(
2920
}
3021
}
3122

32-
const lsString = localStorage.getItem("acceptedCookies");
33-
let acceptedCookies: AcceptedCookies | null;
34-
if (lsString !== undefined && lsString !== null && lsString !== "") {
35-
try {
36-
acceptedCookies = parseJsonWithSchema(lsString, AcceptedCookiesSchema);
37-
} catch (e) {
38-
console.error("Failed to parse accepted cookies:", e);
39-
acceptedCookies = null;
40-
}
41-
} else {
42-
acceptedCookies = null;
43-
}
44-
45-
if (acceptedCookies !== null) {
46-
if (acceptedCookies.analytics) {
47-
activateAnalytics();
48-
}
49-
}
50-
5123
export function activateAnalytics(): void {
24+
if (analytics !== undefined) {
25+
console.warn("Analytics already activated");
26+
return;
27+
}
28+
console.log("Activating Analytics");
5229
try {
5330
analytics = getAnalytics(firebaseApp);
5431
setAnalyticsCollectionEnabled(analytics, true);

frontend/src/ts/cookies.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { z } from "zod";
2+
import { LocalStorageWithSchema } from "./utils/local-storage-with-schema";
3+
import { activateAnalytics } from "./controllers/analytics-controller";
4+
import { activateSentry } from "./sentry";
5+
6+
const AcceptedCookiesSchema = z
7+
.object({
8+
security: z.boolean(),
9+
analytics: z.boolean(),
10+
sentry: z.boolean(),
11+
})
12+
.strict()
13+
.nullable();
14+
15+
export type AcceptedCookies = z.infer<typeof AcceptedCookiesSchema>;
16+
17+
export function getAcceptedCookies(): AcceptedCookies | null {
18+
return cookies.get();
19+
}
20+
21+
export function setAcceptedCookies(accepted: AcceptedCookies): void {
22+
cookies.set(accepted);
23+
24+
activateWhatsAccepted();
25+
}
26+
27+
const cookies = new LocalStorageWithSchema({
28+
key: "acceptedCookies",
29+
schema: AcceptedCookiesSchema,
30+
fallback: null,
31+
// no migration here, if cookies changed, we need to ask the user again
32+
});
33+
34+
export function activateWhatsAccepted(): void {
35+
const accepted = getAcceptedCookies();
36+
if (accepted?.analytics) {
37+
activateAnalytics();
38+
}
39+
if (accepted?.sentry) {
40+
activateSentry();
41+
}
42+
}

frontend/src/ts/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import "./controllers/account-controller";
2929
import { enable } from "./states/glarses-mode";
3030
import "./test/caps-warning";
3131
import "./modals/simple-modals";
32+
import * as CookiesModal from "./modals/cookies";
3233
import "./controllers/input-controller";
3334
import "./ready";
3435
import "./controllers/route-controller";
@@ -46,6 +47,7 @@ import * as VersionButton from "./elements/version-button";
4647
import * as Focus from "./test/focus";
4748
import { getDevOptionsModal } from "./utils/async-modules";
4849
import * as Sentry from "./sentry";
50+
import * as Cookies from "./cookies";
4951

5052
function addToGlobal(items: Record<string, unknown>): void {
5153
for (const [name, item] of Object.entries(items)) {
@@ -57,7 +59,13 @@ function addToGlobal(items: Record<string, unknown>): void {
5759
void loadFromLocalStorage();
5860
void VersionButton.update();
5961
Focus.set(true, true);
60-
Sentry.init();
62+
63+
const accepted = Cookies.getAcceptedCookies();
64+
if (accepted === null) {
65+
CookiesModal.show();
66+
} else {
67+
Cookies.activateWhatsAccepted();
68+
}
6169

6270
addToGlobal({
6371
snapshot: DB.getSnapshot,

frontend/src/ts/modals/cookies.ts

Lines changed: 34 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,20 @@
1-
import { activateAnalytics } from "../controllers/analytics-controller";
21
import * as Notifications from "../elements/notifications";
32
import { isPopupVisible } from "../utils/misc";
43
import * as AdController from "../controllers/ad-controller";
54
import AnimatedModal from "../utils/animated-modal";
65
import { focusWords } from "../test/test-ui";
7-
import { LocalStorageWithSchema } from "../utils/local-storage-with-schema";
8-
import { z } from "zod";
9-
10-
const AcceptedSchema = z.object({
11-
security: z.boolean(),
12-
analytics: z.boolean(),
13-
});
14-
type Accepted = z.infer<typeof AcceptedSchema>;
15-
16-
const acceptedCookiesLS = new LocalStorageWithSchema({
17-
key: "acceptedCookies",
18-
schema: AcceptedSchema,
19-
fallback: undefined,
20-
});
21-
22-
function setAcceptedObject(obj: Accepted): void {
23-
acceptedCookiesLS.set(obj);
24-
}
25-
26-
export function check(): void {
27-
if (acceptedCookiesLS.get() === undefined) {
28-
show();
29-
}
30-
}
6+
import {
7+
AcceptedCookies,
8+
getAcceptedCookies,
9+
setAcceptedCookies,
10+
} from "../cookies";
3111

3212
export function show(goToSettings?: boolean): void {
3313
void modal.show({
3414
beforeAnimation: async () => {
3515
if (goToSettings) {
36-
showSettings();
16+
const currentAcceptedCookies = getAcceptedCookies();
17+
showSettings(currentAcceptedCookies);
3718
}
3819
},
3920
afterAnimation: async () => {
@@ -44,9 +25,26 @@ export function show(goToSettings?: boolean): void {
4425
});
4526
}
4627

47-
function showSettings(): void {
28+
function showSettings(currentAcceptedCookies?: AcceptedCookies): void {
4829
modal.getModal().querySelector(".main")?.classList.add("hidden");
4930
modal.getModal().querySelector(".settings")?.classList.remove("hidden");
31+
32+
if (currentAcceptedCookies) {
33+
if (currentAcceptedCookies.analytics) {
34+
(
35+
modal
36+
.getModal()
37+
.querySelector(".cookie.analytics input") as HTMLInputElement
38+
).checked = true;
39+
}
40+
if (currentAcceptedCookies.sentry) {
41+
(
42+
modal
43+
.getModal()
44+
.querySelector(".cookie.sentry input") as HTMLInputElement
45+
).checked = true;
46+
}
47+
}
5048
}
5149

5250
async function hide(): Promise<void> {
@@ -57,14 +55,6 @@ async function hide(): Promise<void> {
5755
});
5856
}
5957

60-
// function verifyVisible(): void {
61-
// if (!modal.isOpen()) return;
62-
// if (!isPopupVisible("cookiePopup")) {
63-
// //removed by cookie popup blocking extension
64-
// modal.destroy();
65-
// }
66-
// }
67-
6858
const modal = new AnimatedModal({
6959
dialogId: "cookiesModal",
7060
customEscapeHandler: (): void => {
@@ -78,17 +68,18 @@ const modal = new AnimatedModal({
7868
const accepted = {
7969
security: true,
8070
analytics: true,
71+
sentry: true,
8172
};
82-
setAcceptedObject(accepted);
83-
activateAnalytics();
73+
setAcceptedCookies(accepted);
8474
void hide();
8575
});
8676
modalEl.querySelector(".rejectAll")?.addEventListener("click", () => {
8777
const accepted = {
8878
security: true,
8979
analytics: false,
80+
sentry: false,
9081
};
91-
setAcceptedObject(accepted);
82+
setAcceptedCookies(accepted);
9283
void hide();
9384
});
9485
modalEl.querySelector(".openSettings")?.addEventListener("click", () => {
@@ -111,16 +102,16 @@ const modal = new AnimatedModal({
111102
const analyticsChecked = (
112103
modalEl.querySelector(".cookie.analytics input") as HTMLInputElement
113104
).checked;
105+
const sentryChecked = (
106+
modalEl.querySelector(".cookie.sentry input") as HTMLInputElement
107+
).checked;
114108
const accepted = {
115109
security: true,
116110
analytics: analyticsChecked,
111+
sentry: sentryChecked,
117112
};
118-
setAcceptedObject(accepted);
113+
setAcceptedCookies(accepted);
119114
void hide();
120-
121-
if (analyticsChecked) {
122-
activateAnalytics();
123-
}
124115
});
125116
},
126117
});

frontend/src/ts/ready.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import * as Misc from "./utils/misc";
22
import * as MonkeyPower from "./elements/monkey-power";
33
import * as MerchBanner from "./elements/merch-banner";
4-
import * as CookiesModal from "./modals/cookies";
54
import * as ConnectionState from "./states/connection";
65
import * as AccountButton from "./elements/account-button";
76
//@ts-expect-error no types for this package
@@ -12,7 +11,6 @@ import { loadPromise } from "./config";
1211

1312
$(async (): Promise<void> => {
1413
await loadPromise;
15-
CookiesModal.check();
1614

1715
//this line goes back to pretty much the beginning of the project and im pretty sure its here
1816
//to make sure the initial theme application doesnt animate the background color

frontend/src/ts/sentry.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ import { envConfig } from "./constants/env-config";
33

44
let debug = false;
55

6-
export function init(): void {
6+
let activated = false;
7+
8+
export function activateSentry(): void {
9+
if (activated) {
10+
console.warn("Sentry already activated");
11+
return;
12+
}
13+
activated = true;
14+
console.log("Activating Sentry");
715
Sentry.init({
816
release: envConfig.clientVersion,
917
dsn: "https://f50c25dc9dd75304a63776063896a39b@o4509236448133120.ingest.us.sentry.io/4509237217394688",

0 commit comments

Comments
 (0)