From 58641ec5aa2390364d60cb4f6c8b54ce31c29858 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Wed, 12 Aug 2020 15:18:15 -0300 Subject: [PATCH 01/20] Add enterpriseModules on Redux --- app/actions/actionsTypes.js | 1 + app/actions/enterpriseModules.js | 14 ++++++++++++++ app/lib/rocketchat.js | 3 +++ app/reducers/enterpriseModules.js | 14 ++++++++++++++ app/reducers/index.js | 4 +++- app/sagas/login.js | 4 ++++ 6 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 app/actions/enterpriseModules.js create mode 100644 app/reducers/enterpriseModules.js diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index 3fec0020120..1deeb4376d6 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -66,3 +66,4 @@ export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [ ]); export const SETTINGS = createRequestTypes('SETTINGS', ['CLEAR', 'ADD']); export const APP_STATE = createRequestTypes('APP_STATE', ['FOREGROUND', 'BACKGROUND']); +export const ENTERPRISE_MODULES = createRequestTypes('ENTERPRISE_MODULES', ['CLEAR', 'SET']); diff --git a/app/actions/enterpriseModules.js b/app/actions/enterpriseModules.js new file mode 100644 index 00000000000..74b0978734f --- /dev/null +++ b/app/actions/enterpriseModules.js @@ -0,0 +1,14 @@ +import { ENTERPRISE_MODULES } from './actionsTypes'; + +export function setEnterpriseModules(modules) { + return { + type: ENTERPRISE_MODULES.SET, + payload: modules + }; +} + +export function clearEnterpriseModules() { + return { + type: ENTERPRISE_MODULES.CLEAR + }; +} diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 52c09f93f38..3c1efab39f5 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -1308,6 +1308,9 @@ const RocketChat = { useInviteToken(token) { // RC 2.4.0 return this.post('useInviteToken', { token }); + }, + getLicenseModules() { + return this.methodCallWrapper('license:getModules'); } }; diff --git a/app/reducers/enterpriseModules.js b/app/reducers/enterpriseModules.js new file mode 100644 index 00000000000..2f1a7ac9a14 --- /dev/null +++ b/app/reducers/enterpriseModules.js @@ -0,0 +1,14 @@ +import { ENTERPRISE_MODULES } from '../actions/actionsTypes'; + +const initialState = []; + +export default (state = initialState, action) => { + switch (action.type) { + case ENTERPRISE_MODULES.SET: + return action.payload; + case ENTERPRISE_MODULES.CLEAR: + return initialState; + default: + return state; + } +}; diff --git a/app/reducers/index.js b/app/reducers/index.js index 968254ffd48..05bcb534f9a 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -17,6 +17,7 @@ import usersTyping from './usersTyping'; import inviteLinks from './inviteLinks'; import createDiscussion from './createDiscussion'; import inquiry from './inquiry'; +import enterpriseModules from './enterpriseModules'; export default combineReducers({ settings, @@ -36,5 +37,6 @@ export default combineReducers({ usersTyping, inviteLinks, createDiscussion, - inquiry + inquiry, + enterpriseModules }); diff --git a/app/sagas/login.js b/app/sagas/login.js index 01ab2845397..d0592c457f2 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -26,6 +26,7 @@ import { inviteLinksRequest } from '../actions/inviteLinks'; import { showErrorAlert } from '../utils/info'; import { localAuthenticate } from '../utils/localAuthentication'; import { setActiveUsers } from '../actions/activeUsers'; +import { setEnterpriseModules } from '../actions/enterpriseModules'; const getServer = state => state.server.server; const loginWithPasswordCall = args => RocketChat.loginWithPassword(args); @@ -102,6 +103,9 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) { yield fork(registerPushToken); yield fork(fetchUsersPresence); + const modules = yield RocketChat.getLicenseModules(); + yield put(setEnterpriseModules(modules)); + I18n.locale = user.language; moment.locale(toMomentLocale(user.language)); From 1b85b3c1c7d65a05fc38b2b9156309b9bab5ceb2 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Wed, 12 Aug 2020 18:08:04 -0300 Subject: [PATCH 02/20] Fetch enterprise modules and put on redux --- app/lib/database/model/Server.js | 2 + app/lib/database/model/serversMigrations.js | 11 +++++ app/lib/database/schema/servers.js | 5 ++- app/lib/methods/getEnterpriseModules.js | 48 +++++++++++++++++++++ app/lib/rocketchat.js | 6 +-- app/sagas/login.js | 9 ++-- app/sagas/selectServer.js | 1 + 7 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 app/lib/methods/getEnterpriseModules.js diff --git a/app/lib/database/model/Server.js b/app/lib/database/model/Server.js index d30b3a3f412..5e98ea95463 100644 --- a/app/lib/database/model/Server.js +++ b/app/lib/database/model/Server.js @@ -27,4 +27,6 @@ export default class Server extends Model { @field('biometry') biometry; @field('unique_id') uniqueID; + + @field('enterprise_modules') enterpriseModules; } diff --git a/app/lib/database/model/serversMigrations.js b/app/lib/database/model/serversMigrations.js index 8d74b043477..86995e5c4b5 100644 --- a/app/lib/database/model/serversMigrations.js +++ b/app/lib/database/model/serversMigrations.js @@ -37,6 +37,17 @@ export default schemaMigrations({ ] }) ] + }, + { + toVersion: 6, + steps: [ + addColumns({ + table: 'servers', + columns: [ + { name: 'enterprise_modules', type: 'string', isOptional: true } + ] + }) + ] } ] }); diff --git a/app/lib/database/schema/servers.js b/app/lib/database/schema/servers.js index b02859e104c..11c115ade47 100644 --- a/app/lib/database/schema/servers.js +++ b/app/lib/database/schema/servers.js @@ -1,7 +1,7 @@ import { appSchema, tableSchema } from '@nozbe/watermelondb'; export default appSchema({ - version: 5, + version: 6, tables: [ tableSchema({ name: 'users', @@ -29,7 +29,8 @@ export default appSchema({ { name: 'auto_lock', type: 'boolean', isOptional: true }, { name: 'auto_lock_time', type: 'number', isOptional: true }, { name: 'biometry', type: 'boolean', isOptional: true }, - { name: 'unique_id', type: 'string', isOptional: true } + { name: 'unique_id', type: 'string', isOptional: true }, + { name: 'enterprise_modules', type: 'string', isOptional: true } ] }) ] diff --git a/app/lib/methods/getEnterpriseModules.js b/app/lib/methods/getEnterpriseModules.js new file mode 100644 index 00000000000..34b9d99ac92 --- /dev/null +++ b/app/lib/methods/getEnterpriseModules.js @@ -0,0 +1,48 @@ +import semver from 'semver'; + +import reduxStore from '../createStore'; +import database from '../database'; +import log from '../../utils/log'; +import { setEnterpriseModules as setEnterpriseModulesAction } from '../../actions/enterpriseModules'; + +export async function setEnterpriseModules() { + try { + const { version: serverVersion, server: serverId } = reduxStore.getState().server; + if (serverVersion && semver.gte(semver.coerce(serverVersion), '3.1.0')) { + const serversDB = database.servers; + const serversCollection = serversDB.collections.get('servers'); + const server = await serversCollection.find(serverId); + if (server.enterpriseModules) { + reduxStore.dispatch(setEnterpriseModulesAction(server.enterpriseModules.split(','))); + } + } + } catch (e) { + log(e); + } +} + +export function getEnterpriseModules() { + return new Promise(async(resolve) => { + try { + const { version: serverVersion, server: serverId } = reduxStore.getState().server; + if (serverVersion && semver.gte(semver.coerce(serverVersion), '3.1.0')) { + const enterpriseModules = await this.methodCallWrapper('license:getModules'); + if (enterpriseModules) { + const serversDB = database.servers; + const serversCollection = serversDB.collections.get('servers'); + const server = await serversCollection.find(serverId); + await serversDB.action(async() => { + await server.update((s) => { + s.enterpriseModules = enterpriseModules.join(','); + }); + }); + reduxStore.dispatch(setEnterpriseModulesAction(enterpriseModules)); + } + return resolve(); + } + } catch (e) { + log(e); + } + return resolve(); + }); +} diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 3c1efab39f5..e8ef17a3cf0 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -30,6 +30,7 @@ import getSettings, { getLoginSettings, setSettings } from './methods/getSetting import getRooms from './methods/getRooms'; import getPermissions from './methods/getPermissions'; import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis'; +import { getEnterpriseModules, setEnterpriseModules } from './methods/getEnterpriseModules'; import getSlashCommands from './methods/getSlashCommands'; import getRoles from './methods/getRoles'; import canOpenRoom from './methods/canOpenRoom'; @@ -625,6 +626,8 @@ const RocketChat = { getPermissions, getCustomEmojis, setCustomEmojis, + getEnterpriseModules, + setEnterpriseModules, getSlashCommands, getRoles, parseSettings: settings => settings.reduce((ret, item) => { @@ -1308,9 +1311,6 @@ const RocketChat = { useInviteToken(token) { // RC 2.4.0 return this.post('useInviteToken', { token }); - }, - getLicenseModules() { - return this.methodCallWrapper('license:getModules'); } }; diff --git a/app/sagas/login.js b/app/sagas/login.js index d0592c457f2..124ed8bf5fd 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -26,7 +26,6 @@ import { inviteLinksRequest } from '../actions/inviteLinks'; import { showErrorAlert } from '../utils/info'; import { localAuthenticate } from '../utils/localAuthentication'; import { setActiveUsers } from '../actions/activeUsers'; -import { setEnterpriseModules } from '../actions/enterpriseModules'; const getServer = state => state.server.server; const loginWithPasswordCall = args => RocketChat.loginWithPassword(args); @@ -69,6 +68,10 @@ const fetchCustomEmojis = function* fetchCustomEmojis() { yield RocketChat.getCustomEmojis(); }; +const fetchEnterpriseModules = function* fetchEnterpriseModules() { + yield RocketChat.getEnterpriseModules(); +}; + const fetchRoles = function* fetchRoles() { yield RocketChat.getRoles(); }; @@ -102,9 +105,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) { yield fork(fetchSlashCommands); yield fork(registerPushToken); yield fork(fetchUsersPresence); - - const modules = yield RocketChat.getLicenseModules(); - yield put(setEnterpriseModules(modules)); + yield fork(fetchEnterpriseModules); I18n.locale = user.language; moment.locale(toMomentLocale(user.language)); diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js index 51728b8a80b..20b65c13d7d 100644 --- a/app/sagas/selectServer.js +++ b/app/sagas/selectServer.js @@ -115,6 +115,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch // and block the selectServerSuccess raising multiples errors RocketChat.setSettings(); RocketChat.setCustomEmojis(); + RocketChat.setEnterpriseModules(); let serverInfo; if (fetchVersion) { From aec8b5bfbbf492ec87a9ace3fe7c6e2042272793 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Wed, 12 Aug 2020 18:39:51 -0300 Subject: [PATCH 03/20] hasLicense --- ...tEnterpriseModules.js => enterpriseModules.js} | 7 +++++++ app/lib/rocketchat.js | 3 ++- app/sagas/login.js | 15 ++++++++++----- 3 files changed, 19 insertions(+), 6 deletions(-) rename app/lib/methods/{getEnterpriseModules.js => enterpriseModules.js} (87%) diff --git a/app/lib/methods/getEnterpriseModules.js b/app/lib/methods/enterpriseModules.js similarity index 87% rename from app/lib/methods/getEnterpriseModules.js rename to app/lib/methods/enterpriseModules.js index 34b9d99ac92..930cdce78d7 100644 --- a/app/lib/methods/getEnterpriseModules.js +++ b/app/lib/methods/enterpriseModules.js @@ -5,6 +5,8 @@ import database from '../database'; import log from '../../utils/log'; import { setEnterpriseModules as setEnterpriseModulesAction } from '../../actions/enterpriseModules'; +export const LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE = 'omnichannel-mobile-enterprise'; + export async function setEnterpriseModules() { try { const { version: serverVersion, server: serverId } = reduxStore.getState().server; @@ -46,3 +48,8 @@ export function getEnterpriseModules() { return resolve(); }); } + +export function hasLicense(module) { + const { enterpriseModules } = reduxStore.getState(); + return enterpriseModules.includes(module); +} diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index e8ef17a3cf0..3d94e0ead10 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -30,7 +30,7 @@ import getSettings, { getLoginSettings, setSettings } from './methods/getSetting import getRooms from './methods/getRooms'; import getPermissions from './methods/getPermissions'; import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis'; -import { getEnterpriseModules, setEnterpriseModules } from './methods/getEnterpriseModules'; +import { getEnterpriseModules, setEnterpriseModules, hasLicense } from './methods/enterpriseModules'; import getSlashCommands from './methods/getSlashCommands'; import getRoles from './methods/getRoles'; import canOpenRoom from './methods/canOpenRoom'; @@ -628,6 +628,7 @@ const RocketChat = { setCustomEmojis, getEnterpriseModules, setEnterpriseModules, + hasLicense, getSlashCommands, getRoles, parseSettings: settings => settings.reduce((ret, item) => { diff --git a/app/sagas/login.js b/app/sagas/login.js index 124ed8bf5fd..28d8ec2a3fe 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -26,6 +26,7 @@ import { inviteLinksRequest } from '../actions/inviteLinks'; import { showErrorAlert } from '../utils/info'; import { localAuthenticate } from '../utils/localAuthentication'; import { setActiveUsers } from '../actions/activeUsers'; +import { LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE } from '../lib/methods/enterpriseModules'; const getServer = state => state.server.server; const loginWithPasswordCall = args => RocketChat.loginWithPassword(args); @@ -68,10 +69,6 @@ const fetchCustomEmojis = function* fetchCustomEmojis() { yield RocketChat.getCustomEmojis(); }; -const fetchEnterpriseModules = function* fetchEnterpriseModules() { - yield RocketChat.getEnterpriseModules(); -}; - const fetchRoles = function* fetchRoles() { yield RocketChat.getRoles(); }; @@ -89,6 +86,15 @@ const fetchUsersPresence = function* fetchUserPresence() { RocketChat.subscribeUsersPresence(); }; +const fetchEnterpriseModules = function* fetchEnterpriseModules() { + yield RocketChat.getEnterpriseModules(); + + const hasOmnichannelLicense = RocketChat.hasLicense(LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE); + if (hasOmnichannelLicense) { + yield put(inquiryRequest()); + } +}; + const handleLoginSuccess = function* handleLoginSuccess({ user }) { try { const adding = yield select(state => state.server.adding); @@ -98,7 +104,6 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) { const server = yield select(getServer); yield put(roomsRequest()); - yield put(inquiryRequest()); yield fork(fetchPermissions); yield fork(fetchCustomEmojis); yield fork(fetchRoles); From cb92179f47a50299e537f4f1679b40ec73a9c043 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 13 Aug 2020 10:31:01 -0300 Subject: [PATCH 04/20] Clear modules --- app/lib/methods/enterpriseModules.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/lib/methods/enterpriseModules.js b/app/lib/methods/enterpriseModules.js index 930cdce78d7..a065cfedda5 100644 --- a/app/lib/methods/enterpriseModules.js +++ b/app/lib/methods/enterpriseModules.js @@ -3,7 +3,7 @@ import semver from 'semver'; import reduxStore from '../createStore'; import database from '../database'; import log from '../../utils/log'; -import { setEnterpriseModules as setEnterpriseModulesAction } from '../../actions/enterpriseModules'; +import { setEnterpriseModules as setEnterpriseModulesAction, clearEnterpriseModules } from '../../actions/enterpriseModules'; export const LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE = 'omnichannel-mobile-enterprise'; @@ -16,8 +16,10 @@ export async function setEnterpriseModules() { const server = await serversCollection.find(serverId); if (server.enterpriseModules) { reduxStore.dispatch(setEnterpriseModulesAction(server.enterpriseModules.split(','))); + return; } } + reduxStore.dispatch(clearEnterpriseModules()); } catch (e) { log(e); } @@ -39,9 +41,10 @@ export function getEnterpriseModules() { }); }); reduxStore.dispatch(setEnterpriseModulesAction(enterpriseModules)); + return resolve(); } - return resolve(); } + reduxStore.dispatch(clearEnterpriseModules()); } catch (e) { log(e); } From 5e79b3aa2e37fea5aab0997b1b1c6b2706796864 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 13 Aug 2020 10:39:09 -0300 Subject: [PATCH 05/20] Hide omnichannel rooms --- app/views/RoomsListView/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 513ce311daa..8e829f9404c 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -64,6 +64,7 @@ import Header, { getHeaderTitlePosition } from '../../containers/Header'; import { withDimensions } from '../../dimensions'; import { showErrorAlert } from '../../utils/info'; import { getInquiryQueueSelector } from '../../selectors/inquiry'; +import { LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE } from '../../lib/methods/enterpriseModules'; const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12; const CHATS_HEADER = 'Chats'; @@ -418,6 +419,11 @@ class RoomsListView extends React.Component { Q.where('open', true) ]; + // Hide omnichannel if there's no license + if (!RocketChat.hasLicense(LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE)) { + defaultWhereClause.push(Q.where('t', Q.notEq('l'))); + } + if (sortBy === 'alphabetical') { defaultWhereClause.push(Q.experimentalSortBy(`${ this.useRealName ? 'fname' : 'name' }`, Q.asc)); } else { @@ -444,7 +450,6 @@ class RoomsListView extends React.Component { .observe(); } - this.querySubscription = observable.subscribe((data) => { let tempChats = []; let chats = data; From a562706cd47b2a11d5babb6d28c710f18fceae15 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 13 Aug 2020 10:39:18 -0300 Subject: [PATCH 06/20] Minor refactor --- app/lib/methods/enterpriseModules.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app/lib/methods/enterpriseModules.js b/app/lib/methods/enterpriseModules.js index a065cfedda5..0a78f22e6ab 100644 --- a/app/lib/methods/enterpriseModules.js +++ b/app/lib/methods/enterpriseModules.js @@ -9,15 +9,13 @@ export const LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE = 'omnichannel-mobile-enterpr export async function setEnterpriseModules() { try { - const { version: serverVersion, server: serverId } = reduxStore.getState().server; - if (serverVersion && semver.gte(semver.coerce(serverVersion), '3.1.0')) { - const serversDB = database.servers; - const serversCollection = serversDB.collections.get('servers'); - const server = await serversCollection.find(serverId); - if (server.enterpriseModules) { - reduxStore.dispatch(setEnterpriseModulesAction(server.enterpriseModules.split(','))); - return; - } + const { server: serverId } = reduxStore.getState().server; + const serversDB = database.servers; + const serversCollection = serversDB.collections.get('servers'); + const server = await serversCollection.find(serverId); + if (server.enterpriseModules) { + reduxStore.dispatch(setEnterpriseModulesAction(server.enterpriseModules.split(','))); + return; } reduxStore.dispatch(clearEnterpriseModules()); } catch (e) { @@ -30,6 +28,7 @@ export function getEnterpriseModules() { try { const { version: serverVersion, server: serverId } = reduxStore.getState().server; if (serverVersion && semver.gte(semver.coerce(serverVersion), '3.1.0')) { + // RC 3.1.0 const enterpriseModules = await this.methodCallWrapper('license:getModules'); if (enterpriseModules) { const serversDB = database.servers; From b9bb749dd5bf2b56947211f7c0fdff716a77f34f Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 13 Aug 2020 10:44:53 -0300 Subject: [PATCH 07/20] Hide omnichannel toggle --- app/views/SettingsView/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js index ee362b77245..d6ea8484f68 100644 --- a/app/views/SettingsView/index.js +++ b/app/views/SettingsView/index.js @@ -38,6 +38,7 @@ import { appStart as appStartAction, ROOT_LOADING } from '../../actions/app'; import { onReviewPress } from '../../utils/review'; import { getUserSelector } from '../../selectors/login'; import SafeAreaView from '../../containers/SafeAreaView'; +import { hasLicense, LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE } from '../../lib/methods/enterpriseModules'; const SectionSeparator = React.memo(({ theme }) => ( { From de474462bccfbd9a5b9e3ef35676eec56448871f Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 13 Aug 2020 10:53:29 -0300 Subject: [PATCH 08/20] Check license on user status --- app/sagas/login.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/sagas/login.js b/app/sagas/login.js index 28d8ec2a3fe..e2cb9e6c552 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -219,7 +219,7 @@ const handleSetUser = function* handleSetUser({ user }) { yield put(setActiveUsers({ [userId]: user })); } - if (user && user.statusLivechat) { + if (user && user.statusLivechat && RocketChat.hasLicense(LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE)) { yield put(inquiryRequest()); } }; From ac2ee0111558d4e57104e0b156b6de6771ba7f03 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 13 Aug 2020 11:29:49 -0300 Subject: [PATCH 09/20] Apply on search --- app/lib/rocketchat.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 3d94e0ead10..b8805ac7859 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -30,7 +30,7 @@ import getSettings, { getLoginSettings, setSettings } from './methods/getSetting import getRooms from './methods/getRooms'; import getPermissions from './methods/getPermissions'; import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis'; -import { getEnterpriseModules, setEnterpriseModules, hasLicense } from './methods/enterpriseModules'; +import { getEnterpriseModules, setEnterpriseModules, hasLicense, LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE } from './methods/enterpriseModules'; import getSlashCommands from './methods/getSlashCommands'; import getRoles from './methods/getRoles'; import canOpenRoom from './methods/canOpenRoom'; @@ -525,6 +525,12 @@ const RocketChat = { } else if (!filterUsers && filterRooms) { data = data.filter(item => item.t !== 'd' || RocketChat.isGroupChat(item)); } + + // Hide omnichannel if there's no license + if (!this.hasLicense(LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE)) { + data = data.filter(item => item.t !== 'l'); + } + data = data.slice(0, 7); data = data.map((sub) => { From ca4ce47c82c423a7f25a15e1de9a34e14ff8521e Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 13 Aug 2020 11:32:10 -0300 Subject: [PATCH 10/20] lint --- app/lib/rocketchat.js | 4 +++- app/views/SettingsView/index.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index b8805ac7859..44f87ba2fe8 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -30,7 +30,9 @@ import getSettings, { getLoginSettings, setSettings } from './methods/getSetting import getRooms from './methods/getRooms'; import getPermissions from './methods/getPermissions'; import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis'; -import { getEnterpriseModules, setEnterpriseModules, hasLicense, LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE } from './methods/enterpriseModules'; +import { + getEnterpriseModules, setEnterpriseModules, hasLicense, LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE +} from './methods/enterpriseModules'; import getSlashCommands from './methods/getSlashCommands'; import getRoles from './methods/getRoles'; import canOpenRoom from './methods/canOpenRoom'; diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js index d6ea8484f68..6e1315d04e0 100644 --- a/app/views/SettingsView/index.js +++ b/app/views/SettingsView/index.js @@ -38,7 +38,7 @@ import { appStart as appStartAction, ROOT_LOADING } from '../../actions/app'; import { onReviewPress } from '../../utils/review'; import { getUserSelector } from '../../selectors/login'; import SafeAreaView from '../../containers/SafeAreaView'; -import { hasLicense, LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE } from '../../lib/methods/enterpriseModules'; +import { LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE } from '../../lib/methods/enterpriseModules'; const SectionSeparator = React.memo(({ theme }) => ( Date: Fri, 14 Aug 2020 14:58:43 -0300 Subject: [PATCH 11/20] Look for 'livechat-enterprise' --- app/lib/methods/enterpriseModules.js | 6 ++++++ app/lib/rocketchat.js | 5 +++-- app/sagas/login.js | 6 ++---- app/views/RoomsListView/index.js | 3 +-- app/views/SettingsView/index.js | 3 +-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/app/lib/methods/enterpriseModules.js b/app/lib/methods/enterpriseModules.js index 0a78f22e6ab..0c547cd515c 100644 --- a/app/lib/methods/enterpriseModules.js +++ b/app/lib/methods/enterpriseModules.js @@ -6,6 +6,7 @@ import log from '../../utils/log'; import { setEnterpriseModules as setEnterpriseModulesAction, clearEnterpriseModules } from '../../actions/enterpriseModules'; export const LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE = 'omnichannel-mobile-enterprise'; +export const LICENSE_LIVECHAT_ENTERPRISE = 'livechat-enterprise'; export async function setEnterpriseModules() { try { @@ -55,3 +56,8 @@ export function hasLicense(module) { const { enterpriseModules } = reduxStore.getState(); return enterpriseModules.includes(module); } + +export function isOmnichannelModuleAvailable() { + const { enterpriseModules } = reduxStore.getState(); + return [LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE, LICENSE_LIVECHAT_ENTERPRISE].every(module => enterpriseModules.includes(module)); +} diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 44f87ba2fe8..57347ccf8c7 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -31,7 +31,7 @@ import getRooms from './methods/getRooms'; import getPermissions from './methods/getPermissions'; import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis'; import { - getEnterpriseModules, setEnterpriseModules, hasLicense, LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE + getEnterpriseModules, setEnterpriseModules, hasLicense, isOmnichannelModuleAvailable } from './methods/enterpriseModules'; import getSlashCommands from './methods/getSlashCommands'; import getRoles from './methods/getRoles'; @@ -529,7 +529,7 @@ const RocketChat = { } // Hide omnichannel if there's no license - if (!this.hasLicense(LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE)) { + if (!this.isOmnichannelModuleAvailable()) { data = data.filter(item => item.t !== 'l'); } @@ -637,6 +637,7 @@ const RocketChat = { getEnterpriseModules, setEnterpriseModules, hasLicense, + isOmnichannelModuleAvailable, getSlashCommands, getRoles, parseSettings: settings => settings.reduce((ret, item) => { diff --git a/app/sagas/login.js b/app/sagas/login.js index e2cb9e6c552..5b5e12d547b 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -26,7 +26,6 @@ import { inviteLinksRequest } from '../actions/inviteLinks'; import { showErrorAlert } from '../utils/info'; import { localAuthenticate } from '../utils/localAuthentication'; import { setActiveUsers } from '../actions/activeUsers'; -import { LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE } from '../lib/methods/enterpriseModules'; const getServer = state => state.server.server; const loginWithPasswordCall = args => RocketChat.loginWithPassword(args); @@ -89,8 +88,7 @@ const fetchUsersPresence = function* fetchUserPresence() { const fetchEnterpriseModules = function* fetchEnterpriseModules() { yield RocketChat.getEnterpriseModules(); - const hasOmnichannelLicense = RocketChat.hasLicense(LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE); - if (hasOmnichannelLicense) { + if (RocketChat.isOmnichannelModuleAvailable()) { yield put(inquiryRequest()); } }; @@ -219,7 +217,7 @@ const handleSetUser = function* handleSetUser({ user }) { yield put(setActiveUsers({ [userId]: user })); } - if (user && user.statusLivechat && RocketChat.hasLicense(LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE)) { + if (user && user.statusLivechat && RocketChat.isOmnichannelModuleAvailable()) { yield put(inquiryRequest()); } }; diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 8e829f9404c..8b2b19cd4f4 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -64,7 +64,6 @@ import Header, { getHeaderTitlePosition } from '../../containers/Header'; import { withDimensions } from '../../dimensions'; import { showErrorAlert } from '../../utils/info'; import { getInquiryQueueSelector } from '../../selectors/inquiry'; -import { LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE } from '../../lib/methods/enterpriseModules'; const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12; const CHATS_HEADER = 'Chats'; @@ -420,7 +419,7 @@ class RoomsListView extends React.Component { ]; // Hide omnichannel if there's no license - if (!RocketChat.hasLicense(LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE)) { + if (!RocketChat.isOmnichannelModuleAvailable()) { defaultWhereClause.push(Q.where('t', Q.notEq('l'))); } diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js index 6e1315d04e0..0cb82c19e6b 100644 --- a/app/views/SettingsView/index.js +++ b/app/views/SettingsView/index.js @@ -38,7 +38,6 @@ import { appStart as appStartAction, ROOT_LOADING } from '../../actions/app'; import { onReviewPress } from '../../utils/review'; import { getUserSelector } from '../../selectors/login'; import SafeAreaView from '../../containers/SafeAreaView'; -import { LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE } from '../../lib/methods/enterpriseModules'; const SectionSeparator = React.memo(({ theme }) => ( { From afb90ae773a94f9d9d6545e113f5e6a2db53c60f Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Wed, 19 Aug 2020 16:23:45 -0300 Subject: [PATCH 12/20] One module is enough to enable the features --- app/lib/methods/enterpriseModules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/methods/enterpriseModules.js b/app/lib/methods/enterpriseModules.js index 0c547cd515c..60600afae7f 100644 --- a/app/lib/methods/enterpriseModules.js +++ b/app/lib/methods/enterpriseModules.js @@ -59,5 +59,5 @@ export function hasLicense(module) { export function isOmnichannelModuleAvailable() { const { enterpriseModules } = reduxStore.getState(); - return [LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE, LICENSE_LIVECHAT_ENTERPRISE].every(module => enterpriseModules.includes(module)); + return [LICENSE_OMNICHANNEL_MOBILE_ENTERPRISE, LICENSE_LIVECHAT_ENTERPRISE].some(module => enterpriseModules.includes(module)); } From 964edbbb8ca4edd5b64c278e1c7ff25811d85ba9 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Wed, 19 Aug 2020 16:52:08 -0300 Subject: [PATCH 13/20] Unhide omnichannel rooms --- app/views/RoomsListView/index.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 8b2b19cd4f4..4c0c8eca260 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -418,11 +418,6 @@ class RoomsListView extends React.Component { Q.where('open', true) ]; - // Hide omnichannel if there's no license - if (!RocketChat.isOmnichannelModuleAvailable()) { - defaultWhereClause.push(Q.where('t', Q.notEq('l'))); - } - if (sortBy === 'alphabetical') { defaultWhereClause.push(Q.experimentalSortBy(`${ this.useRealName ? 'fname' : 'name' }`, Q.asc)); } else { From 19064a36fadd42d96ec0db5fc3d92e388af8d44c Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 20 Aug 2020 13:44:07 -0300 Subject: [PATCH 14/20] Sort tweaks --- app/views/RoomsListView/ListHeader/Queue.js | 4 ++-- app/views/RoomsListView/ListHeader/Sort.js | 2 +- app/views/RoomsListView/SortDropdown/index.js | 2 +- app/views/RoomsListView/styles.js | 10 ++++++++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/views/RoomsListView/ListHeader/Queue.js b/app/views/RoomsListView/ListHeader/Queue.js index 0a85d657a19..6bb7e6b8337 100644 --- a/app/views/RoomsListView/ListHeader/Queue.js +++ b/app/views/RoomsListView/ListHeader/Queue.js @@ -27,9 +27,9 @@ const Queue = React.memo(({ { borderBottomWidth: StyleSheet.hairlineWidth, borderColor: themes[theme].separatorColor } ]} > - {I18n.t('Queued_chats')} + {I18n.t('Queued_chats')} diff --git a/app/views/RoomsListView/ListHeader/Sort.js b/app/views/RoomsListView/ListHeader/Sort.js index a2bdfac42cf..972b2980174 100644 --- a/app/views/RoomsListView/ListHeader/Sort.js +++ b/app/views/RoomsListView/ListHeader/Sort.js @@ -28,8 +28,8 @@ const Sort = React.memo(({ { borderBottomWidth: StyleSheet.hairlineWidth, borderColor: themes[theme].separatorColor } ]} > + {I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })} - ); diff --git a/app/views/RoomsListView/SortDropdown/index.js b/app/views/RoomsListView/SortDropdown/index.js index f50c939d7ad..536f5738c43 100644 --- a/app/views/RoomsListView/SortDropdown/index.js +++ b/app/views/RoomsListView/SortDropdown/index.js @@ -156,8 +156,8 @@ class Sort extends PureComponent { > + {I18n.t('Sorting_by', { key: I18n.t(sortBy === 'alphabetical' ? 'name' : 'activity') })} - diff --git a/app/views/RoomsListView/styles.js b/app/views/RoomsListView/styles.js index f304518e15e..96e7352c8cb 100644 --- a/app/views/RoomsListView/styles.js +++ b/app/views/RoomsListView/styles.js @@ -21,6 +21,11 @@ export default StyleSheet.create({ width: '100%' }, sortToggleText: { + fontSize: 16, + flex: 1, + ...sharedStyles.textRegular + }, + queueToggleText: { fontSize: 16, flex: 1, marginLeft: 12, @@ -58,6 +63,11 @@ export default StyleSheet.create({ height: 22, marginHorizontal: 12 }, + queueIcon: { + width: 22, + height: 22, + marginHorizontal: 12 + }, groupTitleContainer: { paddingHorizontal: 12, paddingTop: 17, From 81e58dc6c5b80c5a3490e2172b10c05fd160dd56 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 20 Aug 2020 14:18:21 -0300 Subject: [PATCH 15/20] Move omnichannel toggle to RoomsListView --- .../ListHeader/OmnichannelStatus.js | 74 +++++++++++++++++++ app/views/RoomsListView/ListHeader/Queue.js | 49 ------------ app/views/RoomsListView/ListHeader/index.js | 10 ++- app/views/RoomsListView/index.js | 5 +- app/views/RoomsListView/styles.js | 4 +- 5 files changed, 87 insertions(+), 55 deletions(-) create mode 100644 app/views/RoomsListView/ListHeader/OmnichannelStatus.js delete mode 100644 app/views/RoomsListView/ListHeader/Queue.js diff --git a/app/views/RoomsListView/ListHeader/OmnichannelStatus.js b/app/views/RoomsListView/ListHeader/OmnichannelStatus.js new file mode 100644 index 00000000000..b9d679797f6 --- /dev/null +++ b/app/views/RoomsListView/ListHeader/OmnichannelStatus.js @@ -0,0 +1,74 @@ +import React from 'react'; +import { + View, Text, StyleSheet, Switch +} from 'react-native'; +import PropTypes from 'prop-types'; + +import Touch from '../../../utils/touch'; +import { CustomIcon } from '../../../lib/Icons'; +import I18n from '../../../i18n'; +import styles from '../styles'; +import { themes, SWITCH_TRACK_COLOR } from '../../../constants/colors'; +import { withTheme } from '../../../theme'; +import UnreadBadge from '../../../presentation/UnreadBadge'; +import RocketChat from '../../../lib/rocketchat'; + +const Sort = React.memo(({ + searching, goQueue, theme, queueSize, inquiryEnabled, user +}) => { + if (searching > 0 || !(RocketChat.isOmnichannelModuleAvailable() && user?.roles?.includes('livechat-agent'))) { + return null; + } + const [status, setStatus] = React.useState(user?.statusLivechat === 'available'); + const toggleLivechat = async() => { + try { + setStatus(v => !v); + await RocketChat.changeLivechatStatus(); + } catch { + // Do nothing + } + }; + return ( + + + + {I18n.t('Omnichannel')} + {inquiryEnabled + ? ( + + ) + : null} + + + + ); +}); + +Sort.propTypes = { + searching: PropTypes.bool, + goQueue: PropTypes.func, + queueSize: PropTypes.number, + inquiryEnabled: PropTypes.bool, + theme: PropTypes.string, + user: PropTypes.object +}; + +export default withTheme(Sort); diff --git a/app/views/RoomsListView/ListHeader/Queue.js b/app/views/RoomsListView/ListHeader/Queue.js deleted file mode 100644 index 6bb7e6b8337..00000000000 --- a/app/views/RoomsListView/ListHeader/Queue.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import { View, Text, StyleSheet } from 'react-native'; -import PropTypes from 'prop-types'; - -import Touch from '../../../utils/touch'; -import I18n from '../../../i18n'; -import styles from '../styles'; -import { themes } from '../../../constants/colors'; -import { withTheme } from '../../../theme'; -import UnreadBadge from '../../../presentation/UnreadBadge'; - -const Queue = React.memo(({ - searching, goQueue, queueSize, inquiryEnabled, theme -}) => { - if (searching > 0 || !inquiryEnabled) { - return null; - } - return ( - - - {I18n.t('Queued_chats')} - - - - ); -}); - -Queue.propTypes = { - searching: PropTypes.bool, - goQueue: PropTypes.func, - queueSize: PropTypes.number, - inquiryEnabled: PropTypes.bool, - theme: PropTypes.string -}; - -export default withTheme(Queue); diff --git a/app/views/RoomsListView/ListHeader/index.js b/app/views/RoomsListView/ListHeader/index.js index 3de54b91390..5fe4462fc13 100644 --- a/app/views/RoomsListView/ListHeader/index.js +++ b/app/views/RoomsListView/ListHeader/index.js @@ -1,8 +1,8 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Queue from './Queue'; import Sort from './Sort'; +import OmnichannelStatus from './OmnichannelStatus'; const ListHeader = React.memo(({ searching, @@ -10,11 +10,12 @@ const ListHeader = React.memo(({ toggleSort, goQueue, queueSize, - inquiryEnabled + inquiryEnabled, + user }) => ( <> - + )); @@ -24,7 +25,8 @@ ListHeader.propTypes = { toggleSort: PropTypes.func, goQueue: PropTypes.func, queueSize: PropTypes.number, - inquiryEnabled: PropTypes.bool + inquiryEnabled: PropTypes.bool, + user: PropTypes.object }; export default ListHeader; diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 46f997f5759..1d734d2f8e6 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -812,7 +812,9 @@ class RoomsListView extends React.Component { renderListHeader = () => { const { searching } = this.state; - const { sortBy, queueSize, inquiryEnabled } = this.props; + const { + sortBy, queueSize, inquiryEnabled, user + } = this.props; return ( ); }; diff --git a/app/views/RoomsListView/styles.js b/app/views/RoomsListView/styles.js index 96e7352c8cb..6577cbbc9ab 100644 --- a/app/views/RoomsListView/styles.js +++ b/app/views/RoomsListView/styles.js @@ -28,7 +28,6 @@ export default StyleSheet.create({ queueToggleText: { fontSize: 16, flex: 1, - marginLeft: 12, ...sharedStyles.textRegular }, dropdownContainer: { @@ -126,5 +125,8 @@ export default StyleSheet.create({ serverSeparator: { height: StyleSheet.hairlineWidth, marginLeft: 72 + }, + omnichannelToggle: { + marginRight: 12 } }); From db18118b3e1be9b2cf255f72b2b486425c1ffafd Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 20 Aug 2020 14:29:03 -0300 Subject: [PATCH 16/20] Remove omnichannel toggle from SettingsView --- .../ListHeader/OmnichannelStatus.js | 5 ++- app/views/SettingsView/index.js | 43 ------------------- 2 files changed, 4 insertions(+), 44 deletions(-) diff --git a/app/views/RoomsListView/ListHeader/OmnichannelStatus.js b/app/views/RoomsListView/ListHeader/OmnichannelStatus.js index b9d679797f6..8a29119b192 100644 --- a/app/views/RoomsListView/ListHeader/OmnichannelStatus.js +++ b/app/views/RoomsListView/ListHeader/OmnichannelStatus.js @@ -68,7 +68,10 @@ Sort.propTypes = { queueSize: PropTypes.number, inquiryEnabled: PropTypes.bool, theme: PropTypes.string, - user: PropTypes.object + user: PropTypes.shape({ + roles: PropTypes.array, + statusLivechat: PropTypes.string + }) }; export default withTheme(Sort); diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js index 0cb82c19e6b..bc54d965a8c 100644 --- a/app/views/SettingsView/index.js +++ b/app/views/SettingsView/index.js @@ -73,20 +73,9 @@ class SettingsView extends React.Component { isMasterDetail: PropTypes.bool, logout: PropTypes.func.isRequired, selectServerRequest: PropTypes.func, - user: PropTypes.shape({ - roles: PropTypes.array, - statusLivechat: PropTypes.string - }), appStart: PropTypes.func } - get showLivechat() { - const { user } = this.props; - const { roles } = user; - - return RocketChat.isOmnichannelModuleAvailable() && roles?.includes('livechat-agent'); - } - handleLogout = () => { logEvent(events.SE_LOG_OUT); showConfirmationAlert({ @@ -131,14 +120,6 @@ class SettingsView extends React.Component { } } - toggleLivechat = async() => { - try { - await RocketChat.changeLivechatStatus(); - } catch { - // Do nothing - } - } - navigateToScreen = (screen) => { logEvent(events[`SE_GO_${ screen.replace('View', '').toUpperCase() }`]); const { navigation } = this.props; @@ -204,18 +185,6 @@ class SettingsView extends React.Component { ); } - renderLivechatSwitch = () => { - const { user } = this.props; - const { statusLivechat } = user; - return ( - - ); - } - render() { const { server, isMasterDetail, theme } = this.props; return ( @@ -336,18 +305,6 @@ class SettingsView extends React.Component { - {this.showLivechat ? ( - <> - this.renderLivechatSwitch()} - theme={theme} - /> - - - ) : null} - Date: Thu, 20 Aug 2020 15:41:33 -0300 Subject: [PATCH 17/20] Fix toggle --- app/lib/methods/subscriptions/rooms.js | 4 +++- app/sagas/login.js | 16 ++++++++++------ .../ListHeader/OmnichannelStatus.js | 16 +++++++++++----- app/views/RoomsListView/index.js | 7 ++++++- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/app/lib/methods/subscriptions/rooms.js b/app/lib/methods/subscriptions/rooms.js index 52e474f1488..fc76c11ae55 100644 --- a/app/lib/methods/subscriptions/rooms.js +++ b/app/lib/methods/subscriptions/rooms.js @@ -244,7 +244,9 @@ export default function subscribeRooms() { const [, ev] = ddpMessage.fields.eventName.split('/'); if (/userData/.test(ev)) { const [{ diff }] = ddpMessage.fields.args; - store.dispatch(setUser({ statusLivechat: diff?.statusLivechat })); + if (diff?.statusLivechat) { + store.dispatch(setUser({ statusLivechat: diff.statusLivechat })); + } } if (/subscriptions/.test(ev)) { if (type === 'removed') { diff --git a/app/sagas/login.js b/app/sagas/login.js index e55622dcb1c..1b40c84b128 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -14,7 +14,7 @@ import { loginFailure, loginSuccess, setUser, logout } from '../actions/login'; import { roomsRequest } from '../actions/rooms'; -import { inquiryRequest } from '../actions/inquiry'; +import { inquiryRequest, inquiryReset } from '../actions/inquiry'; import { toMomentLocale } from '../utils/moment'; import RocketChat from '../lib/rocketchat'; import log, { logEvent, events } from '../utils/log'; @@ -85,10 +85,10 @@ const fetchUsersPresence = function* fetchUserPresence() { RocketChat.subscribeUsersPresence(); }; -const fetchEnterpriseModules = function* fetchEnterpriseModules() { +const fetchEnterpriseModules = function* fetchEnterpriseModules({ user }) { yield RocketChat.getEnterpriseModules(); - if (RocketChat.isOmnichannelModuleAvailable()) { + if (user && user.statusLivechat === 'available' && RocketChat.isOmnichannelModuleAvailable()) { yield put(inquiryRequest()); } }; @@ -108,7 +108,7 @@ const handleLoginSuccess = function* handleLoginSuccess({ user }) { yield fork(fetchSlashCommands); yield fork(registerPushToken); yield fork(fetchUsersPresence); - yield fork(fetchEnterpriseModules); + yield fork(fetchEnterpriseModules, { user }); I18n.locale = user.language; moment.locale(toMomentLocale(user.language)); @@ -218,8 +218,12 @@ const handleSetUser = function* handleSetUser({ user }) { yield put(setActiveUsers({ [userId]: user })); } - if (user && user.statusLivechat && RocketChat.isOmnichannelModuleAvailable()) { - yield put(inquiryRequest()); + if (user?.statusLivechat && RocketChat.isOmnichannelModuleAvailable()) { + if (user.statusLivechat === 'available') { + yield put(inquiryRequest()); + } else { + yield put(inquiryReset()); + } } }; diff --git a/app/views/RoomsListView/ListHeader/OmnichannelStatus.js b/app/views/RoomsListView/ListHeader/OmnichannelStatus.js index 8a29119b192..4f1b9d500e6 100644 --- a/app/views/RoomsListView/ListHeader/OmnichannelStatus.js +++ b/app/views/RoomsListView/ListHeader/OmnichannelStatus.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { memo, useState, useEffect } from 'react'; import { View, Text, StyleSheet, Switch } from 'react-native'; @@ -13,13 +13,18 @@ import { withTheme } from '../../../theme'; import UnreadBadge from '../../../presentation/UnreadBadge'; import RocketChat from '../../../lib/rocketchat'; -const Sort = React.memo(({ +const OmnichannelStatus = memo(({ searching, goQueue, theme, queueSize, inquiryEnabled, user }) => { if (searching > 0 || !(RocketChat.isOmnichannelModuleAvailable() && user?.roles?.includes('livechat-agent'))) { return null; } - const [status, setStatus] = React.useState(user?.statusLivechat === 'available'); + const [status, setStatus] = useState(user?.statusLivechat === 'available'); + + useEffect(() => { + setStatus(user.statusLivechat === 'available'); + }, [user.statusLivechat]); + const toggleLivechat = async() => { try { setStatus(v => !v); @@ -28,6 +33,7 @@ const Sort = React.memo(({ // Do nothing } }; + return ( { logEvent(events.RL_GO_QUEUE); - const { navigation, isMasterDetail, queueSize } = this.props; + const { + navigation, isMasterDetail, queueSize, inquiryEnabled + } = this.props; + if (!inquiryEnabled) { + return; + } // prevent navigation to empty list if (!queueSize) { return showErrorAlert(I18n.t('Queue_is_empty'), I18n.t('Oops')); From 0b4e8f66475ab1ffa0d5d27d9d62c96c0ea769a8 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 20 Aug 2020 17:10:47 -0300 Subject: [PATCH 18/20] Ask to enable omnichannel --- app/i18n/locales/en.js | 1 + app/i18n/locales/pt-BR.js | 2 ++ app/views/RoomsListView/index.js | 20 ++++++++++++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js index 72ebbdb4efb..d28a36a91f6 100644 --- a/app/i18n/locales/en.js +++ b/app/i18n/locales/en.js @@ -335,6 +335,7 @@ export default { Offline: 'Offline', Oops: 'Oops!', Omnichannel: 'Omnichannel', + Omnichannel_enable_alert: 'You\'re not available on Omnichannel. Would you like to be available?', Onboarding_description: 'A workspace is your team or organization’s space to collaborate. Ask the workspace admin for address to join or create one for your team.', Onboarding_join_workspace: 'Join a workspace', Onboarding_subtitle: 'Beyond Team Collaboration', diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js index 8ed4e76da6b..2dd14ac79f9 100644 --- a/app/i18n/locales/pt-BR.js +++ b/app/i18n/locales/pt-BR.js @@ -310,6 +310,8 @@ export default { Not_RC_Server: 'Este não é um servidor Rocket.Chat.\n{{contact}}', No_available_agents_to_transfer: 'Nenhum agente disponível para transferência', Offline: 'Offline', + Omnichannel: 'Omnichannel', + Omnichannel_enable_alert: 'Você não está disponível no Omnichannel. Você quer ficar disponível?', Oops: 'Ops!', Onboarding_description: 'Workspace é o espaço de colaboração do seu time ou organização. Peça um convite ou o endereço ao seu administrador ou crie uma workspace para o seu time.', Onboarding_join_workspace: 'Entre numa workspace', diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 3d3d82b04fe..aaacb37b1c5 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -62,7 +62,7 @@ import { goRoom } from '../../utils/goRoom'; import SafeAreaView from '../../containers/SafeAreaView'; import Header, { getHeaderTitlePosition } from '../../containers/Header'; import { withDimensions } from '../../dimensions'; -import { showErrorAlert } from '../../utils/info'; +import { showErrorAlert, showConfirmationAlert } from '../../utils/info'; import { getInquiryQueueSelector } from '../../selectors/inquiry'; const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12; @@ -685,8 +685,24 @@ class RoomsListView extends React.Component { goQueue = () => { logEvent(events.RL_GO_QUEUE); const { - navigation, isMasterDetail, queueSize, inquiryEnabled + navigation, isMasterDetail, queueSize, inquiryEnabled, user } = this.props; + + // if not-available, prompt to change to available + if (user?.statusLivechat !== 'available') { + showConfirmationAlert({ + message: I18n.t('Omnichannel_enable_alert'), + callToAction: I18n.t('Yes'), + onPress: async() => { + try { + await RocketChat.changeLivechatStatus(); + } catch { + // Do nothing + } + } + }); + } + if (!inquiryEnabled) { return; } From 774750d22e6819b721139ad8eb3f7d1139ba4f86 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Thu, 20 Aug 2020 17:13:35 -0300 Subject: [PATCH 19/20] Lint --- app/views/RoomsListView/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index aaacb37b1c5..8f75eeb0bea 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -109,7 +109,8 @@ class RoomsListView extends React.Component { user: PropTypes.shape({ id: PropTypes.string, username: PropTypes.string, - token: PropTypes.string + token: PropTypes.string, + statusLivechat: PropTypes.string }), server: PropTypes.string, searchText: PropTypes.string, From 40bb0a963523167204e294760fc3e1b3637eb76c Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Fri, 21 Aug 2020 10:37:11 -0300 Subject: [PATCH 20/20] Fix issues found on review --- app/lib/rocketchat.js | 5 ----- app/views/RoomsListView/ListHeader/OmnichannelStatus.js | 6 +++--- app/views/SettingsView/index.js | 2 -- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index d563dd7e84d..8e659fcf909 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -523,11 +523,6 @@ const RocketChat = { data = data.filter(item => item.t !== 'd' || RocketChat.isGroupChat(item)); } - // Hide omnichannel if there's no license - if (!this.isOmnichannelModuleAvailable()) { - data = data.filter(item => item.t !== 'l'); - } - data = data.slice(0, 7); data = data.map((sub) => { diff --git a/app/views/RoomsListView/ListHeader/OmnichannelStatus.js b/app/views/RoomsListView/ListHeader/OmnichannelStatus.js index 4f1b9d500e6..cc3df2ad9c4 100644 --- a/app/views/RoomsListView/ListHeader/OmnichannelStatus.js +++ b/app/views/RoomsListView/ListHeader/OmnichannelStatus.js @@ -30,7 +30,7 @@ const OmnichannelStatus = memo(({ setStatus(v => !v); await RocketChat.changeLivechatStatus(); } catch { - // Do nothing + setStatus(v => !v); } }; @@ -46,8 +46,8 @@ const OmnichannelStatus = memo(({ { borderBottomWidth: StyleSheet.hairlineWidth, borderColor: themes[theme].separatorColor } ]} > - - {I18n.t('Omnichannel')} + + {I18n.t('Omnichannel')} {inquiryEnabled ? ( ( @@ -344,7 +343,6 @@ class SettingsView extends React.Component { const mapStateToProps = state => ({ server: state.server, - user: getUserSelector(state), allowCrashReport: state.crashReport.allowCrashReport, isMasterDetail: state.app.isMasterDetail });