Restore New Document Functionality (#94)

* Restored New Document functionality

* Declarative SidebarHidden

* Fix edit route
This commit is contained in:
Tom Moor
2017-07-04 23:02:06 -07:00
committed by GitHub
parent 88cdbc413a
commit 1c83257483
8 changed files with 111 additions and 72 deletions

View File

@@ -45,23 +45,24 @@ class PublishingInfo extends Component {
<Avatar key={user.id} src={user.avatarUrl} title={user.name} />
))}
</Avatars>}
<span>
{this.props.createdBy.name}
{' '}
published
{' '}
{moment(this.props.createdAt).fromNow()}
{this.props.createdAt !== this.props.updatedAt
? <span>
&nbsp;and&nbsp;
{this.props.createdBy.id !== this.props.updatedBy.id &&
` ${this.props.updatedBy.name} `}
modified
{' '}
{moment(this.props.updatedAt).fromNow()}
</span>
: null}
</span>
{this.props.createdBy &&
<span>
{this.props.createdBy.name}
{' '}
published
{' '}
{moment(this.props.createdAt).fromNow()}
{this.props.createdAt !== this.props.updatedAt
? <span>
&nbsp;and&nbsp;
{this.props.createdBy.id !== this.props.updatedBy.id &&
` ${this.props.updatedBy.name} `}
modified
{' '}
{moment(this.props.updatedAt).fromNow()}
</span>
: null}
</span>}
</Container>
);
}

View File

@@ -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<any>,
};
componentDidMount() {
this.props.ui.enableEditMode();
}
componentWillUnmount() {
this.props.ui.disableEditMode();
}
render() {
return this.props.children;
}
}
export default inject('ui')(SidebarHidden);

View File

@@ -0,0 +1,3 @@
// @flow
import SidebarHidden from './SidebarHidden';
export default SidebarHidden;

View File

@@ -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 = () => <Flatpage title="API" content={flatpages.api} />;
const DocumentNew = () => <Document newDocument />;
const DocumentNewChild = () => <Document newChildDocument />;
const RedirectDocument = ({ match }: { match: Object }) => (
<Redirect to={`/doc/${match.params.documentSlug}`} />
);
const matchDocumentSlug = ':documentSlug([0-9a-zA-Z-]*-[a-zA-z0-9]{10,15})';
render(
<div style={{ display: 'flex', flex: 1, height: '100%' }}>
<Provider {...stores}>
@@ -111,26 +113,28 @@ render(
<Route exact path="/collections/:id" component={Collection} />
<Route
exact
path="/d/:documentSlug([0-9a-zA-Z-]*-[a-zA-z]{10,15})"
path={`/d/${matchDocumentSlug}`}
component={RedirectDocument}
/>
<Route
exact
path="/doc/:documentSlug([0-9a-zA-Z-]*-[a-zA-z]{10,15})"
path={`/doc/${matchDocumentSlug}`}
component={Document}
/>
<Route
exact
path="/doc/:documentSlug([0-9a-zA-Z-]*-[a-zA-z]{10,15})/:edit"
component={Document}
/>
<Route
exact
path="/collections/:id/new"
component={DocumentNew}
/>
<Route exact path="/d/:id/new" component={DocumentNewChild} />
<SidebarHidden>
<Switch>
<Route
exact
path={`/doc/${matchDocumentSlug}/:edit`}
component={Document}
/>
<Route
exact
path="/collections/:id/new"
component={DocumentNew}
/>
</Switch>
</SidebarHidden>
<Route exact path="/search" component={Search} />
<Route exact path="/search/:query" component={Search} />

View File

@@ -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;
}

View File

@@ -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));

View File

@@ -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 = {
<MenuItem onClick={this.onCreateDocument}>
New document
</MenuItem>
<MenuItem onClick={this.onCreateChild}>New child</MenuItem>
</div>}
<MenuItem onClick={this.onExport}>Export</MenuItem>
{allowDelete && <MenuItem onClick={this.onDelete}>Delete</MenuItem>}

View File

@@ -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 =>