Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/app/query-params.ts
5711 views
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
// Initialize various things related to the overall page and query params (e.g., fullscreen).
7
import { redux } from "@cocalc/frontend/app-framework";
8
import target from "@cocalc/frontend/client/handle-target";
9
import { appBasePath } from "@cocalc/frontend/customize/app-base-path";
10
import { COCALC_FULLSCREEN } from "@cocalc/frontend/fullscreen";
11
import { parse_target } from "@cocalc/frontend/history";
12
import {
13
get_local_storage,
14
set_local_storage,
15
} from "@cocalc/frontend/misc/local-storage";
16
import { QueryParams } from "@cocalc/frontend/misc/query-params";
17
import { A11Y } from "@cocalc/util/consts/ui";
18
import { is_valid_uuid_string } from "@cocalc/util/misc";
19
20
function init_fullscreen_mode(): void {
21
const actions = redux.getActions("page");
22
// enable fullscreen mode upon loading a URL like /app?fullscreen and
23
// additionally kiosk-mode upon /app?fullscreen=kiosk
24
if (COCALC_FULLSCREEN === "kiosk") {
25
actions.set_fullscreen("kiosk");
26
// We also check if user is loading a specific project in kiosk mode
27
// (which is the only thing they should ever do!), and in that
28
// case we record the project_id, so that we can make various
29
// query optimizations elsewhere.
30
const x = parse_target(target);
31
if (x.page === "project" && x.target != null) {
32
const kiosk_project_id = x.target.slice(0, 36);
33
if (is_valid_uuid_string(kiosk_project_id)) {
34
actions.setState({ kiosk_project_id });
35
}
36
}
37
} else if (COCALC_FULLSCREEN === "default") {
38
actions.set_fullscreen("default");
39
// We no longer need fullscreen in the query parameter:
40
QueryParams.remove("fullscreen");
41
} else if (COCALC_FULLSCREEN === "project") {
42
actions.set_fullscreen("project");
43
}
44
}
45
46
function init_api_key(): void {
47
const actions = redux.getActions("page");
48
const get_api_key_query_value = QueryParams.get("get_api_key");
49
if (get_api_key_query_value) {
50
actions.set_get_api_key(get_api_key_query_value);
51
actions.set_fullscreen("project");
52
}
53
}
54
55
function init_session(): void {
56
const actions = redux.getActions("page");
57
// configure the session
58
// This makes it so the default session is 'default' and there is no
59
// way to NOT have a session, except via session=, which is treated
60
// as "no session" (also no session for kiosk mode).
61
// Note that we never have a session in kiosk mode, since you can't
62
// access the other files.
63
// If click on link with ?session=, then you get no session, e.g,. this
64
// is used for doing a pop-out of a single file. Should have no impact
65
// on sessions at all.
66
if (COCALC_FULLSCREEN === "kiosk") {
67
actions.set_session(""); // no session
68
} else {
69
const key = `session${appBasePath}`;
70
const querySession = QueryParams.get("session");
71
let session: any = querySession ?? get_local_storage(key) ?? "default";
72
73
if (typeof session != "string") {
74
// should never happen, but of course it could since user could put anything in URL query params
75
// We just reset to default in this case.
76
session = "default";
77
}
78
actions.set_session(session);
79
if (session) {
80
// So when you don't give session= param in this browser in the future
81
// it defaults to the one you did use (or "default").
82
set_local_storage(key, session);
83
}
84
}
85
// Do not need or want it in our URL once we've consumed it. Critical to
86
// not have session in the URL, so we can share url's without infected
87
// other user's session.
88
QueryParams.remove("session");
89
}
90
91
function parse_accessibility_param(param: string): boolean | null {
92
if (param === "true" || param === "on" || param === "1") {
93
return true;
94
}
95
if (param === "false" || param === "off" || param === "0") {
96
return false;
97
}
98
return null;
99
}
100
101
async function init_accessibility(): Promise<void> {
102
// Handle accessibility query parameter
103
// If ?accessibility=true or =on, enable accessibility mode permanently
104
// If ?accessibility=false or =off, disable it permanently
105
// This allows sharing URLs that automatically enable accessibility
106
const accessibilityParam = QueryParams.get(A11Y);
107
if (accessibilityParam == null) {
108
return;
109
}
110
111
const enabled = parse_accessibility_param(accessibilityParam);
112
QueryParams.remove(A11Y);
113
114
if (enabled == null) {
115
return;
116
}
117
118
try {
119
// Wait for account store to be ready before setting accessibility
120
const store = redux.getStore("account");
121
if (!store || typeof store.async_wait !== "function") {
122
console.warn("Account store not ready");
123
return;
124
}
125
126
await store.async_wait({
127
until: () => store.get_account_id() != null,
128
timeout: 0,
129
});
130
131
// Preserve existing accessibility settings
132
const existingSettingsStr = store.getIn(["other_settings", A11Y]);
133
let existingSettings = { enabled: false };
134
if (existingSettingsStr) {
135
try {
136
existingSettings = JSON.parse(existingSettingsStr);
137
} catch {
138
// Ignore parse errors, use default
139
}
140
}
141
142
// Merge with new enabled value
143
const settings = { ...existingSettings, enabled };
144
const accountActions = redux.getActions("account");
145
accountActions.set_other_settings(A11Y, JSON.stringify(settings));
146
} catch (err) {
147
console.warn("Failed to set accessibility from query param:", err);
148
}
149
}
150
151
export function init_query_params(): void {
152
init_fullscreen_mode();
153
init_api_key();
154
init_session();
155
// Run accessibility init in background without blocking
156
// to avoid delaying other store initializations
157
init_accessibility();
158
}
159
160