diff --git a/package-lock.json b/package-lock.json index 1d2ee8e..a1f4d09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17872,6 +17872,17 @@ "prop-types": "^15.6.0" } }, + "react-toastify": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-6.0.5.tgz", + "integrity": "sha512-1YXSb6Jr478c1TJEyVpxLHFvtmeXGMvdpZc0fke/7lK+MoLBC+NFgB74bq+C2SZe6LdK+K1voEURJoY88WqWvA==", + "dev": true, + "requires": { + "classnames": "^2.2.6", + "prop-types": "^15.7.2", + "react-transition-group": "^4.4.1" + } + }, "react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", diff --git a/package.json b/package.json index 70041ba..4737e97 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "postcss-import": "^12.0.1", "prettier": "^2.0.5", "puppeteer": "^3.1.0", + "react-toastify": "^6.0.5", "sass": "^1.26.3", "sass-loader": "^8.0.2", "start-server-and-test": "^1.11.0", diff --git a/src/containers/app/app.js b/src/containers/app/app.js index 7f95ef4..29977c6 100644 --- a/src/containers/app/app.js +++ b/src/containers/app/app.js @@ -2,7 +2,8 @@ import { hot } from 'react-hot-loader/root'; import React, { Component } from 'react'; import { HashRouter as Router, Route, Switch } from 'react-router-dom'; import copy from 'copy-to-clipboard'; -import { Translation } from 'react-i18next'; +import { withTranslation } from 'react-i18next'; +import { ToastContainer, Slide } from 'react-toastify'; import { CARDS_DICTIONARIES, CARDS_TYPES, @@ -13,6 +14,7 @@ import { import * as Location from 'src/utils/location'; import * as FirebaseService from 'src/service'; import { getRemainingCardsCount } from 'src/utils/team-progress'; +import { toast } from 'src/utils/toast'; import { Loader } from 'src/components/loader'; import { Lobby } from '../lobby'; import { NotFound } from '../not-found'; @@ -44,6 +46,8 @@ class App extends Component { isLoading: true, }; this.sessionId = ''; + // eslint-disable-next-line react/prop-types + this.t = props.t; } async componentDidMount() { @@ -172,31 +176,55 @@ class App extends Component { shareSession() { if (navigator.share) { - navigator.share({ - title: 'Codenames - кодовые имена', - url: Location.getGameLink(this.sessionId), - }); + navigator + .share({ + title: this.t('root.title'), + url: Location.getGameLink(this.sessionId), + }) + .then(() => { + toast.success(this.t('root.linkCopied')); + }) + .catch(() => { + toast.error(this.t('error.linkNotCopied')); + }); } else { - copy(Location.getGameLink(this.sessionId)); + const isCopied = copy(Location.getGameLink(this.sessionId)); + if (isCopied) { + toast.success(this.t('root.linkCopied')); + } else { + toast.error(this.t('error.linkNotCopied')); + } } } generateCards() { - FirebaseService.saveSettings(this.sessionId, this.state.settings); + FirebaseService.saveSettings(this.sessionId, this.state.settings) + .then(() => { + toast.success(this.t('root.cardsUpdated')); + }) + .catch(() => { + toast.error(this.t('root.cardsNotUpdated')); + }); } render() { return ( <> + {this.state.isLoading && ( - - {(t) => ( - - )} - + )} @@ -234,6 +262,6 @@ class App extends Component { } } -const HotApp = hot(App); +const HotApp = hot(withTranslation()(App)); export { HotApp as App }; diff --git a/src/containers/lobby/lobby.js b/src/containers/lobby/lobby.js index 1b7161b..43ca7a0 100644 --- a/src/containers/lobby/lobby.js +++ b/src/containers/lobby/lobby.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -import { Link, useHistory } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useTranslation } from 'react-i18next'; import { CARDS_DICTIONARIES, FIELD_SIZES, TEAMS } from 'src/data/constants'; @@ -158,7 +158,7 @@ function Lobby({
diff --git a/src/data/translation-ru.json b/src/data/translation-ru.json index a3776d5..040d485 100644 --- a/src/data/translation-ru.json +++ b/src/data/translation-ru.json @@ -1,6 +1,9 @@ { "root": { - "appLoadingText": "Запуск приложения" + "appLoadingText": "Запуск приложения", + "linkCopied": "Ссылка скопирована", + "title": "Codenames - кодовые имена", + "cardsUpdated": "Карточки обновлены" }, "lobby": { "sessionId": "ID сессии", @@ -9,7 +12,7 @@ "share": "Поделиться", "setUsername": "Ваш ник", "chooseTeam": "Выберите команду", - "generateCards": "Сгенерировать карточки", + "updateCards": "Обновить карточки", "startGame": "Погнали", "dictionary": "Словарь" }, @@ -28,5 +31,7 @@ "captain": "Капитан" }, "error": { + "linkNotCopied": "Ошибка. Не удалось скопировать ссылку", + "cardsNotUpdated": "Ошибка. Не удалось обновить карточки" } } diff --git a/src/styles/styles.css b/src/styles/styles.css index 6f70d02..40b4a0a 100644 --- a/src/styles/styles.css +++ b/src/styles/styles.css @@ -2,6 +2,7 @@ @import 'tailwindcss/components.css'; @import 'tailwindcss/utilities.css'; @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700;900&display=swap'); +@import "./toastify.css"; body { font-family: 'Roboto', sans-serif; diff --git a/src/styles/toastify.css b/src/styles/toastify.css new file mode 100644 index 0000000..202933d --- /dev/null +++ b/src/styles/toastify.css @@ -0,0 +1,52 @@ +@import 'react-toastify/dist/ReactToastify.min.css'; + +/** Used to define container behavior: width, position: fixed etc... **/ +.Toastify__toast-container { +} + +/** Classes for the displayed toast **/ +.Toastify__toast { + @apply text-center text-lg; + @apply rounded-sm; + @apply shadow-lg; + min-height: 58px; +} + +.Toastify__toast--rtl { +} +.Toastify__toast--dark { +} +.Toastify__toast--default { +} +.Toastify__toast--info { +} +.Toastify__toast--success { + @apply bg-green-400; +} +.Toastify__toast--warning { +} +.Toastify__toast--error { + @apply bg-red-300; +} + +.Toastify__close-button { + @apply hidden; +} + +@responsive { + @screen sm { + .Toastify__toast { + @apply font-normal text-base; + } + } + @screen md { + .Toastify__close-button { + @apply block; + } + } + @screen lg { + .Toastify__toast { + min-height: 64px; + } + } +} diff --git a/src/utils/toast.js b/src/utils/toast.js new file mode 100644 index 0000000..8ad4ad5 --- /dev/null +++ b/src/utils/toast.js @@ -0,0 +1,23 @@ +import { toast } from 'react-toastify'; + +const defaultOptions = { + position: 'top-right', + autoClose: 1400, + hideProgressBar: true, + closeOnClick: true, + progress: false, +}; + +const localToast = { + success(content, options) { + return toast.success(content, { ...defaultOptions, ...options }); + }, + error(content, options) { + return toast.error(content, { ...defaultOptions, ...options }); + }, + info(content, options) { + return toast.dark(content, { ...defaultOptions, ...options }); + }, +}; + +export { localToast as toast }; diff --git a/tailwind.config.js b/tailwind.config.js index 8f7196c..2d5fab8 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -26,11 +26,15 @@ module.exports = { red: { 100: '#cb6060', 200: '#af4848', + 300: '#db4242', }, yellow: { 100: '#feebc8', 200: '#f0d6a6', }, + green: { + 400: '#4ec362', + }, }, extend: { inset: {