feat: Separate title from body (#1216)
* first pass at updating all Time components each second * fix a couple date variable typos * use class style state management instead of hooks * wip: Separate title from body * address feedback * test: Remove unused test * feat: You in publishing info language fix: Removal of secondary headings * After much deliberation… a migration is needed for this to be reliable * fix: Export to work with new title structure * fix: Untitled * fix: Consistent spacing of first editor node * fix: Emoji in title handling * fix: Time component not updating for new props * chore: Add createdAt case * fix: Conflict after merging new TOC * PR feedback * lint * fix: Heading level adjustment Co-authored-by: Taylor Lapeyre <taylorlapeyre@gmail.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import styled from 'styled-components';
|
||||
import breakpoint from 'styled-components-breakpoint';
|
||||
import { observable } from 'mobx';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import { schema } from 'rich-markdown-editor';
|
||||
import { Prompt, Route, withRouter } from 'react-router-dom';
|
||||
import type { Location, RouterHistory } from 'react-router-dom';
|
||||
import keydown from 'react-keydown';
|
||||
@@ -37,8 +38,6 @@ import AuthStore from 'stores/AuthStore';
|
||||
import Document from 'models/Document';
|
||||
import Revision from 'models/Revision';
|
||||
|
||||
import schema from '../schema';
|
||||
|
||||
let EditorImport;
|
||||
const AUTOSAVE_DELAY = 3000;
|
||||
const IS_DIRTY_DELAY = 500;
|
||||
@@ -75,9 +74,11 @@ class DocumentScene extends React.Component<Props> {
|
||||
@observable isDirty: boolean = false;
|
||||
@observable isEmpty: boolean = true;
|
||||
@observable moveModalOpen: boolean = false;
|
||||
@observable title: string;
|
||||
|
||||
constructor(props) {
|
||||
super();
|
||||
this.title = props.document.title;
|
||||
this.loadEditor();
|
||||
}
|
||||
|
||||
@@ -168,13 +169,20 @@ class DocumentScene extends React.Component<Props> {
|
||||
|
||||
// get the latest version of the editor text value
|
||||
const text = this.getEditorText ? this.getEditorText() : document.text;
|
||||
const title = this.title;
|
||||
|
||||
// prevent save before anything has been written (single hash is empty doc)
|
||||
if (text.trim() === '#') return;
|
||||
if (text.trim() === '' && title.trim === '') return;
|
||||
|
||||
// prevent autosave if nothing has changed
|
||||
if (options.autosave && document.text.trim() === text.trim()) return;
|
||||
if (
|
||||
options.autosave &&
|
||||
document.text.trim() === text.trim() &&
|
||||
document.title.trim() === title.trim()
|
||||
)
|
||||
return;
|
||||
|
||||
document.title = title;
|
||||
document.text = text;
|
||||
|
||||
let isNew = !document.id;
|
||||
@@ -201,10 +209,12 @@ class DocumentScene extends React.Component<Props> {
|
||||
updateIsDirty = () => {
|
||||
const { document } = this.props;
|
||||
const editorText = this.getEditorText().trim();
|
||||
const titleChanged = this.title !== document.title;
|
||||
const bodyChanged = editorText !== document.text.trim();
|
||||
|
||||
// a single hash is a doc with just an empty title
|
||||
this.isEmpty = !editorText || editorText === '#';
|
||||
this.isDirty = !!document && editorText !== document.text.trim();
|
||||
this.isEmpty = (!editorText || editorText === '#') && !this.title;
|
||||
this.isDirty = bodyChanged || titleChanged;
|
||||
};
|
||||
|
||||
updateIsDirtyDebounced = debounce(this.updateIsDirty, IS_DIRTY_DELAY);
|
||||
@@ -223,6 +233,12 @@ class DocumentScene extends React.Component<Props> {
|
||||
this.autosave();
|
||||
};
|
||||
|
||||
onChangeTitle = event => {
|
||||
this.title = event.target.value;
|
||||
this.updateIsDirtyDebounced();
|
||||
this.autosave();
|
||||
};
|
||||
|
||||
goBack = () => {
|
||||
let url;
|
||||
if (this.props.document.url) {
|
||||
@@ -245,7 +261,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
} = this.props;
|
||||
const team = auth.team;
|
||||
const Editor = this.editorComponent;
|
||||
const isShare = match.params.shareId;
|
||||
const isShare = !!match.params.shareId;
|
||||
|
||||
if (!Editor) {
|
||||
return <Loading location={location} />;
|
||||
@@ -334,13 +350,16 @@ class DocumentScene extends React.Component<Props> {
|
||||
readOnly && <Contents document={revision || document} />}
|
||||
<Editor
|
||||
id={document.id}
|
||||
isDraft={document.isDraft}
|
||||
key={disableEmbeds ? 'embeds-disabled' : 'embeds-enabled'}
|
||||
title={revision ? revision.title : this.title}
|
||||
document={document}
|
||||
defaultValue={revision ? revision.text : document.text}
|
||||
pretitle={document.emoji}
|
||||
disableEmbeds={disableEmbeds}
|
||||
onImageUploadStart={this.onImageUploadStart}
|
||||
onImageUploadStop={this.onImageUploadStop}
|
||||
onSearchLink={this.props.onSearchLink}
|
||||
onChangeTitle={this.onChangeTitle}
|
||||
onChange={this.onChange}
|
||||
onSave={this.onSave}
|
||||
onPublish={this.onPublish}
|
||||
@@ -350,7 +369,6 @@ class DocumentScene extends React.Component<Props> {
|
||||
schema={schema}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
{readOnly &&
|
||||
!isShare &&
|
||||
!revision && (
|
||||
@@ -389,7 +407,6 @@ const MaxWidth = styled(Flex)`
|
||||
|
||||
${breakpoint('desktopLarge')`
|
||||
max-width: calc(48px + 46em);
|
||||
box-sizing:
|
||||
`};
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user