Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polish admins-only policy room feature #21950

Merged
merged 39 commits into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b8198c8
add writeCapability to create new room
rezkiy37 Jun 27, 2023
823d28a
update admin room welcome message
rezkiy37 Jun 27, 2023
43a46cc
filter out admin-only rooms for members
rezkiy37 Jun 27, 2023
8135994
Revert "update admin room welcome message"
rezkiy37 Jun 27, 2023
a937311
update admin room welcome message
rezkiy37 Jun 27, 2023
42e21d7
fix reports visibility
rezkiy37 Jun 27, 2023
3cacb87
remove extra space for offline indicator
rezkiy37 Jun 27, 2023
e7e9cae
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jun 28, 2023
fc6d024
omit padding bottom of ReportActionItemCreated
rezkiy37 Jun 28, 2023
b298dea
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jun 30, 2023
e40d85c
Apply destructuring assignment of values of validate method
rezkiy37 Jun 30, 2023
e16e167
Add JSDoc
rezkiy37 Jun 30, 2023
35807e8
Add dummy translation
rezkiy37 Jun 30, 2023
e526c7e
Add real translations
rezkiy37 Jul 1, 2023
78e9c5b
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 2, 2023
5ad14aa
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 3, 2023
5e0e492
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 4, 2023
ae5b9dd
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 6, 2023
e8d2625
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 7, 2023
2865c7f
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 14, 2023
36f9d56
restrict some actions
rezkiy37 Jul 14, 2023
f4c89d9
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 17, 2023
3c91515
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 18, 2023
c6279a9
Revert "restrict some actions"
rezkiy37 Jul 18, 2023
9654e73
Revert "remove extra space for offline indicator"
rezkiy37 Jul 18, 2023
8b7fe70
Revert "fix reports visibility"
rezkiy37 Jul 18, 2023
fbbf0b7
Revert "filter out admin-only rooms for members"
rezkiy37 Jul 18, 2023
c1f8509
one-line of isAdminsOnlyPostingRoom
rezkiy37 Jul 18, 2023
3b23219
revert redundant changes
rezkiy37 Jul 18, 2023
9e8e423
show write capability picker for admins only
rezkiy37 Jul 18, 2023
3b3f3c9
move down write capability picker
rezkiy37 Jul 18, 2023
e69102f
Revert "omit padding bottom of ReportActionItemCreated"
rezkiy37 Jul 18, 2023
50a6315
remove bottom spacing of messages list when no composer
rezkiy37 Jul 18, 2023
d1a792c
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 19, 2023
8a224fc
omit unmounted inputs of form
rezkiy37 Jul 19, 2023
ccc00ba
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 19, 2023
76ba533
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 24, 2023
38659cb
add a comment
rezkiy37 Jul 24, 2023
b886d07
Merge branch 'main' of https://github.com/rezkiy37/Expensify into fea…
rezkiy37 Jul 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to add a comment for what we're doing here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, will add it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - 38659cb

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 @@ -653,6 +675,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 @@ -1720,6 +1745,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 @@ -1734,6 +1760,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 @@ -1764,6 +1791,7 @@ function buildOptimisticChatReport(
statusNum: 0,
visibility,
welcomeMessage: '',
writeCapability,
};
}

Expand Down Expand Up @@ -2642,6 +2670,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 @@ -1254,8 +1254,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 @@ -1268,6 +1269,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 @@ -1334,6 +1336,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
Loading