diff options
author | Luke "Jared" Bennett <lbennett@gitlab.com> | 2017-05-18 13:24:34 +0100 |
---|---|---|
committer | Luke "Jared" Bennett <lbennett@gitlab.com> | 2017-05-31 11:35:05 +0100 |
commit | 07c984d81cd7985d4ab7597cbb21b5f623b438e9 (patch) | |
tree | 8296ba468bad7e2d62f8d82d6dcbfac5fa5a659e /app | |
parent | 228926daee799c95e752a3c284c860e5bc60e528 (diff) | |
download | gitlab-ce-07c984d81cd7985d4ab7597cbb21b5f623b438e9.tar.gz |
Port fix-realtime-edited-text-for-issues 9-2-stable fix to master.
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/issue_show/components/app.vue | 27 | ||||
-rw-r--r-- | app/assets/javascripts/issue_show/components/description.vue | 11 | ||||
-rw-r--r-- | app/assets/javascripts/issue_show/components/edited.vue | 56 | ||||
-rw-r--r-- | app/assets/javascripts/issue_show/index.js | 10 | ||||
-rw-r--r-- | app/assets/javascripts/issue_show/stores/index.js | 9 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/mobile.scss | 5 | ||||
-rw-r--r-- | app/controllers/projects/issues_controller.rb | 13 | ||||
-rw-r--r-- | app/helpers/application_helper.rb | 2 | ||||
-rw-r--r-- | app/helpers/editable_helper.rb | 13 | ||||
-rw-r--r-- | app/models/concerns/editable.rb | 7 | ||||
-rw-r--r-- | app/models/concerns/issuable.rb | 1 | ||||
-rw-r--r-- | app/models/note.rb | 1 | ||||
-rw-r--r-- | app/models/snippet.rb | 1 | ||||
-rw-r--r-- | app/views/projects/issues/show.html.haml | 4 |
14 files changed, 136 insertions, 24 deletions
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" /> + <edited-component + v-if="!!state.updatedAt" + :updated-at="state.updatedAt" + :updated-by-name="state.updatedByName" + :updated-by-path="state.updatedByPath" + /> </div> </template> 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 @@ +<script> +import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue'; + +export default { + props: { + updatedAt: { + type: String, + required: false, + default: '', + }, + updatedByName: { + type: String, + required: false, + default: '', + }, + updatedByPath: { + type: String, + required: false, + default: '', + }, + }, + components: { + timeAgoTooltip, + }, + computed: { + hasUpdatedBy() { + return this.updatedByName && this.updatedByPath; + }, + }, +}; +</script> + +<template> + <small + class="edited-text" + > + Edited + <time-ago-tooltip + v-if="updatedAt" + placement="bottom" + :time="updatedAt" + /> + <span + v-if="hasUpdatedBy" + > + by + <a + class="author_link" + :href="updatedByPath" + > + <span>{{updatedByName}}</span> + </a> + </span> + </small> +</template> + 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. |