diff --git a/src/components/Settings/Language/Language.container.js b/src/components/Settings/Language/Language.container.js
index 5cfc1056b..a29456916 100644
--- a/src/components/Settings/Language/Language.container.js
+++ b/src/components/Settings/Language/Language.container.js
@@ -60,10 +60,6 @@ export class LanguageContainer extends Component {
* TTS default engine
*/
ttsDefaultEngine: PropTypes.object,
- /**
- * list of available voices
- */
- voices: PropTypes.array,
/**
* Callback fired when language changes
*/
@@ -78,7 +74,28 @@ export class LanguageContainer extends Component {
onClose: PropTypes.func,
history: PropTypes.object.isRequired,
getVoices: PropTypes.func.isRequired,
- updateLangSpeechStatus: PropTypes.func.isRequired
+ updateLangSpeechStatus: PropTypes.func.isRequired,
+ /**
+ * Object that contain properties refered to "download a language feature"
+ */
+ downloadingLang: PropTypes.shape({
+ // the indicator that the user is trying to download a language.
+ isdownloading: PropTypes.bool,
+ // the indicator that availables voices have been updated.
+ isUpdated: PropTypes.bool,
+ // the indicator that the user pressed the 'configure a local voice' button.
+ isDiferentTts: PropTypes.bool,
+ // the tts engine name that the user is trying to download.
+ engineName: PropTypes.string,
+ // the tts Engine market ID that the user is trying to download.
+ marketId: PropTypes.string,
+ // the lang that the user is trying to download.
+ selectedLang: PropTypes.string,
+ // the indicator that the lang is already available online and the app would change the lang after starting downloading.
+ continueOnline: PropTypes.bool,
+ // the indicator that the user clicks to download and the tts is already available. is Used to show a different message.
+ firstClick: PropTypes.bool
+ })
};
state = {
@@ -88,10 +105,132 @@ export class LanguageContainer extends Component {
avaliableAndDownloadablesLangs: [],
downloadablesOnly: []
},
- downloadLangLoading: true,
+ downloadLangLoading: false,
downloadingLangError: { ttsError: false, langError: false }
};
+ componentDidMount = () => {
+ if (isAndroid()) {
+ const { isdownloading } = this.props.downloadingLang;
+ this.refreshAndroidLanguageList();
+ this.setState({
+ downloadLangLoading: isdownloading
+ });
+ document.addEventListener(
+ 'backbutton',
+ this.handleAndroidBackButton,
+ false
+ );
+ }
+ };
+
+ componentWillUnmount = () => {
+ if (isAndroid())
+ document.removeEventListener(
+ 'backbutton',
+ this.handleAndroidBackButton,
+ false
+ );
+ };
+
+ componentDidUpdate = async () => {
+ if (isAndroid()) {
+ const { isdownloading, isUpdated } = this.props.downloadingLang;
+ if (isdownloading && isUpdated) {
+ await this.lookDownloadingLang();
+ }
+ }
+ };
+
+ refreshAndroidLanguageList = () => {
+ if (!isAndroid()) return;
+ const prepareDownloadablesLenguages = () => {
+ const getDownloadablesLenguages = downloadablesTts => {
+ const formatLangObject = downloadablesLangs => {
+ return downloadablesLangs.map(langObject => {
+ //const code = ISO6391.getCode(langObject.lang);
+ const { lang } = langObject;
+ const code = lang.slice(0, 2).toLowerCase();
+ langObject.langCode = code;
+ langObject.nativeName = ISO6391.getNativeName(code);
+ const showLangCode =
+ downloadablesLangs.filter(language => language.langCode === code)
+ .length > 1;
+
+ const langFullCode = showLangCode ? `(${lang})` : '';
+ langObject.nativeName = `${langObject.nativeName} ${langFullCode}`;
+ return langObject;
+ });
+ };
+ const downloadablesLangsArray = downloadablesTts.map(tts => {
+ return {
+ langs: tts.langs,
+ marketId: tts.marketId,
+ ttsName: tts.name
+ };
+ });
+ const identifiedLangsArray = downloadablesLangsArray.map(langObject =>
+ langObject.langs.map(language => {
+ return {
+ lang: language,
+ marketId: langObject.marketId,
+ ttsName: langObject.ttsName
+ };
+ })
+ );
+ const INITIAL_VALUE = [];
+ const downloadablesLangs = identifiedLangsArray.reduce(
+ (accumulator, currentValue) => accumulator.concat(currentValue),
+ INITIAL_VALUE
+ );
+ return formatLangObject(downloadablesLangs);
+ };
+
+ const filterAvailablesAndDownloadablesLangs = downloadablesLangs => {
+ const { ttsEngines } = this.props;
+ const { langs } = this.props;
+ const ttsEnginesNames = ttsEngines.map(tts => tts.name);
+
+ const availableAndDownloadableLang = downloadablesLangs.filter(
+ ({ lang, ttsName }) => langs.includes(lang)
+ );
+
+ return availableAndDownloadableLang.map(item => {
+ item.ttsAvailable = ttsEnginesNames.includes(item.ttsName);
+ return item;
+ });
+ };
+
+ const filterDownloadablesOnlyLangs = (
+ downloadables,
+ availableAndDownloadables
+ ) => {
+ return downloadables.filter(
+ downloadableLang =>
+ !availableAndDownloadables.includes(downloadableLang)
+ );
+ };
+
+ const downloadablesLangsList = getDownloadablesLenguages(
+ downloadablesTts
+ );
+ const avaliableAndDownloadablesLangs = filterAvailablesAndDownloadablesLangs(
+ downloadablesLangsList
+ );
+ const downloadablesOnly = filterDownloadablesOnlyLangs(
+ downloadablesLangsList,
+ avaliableAndDownloadablesLangs
+ );
+ return {
+ avaliableAndDownloadablesLangs,
+ downloadablesOnly
+ };
+ };
+ this.setState({
+ downloadablesLangs: prepareDownloadablesLenguages()
+ });
+ };
+
handleSubmit = async (optionalLang = null) => {
const { onLangChange } = this.props;
const selectedLang = optionalLang ? optionalLang : this.state.selectedLang;
@@ -132,6 +271,7 @@ export class LanguageContainer extends Component {
await setTtsEngine(engineName);
const voices = await getVoices();
await updateLangSpeechStatus(voices);
+ this.refreshAndroidLanguageList();
} catch (err) {
throw new Error('TTS engine selection error on handleSetTtsEngine');
}
@@ -202,6 +342,7 @@ export class LanguageContainer extends Component {
isDiferentTts: false,
engineName: ttsName,
marketId: marketId,
+ isUpdated: false,
selectedLang: lang
};
this.props.setDownloadingLang(downloadingLangState);
@@ -213,83 +354,6 @@ export class LanguageContainer extends Component {
this.setState({ openDialog: { open: false, downloadingLangData: {} } });
};
- prepareDownloadablesLenguages = () => {
- const getDownloadablesLenguages = downloadablesTts => {
- const formatLangObject = downloadablesLangs => {
- return downloadablesLangs.map(langObject => {
- //const code = ISO6391.getCode(langObject.lang);
- const { lang } = langObject;
- const code = lang.slice(0, 2).toLowerCase();
- langObject.langCode = code;
- langObject.nativeName = ISO6391.getNativeName(code);
- const showLangCode =
- downloadablesLangs.filter(language => language.langCode === code)
- .length > 1;
-
- const langFullCode = showLangCode ? `(${lang})` : '';
- langObject.nativeName = `${langObject.nativeName} ${langFullCode}`;
- return langObject;
- });
- };
- const downloadablesLangsArray = downloadablesTts.map(tts => {
- return { langs: tts.langs, marketId: tts.marketId, ttsName: tts.name };
- });
- const identifiedLangsArray = downloadablesLangsArray.map(langObject =>
- langObject.langs.map(language => {
- return {
- lang: language,
- marketId: langObject.marketId,
- ttsName: langObject.ttsName
- };
- })
- );
- const INITIAL_VALUE = [];
- const downloadablesLangs = identifiedLangsArray.reduce(
- (accumulator, currentValue) => accumulator.concat(currentValue),
- INITIAL_VALUE
- );
- return formatLangObject(downloadablesLangs);
- };
-
- const filterAvailablesAndDownloadablesLangs = downloadablesLangs => {
- const { ttsEngines } = this.props;
- const { langs } = this.props;
- const ttsEnginesNames = ttsEngines.map(tts => tts.name);
-
- const availableAndDownloadableLang = downloadablesLangs.filter(
- ({ lang, ttsName }) => langs.includes(lang)
- );
-
- return availableAndDownloadableLang.map(item => {
- item.ttsAvailable = ttsEnginesNames.includes(item.ttsName);
- return item;
- });
- };
-
- const filterDownloadablesOnlyLangs = (
- downloadables,
- availableAndDownloadables
- ) => {
- return downloadables.filter(
- downloadableLang =>
- !availableAndDownloadables.includes(downloadableLang)
- );
- };
-
- const downloadablesLangsList = getDownloadablesLenguages(downloadablesTts);
- const avaliableAndDownloadablesLangs = filterAvailablesAndDownloadablesLangs(
- downloadablesLangsList
- );
- const downloadablesOnly = filterDownloadablesOnlyLangs(
- downloadablesLangsList,
- avaliableAndDownloadablesLangs
- );
- return {
- avaliableAndDownloadablesLangs,
- downloadablesOnly
- };
- };
-
pauseCallback = () => {
const downloadingLangState = {
...this.props.downloadingLang,
@@ -305,12 +369,7 @@ export class LanguageContainer extends Component {
downloadingLangData,
firstClick = false
) => {
- const {
- setDownloadingLang,
- ttsEngine,
- localLangs,
- lang: appLang
- } = this.props;
+ const { setDownloadingLang, localLangs, lang: appLang } = this.props;
const { ttsName, lang, marketId } = downloadingLangData;
const { avaliableAndDownloadablesLangs } = this.state.downloadablesLangs;
@@ -321,39 +380,34 @@ export class LanguageContainer extends Component {
.includes(downloadingLangData.lang) && appLang !== lang
? true
: false;
-
- if (ttsEngine.name === ttsName) {
- const downloadingLangState = {
- isdownloading: true,
- isDiferentTts: false,
- engineName: ttsName,
- marketId: marketId,
- selectedLang: lang,
- continueOnline,
- firstClick
- };
- setDownloadingLang(downloadingLangState);
- this.setState({
- downloadingLangError: {
- ttsError: false,
- langError: true
- }
- });
- return;
- }
+ event.stopPropagation();
this.setState({ downloadLangLoading: true });
+ try {
+ await this.handleSetTtsEngine(ttsName);
+ } catch (err) {
+ console.error(err);
+ }
const downloadingLangState = {
isdownloading: true,
isDiferentTts: true,
engineName: ttsName,
marketId: marketId,
selectedLang: lang,
+ isUpdated: true,
continueOnline,
firstClick
};
setDownloadingLang(downloadingLangState);
};
+ handleAndroidBackButton = () => {
+ if (this.props.downloadingLang.isdownloading) {
+ this.onErrorDialogCancel();
+ return;
+ }
+ this.onClose();
+ };
+
onErrorDialogAcepted = () => {
const { ttsError } = this.state.downloadingLangError;
const { marketId, continueOnline, lang } = this.props.downloadingLang;
@@ -402,45 +456,42 @@ export class LanguageContainer extends Component {
};
lookDownloadingLang = async () => {
- const {
- isDiferentTts,
- engineName,
- selectedLang
- } = this.props.downloadingLang;
-
- const {
- setDownloadingLang,
-
- ttsEngines,
- ttsEngine,
- history,
- showNotification
- } = this.props;
+ const { setDownloadingLang, downloadingLang } = this.props;
+
+ const processUpdates = async () => {
+ const { isDiferentTts, engineName, selectedLang } = downloadingLang;
+
+ const {
+ localLangs,
+ ttsEngines,
+ ttsEngine,
+ history,
+ showNotification
+ } = this.props;
+
+ if (engineName === ttsEngine.name && localLangs.includes(selectedLang)) {
+ setDownloadingLang({ isdownloading: false, isUpdated: false });
+ this.setState({ selectedLang: selectedLang });
+ if (isDiferentTts) return;
+ await this.handleSubmit(selectedLang);
+ showNotification(
+
+ );
+ history.push('/settings');
+ return;
+ }
- const ttsEnginesNames = ttsEngines.map(tts => tts.name);
- if (!ttsEnginesNames.includes(engineName)) {
- this.setState({
- downloadingLangError: {
- ...this.state.downloadingLangError,
- ttsError: true
- }
- });
- return;
- }
- if (ttsEngine.name !== engineName) {
- try {
- await this.handleSetTtsEngine(engineName);
- } catch {
+ const ttsEnginesNames = ttsEngines.map(tts => tts.name);
+ if (!ttsEnginesNames.includes(engineName)) {
this.setState({
downloadingLangError: {
- ttsError: false,
- langError: true
+ ...this.state.downloadingLangError,
+ ttsError: true
}
});
+ return;
}
- }
- const localLangs = this.props.localLangs;
- if (!localLangs.includes(selectedLang)) {
+
this.setState({
downloadingLangError: {
ttsError: false,
@@ -448,66 +499,13 @@ export class LanguageContainer extends Component {
}
});
return;
- }
- const downloadingLangState = {
- isdownloading: false
};
- setDownloadingLang(downloadingLangState);
- this.setState({
- downloadingLangError: {
- ttsError: false,
- langError: false
- },
- selectedLang: selectedLang
- });
- this.refreshLanguageList();
- if (isDiferentTts) return;
- await this.handleSubmit(selectedLang);
- showNotification(
-
- );
- history.push('/settings');
- };
-
- refreshLanguageList = () => {
- this.setState({
- downloadablesLangs: isAndroid()
- ? this.prepareDownloadablesLenguages()
- : {
- //downloadablesLangsList: []
- avaliableAndDownloadablesLangs: [],
- downloadablesOnly: []
- }
- });
- };
-
- refreshDownloadLanguage = async () => {
- const { isdownloading } = this.props.downloadingLang;
-
- this.refreshLanguageList();
- if (isdownloading) await this.lookDownloadingLang();
+ setDownloadingLang({ ...downloadingLang, isUpdated: false });
+ await processUpdates();
this.setState({ downloadLangLoading: false });
};
- componentDidMount = async () => {
- if (this.props.langsFetched) this.refreshDownloadLanguage();
- };
-
- componentDidUpdate = async prevProps => {
- const isdownloading = this.props.downloadingLang?.isdownloading;
- const langsFetched = this.props.langsFetched;
-
- if (!prevProps.langsFetched && langsFetched) {
- this.setState({ downloadLangLoading: true });
- await this.refreshDownloadLanguage();
- }
- if (!isdownloading) return;
- if (prevProps.downloadingLang.isdownloading === false) {
- await this.refreshDownloadLanguage();
- }
- };
-
render() {
const { lang, langs, localLangs, ttsEngines, ttsEngine } = this.props;
@@ -563,13 +561,11 @@ export class LanguageContainer extends Component {
const mapStateToProps = state => ({
lang: state.language.lang,
- langsFetched: state.language.langsFetched,
langs: state.language.langs,
localLangs: state.language.localLangs,
ttsEngines: state.speech.ttsEngines,
ttsEngine: state.speech.ttsEngine,
- downloadingLang: state.language.downloadingLang,
- voices: state.speech.voices
+ downloadingLang: state.language.downloadingLang
});
const mapDispatchToProps = {
diff --git a/src/providers/LanguageProvider/LanguageProvider.reducer.js b/src/providers/LanguageProvider/LanguageProvider.reducer.js
index 68a17418d..3b22429ad 100644
--- a/src/providers/LanguageProvider/LanguageProvider.reducer.js
+++ b/src/providers/LanguageProvider/LanguageProvider.reducer.js
@@ -16,7 +16,6 @@ const initialState = {
dir: 'ltr',
langs: [],
localLangs: [],
- langsFetched: false,
downloadingLang: { isdownloading: false }
};
@@ -45,8 +44,7 @@ function languageProviderReducer(state = initialState, action) {
return {
...state,
langs: action.langs.sort(),
- localLangs: action.localLangs || [],
- langsFetched: true
+ localLangs: action.localLangs || []
};
case SET_DOWNLOADING_LANG:
return { ...state, downloadingLang: action.downloadingLangData };
diff --git a/src/providers/SpeechProvider/SpeechProvider.actions.js b/src/providers/SpeechProvider/SpeechProvider.actions.js
index 7c99b9346..e63afd439 100644
--- a/src/providers/SpeechProvider/SpeechProvider.actions.js
+++ b/src/providers/SpeechProvider/SpeechProvider.actions.js
@@ -53,26 +53,19 @@ export function receiveTtsEngine(ttsEngineName) {
}
export function getTtsEngines() {
- const ttsEngines = tts?.getTtsEngines();
+ const ttsEngines = tts.getTtsEngines();
return {
type: RECEIVE_TTS_ENGINES,
ttsEngines
};
}
-export function setTtsEngine(selectedTtsEngineName) {
+export function setTtsEngine(ttsEngineName) {
return async dispatch => {
dispatch(requestTtsEngine());
try {
- const engineAvailable = tts
- .getTtsEngines()
- .map(tts => tts.name)
- .includes(selectedTtsEngineName);
- const engineName = engineAvailable
- ? selectedTtsEngineName
- : tts.getTtsDefaultEngine().name;
- const voices = await tts.setTtsEngine(engineName);
- dispatch(receiveTtsEngine(engineName));
+ const voices = await tts.setTtsEngine(ttsEngineName);
+ dispatch(receiveTtsEngine(ttsEngineName));
if (!voices.length) {
throw new Error('TTS engine does not have a language.');
}
@@ -82,7 +75,7 @@ export function setTtsEngine(selectedTtsEngineName) {
};
}
-export function updateLangSpeechStatus(voices) {
+export function updateLangSpeechStatus(voices, forceChangeVoice = false) {
return async (dispatch, getState) => {
try {
const supportedLangs = getSupportedLangs(voices);
@@ -103,7 +96,9 @@ export function updateLangSpeechStatus(voices) {
// last step is to change voice in case it is available
if (
- getState().speech.options.lang.substring(0, 2) !== lang.substring(0, 2)
+ getState().speech.options.lang.substring(0, 2) !==
+ lang.substring(0, 2) ||
+ forceChangeVoice
) {
const uris = voices.map(v => {
return v.voiceURI;
diff --git a/src/providers/SpeechProvider/SpeechProvider.container.js b/src/providers/SpeechProvider/SpeechProvider.container.js
index 413b4b157..c61a805a9 100644
--- a/src/providers/SpeechProvider/SpeechProvider.container.js
+++ b/src/providers/SpeechProvider/SpeechProvider.container.js
@@ -11,13 +11,17 @@ import {
setTtsEngine,
setCurrentVoiceSource
} from './SpeechProvider.actions';
+import { setDownloadingLang } from '../LanguageProvider/LanguageProvider.actions';
+
import { isAndroid } from '../../cordova-util';
export class SpeechProvider extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
ttsEngine: PropTypes.object,
- setTtsEngine: PropTypes.func
+ setTtsEngine: PropTypes.func,
+ downloadingLang: PropTypes.object,
+ setDownloadingLang: PropTypes.func
};
async componentDidMount() {
@@ -26,30 +30,68 @@ export class SpeechProvider extends Component {
updateLangSpeechStatus,
getTtsEngines,
getTtsDefaultEngine,
+ ttsEngines,
ttsEngine,
+ ttsDefaultEngine,
setTtsEngine,
- setCurrentVoiceSource
+ setCurrentVoiceSource,
+ downloadingLang,
+ setDownloadingLang
} = this.props;
+ const getTtsEngineName = () => {
+ const ttsEnginesNames = ttsEngines?.map(tts => tts.name);
+ if (!ttsEnginesNames) return null;
+ if (
+ downloadingLang?.isdownloading &&
+ downloadingLang.engineName &&
+ ttsEnginesNames.includes(downloadingLang.engineName)
+ ) {
+ forceChangeVoice = true;
+ return downloadingLang.engineName;
+ }
+
+ if (
+ ttsEngine &&
+ ttsEngine.name &&
+ ttsEnginesNames.includes(ttsEngine.name)
+ )
+ return ttsEngine.name;
+
+ const defaultEngineName = ttsDefaultEngine?.name;
+ if (ttsEnginesNames.includes(defaultEngineName)) return defaultEngineName;
+ return null;
+ };
+
+ let forceChangeVoice = false;
if (tts.isSupported()) {
//if android we have to set the tts engine first
- if (isAndroid()) {
- getTtsEngines();
- getTtsDefaultEngine();
+ try {
+ if (isAndroid()) {
+ getTtsEngines();
+ getTtsDefaultEngine();
+ }
+ } catch (error) {
+ console.error(error);
}
- if (ttsEngine && ttsEngine.name) {
+
+ const ttsEngineName = getTtsEngineName();
+
+ if (ttsEngineName) {
try {
- await setTtsEngine(ttsEngine.name);
+ await setTtsEngine(ttsEngineName);
} catch (err) {
console.error(err.message);
+ forceChangeVoice = false;
}
}
try {
const voices = await getVoices();
- await updateLangSpeechStatus(voices);
+ await updateLangSpeechStatus(voices, forceChangeVoice);
} catch (err) {
console.error(err.message);
}
+ setDownloadingLang({ ...downloadingLang, isUpdated: true });
}
setCurrentVoiceSource();
}
@@ -62,7 +104,11 @@ export class SpeechProvider extends Component {
}
const mapStateToProps = state => ({
- ttsEngine: state.speech.ttsEngine
+ ttsEngines: state.speech.ttsEngines,
+ ttsEngine: state.speech.ttsEngine,
+ ttsDefaultEngine: state.speech.ttsDefaultEngine,
+ //todo: downloadingVoices
+ downloadingLang: state.language.downloadingLang
});
const mapDispatchToProps = {
@@ -71,7 +117,9 @@ const mapDispatchToProps = {
getTtsDefaultEngine,
setTtsEngine,
updateLangSpeechStatus,
- setCurrentVoiceSource
+ setCurrentVoiceSource,
+ //todo: setDownloadingVoices
+ setDownloadingLang
};
export default connect(
diff --git a/src/reducers.js b/src/reducers.js
index 26e891a95..ad32e6ecb 100644
--- a/src/reducers.js
+++ b/src/reducers.js
@@ -1,8 +1,4 @@
-import {
- persistCombineReducers,
- persistReducer,
- createMigrate
-} from 'redux-persist';
+import { persistCombineReducers, createMigrate } from 'redux-persist';
import appReducer from './components/App/App.reducer';
import languageProviderReducer from './providers/LanguageProvider/LanguageProvider.reducer';
@@ -30,21 +26,14 @@ const boardMigrations = {
const config = {
key: 'root',
storage,
- blacklist: ['language'],
version: 0,
migrate: createMigrate(boardMigrations, { debug: false })
};
-const languagePersistConfig = {
- key: 'language',
- storage: storage,
- blacklist: ['langsFetched']
-};
-
export default function createReducer() {
return persistCombineReducers(config, {
app: appReducer,
- language: persistReducer(languagePersistConfig, languageProviderReducer),
+ language: languageProviderReducer,
speech: speechProviderReducer,
board: boardReducer,
communicator: communicatorReducer,