[IMPROVEMENT] Move toggle and inquiry to Enterprise Edition license (#2426)

* [IMPROVEMENT] Move toggle and inquiry to Enterprise

* Move inquiry stream to ee

* Emit inquiry subscribe

* imports to ee last

* Add readme to ee

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
This commit is contained in:
Diego Mello 2020-08-28 16:41:08 -03:00 committed by GitHub
parent 0351276e30
commit 7172c028cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 152 additions and 76 deletions

43
app/ee/LICENSE Normal file
View File

@ -0,0 +1,43 @@
The Rocket.Chat Enterprise Edition (EE) license (the "EE License")
Copyright (c) 2015-2020 Rocket.Chat Technologies Corp.
With regard to the Rocket.Chat Software:
This software and associated documentation files (the "Software") may only be
used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the Rocket.Chat Subscription Terms of Service,
available at https://rocket.chat/terms (the "EE Terms"), or other agreement
governing the use of the Software, as agreed by you and Rocket.Chat, and otherwise
have a valid Rocket.Chat Enterprise Edition subscription for the correct number
of user seats. Subject to the foregoing sentence, you are free to modify this
Software and publish patches to the Software. You agree that Rocket.Chat and/or
its licensors (as applicable) retain all right, title and interest in and to all
such modifications and/or patches, and all such modifications and/or patches may
only be used, copied, modified, displayed, distributed, or otherwise exploited
with a valid Rocket.Chat Enterprise Edition subscription for the correct number
of user seats. Notwithstanding the foregoing, you may copy and modify the Software
for development and testing purposes, without requiring a Subscription. You agree
that Rocket.Chat and/or its licensors (as applicable) retain all right, title and
interest in and to all such modifications. You are not granted any other rights
beyond what is expressly stated herein. Subject to the foregoing, it is forbidden
to copy, merge, publish, distribute, sublicense, and/or sell the Software.
This EE License applies only to the part of this Software that is not distributed
as part of Rocket.Chat Community Edition (CE). Any part of this Software distributed
as part of Rocket.Chat CE or is served client-side as an image, font, cascading
stylesheet (CSS), file which produces or is compiled, arranged, augmented, or combined
into client-side JavaScript, in whole or in part, is copyrighted under the MIT Expat
license. The full text of this EE License shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
For all third-party components incorporated into the Rocket.Chat Software,
those components are licensed under the original license provided by the owner
of the applicable component.

4
app/ee/README.md Normal file
View File

@ -0,0 +1,4 @@
### How to remove ee folder
If you're willing to remove Enterprise Edition features from your app, follow this diff:
https://github.com/RocketChat/Rocket.Chat.ReactNative/compare/develop...no-ee?expand=1

View File

@ -1,4 +1,4 @@
import * as types from './actionsTypes';
import * as types from '../../../actions/actionsTypes';
export function inquirySetEnabled(enabled) {
return {

View File

@ -7,11 +7,12 @@ import PropTypes from 'prop-types';
import Touch from '../../../utils/touch';
import { CustomIcon } from '../../../lib/Icons';
import I18n from '../../../i18n';
import styles from '../styles';
import styles from '../../../views/RoomsListView/styles';
import { themes, SWITCH_TRACK_COLOR } from '../../../constants/colors';
import { withTheme } from '../../../theme';
import UnreadBadge from '../../../presentation/UnreadBadge';
import RocketChat from '../../../lib/rocketchat';
import { isOmnichannelStatusAvailable, changeLivechatStatus } from '../lib';
const OmnichannelStatus = memo(({
searching, goQueue, theme, queueSize, inquiryEnabled, user
@ -19,16 +20,16 @@ const OmnichannelStatus = memo(({
if (searching > 0 || !(RocketChat.isOmnichannelModuleAvailable() && user?.roles?.includes('livechat-agent'))) {
return null;
}
const [status, setStatus] = useState(user?.statusLivechat === 'available');
const [status, setStatus] = useState(isOmnichannelStatusAvailable(user));
useEffect(() => {
setStatus(user.statusLivechat === 'available');
setStatus(isOmnichannelStatusAvailable(user));
}, [user.statusLivechat]);
const toggleLivechat = async() => {
try {
setStatus(v => !v);
await RocketChat.changeLivechatStatus();
await changeLivechatStatus();
} catch {
setStatus(v => !v);
}

View File

@ -0,0 +1,40 @@
import subscribeInquiry from './subscriptions/inquiry';
import RocketChat from '../../../lib/rocketchat';
import EventEmitter from '../../../utils/events';
export const isOmnichannelStatusAvailable = user => user?.statusLivechat === 'available';
// RC 0.26.0
export const changeLivechatStatus = () => RocketChat.methodCallWrapper('livechat:changeLivechatStatus');
// RC 2.4.0
export const getInquiriesQueued = () => RocketChat.sdk.get('livechat/inquiries.queued');
// this inquiry is added to the db by the subscriptions stream
// and will be removed by the queue stream
// RC 2.4.0
export const takeInquiry = inquiryId => RocketChat.methodCallWrapper('livechat:takeInquiry', inquiryId);
class Omnichannel {
constructor() {
this.inquirySub = null;
EventEmitter.addEventListener('INQUIRY_SUBSCRIBE', this.subscribeInquiry);
EventEmitter.addEventListener('INQUIRY_UNSUBSCRIBE', this.unsubscribeInquiry);
}
subscribeInquiry = async() => {
console.log('[RCRN] Subscribing to inquiry');
this.inquirySub = await subscribeInquiry();
}
unsubscribeInquiry = () => {
if (this.inquirySub) {
console.log('[RCRN] Unsubscribing from inquiry');
this.inquirySub.stop();
this.inquirySub = null;
}
}
}
// eslint-disable-next-line no-unused-vars
const omnichannel = new Omnichannel();

View File

@ -1,12 +1,12 @@
import log from '../../../utils/log';
import store from '../../createStore';
import RocketChat from '../../rocketchat';
import log from '../../../../utils/log';
import store from '../../../../lib/createStore';
import RocketChat from '../../../../lib/rocketchat';
import {
inquiryRequest,
inquiryQueueAdd,
inquiryQueueUpdate,
inquiryQueueRemove
} from '../../../actions/inquiry';
} from '../../actions/inquiry';
const removeListener = listener => listener.stop();
@ -63,9 +63,9 @@ export default function subscribeInquiry() {
}
};
connectedListener = this.sdk.onStreamData('connected', handleConnection);
disconnectedListener = this.sdk.onStreamData('close', handleConnection);
queueListener = this.sdk.onStreamData(streamTopic, handleQueueMessageReceived);
connectedListener = RocketChat.onStreamData('connected', handleConnection);
disconnectedListener = RocketChat.onStreamData('close', handleConnection);
queueListener = RocketChat.onStreamData(streamTopic, handleQueueMessageReceived);
try {
const { user } = store.getState().login;
@ -74,13 +74,13 @@ export default function subscribeInquiry() {
const { departments } = result;
if (!departments.length || RocketChat.hasRole('livechat-manager')) {
this.sdk.subscribe(streamTopic, 'public').catch(e => console.log(e));
RocketChat.subscribe(streamTopic, 'public').catch(e => console.log(e));
}
const departmentIds = departments.map(({ departmentId }) => departmentId);
departmentIds.forEach((departmentId) => {
// subscribe to all departments of the agent
this.sdk.subscribe(streamTopic, `department/${ departmentId }`).catch(e => console.log(e));
RocketChat.subscribe(streamTopic, `department/${ departmentId }`).catch(e => console.log(e));
});
}
});

View File

@ -1,4 +1,4 @@
import { INQUIRY } from '../actions/actionsTypes';
import { INQUIRY } from '../../../actions/actionsTypes';
const initialState = {
enabled: false,

View File

@ -1,24 +1,26 @@
import { put, takeLatest, select } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat';
import * as types from '../../../actions/actionsTypes';
import RocketChat from '../../../lib/rocketchat';
import EventEmitter from '../../../utils/events';
import { inquirySuccess, inquiryFailure, inquirySetEnabled } from '../actions/inquiry';
import { isOmnichannelStatusAvailable, getInquiriesQueued } from '../lib';
const handleRequest = function* handleRequest() {
try {
const routingConfig = yield RocketChat.getRoutingConfig();
const statusLivechat = yield select(state => state.login.user.statusLivechat);
const user = yield select(state => state.login.user);
// if routingConfig showQueue is enabled and omnichannel is enabled
const showQueue = routingConfig.showQueue && statusLivechat === 'available';
const showQueue = routingConfig.showQueue && isOmnichannelStatusAvailable(user);
if (showQueue) {
// get all the current chats on the queue
const result = yield RocketChat.getInquiriesQueued();
const result = yield getInquiriesQueued();
if (result.success) {
const { inquiries } = result;
// subscribe to inquiry queue changes
RocketChat.subscribeInquiry();
EventEmitter.emit('INQUIRY_SUBSCRIBE');
// put request result on redux state
yield put(inquirySuccess(inquiries));

View File

@ -4,20 +4,20 @@ import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import isEqual from 'react-fast-compare';
import I18n from '../i18n';
import RoomItem, { ROW_HEIGHT } from '../presentation/RoomItem';
import { MAX_SIDEBAR_WIDTH } from '../constants/tablet';
import { isTablet, isIOS } from '../utils/deviceInfo';
import { getUserSelector } from '../selectors/login';
import { withTheme } from '../theme';
import { withDimensions } from '../dimensions';
import SafeAreaView from '../containers/SafeAreaView';
import { themes } from '../constants/colors';
import StatusBar from '../containers/StatusBar';
import { goRoom } from '../utils/goRoom';
import { CloseModalButton } from '../containers/HeaderButton';
import RocketChat from '../lib/rocketchat';
import { logEvent, events } from '../utils/log';
import I18n from '../../../i18n';
import RoomItem, { ROW_HEIGHT } from '../../../presentation/RoomItem';
import { MAX_SIDEBAR_WIDTH } from '../../../constants/tablet';
import { isTablet, isIOS } from '../../../utils/deviceInfo';
import { getUserSelector } from '../../../selectors/login';
import { withTheme } from '../../../theme';
import { withDimensions } from '../../../dimensions';
import SafeAreaView from '../../../containers/SafeAreaView';
import { themes } from '../../../constants/colors';
import StatusBar from '../../../containers/StatusBar';
import { goRoom } from '../../../utils/goRoom';
import { CloseModalButton } from '../../../containers/HeaderButton';
import RocketChat from '../../../lib/rocketchat';
import { logEvent, events } from '../../../utils/log';
import { getInquiryQueueSelector } from '../selectors/inquiry';
const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12;

View File

@ -19,7 +19,6 @@ import {
} from '../actions/share';
import subscribeRooms from './methods/subscriptions/rooms';
import subscribeInquiry from './methods/subscriptions/inquiry';
import getUsersPresence, { getUserPresence, subscribeUsersPresence } from './methods/getUsersPresence';
import protectedFunction from './methods/helpers/protectedFunction';
@ -54,6 +53,7 @@ import { twoFactor } from '../utils/twoFactor';
import { selectServerFailure } from '../actions/server';
import { useSsl } from '../utils/url';
import UserPreferences from './userPreferences';
import EventEmitter from '../utils/events';
const TOKEN_KEY = 'reactnativemeteor_usertoken';
const CURRENT_SERVER = 'currentServer';
@ -78,15 +78,6 @@ const RocketChat = {
}
}
},
async subscribeInquiry() {
if (!this.inquirySub) {
try {
this.inquirySub = await subscribeInquiry.call(this);
} catch (e) {
log(e);
}
}
},
canOpenRoom,
createChannel({
name, users, type, readOnly, broadcast
@ -211,10 +202,7 @@ const RocketChat = {
this.roomsSub = null;
}
if (this.inquirySub) {
this.inquirySub.stop();
this.inquirySub = null;
}
EventEmitter.emit('INQUIRY_UNSUBSCRIBE');
if (this.sdk) {
this.sdk.disconnect();
@ -858,20 +846,6 @@ const RocketChat = {
// RC 2.2.0
return this.sdk.get('livechat/custom-fields');
},
changeLivechatStatus() {
// RC 0.26.0
return this.methodCallWrapper('livechat:changeLivechatStatus');
},
getInquiriesQueued() {
// RC 2.4.0
return this.sdk.get('livechat/inquiries.queued');
},
takeInquiry(inquiryId) {
// this inquiry is added to the db by the subscriptions stream
// and will be removed by the queue stream
// RC 2.4.0
return this.methodCallWrapper('livechat:takeInquiry', inquiryId);
},
getUidDirectMessage(room) {
const { id: userId } = reduxStore.getState().login.user;

View File

@ -16,9 +16,10 @@ import activeUsers from './activeUsers';
import usersTyping from './usersTyping';
import inviteLinks from './inviteLinks';
import createDiscussion from './createDiscussion';
import inquiry from './inquiry';
import enterpriseModules from './enterpriseModules';
import inquiry from '../ee/omnichannel/reducers/inquiry';
export default combineReducers({
settings,
login,

View File

@ -10,7 +10,8 @@ import state from './state';
import deepLinking from './deepLinking';
import inviteLinks from './inviteLinks';
import createDiscussion from './createDiscussion';
import inquiry from './inquiry';
import inquiry from '../ee/omnichannel/sagas/inquiry';
const root = function* root() {
yield all([

View File

@ -14,7 +14,6 @@ import {
loginFailure, loginSuccess, setUser, logout
} from '../actions/login';
import { roomsRequest } from '../actions/rooms';
import { inquiryRequest, inquiryReset } from '../actions/inquiry';
import { toMomentLocale } from '../utils/moment';
import RocketChat from '../lib/rocketchat';
import log, { logEvent, events } from '../utils/log';
@ -27,6 +26,9 @@ import { localAuthenticate } from '../utils/localAuthentication';
import { setActiveUsers } from '../actions/activeUsers';
import UserPreferences from '../lib/userPreferences';
import { inquiryRequest, inquiryReset } from '../ee/omnichannel/actions/inquiry';
import { isOmnichannelStatusAvailable } from '../ee/omnichannel/lib';
const getServer = state => state.server.server;
const loginWithPasswordCall = args => RocketChat.loginWithPassword(args);
const loginCall = args => RocketChat.login(args);
@ -88,7 +90,7 @@ const fetchUsersPresence = function* fetchUserPresence() {
const fetchEnterpriseModules = function* fetchEnterpriseModules({ user }) {
yield RocketChat.getEnterpriseModules();
if (user && user.statusLivechat === 'available' && RocketChat.isOmnichannelModuleAvailable()) {
if (isOmnichannelStatusAvailable(user) && RocketChat.isOmnichannelModuleAvailable()) {
yield put(inquiryRequest());
}
};
@ -221,7 +223,7 @@ const handleSetUser = function* handleSetUser({ user }) {
}
if (user?.statusLivechat && RocketChat.isOmnichannelModuleAvailable()) {
if (user.statusLivechat === 'available') {
if (isOmnichannelStatusAvailable(user)) {
yield put(inquiryRequest());
} else {
yield put(inquiryReset());

View File

@ -18,7 +18,8 @@ import I18n from '../i18n';
import { BASIC_AUTH_KEY, setBasicAuth } from '../utils/fetch';
import { appStart, ROOT_INSIDE, ROOT_OUTSIDE } from '../actions/app';
import UserPreferences from '../lib/userPreferences';
import { inquiryReset } from '../actions/inquiry';
import { inquiryReset } from '../ee/omnichannel/actions/inquiry';
const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
try {

View File

@ -30,7 +30,6 @@ import PickerView from '../views/PickerView';
import ThreadMessagesView from '../views/ThreadMessagesView';
import MarkdownTableView from '../views/MarkdownTableView';
import ReadReceiptsView from '../views/ReadReceiptView';
import QueueListView from '../views/QueueListView';
// Profile Stack
import ProfileView from '../views/ProfileView';
@ -59,6 +58,8 @@ import StatusView from '../views/StatusView';
import ShareView from '../views/ShareView';
import CreateDiscussionView from '../views/CreateDiscussionView';
import QueueListView from '../ee/omnichannel/views/QueueListView';
// ChatsStackNavigator
const ChatsStack = createStackNavigator();
const ChatsStackNavigator = () => {

View File

@ -41,7 +41,6 @@ import ScreenLockConfigView from '../../views/ScreenLockConfigView';
import AdminPanelView from '../../views/AdminPanelView';
import NewMessageView from '../../views/NewMessageView';
import CreateChannelView from '../../views/CreateChannelView';
import QueueListView from '../../views/QueueListView';
import UserPreferencesView from '../../views/UserPreferencesView';
import UserNotificationPrefView from '../../views/UserNotificationPreferencesView';
@ -55,6 +54,8 @@ import CreateDiscussionView from '../../views/CreateDiscussionView';
import { setKeyCommands, deleteKeyCommands } from '../../commands';
import ShareView from '../../views/ShareView';
import QueueListView from '../../ee/omnichannel/views/QueueListView';
// ChatsStackNavigator
const ChatsStack = createStackNavigator();
const ChatsStackNavigator = React.memo(() => {

View File

@ -56,6 +56,8 @@ import SafeAreaView from '../../containers/SafeAreaView';
import { withDimensions } from '../../dimensions';
import { getHeaderTitlePosition } from '../../containers/Header';
import { takeInquiry } from '../../ee/omnichannel/lib';
const stateAttrsUpdate = [
'joined',
'lastOpen',
@ -689,7 +691,7 @@ class RoomView extends React.Component {
const { room } = this.state;
if (this.isOmnichannel) {
await RocketChat.takeInquiry(room._id);
await takeInquiry(room._id);
} else {
await RocketChat.joinRoom(this.rid, this.t);
}

View File

@ -2,7 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import Sort from './Sort';
import OmnichannelStatus from './OmnichannelStatus';
import OmnichannelStatus from '../../../ee/omnichannel/containers/OmnichannelStatus';
const ListHeader = React.memo(({
searching,

View File

@ -63,7 +63,9 @@ import SafeAreaView from '../../containers/SafeAreaView';
import Header, { getHeaderTitlePosition } from '../../containers/Header';
import { withDimensions } from '../../dimensions';
import { showErrorAlert, showConfirmationAlert } from '../../utils/info';
import { getInquiryQueueSelector } from '../../selectors/inquiry';
import { getInquiryQueueSelector } from '../../ee/omnichannel/selectors/inquiry';
import { changeLivechatStatus, isOmnichannelStatusAvailable } from '../../ee/omnichannel/lib';
const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12;
const CHATS_HEADER = 'Chats';
@ -701,13 +703,13 @@ class RoomsListView extends React.Component {
} = this.props;
// if not-available, prompt to change to available
if (user?.statusLivechat !== 'available') {
if (!isOmnichannelStatusAvailable(user)) {
showConfirmationAlert({
message: I18n.t('Omnichannel_enable_alert'),
confirmationText: I18n.t('Yes'),
onPress: async() => {
try {
await RocketChat.changeLivechatStatus();
await changeLivechatStatus();
} catch {
// Do nothing
}