Restore New Document Functionality (#94)
* Restored New Document functionality * Declarative SidebarHidden * Fix edit route
This commit is contained in:
@@ -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>
|
||||
and
|
||||
{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>
|
||||
and
|
||||
{this.props.createdBy.id !== this.props.updatedBy.id &&
|
||||
` ${this.props.updatedBy.name} `}
|
||||
modified
|
||||
{' '}
|
||||
{moment(this.props.updatedAt).fromNow()}
|
||||
</span>
|
||||
: null}
|
||||
</span>}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
25
frontend/components/SidebarHidden/SidebarHidden.js
Normal file
25
frontend/components/SidebarHidden/SidebarHidden.js
Normal 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);
|
||||
3
frontend/components/SidebarHidden/index.js
Normal file
3
frontend/components/SidebarHidden/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
// @flow
|
||||
import SidebarHidden from './SidebarHidden';
|
||||
export default SidebarHidden;
|
||||
@@ -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} />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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>}
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
Reference in New Issue
Block a user