Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/chat/video/video-chat.ts
1496 views
1
import { client_db } from "@cocalc/util/schema";
2
import { webapp_client } from "@cocalc/frontend/webapp-client";
3
import { len, trunc_middle } from "@cocalc/util/misc";
4
import { redux } from "@cocalc/frontend/app-framework";
5
import { open_new_tab } from "../../misc/open-browser-tab";
6
import { getSideChatActions } from "@cocalc/frontend/frame-editors/generic/chat";
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
this.openVideoChatWindow();
88
redux.getActions("file_use")?.mark_file(this.project_id, this.path, "chat");
89
const sideChatActions =
90
getSideChatActions({
91
project_id: this.project_id,
92
path: this.path,
93
}) ??
94
redux.getEditorActions(this.project_id, this.path)?.getChatActions();
95
sideChatActions?.sendChat({
96
input: `[${this.getUserName()} joined Video Chat](${this.url()})`,
97
});
98
setTimeout(() => sideChatActions?.scrollToBottom(), 100);
99
setTimeout(() => sideChatActions?.scrollToBottom(), 1000);
100
};
101
102
// The canonical secret chatroom id.
103
private chatroomId = (): string => {
104
const secret_token = redux
105
.getStore("projects")
106
.getIn(["project_map", this.project_id, "secret_token"]);
107
return client_db.sha1(secret_token, this.path);
108
};
109
110
url = (): string => {
111
const room_id = this.chatroomId();
112
return `${VIDEO_CHAT_SERVER}/${room_id}`;
113
};
114
115
// Open the video chat window, if it isn't already opened
116
private openVideoChatWindow = (): void => {
117
const room_id = this.chatroomId();
118
if (videoWindows[room_id]) {
119
return;
120
}
121
122
const chatWindowIsOpen = () => {
123
return redux
124
.getActions("file_use")
125
?.mark_file(this.project_id, this.path, "video", 0);
126
};
127
128
chatWindowIsOpen();
129
this.intervalId = setInterval(
130
chatWindowIsOpen,
131
VIDEO_UPDATE_INTERVAL_MS * 0.8,
132
);
133
134
//const title = `CoCalc Video Chat: ${trunc_middle(this.path, 30)}`;
135
const w = videoWindow(this.url());
136
// https://github.com/sagemathinc/cocalc/issues/3648
137
if (w == null) {
138
return;
139
}
140
videoWindows[room_id] = w;
141
// disabled -- see https://github.com/sagemathinc/cocalc/issues/1899
142
//w.addEventListener "unload", =>
143
// @close_video_chat_window()
144
// workaround for https://github.com/sagemathinc/cocalc/issues/1899
145
const pollWindow = setInterval(() => {
146
if (w.closed !== false) {
147
// != is required for compatibility with Opera
148
clearInterval(pollWindow);
149
this.closeVideoChatWindow();
150
}
151
}, 1000);
152
};
153
154
// User wants to close the video chat window, but not via just clicking the
155
// close button on the popup window
156
private closeVideoChatWindow = (): void => {
157
const room_id = this.chatroomId();
158
const w = videoWindows[room_id];
159
if (!w) {
160
return;
161
}
162
redux
163
.getActions("file_use")
164
?.mark_file(this.project_id, this.path, "video", 0, true, new Date(0));
165
if (this.intervalId) {
166
clearInterval(this.intervalId);
167
delete this.intervalId;
168
}
169
delete videoWindows[room_id];
170
w.close();
171
};
172
}
173
174