Feature: Api to toggle typing status on a conversation (#807)
- api to toggle typing status on a conversation - clients receive webhook events on the same Addresses: #718 , #719 , #775
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
class Api::V1::Accounts::ConversationsController < Api::BaseController
|
class Api::V1::Accounts::ConversationsController < Api::BaseController
|
||||||
|
include Events::Types
|
||||||
before_action :conversation, except: [:index]
|
before_action :conversation, except: [:index]
|
||||||
before_action :contact_inbox, only: [:create]
|
before_action :contact_inbox, only: [:create]
|
||||||
|
|
||||||
@@ -23,6 +24,16 @@ class Api::V1::Accounts::ConversationsController < Api::BaseController
|
|||||||
@status = @conversation.toggle_status
|
@status = @conversation.toggle_status
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def toggle_typing_status
|
||||||
|
user = current_user.presence || @resource
|
||||||
|
if params[:typing_status] == 'on'
|
||||||
|
Rails.configuration.dispatcher.dispatch(CONVERSATION_TYPING_ON, Time.zone.now, conversation: @conversation, user: user)
|
||||||
|
elsif params[:typing_status] == 'off'
|
||||||
|
Rails.configuration.dispatcher.dispatch(CONVERSATION_TYPING_OFF, Time.zone.now, conversation: @conversation)
|
||||||
|
end
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
def update_last_seen
|
def update_last_seen
|
||||||
@conversation.agent_last_seen_at = parsed_last_seen_at
|
@conversation.agent_last_seen_at = parsed_last_seen_at
|
||||||
@conversation.save!
|
@conversation.save!
|
||||||
|
|||||||
@@ -48,6 +48,28 @@ class ActionCableListener < BaseListener
|
|||||||
broadcast(user_tokens(account, conversation.inbox.members), CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data)
|
broadcast(user_tokens(account, conversation.inbox.members), CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def conversation_typing_on(event)
|
||||||
|
conversation = event.data[:conversation]
|
||||||
|
account = conversation.account
|
||||||
|
user = event.data[:user]
|
||||||
|
tokens = user_tokens(account, conversation.inbox.members) +
|
||||||
|
[conversation.contact.pubsub_token]
|
||||||
|
|
||||||
|
broadcast(tokens, CONVERSATION_TYPING_ON,
|
||||||
|
conversation: conversation.push_event_data, user: user.push_event_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_typing_off(event)
|
||||||
|
conversation = event.data[:conversation]
|
||||||
|
account = conversation.account
|
||||||
|
user = event.data[:user]
|
||||||
|
tokens = user_tokens(account, conversation.inbox.members) +
|
||||||
|
[conversation.contact.pubsub_token]
|
||||||
|
|
||||||
|
broadcast(tokens, CONVERSATION_TYPING_OFF,
|
||||||
|
conversation: conversation.push_event_data, user: user.push_event_data)
|
||||||
|
end
|
||||||
|
|
||||||
def assignee_changed(event)
|
def assignee_changed(event)
|
||||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||||
|
|
||||||
|
|||||||
@@ -16,4 +16,20 @@ class AgentBot < ApplicationRecord
|
|||||||
|
|
||||||
has_many :agent_bot_inboxes, dependent: :destroy
|
has_many :agent_bot_inboxes, dependent: :destroy
|
||||||
has_many :inboxes, through: :agent_bot_inboxes
|
has_many :inboxes, through: :agent_bot_inboxes
|
||||||
|
|
||||||
|
def push_event_data
|
||||||
|
{
|
||||||
|
name: name,
|
||||||
|
avatar_url: avatar_url,
|
||||||
|
type: 'agent_bot'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def webhook_data
|
||||||
|
{
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
type: 'agent_bot'
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class Contact < ApplicationRecord
|
|||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
thumbnail: avatar_url,
|
thumbnail: avatar_url,
|
||||||
|
type: 'contact',
|
||||||
pubsub_token: pubsub_token
|
pubsub_token: pubsub_token
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@@ -58,7 +59,8 @@ class Contact < ApplicationRecord
|
|||||||
{
|
{
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
avatar: avatar_url
|
avatar: avatar_url,
|
||||||
|
type: 'contact'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,8 @@ class User < ApplicationRecord
|
|||||||
def push_event_data
|
def push_event_data
|
||||||
{
|
{
|
||||||
name: name,
|
name: name,
|
||||||
avatar_url: avatar_url
|
avatar_url: avatar_url,
|
||||||
|
type: 'user'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -137,7 +138,8 @@ class User < ApplicationRecord
|
|||||||
{
|
{
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
email: email
|
email: email,
|
||||||
|
type: 'user'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
member do
|
member do
|
||||||
post :toggle_status
|
post :toggle_status
|
||||||
|
post :toggle_typing_status
|
||||||
post :update_last_seen
|
post :update_last_seen
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ module Events::Types
|
|||||||
CONVERSATION_RESOLVED = 'conversation.resolved'
|
CONVERSATION_RESOLVED = 'conversation.resolved'
|
||||||
CONVERSATION_LOCK_TOGGLE = 'conversation.lock_toggle'
|
CONVERSATION_LOCK_TOGGLE = 'conversation.lock_toggle'
|
||||||
ASSIGNEE_CHANGED = 'assignee.changed'
|
ASSIGNEE_CHANGED = 'assignee.changed'
|
||||||
|
CONVERSATION_TYPING_ON = 'conversation.typing_on'
|
||||||
|
CONVERSATION_TYPING_OFF = 'conversation.typing_off'
|
||||||
|
|
||||||
# message events
|
# message events
|
||||||
MESSAGE_CREATED = 'message.created'
|
MESSAGE_CREATED = 'message.created'
|
||||||
|
|||||||
@@ -122,6 +122,34 @@ RSpec.describe 'Conversations API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'POST /api/v1/accounts/{account.id}/conversations/:id/toggle_typing_status' do
|
||||||
|
let(:conversation) { create(:conversation, account: account) }
|
||||||
|
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_typing_status"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated user' do
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
|
||||||
|
it 'toggles the conversation status' do
|
||||||
|
allow(Rails.configuration.dispatcher).to receive(:dispatch)
|
||||||
|
post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_typing_status",
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
params: { typing_status: 'on' },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
||||||
|
.with(Conversation::CONVERSATION_TYPING_ON, kind_of(Time), { conversation: conversation, user: agent })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'POST /api/v1/accounts/{account.id}/conversations/:id/update_last_seen' do
|
describe 'POST /api/v1/accounts/{account.id}/conversations/:id/update_last_seen' do
|
||||||
let(:conversation) { create(:conversation, account: account) }
|
let(:conversation) { create(:conversation, account: account) }
|
||||||
|
|
||||||
|
|||||||
@@ -29,4 +29,36 @@ describe ActionCableListener do
|
|||||||
listener.message_created(event)
|
listener.message_created(event)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#typing_on' do
|
||||||
|
let(:event_name) { :'conversation.typing_on' }
|
||||||
|
let!(:event) { Events::Base.new(event_name, Time.zone.now, conversation: conversation, user: agent) }
|
||||||
|
|
||||||
|
it 'sends message to account admins, inbox agents and the contact' do
|
||||||
|
# HACK: to reload conversation inbox members
|
||||||
|
expect(conversation.inbox.reload.inbox_members.count).to eq(1)
|
||||||
|
expect(ActionCableBroadcastJob).to receive(:perform_later).with(
|
||||||
|
[agent.pubsub_token, admin.pubsub_token, conversation.contact.pubsub_token],
|
||||||
|
'conversation.typing_on', conversation: conversation.push_event_data,
|
||||||
|
user: agent.push_event_data
|
||||||
|
)
|
||||||
|
listener.conversation_typing_on(event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#typing_off' do
|
||||||
|
let(:event_name) { :'conversation.typing_off' }
|
||||||
|
let!(:event) { Events::Base.new(event_name, Time.zone.now, conversation: conversation, user: agent) }
|
||||||
|
|
||||||
|
it 'sends message to account admins, inbox agents and the contact' do
|
||||||
|
# HACK: to reload conversation inbox members
|
||||||
|
expect(conversation.inbox.reload.inbox_members.count).to eq(1)
|
||||||
|
expect(ActionCableBroadcastJob).to receive(:perform_later).with(
|
||||||
|
[agent.pubsub_token, admin.pubsub_token, conversation.contact.pubsub_token],
|
||||||
|
'conversation.typing_off', conversation: conversation.push_event_data,
|
||||||
|
user: agent.push_event_data
|
||||||
|
)
|
||||||
|
listener.conversation_typing_off(event)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user