Path: blob/master/src/packages/frontend/chat/video/video-chat.ts
5968 views
import { client_db } from "@cocalc/util/schema";1import { webapp_client } from "@cocalc/frontend/webapp-client";2import { len, trunc_middle } from "@cocalc/util/misc";3import { redux } from "@cocalc/frontend/app-framework";4import { open_new_tab } from "../../misc/open-browser-tab";56const VIDEO_CHAT_SERVER = "https://meet.jit.si";7const VIDEO_UPDATE_INTERVAL_MS = 30 * 1000;89// Create pop-up window for video chat10function videoWindow(url: string) {11return open_new_tab(url, true, { noopener: false });12}1314const videoWindows = {};1516export class VideoChat {17private intervalId?: any;18private project_id: string;19private path: string;2021constructor({ project_id, path }: { project_id: string; path: string }) {22this.project_id = project_id;23this.path = path;24}2526close = () => {27// this.closeVideoChatWindow();28delete this.intervalId;29};3031weAreChatting = (): boolean => {32const { account_id } = webapp_client;33if (account_id == null) {34return false;35}36const timestamp: Date | undefined = this.getUsers()?.[account_id];37return (38timestamp != null &&39webapp_client.server_time().valueOf() - timestamp.valueOf() <=40VIDEO_UPDATE_INTERVAL_MS41);42};4344numUsersChatting = (): number => {45return len(this.getUsers());46};4748getUserName = (): string | undefined => {49const users = redux.getStore("users");50const { account_id } = webapp_client;51if (account_id == null) {52return;53}54return users?.get_name(account_id);55};5657getUserNames = (): string[] => {58const users = redux.getStore("users");59const v: string[] = [];60for (const account_id in this.getUsers()) {61const name = users.get_name(account_id)?.trim();62if (name) {63v.push(trunc_middle(name, 25));64}65}66return v;67};6869private getUsers = (): { [account_id: string]: Date } => {70// Users is a map {account_id:timestamp of last chat file marking}71return (72redux.getStore("file_use")?.get_video_chat_users({73project_id: this.project_id,74path: this.path,75ttl: 1.3 * VIDEO_UPDATE_INTERVAL_MS,76}) ?? {}77);78};7980stopChatting = () => {81this.closeVideoChatWindow();82};8384startChatting = (actions) => {85this.openVideoChatWindow();86redux.getActions("file_use")?.mark_file(this.project_id, this.path, "chat");87setTimeout(() => actions?.scrollToBottom(), 100);88setTimeout(() => actions?.scrollToBottom(), 1000);89return `[${this.getUserName()} joined Video Chat](${this.url()})`90};9192// The canonical secret chatroom id.93private chatroomId = (): string => {94const secret_token = redux95.getStore("projects")96.getIn(["project_map", this.project_id, "secret_token"]);97return client_db.sha1(secret_token, this.path);98};99100url = (): string => {101const room_id = this.chatroomId();102return `${VIDEO_CHAT_SERVER}/${room_id}`;103};104105// Open the video chat window, if it isn't already opened106private openVideoChatWindow = (): void => {107const room_id = this.chatroomId();108if (videoWindows[room_id]) {109return;110}111112const chatWindowIsOpen = () => {113return redux114.getActions("file_use")115?.mark_file(this.project_id, this.path, "video", 0);116};117118chatWindowIsOpen();119this.intervalId = setInterval(120chatWindowIsOpen,121VIDEO_UPDATE_INTERVAL_MS * 0.8,122);123124//const title = `CoCalc Video Chat: ${trunc_middle(this.path, 30)}`;125const w = videoWindow(this.url());126// https://github.com/sagemathinc/cocalc/issues/3648127if (w == null) {128return;129}130videoWindows[room_id] = w;131// disabled -- see https://github.com/sagemathinc/cocalc/issues/1899132//w.addEventListener "unload", =>133// @close_video_chat_window()134// workaround for https://github.com/sagemathinc/cocalc/issues/1899135const pollWindow = setInterval(() => {136if (w.closed !== false) {137// != is required for compatibility with Opera138clearInterval(pollWindow);139this.closeVideoChatWindow();140}141}, 1000);142};143144// User wants to close the video chat window, but not via just clicking the145// close button on the popup window146private closeVideoChatWindow = (): void => {147const room_id = this.chatroomId();148const w = videoWindows[room_id];149if (!w) {150return;151}152redux153.getActions("file_use")154?.mark_file(this.project_id, this.path, "video", 0, true, new Date(0));155if (this.intervalId) {156clearInterval(this.intervalId);157delete this.intervalId;158}159delete videoWindows[room_id];160w.close();161};162}163164165