diff options
author | André Luís <me@andr3.net> | 2018-05-07 17:42:56 +0000 |
---|---|---|
committer | Phil Hughes <me@iamphill.com> | 2018-05-07 17:42:56 +0000 |
commit | b7f3d7470b41292aa484cf2dd821ff91086efe96 (patch) | |
tree | 6602c223b5b0100ec678cb295ea29f62570124a2 | |
parent | 68b71df67f18f9b57a613610849b619361df246e (diff) | |
download | gitlab-ce-b7f3d7470b41292aa484cf2dd821ff91086efe96.tar.gz |
Resolve "Clean up bottom status bar Web IDE"
-rw-r--r-- | app/assets/javascripts/ide/components/ide.vue | 98 | ||||
-rw-r--r-- | app/assets/javascripts/ide/components/ide_status_bar.vue | 96 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/actions/project.js | 23 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/getters.js | 6 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/modules/commit/actions.js | 6 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/mutation_types.js | 1 | ||||
-rw-r--r-- | app/assets/javascripts/ide/stores/mutations/branch.js | 5 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/variables.scss | 1 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/repo.scss | 17 | ||||
-rw-r--r-- | changelogs/unreleased/44833-ide-clean-up-status-bar.yml | 5 | ||||
-rw-r--r-- | spec/javascripts/ide/components/ide_status_bar_spec.js | 63 | ||||
-rw-r--r-- | spec/javascripts/ide/stores/actions/project_spec.js | 71 | ||||
-rw-r--r-- | spec/javascripts/ide/stores/getters_spec.js | 20 | ||||
-rw-r--r-- | spec/javascripts/ide/stores/mutations/branch_spec.js | 22 |
14 files changed, 364 insertions, 70 deletions
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue index 77b35078e56..d479b705e35 100644 --- a/app/assets/javascripts/ide/components/ide.vue +++ b/app/assets/javascripts/ide/components/ide.vue @@ -65,61 +65,63 @@ export default { </script> <template> - <div - class="ide-view" - > - <find-file - v-show="fileFindVisible" - /> - <ide-sidebar /> + <article class="ide"> <div - class="multi-file-edit-pane" + class="ide-view" > - <template - v-if="activeFile" - > - <repo-tabs - :active-file="activeFile" - :files="openFiles" - :viewer="viewer" - :has-changes="hasChanges" - :merge-request-id="currentMergeRequestId" - /> - <repo-editor - class="multi-file-edit-pane-content" - :file="activeFile" - /> - <ide-status-bar - :file="activeFile" - /> - </template> - <template - v-else + <find-file + v-show="fileFindVisible" + /> + <ide-sidebar /> + <div + class="multi-file-edit-pane" > - <div - v-once - class="ide-empty-state" + <template + v-if="activeFile" + > + <repo-tabs + :active-file="activeFile" + :files="openFiles" + :viewer="viewer" + :has-changes="hasChanges" + :merge-request-id="currentMergeRequestId" + /> + <repo-editor + class="multi-file-edit-pane-content" + :file="activeFile" + /> + </template> + <template + v-else > - <div class="row js-empty-state"> - <div class="col-xs-12"> - <div class="svg-content svg-250"> - <img :src="emptyStateSvgPath" /> + <div + v-once + class="ide-empty-state" + > + <div class="row js-empty-state"> + <div class="col-xs-12"> + <div class="svg-content svg-250"> + <img :src="emptyStateSvgPath" /> + </div> </div> - </div> - <div class="col-xs-12"> - <div class="text-content text-center"> - <h4> - Welcome to the GitLab IDE - </h4> - <p> - You can select a file in the left sidebar to begin - editing and use the right sidebar to commit your changes. - </p> + <div class="col-xs-12"> + <div class="text-content text-center"> + <h4> + Welcome to the GitLab IDE + </h4> + <p> + You can select a file in the left sidebar to begin + editing and use the right sidebar to commit your changes. + </p> + </div> </div> </div> </div> - </div> - </template> + </template> + </div> </div> - </div> + <ide-status-bar + :file="activeFile" + /> + </article> </template> diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue index c13eeeace3f..70c6d53c3ab 100644 --- a/app/assets/javascripts/ide/components/ide_status_bar.vue +++ b/app/assets/javascripts/ide/components/ide_status_bar.vue @@ -1,11 +1,14 @@ <script> +import { mapGetters } from 'vuex'; import icon from '~/vue_shared/components/icon.vue'; import tooltip from '~/vue_shared/directives/tooltip'; import timeAgoMixin from '~/vue_shared/mixins/timeago'; +import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue'; export default { components: { icon, + userAvatarImage, }, directives: { tooltip, @@ -14,40 +17,93 @@ export default { props: { file: { type: Object, - required: true, + required: false, + default: null, + }, + }, + data() { + return { + lastCommitFormatedAge: null, + }; + }, + computed: { + ...mapGetters(['currentProject', 'lastCommit']), + }, + mounted() { + this.startTimer(); + }, + beforeDestroy() { + if (this.intervalId) { + clearInterval(this.intervalId); + } + }, + methods: { + startTimer() { + this.intervalId = setInterval(() => { + this.commitAgeUpdate(); + }, 1000); + }, + commitAgeUpdate() { + if (this.lastCommit) { + this.lastCommitFormatedAge = this.timeFormated(this.lastCommit.committed_date); + } + }, + getCommitPath(shortSha) { + return `${this.currentProject.web_url}/commit/${shortSha}`; }, }, }; </script> <template> - <div class="ide-status-bar"> - <div> - <div v-if="file.lastCommit && file.lastCommit.id"> - Last commit: - <a - v-tooltip - :title="file.lastCommit.message" - :href="file.lastCommit.url" - > - {{ timeFormated(file.lastCommit.updatedAt) }} by - {{ file.lastCommit.author }} - </a> - </div> + <footer class="ide-status-bar"> + <div + class="ide-status-branch" + v-if="lastCommit && lastCommitFormatedAge" + > + <icon + name="commit" + /> + <a + v-tooltip + class="commit-sha" + :title="lastCommit.message" + :href="getCommitPath(lastCommit.short_id)" + >{{ lastCommit.short_id }}</a> + by + {{ lastCommit.author_name }} + <time + v-tooltip + data-placement="top" + data-container="body" + :datetime="lastCommit.committed_date" + :title="tooltipTitle(lastCommit.committed_date)" + > + {{ lastCommitFormatedAge }} + </time> </div> - <div class="text-right"> + <div + v-if="file" + class="ide-status-file" + > {{ file.name }} </div> - <div class="text-right"> + <div + v-if="file" + class="ide-status-file" + > {{ file.eol }} </div> <div - class="text-right" - v-if="!file.binary"> + class="ide-status-file" + v-if="file && !file.binary"> {{ file.editorRow }}:{{ file.editorColumn }} </div> - <div class="text-right"> + <div + v-if="file" + class="ide-status-file" + > {{ file.fileLanguage }} </div> - </div> + </footer> </template> diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js index 971a15a4036..eff9bc03651 100644 --- a/app/assets/javascripts/ide/stores/actions/project.js +++ b/app/assets/javascripts/ide/stores/actions/project.js @@ -72,3 +72,26 @@ export const getBranchData = ( resolve(state.projects[`${projectId}`].branches[branchId]); } }); + +export const refreshLastCommitData = ( + { commit, state, dispatch }, + { projectId, branchId } = {}, +) => service + .getBranchData(projectId, branchId) + .then(({ data }) => { + commit(types.SET_BRANCH_COMMIT, { + projectId, + branchId, + commit: data.commit, + }); + }) + .catch(() => { + flash( + 'Error loading last commit.', + 'alert', + document, + null, + false, + true, + ); + }); diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js index 75286ed0cd2..b239a605371 100644 --- a/app/assets/javascripts/ide/stores/getters.js +++ b/app/assets/javascripts/ide/stores/getters.js @@ -81,5 +81,11 @@ export const getUnstagedFilesCountForPath = state => path => export const getStagedFilesCountForPath = state => path => getChangesCountForFiles(state.stagedFiles, path); +export const lastCommit = (state, getters) => { + const branch = getters.currentProject && getters.currentProject.branches[state.currentBranchId]; + + return branch ? branch.commit : null; +}; + // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js index ab00d12089d..b85246b2502 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/actions.js +++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js @@ -210,7 +210,11 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo ); } }) - .then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH)); + .then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH)) + .then(() => dispatch('refreshLastCommitData', { + projectId: rootState.currentProjectId, + branchId: rootState.currentBranchId, + }, { root: true })); }) .catch(err => { let errMsg = __('Error committing changes. Please try again.'); diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index 08e22e5ad7e..a3fb3232f1d 100644 --- a/app/assets/javascripts/ide/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -20,6 +20,7 @@ export const SET_MERGE_REQUEST_VERSIONS = 'SET_MERGE_REQUEST_VERSIONS'; // Branch Mutation Types export const SET_BRANCH = 'SET_BRANCH'; +export const SET_BRANCH_COMMIT = 'SET_BRANCH_COMMIT'; export const SET_BRANCH_WORKING_REFERENCE = 'SET_BRANCH_WORKING_REFERENCE'; export const TOGGLE_BRANCH_OPEN = 'TOGGLE_BRANCH_OPEN'; diff --git a/app/assets/javascripts/ide/stores/mutations/branch.js b/app/assets/javascripts/ide/stores/mutations/branch.js index 2972ba5e38e..e09f88878f4 100644 --- a/app/assets/javascripts/ide/stores/mutations/branch.js +++ b/app/assets/javascripts/ide/stores/mutations/branch.js @@ -23,4 +23,9 @@ export default { workingReference: reference, }); }, + [types.SET_BRANCH_COMMIT](state, { projectId, branchId, commit }) { + Object.assign(state.projects[projectId].branches[branchId], { + commit, + }); + }, }; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 8d43bb32175..cbf99e411cd 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -230,6 +230,7 @@ $row-hover: $blue-50; $row-hover-border: $blue-200; $progress-color: #c0392b; $header-height: 40px; +$ide-statusbar-height: 27px; $fixed-layout-width: 1280px; $limited-layout-width: 990px; $limited-layout-width-sm: 790px; diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 7f1f0c1f5f1..00457717f00 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -23,6 +23,7 @@ margin-top: 0; border-top: 1px solid $white-dark; border-bottom: 1px solid $white-dark; + padding-bottom: $ide-statusbar-height; &.is-collapsed { .ide-file-list { @@ -375,7 +376,13 @@ padding: $gl-bar-padding $gl-padding; background: $white-light; display: flex; - justify-content: flex-end; + justify-content: space-between; + height: $ide-statusbar-height; + + position: absolute; + bottom: 0; + left: 0; + width: 100%; > div + div { padding-left: $gl-padding; @@ -386,6 +393,14 @@ } } +.ide-status-file { + text-align: right; + + .ide-status-branch + &, + &:first-child { + margin-left: auto; + } +} // Not great, but this is to deal with our current output .multi-file-preview-holder { height: 100%; diff --git a/changelogs/unreleased/44833-ide-clean-up-status-bar.yml b/changelogs/unreleased/44833-ide-clean-up-status-bar.yml new file mode 100644 index 00000000000..4c827e57195 --- /dev/null +++ b/changelogs/unreleased/44833-ide-clean-up-status-bar.yml @@ -0,0 +1,5 @@ +--- +title: Clean up WebIDE status bar and add useful info +merge_request: +author: +type: changed diff --git a/spec/javascripts/ide/components/ide_status_bar_spec.js b/spec/javascripts/ide/components/ide_status_bar_spec.js new file mode 100644 index 00000000000..770dca9cb0f --- /dev/null +++ b/spec/javascripts/ide/components/ide_status_bar_spec.js @@ -0,0 +1,63 @@ +import Vue from 'vue'; +import store from '~/ide/stores'; +import ideStatusBar from '~/ide/components/ide_status_bar.vue'; +import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { resetStore } from '../helpers'; +import { projectData } from '../mock_data'; + +describe('ideStatusBar', () => { + let vm; + + beforeEach(() => { + const Component = Vue.extend(ideStatusBar); + + store.state.currentProjectId = 'abcproject'; + store.state.projects.abcproject = projectData; + + vm = createComponentWithStore(Component, store).$mount(); + }); + + afterEach(() => { + vm.$destroy(); + + resetStore(vm.$store); + }); + + it('renders the statusbar', () => { + expect(vm.$el.className).toBe('ide-status-bar'); + }); + + describe('mounted', () => { + it('triggers a setInterval', () => { + expect(vm.intervalId).not.toBe(null); + }); + }); + + describe('commitAgeUpdate', () => { + beforeEach(function() { + jasmine.clock().install(); + spyOn(vm, 'commitAgeUpdate').and.callFake(() => {}); + vm.startTimer(); + }); + + afterEach(function() { + jasmine.clock().uninstall(); + }); + + it('gets called every second', () => { + expect(vm.commitAgeUpdate).not.toHaveBeenCalled(); + + jasmine.clock().tick(1100); + expect(vm.commitAgeUpdate.calls.count()).toEqual(1); + + jasmine.clock().tick(1000); + expect(vm.commitAgeUpdate.calls.count()).toEqual(2); + }); + }); + + describe('getCommitPath', () => { + it('returns the path to the commit details', () => { + expect(vm.getCommitPath('abc123de')).toBe('/commit/abc123de'); + }); + }); +}); diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js new file mode 100644 index 00000000000..ebd08d95810 --- /dev/null +++ b/spec/javascripts/ide/stores/actions/project_spec.js @@ -0,0 +1,71 @@ +import { + refreshLastCommitData, +} from '~/ide/stores/actions'; +import store from '~/ide/stores'; +import service from '~/ide/services'; +import { resetStore } from '../../helpers'; +import testAction from '../../../helpers/vuex_action_helper'; + +describe('IDE store project actions', () => { + beforeEach(() => { + store.state.projects.abcproject = {}; + }); + + afterEach(() => { + resetStore(store); + }); + + describe('refreshLastCommitData', () => { + beforeEach(() => { + store.state.currentProjectId = 'abcproject'; + store.state.currentBranchId = 'master'; + store.state.projects.abcproject = { + branches: { + master: { + commit: null, + }, + }, + }; + }); + + it('calls the service', done => { + spyOn(service, 'getBranchData').and.returnValue( + Promise.resolve({ + data: { + commit: { id: '123' }, + }, + }), + ); + + store + .dispatch('refreshLastCommitData', { + projectId: store.state.currentProjectId, + branchId: store.state.currentBranchId, + }) + .then(() => { + expect(service.getBranchData).toHaveBeenCalledWith('abcproject', 'master'); + + done(); + }) + .catch(done.fail); + }); + + it('commits getBranchData', done => { + testAction( + refreshLastCommitData, + {}, + {}, + [{ + type: 'SET_BRANCH_COMMIT', + payload: { + projectId: 'abcproject', + branchId: 'master', + commit: { id: '123' }, + }, + }], // mutations + [], // action + done, + ); + }); + }); +}); diff --git a/spec/javascripts/ide/stores/getters_spec.js b/spec/javascripts/ide/stores/getters_spec.js index 67a848e8edd..4833ba3edfd 100644 --- a/spec/javascripts/ide/stores/getters_spec.js +++ b/spec/javascripts/ide/stores/getters_spec.js @@ -141,4 +141,24 @@ describe('IDE store getters', () => { expect(getters.getChangesInFolder(localState)('test')).toBe(2); }); }); + + describe('lastCommit', () => { + it('returns the last commit of the current branch on the current project', () => { + const commitTitle = 'Example commit title'; + const localGetters = { + currentProject: { + branches: { + 'example-branch': { + commit: { + title: commitTitle, + }, + }, + }, + }, + }; + localState.currentBranchId = 'example-branch'; + + expect(getters.lastCommit(localState, localGetters).title).toBe(commitTitle); + }); + }); }); diff --git a/spec/javascripts/ide/stores/mutations/branch_spec.js b/spec/javascripts/ide/stores/mutations/branch_spec.js index a7167537ef2..29eb859ddaf 100644 --- a/spec/javascripts/ide/stores/mutations/branch_spec.js +++ b/spec/javascripts/ide/stores/mutations/branch_spec.js @@ -15,4 +15,26 @@ describe('Multi-file store branch mutations', () => { expect(localState.currentBranchId).toBe('master'); }); }); + + describe('SET_BRANCH_COMMIT', () => { + it('sets the last commit on current project', () => { + localState.projects = { + Example: { + branches: { + master: {}, + }, + }, + }; + + mutations.SET_BRANCH_COMMIT(localState, { + projectId: 'Example', + branchId: 'master', + commit: { + title: 'Example commit', + }, + }); + + expect(localState.projects.Example.branches.master.commit.title).toBe('Example commit'); + }); + }); }); |