diff --git a/frontend/components/PublishingInfo/PublishingInfo.js b/frontend/components/PublishingInfo/PublishingInfo.js index 166c23fc..298e7ef6 100644 --- a/frontend/components/PublishingInfo/PublishingInfo.js +++ b/frontend/components/PublishingInfo/PublishingInfo.js @@ -45,23 +45,24 @@ class PublishingInfo extends Component { ))} } - - {this.props.createdBy.name} - {' '} - published - {' '} - {moment(this.props.createdAt).fromNow()} - {this.props.createdAt !== this.props.updatedAt - ? -  and  - {this.props.createdBy.id !== this.props.updatedBy.id && - ` ${this.props.updatedBy.name} `} - modified - {' '} - {moment(this.props.updatedAt).fromNow()} - - : null} - + {this.props.createdBy && + + {this.props.createdBy.name} + {' '} + published + {' '} + {moment(this.props.createdAt).fromNow()} + {this.props.createdAt !== this.props.updatedAt + ? +  and  + {this.props.createdBy.id !== this.props.updatedBy.id && + ` ${this.props.updatedBy.name} `} + modified + {' '} + {moment(this.props.updatedAt).fromNow()} + + : null} + } ); } diff --git a/frontend/components/SidebarHidden/SidebarHidden.js b/frontend/components/SidebarHidden/SidebarHidden.js new file mode 100644 index 00000000..adf26b44 --- /dev/null +++ b/frontend/components/SidebarHidden/SidebarHidden.js @@ -0,0 +1,25 @@ +// @flow +import { Component } from 'react'; +import { inject } from 'mobx-react'; +import UiStore from 'stores/UiStore'; + +class SidebarHidden extends Component { + props: { + ui: UiStore, + children: React$Element, + }; + + componentDidMount() { + this.props.ui.enableEditMode(); + } + + componentWillUnmount() { + this.props.ui.disableEditMode(); + } + + render() { + return this.props.children; + } +} + +export default inject('ui')(SidebarHidden); diff --git a/frontend/components/SidebarHidden/index.js b/frontend/components/SidebarHidden/index.js new file mode 100644 index 00000000..b63c0b7a --- /dev/null +++ b/frontend/components/SidebarHidden/index.js @@ -0,0 +1,3 @@ +// @flow +import SidebarHidden from './SidebarHidden'; +export default SidebarHidden; diff --git a/frontend/index.js b/frontend/index.js index 160ef99d..22843cda 100644 --- a/frontend/index.js +++ b/frontend/index.js @@ -35,6 +35,7 @@ import Error404 from 'scenes/Error404'; import ScrollToTop from 'components/ScrollToTop'; import Layout from 'components/Layout'; +import SidebarHidden from 'components/SidebarHidden'; import flatpages from 'static/flatpages'; @@ -86,11 +87,12 @@ const KeyboardShortcuts = () => ( ); const Api = () => ; const DocumentNew = () => ; -const DocumentNewChild = () => ; const RedirectDocument = ({ match }: { match: Object }) => ( ); +const matchDocumentSlug = ':documentSlug([0-9a-zA-Z-]*-[a-zA-z0-9]{10,15})'; + render(
@@ -111,26 +113,28 @@ render( - - - - + + + + + + diff --git a/frontend/models/Document.js b/frontend/models/Document.js index e19f7971..097e4563 100644 --- a/frontend/models/Document.js +++ b/frontend/models/Document.js @@ -15,7 +15,7 @@ const parseHeader = text => { }; class Document { - isSaving: boolean; + isSaving: boolean = false; hasPendingChanges: boolean = false; errors: ErrorsStore; @@ -25,10 +25,10 @@ class Document { createdBy: User; html: string; id: string; - private: boolean; - starred: boolean; team: string; - text: string; + private: boolean = false; + starred: boolean = false; + text: string = ''; title: string = 'Untitled document'; updatedAt: string; updatedBy: User; @@ -83,9 +83,9 @@ class Document { }; @action view = async () => { + this.views++; try { await client.post('/views.create', { id: this.id }); - this.views++; } catch (e) { this.errors.add('Document failed to record view'); } @@ -133,20 +133,25 @@ class Document { } invariant(res && res.data, 'Data should be available'); - this.hasPendingChanges = false; + this.updateData({ + ...res.data, + hasPendingChanges: false, + }); } catch (e) { this.errors.add('Document failed saving'); } finally { this.isSaving = false; } + + return this; }; updateData(data: Object | Document) { - data.title = parseHeader(data.text); + if (data.text) data.title = parseHeader(data.text); extendObservable(this, data); } - constructor(document: Document) { + constructor(document?: Object = {}) { this.updateData(document); this.errors = stores.errors; } diff --git a/frontend/scenes/Document/Document.js b/frontend/scenes/Document/Document.js index f3cfa06c..c37c5172 100644 --- a/frontend/scenes/Document/Document.js +++ b/frontend/scenes/Document/Document.js @@ -6,6 +6,7 @@ import { observer, inject } from 'mobx-react'; import { withRouter, Prompt } from 'react-router'; import { Flex } from 'reflexbox'; +import Document from 'models/Document'; import UiStore from 'stores/UiStore'; import DocumentsStore from 'stores/DocumentsStore'; import Menu from './components/Menu'; @@ -28,15 +29,18 @@ type Props = { history: Object, keydown: Object, documents: DocumentsStore, - newChildDocument?: boolean, + newDocument?: boolean, ui: UiStore, }; -@observer class Document extends Component { +@observer class DocumentScene extends Component { props: Props; - + state: { + newDocument?: Document, + }; state = { isLoading: false, + newDocument: undefined, }; componentDidMount() { @@ -57,27 +61,29 @@ type Props = { } loadDocument = async props => { - let document = this.document; - if (document) { - this.props.ui.setActiveDocument(document); - } - - await this.props.documents.fetch(props.match.params.documentSlug); - document = this.document; - - if (document) { - this.props.ui.setActiveDocument(document); - document.view(); - } - - if (this.props.match.params.edit) { - this.props.ui.enableEditMode(); + if (props.newDocument) { + const newDocument = new Document({ + collection: { id: props.match.params.id }, + }); + this.setState({ newDocument }); } else { - this.props.ui.disableEditMode(); + let document = this.document; + if (document) { + this.props.ui.setActiveDocument(document); + } + + await this.props.documents.fetch(props.match.params.documentSlug); + document = this.document; + + if (document) { + this.props.ui.setActiveDocument(document); + document.view(); + } } }; get document() { + if (this.state.newDocument) return this.state.newDocument; return this.props.documents.getByUrl( `/doc/${this.props.match.params.documentSlug}` ); @@ -87,19 +93,17 @@ type Props = { if (!this.document) return; const url = `${this.document.url}/edit`; this.props.history.push(url); - this.props.ui.enableEditMode(); }; onSave = async (redirect: boolean = false) => { - const document = this.document; + let document = this.document; if (!document) return; this.setState({ isLoading: true }); - await document.save(); + document = await document.save(); this.setState({ isLoading: false }); - this.props.ui.disableEditMode(); - if (redirect) { + if (redirect || this.props.newDocument) { this.props.history.push(document.url); } }; @@ -122,8 +126,8 @@ type Props = { }; render() { - const isNew = this.props.newDocument || this.props.newChildDocument; - const isEditing = this.props.match.params.edit; + const isNew = this.props.newDocument; + const isEditing = this.props.match.params.edit || isNew; const isFetching = !this.document; const titleText = get(this.document, 'title', 'Loading'); @@ -217,4 +221,4 @@ const DocumentContainer = styled.div` width: 50em; `; -export default withRouter(inject('ui', 'documents')(Document)); +export default withRouter(inject('ui', 'user', 'documents')(DocumentScene)); diff --git a/frontend/scenes/Document/components/Menu.js b/frontend/scenes/Document/components/Menu.js index 76ac51a9..a5fe0976 100644 --- a/frontend/scenes/Document/components/Menu.js +++ b/frontend/scenes/Document/components/Menu.js @@ -16,9 +16,7 @@ type Props = { props: Props; onCreateDocument = () => { - // Disabled until created a better API - // invariant(this.props.collectionTree, 'collectionTree is not available'); - // this.props.history.push(`${this.props.collectionTree.url}/new`); + this.props.history.push(`${this.props.document.collection.url}/new`); }; onCreateChild = () => { @@ -68,7 +66,6 @@ type Props = { New document - New child
} Export {allowDelete && Delete} diff --git a/server/models/Document.js b/server/models/Document.js index 206cb161..d94fe7f5 100644 --- a/server/models/Document.js +++ b/server/models/Document.js @@ -9,7 +9,7 @@ import { convertToMarkdown } from '../../frontend/utils/markdown'; import { truncateMarkdown } from '../utils/truncate'; import Revision from './Revision'; -const URL_REGEX = /^[a-zA-Z0-9-]*-([a-zA-Z]{10,15})$/; +const URL_REGEX = /^[a-zA-Z0-9-]*-([a-zA-Z0-9]{10,15})$/; slug.defaults.mode = 'rfc3986'; const slugify = text =>