Class: Bible270::Comment
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Bible270::Comment
- 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
-
.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.
Instance Method Summary collapse
-
#edited? ⇒ Boolean
A second's grace: created_at and updated_at differ by microseconds on insert, which would mark every reflection as edited.
- #hidden? ⇒ Boolean
-
#hide! ⇒ Object
Every reflection is visible when written; these are the moderation actions.
- #liked_by?(reader) ⇒ Boolean
- #moderated? ⇒ Boolean
- #reply? ⇒ Boolean
- #set_like!(reader, liked:) ⇒ Object
-
#toggle_like!(reader) ⇒ Object
Toggling returns the new state, so a caller can say what happened without asking again.
- #unhide! ⇒ Object
-
#visible_replies ⇒ Object
Visible replies, oldest first: a conversation reads forwards.
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.
130 |
# File 'app/models/bible270/comment.rb', line 130 def edited? = updated_at - created_at > 1 |
#hidden? ⇒ 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
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
134 |
# File 'app/models/bible270/comment.rb', line 134 def moderated? = moderated_at.present? |
#reply? ⇒ 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_replies ⇒ Object
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) |