Class: Chats::Reaction
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Chats::Reaction
- Defined in:
- lib/chats/models/reaction.rb
Overview
An emoji reaction on a message. One row per (message, reactor, emoji) โthe unique index makes โtoggle!` race-safe. Reactors are polymorphic like every actor in this gem.
Constant Summary collapse
- EMOJI_MAX_LENGTH =
Reactions are short by nature; 16 chars comfortably fits any emoji grapheme cluster (ZWJ sequences like ๐จโ๐ฉโ๐งโ๐ฆ are up to ~11 chars) while making โsmuggle a paragraph into a reactionโ impossible.
16
Class Method Summary collapse
-
.summary_for(message) ⇒ Object
Grouped summary for rendering: [[โ๐โ, 3], [โ๐โ, 1]] โ stable order so bubbles donโt shuffle when counts change.
-
.toggle!(message:, reactor:, emoji:) ⇒ Object
Add the reaction if absent, remove it if present (the universal tap-to-toggle semantic).
Class Method Details
.summary_for(message) ⇒ Object
Grouped summary for rendering: [[โ๐โ, 3], [โ๐โ, 1]] โ stable order so bubbles donโt shuffle when counts change.
50 51 52 |
# File 'lib/chats/models/reaction.rb', line 50 def self.summary_for() where(message: ).group(:emoji).count.sort_by { |emoji, _count| emoji } end |
.toggle!(message:, reactor:, emoji:) ⇒ Object
Add the reaction if absent, remove it if present (the universal tap-to-toggle semantic). Returns the created reaction, or false when toggled off. Race-safe: a concurrent double-tap resolves through the unique index instead of raising.
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/chats/models/reaction.rb', line 34 def self.toggle!(message:, reactor:, emoji:) existing = find_by(message: , reactor: reactor, emoji: emoji) if existing existing.destroy! false else create!(message: , reactor: reactor, emoji: emoji) end rescue ActiveRecord::RecordNotUnique # Lost the race with an identical create โ treat as toggle-off. find_by(message: , reactor: reactor, emoji: emoji)&.destroy! false end |