Skip to content

Feat - Move user languages to top of the list #4853

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

Merged

Conversation

HarikalarKutusu
Copy link
Contributor

This implements the idea presented in #4734

For logged in users who have languages set in their profiles, the user language codes are passed to the language selector component, where the list is re-organized.

When not logged-in or no user languages selected, it behaves normally.

Screenshot:
image

@HarikalarKutusu HarikalarKutusu requested a review from a team as a code owner March 28, 2025 01:59
@HarikalarKutusu HarikalarKutusu requested review from moz-rotimib and removed request for a team March 28, 2025 01:59
const availableLocales = useAvailableLocales()
const availableLocalesWithNames = useNativeNameAvailableLocales()
let availableLocalesWithNames = useNativeNameAvailableLocales()
Copy link
Contributor

@moz-rotimib moz-rotimib Mar 28, 2025

Choose a reason for hiding this comment

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

Generally we try to avoid using let for variable declaration when possible. This can be refactored like below so you don't need to use let:

const initialAvailableLocalesWithNames = useNativeNameAvailableLocales()

const getAvailableLocalesWithNames = () => {
    // Get user languages to top of the list
    if (userLanguages.length > 0) {
      const userLanguagesWithNames = userLanguages.map(lang =>
        initialAvailableLocalesWithNames.find(({ code }) => code === lang)
      )

      return userLanguagesWithNames.concat(
        initialAvailableLocalesWithNames.filter(
          item => !userLanguages.includes(item.code)
        )
      )
    }

    return initialAvailableLocalesWithNames
  }

  const availableLocalesWithNames = getAvailableLocalesWithNames()

  function getLocaleWithName(locale: string) {
    return availableLocalesWithNames.find(({ code }) => code === locale)
  }

Copy link
Contributor

Choose a reason for hiding this comment

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

One change I just made is to check if the userLanguages array is not empty so we don't run the if-statement even when the array is empty. Empty arrays are truthy.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We all do. It is just CPU cycles against RAM :) That refactoring is a good idea of course. Implemented with the initial value fix.
Done

@@ -82,9 +91,11 @@ const LocalizationSelectComplex = ({ locale, onLocaleChange }: Props) => {
key={item}
className={classNames('list-item-wrapper', {
highlighted: index === highlightedIndex,
})}>
})}
style={{ borderBottomColor: userLanguages.length > 0 && index === userLanguages.length-1 ? "#333" : "#f3f2f0" }}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you use the classNames library to add a class that does? Also use the colors we have defined in vars.css in the css for this component

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 nearly always used CSS libraries. classNames is a simple and powerful one, especially conditionals!
Done

@moz-rotimib
Copy link
Contributor

Thanks for your contribution @HarikalarKutusu ❤️ 🚀

I've left two minor comments

@HarikalarKutusu
Copy link
Contributor Author

@moz-rotimib, there is one weird behavior I encounter, which also exists in original.
When I refresh the page it defaults to English (grey), I expect it to emphasize TR. I think it is a race condition on highlightedIndex which might need a useEffect.

image

@moz-rotimib
Copy link
Contributor

@moz-rotimib, there is one weird behavior I encounter, which also exists in original. When I refresh the page it defaults to English (grey), I expect it to emphasize TR. I think it is a race condition on highlightedIndex which might need a useEffect.

image

@HarikalarKutusu I got this too. I was able to solve it with useEffect like you mentioned. This is what worked for me. You can test and check on your end.

const initialSelectedItem =
    userLanguages.length > 0
      ? availableLocalesWithNames[0].code
      : localWithName.code


React.useEffect(() => {
    selectItem(initialSelectedItem)
  }, [initialSelectedItem])

I also removed initialSelectedItem from the call to useSelect

@HarikalarKutusu
Copy link
Contributor Author

HarikalarKutusu commented Mar 28, 2025

const initialSelectedItem =
    userLanguages.length > 0
      ? availableLocalesWithNames[0].code
      : localWithName.code

@moz-rotimib: Above one enforces user's first language :) The following works as desired - with added useEffect...

  const initialSelectedItem = localWithName
    ? localWithName.code
    : availableLocalesWithNames[0].code

I need to check for locales without native names, probably : availableLocales[0] is better indeed.

@moz-rotimib
Copy link
Contributor

const initialSelectedItem =
    userLanguages.length > 0
      ? availableLocalesWithNames[0].code
      : localWithName.code

@moz-rotimib: Above one enforces user's first language :) The following works as desired - with added useEffect...

  const initialSelectedItem = localWithName
    ? localWithName.code
    : availableLocalesWithNames[0].code

I need to check for locales without native names, probably : availableLocales[0] is better indeed.

@HarikalarKutusu all good 🚀 once the changes are made I'll test locally and approve

@HarikalarKutusu
Copy link
Contributor Author

It seems OK on my side, you can test now...

@moz-rotimib
Copy link
Contributor

@HarikalarKutusu it seems the localization-select-complex test is failing.

This is because in the test we are asserting that onLocaleChange is called only once when the user localization dropdown is selected. In reality it's called twice because we first call selectItem on render which triggers onLocaleChange.

So to fix this text, I advise that we reset the mock after the component is rendered and then make the assertion as usual. Here's a code snippet to show what I mean.

it('should call onLocalChange with a new locale when changed', async () => {
    const onLocalChangeMock = jest.fn()
    renderWithProviders(
      <LocalizationSelectComplex onLocaleChange={onLocalChangeMock} />
    )

    // resets the mock because we call onLocaleChange once on render
    onLocalChangeMock.mockClear() ----> // RESETS THE MOCK

    // pick an option in the select box
    userEvent.selectOptions(
      screen.getAllByLabelText('Choose language/localization')[1],
      screen.getByRole('option', { name: '臺語' })
    )

    expect(onLocalChangeMock).toBeCalledWith('nan-tw')
    expect(onLocalChangeMock).toBeCalledTimes(1)
  })

Let me know what you think

@HarikalarKutusu
Copy link
Contributor Author

HarikalarKutusu commented Mar 31, 2025

I have Jest extension on CV Code in this new dev environment but it gives a long list warnings and errors. I need to properly configure it to catch these locally.

Not ideal that it renders twice because of useEffect, but we would not get it right otherwise... Implemented what you suggested, let's see how it tests.

@moz-rotimib
Copy link
Contributor

Looks good 🚀 will merge and deploy in the next release. Thanks

@HarikalarKutusu
Copy link
Contributor Author

Great 👍 Thank you for your help 🥇

@moz-rotimib moz-rotimib merged commit 7bac0fd into common-voice:main Apr 2, 2025
2 of 3 checks passed
@HarikalarKutusu HarikalarKutusu deleted the feat-focused-language-selector branch April 14, 2025 18:13
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.

2 participants