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/chat/video/video-chat.ts
Views: 687
1
import { client_db } from "@cocalc/util/schema";
2
import { alert_message } from "@cocalc/frontend/alerts";
3
import { webapp_client } from "@cocalc/frontend/webapp-client";
4
import { len, trunc_middle } from "@cocalc/util/misc";
5
import { redux } from "@cocalc/frontend/app-framework";
6
import { open_new_tab } from "../../misc/open-browser-tab";
7
8
const VIDEO_CHAT_SERVER = "https://meet.jit.si";
9
const VIDEO_UPDATE_INTERVAL_MS = 30 * 1000;
10
11
// Create pop-up window for video chat
12
function videoWindow(url: string) {
13
return open_new_tab(url, true, { noopener: false });
14
}
15
16
const videoWindows = {};
17
18
export class VideoChat {
19
private intervalId?: any;
20
private project_id: string;
21
private path: string;
22
23
constructor({ project_id, path }: { project_id: string; path: string }) {
24
this.project_id = project_id;
25
this.path = path;
26
}
27
28
close = () => {
29
this.closeVideoChatWindow();
30
delete this.intervalId;
31
};
32
33
weAreChatting = (): boolean => {
34
const { account_id } = webapp_client;
35
if (account_id == null) {
36
return false;
37
}
38
const timestamp: Date | undefined = this.getUsers()?.[account_id];
39
return (
40
timestamp != null &&
41
webapp_client.server_time().valueOf() - timestamp.valueOf() <=
42
VIDEO_UPDATE_INTERVAL_MS
43
);
44
};
45
46
numUsersChatting = (): number => {
47
return len(this.getUsers());
48
};
49
50
getUserName = (): string | undefined => {
51
const users = redux.getStore("users");
52
const { account_id } = webapp_client;
53
if (account_id == null) {
54
return;
55
}
56
return users?.get_name(account_id);
57
};
58
59
getUserNames = (): string[] => {
60
const users = redux.getStore("users");
61
const v: string[] = [];
62
for (const account_id in this.getUsers()) {
63
const name = users.get_name(account_id)?.trim();
64
if (name) {
65
v.push(trunc_middle(name, 25));
66
}
67
}
68
return v;
69
};
70
71
private getUsers = (): { [account_id: string]: Date } => {
72
// Users is a map {account_id:timestamp of last chat file marking}
73
return (
74
redux.getStore("file_use")?.get_video_chat_users({
75
project_id: this.project_id,
76
path: this.path,
77
ttl: 1.3 * VIDEO_UPDATE_INTERVAL_MS,
78
}) ?? {}
79
);
80
};
81
82
stopChatting = () => {
83
this.closeVideoChatWindow();
84
};
85
86
startChatting = () => {
87
redux.getActions("file_use")?.mark_file(this.project_id, this.path, "chat");
88
this.openVideoChatWindow();
89
};
90
91
// The canonical secret chatroom id.
92
private chatroomId = (): string => {
93
const secret_token = redux
94
.getStore("projects")
95
.getIn(["project_map", this.project_id, "status", "secret_token"]);
96
if (!secret_token) {
97
alert_message({
98
type: "error",
99
message: "You MUST be a project collaborator -- video chat will fail.",
100
});
101
}
102
return client_db.sha1(secret_token, this.path);
103
};
104
105
url = (): string => {
106
const room_id = this.chatroomId();
107
return `${VIDEO_CHAT_SERVER}/${room_id}`;
108
};
109
110
// Open the video chat window, if it isn't already opened
111
private openVideoChatWindow = (): void => {
112
const room_id = this.chatroomId();
113
if (videoWindows[room_id]) {
114
return;
115
}
116
117
const chatWindowIsOpen = () => {
118
return redux
119
.getActions("file_use")
120
?.mark_file(this.project_id, this.path, "video", 0);
121
};
122
123
chatWindowIsOpen();
124
this.intervalId = setInterval(
125
chatWindowIsOpen,
126
VIDEO_UPDATE_INTERVAL_MS * 0.8,
127
);
128
129
//const title = `CoCalc Video Chat: ${trunc_middle(this.path, 30)}`;
130
const w = videoWindow(this.url());
131
// https://github.com/sagemathinc/cocalc/issues/3648
132
if (w == null) {
133
return;
134
}
135
videoWindows[room_id] = w;
136
// disabled -- see https://github.com/sagemathinc/cocalc/issues/1899
137
//w.addEventListener "unload", =>
138
// @close_video_chat_window()
139
// workaround for https://github.com/sagemathinc/cocalc/issues/1899
140
const pollWindow = setInterval(() => {
141
if (w.closed !== false) {
142
// != is required for compatibility with Opera
143
clearInterval(pollWindow);
144
this.closeVideoChatWindow();
145
}
146
}, 1000);
147
};
148
149
// User wants to close the video chat window, but not via just clicking the
150
// close button on the popup window
151
private closeVideoChatWindow = (): void => {
152
const room_id = this.chatroomId();
153
const w = videoWindows[room_id];
154
if (!w) {
155
return;
156
}
157
redux
158
.getActions("file_use")
159
?.mark_file(this.project_id, this.path, "video", 0, true, new Date(0));
160
if (this.intervalId) {
161
clearInterval(this.intervalId);
162
delete this.intervalId;
163
}
164
delete videoWindows[room_id];
165
w.close();
166
};
167
}
168
169