diff --git a/README.md b/README.md index b3bb57e..17cec7a 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,13 @@ **PokéNurse** is a desktop application for Windows and Mac that allows you to manage your pokémon from Pokémon Go without the need for a mobile device. You can now favorite, transfer, and evolve from the comfort of your own home! -## Downloads for v1.4.0 +## Downloads for v1.5.0 You may view all the releases [here](https://github.com/vinnymac/PokeNurse/releases) -* [Mac OS X](https://github.com/vinnymac/PokeNurse/releases/download/v1.4.0/PokeNurse-darwin-x64.zip) -* [Windows 32 bit](https://github.com/vinnymac/PokeNurse/releases/download/v1.4.0/PokeNurse-win32-ia32.zip) -* [Windows 64 bit](https://github.com/vinnymac/PokeNurse/releases/download/v1.4.0/PokeNurse-win32-x64.zip) -* [Linux 32 bit](https://github.com/vinnymac/PokeNurse/releases/download/v1.4.0/PokeNurse-linux-ia32.zip) -* [Linux 64 bit](https://github.com/vinnymac/PokeNurse/releases/download/v1.4.0/PokeNurse-linux-x64.zip) +* [Mac OS X](https://github.com/vinnymac/PokeNurse/releases/download/v1.5.0/PokeNurse-darwin-x64.zip) +* [Windows 32 bit](https://github.com/vinnymac/PokeNurse/releases/download/v1.5.0/PokeNurse-win32-ia32.zip) +* [Windows 64 bit](https://github.com/vinnymac/PokeNurse/releases/download/v1.5.0/PokeNurse-win32-x64.zip) +* [Linux 32 bit](https://github.com/vinnymac/PokeNurse/releases/download/v1.5.0/PokeNurse-linux-ia32.zip) +* [Linux 64 bit](https://github.com/vinnymac/PokeNurse/releases/download/v1.5.0/PokeNurse-linux-x64.zip) ## Examples ![Login Window](app/loginExample.png) diff --git a/app/actions/authenticate.js b/app/actions/authenticate.js new file mode 100644 index 0000000..04b8e2b --- /dev/null +++ b/app/actions/authenticate.js @@ -0,0 +1,91 @@ +import path from 'path' +import pogobuf from 'pogobuf' +import { + remote +} from 'electron' +import { + createAction +} from 'redux-actions' + +import * as fs from 'async-file' + +import client from '../client' + +import { + getTrainerInfo, + getTrainerPokemon +} from './trainer' + +const saveAccountCredentialsFailed = createAction('SAVE_ACCOUNT_CREDENTIALS_FAILED') +const saveAccountCredentialsSuccess = createAction('SAVE_ACCOUNT_CREDENTIALS_SUCCESS') + +const checkAndDeleteCredentialsFailed = createAction('CHECK_AND_DELETE_CREDENTIALS_FAILED') +const checkAndDeleteCredentialsSuccess = createAction('CHECK_AND_DELETE_CREDENTIALS_SUCCESS') + +const userLoginStarted = createAction('USER_LOGIN_STARTED') +const userLoginSuccess = createAction('USER_LOGIN_SUCCESS') +const userLoginFailed = createAction('USER_LOGIN_FAILED') + +const accountPath = path.join(remote.app.getPath('appData'), '/pokenurse/account.json') + +export default { + client, + + login({ method, username, password }) { + return async (dispatch) => { + dispatch(userLoginStarted()) + + let login + if (method === 'google') { + login = new pogobuf.GoogleLogin() + } else { + login = new pogobuf.PTCLogin() + } + + try { + const token = await login.login(username, password) + + client.setAuthInfo(method, token) + client.init() + + // TODO display a loading spinner + // then fetch all necessary things + await Promise.all([ + dispatch(getTrainerInfo()), + dispatch(getTrainerPokemon()) + ]) + + dispatch(userLoginSuccess()) + } catch (error) { + console.error(error) // eslint-disable-line + dispatch(userLoginFailed({ error })) + } + } + }, + + logout: createAction('USER_LOGOUT'), + + checkAndDeleteCredentials() { + return async (dispatch) => { + try { + if (await fs.exists(accountPath)) { + await fs.unlink(accountPath) + } + dispatch(checkAndDeleteCredentialsSuccess()) + } catch (error) { + dispatch(checkAndDeleteCredentialsFailed()) + } + } + }, + + saveAccountCredentials(credentials) { + return async (dispatch) => { + try { + const response = await fs.writeFile(accountPath, JSON.stringify(credentials)) + dispatch(saveAccountCredentialsSuccess({ response, credentials })) + } catch (error) { + dispatch(saveAccountCredentialsFailed(error)) + } + } + } +} diff --git a/app/actions/index.js b/app/actions/index.js index 8571a4d..e5b76e6 100644 --- a/app/actions/index.js +++ b/app/actions/index.js @@ -1,5 +1,11 @@ import * as statusActions from './status' +import * as authenticateActions from './authenticate' +import * as trainerActions from './trainer' +import * as settingsActions from './settings' export default { - ...statusActions + ...statusActions, + ...authenticateActions, + ...trainerActions, + ...settingsActions, } diff --git a/app/actions/settings.js b/app/actions/settings.js new file mode 100644 index 0000000..ede5c2f --- /dev/null +++ b/app/actions/settings.js @@ -0,0 +1,13 @@ +import { + createAction +} from 'redux-actions' + +export default { + toggleShowSpeciesWithZeroPokemon: createAction('TOGGLE_SHOW_SPECIES_WITH_ZERO_POKEMON'), + toggleAutoLogin: createAction('TOGGLE_AUTO_LOGIN'), + resetAllSettings: createAction('RESET_ALL_SETTINGS'), + changeDefaultPokedexSortBy: createAction('CHANGE_DEFAULT_POKEDEX_SORT_BY'), + changeDefaultPokedexSortDirection: createAction('CHANGE_DEFAULT_POKEDEX_SORT_DIRECTION'), + changeDefaultSpecieSortBy: createAction('CHANGE_DEFAULT_SPECIE_SORT_BY'), + changeDefaultSpecieSortDirection: createAction('CHANGE_DEFAULT_SPECIE_SORT_DIRECTION'), +} diff --git a/app/actions/trainer.js b/app/actions/trainer.js new file mode 100644 index 0000000..59c8053 --- /dev/null +++ b/app/actions/trainer.js @@ -0,0 +1,289 @@ +import { + createAction +} from 'redux-actions' +import { + times, + keyBy, +} from 'lodash' +import pogobuf from 'pogobuf' +import POGOProtos from 'node-pogo-protos' + +import client from '../client' + +// TODO Must move these helpers to app folder +import utils from '../utils' +import baseStats from '../../baseStats' + +// Maybe put this info and the helper methods in utils? +const kantoDexCount = 151 + +function generateEmptySpecies(candies) { + const candiesByFamilyId = keyBy(candies, (candy) => String(candy.family_id)) + + return times(kantoDexCount, (i) => { + const pokemonDexNumber = String(i + 1) + const basePokemon = baseStats.pokemon[pokemonDexNumber] + + const candyByFamilyId = candiesByFamilyId[basePokemon.familyId] + const candy = candyByFamilyId ? candyByFamilyId.candy : 0 + + return { + candy, + pokemon_id: pokemonDexNumber, + name: basePokemon.name, + count: 0, + evolves: 0, + pokemon: [] + } + }) +} + +function parseInventory(inventory) { + const { player, candies, pokemon } = pogobuf.Utils.splitInventory(inventory) + + const speciesList = generateEmptySpecies(candies) + const eggList = [] + + // populates the speciesList with pokemon and counts + // populates the eggList with pokemon + pokemon.forEach(p => { + if (p.is_egg) { + eggList.push(p) + return + } + + let pokemonName = pogobuf.Utils.getEnumKeyByValue( + POGOProtos.Enums.PokemonId, + p.pokemon_id + ) + + pokemonName = pokemonName.replace('Female', '♀').replace('Male', '♂') + + const stats = baseStats.pokemon[p.pokemon_id] + + const totalCpMultiplier = p.cp_multiplier + p.additional_cp_multiplier + + const attack = stats.BaseAttack + p.individual_attack + const defense = stats.BaseDefense + p.individual_defense + const stamina = stats.BaseStamina + p.individual_stamina + + const maxCP = utils.getMaxCpForTrainerLevel(attack, defense, stamina, player.level) + const candyCost = utils.getCandyCostsForPowerup(totalCpMultiplier, p.num_upgrades) + const stardustCost = utils.getStardustCostsForPowerup(totalCpMultiplier, p.num_upgrades) + const candyMaxCost = utils.getMaxCandyCostsForPowerup( + player.level, + p.num_upgrades, + totalCpMultiplier + ) + + const stardustMaxCost = utils.getMaxStardustCostsForPowerup( + player.level, + p.num_upgrades, + totalCpMultiplier + ) + + const nextCP = utils.getCpAfterPowerup(p.cp, totalCpMultiplier) + + const iv = utils.getIVs(p) + + // TODO Use CamelCase instead of under_score for all keys except responses + const pokemonWithStats = { + iv, + cp: p.cp, + next_cp: nextCP, + max_cp: maxCP, + candy_cost: candyCost, + candy_max_cost: candyMaxCost, + stardust_cost: stardustCost, + stardust_max_cost: stardustMaxCost, + creation_time_ms: p.creation_time_ms.toString(), + deployed: p.deployed_fort_id !== '', + id: p.id.toString(), + attack: p.individual_attack, + defense: p.individual_defense, + stamina: p.individual_stamina, + current_stamina: p.stamina, + stamina_max: p.stamina_max, + pokemon_id: p.pokemon_id, + name: pokemonName, + height: p.height_m, + weight: p.weight_kg, + nickname: p.nickname || pokemonName, + // Multiply by -1 for sorting + favorite: p.favorite * -1, + move_1: p.move_1, + move_2: p.move_2 + } + + const speciesIndex = p.pokemon_id - 1 + + speciesList[speciesIndex].count += 1 + speciesList[speciesIndex].pokemon.push(pokemonWithStats) + }) + + // TODO use map + speciesList.forEach((s) => { + s.evolves = utils.getEvolvesCount(s) + }) + + return { + success: true, + species: speciesList, + eggs: eggList + } +} + +function sleep(time) { + return new Promise(r => setTimeout(r, time)) +} + +const getTrainerInfoSuccess = createAction('GET_TRAINER_INFO_SUCCESS') +const getTrainerInfoFailed = createAction('GET_TRAINER_INFO_FAILED') + +const getTrainerPokemonSuccess = createAction('GET_TRAINER_POKEMON_SUCCESS') +const getTrainerPokemonFailed = createAction('GET_TRAINER_POKEMON_FAILED') + +const toggleFavoritePokemonSuccess = createAction('TOGGLE_FAVORITE_POKEMON_SUCCESS') +const toggleFavoritePokemonFailed = createAction('TOGGLE_FAVORITE_POKEMON_FAILED') + +const powerUpPokemonSuccess = createAction('POWER_UP_POKEMON_SUCCESS') +const powerUpPokemonFailed = createAction('POWER_UP_POKEMON_FAILED') + +const renamePokemonSuccess = createAction('RENAME_POKEMON_SUCCESS') +const renamePokemonFailed = createAction('RENAME_POKEMON_FAILED') + +const transferPokemonSuccess = createAction('TRANSFER_POKEMON_SUCCESS') +const transferPokemonFailed = createAction('TRANSFER_POKEMON_FAILED') + +const evolvePokemonSuccess = createAction('EVOLVE_POKEMON_SUCCESS') +const evolvePokemonFailed = createAction('EVOLVE_POKEMON_FAILED') + +function getTrainerInfo() { + return async (dispatch) => { + try { + const response = await client.getPlayer() + + if (!response.success) { + dispatch(getTrainerInfoFailed('Failed in retrieving player info. Please restart.')) + return + } + dispatch(getTrainerInfoSuccess({ + trainerData: response.player_data + })) + } catch (error) { + dispatch(getTrainerInfoFailed(error)) + } + } +} + +function getTrainerPokemon() { + return async (dispatch) => { + try { + const inventory = await client.getInventory(0) + + if (!inventory.success) { + dispatch(getTrainerPokemonFailed('Failed to retrieve Trainers Pokemon')) + return + } + + const payload = parseInventory(inventory) + + dispatch(getTrainerPokemonSuccess(payload)) + } catch (error) { + dispatch(getTrainerPokemonFailed(error)) + } + } +} + +function powerUpPokemon(pokemon) { + return async (dispatch) => { + try { + await client.upgradePokemon(pokemon.id) + + // TODO parse the response instead of retrieving all the new pokemon + // Requires replacing the main parsing with more functional code + await dispatch(getTrainerPokemon()) + dispatch(powerUpPokemonSuccess(pokemon)) + } catch (error) { + dispatch(powerUpPokemonFailed(error)) + } + } +} + +function toggleFavoritePokemon(pokemon) { + return async (dispatch) => { + try { + // TODO Stop this -1 0 shit + const updatedPokemon = Object.assign({}, pokemon, { + favorite: !pokemon.favorite ? -1 : -0 + }) + + await client.setFavoritePokemon(pokemon.id, !!updatedPokemon.favorite) + dispatch(toggleFavoritePokemonSuccess(updatedPokemon)) + } catch (error) { + dispatch(toggleFavoritePokemonFailed(error)) + } + } +} + +function renamePokemon(pokemon, nickname, callback) { + const updatedPokemon = Object.assign({}, pokemon, { nickname }) + + return async (dispatch) => { + try { + await client.nicknamePokemon(updatedPokemon.id, updatedPokemon.nickname) + + dispatch(renamePokemonSuccess(updatedPokemon)) + + // modals are outside of the lifecycle of the table + // so we must inform it manually + // this would be fixed if we were using react modals + callback(updatedPokemon) + } catch (error) { + dispatch(renamePokemonFailed(error)) + } + } +} + +function transferPokemon(pokemon, delay) { + return async (dispatch) => { + try { + await sleep(delay) + await client.releasePokemon(pokemon.id) + dispatch(transferPokemonSuccess(pokemon)) + } catch (error) { + dispatch(transferPokemonFailed(error)) + } + } +} + +function evolvePokemon(pokemon, delay) { + return async (dispatch) => { + try { + await sleep(delay) + await client.evolvePokemon(pokemon.id) + dispatch(evolvePokemonSuccess(pokemon)) + } catch (error) { + dispatch(evolvePokemonFailed(error)) + } + } +} + +export default { + updateMonster: createAction('UPDATE_MONSTER'), + updateSpecies: createAction('UPDATE_SPECIES'), + updateMonsterSort: createAction('UPDATE_MONSTER_SORT'), + sortSpecies: createAction('SORT_SPECIES'), + checkPokemon: createAction('CHECK_POKEMON'), + checkAllBySpecies: createAction('CHECK_ALL_BY_SPECIES'), + collapseBySpecies: createAction('COLLAPSE_BY_SPECIES'), + sortAllSpecies: createAction('SORT_ALL_SPECIES'), + sortWithDefaults: createAction('SORT_WITH_DEFAULTS'), + getTrainerInfo, + getTrainerPokemon, + powerUpPokemon, + toggleFavoritePokemon, + renamePokemon, + transferPokemon, + evolvePokemon, +} diff --git a/app/client.js b/app/client.js new file mode 100644 index 0000000..7742893 --- /dev/null +++ b/app/client.js @@ -0,0 +1,3 @@ +import pogobuf from 'pogobuf' + +export default new pogobuf.Client() diff --git a/app/css/pokenurse.css b/app/css/pokenurse.css index d65b532..a771809 100644 --- a/app/css/pokenurse.css +++ b/app/css/pokenurse.css @@ -5,7 +5,7 @@ width: 14px; height: 14px; left: 15px; - top: 17px; + top: 19px; } /* Color/shape of burger icon bars */ @@ -48,6 +48,7 @@ .bm-menu .bm-item-list li a { padding: 5px 0px; + cursor: pointer; } .bm-menu .bm-item-list i { @@ -55,40 +56,6 @@ float: right; } -.switch { - border: 1px solid #ccc; - width: 50px; - height: 26px; - border-radius: 13px; - cursor: pointer; - display: inline-block; - float: right; -} - -.switch-toggle { - border: 1px solid #999; - box-shadow: 1px 1px 1px #ccc; - width: 25px; - height: 24px; - left: 0; - border-radius: 12px; - background: white; - position: relative; - transition: left .2s ease-in-out; -} - -.switch.on { - background: green; -} - -.switch.on .switch-toggle { - left: 23px; -} - -.switch.disabled { - cursor: not-allowed; -} - /* Morph shape necessary with bubble or elastic */ .bm-morph-shape { fill: #373a47; @@ -129,8 +96,8 @@ } .username { - padding-top: 15px; - margin-left: 15px; + padding-top: 17px; + margin-left: 40px; color: white; } @@ -608,7 +575,7 @@ fontSize: 18px; font-weight: bold; } -.modal-outline-white{ +#detailModal .modal-outline-white{ text-shadow: 1px 2px 0 #000, -1px -1px 0 #000, @@ -617,7 +584,7 @@ text-shadow: 1px 1px 0 #000; -webkit-text-fill-color: white; } -.modal-header{ +#detailModal .modal-header{ border-bottom: 0px solid; } @@ -925,6 +892,79 @@ table tbody tr.child:hover { } /*Input Styles*/ + +input#toggle { + display: block; + position: relative; + /* box-shadow: inset 0 0 0px 1px #d5d5d5; */ + border-width: 1.5px; + border-style: solid; + border-color: #d5d5d5; + box-sizing: border-box; + text-indent: -5000px; + height: 31px; + width: 51px; + border-radius: 15.5px; + -webkit-appearance: none; +} + +input#toggle:checked { + border-color: rgba(76,217,100,1); + -webkit-transition: .25s cubic-bezier(0.77, 0, 0.175, 1); +} + +/* background OFF */ + +input#toggle:before { + content: ""; + position: absolute; + display: block; + height: 29px; + width: 28px; + border-radius: 14px; + top: 0px; + left: 0px; + background: rgba(19,191,17,0); + box-sizing: border-box; + -webkit-transition: .25s cubic-bezier(0.77, 0, 0.175, 1); +} + +/* button OFF */ + +input#toggle:after { + content: ""; + position: absolute; + display: block; + height: 28px; + width: 28px; + top: 0px; + left: 0px; + border-radius: 14px; + background: white; + box-shadow: 0 0px 1px rgba(0,0,0,.4), 0 4px 2px rgba(0,0,0,.1); + -webkit-transition: .2s cubic-bezier(0.77, 0, 0.175, 1); +} + +/* background ON */ + +input#toggle:checked:before { + width: 48px; + background: rgb(76,217,100); /* Old browsers */ + background: -moz-linear-gradient(top, rgba(76,217,100,1) 0%, rgba(73,211,97,1) 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(76,217,100,1)), color-stop(100%,rgba(73,211,97,1))); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, rgba(76,217,100,1) 0%,rgba(73,211,97,1) 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, rgba(76,217,100,1) 0%,rgba(73,211,97,1) 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(top, rgba(76,217,100,1) 0%,rgba(73,211,97,1) 100%); /* IE10+ */ + background: linear-gradient(to bottom, rgba(76,217,100,1) 0%,rgba(73,211,97,1) 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4cd964', endColorstr='#49d361',GradientType=0 ); /* IE6-9 */ +} + +/* button ON */ + +input#toggle:checked:after { + left: 20.25px; +} + textarea:focus, input[type="text"]:focus, input[type="password"]:focus, @@ -940,6 +980,7 @@ input[type="url"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="color"]:focus, +input#toggle, .btn:focus, .btn:active:focus { border-color: rgba(0, 0, 0, 0.2); diff --git a/app/imgs/3d/bulbasaur.jpg b/app/imgs/3d/1.png similarity index 100% rename from app/imgs/3d/bulbasaur.jpg rename to app/imgs/3d/1.png diff --git a/app/imgs/3d/caterpie.jpg b/app/imgs/3d/10.png similarity index 100% rename from app/imgs/3d/caterpie.jpg rename to app/imgs/3d/10.png diff --git a/app/imgs/3d/voltorb.jpg b/app/imgs/3d/100.png similarity index 100% rename from app/imgs/3d/voltorb.jpg rename to app/imgs/3d/100.png diff --git a/app/imgs/3d/electrode.jpg b/app/imgs/3d/101.png similarity index 100% rename from app/imgs/3d/electrode.jpg rename to app/imgs/3d/101.png diff --git a/app/imgs/3d/exeggcute.jpg b/app/imgs/3d/102.png similarity index 100% rename from app/imgs/3d/exeggcute.jpg rename to app/imgs/3d/102.png diff --git a/app/imgs/3d/exeggutor.jpg b/app/imgs/3d/103.png similarity index 100% rename from app/imgs/3d/exeggutor.jpg rename to app/imgs/3d/103.png diff --git a/app/imgs/3d/cubone.jpg b/app/imgs/3d/104.png similarity index 100% rename from app/imgs/3d/cubone.jpg rename to app/imgs/3d/104.png diff --git a/app/imgs/3d/marowak.jpg b/app/imgs/3d/105.png similarity index 100% rename from app/imgs/3d/marowak.jpg rename to app/imgs/3d/105.png diff --git a/app/imgs/3d/hitmonlee.jpg b/app/imgs/3d/106.png similarity index 100% rename from app/imgs/3d/hitmonlee.jpg rename to app/imgs/3d/106.png diff --git a/app/imgs/3d/hitmonchan.jpg b/app/imgs/3d/107.png similarity index 100% rename from app/imgs/3d/hitmonchan.jpg rename to app/imgs/3d/107.png diff --git a/app/imgs/3d/lickitung.jpg b/app/imgs/3d/108.png similarity index 100% rename from app/imgs/3d/lickitung.jpg rename to app/imgs/3d/108.png diff --git a/app/imgs/3d/koffing.jpg b/app/imgs/3d/109.png similarity index 100% rename from app/imgs/3d/koffing.jpg rename to app/imgs/3d/109.png diff --git a/app/imgs/3d/metapod.jpg b/app/imgs/3d/11.png similarity index 100% rename from app/imgs/3d/metapod.jpg rename to app/imgs/3d/11.png diff --git a/app/imgs/3d/weezing.jpg b/app/imgs/3d/110.png similarity index 100% rename from app/imgs/3d/weezing.jpg rename to app/imgs/3d/110.png diff --git a/app/imgs/3d/rhyhorn.jpg b/app/imgs/3d/111.png similarity index 100% rename from app/imgs/3d/rhyhorn.jpg rename to app/imgs/3d/111.png diff --git a/app/imgs/3d/rhydon.jpg b/app/imgs/3d/112.png similarity index 100% rename from app/imgs/3d/rhydon.jpg rename to app/imgs/3d/112.png diff --git a/app/imgs/3d/chansey.jpg b/app/imgs/3d/113.png similarity index 100% rename from app/imgs/3d/chansey.jpg rename to app/imgs/3d/113.png diff --git a/app/imgs/3d/114.png b/app/imgs/3d/114.png new file mode 100644 index 0000000..a902488 Binary files /dev/null and b/app/imgs/3d/114.png differ diff --git a/app/imgs/3d/kangaskhan.jpg b/app/imgs/3d/115.png similarity index 100% rename from app/imgs/3d/kangaskhan.jpg rename to app/imgs/3d/115.png diff --git a/app/imgs/3d/horsea.jpg b/app/imgs/3d/116.png similarity index 100% rename from app/imgs/3d/horsea.jpg rename to app/imgs/3d/116.png diff --git a/app/imgs/3d/seadra.jpg b/app/imgs/3d/117.png similarity index 100% rename from app/imgs/3d/seadra.jpg rename to app/imgs/3d/117.png diff --git a/app/imgs/3d/goldeen.jpg b/app/imgs/3d/118.png similarity index 100% rename from app/imgs/3d/goldeen.jpg rename to app/imgs/3d/118.png diff --git a/app/imgs/3d/seaking.jpg b/app/imgs/3d/119.png similarity index 100% rename from app/imgs/3d/seaking.jpg rename to app/imgs/3d/119.png diff --git a/app/imgs/3d/butterfree.jpg b/app/imgs/3d/12.png similarity index 100% rename from app/imgs/3d/butterfree.jpg rename to app/imgs/3d/12.png diff --git a/app/imgs/3d/staryu.jpg b/app/imgs/3d/120.png similarity index 100% rename from app/imgs/3d/staryu.jpg rename to app/imgs/3d/120.png diff --git a/app/imgs/3d/starmie.jpg b/app/imgs/3d/121.png similarity index 100% rename from app/imgs/3d/starmie.jpg rename to app/imgs/3d/121.png diff --git a/app/imgs/3d/122.png b/app/imgs/3d/122.png new file mode 100644 index 0000000..1ac65fc Binary files /dev/null and b/app/imgs/3d/122.png differ diff --git a/app/imgs/3d/scyther.jpg b/app/imgs/3d/123.png similarity index 100% rename from app/imgs/3d/scyther.jpg rename to app/imgs/3d/123.png diff --git a/app/imgs/3d/jynx.jpg b/app/imgs/3d/124.png similarity index 100% rename from app/imgs/3d/jynx.jpg rename to app/imgs/3d/124.png diff --git a/app/imgs/3d/electabuzz.jpg b/app/imgs/3d/125.png similarity index 100% rename from app/imgs/3d/electabuzz.jpg rename to app/imgs/3d/125.png diff --git a/app/imgs/3d/magmar.jpg b/app/imgs/3d/126.png similarity index 100% rename from app/imgs/3d/magmar.jpg rename to app/imgs/3d/126.png diff --git a/app/imgs/3d/pinsir.jpg b/app/imgs/3d/127.png similarity index 100% rename from app/imgs/3d/pinsir.jpg rename to app/imgs/3d/127.png diff --git a/app/imgs/3d/tauros.jpg b/app/imgs/3d/128.png similarity index 100% rename from app/imgs/3d/tauros.jpg rename to app/imgs/3d/128.png diff --git a/app/imgs/3d/magikarp.jpg b/app/imgs/3d/129.png similarity index 100% rename from app/imgs/3d/magikarp.jpg rename to app/imgs/3d/129.png diff --git a/app/imgs/3d/weedle.jpg b/app/imgs/3d/13.png similarity index 100% rename from app/imgs/3d/weedle.jpg rename to app/imgs/3d/13.png diff --git a/app/imgs/3d/gyarados.jpg b/app/imgs/3d/130.png similarity index 100% rename from app/imgs/3d/gyarados.jpg rename to app/imgs/3d/130.png diff --git a/app/imgs/3d/lapras.jpg b/app/imgs/3d/131.png similarity index 100% rename from app/imgs/3d/lapras.jpg rename to app/imgs/3d/131.png diff --git a/app/imgs/3d/ditto.jpg b/app/imgs/3d/132.png similarity index 100% rename from app/imgs/3d/ditto.jpg rename to app/imgs/3d/132.png diff --git a/app/imgs/3d/eevee.jpg b/app/imgs/3d/133.png similarity index 100% rename from app/imgs/3d/eevee.jpg rename to app/imgs/3d/133.png diff --git a/app/imgs/3d/vaporeon.jpg b/app/imgs/3d/134.png similarity index 100% rename from app/imgs/3d/vaporeon.jpg rename to app/imgs/3d/134.png diff --git a/app/imgs/3d/jolteon.jpg b/app/imgs/3d/135.png similarity index 100% rename from app/imgs/3d/jolteon.jpg rename to app/imgs/3d/135.png diff --git a/app/imgs/3d/flareon.jpg b/app/imgs/3d/136.png similarity index 100% rename from app/imgs/3d/flareon.jpg rename to app/imgs/3d/136.png diff --git a/app/imgs/3d/porygon.jpg b/app/imgs/3d/137.png similarity index 100% rename from app/imgs/3d/porygon.jpg rename to app/imgs/3d/137.png diff --git a/app/imgs/3d/omanyte.jpg b/app/imgs/3d/138.png similarity index 100% rename from app/imgs/3d/omanyte.jpg rename to app/imgs/3d/138.png diff --git a/app/imgs/3d/omastar.jpg b/app/imgs/3d/139.png similarity index 100% rename from app/imgs/3d/omastar.jpg rename to app/imgs/3d/139.png diff --git a/app/imgs/3d/kakuna.jpg b/app/imgs/3d/14.png similarity index 100% rename from app/imgs/3d/kakuna.jpg rename to app/imgs/3d/14.png diff --git a/app/imgs/3d/kabuto.jpg b/app/imgs/3d/140.png similarity index 100% rename from app/imgs/3d/kabuto.jpg rename to app/imgs/3d/140.png diff --git a/app/imgs/3d/kabutops.jpg b/app/imgs/3d/141.png similarity index 100% rename from app/imgs/3d/kabutops.jpg rename to app/imgs/3d/141.png diff --git a/app/imgs/3d/aerodactyl.jpg b/app/imgs/3d/142.png similarity index 100% rename from app/imgs/3d/aerodactyl.jpg rename to app/imgs/3d/142.png diff --git a/app/imgs/3d/snorlax.jpg b/app/imgs/3d/143.png similarity index 100% rename from app/imgs/3d/snorlax.jpg rename to app/imgs/3d/143.png diff --git a/app/imgs/3d/articuno.jpg b/app/imgs/3d/144.png similarity index 100% rename from app/imgs/3d/articuno.jpg rename to app/imgs/3d/144.png diff --git a/app/imgs/3d/zapdos.jpg b/app/imgs/3d/145.png similarity index 100% rename from app/imgs/3d/zapdos.jpg rename to app/imgs/3d/145.png diff --git a/app/imgs/3d/moltres.jpg b/app/imgs/3d/146.png similarity index 100% rename from app/imgs/3d/moltres.jpg rename to app/imgs/3d/146.png diff --git a/app/imgs/3d/dratini.jpg b/app/imgs/3d/147.png similarity index 100% rename from app/imgs/3d/dratini.jpg rename to app/imgs/3d/147.png diff --git a/app/imgs/3d/dragonair.jpg b/app/imgs/3d/148.png similarity index 100% rename from app/imgs/3d/dragonair.jpg rename to app/imgs/3d/148.png diff --git a/app/imgs/3d/dragonite.jpg b/app/imgs/3d/149.png similarity index 100% rename from app/imgs/3d/dragonite.jpg rename to app/imgs/3d/149.png diff --git a/app/imgs/3d/beedrill.jpg b/app/imgs/3d/15.png similarity index 100% rename from app/imgs/3d/beedrill.jpg rename to app/imgs/3d/15.png diff --git a/app/imgs/3d/mewtwo.jpg b/app/imgs/3d/150.png similarity index 100% rename from app/imgs/3d/mewtwo.jpg rename to app/imgs/3d/150.png diff --git a/app/imgs/3d/mew.jpg b/app/imgs/3d/151.png similarity index 100% rename from app/imgs/3d/mew.jpg rename to app/imgs/3d/151.png diff --git a/app/imgs/3d/pidgey.jpg b/app/imgs/3d/16.png similarity index 100% rename from app/imgs/3d/pidgey.jpg rename to app/imgs/3d/16.png diff --git a/app/imgs/3d/pidgeotto.jpg b/app/imgs/3d/17.png similarity index 100% rename from app/imgs/3d/pidgeotto.jpg rename to app/imgs/3d/17.png diff --git a/app/imgs/3d/pidgeot.jpg b/app/imgs/3d/18.png similarity index 100% rename from app/imgs/3d/pidgeot.jpg rename to app/imgs/3d/18.png diff --git a/app/imgs/3d/rattata.jpg b/app/imgs/3d/19.png similarity index 100% rename from app/imgs/3d/rattata.jpg rename to app/imgs/3d/19.png diff --git a/app/imgs/3d/ivysaur.jpg b/app/imgs/3d/2.png similarity index 100% rename from app/imgs/3d/ivysaur.jpg rename to app/imgs/3d/2.png diff --git a/app/imgs/3d/raticate.jpg b/app/imgs/3d/20.png similarity index 100% rename from app/imgs/3d/raticate.jpg rename to app/imgs/3d/20.png diff --git a/app/imgs/3d/spearow.jpg b/app/imgs/3d/21.png similarity index 100% rename from app/imgs/3d/spearow.jpg rename to app/imgs/3d/21.png diff --git a/app/imgs/3d/fearow.jpg b/app/imgs/3d/22.png similarity index 100% rename from app/imgs/3d/fearow.jpg rename to app/imgs/3d/22.png diff --git a/app/imgs/3d/ekans.jpg b/app/imgs/3d/23.png similarity index 100% rename from app/imgs/3d/ekans.jpg rename to app/imgs/3d/23.png diff --git a/app/imgs/3d/arbok.jpg b/app/imgs/3d/24.png similarity index 100% rename from app/imgs/3d/arbok.jpg rename to app/imgs/3d/24.png diff --git a/app/imgs/3d/pikachu.jpg b/app/imgs/3d/25.png similarity index 100% rename from app/imgs/3d/pikachu.jpg rename to app/imgs/3d/25.png diff --git a/app/imgs/3d/raichu.jpg b/app/imgs/3d/26.png similarity index 100% rename from app/imgs/3d/raichu.jpg rename to app/imgs/3d/26.png diff --git a/app/imgs/3d/sandshrew.jpg b/app/imgs/3d/27.png similarity index 100% rename from app/imgs/3d/sandshrew.jpg rename to app/imgs/3d/27.png diff --git a/app/imgs/3d/sandslash.jpg b/app/imgs/3d/28.png similarity index 100% rename from app/imgs/3d/sandslash.jpg rename to app/imgs/3d/28.png diff --git a/app/imgs/3d/nidoran-f.jpg b/app/imgs/3d/29.png similarity index 100% rename from app/imgs/3d/nidoran-f.jpg rename to app/imgs/3d/29.png diff --git a/app/imgs/3d/venusaur.jpg b/app/imgs/3d/3.png similarity index 100% rename from app/imgs/3d/venusaur.jpg rename to app/imgs/3d/3.png diff --git a/app/imgs/3d/nidorina.jpg b/app/imgs/3d/30.png similarity index 100% rename from app/imgs/3d/nidorina.jpg rename to app/imgs/3d/30.png diff --git a/app/imgs/3d/nidoqueen.jpg b/app/imgs/3d/31.png similarity index 100% rename from app/imgs/3d/nidoqueen.jpg rename to app/imgs/3d/31.png diff --git a/app/imgs/3d/nidoran-m.jpg b/app/imgs/3d/32.png similarity index 100% rename from app/imgs/3d/nidoran-m.jpg rename to app/imgs/3d/32.png diff --git a/app/imgs/3d/nidorino.jpg b/app/imgs/3d/33.png similarity index 100% rename from app/imgs/3d/nidorino.jpg rename to app/imgs/3d/33.png diff --git a/app/imgs/3d/nidoking.jpg b/app/imgs/3d/34.png similarity index 100% rename from app/imgs/3d/nidoking.jpg rename to app/imgs/3d/34.png diff --git a/app/imgs/3d/clefairy.jpg b/app/imgs/3d/35.png similarity index 100% rename from app/imgs/3d/clefairy.jpg rename to app/imgs/3d/35.png diff --git a/app/imgs/3d/clefable.jpg b/app/imgs/3d/36.png similarity index 100% rename from app/imgs/3d/clefable.jpg rename to app/imgs/3d/36.png diff --git a/app/imgs/3d/vulpix.jpg b/app/imgs/3d/37.png similarity index 100% rename from app/imgs/3d/vulpix.jpg rename to app/imgs/3d/37.png diff --git a/app/imgs/3d/ninetales.jpg b/app/imgs/3d/38.png similarity index 100% rename from app/imgs/3d/ninetales.jpg rename to app/imgs/3d/38.png diff --git a/app/imgs/3d/jigglypuff.jpg b/app/imgs/3d/39.png similarity index 100% rename from app/imgs/3d/jigglypuff.jpg rename to app/imgs/3d/39.png diff --git a/app/imgs/3d/charmander.jpg b/app/imgs/3d/4.png similarity index 100% rename from app/imgs/3d/charmander.jpg rename to app/imgs/3d/4.png diff --git a/app/imgs/3d/wigglytuff.jpg b/app/imgs/3d/40.png similarity index 100% rename from app/imgs/3d/wigglytuff.jpg rename to app/imgs/3d/40.png diff --git a/app/imgs/3d/zubat.jpg b/app/imgs/3d/41.png similarity index 100% rename from app/imgs/3d/zubat.jpg rename to app/imgs/3d/41.png diff --git a/app/imgs/3d/golbat.jpg b/app/imgs/3d/42.png similarity index 100% rename from app/imgs/3d/golbat.jpg rename to app/imgs/3d/42.png diff --git a/app/imgs/3d/oddish.jpg b/app/imgs/3d/43.png similarity index 100% rename from app/imgs/3d/oddish.jpg rename to app/imgs/3d/43.png diff --git a/app/imgs/3d/gloom.jpg b/app/imgs/3d/44.png similarity index 100% rename from app/imgs/3d/gloom.jpg rename to app/imgs/3d/44.png diff --git a/app/imgs/3d/vileplume.jpg b/app/imgs/3d/45.png similarity index 100% rename from app/imgs/3d/vileplume.jpg rename to app/imgs/3d/45.png diff --git a/app/imgs/3d/paras.jpg b/app/imgs/3d/46.png similarity index 100% rename from app/imgs/3d/paras.jpg rename to app/imgs/3d/46.png diff --git a/app/imgs/3d/parasect.jpg b/app/imgs/3d/47.png similarity index 100% rename from app/imgs/3d/parasect.jpg rename to app/imgs/3d/47.png diff --git a/app/imgs/3d/venonat.jpg b/app/imgs/3d/48.png similarity index 100% rename from app/imgs/3d/venonat.jpg rename to app/imgs/3d/48.png diff --git a/app/imgs/3d/venomoth.jpg b/app/imgs/3d/49.png similarity index 100% rename from app/imgs/3d/venomoth.jpg rename to app/imgs/3d/49.png diff --git a/app/imgs/3d/charmeleon.jpg b/app/imgs/3d/5.png similarity index 100% rename from app/imgs/3d/charmeleon.jpg rename to app/imgs/3d/5.png diff --git a/app/imgs/3d/diglett.jpg b/app/imgs/3d/50.png similarity index 100% rename from app/imgs/3d/diglett.jpg rename to app/imgs/3d/50.png diff --git a/app/imgs/3d/dugtrio.jpg b/app/imgs/3d/51.png similarity index 100% rename from app/imgs/3d/dugtrio.jpg rename to app/imgs/3d/51.png diff --git a/app/imgs/3d/meowth.jpg b/app/imgs/3d/52.png similarity index 100% rename from app/imgs/3d/meowth.jpg rename to app/imgs/3d/52.png diff --git a/app/imgs/3d/persian.jpg b/app/imgs/3d/53.png similarity index 100% rename from app/imgs/3d/persian.jpg rename to app/imgs/3d/53.png diff --git a/app/imgs/3d/psyduck.jpg b/app/imgs/3d/54.png similarity index 100% rename from app/imgs/3d/psyduck.jpg rename to app/imgs/3d/54.png diff --git a/app/imgs/3d/golduck.jpg b/app/imgs/3d/55.png similarity index 100% rename from app/imgs/3d/golduck.jpg rename to app/imgs/3d/55.png diff --git a/app/imgs/3d/mankey.jpg b/app/imgs/3d/56.png similarity index 100% rename from app/imgs/3d/mankey.jpg rename to app/imgs/3d/56.png diff --git a/app/imgs/3d/primeape.jpg b/app/imgs/3d/57.png similarity index 100% rename from app/imgs/3d/primeape.jpg rename to app/imgs/3d/57.png diff --git a/app/imgs/3d/growlithe.jpg b/app/imgs/3d/58.png similarity index 100% rename from app/imgs/3d/growlithe.jpg rename to app/imgs/3d/58.png diff --git a/app/imgs/3d/arcanine.jpg b/app/imgs/3d/59.png similarity index 100% rename from app/imgs/3d/arcanine.jpg rename to app/imgs/3d/59.png diff --git a/app/imgs/3d/charizard.jpg b/app/imgs/3d/6.png similarity index 100% rename from app/imgs/3d/charizard.jpg rename to app/imgs/3d/6.png diff --git a/app/imgs/3d/poliwag.jpg b/app/imgs/3d/60.png similarity index 100% rename from app/imgs/3d/poliwag.jpg rename to app/imgs/3d/60.png diff --git a/app/imgs/3d/poliwhirl.jpg b/app/imgs/3d/61.png similarity index 100% rename from app/imgs/3d/poliwhirl.jpg rename to app/imgs/3d/61.png diff --git a/app/imgs/3d/poliwrath.jpg b/app/imgs/3d/62.png similarity index 100% rename from app/imgs/3d/poliwrath.jpg rename to app/imgs/3d/62.png diff --git a/app/imgs/3d/abra.jpg b/app/imgs/3d/63.png similarity index 100% rename from app/imgs/3d/abra.jpg rename to app/imgs/3d/63.png diff --git a/app/imgs/3d/kadabra.jpg b/app/imgs/3d/64.png similarity index 100% rename from app/imgs/3d/kadabra.jpg rename to app/imgs/3d/64.png diff --git a/app/imgs/3d/alakazam.jpg b/app/imgs/3d/65.png similarity index 100% rename from app/imgs/3d/alakazam.jpg rename to app/imgs/3d/65.png diff --git a/app/imgs/3d/machop.jpg b/app/imgs/3d/66.png similarity index 100% rename from app/imgs/3d/machop.jpg rename to app/imgs/3d/66.png diff --git a/app/imgs/3d/machoke.jpg b/app/imgs/3d/67.png similarity index 100% rename from app/imgs/3d/machoke.jpg rename to app/imgs/3d/67.png diff --git a/app/imgs/3d/machamp.jpg b/app/imgs/3d/68.png similarity index 100% rename from app/imgs/3d/machamp.jpg rename to app/imgs/3d/68.png diff --git a/app/imgs/3d/bellsprout.jpg b/app/imgs/3d/69.png similarity index 100% rename from app/imgs/3d/bellsprout.jpg rename to app/imgs/3d/69.png diff --git a/app/imgs/3d/squirtle.jpg b/app/imgs/3d/7.png similarity index 100% rename from app/imgs/3d/squirtle.jpg rename to app/imgs/3d/7.png diff --git a/app/imgs/3d/weepinbell.jpg b/app/imgs/3d/70.png similarity index 100% rename from app/imgs/3d/weepinbell.jpg rename to app/imgs/3d/70.png diff --git a/app/imgs/3d/victreebel.jpg b/app/imgs/3d/71.png similarity index 100% rename from app/imgs/3d/victreebel.jpg rename to app/imgs/3d/71.png diff --git a/app/imgs/3d/tentacool.jpg b/app/imgs/3d/72.png similarity index 100% rename from app/imgs/3d/tentacool.jpg rename to app/imgs/3d/72.png diff --git a/app/imgs/3d/tentacruel.jpg b/app/imgs/3d/73.png similarity index 100% rename from app/imgs/3d/tentacruel.jpg rename to app/imgs/3d/73.png diff --git a/app/imgs/3d/geodude.jpg b/app/imgs/3d/74.png similarity index 100% rename from app/imgs/3d/geodude.jpg rename to app/imgs/3d/74.png diff --git a/app/imgs/3d/graveler.jpg b/app/imgs/3d/75.png similarity index 100% rename from app/imgs/3d/graveler.jpg rename to app/imgs/3d/75.png diff --git a/app/imgs/3d/golem.jpg b/app/imgs/3d/76.png similarity index 100% rename from app/imgs/3d/golem.jpg rename to app/imgs/3d/76.png diff --git a/app/imgs/3d/ponyta.jpg b/app/imgs/3d/77.png similarity index 100% rename from app/imgs/3d/ponyta.jpg rename to app/imgs/3d/77.png diff --git a/app/imgs/3d/rapidash.jpg b/app/imgs/3d/78.png similarity index 100% rename from app/imgs/3d/rapidash.jpg rename to app/imgs/3d/78.png diff --git a/app/imgs/3d/slowpoke.jpg b/app/imgs/3d/79.png similarity index 100% rename from app/imgs/3d/slowpoke.jpg rename to app/imgs/3d/79.png diff --git a/app/imgs/3d/wartortle.jpg b/app/imgs/3d/8.png similarity index 100% rename from app/imgs/3d/wartortle.jpg rename to app/imgs/3d/8.png diff --git a/app/imgs/3d/slowbro.jpg b/app/imgs/3d/80.png similarity index 100% rename from app/imgs/3d/slowbro.jpg rename to app/imgs/3d/80.png diff --git a/app/imgs/3d/magnemite.jpg b/app/imgs/3d/81.png similarity index 100% rename from app/imgs/3d/magnemite.jpg rename to app/imgs/3d/81.png diff --git a/app/imgs/3d/magneton.jpg b/app/imgs/3d/82.png similarity index 100% rename from app/imgs/3d/magneton.jpg rename to app/imgs/3d/82.png diff --git a/app/imgs/3d/farfetchd.jpg b/app/imgs/3d/83.png similarity index 100% rename from app/imgs/3d/farfetchd.jpg rename to app/imgs/3d/83.png diff --git a/app/imgs/3d/doduo.jpg b/app/imgs/3d/84.png similarity index 100% rename from app/imgs/3d/doduo.jpg rename to app/imgs/3d/84.png diff --git a/app/imgs/3d/dodrio.jpg b/app/imgs/3d/85.png similarity index 100% rename from app/imgs/3d/dodrio.jpg rename to app/imgs/3d/85.png diff --git a/app/imgs/3d/seel.jpg b/app/imgs/3d/86.png similarity index 100% rename from app/imgs/3d/seel.jpg rename to app/imgs/3d/86.png diff --git a/app/imgs/3d/dewgong.jpg b/app/imgs/3d/87.png similarity index 100% rename from app/imgs/3d/dewgong.jpg rename to app/imgs/3d/87.png diff --git a/app/imgs/3d/grimer.jpg b/app/imgs/3d/88.png similarity index 100% rename from app/imgs/3d/grimer.jpg rename to app/imgs/3d/88.png diff --git a/app/imgs/3d/muk.jpg b/app/imgs/3d/89.png similarity index 100% rename from app/imgs/3d/muk.jpg rename to app/imgs/3d/89.png diff --git a/app/imgs/3d/blastoise.jpg b/app/imgs/3d/9.png similarity index 100% rename from app/imgs/3d/blastoise.jpg rename to app/imgs/3d/9.png diff --git a/app/imgs/3d/shellder.jpg b/app/imgs/3d/90.png similarity index 100% rename from app/imgs/3d/shellder.jpg rename to app/imgs/3d/90.png diff --git a/app/imgs/3d/cloyster.jpg b/app/imgs/3d/91.png similarity index 100% rename from app/imgs/3d/cloyster.jpg rename to app/imgs/3d/91.png diff --git a/app/imgs/3d/gastly.jpg b/app/imgs/3d/92.png similarity index 100% rename from app/imgs/3d/gastly.jpg rename to app/imgs/3d/92.png diff --git a/app/imgs/3d/haunter.jpg b/app/imgs/3d/93.png similarity index 100% rename from app/imgs/3d/haunter.jpg rename to app/imgs/3d/93.png diff --git a/app/imgs/3d/gengar.jpg b/app/imgs/3d/94.png similarity index 100% rename from app/imgs/3d/gengar.jpg rename to app/imgs/3d/94.png diff --git a/app/imgs/3d/onix.jpg b/app/imgs/3d/95.png similarity index 100% rename from app/imgs/3d/onix.jpg rename to app/imgs/3d/95.png diff --git a/app/imgs/3d/drowzee.jpg b/app/imgs/3d/96.png similarity index 100% rename from app/imgs/3d/drowzee.jpg rename to app/imgs/3d/96.png diff --git a/app/imgs/3d/hypno.jpg b/app/imgs/3d/97.png similarity index 100% rename from app/imgs/3d/hypno.jpg rename to app/imgs/3d/97.png diff --git a/app/imgs/3d/krabby.jpg b/app/imgs/3d/98.png similarity index 100% rename from app/imgs/3d/krabby.jpg rename to app/imgs/3d/98.png diff --git a/app/imgs/3d/kingler.jpg b/app/imgs/3d/99.png similarity index 100% rename from app/imgs/3d/kingler.jpg rename to app/imgs/3d/99.png diff --git a/app/imgs/3d/mime.jpg b/app/imgs/3d/mime.jpg deleted file mode 100644 index 8537307..0000000 --- a/app/imgs/3d/mime.jpg +++ /dev/null @@ -1 +0,0 @@ -Not Found \ No newline at end of file diff --git a/app/imgs/3d/tangela.jpg b/app/imgs/3d/tangela.jpg deleted file mode 100644 index 8537307..0000000 --- a/app/imgs/3d/tangela.jpg +++ /dev/null @@ -1 +0,0 @@ -Not Found \ No newline at end of file diff --git a/app/index.js b/app/index.js index eda25cc..8506a0a 100644 --- a/app/index.js +++ b/app/index.js @@ -1,32 +1,55 @@ -import React from 'react' +import React, { + PropTypes +} from 'react' import ReactDOM from 'react-dom' -import { ipcRenderer } from 'electron' import { - Provider + Provider, + connect } from 'react-redux' +import { bindActionCreators } from 'redux' import store from './store' import Login from './screens/Login' import Table from './screens/Table' +import { + login +} from './actions' + require('./css/pokenurse.css') const App = React.createClass({ - getInitialState() { - return { loggedIn: false } + propTypes: { + authenticate: PropTypes.object.isRequired, + autoLogin: PropTypes.bool.isRequired, + login: PropTypes.func.isRequired, }, componentDidMount() { - ipcRenderer.on('pokemon-logged-in', () => { - this.setState({ loggedIn: true }) - }) + const { + autoLogin, + authenticate, + } = this.props + + const { credentials } = authenticate + + if (autoLogin && credentials.method && credentials.password && credentials.username) { + this.props.login(credentials) + } }, render() { - if (this.state.loggedIn) return () + if (this.props.authenticate.loggedIn) return (
) return () } }) -ReactDOM.render(, document.getElementById('content')) +const ConnectedApp = connect((state => ({ + authenticate: state.authenticate, + autoLogin: state.settings.autoLogin, +})), (dispatch => bindActionCreators({ + login +}, dispatch)))(App) + +ReactDOM.render(, document.getElementById('content')) diff --git a/app/reducers/authenticate.js b/app/reducers/authenticate.js new file mode 100644 index 0000000..247dea7 --- /dev/null +++ b/app/reducers/authenticate.js @@ -0,0 +1,78 @@ +import fs from 'fs' +import path from 'path' +import { + handleActions +} from 'redux-actions' +import { + remote, + ipcRenderer +} from 'electron' + +const accountPath = path.join(remote.app.getPath('appData'), '/pokenurse/account.json') + +const credentialsInitialState = {} + +// Helper to initialize the credentials state with existing account.json +function getAccountCredentials() { + if (!fs.existsSync(accountPath)) { + return credentialsInitialState + } + + // Maybe use readFile instead + const credentials = JSON.parse(fs.readFileSync(accountPath)) + + return { + method: credentials.method, + username: credentials.username, + password: credentials.password, + } +} + +const initialState = { + loggedIn: false, + authenticating: false, + credentials: getAccountCredentials(), +} + +export default handleActions({ + USER_LOGIN_STARTED(state) { + return Object.assign({}, state, { authenticating: true }) + }, + + USER_LOGIN_SUCCESS(state) { + return Object.assign({}, state, { + loggedIn: true, + authenticating: false, + }) + }, + + USER_LOGIN_FAILED(state, action) { + ipcRenderer.send('error-message', String(action.payload.error)) + return Object.assign({}, state, { + loggedIn: false, + authenticating: false, + }) + }, + + USER_LOGOUT(state) { + return Object.assign({}, state, { loggedIn: false }) + }, + + CHECK_AND_DELETE_CREDENTIALS_SUCCESS(state) { + return Object.assign({}, state, { credentials: credentialsInitialState }) + }, + + CHECK_AND_DELETE_CREDENTIALS_FAILED(state) { + console.error('Failed to check and delete credentials.') // eslint-disable-line + return state + }, + + SAVE_ACCOUNT_CREDENTIALS_SUCCESS(state, action) { + return Object.assign({}, state, { credentials: action.payload.credentials }) + }, + + SAVE_ACCOUNT_CREDENTIALS_FAILED(state) { + console.error('Failed to save account credentials.') // eslint-disable-line + return state + }, +}, initialState) diff --git a/app/reducers/index.js b/app/reducers/index.js index 172dcfd..12ee9c7 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -3,7 +3,24 @@ import { } from 'redux' import status from './status' +import authenticate from './authenticate' +import trainer from './trainer' +import settings from './settings' -export default combineReducers({ - status +const appReducer = combineReducers({ + status, + authenticate, + trainer, + settings, }) + +export default (state, action) => { + if (action.type === 'USER_LOGOUT') { + state = { + authenticate: state.authenticate, + settings: state.settings, + } + } + + return appReducer(state, action) +} diff --git a/app/reducers/settings.js b/app/reducers/settings.js new file mode 100644 index 0000000..592a4a1 --- /dev/null +++ b/app/reducers/settings.js @@ -0,0 +1,88 @@ +import path from 'path' +import fs from 'fs' +import { + handleActions +} from 'redux-actions' +import { + remote +} from 'electron' + +const settingsPath = path.join(remote.app.getPath('appData'), '/pokenurse/settings.json') + +const ASCENDING = 'ASC' +const DESCENDING = 'DESC' + +const initialSettingsState = { + showSpeciesWithZeroPokemon: true, + autoLogin: false, + defaultPokedexSortBy: 'pokemon_id', + defaultPokedexSortDirection: ASCENDING, + defaultSpecieSortBy: 'cp', + defaultSpecieSortDirection: DESCENDING, +} + +function getInitialSettingsState() { + if (!fs.existsSync(settingsPath)) { + return initialSettingsState + } + + const settingsJSON = JSON.parse(fs.readFileSync(settingsPath)) + + return Object.assign({}, initialSettingsState, settingsJSON) +} + +function updateSettingState(state, setting) { + const updated = Object.assign({}, state, setting) + + fs.writeFileSync(settingsPath, JSON.stringify(updated)) + + return updated +} + +const defaultSettings = getInitialSettingsState() + +export { + defaultSettings, +} + +export default handleActions({ + TOGGLE_SHOW_SPECIES_WITH_ZERO_POKEMON(state) { + return updateSettingState(state, { + showSpeciesWithZeroPokemon: !state.showSpeciesWithZeroPokemon + }) + }, + + TOGGLE_AUTO_LOGIN(state) { + return updateSettingState(state, { + autoLogin: !state.autoLogin + }) + }, + + RESET_ALL_SETTINGS(state) { + return updateSettingState(state, initialSettingsState) + }, + + CHANGE_DEFAULT_POKEDEX_SORT_BY(state, action) { + return updateSettingState(state, { + defaultPokedexSortBy: action.payload + }) + }, + + CHANGE_DEFAULT_POKEDEX_SORT_DIRECTION(state, action) { + return updateSettingState(state, { + defaultPokedexSortDirection: action.payload + }) + }, + + CHANGE_DEFAULT_SPECIE_SORT_DIRECTION(state, action) { + return updateSettingState(state, { + defaultSpecieSortDirection: action.payload + }) + }, + + CHANGE_DEFAULT_SPECIE_SORT_BY(state, action) { + return updateSettingState(state, { + defaultSpecieSortBy: action.payload + }) + }, +}, defaultSettings) diff --git a/app/reducers/trainer.js b/app/reducers/trainer.js new file mode 100644 index 0000000..7cc40a7 --- /dev/null +++ b/app/reducers/trainer.js @@ -0,0 +1,469 @@ +import { + every, + mapValues, +} from 'lodash' +import { + handleActions +} from 'redux-actions' +import { + ipcRenderer, +} from 'electron' + +import { Immutable, Organize } from '../utils' + +import { + defaultSettings +} from './settings' + +const { + getSortedPokemon, + getSortedSpecies +} = Organize + +const ASCENDING = 'ASC' +const DESCENDING = 'DESC' + +const initialState = { + trainerData: null, + monsters: null, + filterBy: '', + sortBy: defaultSettings.defaultPokedexSortBy, + sortDir: defaultSettings.defaultPokedexSortDirection, + speciesState: null, + selectedCount: 0, +} + +function getInitialPokemonState(specie) { + const pokemonState = {} + specie.pokemon.forEach((p) => { + pokemonState[p.id] = { check: false } + }) + return pokemonState +} + +function getNewSpeciesState(state) { + const speciesState = {} + + const sortBy = defaultSettings.defaultSpecieSortBy + const sortDir = defaultSettings.defaultSpecieSortDirection + + let selectedCount = 0 + + state.monsters.species.forEach((specie) => { + const pid = String(specie.pokemon_id) + let existingSpecieState = null + + if (state.speciesState) existingSpecieState = state.speciesState[pid] + + // specie state already exists + if (existingSpecieState) { + const updatedSpecieState = { pokemonState: {} } + let checkAll = true + specie.pokemon.forEach((p) => { + // pokemon already exists + if (existingSpecieState.pokemonState[p.id]) { + updatedSpecieState.pokemonState[p.id] = existingSpecieState.pokemonState[p.id] + const isChecked = updatedSpecieState.pokemonState[p.id].check + checkAll = checkAll && isChecked + if (isChecked) selectedCount++ + // pokemon does not exist + } else { + updatedSpecieState.pokemonState[p.id] = { check: false } + checkAll = false + } + }) + updatedSpecieState.checkAll = checkAll + speciesState[pid] = Object.assign({}, existingSpecieState, updatedSpecieState) + // specie state does not exist + } else { + speciesState[pid] = { + pokemonState: getInitialPokemonState(specie), + checkAll: false, + collapsed: true, + sortBy, + sortDir + } + } + }) + + return { + speciesState, + selectedCount + } +} + +// TODO Utils +function getNewMonsters(state, monsters, sortBy, sortDir) { + const sortedSpecies = getSortedSpecies(monsters, sortBy, sortDir) + + // Mutates, but it is okay because we sliced/sorted above ^ + sortedSpecies.forEach((specie) => { + // we don't have a specieState on start, sort will fallback to defaults + const specieState = state.speciesState ? state.speciesState[specie.pokemon_id] : null + + specie.pokemon = getSortedPokemon(specie, specieState) + }) + + return Object.assign({}, monsters, { + species: sortedSpecies + }) +} + +// Anytime the speciesState changes we should recount selected +function updateSpecies(state, index, updater) { + const speciesAtIndex = state.monsters.species[index] + const updatedSpecies = Object.assign({}, speciesAtIndex, updater(speciesAtIndex)) + + const updatedMonsters = Object.assign({}, state.monsters, { + species: Immutable.array.set(state.monsters.species, index, updatedSpecies) + }) + + const updatedStateWithMonsters = Object.assign({}, state, { + monsters: updatedMonsters + }) + + const { + speciesState, + selectedCount + } = getNewSpeciesState(updatedStateWithMonsters) + + return Object.assign({}, updatedStateWithMonsters, { + speciesState, + selectedCount + }) +} + +function updateSpeciesState(state, id, updater) { + const { + speciesState + } = state + const newSpecieState = {} + const existingSpecieState = speciesState[String(id)] + + newSpecieState[String(id)] = Object.assign( + {}, + existingSpecieState, + updater(existingSpecieState) + ) + + return Object.assign({}, speciesState, newSpecieState) +} + +function updatePokemonState(speciesState, pid, updater) { + const existingPokemonByIdState = speciesState.pokemonState[String(pid)] + + const newPokemonByIdState = {} + newPokemonByIdState[String(pid)] = Object.assign( + {}, + existingPokemonByIdState, + updater(existingPokemonByIdState) + ) + return Object.assign({}, speciesState.pokemonState, newPokemonByIdState) +} + +function updateMonster(state, pokemon, options = {}) { + const speciesIndex = pokemon.pokemon_id - 1 + + const updatedPokemon = options.remove ? null : pokemon + + return updateSpecies(state, speciesIndex, (speciesAtIndex) => { + const index = speciesAtIndex.pokemon.findIndex((p) => p.id === pokemon.id) + + const sorted = getSortedPokemon(Object.assign({}, speciesAtIndex, { + pokemon: Immutable.array.set(speciesAtIndex.pokemon, index, updatedPokemon) + }), state.speciesState[pokemon.pokemon_id]) + + return { // make sure we sort the new pokemon index now that we updated it + pokemon: sorted + } + }) +} + +function getNewSortDirectionFromSortBy(sortBy, specieState) { + let sortDir = null + + // If we are already sorting this way, flip direction + if (sortBy === specieState.sortBy) { + sortDir = specieState.sortDir === ASCENDING ? DESCENDING : ASCENDING + // Otherwise use descending + } else { + sortDir = DESCENDING + } + + return sortDir +} + +export default handleActions({ + GET_TRAINER_INFO_SUCCESS(state, action) { + return Object.assign({}, state, action.payload) + }, + + GET_TRAINER_INFO_FAILED(state, action) { + ipcRenderer.send('error-message', String(action.payload)) + return state + }, + + GET_TRAINER_POKEMON_SUCCESS(state, action) { + const monsters = getNewMonsters(state, action.payload, state.sortBy, state.sortDir) + + const updatedStateWithMonsters = Object.assign({}, state, { monsters }) + + const { + speciesState, + selectedCount + } = getNewSpeciesState(updatedStateWithMonsters) + + // TODO always sort the data before we return it + return Object.assign({}, updatedStateWithMonsters, { + speciesState, + selectedCount + }) + }, + + GET_TRAINER_POKEMON_FAILED(state, action) { + console.error(action.payload) // eslint-disable-line + return state + }, + + UPDATE_MONSTER(state, action) { + const { + pokemon, + options + } = action.payload + + return updateMonster(state, pokemon, options) + }, + + UPDATE_SPECIES(state, action) { + const { + index, + updater + } = action.payload + + return updateSpecies(state, index, updater) + }, + + // TODO Maybe just generic UPDATE_TRAINER_STORE + // using this too much right now, taking the easy way out of refactoring + UPDATE_MONSTER_SORT(state, action) { + return Object.assign({}, state, action.payload) + }, + + SORT_SPECIES(state, action) { + const { + sortBy, + speciesIndex + } = action.payload + + const pokemonId = state.monsters.species[speciesIndex].pokemon_id + const specieState = state.speciesState[pokemonId] + + const sortDir = getNewSortDirectionFromSortBy(sortBy, specieState) + + const updatedSpeciesState = Object.assign({}, state, { + speciesState: updateSpeciesState(state, pokemonId, () => ({ sortDir, sortBy })) + }) + + return updateSpecies(updatedSpeciesState, speciesIndex, (speciesAtIndex) => { + const sorted = getSortedPokemon(speciesAtIndex, null, sortBy, sortDir) + + return { + pokemon: sorted + } + }) + }, + + TOGGLE_FAVORITE_POKEMON_SUCCESS(state, action) { + return updateMonster(state, action.payload) + }, + + TOGGLE_FAVORITE_POKEMON_FAILED(state, action) { + console.error(action.payload) // eslint-disable-line + return state + }, + + POWER_UP_POKEMON_SUCCESS(state, action) { + const pokemon = action.payload + const message = `Upgraded ${pokemon.nickname} succesfully!` + const title = `Power Up ${pokemon.nickname}` + ipcRenderer.send('information-dialog', message, title) + + return state + }, + + POWER_UP_POKEMON_FAILED(state, action) { + console.error(action.payload) // eslint-disable-line + return state + }, + + RENAME_POKEMON_SUCCESS(state, action) { + return updateMonster(state, action.payload) + }, + + RENAME_POKEMON_FAILED(state, action) { + console.error(action.payload) // eslint-disable-line + return state + }, + + TRANSFER_POKEMON_SUCCESS(state, action) { + const pokemon = action.payload + console.info(`Transferred ${pokemon.id}`) // eslint-disable-line + return state + }, + + TRANSFER_POKEMON_FAILED(state, action) { + console.error(action.payload) // eslint-disable-line + return state + }, + + EVOLVE_POKEMON_SUCCESS(state, action) { + const pokemon = action.payload + console.info(`Evolved ${pokemon.id}`) // eslint-disable-line + return state + }, + + EVOLVE_POKEMON_FAILED(state, action) { + console.error(action.payload) // eslint-disable-line + return state + }, + + COLLAPSE_BY_SPECIES(state, action) { + const specie = action.payload + + if (specie.count < 1) return state + + return Object.assign({}, state, { + speciesState: updateSpeciesState(state, specie.pokemon_id, (speciesState) => { + const newCollapsed = !speciesState.collapsed + + return { collapsed: newCollapsed } + }) + }) + }, + + CHECK_ALL_BY_SPECIES(state, action) { + const species = action.payload + + let selectedCount = state.selectedCount + + const speciesState = updateSpeciesState(state, species.pokemon_id, (specieState) => { + const newCheckAllState = !specieState.checkAll + const newPokemonState = {} + const ids = Object.keys(specieState.pokemonState) + + ids.forEach(id => { + if (newCheckAllState !== specieState.pokemonState[id].check) { + if (newCheckAllState) { + selectedCount++ + } else { + selectedCount-- + } + } + + newPokemonState[id] = Object.assign( + {}, + specieState.pokemonState[id], + { check: newCheckAllState } + ) + }) + + return { + checkAll: newCheckAllState, + pokemonState: newPokemonState + } + }) + + return Object.assign({}, state, { + speciesState, + selectedCount + }) + }, + + CHECK_POKEMON(state, action) { + const pokemon = action.payload + + let selectedCount = state.selectedCount + + const speciesState = updateSpeciesState(state, + String(pokemon.pokemon_id), + (specieState) => { + const updatedPokemonState = updatePokemonState( + specieState, + String(pokemon.id), + (pokemonState) => { + const newChecked = !pokemonState.check + + if (newChecked) { + selectedCount++ + } else { + selectedCount-- + } + + return { check: newChecked } + } + ) + + return { + checkAll: every(updatedPokemonState, { check: true }), + pokemonState: updatedPokemonState + } + } + ) + + return Object.assign({}, state, { + speciesState, + selectedCount + }) + }, + + SORT_ALL_SPECIES(state, action) { + const sortBy = action.payload + + const sortDir = getNewSortDirectionFromSortBy(sortBy, state) + + const monsters = Object.assign({}, state.monsters, { + species: getSortedSpecies(state.monsters, sortBy, sortDir) + }) + + return Object.assign({}, state, { + sortDir, + sortBy, + monsters + }) + }, + + SORT_WITH_DEFAULTS(state, action) { + const { + defaultPokedexSortBy, + defaultPokedexSortDirection, + defaultSpecieSortBy, + defaultSpecieSortDirection, + } = action.payload + + const updatedSpeciesAndMonstersState = Object.assign({}, state.monsters, { + species: getSortedSpecies(state.monsters, defaultPokedexSortBy, defaultPokedexSortDirection).map((s) => { + const sorted = getSortedPokemon(s, null, defaultSpecieSortBy, defaultSpecieSortDirection) + + return Object.assign({}, s, { + pokemon: sorted + }) + }) + }) + + const updatedSpeciesState = mapValues(state.speciesState, (specieState) => { + const newSpecieState = Object.assign({}, specieState, { + sortBy: defaultSpecieSortBy, + sortDir: defaultSpecieSortDirection, + }) + + return newSpecieState + }) + + return Object.assign({}, state, { + monsters: updatedSpeciesAndMonstersState, + sortBy: defaultPokedexSortBy, + sortDir: defaultPokedexSortDirection, + speciesState: updatedSpeciesState, + }) + }, +}, initialState) diff --git a/app/screens/Detail/components/CinematicMove.js b/app/screens/Detail/components/CinematicMove.js index f57b633..cfd035f 100644 --- a/app/screens/Detail/components/CinematicMove.js +++ b/app/screens/Detail/components/CinematicMove.js @@ -1,7 +1,9 @@ import React, { PropTypes } from 'react' -import times from 'lodash/times' +import { + times +} from 'lodash' import Tooltip from '../../Tooltip' diff --git a/app/screens/Detail/components/ModalBody.js b/app/screens/Detail/components/ModalBody.js index de69d34..5396c00 100644 --- a/app/screens/Detail/components/ModalBody.js +++ b/app/screens/Detail/components/ModalBody.js @@ -21,13 +21,11 @@ const ModalBody = React.createClass({ defense: PropTypes.string.isRequired, cpPerUpgrade: PropTypes.string.isRequired, candies: PropTypes.number.isRequired, - spriteImageName: PropTypes.string.isRequired, fastMove: PropTypes.object.isRequired, chargedMove: PropTypes.object.isRequired, evolvesTo: PropTypes.node, possibleQuickMoves: PropTypes.array, possibleCinematicMoves: PropTypes.array, - monsterUpdater: PropTypes.func, }, render() { @@ -45,13 +43,11 @@ const ModalBody = React.createClass({ defense, cpPerUpgrade, candies, - spriteImageName, fastMove, chargedMove, evolvesTo, possibleQuickMoves, possibleCinematicMoves, - monsterUpdater, } = this.props const quickMoves = possibleQuickMoves.map((possibleQuickMove, i) => @@ -79,7 +75,7 @@ const ModalBody = React.createClass({ title="Listen to Cry" alt="Profile Sprite" id="pokemon_profile_sprite" - src={`./imgs/3d/${spriteImageName}.jpg`} + src={`./imgs/3d/${pokemon.pokemon_id}.png`} />