Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/collaborators/current-collabs.tsx
1496 views
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { Button, Card, Popconfirm } from "antd";
7
import React from "react";
8
import { FormattedMessage, useIntl } from "react-intl";
9
import { CSS, redux, useRedux } from "@cocalc/frontend/app-framework";
10
import {
11
Gap,
12
Icon,
13
Paragraph,
14
SettingBox,
15
Title,
16
} from "@cocalc/frontend/components";
17
import { useStudentProjectFunctionality } from "@cocalc/frontend/course";
18
import { labels } from "@cocalc/frontend/i18n";
19
import { CancelText } from "@cocalc/frontend/i18n/components";
20
import { Project } from "@cocalc/frontend/project/settings/types";
21
import { COLORS } from "@cocalc/util/theme";
22
import { FIX_BORDER } from "../project/page/common";
23
import { User } from "../users";
24
25
interface Props {
26
project: Project;
27
user_map?: any;
28
mode?: "project" | "flyout";
29
}
30
31
export const CurrentCollaboratorsPanel: React.FC<Props> = (props: Props) => {
32
const { project, user_map, mode = "project" } = props;
33
const isFlyout = mode === "flyout";
34
const intl = useIntl();
35
const get_account_id = useRedux("account", "get_account_id");
36
const sort_by_activity = useRedux("projects", "sort_by_activity");
37
const student = useStudentProjectFunctionality(project.get("project_id"));
38
39
function remove_collaborator(account_id: string) {
40
const project_id = project.get("project_id");
41
redux.getActions("projects").remove_collaborator(project_id, account_id);
42
if (account_id === get_account_id()) {
43
(redux.getActions("page") as any).close_project_tab(project_id);
44
// TODO: better types
45
}
46
}
47
48
function user_remove_confirm_text(account_id: string) {
49
const style: CSS = { maxWidth: "300px" };
50
if (account_id === get_account_id()) {
51
return (
52
<div style={style}>
53
<FormattedMessage
54
id="collaborators.current-collabs.remove_self"
55
defaultMessage={`Are you sure you want to remove <b>yourself</b> from this project?
56
You will no longer have access to this project and cannot add yourself back.`}
57
/>
58
</div>
59
);
60
} else {
61
return (
62
<div style={style}>
63
<FormattedMessage
64
id="collaborators.current-collabs.remove_other"
65
defaultMessage={`Are you sure you want to remove {user} from this project?
66
They will no longer have access to this project.`}
67
values={{
68
user: <User account_id={account_id} user_map={user_map} />,
69
}}
70
/>
71
</div>
72
);
73
}
74
}
75
76
function user_remove_button(account_id: string, group?: string) {
77
if (student.disableCollaborators) return;
78
const text = user_remove_confirm_text(account_id);
79
const isOwner = group === "owner";
80
return (
81
<Popconfirm
82
title={text}
83
onConfirm={() => remove_collaborator(account_id)}
84
okText={"Yes, remove collaborator"}
85
cancelText={<CancelText />}
86
disabled={isOwner}
87
>
88
<Button
89
disabled={isOwner}
90
type={isFlyout ? "link" : "default"}
91
style={{
92
marginBottom: "0",
93
float: "right",
94
...(isFlyout ? { color: COLORS.ANTD_RED_WARN } : {}),
95
}}
96
>
97
<Icon name="user-times" /> {intl.formatMessage(labels.remove)} ...
98
</Button>
99
</Popconfirm>
100
);
101
}
102
103
function render_user(user: any, is_last?: boolean) {
104
const style = {
105
width: "100%",
106
flex: "1 1 auto",
107
...(!is_last ? { marginBottom: "20px" } : {}),
108
};
109
return (
110
<div key={user.account_id} style={style}>
111
<User
112
account_id={user.account_id}
113
user_map={user_map}
114
last_active={user.last_active}
115
show_avatar={true}
116
/>
117
<span>
118
<Gap />({user.group})
119
</span>
120
{user_remove_button(user.account_id, user.group)}
121
</div>
122
);
123
}
124
125
function render_users() {
126
const u = project.get("users");
127
if (u === undefined) {
128
return;
129
}
130
const users = u
131
.map((v, k) => ({ account_id: k, group: v.get("group") }))
132
.toList()
133
.toJS();
134
return sort_by_activity(users, project.get("project_id")).map((u, i) =>
135
render_user(u, i === users.length - 1),
136
);
137
}
138
139
function render_collaborators_list() {
140
const style: CSS = {
141
maxHeight: "20em",
142
overflowY: "auto",
143
overflowX: "hidden",
144
marginBottom: "0",
145
display: "flex",
146
flexDirection: "column",
147
};
148
if (isFlyout) {
149
return (
150
<div style={{ ...style, borderBottom: FIX_BORDER }}>
151
{render_users()}
152
</div>
153
);
154
} else {
155
return (
156
<Card style={{ ...style, backgroundColor: COLORS.GRAY_LLL }}>
157
{render_users()}
158
</Card>
159
);
160
}
161
}
162
163
const introText = intl.formatMessage({
164
id: "collaborators.current-collabs.intro",
165
defaultMessage:
166
"Everybody listed below can collaboratively work with you on any Jupyter Notebook, Linux Terminal or file in this project, and add or remove other collaborators.",
167
});
168
169
switch (mode) {
170
case "project":
171
return (
172
<SettingBox title="Current Collaborators" icon="user">
173
{introText}
174
<hr />
175
{render_collaborators_list()}
176
</SettingBox>
177
);
178
case "flyout":
179
return (
180
<div style={{ paddingLeft: "5px" }}>
181
<Title level={3}>
182
<Icon name="user" />{" "}
183
<FormattedMessage
184
id="collaborators.current-collabs.title"
185
defaultMessage={"Current Collaborators"}
186
description={
187
"Title of a table listing users collaborating on that project"
188
}
189
/>
190
</Title>
191
<Paragraph
192
type="secondary"
193
ellipsis={{ rows: 1, expandable: true, symbol: "more" }}
194
>
195
{introText}
196
</Paragraph>
197
{render_collaborators_list()}
198
</div>
199
);
200
}
201
};
202
203