Class: Livechat::Conversation
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Livechat::Conversation
- Defined in:
- app/models/livechat/conversation.rb
Overview
One thread per visitor — like a phone line, not a ticket queue. A visitor writing into a resolved conversation reopens it; history stays in place. Visitor identity is loose (no foreign key to the host's user table): signed-in visitors are keyed by visitor_id, guests by a cookie token.
Constant Summary collapse
- STATUSES =
%w[open resolved].freeze
- TYPING_TTL =
8.seconds
Class Method Summary collapse
-
.claim!(visitor_token:, visitor_id:, visitor_label: nil) ⇒ Object
A guest who signs in keeps their conversation: threads started under the cookie token are adopted by the user, once, permanently.
-
.for_visitor(visitor_id: nil, visitor_token: nil) ⇒ Object
The visitor's ongoing thread.
Instance Method Summary collapse
- #clear_typing!(author_type) ⇒ Object
- #display_name ⇒ Object
- #mark_read_for_agent! ⇒ Object
- #mark_read_for_visitor! ⇒ Object
- #post_agent_message!(body:, agent_id:, agent_label:, files: nil) ⇒ Object
- #post_visitor_message!(body, files: nil) ⇒ Object
-
#register_message(message) ⇒ Object
Denormalized inbox-list fields, refreshed on every message.
- #reopen_by!(agent_label) ⇒ Object
- #resolve_by!(agent_label) ⇒ Object
- #typing!(author_type) ⇒ Object
- #typing?(author_type) ⇒ Boolean
- #unread_from_visitor_count ⇒ Object
Class Method Details
.claim!(visitor_token:, visitor_id:, visitor_label: nil) ⇒ Object
A guest who signs in keeps their conversation: threads started under the cookie token are adopted by the user, once, permanently.
39 40 41 42 43 44 |
# File 'app/models/livechat/conversation.rb', line 39 def self.claim!(visitor_token:, visitor_id:, visitor_label: nil) return if visitor_token.blank? || visitor_id.blank? where(visitor_token: visitor_token, visitor_id: nil) .update_all(visitor_id: visitor_id.to_s, visitor_label: visitor_label) end |
.for_visitor(visitor_id: nil, visitor_token: nil) ⇒ Object
The visitor's ongoing thread. Signed-in identity wins; guests are found by their cookie token (only unclaimed threads — a token that has been adopted by a user belongs to that user now).
29 30 31 32 33 34 35 |
# File 'app/models/livechat/conversation.rb', line 29 def self.for_visitor(visitor_id: nil, visitor_token: nil) if visitor_id.present? where(visitor_id: visitor_id.to_s).order(id: :desc).first elsif visitor_token.present? where(visitor_token: visitor_token, visitor_id: nil).order(id: :desc).first end end |
Instance Method Details
#clear_typing!(author_type) ⇒ Object
114 115 116 |
# File 'app/models/livechat/conversation.rb', line 114 def clear_typing!() Rails.cache.delete(typing_cache_key()) end |
#display_name ⇒ Object
102 103 104 |
# File 'app/models/livechat/conversation.rb', line 102 def display_name visitor_label.presence || visitor_email.presence || "Visitor ##{id}" end |
#mark_read_for_agent! ⇒ Object
94 95 96 |
# File 'app/models/livechat/conversation.rb', line 94 def mark_read_for_agent! .from_visitor.unread.update_all(read_at: Time.current) end |
#mark_read_for_visitor! ⇒ Object
98 99 100 |
# File 'app/models/livechat/conversation.rb', line 98 def mark_read_for_visitor! .from_agent.unread.update_all(read_at: Time.current) end |
#post_agent_message!(body:, agent_id:, agent_label:, files: nil) ⇒ Object
57 58 59 60 61 62 63 64 |
# File 'app/models/livechat/conversation.rb', line 57 def (body:, agent_id:, agent_label:, files: nil) = .new(author_type: 'agent', body: body, agent_id: agent_id.to_s, agent_label: agent_label) attach(, files) .save! clear_typing!('agent') end |
#post_visitor_message!(body, files: nil) ⇒ Object
46 47 48 49 50 51 52 53 54 55 |
# File 'app/models/livechat/conversation.rb', line 46 def (body, files: nil) transaction do update!(status: 'open') if resolved? = .new(author_type: 'visitor', body: body) attach(, files) .save! clear_typing!('visitor') end end |
#register_message(message) ⇒ Object
Denormalized inbox-list fields, refreshed on every message. Written with update_columns: cheap, no callbacks, no updated_at churn.
86 87 88 89 90 |
# File 'app/models/livechat/conversation.rb', line 86 def () updates = { last_activity_at: .created_at } updates[:last_message_preview] = .preview unless .system? update_columns(updates) end |
#reopen_by!(agent_label) ⇒ Object
75 76 77 78 79 80 81 82 |
# File 'app/models/livechat/conversation.rb', line 75 def reopen_by!(agent_label) return if open? transaction do update!(status: 'open') .create!(author_type: 'system', event: 'reopened', agent_label: agent_label) end end |
#resolve_by!(agent_label) ⇒ Object
66 67 68 69 70 71 72 73 |
# File 'app/models/livechat/conversation.rb', line 66 def resolve_by!(agent_label) return if resolved? transaction do update!(status: 'resolved') .create!(author_type: 'system', event: 'resolved', agent_label: agent_label) end end |
#typing!(author_type) ⇒ Object
106 107 108 |
# File 'app/models/livechat/conversation.rb', line 106 def typing!() Rails.cache.write(typing_cache_key(), true, expires_in: TYPING_TTL) end |
#typing?(author_type) ⇒ Boolean
110 111 112 |
# File 'app/models/livechat/conversation.rb', line 110 def typing?() Rails.cache.exist?(typing_cache_key()) end |
#unread_from_visitor_count ⇒ Object
92 |
# File 'app/models/livechat/conversation.rb', line 92 def unread_from_visitor_count = .from_visitor.unread.count |