Commit 40f01b39 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'leipert-migrate-merge-conflicts-to-sfc-321090' into 'master'

Migrate Merge Conflicts App to Vue SFC

See merge request gitlab-org/gitlab!54463
parents f1bdd682 61555205
......@@ -213,10 +213,6 @@ linters:
- 'app/views/projects/mattermosts/new.html.haml'
- 'app/views/projects/merge_requests/_commits.html.haml'
- 'app/views/projects/merge_requests/_mr_title.html.haml'
- 'app/views/projects/merge_requests/conflicts/_commit_stats.html.haml'
- 'app/views/projects/merge_requests/conflicts/_file_actions.html.haml'
- 'app/views/projects/merge_requests/conflicts/_submit_form.html.haml'
- 'app/views/projects/merge_requests/conflicts/show.html.haml'
- 'app/views/projects/merge_requests/creations/_diffs.html.haml'
- 'app/views/projects/merge_requests/creations/_new_compare.html.haml'
- 'app/views/projects/merge_requests/creations/_new_submit.html.haml'
......
<script>
import { GlSprintf } from '@gitlab/ui';
import { __ } from '~/locale';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import DiffFileEditor from './components/diff_file_editor.vue';
import InlineConflictLines from './components/inline_conflict_lines.vue';
import ParallelConflictLines from './components/parallel_conflict_lines.vue';
/**
* NOTE: Most of this component is directly using $root, rather than props or a better data store.
* This is BAD and one shouldn't copy that behavior. Similarly a lot of the classes below should
* be replaced with GitLab UI components.
*
* We are just doing it temporarily in order to migrate the template from HAML => Vue in an iterative manner
* and are going to clean it up as part of:
*
* https://gitlab.com/gitlab-org/gitlab/-/issues/321090
*
*/
export default {
components: {
GlSprintf,
FileIcon,
DiffFileEditor,
InlineConflictLines,
ParallelConflictLines,
},
inject: ['mergeRequestPath', 'sourceBranchPath'],
i18n: {
commitStatSummary: __('Showing %{conflict} between %{sourceBranch} and %{targetBranch}'),
resolveInfo: __(
'You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}',
),
},
};
</script>
<template>
<div id="conflicts">
<div v-if="$root.isLoading" class="loading">
<div class="spinner spinner-md"></div>
</div>
<div v-if="$root.hasError" class="nothing-here-block">
{{ $root.conflictsData.errorMessage }}
</div>
<template v-if="!$root.isLoading && !$root.hasError">
<div class="content-block oneline-block files-changed">
<div v-if="$root.showDiffViewTypeSwitcher" class="inline-parallel-buttons">
<div class="btn-group">
<button
:class="{ active: !$root.isParallel }"
class="btn gl-button"
@click="$root.handleViewTypeChange('inline')"
>
{{ __('Inline') }}
</button>
<button
:class="{ active: $root.isParallel }"
class="btn gl-button"
@click="$root.handleViewTypeChange('parallel')"
>
{{ __('Side-by-side') }}
</button>
</div>
</div>
<div class="js-toggle-container">
<div class="commit-stat-summary">
<gl-sprintf :message="$options.i18n.commitStatSummary">
<template #conflict>
<strong class="cred">
{{ $root.conflictsCountText }}
</strong>
</template>
<template #sourceBranch>
<strong class="ref-name">
{{ $root.conflictsData.sourceBranch }}
</strong>
</template>
<template #targetBranch>
<strong class="ref-name">
{{ $root.conflictsData.targetBranch }}
</strong>
</template>
</gl-sprintf>
</div>
</div>
</div>
<div class="files-wrapper">
<div class="files">
<div
v-for="file in $root.conflictsData.files"
:key="file.blobPath"
class="diff-file file-holder conflict"
>
<div class="js-file-title file-title file-title-flex-parent cursor-default">
<div class="file-header-content">
<file-icon :file-name="file.filePath" :size="18" css-classes="gl-mr-2" />
<strong class="file-title-name">{{ file.filePath }}</strong>
</div>
<div class="file-actions d-flex align-items-center gl-ml-auto gl-align-self-start">
<div v-if="file.type === 'text'" class="btn-group gl-mr-3">
<button
:class="{ active: file.resolveMode === 'interactive' }"
class="btn gl-button"
type="button"
@click="$root.onClickResolveModeButton(file, 'interactive')"
>
{{ __('Interactive mode') }}
</button>
<button
:class="{ active: file.resolveMode === 'edit' }"
class="btn gl-button"
type="button"
@click="$root.onClickResolveModeButton(file, 'edit')"
>
{{ __('Edit inline') }}
</button>
</div>
<a :href="file.blobPath" class="btn gl-button view-file">
<gl-sprintf :message="__('View file @ %{commitSha}')">
<template #commitSha>
{{ $root.conflictsData.shortCommitSha }}
</template>
</gl-sprintf>
</a>
</div>
</div>
<div class="diff-content diff-wrap-lines">
<div
v-show="
!$root.isParallel && file.resolveMode === 'interactive' && file.type === 'text'
"
class="file-content"
>
<inline-conflict-lines :file="file" />
</div>
<div
v-show="
$root.isParallel && file.resolveMode === 'interactive' && file.type === 'text'
"
class="file-content"
>
<parallel-conflict-lines :file="file" />
</div>
<div v-show="file.resolveMode === 'edit' || file.type === 'text-editor'">
<diff-file-editor
:file="file"
:on-accept-discard-confirmation="$root.acceptDiscardConfirmation"
:on-cancel-discard-confirmation="$root.cancelDiscardConfirmation"
/>
</div>
</div>
</div>
</div>
</div>
<hr />
<div class="resolve-conflicts-form">
<div class="form-group row">
<div class="col-md-4">
<h4>
{{ __('Resolve conflicts on source branch') }}
</h4>
<div class="resolve-info">
<gl-sprintf :message="$options.i18n.resolveInfo">
<template #use_ours>
<code>{{ s__('MergeConflict|Use ours') }}</code>
</template>
<template #use_theirs>
<code>{{ s__('MergeConflict|Use theirs') }}</code>
</template>
<template #branch_name>
<a class="ref-name" :href="sourceBranchPath">
{{ $root.conflictsData.sourceBranch }}
</a>
</template>
</gl-sprintf>
</div>
</div>
<div class="col-md-8">
<label class="label-bold" for="commit-message">
{{ __('Commit message') }}
</label>
<div class="commit-message-container">
<div class="max-width-marker"></div>
<textarea
id="commit-message"
v-model="$root.conflictsData.commitMessage"
class="form-control js-commit-message"
rows="5"
></textarea>
</div>
</div>
</div>
<div class="form-group row">
<div class="offset-md-4 col-md-8">
<div class="row">
<div class="col-6">
<button
:disabled="!$root.readyToCommit"
class="btn gl-button btn-success js-submit-button"
type="button"
@click="$root.commit()"
>
<span>{{ $root.commitButtonText }}</span>
</button>
</div>
<div class="col-6 text-right">
<a :href="mergeRequestPath" class="gl-button btn btn-default">
{{ __('Cancel') }}
</a>
</div>
</div>
</div>
</div>
</div>
</template>
</div>
</template>
// This is a true violation of @gitlab/no-runtime-template-compiler, as it
// relies on app/views/projects/merge_requests/conflicts/show.html.haml for its
// template.
/* eslint-disable @gitlab/no-runtime-template-compiler */
import $ from 'jquery';
import Vue from 'vue';
import { __ } from '~/locale';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import { deprecatedCreateFlash as createFlash } from '../flash';
import initIssuableSidebar from '../init_issuable_sidebar';
import './merge_conflict_store';
import syntaxHighlight from '../syntax_highlight';
import DiffFileEditor from './components/diff_file_editor.vue';
import InlineConflictLines from './components/inline_conflict_lines.vue';
import ParallelConflictLines from './components/parallel_conflict_lines.vue';
import MergeConflictsResolverApp from './merge_conflict_resolver_app.vue';
import MergeConflictsService from './merge_conflict_service';
export default function initMergeConflicts() {
......@@ -24,15 +17,15 @@ export default function initMergeConflicts() {
resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath,
});
const { sourceBranchPath, mergeRequestPath } = conflictsEl.dataset;
initIssuableSidebar();
gl.MergeConflictsResolverApp = new Vue({
el: '#conflicts',
components: {
FileIcon,
DiffFileEditor,
InlineConflictLines,
ParallelConflictLines,
return new Vue({
el: conflictsEl,
provide: {
sourceBranchPath,
mergeRequestPath,
},
data: mergeConflictsStore.state,
computed: {
......@@ -103,5 +96,8 @@ export default function initMergeConflicts() {
});
},
},
render(createElement) {
return createElement(MergeConflictsResolverApp);
},
});
}
.content-block.oneline-block.files-changed{ "v-if" => "!isLoading && !hasError" }
.inline-parallel-buttons{ "v-if" => "showDiffViewTypeSwitcher" }
.btn-group
%button.btn.gl-button{ ":class" => "{'active': !isParallel}", "@click" => "handleViewTypeChange('inline')" }
= _('Inline')
%button.btn.gl-button{ ":class" => "{'active': isParallel}", "@click" => "handleViewTypeChange('parallel')" }
= _('Side-by-side')
.js-toggle-container
.commit-stat-summary
= _('Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}').html_safe % { conflict_start: '<strong class="cred">'.html_safe, ref_start: '<strong class="ref-name">'.html_safe, strong_end: '</strong>'.html_safe, conflicts_text: '{{conflictsCountText}}', source_branch: '{{conflictsData.sourceBranch}}', target_branch: '{{conflictsData.targetBranch}}' }
.file-actions.d-flex.align-items-center.gl-ml-auto.gl-align-self-start
.btn-group.gl-mr-3{ "v-if" => "file.type === 'text'" }
%button.btn.gl-button{ ":class" => "{ 'active': file.resolveMode == 'interactive' }",
'@click' => "onClickResolveModeButton(file, 'interactive')",
type: 'button' }
= _('Interactive mode')
%button.btn.gl-button{ ':class' => "{ 'active': file.resolveMode == 'edit' }",
'@click' => "onClickResolveModeButton(file, 'edit')",
type: 'button' }
= _('Edit inline')
%a.btn.gl-button.view-file{ ":href" => "file.blobPath" }
= _('View file @%{commit_sha}') % { commit_sha: '{{conflictsData.shortCommitSha}}' }
- branch_name = link_to @merge_request.source_branch, project_tree_path(@merge_request.project, @merge_request.source_branch), class: "ref-name"
- translation =_('You can resolve the merge conflict using either the Interactive mode, by choosing %{use_ours} or %{use_theirs} buttons, or by editing the files directly. Commit these changes into %{branch_name}') % { use_ours: '<code>Use Ours</code>', use_theirs: '<code>Use Theirs</code>', branch_name: branch_name }
%hr
.resolve-conflicts-form
.form-group.row
.col-md-4
%h4= _('Resolve conflicts on source branch')
.resolve-info{ "v-pre": true }
= translation.html_safe
.col-md-8
%label.label-bold{ "for" => "commit-message" }
#{ _('Commit message') }
.commit-message-container
.max-width-marker
%textarea.form-control.js-commit-message#commit-message{ "v-model" => "conflictsData.commitMessage", "rows" => "5" }
.form-group.row
.offset-md-4.col-md-8
.row
.col-6
%button.btn.gl-button.btn-success.js-submit-button{ type: "button", "@click" => "commit()", ":disabled" => "!readyToCommit" }
%span {{commitButtonText}}
.col-6.text-right
= link_to "Cancel", project_merge_request_path(@merge_request.project, @merge_request), class: "gl-button btn btn-default"
- page_title _("Merge Conflicts"), "#{@merge_request.title} (#{@merge_request.to_reference}", _("Merge Requests")
- add_page_specific_style 'page_bundles/merge_conflicts'
= render "projects/merge_requests/mr_title"
.merge-request-details.issuable-details
......@@ -7,30 +8,7 @@
= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, source_branch: @merge_request.source_branch
#conflicts{ "v-cloak" => "true", data: { conflicts_path: conflicts_project_merge_request_path(@merge_request.project, @merge_request, format: :json),
resolve_conflicts_path: resolve_conflicts_project_merge_request_path(@merge_request.project, @merge_request) } }
.loading{ "v-if" => "isLoading" }
.spinner.spinner-md
.nothing-here-block{ "v-if" => "hasError" }
{{conflictsData.errorMessage}}
= render partial: "projects/merge_requests/conflicts/commit_stats"
.files-wrapper{ "v-if" => "!isLoading && !hasError" }
.files
.diff-file.file-holder.conflict{ "v-for" => "file in conflictsData.files" }
.js-file-title.file-title.file-title-flex-parent.cursor-default
.file-header-content
%file-icon{ ':file-name': 'file.filePath', ':size': '18', 'css-classes': 'gl-mr-2' }
%strong.file-title-name {{file.filePath}}
= render partial: 'projects/merge_requests/conflicts/file_actions'
.diff-content.diff-wrap-lines
.file-content{ "v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
%inline-conflict-lines{ ":file" => "file" }
.file-content{ "v-show" => "isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
%parallel-conflict-lines{ ":file" => "file" }
%div{ "v-show" => "file.resolveMode === 'edit' || file.type === 'text-editor'" }
%diff-file-editor{ ":file" => "file", ":on-cancel-discard-confirmation" => "cancelDiscardConfirmation", ":on-accept-discard-confirmation" => "acceptDiscardConfirmation" }
= render partial: "projects/merge_requests/conflicts/submit_form"
#conflicts{ data: { conflicts_path: conflicts_project_merge_request_path(@merge_request.project, @merge_request, format: :json),
resolve_conflicts_path: resolve_conflicts_project_merge_request_path(@merge_request.project, @merge_request),
source_branch_path: project_tree_path(@merge_request.project, @merge_request.source_branch),
merge_request_path: project_merge_request_path(@merge_request.project, @merge_request) } }
......@@ -27387,7 +27387,7 @@ msgid_plural "Showing %d events"
msgstr[0] ""
msgstr[1] ""
msgid "Showing %{conflict_start}%{conflicts_text}%{strong_end} between %{ref_start}%{source_branch}%{strong_end} and %{ref_start}%{target_branch}%{strong_end}"
msgid "Showing %{conflict} between %{sourceBranch} and %{targetBranch}"
msgstr ""
msgid "Showing %{count} of %{total} projects"
......@@ -32776,9 +32776,6 @@ msgstr ""
msgid "View file @ %{commitSha}"
msgstr ""
msgid "View file @%{commit_sha}"
msgstr ""
msgid "View full dashboard"
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment