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: Fixes #10077 - Special characters in notebooks and tags are not sorted alphabetically #10085

Merged
merged 13 commits into from Mar 20, 2024

Conversation

cagnusmarlsen
Copy link
Contributor

Summary

Fixes the issue where the names of notebooks and tags are not sorted alphabetically if the name starts with a special character.

Fixes #10077

Testing

Follow reproduction steps in #10077:

  • Notes: When alphabetical sort order is applied, "normal" and special characters are sorted correctly.

  • Notebooks and Tags: Verify that both normal and special characters are sorted correctly here as well.

This has been tested successfully on Windows 11 and Ubuntu.

@@ -29,9 +29,9 @@ function TagList(props: Props) {

const tags = useMemo(() => {
const output = props.items.slice();

const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
Copy link
Collaborator

Choose a reason for hiding this comment

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

does it not need to know the locale? looking at the MDN page, it has this example:

// in German, ä sorts with a
console.log(new Intl.Collator("de").compare("ä", "z"));
// -1, or some other negative value

// in Swedish, ä sorts after z
console.log(new Intl.Collator("sv").compare("ä", "z"));
// 1, or some other positive value

how does your change handle it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The original issue stated that the names were sorted correctly for Notes and the problem only occurred for Notebooks and Tags.

Assuming that the sorting is fine for Notes, I took a similar approach here. Looking at the code for Notes, I don't think that the locale is being passed as a parameter to Intl.Collator during sorting, which is why I think your example wouldn't work for Notes either.

https://github.com/laurent22/joplin/blob/1b96a165866f3f9ad3499f45e01097e224797a27/packages/lib/models/Note.ts#L1121C2-L1123C3

    public static getNaturalSortingCollator() {
            return new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
    }

If we want to take the locale into consideration, the changes would have to be made for Notes as well, which I thought was out of the scope of this issue.

If that is something that we actually want to do, I could try to make those changes test it with your example.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, I'm not sure what's the right thing here.

From what I can see the original issue was only for Turkish language. It can be that the sorting method used for folders produces correct result for Turkish by accident but isn't correct for some other language. Unfortunately, I don't think there's an easy way to check, except maybe ask on the forum.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey, I made some changes to the code to also consider the current locale before we do the sorting. I made it so that the notes, folders and tags are all sorted according to the current locale of the system. I tested for German, Swedish and some other languages and it seems to be working correctly, changing the sort order based on the locale. Some screenshots -

For German, ä sorts with a -

german

For Swedish, ä sorts after z -

svenska

I think this approach makes more sense and is more inclusive than the previous commit.

@@ -13,6 +14,7 @@ interface Props {
}

function TagList(props: Props) {
const collatorLocale = Setting.value('locale').slice(0, 2);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Consider using one of the functions in packages/lib/locale.ts

@roman-r-m
Copy link
Collaborator

I wonder if there are other places we order things by title and if it makes sense to apply the same change there. I think there's one in ResourceScreen where it sorts resources by name.
There's also some sorting going on in sortFolderTree in Folder.ts
Also in setTags.ts

May be out of scope for this change though.

@JackGruber
Copy link
Contributor

I think it should be noted that this also needs to be adapted for the mobile app. Not that these have different sorting again like in the last issue: #9996

@laurent22
Copy link
Owner

@cagnusmarlsen, marph91 also addressed this issue in this pull request #10081 but used localeCompare instead, however as a result it doesn't address sorting numbers (1, 10, 2 instead of 1, 2, 10). Could you maybe collaborate with him to see what might the complete solution?

@cagnusmarlsen
Copy link
Contributor Author

cagnusmarlsen commented Mar 9, 2024

Hey laurent, Overall, I think this PR already addresses the following issues,

  1. Sorts numbers properly.
  2. Sorts Notes, Folders and Tags based on the current locale (as per the discussion I had above).

I took a look at marph91's pull request and I think the reason the numbers are not sorted is because the numeric argument was not passed to localeCompare. Plus, I think localCompare also accepts a locale argument. Their PR should work otherwise.

@marph91
Copy link
Contributor

marph91 commented Mar 9, 2024

Since the scope gets bigger and bigger, I guess it makes time-wise sense for me to give up my PR and just review the changes here.

I could collect the following requirements:

  1. Sort special characters on desktop correctly for notebooks, tags (and notes). This was the original scope and works at both PR.
  2. Sort numbers correctly. For example 1, 2, 10 instead of 1, 10, 2 (reference). This doesn't work at my PR, even when using the numeric argument. Not sure about yours.
  3. Make sure the mobile app sorts everything in the same way as the desktop app (reference). I mentioned two potential changes here.
  4. Check for other places, where the sorting could be applied (reference).

Not sure if all of them are needed to get the PR accepted, though.

NB: I added tests for 1. and 2. in fd29f7f. You can cherry-pick them if you like.

@cagnusmarlsen
Copy link
Contributor Author

marph91, Thanks for this, I have cherry picked the tests that you wrote.
And yeah, I think the scope has gotten bigger than what the original issue mentioned. As mentioned previously, this PR addresses the following points -

  1. Sorts the Notes, folders and tags based on the currentLocale, while taking care of special characters.
  2. Sorts the numbers properly.

I am also not sure if the rest of the changes that you mentioned should be added to this PR or not, I think the main points of the original issue have been addressed.

@cagnusmarlsen
Copy link
Contributor Author

cagnusmarlsen commented Mar 9, 2024

I don't know why the build failed, seems like a weird issue. The logs say this -

/home/runner/work/joplin/joplin/packages/app-desktop/gui/JoplinCloudLoginScreen.tsx:30:8 - Unknown word (applicatio)

Edit: Seems like its a spellchecker issue here -

const applicatioAuthId = useMemo(() => uuidgen(), []);

@marph91
Copy link
Contributor

marph91 commented Mar 9, 2024

It fails on the dev branch, too. I think it's not relevant for this PR.

Comment on lines 25 to 26


Copy link
Owner

Choose a reason for hiding this comment

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

No white space changes please

@@ -13,6 +14,7 @@ interface Props {
}

function TagList(props: Props) {
const collatorLocale = currentLocale().slice(0, 2);
Copy link
Owner

Choose a reason for hiding this comment

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

I think you misunderstood Roman's comment - there are functions in lib/locale to parse these strings. For example the languageName function and you should use this instead of string manipulation

@@ -1125,7 +1126,8 @@ export default class Note extends BaseItem {
}

public static getNaturalSortingCollator() {
return new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
const collatorLocale = currentLocale().slice(0, 2);
return new Intl.Collator(collatorLocale, { numeric: true, sensitivity: 'base' });
Copy link
Owner

Choose a reason for hiding this comment

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

You've added the same code in 4 different places. Could you move all this to a function and call it from those 4 places please? A good place for the function could be lib/models/utils for example

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good. I will make these changes

@@ -72,6 +73,8 @@ export const renderFolders = (props: Props, renderItem: RenderFolderItem) => {

export const renderTags = (props: Props, renderItem: RenderTagItem) => {
const tags = props.tags.slice();
const collatorLocale = getCollatorLocale();
const collator = getCollator(collatorLocale);
Copy link
Contributor

Choose a reason for hiding this comment

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

You could remove even more duplicated code by calling getCollatorLocale() inside getCollator(). Either as default parameter or inside the function.

Copy link
Owner

Choose a reason for hiding this comment

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

I agree. getCollatorLocale is used only by getCollator so not really needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have made it so that getCollatorLocale() is passed as the default parameter to getCollator().

@laurent22 laurent22 merged commit e9ebd84 into laurent22:dev Mar 20, 2024
10 checks passed
@laurent22
Copy link
Owner

That looks good, thanks @cagnusmarlsen, and thank @roman-r-m and @marph91 for reviewing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Special characters in notebooks and tags are not sorted alphabetically
5 participants