Skip to content

Commit

Permalink
Merge pull request #21950 from rezkiy37/feature/21456-polish-admin-rooms
Browse files Browse the repository at this point in the history
Polish admins-only policy room feature
  • Loading branch information
amyevans authored Jul 25, 2023
2 parents beebb67 + b886d07 commit 0e2f742
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 7 deletions.
27 changes: 24 additions & 3 deletions src/components/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ function Form(props) {
* @returns {React.Component}
*/
const childrenWrapperWithProps = useCallback(
(childNodes) =>
React.Children.map(childNodes, (child) => {
(childNodes) => {
const childrenElements = React.Children.map(childNodes, (child) => {
// Just render the child if it is not a valid React element, e.g. text within a <Text> component
if (!React.isValidElement(child)) {
return child;
Expand Down Expand Up @@ -330,7 +330,28 @@ function Form(props) {
}
},
});
}),
});

// We need to verify that all references and values are still actual.
// We should not store it when e.g. some input has been unmounted
_.each(inputRefs.current, (inputRef, inputID) => {
if (inputRef) {
return;
}

delete inputRefs.current[inputID];

setInputValues((prevState) => {
const copyPrevState = _.clone(prevState);

delete copyPrevState[inputID];

return copyPrevState;
});
});

return childrenElements;
},
[errors, inputRefs, inputValues, onValidate, props.draftValues, props.formID, props.formState, setTouchedInput],
);

Expand Down
2 changes: 2 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ export default {
beginningOfChatHistoryDomainRoomPartTwo: ' to chat with colleagues, share tips, and ask questions.',
beginningOfChatHistoryAdminRoomPartOne: ({workspaceName}) => `Collaboration among ${workspaceName} admins starts here! 🎉\nUse `,
beginningOfChatHistoryAdminRoomPartTwo: ' to chat about topics such as workspace configurations and more.',
beginningOfChatHistoryAdminOnlyPostingRoomPartOne: 'Use ',
beginningOfChatHistoryAdminOnlyPostingRoomPartTwo: ({workspaceName}) => ` to hear about important announcements related to ${workspaceName}`,
beginningOfChatHistoryAnnounceRoomPartOne: ({workspaceName}) => `Collaboration between all ${workspaceName} members starts here! 🎉\nUse `,
beginningOfChatHistoryAnnounceRoomPartTwo: ({workspaceName}) => ` to chat about anything ${workspaceName} related.`,
beginningOfChatHistoryUserRoomPartOne: 'Collaboration starts here! 🎉\nUse this space to chat about anything ',
Expand Down
2 changes: 2 additions & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ export default {
beginningOfChatHistoryDomainRoomPartTwo: ' para chatear con compañeros, compartir consejos o hacer una pregunta.',
beginningOfChatHistoryAdminRoomPartOne: ({workspaceName}) => `Este es el lugar para que los administradores de ${workspaceName} colaboren! 🎉\nUsa `,
beginningOfChatHistoryAdminRoomPartTwo: ' para chatear sobre temas como la configuración del espacio de trabajo y mas.',
beginningOfChatHistoryAdminOnlyPostingRoomPartOne: 'Utiliza ',
beginningOfChatHistoryAdminOnlyPostingRoomPartTwo: ({workspaceName}) => ` para enterarte de anuncios importantes relacionados con ${workspaceName}`,
beginningOfChatHistoryAnnounceRoomPartOne: ({workspaceName}) => `Este es el lugar para que todos los miembros de ${workspaceName} colaboren! 🎉\nUsa `,
beginningOfChatHistoryAnnounceRoomPartTwo: ({workspaceName}) => ` para chatear sobre cualquier cosa relacionada con ${workspaceName}.`,
beginningOfChatHistoryUserRoomPartOne: 'Este es el lugar para colaborar! 🎉\nUsa este espacio para chatear sobre cualquier cosa relacionada con ',
Expand Down
29 changes: 29 additions & 0 deletions src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,16 @@ function isAdminRoom(report) {
return getChatType(report) === CONST.REPORT.CHAT_TYPE.POLICY_ADMINS;
}

/**
* Whether the provided report is an Admin-only posting room
* @param {Object} report
* @param {String} report.writeCapability
* @returns {Boolean}
*/
function isAdminsOnlyPostingRoom(report) {
return lodashGet(report, 'writeCapability', CONST.REPORT.WRITE_CAPABILITIES.ALL) === CONST.REPORT.WRITE_CAPABILITIES.ADMINS;
}

/**
* Whether the provided report is a Announce room
* @param {Object} report
Expand Down Expand Up @@ -567,6 +577,18 @@ function isPolicyExpenseChatAdmin(report, policies) {
return policyRole === CONST.POLICY.ROLE.ADMIN;
}

/**
* Checks if the current user is the admin of the policy.
* @param {String} policyID
* @param {Object} policies must have OnyxKey prefix (i.e 'policy_') for keys
* @returns {Boolean}
*/
function isPolicyAdmin(policyID, policies) {
const policyRole = lodashGet(policies, [`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, 'role']);

return policyRole === CONST.POLICY.ROLE.ADMIN;
}

/**
* Returns true if report is a DM/Group DM chat.
*
Expand Down Expand Up @@ -663,6 +685,9 @@ function getRoomWelcomeMessage(report) {
} else if (isAdminRoom(report)) {
welcomeMessage.phrase1 = Localize.translateLocal('reportActionsView.beginningOfChatHistoryAdminRoomPartOne', {workspaceName});
welcomeMessage.phrase2 = Localize.translateLocal('reportActionsView.beginningOfChatHistoryAdminRoomPartTwo');
} else if (isAdminsOnlyPostingRoom(report)) {
welcomeMessage.phrase1 = Localize.translateLocal('reportActionsView.beginningOfChatHistoryAdminOnlyPostingRoomPartOne');
welcomeMessage.phrase2 = Localize.translateLocal('reportActionsView.beginningOfChatHistoryAdminOnlyPostingRoomPartTwo', {workspaceName});
} else if (isAnnounceRoom(report)) {
welcomeMessage.phrase1 = Localize.translateLocal('reportActionsView.beginningOfChatHistoryAnnounceRoomPartOne', {workspaceName});
welcomeMessage.phrase2 = Localize.translateLocal('reportActionsView.beginningOfChatHistoryAnnounceRoomPartTwo', {workspaceName});
Expand Down Expand Up @@ -1730,6 +1755,7 @@ function buildOptimisticTaskReportAction(taskReportID, actionName, message = '')
* @param {Boolean} isOwnPolicyExpenseChat
* @param {String} oldPolicyName
* @param {String} visibility
* @param {String} writeCapability
* @param {String} notificationPreference
* @param {String} parentReportActionID
* @param {String} parentReportID
Expand All @@ -1744,6 +1770,7 @@ function buildOptimisticChatReport(
isOwnPolicyExpenseChat = false,
oldPolicyName = '',
visibility = undefined,
writeCapability = CONST.REPORT.WRITE_CAPABILITIES.ALL,
notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS,
parentReportActionID = '',
parentReportID = '',
Expand Down Expand Up @@ -1774,6 +1801,7 @@ function buildOptimisticChatReport(
statusNum: 0,
visibility,
welcomeMessage: '',
writeCapability,
};
}

Expand Down Expand Up @@ -2653,6 +2681,7 @@ export {
getPolicyType,
isArchivedRoom,
isPolicyExpenseChatAdmin,
isPolicyAdmin,
isPublicRoom,
isPublicAnnounceRoom,
isConciergeChatReport,
Expand Down
5 changes: 4 additions & 1 deletion src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -1264,8 +1264,9 @@ function navigateToConciergeChat() {
* @param {String} reportName
* @param {String} visibility
* @param {Array<Number>} policyMembersAccountIDs
* @param {String} writeCapability
*/
function addPolicyReport(policyID, reportName, visibility, policyMembersAccountIDs) {
function addPolicyReport(policyID, reportName, visibility, policyMembersAccountIDs, writeCapability) {
// The participants include the current user (admin), and for restricted rooms, the policy members. Participants must not be empty.
const members = visibility === CONST.REPORT.VISIBILITY.RESTRICTED ? policyMembersAccountIDs : [];
const participants = _.unique([currentUserAccountID, ...members]);
Expand All @@ -1278,6 +1279,7 @@ function addPolicyReport(policyID, reportName, visibility, policyMembersAccountI
false,
'',
visibility,
writeCapability,

// The room might contain all policy members so notifying always should be opt-in only.
CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY,
Expand Down Expand Up @@ -1344,6 +1346,7 @@ function addPolicyReport(policyID, reportName, visibility, policyMembersAccountI
visibility,
reportID: policyReport.reportID,
createdReportActionID: createdReportAction.reportActionID,
writeCapability,
},
{optimisticData, successData, failureData},
);
Expand Down
9 changes: 8 additions & 1 deletion src/pages/home/report/ReportActionsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useState} from 'react';
import Animated, {useSharedValue, useAnimatedStyle, withTiming} from 'react-native-reanimated';
import _ from 'underscore';
import lodashGet from 'lodash/get';
import InvertedFlatList from '../../../components/InvertedFlatList';
import compose from '../../../libs/compose';
import styles from '../../../styles/styles';
Expand Down Expand Up @@ -162,14 +163,20 @@ function ReportActionsList(props) {
// To notify there something changes we can use extraData prop to flatlist
const extraData = [props.isSmallScreenWidth ? props.newMarkerReportActionID : undefined, ReportUtils.isArchivedRoom(props.report)];
const shouldShowReportRecipientLocalTime = ReportUtils.canShowReportRecipientLocalTime(props.personalDetailsList, props.report, props.currentUserPersonalDetails.accountID);

const errors = lodashGet(props.report, 'errorFields.addWorkspaceRoom') || lodashGet(props.report, 'errorFields.createChat');
const isArchivedRoom = ReportUtils.isArchivedRoom(props.report);
const hideComposer = ReportUtils.shouldHideComposer(props.report, errors);
const shouldOmitBottomSpace = hideComposer || isArchivedRoom;

return (
<Animated.View style={[animatedStyles, styles.flex1]}>
<InvertedFlatList
accessibilityLabel={props.translate('sidebarScreen.listOfChatMessages')}
ref={reportScrollManager.ref}
data={props.sortedReportActions}
renderItem={renderItem}
contentContainerStyle={[styles.chatContentScrollView, shouldShowReportRecipientLocalTime ? styles.pt0 : {}]}
contentContainerStyle={[styles.chatContentScrollView, shouldShowReportRecipientLocalTime || shouldOmitBottomSpace ? styles.pt0 : {}]}
keyExtractor={keyExtractor}
initialRowHeight={32}
initialNumToRender={calculateInitialNumToRender()}
Expand Down
33 changes: 31 additions & 2 deletions src/pages/workspace/WorkspaceNewRoomPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Permissions from '../../libs/Permissions';
import Log from '../../libs/Log';
import * as ErrorUtils from '../../libs/ErrorUtils';
import * as ValidationUtils from '../../libs/ValidationUtils';
import * as ReportUtils from '../../libs/ReportUtils';
import * as PolicyUtils from '../../libs/PolicyUtils';
import Form from '../../components/Form';
import shouldDelayFocus from '../../libs/shouldDelayFocus';
Expand Down Expand Up @@ -66,14 +67,22 @@ const defaultProps = {
function WorkspaceNewRoomPage(props) {
const {translate} = useLocalize();
const [visibility, setVisibility] = useState(CONST.REPORT.VISIBILITY.RESTRICTED);
const [policyID, setPolicyID] = useState(null);
const visibilityDescription = useMemo(() => translate(`newRoomPage.${visibility}Description`), [translate, visibility]);
const isPolicyAdmin = useMemo(() => {
if (!policyID) {
return false;
}

return ReportUtils.isPolicyAdmin(policyID, props.policies);
}, [policyID, props.policies]);

/**
* @param {Object} values - form input values passed by the Form component
*/
const submit = (values) => {
const policyMembers = _.map(_.keys(props.allPolicyMembers[`${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${values.policyID}`]), (accountID) => Number(accountID));
Report.addPolicyReport(values.policyID, values.roomName, values.visibility, policyMembers);
Report.addPolicyReport(values.policyID, values.roomName, values.visibility, policyMembers, values.writeCapability);
};

/**
Expand Down Expand Up @@ -109,6 +118,15 @@ function WorkspaceNewRoomPage(props) {

const workspaceOptions = useMemo(() => _.map(PolicyUtils.getActivePolicies(props.policies), (policy) => ({label: policy.name, key: policy.id, value: policy.id})), [props.policies]);

const writeCapabilityOptions = useMemo(
() =>
_.map(CONST.REPORT.WRITE_CAPABILITIES, (value) => ({
value,
label: translate(`writeCapabilityPage.writeCapability.${value}`),
})),
[translate],
);

const visibilityOptions = useMemo(
() =>
_.map(
Expand Down Expand Up @@ -156,14 +174,25 @@ function WorkspaceNewRoomPage(props) {
shouldDelayFocus={shouldDelayFocus}
/>
</View>
<View style={styles.mb5}>
<View style={styles.mb2}>
<Picker
inputID="policyID"
label={translate('workspace.common.workspace')}
placeholder={{value: '', label: translate('newRoomPage.selectAWorkspace')}}
items={workspaceOptions}
onValueChange={setPolicyID}
/>
</View>
{isPolicyAdmin && (
<View style={styles.mb2}>
<Picker
inputID="writeCapability"
label={translate('writeCapabilityPage.label')}
items={writeCapabilityOptions}
defaultValue={CONST.REPORT.WRITE_CAPABILITIES.ALL}
/>
</View>
)}
<View style={styles.mb2}>
<Picker
inputID="visibility"
Expand Down

0 comments on commit 0e2f742

Please sign in to comment.