diff --git a/ghost/admin/app/components/modal-post-history.js b/ghost/admin/app/components/modal-post-history.js index 6248f60adb..a7b99d9f4a 100644 --- a/ghost/admin/app/components/modal-post-history.js +++ b/ghost/admin/app/components/modal-post-history.js @@ -21,217 +21,218 @@ function checkFinishedRendering(element, done) { } export default class ModalPostHistory extends Component { - @service notifications; - @service modals; - @service feature; - constructor() { - super(...arguments); - this.post = this.args.model.post; - this.editorAPI = this.args.model.editorAPI; - this.toggleSettingsMenu = this.args.model.toggleSettingsMenu; - } - @tracked selectedHTML = null; - @tracked diffHtml = null; - @tracked showDifferences = this.feature.get('postDiffing'); // should default to true in future - @tracked selectedRevisionIndex = 0; + @service notifications; + @service modals; + @service feature; + @tracked selectedHTML = null; + @tracked diffHtml = null; + @tracked showDifferences = this.feature.get('postDiffing'); // should default to true in future + @tracked selectedRevisionIndex = 0; - get selectedRevision() { - return this.revisionList[this.selectedRevisionIndex]; - } + constructor() { + super(...arguments); + this.post = this.args.model.post; + this.editorAPI = this.args.model.editorAPI; + this.toggleSettingsMenu = this.args.model.toggleSettingsMenu; + } - get comparisonRevision() { - return this.revisionList[this.selectedRevisionIndex + 1] || this.selectedRevision; - } + get selectedRevision() { + return this.revisionList[this.selectedRevisionIndex]; + } - get previousTitle() { - return this.comparisonRevision.title || this.post.get('title'); - } + get comparisonRevision() { + return this.revisionList[this.selectedRevisionIndex + 1] || this.selectedRevision; + } - get currentTitle() { - return this.selectedRevision.title || this.post.get('title'); - } + get previousTitle() { + return this.comparisonRevision.title || this.post.get('title'); + } - get revisionList() { - const revisions = this.post.get('postRevisions').toArray().sort((a, b) => b.get('createdAt') - a.get('createdAt')); - return revisions.map((revision, index) => { - return { - lexical: revision.get('lexical'), - selected: index === this.selectedRevisionIndex, - latest: index === 0, - createdAt: revision.get('createdAt'), - title: revision.get('title'), - feature_image: revision.get('featureImage'), - feature_image_alt: revision.get('featureImageAlt'), - feature_image_caption: revision.get('featureImageCaption'), - author: { - name: revision.get('author.name') || 'Deleted staff user' - }, - postStatus: revision.get('postStatus'), - reason: revision.get('reason'), - new_publish: revision.get('postStatus') === 'published' && revisions[index + 1]?.get('postStatus') === 'draft', - new_unpublish: revision.get('postStatus') === 'draft' && revisions[index + 1]?.get('postStatus') === 'published' - }; - }); - } + get currentTitle() { + return this.selectedRevision.title || this.post.get('title'); + } - @action - onInsert() { - this.updateDiff(); - window.addEventListener('keydown', this.handleKeyDown); - } + get revisionList() { + const revisions = this.post.get('postRevisions').toArray().sort((a, b) => b.get('createdAt') - a.get('createdAt')); + return revisions.map((revision, index) => { + return { + lexical: revision.get('lexical'), + selected: index === this.selectedRevisionIndex, + latest: index === 0, + createdAt: revision.get('createdAt'), + title: revision.get('title'), + feature_image: revision.get('featureImage'), + feature_image_alt: revision.get('featureImageAlt'), + feature_image_caption: revision.get('featureImageCaption'), + author: { + name: revision.get('author.name') || 'Deleted staff user' + }, + postStatus: revision.get('postStatus'), + reason: revision.get('reason'), + new_publish: revision.get('postStatus') === 'published' && revisions[index + 1]?.get('postStatus') === 'draft', + new_unpublish: revision.get('postStatus') === 'draft' && revisions[index + 1]?.get('postStatus') === 'published' + }; + }); + } - @action - willDestroy() { - super.willDestroy(...arguments); - window.removeEventListener('keydown', this.handleKeyDown); - } + @action + onInsert() { + this.updateDiff(); + window.addEventListener('keydown', this.handleKeyDown); + } - @action - handleKeyDown(event) { - if (event.key === 'Escape') { - this.args.closeModal(); - } - } + @action + willDestroy() { + super.willDestroy(...arguments); + window.removeEventListener('keydown', this.handleKeyDown); + } - @action - handleClick(index) { - this.selectedRevisionIndex = index; - this.updateDiff(); - } + @action + handleKeyDown(event) { + if (event.key === 'Escape') { + this.args.closeModal(); + } + } - @action - registerSelectedEditorApi(api) { - this.selectedEditor = api; - } + @action + handleClick(index) { + this.selectedRevisionIndex = index; + this.updateDiff(); + } - @action - registerComparisonEditorApi(api) { - this.comparisonEditor = api; - } + @action + registerSelectedEditorApi(api) { + this.selectedEditor = api; + } - @action - closeModal() { - this.args.closeModal(); - } + @action + registerComparisonEditorApi(api) { + this.comparisonEditor = api; + } - stripInitialPlaceholder(html) { - //TODO: we should probably add a data attribute to Koenig and grab that instead - const regex = /]*>(\s*Begin writing your post\.\.\.\s*)<\/div>/i; - const strippedHtml = html.replace(regex, ''); - return strippedHtml; - } + @action + closeModal() { + this.args.closeModal(); + } - @action - restoreRevision(index) { - const revision = this.revisionList[index]; - this.modals.open(RestoreRevisionModal, { - post: this.post, - revision, - updateTitle: () => { - set(this.post, 'titleScratch', revision.title); - }, - updateEditor: () => { - const state = this.editorAPI.editorInstance.parseEditorState(revision.lexical); - this.editorAPI.editorInstance.setEditorState(state); - }, - closePostHistoryModal: () => { - this.closeModal(); - this.toggleSettingsMenu(); - } - }); - } + stripInitialPlaceholder(html) { + //TODO: we should probably add a data attribute to Koenig and grab that instead + const regex = /]*>(\s*Begin writing your post\.\.\.\s*)<\/div>/i; + const strippedHtml = html.replace(regex, ''); + return strippedHtml; + } - @action - toggleDifferences() { - this.showDifferences = !this.showDifferences; - } + @action + restoreRevision(index) { + const revision = this.revisionList[index]; + this.modals.open(RestoreRevisionModal, { + post: this.post, + revision, + updateTitle: () => { + set(this.post, 'titleScratch', revision.title); + }, + updateEditor: () => { + const state = this.editorAPI.editorInstance.parseEditorState(revision.lexical); + this.editorAPI.editorInstance.setEditorState(state); + }, + closePostHistoryModal: () => { + this.closeModal(); + this.toggleSettingsMenu(); + } + }); + } - get cardConfig() { - return { - post: this.args.model - }; - } + @action + toggleDifferences() { + this.showDifferences = !this.showDifferences; + } - calculateHTMLDiff(previousHTML, currentHTML) { - const result = diff(previousHTML, currentHTML); - const div = document.createElement('div'); - div.innerHTML = result; - this.diffCards(div); - return div.innerHTML; - } + get cardConfig() { + return { + post: this.args.model + }; + } - diffCards(div) { - const cards = div.querySelectorAll('div[data-kg-card]'); - for (const card of cards) { - const hasChanges = !!card.querySelectorAll('del').length || !!card.querySelectorAll('ins').length; - if (hasChanges) { - const delCard = card.cloneNode(true); - const insCard = card.cloneNode(true); + calculateHTMLDiff(previousHTML, currentHTML) { + const result = diff(previousHTML, currentHTML); + const div = document.createElement('div'); + div.innerHTML = result; + this.diffCards(div); + return div.innerHTML; + } - const ins = document.createElement('ins'); - const del = document.createElement('del'); + diffCards(div) { + const cards = div.querySelectorAll('div[data-kg-card]'); + for (const card of cards) { + const hasChanges = !!card.querySelectorAll('del').length || !!card.querySelectorAll('ins').length; + if (hasChanges) { + const delCard = card.cloneNode(true); + const insCard = card.cloneNode(true); - delCard.querySelectorAll('ins').forEach((el) => { - el.remove(); - }); - insCard.querySelectorAll('del').forEach((el) => { - el.remove(); - }); - delCard.querySelectorAll('del').forEach((el) => { - el.parentNode.appendChild(el.firstChild); - el.remove(); - }); - insCard.querySelectorAll('ins').forEach((el) => { - el.parentNode.appendChild(el.firstChild); - el.remove(); - }); + const ins = document.createElement('ins'); + const del = document.createElement('del'); - ins.appendChild(insCard); - del.appendChild(delCard); + delCard.querySelectorAll('ins').forEach((el) => { + el.remove(); + }); + insCard.querySelectorAll('del').forEach((el) => { + el.remove(); + }); + delCard.querySelectorAll('del').forEach((el) => { + el.parentNode.appendChild(el.firstChild); + el.remove(); + }); + insCard.querySelectorAll('ins').forEach((el) => { + el.parentNode.appendChild(el.firstChild); + el.remove(); + }); - card.parentNode.appendChild(del); - card.parentNode.appendChild(ins); - card.remove(); - } - } - } + ins.appendChild(insCard); + del.appendChild(delCard); - updateDiff() { - if (this.comparisonEditor && this.selectedEditor) { - let comparisonState = this.comparisonEditor.editorInstance.parseEditorState(this.comparisonRevision.lexical); - let selectedState = this.selectedEditor.editorInstance.parseEditorState(this.selectedRevision.lexical); + card.parentNode.appendChild(del); + card.parentNode.appendChild(ins); + card.remove(); + } + } + } - this.comparisonEditor.editorInstance.setEditorState(comparisonState); - this.selectedEditor.editorInstance.setEditorState(selectedState); - } + updateDiff() { + if (this.comparisonEditor && this.selectedEditor) { + let comparisonState = this.comparisonEditor.editorInstance.parseEditorState(this.comparisonRevision.lexical); + let selectedState = this.selectedEditor.editorInstance.parseEditorState(this.selectedRevision.lexical); - let previous = document.querySelector('.gh-post-history-hidden-lexical.previous'); - let current = document.querySelector('.gh-post-history-hidden-lexical.current'); + this.comparisonEditor.editorInstance.setEditorState(comparisonState); + this.selectedEditor.editorInstance.setEditorState(selectedState); + } - let previousDone = false; - let currentDone = false; + let previous = document.querySelector('.gh-post-history-hidden-lexical.previous'); + let current = document.querySelector('.gh-post-history-hidden-lexical.current'); - let updateIfBothDone = () => { - if (previousDone && currentDone) { - this.diffHtml = this.calculateHTMLDiff(this.stripInitialPlaceholder(previous.innerHTML), this.stripInitialPlaceholder(current.innerHTML)); - this.selectedHTML = this.stripInitialPlaceholder(current.innerHTML); - } - }; + let previousDone = false; + let currentDone = false; - checkFinishedRendering(previous, () => { - previous.querySelectorAll('[contenteditable]').forEach((el) => { - el.setAttribute('contenteditable', false); - }); - previousDone = true; - updateIfBothDone(); - }); + let updateIfBothDone = () => { + if (previousDone && currentDone) { + this.diffHtml = this.calculateHTMLDiff(this.stripInitialPlaceholder(previous.innerHTML), this.stripInitialPlaceholder(current.innerHTML)); + this.selectedHTML = this.stripInitialPlaceholder(current.innerHTML); + } + }; - checkFinishedRendering(current, () => { - current.querySelectorAll('[contenteditable]').forEach((el) => { - el.setAttribute('contenteditable', false); - }); - currentDone = true; - updateIfBothDone(); - }); - } + checkFinishedRendering(previous, () => { + previous.querySelectorAll('[contenteditable]').forEach((el) => { + el.setAttribute('contenteditable', false); + }); + previousDone = true; + updateIfBothDone(); + }); + + checkFinishedRendering(current, () => { + current.querySelectorAll('[contenteditable]').forEach((el) => { + el.setAttribute('contenteditable', false); + }); + currentDone = true; + updateIfBothDone(); + }); + } }