CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/account/store.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { List, Map } from "immutable";
7
import { reduce } from "lodash";
8
import { store as customizeStore } from "@cocalc/frontend/customize";
9
import { make_valid_name } from "@cocalc/util/misc";
10
import { Store } from "@cocalc/util/redux/Store";
11
import { get_total_upgrades } from "@cocalc/util/upgrades";
12
import { AccountState } from "./types";
13
14
declare var DEBUG: boolean;
15
16
// Define account store
17
export class AccountStore extends Store<AccountState> {
18
// User type
19
// - 'public' : user is not signed in at all, and not trying to sign in
20
// - 'signing_in' : user is currently waiting to see if sign-in attempt will succeed
21
// - 'signed_in' : user has successfully authenticated and has an id
22
constructor(name, redux) {
23
super(name, redux);
24
this.setup_selectors();
25
}
26
27
get_user_type(): string {
28
return this.get("user_type");
29
}
30
31
get_account_id(): string {
32
return this.get("account_id");
33
}
34
35
selectors = {
36
is_anonymous: {
37
fn: () => {
38
return is_anonymous(
39
this.get("is_logged_in"),
40
this.get("email_address"),
41
this.get("passports"),
42
this.get("lti_id"),
43
);
44
},
45
dependencies: [
46
"email_address",
47
"passports",
48
"is_logged_in",
49
"lti_id",
50
] as const,
51
},
52
is_admin: {
53
fn: () => {
54
const groups = this.get("groups");
55
return !!groups && groups.includes("admin");
56
},
57
dependencies: ["groups"] as const,
58
},
59
};
60
61
get_terminal_settings(): { [key: string]: any } | undefined {
62
return this.get("terminal") ? this.get("terminal").toJS() : undefined;
63
}
64
65
get_editor_settings(): { [key: string]: any } | undefined {
66
return this.get("editor_settings")
67
? this.get("editor_settings").toJS()
68
: undefined;
69
}
70
71
get_fullname(): string {
72
const first_name = this.get("first_name");
73
const last_name = this.get("last_name");
74
if (first_name == null && last_name == null) {
75
return "Anonymous";
76
} else if (first_name == undefined) {
77
return last_name ?? "";
78
} else if (last_name == undefined) {
79
return first_name ?? "";
80
} else {
81
return `${first_name} ${last_name}`;
82
}
83
}
84
85
get_first_name(): string {
86
return this.get("first_name", "Anonymous");
87
}
88
89
get_color(): string {
90
return this.getIn(
91
["profile", "color"],
92
this.get("account_id", "f00").slice(0, 6),
93
);
94
}
95
96
get_username(): string {
97
return make_valid_name(this.get_fullname());
98
}
99
100
get_email_address(): string | undefined {
101
return this.get("email_address");
102
}
103
104
get_confirm_close(): string {
105
return this.getIn(["other_settings", "confirm_close"]);
106
}
107
108
// Total upgrades this user is paying for (sum of all upgrades from subscriptions)
109
get_total_upgrades(): { [key: string]: number } | undefined {
110
const stripe_data = this.getIn([
111
"stripe_customer",
112
"subscriptions",
113
"data",
114
]);
115
// to fake having upgrades, type this in the console
116
// cc.redux.getStore('account').fake_upgrades = true
117
if (DEBUG && (this as any).fake_upgrades && !stripe_data) {
118
// fake debugging data
119
return get_total_upgrades({}, true);
120
}
121
return stripe_data && get_total_upgrades(stripe_data.toJS());
122
}
123
124
hasLegacyUpgrades = () => {
125
return this.getIn(["stripe_customer", "subscriptions", "data"]) != null;
126
};
127
128
// uses the total upgrades information to determine, if this is a paying member
129
// TODO: this is not used anywhere; but, if it was, it should also take into account
130
// being a license manager...
131
is_paying_member(): boolean {
132
const ups = this.get_total_upgrades();
133
return ups != null && reduce(ups, (a: number, b: number) => a + b, 0) > 0;
134
}
135
136
get_page_size(): number {
137
return this.getIn(["other_settings", "page_size"], 500);
138
}
139
140
isTourDone(tour: string): boolean {
141
const tours = this.get("tours");
142
if (!tours) return false;
143
return tours.includes(tour) || tours.includes("all");
144
}
145
}
146
147
// A user is anonymous if they have not provided a way to sign
148
// in later (besides their cookie), i.e., if they have no
149
// passport strategies and have not provided an email address.
150
// In is_personal mode, user is never anonymous.
151
function is_anonymous(
152
is_logged_in: boolean,
153
email_address: string | undefined | null,
154
passports: Map<string, any> | undefined | null,
155
lti_id: List<string> | undefined | null,
156
): boolean {
157
if (!is_logged_in) {
158
return false;
159
}
160
if (email_address) {
161
return false;
162
}
163
if (passports != null && passports.size > 0) {
164
return false;
165
}
166
if (lti_id != null && lti_id.size > 0) {
167
return false;
168
}
169
if (customizeStore.get("is_personal")) {
170
return false;
171
}
172
return true;
173
}
174
175