\n
\n
\n
\n
\n \n \n {worlds.map((worldItem, index) => {\n const worldName = t(`worlds.${worldItem.Id}.title`);\n return (\n \n );\n })}\n \n { t => (\n \n )}\n \n\n
\n \n \n
\n );\n }\n\n componentWillUnmount() {\n this.isActive = false;\n }\n\n /******************************************\n * COMPONENT HANDLERS\n ******************************************/\n\n handlerContinueClick() {\n AppSoundManager.playButtonSound();\n const lvl = getLastVisitedLevel();\n if (lvl) {\n this.props.history.push(`/game/level/${lvl.worldId}/${lvl.packId}/${lvl.levelId}`);\n return;\n }\n }\n\n handlerNewGameClick() {\n // New Game - появляется один раз. При клике - кидаем на туториал.\n AppSoundManager.playButtonSound();\n this.props.history.push('/tutorial');\n }\n\n handlerTutorialClick() {\n AppSoundManager.playButtonSound();\n this.props.history.push('/tutorial');\n }\n\n handlerChooseLevelClick() {\n // (показывется вместо new game после первого запуска)\n AppSoundManager.playButtonSound();\n const firstWorldId = get(this.props.worlds, '[0].Id', 1);\n this.props.history.push(`/world/${firstWorldId}`);\n }\n\n handlerWorldClick(worldId) {\n sendAnalyticsEvent('worldClick', withCount({\n worldId: composeAnalyticsEntityId(worldId)\n }));\n AppSoundManager.playButtonSound();\n this.props.history.push(`/world/${worldId}`);\n }\n\n}\n\nexport default withTracker(withNamespaces()(withRouter(connect(mapStateToProps)(LandingPage))));\n","import LandingPage from './LandingPage';\n\nexport default LandingPage;\n","import PageTitle from './PageTitle';\n\nexport default PageTitle;\n","/* eslint react/jsx-pascal-case: [0, { ignore: 1 }] */\n\nimport React from 'react';\nimport classNames from 'classnames';\n\n// styles\nimport styles from './PageTitle.module.css';\nimport 'shared/styles/header.css';\n\n\nclass PageTitle extends React.PureComponent {\n\n /******************************************\n * COMPONENT HOOKS\n ******************************************/\n\n render() {\n const { className, children } = this.props;\n\n const titleClasses = classNames({\n [styles.PageTitle]: true,\n [className]: !!className,\n });\n\n return (\n
{children}
\n );\n }\n}\n\nexport default PageTitle;","import StarsCounter from './StarsCounter';\n\nexport default StarsCounter;","import React from 'react';\nimport classNames from 'classnames';\n\n// styles\nimport styles from './StarsCounter.module.css';\n\n\nclass StarsCounter extends React.PureComponent {\n\n\trender() {\n\t\tconst counterClasses = classNames({\n\t\t [styles.StarsCounter]: true,\n\t\t [this.props.className]: !!this.props.className,\n\t\t});\n\t\treturn (\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t{this.props.currentStarsNumber}\n\t\t\t\t\t/\n\t\t\t\t\t{this.props.maxStarsNumber}\n\t\t\t\t
\n\t\t);\n\t}\n\n}\n\nexport default StarsCounter;","export const windowInnerHeight = window.innerHeight;\nexport const windowInnerWidth = window.innerWidth;","/* eslint react/jsx-pascal-case: [0, { ignore: 1 }] */\n\nimport React, { Component } from 'react';\nimport { connect } from 'react-redux';\nimport { withRouter } from 'react-router';\nimport classNames from 'classnames';\nimport get from 'lodash/get';\nimport { Scrollbars } from 'react-custom-scrollbars';\nimport { withNamespaces } from 'react-i18next';\n\n// components\nimport Stars from 'shared/components/Stars';\nimport PageTitle from 'shared/components/PageTitle';\nimport StarsCounter from 'shared/components/StarsCounter';\nimport PageHeaderManager from 'shared/containers/PageHeaderManager';\nimport ScrollbarHeightAdjuster from 'shared/components/ScrollbarHeightAdjuster';\nimport withTracker from 'shared/components/withTracker';\n\n// constants\nimport cssEnvVars from 'shared/cssEnvironmentVariables';\n\n// helpers\nimport { hasPackAccess, hasLevelAccess, getAchievedStarsAmount, } from 'shared/helpers/progress';\nimport { windowInnerWidth } from 'shared/helpers/resize';\n\nimport AppSoundManager from 'shared/modules/AppSoundManager';\n\n// styles\nimport styles from './LevelsPage.module.css';\n\n// redux\nimport { getPackWithLevelsSelector } from 'redux/selectors/worlds';\nimport {\n getAchievedStarsInPack, getAllAchievedStars, getLevelProgress\n} from 'redux/selectors/progress';\n\n\nconst mapStateToProps = (state, props) => ({\n progress: state.progress.progress,\n pack: getPackWithLevelsSelector(state, props),\n achievedStars: state.progress.achievedStars,\n});\n\nclass LevelsPage extends Component {\n\n constructor(props) {\n super(props);\n this.isActive = true;\n }\n\n get worldId() {\n return +get(this.props.match.params, 'worldId');\n }\n\n get packId() {\n return +get(this.props.match.params, 'packId');\n }\n\n /******************************************\n * COMPONENT HOOKS\n ******************************************/\n\n render() {\n const { t, pack, achievedStars, progress } = this.props;\n const packName = t(`worlds.${this.worldId}.packs.${this.packId}`);\n\n return (\n
\n
\n {!!pack.Levels.length && \n
\n }\n
\n );\n }\n\n componentDidMount() {\n const { pack, achievedStars } = this.props;\n if (!this.isActive) return;\n if (!this.props.pack) return this.goPageBack();\n // user must achieve stars more or equal than pack requires\n if (!hasPackAccess(pack.StarsRequired, getAllAchievedStars(achievedStars))) {\n this.goPageBack();\n return;\n }\n }\n\n componentWillUnmount() {\n this.isActive = false;\n }\n\n /******************************************\n * COMPONENT HANDLERS\n ******************************************/\n \n handlerLevelClick(levelId, isLevelLocked) {\n if (isLevelLocked) return;\n AppSoundManager.playButtonSound();\n if (this.worldId) {\n this.props.history.push(`/game/level/${this.worldId}/${this.packId}/${levelId}`);\n }\n }\n\n handlerBackClick = () => {\n AppSoundManager.playButtonSound();\n if (this.worldId) {\n this.goPageBack();\n } else {\n this.props.history.push('/', {routeBack: true});\n }\n }\n\n /******************************************\n * COMPONENT METHODS\n ******************************************/\n\n goPageBack() {\n this.props.history.push(`/world/${this.worldId}`, {routeBack: true});\n }\n}\n\nexport default withTracker(withNamespaces()(withRouter(connect(mapStateToProps, null)(LevelsPage))));\n","import LevelsPage from './LevelsPage';\n\nexport default LevelsPage;\n","import React, { Component } from 'react';\nimport { connect } from 'react-redux';\nimport { withRouter } from 'react-router';\nimport get from 'lodash/get';\nimport { Scrollbars } from 'react-custom-scrollbars';\nimport { withNamespaces } from 'react-i18next';\n\nimport { hasPackAccess } from 'shared/helpers/progress';\n\n// modules\nimport { sendAnalyticsEvent, composeAnalyticsEntityId, withCount } from 'shared/modules/AnalyticsEvents';\n\n// components\nimport PageTitle from 'shared/components/PageTitle';\nimport StarsCounter from 'shared/components/StarsCounter';\nimport AppSoundManager from 'shared/modules/AppSoundManager';\nimport CustomListItem from 'shared/components/CustomListItem';\nimport PageHeaderManager from 'shared/containers/PageHeaderManager';\nimport ScrollbarHeightAdjuster from 'shared/components/ScrollbarHeightAdjuster';\nimport withTracker from 'shared/components/withTracker';\n\n// constants\nimport cssEnvVars from 'shared/cssEnvironmentVariables';\n\n// redux\nimport { getWorldPacksSelector } from 'redux/selectors/worlds';\nimport {\n\tgetAchievedStarsInWorld, getAchievedStarsInPack, getAllAchievedStars\n} from 'redux/selectors/progress';\nimport { getMaxStarsInWorld } from 'redux/selectors/worlds';\n\n// styles\nimport styles from './WorldPage.module.css';\n\n\nconst mapStateToProps = (state, props) => ({\n\tworldsMaxStars: state.worlds.worldsMaxStars,\n\tpacks: getWorldPacksSelector(state, props),\n\tachievedStars: state.progress.achievedStars,\n});\n\nclass WorldPage extends Component {\n\tconstructor(props) {\n\t\tsuper(props);\n\t\tthis.isActive = true;\n\t}\n\n\tget worldId() {\n\t return +get(this.props.match.params, 'worldId');\n\t}\n\n\t/******************************************\n\t* COMPONENT HOOKS\n\t******************************************/\n\n\trender() {\n\t\tconst { t, achievedStars, worldsMaxStars, packs } = this.props;\n\n\t\treturn (\n\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t
{t(`worlds.${this.worldId}.title`)}\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{packs.map((packItem, index) => {\n\t\t\t\t\t\t\t\t\tconst isPackBlocked = !hasPackAccess(packItem.StarsRequired, getAllAchievedStars(achievedStars));\n\t\t\t\t\t\t\t\t\tconst packName = t(`worlds.${this.worldId}.packs.${packItem.Id}`);\n\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t);\n\t}\n\n\tcomponentWillUnmount() {\n\t\tthis.isActive = false;\n\t}\n\n\t/******************************************\n\t* COMPONENT HANDLERS\n\t******************************************/\n\n\thandlerPackClick(packId) {\n\t\tconst { worldId } = this;\n\t\tsendAnalyticsEvent('packClick', withCount({ \n\t\t\tpackId: composeAnalyticsEntityId(worldId, packId) \n\t\t}));\n\t\tAppSoundManager.playButtonSound();\n\t\tthis.props.history.push(`/world/${worldId}/pack/${packId}`);\n\t}\n\n\thandlerBackClick = () => {\n\t\tAppSoundManager.playButtonSound();\n\t\tthis.props.history.push(`/`, {routeBack: true});\n\t}\n}\n\nexport default withTracker(withNamespaces()(withRouter(connect(mapStateToProps, null)(WorldPage))));","import WorldPage from './WorldPage';\n\nexport default WorldPage;","import React from 'react';\nimport { connect } from 'react-redux';\nimport { Switch, Route, Redirect } from 'react-router-dom';\nimport { withRouter, matchPath } from 'react-router';\nimport {\n CSSTransition,\n TransitionGroup,\n} from 'react-transition-group';\nimport get from 'lodash/get';\nimport defer from 'lodash/defer';\nimport some from 'lodash/some';\nimport upperCase from 'lodash/upperCase';\nimport { setAutoFreeze } from 'immer';\nimport 'fuckadblock';\nimport classNames from 'classnames';\nimport loadable from '@loadable/component';\nimport Div100vh from 'react-div-100vh';\n\nimport i18n from 'shared/i18n';\nimport config from 'shared/config';\n\n// components\nimport Header from 'containers/Header';\nimport Footer from 'components/Footer';\n\nimport Preloader from 'shared/components/Preloader';\nimport OKAndroidManager from 'shared/containers/OKAndroidManager';\nimport PageWrap from 'shared/components/PageWrap';\nimport Tutorial from 'shared/containers/Tutorial';\nimport ModalManager from 'shared/containers/ModalManager';\n\n// constants\nimport { SUCCESS, PAGE_ANIMATION_TIME } from 'shared/constants';\n\n// helpers\nimport { detectTabVisibility, tabVisibility$ } from 'shared/helpers/tabVisibility';\nimport { getLastVisitedLevel } from 'shared/helpers/progress';\nimport { initAdBlockDetector } from 'shared/helpers/adBlockDetector';\nimport { setLang } from 'shared/helpers/locales';\nimport 'shared/helpers/arrayIncludesPolyfill';\nimport { getAnalyticsID, isAnalyticsEnabled, sendRouteMetric } from 'shared/helpers/analytics';\n\n// modules\nimport AppSoundManager from 'shared/modules/AppSoundManager';\nimport Auth from 'shared/modules/Auth';\nimport TutorialManager from 'shared/modules/TutorialManager';\nimport Advertizer from 'shared/adv/Advertizer';\nimport loadSDK from 'shared/sdk/index';\n\n// redux\nimport { getSettings, setSettings, fetchSettingsTutorial } from 'redux/actions/settings';\nimport { getLives } from 'redux/actions/lives';\nimport { getBoosters } from 'redux/actions/boosters';\nimport { getProgress, calcAchievedStarsAsync } from 'redux/actions/progress';\nimport { getWorlds } from 'redux/actions/worlds';\nimport { checkSecondSession } from 'redux/actions/secondSession';\nimport { setInfiniteBoosterHint, setInfiniteBoosterMoveBack } from 'redux/actions/boosters';\n\nimport styles from './App.module.css';\nimport 'shared/styles/animation.css';\n\nimport LandingPage from 'containers/LandingPage';\nimport LevelsPage from 'containers/LevelsPage';\nimport WorldPage from 'containers/WorldPage';\nimport { createUUID } from 'shared/helpers/user';\n\n// libs lazy loading\nconst ReactYMLoadable = loadable.lib(() => import('react-yandex-metrika'));\n\n// pages lazy loading\nconst PlaygroundPage = loadable(() => import('containers/PlaygroundPage'));\n\n// components lazy loading\nconst CheatPanel = loadable(() => import('shared/containers/CheatPanel'))\n\n// Stub for testing\n// Uncomment for testing android app emulation\n// window.OKApp = {\n// suggestDesktopIcon: () => {\n// console.log('window.OKApp.suggestDesktopIcon() was called');\n// },\n// };\n\nconst mapStateToProps = state => ({\n isAuth: state.user.isAuth,\n isDeveloper: state.user.developer,\n language: state.settings.language,\n progressFetched: state.progress.isGetWorldsProgressSuccess,\n isAchievedStarsCalculated: state.progress.isAchievedStarsCalculated,\n worldsFetched: state.worlds.isGetWorldsSuccess,\n isTutorialLoad: !!state.settings.tutorialProgress\n});\n\nconst mapDispatchToProps = dispatch => ({\n fetchSettings: () => dispatch(getSettings()),\n fetchSettingsTutorial: () => dispatch(fetchSettingsTutorial()),\n getProgress: () => dispatch(getProgress()),\n calcAchievedStarsAsync: () => dispatch(calcAchievedStarsAsync()),\n getWorlds: () => dispatch(getWorlds()),\n setSettingsLanguage: language => dispatch(setSettings({ language })),\n fetchLives: () => dispatch(getLives()),\n fetchBoosters: () => dispatch(getBoosters()),\n checkSecondSession: () => dispatch(checkSecondSession()),\n setInfiniteBoosters: () => {\n dispatch(setInfiniteBoosterHint(true))\n dispatch(setInfiniteBoosterMoveBack(true))\n },\n});\n\nclass App extends React.PureComponent {\n\n constructor(props) {\n super(props);\n // https://github.com/mweststrate/immer#auto-freezing\n setAutoFreeze(false);\n // set pages paths on which footer must be hidden\n this.hiddenFooterPaths = [\n { path: '/game/level/:worldId/:packId/:levelId', exact: true }\n ];\n }\n\n get isAndroidOkApp() {\n return (process.env.REACT_APP_SOCIAL_MEDIA === 'ok') && get(window, 'OKApp.suggestDesktopIcon');\n }\n\n /******************************************\n * COMPONENT HOOKS\n ******************************************/\n\n render() {\n const { location, progressFetched, worldsFetched, isAchievedStarsCalculated, isDeveloper } = this.props;\n // For animating route back different way add {routeBack: true} state to the history.push or to the
\n const transitionClassName = get(location, 'state.routeBack') ? 'page-back' : 'page-forward';\n const hideFooter = some(this.hiddenFooterPaths, (item) => matchPath(location.pathname, item));\n\n // Show preloader until progress, worlds data are fetched and achievedStars are calculated\n // Thus all pages will have these data before rendering\n const isLoading = !(progressFetched && worldsFetched && isAchievedStarsCalculated);\n\n return (\n
\n {isLoading ? (\n \n ) : (\n \n \n\n {/* Header */}\n \n\n {/* Page content */}\n \n
React.cloneElement(\n child,\n { classNames: transitionClassName }\n )} >\n \n \n (\n \n )} />\n {\n return ();\n }} />\n {\n return ();\n }} />\n {\n return ();\n }} />\n (\n \n )} />\n (\n \n )} />\n (\n \n )} />\n \n \n \n
\n\n {/* Footer */}\n \n\n {/* Purchase Manager */}\n \n\n {/* Cheat Panel */}\n {isDeveloper && >} className={styles.CheatPanel}>}\n { this.isAndroidOkApp && }\n \n )}\n {isAnalyticsEnabled && (\n \n {({ YMInitializer }) => (\n \n )}\n \n )}\n \n );\n }\n\n componentDidMount() {\n sendRouteMetric(get(this.props, 'history.location.pathname'));\n\n if (this.props.isAuth) {\n this.init();\n }\n\n if (process.env.REACT_APP_SOCIAL_MEDIA === 'ok') {\n this.initSdkOk();\n } else {\n Auth.authUser(createUUID());\n }\n\n if (process.env.REACT_APP_SHOW_ADV) {\n defer(() => {\n Advertizer.init();\n });\n }\n initAdBlockDetector();\n\n if (process.env.REACT_APP_INFINITE_BOOSTERS === 'true') {\n this.props.setInfiniteBoosters()\n }\n }\n\n componentDidUpdate(prevProps) {\n if (this.props.isAuth && !prevProps.isAuth) {\n this.init();\n\n // fetch worlds data and user progress after authorization\n Promise.all([this.props.getProgress(), this.props.getWorlds()])\n .then((result) => {\n // calculate and store achieved stars in worlds, packs\n this.props.calcAchievedStarsAsync();\n });\n }\n\n if (process.env.REACT_APP_TUTORIAL === 'true' && this.isAllowToRedirectActions(prevProps)) {\n // check tutorial only after worlds data is fetched - to get available level to redirect\n const levelToRedirect = TutorialManager.getLevelForRedirect(this.props.location.pathname);\n\n if (levelToRedirect) {\n this.props.history.push(`/game/level/${levelToRedirect.worldId}/${levelToRedirect.packId}/${levelToRedirect.levelId}`);\n }\n }\n\n if (prevProps.location !== this.props.location) {\n sendRouteMetric(get(this.props, 'location.pathname'), get(prevProps, 'location.pathname'));\n }\n }\n\n isAllowToRedirectActions(prevProps) {\n const { worldsFetched, isTutorialLoad } = this.props;\n if (worldsFetched && isTutorialLoad) {\n return !prevProps.worldsFetched || isTutorialLoad !== prevProps.isTutorialLoad;\n }\n return false;\n }\n\n /******************************************\n * COMPONENT METHODS\n ******************************************/\n\n init() {\n this.updateSettings();\n this.props.fetchLives();\n this.props.fetchBoosters();\n this.props.checkSecondSession();\n }\n\n updateSettings() {\n // fetch settings\n Promise.all([\n this.props.fetchSettings(),\n this.props.fetchSettingsTutorial()\n ]).then(() => {\n // add tab visibility event listeners\n detectTabVisibility();\n\n AppSoundManager.init(!!getLastVisitedLevel());\n\n // subscribe to tab visibility change\n tabVisibility$.subscribe((isTabVisible) => {\n if (isTabVisible) {\n AppSoundManager.startBackgroundMusic();\n } else {\n AppSoundManager.pauseBackgroundMusic();\n }\n });\n\n // init language\n if (this.props.language) {\n setLang(this.props.language);\n } else {\n this.props.setSettingsLanguage(i18n.language);\n }\n });\n }\n\n initSdkOk() {\n loadSDK().then(SdkOk =>\n SdkOk.default.init()\n .then(({ status, okParameters }) => {\n if (status === SUCCESS) {\n Auth.authUser(okParameters.logged_user_id);\n } else {\n return Promise.reject(new Error(`invalid status=${status}`));\n }\n })\n ).catch(() => {\n Auth.authUser(config.ok.logged_user_id_test);\n })\n\n }\n}\n\nexport default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));\n","import React from 'react'\nimport ReactDOM from 'react-dom'\nimport 'reset-css';\nimport 'index.css';\nimport { Router } from 'react-router-dom'\n// import registerServiceWorker from './registerServiceWorker';\nimport { unregister } from './registerServiceWorker';\nimport { I18nextProvider } from 'react-i18next';\nimport { Provider } from 'react-redux';\nimport { createBrowserHistory } from 'history';\n\nimport App from './App'\nimport i18n from 'shared/i18n'\nimport store from 'redux/store';\n\nimport 'shared/styles/fonts.css';\n\nexport default function() {\n const rootEl = document.getElementById('root');\n const history = createBrowserHistory();\n\n ReactDOM.render((\n
\n \n \n \n \n \n \n ), rootEl);\n\n unregister();\n}\n// window.store = store;\n","import 'react-app-polyfill/ie11';\nimport loadable from '@loadable/component';\nimport main from './main';\n\nconst polyfillStable = loadable.lib(() => import('react-app-polyfill/stable'));\n\nfunction browserSupportsFeatures() {\n return !!(\n Array.of\n && Array.prototype.find\n && Array.prototype.flatMap\n && Array.prototype.values\n && String.prototype.endsWith\n && String.prototype.padStart\n && String.prototype.repeat\n && Object.entries\n )\n}\n\nif (browserSupportsFeatures()) {\n main();\n} else {\n polyfillStable.load().then(main);\n}","// extracted by mini-css-extract-plugin\nmodule.exports = {\"cTradewind\":\"#6BBCB4\",\"cWhite\":\"#FFFFFF\",\"Hint\":\"Hint_Hint__3nGsC\",\"HintWrap\":\"Hint_HintWrap__mNAdS\",\"TextWrap\":\"Hint_TextWrap__gdV-h\",\"Visible\":\"Hint_Visible__SiBhZ\",\"Text\":\"Hint_Text__1qGhe\",\"Btn\":\"Hint_Btn__1VTpJ\",\"Hidden\":\"Hint_Hidden__3yeEp\",\"SkipBtn\":\"Hint_SkipBtn__2HsyR Hint_Btn__1VTpJ\",\"SkipBtnPosition\":\"Hint_SkipBtnPosition__3MV2T\"};","import {\n GET_LIVES_REQUEST,\n GET_LIVES_SUCCESS,\n GET_LIVES_FAILURE,\n RESET_LIVES_NUMBER,\n SET_NEW_LIFE_TIMER,\n CLEAR_NEW_LIFE_TIMER\n} from 'redux/actionTypes';\nimport * as livesAPI from 'shared/api/livesAPI';\nimport { Dispatch } from 'redux';\n\nexport const getLivesRequest = () => ({\n type: GET_LIVES_REQUEST\n});\n\nexport const getLivesSuccess = (lives: livesAPI.IPutLivesParams) => ({\n type: GET_LIVES_SUCCESS,\n lives\n});\n\nexport const getLivesFailure = () => ({\n type: GET_LIVES_FAILURE\n});\n\nexport const resetLivesNumber = () => ({\n type: RESET_LIVES_NUMBER\n});\n\nexport const setNewLifeTimer = () => ({\n type: SET_NEW_LIFE_TIMER\n});\n\nexport const clearNewLifeTimer = () => ({\n type: CLEAR_NEW_LIFE_TIMER\n});\n\n/* ASYNC ACTION CREATORS */\n\nexport function getLivesPromise(promise: Promise
) {\n return function(dispatch: Dispatch, _?: () => any) {\n dispatch(getLivesRequest());\n return promise.then(\n lives => {\n dispatch(getLivesSuccess(lives));\n },\n () => {\n dispatch(getLivesFailure());\n }\n );\n };\n}\n\nexport const addLives = () => getLivesPromise(livesAPI.increaseLivesNumber());\nexport const removeLife = () => getLivesPromise(livesAPI.deleteLivesNumber());\nexport const getLives = () => getLivesPromise(livesAPI.fetchLives());\n// <-- METHODS ONLY FOR DEVELOPERS ROLES\n\nexport function addLivesDeveloper() {\n return (dispatch: Dispatch, getState: () => any) => {\n const lives = getState().lives as livesAPI.IPutLivesParams;\n let livesNumber = lives.livesNumber + 1;\n if (livesNumber > lives.maxLivesNumber) {\n livesNumber = lives.maxLivesNumber;\n }\n putLives({ livesNumber })(dispatch, getState);\n };\n}\n\nexport function removeLivesDeveloper() {\n return (dispatch: Dispatch, getState: () => any) => {\n const lives = getState().lives as livesAPI.IPutLivesParams;\n let livesNumber = lives.livesNumber - 1;\n if (livesNumber < 0) {\n livesNumber = 0;\n }\n putLives({ livesNumber })(dispatch, getState);\n };\n}\n\nexport function putLives(lives: Partial) {\n return function(dispatch: Dispatch, getState: () => any) {\n const request = getState().user.developer\n ? livesAPI.putLives(lives)\n : Promise.reject(new Error('This API available only for developers'));\n\n return getLivesPromise(request)(dispatch, getState);\n };\n}\n\n// METHODS ONLY FOR DEVELOPERS ROLES -->\n","import {\n HEADER_RESET, HEADER_TOGGLE_SETTINGS, HEADER_TOGGLE_BACK_BTN,\n HEADER_TOGGLE_UPDATE_BTN, HEADER_TOGGLE_USER_LIFES, HEADER_TOGGLE_TITLE,\n HEADER_BACK_BTN_CLICK, HEADER_UPDATE_BTN_CLICK,\n} from '../../redux/actionTypes';\n\n\nexport const reset = () => ({\n type: HEADER_RESET,\n});\n\nexport const toggleSettings = (toggle: boolean) => ({\n type: HEADER_TOGGLE_SETTINGS,\n toggle,\n});\n\nexport const toggleBackBtn = (toggle: boolean) => ({\n type: HEADER_TOGGLE_BACK_BTN,\n toggle,\n});\n\nexport const toggleUpdateBtn = (toggle: boolean) => ({\n type: HEADER_TOGGLE_UPDATE_BTN,\n toggle,\n});\n\nexport const toggleUserLives = (toggle: boolean) => ({\n type: HEADER_TOGGLE_USER_LIFES,\n toggle,\n});\n\nexport const toggleTitle = (toggle: boolean, title: string) => ({\n type: HEADER_TOGGLE_TITLE,\n toggle,\n title,\n});\n\nexport const backBtnClick = (backBtnClick: boolean) => ({\n type: HEADER_BACK_BTN_CLICK,\n backBtnClick,\n});\n\nexport const updateBtnClick = (updateBtnClick: boolean) => ({\n type: HEADER_UPDATE_BTN_CLICK,\n updateBtnClick,\n});\n","// extracted by mini-css-extract-plugin\nmodule.exports = {\"cTradewind\":\"#6BBCB4\",\"pageTopPadding\":\"65px\",\"headerHorizontalOffset\":\"20px\",\"App\":\"App_App__16ZpL\",\"PageWrap\":\"App_PageWrap__2a8e6\",\"Header\":\"App_Header__ZS1m2\",\"Footer\":\"App_Footer__XLEFd\",\"OK\":\"App_OK__2NFXZ\",\"FooterHidden\":\"App_FooterHidden__tK5MW\",\"CheatPanel\":\"App_CheatPanel__3-GA5\",\"PreloaderWrap\":\"App_PreloaderWrap__3rdGY\",\"Tutorial\":\"App_Tutorial__1Qmgx\"};","import {\n HINT_AI_BEST_VERTEX, HINT_NOT_BEST_VERTEX,\n HINT_GENERAL, HINT_AI_GENERAL,\n HINT_TEXT_BOTTOM, HINT_TEXT_TOP,\n BTN_SKIP_TOP,\n HINT_BOOSTER_HINT, HINT_BOOSTER_MOVE_BACK, HINT_TEXT_STEPS_REGEXP,\n} from 'shared/constants';\n\n\nexport const tutorialConfig = {\n // worldId = 1, packId = 1, levelId = 3\n '100-100-1': {\n // tutorial hints on this level\n // they goes one by one\n tutorialHints: [{\n type: HINT_GENERAL,\n delay: 2000, //delay before this hint appears\n text: {\n ru: 'Цель: все числа в кругах должны быть больше либо равны нулю',\n en: 'Goal: all numbers in circles should be more or equal zero',\n },\n textPosition: HINT_TEXT_BOTTOM,\n button: { // if hint object has button click on it move user to the next hint, if not - click on circle area\n ru: 'Понятно!',\n en: 'Got it!',\n },\n // deltaY: -63, // looks like this is not needed in the web version\n deltaCircleR: -10,\n }, {\n type: HINT_GENERAL,\n text: {\n ru: 'Ты можешь изменить баланс если нажмёшь на этот круг',\n en: 'You can change the balance if you click on this circle',\n },\n textPosition: HINT_TEXT_BOTTOM,\n button: { // if hint object has button click on it move user to the next hint, if not - click on circle area\n ru: 'Понятно!',\n en: 'Got it!',\n },\n // deltaY: -63, // looks like this is not needed in the web version\n deltaCircleR: -10,\n }],\n isInfiniteSteps: true,\n isMainTutorialLevel: true,\n },\n // worldId = 1, packId = 1, levelId = 4\n /* '10-10-2': {\n // tutorial hints on this level\n // they goes one by one\n tutorialHints: [{\n type: HINT_GENERAL,\n delay: 2000, //delay before this hint appears\n text: {\n ru: 'Задача усложняется? Но ты уже знаешь что делать!',\n en: 'The task gets harder, but you know what to do!',\n },\n textPosition: HINT_TEXT_BOTTOM,\n button: { // if hint object has button click on it move user to the next hint, if not - click on circle area\n ru: 'Понятно!',\n en: 'Got it!',\n },\n deltaCircleR: -40,\n }],\n waitHint: {\n time: 5000,\n text: {\n ru: 'Ты можешь изменить баланс если нажмёшь на любой круг',\n en: 'You can change the balance if you click on any circle',\n },\n },\n isInfiniteSteps: true,\n isMainTutorialLevel: true,\n },\n // worldId = 1, packId = 1, levelId = 5\n '10-10-3': {\n // there're no tutorial hints on this level\n waitHint: {\n time: 10000,\n text: {\n ru: 'Ты можешь изменить баланс если нажмёшь на любой круг',\n en: 'You can change the balance if you click on any circle',\n },\n },\n isInfiniteSteps: true,\n isMainTutorialLevel: true,\n }, */\n // worldId = 1, packId = 1, levelId = 10\n '100-100-2': {\n tutorialHints: [{\n type: HINT_BOOSTER_HINT,\n delay: 2000, //delay before this hint appears\n text: {\n ru: 'Давай попросим Искусственный Интеллект решить эту задачу? Нажми на лампочку!',\n en: 'Let’s ask the Artificial Intelligence to solve this task! Click on this bulb!',\n },\n textPosition: HINT_TEXT_TOP,\n btnSkipPosition: BTN_SKIP_TOP,\n isInfiniteBoosterHint: true,\n }, {\n type: HINT_AI_BEST_VERTEX,\n text: {\n ru: `ИИ: Нажмите сюда, если хотите решить задачу за ${HINT_TEXT_STEPS_REGEXP}`,\n en: `AI: Click here if you want to solve this task in ${HINT_TEXT_STEPS_REGEXP}`,\n },\n textPosition: HINT_TEXT_BOTTOM,\n btnSkipPosition: BTN_SKIP_TOP,\n isInfiniteBoosterHint: true,\n }, {\n type: HINT_AI_BEST_VERTEX,\n text: {\n ru: `ИИ: Нажмите сюда, если хотите решить задачу за ${HINT_TEXT_STEPS_REGEXP}`,\n en: `AI: Click here if you want to solve this task in ${HINT_TEXT_STEPS_REGEXP}`,\n },\n textPosition: HINT_TEXT_BOTTOM,\n btnSkipPosition: BTN_SKIP_TOP,\n isInfiniteBoosterHint: true,\n }, {\n type: HINT_AI_BEST_VERTEX,\n text: {\n ru: `ИИ: Нажмите сюда, если хотите решить задачу за ${HINT_TEXT_STEPS_REGEXP}`,\n en: `AI: Click here if you want to solve this task in ${HINT_TEXT_STEPS_REGEXP}`,\n },\n textPosition: HINT_TEXT_BOTTOM,\n btnSkipPosition: BTN_SKIP_TOP,\n isInfiniteBoosterHint: true,\n }, {\n type: HINT_AI_GENERAL,\n text: {\n ru: `ИИ: До решения задачи осталось ${HINT_TEXT_STEPS_REGEXP}. Теперь вы сами решаете когда вам нужна подсказка.`,\n en: `AI: The task’ll be solved in ${HINT_TEXT_STEPS_REGEXP}. Now you can use hint whenewer you want.`,\n },\n deltaCircleR: -20,\n deltaY: 25,\n textPosition: HINT_TEXT_BOTTOM,\n btnSkipPosition: BTN_SKIP_TOP,\n button: { // if hint object has button click on it move user to the next hint, if not - click on circle area\n ru: 'Понятно!',\n en: 'Got it!',\n },\n isInfiniteBoosterHint: true,\n }],\n isInfiniteSteps: true,\n isMainTutorialLevel: true,\n },\n // worldId = 1, packId = 1, levelId = 2 0\n '100-100-3': {\n tutorialHints: [{\n type: HINT_GENERAL,\n delay: 2000, //delay before this hint appears\n text: {\n ru: 'Давай кое-что попробуем?',\n en: 'Let’s try something!',\n },\n textPosition: HINT_TEXT_BOTTOM,\n btnSkipPosition: BTN_SKIP_TOP,\n button: { // if hint object has button click on it move user to the next hint, if not - click on circle area\n ru: 'Давай!',\n en: 'Okay!',\n },\n // deltaCircleR: -40,\n isInfiniteBoosterMoveBack: true,\n }, {\n type: HINT_NOT_BEST_VERTEX,\n text: {\n ru: 'Нажми сюда!',\n en: 'Click here!',\n },\n textPosition: HINT_TEXT_BOTTOM,\n btnSkipPosition: BTN_SKIP_TOP,\n isInfiniteBoosterMoveBack: true,\n }, {\n type: HINT_GENERAL,\n text: {\n ru: 'А что, если тебе понадобится отменить ход? Сказать, как можно это сделать?',\n en: 'What if you want to cancel move? Do you want to know how to do it?',\n },\n textPosition: HINT_TEXT_BOTTOM,\n btnSkipPosition: BTN_SKIP_TOP,\n button: { // if hint object has button click on it move user to the next hint, if not - click on circle area\n ru: 'Давай!',\n en: 'Yep!',\n },\n isInfiniteBoosterMoveBack: true,\n }, {\n type: HINT_BOOSTER_MOVE_BACK,\n text: {\n ru: 'Ты можешь отменить ход, если захочешь! Нажми на эту стрелку!',\n en: 'You can cancel last move if you want! Click on this arrow!',\n },\n textPosition: HINT_TEXT_TOP,\n btnSkipPosition: BTN_SKIP_TOP,\n isInfiniteBoosterMoveBack: true,\n }, {\n type: HINT_GENERAL,\n text: {\n ru: 'Мы отменили последний ход! Если тебе нужно отменить неправильный ход, то ты уже знаешь что делать!',\n en: 'This move was canceled! If you want to cancel incorrect move you know what to do, right?',\n },\n textPosition: HINT_TEXT_BOTTOM,\n btnSkipPosition: BTN_SKIP_TOP,\n button: { // if hint object has button click on it move user to the next hint, if not - click on circle area\n ru: 'Да!',\n en: 'Yep!',\n },\n isInfiniteBoosterMoveBack: true,\n }],\n isInfiniteSteps: true,\n isMainTutorialLevel: true,\n },\n};\n","import isNil from 'lodash/isNil';\n\n/**\n * Returns restore minutes in milliseconds\n * @return {number} - minutes\n */\nexport const minToMs = (mins: number | string) => {\n if (typeof mins === 'number') {\n return mins * 1000 * 60;\n }\n if (typeof mins !== 'string') {\n return 0;\n }\n if (/^(\\d+):(\\d+)$/.exec(mins)) {\n const m = +RegExp.$1;\n const s = +RegExp.$2;\n return (m * 60 + s) * 1000;\n }\n const v = parseInt(mins, 10);\n if (Number.isNaN(v)) {\n return 0;\n }\n return v * 1000 * 60;\n};\n\n/**\n * Gets minutes amount since new life timer started\n * @return {number} - minutes\n */\nexport const getMinutesPassed = (newLifeTimer: number | null): number => {\n if (isNil(newLifeTimer)) return 0;\n const now = Date.now();\n const diff = Math.abs(now - newLifeTimer);\n return diff / 1000 / 60;\n};\n\n/**\n * Gets restored lives number\n * For example user with 1 life stops the game and gets back in 60min\n * So we should add to user 60min/20min = 3 lives\n * @return {number} - restored lives number\n */\nexport const getRestoredLivesNumber = (\n newLifeTimer: number | null,\n lifeRestoreMinutes: number\n): number => {\n return Math.floor(getMinutesPassed(newLifeTimer) / lifeRestoreMinutes);\n};\n","import axiosInstance from 'shared/api/axiosInstance';\n\nexport const fetchSettings = () =>\n axiosInstance.get<{\n music: boolean,\n sounds: boolean,\n language: string\n }>('/settings')\n\nexport const putSettings = (settingsData: {\n music: boolean,\n sounds: boolean,\n language: string\n}) => {\n return axiosInstance.put('/settings', settingsData);\n};\n\nexport const fetchSettingsTutorial = () =>\n axiosInstance.get<{ tutorial: string[] }>('/settings/tutorial')\n //.then(p => ({ ...p, tutorial: [ '100-100-1','100-100-2','100-100-3']}))\n\nexport const putSettingsTutorial = (items: string[]) =>\n axiosInstance.put<{ tutorial: string[] }>('/settings/tutorial', { items })\n"],"sourceRoot":""}