diff --git a/.all-contributorsrc b/.all-contributorsrc index 77967d7..2c38b22 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -28,6 +28,18 @@ "code", "ideas" ] + }, + { + "login": "simon300000", + "name": "simon3000", + "avatar_url": "https://avatars1.githubusercontent.com/u/12656264?v=4", + "profile": "https://github.com/simon300000", + "contributions": [ + "code", + "ideas", + "translation" + ] } - ] + ], + "repoType": "github" } diff --git a/.eslintrc b/.eslintrc index 9971537..b6ba07b 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,10 +7,17 @@ "mocha": true }, "extends": ["kotori"], + "plugins": ["prettier"], "globals": { "autobind": true }, "rules": { - "new-cap": 0 + "new-cap": 0, + "prettier/prettier": [1, { + "singleQuote": true, + "trailingComma": "none", + "bracketSpacing": true, + "jsxBracketSameLine": true + }] } } diff --git a/README.md b/README.md index 0a33a30..ac76cab 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Table of Contents * [Dev](#dev) * [Commands](#commands) * [API](#api) + * [Localization](#localization) * [Contribute](#contribute) * [Contributors](#contributors) * [License](#license) @@ -63,12 +64,12 @@ A: Pixiv change the `REFRESH TOKEN` auth method, so re-login with refresh_token # Start for development $ git clone https://github.com/LoveLiveSunshine/pixiv.moe $ cd pixiv.moe -$ npm install +$ yarn $ npm start ``` ### Commands -- Install dependencies: `npm install` +- Install dependencies: `yarn` - Run: `npm start` - Test: `npm test` - Build: `npm run dist` @@ -539,16 +540,24 @@ curl 'https://api.pixiv.moe/v1/favourite/46453302' \ ``` +## Localization + +App will auto detect your browser language and use the localization. You can set language manually in drawer. +Now the app supports Japanese and English. +Help us if you can translate this app. Please follow the guide in `src/locale`. + ## Contribute -Feel free to contribute (PR-s and issues welcomed). +Feel free to contribute (PR-s and issues welcomed). +Only `1.x-dev` branch is accepted. ## Contributors Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)): -| [
そら](http://kokororin.github.io)
[💻](https://github.com/LoveLiveSunshine/pixiv.moe/commits?author=kokororin "Code") [📖](https://github.com/LoveLiveSunshine/pixiv.moe/commits?author=kokororin "Documentation") [🎨](#design-kokororin "Design") [⚠️](https://github.com/LoveLiveSunshine/pixiv.moe/commits?author=kokororin "Tests") | [
吟夢ちゃん](https://kirainmoe.com/)
[💻](https://github.com/LoveLiveSunshine/pixiv.moe/commits?author=kirainmoe "Code") [🤔](#ideas-kirainmoe "Ideas, Planning, & Feedback") | -| :---: | :---: | + +| [
そら](http://kokororin.github.io)
[💻](https://github.com/LoveLiveSunshine/pixiv.moe/commits?author=kokororin "Code") [📖](https://github.com/LoveLiveSunshine/pixiv.moe/commits?author=kokororin "Documentation") [🎨](#design-kokororin "Design") [⚠️](https://github.com/LoveLiveSunshine/pixiv.moe/commits?author=kokororin "Tests") | [
吟夢ちゃん](https://kirainmoe.com/)
[💻](https://github.com/LoveLiveSunshine/pixiv.moe/commits?author=kirainmoe "Code") [🤔](#ideas-kirainmoe "Ideas, Planning, & Feedback") | [
simon3000](https://github.com/simon300000)
[💻](https://github.com/LoveLiveSunshine/pixiv.moe/commits?author=simon300000 "Code") [🤔](#ideas-simon300000 "Ideas, Planning, & Feedback") [🌍](#translation-simon300000 "Translation") | +| :---: | :---: | :---: | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! diff --git a/package.json b/package.json index 3f61ecf..3df5590 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,10 @@ { "name": "pixiv.moe", - "version": "1.2.6", + "version": "1.3.0", "description": "Source code of https://pixiv.moe", "scripts": { "clean": "rimraf dist/*", - "copy": "copyfiles -f ./src/index.html ./dist", - "predist": "npm run clean && npm run copy", + "predist": "npm run clean", "dist": "cross-env NODE_ENV=production webpack --config build/webpack.config.js", "postdist": "copyfiles -f ./src/icons/*.ico ./src/icons/*.png ./dist/assets && copyfiles -f ./src/manifest.json ./dist", "archive": "cd dist && tar -czvf dist.tar.gz * --exclude=*.gz", @@ -35,11 +34,12 @@ "devDependencies": { "autoprefixer": "^7.1.2", "babel-core": "^6.26.0", + "babel-eslint": "^8.2.3", "babel-loader": "^7.1.2", - "babel-plugin-minify-dead-code-elimination": "^0.2.0", - "babel-plugin-minify-guarded-expressions": "^0.2.0", + "babel-plugin-minify-dead-code-elimination": "^0.4.3", + "babel-plugin-minify-guarded-expressions": "^0.4.3", "babel-plugin-transform-async-to-generator": "^6.16.0", - "babel-plugin-transform-decorators-legacy": "^1.3.4", + "babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-plugin-transform-object-assign": "^6.8.0", "babel-plugin-transform-react-remove-prop-types": "^0.4.8", "babel-polyfill": "^6.26.0", @@ -53,8 +53,9 @@ "cross-env": "^5.0.5", "css-loader": "^0.28.5", "eslint": "^4.5.0", - "eslint-config-kotori": "^0.1.6", + "eslint-config-kotori": "^0.2.3", "eslint-loader": "^1.9.0", + "eslint-plugin-prettier": "^2.6.0", "fetch-mock": "^5.12.2", "file-loader": "^0.11.2", "glob": "^7.1.2", @@ -78,6 +79,7 @@ "open-browser-webpack-plugin": "^0.0.5", "optimize-js-plugin": "^0.0.4", "postcss-loader": "^2.0.6", + "prettier": "^1.13.5", "puppeteer": "^0.11.0", "react-hot-loader": "next", "react-test-renderer": "^16.3.2", @@ -103,13 +105,14 @@ "moment": "^2.16.0", "namespaced-types": "^0.1.2", "prop-types": "^15.5.10", - "react": "^16.3.2", + "react": "^16.4.1", "react-css-modules": "^4.7.1", "react-document-title": "^2.0.3", - "react-dom": "^16.3.2", + "react-dom": "^16.4.1", "react-ga": "^2.1.2", "react-image": "^1.0.1", - "react-masonry-component": "^6.2.0", + "react-intl": "^2.4.0", + "react-masonry-component": "^6.2.1", "react-mdl": "^1.10.3", "react-modal": "^3.3.1", "react-redux": "^5.0.6", diff --git a/src/actions/gallery.js b/src/actions/gallery.js index 4e53c80..d72e268 100644 --- a/src/actions/gallery.js +++ b/src/actions/gallery.js @@ -1,7 +1,7 @@ import namespacedTypes from 'namespaced-types'; import config from '@/config'; -import { cachedFetch } from '@/utils'; +import cachedFetch from '@/utils/cachedFetch'; export const types = namespacedTypes('gallery', [ 'SET_ITEMS', diff --git a/src/actions/illust.js b/src/actions/illust.js index ab81b36..f86fbcf 100644 --- a/src/actions/illust.js +++ b/src/actions/illust.js @@ -1,7 +1,8 @@ import namespacedTypes from 'namespaced-types'; import config from '@/config'; -import { cachedFetch, getImagesFromZip } from '@/utils'; +import cachedFetch from '@/utils/cachedFetch'; +import getImagesFromZip from '@/utils/getImagesFromZip'; export const types = namespacedTypes('illust', [ 'SET_ITEM', diff --git a/src/actions/index.js b/src/actions/index.js deleted file mode 100644 index f1c0082..0000000 --- a/src/actions/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export * as GalleryActions from '@/actions/gallery'; -export * as IllustActions from '@/actions/illust'; diff --git a/src/actions/locale.js b/src/actions/locale.js new file mode 100644 index 0000000..b14367e --- /dev/null +++ b/src/actions/locale.js @@ -0,0 +1,10 @@ +import namespacedTypes from 'namespaced-types'; + +export const types = namespacedTypes('locale', ['SET_LOCALE']); + +export function setLocale(data) { + return { + type: types.SET_LOCALE, + payload: data + }; +} diff --git a/src/components/Comment.js b/src/components/Comment.js index 918b793..7e49374 100644 --- a/src/components/Comment.js +++ b/src/components/Comment.js @@ -6,7 +6,7 @@ import CSSModules from 'react-css-modules'; import Img from 'react-image'; import ListItem from 'react-mdl/lib/List/ListItem'; import ListItemContent from 'react-mdl/lib/List/ListItemContent'; -import { EmojiParser } from '@/utils'; +import EmojiParser from '@/utils/EmojiParser'; const Comment = ({ item }) => { for (const badWord of Comment.badWords) { @@ -60,4 +60,5 @@ Comment.propTypes = { item: PropTypes.object }; +// eslint-disable-next-line babel/new-cap export default CSSModules(Comment, styles, { allowMultiple: true }); diff --git a/src/components/ConnectedIntlProvider.js b/src/components/ConnectedIntlProvider.js new file mode 100644 index 0000000..e5576c4 --- /dev/null +++ b/src/components/ConnectedIntlProvider.js @@ -0,0 +1,7 @@ +import { connect } from 'react-redux'; +import { IntlProvider } from 'react-intl'; + +export default connect(state => ({ + locale: state.locale.lang, + messages: state.locale.messages +}))(IntlProvider); diff --git a/src/components/InfiniteScroll.js b/src/components/InfiniteScroll.js index ae91986..54df23d 100644 --- a/src/components/InfiniteScroll.js +++ b/src/components/InfiniteScroll.js @@ -2,6 +2,8 @@ import React from 'react'; import PropTypes from 'prop-types'; export default class InfiniteScroll extends React.Component { + static scrollingClassName = 'mdl-layout__content'; + static propTypes = { distance: PropTypes.number.isRequired, onLoadMore: PropTypes.func.isRequired, @@ -9,8 +11,6 @@ export default class InfiniteScroll extends React.Component { isLoading: PropTypes.bool.isRequired }; - static scrollingClassName = 'mdl-layout__content'; - constructor(props) { super(props); } diff --git a/src/components/Item.js b/src/components/Item.js index 1701f73..1643b49 100644 --- a/src/components/Item.js +++ b/src/components/Item.js @@ -4,6 +4,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import CSSModules from 'react-css-modules'; import { Link } from 'react-router-dom'; +import { FormattedMessage } from 'react-intl'; import Icon from 'react-mdl/lib/Icon'; @CSSModules(styles, { allowMultiple: true }) @@ -18,10 +19,6 @@ export default class Item extends React.Component { super(props); } - shouldComponentUpdate() { - return false; - } - onImageMouseMove(event) { event = event.nativeEvent; const target = event.target; @@ -39,7 +36,11 @@ export default class Item extends React.Component { renderRankText() { if (this.props.item.previous_rank === 0) { - return 初登场; + return ( + + + + ); } let icon; if (this.props.item.previous_rank < this.props.item.rank) { @@ -49,7 +50,11 @@ export default class Item extends React.Component { } return ( - {icon} {'前日 ' + this.props.item.previous_rank + '位'} + {icon} + ); } @@ -69,7 +74,12 @@ export default class Item extends React.Component { {this.props.item.work.title}
- {`${this.props.item.rank}位`} + + + {this.renderRankText()}
diff --git a/src/components/List.js b/src/components/List.js index 90ae8d3..b34d8a7 100644 --- a/src/components/List.js +++ b/src/components/List.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Masonry from 'react-masonry-component'; -import { Item } from '@/components'; +import Item from '@/components/Item'; export default class List extends React.Component { static propTypes = { diff --git a/src/components/Loading.js b/src/components/Loading.js index eb840c9..c292e58 100644 --- a/src/components/Loading.js +++ b/src/components/Loading.js @@ -6,7 +6,6 @@ import CSSModules from 'react-css-modules'; import Spinner from 'react-mdl/lib/Spinner'; const Loading = ({ isHidden }) => { - // it means ローディング return isHidden ? null : (
@@ -18,4 +17,5 @@ Loading.propTypes = { isHidden: PropTypes.bool }; +// eslint-disable-next-line babel/new-cap export default CSSModules(Loading, styles, { allowMultiple: true }); diff --git a/src/components/Login.js b/src/components/Login.js index c8cc372..fa1fa2a 100644 --- a/src/components/Login.js +++ b/src/components/Login.js @@ -9,10 +9,12 @@ import classNames from 'classnames'; import Button from 'react-mdl/lib/Button'; import Textfield from 'react-mdl/lib/Textfield'; import Icon from 'react-mdl/lib/Icon'; -import { moment, Storage } from '@/utils'; +import { FormattedMessage, injectIntl } from 'react-intl'; +import moment from '@/utils/moment'; +import Storage from '@/utils/Storage'; +import withRef from '@/utils/withRef'; -@CSSModules(styles, { allowMultiple: true }) -export default class Login extends React.Component { +class Login extends React.Component { static propTypes = { onLogoutClick: PropTypes.func, onLogoutClick: PropTypes.func, @@ -87,12 +89,14 @@ export default class Login extends React.Component {
- ニックネーム 「{this.props.authData.user.name}」 + 「{ + this.props.authData.user.name + }」
@@ -103,7 +107,9 @@ export default class Login extends React.Component { this.setUsername(event.target.value)} value={this.getUsername()} - label="メールアドレス / pixiv ID" + label={this.props.intl.formatMessage({ + id: 'Email Address / pixiv ID' + })} spellCheck={false} floatingLabel style={{ width: '100%' }} @@ -112,7 +118,9 @@ export default class Login extends React.Component { type="password" onChange={event => this.setPassword(event.target.value)} value={this.getPassword()} - label="パスワード" + label={this.props.intl.formatMessage({ + id: 'Password' + })} floatingLabel style={{ width: '100%' }} /> @@ -126,7 +134,9 @@ export default class Login extends React.Component { raised accent ripple> - {this.props.isSubmitting ? 'ちょっとまって' : 'ログイン'} +
@@ -151,3 +161,9 @@ export default class Login extends React.Component { ); } } + +export default withRef( + // eslint-disable-next-line babel/new-cap + CSSModules(Login, styles, { allowMultiple: true }), + injectIntl +); diff --git a/src/components/Message.js b/src/components/Message.js index bb92b63..cabdac9 100644 --- a/src/components/Message.js +++ b/src/components/Message.js @@ -3,19 +3,19 @@ import styles from '@/styles/Message.scss'; import React from 'react'; import PropTypes from 'prop-types'; import CSSModules from 'react-css-modules'; +import { FormattedMessage } from 'react-intl'; @CSSModules(styles, { allowMultiple: true }) export default class Message extends React.Component { - static defaultProps = { - isHidden: false, - text: 'エラーが発生しました。URLを確認するか、しばらく時間を置いて再度アクセスしてください。' - }; - static propTypes = { isHidden: PropTypes.bool, text: PropTypes.string }; + static defaultProps = { + isHidden: false + }; + constructor(props) { super(props); } @@ -23,7 +23,13 @@ export default class Message extends React.Component { render() { return this.props.isHidden ? null : (
-

{this.props.text}

+

+ {this.props.text ? ( + this.props.text + ) : ( + + )} +

); } diff --git a/src/components/Refresh.js b/src/components/Refresh.js index d39b31d..ff8647d 100644 --- a/src/components/Refresh.js +++ b/src/components/Refresh.js @@ -19,4 +19,5 @@ Refresh.propTypes = { onClick: PropTypes.func }; +// eslint-disable-next-line babel/new-cap export default CSSModules(Refresh, styles, { allowMultiple: true }); diff --git a/src/components/TrackPageView.js b/src/components/TrackPageView.js index ef6c1ba..a5ec842 100644 --- a/src/components/TrackPageView.js +++ b/src/components/TrackPageView.js @@ -3,7 +3,8 @@ import ReactGA from 'react-ga'; import { Route } from 'react-router-dom'; import config from '@/config'; -import { Piwik, cachedFetch } from '@/utils'; +import Piwik from '@/utils/Piwik'; +import cachedFetch from '@/utils/cachedFetch'; export default class TrackPageView extends React.Component { constructor(props) { diff --git a/src/components/index.js b/src/components/index.js deleted file mode 100644 index 7bbee24..0000000 --- a/src/components/index.js +++ /dev/null @@ -1,12 +0,0 @@ -export Alert from '@/components/Alert'; -export Comment from '@/components/Comment'; -export GifPlayer from '@/components/GifPlayer'; -export InfiniteScroll from '@/components/InfiniteScroll'; -export Item from '@/components/Item'; -export List from '@/components/List'; -export Login from '@/components/Login'; -export Loading from '@/components/Loading'; -export Message from '@/components/Message'; -export Refresh from '@/components/Refresh'; -export ScrollContext from '@/components/ScrollContext'; -export TrackPageView from '@/components/TrackPageView'; diff --git a/src/config/index.js b/src/config/index.js index ba950ab..3c5231c 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -13,6 +13,16 @@ export default { piwikSiteId: '2PMEnB', projectLink: 'https://github.com/LoveLiveSunshine/pixiv.moe', siteTitle: 'pixivギャラリー', + languages: [ + { + name: '日本語', + value: 'ja' + }, + { + name: 'English', + value: 'en' + } + ], keywords: [ { en: 'ranking', diff --git a/src/containers/AppContainer.js b/src/containers/AppContainer.js index b8eac1e..2494827 100644 --- a/src/containers/AppContainer.js +++ b/src/containers/AppContainer.js @@ -7,13 +7,13 @@ import '@/styles/Base.scss'; import '@/styles/Reset.scss'; import { history } from '@/stores'; -import { - GalleryContainer, - IllustContainer, - RedirectContainer, - NotFoundContainer -} from '@/containers'; -import { ScrollContext, TrackPageView } from '@/components'; +import GalleryContainer from '@/containers/GalleryContainer'; +import IllustContainer from '@/containers/IllustContainer'; +import RedirectContainer from '@/containers/RedirectContainer'; +import NotFoundContainer from '@/containers/NotFoundContainer'; + +import ScrollContext from '@/components/ScrollContext'; +import TrackPageView from '@/components/TrackPageView'; const AppContainer = () => ( diff --git a/src/containers/GalleryContainer.js b/src/containers/GalleryContainer.js index 05e2595..1efa9a6 100644 --- a/src/containers/GalleryContainer.js +++ b/src/containers/GalleryContainer.js @@ -11,14 +11,25 @@ import Content from 'react-mdl/lib/Layout/Content'; import Icon from 'react-mdl/lib/Icon'; import shortid from 'shortid'; import DocumentTitle from 'react-document-title'; +import { FormattedMessage, injectIntl } from 'react-intl'; +import classNames from 'classnames'; import config from '@/config'; -import { GalleryActions } from '@/actions'; -import { InfiniteScroll, List, Loading, Refresh, Message } from '@/components'; -import { scrollTo, Storage } from '@/utils'; +import * as GalleryActions from '@/actions/gallery'; +import InfiniteScroll from '@/components/InfiniteScroll'; +import List from '@/components/List'; +import Loading from '@/components/Loading'; +import Refresh from '@/components/Refresh'; +import Message from '@/components/Message'; +import scrollTo from '@/utils/scrollTo'; +import Storage from '@/utils/Storage'; -export class GalleryContainerWithoutStore extends React.Component { +import chooseLocale from '@/locale/chooseLocale'; + +@connect(state => ({ gallery: state.gallery })) +@injectIntl +export default class GalleryContainer extends React.Component { constructor(props) { super(props); } @@ -102,45 +113,70 @@ export class GalleryContainerWithoutStore extends React.Component { document.body.removeChild(temp); } + @autobind + onLanguageClick(event) { + event.preventDefault(); + + this.layoutDOMNode.MaterialLayout.toggleDrawer(); + const value = event.target.dataset.value; + Storage.set('lang', value); + chooseLocale(value, this.props.dispatch); + } + @autobind onKeywordClick(event) { - event.nativeEvent.preventDefault(); + event.preventDefault(); this.layoutDOMNode.MaterialLayout.toggleDrawer(); - const tag = event.nativeEvent.target.dataset.tag; + const tag = event.target.dataset.tag; this.props.dispatch(GalleryActions.setTag(tag)); this.reRenderContent(false); Storage.set('tag', tag); } + renderLanguages() { + const languages = config.languages; + + return languages.map(elem => { + const lang = Storage.get('lang'); + const highlight = elem.value === lang; + + return ( + + {highlight && } + {elem.name} + + ); + }); + } + renderKeywords() { const keywords = config.keywords; return keywords.map(elem => { - let linkStyle = null, - iconStyle = { - display: 'none' - }; - if (elem.en === this.props.gallery.tag) { - linkStyle = { - fontWeight: 'bold', - fontSize: '16px' - }; - iconStyle = { - color: '#4caf50', - display: 'inline-block' - }; - } + const highlight = elem.en === this.props.gallery.tag; + return ( - - {elem.jp} + className={classNames({ + [`nav-link__${elem.en}`]: true, + 'nav-link__highlight': highlight + })}> + {highlight && } + {elem.en === 'ranking' + ? this.props.intl.formatMessage({ id: 'Ranking' }) + : elem.jp} ); }); @@ -148,9 +184,9 @@ export class GalleryContainerWithoutStore extends React.Component { @autobind onHeaderClick(event) { - const target = event.nativeEvent.target, + const target = event.target, tagName = target.tagName.toLowerCase(), - classList = event.nativeEvent.target.classList; + classList = event.target.classList; if ( !classList.contains('material-icons') && @@ -178,8 +214,20 @@ export class GalleryContainerWithoutStore extends React.Component { - - {this.renderKeywords()} + + + + + + {this.renderLanguages()} + + + + + + + {this.renderKeywords()} + (this.errorRef = ref)} - text="読み込みに失敗しました" + text={this.props.intl.formatMessage({ id: 'Failed to Load' })} isHidden={!this.props.gallery.isError} /> ({ gallery: state.gallery }))( - GalleryContainerWithoutStore -); diff --git a/src/containers/IllustContainer.js b/src/containers/IllustContainer.js index fb6e816..6d6c02c 100644 --- a/src/containers/IllustContainer.js +++ b/src/containers/IllustContainer.js @@ -14,23 +14,26 @@ import { List } from 'react-mdl/lib/List'; import shortid from 'shortid'; import Img from 'react-image'; import DocumentTitle from 'react-document-title'; +import { FormattedMessage, injectIntl } from 'react-intl'; import config from '@/config'; -import { IllustActions } from '@/actions'; -import { - Alert, - Comment, - GifPlayer, - InfiniteScroll, - Loading, - Message -} from '@/components'; -import { LoginContainer } from '@/containers'; -import { cachedFetch, moment, Storage } from '@/utils'; +import * as IllustActions from '@/actions/illust'; +import Alert from '@/components/Alert'; +import Comment from '@/components/Comment'; +import GifPlayer from '@/components/GifPlayer'; +import InfiniteScroll from '@/components/InfiniteScroll'; +import Loading from '@/components/Loading'; +import Message from '@/components/Message'; +import LoginContainer from '@/containers/LoginContainer'; +import cachedFetch from '@/utils/cachedFetch'; +import moment from '@/utils/moment'; +import Storage from '@/utils/Storage'; +@connect(state => ({ illust: state.illust })) +@injectIntl @CSSModules(styles, { allowMultiple: true }) -export class IllustContainerWithoutStore extends React.Component { +export default class IllustContainer extends React.Component { constructor(props) { super(props); @@ -94,9 +97,9 @@ export class IllustContainerWithoutStore extends React.Component { onFavouriteClick(event) { const authData = Storage.get('auth'); if (authData === null || authData.expires_at < moment().unix()) { - return this.loginRef.open(); + return this.loginRef.getRef().open(); } - const target = event.nativeEvent.target, + const target = event.target, body = document.body; if (target.classList.contains('fn-wait')) { return; @@ -121,8 +124,9 @@ export class IllustContainerWithoutStore extends React.Component { .catch(() => { target.classList.remove('fn-wait'); body.classList.remove('fn-wait'); - // text from SIF - this.alertRef.setContent('通信エラーが発生しました'); + this.alertRef.setContent( + this.props.intl.formatMessage({ id: 'Communication Error Occurred' }) + ); }); } @@ -196,7 +200,12 @@ export class IllustContainerWithoutStore extends React.Component { return ; } if (this.props.illust.isError) { - return ; + return ( + + ); } try { return ( @@ -230,13 +239,13 @@ export class IllustContainerWithoutStore extends React.Component {
@@ -252,8 +261,9 @@ export class IllustContainerWithoutStore extends React.Component { {`${moment(this.item.created_time).format('LLL')}(JST)`}
- {`${this.item.width}x${this.item - .height}`} + {`${this.item.width}x${ + this.item.height + }`} {Array.isArray(this.item.tools) && (

- pixivにリダイレクトする +

- this.props.dispatch(IllustActions.fetchComments(this.illustId))} + this.props.dispatch(IllustActions.fetchComments(this.illustId)) + } isLoading={this.props.illust.isFetchingComments} hasMore={!this.props.illust.isCommentsEnd}>
- {this.props.illust.comments.length === 0 ? ( -

コメントはありません

- ) : ( -

コメント

- )} +

+ +

{this.props.illust.comments.map(elem => { return ; @@ -297,7 +312,12 @@ export class IllustContainerWithoutStore extends React.Component {
); } catch (e) { - return ; + return ( + + ); } } @@ -313,7 +333,3 @@ export class IllustContainerWithoutStore extends React.Component { ); } } - -export default connect(state => ({ illust: state.illust }))( - IllustContainerWithoutStore -); diff --git a/src/containers/LoginContainer.js b/src/containers/LoginContainer.js index 366a528..d3b1885 100644 --- a/src/containers/LoginContainer.js +++ b/src/containers/LoginContainer.js @@ -1,11 +1,16 @@ import React from 'react'; +import { injectIntl } from 'react-intl'; -import { Alert, Login } from '@/components'; -import { cachedFetch, moment, Storage } from '@/utils'; +import Alert from '@/components/Alert'; +import Login from '@/components/Login'; +import cachedFetch from '@/utils/cachedFetch'; +import moment from '@/utils/moment'; +import Storage from '@/utils/Storage'; +import withRef from '@/utils/withRef'; import config from '@/config'; -export default class LoginContainer extends React.Component { +class LoginContainer extends React.Component { constructor(props) { super(props); @@ -30,22 +35,25 @@ export default class LoginContainer extends React.Component { @autobind onKeydown(event) { if (event.keyCode === 27) { - this.loginRef.close(); + this.loginRef.getRef().close(); } - if (this.loginRef.state.isHidden === false && event.keyCode === 13) { + if ( + this.loginRef.getRef().state.isHidden === false && + event.keyCode === 13 + ) { this.onLoginClick(); } } @autobind open() { - this.loginRef.open(); + this.loginRef.getRef().open(); } @autobind close() { - this.loginRef.close(); + this.loginRef.getRef().close(); } @autobind @@ -55,18 +63,28 @@ export default class LoginContainer extends React.Component { } if (!Storage.isSupport()) { - return this.alertRef.setContent('localStorageをサポートしていないブラウザ'); + return this.alertRef.setContent( + this.props.intl.formatMessage({ + id: 'Web Browser does not support localStorage' + }) + ); } - const username = this.loginRef.getUsername(); - const password = this.loginRef.getPassword(); + const username = this.loginRef.getRef().getUsername(); + const password = this.loginRef.getRef().getPassword(); if (username === '') { - return this.alertRef.setContent('pixiv ID、またはメールアドレスが未記入です'); + return this.alertRef.setContent( + this.props.intl.formatMessage({ + id: 'pixiv ID or Email Address is Blank' + }) + ); } if (password === '') { - return this.alertRef.setContent('パスワードが未記入です'); + return this.alertRef.setContent( + this.props.intl.formatMessage({ id: 'Password is Blank' }) + ); } this.setState({ @@ -97,8 +115,8 @@ export default class LoginContainer extends React.Component { }); setTimeout(() => { this.close(); - this.loginRef.setUsername(''); - this.loginRef.setPassword(''); + this.loginRef.getRef().setUsername(''); + this.loginRef.getRef().setPassword(''); }, 1500); } else { this.alertRef.setContent(data.message); @@ -113,8 +131,9 @@ export default class LoginContainer extends React.Component { this.setState({ isSubmitting: false }); - // text from SIF - this.alertRef.setContent('通信エラーが発生しました'); + this.alertRef.setContent( + this.props.intl.formatMessage({ id: 'Communication Error Occurred' }) + ); }); } @@ -141,3 +160,5 @@ export default class LoginContainer extends React.Component { ); } } + +export default withRef(LoginContainer, injectIntl); diff --git a/src/containers/NotFoundContainer.js b/src/containers/NotFoundContainer.js index a1d907a..e9fceb5 100644 --- a/src/containers/NotFoundContainer.js +++ b/src/containers/NotFoundContainer.js @@ -1,6 +1,6 @@ import React from 'react'; -import { Message } from '@/components'; +import Message from '@/components/Message'; const NotFoundContainer = () => ; diff --git a/src/containers/RedirectContainer.js b/src/containers/RedirectContainer.js index 5d4f669..50ee595 100644 --- a/src/containers/RedirectContainer.js +++ b/src/containers/RedirectContainer.js @@ -1,8 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { injectIntl } from 'react-intl'; -import { Message } from '@/components'; +import Message from '@/components/Message'; +@injectIntl export default class RedirectContainer extends React.Component { static propTypes = { illustId: PropTypes.string @@ -38,7 +40,11 @@ export default class RedirectContainer extends React.Component { {this.state.isError ? ( ) : ( - + )}
); diff --git a/src/containers/index.js b/src/containers/index.js deleted file mode 100644 index 2d44b3c..0000000 --- a/src/containers/index.js +++ /dev/null @@ -1,10 +0,0 @@ -export AppContainer from '@/containers/AppContainer'; -export GalleryContainer, { - GalleryContainerWithoutStore -} from '@/containers/GalleryContainer'; -export IllustContainer, { - IllustContainerWithoutStore -} from '@/containers/IllustContainer'; -export LoginContainer from '@/containers/LoginContainer'; -export NotFoundContainer from '@/containers/NotFoundContainer'; -export RedirectContainer from '@/containers/RedirectContainer'; diff --git a/src/icons/convert.sh b/src/icons/convert.sh old mode 100755 new mode 100644 diff --git a/src/index.ejs b/src/index.ejs index 2f068b0..f7a059a 100644 --- a/src/index.ejs +++ b/src/index.ejs @@ -1,5 +1,5 @@ - + diff --git a/src/index.js b/src/index.js index 88a0ff3..35408a8 100644 --- a/src/index.js +++ b/src/index.js @@ -6,15 +6,22 @@ import { Provider } from 'react-redux'; import { configureStore } from '@/stores'; import AppContainer from '@/containers/AppContainer'; +import ConnectedIntlProvider from '@/components/ConnectedIntlProvider'; +import chooseLocale from '@/locale/chooseLocale'; + const store = configureStore(); +chooseLocale(navigator.language, store.dispatch); + // Render the main component into the dom const render = Component => { ReactDOM.render( - - - + + + + + , document.getElementById('app') ); diff --git a/src/locale/chooseLocale.js b/src/locale/chooseLocale.js new file mode 100644 index 0000000..6843d0d --- /dev/null +++ b/src/locale/chooseLocale.js @@ -0,0 +1,42 @@ +import { addLocaleData } from 'react-intl'; +import jaLocaleData from 'react-intl/locale-data/ja'; +import ja from '@/locale/ja'; +import enLocaleData from 'react-intl/locale-data/en'; +import en from '@/locale/en'; +import Storage from '@/utils/Storage'; +import * as LocaleActions from '@/actions/locale'; + +const chooseLocale = (language, dispatch) => { + const cachedLang = Storage.get('lang'); + let lang; + let messages; + + if (!cachedLang) { + lang = language.split('-')[0]; + } else { + lang = cachedLang; + } + + switch (lang) { + case 'en': + addLocaleData(enLocaleData); + lang = 'en'; + messages = en; + break; + default: + addLocaleData(jaLocaleData); + lang = 'ja'; + messages = ja; + } + + Storage.set('lang', lang); + + dispatch( + LocaleActions.setLocale({ + lang, + messages + }) + ); +}; + +export default chooseLocale; diff --git a/src/locale/en.js b/src/locale/en.js new file mode 100644 index 0000000..685e51c --- /dev/null +++ b/src/locale/en.js @@ -0,0 +1,32 @@ +const en = { + Debut: 'Debut', + 'Yesterday x rank': 'Yesterday #{rank}', + 'x rank': '#{rank}', + 'Redirect to pixiv': 'Redirect to pixiv', + 'Add to Bookmarks': 'Add to Bookmarks', + Download: 'Download', + Tweet: 'Tweet', + Comments: 'Comments', + 'No Comments': 'No Comments', + 'An Error Occurred': 'An Error Occurred', + 'Communication Error Occurred': 'Communication Error Occurred', + 'Web Browser does not support localStorage': + 'Web Browser does not support localStorage', + 'pixiv ID or Email Address is Blank': 'pixiv ID or Email Address is Blank', + 'Password is Blank': 'Password is Blank', + 'Wait a Moment': 'Wait a Moment', + Login: 'Login', + Logout: 'Logout', + 'Email Address / pixiv ID': 'Email Address / pixiv ID', + Password: 'Password', + Nickname: 'Nickname', + 'An error occurred. Check the URL or wait for a while and access again.': + 'An error occurred. Check the URL or wait for a while and access again.', + 'Failed to Load': 'Failed to Load', + Tags: 'Tags', + 'Redirecting to pixiv.net': 'Redirecting to pixiv.net', + Language: 'Language', + Ranking: 'Ranking' +}; + +export default en; diff --git a/src/locale/ja.js b/src/locale/ja.js new file mode 100644 index 0000000..da34727 --- /dev/null +++ b/src/locale/ja.js @@ -0,0 +1,33 @@ +const ja = { + Debut: '初登场', + 'Yesterday x rank': '前日 {rank}位', + 'x rank': '{rank}位', + 'Redirect to pixiv': 'pixivにリダイレクトする', + 'Add to Bookmarks': 'ブックマークに追加', + Download: 'ダウンロード', + Tweet: 'ツイート', + Comments: 'コメント', + 'No Comments': 'コメントはありません', + 'An Error Occurred': 'エラーが発生しました', + 'Communication Error Occurred': '通信エラーが発生しました', + 'Web Browser does not support localStorage': + 'localStorageをサポートしていないブラウザ', + 'pixiv ID or Email Address is Blank': + 'pixiv ID、またはメールアドレスが未記入です', + 'Password is Blank': 'パスワードが未記入です', + 'Wait a Moment': 'ちょっとまって', + Login: 'ログイン', + Logout: 'ログアウト', + 'Email Address / pixiv ID': 'メールアドレス / pixiv ID', + Password: 'パスワード', + Nickname: 'ニックネーム', + 'An error occurred. Check the URL or wait for a while and access again.': + 'エラーが発生しました。URLを確認するか、しばらく時間を置いて再度アクセスしてください。', + 'Failed to Load': '読み込みに失敗しました', + Tags: 'タグ', + 'Redirecting to pixiv.net': 'あなたはpixiv.netへリダイレクトしています', + Language: '言語', + Ranking: 'ランキング' +}; + +export default ja; diff --git a/src/reducers/gallery.js b/src/reducers/gallery.js index 217cb72..45c7e67 100644 --- a/src/reducers/gallery.js +++ b/src/reducers/gallery.js @@ -1,4 +1,4 @@ -import { GalleryActions } from '@/actions'; +import * as GalleryActions from '@/actions/gallery'; const types = GalleryActions.types; diff --git a/src/reducers/illust.js b/src/reducers/illust.js index c228a81..30bde94 100644 --- a/src/reducers/illust.js +++ b/src/reducers/illust.js @@ -1,4 +1,4 @@ -import { IllustActions } from '@/actions'; +import * as IllustActions from '@/actions/illust'; const types = IllustActions.types; diff --git a/src/reducers/index.js b/src/reducers/index.js index afe1420..07f38f9 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -2,10 +2,12 @@ import { combineReducers } from 'redux'; import gallery from '@/reducers/gallery'; import illust from '@/reducers/illust'; +import locale from '@/reducers/locale'; const reducers = combineReducers({ gallery, - illust + illust, + locale }); export default reducers; diff --git a/src/reducers/locale.js b/src/reducers/locale.js new file mode 100644 index 0000000..8518af5 --- /dev/null +++ b/src/reducers/locale.js @@ -0,0 +1,23 @@ +import * as LocaleActions from '@/actions/locale'; + +const types = LocaleActions.types; + +export default function locale( + state = { + lang: '', + messages: {} + }, + action +) { + switch (action.type) { + case types.SET_LOCALE: + return { + ...state, + lang: action.payload.lang, + messages: action.payload.messages + }; + + default: + return state; + } +} diff --git a/src/styles/Reset.scss b/src/styles/Reset.scss index a08a5b0..d55c0a6 100644 --- a/src/styles/Reset.scss +++ b/src/styles/Reset.scss @@ -26,6 +26,14 @@ .nav-link__sunshine { border-top: 1px solid rgba(0, 0, 0, 0.12); } + .nav-link__highlight { + font-weight: bold; + font-size: 16px; + .material-icons { + color: #4caf50; + display: inline-block; + } + } } .mdl-list__item-avatar { diff --git a/src/utils/EmojiParser.js b/src/utils/EmojiParser.js index 830b5cc..ff11254 100644 --- a/src/utils/EmojiParser.js +++ b/src/utils/EmojiParser.js @@ -161,9 +161,11 @@ export default class EmojiParser { let replaceStr = ''; for (const series of EmojiParser.emojiSeries) { if (match === `(${series.name})`) { - replaceStr += ``; + replaceStr += ``; } } return replaceStr === '' ? match : replaceStr; diff --git a/src/utils/Piwik.js b/src/utils/Piwik.js index f18893a..1898afa 100644 --- a/src/utils/Piwik.js +++ b/src/utils/Piwik.js @@ -109,7 +109,7 @@ export default class Piwik { } connectToHistory(history) { - this.unlistenFromHistory = history.listen(function(loc) { + this.unlistenFromHistory = history.listen(loc => { this.track(loc); }); diff --git a/src/utils/cachedFetch.js b/src/utils/cachedFetch.js index 3333e15..68a9400 100644 --- a/src/utils/cachedFetch.js +++ b/src/utils/cachedFetch.js @@ -1,5 +1,7 @@ // modify from https://www.sitepoint.com/cache-fetched-ajax-requests/ -import { hashStr, moment, Storage } from '@/utils'; +import hashStr from '@/utils/hashStr'; +import moment from '@/utils/moment'; +import Storage from '@/utils/Storage'; function buildURL(url, params) { if (!params) { diff --git a/src/utils/getImagesFromZip.js b/src/utils/getImagesFromZip.js index c8a46bd..93d0957 100644 --- a/src/utils/getImagesFromZip.js +++ b/src/utils/getImagesFromZip.js @@ -1,5 +1,5 @@ import JSZip from 'jszip'; -import { cachedFetch } from '@/utils'; +import cachedFetch from '@/utils/cachedFetch'; export default function getImagesFromZip(zipURL) { return new Promise(resolve => { diff --git a/src/utils/index.js b/src/utils/index.js deleted file mode 100644 index acc6e16..0000000 --- a/src/utils/index.js +++ /dev/null @@ -1,8 +0,0 @@ -export cachedFetch from '@/utils/cachedFetch'; -export hashStr from '@/utils/hashStr'; -export EmojiParser from '@/utils/EmojiParser'; -export getImagesFromZip from '@/utils/getImagesFromZip'; -export moment from '@/utils/moment'; -export Piwik from '@/utils/Piwik'; -export scrollTo from '@/utils/scrollTo'; -export Storage from '@/utils/Storage'; diff --git a/src/utils/withRef.js b/src/utils/withRef.js new file mode 100644 index 0000000..db37799 --- /dev/null +++ b/src/utils/withRef.js @@ -0,0 +1,75 @@ +import React from 'react'; +import shortid from 'shortid'; + +export function refComponentHoc(ChildComponent, refer) { + return class RefComponent extends React.Component { + constructor(props) { + super(props); + } + + @autobind + setRef(childComponentInstance) { + if (refer) { + refer.ref = childComponentInstance; + } + this[ChildComponent.name] = childComponentInstance; + } + + render() { + const props = { ...this.props, ref: this.setRef }; + return ; + } + }; +} + +export default function withRef(wrappedComponent, ...decorators) { + const refer = new Proxy( + { id: '' }, + { + get(target, key) { + if (key === 'id') { + const value = target.id; + target.id = ''; + return value; + } + return target[key]; + }, + set(target, key, value) { + key = shortid.generate(); + target.id = key; + target[key] = value; + return true; + } + } + ); + + const refComponent = refComponentHoc(wrappedComponent, refer); + const ResultComponent = decorators.reduce((prev, next) => { + return next(prev); + }, refComponent); + + return class RefComponentWrapper extends React.Component { + constructor(props) { + super(props); + } + + componentDidMount() { + !this._refId && (this._refId = refer.id); + } + + componentWillUnmount() { + refer[this._refId] = null; + this._refId = null; + } + + @autobind + getRef() { + return refer[this._refId]; + } + + render() { + const props = { ...this.props }; + return ; + } + }; +} diff --git a/test/actions/galleryTest.js b/test/actions/galleryTest.js index e9f8f36..78fdcfc 100644 --- a/test/actions/galleryTest.js +++ b/test/actions/galleryTest.js @@ -2,7 +2,7 @@ import nock from 'nock'; import mockStore from '../helpers/mockStoreHelper'; import config from '@/config'; -import { GalleryActions } from '@/actions'; +import * as GalleryActions from '@/actions/gallery'; describe('GalleryActions', () => { afterEach(() => { @@ -10,7 +10,9 @@ describe('GalleryActions', () => { }); it('fire SET_FETCH_ERROR when fetching sources has been done', done => { - nock(config.baseURL).get(`${config.galleryURI}?tag=nico`).reply(200); + nock(config.baseURL) + .get(`${config.galleryURI}?tag=nico`) + .reply(200); const expectedActions = [ { diff --git a/test/containers/GalleryContainerTest.js b/test/containers/GalleryContainerTest.js deleted file mode 100644 index 398583f..0000000 --- a/test/containers/GalleryContainerTest.js +++ /dev/null @@ -1,19 +0,0 @@ -import { expect } from 'chai'; -import createComponent from '../helpers/shallowRenderHelper'; - -import { GalleryContainerWithoutStore } from '@/containers'; -import config from '@/config'; - -describe('GalleryContainer', () => { - let galleryContainer; - - beforeEach(() => { - galleryContainer = createComponent(GalleryContainerWithoutStore, { - gallery: [] - }); - }); - - it('GalleryContainerの has a title', () => { - expect(galleryContainer.props.title).to.equal(config.siteTitle); - }); -}); diff --git a/test/containers/NotFoundContainerTest.js b/test/containers/NotFoundContainerTest.js deleted file mode 100644 index cf781c1..0000000 --- a/test/containers/NotFoundContainerTest.js +++ /dev/null @@ -1,18 +0,0 @@ -import { expect } from 'chai'; -import createComponent from '../helpers/shallowRenderHelper'; - -import { NotFoundContainer } from '@/containers'; - -describe('NotFoundContainer', () => { - let notFoundContainer; - - beforeEach(() => { - notFoundContainer = createComponent(NotFoundContainer); - }); - - it('NotFoundContainer has a correct message', () => { - expect(notFoundContainer.props.text).to.equal( - 'エラーが発生しました。URLを確認するか、しばらく時間を置いて再度アクセスしてください。' - ); - }); -}); diff --git a/test/containers/RedirectContainerTest.js b/test/containers/RedirectContainerTest.js deleted file mode 100644 index edc56f4..0000000 --- a/test/containers/RedirectContainerTest.js +++ /dev/null @@ -1,20 +0,0 @@ -import { expect } from 'chai'; -import createComponent from '../helpers/shallowRenderHelper'; - -import { RedirectContainer } from '@/containers'; - -describe('RedirectContainer', () => { - let redirectContainer; - - beforeEach(() => { - redirectContainer = createComponent(RedirectContainer, { - illustId: '45944782' - }); - }); - - it('RedirectContainer has a correct message', () => { - expect(redirectContainer.props.children.props.text).to.equal( - 'あなたはpixiv.netへリダイレクトしています' - ); - }); -}); diff --git a/test/utils/EmojiParserTest.js b/test/utils/EmojiParserTest.js index 447b7be..6badba9 100644 --- a/test/utils/EmojiParserTest.js +++ b/test/utils/EmojiParserTest.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { EmojiParser } from '@/utils'; +import EmojiParser from '@/utils/EmojiParser'; describe('EmojiParser', () => { it('should parse out with a ', () => { diff --git a/test/utils/StorageTest.js b/test/utils/StorageTest.js index 747032a..b0b586a 100644 --- a/test/utils/StorageTest.js +++ b/test/utils/StorageTest.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { Storage } from '@/utils'; +import Storage from '@/utils/Storage'; describe('Storage', () => { it('set and get storage should work', () => { diff --git a/test/utils/cachedFetchTest.js b/test/utils/cachedFetchTest.js index 79621c6..7f7d169 100644 --- a/test/utils/cachedFetchTest.js +++ b/test/utils/cachedFetchTest.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import fetchMock from 'fetch-mock'; -import { cachedFetch } from '@/utils'; +import cachedFetch from '@/utils/cachedFetch'; describe('cachedFetch', () => { afterEach(() => { diff --git a/test/utils/hashStrTest.js b/test/utils/hashStrTest.js index a651af9..a69ce0a 100644 --- a/test/utils/hashStrTest.js +++ b/test/utils/hashStrTest.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { hashStr } from '@/utils'; +import hashStr from '@/utils/hashStr'; describe('hashStr', () => { it('hashStr of `kotori` should be -1125571242', () => { diff --git a/test/utils/momentTest.js b/test/utils/momentTest.js index e43d27f..e27ea02 100644 --- a/test/utils/momentTest.js +++ b/test/utils/momentTest.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { moment } from '@/utils'; +import moment from '@/utils/moment'; describe('moment', () => { it('convert YYYY-MM-DD H:i:s to JST', () => { diff --git a/yarn.lock b/yarn.lock index 9b35891..1391c79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,70 +2,77 @@ # yarn lockfile v1 -"@babel/code-frame@7.0.0-beta.40", "@babel/code-frame@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.40.tgz#37e2b0cf7c56026b4b21d3927cadf81adec32ac6" +"@babel/code-frame@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz#2a02643368de80916162be70865c97774f3adbd9" dependencies: - "@babel/highlight" "7.0.0-beta.40" + "@babel/highlight" "7.0.0-beta.44" -"@babel/generator@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.40.tgz#ab61f9556f4f71dbd1138949c795bb9a21e302ea" +"@babel/generator@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42" dependencies: - "@babel/types" "7.0.0-beta.40" + "@babel/types" "7.0.0-beta.44" jsesc "^2.5.1" lodash "^4.2.0" source-map "^0.5.0" trim-right "^1.0.1" -"@babel/helper-function-name@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.40.tgz#9d033341ab16517f40d43a73f2d81fc431ccd7b6" +"@babel/helper-function-name@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd" dependencies: - "@babel/helper-get-function-arity" "7.0.0-beta.40" - "@babel/template" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" + "@babel/helper-get-function-arity" "7.0.0-beta.44" + "@babel/template" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" -"@babel/helper-get-function-arity@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.40.tgz#ac0419cf067b0ec16453e1274f03878195791c6e" +"@babel/helper-get-function-arity@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15" dependencies: - "@babel/types" "7.0.0-beta.40" + "@babel/types" "7.0.0-beta.44" -"@babel/highlight@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.40.tgz#b43d67d76bf46e1d10d227f68cddcd263786b255" +"@babel/helper-split-export-declaration@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc" + dependencies: + "@babel/types" "7.0.0-beta.44" + +"@babel/highlight@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5" dependencies: chalk "^2.0.0" esutils "^2.0.2" js-tokens "^3.0.0" -"@babel/template@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.40.tgz#034988c6424eb5c3268fe6a608626de1f4410fc8" +"@babel/template@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f" dependencies: - "@babel/code-frame" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" - babylon "7.0.0-beta.40" + "@babel/code-frame" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" + babylon "7.0.0-beta.44" lodash "^4.2.0" -"@babel/traverse@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.40.tgz#d140e449b2e093ef9fe1a2eecc28421ffb4e521e" +"@babel/traverse@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966" dependencies: - "@babel/code-frame" "7.0.0-beta.40" - "@babel/generator" "7.0.0-beta.40" - "@babel/helper-function-name" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" - babylon "7.0.0-beta.40" - debug "^3.0.1" + "@babel/code-frame" "7.0.0-beta.44" + "@babel/generator" "7.0.0-beta.44" + "@babel/helper-function-name" "7.0.0-beta.44" + "@babel/helper-split-export-declaration" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" + babylon "7.0.0-beta.44" + debug "^3.1.0" globals "^11.1.0" invariant "^2.2.0" lodash "^4.2.0" -"@babel/types@7.0.0-beta.40", "@babel/types@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.40.tgz#25c3d7aae14126abe05fcb098c65a66b6d6b8c14" +"@babel/types@7.0.0-beta.44": + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757" dependencies: esutils "^2.0.2" lodash "^4.2.0" @@ -447,14 +454,14 @@ babel-core@^6.26.0: slash "^1.0.0" source-map "^0.5.7" -babel-eslint@^8.0.2: - version "8.2.2" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.2.tgz#1102273354c6f0b29b4ea28a65f97d122296b68b" +babel-eslint@^8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.3.tgz#1a2e6681cc9bc4473c32899e59915e19cd6733cf" dependencies: - "@babel/code-frame" "^7.0.0-beta.40" - "@babel/traverse" "^7.0.0-beta.40" - "@babel/types" "^7.0.0-beta.40" - babylon "^7.0.0-beta.40" + "@babel/code-frame" "7.0.0-beta.44" + "@babel/traverse" "7.0.0-beta.44" + "@babel/types" "7.0.0-beta.44" + babylon "7.0.0-beta.44" eslint-scope "~3.7.1" eslint-visitor-keys "^1.0.0" @@ -513,9 +520,9 @@ babel-helper-define-map@^6.24.1: babel-types "^6.26.0" lodash "^4.17.4" -babel-helper-evaluate-path@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.2.0.tgz#0bb2eb01996c0cef53c5e8405e999fe4a0244c08" +babel-helper-evaluate-path@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.4.3.tgz#0a89af702c06b217027fa371908dd4989d3e633f" babel-helper-explode-assignable-expression@^6.24.1: version "6.24.1" @@ -534,9 +541,9 @@ babel-helper-explode-class@^6.24.1: babel-traverse "^6.24.1" babel-types "^6.24.1" -babel-helper-flip-expressions@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.2.0.tgz#160d2090a3d9f9c64a750905321a0bc218f884ec" +babel-helper-flip-expressions@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz#3696736a128ac18bc25254b5f40a22ceb3c1d3fd" babel-helper-function-name@^6.24.1: version "6.24.1" @@ -562,9 +569,9 @@ babel-helper-hoist-variables@^6.24.1: babel-runtime "^6.22.0" babel-types "^6.24.1" -babel-helper-mark-eval-scopes@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.2.0.tgz#7648aaf2ec92aae9b09a20ad91e8df5e1fcc94b2" +babel-helper-mark-eval-scopes@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz#d244a3bef9844872603ffb46e22ce8acdf551562" babel-helper-optimise-call-expression@^6.24.1: version "6.24.1" @@ -591,9 +598,9 @@ babel-helper-remap-async-to-generator@^6.24.1: babel-traverse "^6.24.1" babel-types "^6.24.1" -babel-helper-remove-or-void@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.2.0.tgz#8e46ad5b30560d57d7510b3fd93f332ee7c67386" +babel-helper-remove-or-void@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz#a4f03b40077a0ffe88e45d07010dee241ff5ae60" babel-helper-replace-supers@^6.24.1: version "6.24.1" @@ -633,20 +640,20 @@ babel-plugin-check-es2015-constants@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-minify-dead-code-elimination@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.2.0.tgz#e8025ee10a1e5e4f202633a6928ce892c33747e3" +babel-plugin-minify-dead-code-elimination@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.4.3.tgz#73628265864f9008d0027506f58abeb3c1d02d98" dependencies: - babel-helper-evaluate-path "^0.2.0" - babel-helper-mark-eval-scopes "^0.2.0" - babel-helper-remove-or-void "^0.2.0" + babel-helper-evaluate-path "^0.4.3" + babel-helper-mark-eval-scopes "^0.4.3" + babel-helper-remove-or-void "^0.4.3" lodash.some "^4.6.0" -babel-plugin-minify-guarded-expressions@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.2.0.tgz#8a8c950040fce3e258a12e6eb21eab94ad7235ab" +babel-plugin-minify-guarded-expressions@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.3.tgz#cc709b4453fd21b1f302877444c89f88427ce397" dependencies: - babel-helper-flip-expressions "^0.2.0" + babel-helper-flip-expressions "^0.4.3" babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" @@ -737,9 +744,9 @@ babel-plugin-transform-class-properties@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-plugin-transform-decorators-legacy@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.4.tgz#741b58f6c5bce9e6027e0882d9c994f04f366925" +babel-plugin-transform-decorators-legacy@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.5.tgz#0e492dffa0edd70529072887f8aa86d4dd8b40a1" dependencies: babel-plugin-syntax-decorators "^6.1.18" babel-runtime "^6.2.0" @@ -1181,9 +1188,9 @@ babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26 lodash "^4.17.4" to-fast-properties "^1.0.3" -babylon@7.0.0-beta.40, babylon@^7.0.0-beta.40: - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.40.tgz#91fc8cd56d5eb98b28e6fde41045f2957779940a" +babylon@7.0.0-beta.44: + version "7.0.0-beta.44" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d" babylon@^6.11.0, babylon@^6.17.4, babylon@^6.18.0: version "6.18.0" @@ -2320,7 +2327,7 @@ debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.4.1, debug@^2.6. dependencies: ms "2.0.0" -debug@^3.0.1, debug@^3.1.0: +debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: @@ -2495,7 +2502,7 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" -doctrine@^2.0.2, doctrine@^2.1.0: +doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" dependencies: @@ -2822,12 +2829,12 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-config-kotori@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/eslint-config-kotori/-/eslint-config-kotori-0.1.6.tgz#556c527515ba6eff8fe4c5063d04bc05c2bbee5e" +eslint-config-kotori@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/eslint-config-kotori/-/eslint-config-kotori-0.2.3.tgz#330a0b38d7135f02272d03acd399e84676c3edfa" dependencies: - babel-eslint "^8.0.2" - eslint-plugin-react "^7.3.0" + eslint-plugin-babel ">=5.0.0" + eslint-plugin-react ">=7.0.0" eslint-loader@^1.9.0: version "1.9.0" @@ -2839,14 +2846,31 @@ eslint-loader@^1.9.0: object-hash "^1.1.4" rimraf "^2.6.1" -eslint-plugin-react@^7.3.0: - version "7.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz#f606c719dbd8a1a2b3d25c16299813878cca0160" +eslint-plugin-babel@>=5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-5.1.0.tgz#9c76e476162041e50b6ba69aa4eae3bdd6a4e1c3" + dependencies: + eslint-rule-composer "^0.3.0" + +eslint-plugin-prettier@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.6.0.tgz#33e4e228bdb06142d03c560ce04ec23f6c767dd7" dependencies: - doctrine "^2.0.2" - has "^1.0.1" + fast-diff "^1.1.1" + jest-docblock "^21.0.0" + +eslint-plugin-react@>=7.0.0: + version "7.9.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.9.1.tgz#101aadd15e7c7b431ed025303ac7b421a8e3dc15" + dependencies: + doctrine "^2.1.0" + has "^1.0.2" jsx-ast-utils "^2.0.1" - prop-types "^15.6.0" + prop-types "^15.6.1" + +eslint-rule-composer@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" eslint-scope@^3.7.1, eslint-scope@~3.7.1: version "3.7.1" @@ -3143,6 +3167,10 @@ fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" +fast-diff@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -3560,10 +3588,14 @@ global@^4.3.0: min-document "^2.19.0" process "~0.5.1" -globals@^11.0.1, globals@^11.1.0: +globals@^11.0.1: version "11.3.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.3.0.tgz#e04fdb7b9796d8adac9c8f64c14837b2313378b0" +globals@^11.1.0: + version "11.5.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.5.0.tgz#6bc840de6771173b191f13d3a9c94d441ee92642" + globals@^9.18.0: version "9.18.0" resolved "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -3731,6 +3763,12 @@ has@^1.0.1: dependencies: function-bind "^1.0.2" +has@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + dependencies: + function-bind "^1.1.1" + hash-base@^3.0.0: version "3.0.4" resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" @@ -4079,21 +4117,41 @@ interpret@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" -invariant@^2.0.0, invariant@^2.2.0: +intl-format-cache@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/intl-format-cache/-/intl-format-cache-2.1.0.tgz#04a369fecbfad6da6005bae1f14333332dcf9316" + +intl-messageformat-parser@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz#b43d45a97468cadbe44331d74bb1e8dea44fc075" + +intl-messageformat@^2.0.0, intl-messageformat@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-2.2.0.tgz#345bcd46de630b7683330c2e52177ff5eab484fc" + dependencies: + intl-messageformat-parser "1.4.0" + +intl-relativeformat@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz#010f1105802251f40ac47d0e3e1a201348a255df" + dependencies: + intl-messageformat "^2.0.0" + +invariant@^2.0.0: version "2.2.3" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.3.tgz#1a827dfde7dcbd7c323f0ca826be8fa7c5e9d688" dependencies: loose-envify "^1.0.0" -invariant@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" +invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: loose-envify "^1.0.0" -invariant@^2.2.2: - version "2.2.4" - resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" +invariant@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" dependencies: loose-envify "^1.0.0" @@ -4515,6 +4573,10 @@ istanbul-reports@^1.3.0: dependencies: handlebars "^4.0.3" +jest-docblock@^21.0.0: + version "21.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" + js-base64@^2.1.8: version "2.4.3" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582" @@ -6275,6 +6337,10 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier@^1.13.5: + version "1.13.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.5.tgz#7ae2076998c8edce79d63834e9b7b09fead6bfd0" + pretty-bytes@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" @@ -6331,7 +6397,7 @@ prop-types@^15.5.0, prop-types@^15.5.4: fbjs "^0.8.9" loose-envify "^1.3.1" -prop-types@^15.5.10: +prop-types@^15.5.10, prop-types@^15.6.1: version "15.6.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" dependencies: @@ -6506,9 +6572,9 @@ react-document-title@^2.0.3: prop-types "^15.5.6" react-side-effect "^1.0.2" -react-dom@^16.3.2: - version "16.3.2" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df" +react-dom@^16.4.1: + version "16.4.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.1.tgz#7f8b0223b3a5fbe205116c56deb85de32685dad6" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0" @@ -6538,13 +6604,22 @@ react-image@^1.0.1: dependencies: prop-types "15.6.0" +react-intl@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.4.0.tgz#66c14dc9df9a73b2fbbfbd6021726e80a613eb15" + dependencies: + intl-format-cache "^2.0.5" + intl-messageformat "^2.1.0" + intl-relativeformat "^2.0.0" + invariant "^2.1.1" + react-is@^16.3.2: version "16.3.2" resolved "https://registry.npmjs.org/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22" -react-masonry-component@^6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/react-masonry-component/-/react-masonry-component-6.2.0.tgz#e0689ff2b819da34785a8d8604ea52ff9e1caae6" +react-masonry-component@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/react-masonry-component/-/react-masonry-component-6.2.1.tgz#dd23b3129d6b922852b160472e6c9a6256fcdb8a" dependencies: create-react-class "^15.6.2" element-resize-detector "^1.1.9" @@ -6626,9 +6701,9 @@ react-test-renderer@^16.3.2: prop-types "^15.6.0" react-is "^16.3.2" -react@^16.3.2: - version "16.3.2" - resolved "https://registry.npmjs.org/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9" +react@^16.4.1: + version "16.4.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0"