CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/next/components/share/public-paths.tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
A table of a list of public paths.
8
*/
9
10
import { Avatar, Space, Table } from "antd";
11
import Badge from "components/misc/badge";
12
import { PublicPath } from "lib/share/types";
13
import A from "components/misc/A";
14
import SanitizedMarkdown from "components/misc/sanitized-markdown";
15
import { Icon } from "@cocalc/frontend/components/icon";
16
import { trunc_middle } from "@cocalc/util/misc";
17
import { SHARE_AUTHENTICATED_ICON } from "@cocalc/util/consts/ui";
18
19
function Description({
20
description,
21
maxWidth,
22
}: {
23
description: string;
24
maxWidth?: string;
25
}) {
26
if (!description?.trim()) return null;
27
return (
28
<div
29
style={{
30
maxWidth,
31
maxHeight: "4em",
32
overflow: "auto",
33
border: "1px solid #eee",
34
borderRadius: "3px",
35
padding: "5px",
36
}}
37
>
38
<SanitizedMarkdown value={description} />
39
</div>
40
);
41
}
42
43
function LastEdited({ last_edited }: { last_edited: string }) {
44
return <>{`${new Date(parseFloat(last_edited)).toLocaleString()}`}</>;
45
}
46
47
function Title({
48
id,
49
title,
50
url,
51
avatar_image_tiny,
52
}: {
53
id: string;
54
title: string;
55
url?: string;
56
avatar_image_tiny?: string;
57
}) {
58
return (
59
<A href={url ? `/${url}` : `/share/public_paths/${id}`}>
60
{avatar_image_tiny && (
61
<Avatar
62
size={24}
63
shape="square"
64
icon={<img src={avatar_image_tiny} />}
65
style={{ marginRight: "5px", marginTop: "-4px" }}
66
/>
67
)}
68
{trunc_middle(title, 48)}
69
</A>
70
);
71
}
72
73
function Visibility({ disabled, unlisted, vhost, authenticated }) {
74
if (disabled) {
75
return (
76
<>
77
<Icon name="lock" /> Private
78
</>
79
);
80
}
81
if (authenticated) {
82
return (
83
<>
84
<Icon name={SHARE_AUTHENTICATED_ICON} /> Authenticated
85
</>
86
);
87
}
88
if (unlisted) {
89
return (
90
<>
91
<Icon name="eye-slash" /> Unlisted
92
</>
93
);
94
}
95
if (vhost) {
96
return <>Virtual Host: {vhost}</>;
97
}
98
return (
99
<>
100
<Icon name="eye" /> Listed
101
</>
102
);
103
}
104
105
function ViewsAndStars({ stars, views }) {
106
return (
107
<div style={{ display: "flex" }}>
108
{views > 0 && (
109
<div style={{ marginRight: "30px" }}>
110
Views <Badge count={views} />
111
</div>
112
)}
113
{stars > 0 && (
114
<div>
115
Stars <Badge count={stars} />
116
</div>
117
)}
118
</div>
119
);
120
}
121
122
// I'm using any[]'s below since it's too much of a pain dealing with TS for this.
123
124
const COLUMNS0: any[] = [
125
{
126
title: "Path",
127
dataIndex: "path",
128
key: "path",
129
render: (title, record) => (
130
<Title
131
id={record.id}
132
title={title}
133
url={record.url}
134
avatar_image_tiny={record.avatar_image_tiny}
135
/>
136
),
137
responsive: ["sm"] as any,
138
//sorter: field_cmp("path"),
139
},
140
{
141
title: "Description",
142
dataIndex: "description",
143
key: "description",
144
render: (description) => (
145
<Description description={description} maxWidth="250px" />
146
),
147
responsive: ["sm"] as any,
148
//sorter: field_cmp("description"),
149
},
150
{
151
title: "Last Modified",
152
dataIndex: "last_edited",
153
key: "last_edited",
154
render: (last_edited) => <LastEdited last_edited={last_edited} />,
155
responsive: ["sm"] as any,
156
//sorter: field_cmp("last_edited"),
157
},
158
{
159
title: "Stars",
160
dataIndex: "stars",
161
key: "stars",
162
render: (stars) => <Badge count={stars} />,
163
responsive: ["sm"] as any,
164
//sorter: field_cmp("stars"),
165
},
166
{
167
title: "Views",
168
dataIndex: "counter",
169
key: "counter",
170
render: (counter) => <Badge count={counter} />,
171
responsive: ["sm"] as any,
172
//sorter: field_cmp("counter"),
173
},
174
];
175
176
const COLUMNS: any[] = COLUMNS0.concat([
177
{
178
title: "Documents",
179
responsive: ["xs"] as any,
180
key: "path",
181
render: (_, record) => {
182
const { path, url, last_edited, id, description, stars, counter } =
183
record;
184
return (
185
<Space direction="vertical" style={{ width: "100%" }}>
186
<Title title={path} id={id} url={url} />
187
<Description description={description} />
188
<LastEdited last_edited={last_edited} />
189
<ViewsAndStars stars={stars} views={counter} />
190
</Space>
191
);
192
},
193
},
194
]);
195
196
const COLUMNS_WITH_VISIBILITY: any[] = COLUMNS0.concat([
197
{
198
title: "Visibility",
199
dataIndex: "disabled",
200
key: "disabled",
201
render: (_, record) => (
202
<Visibility
203
disabled={record.disabled}
204
unlisted={record.unlisted}
205
authenticated={record.authenticated}
206
vhost={record.vhost}
207
/>
208
),
209
responsive: ["sm"] as any,
210
//sorter: field_cmp(["disabled", "unlisted", "vhost", "authenticated"]),
211
},
212
{
213
title: "Documents",
214
responsive: ["xs"] as any,
215
key: "path",
216
render: (_, record) => {
217
const { path, last_edited, id, description, stars, counter, url } =
218
record;
219
return (
220
<Space direction="vertical" style={{ width: "100%" }}>
221
<Title title={path} id={id} url={url} />
222
<Description description={description} />
223
<LastEdited last_edited={last_edited} />
224
<Visibility
225
disabled={record.disabled}
226
unlisted={record.unlisted}
227
authenticated={record.authenticated}
228
vhost={record.vhost}
229
/>
230
<ViewsAndStars stars={stars} views={counter} />
231
</Space>
232
);
233
},
234
},
235
]);
236
237
interface Props {
238
publicPaths?: PublicPath[];
239
}
240
241
export default function PublicPaths({ publicPaths }: Props): JSX.Element {
242
let showVisibility = false;
243
if (publicPaths) {
244
for (const path of publicPaths) {
245
const { disabled, unlisted, authenticated } = path;
246
if (disabled || unlisted || authenticated) {
247
showVisibility = true;
248
break;
249
}
250
}
251
}
252
return (
253
<Table
254
pagination={false}
255
rowKey={"id"}
256
loading={publicPaths == null}
257
dataSource={publicPaths}
258
columns={showVisibility ? COLUMNS_WITH_VISIBILITY : COLUMNS}
259
style={{ overflowX: "auto" }}
260
/>
261
);
262
}
263
264