feat: Child document references (#1054)
* feat: Child document references * refactor: naming * lint: flow
This commit is contained in:
@@ -51,31 +51,31 @@ function PublishingInfo({
|
||||
if (deletedAt) {
|
||||
content = (
|
||||
<span>
|
||||
deleted <Time dateTime={deletedAt} /> ago
|
||||
deleted <Time dateTime={deletedAt} /> ago
|
||||
</span>
|
||||
);
|
||||
} else if (archivedAt) {
|
||||
content = (
|
||||
<span>
|
||||
archived <Time dateTime={archivedAt} /> ago
|
||||
archived <Time dateTime={archivedAt} /> ago
|
||||
</span>
|
||||
);
|
||||
} else if (publishedAt && (neverUpdated || showPublished)) {
|
||||
content = (
|
||||
<span>
|
||||
published <Time dateTime={publishedAt} /> ago
|
||||
published <Time dateTime={publishedAt} /> ago
|
||||
</span>
|
||||
);
|
||||
} else if (isDraft) {
|
||||
content = (
|
||||
<span>
|
||||
saved <Time dateTime={updatedAt} /> ago
|
||||
saved <Time dateTime={updatedAt} /> ago
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<Modified highlight={modifiedSinceViewed}>
|
||||
updated <Time dateTime={updatedAt} /> ago
|
||||
updated <Time dateTime={updatedAt} /> ago
|
||||
</Modified>
|
||||
);
|
||||
}
|
||||
@@ -84,7 +84,7 @@ function PublishingInfo({
|
||||
|
||||
return (
|
||||
<Container align="center">
|
||||
{updatedBy.name}
|
||||
{updatedBy.name}
|
||||
{content}
|
||||
{showCollection &&
|
||||
collection && (
|
||||
|
||||
@@ -60,6 +60,25 @@ export default class Collection extends BaseModel {
|
||||
travelDocuments(this.documents);
|
||||
}
|
||||
|
||||
getDocumentChildren(documentId: string): NavigationNode[] {
|
||||
let result = [];
|
||||
const traveler = nodes => {
|
||||
nodes.forEach(childNode => {
|
||||
if (childNode.id === documentId) {
|
||||
result = childNode.children;
|
||||
return;
|
||||
}
|
||||
return traveler(childNode.children);
|
||||
});
|
||||
};
|
||||
|
||||
if (this.documents) {
|
||||
traveler(this.documents);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pathToDocument(document: Document) {
|
||||
let path;
|
||||
const traveler = (nodes, previousPath) => {
|
||||
|
||||
@@ -23,7 +23,7 @@ import Header from './components/Header';
|
||||
import DocumentMove from './components/DocumentMove';
|
||||
import Branding from './components/Branding';
|
||||
import KeyboardShortcuts from './components/KeyboardShortcuts';
|
||||
import Backlinks from './components/Backlinks';
|
||||
import References from './components/References';
|
||||
import ErrorBoundary from 'components/ErrorBoundary';
|
||||
import LoadingPlaceholder from 'components/LoadingPlaceholder';
|
||||
import LoadingIndicator from 'components/LoadingIndicator';
|
||||
@@ -415,12 +415,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
schema={schema}
|
||||
/>
|
||||
{!this.isEditing &&
|
||||
!isShare && (
|
||||
<Backlinks
|
||||
documents={this.props.documents}
|
||||
document={document}
|
||||
/>
|
||||
)}
|
||||
!isShare && <References document={document} />}
|
||||
</MaxWidth>
|
||||
</Container>
|
||||
</Container>
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import Fade from 'components/Fade';
|
||||
import Subheading from 'components/Subheading';
|
||||
import DocumentsStore from 'stores/DocumentsStore';
|
||||
import Document from 'models/Document';
|
||||
import Backlink from './Backlink';
|
||||
|
||||
type Props = {
|
||||
document: Document,
|
||||
documents: DocumentsStore,
|
||||
};
|
||||
|
||||
@observer
|
||||
class Backlinks extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
this.props.documents.fetchBacklinks(this.props.document.id);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { documents, document } = this.props;
|
||||
const backlinks = documents.getBacklinedDocuments(document.id);
|
||||
const showBacklinks = !!backlinks.length;
|
||||
|
||||
return (
|
||||
showBacklinks && (
|
||||
<Fade>
|
||||
<Subheading>Referenced By</Subheading>
|
||||
{backlinks.map(backlinkedDocument => (
|
||||
<Backlink
|
||||
anchor={document.urlId}
|
||||
key={backlinkedDocument.id}
|
||||
document={backlinkedDocument}
|
||||
showCollection={
|
||||
backlinkedDocument.collectionId !== document.collectionId
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Fade>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Backlinks;
|
||||
@@ -5,10 +5,11 @@ import { Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import PublishingInfo from 'components/PublishingInfo';
|
||||
import Document from 'models/Document';
|
||||
import type { NavigationNode } from 'types';
|
||||
|
||||
type Props = {
|
||||
document: Document,
|
||||
anchor: string,
|
||||
document: Document | NavigationNode,
|
||||
anchor?: string,
|
||||
showCollection?: boolean,
|
||||
};
|
||||
|
||||
@@ -43,7 +44,7 @@ const Title = styled.h3`
|
||||
`;
|
||||
|
||||
@observer
|
||||
class Backlink extends React.Component<Props> {
|
||||
class ReferenceListItem extends React.Component<Props> {
|
||||
render() {
|
||||
const { document, showCollection, anchor, ...rest } = this.props;
|
||||
|
||||
@@ -51,16 +52,18 @@ class Backlink extends React.Component<Props> {
|
||||
<DocumentLink
|
||||
to={{
|
||||
pathname: document.url,
|
||||
hash: `d-${anchor}`,
|
||||
hash: anchor ? `d-${anchor}` : undefined,
|
||||
state: { title: document.title },
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<Title>{document.title}</Title>
|
||||
<PublishingInfo document={document} showCollection={showCollection} />
|
||||
{document.updatedBy && (
|
||||
<PublishingInfo document={document} showCollection={showCollection} />
|
||||
)}
|
||||
</DocumentLink>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Backlink;
|
||||
export default ReferenceListItem;
|
||||
83
app/scenes/Document/components/References.js
Normal file
83
app/scenes/Document/components/References.js
Normal file
@@ -0,0 +1,83 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import { withRouter, type Location } from 'react-router-dom';
|
||||
import Fade from 'components/Fade';
|
||||
import Tabs from 'components/Tabs';
|
||||
import Tab from 'components/Tab';
|
||||
import DocumentsStore from 'stores/DocumentsStore';
|
||||
import CollectionsStore from 'stores/CollectionsStore';
|
||||
import Document from 'models/Document';
|
||||
import ReferenceListItem from './ReferenceListItem';
|
||||
|
||||
type Props = {
|
||||
document: Document,
|
||||
documents: DocumentsStore,
|
||||
collections: CollectionsStore,
|
||||
location: Location,
|
||||
};
|
||||
|
||||
@observer
|
||||
class References extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
this.props.documents.fetchBacklinks(this.props.document.id);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { documents, collections, document } = this.props;
|
||||
const backlinks = documents.getBacklinedDocuments(document.id);
|
||||
const collection = collections.get(document.collectionId);
|
||||
const children = collection
|
||||
? collection.getDocumentChildren(document.id)
|
||||
: [];
|
||||
|
||||
const showBacklinks = !!backlinks.length;
|
||||
const showChildren = !!children.length;
|
||||
const isBacklinksTab =
|
||||
this.props.location.hash === '#backlinks' || !showChildren;
|
||||
|
||||
return (
|
||||
(showBacklinks || showChildren) && (
|
||||
<Fade>
|
||||
<Tabs>
|
||||
{showChildren && (
|
||||
<Tab to="#children" isActive={() => !isBacklinksTab}>
|
||||
Child documents
|
||||
</Tab>
|
||||
)}
|
||||
{showBacklinks && (
|
||||
<Tab to="#backlinks" isActive={() => isBacklinksTab}>
|
||||
References
|
||||
</Tab>
|
||||
)}
|
||||
</Tabs>
|
||||
{isBacklinksTab
|
||||
? backlinks.map(backlinkedDocument => (
|
||||
<ReferenceListItem
|
||||
anchor={document.urlId}
|
||||
key={backlinkedDocument.id}
|
||||
document={backlinkedDocument}
|
||||
showCollection={
|
||||
backlinkedDocument.collectionId !== document.collectionId
|
||||
}
|
||||
/>
|
||||
))
|
||||
: children.map(node => {
|
||||
// If we have the document in the store already then use it to get the extra
|
||||
// contextual info, otherwise the collection node will do (only has title and id)
|
||||
const document = documents.get(node.id);
|
||||
return (
|
||||
<ReferenceListItem
|
||||
key={node.id}
|
||||
document={document || node}
|
||||
showCollection={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Fade>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(inject('documents', 'collections')(References));
|
||||
@@ -128,7 +128,9 @@ class Search extends React.Component<Props> {
|
||||
};
|
||||
|
||||
handleNewDoc = () => {
|
||||
this.props.history.push(newDocumentUrl(this.collectionId));
|
||||
if (this.collectionId) {
|
||||
this.props.history.push(newDocumentUrl(this.collectionId));
|
||||
}
|
||||
};
|
||||
|
||||
get includeArchived() {
|
||||
|
||||
Reference in New Issue
Block a user