Added: Recently published view to collection

Added: Infinite scroll to collection
This commit is contained in:
Tom Moor
2019-01-07 23:14:43 -08:00
parent 74515e0b19
commit e6e89dc243
10 changed files with 87 additions and 25 deletions

View File

@ -7,12 +7,14 @@ import ArrowKeyNavigation from 'boundless-arrow-key-navigation';
type Props = { type Props = {
documents: Document[], documents: Document[],
showCollection?: boolean, showCollection?: boolean,
showPublished?: boolean,
limit?: number, limit?: number,
}; };
export default function DocumentList({ export default function DocumentList({
limit, limit,
showCollection, showCollection,
showPublished,
documents, documents,
}: Props) { }: Props) {
const items = limit ? documents.splice(0, limit) : documents; const items = limit ? documents.splice(0, limit) : documents;
@ -27,6 +29,7 @@ export default function DocumentList({
key={document.id} key={document.id}
document={document} document={document}
showCollection={showCollection} showCollection={showCollection}
showPublished={showPublished}
/> />
))} ))}
</ArrowKeyNavigation> </ArrowKeyNavigation>

View File

@ -15,6 +15,7 @@ type Props = {
highlight?: ?string, highlight?: ?string,
context?: ?string, context?: ?string,
showCollection?: boolean, showCollection?: boolean,
showPublished?: boolean,
ref?: *, ref?: *,
}; };
@ -133,6 +134,7 @@ class DocumentPreview extends React.Component<Props> {
const { const {
document, document,
showCollection, showCollection,
showPublished,
highlight, highlight,
context, context,
...rest ...rest
@ -173,6 +175,7 @@ class DocumentPreview extends React.Component<Props> {
<PublishingInfo <PublishingInfo
document={document} document={document}
collection={showCollection ? document.collection : undefined} collection={showCollection ? document.collection : undefined}
showPublished={showPublished}
/> />
</DocumentLink> </DocumentLink>
); );

View File

@ -19,11 +19,12 @@ const Modified = styled.span`
type Props = { type Props = {
collection?: Collection, collection?: Collection,
showPublished?: boolean,
document: Document, document: Document,
views?: number, views?: number,
}; };
function PublishingInfo({ collection, document }: Props) { function PublishingInfo({ collection, showPublished, document }: Props) {
const { const {
modifiedSinceViewed, modifiedSinceViewed,
updatedAt, updatedAt,
@ -35,7 +36,7 @@ function PublishingInfo({ collection, document }: Props) {
return ( return (
<Container align="center"> <Container align="center">
{publishedAt && neverUpdated ? ( {publishedAt && (neverUpdated || showPublished) ? (
<span> <span>
{updatedBy.name} published <Time dateTime={publishedAt} /> ago {updatedBy.name} published <Time dateTime={publishedAt} /> ago
</span> </span>
@ -48,7 +49,7 @@ function PublishingInfo({ collection, document }: Props) {
</span> </span>
) : ( ) : (
<Modified highlight={modifiedSinceViewed}> <Modified highlight={modifiedSinceViewed}>
&nbsp;modified <Time dateTime={updatedAt} /> ago &nbsp;updated <Time dateTime={updatedAt} /> ago
</Modified> </Modified>
)} )}
</React.Fragment> </React.Fragment>

View File

@ -11,6 +11,7 @@ import { ListPlaceholder } from 'components/LoadingPlaceholder';
type Props = { type Props = {
showCollection?: boolean, showCollection?: boolean,
showPublished?: boolean,
documents: Document[], documents: Document[],
fetch: (options: ?Object) => Promise<*>, fetch: (options: ?Object) => Promise<*>,
options?: Object, options?: Object,
@ -64,11 +65,15 @@ class PaginatedDocumentList extends React.Component<Props> {
}; };
render() { render() {
const { showCollection, documents } = this.props; const { showCollection, showPublished, documents } = this.props;
return this.isLoaded || documents.length ? ( return this.isLoaded || documents.length ? (
<React.Fragment> <React.Fragment>
<DocumentList documents={documents} showCollection={showCollection} /> <DocumentList
documents={documents}
showCollection={showCollection}
showPublished={showPublished}
/>
{this.allowLoadMore && ( {this.allowLoadMore && (
<Waypoint key={this.offset} onEnter={this.loadMoreResults} /> <Waypoint key={this.offset} onEnter={this.loadMoreResults} />
)} )}

View File

@ -59,6 +59,7 @@ class CollectionLink extends React.Component<Props> {
hideDisclosure hideDisclosure
menuOpen={this.menuOpen} menuOpen={this.menuOpen}
label={collection.name} label={collection.name}
exact={false}
menu={ menu={
<CollectionMenu <CollectionMenu
history={history} history={history}

View File

@ -68,6 +68,7 @@ export default function Routes() {
component={Zapier} component={Zapier}
/> />
<Route exact path="/settings/export" component={Export} /> <Route exact path="/settings/export" component={Export} />
<Route exact path="/collections/:id/:tab" component={Collection} />
<Route exact path="/collections/:id" component={Collection} /> <Route exact path="/collections/:id" component={Collection} />
<Route exact path={`/d/${slug}`} component={RedirectDocument} /> <Route exact path={`/d/${slug}`} component={RedirectDocument} />
<Route <Route

View File

@ -2,7 +2,8 @@
import * as React from 'react'; import * as React from 'react';
import { observable } from 'mobx'; import { observable } from 'mobx';
import { observer, inject } from 'mobx-react'; import { observer, inject } from 'mobx-react';
import { withRouter, Link } from 'react-router-dom'; import { withRouter, Link, Switch, Route } from 'react-router-dom';
import styled from 'styled-components'; import styled from 'styled-components';
import { import {
CollectionIcon, CollectionIcon,
@ -12,7 +13,7 @@ import {
} from 'outline-icons'; } from 'outline-icons';
import RichMarkdownEditor from 'rich-markdown-editor'; import RichMarkdownEditor from 'rich-markdown-editor';
import { newDocumentUrl } from 'utils/routeHelpers'; import { newDocumentUrl, collectionUrl } from 'utils/routeHelpers';
import CollectionsStore from 'stores/CollectionsStore'; import CollectionsStore from 'stores/CollectionsStore';
import DocumentsStore from 'stores/DocumentsStore'; import DocumentsStore from 'stores/DocumentsStore';
import UiStore from 'stores/UiStore'; import UiStore from 'stores/UiStore';
@ -33,6 +34,9 @@ import PageTitle from 'components/PageTitle';
import Flex from 'shared/components/Flex'; import Flex from 'shared/components/Flex';
import Modal from 'components/Modal'; import Modal from 'components/Modal';
import CollectionPermissions from 'scenes/CollectionPermissions'; import CollectionPermissions from 'scenes/CollectionPermissions';
import Tabs from 'components/Tabs';
import Tab from 'components/Tab';
import PaginatedDocumentList from 'components/PaginatedDocumentList';
type Props = { type Props = {
ui: UiStore, ui: UiStore,
@ -69,15 +73,9 @@ class CollectionScene extends React.Component<Props> {
this.props.ui.setActiveCollection(collection); this.props.ui.setActiveCollection(collection);
this.collection = collection; this.collection = collection;
await Promise.all([ await this.props.documents.fetchPinned({
this.props.documents.fetchRecentlyUpdated({
limit: 10,
collection: id, collection: id,
}), });
this.props.documents.fetchPinned({
collection: id,
}),
]);
} }
this.isFetching = false; this.isFetching = false;
@ -124,15 +122,14 @@ class CollectionScene extends React.Component<Props> {
} }
render() { render() {
const { documents } = this.props;
if (!this.isFetching && !this.collection) { if (!this.isFetching && !this.collection) {
return this.renderNotFound(); return this.renderNotFound();
} }
const pinnedDocuments = this.collection const pinnedDocuments = this.collection
? this.props.documents.pinnedInCollection(this.collection.id) ? documents.pinnedInCollection(this.collection.id)
: [];
const recentDocuments = this.collection
? this.props.documents.recentlyUpdatedInCollection(this.collection.id)
: []; : [];
const hasPinnedDocuments = !!pinnedDocuments.length; const hasPinnedDocuments = !!pinnedDocuments.length;
const collection = this.collection; const collection = this.collection;
@ -207,8 +204,36 @@ class CollectionScene extends React.Component<Props> {
</React.Fragment> </React.Fragment>
)} )}
<Subheading>Recently edited</Subheading> <Tabs>
<DocumentList documents={recentDocuments} limit={10} /> <Tab to={collectionUrl(collection.id)} exact>
Recently updated
</Tab>
<Tab to={collectionUrl(collection.id, 'recent')} exact>
Recently published
</Tab>
</Tabs>
<Switch>
<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> </React.Fragment>
)} )}

View File

@ -12,8 +12,8 @@ import CenteredContent from 'components/CenteredContent';
import PageTitle from 'components/PageTitle'; import PageTitle from 'components/PageTitle';
import Tabs from 'components/Tabs'; import Tabs from 'components/Tabs';
import Tab from 'components/Tab'; import Tab from 'components/Tab';
import TipInvite from 'components/TipInvite';
import PaginatedDocumentList from '../components/PaginatedDocumentList'; import PaginatedDocumentList from '../components/PaginatedDocumentList';
import TipInvite from 'components/TipInvite';
type Props = { type Props = {
documents: DocumentsStore, documents: DocumentsStore,

View File

@ -67,6 +67,18 @@ export default class DocumentsStore extends BaseStore<Document> {
); );
} }
recentlyPublishedInCollection(collectionId: string): Document[] {
return orderBy(
filter(
Array.from(this.data.values()),
document =>
document.collectionId === collectionId && !!document.publishedAt
),
'publishedAt',
'desc'
);
}
@computed @computed
get starred(): Document[] { get starred(): Document[] {
return filter(this.orderedData, d => d.starred); return filter(this.orderedData, d => d.starred);
@ -126,6 +138,15 @@ export default class DocumentsStore extends BaseStore<Document> {
return data; return data;
}; };
@action
fetchRecentlyPublished = async (options: ?PaginationParams): Promise<*> => {
return this.fetchNamedPage('list', {
sort: 'publishedAt',
direction: 'DESC',
...options,
});
};
@action @action
fetchRecentlyViewed = async (options: ?PaginationParams): Promise<*> => { fetchRecentlyViewed = async (options: ?PaginationParams): Promise<*> => {
const data = await this.fetchNamedPage('viewed', options); const data = await this.fetchNamedPage('viewed', options);

View File

@ -14,8 +14,10 @@ export function newCollectionUrl(): string {
return '/collections/new'; return '/collections/new';
} }
export function collectionUrl(collectionId: string): string { export function collectionUrl(collectionId: string, section: ?string): string {
return `/collections/${collectionId}`; const path = `/collections/${collectionId}`;
if (section) return `${path}/${section}`;
return path;
} }
export function documentUrl(doc: Document): string { export function documentUrl(doc: Document): string {