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