summaryrefslogtreecommitdiff
path: root/app/presenters/gitlab/blame_presenter.rb
blob: 81a954761eaf4c86e0b1e62eb3f2484bcf693ecb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# frozen_string_literal: true

module Gitlab
  class BlamePresenter < Gitlab::View::Presenter::Simple
    include ActionView::Helpers::TranslationHelper
    include ActionView::Context
    include AvatarsHelper
    include BlameHelper
    include CommitsHelper
    include ApplicationHelper
    include TreeHelper
    include IconsHelper

    presents nil, as: :blame

    CommitData = Struct.new(
      :author_avatar,
      :age_map_class,
      :commit_link,
      :commit_author_link,
      :project_blame_link,
      :time_ago_tooltip)

    def initialize(blame, **attributes)
      super

      @commits = {}
      precalculate_data_by_commit!
    end

    def first_line
      blame.first_line
    end

    def groups
      @groups ||= blame.groups
    end

    def commit_data(commit, previous_path = nil)
      @commits[commit.id] ||= get_commit_data(commit, previous_path)
    end

    private

    # Huge source files with a high churn rate (e.g. 'locale/gitlab.pot') could have
    # 10x times more blame groups than unique commits across all the groups.
    # That means we could cache per-commit data we need
    # to avoid recalculating it multiple times.
    # For such files, it could significantly improve the performance of the Blame.
    def precalculate_data_by_commit!
      groups.each { |group| commit_data(group[:commit], group[:previous_path]) }
    end

    def get_commit_data(commit, previous_path = nil)
      CommitData.new.tap do |data|
        data.author_avatar = author_avatar(commit, size: 36, has_tooltip: false, lazy: true)
        data.age_map_class = age_map_class(commit.committed_date, project_duration)
        data.commit_link = link_to commit.title, project_commit_path(project, commit.id), class: "cdark", title: commit.title
        data.commit_author_link = commit_author_link(commit, avatar: false)
        data.project_blame_link = project_blame_link(commit, previous_path)
        data.time_ago_tooltip = time_ago_with_tooltip(commit.committed_date)
      end
    end

    def project_blame_link(commit, previous_path = nil)
      previous_commit_id = commit.parent_id
      return unless previous_commit_id && !previous_path.nil?

      link_to project_blame_path(project, tree_join(previous_commit_id, previous_path)),
        title: _('View blame prior to this change'),
        aria: { label: _('View blame prior to this change') },
        class: 'version-link',
        data: { toggle: 'tooltip', placement: 'right', container: 'body' } do
          '&nbsp;'.html_safe
        end
    end

    def project_duration
      @project_duration ||= age_map_duration(groups, project)
    end

    def link_to(*args, &block)
      ActionController::Base.helpers.link_to(*args, &block)
    end

    def mail_to(*args, &block)
      ActionController::Base.helpers.mail_to(*args, &block)
    end
  end
end