Class: Bible270::Comment

Inherits:
ApplicationRecord show all
Defined in:
app/models/bible270/comment.rb

Overview

A publicly visible reflection attached to a plan day (optionally a track).

Defined Under Namespace

Classes: ThreadPage

Constant Summary collapse

HEART =

One heart for both states. An unliked one is the same emoji shown grey by CSS, so it cannot drift in size from a real like, and hovering it previews exactly what clicking will produce.

The white heart this replaced was legible on white but nearly invisible on the paper background; an outline glyph like U+2661 is drawn by the font rather than the emoji set and needs hand-tuning to match.

'❤️'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.thread_page(day: nil, reader_id: nil, page: 1, per_page: 10) ⇒ Object

One page of top-level reflections ordered by the newest activity in each conversation. Loading only IDs and timestamps keeps older archives cheap to sort, while the displayed page still eager-loads its readers, replies and likes.



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
# File 'app/models/bible270/comment.rb', line 60

def self.thread_page(day: nil, reader_id: nil, page: 1, per_page: 10)
  roots = approved.where(parent_id: nil)
  roots = roots.where(day: day) if day
  roots = roots.where(reader_id: reader_id) if reader_id

  root_rows = roots.pluck(:id, :created_at)
  reply_activity = approved.where.not(parent_id: nil).group(:parent_id).maximum(:created_at)
  ordered = root_rows.map do |id, created_at|
    [id, [created_at, reply_activity[id]].compact.max]
  end
  ordered.sort_by! { |id, activity_at| [-activity_at.to_f, -id] }

  per_page = per_page.to_i
  per_page = 10 unless per_page.positive?
  pages = [(ordered.length.to_f / per_page).ceil, 1].max
  page = page.to_i.clamp(1, pages)
  page_rows = ordered.slice((page - 1) * per_page, per_page) || []
  ids = page_rows.map(&:first)
  loaded = approved.where(id: ids)
    .includes(:reader, { likes: :reader }, { replies: [:reader, { likes: :reader }] })
    .index_by(&:id)

  ThreadPage.new(
    threads: ids.filter_map { |id| loaded[id] },
    activity_by_id: page_rows.to_h,
    page: page,
    pages: pages,
    total: ordered.length
  )
end

Instance Method Details

#edited?Boolean

A second's grace: created_at and updated_at differ by microseconds on insert, which would mark every reflection as edited.

Returns:

  • (Boolean)


130
# File 'app/models/bible270/comment.rb', line 130

def edited? = updated_at - created_at > 1

#hidden?Boolean

Returns:

  • (Boolean)


94
# File 'app/models/bible270/comment.rb', line 94

def hidden?  = !approved

#hide!Object

Every reflection is visible when written; these are the moderation actions.



92
# File 'app/models/bible270/comment.rb', line 92

def hide!    = update!(approved: false, moderated_at: Time.current)

#liked_by?(reader) ⇒ Boolean

Returns:

  • (Boolean)


97
98
99
100
101
# File 'app/models/bible270/comment.rb', line 97

def liked_by?(reader)
  return false if reader.nil?

  likes.any? { |like| like.reader_id == reader.id }
end

#moderated?Boolean

Returns:

  • (Boolean)


134
# File 'app/models/bible270/comment.rb', line 134

def moderated? = moderated_at.present?

#reply?Boolean

Returns:

  • (Boolean)


95
# File 'app/models/bible270/comment.rb', line 95

def reply?   = parent_id.present?

#set_like!(reader, liked:) ⇒ Object



118
119
120
121
122
123
124
125
126
# File 'app/models/bible270/comment.rb', line 118

def set_like!(reader, liked:)
  if liked
    likes.create_or_find_by!(reader: reader)
  else
    likes.where(reader: reader).destroy_all
  end
  likes.reload
  liked
end

#toggle_like!(reader) ⇒ Object

Toggling returns the new state, so a caller can say what happened without asking again.



105
106
107
108
109
110
111
112
113
114
115
116
# File 'app/models/bible270/comment.rb', line 105

def toggle_like!(reader)
  existing = likes.find { |like| like.reader_id == reader.id }
  if existing
    existing.destroy
    likes.reload
    false
  else
    likes.create(reader: reader)
    likes.reload
    true
  end
end

#unhide!Object



93
# File 'app/models/bible270/comment.rb', line 93

def unhide!  = update!(approved: true, moderated_at: Time.current)

#visible_repliesObject

Visible replies, oldest first: a conversation reads forwards.



133
# File 'app/models/bible270/comment.rb', line 133

def visible_replies = replies.approved.order(created_at: :asc)