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

Desktop: Resolves #4251: Refactor sidebar to better handle thousands of tags and notebooks #10331

Conversation

personalizedrefrigerator
Copy link
Collaborator

@personalizedrefrigerator personalizedrefrigerator commented Apr 18, 2024

Summary

This pull request refactors the desktop sidebar to:

  1. Avoid re-generating the list of tags and folders when the selection changes.
  2. Render the currently visible notes and/or tags, rather than all notes and tags on each re-render.
  3. Have Playwright tests.
    • To support this change, several items that were missing accessibility labels have been them. This includes the "new note" button and the expand/unexpand folder buttons.

With this pull request,

  • Adding tags is faster.
  • Switching notebooks is faster.
  • Moving a large notebook with thousands of items to the trash is still slow.
  • Expanding a large notebook with many nested subnotebooks is still slow (though doesn't seem to be as slow as before).

Fixes #4251.

Notes

  • This should only affect desktop. A similar pull request may need to be created to refactor the mobile sidebar.
  • At present, this pull request assumes that all notebook, tag, and header items have the same height in the sidebar.
    • At present, this height is 30px.
    • This may not be the case if the user installs a theme like the ReMoods theme or has changed Joplin's notebook list styles with custom CSS.
  • Much of the logic that was originally in Sidebar.tsx has been moved to the useOnRenderItem.ts hook.
  • The following issues seem to have been present before this PR and are still present:
    • When dragging one folder to another, the note count for the parent folder may briefly go negative.
    • When adding a new notebook, the notebook is initially added below the trash folder, then moved to the correct location. This seems to be because we set doRefreshFolders to true instead of now here (true causes a delayed update, while now updates without delay).

To-do

  • Test manually: How much faster is it?
  • Automated tests
    • Potentially add more: Include tests for note count and adding/removing tags.
  • Follow-up pull request: Make finding which notebooks are children of the current notebook faster. At present, it seems to be in $\mathcal{O}(n^2)$.
    • As a result, Joplin is still slow with many deeply-nested notebooks.

Manual testing

On Ubuntu 23.10, I have manually verified that:

  1. Dragging a note to a notebook heading moves that note to the target notebook.
  2. Clicking on the "NOTEBOOKS" header hides/shows all notebooks
  3. Clicking on the "TAGS" header hides/shows all tags
  4. Selecting a notebook and/or tag outside of the viewport with the up/down arrow keys scrolls the viewport
  5. Note counts update when dragging one notebook to another
  6. It is still possible to use "go to anything" to search and scroll through search results with arrow keys
    • GoToAnything uses ItemList, which is refactored by this pull request.
  7. On mobile (Android 10 emulator), with 4-5 notebooks, it is still possible to select and expand/collapse notebooks in the sidemenu.

Note that this pull request has automated tests, which additionally tests:

  • Expanding/collapsing notebooks
  • Dragging one notebook to another
  • Dragging one notebook to the "all notes" heading
  • Creating new notebooks using the "+" button
  • Changing focus using the up and down arrow keys

Screen recordings

Faster notebook switching

faster-notebook-switching.mp4

Faster tagging

In the recording below, the main screen is open in the background in both copies of Joplin. The numbers of notes/tags/notebooks in both profiles are similar to in the tests done here.

faster-tagging.mp4

@personalizedrefrigerator personalizedrefrigerator changed the title Desktop: Resolves #4251: Refactor sidebar to better handle thousands tags and notebooks Desktop: Resolves #4251: Refactor sidebar to better handle thousands of tags and notebooks Apr 18, 2024
@personalizedrefrigerator personalizedrefrigerator marked this pull request as ready for review April 19, 2024 06:01
@laurent22
Copy link
Owner

With this pull request is it faster to render the list of tags?

Also I think the problem that this user is having is due to having many tags, and every time a sync operation is performed the tag list is rendered (slowly) again. I'm wondering if your PR would help with this? See also here for example: https://discourse.joplinapp.org/t/desktop-ui-is-too-slow-to-work-efficiently/23657/10?u=laurent

Copy link
Owner

@laurent22 laurent22 left a comment

Choose a reason for hiding this comment

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

It's quite a big refactoring and difficult to review but I didn't notice any obvious issue other than the folder/notebook issue. Splitting the sidebar into multiple components should definitely help with optimising it.

}


const NotebookAndTagList: React.FC<Props> = props => {
Copy link
Owner

Choose a reason for hiding this comment

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

Should be FolderAndTagList. There are several instances of "notebook" being used in this pull request instead of "folder" so if you could change everything to "folder" that would be great. As always, we only use the term "notebook" in user-facing string, and only "folder" internally.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've made this change in most places.

For now, I've left sidebar.spec.ts as-is, because Playwright is accessing user-facing labels (which use "notebook") to run the test. I can change the internal variable names there well if desired.

Copy link
Owner

Choose a reason for hiding this comment

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

I would prefer we use "folder" there too if that's ok, including in test descriptions, and even when creating folders for test (So "Folder A" instead of "Notebook A").

Basically the only time we should use the term "notebook" is in strings to be translated or in documentation. Ideally we'd change everything to use only one term or the other, but that would be more trouble than it's worth.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Got it! This should be addressed by 97ec1a9.

@personalizedrefrigerator
Copy link
Collaborator Author

With this pull request is it faster to render the list of tags?

Yes. This pull request refactors the sidebar to use an ItemList, which only renders the tags/folder headers that are currently visible on the screen.

It still needs to load the sorted list of tags and folders, but does so less often than before this pull request.

Additionally, switching notebooks/tags should be faster (see the screen recording above):

  • Rather than re-rendering the full sidebar when the selected notebook/tag changes, we now only update the currently selected sidebar index (see useSelectedSidebarIndex.ts). This is partially to make it easier to manage keyboard focus and partially for performance.
  • If the notebook list or tag list needs to be regenerated (e.g. because a notebook is added), useSidebarListData.ts caches the other list. As a result, adding a tag does not force the list of notebooks to be regenerated.
    • Previously, we would both re-generate this data and render it as components.

Also I think the problem that this user is having is due to having many tags, and every time a sync operation is performed the tag list is rendered (slowly) again. I'm wondering if your PR would help with this?

This pull request should help with that (see the "Faster tagging" video above for an example).

itemRenderer={onRenderItem}
onKeyDown={onKeyEventHandler}

itemHeight={30}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sure I like hardcoding the item height as 30... Doing so makes it difficult to force a larger-than-30px sidebar font with custom CSS.

Aside from measuring one of the children (e.g. with .clientHeight), I'm not sure how else to do this, however.

@laurent22
Copy link
Owner

There's a conflict on the Sidebar file

@laurent22 laurent22 merged commit 97b5276 into laurent22:dev Apr 25, 2024
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add tags to a note is extrem slow
3 participants