Module: Collavre::CreativesHelper

Defined in:
app/helpers/collavre/creatives_helper.rb

Instance Method Summary collapse

Instance Method Details

Delegate to MarkdownConverter for backward compatibility. These methods are used in views and by MarkdownImporter via ApplicationController.helpers.



202
203
204
# File 'app/helpers/collavre/creatives_helper.rb', line 202

def markdown_links_to_html(text, image_refs = {})
  MarkdownConverter.markdown_to_html(text, image_refs)
end

#render_creative_progress(creative, select_mode: false, has_children: nil, can_write: nil, can_feedback: nil, unread_count: nil) ⇒ Object

can_write, can_feedback and unread_count let a caller rendering many creatives at once (the browse tree) resolve them in batch and hand them in. Left nil, each is resolved for this creative alone — correct, but a query per node. Single-creative call sites take that path.



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
91
92
93
94
95
96
97
98
# File 'app/helpers/collavre/creatives_helper.rb', line 39

def render_creative_progress(creative, select_mode: false, has_children: nil, can_write: nil, can_feedback: nil, unread_count: nil)
  progress_value = if params[:tags].present?
    tag_ids = Array(params[:tags]).map(&:to_s)
    creative.filtered_progress || creative.progress_for_tags(tag_ids) || 0
  else
    creative.progress
  end

  can_feedback = creative.has_permission?(Current.user, :feedback) if can_feedback.nil?

  (:div, class: "creative-row-end") do
    comment_part = if creative.archived?
      safe_join([])
    elsif can_feedback
      origin = creative.effective_origin
      comments_count = origin.comments_count
      unread_count = unread_count_for(origin, comments_count) if unread_count.nil?
      if Current.user && CommentPresenceStore.list(origin.id).include?(Current.user.id)
        unread_count = 0
      end
      classes = [ "comments-btn", "creative-action-btn" ]
      classes << "no-comments" if comments_count.zero?
      comment_icon = svg_tag(
        "comment.svg",
        class: "comment-icon"
      )
      badge_id = "comment-badge-#{origin.id}"
      stream = turbo_stream_from [ Current.user, origin, :comment_badge ]
      badge = render(
        Inbox::BadgeComponent.new(
          count: unread_count,
          badge_id: badge_id,
          show_zero: comments_count.positive?
        )
      )
      stream + button_tag(
        comment_icon + badge,
        name: "show-comments-btn",
        data: { creative_id: creative.id, can_comment: true, creative_snippet: creative.creative_snippet },
        class: classes.join(" ")
      )
    else
      safe_join([])
    end
    is_leaf = has_children.nil? ? !creative.children.exists? : !has_children
    can_write = creative.has_permission?(Current.user, :write) if can_write.nil?
    progress_part = if is_leaf && can_write && !select_mode
      render_progress_toggle(creative, progress_value)
    else
      render_progress_value(progress_value)
    end

    safe_join([
      progress_part,
      comment_part,
      tag.br,
      (creative.tags ? render_creative_tags(creative) : safe_join([]))
    ])
  end
end

#render_creative_tags(creative) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'app/helpers/collavre/creatives_helper.rb', line 20

def render_creative_tags(creative)
  # The browse tree preloads tags -> label per level; `includes` on an
  # already-loaded association builds a fresh relation and re-queries it,
  # which would reinstate the N+1 this is called from.
  labels = if creative.tags.loaded?
    creative.tags.map(&:label).compact
  else
    creative.tags&.includes(:label)&.map(&:label)&.compact
  end
  return "" if labels&.empty?
  (:div, class: "creative-tags", style: "display: none;") do
    render_tags(labels, "unstyled-link", true)
  end
end

#render_creative_tree_markdown(creatives, level = 1, with_progress = false, max_depth: nil) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'app/helpers/collavre/creatives_helper.rb', line 151

def render_creative_tree_markdown(creatives, level = 1, with_progress = false, max_depth: nil)
  return "" if creatives.blank?
  md = ""
  creatives.each do |creative|
    desc = creative.effective_description(nil, true)
    if with_progress && creative.respond_to?(:progress) && !creative.progress.nil?
      pct = (creative.progress.to_f * 100).round
      desc = "#{desc} (#{pct}%)"
    end
    raw_html = desc.gsub(/<!--.*?-->/m, "").strip
    markdown_content = MarkdownConverter.html_to_markdown(raw_html)
    cleaned_markdown = markdown_content.strip
    rendered_table_block = false

    table_match = cleaned_markdown.match(/^<div[^>]*>\s*<div[^>]*>\s*(\|.*?\|(?:\n\|.*?\|)*)\s*<\/div>\s*<\/div>$/m)
    if level <= 4 && table_match
      table_content = table_match[1].strip
      if MarkdownConverter.table_block?(table_content)
        md += "#{table_content}\n\n"
        rendered_table_block = true
      end
    elsif level <= 4 && MarkdownConverter.table_block?(cleaned_markdown)
      md += "#{cleaned_markdown}\n\n"
      rendered_table_block = true
    elsif level <= 4
      md += "#{'#' * level} #{ActionView::Base.full_sanitizer.sanitize(markdown_content).strip}\n\n"
    else
      inner_html = begin
        fragment = Nokogiri::HTML.fragment(raw_html)
        wrapper = fragment.at_css("div.trix-content")
        if wrapper
          wrapper.inner_html.strip
        else
          ActionView::Base.full_sanitizer.sanitize(markdown_content).strip
        end
      end
      inner = ActionView::Base.full_sanitizer.sanitize(inner_html)
      indent = "  " * (level - 5)
      md += "#{indent}* #{inner}\n"
    end
    children = creative.linked_children
    if children.present? && (max_depth.nil? || level < max_depth)
      md += render_creative_tree_markdown(children, level + 1, with_progress, max_depth: max_depth)
    end
    md += "\n" if level <= 4 && !rendered_table_block
  end
  md
end

#render_progress_toggle(creative, value) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'app/helpers/collavre/creatives_helper.rb', line 111

def render_progress_toggle(creative, value)
  complete = value == 1
  new_value = complete ? 0 : 1
  tooltip = complete ? t("collavre.creatives.index.mark_incomplete") : t("collavre.creatives.index.mark_complete")
  (
    :span,
    class: "progress-toggle-wrap",
    data: {
      progress_toggle: true,
      creative_id: creative.id,
      current_progress: value,
      new_progress: new_value
    },
    title: tooltip
  ) do
    checkbox = tag.input(
      type: "checkbox",
      checked: complete || nil,
      class: "progress-toggle-checkbox",
      tabindex: -1,
      "aria-label": tooltip
    )
    safe_join([ render_progress_value(value), checkbox ])
  end
end

#render_progress_value(value) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'app/helpers/collavre/creatives_helper.rb', line 137

def render_progress_value(value)
  text = number_to_percentage(value * 100, precision: 0)
  completion_mark = Collavre::SystemSetting.completion_mark
  if value == 1 && !completion_mark.nil?
    text = completion_mark
  end
  display_text = text.blank? ? "\u00A0\u00A0" : text
  (
    :span,
    display_text,
    class: "creative-progress-#{value == 1 ? 'complete' : 'incomplete'}"
  )
end

#render_tags(labels, class_name = nil, name_only = false) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'app/helpers/collavre/creatives_helper.rb', line 3

def render_tags(labels, class_name = nil, name_only = false)
  return "" if labels&.empty? or labels.nil?

  index = 0
  safe_join(labels.map do |label|
    suffix = name_only ? nil : render_label_suffix(label)
    index += 1
    (:span, class: "tag") do
      safe_join([
        (index == 1 ? "" : " "),
        link_to("##{strip_tags(label.name)}", collavre.creatives_path(tags: [ label.id ]), class: class_name ? class_name: "", title: strip_tags(label.name)),
        suffix
      ].compact)
    end
  end)
end

#unread_count_for(origin, comments_count) ⇒ Object

Unread comments on origin for the current user. With no read pointer, every comment is unread. Batched by CommentBadgeIndex for the browse tree; this is the single-creative path.



103
104
105
106
107
108
109
# File 'app/helpers/collavre/creatives_helper.rb', line 103

def unread_count_for(origin, comments_count)
  pointer = CommentReadPointer.find_by(user: Current.user, creative: origin)
  last_read_id = pointer&.last_read_comment_id
  return comments_count unless last_read_id

  origin.comments.where("id > ? and private = ?", last_read_id, false).count
end