mirror of
https://github.com/zoriya/flood.git
synced 2026-06-08 04:41:03 +00:00
Merge pull request #65 from jfurrow/feature/allow-language-changes
Allow language changes within SettingsModal
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
$form--label--foreground: desaturate(lighten(#5c7087, 3%), 3%);
|
$form--label--foreground: desaturate(lighten(#5c7087, 3%), 3%);
|
||||||
|
|
||||||
|
$form--element--border-radius: 4px;
|
||||||
|
|
||||||
$textbox--background: #242b36;
|
$textbox--background: #242b36;
|
||||||
$textbox--foreground: #5e728c;
|
$textbox--foreground: #5e728c;
|
||||||
$textbox--placeholder: #424d5e;
|
$textbox--placeholder: #424d5e;
|
||||||
@@ -76,7 +78,7 @@ $interacative-list--item--icon--fill--hover: rgba(lighten($interacative-list--it
|
|||||||
|
|
||||||
.textbox {
|
.textbox {
|
||||||
background: $textbox--background;
|
background: $textbox--background;
|
||||||
border-radius: 4px;
|
border-radius: $form--element--border-radius;
|
||||||
border: 1px solid $textbox--border;
|
border: 1px solid $textbox--border;
|
||||||
color: $textbox--foreground;
|
color: $textbox--foreground;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -119,7 +121,7 @@ $interacative-list--item--icon--fill--hover: rgba(lighten($interacative-list--it
|
|||||||
.button {
|
.button {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 4px;
|
border-radius: $form--element--border-radius;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding: 8px 22px;
|
padding: 8px 22px;
|
||||||
@@ -253,7 +255,7 @@ $interacative-list--item--icon--fill--hover: rgba(lighten($interacative-list--it
|
|||||||
.interactive-list {
|
.interactive-list {
|
||||||
@extend .textbox;
|
@extend .textbox;
|
||||||
@extend .textbox.is-fulfilled;
|
@extend .textbox.is-fulfilled;
|
||||||
border-radius: 4px;
|
border-radius: $form--element--border-radius;
|
||||||
color: $interacative-list--item--foreground;
|
color: $interacative-list--item--foreground;
|
||||||
padding: $spacing-unit * 1/2;
|
padding: $spacing-unit * 1/2;
|
||||||
|
|
||||||
@@ -384,6 +386,31 @@ $interacative-list--item--icon--fill--hover: rgba(lighten($interacative-list--it
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-top: $spacing-unit * 3/5;
|
padding-top: $spacing-unit * 3/5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > .dropdown {
|
||||||
|
|
||||||
|
& > .dropdown {
|
||||||
|
|
||||||
|
&__trigger {
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
@extend .button--deemphasize;
|
||||||
|
border-radius: $form--element--border-radius;
|
||||||
|
transition: background 0.25s, color 0.25s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
padding-top: 7px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__actions {
|
&__actions {
|
||||||
|
|||||||
+56
-14
@@ -5,32 +5,74 @@ import ReactDOM from 'react-dom';
|
|||||||
|
|
||||||
import * as i18n from './i18n';
|
import * as i18n from './i18n';
|
||||||
import Application from './components/Layout/Application';
|
import Application from './components/Layout/Application';
|
||||||
|
import EventTypes from './constants/EventTypes';
|
||||||
import Login from './views/Login';
|
import Login from './views/Login';
|
||||||
import Register from './views/Register';
|
import Register from './views/Register';
|
||||||
import SettingsStore from './stores/SettingsStore';
|
import SettingsStore from './stores/SettingsStore';
|
||||||
import TorrentList from './views/TorrentList';
|
import TorrentList from './views/TorrentList';
|
||||||
|
import UIStore from './stores/UIStore';
|
||||||
|
|
||||||
|
let appRoutes = (
|
||||||
|
<Router history={browserHistory}>
|
||||||
|
<Route path="/" component={Application}>
|
||||||
|
<IndexRoute component={Login} />
|
||||||
|
<Route path="login" component={Login} />
|
||||||
|
<Route path="register" component={Register} />
|
||||||
|
<Route path="list" component={TorrentList} />
|
||||||
|
<Route path="*" component={Login} />
|
||||||
|
</Route>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
|
||||||
|
const METHODS_TO_BIND = ['handleSettingsChange'];
|
||||||
|
|
||||||
class FloodApp extends React.Component {
|
class FloodApp extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
locale: SettingsStore.getFloodSettings('language')
|
||||||
|
};
|
||||||
|
|
||||||
|
METHODS_TO_BIND.forEach((method) => {
|
||||||
|
this[method] = this[method].bind(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
UIStore.registerDependency('flood-settings');
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
SettingsStore.listen(EventTypes.SETTINGS_CHANGE,
|
||||||
|
this.handleSettingsChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
SettingsStore.unlisten(EventTypes.SETTINGS_CHANGE,
|
||||||
|
this.handleSettingsChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSettingsChange() {
|
||||||
|
if (SettingsStore.getFloodSettings('language') !== this.state.language) {
|
||||||
|
this.setState({locale: SettingsStore.getFloodSettings('language')});
|
||||||
|
}
|
||||||
|
|
||||||
|
UIStore.satisfyDependency('flood-settings');
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let {locale} = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router history={browserHistory}>
|
<IntlProvider locale={locale} messages={i18n[locale]}>
|
||||||
<Route path="/" component={Application}>
|
{appRoutes}
|
||||||
<IndexRoute component={Login} />
|
</IntlProvider>
|
||||||
<Route path="login" component={Login} />
|
|
||||||
<Route path="register" component={Register} />
|
|
||||||
<Route path="list" component={TorrentList} />
|
|
||||||
<Route path="*" component={Login} />
|
|
||||||
</Route>
|
|
||||||
</Router>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let locale = SettingsStore.getFloodSettings('language');
|
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<IntlProvider locale={locale} messages={i18n[locale]}>
|
<FloodApp />,
|
||||||
<FloodApp />
|
|
||||||
</IntlProvider>,
|
|
||||||
document.getElementById('app')
|
document.getElementById('app')
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import {formatMessage, FormattedMessage, injectIntl} from 'react-intl';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import AuthStore from '../../../stores/AuthStore';
|
||||||
|
import Checkbox from '../../General/FormElements/Checkbox';
|
||||||
|
import Close from '../../Icons/Close';
|
||||||
|
import Dropdown from '../../General/FormElements/Dropdown';
|
||||||
|
import EventTypes from '../../../constants/EventTypes';
|
||||||
|
import Languages from '../../../constants/Languages';
|
||||||
|
import SettingsStore from '../../../stores/SettingsStore';
|
||||||
|
import SettingsTab from './SettingsTab';
|
||||||
|
|
||||||
|
const METHODS_TO_BIND = [
|
||||||
|
'handleItemSelect'
|
||||||
|
];
|
||||||
|
|
||||||
|
class UITab extends SettingsTab {
|
||||||
|
constructor() {
|
||||||
|
super(...arguments);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
selectedLanguage: SettingsStore.getFloodSettings('language')
|
||||||
|
};
|
||||||
|
|
||||||
|
METHODS_TO_BIND.forEach((method) => {
|
||||||
|
this[method] = this[method].bind(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getDropdownHeader() {
|
||||||
|
return (
|
||||||
|
<a className="dropdown__button">
|
||||||
|
<span className="dropdown__value">
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage={Languages[this.state.selectedLanguage].defaultMessage}
|
||||||
|
id={Languages[this.state.selectedLanguage].id} />
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDropdownMenu() {
|
||||||
|
let items = Object.keys(Languages).map((language) => {
|
||||||
|
return {
|
||||||
|
displayName: this.props.intl.formatMessage(
|
||||||
|
Languages[language]
|
||||||
|
),
|
||||||
|
selected: this.state.selectedLanguage === language,
|
||||||
|
language
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dropdown expects an array of arrays.
|
||||||
|
return [items];
|
||||||
|
}
|
||||||
|
|
||||||
|
handleItemSelect(item) {
|
||||||
|
let {language} = item;
|
||||||
|
|
||||||
|
this.setState({selectedLanguage: language});
|
||||||
|
this.props.onSettingsChange({language});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="form">
|
||||||
|
<div className="form__section">
|
||||||
|
<div className="form__section__heading">
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Locale"
|
||||||
|
id="settings.ui.locale" />
|
||||||
|
</div>
|
||||||
|
<div className="form__row">
|
||||||
|
<div className="form__column form__column--auto">
|
||||||
|
<label className="form__label">
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Language"
|
||||||
|
id="settings.ui.language" />
|
||||||
|
</label>
|
||||||
|
<Dropdown
|
||||||
|
handleItemSelect={this.handleItemSelect}
|
||||||
|
header={this.getDropdownHeader()}
|
||||||
|
menuItems={this.getDropdownMenu()} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectIntl(UITab);
|
||||||
@@ -11,6 +11,7 @@ import LoadingIndicatorDots from '../../Icons/LoadingIndicatorDots';
|
|||||||
import Modal from '../Modal';
|
import Modal from '../Modal';
|
||||||
import ResourcesTab from './ResourcesTab';
|
import ResourcesTab from './ResourcesTab';
|
||||||
import SettingsStore from '../../../stores/SettingsStore';
|
import SettingsStore from '../../../stores/SettingsStore';
|
||||||
|
import UITab from './UITab';
|
||||||
|
|
||||||
const METHODS_TO_BIND = [
|
const METHODS_TO_BIND = [
|
||||||
'handleClientSettingsChange',
|
'handleClientSettingsChange',
|
||||||
@@ -213,6 +214,16 @@ class SettingsModal extends React.Component {
|
|||||||
id: 'settings.tabs.authentication',
|
id: 'settings.tabs.authentication',
|
||||||
defaultMessage: 'Authentication'
|
defaultMessage: 'Authentication'
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
content: UITab,
|
||||||
|
label: this.props.intl.formatMessage({
|
||||||
|
id: 'settings.tabs.userinterface',
|
||||||
|
defaultMessage: 'User Interface'
|
||||||
|
}),
|
||||||
|
props: {
|
||||||
|
onSettingsChange: this.handleFloodSettingsChange
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
const Languages = {
|
||||||
|
en: {
|
||||||
|
defaultMessage: 'English',
|
||||||
|
id: 'locale.language.en'
|
||||||
|
},
|
||||||
|
nl: {
|
||||||
|
defaultMessage: 'Nederlands',
|
||||||
|
id: 'locale.language.nl'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Languages;
|
||||||
@@ -93,7 +93,9 @@ class SettingsStoreClass extends BaseStore {
|
|||||||
this.fetchStatus.floodSettingsFetched = true;
|
this.fetchStatus.floodSettingsFetched = true;
|
||||||
|
|
||||||
Object.keys(settings).forEach((property) => {
|
Object.keys(settings).forEach((property) => {
|
||||||
this.floodSettings[property] = settings[property];
|
if (settings[property] != null) {
|
||||||
|
this.floodSettings[property] = settings[property];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.emit(EventTypes.SETTINGS_FETCH_REQUEST_SUCCESS);
|
this.emit(EventTypes.SETTINGS_FETCH_REQUEST_SUCCESS);
|
||||||
|
|||||||
Reference in New Issue
Block a user