* WIP: Slate editor
* WIP
* Focus at start / end working
* ah ha
* Super basic floating toolbar
* Nested list editing
* Pulling more logic into plugins
* inline code markdown
* Backspace at end of code block should remove mark
* Ensure there is always an empty line at editor end
* Add keyboard shortcuts for bold, italic, underline
* Add strikethrough shortcode and toolbar
* Toolbar to declarative
Fixed paragraph styling
Removed unused stuffs
* Super basic link editing
* Split Toolbar, now possible to edit and remove links
* Add new link to selection from toolbar working
* Ensure toolbar doesn't extend off screen
* Fix minor js issues, disable formatting of document title
* Boom, icons
* Remove codemirror, fix MD parsing issues
* CMD+S now saves inplace
* Add --- shortcut for horizontal rule
* Improved styling for link editor
* Add header anchors in readOnly
* More readable core text color
* Restored image file uploading 🎉
* Add support for inline md syntax, ** __ etc
* Centered
* Flooooow
* Checklist support
* Upgrade edit list plugin
* Finally. Allow keydown within rich textarea
* Update Markdown serializer
* Cleanup, remove async editor loading
* Editor > MarkdownEditor
Fixed unsaved changes warning triggered when all changes are saved
* MOAR typing
* Combine edit and view
* Fixed checkboxes still editable in readOnly
* wip
* Breadcrumb
Restored scroll
* Move document scene actions to menu
* Added: Support for code blocks, syntax highlighting
* Cleanup
* > styled component
* Prevent CMD+Enter from adding linebreak
* Show image uploading in layout activity indicator
* Upgrade editor deps
* Improve link toolbar. Only one scenario where it's not working now
118 lines
2.9 KiB
JavaScript
118 lines
2.9 KiB
JavaScript
// @flow
|
|
import _ from 'lodash';
|
|
import { browserHistory } from 'react-router';
|
|
import stores from 'stores';
|
|
|
|
type Options = {
|
|
baseUrl?: string,
|
|
};
|
|
|
|
class ApiClient {
|
|
baseUrl: string;
|
|
userAgent: string;
|
|
|
|
constructor(options: Options = {}) {
|
|
this.baseUrl = options.baseUrl || '/api';
|
|
this.userAgent = 'AtlasFrontend';
|
|
}
|
|
|
|
fetch = (
|
|
path: string,
|
|
method: string,
|
|
data: ?Object,
|
|
options: Object = {}
|
|
) => {
|
|
let body;
|
|
let modifiedPath;
|
|
|
|
if (method === 'GET') {
|
|
if (data) {
|
|
modifiedPath = `${path}?${data && this.constructQueryString(data)}`;
|
|
} else {
|
|
modifiedPath = path;
|
|
}
|
|
} else if (method === 'POST' || method === 'PUT') {
|
|
body = JSON.stringify(data);
|
|
}
|
|
|
|
// Construct headers
|
|
const headers = new Headers({
|
|
Accept: 'application/json',
|
|
'Content-Type': 'application/json',
|
|
});
|
|
if (stores.user.authenticated) {
|
|
// $FlowFixMe this is not great, need to refactor
|
|
headers.set('Authorization', `Bearer ${stores.user.token}`);
|
|
}
|
|
|
|
// Construct request
|
|
// $FlowFixMe don't care much about this right now
|
|
const request = fetch(this.baseUrl + (modifiedPath || path), {
|
|
method,
|
|
body,
|
|
headers,
|
|
redirect: 'follow',
|
|
});
|
|
|
|
// Handle request promises and return a new promise
|
|
return new Promise((resolve, reject) => {
|
|
request
|
|
.then(response => {
|
|
// Handle successful responses
|
|
if (response.status >= 200 && response.status < 300) {
|
|
return response;
|
|
}
|
|
|
|
// Handle 404
|
|
if (response.status === 404) {
|
|
return browserHistory.push('/404');
|
|
}
|
|
|
|
// Handle 401, log out user
|
|
if (response.status === 401) {
|
|
return stores.user.logout();
|
|
}
|
|
|
|
// Handle failed responses
|
|
const error = {};
|
|
error.statusCode = response.status;
|
|
error.response = response;
|
|
throw error;
|
|
})
|
|
.then(response => {
|
|
return response && response.json();
|
|
})
|
|
.then(json => {
|
|
resolve(json);
|
|
})
|
|
.catch(error => {
|
|
error.response.json().then(json => {
|
|
error.data = json;
|
|
reject(error);
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
get = (path: string, data?: Object, options?: Object) => {
|
|
return this.fetch(path, 'GET', data, options);
|
|
};
|
|
|
|
post = (path: string, data?: Object, options?: Object) => {
|
|
return this.fetch(path, 'POST', data, options);
|
|
};
|
|
|
|
// Helpers
|
|
constructQueryString = (data: Object) => {
|
|
return _.map(data, (v, k) => {
|
|
return `${encodeURIComponent(k)}=${encodeURIComponent(v)}`;
|
|
}).join('&');
|
|
};
|
|
}
|
|
|
|
export default ApiClient;
|
|
|
|
// In case you don't want to always initiate, just import with `import { client } ...`
|
|
const client = new ApiClient();
|
|
export { client };
|