From 07c984d81cd7985d4ab7597cbb21b5f623b438e9 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Thu, 18 May 2017 13:24:34 +0100 Subject: Port fix-realtime-edited-text-for-issues 9-2-stable fix to master. --- .../javascripts/issue_show/components/app.vue | 27 ++++++++++- .../issue_show/components/description.vue | 11 ----- .../javascripts/issue_show/components/edited.vue | 56 ++++++++++++++++++++++ app/assets/javascripts/issue_show/index.js | 10 ++++ app/assets/javascripts/issue_show/stores/index.js | 9 +++- app/assets/stylesheets/framework/mobile.scss | 5 -- app/controllers/projects/issues_controller.rb | 13 +++-- app/helpers/application_helper.rb | 2 +- app/helpers/editable_helper.rb | 13 +++++ app/models/concerns/editable.rb | 7 +++ app/models/concerns/issuable.rb | 1 + app/models/note.rb | 1 + app/models/snippet.rb | 1 + app/views/projects/issues/show.html.haml | 4 +- spec/helpers/editable_helper_spec.rb | 22 +++++++++ spec/javascripts/issue_show/components/app_spec.js | 15 +++++- .../issue_show/components/edited_spec.js | 49 +++++++++++++++++++ spec/javascripts/issue_show/mock_data.js | 12 +++-- spec/models/concerns/editable_spec.rb | 11 +++++ 19 files changed, 241 insertions(+), 28 deletions(-) create mode 100644 app/assets/javascripts/issue_show/components/edited.vue create mode 100644 app/helpers/editable_helper.rb create mode 100644 app/models/concerns/editable.rb create mode 100644 spec/helpers/editable_helper_spec.rb create mode 100644 spec/javascripts/issue_show/components/edited_spec.js create mode 100644 spec/models/concerns/editable_spec.rb diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index 770a0dcd27e..aa2b8961f50 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -5,6 +5,7 @@ import Service from '../services/index'; import Store from '../stores'; import titleComponent from './title.vue'; import descriptionComponent from './description.vue'; +import editedComponent from './edited.vue'; export default { props: { @@ -34,12 +35,30 @@ export default { required: false, default: '', }, + updatedAt: { + type: String, + required: false, + default: '', + }, + updatedByName: { + type: String, + required: false, + default: '', + }, + updatedByPath: { + type: String, + required: false, + default: '', + }, }, data() { const store = new Store({ titleHtml: this.initialTitle, descriptionHtml: this.initialDescriptionHtml, descriptionText: this.initialDescriptionText, + updatedAt: this.updatedAt, + updatedByName: this.updatedByName, + updatedByPath: this.updatedByPath, }); return { @@ -50,6 +69,7 @@ export default { components: { descriptionComponent, titleComponent, + editedComponent, }, created() { const resource = new Service(this.endpoint); @@ -90,7 +110,12 @@ export default { :can-update="canUpdate" :description-html="state.descriptionHtml" :description-text="state.descriptionText" - :updated-at="state.updatedAt" :task-status="state.taskStatus" /> + diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index 4ad3eb7dfd7..88f9fadf3e8 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -16,10 +16,6 @@ type: String, required: true, }, - updatedAt: { - type: String, - required: true, - }, taskStatus: { type: String, required: true, @@ -29,7 +25,6 @@ return { preAnimation: false, pulseAnimation: false, - timeAgoEl: $('.js-issue-edited-ago'), }; }, watch: { @@ -37,12 +32,6 @@ this.animateChange(); this.$nextTick(() => { - const toolTipTime = gl.utils.formatDate(this.updatedAt); - - this.timeAgoEl.attr('datetime', this.updatedAt) - .attr('title', toolTipTime) - .tooltip('fixTitle'); - this.renderGFM(); }); }, diff --git a/app/assets/javascripts/issue_show/components/edited.vue b/app/assets/javascripts/issue_show/components/edited.vue new file mode 100644 index 00000000000..d59e6d11032 --- /dev/null +++ b/app/assets/javascripts/issue_show/components/edited.vue @@ -0,0 +1,56 @@ + + + + diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js index f06e33dee60..5e2480f9499 100644 --- a/app/assets/javascripts/issue_show/index.js +++ b/app/assets/javascripts/issue_show/index.js @@ -12,10 +12,14 @@ document.addEventListener('DOMContentLoaded', () => new Vue({ const issuableTitleElement = issuableElement.querySelector('.title'); const issuableDescriptionElement = issuableElement.querySelector('.wiki'); const issuableDescriptionTextarea = issuableElement.querySelector('.js-task-list-field'); + const { canUpdate, endpoint, issuableRef, + updatedAt, + updatedByName, + updatedByPath, } = issuableElement.dataset; return { @@ -25,6 +29,9 @@ document.addEventListener('DOMContentLoaded', () => new Vue({ initialTitle: issuableTitleElement.innerHTML, initialDescriptionHtml: issuableDescriptionElement ? issuableDescriptionElement.innerHTML : '', initialDescriptionText: issuableDescriptionTextarea ? issuableDescriptionTextarea.textContent : '', + updatedAt, + updatedByName, + updatedByPath, }; }, render(createElement) { @@ -36,6 +43,9 @@ document.addEventListener('DOMContentLoaded', () => new Vue({ initialTitle: this.initialTitle, initialDescriptionHtml: this.initialDescriptionHtml, initialDescriptionText: this.initialDescriptionText, + updatedAt: this.updatedAt, + updatedByName: this.updatedByName, + updatedByPath: this.updatedByPath, }, }); }, diff --git a/app/assets/javascripts/issue_show/stores/index.js b/app/assets/javascripts/issue_show/stores/index.js index 8e89a2b7730..4f6a0bbc59c 100644 --- a/app/assets/javascripts/issue_show/stores/index.js +++ b/app/assets/javascripts/issue_show/stores/index.js @@ -3,6 +3,9 @@ export default class Store { titleHtml, descriptionHtml, descriptionText, + updatedAt, + updatedByName, + updatedByPath, }) { this.state = { titleHtml, @@ -10,7 +13,9 @@ export default class Store { descriptionHtml, descriptionText, taskStatus: '', - updatedAt: '', + updatedAt, + updatedByName, + updatedByPath, }; } @@ -21,5 +26,7 @@ export default class Store { this.state.descriptionText = data.description_text; this.state.taskStatus = data.task_status; this.state.updatedAt = data.updated_at; + this.state.updatedByName = data.updated_by_name; + this.state.updatedByPath = data.updated_by_path; } } diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index 678af978edd..0140dcf19c3 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -112,11 +112,6 @@ } } - .issue-edited-ago, - .note_edited_ago { - display: none; - } - aside:not(.right-sidebar) { display: none; } diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index cbef8fa94d4..106c990f748 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -202,14 +202,21 @@ class Projects::IssuesController < Projects::ApplicationController def realtime_changes Gitlab::PollingInterval.set_header(response, interval: 3_000) - render json: { + response = { title: view_context.markdown_field(@issue, :title), title_text: @issue.title, description: view_context.markdown_field(@issue, :description), description_text: @issue.description, - task_status: @issue.task_status, - updated_at: @issue.updated_at + task_status: @issue.task_status } + + if @issue.is_edited? + response[:updated_at] = @issue.updated_at + response[:updated_by_name] = @issue.last_edited_by.name + response[:updated_by_path] = user_path(@issue.last_edited_by) + end + + render json: response end def create_merge_request diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e5e64650708..e87d8dc21cc 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -181,7 +181,7 @@ module ApplicationHelper end def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', exclude_author: false) - return if object.last_edited_at == object.created_at || object.last_edited_at.blank? + return unless object.is_edited? content_tag :small, class: 'edited-text' do output = content_tag(:span, 'Edited ') diff --git a/app/helpers/editable_helper.rb b/app/helpers/editable_helper.rb new file mode 100644 index 00000000000..e3ad62e8c18 --- /dev/null +++ b/app/helpers/editable_helper.rb @@ -0,0 +1,13 @@ +module EditableHelper + def updated_at_by(editable) + return nil unless editable.is_edited? + + { + updated_at: editable.updated_at, + updated_by: { + name: editable.last_edited_by.name, + path: user_path(editable.last_edited_by) + } + } + end +end diff --git a/app/models/concerns/editable.rb b/app/models/concerns/editable.rb new file mode 100644 index 00000000000..c62c7e1e936 --- /dev/null +++ b/app/models/concerns/editable.rb @@ -0,0 +1,7 @@ +module Editable + extend ActiveSupport::Concern + + def is_edited? + last_edited_at.present? && last_edited_at != created_at + end +end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 075ec575f9d..ea10d004c9c 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -15,6 +15,7 @@ module Issuable include Taskable include TimeTrackable include Importable + include Editable # This object is used to gather issuable meta data for displaying # upvotes, downvotes, notes and closing merge requests count for issues and merge requests diff --git a/app/models/note.rb b/app/models/note.rb index 60257aac93b..9320bbec314 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -13,6 +13,7 @@ class Note < ActiveRecord::Base include AfterCommitQueue include ResolvableNote include IgnorableColumn + include Editable ignore_column :original_discussion_id diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 882e2fa0594..6c3358685fe 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -8,6 +8,7 @@ class Snippet < ActiveRecord::Base include Awardable include Mentionable include Spammable + include Editable cache_markdown_field :title, pipeline: :single_line cache_markdown_field :content diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 67403c36d7f..8227d977da9 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -58,14 +58,14 @@ #js-issuable-app{ "data" => { "endpoint" => realtime_changes_namespace_project_issue_path(@project.namespace, @project, @issue), "can-update" => can?(current_user, :update_issue, @issue).to_s, "issuable-ref" => @issue.to_reference, - } } + }.merge(updated_at_by(@issue)) } %h2.title= markdown_field(@issue, :title) - if @issue.description.present? .description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' } .wiki= markdown_field(@issue, :description) %textarea.hidden.js-task-list-field= @issue.description - = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue-edited-ago js-issue-edited-ago') + = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue-edited-ago js-issue-edited-ago') #merge-requests{ data: { url: referenced_merge_requests_namespace_project_issue_url(@project.namespace, @project, @issue) } } // This element is filled in using JavaScript. diff --git a/spec/helpers/editable_helper_spec.rb b/spec/helpers/editable_helper_spec.rb new file mode 100644 index 00000000000..81d2c33ccca --- /dev/null +++ b/spec/helpers/editable_helper_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe EditableHelper do + describe '#updated_at_by' do + let(:user) { create(:user) } + let(:unedited_editable) { create(:issue) } + let(:edited_editable) { create(:issue, last_edited_by: user, created_at: 3.days.ago, updated_at: 2.days.ago, last_edited_at: 2.days.ago) } + let(:edited_updated_at_by) do + { + updated_at: edited_editable.updated_at, + updated_by: { + name: user.name, + path: user_path(user) + } + } + end + + it { expect(helper.updated_at_by(unedited_editable)).to eq(nil) } + + it { expect(helper.updated_at_by(edited_editable)).to eq(edited_updated_at_by) } + end +end diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index ee456869c53..28c0ecc10a4 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -13,6 +13,10 @@ const issueShowInterceptor = data => (request, next) => { })); }; +function formatText(text) { + return text.trim().replace(/\s\s+/g, ' '); +} + describe('Issuable output', () => { document.body.innerHTML = ''; @@ -38,12 +42,17 @@ describe('Issuable output', () => { Vue.http.interceptors = _.without(Vue.http.interceptors, issueShowInterceptor); }); - it('should render a title/description and update title/description on update', (done) => { + it('should render a title/description/edited and update title/description/edited on update', (done) => { setTimeout(() => { + const editedText = vm.$el.querySelector('.edited-text'); + expect(document.querySelector('title').innerText).toContain('this is a title (#1)'); expect(vm.$el.querySelector('.title').innerHTML).toContain('

this is a title

'); expect(vm.$el.querySelector('.wiki').innerHTML).toContain('

this is a description!

'); expect(vm.$el.querySelector('.js-task-list-field').value).toContain('this is a description'); + expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/); + expect(editedText.querySelector('.author_link').href).toMatch(/\/some_user$/); + expect(editedText.querySelector('time')).toBeTruthy(); Vue.http.interceptors.push(issueShowInterceptor(issueShowData.secondRequest)); @@ -52,6 +61,10 @@ describe('Issuable output', () => { expect(vm.$el.querySelector('.title').innerHTML).toContain('

2

'); expect(vm.$el.querySelector('.wiki').innerHTML).toContain('

42

'); expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42'); + expect(vm.$el.querySelector('.edited-text')).toBeTruthy(); + expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(/Edited[\s\S]+?by Other User/); + expect(editedText.querySelector('.author_link').href).toMatch(/\/other_user$/); + expect(editedText.querySelector('time')).toBeTruthy(); done(); }); diff --git a/spec/javascripts/issue_show/components/edited_spec.js b/spec/javascripts/issue_show/components/edited_spec.js new file mode 100644 index 00000000000..a0d0750ae34 --- /dev/null +++ b/spec/javascripts/issue_show/components/edited_spec.js @@ -0,0 +1,49 @@ +import Vue from 'vue'; +import edited from '~/issue_show/components/edited.vue'; + +function formatText(text) { + return text.trim().replace(/\s\s+/g, ' '); +} + +describe('edited', () => { + const EditedComponent = Vue.extend(edited); + + it('should render an edited at+by string', () => { + const editedComponent = new EditedComponent({ + propsData: { + updatedAt: '2017-05-15T12:31:04.428Z', + updatedByName: 'Some User', + updatedByPath: '/some_user', + }, + }).$mount(); + + expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited[\s\S]+?by Some User/); + expect(editedComponent.$el.querySelector('.author_link').href).toMatch(/\/some_user$/); + expect(editedComponent.$el.querySelector('time')).toBeTruthy(); + }); + + it('if no updatedAt is provided, no time element will be rendered', () => { + const editedComponent = new EditedComponent({ + propsData: { + updatedByName: 'Some User', + updatedByPath: '/some_user', + }, + }).$mount(); + + expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited by Some User/); + expect(editedComponent.$el.querySelector('.author_link').href).toMatch(/\/some_user$/); + expect(editedComponent.$el.querySelector('time')).toBeFalsy(); + }); + + it('if no updatedByName and updatedByPath is provided, no user element will be rendered', () => { + const editedComponent = new EditedComponent({ + propsData: { + updatedAt: '2017-05-15T12:31:04.428Z', + }, + }).$mount(); + + expect(formatText(editedComponent.$el.innerText)).not.toMatch(/by Some User/); + expect(editedComponent.$el.querySelector('.author_link')).toBeFalsy(); + expect(editedComponent.$el.querySelector('time')).toBeTruthy(); + }); +}); diff --git a/spec/javascripts/issue_show/mock_data.js b/spec/javascripts/issue_show/mock_data.js index 6683d581bc5..eb3111412a7 100644 --- a/spec/javascripts/issue_show/mock_data.js +++ b/spec/javascripts/issue_show/mock_data.js @@ -5,7 +5,9 @@ export default { description: '

this is a description!

', description_text: 'this is a description', task_status: '2 of 4 completed', - updated_at: new Date().toString(), + updated_at: '2015-05-15T12:31:04.428Z', + updated_by_name: 'Some User', + updated_by_path: '/some_user', }, secondRequest: { title: '

2

', @@ -13,7 +15,9 @@ export default { description: '

42

', description_text: '42', task_status: '0 of 0 completed', - updated_at: new Date().toString(), + updated_at: '2016-05-15T12:31:04.428Z', + updated_by_name: 'Other User', + updated_by_path: '/other_user', }, issueSpecRequest: { title: '

this is a title

', @@ -21,6 +25,8 @@ export default { description: '
  • Task List Item
  • ', description_text: '- [ ] Task List Item', task_status: '0 of 1 completed', - updated_at: new Date().toString(), + updated_at: '2017-05-15T12:31:04.428Z', + updated_by_name: 'Last User', + updated_by_path: '/last_user', }, }; diff --git a/spec/models/concerns/editable_spec.rb b/spec/models/concerns/editable_spec.rb new file mode 100644 index 00000000000..cd73af3b480 --- /dev/null +++ b/spec/models/concerns/editable_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe Editable do + describe '#is_edited?' do + let(:issue) { create(:issue, last_edited_at: nil) } + let(:edited_issue) { create(:issue, created_at: 3.days.ago, last_edited_at: 2.days.ago) } + + it { expect(issue.is_edited?).to eq(false) } + it { expect(edited_issue.is_edited?).to eq(true) } + end +end -- cgit v1.2.1 From ab0374f4d6e2bb72ee3040b9b906c2be23519cd7 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Thu, 18 May 2017 16:37:57 +0100 Subject: Review changes, improve editable helper spec and add hasUpdated computer to issue_show app Fix builds by only merge when updated_at_by is presnse Fix issue_show app.vue hasUpdated reference to state Fix missing bracket --- app/assets/javascripts/issue_show/components/app.vue | 7 ++++++- app/views/projects/issues/show.html.haml | 8 ++++---- spec/helpers/editable_helper_spec.rb | 3 +-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index aa2b8961f50..638708da100 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -71,6 +71,11 @@ export default { titleComponent, editedComponent, }, + computed: { + hasUpdated() { + return !!this.state.updatedAt; + }, + }, created() { const resource = new Service(this.endpoint); const poll = new Poll({ @@ -112,7 +117,7 @@ export default { :description-text="state.descriptionText" :task-status="state.taskStatus" /> { "endpoint" => realtime_changes_namespace_project_issue_path(@project.namespace, @project, @issue), - "can-update" => can?(current_user, :update_issue, @issue).to_s, - "issuable-ref" => @issue.to_reference, - }.merge(updated_at_by(@issue)) } + - issuable_app_data = { "endpoint" => realtime_changes_namespace_project_issue_path(@project.namespace, @project, @issue), "can-update" => can?(current_user, :update_issue, @issue).to_s, "issuable-ref" => @issue.to_reference } + - updated_at_by = updated_at_by(@issue) + - issuable_app_data.merge(updated_at_by) if updated_at_by.present? + #js-issuable-app{ data: issuable_app_data } %h2.title= markdown_field(@issue, :title) - if @issue.description.present? .description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' } diff --git a/spec/helpers/editable_helper_spec.rb b/spec/helpers/editable_helper_spec.rb index 81d2c33ccca..dc72819c1cc 100644 --- a/spec/helpers/editable_helper_spec.rb +++ b/spec/helpers/editable_helper_spec.rb @@ -15,8 +15,7 @@ describe EditableHelper do } end - it { expect(helper.updated_at_by(unedited_editable)).to eq(nil) } - + it { expect(helper.updated_at_by(unedited_editable)).to be_nil } it { expect(helper.updated_at_by(edited_editable)).to eq(edited_updated_at_by) } end end -- cgit v1.2.1 From d537a9a45b23bac1aaff028302aae515d6afe949 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Mon, 22 May 2017 11:38:11 +0100 Subject: Move issuable_app_data to helper --- app/helpers/issuables_helper.rb | 8 ++++++++ app/views/projects/issues/show.html.haml | 5 +---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 9290e4ec133..39982eb2e2c 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -1,5 +1,6 @@ module IssuablesHelper include GitlabRoutingHelper + include EditableHelper def sidebar_gutter_toggle_icon sidebar_gutter_collapsed? ? icon('angle-double-left', { 'aria-hidden': 'true' }) : icon('angle-double-right', { 'aria-hidden': 'true' }) @@ -273,4 +274,11 @@ module IssuablesHelper container: (is_collapsed ? 'body' : nil) } end + + def issuable_app_data(project, issue) + data = { "endpoint" => realtime_changes_namespace_project_issue_path(project.namespace, project, issue), "can-update" => can?(current_user, :update_issue, issue).to_s, "issuable-ref" => issue.to_reference } + updated_at_by = updated_at_by(issue) + + data.merge(updated_at_by) if updated_at_by.present? + end end diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index f81a68744c0..4ce1f56da7d 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -55,10 +55,7 @@ .issue-details.issuable-details .detail-page-description.content-block - - issuable_app_data = { "endpoint" => realtime_changes_namespace_project_issue_path(@project.namespace, @project, @issue), "can-update" => can?(current_user, :update_issue, @issue).to_s, "issuable-ref" => @issue.to_reference } - - updated_at_by = updated_at_by(@issue) - - issuable_app_data.merge(updated_at_by) if updated_at_by.present? - #js-issuable-app{ data: issuable_app_data } + #js-issuable-app{ data: issuable_app_data(@project, @issue) } %h2.title= markdown_field(@issue, :title) - if @issue.description.present? .description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' } -- cgit v1.2.1 From aa1d87bd81df0c4020f2cee7788d1f8ea1635838 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Mon, 22 May 2017 16:29:49 +0100 Subject: Fix helper methods returning wrong data types for vue --- app/helpers/editable_helper.rb | 2 +- app/helpers/issuables_helper.rb | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/helpers/editable_helper.rb b/app/helpers/editable_helper.rb index e3ad62e8c18..166963f993d 100644 --- a/app/helpers/editable_helper.rb +++ b/app/helpers/editable_helper.rb @@ -1,6 +1,6 @@ module EditableHelper def updated_at_by(editable) - return nil unless editable.is_edited? + return {} unless editable.is_edited? { updated_at: editable.updated_at, diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 39982eb2e2c..f995c86c059 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -276,9 +276,13 @@ module IssuablesHelper end def issuable_app_data(project, issue) - data = { "endpoint" => realtime_changes_namespace_project_issue_path(project.namespace, project, issue), "can-update" => can?(current_user, :update_issue, issue).to_s, "issuable-ref" => issue.to_reference } + data = { + endpoint: realtime_changes_namespace_project_issue_path(project.namespace, project, issue), + 'can-update' => can?(current_user, :update_issue, issue).to_s, + 'issuable-ref' => issue.to_reference || '' + } updated_at_by = updated_at_by(issue) - data.merge(updated_at_by) if updated_at_by.present? + data.merge(updated_at_by) end end -- cgit v1.2.1 From 33d66a47f6da19831889d8717ec9f11b8b5c6b6c Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Wed, 24 May 2017 11:53:28 +0100 Subject: Correct editable_helper spec and format issuable_app_data updated_at to iso8061 --- app/helpers/editable_helper.rb | 2 +- spec/helpers/editable_helper_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/helpers/editable_helper.rb b/app/helpers/editable_helper.rb index 166963f993d..ddb2fd22a35 100644 --- a/app/helpers/editable_helper.rb +++ b/app/helpers/editable_helper.rb @@ -3,7 +3,7 @@ module EditableHelper return {} unless editable.is_edited? { - updated_at: editable.updated_at, + updated_at: editable.updated_at.to_time.iso8601, updated_by: { name: editable.last_edited_by.name, path: user_path(editable.last_edited_by) diff --git a/spec/helpers/editable_helper_spec.rb b/spec/helpers/editable_helper_spec.rb index dc72819c1cc..fe7a3bccd4b 100644 --- a/spec/helpers/editable_helper_spec.rb +++ b/spec/helpers/editable_helper_spec.rb @@ -7,7 +7,7 @@ describe EditableHelper do let(:edited_editable) { create(:issue, last_edited_by: user, created_at: 3.days.ago, updated_at: 2.days.ago, last_edited_at: 2.days.ago) } let(:edited_updated_at_by) do { - updated_at: edited_editable.updated_at, + updated_at: edited_editable.updated_at.to_time.iso8601, updated_by: { name: user.name, path: user_path(user) @@ -15,7 +15,7 @@ describe EditableHelper do } end - it { expect(helper.updated_at_by(unedited_editable)).to be_nil } + it { expect(helper.updated_at_by(unedited_editable)).to eq({}) } it { expect(helper.updated_at_by(edited_editable)).to eq(edited_updated_at_by) } end end -- cgit v1.2.1 From 632d0c0a4c6ed8dd099f67a6914868811490e6a4 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Wed, 31 May 2017 11:10:33 +0100 Subject: Remove EditableHelper and move method to IssuablesHelper --- app/helpers/editable_helper.rb | 13 ------------- app/helpers/issuables_helper.rb | 13 ++++++++++++- spec/helpers/editable_helper_spec.rb | 21 --------------------- spec/helpers/issuables_helper_spec.rb | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+), 35 deletions(-) delete mode 100644 app/helpers/editable_helper.rb delete mode 100644 spec/helpers/editable_helper_spec.rb diff --git a/app/helpers/editable_helper.rb b/app/helpers/editable_helper.rb deleted file mode 100644 index ddb2fd22a35..00000000000 --- a/app/helpers/editable_helper.rb +++ /dev/null @@ -1,13 +0,0 @@ -module EditableHelper - def updated_at_by(editable) - return {} unless editable.is_edited? - - { - updated_at: editable.updated_at.to_time.iso8601, - updated_by: { - name: editable.last_edited_by.name, - path: user_path(editable.last_edited_by) - } - } - end -end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index f995c86c059..afe19f017d7 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -1,6 +1,5 @@ module IssuablesHelper include GitlabRoutingHelper - include EditableHelper def sidebar_gutter_toggle_icon sidebar_gutter_collapsed? ? icon('angle-double-left', { 'aria-hidden': 'true' }) : icon('angle-double-right', { 'aria-hidden': 'true' }) @@ -285,4 +284,16 @@ module IssuablesHelper data.merge(updated_at_by) end + + def updated_at_by(issuable) + return {} unless issuable.is_edited? + + { + updated_at: issuable.updated_at.to_time.iso8601, + updated_by: { + name: issuable.last_edited_by.name, + path: user_path(issuable.last_edited_by) + } + } + end end diff --git a/spec/helpers/editable_helper_spec.rb b/spec/helpers/editable_helper_spec.rb deleted file mode 100644 index fe7a3bccd4b..00000000000 --- a/spec/helpers/editable_helper_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'spec_helper' - -describe EditableHelper do - describe '#updated_at_by' do - let(:user) { create(:user) } - let(:unedited_editable) { create(:issue) } - let(:edited_editable) { create(:issue, last_edited_by: user, created_at: 3.days.ago, updated_at: 2.days.ago, last_edited_at: 2.days.ago) } - let(:edited_updated_at_by) do - { - updated_at: edited_editable.updated_at.to_time.iso8601, - updated_by: { - name: user.name, - path: user_path(user) - } - } - end - - it { expect(helper.updated_at_by(unedited_editable)).to eq({}) } - it { expect(helper.updated_at_by(edited_editable)).to eq(edited_updated_at_by) } - end -end diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index c1ecb46aece..e55e489b02f 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -192,4 +192,22 @@ describe IssuablesHelper do expect(helper.issuable_filter_present?).to be_falsey end end + + describe '#updated_at_by' do + let(:user) { create(:user) } + let(:unedited_issuable) { create(:issue) } + let(:edited_issuable) { create(:issue, last_edited_by: user, created_at: 3.days.ago, updated_at: 2.days.ago, last_edited_at: 2.days.ago) } + let(:edited_updated_at_by) do + { + updated_at: edited_issuable.updated_at.to_time.iso8601, + updated_by: { + name: user.name, + path: user_path(user) + } + } + end + + it { expect(helper.updated_at_by(unedited_issuable)).to eq({}) } + it { expect(helper.updated_at_by(edited_issuable)).to eq(edited_updated_at_by) } + end end -- cgit v1.2.1 From b888ed59abd3a6cc6a1c14278c38fda472f5b90c Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Wed, 31 May 2017 14:03:32 +0100 Subject: Fixed issuables_helper_spec and added a test for issuable_app_data --- app/helpers/issuables_helper.rb | 46 +++++++++++++++++------------------ spec/helpers/issuables_helper_spec.rb | 26 ++++++++++++++++++++ 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index afe19f017d7..a4197d34db3 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -199,6 +199,29 @@ module IssuablesHelper issuable_filter_params.any? { |k| params.key?(k) } end + def issuable_app_data(project, issue) + data = { + endpoint: realtime_changes_namespace_project_issue_path(project.namespace, project, issue), + 'can-update' => can?(current_user, :update_issue, issue).to_s, + 'issuable-ref' => issue.to_reference || '' + } + updated_at_by = updated_at_by(issue) + + data.merge(updated_at_by) + end + + def updated_at_by(issuable) + return {} unless issuable.is_edited? + + { + updated_at: issuable.updated_at.to_time.iso8601, + updated_by: { + name: issuable.last_edited_by.name, + path: user_path(issuable.last_edited_by) + } + } + end + private def sidebar_gutter_collapsed? @@ -273,27 +296,4 @@ module IssuablesHelper container: (is_collapsed ? 'body' : nil) } end - - def issuable_app_data(project, issue) - data = { - endpoint: realtime_changes_namespace_project_issue_path(project.namespace, project, issue), - 'can-update' => can?(current_user, :update_issue, issue).to_s, - 'issuable-ref' => issue.to_reference || '' - } - updated_at_by = updated_at_by(issue) - - data.merge(updated_at_by) - end - - def updated_at_by(issuable) - return {} unless issuable.is_edited? - - { - updated_at: issuable.updated_at.to_time.iso8601, - updated_by: { - name: issuable.last_edited_by.name, - path: user_path(issuable.last_edited_by) - } - } - end end diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index e55e489b02f..b5dd0f21cbd 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -193,6 +193,32 @@ describe IssuablesHelper do end end + describe '#issuable_app_data' do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:issue) { create(:issue, project: project, last_edited_by: user, created_at: 3.days.ago, updated_at: 2.days.ago, last_edited_at: 2.days.ago) } + + let(:issue_app_data) do + { + endpoint: realtime_changes_namespace_project_issue_path(project.namespace, project, issue), + 'can-update' => "true", + 'issuable-ref' => issue.to_reference || '', + updated_at: issue.updated_at.to_time.iso8601, + updated_by: { + name: user.name, + path: user_path(user) + } + } + end + + before do + allow(helper).to receive(:current_user).and_return(nil) + allow(helper).to receive(:can?).with(nil, :update_issue, issue).and_return(true) + end + + it { expect(helper.issuable_app_data(issue.project, issue)).to eq(issue_app_data) } + end + describe '#updated_at_by' do let(:user) { create(:user) } let(:unedited_issuable) { create(:issue) } -- cgit v1.2.1 From e591401b0b3f08baf4cd28d0fcb8e184a515fc74 Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Fri, 2 Jun 2017 10:27:53 +0100 Subject: Removed unneeded newline in issuables_helper_spec and removed unneeded updated_at_by variable in issuable_app_data --- app/helpers/issuables_helper.rb | 3 +-- spec/helpers/issuables_helper_spec.rb | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index a4197d34db3..e9389178e68 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -205,9 +205,8 @@ module IssuablesHelper 'can-update' => can?(current_user, :update_issue, issue).to_s, 'issuable-ref' => issue.to_reference || '' } - updated_at_by = updated_at_by(issue) - data.merge(updated_at_by) + data.merge(updated_at_by(issue)) end def updated_at_by(issuable) diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index b5dd0f21cbd..728fdfc85f2 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -197,7 +197,6 @@ describe IssuablesHelper do let(:user) { create(:user) } let(:project) { create(:project) } let(:issue) { create(:issue, project: project, last_edited_by: user, created_at: 3.days.ago, updated_at: 2.days.ago, last_edited_at: 2.days.ago) } - let(:issue_app_data) do { endpoint: realtime_changes_namespace_project_issue_path(project.namespace, project, issue), -- cgit v1.2.1 From b2d577a7a293ac6c82a8bc64f5b134558460df5b Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Fri, 2 Jun 2017 17:30:58 +0100 Subject: Fix up merge issues --- app/assets/javascripts/issue_show/components/app.vue | 3 +++ app/helpers/issuables_helper.rb | 4 +++- db/schema.rb | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index 75d42b1eb1b..e14414d3f68 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -117,6 +117,9 @@ export default { formState() { return this.store.formState; }, + hasUpdated() { + return !!this.state.updatedAt; + }, }, components: { descriptionComponent, diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 0269a4cc4e2..5e8f0849969 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -219,7 +219,9 @@ module IssuablesHelper initialDescriptionText: issuable.description } - data.merge(updated_at_by(issue)) + data.merge!(updated_at_by(issuable)) + + data.to_json end def updated_at_by(issuable) diff --git a/db/schema.rb b/db/schema.rb index fa1c5dc15c4..368b8e99c78 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1446,8 +1446,8 @@ ActiveRecord::Schema.define(version: 20170525174156) do t.string "token" t.boolean "pipeline_events", default: false, null: false t.boolean "confidential_issues_events", default: false, null: false - t.boolean "job_events", default: false, null: false t.boolean "repository_update_events", default: false, null: false + t.boolean "job_events", default: false, null: false end add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree -- cgit v1.2.1