Files
outline/app/scenes/Shared/Collection.tsx
T
Hemachandar d3eb3db7ba feat: Public sharing of collections (#9529)
* shares.info, collections.info, documents.info

* shares.list, shares.create, shares.update

* shares.sitemap

* parity with existing document shared screen

* collection share popover

* parent share and table

* collection scene

* collection link in sidebar

* sidebar and breadcrumb collection link click

* collection link click in editor

* meta

* more meta + 404 page

* map internal link, remove showLastUpdated option

* fix shares.list pagination

* show last updated

* shareLoader tests

* lint

* sidebar context for collection link

* badge in shares table

* fix existing tests

* tsc

* update failing test snapshot

* env

* signed url for collection attachments

* include collection content in SSR for screen readers

* search

* drafts can be shared

* review

* tsc, remove old shared-doc scene

* tweaks

* DRY

* refactor loader

* Remove share/collection urls

* fix: Collection overview should not be editable when viewing shared link and logged in

* Tweak public breadcrumb

* fix: Deleted documents should never be exposed through share

* empty sharedTree array where includeChildDocuments is false

* revert includeChildDocs guard for logical correctness + SSR bug fix

* fix: check document is part of share

---------

Co-authored-by: Tom Moor <tom@getoutline.com>
2025-08-03 13:07:39 -04:00

108 lines
3.0 KiB
TypeScript

import { observer } from "mobx-react";
import { EditIcon } from "outline-icons";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { IconTitleWrapper } from "@shared/components/Icon";
import CollectionModel from "~/models/Collection";
import { Action } from "~/components/Actions";
import Button from "~/components/Button";
import CenteredContent from "~/components/CenteredContent";
import Flex from "~/components/Flex";
import Heading from "~/components/Heading";
import CollectionIcon from "~/components/Icons/CollectionIcon";
import Scene from "~/components/Scene";
import Text from "~/components/Text";
import Time from "~/components/Time";
import Tooltip from "~/components/Tooltip";
import useMobile from "~/hooks/useMobile";
import usePolicy from "~/hooks/usePolicy";
import { collectionPath } from "~/utils/routeHelpers";
import Overview from "../Collection/components/Overview";
import { AppearanceAction } from "~/components/Sharing/components/Actions";
type Props = {
collection: CollectionModel;
shareId: string;
};
function SharedCollection({ collection, shareId }: Props) {
const { t } = useTranslation();
const can = usePolicy(collection);
const isMobile = useMobile();
const editAction = (
<Action>
<Tooltip content={t("Edit collection")} shortcut="e" placement="bottom">
<Button
as={Link}
icon={<EditIcon />}
to={{
pathname: collectionPath(collection.path, "overview"),
}}
neutral
>
{isMobile ? null : t("Edit")}
</Button>
</Tooltip>
</Action>
);
return (
<Scene
centered={false}
textTitle={collection.name}
left={<div />}
title={
<>
<CollectionIcon collection={collection} expanded />
&nbsp;{collection.name}
</>
}
actions={
<>
<AppearanceAction />
{can.update ? editAction : null}
</>
}
>
<CenteredContent withStickyHeader>
<Flex column>
<CollectionHeading>
<IconTitleWrapper>
<CollectionIcon collection={collection} size={40} expanded />
</IconTitleWrapper>
{collection.name}
</CollectionHeading>
{!!shareId && !!collection.updatedAt ? (
<SharedMeta type="tertiary">
{t("Last updated")}{" "}
<Time dateTime={collection.updatedAt} addSuffix />
</SharedMeta>
) : null}
</Flex>
<Overview collection={collection} shareId={shareId} />
</CenteredContent>
</Scene>
);
}
const CollectionHeading = styled(Heading)`
display: flex;
align-items: center;
position: relative;
margin-left: 40px;
${breakpoint("tablet")`
margin-left: 0;
`}
`;
const SharedMeta = styled(Text)`
margin: -12px 0 2em 0;
font-size: 14px;
`;
export const Collection = observer(SharedCollection);