Open kbecciv opened 2 years ago
Triggered auto assignment to @neil-marcellini (Engineering
), see https://stackoverflow.com/c/expensify/questions/4319 for more details.
Hmm, so if you navigate to any normal chat on mobile, the composer is not focused until you tap into it. That is the desired behavior. I think this is a valid issue if we change it from: "Chat composer is not focused after IOU request" to "Chat composer is focused after IOU request from green plus button"
Also it looks like this only happens when the request is with a brand new chat. I was not able to see the issue when sending a request via the green plus to a chat where there was a good amount of history and zero IOU requests. This is a good external issue with lower priority (weekly).
I updated the issue title and description to reflect the actual problem.
Triggered auto assignment to @bfitzexpensify (External
), see https://stackoverflow.com/c/expensify/questions/8582 for more details.
Upwork job here
Triggered auto assignment to Contributor-plus team member for initial proposal review - @Santhosh-Sellavel (Exported
)
Triggered auto assignment to @iwiznia (Exported
), see https://stackoverflow.com/c/expensify/questions/7972 for more details.
Proposal
This issue is caused by _.size(this.props.reportActions) === 1 in autofocus props of Composer when this.props.reportActions === 1 is true it focuses in the input field to fix the issue we need to remove it.
Solution :- To fix the issue we need to set autofocus props like this autoFocus={this.shouldFocusInputOnScreenFocus}
After Fixing :-
The issue is in src/pages/home/report/ReportActionCompose.js
.
This is due to when we split/request money for the first time for a new group/person, the this.props.reportActions
is loaded with only 1 IOU
message initially and then it will be populated with other messages like the CREATED
message for the group. Thus, initially the _.size(this.props.reportActions) === 1
will be true (before it loads everything) and the composer will be focused incorrectly.
The previous proposal to remove the _.size(this.props.reportActions) === 1
will cause a regression because that code is there so that when user enters a new chat, the keyboard will be focused automatically. We can refer to the requirement for that here https://github.com/Expensify/App/pull/4921#discussion_r718023887.
We'll implement a new isNewChat
method that checks both _.size(this.props.reportActions) === 1
and the only message in the chat is the CREATED
message, this will help us avoid the lazy loading problem mentioned.
+isNewChat() {
+ if (_.size(this.props.reportActions) === 1) {
+ const firstReportAction = _.head(_.values(this.props.reportActions));
+ return firstReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED;
+ }
+ return false;
+ }
....
<Composer
- autoFocus={this.shouldFocusInputOnScreenFocus || _.size(this.props.reportActions) === 1}
+ autoFocus={this.shouldFocusInputOnScreenFocus || this.isNewChat()}
After the fix, the chat composer is no longer focused incorrectly
I also discover another bug where the Chat composer will initially have the Say hello!
placeholder (which should only appear if the chat is completely new and there's no message in the chat), only a few moments later it will change to Write something...
placeholder. You can see the issue in the video capture attached in the issue description:
The fix for that will use the same isNewChat
method I mentioned above, please confirm if I should fix it in the scope of this issue as well.
Please let me know if you have any concerns regarding the above proposal. Thanks!
@iwiznia, @bfitzexpensify, @Santhosh-Sellavel Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
As mentioned by @christianwen _.size(this.props.reportActions) === 1 will cause a regression because that code is there so that when a user enters a new chat, the keyboard will be focused automatically.
But I tested it thoroughly when was proposing a solution the composer was not focused when a user enters a new chat
So I thought there is no need of .size(this.props.reportActions) === 1 so we will be able to resolve the issue by removing this condition. If we need to focus when a user enters a new chat then we could change condition to .size(this.props.reportActions) === 0 or we need to find a solution to check why _.size(this.props.reportActions) === 1 is not getting correct size of messages on intial render.
We need to identify first what we want to achieve does the composer needs to be focused on a new chat or not ? After identifying what needs to be done then we can work on a solution. @neil-marcellini
@syedsaroshfarrukhdot thanks for investigating. I think we want to have the composer focused when creating a new chat but not after creating an IOU request. I'll let @iwiznia handle this going forward since he's the CME on this issue.
would it be more suitable for the composer not focused when creating a new chat
to be a separate bug issue since that's not really included in the scope of this issue, or if it's included, we can update the description of this issue to reflect both.
For this issue, the solution mentioned in https://github.com/Expensify/App/issues/10731#issuecomment-1235329280 should work, for the composer not focused when creating a new chat
issue, it will require investigation of why this.props.reportActions
is incorrect on new chat creation as also mentioned by @syedsaroshfarrukhdot.
@Santhosh-Sellavel some proposals for you to review above
@bfitzexpensify Thanks for the bump, But I'm following the discussions and proposals.
Will post my comments by tomorrow.
After testing this.props.reportActions
i figured out it is nothing to do with this.props.reportActions
it return always correct value the issue lies with autofocus props of the composer which if true
, focuses the input on componentDidMount
and didn't update the focus on componentDidUpdate
as it is the default behavior of the autofocus props as it takes value always componentDidMount
. So, I think as it is going to be when ReportScreen Is loaded with no chat _.size(this.props.reportActions)
is always 0
and after few seconds it updates to 1
when first message in it the list renders. And when IOURequest is made first time in the new chat it has a _.size(this.props.reportActions)
is always 1
.
So to resolve both issue the viable solution i would suggest would be to set
_.size(this.props.reportActions) === 0}
is autofocus prop
After fixing it resolves both issues
Sorry might need some more time to dig this, will update later today or tomorrow!
I believe the solution to use _.size(this.props.reportActions) === 0
that relies on the observed behavior of
when ReportScreen Is loaded with no chat _.size(this.props.reportActions) is always 0 and after few seconds it updates to 1 when first message in it the list renders
is flaky and not very future proof. Imagine later when it's updated so that the ReportScreen
is loaded with correct messages list from Onyx initially (instead of having 0 then 1 message after a few seconds), that solution will break.
Original proposal: https://github.com/Expensify/App/issues/10731#issuecomment-1235329280
We'll implement a new isNewChat
method that checks both _.size(this.props.reportActions) === 1
and the only message in the chat is the CREATED
message, this will help us avoid the lazy loading problem mentioned. And the logic to autofocus will be put in the componentDidUpdate
instead of relying on the autoFocus
property of the Composer
which is flaky and does not work as expected when there's rerender.
This will fix both issues:
this.state = {
isFocused: this.shouldFocusInputOnScreenFocus,
isFullComposerAvailable: props.isComposerFullSize,
textInputShouldClear: false,
isCommentEmpty: props.comment.length === 0,
isMenuVisible: false,
selection: {
start: props.comment.length,
end: props.comment.length,
},
maxLines: props.isSmallScreenWidth ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES,
+ didAutoFocusOnNewChat: false,
};
componentDidUpdate(prevProps) {
...
+ if (!this.state.didAutoFocusOnNewChat && this.isNewChat()) {
+ this.setDidAutoFocusOnNewChat(true);
+ this.focus();
+ }
...
}
+setDidAutoFocusOnNewChat(didAutoFocusOnNewChat) {
+ this.setState({didAutoFocusOnNewChat});
+}
+isNewChat() {
+ if (_.size(this.props.reportActions) === 1) {
+ const firstReportAction = _.head(_.values(this.props.reportActions));
+ return firstReportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED;
+ }
+ return false;
+ }
....
<Composer
- autoFocus={this.shouldFocusInputOnScreenFocus || _.size(this.props.reportActions) === 1}
+ autoFocus={this.shouldFocusInputOnScreenFocus}
The chat composer will no longer be focused incorrectly after IOU request from green plus button https://user-images.githubusercontent.com/21312517/188118275-8c39c491-5048-4b85-8e31-240e0f5b3666.mp4
When a new group is created, the chat composer will be focused correctly https://user-images.githubusercontent.com/21312517/189062189-5b447c22-e3ad-4fa4-93bb-a7782b873581.MP4
@Expensify/design @iwiznia
Need some clarifications
What's the expected behavior here? When should we set focus on the chat composer?
Should the focus be set only when the chat is opened for the first time?
Thanks.
Hmm maybe just when a new chat or group is created and opened, but not after a new IOU or Split is created?
This might be a good one to bring to the open source room for more visibility though!
I think it's all of them. If I am left in a chat, I expect to be able to write directly in the chat, if it is not focused on the composer, where is it focused on and is it useful to be focused on that element?
But yeah, maybe bringing the conversation to the slack channel is a good idea.
Thread is here for discussion
Thanks for raising the discussion @Santhosh-Sellavel. Looks like we're going with this:
Always focus the input, but prevent keyboard from opening on mobile & mWeb
@iwiznia, @bfitzexpensify, @Santhosh-Sellavel Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
Triggered auto assignment to @mallenexpensify (External
), see https://stackoverflow.com/c/expensify/questions/8582 for more details.
Doubled to $500. Reassigning as I head ooo for 2 weeks
Not overdue, Melvin
We'll make use of showSoftInputOnFocus
of React Native Text Input and inputMode
of web input to achieve the intended behavior.
In src/pages/home/report/ReportActionCompose.js
this.state = {
...
+ showSoftInputOnFocus: false,
};
+setShowSoftInputOnFocus(showSoftInputOnFocus) {
+ this.setState({showSoftInputOnFocus});
+ }
+blur() {
+ if (!this.textInput) {
+ return;
+ }
+ this.textInput.blur();
+}
....
<Composer
// since it will always autofocus
- autoFocus={this.shouldFocusInputOnScreenFocus || _.size(this.props.reportActions) === 1}
+ autoFocus
+. showSoftInputOnFocus={this.state.showSoftInputOnFocus}
+ onTouchStart={() => {
+ if (!this.state.showSoftInputOnFocus) {
+ this.setShowSoftInputOnFocus(true);
// after enable keyboard on focus, we need to retrigger focus again for the keyboard to apply
+ this.blur();
+ this.focus();
}
}}
In src/components/Composer/index.ios.js
, src/components/Composer/index.android.js
, src/components/Composer/index.js
const propTypes = {
+ showSoftInputOnFocus: PropTypes.bool,
}
const defaultProps = {
+ showSoftInputOnFocus: true,
}
<RNTextInput
+ showSoftInputOnFocus={this.props.showSoftInputOnFocus}
/>
Since react-native-web
does not support showSoftInputOnFocus
yet, we need to make an update to support it
In https://github.com/Expensify/react-native-web
-> packages/react-native-web/src/exports/TextInput/index.js
const {
+ showSoftInputOnFocus = true
} = props;
...
switch (keyboardType) {
case 'email-address':
type = 'email';
break;
case 'number-pad':
case 'numeric':
inputMode = 'numeric';
break;
case 'decimal-pad':
inputMode = 'decimal';
break;
case 'phone-pad':
type = 'tel';
break;
case 'search':
case 'web-search':
type = 'search';
break;
case 'url':
type = 'url';
break;
default:
type = 'text';
}
+ if (!showSoftInputOnFocus) {
+ inputMode = 'none';
+ }
After the fix, we have the intended behavior, I demo in iOS and mWeb below
mWeb
Please let me know if you have any concerns regarding the above proposal. Thanks!
@Santhosh-Sellavel can you please review @christianwen 's proposal above? Bumping back to Daily
til then
Reviewing this now!
@christianwen This is what we want,
Always focus the input, but prevent keyboard from opening on mobile & mWeb
I've tested your proposal on iOS but it always opens keyboard without any interaction.
@Santhosh-Sellavel
Always focus the input, but prevent keyboard from opening on mobile & mWeb
To achieve this we need to introduce a new state such as inputFocus: false, our this.state will become
isFocused: this.shouldFocusInputOnScreenFocus,
isFullComposerAvailable: props.isComposerFullSize,
textInputShouldClear: false,
isCommentEmpty: props.comment.length === 0,
isMenuVisible: false,
selection: {
start: props.comment.length,
end: props.comment.length,
},
maxLines: props.isSmallScreenWidth ? CONST.COMPOSER.MAX_LINES_SMALL_SCREEN : CONST.COMPOSER.MAX_LINES,
inputFocus: false,
};
We need to write function to clear the inital focus so we can set focus again on basis of our state
if (!this.textInput) {
return;
}
this.textInput.blur();
}
We need to write a state setter function such as
this.setState({inputFocus});
}
And in Composer we need to pass a props show showSoftInputOnFocus When false, it will prevent the soft keyboard from showing when the field is focused. then we can toggle it onTouchStart. autoFocus will be always true now.
autoFocus
showSoftInputOnFocus={this.state.inputFocus}
onTouchStart={() => {
if (!this.state.inputFocus) {
this.setInputFocus(true);
this.blur();
this.focus();
}
}}
multiline
After Fixing :-
PS:- Difference b/w my Proposal and above propsal is that they are not passing showSoftInputOnFocus={this.state.inputFocus} which When false, it will prevent the soft keyboard from showing when the field is focused.
@Santhosh-Sellavel can you help to add this line? showSoftInputOnFocus={this.state.showSoftInputOnFocus}
, in here
<Composer
// since it will always autofocus
- autoFocus={this.shouldFocusInputOnScreenFocus || _.size(this.props.reportActions) === 1}
+ autoFocus
+. showSoftInputOnFocus={this.state.showSoftInputOnFocus}
+ onTouchStart={() => {
+ if (!this.state.showSoftInputOnFocus) {
+ this.setShowSoftInputOnFocus(true);
// after enable keyboard on focus, we need to retrigger focus again for the keyboard to apply
+ this.blur();
+ this.focus();
}
}}
it should work fine.
I might have missed it out when composing the diff change, but my intention was to add it since as you can see in the original proposal I introduced the new showSoftInputOnFocus
prop in the Composer
component definition.
Alternative, you can pull my branch here https://github.com/christianwen/expensify-app/tree/fix/10731-chat-composer-behavior that has the fix and run it for testing, will save you some time, also using the branch will eliminate cases of change diffs missing something or are applied incorrectly.
In order to test the react-native-web
patch, you'll need to modify the built code in node_modules/@expensify/react-native-web/dist/exports/TextInput/index.js
manually.
the other proposal mentioned in https://github.com/Expensify/App/issues/10731#issuecomment-1263101744 will not work on mWeb because the showSoftInputOnFocus
is not currently supported in react-native-web
so we need to patch react-native-web
as in my proposal.
I agree with you @christianwen that's easy to miss!
@christianwen I verified this seems to work but I noticed some lag in setting focus.
Can you share the screen recording from mWeb, Android, and iOS?
Triggered auto assignment to @puneetlath (External
), see https://stackoverflow.com/c/expensify/questions/8582 for more details.
Current assignee @Santhosh-Sellavel is eligible for the External assigner, not assigning anyone new.
Current assignee @iwiznia is eligible for the External assigner, not assigning anyone new.
@puneetlath auto-assigned to you, I'm going to be OOO for a month. Thx
@Santhosh-Sellavel the lagging is due to the onFocus
is triggered on the Composer
only after the screen finished mounting, so the style of the border is updated later. I believe this is already an existing behavior and not due to the above changes in this issue, in order to fix this we can further modify the following in https://github.com/Expensify/App/blob/9ff60c3a220c9d05001007b2c0dfe0d954623edb/src/pages/home/report/ReportActionCompose.js#L135
this.state = {
- isFocused: this.shouldFocusInputOnScreenFocus,
+ isFocused: true,
This is ok since the component is always focused when enter the screen for all platforms so we can make true
the default state to show the correct initial border highlight.
Screen recording after applying the above change: iOS https://user-images.githubusercontent.com/21312517/193393980-122451a0-a1da-456e-92fe-4906da879905.mp4
Android Android-chat-compose.webm
Not overdue
@puneetlath assigned myself and unassigned you cuz I'm NOT going to be OOO the next month ¯_(ツ)_/¯
Doubled job price to $1000 https://www.upwork.com/jobs/~0171651b178a420568
@christianwen proposal mostly looks good. Also need to check this again!
Also need to check this again!
@Santhosh-Sellavel so should I review the proposal or not? Not sure what you meant by check this again
...
@iwiznia didn't get a chance to test the proposal after this https://github.com/Expensify/App/issues/10731#issuecomment-1264264720 that's why commented " need to check this again."
@christianwen
Based the docs here if showSoftInputOnFocus
set true
keyboard should be shown right?
Not sure why this is required, this feels kind of hacky.
// after enable keyboard on focus, we need to retrigger focus again for the keyboard to apply
this.blur();
this.focus();
@Santhosh-Sellavel if showSoftInputOnFocus set true keyboard should be shown right
-> this is correct if the input is currently not in focus and then it's focused, the keyboard will be shown.
But in this case, since the input is already focused (it's focused by default when entering the chat screen), when we change showSoftInputOnFocus
to true it will not pop the keyboard up but will require refocusing in order for the keyboard to open.
You can think of the showSoftInputOnFocus
as show soft input on/when focus
, meaning this property will only take effect onFocus
(when the focus event takes place) and not when the TextInput is already focused before.
@iwiznia, @mallenexpensify, @Santhosh-Sellavel Whoops! This issue is 2 days overdue. Let's get this updated quick!
@Santhosh-Sellavel , can you review @christianwen's comment above plz?
if showSoftInputOnFocus is set to true keyboard should be shown right -> this is correct if the input is currently not in focus and then it's focused, the keyboard will be shown.
I disagree as per this doc here.
When false, it will prevent the soft keyboard from showing when the field is focused. The default value is true.
I tried it out without the below lines.
this.blur();
this.focus();
It works fine on android, but it doesn't work on iOS.
@christianwen
If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!
Action Performed:
Next
confirmation button Complete the request money flowExpected Result:
Always focus the input, but prevent keyboard from opening on mobile & mWeb
Actual Result:
The composer is focused.
Workaround:
Swipe the keyboard down to un-focus the composer
Platform:
Where is this issue occurring?
Version Number: 1.1.95.4
Reproducible in staging?: Yes
Reproducible in production?: Yes
Email or phone of affected tester (no customers): any
Logs: https://stackoverflow.com/c/expensify/questions/4856
Notes/Photos/Videos: Any additional supporting documentation
https://user-images.githubusercontent.com/93399543/187747715-3711327d-e3d4-4198-9df5-32400f8cb46e.mp4
https://user-images.githubusercontent.com/93399543/187747751-f8f2a162-cb62-4b85-91cc-d9bf024a13ba.mp4
Expensify/Expensify Issue URL:
Issue reported by: Applause - Internal Team
Slack conversation:
View all open jobs on GitHub
Issue Owner
Current Issue Owner: @Christinadobrzyn