//////////////////////////////////////////////////////////////////////////// // Item description (also called "Summary") // // Provides a way to edit the description. If the content changed on the backend // in the meantime, opens a dialog and offers to resolve potential merge // conflicts. //////////////////////////////////////////////////////////////////////////// Vue.component('CategoryItemDescription', { props: { /** The ID of the item that the description belongs to */ itemId: {type: String, required: true}, /** Description itself, contains `text` and `html` */ initContent: {type: Object, required: true}, }, data: function() { return { /** * `editing` signals whether we're in editing mode * */ editing: false, /** * `unsaved` signals whether the editor might have unsaved changes. If * this is the case, we hide it instead of destroying it. When the user * clicks "Cancel", their changes get lost; when they click the pencil * button, they remain. * */ unsaved: false, /** * `startingContent` is the "starting point" of editing. When we submit, * the backend does a three-way diff between the description as we've * last seen it (i.e. `startingContent`), our proposed edited * description, and the one that the backend currently has. * */ startingContent: initContent, }}, computed: { /** DOM identifier for our item */ itemNode: function() { return "item-" + this.itemId }, }, methods: { /** * Submit description to the backend and, if it changed on the backend * in the meantime, show a merge resolution popup. If the content * changes *again* while the user resolves the merge, we'll show another * popup, etc. * * TODO: it would be nice to replace `startingContent` when we submit * the merged version, but we don't get rendered Markdown from the * backend, so we can't do that. * */ submitDescription(startingContentText, editedContentText) { $.post({ url: `/haskell/set/item/${this.itemId}/description`, data: { original: startingContentText, content: editedContentText }, success: (data) => { $.magnificPopup.close(); this.editing = false; this.unsaved = false; this.startingContent = data; }, statusCode: { 409: (xhr, st, err) => { /** Current item description on the backend */ let backendText = xhr.responseJSON["modified"]; /** Backend's proposed merge (the diff between our * 'startingContentText' and 'editedContentText, applied * to 'backendText') */ let proposedMerge = xhr.responseJSON["merged"]; showDiffPopup(startingContentText, backendText, proposedMerge, (ourMerge) => { // Now the user looked at the `backendText` and // (hopefully) applied their edits to it, so // `backendText` becomes the new starting point this.submitDescription(backendText, ourMerge) }); } }, }); }, }, // TODO: we don't need 'section' anymore, I think. Also, there's a bit of // duplication in this template. // // TODO: check that when an editor opens, it gets focus (everywhere we use // an editor) template: `
Summary

write something here!

Summary
`, }); // rows=10 should go somewhere