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

Migrate policy API to remove policy members #10452

Merged
merged 27 commits into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e6f1619
Remove unnneded code for DeleteMembersFromWorkspace
iwiznia Aug 18, 2022
a1ae9f5
Merge from main
iwiznia Sep 2, 2022
fc96430
Use employeeList in members page
iwiznia Sep 2, 2022
ec7e20d
Merge branch 'main' into ionatan_DeleteMembersFromWorkspace
iwiznia Sep 7, 2022
9ad6ec9
Transalate error
iwiznia Sep 7, 2022
41ad34c
Update to new Onyx version and fix editing comments
iwiznia Sep 13, 2022
c92093d
Fix active clients
iwiznia Sep 13, 2022
48c9543
Merge branch 'main' into ionatan_DeleteMembersFromWorkspace
iwiznia Sep 13, 2022
f1a5ba4
Address review comments
iwiznia Sep 13, 2022
faae0c7
Use each instead of map
iwiznia Sep 15, 2022
77cd2f3
Merge branch 'main' into ionatan_DeleteMembersFromWorkspace
iwiznia Sep 15, 2022
ff75e67
Merge branch 'main' into ionatan_DeleteMembersFromWorkspace
iwiznia Sep 16, 2022
11b8404
Fix activeClients bug and inviting new users
iwiznia Sep 16, 2022
8a1c3e9
Lint
iwiznia Sep 19, 2022
e32fc79
Remove unnecessary code
iwiznia Sep 19, 2022
c351e69
Update src/libs/ActiveClientManager/index.js
iwiznia Sep 20, 2022
f995ec4
Simplify ActiveClientManager
iwiznia Sep 20, 2022
d20176e
Merge branch 'ionatan_DeleteMembersFromWorkspace' of github.com:Expen…
iwiznia Sep 20, 2022
42bd676
Move promise to correct place
iwiznia Sep 20, 2022
4bc6ad9
Linting
iwiznia Sep 20, 2022
10b97ca
Add comment about init method
iwiznia Sep 20, 2022
2f08c2c
Merge branch 'main' into ionatan_DeleteMembersFromWorkspace
iwiznia Sep 23, 2022
c694ae7
Show no access page when you lose access to a chat
iwiznia Sep 28, 2022
57b6cb8
Merge branch 'main' into ionatan_DeleteMembersFromWorkspace
iwiznia Sep 28, 2022
93b977c
Lint
iwiznia Sep 28, 2022
67e8509
Merge with main
iwiznia Sep 30, 2022
fea93ab
Fix not found page showing close
iwiznia Sep 30, 2022
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
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"react-native-image-picker": "^4.8.5",
"react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#6b5ab5110dc3ed554f8eafbc38d7d87c17147972",
"react-native-modal": "^13.0.0",
"react-native-onyx": "1.0.15",
"react-native-onyx": "1.0.16",
"react-native-pdf": "^6.6.2",
"react-native-performance": "^2.0.0",
"react-native-permissions": "^3.0.1",
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,7 @@ export default {
selectAll: 'Select all',
error: {
cannotRemove: 'You cannot remove yourself or the workspace owner.',
genericRemove: 'There was a problem removing that workspace member.',
},
},
card: {
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,7 @@ export default {
selectAll: 'Seleccionar todo',
error: {
cannotRemove: 'No puedes eliminarte ni a ti mismo ni al dueño del espacio de trabajo.',
genericRemove: 'Ha ocurrido un problema al eliminar al miembro del espacio de trabajo.',
},
},
card: {
Expand Down
6 changes: 4 additions & 2 deletions src/libs/ActiveClientManager/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as ActiveClients from '../actions/ActiveClients';
const clientID = Str.guid();
const maxClients = 20;

let activeClients;
let activeClients = [];

let resolveIsReadyPromise;
const isReadyPromise = new Promise((resolve) => {
Expand All @@ -24,7 +24,9 @@ function isReady() {
Onyx.connect({
key: ONYXKEYS.ACTIVE_CLIENTS,
callback: (val) => {
activeClients = !val ? [] : val;
if (val) {
activeClients = _.unique(activeClients.concat(val));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Use if (val && activeClients.indexOf(value) < 0) instead of _.unique(). I think it would be better to do if you're trying to prevent the same value being put into the array again.

(sorry, I'm jaded against _.unique() after learning that it's not very performant).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not that it matters much here, since at most there will be 20 items, but updated

}
if (activeClients.length >= maxClients) {
activeClients.shift();
ActiveClients.setActiveClients(activeClients);
Expand Down
53 changes: 19 additions & 34 deletions src/libs/actions/Policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,29 +218,21 @@ function removeMembers(members, policyID) {
if (members.length === 0) {
return;
}

const employeeListUpdate = {};
_.each(members, login => employeeListUpdate[login] = null);
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policyID}`, employeeListUpdate);

// Make the API call to remove a login from the policy
DeprecatedAPI.Policy_Employees_Remove({
const membersListKey = `${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policyID}`;
const optimisticData = [{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: membersListKey,
value: _.object(members, Array(members.length).fill({pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE})),
}];
const failureData = [{
onyxMethod: CONST.ONYX.METHOD.MERGE,
key: membersListKey,
value: _.object(members, Array(members.length).fill({errors: {[DateUtils.getMicroseconds()]: Localize.translateLocal('workspace.people.error.genericRemove')}})),
Copy link
Contributor

Choose a reason for hiding this comment

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

I have a question with this failureData. It looks good to me, but assume it fails when removing a user. Assume the the failure happens in PHP/auth layer. Now your PHP code here will throw an ExpError with an errorMessage.
Since the API is a failure, the failureData here will get triggered to insert all members including removed ones with the error. Additionally the workspace.people.error.genericRemove is also inserted.
So if you removed 3 users, and the PHP layer failed on user 2.

  1. Won't all 3 users get workspace.people.error.genericRemove added to them since the API is considered a failure?
  2. Won't user 2 show workspace.people.error.genericRemove and errorMessage i.e., two error messages which could be confusing?

Anyway my concern is mostly point 1, showing user1 and user3 back even though they were successfully removed, because push notification would run first to remove them and then failureData from the ExpError of user2 would add them back, right?

Let me know if that makes sense, if so I think maybe we should not have failureData over here.

Copy link
Contributor

@chiragsalian chiragsalian Sep 20, 2022

Choose a reason for hiding this comment

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

Also for any es device won't it be weird that the PHP layer is providing the ExpError in English while the app adds workspace.people.error.genericRemove in Spanish? and then the app would display both.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also for any es device won't it be weird that the PHP layer is providing the ExpError in English while the app adds workspace.people.error.genericRemove in Spanish? and then the app would display both.

We don't care. We are not supporting multiple languages.

As for the other comment, you are incorrect on point 1, here's a test I did by adding a throw before calling sharePolicy on a specific user (I had removed 2 users):
image

As you can see, you are correct on point 2. I thought we would replace it, but that's not correct since we merge... this is a problem in many other APIs, since this is the pattern we are following in lots of them... I don't have a great solution for it right now, because:

  • If we add the generic error, we get this behavior
  • If we don't add the generic error, we risk not showing any error at all if what we got from the request was a failure with no data back

Any ideas? In any case, I don't think I would want to block on this, given this problem is endemic and must probably be fixed in many other places.

Copy link
Contributor

Choose a reason for hiding this comment

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

NAB, but does failureData still set this generic error if one is provided from the server? I haven't personally relied on any default error messages like this yet. It's pretty unclear to me when I should use a default error message or when I should rely on the server. But maybe something we could be more consistent about or provide guidance on in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, agree

}];
API.write('DeleteMembersFromWorkspace', {
emailList: members.join(','),
policyID,
})
.then((data) => {
if (data.jsonCode === 200) {
return;
}

// Rollback removal on failure
_.each(members, login => employeeListUpdate[login] = {});
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_MEMBER_LIST}${policyID}`, employeeListUpdate);

// Show the user feedback that the removal failed
const errorMessage = data.jsonCode === 666 ? data.message : Localize.translateLocal('workspace.people.genericFailureMessage');
Growl.show(errorMessage, CONST.GROWL.ERROR, 5000);
});
}, {optimisticData, failureData});
}

/**
Expand Down Expand Up @@ -699,21 +691,14 @@ function updateLastAccessedWorkspace(policyID) {
function subscribeToPolicyEvents() {
_.each(allPolicies, (policy) => {
const pusherChannelName = `public-policyEditor-${policy.id}${CONFIG.PUSHER.SUFFIX}`;
Pusher.subscribe(pusherChannelName, 'policyEmployeeRemoved', ({removedEmails, policyExpenseChatIDs, defaultRoomChatIDs}) => {
// Refetch the policy expense chats to update their state and their actions to get the archive reason
if (!_.isEmpty(policyExpenseChatIDs)) {
Report.fetchChatReportsByIDs(policyExpenseChatIDs);
_.each(policyExpenseChatIDs, (reportID) => {
Report.reconnect(reportID);
Copy link
Contributor

Choose a reason for hiding this comment

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

NAB, I did not see the Web PR, but curious if we kept this logic here to fetch the report data and report history for any policyExpenseChats?

Copy link
Contributor Author

@iwiznia iwiznia Sep 30, 2022

Choose a reason for hiding this comment

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

Policy expense chats should be sent via onyx when adding, removing or changing the role of users (and AFAIK we are doing that, at least the remove part is done in my web PR)

});
}

Pusher.subscribe(pusherChannelName, 'policyEmployeeRemoved', ({removedEmails, defaultRoomChatIDs}) => {
// Remove the default chats if we are one of the users getting removed
if (removedEmails.includes(sessionEmail) && !_.isEmpty(defaultRoomChatIDs)) {
_.each(defaultRoomChatIDs, (chatID) => {
Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${chatID}`, null);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

NAB, are we doing this part via the server now? What does it look like for the user if they are looking at a chat and it is set to null?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. Added a screenshot of how it looks to the description

if (!removedEmails.includes(sessionEmail) || _.isEmpty(defaultRoomChatIDs)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to do this logic in PHP so that the logic on the client can be more simple? Also, why isn't PHP sending Onyx instructions that are passed to Onyx.update()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes, we are doing this in PHP already, we can now remove all this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

return;
}
_.each(defaultRoomChatIDs, (chatID) => {
Onyx.set(`${ONYXKEYS.COLLECTION.REPORT}${chatID}`, null);
});
});
});
}
Expand Down
1 change: 1 addition & 0 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,7 @@ function editReportComment(reportID, originalReportAction, textForNewComment) {
isEdited: true,
html: htmlForNewComment,
text: textForNewComment,
type: originalReportAction.message[0].type,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this change in this PR? It seems unrelated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is not. Messages is an array and we were not sending the type in the optimistic action (neither in the action from the server), so when we replace the message fully now, this property was not set and was causing issues (we would render the comment as fragment.text here

}],
},
};
Expand Down
21 changes: 13 additions & 8 deletions src/pages/workspace/WorkspaceMembersPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,14 +283,19 @@ class WorkspaceMembersPage extends React.Component {
}

render() {
const policyMemberList = _.keys(lodashGet(this.props, 'policyMemberList', {}));
const removableMembers = _.without(policyMemberList, this.props.session.email, this.props.policy.owner);
const data = _.chain(policyMemberList)
.map(email => this.props.personalDetails[email])
.filter()
.sortBy(person => person.displayName.toLowerCase())
.map(person => ({...person})) // TODO: here we will add the pendingAction and errors prop
.value();
const policyMemberList = lodashGet(this.props, 'policyMemberList', {});
const removableMembers = [];
let data = _.map(policyMemberList, (value, email) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

value is very ambiguious. Name suggestion: policyMember

if (email !== this.props.session.email && email !== this.props.policy.owner) {
removableMembers.push(email);
}
const details = this.props.personalDetails[email];
return {
...value,
...details,
};
});
data = _.sortBy(data, value => value.displayName.toLowerCase());
const policyID = lodashGet(this.props.route, 'params.policyID');
const policyName = lodashGet(this.props.policy, 'name');

Expand Down
4 changes: 0 additions & 4 deletions src/pages/workspace/withPolicy.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import CONST from '../../CONST';
import getComponentDisplayName from '../../libs/getComponentDisplayName';
import * as Policy from '../../libs/actions/Policy';
import ONYXKEYS from '../../ONYXKEYS';
import policyMemberPropType from '../policyMemberPropType';

/**
* @param {Object} route
Expand Down Expand Up @@ -56,9 +55,6 @@ const policyPropTypes = {
*/
errorFields: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string)),
}),

/** The policy member list for the current route */
policyMemberList: PropTypes.objectOf(policyMemberPropType),
};

const policyDefaultProps = {
Expand Down