mirror of
https://github.com/plankanban/planka.git
synced 2025-07-18 20:59:44 +02:00
parent
a1cb04ea8e
commit
1329da3fe5
31 changed files with 277 additions and 40 deletions
|
@ -41,6 +41,13 @@ export const handleUserUpdate = (user) => ({
|
|||
},
|
||||
});
|
||||
|
||||
export const updateCurrentUserLanguage = (language) => ({
|
||||
type: EntryActionTypes.CURRENT_USER_LANGUAGE_UPDATE,
|
||||
payload: {
|
||||
language,
|
||||
},
|
||||
});
|
||||
|
||||
export const updateUserEmail = (id, data) => ({
|
||||
type: EntryActionTypes.USER_EMAIL_UPDATE,
|
||||
payload: {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button, Divider, Header, Tab } from 'semantic-ui-react';
|
||||
import { Button, Divider, Dropdown, Header, Tab } from 'semantic-ui-react';
|
||||
|
||||
import locales from '../../../locales';
|
||||
import AvatarEditPopup from './AvatarEditPopup';
|
||||
import User from '../../User';
|
||||
import UserInformationEdit from '../../UserInformationEdit';
|
||||
|
@ -20,12 +21,14 @@ const AccountPane = React.memo(
|
|||
avatarUrl,
|
||||
phone,
|
||||
organization,
|
||||
language,
|
||||
isAvatarUpdating,
|
||||
usernameUpdateForm,
|
||||
emailUpdateForm,
|
||||
passwordUpdateForm,
|
||||
onUpdate,
|
||||
onAvatarUpdate,
|
||||
onLanguageUpdate,
|
||||
onUsernameUpdate,
|
||||
onUsernameUpdateMessageDismiss,
|
||||
onEmailUpdate,
|
||||
|
@ -41,6 +44,13 @@ const AccountPane = React.memo(
|
|||
});
|
||||
}, [onUpdate]);
|
||||
|
||||
const handleLanguageChange = useCallback(
|
||||
(_, { value }) => {
|
||||
onLanguageUpdate(value === 'auto' ? null : value); // FIXME: hack
|
||||
},
|
||||
[onLanguageUpdate],
|
||||
);
|
||||
|
||||
return (
|
||||
<Tab.Pane attached={false} className={styles.wrapper}>
|
||||
<AvatarEditPopup
|
||||
|
@ -60,6 +70,32 @@ const AccountPane = React.memo(
|
|||
}}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
<Divider horizontal section>
|
||||
<Header as="h4">
|
||||
{t('common.language', {
|
||||
context: 'title',
|
||||
})}
|
||||
</Header>
|
||||
</Divider>
|
||||
<Dropdown
|
||||
fluid
|
||||
selection
|
||||
options={[
|
||||
{
|
||||
key: 'auto',
|
||||
value: 'auto',
|
||||
text: t('common.detectAutomatically'),
|
||||
},
|
||||
...locales.map((locale) => ({
|
||||
key: locale.language,
|
||||
value: locale.language,
|
||||
flag: locale.country,
|
||||
text: locale.name,
|
||||
})),
|
||||
]}
|
||||
value={language || 'auto'}
|
||||
onChange={handleLanguageChange}
|
||||
/>
|
||||
<Divider horizontal section>
|
||||
<Header as="h4">
|
||||
{t('common.authentication', {
|
||||
|
@ -129,6 +165,7 @@ AccountPane.propTypes = {
|
|||
avatarUrl: PropTypes.string,
|
||||
phone: PropTypes.string,
|
||||
organization: PropTypes.string,
|
||||
language: PropTypes.string,
|
||||
isAvatarUpdating: PropTypes.bool.isRequired,
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
usernameUpdateForm: PropTypes.object.isRequired,
|
||||
|
@ -137,6 +174,7 @@ AccountPane.propTypes = {
|
|||
/* eslint-enable react/forbid-prop-types */
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
onAvatarUpdate: PropTypes.func.isRequired,
|
||||
onLanguageUpdate: PropTypes.func.isRequired,
|
||||
onUsernameUpdate: PropTypes.func.isRequired,
|
||||
onUsernameUpdateMessageDismiss: PropTypes.func.isRequired,
|
||||
onEmailUpdate: PropTypes.func.isRequired,
|
||||
|
@ -150,6 +188,7 @@ AccountPane.defaultProps = {
|
|||
avatarUrl: undefined,
|
||||
phone: undefined,
|
||||
organization: undefined,
|
||||
language: undefined,
|
||||
};
|
||||
|
||||
export default AccountPane;
|
||||
|
|
|
@ -14,6 +14,7 @@ const UserSettingsModal = React.memo(
|
|||
avatarUrl,
|
||||
phone,
|
||||
organization,
|
||||
language,
|
||||
subscribeToOwnCards,
|
||||
isAvatarUpdating,
|
||||
usernameUpdateForm,
|
||||
|
@ -21,6 +22,7 @@ const UserSettingsModal = React.memo(
|
|||
passwordUpdateForm,
|
||||
onUpdate,
|
||||
onAvatarUpdate,
|
||||
onLanguageUpdate,
|
||||
onUsernameUpdate,
|
||||
onUsernameUpdateMessageDismiss,
|
||||
onEmailUpdate,
|
||||
|
@ -44,12 +46,14 @@ const UserSettingsModal = React.memo(
|
|||
avatarUrl={avatarUrl}
|
||||
phone={phone}
|
||||
organization={organization}
|
||||
language={language}
|
||||
isAvatarUpdating={isAvatarUpdating}
|
||||
usernameUpdateForm={usernameUpdateForm}
|
||||
emailUpdateForm={emailUpdateForm}
|
||||
passwordUpdateForm={passwordUpdateForm}
|
||||
onUpdate={onUpdate}
|
||||
onAvatarUpdate={onAvatarUpdate}
|
||||
onLanguageUpdate={onLanguageUpdate}
|
||||
onUsernameUpdate={onUsernameUpdate}
|
||||
onUsernameUpdateMessageDismiss={onUsernameUpdateMessageDismiss}
|
||||
onEmailUpdate={onEmailUpdate}
|
||||
|
@ -92,6 +96,7 @@ UserSettingsModal.propTypes = {
|
|||
avatarUrl: PropTypes.string,
|
||||
phone: PropTypes.string,
|
||||
organization: PropTypes.string,
|
||||
language: PropTypes.string,
|
||||
subscribeToOwnCards: PropTypes.bool.isRequired,
|
||||
isAvatarUpdating: PropTypes.bool.isRequired,
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
|
@ -101,6 +106,7 @@ UserSettingsModal.propTypes = {
|
|||
/* eslint-enable react/forbid-prop-types */
|
||||
onUpdate: PropTypes.func.isRequired,
|
||||
onAvatarUpdate: PropTypes.func.isRequired,
|
||||
onLanguageUpdate: PropTypes.func.isRequired,
|
||||
onUsernameUpdate: PropTypes.func.isRequired,
|
||||
onUsernameUpdateMessageDismiss: PropTypes.func.isRequired,
|
||||
onEmailUpdate: PropTypes.func.isRequired,
|
||||
|
@ -115,6 +121,7 @@ UserSettingsModal.defaultProps = {
|
|||
avatarUrl: undefined,
|
||||
phone: undefined,
|
||||
organization: undefined,
|
||||
language: undefined,
|
||||
};
|
||||
|
||||
export default UserSettingsModal;
|
||||
|
|
|
@ -31,6 +31,7 @@ export default {
|
|||
USER_UPDATE: `${PREFIX}/USER_UPDATE`,
|
||||
CURRENT_USER_UPDATE: `${PREFIX}/CURRENT_USER_UPDATE`,
|
||||
USER_UPDATE_HANDLE: `${PREFIX}/USER_UPDATE_HANDLE`,
|
||||
CURRENT_USER_LANGUAGE_UPDATE: `${PREFIX}/CURRENT_USER_LANGUAGE_UPDATE`,
|
||||
USER_EMAIL_UPDATE: `${PREFIX}/USER_EMAIL_UPDATE`,
|
||||
CURRENT_USER_EMAIL_UPDATE: `${PREFIX}/CURRENT_USER_EMAIL_UPDATE`,
|
||||
USER_EMAIL_UPDATE_ERROR_CLEAR: `${PREFIX}/USER_EMAIL_UPDATE_ERROR_CLEAR`,
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
updateCurrentUser,
|
||||
updateCurrentUserAvatar,
|
||||
updateCurrentUserEmail,
|
||||
updateCurrentUserLanguage,
|
||||
updateCurrentUserPassword,
|
||||
updateCurrentUserUsername,
|
||||
} from '../actions/entry';
|
||||
|
@ -23,6 +24,7 @@ const mapStateToProps = (state) => {
|
|||
avatarUrl,
|
||||
phone,
|
||||
organization,
|
||||
language,
|
||||
subscribeToOwnCards,
|
||||
isAvatarUpdating,
|
||||
emailUpdateForm,
|
||||
|
@ -37,6 +39,7 @@ const mapStateToProps = (state) => {
|
|||
avatarUrl,
|
||||
phone,
|
||||
organization,
|
||||
language,
|
||||
subscribeToOwnCards,
|
||||
isAvatarUpdating,
|
||||
emailUpdateForm,
|
||||
|
@ -50,6 +53,7 @@ const mapDispatchToProps = (dispatch) =>
|
|||
{
|
||||
onUpdate: updateCurrentUser,
|
||||
onAvatarUpdate: updateCurrentUserAvatar,
|
||||
onLanguageUpdate: updateCurrentUserLanguage,
|
||||
onUsernameUpdate: updateCurrentUserUsername,
|
||||
onUsernameUpdateMessageDismiss: clearCurrentUserUsernameUpdateError,
|
||||
onEmailUpdate: updateCurrentUserEmail,
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import i18n from 'i18next';
|
||||
import languageDetector from 'i18next-browser-languagedetector';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import formatDate from 'date-fns/format';
|
||||
import parseDate from 'date-fns/parse';
|
||||
import { registerLocale, setDefaultLocale } from 'react-datepicker';
|
||||
|
||||
import { embedLocales, languages } from './locales';
|
||||
import { embeddedLocales, languages } from './locales';
|
||||
|
||||
i18n.dateFns = {
|
||||
locales: {},
|
||||
|
@ -52,12 +52,12 @@ const parseDatePostProcessor = {
|
|||
};
|
||||
|
||||
i18n
|
||||
.use(languageDetector)
|
||||
.use(LanguageDetector)
|
||||
.use(formatDatePostProcessor)
|
||||
.use(parseDatePostProcessor)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources: embedLocales,
|
||||
resources: embeddedLocales,
|
||||
fallbackLng: 'en',
|
||||
supportedLngs: languages,
|
||||
load: 'languageOnly',
|
||||
|
@ -74,7 +74,7 @@ i18n
|
|||
},
|
||||
},
|
||||
react: {
|
||||
useSuspense: false,
|
||||
useSuspense: true,
|
||||
},
|
||||
debug: process.env.NODE_ENV !== 'production',
|
||||
});
|
||||
|
@ -95,4 +95,20 @@ i18n.loadCoreLocale = async (language = i18n.resolvedLanguage) => {
|
|||
});
|
||||
};
|
||||
|
||||
i18n.detectLanguage = () => {
|
||||
const {
|
||||
services: { languageDetector, languageUtils },
|
||||
} = i18n;
|
||||
|
||||
localStorage.removeItem(languageDetector.options.lookupLocalStorage);
|
||||
|
||||
const detectedLanguages = languageDetector.detect();
|
||||
|
||||
i18n.language = languageUtils.getBestMatchFromCodes(detectedLanguages);
|
||||
i18n.languages = languageUtils.toResolveHierarchy(i18n.language);
|
||||
|
||||
i18n.resolvedLanguage = undefined;
|
||||
i18n.setResolvedLanguage(i18n.language);
|
||||
};
|
||||
|
||||
export default i18n;
|
||||
|
|
8
client/src/locales/cs/index.js
Normal file
8
client/src/locales/cs/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'cs',
|
||||
country: 'cz',
|
||||
name: 'Čeština',
|
||||
embeddedLocale: login,
|
||||
};
|
8
client/src/locales/da/index.js
Normal file
8
client/src/locales/da/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'da',
|
||||
country: 'dk',
|
||||
name: 'Dansk',
|
||||
embeddedLocale: login,
|
||||
};
|
|
@ -72,6 +72,7 @@ export default {
|
|||
deleteTask_title: 'Aufgabe löschen',
|
||||
deleteUser_title: 'Benutzer löschen',
|
||||
description: 'Beschreibung',
|
||||
detectAutomatically: 'Automatische Erkennung',
|
||||
dropFileToUpload: 'Datei ablegen, um hochzuladen',
|
||||
editAttachment_title: 'Anhang bearbieten',
|
||||
editAvatar_title: 'Avatar bearbeiten',
|
||||
|
@ -97,6 +98,7 @@ export default {
|
|||
hours: 'Stunden',
|
||||
invalidCurrentPassword: 'Das aktuelle Passwort ist falsch',
|
||||
labels: 'Labels',
|
||||
language: 'Sprache',
|
||||
leaveBoard_title: 'Board verlassen',
|
||||
leaveProject_title: 'Projekt verlassen',
|
||||
list: 'Listen',
|
||||
|
|
8
client/src/locales/de/index.js
Normal file
8
client/src/locales/de/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'de',
|
||||
country: 'de',
|
||||
name: 'Deutsch',
|
||||
embeddedLocale: login,
|
||||
};
|
|
@ -64,6 +64,7 @@ export default {
|
|||
deleteTask_title: 'Delete Task',
|
||||
deleteUser_title: 'Delete User',
|
||||
description: 'Description',
|
||||
detectAutomatically: 'Detect automatically',
|
||||
dropFileToUpload: 'Drop file to upload',
|
||||
editAttachment_title: 'Edit Attachment',
|
||||
editAvatar_title: 'Edit Avatar',
|
||||
|
@ -90,6 +91,7 @@ export default {
|
|||
hours: 'Hours',
|
||||
invalidCurrentPassword: 'Invalid current password',
|
||||
labels: 'Labels',
|
||||
language: 'Language',
|
||||
leaveBoard_title: 'Leave Board',
|
||||
leaveProject_title: 'Leave Project',
|
||||
list: 'List',
|
||||
|
|
11
client/src/locales/en/index.js
Normal file
11
client/src/locales/en/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import merge from 'lodash/merge';
|
||||
|
||||
import login from './login';
|
||||
import core from './core';
|
||||
|
||||
export default {
|
||||
language: 'en',
|
||||
country: 'us',
|
||||
name: 'English',
|
||||
embeddedLocale: merge(login, core),
|
||||
};
|
8
client/src/locales/es/index.js
Normal file
8
client/src/locales/es/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'es',
|
||||
country: 'es',
|
||||
name: 'Español',
|
||||
embeddedLocale: login,
|
||||
};
|
8
client/src/locales/fr/index.js
Normal file
8
client/src/locales/fr/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'fr',
|
||||
country: 'fr',
|
||||
name: 'Français',
|
||||
embeddedLocale: login,
|
||||
};
|
|
@ -1,37 +1,27 @@
|
|||
import merge from 'lodash/merge';
|
||||
import fromPairs from 'lodash/fromPairs';
|
||||
import cs from './cs';
|
||||
import da from './da';
|
||||
import de from './de';
|
||||
import en from './en';
|
||||
import es from './es';
|
||||
import fr from './fr';
|
||||
import ja from './ja';
|
||||
import pl from './pl';
|
||||
import ru from './ru';
|
||||
import sk from './sk';
|
||||
import sv from './sv';
|
||||
import uz from './uz';
|
||||
import zh from './zh';
|
||||
|
||||
import csLogin from './cs/login';
|
||||
import daLogin from './da/login';
|
||||
import deLogin from './de/login';
|
||||
import enLogin from './en/login';
|
||||
import enCore from './en/core';
|
||||
import esLogin from './es/login';
|
||||
import frLogin from './fr/login';
|
||||
import jaLogin from './ja/login';
|
||||
import plLogin from './pl/login';
|
||||
import ruLogin from './ru/login';
|
||||
import skLogin from './sk/login';
|
||||
import svLogin from './sv/login';
|
||||
import uzLogin from './uz/login';
|
||||
import zhLogin from './zh/login';
|
||||
const locales = [cs, da, de, en, es, fr, ja, pl, ru, sk, sv, uz, zh];
|
||||
|
||||
const localePairs = [
|
||||
['cs', csLogin],
|
||||
['da', daLogin],
|
||||
['de', deLogin],
|
||||
['en', merge(enLogin, enCore)],
|
||||
['es', esLogin],
|
||||
['fr', frLogin],
|
||||
['ja', jaLogin],
|
||||
['pl', plLogin],
|
||||
['ru', ruLogin],
|
||||
['sk', skLogin],
|
||||
['sv', svLogin],
|
||||
['uz', uzLogin],
|
||||
['zh', zhLogin],
|
||||
];
|
||||
export default locales;
|
||||
|
||||
export const languages = localePairs.map((locale) => locale[0]);
|
||||
export const languages = locales.map((locale) => locale.language);
|
||||
|
||||
export const embedLocales = fromPairs(localePairs);
|
||||
export const embeddedLocales = locales.reduce(
|
||||
(result, locale) => ({
|
||||
...result,
|
||||
[locale.language]: locale.embeddedLocale,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
|
8
client/src/locales/ja/index.js
Normal file
8
client/src/locales/ja/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'ja',
|
||||
country: 'jp',
|
||||
name: '日本語',
|
||||
embeddedLocale: login,
|
||||
};
|
8
client/src/locales/pl/index.js
Normal file
8
client/src/locales/pl/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'pl',
|
||||
country: 'pl',
|
||||
name: 'Polski',
|
||||
embeddedLocale: login,
|
||||
};
|
|
@ -62,6 +62,7 @@ export default {
|
|||
deleteTask: 'Удаление задачи',
|
||||
deleteUser: 'Удаление пользователя',
|
||||
description: 'Описание',
|
||||
detectAutomatically: 'Определить автоматически',
|
||||
dropFileToUpload: 'Перетяните файл, чтобы загрузить',
|
||||
editAttachment: 'Изменение вложения',
|
||||
editAvatar: 'Изменение аватара',
|
||||
|
@ -88,6 +89,7 @@ export default {
|
|||
hours: 'Часы',
|
||||
invalidCurrentPassword: 'Неверный текущий пароль',
|
||||
labels: 'Метки',
|
||||
language: 'Язык',
|
||||
list: 'Список',
|
||||
listActions: 'Действия со списком',
|
||||
members: 'Участники',
|
||||
|
|
8
client/src/locales/ru/index.js
Normal file
8
client/src/locales/ru/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'ru',
|
||||
country: 'ru',
|
||||
name: 'Русский',
|
||||
embeddedLocale: login,
|
||||
};
|
8
client/src/locales/sk/index.js
Normal file
8
client/src/locales/sk/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'sk',
|
||||
country: 'sk',
|
||||
name: 'Slovenčina',
|
||||
embeddedLocale: login,
|
||||
};
|
8
client/src/locales/sv/index.js
Normal file
8
client/src/locales/sv/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'sv',
|
||||
country: 'se',
|
||||
name: 'Svenska',
|
||||
embeddedLocale: login,
|
||||
};
|
8
client/src/locales/uz/index.js
Normal file
8
client/src/locales/uz/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'uz',
|
||||
country: 'uz',
|
||||
name: "O'zbek",
|
||||
embeddedLocale: login,
|
||||
};
|
8
client/src/locales/zh/index.js
Normal file
8
client/src/locales/zh/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import login from './login';
|
||||
|
||||
export default {
|
||||
language: 'zh',
|
||||
country: 'cn',
|
||||
name: '中文',
|
||||
embeddedLocale: login,
|
||||
};
|
|
@ -40,6 +40,7 @@ export default class extends Model {
|
|||
avatarUrl: attr(),
|
||||
phone: attr(),
|
||||
organization: attr(),
|
||||
language: attr(),
|
||||
subscribeToOwnCards: attr(),
|
||||
deletedAt: attr(),
|
||||
isAdmin: attr({
|
||||
|
|
|
@ -4,7 +4,6 @@ import { fetchCoreRequest } from '../requests';
|
|||
import { initializeCore } from '../../../actions';
|
||||
import i18n from '../../../i18n';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function* initializeCoreService() {
|
||||
const {
|
||||
user,
|
||||
|
@ -25,6 +24,7 @@ export function* initializeCoreService() {
|
|||
notifications,
|
||||
} = yield call(fetchCoreRequest); // TODO: handle error
|
||||
|
||||
yield call(i18n.changeLanguage, user.language);
|
||||
yield call(i18n.loadCoreLocale);
|
||||
|
||||
yield put(
|
||||
|
@ -48,3 +48,14 @@ export function* initializeCoreService() {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function* changeCoreLanguageService(language) {
|
||||
if (language === null) {
|
||||
yield call(i18n.detectLanguage);
|
||||
yield call(i18n.loadCoreLocale);
|
||||
yield call(i18n.changeLanguage, i18n.resolvedLanguage);
|
||||
} else {
|
||||
yield call(i18n.loadCoreLocale, language);
|
||||
yield call(i18n.changeLanguage, language);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { call, put, select } from 'redux-saga/effects';
|
||||
|
||||
import { logoutService } from './login';
|
||||
import { changeCoreLanguageService } from './core';
|
||||
import request from '../request';
|
||||
import { currentUserIdSelector, currentUserSelector, pathSelector } from '../../../selectors';
|
||||
import {
|
||||
|
@ -81,6 +82,21 @@ export function* handleUserUpdateService(user) {
|
|||
yield put(handleUserUpdate(user, users, isCurrent));
|
||||
}
|
||||
|
||||
// TODO: add loading state
|
||||
export function* updateUserLanguageService(id, language) {
|
||||
yield call(changeCoreLanguageService, language);
|
||||
|
||||
yield call(updateUserService, id, {
|
||||
language,
|
||||
});
|
||||
}
|
||||
|
||||
export function* updateCurrentUserLanguageService(language) {
|
||||
const id = yield select(currentUserIdSelector);
|
||||
|
||||
yield call(updateUserLanguageService, id, language);
|
||||
}
|
||||
|
||||
export function* updateUserEmailService(id, data) {
|
||||
yield put(updateUserEmail(id, data));
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
updateUserService,
|
||||
updateCurrentUserAvatarService,
|
||||
updateCurrentUserEmailService,
|
||||
updateCurrentUserLanguageService,
|
||||
updateCurrentUserPasswordService,
|
||||
updateCurrentUserService,
|
||||
updateCurrentUserUsernameService,
|
||||
|
@ -49,6 +50,9 @@ export default function* userWatchers() {
|
|||
takeEvery(EntryActionTypes.USER_UPDATE_HANDLE, ({ payload: { user } }) =>
|
||||
handleUserUpdateService(user),
|
||||
),
|
||||
takeEvery(EntryActionTypes.CURRENT_USER_LANGUAGE_UPDATE, ({ payload: { language } }) =>
|
||||
updateCurrentUserLanguageService(language),
|
||||
),
|
||||
takeEvery(EntryActionTypes.USER_EMAIL_UPDATE, ({ payload: { id, data } }) =>
|
||||
updateUserEmailService(id, data),
|
||||
),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue