mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-11-26 16:52:06 +05:30
#379: split languageSwitcher to smaller components
This commit is contained in:
parent
b19eeb9e94
commit
7dbd569d45
@ -109,7 +109,7 @@ function sortByKeys(object) {
|
||||
* @return {string}
|
||||
*/
|
||||
function trimValueInBrackets(value) {
|
||||
return value.match(/^([^\(]+)/)[0].trim();
|
||||
return value.match(/^([^(]+)/)[0].trim();
|
||||
}
|
||||
|
||||
async function pullReadyLanguages() {
|
||||
@ -141,6 +141,7 @@ async function pull() {
|
||||
const mapFileContent = {};
|
||||
langs.map((elem) => {
|
||||
mapFileContent[elem.locale] = {
|
||||
code: elem.locale,
|
||||
name: ORIGINAL_NAMES_MAP[elem.locale] || trimValueInBrackets(elem.local_name),
|
||||
englishName: ENGLISH_NAMES_MAP[elem.locale] || trimValueInBrackets(elem.english_name),
|
||||
progress: parseFloat(elem.translation_progress),
|
||||
|
@ -1,8 +1,11 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import ContactForm from 'components/contact/ContactForm';
|
||||
import LanguageSwitcher from 'components/languageSwitcher';
|
||||
import { create as createPopup } from 'components/ui/popup/actions';
|
||||
|
||||
import styles from './footerMenu.scss';
|
||||
import messages from './footerMenu.intl.json';
|
||||
@ -47,11 +50,6 @@ class FooterMenu extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import ContactForm from 'components/contact/ContactForm';
|
||||
import LanguageSwitcher from 'components/languageSwitcher/LanguageSwitcher';
|
||||
import { create as createPopup } from 'components/ui/popup/actions';
|
||||
|
||||
// mark this component, as not pure, because it is stateless,
|
||||
// but should be re-rendered, if current lang was changed
|
||||
export default connect(null, {
|
||||
|
148
src/components/languageSwitcher/LanguageList.js
Normal file
148
src/components/languageSwitcher/LanguageList.js
Normal file
@ -0,0 +1,148 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { TransitionMotion, spring, presets } from 'react-motion';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import LocaleItem from './LocaleItem';
|
||||
import messages from './languageSwitcher.intl.json';
|
||||
import styles from './languageSwitcher.scss';
|
||||
|
||||
import thatFuckingPumpkin from './images/that_fucking_pumpkin.svg';
|
||||
import mayTheForceBeWithYou from './images/may_the_force_be_with_you.svg';
|
||||
import biteMyShinyMetalAss from './images/bite_my_shiny_metal_ass.svg';
|
||||
import iTookAnArrowInMyKnee from './images/i_took_an_arrow_in_my_knee.svg';
|
||||
|
||||
import type { LocalesMap } from './LanguageSwitcher';
|
||||
|
||||
const itemHeight = 51;
|
||||
|
||||
export default class LanguageList extends React.Component<{
|
||||
userLang: string,
|
||||
langs: LocalesMap,
|
||||
onChangeLang: (lang: string) => void,
|
||||
}> {
|
||||
emptyListStateInner: ?HTMLDivElement;
|
||||
|
||||
render() {
|
||||
const { userLang, langs } = this.props;
|
||||
const isListEmpty = Object.keys(langs).length === 0;
|
||||
const firstLocale = Object.keys(langs)[0] || null;
|
||||
const emptyCaption = this.getEmptyCaption();
|
||||
|
||||
return (
|
||||
<TransitionMotion
|
||||
defaultStyles={this.getItemsWithDefaultStyles()}
|
||||
styles={this.getItemsWithStyles()}
|
||||
willLeave={this.willLeave}
|
||||
willEnter={this.willEnter}
|
||||
>
|
||||
{(items) => (
|
||||
<div className={styles.languagesList}>
|
||||
<div
|
||||
className={classNames(styles.emptyLanguagesListWrapper, {
|
||||
[styles.emptyLanguagesListVisible]: isListEmpty,
|
||||
})}
|
||||
style={{
|
||||
height: isListEmpty && this.emptyListStateInner ? this.emptyListStateInner.clientHeight : 0,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={(elem: ?HTMLDivElement) => this.emptyListStateInner = elem}
|
||||
className={styles.emptyLanguagesList}
|
||||
>
|
||||
<img
|
||||
src={emptyCaption.src}
|
||||
alt={emptyCaption.caption}
|
||||
className={styles.emptyLanguagesListCaption}
|
||||
/>
|
||||
<div className={styles.emptyLanguagesListSubtitle}>
|
||||
<Message {...messages.weDoNotSupportThisLang} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{items.map(({key: locale, data: definition, style}) => (
|
||||
<div
|
||||
key={locale}
|
||||
style={style}
|
||||
className={classNames(styles.languageItem, {
|
||||
[styles.activeLanguageItem]: locale === userLang,
|
||||
[styles.firstLanguageItem]: locale === firstLocale,
|
||||
})}
|
||||
onClick={this.onChangeLang(locale)}
|
||||
>
|
||||
<LocaleItem locale={definition} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</TransitionMotion>
|
||||
);
|
||||
}
|
||||
|
||||
getEmptyCaption() {
|
||||
const emptyCaptions = [
|
||||
{
|
||||
// Homestuck
|
||||
src: thatFuckingPumpkin,
|
||||
caption: 'That fucking pumpkin',
|
||||
},
|
||||
{
|
||||
// Star Wars
|
||||
src: mayTheForceBeWithYou,
|
||||
caption: 'May The Force Be With You',
|
||||
},
|
||||
{
|
||||
// Futurama
|
||||
src: biteMyShinyMetalAss,
|
||||
caption: 'Bite my shiny metal ass',
|
||||
},
|
||||
{
|
||||
// The Elder Scrolls V: Skyrim
|
||||
src: iTookAnArrowInMyKnee,
|
||||
caption: 'I took an arrow in my knee',
|
||||
},
|
||||
];
|
||||
|
||||
return emptyCaptions[Math.floor(Math.random() * emptyCaptions.length)];
|
||||
}
|
||||
|
||||
onChangeLang(lang: string) {
|
||||
return (event: SyntheticMouseEvent<HTMLElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
this.props.onChangeLang(lang);
|
||||
};
|
||||
}
|
||||
|
||||
getItemsWithDefaultStyles = () => this.getItemsWithStyles({ useSpring: false });
|
||||
|
||||
getItemsWithStyles = ({ useSpring }: { useSpring?: bool } = { useSpring: true }) =>
|
||||
Object.keys({...this.props.langs})
|
||||
.reduce((previous, key) => [
|
||||
...previous,
|
||||
{
|
||||
key,
|
||||
data: this.props.langs[key],
|
||||
style: {
|
||||
height: useSpring ? spring(itemHeight, presets.gentle) : itemHeight,
|
||||
opacity: useSpring ? spring(1, presets.gentle) : 1,
|
||||
},
|
||||
},
|
||||
], []);
|
||||
|
||||
willEnter() {
|
||||
return {
|
||||
height: 0,
|
||||
opacity: 1,
|
||||
};
|
||||
}
|
||||
|
||||
willLeave() {
|
||||
return {
|
||||
height: spring(0),
|
||||
opacity: spring(0),
|
||||
};
|
||||
}
|
||||
}
|
@ -1,46 +1,41 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { TransitionMotion, spring, presets } from 'react-motion';
|
||||
import { FormattedMessage as Message, intlShape } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { localeFlags } from 'components/i18n';
|
||||
import LANGS from 'i18n/index.json';
|
||||
|
||||
import formStyles from 'components/ui/form/form.scss';
|
||||
import popupStyles from 'components/ui/popup/popup.scss';
|
||||
import icons from 'components/ui/icons.scss';
|
||||
import styles from './languageSwitcher.scss';
|
||||
import messages from './languageSwitcher.intl.json';
|
||||
|
||||
import thatFuckingPumpkin from './images/that_fucking_pumpkin.svg';
|
||||
import mayTheForceBeWithYou from './images/may_the_force_be_with_you.svg';
|
||||
import biteMyShinyMetalAss from './images/bite_my_shiny_metal_ass.svg';
|
||||
import iTookAnArrowInMyKnee from './images/i_took_an_arrow_in_my_knee.svg';
|
||||
import LanguageList from './LanguageList';
|
||||
|
||||
const improveTranslationUrl = 'http://ely.by/erickskrauch/posts/174943';
|
||||
const itemHeight = 51;
|
||||
|
||||
class LanguageSwitcher extends Component {
|
||||
static displayName = 'LanguageSwitcher';
|
||||
export type LocaleData = {
|
||||
code: string,
|
||||
name: string,
|
||||
englishName: string,
|
||||
progress: number,
|
||||
isReleased: bool,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
onClose: PropTypes.func,
|
||||
userLang: PropTypes.string,
|
||||
changeLang: PropTypes.func,
|
||||
langs: PropTypes.objectOf(PropTypes.shape({
|
||||
name: PropTypes.string.isRequired,
|
||||
englishName: PropTypes.string.isRequired,
|
||||
progress: PropTypes.number.isRequired,
|
||||
isReleased: PropTypes.bool.isRequired,
|
||||
})).isRequired,
|
||||
emptyCaptions: PropTypes.arrayOf(PropTypes.shape({
|
||||
src: PropTypes.string.isRequired,
|
||||
caption: PropTypes.string.isRequired,
|
||||
})).isRequired,
|
||||
};
|
||||
export type LocalesMap = {[code: string]: LocaleData};
|
||||
|
||||
class LanguageSwitcher extends Component<{
|
||||
onClose: Function,
|
||||
userLang: string,
|
||||
changeLang: (lang: string) => void,
|
||||
langs: LocalesMap,
|
||||
emptyCaptions: Array<{
|
||||
src: string,
|
||||
caption: string,
|
||||
}>,
|
||||
}, {
|
||||
filter: string,
|
||||
filteredLangs: LocalesMap,
|
||||
}> {
|
||||
static contextTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
@ -53,35 +48,11 @@ class LanguageSwitcher extends Component {
|
||||
static defaultProps = {
|
||||
langs: LANGS,
|
||||
onClose() {},
|
||||
emptyCaptions: [
|
||||
{
|
||||
// Homestuck
|
||||
src: thatFuckingPumpkin,
|
||||
caption: 'That fucking pumpkin',
|
||||
},
|
||||
{
|
||||
// Star Wars
|
||||
src: mayTheForceBeWithYou,
|
||||
caption: 'May The Force Be With You',
|
||||
},
|
||||
{
|
||||
// Futurama
|
||||
src: biteMyShinyMetalAss,
|
||||
caption: 'Bite my shiny metal ass',
|
||||
},
|
||||
{
|
||||
// The Elder Scrolls V: Skyrim
|
||||
src: iTookAnArrowInMyKnee,
|
||||
caption: 'I took an arrow in my knee',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
render() {
|
||||
const {userLang, onClose, emptyCaptions} = this.props;
|
||||
const isListEmpty = Object.keys(this.state.filteredLangs).length === 0;
|
||||
const firstLocale = Object.keys(this.state.filteredLangs)[0] || null;
|
||||
const emptyCaption = emptyCaptions[Math.floor(Math.random() * emptyCaptions.length)];
|
||||
const { userLang, onClose } = this.props;
|
||||
const { filteredLangs } = this.state;
|
||||
|
||||
return (
|
||||
<div className={styles.languageSwitcher}>
|
||||
@ -108,53 +79,11 @@ class LanguageSwitcher extends Component {
|
||||
<span className={styles.searchIcon} />
|
||||
</div>
|
||||
|
||||
<TransitionMotion
|
||||
defaultStyles={this.getItemsWithDefaultStyles()}
|
||||
styles={this.getItemsWithStyles()}
|
||||
willLeave={this.willLeave}
|
||||
willEnter={this.willEnter}
|
||||
>
|
||||
{(items) => (
|
||||
<div className={styles.languagesList}>
|
||||
<div
|
||||
className={classNames(styles.emptyLanguagesListWrapper, {
|
||||
[styles.emptyLanguagesListVisible]: isListEmpty,
|
||||
})}
|
||||
style={{
|
||||
height: isListEmpty ? this.emptyListStateInner.clientHeight : 0,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={(elem) => this.emptyListStateInner = elem}
|
||||
className={styles.emptyLanguagesList}
|
||||
>
|
||||
<img
|
||||
src={emptyCaption.src}
|
||||
alt={emptyCaption.caption}
|
||||
className={styles.emptyLanguagesListCaption}
|
||||
/>
|
||||
<div className={styles.emptyLanguagesListSubtitle}>
|
||||
<Message {...messages.weDoNotSupportThisLang} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{items.map(({key: locale, data: definition, style}) => (
|
||||
<li
|
||||
key={locale}
|
||||
style={style}
|
||||
className={classNames(styles.languageItem, {
|
||||
[styles.activeLanguageItem]: locale === userLang,
|
||||
[styles.firstLanguageItem]: locale === firstLocale,
|
||||
})}
|
||||
onClick={this.onChangeLang(locale)}
|
||||
>
|
||||
{this.renderLanguageItem(locale, definition)}
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</TransitionMotion>
|
||||
<LanguageList
|
||||
userLang={userLang}
|
||||
langs={filteredLangs}
|
||||
onChangeLang={this.onChangeLang}
|
||||
/>
|
||||
|
||||
<div className={styles.improveTranslates}>
|
||||
<div className={styles.improveTranslatesIcon} />
|
||||
@ -179,57 +108,21 @@ class LanguageSwitcher extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderLanguageItem(locale, localeData) {
|
||||
const {name, progress, isReleased} = localeData;
|
||||
let progressLabel;
|
||||
if (progress !== 100) {
|
||||
progressLabel = (
|
||||
<Message {...messages.translationProgress} values={{
|
||||
progress,
|
||||
}} />
|
||||
);
|
||||
} else if (!isReleased) {
|
||||
progressLabel = (
|
||||
<Message {...messages.mayBeInaccurate} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.languageFlex}>
|
||||
<div className={styles.languageIco} style={{
|
||||
backgroundImage: `url('${localeFlags.getIconUrl(locale)}')`,
|
||||
}} />
|
||||
<div className={styles.languageCaptions}>
|
||||
<div className={styles.languageName}>
|
||||
{name}
|
||||
</div>
|
||||
<div className={styles.languageSubName}>
|
||||
{localeData.englishName} {progressLabel ? '| ' : ''} {progressLabel}
|
||||
</div>
|
||||
</div>
|
||||
<span className={styles.languageCircle} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChangeLang(lang) {
|
||||
return (event) => {
|
||||
event.preventDefault();
|
||||
this.changeLang(lang);
|
||||
};
|
||||
}
|
||||
onChangeLang = this.changeLang.bind(this);
|
||||
|
||||
changeLang(lang) {
|
||||
this.props.changeLang(lang);
|
||||
|
||||
setTimeout(this.props.onClose, 300);
|
||||
}
|
||||
|
||||
onFilterUpdate = (event) => {
|
||||
const filter = event.target.value.trim().toLowerCase();
|
||||
onFilterUpdate = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
const filter = event.currentTarget.value.trim().toLowerCase();
|
||||
const { langs } = this.props;
|
||||
|
||||
const result = Object.keys(langs).reduce((previous, key) => {
|
||||
if (langs[key].englishName.toLowerCase().search(filter) === -1
|
||||
&& langs[key].name.toLowerCase().search(filter) === -1
|
||||
&& langs[key].name.toLowerCase().search(filter) === -1
|
||||
) {
|
||||
return previous;
|
||||
}
|
||||
@ -246,12 +139,13 @@ class LanguageSwitcher extends Component {
|
||||
};
|
||||
|
||||
onFilterKeyPress() {
|
||||
return (event) => {
|
||||
return (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
if (event.key !== 'Enter' || this.state.filter === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const locales = Object.keys(this.state.filteredLangs);
|
||||
|
||||
if (locales.length === 0) {
|
||||
return;
|
||||
}
|
||||
@ -259,46 +153,6 @@ class LanguageSwitcher extends Component {
|
||||
this.changeLang(locales[0]);
|
||||
};
|
||||
}
|
||||
|
||||
getItemsWithDefaultStyles = () => Object.keys(this.props.langs).reduce((previous, key) => {
|
||||
return [
|
||||
...previous,
|
||||
{
|
||||
key,
|
||||
data: this.props.langs[key],
|
||||
style: {
|
||||
height: itemHeight,
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
}, {});
|
||||
|
||||
getItemsWithStyles = () => Object.keys({...this.state.filteredLangs}).reduce((previous, key) => [
|
||||
...previous,
|
||||
{
|
||||
key,
|
||||
data: this.props.langs[key],
|
||||
style: {
|
||||
height: spring(itemHeight, presets.gentle),
|
||||
opacity: spring(1, presets.gentle),
|
||||
},
|
||||
},
|
||||
], []);
|
||||
|
||||
willEnter() {
|
||||
return {
|
||||
height: 0,
|
||||
opacity: 1,
|
||||
};
|
||||
}
|
||||
|
||||
willLeave() {
|
||||
return {
|
||||
height: spring(0),
|
||||
opacity: spring(0),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
47
src/components/languageSwitcher/LocaleItem.js
Normal file
47
src/components/languageSwitcher/LocaleItem.js
Normal file
@ -0,0 +1,47 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
|
||||
import { localeFlags } from 'components/i18n';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import messages from './languageSwitcher.intl.json';
|
||||
import styles from './languageSwitcher.scss';
|
||||
import type { LocaleData } from './LanguageSwitcher';
|
||||
|
||||
export default function LocaleItem({
|
||||
locale,
|
||||
}: {
|
||||
locale: LocaleData,
|
||||
}) {
|
||||
const {code, name, englishName, progress, isReleased} = locale;
|
||||
|
||||
let progressLabel;
|
||||
|
||||
if (progress !== 100) {
|
||||
progressLabel = (
|
||||
<Message {...messages.translationProgress} values={{
|
||||
progress,
|
||||
}} />
|
||||
);
|
||||
} else if (!isReleased) {
|
||||
progressLabel = (
|
||||
<Message {...messages.mayBeInaccurate} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.languageFlex}>
|
||||
<div className={styles.languageIco} style={{
|
||||
backgroundImage: `url('${localeFlags.getIconUrl(code)}')`,
|
||||
}} />
|
||||
<div className={styles.languageCaptions}>
|
||||
<div className={styles.languageName}>
|
||||
{name}
|
||||
</div>
|
||||
<div className={styles.languageSubName}>
|
||||
{englishName} {progressLabel ? '| ' : ''} {progressLabel}
|
||||
</div>
|
||||
</div>
|
||||
<span className={styles.languageCircle} />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { create as createPopup } from 'components/ui/popup/actions';
|
||||
import { localeFlags } from 'components/i18n';
|
||||
import LANGS from 'i18n/index.json';
|
||||
import LanguageSwitcher from '../LanguageSwitcher';
|
||||
import styles from './link.scss';
|
||||
|
||||
function LanguageLink({
|
||||
userLang,
|
||||
showLanguageSwitcherPopup,
|
||||
}: {
|
||||
userLang: string,
|
||||
showLanguageSwitcherPopup: Function,
|
||||
}) {
|
||||
return (
|
||||
<span className={styles.languageLink} onClick={showLanguageSwitcherPopup}>
|
||||
<span className={styles.languageIcon} style={{
|
||||
backgroundImage: `url('${localeFlags.getIconUrl(userLang)}')`
|
||||
}} />
|
||||
{LANGS[userLang].name}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect((state) => ({
|
||||
userLang: state.user.lang,
|
||||
}), {
|
||||
showLanguageSwitcherPopup: () => createPopup(LanguageSwitcher),
|
||||
})(LanguageLink);
|
21
src/components/languageSwitcher/changeLanguageLink/link.scss
Normal file
21
src/components/languageSwitcher/changeLanguageLink/link.scss
Normal file
@ -0,0 +1,21 @@
|
||||
.languageLink {
|
||||
color: #666;
|
||||
border-bottom: 1px dotted #666;
|
||||
text-decoration: none;
|
||||
transition: .25s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.languageIcon {
|
||||
$height: 13px;
|
||||
|
||||
position: relative;
|
||||
top: 1px;
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
height: $height;
|
||||
width: $height * 4 / 3;
|
||||
box-shadow: 0 0 1px rgba(#000, .2);
|
||||
|
||||
background-size: cover;
|
||||
}
|
3
src/components/languageSwitcher/index.js
Normal file
3
src/components/languageSwitcher/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
// @flow
|
||||
export { default } from './LanguageSwitcher';
|
||||
export { default as ChangeLanguageLink } from './changeLanguageLink/ChangeLanguageLink';
|
@ -76,6 +76,7 @@ $languageListBorderStyle: 1px solid $languageListBorderColor;
|
||||
transition: background-color .25s;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
line-height: 1;
|
||||
|
||||
&:hover {
|
||||
background-color: $whiteButtonLight;
|
||||
|
@ -1,28 +1,22 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { FormattedMessage as Message, FormattedRelative as Relative } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Helmet from 'react-helmet';
|
||||
|
||||
import { localeFlags } from 'components/i18n';
|
||||
import LANGS from 'i18n/index.json';
|
||||
|
||||
import { userShape } from 'components/user/User';
|
||||
|
||||
import { ChangeLanguageLink } from 'components/languageSwitcher';
|
||||
import ProfileField from './ProfileField';
|
||||
import styles from './profile.scss';
|
||||
import profileForm from './profileForm.scss';
|
||||
import messages from './Profile.intl.json';
|
||||
|
||||
import RulesPage from 'pages/rules/RulesPage';
|
||||
|
||||
class Profile extends Component {
|
||||
static displayName = 'Profile';
|
||||
static propTypes = {
|
||||
user: userShape,
|
||||
createLanguageSwitcherPopup: PropTypes.func.isRequired,
|
||||
};
|
||||
import type { User } from 'components/user';
|
||||
|
||||
class Profile extends Component<{
|
||||
user: User
|
||||
}> {
|
||||
UUID: ?HTMLDivElement;
|
||||
|
||||
render() {
|
||||
const { user } = this.props;
|
||||
@ -30,7 +24,7 @@ class Profile extends Component {
|
||||
return (
|
||||
<div>
|
||||
<Message {...messages.accountPreferencesTitle}>
|
||||
{(pageTitle) => (
|
||||
{(pageTitle: string) => (
|
||||
<h2 className={styles.indexTitle}>
|
||||
<Helmet title={pageTitle} />
|
||||
{pageTitle}
|
||||
@ -90,14 +84,7 @@ class Profile extends Component {
|
||||
|
||||
<ProfileField
|
||||
label={<Message {...messages.siteLanguage} />}
|
||||
value={
|
||||
<span className={styles.language} onClick={this.onLanguageSwitcher.bind(this)}>
|
||||
<span className={styles.languageIcon} style={{
|
||||
backgroundImage: `url('${localeFlags.getIconUrl(user.lang)}')`
|
||||
}} />
|
||||
{LANGS[user.lang].name}
|
||||
</span>
|
||||
}
|
||||
value={<ChangeLanguageLink />}
|
||||
/>
|
||||
|
||||
<ProfileField
|
||||
@ -129,15 +116,17 @@ class Profile extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
onLanguageSwitcher() {
|
||||
this.props.createLanguageSwitcherPopup();
|
||||
}
|
||||
|
||||
handleUUIDMouseOver() {
|
||||
if (!this.UUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
const el = this.UUID;
|
||||
|
||||
try {
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(this.UUID);
|
||||
range.selectNodeContents(el);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
} catch (err) {
|
||||
@ -145,17 +134,11 @@ class Profile extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
setUUID(el) {
|
||||
setUUID(el: ?HTMLDivElement) {
|
||||
this.UUID = el;
|
||||
}
|
||||
}
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import LanguageSwitcher from 'components/languageSwitcher/LanguageSwitcher';
|
||||
import { create as createPopup } from 'components/ui/popup/actions';
|
||||
|
||||
export default connect((state) => ({
|
||||
user: state.user,
|
||||
}), {
|
||||
createLanguageSwitcherPopup: () => createPopup(LanguageSwitcher),
|
||||
})(Profile);
|
||||
}))(Profile);
|
||||
|
@ -68,28 +68,6 @@ $formColumnWidth: 416px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.language {
|
||||
color: #666;
|
||||
border-bottom: 1px dotted #666;
|
||||
text-decoration: none;
|
||||
transition: .25s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.languageIcon {
|
||||
$height: 13px;
|
||||
|
||||
position: relative;
|
||||
top: 1px;
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
height: $height;
|
||||
width: $height * 4 / 3;
|
||||
box-shadow: 0 0 1px rgba(#000, .2);
|
||||
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.uuidValue {
|
||||
composes: paramName;
|
||||
composes: paramValue;
|
||||
|
@ -12,7 +12,7 @@ export type User = {|
|
||||
isGuest: bool,
|
||||
isActive: bool,
|
||||
isOtpEnabled: bool,
|
||||
passwordChangedAt: ?number,
|
||||
passwordChangedAt: number,
|
||||
hasMojangUsernameCollision: bool,
|
||||
maskedEmail?: string,
|
||||
shouldAcceptRules?: bool,
|
||||
@ -32,7 +32,7 @@ const defaults: User = {
|
||||
isActive: false,
|
||||
isOtpEnabled: false,
|
||||
shouldAcceptRules: false, // whether user need to review updated rules
|
||||
passwordChangedAt: null,
|
||||
passwordChangedAt: 0,
|
||||
hasMojangUsernameCollision: false,
|
||||
|
||||
// frontend specific attributes
|
||||
|
@ -1,71 +1,83 @@
|
||||
{
|
||||
"be": {
|
||||
"code": "be",
|
||||
"name": "Беларуская",
|
||||
"englishName": "Belarusian",
|
||||
"progress": 100,
|
||||
"isReleased": true
|
||||
},
|
||||
"en": {
|
||||
"code": "en",
|
||||
"name": "English, UK",
|
||||
"englishName": "English, UK",
|
||||
"progress": 100,
|
||||
"isReleased": true
|
||||
},
|
||||
"fr": {
|
||||
"code": "fr",
|
||||
"name": "Français",
|
||||
"englishName": "French",
|
||||
"progress": 85.6,
|
||||
"isReleased": true
|
||||
},
|
||||
"id": {
|
||||
"code": "id",
|
||||
"name": "Bahasa Indonesia",
|
||||
"englishName": "Indonesian",
|
||||
"progress": 96.2,
|
||||
"isReleased": true
|
||||
},
|
||||
"lt": {
|
||||
"code": "lt",
|
||||
"name": "Lietuvių",
|
||||
"englishName": "Lithuanian",
|
||||
"progress": 92.8,
|
||||
"isReleased": false
|
||||
},
|
||||
"pl": {
|
||||
"code": "pl",
|
||||
"name": "Polski",
|
||||
"englishName": "Polish",
|
||||
"progress": 81.8,
|
||||
"isReleased": false
|
||||
},
|
||||
"pt": {
|
||||
"code": "pt",
|
||||
"name": "Português do Brasil",
|
||||
"englishName": "Portuguese, Brazilian",
|
||||
"progress": 96.2,
|
||||
"isReleased": true
|
||||
},
|
||||
"ro": {
|
||||
"code": "ro",
|
||||
"name": "Română",
|
||||
"englishName": "Romanian",
|
||||
"progress": 81.8,
|
||||
"isReleased": false
|
||||
},
|
||||
"ru": {
|
||||
"code": "ru",
|
||||
"name": "Русский",
|
||||
"englishName": "Russian",
|
||||
"progress": 100,
|
||||
"isReleased": true
|
||||
},
|
||||
"sl": {
|
||||
"code": "sl",
|
||||
"name": "Slovenščina",
|
||||
"englishName": "Slovenian",
|
||||
"progress": 82.2,
|
||||
"isReleased": false
|
||||
},
|
||||
"uk": {
|
||||
"code": "uk",
|
||||
"name": "Українська",
|
||||
"englishName": "Ukrainian",
|
||||
"progress": 85.6,
|
||||
"isReleased": true
|
||||
},
|
||||
"vi": {
|
||||
"code": "vi",
|
||||
"name": "Tiếng Việt",
|
||||
"englishName": "Vietnamese",
|
||||
"progress": 96.2,
|
||||
|
@ -268,7 +268,7 @@ if (isProduction) {
|
||||
];
|
||||
|
||||
webpackConfig.entry.vendor = Object.keys(packageJson.dependencies)
|
||||
.filter((module) => ignoredPlugins.indexOf(module) === -1);
|
||||
.filter((module) => !ignoredPlugins.includes(module));
|
||||
} else {
|
||||
webpackConfig.plugins.push(
|
||||
new webpack.DllReferencePlugin({
|
||||
|
Loading…
Reference in New Issue
Block a user