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