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

[Awaiting checklist] [$250] Composer - Cursor Position Resets to Start of Message on Page Refresh #44259

Closed
2 of 6 tasks
lanitochka17 opened this issue Jun 24, 2024 · 39 comments
Closed
2 of 6 tasks
Assignees
Labels
Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor

Comments

@lanitochka17
Copy link

lanitochka17 commented Jun 24, 2024

If you haven’t already, check out our contributing guidelines for onboarding and email [email protected] to request to join our Slack channel!


Version Number: 9.0.1-0
Reproducible in staging?: Y
Reproducible in production?: Y
If this was caught during regression testing, add the test name, ID and link from TestRail: https://expensify.testrail.io/index.php?/tests/view/4661747&group_by=cases:section_id&group_order=asc&group_id=296775
Email or phone of affected tester (no customers): [email protected]
Issue reported by: Applause - Internal Team

Action Performed:

  1. Open a chat
  2. Write text in composer box
  3. Refresh the page

Expected Result:

Cursor Position Should Not Resets to Start of Message on Page Refresh

Actual Result:

Cursor Position Resets to Start of Message on Page Refresh

Workaround:

Unknown

Platforms:

Which of our officially supported platforms is this issue occurring on?

  • Android: Native
  • Android: mWeb Chrome
  • iOS: Native
  • iOS: mWeb Safari
  • MacOS: Chrome / Safari
  • MacOS: Desktop

Screenshots/Videos

Add any screenshot/video evidence

Bug6522559_1719208729219.2024-06-24_10-56-46.mp4

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~0121d7a8303e8c6df6
  • Upwork Job ID: 1805917059078868913
  • Last Price Increase: 2024-07-03
  • Automatic offers:
    • hoangzinh | Reviewer | 103037350
Issue OwnerCurrent Issue Owner: @hoangzinh
@lanitochka17 lanitochka17 added Daily KSv2 Bug Something is broken. Auto assigns a BugZero manager. labels Jun 24, 2024
Copy link

melvin-bot bot commented Jun 24, 2024

Triggered auto assignment to @twisterdotcom (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

@lanitochka17
Copy link
Author

@twisterdotcom FYI I haven't added the External label as I wasn't 100% sure about this issue. Please take a look and add the label if you agree it's a bug and can be handled by external contributors

@lanitochka17
Copy link
Author

We think that this bug might be related to #vip-vsp

@BartoszGrajdek
Copy link
Contributor

This issue is possibly related to live markdown, please add it to the project 🙏🏻 cc @thienlnam

@BartoszGrajdek
Copy link
Contributor

@twisterdotcom I believe this issue could be resolved by external contributors. can we open it up for proposals?

@twisterdotcom twisterdotcom added the External Added to denote the issue can be worked on by a contributor label Jun 26, 2024
Copy link

melvin-bot bot commented Jun 26, 2024

Job added to Upwork: https://www.upwork.com/jobs/~0121d7a8303e8c6df6

@melvin-bot melvin-bot bot changed the title Composer - Cursor Position Resets to Start of Message on Page Refresh [$250] Composer - Cursor Position Resets to Start of Message on Page Refresh Jun 26, 2024
@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Jun 26, 2024
Copy link

melvin-bot bot commented Jun 26, 2024

Triggered auto assignment to Contributor-plus team member for initial proposal review - @hoangzinh (External)

@grogou
Copy link

grogou commented Jun 27, 2024

Proposal

Please re-state the problem that we are trying to solve in this issue.

Cursor position reverts to the beginning of the message when the page is refreshed.

What is the root cause of that problem?

The root cause is that the input field’s state, including the selection state for cursor position, resets on page refresh. Without explicit management using setSelection, the cursor defaults to the beginning.

What changes do you think we should make in order to solve the problem?

We need to check if value exist at first render and move cursor to the end

if (value && value.length > 0) {
    setSelection({
         start: value.length,
         end: value.length
    });
}

Here we can add this

useEffect(() => {
if (!textInput.current) {
return;
}
const debouncedSetPrevScroll = lodashDebounce(() => {
if (!textInput.current) {
return;
}
setPrevScroll(textInput.current.scrollTop);
}, 100);
textInput.current.addEventListener('scroll', debouncedSetPrevScroll);
return () => {
textInput.current?.removeEventListener('scroll', debouncedSetPrevScroll);
};
}, []);

Contributor details
Your Expensify account email: [email protected]
Upwork Profile Link: https://www.upwork.com/freelancers/~01631ccf59d885b1c2

Copy link

melvin-bot bot commented Jun 27, 2024

✅ Contributor details stored successfully. Thank you for contributing to Expensify!

@iCodePrograms
Copy link

Proposal

Please re-state the problem that we are trying to solve in this issue.
The position of the caret(vertical blinking line which determines position of text) needs to move to the end of the text when the page refreshes

What is the root cause of that problem?
The position of the caret is determined by the browser which happens before, after which the text is added in the input box, this results in cursor position to be in the beginning rather than the end.

What changes do you think we should make in order to solve the problem?
In order to solve this problem, we would have to set the position of the cursor dynamically to the end of the text whichever is added later(if it exists) for which we can use the Selection API and trigger this in one of the useEffects already in place which handles page load functionalities.

Selection.collapseToEnd()

this will ensure that the caret is always placed in the end of the text. There would no need to add another useEffect or store the position of the caret everytime when the data changes in the page.

@hoangzinh
Copy link
Contributor

hoangzinh commented Jun 28, 2024

@grogou @iCodePrograms Thanks for your proposal. I don't think your RCA is correct. We already trigger scroll and set selection on mount here

useEffect(() => {
// Scrolls the composer to the bottom and sets the selection to the end, so that longer drafts are easier to edit
updateMultilineInputRange(textInputRef.current, !!shouldAutoFocus);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

@bernhardoj
Copy link
Contributor

Proposal

Please re-state the problem that we are trying to solve in this issue.

Cursor/selection position resets to the start of the message when refreshing.

What is the root cause of that problem?

When the composer mounts, we already call a function to set the selection based on the text length.

useEffect(() => {
// Scrolls the composer to the bottom and sets the selection to the end, so that longer drafts are easier to edit
updateMultilineInputRange(textInputRef.current, !!shouldAutoFocus);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

if ('value' in input && typeof input.value === 'string' && input.setSelectionRange) {
const length = input.value.length;
if (shouldAutoFocus) {
(input as HTMLInputElement).setSelectionRange(length, length);
}
// eslint-disable-next-line no-param-reassign
input.scrollTop = input.scrollHeight;
}

However, setSelectionRange is missing. The function doesn't exist in live markdown input.

What changes do you think we should make in order to solve the problem?

Add setSelectionRange to the live markdown. We can add it when setting up the ref here.

r.setSelectionRange = (start, end) => {
  const newSelection = {start, end};
  contentSelection.current = newSelection;
  updateRefSelectionVariables(newSelection);
  CursorUtils.setCursorPosition(currentRef, newSelection.start, newSelection.end);
}

The code that we will add is actually the same as this one, but I just simplify it for testing purpose. I think we can create a new function for that to make it reusable.

But this isn't enough because there is a problem with the app composer code. There is a useEffect that runs every time the composer selection prop is updated. It's basically to sync the selection prop and the selection state.

useEffect(() => {
setSelection((prevSelection) => {
if (!!prevSelection && selectionProp.start === prevSelection.start && selectionProp.end === prevSelection.end) {
return;
}
return selectionProp;
});
}, [selectionProp]);

The selectionProp is initially both 0 (start and end), but the prevSelection already contains the updated selection from setSelectionRange even though we initialize the selection state with the selectionProp, so the selection will be updated to the selection prop which is 0.

const [selection, setSelection] = useState<
| {
start: number;
end?: number;
positionX?: number;
positionY?: number;
}
| undefined
>({
start: selectionProp.start,
end: selectionProp.end,
});

Based on the react doc, using a set state function updater, the updater will be queued. Instead of doing that, we can just do it like this:

useEffect(() => {
    if (!!selection && selectionProp.start === selection.start && selectionProp.end === selection.end) {
        return;
    }
    setSelection(selectionProp);
}, [selectionProp]);

Copy link

melvin-bot bot commented Jul 1, 2024

@twisterdotcom, @hoangzinh Whoops! This issue is 2 days overdue. Let's get this updated quick!

@melvin-bot melvin-bot bot added the Overdue label Jul 1, 2024
@twisterdotcom
Copy link
Contributor

One more proposal from @bernhardoj here @hoangzinh.

@hoangzinh
Copy link
Contributor

The selectionProp is initially both 0 (start and end), but the prevSelection already contains the updated selection from setSelectionRange even though we initialize the selection state with the selectionProp

@bernhardoj can you help me understand this? When I put console.log here, it's true that prevSelection already contains the updated selection but why selectionProp is initially both 0? Doesn't selectionProp must be equal with prevSelection? Thank you. Moreover, if I only apply your solution related to useEffect, it works without define setSelectionRange to the live markdown input

@melvin-bot melvin-bot bot removed the Overdue label Jul 3, 2024
Copy link

melvin-bot bot commented Jul 3, 2024

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

@bernhardoj
Copy link
Contributor

but why selectionProp is initially both 0?

Because we set the initial state of the selection of the parent component (ComposerWithSuggestions) to 0.

const [selection, setSelection] = useState<TextSelection>(() => ({start: 0, end: 0, positionX: 0, positionY: 0}));

It's coming from #32168 to fix a safari-only issue, so we need to handle the selection manually, but the safari issue is already fixed and the workaround is removed in #40295.

Moreover, if I only apply your solution related to useEffect, it works without define setSelectionRange to the live markdown input

Oh, you're right. I found that the onSelectionChange of live-markdown is triggered on focus which updates the selection state. Not sure if that's expected or not, but if it's, then I think we only need to fix the useEffect since the safari workaround isn't needed anymore too.

@hoangzinh
Copy link
Contributor

then I think we only need to fix the useEffect since the safari workaround isn't needed anymore too.

HI @bernhardoj, do you mind updating your proposal with this approach to clarify what you mean? (you can put it as an alternative solution). Thank you.

@bernhardoj
Copy link
Contributor

Oh sorry, the useEffect that I meant is this one which is already in my proposal.
image

@melvin-bot melvin-bot bot added the Weekly KSv2 label Jul 9, 2024
@bernhardoj
Copy link
Contributor

PR is ready

cc: @hoangzinh

@melvin-bot melvin-bot bot added Weekly KSv2 Awaiting Payment Auto-added when associated PR is deployed to production and removed Weekly KSv2 labels Jul 17, 2024
@melvin-bot melvin-bot bot changed the title [$250] Composer - Cursor Position Resets to Start of Message on Page Refresh [HOLD for payment 2024-07-24] [$250] Composer - Cursor Position Resets to Start of Message on Page Refresh Jul 17, 2024
Copy link

melvin-bot bot commented Jul 17, 2024

Reviewing label has been removed, please complete the "BugZero Checklist".

@melvin-bot melvin-bot bot removed the Reviewing Has a PR in review label Jul 17, 2024
Copy link

melvin-bot bot commented Jul 17, 2024

The solution for this issue has been 🚀 deployed to production 🚀 in version 9.0.7-8 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

If no regressions arise, payment will be issued on 2024-07-24. 🎊

For reference, here are some details about the assignees on this issue:

Copy link

melvin-bot bot commented Jul 17, 2024

BugZero Checklist: The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed:

  • [@hoangzinh] The PR that introduced the bug has been identified. Link to the PR:
  • [@hoangzinh] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake. Link to comment:
  • [@hoangzinh] A discussion in #expensify-bugs has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner. Link to discussion:
  • [@hoangzinh] Determine if we should create a regression test for this bug.
  • [@hoangzinh] If we decide to create a regression test for the bug, please propose the regression test steps to ensure the same bug will not reach production again.
  • [@twisterdotcom] Link the GH issue for creating/updating the regression test once above steps have been agreed upon:

@mallenexpensify
Copy link
Contributor

Have another 'cursor position isn't in right place issue`. If it's related to this or, or if this might fix it, plz comment on the issue

@bernhardoj
Copy link
Contributor

@mallenexpensify I don't think it's related because the components being used are different (the PR for this issue is already merged too)

@melvin-bot melvin-bot bot added Daily KSv2 and removed Weekly KSv2 labels Jul 23, 2024
@twisterdotcom
Copy link
Contributor

twisterdotcom commented Jul 24, 2024

Payment Summary:

@hoangzinh please complete the checklist.

@twisterdotcom twisterdotcom removed the Awaiting Payment Auto-added when associated PR is deployed to production label Jul 24, 2024
@twisterdotcom twisterdotcom changed the title [HOLD for payment 2024-07-24] [$250] Composer - Cursor Position Resets to Start of Message on Page Refresh [Awaiting checklist] [$250] Composer - Cursor Position Resets to Start of Message on Page Refresh Jul 24, 2024
@bernhardoj
Copy link
Contributor

Requested in ND.

@JmillsExpensify
Copy link

$250 approved for @bernhardoj

@hoangzinh
Copy link
Contributor

Regression Test Proposal

  1. Sign in to NewDot
  2. Open any chat
  3. Type a message in the composer
  4. Navigate to another chat
  5. Navigate back to the chat in step 2
  6. Verify that the composer is focused and the cursor is at the end of the draft message

Do we agree 👍 or 👎

@hoangzinh
Copy link
Contributor

hoangzinh commented Jul 26, 2024

BugZero Checklist:

  • The PR that introduced the bug has been identified. Link to the PR: Migrate Composer. web #23359
  • The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake. Link to comment: https://github.com/Expensify/App/pull/23359/files#r1692999112
  • A discussion in #expensify-bugs has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner. Link to discussion: N/A
  • Determine if we should create a regression test for this bug: ✅ Yes we should

@hoangzinh hoangzinh mentioned this issue Jul 26, 2024
58 tasks
@twisterdotcom
Copy link
Contributor

please propose the regression test steps to ensure the same bug will not reach production again.

@hoangzinh
Copy link
Contributor

I posted the Regression Test Proposal here #44259 (comment)

@mallenexpensify
Copy link
Contributor

@perunt
Copy link
Contributor

perunt commented Jul 29, 2024

Hey folks!

I came from this PR as I was mentioned here.

I tested this before the fix on dev(and also on staging, but today, it potentially already has those changes) and noticed that the focus was at the end of the text input.
After the fix, I tested it again and found that after a refresh, the focus always moves to the end. I'm just curious if an option to preserve the cursor position has been discussed rather than moving it to the end @lanitochka17? If not, what do you think about implementing this feature? @hoangzinh

@BartoszGrajdek BartoszGrajdek moved this from MEDIUM to Done in Live Markdown Jul 29, 2024
@hoangzinh
Copy link
Contributor

If not, what do you think about implementing this feature?

Hi @perunt, It's an interesting idea for me. But I'm unsure if we will prioritize it right now. I think you can post it in Slack as a feature request to collect opinions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor
Projects
Status: Done
Development

No branches or pull requests