Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/ide')
-rw-r--r--app/assets/javascripts/ide/components/activity_bar.vue106
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/actions.vue16
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue45
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/form.vue171
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list.vue103
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue3
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue30
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/success_message.vue8
-rw-r--r--app/assets/javascripts/ide/components/editor_mode_dropdown.vue80
-rw-r--r--app/assets/javascripts/ide/components/ide.vue129
-rw-r--r--app/assets/javascripts/ide/components/ide_context_bar.vue42
-rw-r--r--app/assets/javascripts/ide/components/ide_external_links.vue43
-rw-r--r--app/assets/javascripts/ide/components/ide_project_branches_tree.vue47
-rw-r--r--app/assets/javascripts/ide/components/ide_project_tree.vue65
-rw-r--r--app/assets/javascripts/ide/components/ide_repo_tree.vue41
-rw-r--r--app/assets/javascripts/ide/components/ide_review.vue62
-rw-r--r--app/assets/javascripts/ide/components/ide_side_bar.vue143
-rw-r--r--app/assets/javascripts/ide/components/ide_tree.vue42
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue76
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue3
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue102
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue52
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue13
-rw-r--r--app/assets/javascripts/ide/components/repo_tab.vue36
-rw-r--r--app/assets/javascripts/ide/components/repo_tabs.vue17
-rw-r--r--app/assets/javascripts/ide/constants.js16
-rw-r--r--app/assets/javascripts/ide/ide_router.js7
-rw-r--r--app/assets/javascripts/ide/index.js15
-rw-r--r--app/assets/javascripts/ide/lib/editor.js8
-rw-r--r--app/assets/javascripts/ide/stores/actions.js14
-rw-r--r--app/assets/javascripts/ide/stores/actions/file.js9
-rw-r--r--app/assets/javascripts/ide/stores/actions/project.js1
-rw-r--r--app/assets/javascripts/ide/stores/getters.js23
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js39
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js2
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js15
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js45
-rw-r--r--app/assets/javascripts/ide/stores/state.js5
38 files changed, 962 insertions, 712 deletions
diff --git a/app/assets/javascripts/ide/components/activity_bar.vue b/app/assets/javascripts/ide/components/activity_bar.vue
new file mode 100644
index 00000000000..05dbc1410de
--- /dev/null
+++ b/app/assets/javascripts/ide/components/activity_bar.vue
@@ -0,0 +1,106 @@
+<script>
+import { mapActions, mapGetters, mapState } from 'vuex';
+import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
+import { activityBarViews } from '../constants';
+
+export default {
+ components: {
+ Icon,
+ },
+ directives: {
+ tooltip,
+ },
+ computed: {
+ ...mapGetters(['currentProject', 'hasChanges']),
+ ...mapState(['currentActivityView']),
+ goBackUrl() {
+ return document.referrer || this.currentProject.web_url;
+ },
+ },
+ methods: {
+ ...mapActions(['updateActivityBarView']),
+ },
+ activityBarViews,
+};
+</script>
+
+<template>
+ <nav class="ide-activity-bar">
+ <ul class="list-unstyled">
+ <li v-once>
+ <a
+ v-tooltip
+ data-container="body"
+ data-placement="right"
+ :href="goBackUrl"
+ class="ide-sidebar-link"
+ :title="s__('IDE|Go back')"
+ :aria-label="s__('IDE|Go back')"
+ >
+ <icon
+ :size="16"
+ name="go-back"
+ />
+ </a>
+ </li>
+ <li>
+ <button
+ v-tooltip
+ data-container="body"
+ data-placement="right"
+ type="button"
+ class="ide-sidebar-link js-ide-edit-mode"
+ :class="{
+ active: currentActivityView === $options.activityBarViews.edit
+ }"
+ @click.prevent="updateActivityBarView($options.activityBarViews.edit)"
+ :title="s__('IDE|Edit')"
+ :aria-label="s__('IDE|Edit')"
+ >
+ <icon
+ name="code"
+ />
+ </button>
+ </li>
+ <li>
+ <button
+ v-tooltip
+ data-container="body"
+ data-placement="right"
+ type="button"
+ class="ide-sidebar-link js-ide-review-mode"
+ :class="{
+ active: currentActivityView === $options.activityBarViews.review
+ }"
+ @click.prevent="updateActivityBarView($options.activityBarViews.review)"
+ :title="s__('IDE|Review')"
+ :aria-label="s__('IDE|Review')"
+ >
+ <icon
+ name="file-modified"
+ />
+ </button>
+ </li>
+ <li v-show="hasChanges">
+ <button
+ v-tooltip
+ data-container="body"
+ data-placement="right"
+ type="button"
+ class="ide-sidebar-link js-ide-commit-mode"
+ :class="{
+ active: currentActivityView === $options.activityBarViews.commit
+ }"
+ @click.prevent="updateActivityBarView($options.activityBarViews.commit)"
+ :title="s__('IDE|Commit')"
+ :aria-label="s__('IDE|Commit')"
+ >
+ <icon
+ name="commit"
+ />
+ </button>
+ </li>
+ </ul>
+ </nav>
+</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
index 45321df191c..6a5790c9dff 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
@@ -1,5 +1,5 @@
<script>
-import { mapState } from 'vuex';
+import { mapActions, mapState } from 'vuex';
import { sprintf, __ } from '~/locale';
import * as consts from '../../stores/modules/commit/constants';
import RadioGroup from './radio_group.vue';
@@ -9,7 +9,7 @@ export default {
RadioGroup,
},
computed: {
- ...mapState(['currentBranchId']),
+ ...mapState(['currentBranchId', 'changedFiles', 'stagedFiles']),
commitToCurrentBranchText() {
return sprintf(
__('Commit to %{branchName} branch'),
@@ -17,6 +17,17 @@ export default {
false,
);
},
+ disableMergeRequestRadio() {
+ return this.changedFiles.length > 0 && this.stagedFiles.length > 0;
+ },
+ },
+ mounted() {
+ if (this.disableMergeRequestRadio) {
+ this.updateCommitAction(consts.COMMIT_TO_CURRENT_BRANCH);
+ }
+ },
+ methods: {
+ ...mapActions('commit', ['updateCommitAction']),
},
commitToCurrentBranch: consts.COMMIT_TO_CURRENT_BRANCH,
commitToNewBranch: consts.COMMIT_TO_NEW_BRANCH,
@@ -44,6 +55,7 @@ export default {
:value="$options.commitToNewBranchMR"
:label="__('Create a new branch and merge request')"
:show-input="true"
+ :disabled="disableMergeRequestRadio"
/>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue b/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue
index 1f6bbca13b5..d0a60d647e5 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue
@@ -1,27 +1,9 @@
<script>
-import { mapActions, mapState, mapGetters } from 'vuex';
-import Icon from '~/vue_shared/components/icon.vue';
-import tooltip from '~/vue_shared/directives/tooltip';
+import { mapState } from 'vuex';
export default {
- components: {
- Icon,
- },
- directives: {
- tooltip,
- },
- props: {
- noChangesStateSvgPath: {
- type: String,
- required: true,
- },
- },
computed: {
- ...mapState(['lastCommitMsg', 'rightPanelCollapsed', 'changedFiles', 'stagedFiles']),
- ...mapGetters(['collapseButtonIcon', 'collapseButtonTooltip']),
- },
- methods: {
- ...mapActions(['toggleRightPanelCollapsed']),
+ ...mapState(['lastCommitMsg', 'noChangesStateSvgPath']),
},
};
</script>
@@ -31,31 +13,8 @@ export default {
v-if="!lastCommitMsg"
class="multi-file-commit-panel-section ide-commit-empty-state js-empty-state"
>
- <header
- class="multi-file-commit-panel-header"
- :class="{
- 'is-collapsed': rightPanelCollapsed,
- }"
- >
- <button
- v-tooltip
- :title="collapseButtonTooltip"
- data-container="body"
- data-placement="left"
- type="button"
- class="btn btn-transparent multi-file-commit-panel-collapse-btn"
- :aria-label="__('Toggle sidebar')"
- @click.stop="toggleRightPanelCollapsed"
- >
- <icon
- :name="collapseButtonIcon"
- :size="18"
- />
- </button>
- </header>
<div
class="ide-commit-empty-state-container"
- v-if="!rightPanelCollapsed"
>
<div class="svg-content svg-80">
<img :src="noChangesStateSvgPath" />
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
new file mode 100644
index 00000000000..4a645c827ad
--- /dev/null
+++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
@@ -0,0 +1,171 @@
+<script>
+import { mapState, mapActions, mapGetters } from 'vuex';
+import { sprintf, __ } from '~/locale';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
+import CommitMessageField from './message_field.vue';
+import Actions from './actions.vue';
+import SuccessMessage from './success_message.vue';
+import { activityBarViews, MAX_WINDOW_HEIGHT_COMPACT, COMMIT_ITEM_PADDING } from '../../constants';
+
+export default {
+ components: {
+ Actions,
+ LoadingButton,
+ CommitMessageField,
+ SuccessMessage,
+ },
+ data() {
+ return {
+ isCompact: true,
+ componentHeight: null,
+ };
+ },
+ computed: {
+ ...mapState(['changedFiles', 'stagedFiles', 'currentActivityView', 'lastCommitMsg']),
+ ...mapState('commit', ['commitMessage', 'submitCommitLoading']),
+ ...mapGetters(['hasChanges']),
+ ...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled']),
+ overviewText() {
+ return sprintf(
+ __(
+ '<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFilesLength} staged</strong> changes',
+ ),
+ {
+ stagedFilesLength: this.stagedFiles.length,
+ changedFilesLength: this.changedFiles.length,
+ },
+ );
+ },
+ },
+ watch: {
+ currentActivityView() {
+ if (this.lastCommitMsg) {
+ this.isCompact = false;
+ } else {
+ this.isCompact = !(
+ this.currentActivityView === activityBarViews.commit &&
+ window.innerHeight >= MAX_WINDOW_HEIGHT_COMPACT
+ );
+ }
+ },
+ lastCommitMsg() {
+ this.isCompact =
+ this.currentActivityView !== activityBarViews.commit && this.lastCommitMsg === '';
+ },
+ },
+ methods: {
+ ...mapActions(['updateActivityBarView']),
+ ...mapActions('commit', ['updateCommitMessage', 'discardDraft', 'commitChanges']),
+ toggleIsSmall() {
+ this.updateActivityBarView(activityBarViews.commit)
+ .then(() => {
+ this.isCompact = !this.isCompact;
+ })
+ .catch(e => {
+ throw e;
+ });
+ },
+ beforeEnterTransition() {
+ const elHeight = this.isCompact
+ ? this.$refs.formEl && this.$refs.formEl.offsetHeight
+ : this.$refs.compactEl && this.$refs.compactEl.offsetHeight;
+
+ this.componentHeight = elHeight + COMMIT_ITEM_PADDING;
+ },
+ enterTransition() {
+ this.$nextTick(() => {
+ const elHeight = this.isCompact
+ ? this.$refs.compactEl && this.$refs.compactEl.offsetHeight
+ : this.$refs.formEl && this.$refs.formEl.offsetHeight;
+
+ this.componentHeight = elHeight + COMMIT_ITEM_PADDING;
+ });
+ },
+ afterEndTransition() {
+ this.componentHeight = null;
+ },
+ },
+ activityBarViews,
+};
+</script>
+
+<template>
+ <div
+ class="multi-file-commit-form"
+ :class="{
+ 'is-compact': isCompact,
+ 'is-full': !isCompact
+ }"
+ :style="{
+ height: componentHeight ? `${componentHeight}px` : null,
+ }"
+ >
+ <transition
+ name="commit-form-slide-up"
+ @before-enter="beforeEnterTransition"
+ @enter="enterTransition"
+ @after-enter="afterEndTransition"
+ >
+ <div
+ v-if="isCompact"
+ class="commit-form-compact"
+ ref="compactEl"
+ >
+ <button
+ type="button"
+ :disabled="!hasChanges"
+ class="btn btn-primary btn-sm btn-block"
+ @click="toggleIsSmall"
+ >
+ {{ __('Commit') }}
+ </button>
+ <p
+ class="text-center"
+ v-html="overviewText"
+ ></p>
+ </div>
+ <form
+ v-if="!isCompact"
+ class="form-horizontal"
+ @submit.prevent.stop="commitChanges"
+ ref="formEl"
+ >
+ <transition name="fade">
+ <success-message
+ v-show="lastCommitMsg"
+ />
+ </transition>
+ <commit-message-field
+ :text="commitMessage"
+ @input="updateCommitMessage"
+ />
+ <div class="clearfix prepend-top-15">
+ <actions />
+ <loading-button
+ :loading="submitCommitLoading"
+ :disabled="commitButtonDisabled"
+ container-class="btn btn-success btn-sm pull-left"
+ :label="__('Commit')"
+ @click="commitChanges"
+ />
+ <button
+ v-if="!discardDraftButtonDisabled"
+ type="button"
+ class="btn btn-default btn-sm pull-right"
+ @click="discardDraft"
+ >
+ {{ __('Discard draft') }}
+ </button>
+ <button
+ v-else
+ type="button"
+ class="btn btn-default btn-sm pull-right"
+ @click="toggleIsSmall"
+ >
+ {{ __('Collapse') }}
+ </button>
+ </div>
+ </form>
+ </transition>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
index ff05ee8682a..c3ac18bfb83 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
@@ -1,16 +1,14 @@
<script>
-import { mapActions, mapState, mapGetters } from 'vuex';
+import { mapActions } from 'vuex';
import { __, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import ListItem from './list_item.vue';
-import ListCollapsed from './list_collapsed.vue';
export default {
components: {
Icon,
ListItem,
- ListCollapsed,
},
directives: {
tooltip,
@@ -24,11 +22,6 @@ export default {
type: Array,
required: true,
},
- showToggle: {
- type: Boolean,
- required: false,
- default: true,
- },
iconName: {
type: String,
required: true,
@@ -51,9 +44,12 @@ export default {
default: false,
},
},
+ data() {
+ return {
+ showActionButton: false,
+ };
+ },
computed: {
- ...mapState(['rightPanelCollapsed']),
- ...mapGetters(['collapseButtonIcon', 'collapseButtonTooltip']),
titleText() {
return sprintf(__('%{title} changes'), {
title: this.title,
@@ -61,10 +57,13 @@ export default {
},
},
methods: {
- ...mapActions(['toggleRightPanelCollapsed', 'stageAllChanges', 'unstageAllChanges']),
+ ...mapActions(['stageAllChanges', 'unstageAllChanges']),
actionBtnClicked() {
this[this.action]();
},
+ setShowActionButton(show) {
+ this.showActionButton = show;
+ },
},
};
</script>
@@ -72,19 +71,14 @@ export default {
<template>
<div
class="ide-commit-list-container"
- :class="{
- 'is-collapsed': rightPanelCollapsed,
- }"
>
<header
class="multi-file-commit-panel-header"
+ @mouseenter="setShowActionButton(true)"
+ @mouseleave="setShowActionButton(false)"
>
<div
- v-if="!rightPanelCollapsed"
class="multi-file-commit-panel-header-title"
- :class="{
- 'append-right-10': showToggle,
- }"
>
<icon
v-once
@@ -92,7 +86,14 @@ export default {
:size="18"
/>
{{ titleText }}
+ <span
+ v-show="!showActionButton"
+ class="ide-commit-file-count"
+ >
+ {{ fileList.length }}
+ </span>
<button
+ v-show="showActionButton"
type="button"
class="btn btn-blank btn-link ide-staged-action-btn"
@click="actionBtnClicked"
@@ -100,52 +101,28 @@ export default {
{{ actionBtnText }}
</button>
</div>
- <button
- v-if="showToggle"
- v-tooltip
- :title="collapseButtonTooltip"
- data-container="body"
- data-placement="left"
- type="button"
- class="btn btn-transparent multi-file-commit-panel-collapse-btn"
- :aria-label="__('Toggle sidebar')"
- @click.stop="toggleRightPanelCollapsed"
- >
- <icon
- :name="collapseButtonIcon"
- :size="18"
- />
- </button>
</header>
- <list-collapsed
- v-if="rightPanelCollapsed"
- :files="fileList"
- :icon-name="iconName"
- :title="title"
- />
- <template v-else>
- <ul
- v-if="fileList.length"
- class="multi-file-commit-list list-unstyled append-bottom-0"
- >
- <li
- v-for="file in fileList"
- :key="file.key"
- >
- <list-item
- :file="file"
- :action-component="itemActionComponent"
- :key-prefix="title"
- :staged-list="stagedList"
- />
- </li>
- </ul>
- <p
- v-else
- class="multi-file-commit-list help-block"
+ <ul
+ v-if="fileList.length"
+ class="multi-file-commit-list list-unstyled append-bottom-0"
+ >
+ <li
+ v-for="file in fileList"
+ :key="file.key"
>
- {{ __('No changes') }}
- </p>
- </template>
+ <list-item
+ :file="file"
+ :action-component="itemActionComponent"
+ :key-prefix="title"
+ :staged-list="stagedList"
+ />
+ </li>
+ </ul>
+ <p
+ v-else
+ class="multi-file-commit-list help-block"
+ >
+ {{ __('No changes') }}
+ </p>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
index 872302840e2..03f3e4de83c 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -3,6 +3,7 @@ import { mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue';
import StageButton from './stage_button.vue';
import UnstageButton from './unstage_button.vue';
+import { viewerTypes } from '../../constants';
export default {
components: {
@@ -53,7 +54,7 @@ export default {
keyPrefix: this.keyPrefix.toLowerCase(),
}).then(changeViewer => {
if (changeViewer) {
- this.updateViewer('diff');
+ this.updateViewer(viewerTypes.diff);
}
});
},
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue
index b660a2961cb..00f2312ae51 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue
@@ -1,5 +1,6 @@
<script>
import { mapActions, mapState, mapGetters } from 'vuex';
+import { __ } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
@@ -26,10 +27,20 @@ export default {
required: false,
default: false,
},
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
...mapState('commit', ['commitAction']),
...mapGetters('commit', ['newBranchName']),
+ tooltipTitle() {
+ return this.disabled
+ ? __('This option is disabled while you still have unstaged changes')
+ : '';
+ },
},
methods: {
...mapActions('commit', ['updateCommitAction', 'updateBranchName']),
@@ -39,19 +50,28 @@ export default {
<template>
<fieldset>
- <label>
+ <label
+ v-tooltip
+ :title="tooltipTitle"
+ :class="{
+ 'is-disabled': disabled
+ }"
+ >
<input
type="radio"
name="commit-action"
:value="value"
@change="updateCommitAction($event.target.value)"
- :checked="checked"
- v-once
+ :checked="commitAction === value"
+ :disabled="disabled"
/>
<span class="prepend-left-10">
- <template v-if="label">
+ <span
+ v-if="label"
+ class="ide-radio-label"
+ >
{{ label }}
- </template>
+ </span>
<slot v-else></slot>
</span>
</label>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue b/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue
index 628a17eddca..a6df91b79c2 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/success_message.vue
@@ -2,14 +2,8 @@
import { mapState } from 'vuex';
export default {
- props: {
- committedStateSvgPath: {
- type: String,
- required: true,
- },
- },
computed: {
- ...mapState(['lastCommitMsg']),
+ ...mapState(['lastCommitMsg', 'committedStateSvgPath']),
},
};
</script>
diff --git a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
index 0c44a755f56..b9af4d27145 100644
--- a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
+++ b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
@@ -1,28 +1,15 @@
<script>
-import Icon from '~/vue_shared/components/icon.vue';
import { __, sprintf } from '~/locale';
+import { viewerTypes } from '../constants';
export default {
- components: {
- Icon,
- },
props: {
- hasChanges: {
- type: Boolean,
- required: false,
- default: false,
- },
- mergeRequestId: {
- type: String,
- required: false,
- default: '',
- },
viewer: {
type: String,
required: true,
},
- showShadow: {
- type: Boolean,
+ mergeRequestId: {
+ type: Number,
required: true,
},
},
@@ -38,84 +25,45 @@ export default {
this.$emit('click', mode);
},
},
+ viewerTypes,
};
</script>
<template>
<div
class="dropdown"
- :class="{
- shadow: showShadow,
- }"
>
<button
type="button"
- class="btn btn-primary btn-sm"
- :class="{
- 'btn-inverted': hasChanges,
- }"
+ class="btn btn-link"
data-toggle="dropdown"
>
- <template v-if="viewer === 'mrdiff' && mergeRequestId">
- {{ mergeReviewLine }}
- </template>
- <template v-else-if="viewer === 'editor'">
- {{ __('Editing') }}
- </template>
- <template v-else>
- {{ __('Reviewing') }}
- </template>
- <icon
- name="angle-down"
- :size="12"
- css-classes="caret-down"
- />
+ {{ __('Edit') }}
</button>
<div class="dropdown-menu dropdown-menu-selectable dropdown-open-left">
<ul>
- <template v-if="mergeRequestId">
- <li>
- <a
- href="#"
- @click.prevent="changeMode('mrdiff')"
- :class="{
- 'is-active': viewer === 'mrdiff',
- }"
- >
- <strong class="dropdown-menu-inner-title">
- {{ mergeReviewLine }}
- </strong>
- <span class="dropdown-menu-inner-content">
- {{ __('Compare changes with the merge request target branch') }}
- </span>
- </a>
- </li>
- <li
- role="separator"
- class="divider"
- >
- </li>
- </template>
<li>
<a
href="#"
- @click.prevent="changeMode('editor')"
+ @click.prevent="changeMode($options.viewerTypes.mr)"
:class="{
- 'is-active': viewer === 'editor',
+ 'is-active': viewer === $options.viewerTypes.mr,
}"
>
- <strong class="dropdown-menu-inner-title">{{ __('Editing') }}</strong>
+ <strong class="dropdown-menu-inner-title">
+ {{ mergeReviewLine }}
+ </strong>
<span class="dropdown-menu-inner-content">
- {{ __('View and edit lines') }}
+ {{ __('Compare changes with the merge request target branch') }}
</span>
</a>
</li>
<li>
<a
href="#"
- @click.prevent="changeMode('diff')"
+ @click.prevent="changeMode($options.viewerTypes.diff)"
:class="{
- 'is-active': viewer === 'diff',
+ 'is-active': viewer === $options.viewerTypes.diff,
}"
>
<strong class="dropdown-menu-inner-title">{{ __('Reviewing') }}</strong>
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index 0274fc7d299..77b35078e56 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -1,82 +1,67 @@
<script>
- import { mapActions, mapState, mapGetters } from 'vuex';
- import Mousetrap from 'mousetrap';
- import ideSidebar from './ide_side_bar.vue';
- import ideContextbar from './ide_context_bar.vue';
- import repoTabs from './repo_tabs.vue';
- import ideStatusBar from './ide_status_bar.vue';
- import repoEditor from './repo_editor.vue';
- import FindFile from './file_finder/index.vue';
+import Mousetrap from 'mousetrap';
+import { mapActions, mapState, mapGetters } from 'vuex';
+import IdeSidebar from './ide_side_bar.vue';
+import RepoTabs from './repo_tabs.vue';
+import IdeStatusBar from './ide_status_bar.vue';
+import RepoEditor from './repo_editor.vue';
+import FindFile from './file_finder/index.vue';
- const originalStopCallback = Mousetrap.stopCallback;
+const originalStopCallback = Mousetrap.stopCallback;
- export default {
- components: {
- ideSidebar,
- ideContextbar,
- repoTabs,
- ideStatusBar,
- repoEditor,
- FindFile,
- },
- props: {
- emptyStateSvgPath: {
- type: String,
- required: true,
- },
- noChangesStateSvgPath: {
- type: String,
- required: true,
- },
- committedStateSvgPath: {
- type: String,
- required: true,
- },
- },
- computed: {
- ...mapState([
- 'changedFiles',
- 'openFiles',
- 'viewer',
- 'currentMergeRequestId',
- 'fileFindVisible',
- ]),
- ...mapGetters(['activeFile', 'hasChanges']),
- },
- mounted() {
- const returnValue = 'Are you sure you want to lose unsaved changes?';
- window.onbeforeunload = e => {
- if (!this.changedFiles.length) return undefined;
+export default {
+ components: {
+ IdeSidebar,
+ RepoTabs,
+ IdeStatusBar,
+ RepoEditor,
+ FindFile,
+ },
+ computed: {
+ ...mapState([
+ 'changedFiles',
+ 'openFiles',
+ 'viewer',
+ 'currentMergeRequestId',
+ 'fileFindVisible',
+ 'emptyStateSvgPath',
+ ]),
+ ...mapGetters(['activeFile', 'hasChanges']),
+ },
+ mounted() {
+ const returnValue = 'Are you sure you want to lose unsaved changes?';
+ window.onbeforeunload = e => {
+ if (!this.changedFiles.length) return undefined;
- Object.assign(e, {
- returnValue,
- });
- return returnValue;
- };
+ Object.assign(e, {
+ returnValue,
+ });
+ return returnValue;
+ };
- Mousetrap.bind(['t', 'command+p', 'ctrl+p'], e => {
- if (e.preventDefault) {
- e.preventDefault();
- }
+ Mousetrap.bind(['t', 'command+p', 'ctrl+p'], e => {
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
- this.toggleFileFinder(!this.fileFindVisible);
- });
+ this.toggleFileFinder(!this.fileFindVisible);
+ });
- Mousetrap.stopCallback = (e, el, combo) => this.mousetrapStopCallback(e, el, combo);
- },
- methods: {
- ...mapActions(['toggleFileFinder']),
- mousetrapStopCallback(e, el, combo) {
- if (combo === 't' && el.classList.contains('dropdown-input-field')) {
- return true;
- } else if (combo === 'command+p' || combo === 'ctrl+p') {
- return false;
- }
+ Mousetrap.stopCallback = (e, el, combo) => this.mousetrapStopCallback(e, el, combo);
+ },
+ methods: {
+ ...mapActions(['toggleFileFinder']),
+ mousetrapStopCallback(e, el, combo) {
+ if (combo === 't' && el.classList.contains('dropdown-input-field')) {
+ return true;
+ } else if (combo === 'command+p' || combo === 'ctrl+p') {
+ return false;
+ }
- return originalStopCallback(e, el, combo);
- },
+ return originalStopCallback(e, el, combo);
},
- };
+ },
+};
</script>
<template>
@@ -136,9 +121,5 @@
</div>
</template>
</div>
- <ide-contextbar
- :no-changes-state-svg-path="noChangesStateSvgPath"
- :committed-state-svg-path="committedStateSvgPath"
- />
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_context_bar.vue b/app/assets/javascripts/ide/components/ide_context_bar.vue
deleted file mode 100644
index 627fbeb9adf..00000000000
--- a/app/assets/javascripts/ide/components/ide_context_bar.vue
+++ /dev/null
@@ -1,42 +0,0 @@
-<script>
-import icon from '~/vue_shared/components/icon.vue';
-import panelResizer from '~/vue_shared/components/panel_resizer.vue';
-import repoCommitSection from './repo_commit_section.vue';
-import ResizablePanel from './resizable_panel.vue';
-
-export default {
- components: {
- repoCommitSection,
- icon,
- panelResizer,
- ResizablePanel,
- },
- props: {
- noChangesStateSvgPath: {
- type: String,
- required: true,
- },
- committedStateSvgPath: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <resizable-panel
- :collapsible="true"
- :initial-width="340"
- side="right"
- >
- <div
- class="multi-file-commit-panel-section"
- >
- <repo-commit-section
- :no-changes-state-svg-path="noChangesStateSvgPath"
- :committed-state-svg-path="committedStateSvgPath"
- />
- </div>
- </resizable-panel>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_external_links.vue b/app/assets/javascripts/ide/components/ide_external_links.vue
deleted file mode 100644
index c6f6e0d2348..00000000000
--- a/app/assets/javascripts/ide/components/ide_external_links.vue
+++ /dev/null
@@ -1,43 +0,0 @@
-<script>
-import icon from '~/vue_shared/components/icon.vue';
-
-export default {
- components: {
- icon,
- },
- props: {
- projectUrl: {
- type: String,
- required: true,
- },
- },
- computed: {
- goBackUrl() {
- return document.referrer || this.projectUrl;
- },
- },
-};
-</script>
-
-<template>
- <nav
- class="ide-external-links"
- v-once
- >
- <p>
- <a
- :href="goBackUrl"
- class="ide-sidebar-link"
- >
- <icon
- :size="16"
- class="append-right-8"
- name="go-back"
- />
- <span class="ide-external-links-text">
- {{ s__('Go back') }}
- </span>
- </a>
- </p>
- </nav>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue b/app/assets/javascripts/ide/components/ide_project_branches_tree.vue
deleted file mode 100644
index eb2749e6151..00000000000
--- a/app/assets/javascripts/ide/components/ide_project_branches_tree.vue
+++ /dev/null
@@ -1,47 +0,0 @@
-<script>
- import icon from '~/vue_shared/components/icon.vue';
- import repoTree from './ide_repo_tree.vue';
- import newDropdown from './new_dropdown/index.vue';
-
- export default {
- components: {
- repoTree,
- icon,
- newDropdown,
- },
- props: {
- projectId: {
- type: String,
- required: true,
- },
- branch: {
- type: Object,
- required: true,
- },
- },
- };
-</script>
-
-<template>
- <div class="branch-container">
- <div class="branch-header">
- <div class="branch-header-title str-truncated ref-name">
- <icon
- name="branch"
- :size="12"
- />
- {{ branch.name }}
- </div>
- <div class="branch-header-btns">
- <new-dropdown
- :project-id="projectId"
- :branch="branch.name"
- path=""
- />
- </div>
- </div>
- <repo-tree
- :tree="branch.tree"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_project_tree.vue b/app/assets/javascripts/ide/components/ide_project_tree.vue
deleted file mode 100644
index a6f40286ac1..00000000000
--- a/app/assets/javascripts/ide/components/ide_project_tree.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<script>
-import ProjectAvatarImage from '~/vue_shared/components/project_avatar/image.vue';
-import Identicon from '../../vue_shared/components/identicon.vue';
-import BranchesTree from './ide_project_branches_tree.vue';
-import ExternalLinks from './ide_external_links.vue';
-
-export default {
- components: {
- BranchesTree,
- ExternalLinks,
- ProjectAvatarImage,
- Identicon,
- },
- props: {
- project: {
- type: Object,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div class="projects-sidebar">
- <div class="context-header">
- <a
- :title="project.name"
- :href="project.web_url"
- >
- <div
- v-if="project.avatar_url"
- class="avatar-container s40 project-avatar"
- >
- <project-avatar-image
- class="avatar-container project-avatar"
- :link-href="project.path"
- :img-src="project.avatar_url"
- :img-alt="project.name"
- :img-size="40"
- />
- </div>
- <identicon
- v-else
- size-class="s40"
- :entity-id="project.id"
- :entity-name="project.name"
- />
- <div class="sidebar-context-title">
- {{ project.name }}
- </div>
- </a>
- </div>
- <external-links
- :project-url="project.web_url"
- />
- <div class="multi-file-commit-panel-inner-scroll">
- <branches-tree
- v-for="branch in project.branches"
- :key="branch.name"
- :project-id="project.path_with_namespace"
- :branch="branch"
- />
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_repo_tree.vue b/app/assets/javascripts/ide/components/ide_repo_tree.vue
deleted file mode 100644
index e6af88e04bc..00000000000
--- a/app/assets/javascripts/ide/components/ide_repo_tree.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-<script>
-import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import RepoFile from './repo_file.vue';
-
-export default {
- components: {
- RepoFile,
- SkeletonLoadingContainer,
- },
- props: {
- tree: {
- type: Object,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div
- class="ide-file-list"
- >
- <template v-if="tree.loading">
- <div
- class="multi-file-loading-container"
- v-for="n in 3"
- :key="n"
- >
- <skeleton-loading-container />
- </div>
- </template>
- <template v-else>
- <repo-file
- v-for="file in tree.tree"
- :key="file.key"
- :file="file"
- :level="0"
- />
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/ide/components/ide_review.vue b/app/assets/javascripts/ide/components/ide_review.vue
new file mode 100644
index 00000000000..0c9ec3b00f0
--- /dev/null
+++ b/app/assets/javascripts/ide/components/ide_review.vue
@@ -0,0 +1,62 @@
+<script>
+import { mapGetters, mapState, mapActions } from 'vuex';
+import IdeTreeList from './ide_tree_list.vue';
+import EditorModeDropdown from './editor_mode_dropdown.vue';
+import { viewerTypes } from '../constants';
+
+export default {
+ components: {
+ IdeTreeList,
+ EditorModeDropdown,
+ },
+ computed: {
+ ...mapGetters(['currentMergeRequest']),
+ ...mapState(['viewer']),
+ showLatestChangesText() {
+ return !this.currentMergeRequest || this.viewer === viewerTypes.diff;
+ },
+ showMergeRequestText() {
+ return this.currentMergeRequest && this.viewer === viewerTypes.mr;
+ },
+ },
+ mounted() {
+ this.$nextTick(() => {
+ this.updateViewer(this.currentMergeRequest ? viewerTypes.mr : viewerTypes.diff);
+ });
+ },
+ methods: {
+ ...mapActions(['updateViewer']),
+ },
+};
+</script>
+
+<template>
+ <ide-tree-list
+ :viewer-type="viewer"
+ header-class="ide-review-header"
+ :disable-action-dropdown="true"
+ >
+ <template
+ slot="header"
+ >
+ <div class="ide-review-button-holder">
+ {{ __('Review') }}
+ <editor-mode-dropdown
+ v-if="currentMergeRequest"
+ :viewer="viewer"
+ :merge-request-id="currentMergeRequest.iid"
+ @click="updateViewer"
+ />
+ </div>
+ <div class="prepend-top-5 ide-review-sub-header">
+ <template v-if="showLatestChangesText">
+ {{ __('Latest changes') }}
+ </template>
+ <template v-else-if="showMergeRequestText">
+ {{ __('Merge request') }}
+ (<a :href="currentMergeRequest.web_url">!{{ currentMergeRequest.iid }}</a>)
+ </template>
+ </div>
+ </template>
+ </ide-tree-list>
+</template>
diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue
index 8cf1ccb4fce..3f980203911 100644
--- a/app/assets/javascripts/ide/components/ide_side_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_side_bar.vue
@@ -1,36 +1,82 @@
<script>
- import { mapState, mapGetters } from 'vuex';
- import icon from '~/vue_shared/components/icon.vue';
- import panelResizer from '~/vue_shared/components/panel_resizer.vue';
- import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
- import projectTree from './ide_project_tree.vue';
- import ResizablePanel from './resizable_panel.vue';
+import { mapState, mapGetters } from 'vuex';
+import ProjectAvatarImage from '~/vue_shared/components/project_avatar/image.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+import tooltip from '~/vue_shared/directives/tooltip';
+import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
+import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import Identicon from '../../vue_shared/components/identicon.vue';
+import IdeTree from './ide_tree.vue';
+import ResizablePanel from './resizable_panel.vue';
+import ActivityBar from './activity_bar.vue';
+import CommitSection from './repo_commit_section.vue';
+import CommitForm from './commit_sidebar/form.vue';
+import IdeReview from './ide_review.vue';
+import SuccessMessage from './commit_sidebar/success_message.vue';
+import { activityBarViews } from '../constants';
- export default {
- components: {
- projectTree,
- icon,
- panelResizer,
- skeletonLoadingContainer,
- ResizablePanel,
+export default {
+ directives: {
+ tooltip,
+ },
+ components: {
+ Icon,
+ PanelResizer,
+ SkeletonLoadingContainer,
+ ResizablePanel,
+ ActivityBar,
+ ProjectAvatarImage,
+ Identicon,
+ CommitSection,
+ IdeTree,
+ CommitForm,
+ IdeReview,
+ SuccessMessage,
+ },
+ data() {
+ return {
+ showTooltip: false,
+ };
+ },
+ computed: {
+ ...mapState([
+ 'loading',
+ 'currentBranchId',
+ 'currentActivityView',
+ 'changedFiles',
+ 'stagedFiles',
+ 'lastCommitMsg',
+ ]),
+ ...mapGetters(['currentProject', 'someUncommitedChanges']),
+ showSuccessMessage() {
+ return (
+ this.currentActivityView === activityBarViews.edit &&
+ (this.lastCommitMsg && !this.someUncommitedChanges)
+ );
},
- computed: {
- ...mapState([
- 'loading',
- ]),
- ...mapGetters([
- 'projectsWithTrees',
- ]),
+ branchTooltipTitle() {
+ return this.showTooltip ? this.currentBranchId : undefined;
},
- };
+ },
+ watch: {
+ currentBranchId() {
+ this.$nextTick(() => {
+ this.showTooltip = this.$refs.branchId.scrollWidth > this.$refs.branchId.offsetWidth;
+ });
+ },
+ },
+};
</script>
<template>
<resizable-panel
:collapsible="false"
- :initial-width="290"
+ :initial-width="340"
side="left"
>
+ <activity-bar
+ v-if="!loading"
+ />
<div class="multi-file-commit-panel-inner">
<template v-if="loading">
<div
@@ -41,11 +87,54 @@
<skeleton-loading-container />
</div>
</template>
- <project-tree
- v-for="project in projectsWithTrees"
- :key="project.id"
- :project="project"
- />
+ <template v-else>
+ <div class="context-header ide-context-header">
+ <a
+ :href="currentProject.web_url"
+ >
+ <div
+ v-if="currentProject.avatar_url"
+ class="avatar-container s40 project-avatar"
+ >
+ <project-avatar-image
+ class="avatar-container project-avatar"
+ :link-href="currentProject.path"
+ :img-src="currentProject.avatar_url"
+ :img-alt="currentProject.name"
+ :img-size="40"
+ />
+ </div>
+ <identicon
+ v-else
+ size-class="s40"
+ :entity-id="currentProject.id"
+ :entity-name="currentProject.name"
+ />
+ <div class="ide-sidebar-project-title">
+ <div class="sidebar-context-title">
+ {{ currentProject.name }}
+ </div>
+ <div
+ class="sidebar-context-title ide-sidebar-branch-title"
+ ref="branchId"
+ v-tooltip
+ :title="branchTooltipTitle"
+ >
+ <icon
+ name="branch"
+ css-classes="append-right-5"
+ />{{ currentBranchId }}
+ </div>
+ </div>
+ </a>
+ </div>
+ <div class="multi-file-commit-panel-inner-scroll">
+ <component
+ :is="currentActivityView"
+ />
+ </div>
+ <commit-form />
+ </template>
</div>
</resizable-panel>
</template>
diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue
new file mode 100644
index 00000000000..8fc4ebe6ca6
--- /dev/null
+++ b/app/assets/javascripts/ide/components/ide_tree.vue
@@ -0,0 +1,42 @@
+<script>
+import { mapState, mapGetters, mapActions } from 'vuex';
+import NewDropdown from './new_dropdown/index.vue';
+import IdeTreeList from './ide_tree_list.vue';
+
+export default {
+ components: {
+ NewDropdown,
+ IdeTreeList,
+ },
+ computed: {
+ ...mapState(['currentBranchId']),
+ ...mapGetters(['currentProject', 'currentTree', 'activeFile']),
+ },
+ mounted() {
+ if (this.activeFile && this.activeFile.pending) {
+ this.$router.push(`/project${this.activeFile.url}`, () => {
+ this.updateViewer('editor');
+ });
+ }
+ },
+ methods: {
+ ...mapActions(['updateViewer']),
+ },
+};
+</script>
+
+<template>
+ <ide-tree-list
+ viewer-type="editor"
+ >
+ <template
+ slot="header"
+ >
+ {{ __('Edit') }}
+ <new-dropdown
+ :project-id="currentProject.name_with_namespace"
+ :branch="currentBranchId"
+ />
+ </template>
+ </ide-tree-list>
+</template>
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
new file mode 100644
index 00000000000..e64a09fcc90
--- /dev/null
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -0,0 +1,76 @@
+<script>
+import { mapActions, mapGetters, mapState } from 'vuex';
+import Icon from '~/vue_shared/components/icon.vue';
+import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+import RepoFile from './repo_file.vue';
+import NewDropdown from './new_dropdown/index.vue';
+
+export default {
+ components: {
+ Icon,
+ RepoFile,
+ SkeletonLoadingContainer,
+ NewDropdown,
+ },
+ props: {
+ viewerType: {
+ type: String,
+ required: true,
+ },
+ headerClass: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ disableActionDropdown: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ ...mapState(['currentBranchId']),
+ ...mapGetters(['currentProject', 'currentTree']),
+ showLoading() {
+ return !this.currentTree || this.currentTree.loading;
+ },
+ },
+ mounted() {
+ this.updateViewer(this.viewerType);
+ },
+ methods: {
+ ...mapActions(['updateViewer']),
+ },
+};
+</script>
+
+<template>
+ <div
+ class="ide-file-list"
+ >
+ <template v-if="showLoading">
+ <div
+ class="multi-file-loading-container"
+ v-for="n in 3"
+ :key="n"
+ >
+ <skeleton-loading-container />
+ </div>
+ </template>
+ <template v-else>
+ <header
+ class="ide-tree-header"
+ :class="headerClass"
+ >
+ <slot name="header"></slot>
+ </header>
+ <repo-file
+ v-for="file in currentTree.tree"
+ :key="file.key"
+ :file="file"
+ :level="0"
+ :disable-action-dropdown="disableActionDropdown"
+ />
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index b1b5c0d4a28..a0ce1c9dac7 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -17,7 +17,8 @@ export default {
},
path: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
},
data() {
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index fa929381744..c5092d8e04d 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -3,13 +3,10 @@ import { mapState, mapActions, mapGetters } from 'vuex';
import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
-import LoadingButton from '~/vue_shared/components/loading_button.vue';
import CommitFilesList from './commit_sidebar/list.vue';
import EmptyState from './commit_sidebar/empty_state.vue';
-import CommitMessageField from './commit_sidebar/message_field.vue';
-import SuccessMessage from './commit_sidebar/success_message.vue';
import * as consts from '../stores/modules/commit/constants';
-import Actions from './commit_sidebar/actions.vue';
+import { activityBarViews } from '../constants';
export default {
components: {
@@ -17,42 +14,50 @@ export default {
Icon,
CommitFilesList,
EmptyState,
- SuccessMessage,
- Actions,
- LoadingButton,
- CommitMessageField,
},
directives: {
tooltip,
},
- props: {
- noChangesStateSvgPath: {
- type: String,
- required: true,
- },
- committedStateSvgPath: {
- type: String,
- required: true,
- },
- },
computed: {
+ ...mapState([
+ 'changedFiles',
+ 'stagedFiles',
+ 'rightPanelCollapsed',
+ 'lastCommitMsg',
+ 'unusedSeal',
+ ]),
+ ...mapState('commit', ['commitMessage', 'submitCommitLoading']),
+ ...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommitedChanges']),
+ ...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled']),
showStageUnstageArea() {
return !!(this.someUncommitedChanges || this.lastCommitMsg || !this.unusedSeal);
},
- someUncommitedChanges() {
- return !!(this.changedFiles.length || this.stagedFiles.length);
+ },
+ watch: {
+ hasChanges() {
+ if (!this.hasChanges) {
+ this.updateActivityBarView(activityBarViews.edit);
+ }
},
- ...mapState(['changedFiles', 'stagedFiles', 'rightPanelCollapsed', 'lastCommitMsg', 'unusedSeal']),
- ...mapState('commit', ['commitMessage', 'submitCommitLoading']),
- ...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled']),
+ },
+ mounted() {
+ if (this.lastOpenedFile) {
+ this.openPendingTab({
+ file: this.lastOpenedFile,
+ })
+ .then(changeViewer => {
+ if (changeViewer) {
+ this.updateViewer('diff');
+ }
+ })
+ .catch(e => {
+ throw e;
+ });
+ }
},
methods: {
- ...mapActions('commit', [
- 'updateCommitMessage',
- 'discardDraft',
- 'commitChanges',
- 'updateCommitAction',
- ]),
+ ...mapActions(['openPendingTab', 'updateViewer', 'updateActivityBarView']),
+ ...mapActions('commit', ['commitChanges', 'updateCommitAction']),
forceCreateNewBranch() {
return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() => this.commitChanges());
},
@@ -80,6 +85,7 @@ export default {
v-if="showStageUnstageArea"
>
<commit-files-list
+ class="is-first"
icon-name="unstaged"
:title="__('Unstaged')"
:file-list="changedFiles"
@@ -94,49 +100,11 @@ export default {
action="unstageAllChanges"
:action-btn-text="__('Unstage all')"
item-action-component="unstage-button"
- :show-toggle="false"
:staged-list="true"
/>
</template>
<empty-state
v-if="unusedSeal"
- :no-changes-state-svg-path="noChangesStateSvgPath"
/>
- <div
- class="multi-file-commit-panel-bottom"
- >
- <form
- class="form-horizontal multi-file-commit-form"
- @submit.prevent.stop="commitChanges"
- v-if="!rightPanelCollapsed"
- >
- <success-message
- v-if="lastCommitMsg && !someUncommitedChanges"
- :committed-state-svg-path="committedStateSvgPath"
- />
- <commit-message-field
- :text="commitMessage"
- @input="updateCommitMessage"
- />
- <div class="clearfix prepend-top-15">
- <actions />
- <loading-button
- :loading="submitCommitLoading"
- :disabled="commitButtonDisabled"
- container-class="btn btn-success btn-sm pull-left"
- :label="__('Commit')"
- @click="commitChanges"
- />
- <button
- v-if="!discardDraftButtonDisabled"
- type="button"
- class="btn btn-default btn-sm pull-right"
- @click="discardDraft"
- >
- {{ __('Discard draft') }}
- </button>
- </div>
- </form>
- </div>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 3a04cdd8e46..ff7e546fb9c 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -3,6 +3,7 @@
import { mapState, mapGetters, mapActions } from 'vuex';
import flash from '~/flash';
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
+import { activityBarViews, viewerTypes } from '../constants';
import monacoLoader from '../monaco_loader';
import Editor from '../lib/editor';
import IdeFileButtons from './ide_file_buttons.vue';
@@ -19,8 +20,14 @@ export default {
},
},
computed: {
- ...mapState(['rightPanelCollapsed', 'viewer', 'delayViewerUpdated', 'panelResizing']),
- ...mapGetters(['currentMergeRequest', 'getStagedFile']),
+ ...mapState(['rightPanelCollapsed', 'viewer', 'panelResizing', 'currentActivityView']),
+ ...mapGetters([
+ 'currentMergeRequest',
+ 'getStagedFile',
+ 'isEditModeActive',
+ 'isCommitModeActive',
+ 'isReviewModeActive',
+ ]),
shouldHideEditor() {
return this.file && this.file.binary && !this.file.content;
},
@@ -40,6 +47,21 @@ export default {
// Compare key to allow for files opened in review mode to be cached differently
if (newVal.key !== this.file.key) {
this.initMonaco();
+
+ if (this.currentActivityView !== activityBarViews.edit) {
+ this.setFileViewMode({
+ file: this.file,
+ viewMode: 'edit',
+ });
+ }
+ }
+ },
+ currentActivityView() {
+ if (this.currentActivityView !== activityBarViews.edit) {
+ this.setFileViewMode({
+ file: this.file,
+ viewMode: 'edit',
+ });
}
},
rightPanelCollapsed() {
@@ -77,7 +99,6 @@ export default {
'setFileViewMode',
'setFileEOL',
'updateViewer',
- 'updateDelayViewerUpdated',
]),
initMonaco() {
if (this.shouldHideEditor) return;
@@ -89,14 +110,6 @@ export default {
baseSha: this.currentMergeRequest ? this.currentMergeRequest.baseCommitSha : '',
})
.then(() => {
- const viewerPromise = this.delayViewerUpdated
- ? this.updateViewer(this.file.pending ? 'diff' : 'editor')
- : Promise.resolve();
-
- return viewerPromise;
- })
- .then(() => {
- this.updateDelayViewerUpdated(false);
this.createEditorInstance();
})
.catch(err => {
@@ -108,10 +121,10 @@ export default {
this.editor.dispose();
this.$nextTick(() => {
- if (this.viewer === 'editor') {
+ if (this.viewer === viewerTypes.edit) {
this.editor.createInstance(this.$refs.editor);
} else {
- this.editor.createDiffInstance(this.$refs.editor);
+ this.editor.createDiffInstance(this.$refs.editor, !this.isReviewModeActive);
}
this.setupEditor();
@@ -127,7 +140,7 @@ export default {
this.file.staged && this.file.key.indexOf('unstaged-') === 0 ? head : null,
);
- if (this.viewer === 'mrdiff') {
+ if (this.viewer === viewerTypes.mr) {
this.editor.attachMergeRequestModel(this.model);
} else {
this.editor.attachModel(this.model);
@@ -168,6 +181,7 @@ export default {
});
},
},
+ viewerTypes,
};
</script>
@@ -176,16 +190,17 @@ export default {
id="ide"
class="blob-viewer-container blob-editor-container"
>
- <div class="ide-mode-tabs clearfix">
+ <div class="ide-mode-tabs clearfix" >
<ul
class="nav-links pull-left"
- v-if="!shouldHideEditor">
+ v-if="!shouldHideEditor && isEditModeActive"
+ >
<li :class="editTabCSS">
<a
href="javascript:void(0);"
role="button"
@click.prevent="setFileViewMode({ file, viewMode: 'edit' })">
- <template v-if="viewer === 'editor'">
+ <template v-if="viewer === $options.viewerTypes.edit">
{{ __('Edit') }}
</template>
<template v-else>
@@ -212,6 +227,9 @@ export default {
v-show="!shouldHideEditor && file.viewMode === 'edit'"
ref="editor"
class="multi-file-editor-holder"
+ :class="{
+ 'is-readonly': isCommitModeActive,
+ }"
>
</div>
<content-viewer
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
index 89c5ce70dd3..14946f8c9fa 100644
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ b/app/assets/javascripts/ide/components/repo_file.vue
@@ -34,6 +34,11 @@ export default {
type: Number,
required: true,
},
+ disableActionDropdown: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
...mapGetters([
@@ -99,16 +104,14 @@ export default {
}
},
methods: {
- ...mapActions(['toggleTreeOpen', 'updateDelayViewerUpdated']),
+ ...mapActions(['toggleTreeOpen']),
clickFile() {
// Manual Action if a tree is selected/opened
if (this.isTree && this.$router.currentRoute.path === `/project${this.file.url}`) {
this.toggleTreeOpen(this.file.path);
}
- return this.updateDelayViewerUpdated(true).then(() => {
- router.push(`/project${this.file.url}`);
- });
+ router.push(`/project${this.file.url}`);
},
},
};
@@ -170,7 +173,7 @@ export default {
/>
</span>
<new-dropdown
- v-if="isTree"
+ v-if="isTree && !disableActionDropdown"
:project-id="file.projectId"
:branch="file.branchId"
:path="file.path"
diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue
index a3ee3184c19..fb26b973236 100644
--- a/app/assets/javascripts/ide/components/repo_tab.vue
+++ b/app/assets/javascripts/ide/components/repo_tab.vue
@@ -32,6 +32,8 @@ export default {
return `Close ${this.tab.name}`;
},
showChangedIcon() {
+ if (this.tab.pending) return true;
+
return this.fileHasChanged ? !this.tabMouseOver : false;
},
fileHasChanged() {
@@ -66,15 +68,32 @@ export default {
<template>
<li
+ :class="{
+ active: tab.active
+ }"
@click="clickFile(tab)"
@mouseover="mouseOverTab"
@mouseout="mouseOutTab"
>
+ <div
+ class="multi-file-tab"
+ :title="tab.url"
+ >
+ <file-icon
+ :file-name="tab.name"
+ :size="16"
+ />
+ {{ tab.name }}
+ <file-status-icon
+ :file="tab"
+ />
+ </div>
<button
type="button"
class="multi-file-tab-close"
@click.stop.prevent="closeFile(tab)"
:aria-label="closeLabel"
+ :disabled="tab.pending"
>
<icon
v-if="!showChangedIcon"
@@ -87,22 +106,5 @@ export default {
:force-modified-icon="true"
/>
</button>
-
- <div
- class="multi-file-tab"
- :class="{
- active: tab.active
- }"
- :title="tab.url"
- >
- <file-icon
- :file-name="tab.name"
- :size="16"
- />
- {{ tab.name }}
- <file-status-icon
- :file="tab"
- />
- </div>
</li>
</template>
diff --git a/app/assets/javascripts/ide/components/repo_tabs.vue b/app/assets/javascripts/ide/components/repo_tabs.vue
index 7bd646ba9b0..99e51097e12 100644
--- a/app/assets/javascripts/ide/components/repo_tabs.vue
+++ b/app/assets/javascripts/ide/components/repo_tabs.vue
@@ -32,16 +32,6 @@ export default {
default: '',
},
},
- data() {
- return {
- showShadow: false,
- };
- },
- updated() {
- if (!this.$refs.tabsScroller) return;
-
- this.showShadow = this.$refs.tabsScroller.scrollWidth > this.$refs.tabsScroller.offsetWidth;
- },
methods: {
...mapActions(['updateViewer', 'removePendingTab']),
openFileViewer(viewer) {
@@ -71,12 +61,5 @@ export default {
:tab="tab"
/>
</ul>
- <editor-mode
- :viewer="viewer"
- :show-shadow="showShadow"
- :has-changes="hasChanges"
- :merge-request-id="mergeRequestId"
- @click="openFileViewer"
- />
</div>
</template>
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index b06da9f95d1..48d4cc43198 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -3,6 +3,22 @@ export const MAX_FILE_FINDER_RESULTS = 40;
export const FILE_FINDER_ROW_HEIGHT = 55;
export const FILE_FINDER_EMPTY_ROW_HEIGHT = 33;
+export const MAX_WINDOW_HEIGHT_COMPACT = 750;
+
+export const COMMIT_ITEM_PADDING = 32;
+
// Commit message textarea
export const MAX_TITLE_LENGTH = 50;
export const MAX_BODY_LENGTH = 72;
+
+export const activityBarViews = {
+ edit: 'ide-tree',
+ commit: 'commit-section',
+ review: 'ide-review',
+};
+
+export const viewerTypes = {
+ mr: 'mrdiff',
+ edit: 'editor',
+ diff: 'diff',
+};
diff --git a/app/assets/javascripts/ide/ide_router.js b/app/assets/javascripts/ide/ide_router.js
index 4a0a303d5a6..adca85dc65b 100644
--- a/app/assets/javascripts/ide/ide_router.js
+++ b/app/assets/javascripts/ide/ide_router.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import VueRouter from 'vue-router';
import flash from '~/flash';
import store from './stores';
+import { activityBarViews } from './constants';
Vue.use(VueRouter);
@@ -63,6 +64,8 @@ router.beforeEach((to, from, next) => {
const fullProjectId = `${to.params.namespace}/${to.params.project}`;
if (to.params.branch) {
+ store.dispatch('setCurrentBranchId', to.params.branch);
+
store.dispatch('getBranchData', {
projectId: fullProjectId,
branchId: to.params.branch,
@@ -99,14 +102,14 @@ router.beforeEach((to, from, next) => {
throw e;
});
} else if (to.params.mrid) {
- store.dispatch('updateViewer', 'mrdiff');
-
store
.dispatch('getMergeRequestData', {
projectId: fullProjectId,
mergeRequestId: to.params.mrid,
})
.then(mr => {
+ store.dispatch('updateActivityBarView', activityBarViews.review);
+
store.dispatch('getBranchData', {
projectId: fullProjectId,
branchId: mr.source_branch,
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
index cbfb3dc54f2..e5c47d09886 100644
--- a/app/assets/javascripts/ide/index.js
+++ b/app/assets/javascripts/ide/index.js
@@ -14,15 +14,16 @@ function initIde(el) {
components: {
ide,
},
- render(createElement) {
- return createElement('ide', {
- props: {
- emptyStateSvgPath: el.dataset.emptyStateSvgPath,
- noChangesStateSvgPath: el.dataset.noChangesStateSvgPath,
- committedStateSvgPath: el.dataset.committedStateSvgPath,
- },
+ created() {
+ this.$store.dispatch('setEmptyStateSvgs', {
+ emptyStateSvgPath: el.dataset.emptyStateSvgPath,
+ noChangesStateSvgPath: el.dataset.noChangesStateSvgPath,
+ committedStateSvgPath: el.dataset.committedStateSvgPath,
});
},
+ render(createElement) {
+ return createElement('ide');
+ },
});
}
diff --git a/app/assets/javascripts/ide/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js
index b65d9c68a0b..9c3bb9cc17d 100644
--- a/app/assets/javascripts/ide/lib/editor.js
+++ b/app/assets/javascripts/ide/lib/editor.js
@@ -61,19 +61,19 @@ export default class Editor {
}
}
- createDiffInstance(domElement) {
+ createDiffInstance(domElement, readOnly = true) {
if (!this.instance) {
clearDomElement(domElement);
this.disposable.add(
(this.instance = this.monaco.editor.createDiffEditor(domElement, {
...defaultEditorOptions,
- readOnly: true,
quickSuggestions: false,
occurrencesHighlight: false,
- renderLineHighlight: 'none',
- hideCursorInOverviewRuler: true,
renderSideBySide: Editor.renderSideBySide(domElement),
+ readOnly,
+ renderLineHighlight: readOnly ? 'all' : 'none',
+ hideCursorInOverviewRuler: !readOnly,
})),
);
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index 7358dd9ef92..1a98b42761e 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -123,6 +123,8 @@ export const scrollToTab = () => {
};
export const stageAllChanges = ({ state, commit }) => {
+ commit(types.SET_LAST_COMMIT_MSG, '');
+
state.changedFiles.forEach(file => commit(types.STAGE_CHANGE, file.path));
};
@@ -138,6 +140,18 @@ export const updateDelayViewerUpdated = ({ commit }, delay) => {
commit(types.UPDATE_DELAY_VIEWER_CHANGE, delay);
};
+export const updateActivityBarView = ({ commit }, view) => {
+ commit(types.UPDATE_ACTIVITY_BAR_VIEW, view);
+};
+
+export const setEmptyStateSvgs = ({ commit }, svgs) => {
+ commit(types.SET_EMPTY_STATE_SVGS, svgs);
+};
+
+export const setCurrentBranchId = ({ commit }, currentBranchId) => {
+ commit(types.SET_CURRENT_BRANCH, currentBranchId);
+};
+
export const updateTempFlagForEntry = ({ commit, dispatch, state }, { file, tempFile }) => {
commit(types.UPDATE_TEMP_FLAG, { path: file.path, tempFile });
diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js
index 861830badee..3ac9b9222ca 100644
--- a/app/assets/javascripts/ide/stores/actions/file.js
+++ b/app/assets/javascripts/ide/stores/actions/file.js
@@ -5,6 +5,7 @@ import service from '../../services';
import * as types from '../mutation_types';
import router from '../../ide_router';
import { setPageTitle } from '../utils';
+import { viewerTypes } from '../../constants';
export const closeFile = ({ commit, state, dispatch }, file) => {
const path = file.path;
@@ -23,13 +24,12 @@ export const closeFile = ({ commit, state, dispatch }, file) => {
const nextFileToOpen = state.openFiles[nextIndexToOpen];
if (nextFileToOpen.pending) {
- dispatch('updateViewer', 'diff');
+ dispatch('updateViewer', viewerTypes.diff);
dispatch('openPendingTab', {
file: nextFileToOpen,
keyPrefix: nextFileToOpen.staged ? 'staged' : 'unstaged',
});
} else {
- dispatch('updateDelayViewerUpdated', true);
router.push(`/project${nextFileToOpen.url}`);
}
} else if (!state.openFiles.length) {
@@ -184,6 +184,7 @@ export const stageChange = ({ commit, state }, path) => {
const stagedFile = state.stagedFiles.find(f => f.path === path);
commit(types.STAGE_CHANGE, path);
+ commit(types.SET_LAST_COMMIT_MSG, '');
if (stagedFile) {
eventHub.$emit(`editor.update.model.new.content.staged-${stagedFile.key}`, stagedFile.content);
@@ -195,9 +196,7 @@ export const unstageChange = ({ commit }, path) => {
};
export const openPendingTab = ({ commit, getters, dispatch, state }, { file, keyPrefix }) => {
- if (getters.activeFile && getters.activeFile === file && state.viewer === 'diff') {
- return false;
- }
+ state.openFiles.forEach(f => eventHub.$emit(`editor.update.model.dispose.${f.key}`));
commit(types.ADD_PENDING_TAB, { file, keyPrefix });
diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js
index 4eb23b2ee0f..971a15a4036 100644
--- a/app/assets/javascripts/ide/stores/actions/project.js
+++ b/app/assets/javascripts/ide/stores/actions/project.js
@@ -55,7 +55,6 @@ export const getBranchData = (
branch: data,
});
commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id });
- commit(types.SET_CURRENT_BRANCH, branchId);
resolve(data);
})
.catch(() => {
diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js
index a93d29fd865..75286ed0cd2 100644
--- a/app/assets/javascripts/ide/stores/getters.js
+++ b/app/assets/javascripts/ide/stores/getters.js
@@ -1,5 +1,5 @@
-import { __ } from '~/locale';
import { getChangesCountForFiles, filePathMatches } from './utils';
+import { activityBarViews } from '../constants';
export const activeFile = state => state.openFiles.find(file => file.active) || null;
@@ -31,15 +31,12 @@ export const currentMergeRequest = state => {
return null;
};
-// eslint-disable-next-line no-confusing-arrow
-export const collapseButtonIcon = state =>
- state.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right';
+export const currentProject = state => state.projects[state.currentProjectId];
-export const hasChanges = state => !!state.changedFiles.length || !!state.stagedFiles.length;
+export const currentTree = state =>
+ state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
-// eslint-disable-next-line no-confusing-arrow
-export const collapseButtonTooltip = state =>
- state.rightPanelCollapsed ? __('Expand sidebar') : __('Collapse sidebar');
+export const hasChanges = state => !!state.changedFiles.length || !!state.stagedFiles.length;
export const hasMergeRequest = state => !!state.currentMergeRequestId;
@@ -59,6 +56,16 @@ export const allBlobs = state =>
export const getChangedFile = state => path => state.changedFiles.find(f => f.path === path);
export const getStagedFile = state => path => state.stagedFiles.find(f => f.path === path);
+export const lastOpenedFile = state =>
+ [...state.changedFiles, ...state.stagedFiles].sort((a, b) => b.lastOpenedAt - a.lastOpenedAt)[0];
+
+export const isEditModeActive = state => state.currentActivityView === activityBarViews.edit;
+export const isCommitModeActive = state => state.currentActivityView === activityBarViews.commit;
+export const isReviewModeActive = state => state.currentActivityView === activityBarViews.review;
+
+export const someUncommitedChanges = state =>
+ !!(state.changedFiles.length || state.stagedFiles.length);
+
export const getChangesInFolder = state => path => {
const changedFilesCount = state.changedFiles.filter(f => filePathMatches(f, path)).length;
const stagedFilesCount = state.stagedFiles.filter(
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index 4fbc97d053e..ab00d12089d 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -8,6 +8,7 @@ import router from '../../../ide_router';
import service from '../../../services';
import * as types from './mutation_types';
import * as consts from './constants';
+import { activityBarViews } from '../../../constants';
import eventHub from '../../../eventhub';
export const updateCommitMessage = ({ commit }, message) => {
@@ -75,7 +76,7 @@ export const checkCommitStatus = ({ rootState }) =>
export const updateFilesAfterCommit = (
{ commit, dispatch, state, rootState, rootGetters },
- { data, branch },
+ { data },
) => {
const selectedProject = rootState.projects[rootState.currentProjectId];
const lastCommit = {
@@ -126,15 +127,9 @@ export const updateFilesAfterCommit = (
changed: !!changedFile,
});
});
-
- if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH && rootGetters.activeFile) {
- router.push(
- `/project/${rootState.currentProjectId}/blob/${branch}/${rootGetters.activeFile.path}`,
- );
- }
};
-export const commitChanges = ({ commit, state, getters, dispatch, rootState }) => {
+export const commitChanges = ({ commit, state, getters, dispatch, rootState, rootGetters }) => {
const newBranch = state.commitAction !== consts.COMMIT_TO_CURRENT_BRANCH;
const payload = createCommitPayload(getters.branchName, newBranch, state, rootState);
const getCommitStatus = newBranch ? Promise.resolve(false) : dispatch('checkCommitStatus');
@@ -187,6 +182,34 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState }) =
commit(rootTypes.SET_LAST_COMMIT_MSG, '', { root: true });
}, 5000);
})
+ .then(() => {
+ if (rootGetters.lastOpenedFile) {
+ dispatch(
+ 'openPendingTab',
+ {
+ file: rootGetters.lastOpenedFile,
+ },
+ { root: true },
+ )
+ .then(changeViewer => {
+ if (changeViewer) {
+ dispatch('updateViewer', 'diff', { root: true });
+ }
+ })
+ .catch(e => {
+ throw e;
+ });
+ } else {
+ dispatch('updateActivityBarView', activityBarViews.edit, { root: true });
+ dispatch('updateViewer', 'editor', { root: true });
+
+ router.push(
+ `/project/${rootState.currentProjectId}/blob/${getters.branchName}/${
+ rootGetters.activeFile.path
+ }`,
+ );
+ }
+ })
.then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH));
})
.catch(err => {
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index 87b39379338..08e22e5ad7e 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -5,6 +5,7 @@ export const SET_LAST_COMMIT_MSG = 'SET_LAST_COMMIT_MSG';
export const SET_LEFT_PANEL_COLLAPSED = 'SET_LEFT_PANEL_COLLAPSED';
export const SET_RIGHT_PANEL_COLLAPSED = 'SET_RIGHT_PANEL_COLLAPSED';
export const SET_RESIZING_STATUS = 'SET_RESIZING_STATUS';
+export const SET_EMPTY_STATE_SVGS = 'SET_EMPTY_STATE_SVGS';
// Project Mutation Types
export const SET_PROJECT = 'SET_PROJECT';
@@ -59,6 +60,7 @@ export const UPDATE_FILE_AFTER_COMMIT = 'UPDATE_FILE_AFTER_COMMIT';
export const ADD_PENDING_TAB = 'ADD_PENDING_TAB';
export const REMOVE_PENDING_TAB = 'REMOVE_PENDING_TAB';
+export const UPDATE_ACTIVITY_BAR_VIEW = 'UPDATE_ACTIVITY_BAR_VIEW';
export const UPDATE_TEMP_FLAG = 'UPDATE_TEMP_FLAG';
export const TOGGLE_FILE_FINDER = 'TOGGLE_FILE_FINDER';
export const BURST_UNUSED_SEAL = 'BURST_UNUSED_SEAL';
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index 539a07116b3..a257e2ef025 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -107,6 +107,21 @@ export default {
delayViewerUpdated,
});
},
+ [types.UPDATE_ACTIVITY_BAR_VIEW](state, currentActivityView) {
+ Object.assign(state, {
+ currentActivityView,
+ });
+ },
+ [types.SET_EMPTY_STATE_SVGS](
+ state,
+ { emptyStateSvgPath, noChangesStateSvgPath, committedStateSvgPath },
+ ) {
+ Object.assign(state, {
+ emptyStateSvgPath,
+ noChangesStateSvgPath,
+ committedStateSvgPath,
+ });
+ },
[types.TOGGLE_FILE_FINDER](state, fileFindVisible) {
Object.assign(state, {
fileFindVisible,
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index c3041c77199..13f123b6630 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-param-reassign */
import * as types from '../mutation_types';
export default {
@@ -169,32 +170,24 @@ export default {
});
},
[types.ADD_PENDING_TAB](state, { file, keyPrefix = 'pending' }) {
- const key = `${keyPrefix}-${file.key}`;
- const pendingTab = state.openFiles.find(f => f.key === key && f.pending);
- let openFiles = state.openFiles.map(f => Object.assign(f, { active: false, opened: false }));
-
- if (!pendingTab) {
- const openFile = openFiles.find(f => f.path === file.path);
-
- openFiles = openFiles.concat(openFile ? null : file).reduce((acc, f) => {
- if (!f) return acc;
-
- if (f.path === file.path) {
- return acc.concat({
- ...f,
- content: file.content,
- active: true,
- pending: true,
- opened: true,
- key,
- });
- }
-
- return acc.concat(f);
- }, []);
- }
-
- Object.assign(state, { openFiles });
+ state.entries[file.path].opened = false;
+ state.entries[file.path].active = false;
+ state.entries[file.path].lastOpenedAt = new Date().getTime();
+ state.openFiles.forEach(f =>
+ Object.assign(f, {
+ opened: false,
+ active: false,
+ }),
+ );
+ state.openFiles = [
+ {
+ ...file,
+ key: `${keyPrefix}-${file.key}`,
+ pending: true,
+ opened: true,
+ active: true,
+ },
+ ];
},
[types.REMOVE_PENDING_TAB](state, file) {
Object.assign(state, {
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index 0976d278559..e7411f16a4f 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -1,3 +1,5 @@
+import { activityBarViews, viewerTypes } from '../constants';
+
export default () => ({
currentProjectId: '',
currentBranchId: '',
@@ -16,8 +18,9 @@ export default () => ({
rightPanelCollapsed: false,
panelResizing: false,
entries: {},
- viewer: 'editor',
+ viewer: viewerTypes.edit,
delayViewerUpdated: false,
+ currentActivityView: activityBarViews.edit,
unusedSeal: true,
fileFindVisible: false,
});