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