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

Implement Invite message page #15672

Merged
merged 28 commits into from
Apr 28, 2023

Conversation

abdulrahuman5196
Copy link
Contributor

@abdulrahuman5196 abdulrahuman5196 commented Mar 5, 2023

@thienlnam @mollfpr

Details

Implementing a new Workspace members invite message page to fix the overlaying of message in the invite members page.

Fixed Issues

$ #15083
PROPOSAL: #15083 (comment)

Tests

  1. Go Workspace -> Manage Members -> Invite Members
  2. Choose members to invite -> Next
  3. New invite message page to be shown with choosen members and invite message
  4. Save should add the members to workspace
  • Verify that no errors appear in the JS console

Offline tests

  1. Go Workspace -> Manage Members -> Invite Members
  2. Choose members to invite -> Next
  3. New invite message page to be shown with choosen members and invite message
  4. Save should add the members to workspace

QA Steps

  1. Go Workspace -> Manage Members -> Invite Members
  2. Choose members to invite -> Next
  3. New invite message page to be shown with choosen members and invite message
  4. Save should add the members to workspace
  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android / native
    • Android / Chrome
    • iOS / native
    • iOS / Safari
    • MacOS / Chrome / Safari
    • MacOS / Desktop
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I verified the translation was requested/reviewed in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is correct English and approved by marketing by adding the Waiting for Copy label for a copy review on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR author checklist, including those that don't apply to this PR.

Screenshots/Videos

Web
Untitled.8.mp4
Mobile Web - Chrome
Untitled.9.mp4
Mobile Web - Safari
Untitled.10.mp4
Desktop
Untitled.11.mp4
iOS
Untitled.12.mp4
Android
Untitled.13.mp4

@MelvinBot
Copy link

Hey! I see that you made changes to our Form component. Make sure to update the docs in FORMS.md accordingly. Cheers!

@abdulrahuman5196 abdulrahuman5196 marked this pull request as ready for review March 7, 2023 19:46
@abdulrahuman5196 abdulrahuman5196 requested a review from a team as a code owner March 7, 2023 19:46
@melvin-bot melvin-bot bot requested review from mollfpr and thienlnam and removed request for a team March 7, 2023 19:46
@MelvinBot
Copy link

@thienlnam @mollfpr One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@mollfpr
Copy link
Contributor

mollfpr commented Mar 8, 2023

@abdulrahuman5196 Could you resolve the conflict? Thanks!

@abdulrahuman5196
Copy link
Contributor Author

abdulrahuman5196 commented Mar 9, 2023

@abdulrahuman5196 Could you resolve the conflict? Thanks!

Resolved the conflicts

@mollfpr

@mollfpr
Copy link
Contributor

mollfpr commented Mar 13, 2023

@abdulrahuman5196 We don't have the +1 avatar if more than 4 members have been added to the list.

Screen.Recording.2023-03-13.at.09.10.36.mov

@mollfpr
Copy link
Contributor

mollfpr commented Mar 13, 2023

The avatar is also not showing on the iOS

Simulator.Screen.Recording.-.iPhone.13.-.2023-03-13.at.09.41.56.mp4

@abdulrahuman5196
Copy link
Contributor Author

@mollfpr Could you kindly check if you are using the latest commit from the PR. Its working fine for me. I have been merging with main to resolve conflicts.

Simulator Screen Shot - iPhone 13 - 2023-03-13 at 20 50 11

Screenshot 2023-03-13 at 8 46 12 PM

@mollfpr
Copy link
Contributor

mollfpr commented Mar 13, 2023

@abdulrahuman5196 Thanks; I'll test it again with the latest commit. I might be missing something before.

@mollfpr
Copy link
Contributor

mollfpr commented Mar 15, 2023

@abdulrahuman5196 Could you update the PR to make the WorkspaceInvitePage and WorkspaceInviteMessagePage can work with Form.js under the hood? Also, check if the welcomeNote update when locale change is working too.

@abdulrahuman5196
Copy link
Contributor Author

@abdulrahuman5196 Could you update the PR to make the WorkspaceInvitePage and WorkspaceInviteMessagePage can work with Form.js under the hood? Also, check if the welcomeNote update when locale change is working too.

@mollfpr
The new page WorkspaceInviteMessagePage already uses Form component under the hood. But I didn't change the existing implementation of WorkspaceInvitePage to use Form component, do we need to update that as well to Form as part of this PR given that is an existing implementation?

And given this is a big PR, Can we do a full review of PR once(I am not sure if that was already done) and post the review comments alltogether, so that I can solve the comments in one shot. It will reduce the time for both of us, if not we might end up doing minor changes on every review.

@mollfpr
Copy link
Contributor

mollfpr commented Mar 16, 2023

@abdulrahuman5196 I Found this regression when searching for a new user outside the list. The new user avatar is not showing.

Step:

  1. Open invite new members' workspace
  2. Search for a user that is not on the list and select it
  3. Press a next button
Screen.Recording.2023-03-16.at.09.51.37.mov

Yes, you use the Form.js in the invite message page, but the form state is still managed in that component (value and onChangeText props). I'll confirm it with someone if that is okay. I also expected we would have a draft value because we use Form.js for the invite message page.

@mollfpr
Copy link
Contributor

mollfpr commented Mar 16, 2023

@luacmartins Hey, I need your help here! We try using Form.js for the new page WorkspaceInviteMessagePage to send the message to the new member. However, in this PR, the input value is not handled by Form.js, so we should let Form.js take it. Here's the page I'm talking about.

Second, this PR is significant in solving the original issue. Could we use the original implementation and handle the refactor to use Form.js on other issues? cc @thienlnam

@abdulrahuman5196
Copy link
Contributor Author

@luacmartins Hey, I need your help here! We try using Form.js for the new page WorkspaceInviteMessagePage to send the message to the new member. However, in this PR, the input value is not handled by Form.js, so we should let Form.js take it. Here's the page I'm talking about.

Second, this PR is significant in solving the original issue. Could we use the original implementation and handle the refactor to use Form.js on other issues? cc @thienlnam

@mollfpr I agree here on the callouts. I made the new implementation with the latest features available, but I intentionally made less refractor on existing code logic unrelated to the problem like 'default message and its translation logic' to avoid any unwanted regressions. But if we want to refractor existing logics like refractoring WorkspaceInvitePage to use Form.js and other refractors we can still do, but it might not add value to solution we are trying to solve.

So a confirm here would be good before I can start making those changes, if we still want to refractor existing code?

@luacmartins luacmartins self-requested a review March 16, 2023 20:39
Comment on lines +68 to +70
this.state = {
welcomeNote: this.getWelcomeNote(),
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of keeping this state, can we just pass the welcome message as the defaultValue for the TextInput, e.g.

<TextInput
  ...props
  defaultValue={this.props.translate('workspace.inviteMessage.welcomeNote', {
            workspaceName: this.props.policy.name,
        })};
/>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Form.js defaultValue takes the value only during first time - https://github.com/Expensify/App/blob/main/src/components/Form.js#L255
So if the language changes when we are already in this message, the default message won't be translated.

Do we want to change Form.js component in a way to support this usecase?

Copy link
Contributor

Choose a reason for hiding this comment

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

That's a good point and IMO a bug with the TextInput itself. I think that we'd want to update the TextInput default value when the locale changes in case we have provided a defaultValue that needs to be translated. We do something similar for Picker.

Copy link
Contributor Author

@abdulrahuman5196 abdulrahuman5196 Mar 21, 2023

Choose a reason for hiding this comment

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

@luacmartins I am not sure if I understood the above response correctly. I see we did a similar fix Picker component, do we want to do a similar to fix to TextInput/Form itself as part of this PR, and update the message field in this particular page accordingly? Or can we do it as a separate improvement fix as its already working now in old way or else it Seems we would be bloating this PR.
I can even volunteer to take it up as a separate fix on TextInput/Form.js as well and modify the invite message page accordingly
But its just my opinion, but I am open to fix it either ways.

cc: @mollfpr

Copy link
Contributor

Choose a reason for hiding this comment

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

@abdulrahuman5196 Here's some basic idea for a similar implementation of the Picker component.

import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import TextInput from './TextInput';
import * as Localize from '../libs/Localize';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import compose from '../libs/compose';
import withPolicy from '../pages/workspace/withPolicy';

const propTypes = {
    inputID: PropTypes.string,
    onInputChange: PropTypes.func.isRequired,

    ...withLocalizePropTypes,
};

const defaultProps = {
    inputID: undefined,
};

class InviteMessageTextInput extends PureComponent {
    constructor(props) {
        super(props);

        this.onInputChange = this.onInputChange.bind(this);
    }

    componentDidUpdate(prevProps) {
        if (!(prevProps.preferredLocale !== this.props.preferredLocale
            && this.props.value === Localize.translate(prevProps.preferredLocale, 'workspace.inviteMessage.welcomeNote', {workspaceName: this.props.policy.name}))
        ) {
            return;
        }
        this.onInputChange(this.props.translate('workspace.inviteMessage.welcomeNote', {
            workspaceName: this.props.policy.name,
        }));
    }

    onInputChange(value) {
        if (!this.props.inputID) {
            return;
        }
        this.props.onInputChange(value);
    }

    render() {
        return (
            <TextInput
                // eslint-disable-next-line react/jsx-props-no-spreading
                {...this.props}
                onInputChange={this.onInputChange}
            />
        );
    }
}

InviteMessageTextInput.propTypes = propTypes;
InviteMessageTextInput.defaultProps = defaultProps;

export default compose(
    withLocalize,
    withPolicy,
)(InviteMessageTextInput);

We override the onInputChange from the Form.js component and pass the translated welcome message when the locale changes.

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, any values should take precedence over the defaultValue. I mean, this.props.defaultValue will not be used this.setState({value: this.props.value || this.props.defaultValue || '' }); because we have this.props.value from the Form.js.

Got it, Its because we don't send 'defaultValue' instead just 'value' to TextInput. In that case we can solve it in Form itself. This case should be generic for component to update when default value changes

We have this PR #15772 that adds an option to force the use of defaultValue, but I'm not sure if it can solve our current concern.

It forcefully uses the defaultValue. It won't solve our case as we want to use the defaultValue only when there is no user provided input.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@luacmartins @mollfpr I think we should revive this thread to think about adding support for Text inside Form to support changing defaultValue.

Another issue which might have used this support if it where available(not entirely same but just quoting)
#19460

Copy link
Contributor

@luacmartins luacmartins May 29, 2023

Choose a reason for hiding this comment

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

@abdulrahuman5196 maybe you can suggest that in the issue you linked? This issue is closed so it wouldn't get much attention.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally feel free to bring this up in Slack

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Started a thread to see if need this support or not
https://expensify.slack.com/archives/C01GTK53T8Q/p1685436048476259

@abdulrahuman5196
Copy link
Contributor Author

Summarizing open questions to reviewers to make everyone's life easier. Let me know if i had missed any.
@mollfpr @luacmartins @thienlnam

  1. Implement Invite message page #15672 (comment)
    Concern: "I understand that the welcomeNote changes when the locale changes, but the Form will not be valid if we manage the value in the local component. We can set a new draft value for the form whenever the locale is changed, and we don't need value nor onChangeText in this input."
    Next action: This requires a fix in TextInput component itself to support our usecase as mentioned here - Implement Invite message page #15672 (comment). We should create a separate issue fix to have this support in TextInput itself and do appropriate changes to the new page created as part of this PR.

  2. Implement Invite message page #15672 (comment)
    Concern: "this PR is significant in solving the original issue. Could we use the original implementation and handle the refactor to use Form.js on other issues?"
    Response: This was intentionally made. But if we want to refractor existing logics like refractoring WorkspaceInvitePage(members selection page) to use Form.js and other refractors we can still do, but it might not add value to solution we are trying to solve. So we can create separate issue to do refractor as an improvement since this PR is already bloated.
    Next action item: We need confirmation from reviewers if we want to do that refractor or not as part of this PR.

@mollfpr For other review comments you have posted, I agree on them. As all other seems minor code changes, I will sort out all of them once we get the alignment on the above 2 open items.

@mollfpr
Copy link
Contributor

mollfpr commented Mar 21, 2023

Thank you @abdulrahuman5196, for the summary. I don't have any concerns with creating a new issue for the form refactor. @thienlnam @luacmartins I'll let you guys decide it.

src/ONYXKEYS.js Outdated Show resolved Hide resolved
src/libs/actions/Policy.js Show resolved Hide resolved
src/pages/workspace/WorkspaceInviteMessagePage.js Outdated Show resolved Hide resolved
src/pages/workspace/WorkspaceInviteMessagePage.js Outdated Show resolved Hide resolved
src/pages/workspace/WorkspaceInvitePage.js Outdated Show resolved Hide resolved
@Santhosh-Sellavel
Copy link
Collaborator

There is regression from this PR check here

@luacmartins
Copy link
Contributor

@abdulrahuman5196 yes, we can remove that.

@bondydaa
Copy link
Contributor

bondydaa commented May 3, 2023

This broke changes we introduce on #16764 to optimistically add the user's Policy Expense Chats when they're invited to a workspace offline.

Currently the functionality is broken on staging only but not prod luckily but this should be considered a blocker IMO.

@bondydaa bondydaa added the DeployBlockerCash This issue or pull request should block deployment label May 3, 2023
@marcaaron
Copy link
Contributor

Who is fixing it? If it broke something can we revert it? What's the plan?

@roryabraham
Copy link
Contributor

So it looks like there's maybe two related issues with this PR:

Issue 1

Reported here

BUG ‘Next’ button is disabled on back navigation even if there is a selected user; as a result user cannot continue to invite the selected user after navigating back

Action Performed:

  1. Click on ‘Profile’ avatar
  2. Click on ‘Workspaces’ link
  3. Open an existing workspace
  4. Click on ‘Manage members’ link
  5. Click on ‘Invite’ button
  6. Enter email address > select user > click on ‘Next’ button
  7. Click on back navigation icon

Expected Result: Next button shouldn’t be disabled as long as there is a selected user
Actual Result: Next button is disabled even thought there is a selected user

Issue 2

Alluded to by @bondydaa here

Action Performed:

  1. Log in with account that has access to the policy-expense-chat beta
  2. Go offline by navigating to [profile icon] > Preferences > and enable Force offline
  3. Create a new workspace.
  4. Invite some people to the workspace. Verify that workspace chats for each member appear optimistically.
  5. Go online by navigating to [profile icon] > Preferences > disable Force online.

Expected Result:
Workspace chats created for each members are updated accordingly

Actual Result:

??? Something else ???

@abdulrahuman5196
Copy link
Contributor Author

@roryabraham
For issue 1, I am working on it and the fix is known #15672 (comment)

For issue 2,

@abdulrahuman5196
Copy link
Contributor Author

abdulrahuman5196 commented May 4, 2023

For issue 2,

From code walkthrough, The intersection i see between both PR which could have caused regression might be

https://github.com/Expensify/App/pull/16764/files#diff-7040f9d045bb4fd912718e140643e494268c688306bec7a1928376d9003566daR274-R276

            Policy.addMembersToWorkspace(filteredLogins, this.state.welcomeNote, this.props.route.params.policyID, this.props.betas);

We are not passing this.props.beta in the new flow. Let me make the change and test it.

@abdulrahuman5196
Copy link
Contributor Author

Created a draft PR with the fixes for issues - #18395

Working on author checklist

@mountiny
Copy link
Contributor

mountiny commented May 4, 2023

@bondydaa Lets make sure to add the DeployBlockerCash to issues and not PRs to make the flow easier, it will autoassign hourly to an engineer and we can summarize more easily what the reproduction steps are, thanks! 🙇

@luacmartins
Copy link
Contributor

Agree with the comment above. Also, if there are multiple issues with this PR, I think that we should revert it.

@luacmartins
Copy link
Contributor

@abdulrahuman5196 does your PR address both issues with this PR?

@abdulrahuman5196
Copy link
Contributor Author

abdulrahuman5196 commented May 4, 2023

@luacmartins Yes. I am addressing both the issues.

But I would require help testing if my fix is working for the issue 2 since it seems to be working before my new fix as well #18395 as per here - #16764 (comment)

@Christinadobrzyn
Copy link
Contributor

Hey team! not sure if this is on your radar but could you check to see if #18390 is a regression of this?

@abdulrahuman5196
Copy link
Contributor Author

abdulrahuman5196 commented May 8, 2023

Raised a Followup PR for the Fixes #18601

@OSBotify
Copy link
Contributor

OSBotify commented May 9, 2023

🚀 Deployed to production by https://github.com/roryabraham in version: 1.3.12-0 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

Copy link
Contributor

Choose a reason for hiding this comment

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

This caused a "regression" #21412. We didn't account for deep access to this page. Deep link access was not being validated as a result we were able to click the invite button but the backend would return an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just to note: I wouldn't say it as a regression, but a miss during original implementation. But thank you for posting the comment. Will keep in mind for future usecases.

sendInvitation() {
Policy.addMembersToWorkspace(this.props.invitedMembersDraft, this.state.welcomeNote || this.getWelcomeNote(), this.props.route.params.policyID);
Policy.setWorkspaceInviteMembersDraft(this.props.route.params.policyID, []);
Navigation.navigate(ROUTES.getWorkspaceMembersRoute(this.props.route.params.policyID));
Copy link
Member

Choose a reason for hiding this comment

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

Hi, this PR caused a bug: #23311

See #23311 (comment) for more details on how you could have prevented it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DeployBlockerCash This issue or pull request should block deployment
Projects
None yet
Development

Successfully merging this pull request may close these issues.