cybersemics / em

A beautiful, minimalistic note-taking app for personal sensemaking.
Other
282 stars 107 forks source link

Cursor scrolls out of view after navigating to favorite #2022

Closed raineorshine closed 1 month ago

raineorshine commented 3 months ago

When navigating to a favorite thought that has expanded thoughts above, the cursor is either not scrolled to, or is pushed out of view.

Steps to Reproduce

- US
  - States
    - x
      - =pin
        - true
      - a
        - b
        - c
        - d
        - e
        - f
        - g
        - h
        - i
        - j
        - k
        - l
        - m
    - Alabama
    - Alaska
    - Arizona
    - Arkansas
    - California
    - Colorado
    - Connecticut
    - Delaware
    - Florida
    - Georgia
    - Hawaii
    - Idaho
    - Illinois
    - Indiana
    - Iowa
    - Kansas
    - Kentucky
    - Louisiana
    - Maine
    - Maryland
    - Massachusetts
    - Michigan
    - Minnesota
    - Mississippi
    - Missouri
    - Montana
    - Nebraska
    - Nevada
    - New Hampshire
    - New Jersey
    - New Mexico
    - New York
    - North Carolina
    - North Dakota
    - Ohio
    - Oklahoma
    - Oregon
    - Pennsylvania
    - Rhode Island
    - South Carolina
    - South Dakota
    - Tennessee
    - Texas
    - Utah
    - Vermont
    - Virginia
    - Washington
    - West Virginia
    - Wisconsin
    - Wyoming
      - =favorite
        - true
      - a
        - b
          - c
- x
- y

Case 1

  1. Paste the above thoughts.
  2. Set the cursor to null.
  3. Refresh the page.
  4. Open the Favorites in the sidebar and click Wisconsin.

Case 2

Add an artificial loading delay (by adding await sleep(500) to the data provider's getThoughtById) and follow the same steps as above.

Current Behavior

Case 1

The cursor is not scrolled to, and remains below the bottom of the viewport:

Case 2

After clicking the favorite link, scrollCursorIntoView is called in scrollCursorIntoViewMiddleware, but when the thoughts above are loaded the cursor gets pushed offscreen:

https://github.com/cybersemics/em/assets/750276/822bea78-25f6-4a6b-8c71-0be8f99df697

Expected Behavior

The cursor should stay in the viewport.

Since we don't know how long it will take for the thoughts to load, we should probably keep calling scrollCursorIntoView whenever new thoughts are loaded.

However, once the user scrolls they are "in control" and we should cancel this behavior, otherwise it would feel disruptive.

Strategy

The solution should be general to any cursor movement, not just favorites, although this can be treated as a representative case. Currently scrollCursorIntoView is called in scrollCursorIntoViewMiddleware whenever the cursor changes, however it is being called before newly expanded thoughts are loaded.

It would be ideal if there was a way to fix this issue without adding more state. Maybe we could detect if scrollTop hasn't changed, then we know it's safe to scrollCursorIntoView?

Here is an approach that adds an additional ministore. Or we could add the state to the scrollTopStore.

  1. Create a new ministore navigated.
  2. In scrollCursorIntoViewMiddleware, set navigated to true whenever the cursor changes.
  3. When the user scrolls, set navigated to false. You can subscribe to the viewport ministore for a throttled scroll callback.
  4. In scrollCursorIntoViewMiddleware, when the updateThoughts action is dispatched, if navigated is true, call scrollCursorIntoView. This should be throttled.