Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/account/public-paths/public-paths.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Alert, Button, Checkbox, Space, Spin, Table } from "antd";6import { join } from "path";7import { FormattedMessage, useIntl } from "react-intl";89import {10React,11redux,12useActions,13useEffect,14useIsMountedRef,15useMemo,16useState,17useTypedRedux,18} from "@cocalc/frontend/app-framework";19import { A, Icon, Loading, TimeAgo } from "@cocalc/frontend/components";20import ShowError from "@cocalc/frontend/components/error";21import { Footer } from "@cocalc/frontend/customize";22import { appBasePath } from "@cocalc/frontend/customize/app-base-path";23import { labels } from "@cocalc/frontend/i18n";24import { ComputeImageSelector } from "@cocalc/frontend/project/settings/compute-image-selector";25import { LICENSES } from "@cocalc/frontend/share/licenses";26import { webapp_client } from "@cocalc/frontend/webapp-client";27import { PublicPath as PublicPath0 } from "@cocalc/util/db-schema/public-paths";28import { KUCALC_COCALC_COM } from "@cocalc/util/db-schema/site-defaults";29import { trunc, trunc_middle } from "@cocalc/util/misc";30import { UnpublishEverything } from "./unpublish-everything";3132interface PublicPath extends PublicPath0 {33status?: string;34}3536type filters = "Listed" | "Unlisted" | "Unpublished" | "Authenticated";37const DEFAULT_CHECKED: filters[] = ["Listed", "Unlisted", "Authenticated"];3839export const PublicPaths: React.FC = () => {40const intl = useIntl();41const account_id = useTypedRedux("account", "account_id");42const customize_kucalc = useTypedRedux("customize", "kucalc");43const showAuthenticatedOption = customize_kucalc !== KUCALC_COCALC_COM;44const [data, set_data] = useState<PublicPath[] | undefined>(undefined);45const [error, setError] = useState<string>("");46const [loading, set_loading] = useState<boolean>(false);4748const [show_listed, set_show_listed] = useState<boolean>(49DEFAULT_CHECKED.indexOf("Listed") != -1,50);51const [show_authenticated, set_show_authenticated] = useState<boolean>(52showAuthenticatedOption && DEFAULT_CHECKED.indexOf("Authenticated") != -1,53);54const [show_unlisted, set_show_unlisted] = useState<boolean>(55DEFAULT_CHECKED.indexOf("Unlisted") != -1,56);57const [show_unpublished, set_show_unpublished] = useState<boolean>(58DEFAULT_CHECKED.indexOf("Unpublished") != -1,59);6061const isMountedRef = useIsMountedRef();62const project_map = useTypedRedux("projects", "project_map");63const actions = useActions("projects");6465const paths: PublicPath[] = useMemo(() => {66const v: PublicPath[] = [];67if (data != null) {68for (const path of data) {69if (path.disabled) {70if (show_unpublished) {71path.status = "Unpublished";72v.push(path);73}74continue;75}76if (path.unlisted) {77if (show_unlisted) {78path.status = "Unlisted";79v.push(path);80}81continue;82}83if (path.authenticated) {84if (show_authenticated) {85path.status = "Authenticated";86v.push(path);87}88continue;89}90if (show_listed) {91path.status = "Listed";92v.push(path);93}94}95}96return v;97}, [data, show_listed, show_unlisted, show_unpublished, show_authenticated]);9899const COLUMNS = [100{101title: "Path",102dataIndex: "path",103key: "path",104render: (path, record) => {105return (106<a107onClick={async () => {108await actions?.open_project({ project_id: record.project_id });109redux110.getProjectActions(record.project_id)111?.show_public_config(path);112}}113>114{trunc_middle(path, 64)}115</a>116);117},118},119{120title: "Project",121dataIndex: "project_id",122key: "project_id",123render: (project_id) => {124const project = project_map?.get(project_id);125if (project == null) {126actions?.load_all_projects();127return <Loading />;128}129const title = project.get("title") ?? "No Title";130return (131<a onClick={() => actions?.open_project({ project_id })}>132{trunc_middle(title, 64)}133</a>134);135},136},137{138title: "Description",139dataIndex: "description",140key: "description",141render: (description) => <span>{trunc(description, 32)}</span>,142},143{144title: "Last edited",145dataIndex: "last_edited",146key: "last_edited",147render: (date) => <TimeAgo date={date} />,148},149{150title: "License",151dataIndex: "license",152key: "license",153render: (license) => trunc_middle(LICENSES[license] ?? "None", 32),154},155{156title: "Counter",157dataIndex: "counter",158key: "counter",159},160{161title: "Status",162dataIndex: "status",163key: "status",164},165{166title: "Image",167dataIndex: "compute_image",168key: "image",169render: (_, record) => {170return <ComputeImage {...record} setError={setError} />;171},172},173];174175async function fetch() {176set_loading(true);177try {178const data = (179await webapp_client.async_query({180query: {181all_public_paths: {182id: null,183project_id: null,184path: null,185description: null,186disabled: null,187unlisted: null,188authenticated: null,189license: null,190last_edited: null,191created: null,192last_saved: null,193counter: null,194compute_image: null,195},196},197})198).query.all_public_paths;199if (!isMountedRef.current) {200return;201}202set_loading(false);203set_data(data);204setError("");205} catch (err) {206if (!isMountedRef.current) {207return;208}209set_loading(false);210setError(err.toString());211}212}213214useEffect(() => {215fetch();216}, []);217218function render_checkboxes() {219if (loading) return;220const options = ["Listed", "Unlisted", "Unpublished"];221if (showAuthenticatedOption) {222options.splice(2, 0, "Authenticated");223}224return (225<Checkbox.Group226options={options}227defaultValue={DEFAULT_CHECKED}228onChange={(v) => {229set_show_listed(v.indexOf("Listed") != -1);230set_show_unlisted(v.indexOf("Unlisted") != -1);231set_show_unpublished(v.indexOf("Unpublished") != -1);232set_show_authenticated(v.indexOf("Authenticated") != -1);233}}234/>235);236}237238return (239<div style={{ marginBottom: "64px" }}>240<Alert241showIcon242style={{ margin: "30px auto" }}243type="info"244banner245message={246<FormattedMessage247id="account.public-paths.banner"248defaultMessage={`This is an overview of your public files.249<A>Visit this page for more details...</A>`}250values={{251A: (c) => (252<A href={join(appBasePath, "share", "accounts", account_id)}>253{c}254</A>255),256}}257/>258}259/>260<Button onClick={fetch} disabled={loading} style={{ float: "right" }}>261<Space>262<Icon name="redo" />263{intl.formatMessage(loading ? labels.loading : labels.refresh)}264</Space>265</Button>266<h2>267{intl.formatMessage(labels.published_files)} ({paths?.length ?? "?"})268</h2>269<FormattedMessage270id="account.public-paths.info"271defaultMessage={272"Files that have been published in any project that you have actively used."273}274/>275<br />276<br />277{loading && <Loading />}278{render_checkboxes()}279<br />280<ShowError error={error} setError={setError} />281<br />282{data != null && (283<Table rowKey="id" columns={COLUMNS} dataSource={paths} />284)}285<UnpublishEverything data={data} refresh={fetch} />286<br />287<Footer />288</div>289);290};291292function ComputeImage({ compute_image, project_id, path, setError }) {293const [selectedImage, setSelectedImage] = useState<string>(compute_image);294const [saving, setSaving] = useState<boolean>(false);295296useEffect(() => {297setSelectedImage(compute_image);298}, [compute_image]);299300return (301<>302<ComputeImageSelector303disabled={saving}304selected_image={selectedImage}305layout={"compact"}306onSelect={async (img) => {307setSelectedImage(img);308try {309setSaving(true);310await webapp_client.async_query({311query: { public_paths: { project_id, path, compute_image: img } },312});313} catch (err) {314setError(`${err}`);315// failed to save -- change back so clear indication316// it didn't work, and also so they can try again.317setSelectedImage(compute_image);318} finally {319setSaving(false);320}321}}322/>323{saving && (324<div>325<Spin />326</div>327)}328</>329);330}331332333