Skip to content

Commit 6bd5240

Browse files
authored
refactor: add auth event to reduce number of dependencies in account controller (@Miodec) (monkeytypegame#6865)
1 parent ab4e1e8 commit 6bd5240

File tree

16 files changed

+201
-103
lines changed

16 files changed

+201
-103
lines changed

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

Lines changed: 57 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
11
import Ape from "../ape";
22
import * as Notifications from "../elements/notifications";
33
import Config, * as UpdateConfig from "../config";
4-
import * as AccountButton from "../elements/account-button";
54
import * as Misc from "../utils/misc";
6-
import * as Settings from "../pages/settings";
75
import * as DB from "../db";
8-
import * as TestLogic from "../test/test-logic";
96
import * as Loader from "../elements/loader";
107
import * as PageTransition from "../states/page-transition";
118
import * as ActivePage from "../states/active-page";
129
import * as LoadingPage from "../pages/loading";
1310
import * as LoginPage from "../pages/login";
14-
import * as ResultFilters from "../elements/account/result-filters";
15-
import * as TagController from "./tag-controller";
1611
import * as RegisterCaptchaModal from "../modals/register-captcha";
17-
import * as LastSignedOutResultModal from "../modals/last-signed-out-result";
18-
import * as URLHandler from "../utils/url-handler";
1912
import * as Account from "../pages/account";
20-
import * as Alerts from "../elements/alerts";
21-
import * as AccountSettings from "../pages/account-settings";
2213
import {
2314
GoogleAuthProvider,
2415
GithubAuthProvider,
@@ -37,17 +28,12 @@ import {
3728
signInWithPopup,
3829
resetIgnoreAuthCallback,
3930
} from "../firebase";
40-
import {
41-
hideFavoriteQuoteLength,
42-
showFavoriteQuoteLength,
43-
} from "../test/test-config";
4431
import * as ConnectionState from "../states/connection";
4532
import { navigate } from "./route-controller";
46-
import * as PSA from "../elements/psa";
4733
import { getActiveFunboxesWithFunction } from "../test/funbox/list";
48-
import { Snapshot } from "../constants/default-snapshot";
4934
import * as Sentry from "../sentry";
5035
import { tryCatch } from "@monkeytype/util/trycatch";
36+
import * as AuthEvent from "../observables/auth-event";
5137

5238
export const gmailProvider = new GoogleAuthProvider();
5339
export const githubProvider = new GithubAuthProvider();
@@ -87,12 +73,58 @@ async function getDataAndInit(): Promise<boolean> {
8773
LoadingPage.updateText("Downloading user data...");
8874
await LoadingPage.showBar();
8975
const snapshot = await DB.initSnapshot();
90-
if (snapshot !== false) {
91-
Sentry.setUser(snapshot.uid, snapshot.name);
76+
77+
if (snapshot === false) {
78+
throw new Error(
79+
"Snapshot didn't initialize due to lacking authentication even though user is authenticated"
80+
);
81+
}
82+
83+
Sentry.setUser(snapshot.uid, snapshot.name);
84+
if (snapshot.needsToChangeName) {
85+
Notifications.addPSA(
86+
"You need to update your account name. <a class='openNameChange'>Click here</a> to change it and learn more about why.",
87+
-1,
88+
undefined,
89+
true,
90+
undefined,
91+
true
92+
);
93+
}
94+
if (ActivePage.get() === "loading") {
95+
LoadingPage.updateBar(100);
96+
} else {
97+
LoadingPage.updateBar(45);
98+
}
99+
LoadingPage.updateText("Applying settings...");
100+
101+
const areConfigsEqual =
102+
JSON.stringify(Config) === JSON.stringify(snapshot.config);
103+
104+
if (Config === undefined || !areConfigsEqual) {
105+
console.log(
106+
"no local config or local and db configs are different - applying db"
107+
);
108+
await UpdateConfig.apply(snapshot.config);
109+
UpdateConfig.saveFullConfigToLocalStorage(true);
110+
111+
//funboxes might be different and they wont activate on the account page
112+
for (const fb of getActiveFunboxesWithFunction("applyGlobalCSS")) {
113+
fb.functions.applyGlobalCSS();
114+
}
115+
}
116+
if (window.location.pathname === "/account") {
117+
LoadingPage.updateBar(90);
118+
await Account.downloadResults();
119+
}
120+
if (window.location.pathname === "/login") {
121+
navigate("/account");
122+
} else {
123+
navigate();
92124
}
125+
return true;
93126
} catch (error) {
94127
console.error(error);
95-
AccountButton.loading(false);
96128
LoginPage.enableInputs();
97129
$("header nav .view-account").css("opacity", 1);
98130
if (error instanceof DB.SnapshotInitError) {
@@ -120,70 +152,17 @@ async function getDataAndInit(): Promise<boolean> {
120152
}
121153
return false;
122154
}
123-
if (ActivePage.get() === "loading") {
124-
LoadingPage.updateBar(100);
125-
} else {
126-
LoadingPage.updateBar(45);
127-
}
128-
LoadingPage.updateText("Applying settings...");
129-
const snapshot = DB.getSnapshot() as Snapshot;
130-
AccountButton.update(snapshot);
131-
Alerts.setNotificationBubbleVisible(snapshot.inboxUnreadSize > 0);
132-
showFavoriteQuoteLength();
133-
134-
ResultFilters.loadTags(snapshot.tags);
135-
136-
// filters = defaultResultFilters;
137-
void ResultFilters.load();
138-
139-
if (snapshot.needsToChangeName) {
140-
Notifications.addPSA(
141-
"You need to update your account name. <a class='openNameChange'>Click here</a> to change it and learn more about why.",
142-
-1,
143-
undefined,
144-
true,
145-
undefined,
146-
true
147-
);
148-
}
149-
150-
const areConfigsEqual =
151-
JSON.stringify(Config) === JSON.stringify(snapshot.config);
152-
153-
if (Config === undefined || !areConfigsEqual) {
154-
console.log(
155-
"no local config or local and db configs are different - applying db"
156-
);
157-
await UpdateConfig.apply(snapshot.config);
158-
UpdateConfig.saveFullConfigToLocalStorage(true);
159-
160-
//funboxes might be different and they wont activate on the account page
161-
for (const fb of getActiveFunboxesWithFunction("applyGlobalCSS")) {
162-
fb.functions.applyGlobalCSS();
163-
}
164-
}
165-
AccountButton.loading(false);
166-
TagController.loadActiveFromLocalStorage();
167-
if (window.location.pathname === "/account") {
168-
LoadingPage.updateBar(90);
169-
await Account.downloadResults();
170-
}
171-
if (window.location.pathname === "/login") {
172-
navigate("/account");
173-
} else {
174-
navigate();
175-
}
176-
return true;
177155
}
178156

179157
export async function loadUser(_user: UserType): Promise<void> {
180158
// User is signed in.
181159
PageTransition.set(false);
182-
AccountButton.loading(true);
183160
if (!(await getDataAndInit())) {
184161
signOut();
185162
}
186163

164+
AuthEvent.dispatch({ type: "snapshotUpdated", data: { isInitial: true } });
165+
187166
// var displayName = user.displayName;
188167
// var email = user.email;
189168
// var emailVerified = user.emailVerified;
@@ -194,21 +173,14 @@ export async function loadUser(_user: UserType): Promise<void> {
194173
LoginPage.hidePreloader();
195174

196175
// showFavouriteThemesAtTheTop();
197-
198-
if (TestLogic.notSignedInLastResult !== null) {
199-
LastSignedOutResultModal.show();
200-
}
201176
}
202177

203178
export async function onAuthStateChanged(
204179
authInitialisedAndConnected: boolean,
205180
user: UserType | null
206181
): Promise<void> {
207-
const search = window.location.search;
208-
const hash = window.location.hash;
209182
console.debug(`account controller ready`);
210183
if (authInitialisedAndConnected) {
211-
void PSA.show();
212184
console.debug(`auth state changed, user ${user ? "true" : "false"}`);
213185
console.debug(user);
214186
if (user) {
@@ -218,13 +190,8 @@ export async function onAuthStateChanged(
218190
window.history.replaceState("", "", "/login");
219191
}
220192

221-
Settings.hideAccountSection();
222-
AccountButton.update(undefined);
223193
DB.setSnapshot(undefined);
224194
Sentry.clearUser();
225-
setTimeout(() => {
226-
hideFavoriteQuoteLength();
227-
}, 125);
228195
PageTransition.set(false);
229196
navigate();
230197
}
@@ -238,12 +205,10 @@ export async function onAuthStateChanged(
238205
navigate();
239206
}
240207

241-
URLHandler.loadCustomThemeFromUrl(search);
242-
URLHandler.loadTestSettingsFromUrl(search);
243-
URLHandler.loadChallengeFromUrl(search);
244-
void URLHandler.linkDiscord(hash);
245-
246-
AccountSettings.updateUI();
208+
AuthEvent.dispatch({
209+
type: "authStateChanged",
210+
data: { isUserSignedIn: user !== null },
211+
});
247212
}
248213

249214
export async function signIn(email: string, password: string): Promise<void> {
@@ -360,7 +325,7 @@ async function addAuthProvider(
360325
await linkWithPopup(user, provider);
361326
Loader.hide();
362327
Notifications.add(`${providerName} authentication added`, 1);
363-
AccountSettings.updateUI();
328+
AuthEvent.dispatch({ type: "authConfigUpdated" });
364329
} catch (error) {
365330
Loader.hide();
366331
const message = Misc.createErrorMessage(

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ const routes: Route[] = [
146146
];
147147

148148
export function navigate(
149-
url = window.location.pathname + window.location.search,
149+
url = window.location.pathname +
150+
window.location.search +
151+
window.location.hash,
150152
options = {} as NavigateOptions
151153
): void {
152154
if (

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as DB from "../db";
33
import * as ModesNotice from "../elements/modes-notice";
44
import { LocalStorageWithSchema } from "../utils/local-storage-with-schema";
55
import { IdSchema } from "@monkeytype/schemas/util";
6+
import * as AuthEvent from "../observables/auth-event";
67

78
const activeTagsLS = new LocalStorageWithSchema({
89
key: "activeTags",
@@ -75,3 +76,9 @@ export function loadActiveFromLocalStorage(): void {
7576
}
7677
saveActiveToLocalStorage();
7778
}
79+
80+
AuthEvent.subscribe((event) => {
81+
if (event.type === "snapshotUpdated" && event.data.isInitial) {
82+
loadActiveFromLocalStorage();
83+
}
84+
});

frontend/src/ts/db.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { getDefaultConfig } from "./constants/default-config";
3131
import { FunboxMetadata } from "../../../packages/funbox/src/types";
3232
import { getFirstDayOfTheWeek } from "./utils/date-and-time";
3333
import { Language } from "@monkeytype/schemas/languages";
34+
import * as AuthEvent from "./observables/auth-event";
3435

3536
let dbSnapshot: Snapshot | undefined;
3637
const firstDayOfTheWeek = getFirstDayOfTheWeek();
@@ -70,6 +71,8 @@ export function setSnapshot(newSnapshot: Snapshot | undefined): void {
7071
dbSnapshot.verified = originalVerified;
7172
dbSnapshot.lbOptOut = lbOptOut;
7273
}
74+
75+
AuthEvent.dispatch({ type: "snapshotUpdated", data: { isInitial: false } });
7376
}
7477

7578
export async function initSnapshot(): Promise<Snapshot | false> {

frontend/src/ts/elements/account-button.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import {
55
} from "../controllers/user-flag-controller";
66
import { isAuthenticated } from "../firebase";
77
import * as XpBar from "./xp-bar";
8-
import { Snapshot } from "../constants/default-snapshot";
98
import { getAvatarElement } from "../utils/discord-avatar";
9+
import * as AuthEvent from "../observables/auth-event";
10+
import { getSnapshot } from "../db";
1011

1112
export function hide(): void {
1213
$("nav .accountButtonAndMenu").addClass("hidden");
@@ -38,11 +39,15 @@ export function updateAvatar(avatar?: {
3839
$("header nav .view-account .avatar").replaceWith(element);
3940
}
4041

41-
export function update(snapshot: Snapshot | undefined): void {
42+
export function update(): void {
4243
if (isAuthenticated()) {
43-
// this function is called after the snapshot is loaded (awaited), so it should be fine
44-
const { xp, name } = snapshot as Snapshot;
44+
const snapshot = getSnapshot();
4545

46+
if (snapshot === undefined) return;
47+
48+
const { xp, name } = snapshot;
49+
50+
loading(false);
4651
updateName(name);
4752
updateFlags(snapshot ?? {});
4853
XpBar.setXp(xp);
@@ -76,3 +81,12 @@ const coarse = window.matchMedia("(pointer:coarse)")?.matches;
7681
if (coarse) {
7782
$("nav .accountButtonAndMenu .textButton.view-account").attr("href", "");
7883
}
84+
85+
AuthEvent.subscribe((event) => {
86+
if (
87+
(event.type === "authStateChanged" && !event.data.isUserSignedIn) ||
88+
event.type === "snapshotUpdated"
89+
) {
90+
update();
91+
}
92+
});

frontend/src/ts/elements/account/result-filters.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ import {
1616
import { LocalStorageWithSchema } from "../../utils/local-storage-with-schema";
1717
import defaultResultFilters from "../../constants/default-result-filters";
1818
import { getAllFunboxes } from "@monkeytype/funbox";
19-
import { Snapshot, SnapshotUserTag } from "../../constants/default-snapshot";
19+
import { Snapshot } from "../../constants/default-snapshot";
2020
import { LanguageList } from "../../constants/languages";
21+
import * as AuthEvent from "../../observables/auth-event";
2122
import { sanitize } from "../../utils/sanitize";
2223

2324
export function mergeWithDefaultFilters(
@@ -272,8 +273,12 @@ function setAllFilters(group: ResultFiltersGroup, value: boolean): void {
272273
});
273274
}
274275

275-
export function loadTags(tags: SnapshotUserTag[]): void {
276-
tags.forEach((tag) => {
276+
export function loadTags(): void {
277+
const snapshot = DB.getSnapshot();
278+
279+
if (snapshot === undefined) return;
280+
281+
snapshot.tags.forEach((tag) => {
277282
defaultResultFilters.tags[tag._id] = true;
278283
});
279284
}
@@ -933,3 +938,10 @@ function verifyResultFiltersStructure(filterIn: ResultFilters): ResultFilters {
933938

934939
return filter;
935940
}
941+
942+
AuthEvent.subscribe((event) => {
943+
if (event.type === "snapshotUpdated" && event.data.isInitial) {
944+
loadTags();
945+
void load();
946+
}
947+
});

frontend/src/ts/elements/alerts.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import AnimatedModal from "../utils/animated-modal";
1111
import { updateXp as accountPageUpdateProfile } from "./profile";
1212
import { MonkeyMail } from "@monkeytype/schemas/users";
1313
import * as XPBar from "../elements/xp-bar";
14+
import * as AuthEvent from "../observables/auth-event";
1415

1516
let accountAlerts: MonkeyMail[] = [];
1617
let maxMail = 0;
@@ -388,6 +389,20 @@ NotificationEvent.subscribe((message, level, customTitle) => {
388389
}
389390
});
390391

392+
AuthEvent.subscribe((event) => {
393+
if (event.type === "snapshotUpdated" && event.data.isInitial) {
394+
const snapshot = DB.getSnapshot();
395+
setNotificationBubbleVisible((snapshot?.inboxUnreadSize ?? 0) > 0);
396+
}
397+
if (event.type === "authStateChanged" && !event.data.isUserSignedIn) {
398+
setNotificationBubbleVisible(false);
399+
accountAlerts = [];
400+
mailToMarkRead = [];
401+
mailToDelete = [];
402+
$("#alertsPopup .accountAlerts .list").empty();
403+
}
404+
});
405+
391406
const modal = new AnimatedModal({
392407
dialogId: "alertsPopup",
393408
customAnimations: {

0 commit comments

Comments
 (0)