From 88191b965a6cec03190202f652bddc2b31f604dd Mon Sep 17 00:00:00 2001 From: Gung Wah <41157464+kresnaputra@users.noreply.github.com> Date: Fri, 2 Jul 2021 00:51:19 +0800 Subject: [PATCH] [IMPROVE] Subscribe to roles (#2992) * [CHORE] Subscribe to Roles * subscribe to roles-change * add subscribe for stream-roles * fixed subscribe roles * Add componentDidUpdate to RoomMembersView and propType * Update componentDidUpdate in RoomMembersView, roles reducer, getRoles method and actionType * Minor tweaks * Remove componentDidUpdate * Fix add role * Fix initialState and remove role * Minor try/catch fix * Fix lint * Fix offline first Co-authored-by: Diego Mello Co-authored-by: Gerzon Z Co-authored-by: Gerzon Z --- app/actions/actionsTypes.js | 1 + app/actions/roles.js | 20 ++++++++++ app/lib/methods/getRoles.js | 60 +++++++++++++++++++++++++++++- app/lib/rocketchat.js | 5 ++- app/reducers/index.js | 4 +- app/reducers/permissions.js | 4 +- app/reducers/roles.js | 22 +++++++++++ app/sagas/login.js | 1 + app/sagas/selectServer.js | 1 + app/views/RoomInfoView/index.js | 22 ++++------- app/views/RoomMembersView/index.js | 3 +- 11 files changed, 121 insertions(+), 22 deletions(-) create mode 100644 app/actions/roles.js create mode 100644 app/reducers/roles.js diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index 6bb169009..7bba8309f 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -72,3 +72,4 @@ export const ENTERPRISE_MODULES = createRequestTypes('ENTERPRISE_MODULES', ['CLE export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DECODE_KEY', 'SET', 'SET_BANNER']); export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET', 'UPDATE']); +export const ROLES = createRequestTypes('ROLES', ['SET', 'UPDATE', 'REMOVE']); diff --git a/app/actions/roles.js b/app/actions/roles.js new file mode 100644 index 000000000..8ee30425b --- /dev/null +++ b/app/actions/roles.js @@ -0,0 +1,20 @@ +import * as types from './actionsTypes'; + +export function setRoles(roles) { + return { + type: types.ROLES.SET, + roles + }; +} +export function updateRoles(id, desc) { + return { + type: types.ROLES.UPDATE, + payload: { id, desc } + }; +} +export function removeRoles(id) { + return { + type: types.ROLES.REMOVE, + payload: { id } + }; +} diff --git a/app/lib/methods/getRoles.js b/app/lib/methods/getRoles.js index df22d382b..98a3895e3 100644 --- a/app/lib/methods/getRoles.js +++ b/app/lib/methods/getRoles.js @@ -2,9 +2,66 @@ import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import database from '../database'; import log from '../../utils/log'; +import reduxStore from '../createStore'; import protectedFunction from './helpers/protectedFunction'; +import { + removeRoles, setRoles as setRolesAction, updateRoles +} from '../../actions/roles'; -export default function() { +export async function setRoles() { + const db = database.active; + const rolesCollection = db.collections.get('roles'); + const allRoles = await rolesCollection.query().fetch(); + const parsed = allRoles.reduce((acc, item) => ({ ...acc, [item.id]: item.description || item.id }), {}); + reduxStore.dispatch(setRolesAction(parsed)); +} + +export async function onRolesChanged(ddpMessage) { + const { type, _id, description } = ddpMessage.fields.args[0]; + if (/changed/.test(type)) { + const db = database.active; + const rolesCollection = db.get('roles'); + try { + const rolesRecord = await rolesCollection.find(_id); + try { + await db.action(async() => { + await rolesRecord.update((u) => { + u.description = description; + }); + }); + } catch (e) { + log(e); + } + reduxStore.dispatch(updateRoles(_id, description)); + } catch (err) { + try { + await db.action(async() => { + await rolesCollection.create((post) => { + post._raw = sanitizedRaw({ id: _id, description }, rolesCollection.schema); + }); + }); + } catch (e) { + log(e); + } + reduxStore.dispatch(updateRoles(_id, description || _id)); + } + } + if (/removed/.test(type)) { + const db = database.active; + const rolesCollection = db.get('roles'); + try { + const rolesRecord = await rolesCollection.find(_id); + await db.action(async() => { + await rolesRecord.destroyPermanently(); + }); + reduxStore.dispatch(removeRoles(_id)); + } catch (err) { + console.log(err); + } + } +} + +export function getRoles() { const db = database.active; return new Promise(async(resolve) => { try { @@ -50,6 +107,7 @@ export default function() { } catch (e) { log(e); } + setRoles(); return allRecords.length; }); return resolve(); diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index bcff9e300..59b85b73d 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -37,7 +37,7 @@ import { getEnterpriseModules, setEnterpriseModules, hasLicense, isOmnichannelModuleAvailable } from './methods/enterpriseModules'; import getSlashCommands from './methods/getSlashCommands'; -import getRoles from './methods/getRoles'; +import { getRoles, setRoles, onRolesChanged } from './methods/getRoles'; import canOpenRoom from './methods/canOpenRoom'; import triggerBlockAction, { triggerSubmitView, triggerCancel } from './methods/actions'; @@ -275,6 +275,8 @@ const RocketChat = { this.usersListener = this.sdk.onStreamData('users', protectedFunction(ddpMessage => RocketChat._setUser(ddpMessage))); + this.rolesListener = this.sdk.onStreamData('stream-roles', protectedFunction(ddpMessage => onRolesChanged(ddpMessage))); + this.notifyLoggedListener = this.sdk.onStreamData('stream-notify-logged', protectedFunction(async(ddpMessage) => { const { eventName } = ddpMessage.fields; if (/user-status/.test(eventName)) { @@ -859,6 +861,7 @@ const RocketChat = { isOmnichannelModuleAvailable, getSlashCommands, getRoles, + setRoles, parseSettings: settings => settings.reduce((ret, item) => { ret[item._id] = defaultSettings[item._id] && item[defaultSettings[item._id].type]; if (item._id === 'Hide_System_Messages') { diff --git a/app/reducers/index.js b/app/reducers/index.js index dfee5f3eb..eb8f09d82 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -19,6 +19,7 @@ import createDiscussion from './createDiscussion'; import enterpriseModules from './enterpriseModules'; import encryption from './encryption'; import permissions from './permissions'; +import roles from './roles'; import inquiry from '../ee/omnichannel/reducers/inquiry'; @@ -43,5 +44,6 @@ export default combineReducers({ inquiry, enterpriseModules, encryption, - permissions + permissions, + roles }); diff --git a/app/reducers/permissions.js b/app/reducers/permissions.js index dbba0e107..034c51420 100644 --- a/app/reducers/permissions.js +++ b/app/reducers/permissions.js @@ -1,8 +1,6 @@ import { PERMISSIONS } from '../actions/actionsTypes'; -const initialState = { - permissions: {} -}; +const initialState = {}; export default function permissions(state = initialState, action) { switch (action.type) { diff --git a/app/reducers/roles.js b/app/reducers/roles.js new file mode 100644 index 000000000..93cbffcb0 --- /dev/null +++ b/app/reducers/roles.js @@ -0,0 +1,22 @@ +import { ROLES } from '../actions/actionsTypes'; + +const initialState = {}; + +export default function permissions(state = initialState, action) { + switch (action.type) { + case ROLES.SET: + return action.roles; + case ROLES.UPDATE: + return { + ...state, + [action.payload.id]: action.payload.desc || action.payload.id + }; + case ROLES.REMOVE: { + const newState = { ...state }; + delete newState[action.payload.id]; + return newState; + } + default: + return state; + } +} diff --git a/app/sagas/login.js b/app/sagas/login.js index 77eda7611..de82cfb7e 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -90,6 +90,7 @@ const fetchCustomEmojis = function* fetchCustomEmojis() { }; const fetchRoles = function* fetchRoles() { + RocketChat.subscribe('stream-roles', 'roles'); yield RocketChat.getRoles(); }; diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js index d367274a3..2abf47061 100644 --- a/app/sagas/selectServer.js +++ b/app/sagas/selectServer.js @@ -125,6 +125,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch RocketChat.setSettings(); RocketChat.setCustomEmojis(); RocketChat.setPermissions(); + RocketChat.setRoles(); RocketChat.setEnterpriseModules(); let serverInfo; diff --git a/app/views/RoomInfoView/index.js b/app/views/RoomInfoView/index.js index 93a7e3a16..a966463bf 100644 --- a/app/views/RoomInfoView/index.js +++ b/app/views/RoomInfoView/index.js @@ -6,7 +6,6 @@ import { connect } from 'react-redux'; import UAParser from 'ua-parser-js'; import isEmpty from 'lodash/isEmpty'; -import database from '../../lib/database'; import { CustomIcon } from '../../lib/Icons'; import Status from '../../containers/Status'; import Avatar from '../../containers/Avatar'; @@ -55,7 +54,8 @@ class RoomInfoView extends React.Component { theme: PropTypes.string, isMasterDetail: PropTypes.bool, jitsiEnabled: PropTypes.bool, - editRoomPermission: PropTypes.array + editRoomPermission: PropTypes.array, + roles: PropTypes.array } constructor(props) { @@ -133,18 +133,9 @@ class RoomInfoView extends React.Component { return room.t === 'l'; } - getRoleDescription = async(id) => { - const db = database.active; - try { - const rolesCollection = db.get('roles'); - const role = await rolesCollection.find(id); - if (role) { - return role.description; - } - return null; - } catch (e) { - return null; - } + getRoleDescription = (id) => { + const { roles } = this.props; + return roles[id]; }; loadVisitor = async() => { @@ -378,7 +369,8 @@ const mapStateToProps = state => ({ rooms: state.room.rooms, isMasterDetail: state.app.isMasterDetail, jitsiEnabled: state.settings.Jitsi_Enabled || false, - editRoomPermission: state.permissions['edit-room'] + editRoomPermission: state.permissions['edit-room'], + roles: state.roles }); export default connect(mapStateToProps)(withTheme(RoomInfoView)); diff --git a/app/views/RoomMembersView/index.js b/app/views/RoomMembersView/index.js index 5f0deb326..aaa0c98f9 100644 --- a/app/views/RoomMembersView/index.js +++ b/app/views/RoomMembersView/index.js @@ -49,7 +49,8 @@ class RoomMembersView extends React.Component { room: PropTypes.object, user: PropTypes.shape({ id: PropTypes.string, - token: PropTypes.string + token: PropTypes.string, + roles: PropTypes.array }), showActionSheet: PropTypes.func, theme: PropTypes.string,