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/client/users.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 { AsyncCall } from "./client";
7
import { User } from "../frame-editors/generic/client";
8
import { isChatBot, chatBotName } from "@cocalc/frontend/account/chatbot";
9
import api from "./api";
10
import TTL from "@isaacs/ttlcache";
11
import { reuseInFlight } from "@cocalc/util/reuse-in-flight";
12
import * as message from "@cocalc/util/message";
13
14
const nameCache = new TTL({ ttl: 60 * 1000 });
15
16
export class UsersClient {
17
private async_call: AsyncCall;
18
19
constructor(_call: Function, async_call: AsyncCall) {
20
this.async_call = async_call;
21
}
22
23
user_search = reuseInFlight(
24
async (opts: {
25
query: string;
26
limit?: number;
27
active?: string; // if given, would restrict to users active this recently
28
admin?: boolean; // admins can do an admin version of the query, which also does substring searches on email address (not just name)
29
only_email?: boolean; // search only via email address
30
}): Promise<User[]> => {
31
if (opts.limit == null) opts.limit = 20;
32
if (opts.active == null) opts.active = "";
33
34
const { results } = await this.async_call({
35
message: message.user_search({
36
query: opts.query,
37
limit: opts.limit,
38
admin: opts.admin,
39
active: opts.active,
40
only_email: opts.only_email
41
}),
42
});
43
return results;
44
},
45
);
46
47
// Gets username with given account_id. We use caching and aggregate to
48
// makes it so this never calls to the backend more than once at a time
49
// (per minute) for a given account_id.
50
get_username = reuseInFlight(
51
async (
52
account_id: string,
53
): Promise<{ first_name: string; last_name: string }> => {
54
if (isChatBot(account_id)) {
55
return { first_name: chatBotName(account_id), last_name: "" };
56
}
57
const v = await this.getNames([account_id]);
58
const u = v[account_id];
59
if (u == null) {
60
throw Error(`no user with account_id ${account_id}`);
61
}
62
return u;
63
},
64
);
65
66
// get map from account_id to first_name, last_name (or undefined if no account); cached
67
// for about a minute client side.
68
getNames = reuseInFlight(async (account_ids: string[]) => {
69
const x: {
70
[account_id: string]:
71
| { first_name: string; last_name: string }
72
| undefined;
73
} = {};
74
const v: string[] = [];
75
for (const account_id of account_ids) {
76
if (nameCache.has(account_id)) {
77
x[account_id] = nameCache.get(account_id);
78
} else {
79
v.push(account_id);
80
}
81
}
82
if (v.length > 0) {
83
const { names } = await api("/accounts/get-names", { account_ids: v });
84
for (const account_id of v) {
85
// iterate over v to record accounts that don't exist too
86
x[account_id] = names[account_id];
87
nameCache.set(account_id, names[account_id]);
88
}
89
}
90
return x;
91
});
92
}
93
94