Module: Chats::Messager

Extended by:
ActiveSupport::Concern
Defined in:
lib/chats/models/concerns/messager.rb

Overview

Included by ‘acts_as_messager`. Gives any model an inbox and the two verbs that make the whole gem read like plain English:

alice.chat_with(bob)                       # find-or-create the DM
alice.chat_with(bob, about: ride)          # the DM about that ride
alice.chat_with(bob, carol, title: "Trip") # a group

alice.message!(bob, "hola!")               # DM in one line
alice.message!(conversation, "hola!")      # or into a conversation

alice.chats                                # her inbox, newest first
alice.unread_chats_count                   # for the nav badge

Instance Method Summary collapse

Instance Method Details

#chat_participation_in(conversation) ⇒ Object

This messager’s participant row in conversation (nil when not in it).



98
99
100
# File 'lib/chats/models/concerns/messager.rb', line 98

def chat_participation_in(conversation)
  conversation.participant_for(self)
end

#chat_with(*others, about: nil, title: nil) ⇒ Object

Find-or-create a conversation with one or more other messagers. One other → the direct thread (per-pair, or per-pair-per-subject when ‘about:` is given). Several others → a group.

Raises Chats::BlockedError / Chats::NotAllowedError — see Conversation.direct_between! / .group!.

Raises:

  • (ArgumentError)


49
50
51
52
53
54
55
56
57
58
# File 'lib/chats/models/concerns/messager.rb', line 49

def chat_with(*others, about: nil, title: nil)
  others = others.flatten.compact
  raise ArgumentError, "chat_with requires at least one other messager" if others.empty?

  if others.size == 1
    Chats::Conversation.direct_between!(self, others.first, about: about)
  else
    Chats::Conversation.group!(self, others, title: title, about: about)
  end
end

#chatsObject

The inbox: every active conversation, blocked counterparts hidden, newest activity first. A relation — chain ‘.limit`, `.includes`, etc.



80
81
82
# File 'lib/chats/models/concerns/messager.rb', line 80

def chats
  Chats::Conversation.inbox_for(self)
end

#message!(target, body = nil, about: nil, files: [], reply_to: nil) ⇒ Object

Send a message — to a messager (resolving/creating the direct thread) or straight into a Chats::Conversation. Returns the Chats::Message.

alice.message!(bob, "are you coming?")
alice.message!(bob, "about the ride", about: ride)
alice.message!(conversation, "hi all!", files: [photo])


66
67
68
69
70
71
72
73
74
75
76
# File 'lib/chats/models/concerns/messager.rb', line 66

def message!(target, body = nil, about: nil, files: [], reply_to: nil)
  conversation =
    case target
    when Chats::Conversation then target
    else chat_with(target, about: about)
    end

  attributes = { sender: self, body: body, reply_to: reply_to }
  attributes[:files] = files if files.present?
  conversation.messages.create!(**attributes)
end

#unread_chats?Boolean

Returns:

  • (Boolean)


93
94
95
# File 'lib/chats/models/concerns/messager.rb', line 93

def unread_chats?
  chats.unread_by(self).reorder(nil).exists?
end

#unread_chats_countObject

Number of conversations with unread messages (the WhatsApp-style nav badge counts conversations, not messages — 47 unread messages in one thread is still “1 thing to deal with”).



87
88
89
90
91
# File 'lib/chats/models/concerns/messager.rb', line 87

def unread_chats_count
  # reorder(nil): the inbox scope orders by a COALESCE expression, which
  # PostgreSQL rejects inside a COUNT(DISTINCT …) aggregate.
  chats.unread_by(self).reorder(nil).distinct.count
end