Skip to content

Commit 1aeba2f

Browse files
committed
video chat: make menu item work, and also clicking button again or closing file also closes chat
1 parent 295929b commit 1aeba2f

File tree

7 files changed

+118
-121
lines changed

7 files changed

+118
-121
lines changed

src/packages/frontend/chat/chatroom.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -150,16 +150,7 @@ export function ChatRoom({
150150

151151
function render_video_chat_button() {
152152
if (project_id == null || path == null) return;
153-
return (
154-
<VideoChatButton
155-
project_id={project_id}
156-
path={path}
157-
sendChat={(value) => {
158-
actions.sendChat({ input: value });
159-
actions.scrollToBottom();
160-
}}
161-
/>
162-
);
153+
return <VideoChatButton actions={actions} />;
163154
}
164155

165156
function isValidFilterRecentCustom(): boolean {

src/packages/frontend/chat/side-chat.tsx

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,7 @@ export default function SideChat({
128128
marginTop: "-5px",
129129
}}
130130
>
131-
<VideoChatButton
132-
project_id={project_id}
133-
path={path}
134-
sendChat={(value) => {
135-
const actions = redux.getEditorActions(
136-
project_id,
137-
path,
138-
) as ChatActions;
139-
actions.sendChat({ input: value });
140-
actions.scrollToBottom();
141-
}}
142-
/>
131+
<VideoChatButton actions={actions} />
143132
<Tooltip title="Show TimeTravel change history of this side chat.">
144133
<Button
145134
onClick={() => {

src/packages/frontend/chat/video/launch-button.tsx

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,50 @@
11
import { Button, Popover } from "antd";
22
import { debounce } from "lodash";
33
import type { CSSProperties, ReactNode } from "react";
4+
import { useMemo } from "react";
45
import { useInterval } from "react-interval-hook";
5-
6-
import {
7-
React,
8-
useRef,
9-
useState,
10-
useTypedRedux,
11-
} from "@cocalc/frontend/app-framework";
6+
import type { ChatActions } from "../actions";
7+
import { React, useState, useTypedRedux } from "@cocalc/frontend/app-framework";
128
import { Icon } from "@cocalc/frontend/components";
13-
import { user_activity } from "@cocalc/frontend/tracker";
14-
import { VideoChat } from "./video-chat";
159

1610
const VIDEO_UPDATE_INTERVAL_MS = 30 * 1000;
1711
// jit.si doesn't seem to have a limit...?
1812
const VIDEO_CHAT_LIMIT = 99999;
1913

2014
interface Props {
21-
project_id: string;
22-
path: string;
23-
sendChat?: (string) => void;
15+
actions: ChatActions;
2416
style?: CSSProperties;
2517
label?: ReactNode;
2618
}
2719

2820
export default function VideoChatButton({
29-
project_id,
30-
path,
31-
sendChat,
21+
actions,
3222
style: style0,
3323
label,
3424
}: Props) {
3525
// to know if somebody else has video chat opened for this file
3626
// @ts-ignore
3727
const file_use = useTypedRedux("file_use", "file_use");
3828

39-
// so we can exclude ourselves
40-
const account_id: string = useTypedRedux("account", "account_id");
41-
4229
const [counter, set_counter] = useState<number>(0); // to force updates periodically.
4330
useInterval(() => set_counter(counter + 1), VIDEO_UPDATE_INTERVAL_MS / 2);
44-
45-
const video_chat = useRef(new VideoChat(project_id, path, account_id));
31+
const videoChat = useMemo(
32+
() => actions.frameTreeActions.getVideoChat(),
33+
[actions],
34+
);
4635

4736
const click_video_button = debounce(
4837
() => {
49-
if (video_chat.current.we_are_chatting()) {
38+
if (videoChat.weAreChatting()) {
5039
// we are chatting, so stop chatting
51-
video_chat.current.stop_chatting();
52-
user_activity("side_chat", "stop_video");
40+
videoChat.stopChatting();
5341
} else {
54-
video_chat.current.start_chatting(); // not chatting, so start
55-
user_activity("side_chat", "start_video");
56-
if (sendChat != null) {
57-
sendChat(
58-
`${
59-
video_chat.current.get_user_name() ?? "User"
60-
} joined [the video chat](${video_chat.current.url()}).`,
61-
);
62-
}
42+
videoChat.startChatting(); // not chatting, so start
43+
actions.sendChat({
44+
input: `${
45+
videoChat.getUserName() ?? "User"
46+
} joined [the video chat](${videoChat.url()}).`,
47+
});
6348
}
6449
},
6550
750,
@@ -75,14 +60,14 @@ export default function VideoChatButton({
7560
<hr />
7661
There following {num_users_chatting} people are using video chat:
7762
<br />
78-
{video_chat.current.get_user_names().join(", ")}
63+
{videoChat.getUserNames().join(", ")}
7964
</span>
8065
);
8166
}
8267
}
8368

8469
function render_join(num_users_chatting: number): JSX.Element {
85-
if (video_chat.current.we_are_chatting()) {
70+
if (videoChat.weAreChatting()) {
8671
return (
8772
<span>
8873
<b>Leave</b> this video chatroom.
@@ -106,8 +91,7 @@ export default function VideoChatButton({
10691
}
10792
}
10893

109-
const num_users_chatting: number =
110-
video_chat.current.num_users_chatting() ?? 0;
94+
const num_users_chatting: number = videoChat.numUsersChatting() ?? 0;
11195
const style: React.CSSProperties = { cursor: "pointer" };
11296
if (num_users_chatting > 0) {
11397
style.color = "#c9302c";
Lines changed: 73 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,75 @@
11
import { client_db } from "@cocalc/util/schema";
2-
import { alert_message } from "../../alerts";
3-
import { webapp_client } from "../../webapp-client";
2+
import { alert_message } from "@cocalc/frontend/alerts";
3+
import { webapp_client } from "@cocalc/frontend/webapp-client";
44
import { len, trunc_middle } from "@cocalc/util/misc";
5+
import { redux } from "@cocalc/frontend/app-framework";
56
import { open_new_tab } from "../../misc/open-browser-tab";
6-
import { redux } from "../../app-framework";
77

88
const VIDEO_CHAT_SERVER = "https://meet.jit.si";
99
const VIDEO_UPDATE_INTERVAL_MS = 30 * 1000;
1010

1111
// Create pop-up window for video chat
12-
function video_window(url: string) {
13-
return open_new_tab(url, true);
12+
function videoWindow(url: string) {
13+
return open_new_tab(url, true, { noopener: false });
1414
}
1515

16-
const video_windows = {};
16+
const videoWindows = {};
1717

1818
export class VideoChat {
19-
private video_interval_id: any;
19+
private intervalId?: any;
2020
private project_id: string;
2121
private path: string;
22-
private account_id: string;
2322

24-
constructor(project_id: string, path: string, account_id: string) {
23+
constructor({ project_id, path }: { project_id: string; path: string }) {
2524
this.project_id = project_id;
2625
this.path = path;
27-
this.account_id = account_id;
2826
}
2927

30-
public we_are_chatting(): boolean {
31-
const timestamp: Date | undefined = this.get_users()?.[this.account_id];
28+
close = () => {
29+
console.log("close video chat!");
30+
this.closeVideoChatWindow();
31+
delete this.intervalId;
32+
};
33+
34+
weAreChatting = (): boolean => {
35+
const { account_id } = webapp_client;
36+
if (account_id == null) {
37+
return false;
38+
}
39+
const timestamp: Date | undefined = this.getUsers()?.[account_id];
3240
return (
3341
timestamp != null &&
3442
webapp_client.server_time().valueOf() - timestamp.valueOf() <=
3543
VIDEO_UPDATE_INTERVAL_MS
3644
);
37-
}
45+
};
3846

39-
public num_users_chatting(): number {
40-
return len(this.get_users());
41-
}
47+
numUsersChatting = (): number => {
48+
return len(this.getUsers());
49+
};
4250

43-
public get_user_name(): string | undefined {
51+
getUserName = (): string | undefined => {
4452
const users = redux.getStore("users");
45-
return users.get_name(this.account_id);
46-
}
53+
const { account_id } = webapp_client;
54+
if (account_id == null) {
55+
return;
56+
}
57+
return users?.get_name(account_id);
58+
};
4759

48-
public get_user_names(): string[] {
60+
getUserNames = (): string[] => {
4961
const users = redux.getStore("users");
5062
const v: string[] = [];
51-
for (const account_id in this.get_users()) {
63+
for (const account_id in this.getUsers()) {
5264
const name = users.get_name(account_id)?.trim();
5365
if (name) {
5466
v.push(trunc_middle(name, 25));
5567
}
5668
}
5769
return v;
58-
}
70+
};
5971

60-
private get_users(): { [account_id: string]: Date } {
72+
private getUsers = (): { [account_id: string]: Date } => {
6173
// Users is a map {account_id:timestamp of last chat file marking}
6274
return (
6375
redux.getStore("file_use")?.get_video_chat_users({
@@ -66,19 +78,19 @@ export class VideoChat {
6678
ttl: 1.3 * VIDEO_UPDATE_INTERVAL_MS,
6779
}) ?? {}
6880
);
69-
}
81+
};
7082

71-
public stop_chatting() {
72-
this.close_video_chat_window();
73-
}
83+
stopChatting = () => {
84+
this.closeVideoChatWindow();
85+
};
7486

75-
public start_chatting() {
87+
startChatting = () => {
7688
redux.getActions("file_use")?.mark_file(this.project_id, this.path, "chat");
77-
this.open_video_chat_window();
78-
}
89+
this.openVideoChatWindow();
90+
};
7991

8092
// The canonical secret chatroom id.
81-
private chatroom_id(): string {
93+
private chatroomId = (): string => {
8294
const secret_token = redux
8395
.getStore("projects")
8496
.getIn(["project_map", this.project_id, "status", "secret_token"]);
@@ -89,63 +101,68 @@ export class VideoChat {
89101
});
90102
}
91103
return client_db.sha1(secret_token, this.path);
92-
}
104+
};
93105

94-
public url(): string {
95-
const room_id = this.chatroom_id();
106+
url = (): string => {
107+
const room_id = this.chatroomId();
96108
return `${VIDEO_CHAT_SERVER}/${room_id}`;
97-
}
109+
};
98110

99111
// Open the video chat window, if it isn't already opened
100-
public open_video_chat_window(): void {
101-
const room_id = this.chatroom_id();
102-
if (video_windows[room_id]) {
112+
private openVideoChatWindow = (): void => {
113+
const room_id = this.chatroomId();
114+
if (videoWindows[room_id]) {
103115
return;
104116
}
105117

106-
const chat_window_is_open = () => {
118+
const chatWindowIsOpen = () => {
107119
return redux
108120
.getActions("file_use")
109121
?.mark_file(this.project_id, this.path, "video", 0);
110122
};
111123

112-
chat_window_is_open();
113-
this.video_interval_id = setInterval(
114-
chat_window_is_open,
115-
VIDEO_UPDATE_INTERVAL_MS * 0.8
124+
chatWindowIsOpen();
125+
this.intervalId = setInterval(
126+
chatWindowIsOpen,
127+
VIDEO_UPDATE_INTERVAL_MS * 0.8,
116128
);
117129

118130
//const title = `CoCalc Video Chat: ${trunc_middle(this.path, 30)}`;
119-
const w = video_window(this.url());
131+
const w = videoWindow(this.url());
120132
// https://github.com/sagemathinc/cocalc/issues/3648
121133
if (w == null) {
122134
return;
123135
}
124-
video_windows[room_id] = w;
136+
videoWindows[room_id] = w;
125137
// disabled -- see https://github.com/sagemathinc/cocalc/issues/1899
126138
//w.addEventListener "unload", =>
127139
// @close_video_chat_window()
128140
// workaround for https://github.com/sagemathinc/cocalc/issues/1899
129-
const poll_window = setInterval(() => {
141+
const pollWindow = setInterval(() => {
130142
if (w.closed !== false) {
131143
// != is required for compatibility with Opera
132-
clearInterval(poll_window);
133-
this.close_video_chat_window();
144+
clearInterval(pollWindow);
145+
this.closeVideoChatWindow();
134146
}
135147
}, 1000);
136-
}
148+
};
137149

138150
// User wants to close the video chat window, but not via just clicking the
139151
// close button on the popup window
140-
public close_video_chat_window(): void {
141-
const room_id = this.chatroom_id();
142-
const w = video_windows[room_id];
143-
if (!w) return;
152+
private closeVideoChatWindow = (): void => {
153+
const room_id = this.chatroomId();
154+
const w = videoWindows[room_id];
155+
if (!w) {
156+
return;
157+
}
144158
redux
145159
.getActions("file_use")
146160
?.mark_file(this.project_id, this.path, "video", 0, true, new Date(0));
147-
clearInterval(this.video_interval_id);
148-
delete video_windows[room_id];
161+
if (this.intervalId) {
162+
clearInterval(this.intervalId);
163+
delete this.intervalId;
164+
}
165+
delete videoWindows[room_id];
149166
w.close();
150-
}
167+
};
151168
}

0 commit comments

Comments
 (0)