API endpoint accepts autosave
This commit is contained in:
@@ -11,6 +11,8 @@ import type { User } from 'types';
|
||||
import BaseModel from './BaseModel';
|
||||
import Collection from './Collection';
|
||||
|
||||
type SaveOptions = { publish: boolean, done: boolean, autosave: boolean };
|
||||
|
||||
class Document extends BaseModel {
|
||||
isSaving: boolean = false;
|
||||
hasPendingChanges: boolean = false;
|
||||
@@ -168,7 +170,7 @@ class Document extends BaseModel {
|
||||
};
|
||||
|
||||
@action
|
||||
save = async (publish: boolean = false, done: boolean = false) => {
|
||||
save = async (options: SaveOptions) => {
|
||||
if (this.isSaving) return this;
|
||||
this.isSaving = true;
|
||||
|
||||
@@ -180,8 +182,7 @@ class Document extends BaseModel {
|
||||
title: this.title,
|
||||
text: this.text,
|
||||
lastRevision: this.revision,
|
||||
publish,
|
||||
done,
|
||||
...options,
|
||||
});
|
||||
} else {
|
||||
const data = {
|
||||
@@ -189,8 +190,7 @@ class Document extends BaseModel {
|
||||
collection: this.collection.id,
|
||||
title: this.title,
|
||||
text: this.text,
|
||||
publish,
|
||||
done,
|
||||
...options,
|
||||
};
|
||||
if (this.parentDocument) {
|
||||
data.parentDocument = this.parentDocument;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import debounce from 'lodash/debounce';
|
||||
import styled from 'styled-components';
|
||||
import breakpoint from 'styled-components-breakpoint';
|
||||
import { observable } from 'mobx';
|
||||
@@ -32,6 +33,7 @@ import CenteredContent from 'components/CenteredContent';
|
||||
import PageTitle from 'components/PageTitle';
|
||||
import Search from 'scenes/Search';
|
||||
|
||||
const AUTOSAVE_INTERVAL = 3000;
|
||||
const DISCARD_CHANGES = `
|
||||
You have unsaved changes.
|
||||
Are you sure you want to discard them?
|
||||
@@ -154,20 +156,20 @@ class DocumentScene extends React.Component<Props> {
|
||||
handleCloseMoveModal = () => (this.moveModalOpen = false);
|
||||
handleOpenMoveModal = () => (this.moveModalOpen = true);
|
||||
|
||||
onSave = async (options: { redirect?: boolean, publish?: boolean } = {}) => {
|
||||
const { redirect, publish } = options;
|
||||
|
||||
onSave = async (
|
||||
options: { redirect?: boolean, publish?: boolean, autosave?: boolean } = {}
|
||||
) => {
|
||||
let document = this.document;
|
||||
if (!document || !document.allowSave) return;
|
||||
|
||||
this.editCache = null;
|
||||
this.isSaving = true;
|
||||
this.isPublishing = !!publish;
|
||||
document = await document.save(publish, redirect);
|
||||
this.isPublishing = !!options.publish;
|
||||
document = await document.save(options);
|
||||
this.isSaving = false;
|
||||
this.isPublishing = false;
|
||||
|
||||
if (redirect) {
|
||||
if (options.redirect) {
|
||||
this.props.history.push(document.url);
|
||||
this.props.ui.setActiveDocument(document);
|
||||
} else if (this.props.newDocument) {
|
||||
@@ -176,6 +178,10 @@ class DocumentScene extends React.Component<Props> {
|
||||
}
|
||||
};
|
||||
|
||||
autosave = debounce(async () => {
|
||||
this.onSave({ redirect: false, autosave: true });
|
||||
}, AUTOSAVE_INTERVAL);
|
||||
|
||||
onImageUploadStart = () => {
|
||||
this.isLoading = true;
|
||||
};
|
||||
@@ -189,6 +195,7 @@ class DocumentScene extends React.Component<Props> {
|
||||
if (!document) return;
|
||||
if (document.text.trim() === text.trim()) return;
|
||||
document.updateData({ text }, true);
|
||||
this.autosave();
|
||||
};
|
||||
|
||||
onDiscard = () => {
|
||||
|
||||
@@ -334,7 +334,7 @@ router.post('documents.create', auth(), async ctx => {
|
||||
});
|
||||
|
||||
router.post('documents.update', auth(), async ctx => {
|
||||
const { id, title, text, publish, done, lastRevision } = ctx.body;
|
||||
const { id, title, text, publish, autosave, done, lastRevision } = ctx.body;
|
||||
ctx.assertPresent(id, 'id is required');
|
||||
ctx.assertPresent(title || text, 'title or text is required');
|
||||
|
||||
@@ -355,7 +355,7 @@ router.post('documents.update', auth(), async ctx => {
|
||||
if (publish) {
|
||||
await document.publish();
|
||||
} else {
|
||||
await document.save();
|
||||
await document.save({ autosave });
|
||||
|
||||
if (document.publishedAt && done) {
|
||||
events.add({ name: 'documents.update', model: document });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
import TestServer from 'fetch-test-server';
|
||||
import app from '..';
|
||||
import { Document, View, Star } from '../models';
|
||||
import { Document, View, Star, Revision } from '../models';
|
||||
import { flushdb, seed } from '../test/support';
|
||||
import { buildUser } from '../test/factories';
|
||||
|
||||
@@ -484,6 +484,31 @@ describe('#documents.update', async () => {
|
||||
expect(body.data.collection.documents[0].title).toBe('Updated title');
|
||||
});
|
||||
|
||||
it('should not create new version when autosave=true', async () => {
|
||||
const { user, document } = await seed();
|
||||
|
||||
const res = await server.post('/api/documents.update', {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
id: document.id,
|
||||
title: 'Updated title',
|
||||
text: 'Updated text',
|
||||
lastRevision: document.revision,
|
||||
autosave: true,
|
||||
},
|
||||
});
|
||||
|
||||
const prevRevisionRecords = await Revision.count();
|
||||
const body = await res.json();
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.title).toBe('Updated title');
|
||||
expect(body.data.text).toBe('Updated text');
|
||||
|
||||
const revisionRecords = await Revision.count();
|
||||
expect(revisionRecords).toBe(prevRevisionRecords);
|
||||
});
|
||||
|
||||
it('should fallback to a default title', async () => {
|
||||
const { user, document } = await seed();
|
||||
|
||||
|
||||
@@ -25,8 +25,9 @@ const slugify = text =>
|
||||
remove: /[.]/g,
|
||||
});
|
||||
|
||||
const createRevision = doc => {
|
||||
// Create revision of the current (latest)
|
||||
const createRevision = (doc, options = {}) => {
|
||||
if (options.autosave) return;
|
||||
|
||||
return Revision.create({
|
||||
title: doc.title,
|
||||
text: doc.text,
|
||||
@@ -204,15 +205,14 @@ Document.searchForUser = async (
|
||||
LIMIT :limit OFFSET :offset;
|
||||
`;
|
||||
|
||||
const results = await sequelize
|
||||
.query(sql, {
|
||||
replacements: {
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
model: Document,
|
||||
})
|
||||
const results = await sequelize.query(sql, {
|
||||
replacements: {
|
||||
query,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
model: Document,
|
||||
});
|
||||
const ids = results.map(document => document.id);
|
||||
|
||||
// Second query to get views for the data
|
||||
|
||||
Reference in New Issue
Block a user