parent
c0354364ff
commit
f4358d9993
28 changed files with 144 additions and 165 deletions
|
@ -1,4 +1 @@
|
||||||
pusher_cluster=
|
|
||||||
pusher_key=
|
|
||||||
|
|
||||||
fb_app_id=
|
fb_app_id=
|
||||||
|
|
|
@ -43,7 +43,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
__WEBPACK_ENV__: true,
|
__WEBPACK_ENV__: true,
|
||||||
__PUSHER__: true,
|
|
||||||
__FB_APP_ID__: true,
|
__FB_APP_ID__: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,7 +40,7 @@ Lint/UselessAssignment:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/controllers/api/v1/callbacks_controller.rb'
|
- 'app/controllers/api/v1/callbacks_controller.rb'
|
||||||
- 'app/controllers/api/v1/facebook_indicators_controller.rb'
|
- 'app/controllers/api/v1/facebook_indicators_controller.rb'
|
||||||
- 'app/listeners/pusher_listener.rb'
|
- 'app/listeners/action_cable_listener.rb'
|
||||||
- 'app/listeners/reporting_listener.rb'
|
- 'app/listeners/reporting_listener.rb'
|
||||||
- 'app/models/channel/facebook_page.rb'
|
- 'app/models/channel/facebook_page.rb'
|
||||||
- 'app/models/facebook_page.rb'
|
- 'app/models/facebook_page.rb'
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -39,7 +39,6 @@ gem 'devise_token_auth', git: 'https://github.com/lynndylanhurley/devise_token_a
|
||||||
gem 'pundit'
|
gem 'pundit'
|
||||||
|
|
||||||
##--- gems for pubsub service ---##
|
##--- gems for pubsub service ---##
|
||||||
gem 'pusher'
|
|
||||||
gem 'wisper', '2.0.0'
|
gem 'wisper', '2.0.0'
|
||||||
|
|
||||||
##--- gems for reporting ---##
|
##--- gems for reporting ---##
|
||||||
|
@ -72,6 +71,7 @@ group :development do
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
|
gem 'action-cable-testing'
|
||||||
gem 'mock_redis'
|
gem 'mock_redis'
|
||||||
gem 'shoulda-matchers'
|
gem 'shoulda-matchers'
|
||||||
end
|
end
|
||||||
|
|
19
Gemfile.lock
19
Gemfile.lock
|
@ -113,6 +113,8 @@ GIT
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
|
action-cable-testing (0.6.0)
|
||||||
|
actioncable (>= 5.0)
|
||||||
addressable (2.7.0)
|
addressable (2.7.0)
|
||||||
public_suffix (>= 2.0.2, < 5.0)
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
ast (2.4.0)
|
ast (2.4.0)
|
||||||
|
@ -161,13 +163,6 @@ GEM
|
||||||
coderay (1.1.2)
|
coderay (1.1.2)
|
||||||
coercible (1.0.0)
|
coercible (1.0.0)
|
||||||
descendants_tracker (~> 0.0.1)
|
descendants_tracker (~> 0.0.1)
|
||||||
coffee-rails (5.0.0)
|
|
||||||
coffee-script (>= 2.2.0)
|
|
||||||
railties (>= 5.2.0)
|
|
||||||
coffee-script (2.4.1)
|
|
||||||
coffee-script-source
|
|
||||||
execjs
|
|
||||||
coffee-script-source (1.12.2)
|
|
||||||
concurrent-ruby (1.1.5)
|
concurrent-ruby (1.1.5)
|
||||||
connection_pool (2.2.2)
|
connection_pool (2.2.2)
|
||||||
crass (1.0.5)
|
crass (1.0.5)
|
||||||
|
@ -211,7 +206,6 @@ GEM
|
||||||
httparty (0.17.1)
|
httparty (0.17.1)
|
||||||
mime-types (~> 3.0)
|
mime-types (~> 3.0)
|
||||||
multi_xml (>= 0.5.2)
|
multi_xml (>= 0.5.2)
|
||||||
httpclient (2.8.3)
|
|
||||||
i18n (1.7.0)
|
i18n (1.7.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
ice_nine (0.11.2)
|
ice_nine (0.11.2)
|
||||||
|
@ -269,7 +263,6 @@ GEM
|
||||||
minitest (5.12.2)
|
minitest (5.12.2)
|
||||||
mock_redis (0.22.0)
|
mock_redis (0.22.0)
|
||||||
msgpack (1.3.1)
|
msgpack (1.3.1)
|
||||||
multi_json (1.14.1)
|
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.1.1)
|
||||||
naught (1.1.0)
|
naught (1.1.0)
|
||||||
|
@ -292,11 +285,6 @@ GEM
|
||||||
puma (3.12.1)
|
puma (3.12.1)
|
||||||
pundit (2.1.0)
|
pundit (2.1.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
pusher (1.3.3)
|
|
||||||
httpclient (~> 2.7)
|
|
||||||
multi_json (~> 1.0)
|
|
||||||
pusher-signature (~> 0.1.8)
|
|
||||||
pusher-signature (0.1.8)
|
|
||||||
rack (2.0.7)
|
rack (2.0.7)
|
||||||
rack-cache (1.9.0)
|
rack-cache (1.9.0)
|
||||||
rack (>= 0.4)
|
rack (>= 0.4)
|
||||||
|
@ -452,6 +440,7 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
action-cable-testing
|
||||||
acts-as-taggable-on!
|
acts-as-taggable-on!
|
||||||
attr_extras
|
attr_extras
|
||||||
bootsnap
|
bootsnap
|
||||||
|
@ -459,7 +448,6 @@ DEPENDENCIES
|
||||||
byebug
|
byebug
|
||||||
carrierwave-aws
|
carrierwave-aws
|
||||||
chargebee (~> 2)
|
chargebee (~> 2)
|
||||||
coffee-rails
|
|
||||||
devise!
|
devise!
|
||||||
devise_token_auth!
|
devise_token_auth!
|
||||||
facebook-messenger
|
facebook-messenger
|
||||||
|
@ -480,7 +468,6 @@ DEPENDENCIES
|
||||||
pry-rails
|
pry-rails
|
||||||
puma (~> 3.0)
|
puma (~> 3.0)
|
||||||
pundit
|
pundit
|
||||||
pusher
|
|
||||||
rack-cors
|
rack-cors
|
||||||
rails (~> 6)!
|
rails (~> 6)!
|
||||||
redis
|
redis
|
||||||
|
|
2
app/channels/application_cable/channel.rb
Normal file
2
app/channels/application_cable/channel.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
class ApplicationCable::Channel < ActionCable::Channel::Base
|
||||||
|
end
|
2
app/channels/application_cable/connection.rb
Normal file
2
app/channels/application_cable/connection.rb
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
class ApplicationCable::Connection < ActionCable::Connection::Base
|
||||||
|
end
|
5
app/channels/room_channel.rb
Normal file
5
app/channels/room_channel.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class RoomChannel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
stream_from params[:pubsub_token]
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,6 +5,6 @@ class SyncDispatcher < BaseDispatcher
|
||||||
end
|
end
|
||||||
|
|
||||||
def listeners
|
def listeners
|
||||||
[PusherListener.instance]
|
[ActionCableListener.instance]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
APP_BASE_URL: '/',
|
APP_BASE_URL: '/',
|
||||||
PUSHER: __PUSHER__,
|
|
||||||
get apiURL() {
|
get apiURL() {
|
||||||
return `${this.APP_BASE_URL}/`;
|
return `${this.APP_BASE_URL}/`;
|
||||||
},
|
},
|
||||||
|
|
70
app/javascript/dashboard/helper/actionCable.js
Normal file
70
app/javascript/dashboard/helper/actionCable.js
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { createConsumer } from '@rails/actioncable';
|
||||||
|
|
||||||
|
import AuthAPI from '../api/auth';
|
||||||
|
|
||||||
|
class ActionCableConnector {
|
||||||
|
constructor(app, pubsubToken) {
|
||||||
|
const consumer = createConsumer();
|
||||||
|
consumer.subscriptions.create(
|
||||||
|
{
|
||||||
|
channel: 'RoomChannel',
|
||||||
|
pubsub_token: pubsubToken,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
received: this.onReceived,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.app = app;
|
||||||
|
this.events = {
|
||||||
|
'message.created': this.onMessageCreated,
|
||||||
|
'conversation.created': this.onConversationCreated,
|
||||||
|
'status_change:conversation': this.onStatusChange,
|
||||||
|
'user:logout': this.onLogout,
|
||||||
|
'page:reload': this.onReload,
|
||||||
|
'assignee.changed': this.onAssigneeChanged,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onAssigneeChanged = payload => {
|
||||||
|
const { meta = {}, id } = payload;
|
||||||
|
const { assignee } = meta || {};
|
||||||
|
if (id) {
|
||||||
|
this.app.$store.dispatch('updateAssignee', { id, assignee });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onConversationCreated = data => {
|
||||||
|
this.app.$store.dispatch('addConversation', data);
|
||||||
|
};
|
||||||
|
|
||||||
|
onLogout = () => AuthAPI.logout();
|
||||||
|
|
||||||
|
onMessageCreated = data => {
|
||||||
|
this.app.$store.dispatch('addMessage', data);
|
||||||
|
};
|
||||||
|
|
||||||
|
onReceived = ({ event, data } = {}) => {
|
||||||
|
if (this.events[event]) {
|
||||||
|
this.events[event](data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onReload = () => window.location.reload();
|
||||||
|
|
||||||
|
onStatusChange = data => {
|
||||||
|
this.app.$store.dispatch('addConversation', data);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
init() {
|
||||||
|
if (AuthAPI.isLoggedIn()) {
|
||||||
|
const actionCable = new ActionCableConnector(
|
||||||
|
window.WOOT,
|
||||||
|
AuthAPI.getPubSubToken()
|
||||||
|
);
|
||||||
|
return actionCable;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,71 +0,0 @@
|
||||||
/* eslint-env browser */
|
|
||||||
/* eslint no-console: 0 */
|
|
||||||
import Pusher from 'pusher-js';
|
|
||||||
import AuthAPI from '../api/auth';
|
|
||||||
import CONSTANTS from '../constants';
|
|
||||||
|
|
||||||
class VuePusher {
|
|
||||||
constructor(apiKey, options) {
|
|
||||||
this.app = options.app;
|
|
||||||
this.pusher = new Pusher(apiKey, options);
|
|
||||||
this.channels = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
subscribe(channelName) {
|
|
||||||
const channel = this.pusher.subscribe(channelName);
|
|
||||||
if (!this.channels.includes(channel)) {
|
|
||||||
this.channels.push(channelName);
|
|
||||||
}
|
|
||||||
this.bindEvent(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsubscribe(channelName) {
|
|
||||||
this.pusher.unsubscribe(channelName);
|
|
||||||
}
|
|
||||||
|
|
||||||
bindEvent(channel) {
|
|
||||||
channel.bind('message.created', data => {
|
|
||||||
this.app.$store.dispatch('addMessage', data);
|
|
||||||
});
|
|
||||||
|
|
||||||
channel.bind('conversation.created', data => {
|
|
||||||
this.app.$store.dispatch('addConversation', data);
|
|
||||||
});
|
|
||||||
|
|
||||||
channel.bind('status_change:conversation', data => {
|
|
||||||
this.app.$store.dispatch('addConversation', data);
|
|
||||||
});
|
|
||||||
|
|
||||||
channel.bind('assignee.changed', payload => {
|
|
||||||
const { meta = {}, id } = payload;
|
|
||||||
const { assignee } = meta || {};
|
|
||||||
if (id) {
|
|
||||||
this.app.$store.dispatch('updateAssignee', { id, assignee });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
channel.bind('user:logout', () => AuthAPI.logout());
|
|
||||||
channel.bind('page:reload', () => window.location.reload());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* eslint no-param-reassign: ["error", { "props": false }] */
|
|
||||||
export default {
|
|
||||||
init() {
|
|
||||||
// Log only if env is testing or development.
|
|
||||||
Pusher.logToConsole = CONSTANTS.PUSHER.logToConsole || true;
|
|
||||||
// Init Pusher
|
|
||||||
const options = {
|
|
||||||
encrypted: true,
|
|
||||||
app: window.WOOT,
|
|
||||||
cluster: CONSTANTS.PUSHER.cluster,
|
|
||||||
};
|
|
||||||
const pusher = new VuePusher(CONSTANTS.PUSHER.token, options);
|
|
||||||
// Add to global Obj
|
|
||||||
if (AuthAPI.isLoggedIn()) {
|
|
||||||
pusher.subscribe(AuthAPI.getPubSubToken());
|
|
||||||
return pusher.pusher;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -14,7 +14,6 @@ jest.mock('./login/login.routes', () => ({
|
||||||
jest.mock('../constants', () => {
|
jest.mock('../constants', () => {
|
||||||
return {
|
return {
|
||||||
APP_BASE_URL: '/',
|
APP_BASE_URL: '/',
|
||||||
PUSHER: false,
|
|
||||||
get apiUrl() {
|
get apiUrl() {
|
||||||
return `${this.APP_BASE_URL}/`;
|
return `${this.APP_BASE_URL}/`;
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,7 @@ import * as types from '../mutation-types';
|
||||||
import router from '../../routes';
|
import router from '../../routes';
|
||||||
import authAPI from '../../api/auth';
|
import authAPI from '../../api/auth';
|
||||||
import createAxios from '../../helper/APIHelper';
|
import createAxios from '../../helper/APIHelper';
|
||||||
import vuePusher from '../../helper/pusher';
|
import actionCable from '../../helper/actionCable';
|
||||||
// initial state
|
// initial state
|
||||||
const state = {
|
const state = {
|
||||||
currentUser: {
|
currentUser: {
|
||||||
|
@ -61,7 +61,7 @@ const actions = {
|
||||||
.then(() => {
|
.then(() => {
|
||||||
commit(types.default.SET_CURRENT_USER);
|
commit(types.default.SET_CURRENT_USER);
|
||||||
window.axios = createAxios(axios);
|
window.axios = createAxios(axios);
|
||||||
window.pusher = vuePusher.init(Vue);
|
actionCable.init(Vue);
|
||||||
router.replace({ name: 'home' });
|
router.replace({ name: 'home' });
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
|
|
|
@ -168,7 +168,7 @@ const mutations = {
|
||||||
_state.chatStatusFilter = data;
|
_state.chatStatusFilter = data;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Update assignee on pusher message
|
// Update assignee on action cable message
|
||||||
[types.default.UPDATE_ASSIGNEE](_state, payload) {
|
[types.default.UPDATE_ASSIGNEE](_state, payload) {
|
||||||
const [chat] = _state.allConversations.filter(c => c.id === payload.id);
|
const [chat] = _state.allConversations.filter(c => c.id === payload.id);
|
||||||
chat.meta.assignee = payload.assignee;
|
chat.meta.assignee = payload.assignee;
|
||||||
|
|
|
@ -24,7 +24,7 @@ import createAxios from '../dashboard/helper/APIHelper';
|
||||||
import commonHelpers from '../dashboard/helper/commons';
|
import commonHelpers from '../dashboard/helper/commons';
|
||||||
import router from '../dashboard/routes';
|
import router from '../dashboard/routes';
|
||||||
import store from '../dashboard/store';
|
import store from '../dashboard/store';
|
||||||
import vuePusher from '../dashboard/helper/pusher';
|
import vueActionCable from '../dashboard/helper/actionCable';
|
||||||
import constants from '../dashboard/constants';
|
import constants from '../dashboard/constants';
|
||||||
|
|
||||||
Vue.config.env = process.env;
|
Vue.config.env = process.env;
|
||||||
|
@ -58,7 +58,7 @@ window.onload = () => {
|
||||||
components: { App },
|
components: { App },
|
||||||
template: '<App/>',
|
template: '<App/>',
|
||||||
}).$mount('#app');
|
}).$mount('#app');
|
||||||
window.pusher = vuePusher.init();
|
vueActionCable.init();
|
||||||
};
|
};
|
||||||
|
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
|
|
|
@ -1,46 +1,53 @@
|
||||||
class PusherListener < BaseListener
|
class ActionCableListener < BaseListener
|
||||||
include Events::Types
|
include Events::Types
|
||||||
|
|
||||||
def conversation_created(event)
|
def conversation_created(event)
|
||||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||||
members = conversation.inbox.members.pluck(:pubsub_token)
|
members = conversation.inbox.members.pluck(:pubsub_token)
|
||||||
Pusher.trigger(members, CONVERSATION_CREATED, conversation.push_event_data) if members.present?
|
send_to_members(members, CONVERSATION_CREATED, conversation.push_event_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversation_read(event)
|
def conversation_read(event)
|
||||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||||
members = conversation.inbox.members.pluck(:pubsub_token)
|
members = conversation.inbox.members.pluck(:pubsub_token)
|
||||||
Pusher.trigger(members, CONVERSATION_READ, conversation.push_event_data) if members.present?
|
send_to_members(members, CONVERSATION_READ, conversation.push_event_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def message_created(event)
|
def message_created(event)
|
||||||
message, account, timestamp = extract_message_and_account(event)
|
message, account, timestamp = extract_message_and_account(event)
|
||||||
conversation = message.conversation
|
conversation = message.conversation
|
||||||
members = conversation.inbox.members.pluck(:pubsub_token)
|
members = conversation.inbox.members.pluck(:pubsub_token)
|
||||||
|
send_to_members(members, MESSAGE_CREATED, message.push_event_data)
|
||||||
Pusher.trigger(members, MESSAGE_CREATED, message.push_event_data) if members.present?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversation_reopened(event)
|
def conversation_reopened(event)
|
||||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||||
members = conversation.inbox.members.pluck(:pubsub_token)
|
members = conversation.inbox.members.pluck(:pubsub_token)
|
||||||
Pusher.trigger(members, CONVERSATION_REOPENED, conversation.push_event_data) if members.present?
|
send_to_members(members, CONVERSATION_REOPENED, conversation.push_event_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversation_lock_toggle(event)
|
def conversation_lock_toggle(event)
|
||||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||||
members = conversation.inbox.members.pluck(:pubsub_token)
|
members = conversation.inbox.members.pluck(:pubsub_token)
|
||||||
Pusher.trigger(members, CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data) if members.present?
|
send_to_members(members, CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data)
|
||||||
end
|
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)
|
||||||
members = conversation.inbox.members.pluck(:pubsub_token)
|
members = conversation.inbox.members.pluck(:pubsub_token)
|
||||||
Pusher.trigger(members, ASSIGNEE_CHANGED, conversation.push_event_data) if members.present?
|
send_to_members(members, ASSIGNEE_CHANGED, conversation.push_event_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def send_to_members(members, event_name, data)
|
||||||
|
return if members.blank?
|
||||||
|
|
||||||
|
members.each do |member|
|
||||||
|
ActionCable.server.broadcast(member, event: event_name, data: data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def push(pubsub_token, data)
|
def push(pubsub_token, data)
|
||||||
# Enqueue sidekiq job to push event to corresponding channel
|
# Enqueue sidekiq job to push event to corresponding channel
|
||||||
end
|
end
|
|
@ -4,7 +4,7 @@ module Pubsubable
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
# Used by the pusher/PubSub Service we use for real time communications
|
# Used by the actionCable/PubSub Service we use for real time communications
|
||||||
has_secure_token :pubsub_token
|
has_secure_token :pubsub_token
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ class User < ApplicationRecord
|
||||||
:validatable,
|
:validatable,
|
||||||
:confirmable
|
:confirmable
|
||||||
|
|
||||||
# Used by the pusher/PubSub Service we use for real time communications
|
# Used by the actionCable/PubSub Service we use for real time communications
|
||||||
has_secure_token :pubsub_token
|
has_secure_token :pubsub_token
|
||||||
|
|
||||||
validates_uniqueness_of :email, scope: :account_id
|
validates_uniqueness_of :email, scope: :account_id
|
||||||
|
|
13
config/cable.yml
Normal file
13
config/cable.yml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
development:
|
||||||
|
adapter: async
|
||||||
|
|
||||||
|
test:
|
||||||
|
adapter: test
|
||||||
|
|
||||||
|
staging:
|
||||||
|
adapter: redis
|
||||||
|
url: <%= ENV.fetch('REDIS_URL', 'redis://127.0.0.1:6379') %>
|
||||||
|
|
||||||
|
production:
|
||||||
|
adapter: redis
|
||||||
|
url: <%= ENV.fetch('REDIS_URL', 'redis://127.0.0.1:6379') %>
|
|
@ -1,6 +0,0 @@
|
||||||
Pusher.app_id = ENV['pusher_app_id']
|
|
||||||
Pusher.key = ENV['pusher_key']
|
|
||||||
Pusher.secret = ENV['pusher_secret']
|
|
||||||
Pusher.encrypted = true
|
|
||||||
Pusher.logger = Rails.logger
|
|
||||||
Pusher.cluster = ENV['pusher_cluster']
|
|
|
@ -17,19 +17,11 @@ environment.loaders.append('audio', {
|
||||||
|
|
||||||
environment.config.merge({ resolve });
|
environment.config.merge({ resolve });
|
||||||
|
|
||||||
const {
|
const { fb_app_id: fbAppID } = process.env;
|
||||||
pusher_cluster: cluster,
|
|
||||||
pusher_key: token,
|
|
||||||
fb_app_id: fbAppID,
|
|
||||||
} = process.env;
|
|
||||||
|
|
||||||
environment.plugins.prepend(
|
environment.plugins.prepend(
|
||||||
'DefinePlugin',
|
'DefinePlugin',
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
__PUSHER__: {
|
|
||||||
token: `"${token}"`,
|
|
||||||
cluster: `"${cluster}"`,
|
|
||||||
},
|
|
||||||
__FB_ID__: `"${fbAppID}"`,
|
__FB_ID__: `"${fbAppID}"`,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,17 +26,6 @@ development:
|
||||||
|
|
||||||
Following changes has to be in `config/application.yml`
|
Following changes has to be in `config/application.yml`
|
||||||
|
|
||||||
### Configure Pusher
|
|
||||||
|
|
||||||
Chatwoot uses [Pusher](https://pusher.com/) to handle realtime messages. Create a free account on Pusher and fill the following environment values.
|
|
||||||
|
|
||||||
```yml
|
|
||||||
pusher_app_id: ''
|
|
||||||
pusher_key: ''
|
|
||||||
pusher_secret: ''
|
|
||||||
pusher_cluster: ''
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configure FB Channel
|
### Configure FB Channel
|
||||||
|
|
||||||
To use FB Channel, you have to create an Facebook app in developer portal. You can find more details about creating FB channels [here](https://developers.facebook.com/docs/apps/#register)
|
To use FB Channel, you have to create an Facebook app in developer portal. You can find more details about creating FB channels [here](https://developers.facebook.com/docs/apps/#register)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/polyfill": "^7.6.0",
|
"@babel/polyfill": "^7.6.0",
|
||||||
|
"@rails/actioncable": "^6.0.0",
|
||||||
"@rails/webpacker": "^4.0.7",
|
"@rails/webpacker": "^4.0.7",
|
||||||
"axios": "^0.19.0",
|
"axios": "^0.19.0",
|
||||||
"babel-helper-vue-jsx-merge-props": "^2.0.3",
|
"babel-helper-vue-jsx-merge-props": "^2.0.3",
|
||||||
|
@ -27,7 +28,6 @@
|
||||||
"js-cookie": "~2.1.3",
|
"js-cookie": "~2.1.3",
|
||||||
"md5": "~2.2.1",
|
"md5": "~2.2.1",
|
||||||
"moment": "~2.19.3",
|
"moment": "~2.19.3",
|
||||||
"pusher-js": "~4.0.0",
|
|
||||||
"query-string": "5",
|
"query-string": "5",
|
||||||
"spinkit": "~1.2.5",
|
"spinkit": "~1.2.5",
|
||||||
"tween.js": "~16.6.0",
|
"tween.js": "~16.6.0",
|
||||||
|
|
|
@ -1,10 +1,3 @@
|
||||||
#pusher
|
|
||||||
|
|
||||||
pusher_app_id: ''
|
|
||||||
pusher_key: ''
|
|
||||||
pusher_secret: ''
|
|
||||||
pusher_cluster: ''
|
|
||||||
|
|
||||||
#fb app
|
#fb app
|
||||||
fb_verify_token: ''
|
fb_verify_token: ''
|
||||||
fb_app_secret: ''
|
fb_app_secret: ''
|
||||||
|
|
15
spec/controllers/room_channel_spec.rb
Normal file
15
spec/controllers/room_channel_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe RoomChannel, type: :channel do
|
||||||
|
let!(:user) { create(:user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_connection
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'subscribes to a stream when pubsub_token is provided' do
|
||||||
|
subscribe(pubsub_token: user.uid)
|
||||||
|
expect(subscription).to be_confirmed
|
||||||
|
expect(subscription).to have_stream_for(user.uid)
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,8 @@ require File.expand_path('../config/environment', __dir__)
|
||||||
# Prevent database truncation if the environment is production
|
# Prevent database truncation if the environment is production
|
||||||
abort('The Rails environment is running in production mode!') if Rails.env.production?
|
abort('The Rails environment is running in production mode!') if Rails.env.production?
|
||||||
require 'rspec/rails'
|
require 'rspec/rails'
|
||||||
|
require 'action_cable/testing/rspec'
|
||||||
|
|
||||||
# Add additional requires below this line. Rails is not loaded until this point!
|
# Add additional requires below this line. Rails is not loaded until this point!
|
||||||
|
|
||||||
# Requires supporting ruby files with custom matchers and macros, etc, in
|
# Requires supporting ruby files with custom matchers and macros, etc, in
|
||||||
|
|
25
yarn.lock
25
yarn.lock
|
@ -966,6 +966,11 @@
|
||||||
"@nodelib/fs.scandir" "2.1.3"
|
"@nodelib/fs.scandir" "2.1.3"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
|
"@rails/actioncable@^6.0.0":
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.0.0.tgz#8bff9c902be1531ef7a9e191562e9771efcfdfe1"
|
||||||
|
integrity sha512-DieouotYHpI6k2EGTCnh1eMvD3W8p3zqjWXEYj4z0khJ+A0qQ5tHxihjTEkio54MVwqHt1DcpUm2woh2n/alCA==
|
||||||
|
|
||||||
"@rails/webpacker@^4.0.7":
|
"@rails/webpacker@^4.0.7":
|
||||||
version "4.0.7"
|
version "4.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/@rails/webpacker/-/webpacker-4.0.7.tgz#268571bf974e78ce57eca9fa478f5bd97fd5182c"
|
resolved "https://registry.yarnpkg.com/@rails/webpacker/-/webpacker-4.0.7.tgz#268571bf974e78ce57eca9fa478f5bd97fd5182c"
|
||||||
|
@ -4431,13 +4436,6 @@ fastq@^1.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
reusify "^1.0.0"
|
reusify "^1.0.0"
|
||||||
|
|
||||||
faye-websocket@0.9.4:
|
|
||||||
version "0.9.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.9.4.tgz#885934c79effb0409549e0c0a3801ed17a40cdad"
|
|
||||||
integrity sha1-iFk0x57/sECVSeDAo4Ae0XpAza0=
|
|
||||||
dependencies:
|
|
||||||
websocket-driver ">=0.5.1"
|
|
||||||
|
|
||||||
faye-websocket@^0.10.0:
|
faye-websocket@^0.10.0:
|
||||||
version "0.10.0"
|
version "0.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
|
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
|
||||||
|
@ -8730,14 +8728,6 @@ punycode@^2.1.0, punycode@^2.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||||
|
|
||||||
pusher-js@~4.0.0:
|
|
||||||
version "4.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/pusher-js/-/pusher-js-4.0.0.tgz#3f53f9a8e2cb55b89b7724881615f891f200ab8e"
|
|
||||||
integrity sha1-P1P5qOLLVbibdySIFhX4kfIAq44=
|
|
||||||
dependencies:
|
|
||||||
faye-websocket "0.9.4"
|
|
||||||
xmlhttprequest "^1.8.0"
|
|
||||||
|
|
||||||
q@^1.1.2:
|
q@^1.1.2:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||||
|
@ -10975,11 +10965,6 @@ xml-name-validator@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||||
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
|
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
|
||||||
|
|
||||||
xmlhttprequest@^1.8.0:
|
|
||||||
version "1.8.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
|
|
||||||
integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=
|
|
||||||
|
|
||||||
xtend@^4.0.0, xtend@~4.0.1:
|
xtend@^4.0.0, xtend@~4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
|
|
Loading…
Reference in a new issue