mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
Fixed: Improve translation loading
(cherry picked from commit 73c5ec1da4dd00301e1b0dddbcea37590a99b045)
This commit is contained in:
@@ -7,13 +7,13 @@ import PageConnector from 'Components/Page/PageConnector';
|
|||||||
import ApplyTheme from './ApplyTheme';
|
import ApplyTheme from './ApplyTheme';
|
||||||
import AppRoutes from './AppRoutes';
|
import AppRoutes from './AppRoutes';
|
||||||
|
|
||||||
function App({ store, history, hasTranslationsError }) {
|
function App({ store, history }) {
|
||||||
return (
|
return (
|
||||||
<DocumentTitle title={window.Prowlarr.instanceName}>
|
<DocumentTitle title={window.Prowlarr.instanceName}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<ConnectedRouter history={history}>
|
<ConnectedRouter history={history}>
|
||||||
<ApplyTheme>
|
<ApplyTheme>
|
||||||
<PageConnector hasTranslationsError={hasTranslationsError}>
|
<PageConnector>
|
||||||
<AppRoutes app={App} />
|
<AppRoutes app={App} />
|
||||||
</PageConnector>
|
</PageConnector>
|
||||||
</ApplyTheme>
|
</ApplyTheme>
|
||||||
@@ -25,8 +25,7 @@ function App({ store, history, hasTranslationsError }) {
|
|||||||
|
|
||||||
App.propTypes = {
|
App.propTypes = {
|
||||||
store: PropTypes.object.isRequired,
|
store: PropTypes.object.isRequired,
|
||||||
history: PropTypes.object.isRequired,
|
history: PropTypes.object.isRequired
|
||||||
hasTranslationsError: PropTypes.bool.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@@ -7,7 +7,7 @@ function ErrorPage(props) {
|
|||||||
const {
|
const {
|
||||||
version,
|
version,
|
||||||
isLocalStorageSupported,
|
isLocalStorageSupported,
|
||||||
hasTranslationsError,
|
translationsError,
|
||||||
indexersError,
|
indexersError,
|
||||||
indexerStatusError,
|
indexerStatusError,
|
||||||
indexerCategoriesError,
|
indexerCategoriesError,
|
||||||
@@ -22,8 +22,8 @@ function ErrorPage(props) {
|
|||||||
|
|
||||||
if (!isLocalStorageSupported) {
|
if (!isLocalStorageSupported) {
|
||||||
errorMessage = 'Local Storage is not supported or disabled. A plugin or private browsing may have disabled it.';
|
errorMessage = 'Local Storage is not supported or disabled. A plugin or private browsing may have disabled it.';
|
||||||
} else if (hasTranslationsError) {
|
} else if (translationsError) {
|
||||||
errorMessage = 'Failed to load translations from API';
|
errorMessage = getErrorMessage(translationsError, 'Failed to load translations from API');
|
||||||
} else if (indexersError) {
|
} else if (indexersError) {
|
||||||
errorMessage = getErrorMessage(indexersError, 'Failed to load indexers from API');
|
errorMessage = getErrorMessage(indexersError, 'Failed to load indexers from API');
|
||||||
} else if (indexerStatusError) {
|
} else if (indexerStatusError) {
|
||||||
@@ -58,7 +58,7 @@ function ErrorPage(props) {
|
|||||||
ErrorPage.propTypes = {
|
ErrorPage.propTypes = {
|
||||||
version: PropTypes.string.isRequired,
|
version: PropTypes.string.isRequired,
|
||||||
isLocalStorageSupported: PropTypes.bool.isRequired,
|
isLocalStorageSupported: PropTypes.bool.isRequired,
|
||||||
hasTranslationsError: PropTypes.bool.isRequired,
|
translationsError: PropTypes.object,
|
||||||
indexersError: PropTypes.object,
|
indexersError: PropTypes.object,
|
||||||
indexerStatusError: PropTypes.object,
|
indexerStatusError: PropTypes.object,
|
||||||
indexerCategoriesError: PropTypes.object,
|
indexerCategoriesError: PropTypes.object,
|
||||||
|
@@ -3,7 +3,7 @@ import React, { Component } from 'react';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
|
import { fetchTranslations, saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
|
||||||
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
|
import { fetchCustomFilters } from 'Store/Actions/customFilterActions';
|
||||||
import { fetchIndexers } from 'Store/Actions/indexerActions';
|
import { fetchIndexers } from 'Store/Actions/indexerActions';
|
||||||
import { fetchIndexerStatus } from 'Store/Actions/indexerStatusActions';
|
import { fetchIndexerStatus } from 'Store/Actions/indexerStatusActions';
|
||||||
@@ -54,6 +54,7 @@ const selectIsPopulated = createSelector(
|
|||||||
(state) => state.indexerStatus.isPopulated,
|
(state) => state.indexerStatus.isPopulated,
|
||||||
(state) => state.settings.indexerCategories.isPopulated,
|
(state) => state.settings.indexerCategories.isPopulated,
|
||||||
(state) => state.system.status.isPopulated,
|
(state) => state.system.status.isPopulated,
|
||||||
|
(state) => state.app.translations.isPopulated,
|
||||||
(
|
(
|
||||||
customFiltersIsPopulated,
|
customFiltersIsPopulated,
|
||||||
tagsIsPopulated,
|
tagsIsPopulated,
|
||||||
@@ -63,7 +64,8 @@ const selectIsPopulated = createSelector(
|
|||||||
indexersIsPopulated,
|
indexersIsPopulated,
|
||||||
indexerStatusIsPopulated,
|
indexerStatusIsPopulated,
|
||||||
indexerCategoriesIsPopulated,
|
indexerCategoriesIsPopulated,
|
||||||
systemStatusIsPopulated
|
systemStatusIsPopulated,
|
||||||
|
translationsIsPopulated
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
customFiltersIsPopulated &&
|
customFiltersIsPopulated &&
|
||||||
@@ -74,7 +76,8 @@ const selectIsPopulated = createSelector(
|
|||||||
indexersIsPopulated &&
|
indexersIsPopulated &&
|
||||||
indexerStatusIsPopulated &&
|
indexerStatusIsPopulated &&
|
||||||
indexerCategoriesIsPopulated &&
|
indexerCategoriesIsPopulated &&
|
||||||
systemStatusIsPopulated
|
systemStatusIsPopulated &&
|
||||||
|
translationsIsPopulated
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -89,6 +92,7 @@ const selectErrors = createSelector(
|
|||||||
(state) => state.indexerStatus.error,
|
(state) => state.indexerStatus.error,
|
||||||
(state) => state.settings.indexerCategories.error,
|
(state) => state.settings.indexerCategories.error,
|
||||||
(state) => state.system.status.error,
|
(state) => state.system.status.error,
|
||||||
|
(state) => state.app.translations.error,
|
||||||
(
|
(
|
||||||
customFiltersError,
|
customFiltersError,
|
||||||
tagsError,
|
tagsError,
|
||||||
@@ -98,7 +102,8 @@ const selectErrors = createSelector(
|
|||||||
indexersError,
|
indexersError,
|
||||||
indexerStatusError,
|
indexerStatusError,
|
||||||
indexerCategoriesError,
|
indexerCategoriesError,
|
||||||
systemStatusError
|
systemStatusError,
|
||||||
|
translationsError
|
||||||
) => {
|
) => {
|
||||||
const hasError = !!(
|
const hasError = !!(
|
||||||
customFiltersError ||
|
customFiltersError ||
|
||||||
@@ -109,7 +114,8 @@ const selectErrors = createSelector(
|
|||||||
indexersError ||
|
indexersError ||
|
||||||
indexerStatusError ||
|
indexerStatusError ||
|
||||||
indexerCategoriesError ||
|
indexerCategoriesError ||
|
||||||
systemStatusError
|
systemStatusError ||
|
||||||
|
translationsError
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -122,7 +128,8 @@ const selectErrors = createSelector(
|
|||||||
indexersError,
|
indexersError,
|
||||||
indexerStatusError,
|
indexerStatusError,
|
||||||
indexerCategoriesError,
|
indexerCategoriesError,
|
||||||
systemStatusError
|
systemStatusError,
|
||||||
|
translationsError
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -184,6 +191,9 @@ function createMapDispatchToProps(dispatch, props) {
|
|||||||
dispatchFetchStatus() {
|
dispatchFetchStatus() {
|
||||||
dispatch(fetchStatus());
|
dispatch(fetchStatus());
|
||||||
},
|
},
|
||||||
|
dispatchFetchTranslations() {
|
||||||
|
dispatch(fetchTranslations());
|
||||||
|
},
|
||||||
onResize(dimensions) {
|
onResize(dimensions) {
|
||||||
dispatch(saveDimensions(dimensions));
|
dispatch(saveDimensions(dimensions));
|
||||||
},
|
},
|
||||||
@@ -217,6 +227,7 @@ class PageConnector extends Component {
|
|||||||
this.props.dispatchFetchUISettings();
|
this.props.dispatchFetchUISettings();
|
||||||
this.props.dispatchFetchGeneralSettings();
|
this.props.dispatchFetchGeneralSettings();
|
||||||
this.props.dispatchFetchStatus();
|
this.props.dispatchFetchStatus();
|
||||||
|
this.props.dispatchFetchTranslations();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +243,6 @@ class PageConnector extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
hasTranslationsError,
|
|
||||||
isPopulated,
|
isPopulated,
|
||||||
hasError,
|
hasError,
|
||||||
dispatchFetchTags,
|
dispatchFetchTags,
|
||||||
@@ -243,15 +253,15 @@ class PageConnector extends Component {
|
|||||||
dispatchFetchUISettings,
|
dispatchFetchUISettings,
|
||||||
dispatchFetchGeneralSettings,
|
dispatchFetchGeneralSettings,
|
||||||
dispatchFetchStatus,
|
dispatchFetchStatus,
|
||||||
|
dispatchFetchTranslations,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (hasTranslationsError || hasError || !this.state.isLocalStorageSupported) {
|
if (hasError || !this.state.isLocalStorageSupported) {
|
||||||
return (
|
return (
|
||||||
<ErrorPage
|
<ErrorPage
|
||||||
{...this.state}
|
{...this.state}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
hasTranslationsError={hasTranslationsError}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -272,7 +282,6 @@ class PageConnector extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PageConnector.propTypes = {
|
PageConnector.propTypes = {
|
||||||
hasTranslationsError: PropTypes.bool.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
hasError: PropTypes.bool.isRequired,
|
hasError: PropTypes.bool.isRequired,
|
||||||
isSidebarVisible: PropTypes.bool.isRequired,
|
isSidebarVisible: PropTypes.bool.isRequired,
|
||||||
@@ -285,6 +294,7 @@ PageConnector.propTypes = {
|
|||||||
dispatchFetchUISettings: PropTypes.func.isRequired,
|
dispatchFetchUISettings: PropTypes.func.isRequired,
|
||||||
dispatchFetchGeneralSettings: PropTypes.func.isRequired,
|
dispatchFetchGeneralSettings: PropTypes.func.isRequired,
|
||||||
dispatchFetchStatus: PropTypes.func.isRequired,
|
dispatchFetchStatus: PropTypes.func.isRequired,
|
||||||
|
dispatchFetchTranslations: PropTypes.func.isRequired,
|
||||||
onSidebarVisibleChange: PropTypes.func.isRequired
|
onSidebarVisibleChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@ import { createThunk, handleThunks } from 'Store/thunks';
|
|||||||
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
import createAjaxRequest from 'Utilities/createAjaxRequest';
|
||||||
import getSectionState from 'Utilities/State/getSectionState';
|
import getSectionState from 'Utilities/State/getSectionState';
|
||||||
import updateSectionState from 'Utilities/State/updateSectionState';
|
import updateSectionState from 'Utilities/State/updateSectionState';
|
||||||
|
import { fetchTranslations as fetchAppTranslations } from 'Utilities/String/translate';
|
||||||
import createHandleActions from './Creators/createHandleActions';
|
import createHandleActions from './Creators/createHandleActions';
|
||||||
|
|
||||||
function getDimensions(width, height) {
|
function getDimensions(width, height) {
|
||||||
@@ -41,7 +42,12 @@ export const defaultState = {
|
|||||||
isReconnecting: false,
|
isReconnecting: false,
|
||||||
isDisconnected: false,
|
isDisconnected: false,
|
||||||
isRestarting: false,
|
isRestarting: false,
|
||||||
isSidebarVisible: !getDimensions(window.innerWidth, window.innerHeight).isSmallScreen
|
isSidebarVisible: !getDimensions(window.innerWidth, window.innerHeight).isSmallScreen,
|
||||||
|
translations: {
|
||||||
|
isFetching: true,
|
||||||
|
isPopulated: false,
|
||||||
|
error: null
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -53,6 +59,7 @@ export const SAVE_DIMENSIONS = 'app/saveDimensions';
|
|||||||
export const SET_VERSION = 'app/setVersion';
|
export const SET_VERSION = 'app/setVersion';
|
||||||
export const SET_APP_VALUE = 'app/setAppValue';
|
export const SET_APP_VALUE = 'app/setAppValue';
|
||||||
export const SET_IS_SIDEBAR_VISIBLE = 'app/setIsSidebarVisible';
|
export const SET_IS_SIDEBAR_VISIBLE = 'app/setIsSidebarVisible';
|
||||||
|
export const FETCH_TRANSLATIONS = 'app/fetchTranslations';
|
||||||
|
|
||||||
export const PING_SERVER = 'app/pingServer';
|
export const PING_SERVER = 'app/pingServer';
|
||||||
|
|
||||||
@@ -66,6 +73,7 @@ export const setAppValue = createAction(SET_APP_VALUE);
|
|||||||
export const showMessage = createAction(SHOW_MESSAGE);
|
export const showMessage = createAction(SHOW_MESSAGE);
|
||||||
export const hideMessage = createAction(HIDE_MESSAGE);
|
export const hideMessage = createAction(HIDE_MESSAGE);
|
||||||
export const pingServer = createThunk(PING_SERVER);
|
export const pingServer = createThunk(PING_SERVER);
|
||||||
|
export const fetchTranslations = createThunk(FETCH_TRANSLATIONS);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Helpers
|
// Helpers
|
||||||
@@ -127,6 +135,17 @@ function pingServerAfterTimeout(getState, dispatch) {
|
|||||||
export const actionHandlers = handleThunks({
|
export const actionHandlers = handleThunks({
|
||||||
[PING_SERVER]: function(getState, payload, dispatch) {
|
[PING_SERVER]: function(getState, payload, dispatch) {
|
||||||
pingServerAfterTimeout(getState, dispatch);
|
pingServerAfterTimeout(getState, dispatch);
|
||||||
|
},
|
||||||
|
[FETCH_TRANSLATIONS]: async function(getState, payload, dispatch) {
|
||||||
|
const isFetchingComplete = await fetchAppTranslations();
|
||||||
|
|
||||||
|
dispatch(setAppValue({
|
||||||
|
translations: {
|
||||||
|
isFetching: false,
|
||||||
|
isPopulated: isFetchingComplete,
|
||||||
|
error: isFetchingComplete ? null : 'Failed to load translations from API'
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -2,20 +2,14 @@ import { createBrowserHistory } from 'history';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import createAppStore from 'Store/createAppStore';
|
import createAppStore from 'Store/createAppStore';
|
||||||
import { fetchTranslations } from 'Utilities/String/translate';
|
|
||||||
import App from './App/App';
|
import App from './App/App';
|
||||||
|
|
||||||
export async function bootstrap() {
|
export async function bootstrap() {
|
||||||
const history = createBrowserHistory();
|
const history = createBrowserHistory();
|
||||||
const store = createAppStore(history);
|
const store = createAppStore(history);
|
||||||
const hasTranslationsError = !(await fetchTranslations());
|
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<App
|
<App store={store} history={history} />,
|
||||||
store={store}
|
|
||||||
history={history}
|
|
||||||
hasTranslationsError={hasTranslationsError}
|
|
||||||
/>,
|
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user