parent
c0354364ff
commit
f4358d9993
28 changed files with 144 additions and 165 deletions
|
@ -1,4 +1 @@
|
|||
pusher_cluster=
|
||||
pusher_key=
|
||||
|
||||
fb_app_id=
|
||||
|
|
|
@ -43,7 +43,6 @@ module.exports = {
|
|||
},
|
||||
globals: {
|
||||
__WEBPACK_ENV__: true,
|
||||
__PUSHER__: true,
|
||||
__FB_APP_ID__: true,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -40,7 +40,7 @@ Lint/UselessAssignment:
|
|||
Exclude:
|
||||
- 'app/controllers/api/v1/callbacks_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/models/channel/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'
|
||||
|
||||
##--- gems for pubsub service ---##
|
||||
gem 'pusher'
|
||||
gem 'wisper', '2.0.0'
|
||||
|
||||
##--- gems for reporting ---##
|
||||
|
@ -72,6 +71,7 @@ group :development do
|
|||
end
|
||||
|
||||
group :test do
|
||||
gem 'action-cable-testing'
|
||||
gem 'mock_redis'
|
||||
gem 'shoulda-matchers'
|
||||
end
|
||||
|
|
19
Gemfile.lock
19
Gemfile.lock
|
@ -113,6 +113,8 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
action-cable-testing (0.6.0)
|
||||
actioncable (>= 5.0)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
ast (2.4.0)
|
||||
|
@ -161,13 +163,6 @@ GEM
|
|||
coderay (1.1.2)
|
||||
coercible (1.0.0)
|
||||
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)
|
||||
connection_pool (2.2.2)
|
||||
crass (1.0.5)
|
||||
|
@ -211,7 +206,6 @@ GEM
|
|||
httparty (0.17.1)
|
||||
mime-types (~> 3.0)
|
||||
multi_xml (>= 0.5.2)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.7.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
ice_nine (0.11.2)
|
||||
|
@ -269,7 +263,6 @@ GEM
|
|||
minitest (5.12.2)
|
||||
mock_redis (0.22.0)
|
||||
msgpack (1.3.1)
|
||||
multi_json (1.14.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.1.1)
|
||||
naught (1.1.0)
|
||||
|
@ -292,11 +285,6 @@ GEM
|
|||
puma (3.12.1)
|
||||
pundit (2.1.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-cache (1.9.0)
|
||||
rack (>= 0.4)
|
||||
|
@ -452,6 +440,7 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
action-cable-testing
|
||||
acts-as-taggable-on!
|
||||
attr_extras
|
||||
bootsnap
|
||||
|
@ -459,7 +448,6 @@ DEPENDENCIES
|
|||
byebug
|
||||
carrierwave-aws
|
||||
chargebee (~> 2)
|
||||
coffee-rails
|
||||
devise!
|
||||
devise_token_auth!
|
||||
facebook-messenger
|
||||
|
@ -480,7 +468,6 @@ DEPENDENCIES
|
|||
pry-rails
|
||||
puma (~> 3.0)
|
||||
pundit
|
||||
pusher
|
||||
rack-cors
|
||||
rails (~> 6)!
|
||||
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
|
||||
|
||||
def listeners
|
||||
[PusherListener.instance]
|
||||
[ActionCableListener.instance]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
export default {
|
||||
APP_BASE_URL: '/',
|
||||
PUSHER: __PUSHER__,
|
||||
get apiURL() {
|
||||
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', () => {
|
||||
return {
|
||||
APP_BASE_URL: '/',
|
||||
PUSHER: false,
|
||||
get apiUrl() {
|
||||
return `${this.APP_BASE_URL}/`;
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@ import * as types from '../mutation-types';
|
|||
import router from '../../routes';
|
||||
import authAPI from '../../api/auth';
|
||||
import createAxios from '../../helper/APIHelper';
|
||||
import vuePusher from '../../helper/pusher';
|
||||
import actionCable from '../../helper/actionCable';
|
||||
// initial state
|
||||
const state = {
|
||||
currentUser: {
|
||||
|
@ -61,7 +61,7 @@ const actions = {
|
|||
.then(() => {
|
||||
commit(types.default.SET_CURRENT_USER);
|
||||
window.axios = createAxios(axios);
|
||||
window.pusher = vuePusher.init(Vue);
|
||||
actionCable.init(Vue);
|
||||
router.replace({ name: 'home' });
|
||||
resolve();
|
||||
})
|
||||
|
|
|
@ -168,7 +168,7 @@ const mutations = {
|
|||
_state.chatStatusFilter = data;
|
||||
},
|
||||
|
||||
// Update assignee on pusher message
|
||||
// Update assignee on action cable message
|
||||
[types.default.UPDATE_ASSIGNEE](_state, payload) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === payload.id);
|
||||
chat.meta.assignee = payload.assignee;
|
||||
|
|
|
@ -24,7 +24,7 @@ import createAxios from '../dashboard/helper/APIHelper';
|
|||
import commonHelpers from '../dashboard/helper/commons';
|
||||
import router from '../dashboard/routes';
|
||||
import store from '../dashboard/store';
|
||||
import vuePusher from '../dashboard/helper/pusher';
|
||||
import vueActionCable from '../dashboard/helper/actionCable';
|
||||
import constants from '../dashboard/constants';
|
||||
|
||||
Vue.config.env = process.env;
|
||||
|
@ -58,7 +58,7 @@ window.onload = () => {
|
|||
components: { App },
|
||||
template: '<App/>',
|
||||
}).$mount('#app');
|
||||
window.pusher = vuePusher.init();
|
||||
vueActionCable.init();
|
||||
};
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
|
|
|
@ -1,46 +1,53 @@
|
|||
class PusherListener < BaseListener
|
||||
class ActionCableListener < BaseListener
|
||||
include Events::Types
|
||||
|
||||
def conversation_created(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
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
|
||||
|
||||
def conversation_read(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
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
|
||||
|
||||
def message_created(event)
|
||||
message, account, timestamp = extract_message_and_account(event)
|
||||
conversation = message.conversation
|
||||
members = conversation.inbox.members.pluck(:pubsub_token)
|
||||
|
||||
Pusher.trigger(members, MESSAGE_CREATED, message.push_event_data) if members.present?
|
||||
send_to_members(members, MESSAGE_CREATED, message.push_event_data)
|
||||
end
|
||||
|
||||
def conversation_reopened(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
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
|
||||
|
||||
def conversation_lock_toggle(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
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
|
||||
|
||||
def assignee_changed(event)
|
||||
conversation, account, timestamp = extract_conversation_and_account(event)
|
||||
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
|
||||
|
||||
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)
|
||||
# Enqueue sidekiq job to push event to corresponding channel
|
||||
end
|
|
@ -4,7 +4,7 @@ module Pubsubable
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
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
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ class User < ApplicationRecord
|
|||
:validatable,
|
||||
: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
|
||||
|
||||
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 });
|
||||
|
||||
const {
|
||||
pusher_cluster: cluster,
|
||||
pusher_key: token,
|
||||
fb_app_id: fbAppID,
|
||||
} = process.env;
|
||||
const { fb_app_id: fbAppID } = process.env;
|
||||
|
||||
environment.plugins.prepend(
|
||||
'DefinePlugin',
|
||||
new webpack.DefinePlugin({
|
||||
__PUSHER__: {
|
||||
token: `"${token}"`,
|
||||
cluster: `"${cluster}"`,
|
||||
},
|
||||
__FB_ID__: `"${fbAppID}"`,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -26,17 +26,6 @@ development:
|
|||
|
||||
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
|
||||
|
||||
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": {
|
||||
"@babel/polyfill": "^7.6.0",
|
||||
"@rails/actioncable": "^6.0.0",
|
||||
"@rails/webpacker": "^4.0.7",
|
||||
"axios": "^0.19.0",
|
||||
"babel-helper-vue-jsx-merge-props": "^2.0.3",
|
||||
|
@ -27,7 +28,6 @@
|
|||
"js-cookie": "~2.1.3",
|
||||
"md5": "~2.2.1",
|
||||
"moment": "~2.19.3",
|
||||
"pusher-js": "~4.0.0",
|
||||
"query-string": "5",
|
||||
"spinkit": "~1.2.5",
|
||||
"tween.js": "~16.6.0",
|
||||
|
|
|
@ -1,10 +1,3 @@
|
|||
#pusher
|
||||
|
||||
pusher_app_id: ''
|
||||
pusher_key: ''
|
||||
pusher_secret: ''
|
||||
pusher_cluster: ''
|
||||
|
||||
#fb app
|
||||
fb_verify_token: ''
|
||||
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
|
||||
abort('The Rails environment is running in production mode!') if Rails.env.production?
|
||||
require 'rspec/rails'
|
||||
require 'action_cable/testing/rspec'
|
||||
|
||||
# Add additional requires below this line. Rails is not loaded until this point!
|
||||
|
||||
# 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"
|
||||
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":
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@rails/webpacker/-/webpacker-4.0.7.tgz#268571bf974e78ce57eca9fa478f5bd97fd5182c"
|
||||
|
@ -4431,13 +4436,6 @@ fastq@^1.6.0:
|
|||
dependencies:
|
||||
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:
|
||||
version "0.10.0"
|
||||
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"
|
||||
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:
|
||||
version "1.5.1"
|
||||
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"
|
||||
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:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
|
|
Loading…
Reference in a new issue