Merge pull request #65 from jfurrow/feature/allow-language-changes

Allow language changes within SettingsModal
This commit is contained in:
John Furrow
2016-07-09 00:19:10 -07:00
committed by GitHub
6 changed files with 205 additions and 18 deletions
+30 -3
View File
@@ -1,5 +1,7 @@
$form--label--foreground: desaturate(lighten(#5c7087, 3%), 3%);
$form--element--border-radius: 4px;
$textbox--background: #242b36;
$textbox--foreground: #5e728c;
$textbox--placeholder: #424d5e;
@@ -76,7 +78,7 @@ $interacative-list--item--icon--fill--hover: rgba(lighten($interacative-list--it
.textbox {
background: $textbox--background;
border-radius: 4px;
border-radius: $form--element--border-radius;
border: 1px solid $textbox--border;
color: $textbox--foreground;
display: block;
@@ -119,7 +121,7 @@ $interacative-list--item--icon--fill--hover: rgba(lighten($interacative-list--it
.button {
background: transparent;
border: none;
border-radius: 4px;
border-radius: $form--element--border-radius;
cursor: pointer;
font-weight: 500;
padding: 8px 22px;
@@ -253,7 +255,7 @@ $interacative-list--item--icon--fill--hover: rgba(lighten($interacative-list--it
.interactive-list {
@extend .textbox;
@extend .textbox.is-fulfilled;
border-radius: 4px;
border-radius: $form--element--border-radius;
color: $interacative-list--item--foreground;
padding: $spacing-unit * 1/2;
@@ -384,6 +386,31 @@ $interacative-list--item--icon--fill--hover: rgba(lighten($interacative-list--it
justify-content: center;
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 {
+56 -14
View File
@@ -5,32 +5,74 @@ import ReactDOM from 'react-dom';
import * as i18n from './i18n';
import Application from './components/Layout/Application';
import EventTypes from './constants/EventTypes';
import Login from './views/Login';
import Register from './views/Register';
import SettingsStore from './stores/SettingsStore';
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 {
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() {
let {locale} = this.state;
return (
<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>
<IntlProvider locale={locale} messages={i18n[locale]}>
{appRoutes}
</IntlProvider>
);
}
}
let locale = SettingsStore.getFloodSettings('language');
ReactDOM.render(
<IntlProvider locale={locale} messages={i18n[locale]}>
<FloodApp />
</IntlProvider>,
<FloodApp />,
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 ResourcesTab from './ResourcesTab';
import SettingsStore from '../../../stores/SettingsStore';
import UITab from './UITab';
const METHODS_TO_BIND = [
'handleClientSettingsChange',
@@ -213,6 +214,16 @@ class SettingsModal extends React.Component {
id: 'settings.tabs.authentication',
defaultMessage: 'Authentication'
})
},
ui: {
content: UITab,
label: this.props.intl.formatMessage({
id: 'settings.tabs.userinterface',
defaultMessage: 'User Interface'
}),
props: {
onSettingsChange: this.handleFloodSettingsChange
}
}
};
+12
View File
@@ -0,0 +1,12 @@
const Languages = {
en: {
defaultMessage: 'English',
id: 'locale.language.en'
},
nl: {
defaultMessage: 'Nederlands',
id: 'locale.language.nl'
}
};
export default Languages;
+3 -1
View File
@@ -93,7 +93,9 @@ class SettingsStoreClass extends BaseStore {
this.fetchStatus.floodSettingsFetched = true;
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);