This repository has been archived on 2022-08-14. You can view files and clone it, but cannot push or open issues or pull requests.
outline/app/scenes/Collection.js

292 lines
9.1 KiB
JavaScript
Raw Normal View History

// @flow
2018-05-05 23:16:08 +00:00
import * as React from 'react';
import { observable } from 'mobx';
import { observer, inject } from 'mobx-react';
2019-01-19 08:23:39 +00:00
import { Redirect, Link, Switch, Route } from 'react-router-dom';
import styled from 'styled-components';
import {
CollectionIcon,
PrivateCollectionIcon,
NewDocumentIcon,
PinIcon,
} from 'outline-icons';
2018-11-18 09:13:28 +00:00
import RichMarkdownEditor from 'rich-markdown-editor';
import { newDocumentUrl, collectionUrl } from 'utils/routeHelpers';
import CollectionsStore from 'stores/CollectionsStore';
2017-11-20 05:32:18 +00:00
import DocumentsStore from 'stores/DocumentsStore';
2017-11-20 04:16:49 +00:00
import UiStore from 'stores/UiStore';
2017-07-24 01:07:11 +00:00
import Collection from 'models/Collection';
2017-11-20 04:16:49 +00:00
import Search from 'scenes/Search';
2017-11-20 07:39:56 +00:00
import CollectionMenu from 'menus/CollectionMenu';
import Actions, { Action, Separator } from 'components/Actions';
2018-08-07 06:22:20 +00:00
import Heading from 'components/Heading';
import CenteredContent from 'components/CenteredContent';
2017-11-20 05:50:42 +00:00
import { ListPlaceholder } from 'components/LoadingPlaceholder';
import Mask from 'components/Mask';
import Button from 'components/Button';
import HelpText from 'components/HelpText';
2017-11-20 05:32:18 +00:00
import DocumentList from 'components/DocumentList';
2017-11-20 04:16:49 +00:00
import Subheading from 'components/Subheading';
import PageTitle from 'components/PageTitle';
2017-10-31 07:03:17 +00:00
import Flex from 'shared/components/Flex';
import Modal from 'components/Modal';
import CollectionPermissions from 'scenes/CollectionPermissions';
import Tabs from 'components/Tabs';
import Tab from 'components/Tab';
import PaginatedDocumentList from 'components/PaginatedDocumentList';
type Props = {
2017-11-20 04:16:49 +00:00
ui: UiStore,
2017-11-20 05:32:18 +00:00
documents: DocumentsStore,
collections: CollectionsStore,
match: Object,
};
2017-11-10 22:14:30 +00:00
@observer
2018-05-05 23:16:08 +00:00
class CollectionScene extends React.Component<Props> {
2017-10-31 07:03:17 +00:00
@observable collection: ?Collection;
2017-11-20 04:16:49 +00:00
@observable isFetching: boolean = true;
@observable permissionsModalOpen: boolean = false;
2019-01-19 08:23:39 +00:00
@observable redirectTo: ?string;
2017-11-20 05:32:18 +00:00
componentDidMount() {
this.loadContent(this.props.match.params.id);
}
componentWillReceiveProps(nextProps) {
if (nextProps.match.params.id !== this.props.match.params.id) {
2017-11-20 05:32:18 +00:00
this.loadContent(nextProps.match.params.id);
}
}
componentWillUnmount() {
this.props.ui.clearActiveCollection();
}
2017-11-20 05:32:18 +00:00
loadContent = async (id: string) => {
const collection = await this.props.collections.fetch(id);
2017-11-20 05:32:18 +00:00
2017-11-20 04:16:49 +00:00
if (collection) {
this.props.ui.setActiveCollection(collection);
this.collection = collection;
await this.props.documents.fetchPinned({
collection: id,
});
}
2017-11-20 04:16:49 +00:00
this.isFetching = false;
};
2018-05-05 23:16:08 +00:00
onNewDocument = (ev: SyntheticEvent<*>) => {
2017-11-20 07:39:56 +00:00
ev.preventDefault();
if (this.collection) {
2019-01-19 08:23:39 +00:00
this.redirectTo = `${this.collection.url}/new`;
2017-11-20 07:39:56 +00:00
}
};
onPermissions = (ev: SyntheticEvent<*>) => {
ev.preventDefault();
this.permissionsModalOpen = true;
};
handlePermissionsModalClose = () => {
this.permissionsModalOpen = false;
};
2018-01-30 21:16:20 +00:00
renderActions() {
return (
<Actions align="center" justify="flex-end">
<Action>
2019-01-19 08:23:39 +00:00
<CollectionMenu collection={this.collection} />
2018-01-30 21:16:20 +00:00
</Action>
<Separator />
<Action>
<a onClick={this.onNewDocument}>
<NewDocumentIcon />
</a>
</Action>
</Actions>
);
}
render() {
const { documents } = this.props;
2019-01-19 08:23:39 +00:00
if (this.redirectTo) return <Redirect to={this.redirectTo} />;
if (!this.isFetching && !this.collection) return <Search notFound />;
2017-10-31 07:03:17 +00:00
const pinnedDocuments = this.collection
? documents.pinnedInCollection(this.collection.id)
: [];
const hasPinnedDocuments = !!pinnedDocuments.length;
const collection = this.collection;
return (
<CenteredContent>
{collection ? (
<React.Fragment>
<PageTitle title={collection.name} />
{collection.isEmpty ? (
<Centered column>
<HelpText>
<strong>{collection.name}</strong> doesnt contain any
documents yet.<br />Get started by creating a new one!
</HelpText>
<Wrapper>
<Link to={newDocumentUrl(collection)}>
<Button icon={<NewDocumentIcon color="#FFF" />}>
Create a document
</Button>
</Link>&nbsp;&nbsp;
{collection.private && (
<Button onClick={this.onPermissions} neutral>
Invite people
</Button>
)}
</Wrapper>
<Modal
title="Collection permissions"
onRequestClose={this.handlePermissionsModalClose}
isOpen={this.permissionsModalOpen}
>
<CollectionPermissions
collection={this.collection}
onSubmit={this.handlePermissionsModalClose}
/>
</Modal>
</Centered>
) : (
<React.Fragment>
<Heading>
{collection.private ? (
<PrivateCollectionIcon
color={collection.color}
size={40}
expanded
/>
) : (
<CollectionIcon
color={collection.color}
size={40}
expanded
/>
)}{' '}
{collection.name}
</Heading>
{collection.description && (
<RichMarkdownEditor
key={collection.description}
defaultValue={collection.description}
readOnly
/>
)}
{hasPinnedDocuments && (
<React.Fragment>
<Subheading>
<TinyPinIcon size={18} /> Pinned
</Subheading>
<DocumentList documents={pinnedDocuments} />
</React.Fragment>
)}
<Tabs>
<Tab to={collectionUrl(collection.id)} exact>
Recently updated
</Tab>
<Tab to={collectionUrl(collection.id, 'recent')} exact>
Recently published
</Tab>
2019-01-08 07:42:55 +00:00
<Tab to={collectionUrl(collection.id, 'old')} exact>
Least recently updated
</Tab>
<Tab to={collectionUrl(collection.id, 'alphabetical')} exact>
AZ
</Tab>
</Tabs>
<Switch>
2019-01-08 07:42:55 +00:00
<Route path={collectionUrl(collection.id, 'alphabetical')}>
<PaginatedDocumentList
key="alphabetical"
documents={documents.alphabeticalInCollection(
collection.id
)}
fetch={documents.fetchAlphabetical}
options={{ collection: collection.id }}
/>
</Route>
<Route path={collectionUrl(collection.id, 'old')}>
<PaginatedDocumentList
key="old"
documents={documents.leastRecentlyUpdatedInCollection(
collection.id
)}
fetch={documents.fetchLeastRecentlyUpdated}
options={{ collection: collection.id }}
/>
</Route>
<Route path={collectionUrl(collection.id, 'recent')}>
<PaginatedDocumentList
key="recent"
documents={documents.recentlyPublishedInCollection(
collection.id
)}
fetch={documents.fetchRecentlyPublished}
options={{ collection: collection.id }}
showPublished
/>
</Route>
<Route path={collectionUrl(collection.id)}>
<PaginatedDocumentList
documents={documents.recentlyUpdatedInCollection(
collection.id
)}
fetch={documents.fetchRecentlyUpdated}
options={{ collection: collection.id }}
/>
</Route>
</Switch>
</React.Fragment>
)}
2018-01-30 21:16:20 +00:00
{this.renderActions()}
</React.Fragment>
2017-11-20 05:50:42 +00:00
) : (
<React.Fragment>
<Heading>
<Mask height={35} />
</Heading>
<ListPlaceholder count={5} />
</React.Fragment>
2017-11-20 05:50:42 +00:00
)}
</CenteredContent>
);
}
}
const Centered = styled(Flex)`
text-align: center;
margin: 40vh auto 0;
max-width: 380px;
transform: translateY(-50%);
`;
const TinyPinIcon = styled(PinIcon)`
position: relative;
top: 4px;
opacity: 0.8;
`;
2017-11-20 07:39:56 +00:00
const Wrapper = styled(Flex)`
justify-content: center;
2017-10-31 07:03:17 +00:00
margin: 10px 0;
`;
2019-01-19 08:23:39 +00:00
export default inject('collections', 'documents', 'ui')(CollectionScene);