-
Notifications
You must be signed in to change notification settings - Fork 3k
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
fix:blank page appears in validate code form page #55588
base: main
Are you sure you want to change the base?
Conversation
@hoangzinh Another issue from other branch bother testing. Should we handle this bug too?(If we don't then we can't test on iOS) 2025-01-28.3.18.24.movProcess:
|
App/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx Lines 171 to 173 in 433778f
This line makes this issue cc: @nkdengineer |
@hoangzinh Please 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] |
@jacobkim9881 is it reproducible if we click on "unverified" contact method in the 1st step? |
@hoangzinh It isn't reproducible if clicking 1st time so it isn't unrelated with the PR. Thanks for clarifying! |
|
||
type ValidateCodeActionProps = ValidateCodeActionModalProps & ValidateCodeActionWithoutModalProps; | ||
|
||
function ValidateCodeActionWithoutModal({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have any other names for this component? It seems is not a good name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated it to ValidateCodeActionForm
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it shares ValidateCodeActionModalProps
, I think ValidateCodeAction
should be included. It has the form element and has props to run actions. I think ValidateCodeActionForm
can be properly used. Please to tell me if you have any idea though.
<ScrollView keyboardShouldPersistTaps="handled"> | ||
<ScrollView | ||
keyboardShouldPersistTaps="handled" | ||
contentContainerStyle={themeStyles.flex1} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you elaborate why do we need this style?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This style makes put "Verify" button to bottom. W/o the style the button will be placed beneath validate code form.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping @jacobkim9881 again ^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The elements can fit the necessary space. The explain, form and button elements are placed in its position with this style.
{!loginData?.validatedDate && ( | ||
<ValidateCodeActionWithoutModal | ||
hasMagicCodeBeenSent={hasMagicCodeBeenSent} | ||
isVisible={isValidateCodeActionModalVisible && !loginData.validatedDate && !!loginData} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should control the visibility of this component in line 323 instead of a prop. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before it is controled with isVisible
in <ValidateCodeActionModal>
. I think it should be controlled in <ValidateCodeActionWithoutModal>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before it is controled with isVisible in
isVisible={isVisible} |
|
||
{!isValidateCodeActionModalVisible && getMenuItems()} | ||
{!isValidateCodeActionModalVisible && !!loginData?.validatedDate && getMenuItems()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate why do we need add an extra condition here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W/o it, trash can will show when navigating out on Android mWeb.
expensify-test19-2025-02-02_12.45.08.mp4
useEffect( | ||
() => () => { | ||
firstRenderRef.current = true; | ||
}, | ||
[], | ||
); | ||
|
||
useEffect(() => { | ||
if (!firstRenderRef.current || !isVisible || hasMagicCodeBeenSent) { | ||
return () => { | ||
clearError(); | ||
}; | ||
} | ||
firstRenderRef.current = false; | ||
sendValidateCode(); | ||
}, [isVisible, sendValidateCode, hasMagicCodeBeenSent, clearError]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's not a modal. Do we need firstRenderRef
or isVisible
anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W/o it, it will send validate code in validated contact method or default method. It only sends validate code in validate code page only.
focusTrapOptions: isMobileSafari() | ||
? undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jacobkim9881 will it cause any issue if we apply it for mweb Safari too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It bothers keyboard showing on mWeb Safari:
REC-20250205094156.mp4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On mWeb Safari, number pad shows at contact method page. It is because of onActive
event fired by focus trap. Number pad gets ready for putting numbers on the validate form. On the screen, contact method page is seen still but focusing is on validate form. As a result before transition animation shows, number pad shows earlier.
<ScrollView keyboardShouldPersistTaps="handled"> | ||
<ScrollView | ||
keyboardShouldPersistTaps="handled" | ||
contentContainerStyle={themeStyles.flex1} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping @jacobkim9881 again ^
<ValidateCodeActionModal | ||
title={formattedContactMethod} | ||
onModalHide={() => {}} | ||
<ValidateCodeActionForm |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<ValidateCodeActionForm | |
{isValidateCodeFormVisible && !loginData.validatedDate && !!loginData && <ValidateCodeActionForm |
Why don't we display/hide ValidateCodeActionForm
here? But we prefer to pass isVisible prop into ValidateCodeActionForm
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have used isVisible
for the modal though.
Oops, pardon for my mistake! |
@jacobkim9881 please let me know when the PR is ready for next review |
@hoangzinh I have replied and the PR is ready for next review. |
@jacobkim9881 could you check review feedbacks again? I couldn't find answer of #55588 (comment) and #55588 (comment) |
I just added answers. Please to review. |
{isValidateCodeFormVisible && !loginData.validatedDate && !!loginData && ( | ||
<ValidateCodeActionForm | ||
hasMagicCodeBeenSent={hasMagicCodeBeenSent} | ||
isVisible={isValidateCodeFormVisible && !loginData.validatedDate && !!loginData} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jacobkim9881 Do we need this prop anymore? 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need isVisble
prop for executing clear function inside. For example, when navigating out from the validating code page, it clears validating error or other errors with RBR.
App/src/components/ValidateCodeActionForm.tsx
Lines 47 to 51 in 0e4b20f
if (!firstRenderRef.current || !isVisible || hasMagicCodeBeenSent) { | |
return () => { | |
clearError(); | |
}; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it always works without isVisible
there. What do you think? Can you try to remove isVisible
condition check and test again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it will works without isVisible
too since clear function works when the component is closed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you try to remove isVisible condition check and test again?
It works without isVisible
but I found an issue. Whenever opening default contact method, the clear function is executed so to run clearError()
too. Clear function and clearError
should run in validate code page only. Let me update the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was an exception. I checked clear function runs when <ValidateCodeActionForm>
unmounted by line 321. It was on mWeb Safari and desktop app. However I agree with you. isVisible
already has at line 321 {isValidateCodeFormVisible && !loginData.validatedDate && !!loginData &&
. It looks redundant if there are 2 of isVisible
s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you going to clean up isVisible
? I feel we don't really need it. isVisible = false
is the same as unmounting the component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me update without isVisible
since it is at line 321.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hoangzinh Updated!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @jacobkim9881. I will try to get another review today
Pardon. I thought it is commented with pending label. I have submitted my pending review. |
|
||
useEffect( | ||
() => () => { | ||
firstRenderRef.current = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we don't need this condition anymore. Because it's only set to true when unmount.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Let me update this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have to check if it's firstly rendered or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, should we remove this ref?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated!
// We need to run clearError in cleanup function to use as onClose function. | ||
// As 'useEffect cleanup function' runs when even the component is unmounted, we need to put clearError() in the if condition. | ||
// So clearError() will not run when the form is unmounted. | ||
if (isClosedRef.current && !isValidated) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (isClosedRef.current && !isValidated) { | |
if (!isValidated) { |
I'm confused if we need to check isClosedRef.current
which only be set to true in previous unmount event. What if this unmount are ran before isClosedRef.current
set to true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if this unmount are ran before isClosedRef.current set to true?
Without isClosedRef.current
, clearError()
will run whenever dependencies are called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// So clearError() will not run when the form is unmounted.
Sorry, this comment confuses me. I thought it should be "// So clearError() will run when the form is unmounted.". If yes, then can we just
useEffect(
() => () => {
clearError();
},
[],
);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried use clearError()
with useEffect(() => () => {}, [])
but when tapping the contact method to open validate form, the browser was frozen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tried this @jacobkim9881
useEffect(() => {
sendValidateCode();
return () => {
clearError();
};
// eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
}, []);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should work but at first time to clicking a not validated contact method, clearError()
isn't fired. Let me find how to fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated to delete unnecessary conditions and I haven't find any solution for this yet:
This should work but at first time to clicking a not validated contact method, clearError() isn't fired
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because of this line
clearError={() => clearContactMethodErrors(contactMethod, !isEmptyObject(validateLoginError) ? 'validateLogin' : 'validateCodeSent')} |
Hmm, then I think we should do following
- We should useCallback for this callback, to avoid it always try to clearError
- Update existing code in ValidateCodeActionForm to
const isUnmounted = useRef(false);
useEffect(() => {
sendValidateCode();
return () => {
isUnmounted.current = true;
};
}, []);
useEffect(() => {
return () => {
if (!isUnmounted.current) {
return;
}
clearError();
};
}, [clearError]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated!
clearError={clearError} | ||
buttonStyles={[themeStyles.justifyContentEnd, themeStyles.flex1]} | ||
ref={forwardedRef} | ||
hasMagicCodeBeenSent={canSendHasMagicCodeBeenSent} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasMagicCodeBeenSent={canSendHasMagicCodeBeenSent} | |
hasMagicCodeBeenSent={hasMagicCodeBeenSent} |
Should we use hasMagicCodeBeenSent
from the component props instead of create a new state canSendHasMagicCodeBeenSent
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pardon, if I misunderstood. Do you want hasMagicCodeBeenSent
defined in <ValidateCodeActionForm>
not as a prop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah no. I mean should we use prop hasMagicCodeBeenSent here
Something like this hasMagicCodeBeenSent={hasMagicCodeBeenSent}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understood. If we use hasMagicCodeBeenSent={hasMagicCodeBeenSent}
, the validate form will get focuses as soon to stop showing number pad on mobile device:
App/src/pages/settings/Profile/Contacts/ValidateCodeForm/BaseValidateCodeForm.tsx
Lines 142 to 145 in 1466c02
if (!hasMagicCodeBeenSent) { | |
return; | |
} | |
inputValidateCodeRef.current?.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because the form was focused before interactions(num pad showing) are finished. So after interactions are finished, hasMagicCodeBeenSent
prop is sent.
App/src/components/ValidateCodeActionForm/index.tsx
Lines 54 to 58 in 3fc7ee4
if (hasMagicCodeBeenSent) { | |
InteractionManager.runAfterInteractions(() => { | |
setCanSendHasMagicCodeBeenSent(true); | |
}); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add autofocus.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bug occurs on staging too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, if we can fix it in this PR, then it should be good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, inputValidateCodeRef has ref's focus()
. ref
's focus()
event can be bothered to cause transition animation error or keyboard not showing error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated!
Explanation of Change
This PR replaces
<ValidateCodeActionModal>
with<ValidateCodeActionForm>
inContactMethodDetailsPage
as it creates<ValidateCodeActionForm>
component. This component doesn't haveHeaderWithBackButton
orModal
so it can be used in any page that needs validate code form but it can uses props for<ValidateCodeActionModal>
.Fixed Issues
$ #53884
PROPOSAL:$ #53884 (comment)
Tests
Preparation
Test
Offline tests
Preparation
Test
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
Preparation
Test
PR Author Checklist
### Fixed Issues
section aboveTests
sectionOffline steps
sectionQA steps
sectiontoggleReport
and notonIconClick
)src/languages/*
files and using the translation methodSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)Design
label and/or tagged@Expensify/design
so the design team can review the changes.ScrollView
component to make it scrollable when more elements are added to the page.main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTest
steps.Screenshots/Videos
Android: Native
expensify-test15-2025-01-29_05.23.39.mp4
Android: mWeb Chrome
expensify-test16-2025-01-29_05.28.07.mp4
iOS: Native
2025-01-29.5.18.03.mov
iOS: mWeb Safari
2025-01-29.5.19.48.mov
MacOS: Chrome / Safari
expensify-test14-2025-01-29_05.12.06.mp4
MacOS: Desktop
2025-01-29.5.30.16.mov