Remember frequently used languages

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-05-17 18:49:27 +02:00
parent 2e43f8967f
commit 32db7c6fad
5 changed files with 50 additions and 4 deletions

View file

@ -15,6 +15,7 @@ import { getFeatures, parseVersion } from 'soapbox/utils/features';
import { chooseEmoji } from './emojis';
import { importFetchedAccounts } from './importer';
import { rememberLanguageUse } from './languages';
import { uploadFile, updateMedia } from './media';
import { openModal, closeModal } from './modals';
import { getSettings } from './settings';
@ -361,6 +362,10 @@ const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
dispatch(submitComposeRequest(composeId));
dispatch(closeModal());
if (compose.language && !statusId) {
dispatch(rememberLanguageUse(compose.language));
}
const idempotencyKey = compose.idempotencyKey;
const params: Record<string, any> = {

16
src/actions/languages.ts Normal file
View file

@ -0,0 +1,16 @@
import { saveSettings } from './settings';
import type { AppDispatch } from 'soapbox/store';
const LANGUAGE_USE = 'LANGUAGE_USE' as const;
const rememberLanguageUse = (language: string) => (dispatch: AppDispatch) => {
dispatch({
type: LANGUAGE_USE,
language,
});
dispatch(saveSettings());
};
export { LANGUAGE_USE, rememberLanguageUse };

View file

@ -2,13 +2,24 @@ import { offset, useFloating, flip, arrow, shift } from '@floating-ui/react';
import clsx from 'clsx';
import { supportsPassiveEvents } from 'detect-passive-events';
import fuzzysort from 'fuzzysort';
import { Map as ImmutableMap } from 'immutable';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { createSelector } from 'reselect';
import { changeComposeLanguage } from 'soapbox/actions/compose';
import { Button, Icon, Input, Portal } from 'soapbox/components/ui';
import { type Language, languages as languagesObject } from 'soapbox/features/preferences';
import { useAppDispatch, useCompose } from 'soapbox/hooks';
import { useAppDispatch, useAppSelector, useCompose } from 'soapbox/hooks';
const getFrequentlyUsedLanguages = createSelector([
state => state.settings.get('frequentlyUsedLanguages', ImmutableMap()),
], (languageCounters: ImmutableMap<Language, number>) => (
languageCounters.keySeq()
.sort((a, b) => languageCounters.get(a, 0) - languageCounters.get(b, 0))
.reverse()
.toArray()
));
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
@ -26,14 +37,15 @@ interface ILanguageDropdown {
const LanguageDropdown: React.FC<ILanguageDropdown> = ({ composeId }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const frequentlyUsedLanguages = useAppSelector(getFrequentlyUsedLanguages);
const node = useRef<HTMLDivElement>(null);
const focusedItem = useRef<HTMLDivElement>(null);
const arrowRef = useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = useState<boolean>(false);
const [searchValue, setSearchValue] = useState('');
const arrowRef = useRef<HTMLDivElement>(null);
const { x, y, strategy, refs, middlewareData, placement } = useFloating<HTMLButtonElement>({
placement: 'top',
@ -131,7 +143,12 @@ const LanguageDropdown: React.FC<ILanguageDropdown> = ({ composeId }) => {
} else if (b[0] === language) {
return 1;
} else {
return 0;
// Sort according to frequently used languages
const indexOfA = frequentlyUsedLanguages.indexOf(a[0]);
const indexOfB = frequentlyUsedLanguages.indexOf(b[0]);
return ((indexOfA > -1 ? indexOfA : Infinity) - (indexOfB > -1 ? indexOfB : Infinity));
}
});
}

View file

@ -415,6 +415,7 @@
"compose.edit_success": "Your post was edited",
"compose.invalid_schedule": "You must schedule a post at least 5 minutes out.",
"compose.language_dropdown.prompt": "Select language",
"compose.language_dropdown.search": "Search language…",
"compose.reply_group_indicator.message": "Posting to {groupLink}",
"compose.submit_success": "Your post was sent!",
"compose_event.create": "Create",

View file

@ -1,6 +1,7 @@
import { Map as ImmutableMap, fromJS } from 'immutable';
import { AnyAction } from 'redux';
import { LANGUAGE_USE } from 'soapbox/actions/languages';
import { ME_FETCH_SUCCESS } from 'soapbox/actions/me';
import { EMOJI_CHOOSE } from '../actions/emojis';
@ -18,7 +19,11 @@ import type { APIEntity } from 'soapbox/types/entities';
type State = ImmutableMap<string, any>;
const updateFrequentEmojis = (state: State, emoji: Emoji) => state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, (count: number) => count + 1)).set('saved', false);
const updateFrequentEmojis = (state: State, emoji: Emoji) =>
state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, (count: number) => count + 1)).set('saved', false);
const updateFrequentLanguages = (state: State, language: string) =>
state.update('frequentlyUsedLanguages', ImmutableMap<string, number>(), map => map.update(language, 0, (count: number) => count + 1)).set('saved', false);
const importSettings = (state: State, account: APIEntity) => {
account = fromJS(account);
@ -45,6 +50,8 @@ const settings = (
.set('saved', false);
case EMOJI_CHOOSE:
return updateFrequentEmojis(state, action.emoji);
case LANGUAGE_USE:
return updateFrequentLanguages(state, action.language);
case SETTING_SAVE:
return state.set('saved', true);
case SETTINGS_UPDATE: