From 26f658decd3943bbc66c567ea91e7b859b32e0e6 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Thu, 8 Sep 2016 11:44:04 -0500 Subject: Implement editor to manually resolve merge conflicts --- .../merge_conflict_data_provider.js.es6 | 165 ++++++++++++++++----- .../javascripts/merge_conflict_resolver.js.es6 | 60 ++++++-- .../components/diff_file_editor.js.es6 | 63 ++++++++ 3 files changed, 234 insertions(+), 54 deletions(-) create mode 100644 app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflict_data_provider.js.es6 b/app/assets/javascripts/merge_conflict_data_provider.js.es6 index 13ee794ba38..bfed78ff99c 100644 --- a/app/assets/javascripts/merge_conflict_data_provider.js.es6 +++ b/app/assets/javascripts/merge_conflict_data_provider.js.es6 @@ -2,7 +2,9 @@ const HEAD_HEADER_TEXT = 'HEAD//our changes'; const ORIGIN_HEADER_TEXT = 'origin//their changes'; const HEAD_BUTTON_TITLE = 'Use ours'; const ORIGIN_BUTTON_TITLE = 'Use theirs'; - +const INTERACTIVE_RESOLVE_MODE = 'interactive'; +const EDIT_RESOLVE_MODE = 'edit'; +const DEFAULT_RESOLVE_MODE = INTERACTIVE_RESOLVE_MODE; class MergeConflictDataProvider { @@ -18,8 +20,7 @@ class MergeConflictDataProvider { diffViewType : diffViewType, fixedLayout : fixedLayout, isSubmitting : false, - conflictsData : {}, - resolutionData : {} + conflictsData : {} } } @@ -35,9 +36,9 @@ class MergeConflictDataProvider { data.shortCommitSha = data.commit_sha.slice(0, 7); data.commitMessage = data.commit_message; + this.decorateFiles(data); this.setParallelLines(data); this.setInlineLines(data); - this.updateResolutionsData(data); } vueInstance.conflictsData = data; @@ -47,16 +48,12 @@ class MergeConflictDataProvider { vueInstance.conflictsData.conflictsText = conflictsText; } - - updateResolutionsData(data) { - const vi = this.vueInstance; - - data.files.forEach( (file) => { - file.sections.forEach( (section) => { - if (section.conflict) { - vi.$set(`resolutionData['${section.id}']`, false); - } - }); + decorateFiles(data) { + data.files.forEach((file) => { + file.content = ''; + file.resolutionData = {}; + file.promptDiscardConfirmation = false; + file.resolveMode = DEFAULT_RESOLVE_MODE; }); } @@ -165,11 +162,14 @@ class MergeConflictDataProvider { } - handleSelected(sectionId, selection) { + handleSelected(file, sectionId, selection) { const vi = this.vueInstance; + let files = vi.conflictsData.files; + + vi.$set(`conflictsData.files[${files.indexOf(file)}].resolutionData['${sectionId}']`, selection); + - vi.resolutionData[sectionId] = selection; - vi.conflictsData.files.forEach( (file) => { + files.forEach( (file) => { file.inlineLines.forEach( (line) => { if (line.id === sectionId && (line.hasConflict || line.isHeader)) { this.markLine(line, selection); @@ -208,6 +208,48 @@ class MergeConflictDataProvider { .toggleClass('container-limited', !vi.isParallel && vi.fixedLayout); } + setFileResolveMode(file, mode) { + const vi = this.vueInstance; + + // Restore Interactive mode when switching to Edit mode + if (mode === EDIT_RESOLVE_MODE) { + file.resolutionData = {}; + + this.restoreFileLinesState(file); + } + + file.resolveMode = mode; + } + + + restoreFileLinesState(file) { + file.inlineLines.forEach((line) => { + if (line.hasConflict || line.isHeader) { + line.isSelected = false; + line.isUnselected = false; + } + }); + + file.parallelLines.forEach((lines) => { + const left = lines[0]; + const right = lines[1]; + const isLeftMatch = left.hasConflict || left.isHeader; + const isRightMatch = right.hasConflict || right.isHeader; + + if (isLeftMatch || isRightMatch) { + left.isSelected = false; + left.isUnselected = false; + right.isSelected = false; + right.isUnselected = false; + } + }); + } + + + setPromptConfirmationState(file, state) { + file.promptDiscardConfirmation = state; + } + markLine(line, selection) { if (selection === 'head' && line.isHead) { @@ -226,31 +268,54 @@ class MergeConflictDataProvider { getConflictsCount() { - return Object.keys(this.vueInstance.resolutionData).length; - } - - - getResolvedCount() { - let count = 0; - const data = this.vueInstance.resolutionData; + const files = this.vueInstance.conflictsData.files; + let count = 0; - for (const id in data) { - const resolution = data[id]; - if (resolution) { - count++; - } - } + files.forEach((file) => { + file.sections.forEach((section) => { + if (section.conflict) { + count++; + } + }); + }); return count; } isReadyToCommit() { - const { conflictsData, isSubmitting } = this.vueInstance - const allResolved = this.getConflictsCount() === this.getResolvedCount(); - const hasCommitMessage = $.trim(conflictsData.commitMessage).length; + const vi = this.vueInstance; + const files = this.vueInstance.conflictsData.files; + const hasCommitMessage = $.trim(this.vueInstance.conflictsData.commitMessage).length; + let unresolved = 0; + + for (let i = 0, l = files.length; i < l; i++) { + let file = files[i]; + + if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) { + let numberConflicts = 0; + let resolvedConflicts = Object.keys(file.resolutionData).length + + for (let j = 0, k = file.sections.length; j < k; j++) { + if (file.sections[j].conflict) { + numberConflicts++; + } + } + + if (resolvedConflicts !== numberConflicts) { + unresolved++; + } + } else if (file.resolveMode === EDIT_RESOLVE_MODE) { + // Unlikely to happen since switching to Edit mode saves content automatically. + // Checking anyway in case the save strategy changes in the future + if (!file.content) { + unresolved++; + continue; + } + } + } - return !isSubmitting && hasCommitMessage && allResolved; + return !vi.isSubmitting && hasCommitMessage && !unresolved; } @@ -332,10 +397,33 @@ class MergeConflictDataProvider { getCommitData() { - return { - commit_message: this.vueInstance.conflictsData.commitMessage, - sections: this.vueInstance.resolutionData - } + let conflictsData = this.vueInstance.conflictsData; + let commitData = {}; + + commitData = { + commitMessage: conflictsData.commitMessage, + files: [] + }; + + conflictsData.files.forEach((file) => { + let addFile; + + addFile = { + old_path: file.old_path, + new_path: file.new_path + }; + + // Submit only one data for type of editing + if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) { + addFile.sections = file.resolutionData; + } else if (file.resolveMode === EDIT_RESOLVE_MODE) { + addFile.content = file.content; + } + + commitData.files.push(addFile); + }); + + return commitData; } @@ -343,5 +431,4 @@ class MergeConflictDataProvider { const { old_path, new_path } = file; return old_path === new_path ? new_path : `${old_path} → ${new_path}`; } - } diff --git a/app/assets/javascripts/merge_conflict_resolver.js.es6 b/app/assets/javascripts/merge_conflict_resolver.js.es6 index 7e756433bf5..c317a6521b5 100644 --- a/app/assets/javascripts/merge_conflict_resolver.js.es6 +++ b/app/assets/javascripts/merge_conflict_resolver.js.es6 @@ -1,13 +1,16 @@ //= require vue +//= require ./merge_conflicts/components/diff_file_editor + +const INTERACTIVE_RESOLVE_MODE = 'interactive'; +const EDIT_RESOLVE_MODE = 'edit'; class MergeConflictResolver { constructor() { - this.dataProvider = new MergeConflictDataProvider() - this.initVue() + this.dataProvider = new MergeConflictDataProvider(); + this.initVue(); } - initVue() { const that = this; this.vue = new Vue({ @@ -17,15 +20,28 @@ class MergeConflictResolver { created : this.fetchData(), computed : this.setComputedProperties(), methods : { - handleSelected(sectionId, selection) { - that.dataProvider.handleSelected(sectionId, selection); + handleSelected(file, sectionId, selection) { + that.dataProvider.handleSelected(file, sectionId, selection); }, handleViewTypeChange(newType) { that.dataProvider.updateViewType(newType); }, commit() { that.commit(); - } + }, + onClickResolveModeButton(file, mode) { + that.toggleResolveMode(file, mode); + }, + acceptDiscardConfirmation(file) { + that.dataProvider.setPromptConfirmationState(file, false); + that.dataProvider.setFileResolveMode(file, INTERACTIVE_RESOLVE_MODE); + }, + cancelDiscardConfirmation(file) { + that.dataProvider.setPromptConfirmationState(file, false); + }, + }, + components: { + 'diff-file-editor': window.gl.diffFileEditor } }) } @@ -36,7 +52,6 @@ class MergeConflictResolver { return { conflictsCount() { return dp.getConflictsCount() }, - resolvedCount() { return dp.getResolvedCount() }, readyToCommit() { return dp.isReadyToCommit() }, commitButtonText() { return dp.getCommitButtonText() } } @@ -69,14 +84,29 @@ class MergeConflictResolver { commit() { this.vue.isSubmitting = true; - $.post($('#conflicts').data('resolveConflictsPath'), this.dataProvider.getCommitData()) - .done((data) => { - window.location.href = data.redirect_to; - }) - .error(() => { - this.vue.isSubmitting = false; - new Flash('Something went wrong!'); - }); + $.ajax({ + url: $('#conflicts').data('resolveConflictsPath'), + data: JSON.stringify(this.dataProvider.getCommitData()), + contentType: "application/json", + dataType: 'json', + method: 'POST' + }) + .done((data) => { + window.location.href = data.redirect_to; + }) + .error(() => { + this.vue.isSubmitting = false; + new Flash('Something went wrong!'); + }); } + + toggleResolveMode(file, mode) { + if (mode === INTERACTIVE_RESOLVE_MODE && file.resolveEditChanged) { + this.dataProvider.setPromptConfirmationState(file, true); + return; + } + + this.dataProvider.setFileResolveMode(file, mode); + } } diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 new file mode 100644 index 00000000000..570d9ff877c --- /dev/null +++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 @@ -0,0 +1,63 @@ +((global) => { + global.diffFileEditor = Vue.extend({ + props: ['file', 'loadFile'], + template: '#diff-file-editor', + data() { + return { + originalState: '', + saved: false, + loading: false, + fileLoaded: false + } + }, + computed: { + classObject() { + return { + 'load-file': this.loadFile, + 'saved': this.saved, + 'is-loading': this.loading + }; + } + }, + watch: { + loadFile(val) { + const self = this; + + if (!val || this.fileLoaded || this.loading) { + return + } + + this.loading = true; + + $.get(this.file.content_path) + .done((file) => { + $(self.$el).find('textarea').val(file.content); + + self.originalState = file.content; + self.fileLoaded = true; + self.saveDiffResolution(); + }) + .fail(() => { + console.log('error'); + }) + .always(() => { + self.loading = false; + }); + } + }, + methods: { + saveDiffResolution() { + this.saved = true; + + // This probably be better placed in the data provider + this.file.content = this.$el.querySelector('textarea').value; + this.file.resolveEditChanged = this.file.content !== this.originalState; + this.file.promptDiscardConfirmation = false; + }, + onInput() { + this.saveDiffResolution(); + } + } + }); + +})(window.gl || (window.gl = {})); -- cgit v1.2.1 From a3eb39a1068df93d732585272c5eaff380801430 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 16 Sep 2016 19:41:15 -0500 Subject: Replace textarea with Ace editor --- .../components/diff_file_editor.js.es6 | 27 ++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 index 570d9ff877c..abdf73febb4 100644 --- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 +++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 @@ -4,7 +4,7 @@ template: '#diff-file-editor', data() { return { - originalState: '', + originalContent: '', saved: false, loading: false, fileLoaded: false @@ -23,6 +23,8 @@ loadFile(val) { const self = this; + this.resetEditorContent(); + if (!val || this.fileLoaded || this.loading) { return } @@ -31,10 +33,19 @@ $.get(this.file.content_path) .done((file) => { - $(self.$el).find('textarea').val(file.content); - self.originalState = file.content; + let content = self.$el.querySelector('pre'); + let fileContent = document.createTextNode(file.content); + + content.textContent = fileContent.textContent; + + self.originalContent = file.content; self.fileLoaded = true; + self.editor = ace.edit(content); + self.editor.$blockScrolling = Infinity; // Turn off annoying warning + self.editor.on('change', () => { + self.saveDiffResolution(); + }); self.saveDiffResolution(); }) .fail(() => { @@ -50,12 +61,14 @@ this.saved = true; // This probably be better placed in the data provider - this.file.content = this.$el.querySelector('textarea').value; - this.file.resolveEditChanged = this.file.content !== this.originalState; + this.file.content = this.editor.getValue(); + this.file.resolveEditChanged = this.file.content !== this.originalContent; this.file.promptDiscardConfirmation = false; }, - onInput() { - this.saveDiffResolution(); + resetEditorContent() { + if (this.fileLoaded) { + this.editor.setValue(this.originalContent, -1); + } } } }); -- cgit v1.2.1 From 12ca16080b86b2b0cee8037800952347c2dcd8c4 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Tue, 20 Sep 2016 12:37:53 -0500 Subject: check if files is set before counting --- app/assets/javascripts/merge_conflict_data_provider.js.es6 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflict_data_provider.js.es6 b/app/assets/javascripts/merge_conflict_data_provider.js.es6 index bfed78ff99c..6d1d3f36b33 100644 --- a/app/assets/javascripts/merge_conflict_data_provider.js.es6 +++ b/app/assets/javascripts/merge_conflict_data_provider.js.es6 @@ -268,6 +268,10 @@ class MergeConflictDataProvider { getConflictsCount() { + if (!this.vueInstance.conflictsData.files) { + return 0; + } + const files = this.vueInstance.conflictsData.files; let count = 0; -- cgit v1.2.1 From a8ac9089afb664e569b34c61dc6782d20d1019d1 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 28 Sep 2016 05:12:13 -0500 Subject: Refactor JS code - Use a store base object to manage application state. - Add a service to handle ajax requests. - Load code only when needed --- app/assets/javascripts/dispatcher.js | 3 - .../components/diff_file_editor.js.es6 | 5 +- .../merge_conflicts/merge_conflict_service.js.es6 | 30 ++ .../merge_conflicts/merge_conflict_store.js.es6 | 419 +++++++++++++++++++++ .../merge_conflicts/merge_conflicts_bundle.js.es6 | 84 +++++ 5 files changed, 537 insertions(+), 4 deletions(-) create mode 100644 app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6 create mode 100644 app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 create mode 100644 app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index f3ef13ce20e..2c277766d2d 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -97,9 +97,6 @@ new ZenMode(); new MergedButtons(); break; - case "projects:merge_requests:conflicts": - window.mcui = new MergeConflictResolver() - break; case 'projects:merge_requests:index': shortcut_handler = new ShortcutsNavigation(); Issuable.init(); diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 index abdf73febb4..49d30f6e9e8 100644 --- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 +++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 @@ -1,5 +1,8 @@ ((global) => { - global.diffFileEditor = Vue.extend({ + + global.mergeConflicts = global.mergeConflicts || {}; + + global.mergeConflicts.diffFileEditor = Vue.extend({ props: ['file', 'loadFile'], template: '#diff-file-editor', data() { diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6 new file mode 100644 index 00000000000..da2fb8b1323 --- /dev/null +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_service.js.es6 @@ -0,0 +1,30 @@ +((global) => { + global.mergeConflicts = global.mergeConflicts || {}; + + class mergeConflictsService { + constructor(options) { + this.conflictsPath = options.conflictsPath; + this.resolveConflictsPath = options.resolveConflictsPath; + } + + fetchConflictsData() { + return $.ajax({ + dataType: 'json', + url: this.conflictsPath + }); + } + + submitResolveConflicts(data) { + return $.ajax({ + url: this.resolveConflictsPath, + data: JSON.stringify(data), + contentType: 'application/json', + dataType: 'json', + method: 'POST' + }); + } + }; + + global.mergeConflicts.mergeConflictsService = mergeConflictsService; + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 new file mode 100644 index 00000000000..d35e6d8aed6 --- /dev/null +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 @@ -0,0 +1,419 @@ +((global) => { + global.mergeConflicts = global.mergeConflicts || {}; + + const diffViewType = $.cookie('diff_view'); + const HEAD_HEADER_TEXT = 'HEAD//our changes'; + const ORIGIN_HEADER_TEXT = 'origin//their changes'; + const HEAD_BUTTON_TITLE = 'Use ours'; + const ORIGIN_BUTTON_TITLE = 'Use theirs'; + const INTERACTIVE_RESOLVE_MODE = 'interactive'; + const EDIT_RESOLVE_MODE = 'edit'; + const DEFAULT_RESOLVE_MODE = INTERACTIVE_RESOLVE_MODE; + const VIEW_TYPES = { + INLINE: 'inline', + PARALLEL: 'parallel' + }; + + global.mergeConflicts.mergeConflictsStore = { + state: { + isLoading: true, + hasError: false, + isSubmitting: false, + isParallel: diffViewType === VIEW_TYPES.PARALLEL, + diffViewType: diffViewType, + conflictsData: {} + }, + + setConflictsData(data) { + this.decorateFiles(data.files); + this.setInlineLines(data.files); + this.setParallelLines(data.files); + + this.state.conflictsData = { + files: data.files, + commitMessage: data.commit_message, + sourceBranch: data.source_branch, + targetBranch: data.target_branch, + commitMessage: data.commit_message, + shortCommitSha: data.commit_sha.slice(0, 7), + }; + }, + + decorateFiles(files) { + files.forEach((file) => { + file.content = ''; + file.resolutionData = {}; + file.promptDiscardConfirmation = false; + file.resolveMode = DEFAULT_RESOLVE_MODE; + }); + }, + + setInlineLines(files) { + files.forEach((file) => { + file.iconClass = `fa-${file.blob_icon}`; + file.blobPath = file.blob_path; + file.filePath = this.getFilePath(file); + file.inlineLines = []; + + file.sections.forEach((section) => { + let currentLineType = 'new'; + const { conflict, lines, id } = section; + + if (conflict) { + file.inlineLines.push(this.getHeadHeaderLine(id)); + } + + lines.forEach((line) => { + const { type } = line; + + if ((type === 'new' || type === 'old') && currentLineType !== type) { + currentLineType = type; + file.inlineLines.push({ lineType: 'emptyLine', richText: '' }); + } + + this.decorateLineForInlineView(line, id, conflict); + file.inlineLines.push(line); + }) + + if (conflict) { + file.inlineLines.push(this.getOriginHeaderLine(id)); + } + }); + }); + }, + + setParallelLines(files) { + files.forEach((file) => { + file.filePath = this.getFilePath(file); + file.iconClass = `fa-${file.blob_icon}`; + file.blobPath = file.blob_path; + file.parallelLines = []; + const linesObj = { left: [], right: [] }; + + file.sections.forEach((section) => { + const { conflict, lines, id } = section; + + if (conflict) { + linesObj.left.push(this.getOriginHeaderLine(id)); + linesObj.right.push(this.getHeadHeaderLine(id)); + } + + lines.forEach((line) => { + const { type } = line; + + if (conflict) { + if (type === 'old') { + linesObj.left.push(this.getLineForParallelView(line, id, 'conflict')); + } + else if (type === 'new') { + linesObj.right.push(this.getLineForParallelView(line, id, 'conflict', true)); + } + } + else { + const lineType = type || 'context'; + + linesObj.left.push (this.getLineForParallelView(line, id, lineType)); + linesObj.right.push(this.getLineForParallelView(line, id, lineType, true)); + } + }); + + this.checkLineLengths(linesObj); + }); + + for (let i = 0, len = linesObj.left.length; i < len; i++) { + file.parallelLines.push([ + linesObj.right[i], + linesObj.left[i] + ]); + } + + return file; + }); + }, + + setLoadingState(state) { + this.state.isLoading = state; + }, + + setErrorState(state) { + this.state.hasError = state; + }, + + setFailedRequest(message) { + console.log('setFailedRequest'); + this.state.hasError = true; + this.state.conflictsData.errorMessage = message; + }, + + getConflictsCount() { + if (!this.state.conflictsData.files) { + return 0; + } + + const files = this.state.conflictsData.files; + let count = 0; + + files.forEach((file) => { + file.sections.forEach((section) => { + if (section.conflict) { + count++; + } + }); + }); + + return count; + }, + + getConflictsCountText() { + const count = this.getConflictsCount(); + const text = count ? 'conflicts' : 'conflict'; + + return `${count} ${text}`; + }, + + setViewType(viewType) { + this.state.diffView = viewType; + this.state.isParallel = viewType === VIEW_TYPES.PARALLEL; + + $.cookie('diff_view', viewType, { + path: gon.relative_url_root || '/' + }); + }, + + getHeadHeaderLine(id) { + return { + id: id, + richText: HEAD_HEADER_TEXT, + buttonTitle: HEAD_BUTTON_TITLE, + type: 'new', + section: 'head', + isHeader: true, + isHead: true, + isSelected: false, + isUnselected: false + } + }, + + decorateLineForInlineView(line, id, conflict) { + const { type } = line; + line.id = id; + line.hasConflict = conflict; + line.isHead = type === 'new'; + line.isOrigin = type === 'old'; + line.hasMatch = type === 'match'; + line.richText = line.rich_text; + line.isSelected = false; + line.isUnselected = false; + }, + + getLineForParallelView(line, id, lineType, isHead) { + const { old_line, new_line, rich_text } = line; + const hasConflict = lineType === 'conflict'; + + return { + id, + lineType, + hasConflict, + isHead: hasConflict && isHead, + isOrigin: hasConflict && !isHead, + hasMatch: lineType === 'match', + lineNumber: isHead ? new_line : old_line, + section: isHead ? 'head' : 'origin', + richText: rich_text, + isSelected: false, + isUnselected: false + } + }, + + getOriginHeaderLine(id) { + return { + id: id, + richText: ORIGIN_HEADER_TEXT, + buttonTitle: ORIGIN_BUTTON_TITLE, + type: 'old', + section: 'origin', + isHeader: true, + isOrigin: true, + isSelected: false, + isUnselected: false + } + }, + + getFilePath(file) { + const { old_path, new_path } = file; + return old_path === new_path ? new_path : `${old_path} → ${new_path}`; + }, + + checkLineLengths(linesObj) { + let { left, right } = linesObj; + + if (left.length !== right.length) { + if (left.length > right.length) { + const diff = left.length - right.length; + for (let i = 0; i < diff; i++) { + right.push({ lineType: 'emptyLine', richText: '' }); + } + } + else { + const diff = right.length - left.length; + for (let i = 0; i < diff; i++) { + left.push({ lineType: 'emptyLine', richText: '' }); + } + } + } + }, + + setPromptConfirmationState(file, state) { + file.promptDiscardConfirmation = state; + }, + + setFileResolveMode(file, mode) { + // Restore Interactive mode when switching to Edit mode + if (mode === EDIT_RESOLVE_MODE) { + file.resolutionData = {}; + + this.restoreFileLinesState(file); + } + + file.resolveMode = mode; + }, + + restoreFileLinesState(file) { + file.inlineLines.forEach((line) => { + if (line.hasConflict || line.isHeader) { + line.isSelected = false; + line.isUnselected = false; + } + }); + + file.parallelLines.forEach((lines) => { + const left = lines[0]; + const right = lines[1]; + const isLeftMatch = left.hasConflict || left.isHeader; + const isRightMatch = right.hasConflict || right.isHeader; + + if (isLeftMatch || isRightMatch) { + left.isSelected = false; + left.isUnselected = false; + right.isSelected = false; + right.isUnselected = false; + } + }); + }, + + isReadyToCommit() { + const files = this.state.conflictsData.files; + const hasCommitMessage = $.trim(this.state.conflictsData.commitMessage).length; + let unresolved = 0; + + for (let i = 0, l = files.length; i < l; i++) { + let file = files[i]; + + if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) { + let numberConflicts = 0; + let resolvedConflicts = Object.keys(file.resolutionData).length + + for (let j = 0, k = file.sections.length; j < k; j++) { + if (file.sections[j].conflict) { + numberConflicts++; + } + } + + if (resolvedConflicts !== numberConflicts) { + unresolved++; + } + } else if (file.resolveMode === EDIT_RESOLVE_MODE) { + // Unlikely to happen since switching to Edit mode saves content automatically. + // Checking anyway in case the save strategy changes in the future + if (!file.content) { + unresolved++; + continue; + } + } + } + + return !this.state.isSubmitting && hasCommitMessage && !unresolved; + }, + + getCommitButtonText() { + const initial = 'Commit conflict resolution'; + const inProgress = 'Committing...'; + + return this.state ? this.state.isSubmitting ? inProgress : initial : initial; + }, + + getCommitData() { + let commitData = {}; + + commitData = { + commit_message: this.state.conflictsData.commitMessage, + files: [] + }; + + this.state.conflictsData.files.forEach((file) => { + let addFile; + + addFile = { + old_path: file.old_path, + new_path: file.new_path + }; + + // Submit only one data for type of editing + if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) { + addFile.sections = file.resolutionData; + } else if (file.resolveMode === EDIT_RESOLVE_MODE) { + addFile.content = file.content; + } + + commitData.files.push(addFile); + }); + + return commitData; + }, + + handleSelected(file, sectionId, selection) { + Vue.set(file.resolutionData, sectionId, selection); + + this.state.conflictsData.files.forEach((file) => { + file.inlineLines.forEach((line) => { + if (line.id === sectionId && (line.hasConflict || line.isHeader)) { + this.markLine(line, selection); + } + }); + + file.parallelLines.forEach((lines) => { + const left = lines[0]; + const right = lines[1]; + const hasSameId = right.id === sectionId || left.id === sectionId; + const isLeftMatch = left.hasConflict || left.isHeader; + const isRightMatch = right.hasConflict || right.isHeader; + + if (hasSameId && (isLeftMatch || isRightMatch)) { + this.markLine(left, selection); + this.markLine(right, selection); + } + }) + }); + }, + + markLine(line, selection) { + if (selection === 'head' && line.isHead) { + line.isSelected = true; + line.isUnselected = false; + } + else if (selection === 'origin' && line.isOrigin) { + line.isSelected = true; + line.isUnselected = false; + } + else { + line.isSelected = false; + line.isUnselected = true; + } + }, + + setSubmitState(state) { + this.state.isSubmitting = state; + } + }; + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 new file mode 100644 index 00000000000..b5123e22f7a --- /dev/null +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 @@ -0,0 +1,84 @@ +//= require vue +//= require ./merge_conflict_store +//= require ./merge_conflict_service +//= require ./components/diff_file_editor + +$(() => { + const INTERACTIVE_RESOLVE_MODE = 'interactive'; + const $conflicts = $(document.getElementById('conflicts')); + const mergeConflictsStore = gl.mergeConflicts.mergeConflictsStore; + const mergeConflictsService = new gl.mergeConflicts.mergeConflictsService({ + conflictsPath: $conflicts.data('conflictsPath'), + resolveConflictsPath: $conflicts.data('resolveConflictsPath') + }); + + gl.MergeConflictsResolverApp = new Vue({ + el: '#conflicts', + data: mergeConflictsStore.state, + components: { + 'diff-file-editor': gl.mergeConflicts.diffFileEditor + }, + computed: { + conflictsCountText() { return mergeConflictsStore.getConflictsCountText() }, + readyToCommit() { return mergeConflictsStore.isReadyToCommit() }, + commitButtonText() { return mergeConflictsStore.getCommitButtonText() } + }, + created() { + mergeConflictsService + .fetchConflictsData() + .done((data) => { + if (data.type === 'error') { + mergeConflictsStore.setFailedRequest(data.message); + } else { + mergeConflictsStore.setConflictsData(data); + } + }) + .error(() => { + mergeConflictsStore.setFailedRequest(); + }) + .always(() => { + mergeConflictsStore.setLoadingState(false); + + this.$nextTick(() => { + $conflicts.find('.js-syntax-highlight').syntaxHighlight(); + }); + }); + }, + methods: { + handleSelected(file, sectionId, selection) { + mergeConflictsStore.handleSelected(file, sectionId, selection); + }, + handleViewTypeChange(viewType) { + mergeConflictsStore.setViewType(viewType); + }, + onClickResolveModeButton(file, mode) { + if (mode === INTERACTIVE_RESOLVE_MODE && file.resolveEditChanged) { + mergeConflictsStore.setPromptConfirmationState(file, true); + return; + } + + mergeConflictsStore.setFileResolveMode(file, mode); + }, + acceptDiscardConfirmation(file) { + mergeConflictsStore.setPromptConfirmationState(file, false); + mergeConflictsStore.setFileResolveMode(file, INTERACTIVE_RESOLVE_MODE); + }, + cancelDiscardConfirmation(file) { + mergeConflictsStore.setPromptConfirmationState(file, false); + }, + commit() { + mergeConflictsStore.setSubmitState(true); + + mergeConflictsService + .submitResolveConflicts(mergeConflictsStore.getCommitData()) + .done((data) => { + window.location.href = data.redirect_to; + }) + .error(() => { + mergeConflictsStore.setSubmitState(false); + new Flash('Failed to save merge conflicts resolutions. Please try again!'); + }); + } + } + }) +}); -- cgit v1.2.1 From 197ac5ebbfd8ca7fbcb79776fd25ca0e213376c4 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Thu, 29 Sep 2016 20:26:01 -0500 Subject: Ability to resolve conflicts for files with `text-editor` as conflict type --- .../components/diff_file_editor.js.es6 | 43 ++-- .../merge_conflicts/merge_conflict_store.js.es6 | 240 +++++++++++---------- 2 files changed, 150 insertions(+), 133 deletions(-) (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 index 49d30f6e9e8..46aad9fe33c 100644 --- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 +++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 @@ -7,10 +7,10 @@ template: '#diff-file-editor', data() { return { - originalContent: '', saved: false, loading: false, - fileLoaded: false + fileLoaded: false, + originalContent: '', } }, computed: { @@ -23,43 +23,48 @@ } }, watch: { - loadFile(val) { - const self = this; - + ['file.showEditor'](val) { this.resetEditorContent(); if (!val || this.fileLoaded || this.loading) { - return + return; } + this.loadEditor(); + } + }, + ready() { + if (this.file.loadEditor) { + this.loadEditor(); + } + }, + methods: { + loadEditor() { this.loading = true; $.get(this.file.content_path) .done((file) => { - - let content = self.$el.querySelector('pre'); + let content = this.$el.querySelector('pre'); let fileContent = document.createTextNode(file.content); content.textContent = fileContent.textContent; - self.originalContent = file.content; - self.fileLoaded = true; - self.editor = ace.edit(content); - self.editor.$blockScrolling = Infinity; // Turn off annoying warning - self.editor.on('change', () => { - self.saveDiffResolution(); + this.originalContent = file.content; + this.fileLoaded = true; + this.editor = ace.edit(content); + this.editor.$blockScrolling = Infinity; // Turn off annoying warning + this.editor.on('change', () => { + this.saveDiffResolution(); }); - self.saveDiffResolution(); + this.saveDiffResolution(); }) .fail(() => { console.log('error'); }) .always(() => { - self.loading = false; + this.loading = false; }); - } - }, - methods: { + }, saveDiffResolution() { this.saved = true; diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 index d35e6d8aed6..e1f862797f5 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 @@ -13,6 +13,10 @@ INLINE: 'inline', PARALLEL: 'parallel' }; + const CONFLICT_TYPES = { + TEXT: 'text', + TEXT_EDITOR: 'text-editor' + }; global.mergeConflicts.mergeConflictsStore = { state: { @@ -26,8 +30,6 @@ setConflictsData(data) { this.decorateFiles(data.files); - this.setInlineLines(data.files); - this.setParallelLines(data.files); this.state.conflictsData = { files: data.files, @@ -45,90 +47,90 @@ file.resolutionData = {}; file.promptDiscardConfirmation = false; file.resolveMode = DEFAULT_RESOLVE_MODE; - }); - }, - - setInlineLines(files) { - files.forEach((file) => { + file.filePath = this.getFilePath(file); file.iconClass = `fa-${file.blob_icon}`; file.blobPath = file.blob_path; - file.filePath = this.getFilePath(file); - file.inlineLines = []; - file.sections.forEach((section) => { - let currentLineType = 'new'; - const { conflict, lines, id } = section; + if (file.type === CONFLICT_TYPES.TEXT) { + file.showEditor = false; + file.loadEditor = false; - if (conflict) { - file.inlineLines.push(this.getHeadHeaderLine(id)); - } + this.setInlineLine(file); + this.setParallelLine(file); + } else if (file.type === CONFLICT_TYPES.TEXT_EDITOR) { + file.showEditor = true; + file.loadEditor = true; + } + }); + }, - lines.forEach((line) => { - const { type } = line; + setInlineLine(file) { + file.inlineLines = []; - if ((type === 'new' || type === 'old') && currentLineType !== type) { - currentLineType = type; - file.inlineLines.push({ lineType: 'emptyLine', richText: '' }); - } + file.sections.forEach((section) => { + let currentLineType = 'new'; + const { conflict, lines, id } = section; + + if (conflict) { + file.inlineLines.push(this.getHeadHeaderLine(id)); + } - this.decorateLineForInlineView(line, id, conflict); - file.inlineLines.push(line); - }) + lines.forEach((line) => { + const { type } = line; - if (conflict) { - file.inlineLines.push(this.getOriginHeaderLine(id)); + if ((type === 'new' || type === 'old') && currentLineType !== type) { + currentLineType = type; + file.inlineLines.push({ lineType: 'emptyLine', richText: '' }); } - }); + + this.decorateLineForInlineView(line, id, conflict); + file.inlineLines.push(line); + }) + + if (conflict) { + file.inlineLines.push(this.getOriginHeaderLine(id)); + } }); }, - setParallelLines(files) { - files.forEach((file) => { - file.filePath = this.getFilePath(file); - file.iconClass = `fa-${file.blob_icon}`; - file.blobPath = file.blob_path; - file.parallelLines = []; - const linesObj = { left: [], right: [] }; - - file.sections.forEach((section) => { - const { conflict, lines, id } = section; + setParallelLine(file) { + file.parallelLines = []; + const linesObj = { left: [], right: [] }; - if (conflict) { - linesObj.left.push(this.getOriginHeaderLine(id)); - linesObj.right.push(this.getHeadHeaderLine(id)); - } + file.sections.forEach((section) => { + const { conflict, lines, id } = section; - lines.forEach((line) => { - const { type } = line; + if (conflict) { + linesObj.left.push(this.getOriginHeaderLine(id)); + linesObj.right.push(this.getHeadHeaderLine(id)); + } - if (conflict) { - if (type === 'old') { - linesObj.left.push(this.getLineForParallelView(line, id, 'conflict')); - } - else if (type === 'new') { - linesObj.right.push(this.getLineForParallelView(line, id, 'conflict', true)); - } - } - else { - const lineType = type || 'context'; + lines.forEach((line) => { + const { type } = line; - linesObj.left.push (this.getLineForParallelView(line, id, lineType)); - linesObj.right.push(this.getLineForParallelView(line, id, lineType, true)); + if (conflict) { + if (type === 'old') { + linesObj.left.push(this.getLineForParallelView(line, id, 'conflict')); + } else if (type === 'new') { + linesObj.right.push(this.getLineForParallelView(line, id, 'conflict', true)); } - }); + } else { + const lineType = type || 'context'; - this.checkLineLengths(linesObj); + linesObj.left.push (this.getLineForParallelView(line, id, lineType)); + linesObj.right.push(this.getLineForParallelView(line, id, lineType, true)); + } }); - for (let i = 0, len = linesObj.left.length; i < len; i++) { - file.parallelLines.push([ - linesObj.right[i], - linesObj.left[i] - ]); - } - - return file; + this.checkLineLengths(linesObj); }); + + for (let i = 0, len = linesObj.left.length; i < len; i++) { + file.parallelLines.push([ + linesObj.right[i], + linesObj.left[i] + ]); + } }, setLoadingState(state) { @@ -140,13 +142,12 @@ }, setFailedRequest(message) { - console.log('setFailedRequest'); this.state.hasError = true; this.state.conflictsData.errorMessage = message; }, getConflictsCount() { - if (!this.state.conflictsData.files) { + if (!this.state.conflictsData.files.length) { return 0; } @@ -154,11 +155,15 @@ let count = 0; files.forEach((file) => { - file.sections.forEach((section) => { - if (section.conflict) { - count++; - } - }); + if (file.type === CONFLICT_TYPES.TEXT) { + file.sections.forEach((section) => { + if (section.conflict) { + count++; + } + }); + } else { + count++; + } }); return count; @@ -172,7 +177,7 @@ }, setViewType(viewType) { - this.state.diffView = viewType; + this.state.diffView = viewType; this.state.isParallel = viewType === VIEW_TYPES.PARALLEL; $.cookie('diff_view', viewType, { @@ -253,8 +258,7 @@ for (let i = 0; i < diff; i++) { right.push({ lineType: 'emptyLine', richText: '' }); } - } - else { + } else { const diff = right.length - left.length; for (let i = 0; i < diff; i++) { left.push({ lineType: 'emptyLine', richText: '' }); @@ -268,8 +272,12 @@ }, setFileResolveMode(file, mode) { - // Restore Interactive mode when switching to Edit mode - if (mode === EDIT_RESOLVE_MODE) { + if (mode === INTERACTIVE_RESOLVE_MODE) { + file.showEditor = false; + } else if (mode === EDIT_RESOLVE_MODE) { + // Restore Interactive mode when switching to Edit mode + file.showEditor = true; + file.loadEditor = true; file.resolutionData = {}; this.restoreFileLinesState(file); @@ -287,9 +295,9 @@ }); file.parallelLines.forEach((lines) => { - const left = lines[0]; - const right = lines[1]; - const isLeftMatch = left.hasConflict || left.isHeader; + const left = lines[0]; + const right = lines[1]; + const isLeftMatch = left.hasConflict || left.isHeader; const isRightMatch = right.hasConflict || right.isHeader; if (isLeftMatch || isRightMatch) { @@ -313,14 +321,17 @@ let numberConflicts = 0; let resolvedConflicts = Object.keys(file.resolutionData).length - for (let j = 0, k = file.sections.length; j < k; j++) { - if (file.sections[j].conflict) { - numberConflicts++; + // We only check if + if (file.type === CONFLICT_TYPES.TEXT) { + for (let j = 0, k = file.sections.length; j < k; j++) { + if (file.sections[j].conflict) { + numberConflicts++; + } } - } - if (resolvedConflicts !== numberConflicts) { - unresolved++; + if (resolvedConflicts !== numberConflicts) { + unresolved++; + } } } else if (file.resolveMode === EDIT_RESOLVE_MODE) { // Unlikely to happen since switching to Edit mode saves content automatically. @@ -358,10 +369,15 @@ new_path: file.new_path }; - // Submit only one data for type of editing - if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) { - addFile.sections = file.resolutionData; - } else if (file.resolveMode === EDIT_RESOLVE_MODE) { + if (file.type === CONFLICT_TYPES.TEXT) { + + // Submit only one data for type of editing + if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) { + addFile.sections = file.resolutionData; + } else if (file.resolveMode === EDIT_RESOLVE_MODE) { + addFile.content = file.content; + } + } else if (file.type === CONFLICT_TYPES.TEXT_EDITOR) { addFile.content = file.content; } @@ -374,39 +390,35 @@ handleSelected(file, sectionId, selection) { Vue.set(file.resolutionData, sectionId, selection); - this.state.conflictsData.files.forEach((file) => { - file.inlineLines.forEach((line) => { - if (line.id === sectionId && (line.hasConflict || line.isHeader)) { - this.markLine(line, selection); - } - }); + file.inlineLines.forEach((line) => { + if (line.id === sectionId && (line.hasConflict || line.isHeader)) { + this.markLine(line, selection); + } + }); - file.parallelLines.forEach((lines) => { - const left = lines[0]; - const right = lines[1]; - const hasSameId = right.id === sectionId || left.id === sectionId; - const isLeftMatch = left.hasConflict || left.isHeader; - const isRightMatch = right.hasConflict || right.isHeader; + file.parallelLines.forEach((lines) => { + const left = lines[0]; + const right = lines[1]; + const hasSameId = right.id === sectionId || left.id === sectionId; + const isLeftMatch = left.hasConflict || left.isHeader; + const isRightMatch = right.hasConflict || right.isHeader; - if (hasSameId && (isLeftMatch || isRightMatch)) { - this.markLine(left, selection); - this.markLine(right, selection); - } - }) + if (hasSameId && (isLeftMatch || isRightMatch)) { + this.markLine(left, selection); + this.markLine(right, selection); + } }); }, markLine(line, selection) { if (selection === 'head' && line.isHead) { - line.isSelected = true; + line.isSelected = true; line.isUnselected = false; - } - else if (selection === 'origin' && line.isOrigin) { - line.isSelected = true; + } else if (selection === 'origin' && line.isOrigin) { + line.isSelected = true; line.isUnselected = false; - } - else { - line.isSelected = false; + } else { + line.isSelected = false; line.isUnselected = true; } }, -- cgit v1.2.1 From cebad0fb60c90ff64cf16b022d262381aff5bf4f Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 30 Sep 2016 16:23:38 -0500 Subject: Do not show Diff view switcher if all files are can be only resolved with an editor --- .../javascripts/merge_conflicts/merge_conflict_store.js.es6 | 12 ++++++++++++ .../merge_conflicts/merge_conflicts_bundle.js.es6 | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 index e1f862797f5..e31cac49d44 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 @@ -425,6 +425,18 @@ setSubmitState(state) { this.state.isSubmitting = state; + }, + + fileTextTypePresent() { + let found = false; + + this.state.conflictsData.files.find((f) => { + if (f.type === CONFLICT_TYPES.TEXT) { + return found = true; + } + }); + + return found; } }; diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 index b5123e22f7a..449b4d2209a 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 @@ -21,7 +21,8 @@ $(() => { computed: { conflictsCountText() { return mergeConflictsStore.getConflictsCountText() }, readyToCommit() { return mergeConflictsStore.isReadyToCommit() }, - commitButtonText() { return mergeConflictsStore.getCommitButtonText() } + commitButtonText() { return mergeConflictsStore.getCommitButtonText() }, + showDiffViewTypeSwitcher() { return mergeConflictsStore.fileTextTypePresent() } }, created() { mergeConflictsService -- cgit v1.2.1 From 4f76ff0ecd0d3b278f9722042b66d1767078ddd0 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Tue, 4 Oct 2016 03:24:44 -0500 Subject: Use .some instead of .find for phantomjs compatibility --- .../javascripts/merge_conflicts/merge_conflict_store.js.es6 | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 index e31cac49d44..bdd6753f66a 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 @@ -428,15 +428,7 @@ }, fileTextTypePresent() { - let found = false; - - this.state.conflictsData.files.find((f) => { - if (f.type === CONFLICT_TYPES.TEXT) { - return found = true; - } - }); - - return found; + return this.state.conflictsData.files.some(f => f.type === CONFLICT_TYPES.TEXT); } }; -- cgit v1.2.1 From c4142cf9c0c0b217034c60a0a973d2e96b17a428 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 5 Oct 2016 07:57:22 -0500 Subject: Improve components for PhantomJs compatibility --- .../merge_conflicts/components/diff_file_editor.js.es6 | 14 +++++++++++--- .../components/inline_conflict_lines.js.es6 | 12 ++++++++++++ .../components/parallel_conflict_line.js.es6 | 14 ++++++++++++++ .../components/parallel_conflict_lines.js.es6 | 15 +++++++++++++++ .../merge_conflicts/merge_conflict_store.js.es6 | 6 +++--- .../merge_conflicts/merge_conflicts_bundle.js.es6 | 12 ++++++++---- .../mixins/line_conflict_actions.js.es6 | 12 ++++++++++++ .../merge_conflicts/mixins/line_conflict_utils.js.es6 | 18 ++++++++++++++++++ 8 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6 create mode 100644 app/assets/javascripts/merge_conflicts/components/parallel_conflict_line.js.es6 create mode 100644 app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6 create mode 100644 app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6 create mode 100644 app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6 (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 index 46aad9fe33c..417095a8db7 100644 --- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 +++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 @@ -3,8 +3,11 @@ global.mergeConflicts = global.mergeConflicts || {}; global.mergeConflicts.diffFileEditor = Vue.extend({ - props: ['file', 'loadFile'], - template: '#diff-file-editor', + props: { + file: Object, + onCancelDiscardConfirmation: Function, + onAcceptDiscardConfirmation: Function + }, data() { return { saved: false, @@ -16,7 +19,6 @@ computed: { classObject() { return { - 'load-file': this.loadFile, 'saved': this.saved, 'is-loading': this.loading }; @@ -77,6 +79,12 @@ if (this.fileLoaded) { this.editor.setValue(this.originalContent, -1); } + }, + cancelDiscardConfirmation(file) { + this.onCancelDiscardConfirmation(file); + }, + acceptDiscardConfirmation(file) { + this.onAcceptDiscardConfirmation(file); } } }); diff --git a/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6 b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6 new file mode 100644 index 00000000000..b4be1c8988d --- /dev/null +++ b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js.es6 @@ -0,0 +1,12 @@ +((global) => { + + global.mergeConflicts = global.mergeConflicts || {}; + + global.mergeConflicts.inlineConflictLines = Vue.extend({ + props: { + file: Object + }, + mixins: [global.mergeConflicts.utils, global.mergeConflicts.actions], + }); + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_line.js.es6 b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_line.js.es6 new file mode 100644 index 00000000000..8b0a8ab2073 --- /dev/null +++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_line.js.es6 @@ -0,0 +1,14 @@ +((global) => { + + global.mergeConflicts = global.mergeConflicts || {}; + + global.mergeConflicts.parallelConflictLine = Vue.extend({ + props: { + file: Object, + line: Object + }, + mixins: [global.mergeConflicts.utils, global.mergeConflicts.actions], + template: '#parallel-conflict-line' + }); + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6 b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6 new file mode 100644 index 00000000000..eb4cc6a9dac --- /dev/null +++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js.es6 @@ -0,0 +1,15 @@ +((global) => { + + global.mergeConflicts = global.mergeConflicts || {}; + + global.mergeConflicts.parallelConflictLines = Vue.extend({ + props: { + file: Object + }, + mixins: [global.mergeConflicts.utils], + components: { + 'parallel-conflict-line': gl.mergeConflicts.parallelConflictLine + } + }); + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 index bdd6753f66a..83bcc3f51aa 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 @@ -196,7 +196,7 @@ isHead: true, isSelected: false, isUnselected: false - } + }; }, decorateLineForInlineView(line, id, conflict) { @@ -227,7 +227,7 @@ richText: rich_text, isSelected: false, isUnselected: false - } + }; }, getOriginHeaderLine(id) { @@ -241,7 +241,7 @@ isOrigin: true, isSelected: false, isUnselected: false - } + }; }, getFilePath(file) { diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 index 449b4d2209a..12688365f19 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 @@ -1,7 +1,12 @@ //= require vue //= require ./merge_conflict_store //= require ./merge_conflict_service +//= require ./mixins/line_conflict_utils +//= require ./mixins/line_conflict_actions //= require ./components/diff_file_editor +//= require ./components/inline_conflict_lines +//= require ./components/parallel_conflict_line +//= require ./components/parallel_conflict_lines $(() => { const INTERACTIVE_RESOLVE_MODE = 'interactive'; @@ -16,7 +21,9 @@ $(() => { el: '#conflicts', data: mergeConflictsStore.state, components: { - 'diff-file-editor': gl.mergeConflicts.diffFileEditor + 'diff-file-editor': gl.mergeConflicts.diffFileEditor, + 'inline-conflict-lines': gl.mergeConflicts.inlineConflictLines, + 'parallel-conflict-lines': gl.mergeConflicts.parallelConflictLines }, computed: { conflictsCountText() { return mergeConflictsStore.getConflictsCountText() }, @@ -46,9 +53,6 @@ $(() => { }); }, methods: { - handleSelected(file, sectionId, selection) { - mergeConflictsStore.handleSelected(file, sectionId, selection); - }, handleViewTypeChange(viewType) { mergeConflictsStore.setViewType(viewType); }, diff --git a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6 b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6 new file mode 100644 index 00000000000..114a2c5b305 --- /dev/null +++ b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js.es6 @@ -0,0 +1,12 @@ +((global) => { + global.mergeConflicts = global.mergeConflicts || {}; + + global.mergeConflicts.actions = { + methods: { + handleSelected(file, sectionId, selection) { + gl.mergeConflicts.mergeConflictsStore.handleSelected(file, sectionId, selection); + } + } + }; + +})(window.gl || (window.gl = {})); diff --git a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6 b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6 new file mode 100644 index 00000000000..b846a90ab2a --- /dev/null +++ b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js.es6 @@ -0,0 +1,18 @@ +((global) => { + global.mergeConflicts = global.mergeConflicts || {}; + + global.mergeConflicts.utils = { + methods: { + lineCssClass(line) { + return { + 'head': line.isHead, + 'origin': line.isOrigin, + 'match': line.hasMatch, + 'selected': line.isSelected, + 'unselected': line.isUnselected + }; + } + } + }; + +})(window.gl || (window.gl = {})); -- cgit v1.2.1 From 54bfe70795e289b86485b2a57d72b6711e4994bd Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 5 Oct 2016 07:57:57 -0500 Subject: Add more tests to check conflicts resolution --- app/assets/javascripts/merge_conflict_data_provider.js.es6 | 2 +- app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflict_data_provider.js.es6 b/app/assets/javascripts/merge_conflict_data_provider.js.es6 index 6d1d3f36b33..5877d2f1896 100644 --- a/app/assets/javascripts/merge_conflict_data_provider.js.es6 +++ b/app/assets/javascripts/merge_conflict_data_provider.js.es6 @@ -290,7 +290,7 @@ class MergeConflictDataProvider { isReadyToCommit() { const vi = this.vueInstance; const files = this.vueInstance.conflictsData.files; - const hasCommitMessage = $.trim(this.vueInstance.conflictsData.commitMessage).length; + const hasCommitMessage = this.vueInstance.conflictsData.commitMessage.trim(); let unresolved = 0; for (let i = 0, l = files.length; i < l; i++) { diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 index 83bcc3f51aa..5c5c65f29d4 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 @@ -321,7 +321,8 @@ let numberConflicts = 0; let resolvedConflicts = Object.keys(file.resolutionData).length - // We only check if + // We only check for conflicts type 'text' + // since conflicts `text_editor` can´t be resolved in interactive mode if (file.type === CONFLICT_TYPES.TEXT) { for (let j = 0, k = file.sections.length; j < k; j++) { if (file.sections[j].conflict) { @@ -334,6 +335,7 @@ } } } else if (file.resolveMode === EDIT_RESOLVE_MODE) { + // Unlikely to happen since switching to Edit mode saves content automatically. // Checking anyway in case the save strategy changes in the future if (!file.content) { -- cgit v1.2.1 From 8fd7a5942286d69eb34406e06ba5263c3944012e Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 5 Oct 2016 20:49:16 -0500 Subject: Remove unused files --- .../merge_conflict_data_provider.js.es6 | 438 --------------------- .../javascripts/merge_conflict_resolver.js.es6 | 112 ------ 2 files changed, 550 deletions(-) delete mode 100644 app/assets/javascripts/merge_conflict_data_provider.js.es6 delete mode 100644 app/assets/javascripts/merge_conflict_resolver.js.es6 (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflict_data_provider.js.es6 b/app/assets/javascripts/merge_conflict_data_provider.js.es6 deleted file mode 100644 index 5877d2f1896..00000000000 --- a/app/assets/javascripts/merge_conflict_data_provider.js.es6 +++ /dev/null @@ -1,438 +0,0 @@ -const HEAD_HEADER_TEXT = 'HEAD//our changes'; -const ORIGIN_HEADER_TEXT = 'origin//their changes'; -const HEAD_BUTTON_TITLE = 'Use ours'; -const ORIGIN_BUTTON_TITLE = 'Use theirs'; -const INTERACTIVE_RESOLVE_MODE = 'interactive'; -const EDIT_RESOLVE_MODE = 'edit'; -const DEFAULT_RESOLVE_MODE = INTERACTIVE_RESOLVE_MODE; - -class MergeConflictDataProvider { - - getInitialData() { - // TODO: remove reliance on jQuery and DOM state introspection - const diffViewType = $.cookie('diff_view'); - const fixedLayout = $('.content-wrapper .container-fluid').hasClass('container-limited'); - - return { - isLoading : true, - hasError : false, - isParallel : diffViewType === 'parallel', - diffViewType : diffViewType, - fixedLayout : fixedLayout, - isSubmitting : false, - conflictsData : {} - } - } - - - decorateData(vueInstance, data) { - this.vueInstance = vueInstance; - - if (data.type === 'error') { - vueInstance.hasError = true; - data.errorMessage = data.message; - } - else { - data.shortCommitSha = data.commit_sha.slice(0, 7); - data.commitMessage = data.commit_message; - - this.decorateFiles(data); - this.setParallelLines(data); - this.setInlineLines(data); - } - - vueInstance.conflictsData = data; - vueInstance.isSubmitting = false; - - const conflictsText = this.getConflictsCount() > 1 ? 'conflicts' : 'conflict'; - vueInstance.conflictsData.conflictsText = conflictsText; - } - - decorateFiles(data) { - data.files.forEach((file) => { - file.content = ''; - file.resolutionData = {}; - file.promptDiscardConfirmation = false; - file.resolveMode = DEFAULT_RESOLVE_MODE; - }); - } - - - setParallelLines(data) { - data.files.forEach( (file) => { - file.filePath = this.getFilePath(file); - file.iconClass = `fa-${file.blob_icon}`; - file.blobPath = file.blob_path; - file.parallelLines = []; - const linesObj = { left: [], right: [] }; - - file.sections.forEach( (section) => { - const { conflict, lines, id } = section; - - if (conflict) { - linesObj.left.push(this.getOriginHeaderLine(id)); - linesObj.right.push(this.getHeadHeaderLine(id)); - } - - lines.forEach( (line) => { - const { type } = line; - - if (conflict) { - if (type === 'old') { - linesObj.left.push(this.getLineForParallelView(line, id, 'conflict')); - } - else if (type === 'new') { - linesObj.right.push(this.getLineForParallelView(line, id, 'conflict', true)); - } - } - else { - const lineType = type || 'context'; - - linesObj.left.push (this.getLineForParallelView(line, id, lineType)); - linesObj.right.push(this.getLineForParallelView(line, id, lineType, true)); - } - }); - - this.checkLineLengths(linesObj); - }); - - for (let i = 0, len = linesObj.left.length; i < len; i++) { - file.parallelLines.push([ - linesObj.right[i], - linesObj.left[i] - ]); - } - - }); - } - - - checkLineLengths(linesObj) { - let { left, right } = linesObj; - - if (left.length !== right.length) { - if (left.length > right.length) { - const diff = left.length - right.length; - for (let i = 0; i < diff; i++) { - right.push({ lineType: 'emptyLine', richText: '' }); - } - } - else { - const diff = right.length - left.length; - for (let i = 0; i < diff; i++) { - left.push({ lineType: 'emptyLine', richText: '' }); - } - } - } - } - - - setInlineLines(data) { - data.files.forEach( (file) => { - file.iconClass = `fa-${file.blob_icon}`; - file.blobPath = file.blob_path; - file.filePath = this.getFilePath(file); - file.inlineLines = [] - - file.sections.forEach( (section) => { - let currentLineType = 'new'; - const { conflict, lines, id } = section; - - if (conflict) { - file.inlineLines.push(this.getHeadHeaderLine(id)); - } - - lines.forEach( (line) => { - const { type } = line; - - if ((type === 'new' || type === 'old') && currentLineType !== type) { - currentLineType = type; - file.inlineLines.push({ lineType: 'emptyLine', richText: '' }); - } - - this.decorateLineForInlineView(line, id, conflict); - file.inlineLines.push(line); - }) - - if (conflict) { - file.inlineLines.push(this.getOriginHeaderLine(id)); - } - }); - }); - } - - - handleSelected(file, sectionId, selection) { - const vi = this.vueInstance; - let files = vi.conflictsData.files; - - vi.$set(`conflictsData.files[${files.indexOf(file)}].resolutionData['${sectionId}']`, selection); - - - files.forEach( (file) => { - file.inlineLines.forEach( (line) => { - if (line.id === sectionId && (line.hasConflict || line.isHeader)) { - this.markLine(line, selection); - } - }); - - file.parallelLines.forEach( (lines) => { - const left = lines[0]; - const right = lines[1]; - const hasSameId = right.id === sectionId || left.id === sectionId; - const isLeftMatch = left.hasConflict || left.isHeader; - const isRightMatch = right.hasConflict || right.isHeader; - - if (hasSameId && (isLeftMatch || isRightMatch)) { - this.markLine(left, selection); - this.markLine(right, selection); - } - }) - }); - } - - - updateViewType(newType) { - const vi = this.vueInstance; - - if (newType === vi.diffViewType || !(newType === 'parallel' || newType === 'inline')) { - return; - } - - vi.diffViewType = newType; - vi.isParallel = newType === 'parallel'; - $.cookie('diff_view', newType, { - path: (gon && gon.relative_url_root) || '/' - }); - $('.content-wrapper .container-fluid') - .toggleClass('container-limited', !vi.isParallel && vi.fixedLayout); - } - - setFileResolveMode(file, mode) { - const vi = this.vueInstance; - - // Restore Interactive mode when switching to Edit mode - if (mode === EDIT_RESOLVE_MODE) { - file.resolutionData = {}; - - this.restoreFileLinesState(file); - } - - file.resolveMode = mode; - } - - - restoreFileLinesState(file) { - file.inlineLines.forEach((line) => { - if (line.hasConflict || line.isHeader) { - line.isSelected = false; - line.isUnselected = false; - } - }); - - file.parallelLines.forEach((lines) => { - const left = lines[0]; - const right = lines[1]; - const isLeftMatch = left.hasConflict || left.isHeader; - const isRightMatch = right.hasConflict || right.isHeader; - - if (isLeftMatch || isRightMatch) { - left.isSelected = false; - left.isUnselected = false; - right.isSelected = false; - right.isUnselected = false; - } - }); - } - - - setPromptConfirmationState(file, state) { - file.promptDiscardConfirmation = state; - } - - - markLine(line, selection) { - if (selection === 'head' && line.isHead) { - line.isSelected = true; - line.isUnselected = false; - } - else if (selection === 'origin' && line.isOrigin) { - line.isSelected = true; - line.isUnselected = false; - } - else { - line.isSelected = false; - line.isUnselected = true; - } - } - - - getConflictsCount() { - if (!this.vueInstance.conflictsData.files) { - return 0; - } - - const files = this.vueInstance.conflictsData.files; - let count = 0; - - files.forEach((file) => { - file.sections.forEach((section) => { - if (section.conflict) { - count++; - } - }); - }); - - return count; - } - - - isReadyToCommit() { - const vi = this.vueInstance; - const files = this.vueInstance.conflictsData.files; - const hasCommitMessage = this.vueInstance.conflictsData.commitMessage.trim(); - let unresolved = 0; - - for (let i = 0, l = files.length; i < l; i++) { - let file = files[i]; - - if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) { - let numberConflicts = 0; - let resolvedConflicts = Object.keys(file.resolutionData).length - - for (let j = 0, k = file.sections.length; j < k; j++) { - if (file.sections[j].conflict) { - numberConflicts++; - } - } - - if (resolvedConflicts !== numberConflicts) { - unresolved++; - } - } else if (file.resolveMode === EDIT_RESOLVE_MODE) { - // Unlikely to happen since switching to Edit mode saves content automatically. - // Checking anyway in case the save strategy changes in the future - if (!file.content) { - unresolved++; - continue; - } - } - } - - return !vi.isSubmitting && hasCommitMessage && !unresolved; - } - - - getCommitButtonText() { - const initial = 'Commit conflict resolution'; - const inProgress = 'Committing...'; - const vue = this.vueInstance; - - return vue ? vue.isSubmitting ? inProgress : initial : initial; - } - - - decorateLineForInlineView(line, id, conflict) { - const { type } = line; - line.id = id; - line.hasConflict = conflict; - line.isHead = type === 'new'; - line.isOrigin = type === 'old'; - line.hasMatch = type === 'match'; - line.richText = line.rich_text; - line.isSelected = false; - line.isUnselected = false; - } - - getLineForParallelView(line, id, lineType, isHead) { - const { old_line, new_line, rich_text } = line; - const hasConflict = lineType === 'conflict'; - - return { - id, - lineType, - hasConflict, - isHead : hasConflict && isHead, - isOrigin : hasConflict && !isHead, - hasMatch : lineType === 'match', - lineNumber : isHead ? new_line : old_line, - section : isHead ? 'head' : 'origin', - richText : rich_text, - isSelected : false, - isUnselected : false - } - } - - - getHeadHeaderLine(id) { - return { - id : id, - richText : HEAD_HEADER_TEXT, - buttonTitle : HEAD_BUTTON_TITLE, - type : 'new', - section : 'head', - isHeader : true, - isHead : true, - isSelected : false, - isUnselected: false - } - } - - - getOriginHeaderLine(id) { - return { - id : id, - richText : ORIGIN_HEADER_TEXT, - buttonTitle : ORIGIN_BUTTON_TITLE, - type : 'old', - section : 'origin', - isHeader : true, - isOrigin : true, - isSelected : false, - isUnselected: false - } - } - - - handleFailedRequest(vueInstance, data) { - vueInstance.hasError = true; - vueInstance.conflictsData.errorMessage = 'Something went wrong!'; - } - - - getCommitData() { - let conflictsData = this.vueInstance.conflictsData; - let commitData = {}; - - commitData = { - commitMessage: conflictsData.commitMessage, - files: [] - }; - - conflictsData.files.forEach((file) => { - let addFile; - - addFile = { - old_path: file.old_path, - new_path: file.new_path - }; - - // Submit only one data for type of editing - if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) { - addFile.sections = file.resolutionData; - } else if (file.resolveMode === EDIT_RESOLVE_MODE) { - addFile.content = file.content; - } - - commitData.files.push(addFile); - }); - - return commitData; - } - - - getFilePath(file) { - const { old_path, new_path } = file; - return old_path === new_path ? new_path : `${old_path} → ${new_path}`; - } -} diff --git a/app/assets/javascripts/merge_conflict_resolver.js.es6 b/app/assets/javascripts/merge_conflict_resolver.js.es6 deleted file mode 100644 index c317a6521b5..00000000000 --- a/app/assets/javascripts/merge_conflict_resolver.js.es6 +++ /dev/null @@ -1,112 +0,0 @@ -//= require vue -//= require ./merge_conflicts/components/diff_file_editor - -const INTERACTIVE_RESOLVE_MODE = 'interactive'; -const EDIT_RESOLVE_MODE = 'edit'; - -class MergeConflictResolver { - - constructor() { - this.dataProvider = new MergeConflictDataProvider(); - this.initVue(); - } - - initVue() { - const that = this; - this.vue = new Vue({ - el : '#conflicts', - name : 'MergeConflictResolver', - data : this.dataProvider.getInitialData(), - created : this.fetchData(), - computed : this.setComputedProperties(), - methods : { - handleSelected(file, sectionId, selection) { - that.dataProvider.handleSelected(file, sectionId, selection); - }, - handleViewTypeChange(newType) { - that.dataProvider.updateViewType(newType); - }, - commit() { - that.commit(); - }, - onClickResolveModeButton(file, mode) { - that.toggleResolveMode(file, mode); - }, - acceptDiscardConfirmation(file) { - that.dataProvider.setPromptConfirmationState(file, false); - that.dataProvider.setFileResolveMode(file, INTERACTIVE_RESOLVE_MODE); - }, - cancelDiscardConfirmation(file) { - that.dataProvider.setPromptConfirmationState(file, false); - }, - }, - components: { - 'diff-file-editor': window.gl.diffFileEditor - } - }) - } - - - setComputedProperties() { - const dp = this.dataProvider; - - return { - conflictsCount() { return dp.getConflictsCount() }, - readyToCommit() { return dp.isReadyToCommit() }, - commitButtonText() { return dp.getCommitButtonText() } - } - } - - - fetchData() { - const dp = this.dataProvider; - - $.get($('#conflicts').data('conflictsPath')) - .done((data) => { - dp.decorateData(this.vue, data); - }) - .error((data) => { - dp.handleFailedRequest(this.vue, data); - }) - .always(() => { - this.vue.isLoading = false; - - this.vue.$nextTick(() => { - $('#conflicts .js-syntax-highlight').syntaxHighlight(); - }); - - $('.content-wrapper .container-fluid') - .toggleClass('container-limited', !this.vue.isParallel && this.vue.fixedLayout); - }) - } - - - commit() { - this.vue.isSubmitting = true; - - $.ajax({ - url: $('#conflicts').data('resolveConflictsPath'), - data: JSON.stringify(this.dataProvider.getCommitData()), - contentType: "application/json", - dataType: 'json', - method: 'POST' - }) - .done((data) => { - window.location.href = data.redirect_to; - }) - .error(() => { - this.vue.isSubmitting = false; - new Flash('Something went wrong!'); - }); - } - - - toggleResolveMode(file, mode) { - if (mode === INTERACTIVE_RESOLVE_MODE && file.resolveEditChanged) { - this.dataProvider.setPromptConfirmationState(file, true); - return; - } - - this.dataProvider.setFileResolveMode(file, mode); - } -} -- cgit v1.2.1 From 6141816c233d87888d1450d3bc48a28fc6044ec1 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 5 Oct 2016 20:49:43 -0500 Subject: Use plain JS to get elements and data and values --- .../javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 index 12688365f19..7fd3749b3e2 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js.es6 @@ -10,14 +10,14 @@ $(() => { const INTERACTIVE_RESOLVE_MODE = 'interactive'; - const $conflicts = $(document.getElementById('conflicts')); + const conflictsEl = document.querySelector('#conflicts'); const mergeConflictsStore = gl.mergeConflicts.mergeConflictsStore; const mergeConflictsService = new gl.mergeConflicts.mergeConflictsService({ - conflictsPath: $conflicts.data('conflictsPath'), - resolveConflictsPath: $conflicts.data('resolveConflictsPath') + conflictsPath: conflictsEl.dataset.conflictsPath, + resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath }); - gl.MergeConflictsResolverApp = new Vue({ + gl.MergeConflictsResolverApp = new Vue({ el: '#conflicts', data: mergeConflictsStore.state, components: { @@ -48,7 +48,7 @@ $(() => { mergeConflictsStore.setLoadingState(false); this.$nextTick(() => { - $conflicts.find('.js-syntax-highlight').syntaxHighlight(); + $(conflictsEl.querySelectorAll('.js-syntax-highlight')).syntaxHighlight(); }); }); }, -- cgit v1.2.1 From f5de2ce3fd74af214556f677f54efa3f4da13ed3 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Thu, 13 Oct 2016 12:32:35 -0500 Subject: Add syntax highlighting to files --- .../javascripts/merge_conflicts/components/diff_file_editor.js.es6 | 1 + 1 file changed, 1 insertion(+) (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 index 417095a8db7..3379414343f 100644 --- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 +++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 @@ -55,6 +55,7 @@ this.fileLoaded = true; this.editor = ace.edit(content); this.editor.$blockScrolling = Infinity; // Turn off annoying warning + this.editor.getSession().setMode(`ace/mode/${file.blob_ace_mode}`); this.editor.on('change', () => { this.saveDiffResolution(); }); -- cgit v1.2.1 From 28dbfd5c4c7793be4147ba217e7d9e162905088b Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Mon, 17 Oct 2016 12:43:17 -0500 Subject: Provide better error message to the user --- .../javascripts/merge_conflicts/components/diff_file_editor.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 index 3379414343f..5012bdfe997 100644 --- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 +++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6 @@ -62,7 +62,7 @@ this.saveDiffResolution(); }) .fail(() => { - console.log('error'); + new Flash('Failed to load the file, please try again.'); }) .always(() => { this.loading = false; -- cgit v1.2.1 From 73988dd994d6c0d7f5cec24ca5c4c20137cc3844 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Mon, 17 Oct 2016 17:47:07 -0500 Subject: Update endpoint to username validator --- app/assets/javascripts/username_validator.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/assets/javascripts') diff --git a/app/assets/javascripts/username_validator.js.es6 b/app/assets/javascripts/username_validator.js.es6 index 2517f778365..bf4b2e320cd 100644 --- a/app/assets/javascripts/username_validator.js.es6 +++ b/app/assets/javascripts/username_validator.js.es6 @@ -76,7 +76,7 @@ this.renderState(); return $.ajax({ type: 'GET', - url: `/u/${username}/exists`, + url: `/users/${username}/exists`, dataType: 'json', success: (res) => this.setAvailabilityState(res.exists) }); -- cgit v1.2.1