From f503c7ec79e4596c487bd38e332d10f8be8b7d27 Mon Sep 17 00:00:00 2001 From: Mirlan Date: Fri, 3 Jul 2020 18:06:47 +0600 Subject: [PATCH] Ott 92 lexics (#21) --- package.json | 2 +- src/config/procedures.tsx | 1 + src/features/GlobalStores/index.tsx | 6 +- .../LexicsStore/helpers/__tests__/index.tsx | 35 +++++++++++ src/features/LexicsStore/helpers/index.tsx | 60 +++++++++++++++++++ src/features/LexicsStore/hooks/index.tsx | 50 ++++++++++++++++ src/features/LexicsStore/hooks/useLang.tsx | 18 ++++++ .../LexicsStore/hooks/useLexicsConfig.tsx | 19 ++++++ .../LexicsStore/hooks/useTranslations.tsx | 26 ++++++++ src/features/LexicsStore/index.tsx | 19 ++++++ src/features/LexicsStore/types.tsx | 3 + src/features/T9n/index.tsx | 16 +++++ src/requests/getLexics.tsx | 30 ++++++++++ src/requests/index.tsx | 1 + 14 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 src/features/LexicsStore/helpers/__tests__/index.tsx create mode 100644 src/features/LexicsStore/helpers/index.tsx create mode 100644 src/features/LexicsStore/hooks/index.tsx create mode 100644 src/features/LexicsStore/hooks/useLang.tsx create mode 100644 src/features/LexicsStore/hooks/useLexicsConfig.tsx create mode 100644 src/features/LexicsStore/hooks/useTranslations.tsx create mode 100644 src/features/LexicsStore/index.tsx create mode 100644 src/features/LexicsStore/types.tsx create mode 100644 src/features/T9n/index.tsx create mode 100644 src/requests/getLexics.tsx diff --git a/package.json b/package.json index fd673ac5..5f29d3ab 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "test": "react-scripts test --testMatch '**/__tests__/*' --passWithNoTests --watchAll=false", "test:watch": "react-scripts test --testMatch '**/__tests__/*'", "eject": "react-scripts eject", - "lint": "eslint 'src/**/*.{ts,tsx}'", + "lint": "eslint \"src/**/*.{ts,tsx}\"", "storybook": "start-storybook -p 9009 -s public", "build-storybook": "build-storybook -s public" }, diff --git a/src/config/procedures.tsx b/src/config/procedures.tsx index 38e2a266..cbfb8f86 100644 --- a/src/config/procedures.tsx +++ b/src/config/procedures.tsx @@ -3,4 +3,5 @@ export const PROCEDURES = { create_user: 'create_user', get_cities: 'get_cities', lst_c_country: 'lst_c_country', + param_lexical: 'param_lexical', } diff --git a/src/features/GlobalStores/index.tsx b/src/features/GlobalStores/index.tsx index 5690cd0f..0ee68352 100644 --- a/src/features/GlobalStores/index.tsx +++ b/src/features/GlobalStores/index.tsx @@ -1,14 +1,16 @@ import React, { ReactNode } from 'react' import { AuthStore } from 'features/AuthStore' +import { LexicsStore } from 'features/LexicsStore' type Props = { children: ReactNode, } -// аутентификация, лексики и другие глобальные сторы export const GlobalStores = ({ children }: Props) => ( - {children} + + {children} + ) diff --git a/src/features/LexicsStore/helpers/__tests__/index.tsx b/src/features/LexicsStore/helpers/__tests__/index.tsx new file mode 100644 index 00000000..2341a587 --- /dev/null +++ b/src/features/LexicsStore/helpers/__tests__/index.tsx @@ -0,0 +1,35 @@ +import { mapTranslationsToLocalKeys } from '..' + +const createTranslation = (id: string) => ({ + id, + lang: '', + lexis_lang_id: '', + text: '', +}) + +it('returns empty object for empty args', () => { + const translations = {} + const config = {} + const expected = {} + + const result = mapTranslationsToLocalKeys(translations, config) + expect(result).toEqual(expected) +}) + +it('empty translations when config is empty', () => { + const translations = { 1: createTranslation('1') } + const lexicsConfig = {} + const expected = {} + + const result = mapTranslationsToLocalKeys(translations, lexicsConfig) + expect(result).toEqual(expected) +}) + +it('translations with lexic key specified in config', () => { + const translations = { 1: createTranslation('1'), 2: createTranslation('2') } + const lexicsConfig = { 2: 2, formInput: 1 } + const expected = { 2: translations[2], formInput: translations[1] } + + const result = mapTranslationsToLocalKeys(translations, lexicsConfig) + expect(result).toEqual(expected) +}) diff --git a/src/features/LexicsStore/helpers/index.tsx b/src/features/LexicsStore/helpers/index.tsx new file mode 100644 index 00000000..76d939f2 --- /dev/null +++ b/src/features/LexicsStore/helpers/index.tsx @@ -0,0 +1,60 @@ +import isArray from 'lodash/isArray' +import entries from 'lodash/entries' +import reduce from 'lodash/reduce' +import uniq from 'lodash/uniq' +import map from 'lodash/map' + +import type { Translations } from 'requests' + +import type { LexicsId, LexicsConfig } from '../types' + +const LANG_KEY = 'lang' +const defaultLang = 'en' + +export const writeLang = (lang: string) => { + localStorage.setItem(LANG_KEY, lang) +} + +export const readLang = () => { + const lang = localStorage.getItem(LANG_KEY) + if (lang) return lang + + writeLang(defaultLang) + return defaultLang +} + +export const getSuffix = (lang: string) => ( + lang === 'rus' ? 'rus' : 'eng' +) + +export const getLexicIds = (ids: Array | LexicsConfig) => ( + uniq(map(ids, (id) => Number(id))) +) + +export const getObjectConfig = (ids: Array | LexicsConfig) => { + if (isArray(ids)) { + return reduce( + ids, + (acc, id) => ({ ...acc, [id]: id }), + {}, + ) + } + return ids +} + +export const mapTranslationsToLocalKeys = ( + translations: Translations, + lexicsConfig: LexicsConfig, +) => { + const pairs = entries(lexicsConfig) + return reduce( + pairs, + (acc, [key, id]) => { + if (translations[id]) { + acc[key] = translations[id] + } + return acc + }, + {} as Translations, + ) +} diff --git a/src/features/LexicsStore/hooks/index.tsx b/src/features/LexicsStore/hooks/index.tsx new file mode 100644 index 00000000..7cdbd463 --- /dev/null +++ b/src/features/LexicsStore/hooks/index.tsx @@ -0,0 +1,50 @@ +import { useEffect, useCallback } from 'react' + +import isEmpty from 'lodash/isEmpty' + +import { getLexics } from 'requests' + +import { + getLexicIds, + mapTranslationsToLocalKeys, +} from 'features/LexicsStore/helpers' + +import { useLang } from './useLang' +import { useLexicsConfig } from './useLexicsConfig' +import { useTranslations } from './useTranslations' + +export const useLexics = () => { + const { changeLang, lang } = useLang() + const { addLexicsConfig, lexicsConfig } = useLexicsConfig() + const { addTranslations, translate } = useTranslations() + + const fetchLexics = useCallback( + async () => { + const lexicIds = getLexicIds(lexicsConfig) + if (isEmpty(lexicIds)) return + + const newTranslations = await getLexics(lang, lexicIds) + addTranslations(mapTranslationsToLocalKeys(newTranslations, lexicsConfig)) + }, + [ + lang, + lexicsConfig, + addTranslations, + ], + ) + + useEffect(() => { + fetchLexics() + }, [ + lang, + lexicsConfig, + fetchLexics, + ]) + + return { + addLexicsConfig, + changeLang, + lang, + translate, + } +} diff --git a/src/features/LexicsStore/hooks/useLang.tsx b/src/features/LexicsStore/hooks/useLang.tsx new file mode 100644 index 00000000..e9099ec6 --- /dev/null +++ b/src/features/LexicsStore/hooks/useLang.tsx @@ -0,0 +1,18 @@ +import { useState, useCallback } from 'react' + +import { readLang, writeLang } from 'features/LexicsStore/helpers' + +export const useLang = () => { + const [lang, setLang] = useState(readLang) + + const changeLang = useCallback( + (newLang: string) => { + if (newLang === lang) return + writeLang(newLang) + setLang(newLang) + }, + [lang], + ) + + return { changeLang, lang } +} diff --git a/src/features/LexicsStore/hooks/useLexicsConfig.tsx b/src/features/LexicsStore/hooks/useLexicsConfig.tsx new file mode 100644 index 00000000..99a9640d --- /dev/null +++ b/src/features/LexicsStore/hooks/useLexicsConfig.tsx @@ -0,0 +1,19 @@ +import { useState, useCallback } from 'react' + +import { getObjectConfig } from 'features/LexicsStore/helpers' + +import type { LexicsConfig, LexicsId } from '../types' + +export const useLexicsConfig = () => { + const [lexicsConfig, setLexicsConfig] = useState({}) + + const addLexicsConfig = useCallback( + (ids: Array | LexicsConfig) => { + const config = getObjectConfig(ids) + setLexicsConfig((state) => ({ ...state, ...config })) + }, + [], + ) + + return { addLexicsConfig, lexicsConfig } +} diff --git a/src/features/LexicsStore/hooks/useTranslations.tsx b/src/features/LexicsStore/hooks/useTranslations.tsx new file mode 100644 index 00000000..f81d6ff2 --- /dev/null +++ b/src/features/LexicsStore/hooks/useTranslations.tsx @@ -0,0 +1,26 @@ +import { useState, useCallback } from 'react' + +import type { Translations } from 'requests' + +import type { LexicsId } from '../types' + +export const useTranslations = () => { + const [translations, setTranslations] = useState({}) + + const translate = useCallback( + (lexicId: LexicsId) => { + const translation = translations[lexicId] + return translation?.text || '' + }, + [translations], + ) + + const addTranslations = useCallback( + (newTranslations: Translations) => { + setTranslations((state) => ({ ...state, ...newTranslations })) + }, + [], + ) + + return { addTranslations, translate } +} diff --git a/src/features/LexicsStore/index.tsx b/src/features/LexicsStore/index.tsx new file mode 100644 index 00000000..23325084 --- /dev/null +++ b/src/features/LexicsStore/index.tsx @@ -0,0 +1,19 @@ +import type { ReactNode } from 'react' +import React, { + createContext, + useContext, +} from 'react' + +import { useLexics } from './hooks' + +type LexicsStore = ReturnType +type Props = { children: ReactNode } + +const LexicsContext = createContext({} as LexicsStore) + +export const LexicsStore = ({ children }: Props) => { + const lexics = useLexics() + return {children} +} + +export const useLexicsStore = () => useContext(LexicsContext) diff --git a/src/features/LexicsStore/types.tsx b/src/features/LexicsStore/types.tsx new file mode 100644 index 00000000..aaa77b05 --- /dev/null +++ b/src/features/LexicsStore/types.tsx @@ -0,0 +1,3 @@ +export type LexicsId = string + +export type LexicsConfig = {[lexicsId: string]: number} diff --git a/src/features/T9n/index.tsx b/src/features/T9n/index.tsx new file mode 100644 index 00000000..de76fe61 --- /dev/null +++ b/src/features/T9n/index.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import styled from 'styled-components/macro' + +import { useLexicsStore } from 'features/LexicsStore' + +const Text = styled.span`` + +type Props = { + className?: string, + t: string | number, +} + +export const T9n = ({ className, t }: Props) => { + const { translate } = useLexicsStore() + return {translate(String(t))} +} diff --git a/src/requests/getLexics.tsx b/src/requests/getLexics.tsx new file mode 100644 index 00000000..3f353ff1 --- /dev/null +++ b/src/requests/getLexics.tsx @@ -0,0 +1,30 @@ +import { DATA_URL, PROCEDURES } from 'config' +import { callApi, getResponseData } from 'helpers' + +const proc = PROCEDURES.param_lexical + +export type Translation = { + id: string, + lang: string, + lexis_lang_id: string, + text: string, +} + +export type Translations = {[id: string]: Translation} + +export const getLexics = (lang: string, lexicIds: Array): Promise => { + const config = { + body: { + params: { + _p_lang: lang, + _p_param_arr: lexicIds, + }, + proc, + }, + } + + return callApi({ + config, + url: DATA_URL, + }).then(getResponseData(proc)) +} diff --git a/src/requests/index.tsx b/src/requests/index.tsx index 46debb05..54087053 100644 --- a/src/requests/index.tsx +++ b/src/requests/index.tsx @@ -2,3 +2,4 @@ export * from './register' export * from './login' export * from './getCountries' export * from './getCountryCities' +export * from './getLexics'