├── public ├── images │ ├── favicon.ico │ └── favicon.ico:Zone.Identifier └── index.html ├── src ├── fonts │ ├── optimusprinceps.woff │ ├── optimusprinceps.woff2 │ ├── optimusprincepssemibold.woff │ └── optimusprincepssemibold.woff2 ├── assets │ ├── custom │ │ ├── Kindred_pixel.png │ │ ├── Kindred_sf_pixel.png │ │ ├── Kindred_pixel.png:Zone.Identifier │ │ └── pixil-frame-0_2.png:Zone.Identifier │ └── RankedEmblems │ │ ├── Emblem_Gold.png │ │ ├── Emblem_Iron.png │ │ ├── Emblem_Bronze.png │ │ ├── Emblem_Diamond.png │ │ ├── Emblem_Master.png │ │ ├── Emblem_Silver.png │ │ ├── Emblem_Challenger.png │ │ ├── Emblem_Platinum.png │ │ └── Emblem_Grandmaster.png ├── config.js ├── redux │ ├── search.js │ ├── configureStore.js │ ├── freeRotation.js │ ├── app.js │ ├── leaderboard.js │ └── summonerInfo.js ├── index.js ├── components │ ├── styles │ │ ├── Home.css │ │ ├── Leaderboard.css │ │ ├── Search.css │ │ ├── FreeRotation.css │ │ ├── SummonerInfo.css │ │ └── NavBar.css │ ├── Footer.js │ ├── Home.js │ ├── BGSlider.js │ ├── Leaderboard.js │ ├── Search.js │ ├── FreeRotation.js │ ├── NavBar.js │ └── SummonerInfo.js ├── App.js └── index.css ├── .gitignore ├── package.json └── README.md /public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/public/images/favicon.ico -------------------------------------------------------------------------------- /src/fonts/optimusprinceps.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/fonts/optimusprinceps.woff -------------------------------------------------------------------------------- /src/fonts/optimusprinceps.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/fonts/optimusprinceps.woff2 -------------------------------------------------------------------------------- /src/assets/custom/Kindred_pixel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/custom/Kindred_pixel.png -------------------------------------------------------------------------------- /src/assets/custom/Kindred_sf_pixel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/custom/Kindred_sf_pixel.png -------------------------------------------------------------------------------- /src/fonts/optimusprincepssemibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/fonts/optimusprincepssemibold.woff -------------------------------------------------------------------------------- /src/assets/RankedEmblems/Emblem_Gold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/RankedEmblems/Emblem_Gold.png -------------------------------------------------------------------------------- /src/assets/RankedEmblems/Emblem_Iron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/RankedEmblems/Emblem_Iron.png -------------------------------------------------------------------------------- /src/fonts/optimusprincepssemibold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/fonts/optimusprincepssemibold.woff2 -------------------------------------------------------------------------------- /src/assets/RankedEmblems/Emblem_Bronze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/RankedEmblems/Emblem_Bronze.png -------------------------------------------------------------------------------- /src/assets/RankedEmblems/Emblem_Diamond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/RankedEmblems/Emblem_Diamond.png -------------------------------------------------------------------------------- /src/assets/RankedEmblems/Emblem_Master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/RankedEmblems/Emblem_Master.png -------------------------------------------------------------------------------- /src/assets/RankedEmblems/Emblem_Silver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/RankedEmblems/Emblem_Silver.png -------------------------------------------------------------------------------- /src/assets/RankedEmblems/Emblem_Challenger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/RankedEmblems/Emblem_Challenger.png -------------------------------------------------------------------------------- /src/assets/RankedEmblems/Emblem_Platinum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/RankedEmblems/Emblem_Platinum.png -------------------------------------------------------------------------------- /src/assets/RankedEmblems/Emblem_Grandmaster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DanceOfArrows/lol-finder-front/HEAD/src/assets/RankedEmblems/Emblem_Grandmaster.png -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | const production = process.env.NODE_ENV === "production"; 2 | export const baseUrl = production ? 'https://lol-finder-back.herokuapp.com' : 'http://localhost:8080'; -------------------------------------------------------------------------------- /public/images/favicon.ico:Zone.Identifier: -------------------------------------------------------------------------------- 1 | [ZoneTransfer] 2 | ZoneId=3 3 | ReferrerUrl=https://hnet.com/png-to-ico/ 4 | HostUrl=https://hnet.com/png-to-ico/download/20200513-11-PgZC2oOMfZC3kSEC-SnMTHd/favicon.ico 5 | -------------------------------------------------------------------------------- /src/assets/custom/Kindred_pixel.png:Zone.Identifier: -------------------------------------------------------------------------------- 1 | [ZoneTransfer] 2 | ZoneId=3 3 | ReferrerUrl=https://cdn.discordapp.com/attachments/384553463767367680/710239194596769842/Kindred_pixel.png 4 | HostUrl=https://cdn.discordapp.com/attachments/384553463767367680/710239194596769842/Kindred_pixel.png 5 | -------------------------------------------------------------------------------- /src/assets/custom/pixil-frame-0_2.png:Zone.Identifier: -------------------------------------------------------------------------------- 1 | [ZoneTransfer] 2 | ZoneId=3 3 | ReferrerUrl=https://cdn.discordapp.com/attachments/472586743686889482/710337287246053386/pixil-frame-0_2.png 4 | HostUrl=https://cdn.discordapp.com/attachments/472586743686889482/710337287246053386/pixil-frame-0_2.png 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/redux/search.js: -------------------------------------------------------------------------------- 1 | const SET_REGION = 'lolfinder/search/SET_REGION'; 2 | 3 | export const setRegion = region => ({ type: SET_REGION, region }) 4 | 5 | export const changeRegion = (region) => async dispatch => { 6 | dispatch(setRegion(region)); 7 | } 8 | 9 | export default function reducer(state = {}, action) { 10 | switch (action.type) { 11 | case SET_REGION: { 12 | return { 13 | ...state, 14 | region: action.region, 15 | } 16 | } 17 | default: return state; 18 | } 19 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { BrowserRouter as Router, Route } from 'react-router-dom'; 4 | import { Provider } from 'react-redux'; 5 | 6 | import App from './App'; 7 | import './index.css'; 8 | 9 | import configureStore from './redux/configureStore'; 10 | 11 | const store = configureStore(); 12 | 13 | ReactDOM.render( 14 | 15 | 16 | 17 | 18 | 19 | 20 | , 21 | document.getElementById('root') 22 | ); 23 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LoL Finder 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/redux/configureStore.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, combineReducers, compose } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | 4 | import freeRotation from './freeRotation'; 5 | import app from './app'; 6 | import leaderboard from './leaderboard'; 7 | import search from './search'; 8 | import summonerInfo from './summonerInfo'; 9 | 10 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 11 | 12 | const reducer = combineReducers({ 13 | freeRotation, 14 | app, 15 | leaderboard, 16 | summonerInfo, 17 | search, 18 | }); 19 | 20 | const configureStore = initialState => { 21 | return createStore( 22 | reducer, 23 | initialState, 24 | composeEnhancers(applyMiddleware(thunk)), 25 | ); 26 | }; 27 | 28 | export default configureStore; -------------------------------------------------------------------------------- /src/components/styles/Home.css: -------------------------------------------------------------------------------- 1 | .splash-container { 2 | align-items: center; 3 | color: white; 4 | display: flex; 5 | flex: 1; 6 | flex-flow: column; 7 | justify-content: space-evenly; 8 | } 9 | 10 | .splash-section { 11 | background-color: rgba(0,0,0, 0.6); 12 | border-radius: 12px; 13 | padding: 2%; 14 | width: 90%; 15 | } 16 | 17 | .splash-title { 18 | font-weight: 700; 19 | font-size: 1rem; 20 | margin: 0 0 2.5% 0; 21 | width: max-content; 22 | } 23 | 24 | .splash-title:after { 25 | display: block; 26 | content: ""; 27 | height: 1px; 28 | background-color: #CDB470; 29 | transition: all 0.15s ease-in-out 0s; 30 | } 31 | 32 | @media only screen and (min-width: 961px) { 33 | .splash-title { 34 | font-size: 2rem; 35 | } 36 | 37 | .splash-section { 38 | width: 60%; 39 | } 40 | } -------------------------------------------------------------------------------- /src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Footer = () => { 4 | return ( 5 | 19 | ) 20 | } 21 | 22 | export default Footer; -------------------------------------------------------------------------------- /src/redux/freeRotation.js: -------------------------------------------------------------------------------- 1 | import { baseUrl } from '../config'; 2 | import { setLoadStateFalse, setLoadStateTrue } from './app'; 3 | 4 | const LOAD_CHAMP_ROTATION = 'lolfinder/freeRotation/LOAD_CHAMP_ROTATION'; 5 | 6 | export const loadChampRotation = champRotation => ({ type: LOAD_CHAMP_ROTATION, champRotation }); 7 | 8 | export const getChampRotation = () => async dispatch => { 9 | dispatch(setLoadStateTrue()); 10 | const res = await fetch(`${baseUrl}/rotation/NA1`); 11 | 12 | if (res.ok) { 13 | const champRotation = await res.json(); 14 | dispatch(loadChampRotation(champRotation)); 15 | dispatch(setLoadStateFalse()); 16 | }; 17 | }; 18 | 19 | export default function reducer(state = {}, action) { 20 | switch (action.type) { 21 | case LOAD_CHAMP_ROTATION: { 22 | return { 23 | ...state, 24 | champRotation: action.champRotation, 25 | }; 26 | } 27 | default: return state; 28 | } 29 | } -------------------------------------------------------------------------------- /src/redux/app.js: -------------------------------------------------------------------------------- 1 | const SET_LOAD_STATE_FALSE = 'lolfinder/app/SET_LOAD_STATE_FALSE'; 2 | const SET_LOAD_STATE_TRUE = 'lolfinder/app/SET_LOAD_STATE_TRUE'; 3 | 4 | export const setLoadFalse = () => ({ type: SET_LOAD_STATE_FALSE, loading: false }); 5 | export const setLoadTrue = () => ({ type: SET_LOAD_STATE_TRUE, loading: true }); 6 | 7 | export const setLoadStateFalse = () => async dispatch => { 8 | dispatch(setLoadFalse()); 9 | }; 10 | 11 | export const setLoadStateTrue = () => async dispatch => { 12 | dispatch(setLoadTrue()); 13 | }; 14 | 15 | export default function reducer(state = {}, action) { 16 | switch (action.type) { 17 | case SET_LOAD_STATE_FALSE: { 18 | return { 19 | ...state, 20 | loading: action.loading, 21 | }; 22 | } 23 | case SET_LOAD_STATE_TRUE: { 24 | return { 25 | ...state, 26 | loading: action.loading, 27 | }; 28 | } 29 | 30 | default: return state; 31 | } 32 | } -------------------------------------------------------------------------------- /src/redux/leaderboard.js: -------------------------------------------------------------------------------- 1 | import { baseUrl } from '../config'; 2 | import { setLoadStateFalse, setLoadStateTrue } from './app'; 3 | 4 | const LOAD_LEADERBOARD = 'lolfinder/leaderboard/LOAD_LEADERBOARD'; 5 | 6 | export const loadLeaderboard = leaderboard => ({ type: LOAD_LEADERBOARD, leaderboard }); 7 | 8 | export const getLeaderboard = () => async dispatch => { 9 | dispatch(setLoadStateTrue()); 10 | const res = await fetch(`${baseUrl}/leaderboard/NA1`); 11 | 12 | if (res.ok) { 13 | const leaderboard = await res.json(); 14 | leaderboard.sort((a, b) => { 15 | return a.leaguePoints > b.leaguePoints ? -1 : 1; 16 | }) 17 | dispatch(loadLeaderboard(leaderboard)); 18 | dispatch(setLoadStateFalse()); 19 | }; 20 | }; 21 | 22 | export default function reducer(state = {}, action) { 23 | switch (action.type) { 24 | case LOAD_LEADERBOARD: { 25 | return { 26 | ...state, 27 | leaderboard: action.leaderboard, 28 | }; 29 | } 30 | default: return state; 31 | } 32 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lol-finder-front", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "react": "^16.13.1", 10 | "react-awesome-slider": "^4.1.0", 11 | "react-dom": "^16.13.1", 12 | "react-infinite": "^0.12.1", 13 | "react-redux": "^7.2.0", 14 | "react-router-dom": "^5.2.0", 15 | "react-scripts": "^3.4.3", 16 | "react-spinners": "^0.8.3", 17 | "react-transition-group": "^4.4.1", 18 | "redux": "^4.0.5", 19 | "redux-thunk": "^2.3.0", 20 | "typescript": "^3.9.2" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject", 27 | "heroku-postbuild": "npm run build" 28 | }, 29 | "eslintConfig": { 30 | "extends": "react-app" 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LoL Finder 2 | LoL Finder allows users to find profile data of players in the popular MOBA game "League of Legends". 3 | The data can normally be found within the client's search function, but is inaccessible while in-game or not 4 | on a desktop. This goal of this app is to alleviates this issue and to also display player data in an 5 | easy to read manner. Champions refer to the playable characters in the game. 6 | 7 | ![Preview](https://raw.githubusercontent.com/lullofthesea/portfolio/master/images/lol-finder.gif) 8 | 9 | Feature List: 10 | * Displays the current free champion rotation on the home page 11 | * Search for player profile which returns: 12 | * Champion Mastery 13 | * Current rank 14 | * Match history 15 | * Use of back end to hide API key and to filter return data 16 | 17 | List of Riot API's used: 18 | * Champion 19 | * Champion Mastery v4 20 | * League v4 21 | * Match v4 22 | * Summoner v4 23 | 24 | BackEnd Routes 25 | 1. Info 26 | * `/info` 27 | * Get requested player's rank, match-history, and relevant account info 28 | 2. Match 29 | * `/match` 30 | * Get specific match info 31 | 3. Champion 32 | * `/rotation` 33 | * Get current free champion rotation 34 | 35 | 36 | FrontEnd Routes 37 | 1. Home Page (`/`) 38 | 2. Rotation Page (`/rotation`) - Current free champion rotation 39 | 3. Summoner Page (`/summoner/:summonerName`) - Player info, match history, and player's champion mastery 40 | -------------------------------------------------------------------------------- /src/components/styles/Leaderboard.css: -------------------------------------------------------------------------------- 1 | .leaderboard-container { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | overflow-x: hidden; 6 | flex: 1; 7 | } 8 | 9 | .leaderboard-title { 10 | border-bottom: 1px solid #CDB470; 11 | } 12 | 13 | .leaderboard-summoner-container { 14 | background-color: rgba(0, 0, 0, 0.7); 15 | color: white; 16 | display: grid; 17 | grid-template: 1fr / 15% 40% 20% auto; 18 | padding: 2.5%; 19 | text-align: center; 20 | width: 100%; 21 | } 22 | 23 | .leaderboard-summoner-name { 24 | background: linear-gradient(to right, white 50%, rgba(0, 0, 0, 0) 50%); 25 | background-size: 201% 100%; 26 | background-position: right bottom; 27 | color: white; 28 | text-decoration: none; 29 | transition: all .15s ease-out; 30 | width: max-content; 31 | } 32 | 33 | .leaderboard-summoner-name:hover { 34 | background-position: left bottom; 35 | color: black; 36 | } 37 | 38 | @media only screen and (min-width: 961px) { 39 | .leaderboard-container { 40 | margin: 2.5% 0 2.5% 0; 41 | } 42 | 43 | .leaderboard-title { 44 | border-top-right-radius: 12px; 45 | border-top-left-radius: 12px; 46 | } 47 | 48 | .leaderboard-last { 49 | border-bottom-left-radius: 12px; 50 | border-bottom-right-radius: 12px; 51 | } 52 | 53 | .leaderboard-summoner-container { 54 | width: 40%; 55 | } 56 | } -------------------------------------------------------------------------------- /src/redux/summonerInfo.js: -------------------------------------------------------------------------------- 1 | import { baseUrl } from '../config'; 2 | import { setLoadStateFalse, setLoadStateTrue } from './app'; 3 | 4 | const LOAD_SUMMONER_DATA = 'lolfinder/summonerInfo/LOAD_SUMMONER_DATA'; 5 | const LOAD_MATCH_DATA = 'lolfinder/summonerInfo/LOAD_MATCH_DATA'; 6 | 7 | export const loadSummoner = (data) => ({ type: LOAD_SUMMONER_DATA, data }); 8 | export const loadMatchData = (matchData, idx) => ({ type: LOAD_MATCH_DATA, matchData, index: idx }); 9 | 10 | export const getSummonerInfo = (name, region) => async dispatch => { 11 | dispatch(setLoadStateTrue()); 12 | 13 | if (!region) region = 'NA1'; 14 | 15 | const res = await fetch(`${baseUrl}/info/${region}/${name}`); 16 | 17 | if (res.ok) { 18 | const data = await res.json(); 19 | const matches = data.matchHistory; 20 | 21 | dispatch(loadSummoner(data)); 22 | await Promise.all(matches.map(async (match, idx) => { 23 | const matchDataRes = await fetch(`${baseUrl}/match/${region}/${match.matchId}`) 24 | 25 | if (matchDataRes.ok) { 26 | const matchData = await matchDataRes.json(); 27 | 28 | dispatch(loadMatchData(matchData, idx)) 29 | } 30 | })) 31 | 32 | dispatch(setLoadStateFalse()); 33 | }; 34 | // TODO: Error Handling 35 | }; 36 | 37 | export default function reducer(state = {}, action) { 38 | switch (action.type) { 39 | case LOAD_SUMMONER_DATA: { 40 | return { 41 | ...state, 42 | summonerInfo: action.data, 43 | } 44 | } 45 | case LOAD_MATCH_DATA: { 46 | return { 47 | ...state, 48 | summonerInfo: { 49 | ...state.summonerInfo, 50 | matchHistory: { 51 | ...state.summonerInfo.matchHistory, 52 | [action.index]: { 53 | ...state.summonerInfo.matchHistory[action.index], 54 | matchData: action.matchData, 55 | } 56 | } 57 | } 58 | } 59 | } 60 | default: return state; 61 | } 62 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import NavBar from './components/NavBar'; 3 | import { Route, Switch } from 'react-router-dom'; 4 | import { TransitionGroup, CSSTransition } from "react-transition-group"; 5 | 6 | import Home from './components/Home'; 7 | import Footer from './components/Footer'; 8 | import FreeRotation from './components/FreeRotation'; 9 | import Leaderboard from './components/Leaderboard'; 10 | import SummonerInfo from './components/SummonerInfo'; 11 | import BGSlider from './components/BGSlider'; 12 | 13 | class App extends React.Component { 14 | // constructor(props) { 15 | // super(props); 16 | // } 17 | 18 | render() { 19 | return ( 20 | <> 21 | 22 | 23 |
24 | ( 25 | 26 | 31 |
32 | 33 | 34 | 35 | 36 | 37 |

404: Page not found

} /> 38 |
39 |
40 |
41 |
42 |
43 | )} /> 44 |
45 | 46 | 47 | ); 48 | } 49 | } 50 | 51 | export default App; 52 | -------------------------------------------------------------------------------- /src/components/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import 'react-awesome-slider/dist/styles.css'; 4 | import './styles/Home.css'; 5 | 6 | 7 | class Home extends React.Component { 8 | render() { 9 | return ( 10 |
11 |
12 |
13 |
14 | Overview 15 |
16 |
17 | LoL Finder is an app that displays a general overview of a player's stats in 18 | a MOBA (multiplayer online battle arena) game called League of Legends. In 19 | this game, players face off in two teams of fives playing characters dubbed as 20 | champions. 21 |
22 |
23 |
24 |
25 | Search 26 |
27 |
28 | Using the region selected in the dropdown menu, searches for a 29 | player with the given name and displays their stats and past ten games. Check 30 | the leaderboards if you don't know who to search for! 31 |
32 |
33 |
34 |
35 | Champion Rotation (Rotation) 36 |
37 |
38 | Displays the portrait of the champions (playable characters) that are 39 | available to play as without being owned by the player. These free 40 | characters are usually switched out every week hence the name 41 | 'champion rotation'. 42 |
43 |
44 |
45 |
46 | ); 47 | } 48 | } 49 | 50 | export default Home; 51 | -------------------------------------------------------------------------------- /src/components/styles/Search.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | @media only screen and (orientation: landscape) { 4 | 5 | } 6 | 7 | @media only screen and (min-width: 961px) { 8 | .search-container { 9 | align-items: center; 10 | background-color: rgba(0, 0, 0, 0.5); 11 | display: flex; 12 | height: 7.5vh; 13 | justify-content: center; 14 | margin-top: 10vh; 15 | position: fixed; 16 | transition: all 0.25s; 17 | width: 100%; 18 | z-index: 3; 19 | } 20 | 21 | .search-form-container { 22 | display: grid; 23 | grid-template: 1fr / 15% 70% 15%; 24 | height: 5vh; 25 | width: 30%; 26 | border: 1px solid black; 27 | border-radius: 8px; 28 | } 29 | 30 | .search-dropdown { 31 | border-radius: 8px; 32 | border-style: none; 33 | grid-column: 1 / 2; 34 | grid-row: 1 / 2; 35 | z-index: 6; 36 | } 37 | 38 | .search-dropdown select { 39 | background-image: 40 | linear-gradient(45deg, transparent 50%, gray 50%), 41 | linear-gradient(135deg, gray 50%, transparent 50%), 42 | linear-gradient(to right, #ccc, #ccc); 43 | background-position: 44 | 82.5%, 45 | 87.5%, 46 | 65%; 47 | background-size: 48 | 5px 5px, 49 | 5px 5px, 50 | 1px 1.5em; 51 | background-repeat: no-repeat; 52 | border: none; 53 | -webkit-border-top-right-radius: 8px; 54 | -webkit-border-bottom-right-radius: 8px; 55 | -moz-border-radius-topright: 8px; 56 | -moz-border-radius-bottomright: 8px; 57 | 58 | /* Reset */ 59 | -webkit-box-sizing: border-box; 60 | -moz-box-sizing: border-box; 61 | box-sizing: border-box; 62 | -webkit-appearance: none; 63 | -moz-appearance: none; 64 | margin: 0; 65 | 66 | /* Styling */ 67 | border-radius: 8px; 68 | padding:2px; 69 | height: 100%; 70 | width: 100%; 71 | background-color: white; 72 | font: inherit; 73 | padding: 10%; 74 | } 75 | 76 | .search-dropdown select:hover, .search-dropdown select:focus { 77 | background-image: 78 | linear-gradient(45deg, transparent 50%, #CDB470 50%), 79 | linear-gradient(135deg, #CDB470 50%, transparent 50%), 80 | linear-gradient(to right, #ccc, #ccc); 81 | } 82 | 83 | .search-input { 84 | border-radius: 8px; 85 | border-style: none; 86 | grid-column: 1 / 4; 87 | grid-row: 1 / 2; 88 | height: 100%; 89 | outline: 0; 90 | padding: 0 20% 0 20%; 91 | max-width: 100%; 92 | } 93 | 94 | .search-btn-navlink { 95 | align-items: center; 96 | border-bottom-right-radius: 8px; 97 | border-top-right-radius: 8px; 98 | display: flex; 99 | grid-column: 3 / 4; 100 | grid-row: 1 / 2; 101 | height: 100%; 102 | justify-content: center; 103 | text-decoration: none; 104 | width: 100%; 105 | z-index: 6; 106 | } 107 | 108 | .search-btn-navlink button { 109 | background-color: #CDB470; 110 | border: none; 111 | border-bottom-right-radius: 8px; 112 | border-top-right-radius: 8px; 113 | height: 100%; 114 | width: 100%; 115 | } 116 | } 117 | 118 | -------------------------------------------------------------------------------- /src/components/BGSlider.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import AwesomeSlider from 'react-awesome-slider'; 3 | import withAutoplay from 'react-awesome-slider/dist/autoplay'; 4 | 5 | // import BPanth from '../assets/DataDragon/img/champion/splash/Pantheon_8.jpg'; 6 | // import DSMorde from '../assets/DataDragon/img/champion/splash/Mordekaiser_6.jpg'; 7 | // import Kindred from '../assets/DataDragon/img/champion/splash/Kindred_0.jpg'; 8 | // import SSNami from '../assets/DataDragon/img/champion/splash/Nami_15.jpg'; 9 | 10 | const AutoplaySlider = withAutoplay(AwesomeSlider); 11 | 12 | 13 | // Dynimcally import and use images (works, but I want to use specified 5) 14 | // function importImages(r) { 15 | // let images = {}; 16 | // r.keys().map((item, index) => { return images[item.replace('./', '')] = r(item); }); 17 | // return images; 18 | // } 19 | 20 | // const champSplash = importImages(require.context('../assets/DataDragon/img/champion/splash', false, /\.jpg/)); 21 | 22 | 23 | class BGSlider extends React.Component { 24 | shouldComponentUpdate() { 25 | return false; 26 | } 27 | 28 | // Choose a random number and use the splash from that number (works, but I want to use specified 5) 29 | // generateImageDiv() { 30 | // const generateRandomNum = () => { 31 | // const min = 0; 32 | // const max = 1205; 33 | // return Math.floor(Math.random() * (max - min)) + min; 34 | // } 35 | 36 | // return
37 | // } 38 | 39 | shuffle() { //Fisher-Yates (aka Knuth) shuffle > https://github.com/Daplie/knuth-shuffle 40 | let array = [ 41 |
, 42 |
, 43 |
, 44 |
, 45 | ]; 46 | let currentIndex = array.length, temporaryValue, randomIndex; 47 | 48 | 49 | // While there remain elements to shuffle... 50 | while (0 !== currentIndex) { 51 | 52 | // Pick a remaining element... 53 | randomIndex = Math.floor(Math.random() * currentIndex); 54 | currentIndex -= 1; 55 | 56 | // And swap it with the current element. 57 | temporaryValue = array[currentIndex]; 58 | array[currentIndex] = array[randomIndex]; 59 | array[randomIndex] = temporaryValue; 60 | } 61 | 62 | return array; 63 | } 64 | 65 | render() { 66 | const imageDivs = this.shuffle(); 67 | return ( 68 |
69 | 79 | 80 | {imageDivs[0]} 81 | {imageDivs[1]} 82 | {imageDivs[2]} 83 | {imageDivs[3]} 84 | 85 | 86 |
87 | ) 88 | } 89 | } 90 | 91 | export default BGSlider; -------------------------------------------------------------------------------- /src/components/Leaderboard.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { NavLink } from 'react-router-dom'; 3 | import { connect } from 'react-redux'; 4 | import ClipLoader from "react-spinners/ClipLoader"; 5 | import { css } from "@emotion/core"; 6 | 7 | import { getLeaderboard } from '../redux/leaderboard'; 8 | import './styles/Leaderboard.css'; 9 | 10 | const override = css` 11 | border-color: #C44017; 12 | `; 13 | 14 | const Leaderboard = (props) => { 15 | const { getLeaderboard } = props; 16 | useEffect(() => { 17 | getLeaderboard(); 18 | }, [getLeaderboard]) 19 | 20 | const summonerGameCalc = (wins, losses) => { 21 | const totalGames = wins + losses; 22 | const winRatio = + (Math.floor(((wins / totalGames) * 100) * 100) / 100); 23 | 24 | return winRatio; 25 | } 26 | 27 | return ( 28 |
29 | {props.leaderboard && !props.loading ? ( 30 |
31 |
32 |
Rank
33 |
Name
34 |
LP
35 |
Win Rate
36 |
37 | {props.leaderboard.map((summoner, idx) => { 38 | const urlName = encodeURI(summoner.summonerName); 39 | if (idx === 299) { 40 | return ( 41 |
42 |
{idx + 1}
43 |
44 | 45 | {summoner.summonerName} 46 | 47 |
48 |
{summoner.leaguePoints}
49 |
{summonerGameCalc(summoner.wins, summoner.losses)}%
50 |
51 | ) 52 | } 53 | return ( 54 |
55 |
{idx + 1}
56 |
57 | 58 | {summoner.summonerName} 59 | 60 |
61 |
{summoner.leaguePoints}
62 |
{summonerGameCalc(summoner.wins, summoner.losses)}%
63 |
64 | ) 65 | })} 66 |
) : ( 67 |
68 | 74 |
75 | )} 76 |
77 | ); 78 | } 79 | 80 | const mapStateToProps = state => { 81 | return { 82 | leaderboard: state.leaderboard.leaderboard, 83 | loading: state.app.loading, 84 | }; 85 | }; 86 | 87 | const mapDispatchToProps = dispatch => { 88 | return { 89 | getLeaderboard: (...args) => dispatch(getLeaderboard(...args)), 90 | }; 91 | }; 92 | 93 | export default connect( 94 | mapStateToProps, 95 | mapDispatchToProps 96 | )( 97 | Leaderboard 98 | ); 99 | -------------------------------------------------------------------------------- /src/components/Search.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { withRouter } from 'react-router-dom'; 4 | import { changeRegion } from '../redux/search'; 5 | 6 | import './styles/Search.css' 7 | 8 | 9 | class Search extends React.Component { 10 | constructor(props) { 11 | super(props); 12 | 13 | this.state = { 14 | summonerName: '', 15 | region: 'NA1', 16 | } 17 | 18 | this.changeRegion = this.changeRegion.bind(this); 19 | this.redirectToSummoner = this.redirectToSummoner.bind(this); 20 | this.inputForm = null; 21 | this.updateSummonerName = this.updateProperty('summonerName'); 22 | this.updateRegion = this.updateProperty('region'); 23 | } 24 | 25 | 26 | 27 | componentDidMount() { 28 | const inputForm = document.querySelector('.search-input'); 29 | if (inputForm) this.inputForm = inputForm; 30 | } 31 | 32 | shouldComponentUpdate(nextProps, nextState) { 33 | if (nextProps.location.pathname !== this.props.location.pathname) { 34 | return true; 35 | } 36 | return nextState.summonerName === this.state.summonerName ? false : true; 37 | } 38 | 39 | changeRegion(e) { 40 | this.inputForm.focus(); 41 | this.updateRegion(e); 42 | this.props.changeRegion(e.target.value); 43 | } 44 | 45 | redirectToSummoner(e) { 46 | e.preventDefault(); 47 | if (this.state.summonerName) { 48 | this.props.history.push(`/summoner/${this.state.summonerName}`) 49 | this.inputForm.value = ''; 50 | this.setState({ 51 | summonerName: '' 52 | }); 53 | this.props.toggleSearch(); 54 | } else { 55 | return; 56 | } 57 | } 58 | 59 | updateProperty = property => e => { 60 | this.setState({ 61 | [property]: e.target.value 62 | }); 63 | } 64 | 65 | render() { 66 | return ( 67 |
68 |
69 |
70 | 85 |
86 |
87 | 93 |
94 | 97 |
98 | 99 |
100 | ); 101 | } 102 | } 103 | 104 | const mapStateToProps = state => { 105 | return { 106 | region: state.search.region, 107 | }; 108 | }; 109 | 110 | const mapDispatchToProps = dispatch => { 111 | return { 112 | changeRegion: (...args) => dispatch(changeRegion(...args)), 113 | }; 114 | }; 115 | 116 | export default withRouter(connect( 117 | mapStateToProps, 118 | mapDispatchToProps 119 | )( 120 | Search 121 | )); 122 | -------------------------------------------------------------------------------- /src/components/styles/FreeRotation.css: -------------------------------------------------------------------------------- 1 | .rotation-container { 2 | display: grid; 3 | margin: 0 10% 0 10%; 4 | justify-items: center; 5 | grid-template: 5vh 65vh / 1fr; 6 | width: 100%; 7 | } 8 | 9 | .rotation { 10 | display: grid; 11 | grid-template: repeat(5, 20%) / repeat(3, 1fr); 12 | grid-column: 1 / 4 13 | } 14 | 15 | .rotation-new-players { 16 | grid-template: repeat(5, 12.5vh) / repeat(2, 1fr); 17 | justify-items: center; 18 | grid-column: 1 / 4; 19 | display: none; 20 | } 21 | 22 | .free-champ, .free-champ-new-players { 23 | margin: 5vw; 24 | max-height: 10vh; 25 | max-width: 15vw; 26 | border: 1px solid #C44017; 27 | border-radius: 10px; 28 | box-shadow: 1px 1px 3px 0px #000000; 29 | -webkit-box-shadow: 1px 1px 3px 0px #000000; 30 | -moz-box-shadow: 1px 1px 3px 0px #000000; 31 | transition: all 0.15s; 32 | } 33 | 34 | .rotation-type { 35 | display: flex; 36 | justify-content: center; 37 | width: 100%; 38 | } 39 | 40 | .rotation-standard-button, .rotation-new-players-button { 41 | font-family: 'optimusprincepsregular'; 42 | background-color: Transparent; 43 | background-repeat:no-repeat; 44 | border: none; 45 | cursor:pointer; 46 | overflow: hidden; 47 | outline:none; 48 | opacity: 1; 49 | font-size: 2vmax; 50 | margin: 0 1vh 0 1vh; 51 | color: white; 52 | transition: all 0.15s; 53 | } 54 | 55 | .rotation-standard-button:after, .rotation-new-players-button:after { 56 | display: block; 57 | content: ""; 58 | width: 100%; 59 | height: 1px; 60 | background-color: #CDB470; 61 | visibility: hidden; 62 | transform: scaleX(0); 63 | transition: all 0.25s; 64 | } 65 | 66 | .rotation-standard-button:hover:after, .rotation-new-players-button:hover:after { 67 | visibility: visible; 68 | transform: scaleX(1); 69 | } 70 | 71 | .rotation-active:after { 72 | visibility: visible; 73 | transform: scaleX(1); 74 | } 75 | 76 | /* Make current selections transparent and new selection opaque*/ 77 | .rotation-type > button:not(.rotation-active) { 78 | transform: scale(0.75); 79 | } 80 | .rotation-type > button:hover:not(.rotation-active) { 81 | transform: scale(1); 82 | opacity: 1; 83 | } 84 | .rotation-active { 85 | transform: scale(1); 86 | } 87 | 88 | .free-champ:hover, .free-champ-new-players { 89 | border-radius: 0px; 90 | } 91 | 92 | .rotation-loading { 93 | align-items: center; 94 | width: 100%; 95 | display: flex; 96 | justify-content: center; 97 | } 98 | 99 | @media only screen and (orientation: landscape) { 100 | .rotation-container { 101 | margin: 0 10% 0 10%; 102 | grid-template: 7.5vh 65vh / 1fr; 103 | } 104 | 105 | .rotation { 106 | display: grid; 107 | grid-template: repeat(3, 20vh) / repeat(5, 1fr); 108 | } 109 | 110 | .rotation-new-players { 111 | display: none; 112 | grid-template: repeat(2, 30%) / repeat(5, 1fr); 113 | justify-items: center; 114 | } 115 | 116 | .free-champ:hover, .free-champ-new-players { 117 | border-radius: 0px; 118 | outline: 1px solid #FFAA40; 119 | outline-offset: 5px; 120 | -moz-outline-radius: 0px 40px 0px 40px; 121 | transform: scale(1.5); 122 | } 123 | } 124 | 125 | @media only screen and (min-width: 961px) { 126 | .rotation-container { 127 | margin: 0 10% 0 10%; 128 | grid-template: 7.5vh 65vh / 1fr; 129 | } 130 | 131 | .rotation { 132 | display: grid; 133 | grid-template: repeat(3, 25vh) / repeat(5, 1fr); 134 | } 135 | 136 | .rotation-new-players { 137 | display: none; 138 | grid-template: repeat(2, 30%) / repeat(5, 1fr); 139 | justify-items: center; 140 | grid-column: 1 / 4 141 | } 142 | 143 | .rotation-type { 144 | grid-row: 1 /2; 145 | } 146 | 147 | .free-champ, .free-champ-new-players { 148 | margin: 2vw; 149 | max-height: 10vh; 150 | max-width: 10vw; 151 | border: 1px solid #C44017; 152 | border-radius: 10px; 153 | box-shadow: 1px 1px 3px 0px #000000; 154 | -webkit-box-shadow: 1px 1px 3px 0px #000000; 155 | -moz-box-shadow: 1px 1px 3px 0px #000000; 156 | transition: all 0.15s; 157 | } 158 | 159 | .free-champ:hover, .free-champ-new-players { 160 | border-radius: 0px; 161 | outline: 1px solid #FFAA40; 162 | outline-offset: 5px; 163 | -moz-outline-radius: 0px 40px 0px 40px; 164 | transform: scale(1.5); 165 | } 166 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @-ms-viewport{ 2 | width: device-width; 3 | height: device-height; 4 | } 5 | 6 | @font-face { 7 | font-family: 'optimusprincepsregular'; 8 | src: url('./fonts/optimusprinceps.woff2') format('woff2'), 9 | url('./fonts/optimusprinceps.woff') format('woff'); 10 | } 11 | 12 | @font-face { 13 | font-family: 'optimusprincepssemiboldRg'; 14 | src: url('./fonts/optimusprincepssemibold.woff2') format('woff2'), 15 | url('./fonts/optimusprincepssemibold.woff') format('woff'); 16 | } 17 | 18 | * { 19 | font-family: 'optimusprincepsregular'; 20 | } 21 | 22 | html, body, #root, #app, #app, .page-content, #body-container > div { 23 | height: 100%; 24 | margin: 0; 25 | overflow: auto; 26 | } 27 | 28 | #body-container { 29 | position: absolute; 30 | height: 90%; 31 | width: 100%; 32 | top: 0; 33 | display: flex; 34 | transition: all 0.25s; 35 | } 36 | 37 | .page-content { 38 | display: flex; 39 | flex-direction: column; 40 | overflow: auto; 41 | width: 100%; 42 | } 43 | 44 | .awesome-slider { 45 | position: fixed; 46 | top: 10%; 47 | height: 100%; 48 | width: 100%; 49 | z-index: -1; 50 | } 51 | 52 | .awesome-slider > div { 53 | position: relative; 54 | height: 100%; 55 | width: 100%; 56 | } 57 | 58 | 59 | .awssld__content > img { 60 | -webkit-filter: blur(5px); 61 | -moz-filter: blur(5px); 62 | -o-filter: blur(5px); 63 | -ms-filter: blur(5px); 64 | filter: blur(5px); 65 | } 66 | 67 | /* Page Transitions */ 68 | .fade-enter { 69 | opacity: 0; 70 | } 71 | .fade-enter.fade-enter-active { 72 | opacity: 1; 73 | transition: opacity 300ms; 74 | } 75 | .fade-exit { 76 | opacity: 1; 77 | } 78 | .fade-exit.fade-exit-active { 79 | opacity: 0; 80 | transition: opacity 300ms; 81 | } 82 | 83 | .body-content { 84 | display: flex; 85 | width: 100%; 86 | position: relative; 87 | transition: all .5s; 88 | will-change: opacity, right; 89 | flex: 1; 90 | } 91 | 92 | /* Footer Container */ 93 | #footer-container { 94 | background-color: #111111; 95 | display: grid; 96 | grid-template: 1fr / 5vw 2fr 1fr 5vw; 97 | z-index: 5; 98 | position: relative; 99 | height: 10vh; 100 | width: 100%; 101 | bottom: 0; 102 | transition: all .5s; 103 | margin-top: auto; 104 | flex-shrink: 0; 105 | } 106 | 107 | .footer-text { 108 | font-family: 'optimusprincepsregular'; 109 | color: #B0AEAE; 110 | grid-column: 2 / 3; 111 | justify-self: center; 112 | align-self: center; 113 | font-size: 1vh; 114 | text-align: left; 115 | } 116 | 117 | .footer-links { 118 | font-family: 'optimusprincepsregular'; 119 | color: #B0AEAE; 120 | grid-column: 3 / 4; 121 | justify-self: center; 122 | align-self: center; 123 | font-size: 1.5vh; 124 | text-align: center; 125 | display: flex; 126 | justify-content: space-around; 127 | flex-direction: column; 128 | width: 100%; 129 | } 130 | 131 | .footer-links > a { 132 | align-self: center; 133 | justify-self: center; 134 | font-family: 'optimusprincepsregular'; 135 | font-weight:normal; 136 | font-style:normal; 137 | color: white; 138 | margin: 0; 139 | text-decoration: none; 140 | } 141 | 142 | /* Landscape Mobile */ 143 | @media only screen and (orientation: landscape) { 144 | .footer-text { 145 | font-size: 0.8vw; 146 | } 147 | 148 | .footer-links { 149 | flex-direction: row; 150 | } 151 | } 152 | 153 | /* Larger Display */ 154 | @media only screen and (min-width: 961px) { 155 | #footer { 156 | margin-right: 0; 157 | } 158 | 159 | /* Footer */ 160 | #footer-container { 161 | height: 10%; 162 | } 163 | 164 | .footer-text { 165 | font-family: 'optimusprincepsregular'; 166 | color: #B0AEAE; 167 | grid-column: 2 / 3; 168 | justify-self: center; 169 | align-self: center; 170 | font-size: 75%; 171 | border-right: 1px solid #CDB470; 172 | } 173 | 174 | .footer-text > div { 175 | width: 97.5%; 176 | } 177 | 178 | .footer-links { 179 | flex-direction: row; 180 | } 181 | 182 | .awesome-slider { 183 | max-height: 100%; 184 | } 185 | 186 | .footer-links > a:after { 187 | display: block; 188 | content: ""; 189 | width: 100%; 190 | height: 1px; 191 | background-color: #CDB470; 192 | visibility: hidden; 193 | transform: scaleX(0); 194 | transition: all 0.15s ease-in-out 0s; 195 | will-change: visibility, transform; 196 | } 197 | 198 | .footer-links > a:hover:after { 199 | visibility: visible; 200 | transform: scaleX(1); 201 | } 202 | } -------------------------------------------------------------------------------- /src/components/FreeRotation.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { css } from "@emotion/core"; 4 | import ClipLoader from "react-spinners/ClipLoader"; 5 | 6 | import { getChampRotation } from '../redux/freeRotation'; 7 | import './styles/FreeRotation.css'; 8 | 9 | const override = css` 10 | border-color: #C44017; 11 | `; 12 | 13 | class FreeRotation extends React.Component { 14 | constructor(props) { 15 | super(props); 16 | 17 | // this.importAll = this.importAll.bind(this); 18 | this.showStandardRotation = this.showStandardRotation.bind(this); 19 | this.showNewPlayerRotation = this.showNewPlayerRotation.bind(this); 20 | this.standardRotationBtn = null; 21 | this.newPlayerRotationBtn = null; 22 | this.standardRotation = null; 23 | this.newPlayerRotation = null; 24 | } 25 | 26 | componentDidMount() { 27 | this.props.getChampRotation(); 28 | } 29 | 30 | componentDidUpdate() { 31 | const standardRotationBtn = document.querySelector('.rotation-standard-button'); 32 | const newPlayerRotationBtn = document.querySelector('.rotation-new-players-button') 33 | const standardRotation = document.querySelector('.rotation'); 34 | const newPlayerRotation = document.querySelector('.rotation-new-players') 35 | 36 | if (standardRotationBtn && newPlayerRotationBtn && standardRotation && newPlayerRotation) { 37 | this.standardRotationBtn = standardRotationBtn; 38 | this.newPlayerRotationBtn = newPlayerRotationBtn; 39 | this.standardRotation = standardRotation; 40 | this.newPlayerRotation = newPlayerRotation; 41 | } 42 | } 43 | 44 | // importAll(r) { 45 | // let images = {}; 46 | // r.keys().map((item, index) => { return images[item.replace('./', '')] = r(item); }); 47 | // return images; 48 | // } 49 | 50 | showStandardRotation() { 51 | this.standardRotationBtn.classList.add('rotation-active'); 52 | this.newPlayerRotationBtn.classList.remove('rotation-active'); 53 | this.standardRotation.style.display = 'grid'; 54 | this.newPlayerRotation.style.display = 'none'; 55 | } 56 | 57 | showNewPlayerRotation() { 58 | this.standardRotationBtn.classList.remove('rotation-active'); 59 | this.newPlayerRotationBtn.classList.add('rotation-active'); 60 | this.standardRotation.style.display = 'none'; 61 | this.newPlayerRotation.style.display = 'grid'; 62 | } 63 | 64 | render() { 65 | // const champIcons = this.importAll(require.context('../assets/DataDragon/img/champion/tiles', false, /_0\.jpg/)); 66 | return ( 67 |
68 | {this.props.champRotation ? ( 69 |
70 | {/* This is messy, but basically check if the fetch for champs finished. 71 | If it is not finished, render the loading symbol. If it is finished, then 72 | update the page to load in all the icons. */} 73 | <> 74 |
75 | 80 | 85 |
86 |
87 | {this.props.champRotation.freeChampionRotation.map((champion, index, arr) => { 88 | const champImgSrc = `https://lol-finder.s3-us-west-1.amazonaws.com/DataDragon/img/champion/tiles/${champion}_0.jpg`; 89 | 90 | return ( 91 | ChampIcon 97 | ) 98 | })} 99 |
100 |
101 | {this.props.champRotation.freeChampionRotationForNewPlayers.map((champion, index, arr) => { 102 | const champImgSrc = `https://lol-finder.s3-us-west-1.amazonaws.com/DataDragon/img/champion/tiles/${champion}_0.jpg`; 103 | 104 | return ( 105 | ChampIcon 111 | ) 112 | })} 113 |
114 | 115 | 116 |
) : ( 117 |
118 | 124 |
125 | ) 126 | } 127 |
128 | ); 129 | } 130 | } 131 | 132 | const mapStateToProps = state => { 133 | return { 134 | champRotation: state.freeRotation.champRotation, 135 | loading: state.app.loading, 136 | }; 137 | }; 138 | 139 | const mapDispatchToProps = dispatch => { 140 | return { 141 | getChampRotation: () => dispatch(getChampRotation()), 142 | }; 143 | }; 144 | 145 | export default connect( 146 | mapStateToProps, 147 | mapDispatchToProps 148 | )( 149 | FreeRotation 150 | ); -------------------------------------------------------------------------------- /src/components/styles/SummonerInfo.css: -------------------------------------------------------------------------------- 1 | .summoner-info-container { 2 | width: 100%; 3 | } 4 | 5 | .summoner-info-main { 6 | width: 100%; 7 | background-color: rgba(0, 0, 0, 0.3); 8 | display: flex; 9 | z-index: 3; 10 | position: relative; 11 | padding: 5% 0 5% 0; 12 | } 13 | 14 | .summoner-info-rank { 15 | display: none; 16 | align-items: center; 17 | justify-content: space-evenly; 18 | flex-direction: column; 19 | color: white; 20 | font-family: 'optimusprincepsregular'; 21 | font-size: 80%; 22 | text-shadow: 1px 1px 20px #000000; 23 | margin-right: 50%; 24 | } 25 | 26 | .summoner-info-rank > img { 27 | max-height: 256px; 28 | max-width: 256px; 29 | } 30 | 31 | .summoner-info-player { 32 | display: flex; 33 | justify-content: space-around; 34 | align-content: center; 35 | width: 100%; 36 | } 37 | 38 | .summoner-info-portrait-area { 39 | display: flex; 40 | flex-direction: column; 41 | align-self: center; 42 | } 43 | 44 | .summoner-info-icon-cut { 45 | font-size: 100%; 46 | width: 100px; 47 | height: 100px; 48 | overflow: hidden; 49 | border-radius: 50%; 50 | border: 1px #CDB470 solid; 51 | color: white; 52 | } 53 | 54 | .summoner-info-icon { 55 | max-height: 100%; 56 | max-width: 100%; 57 | } 58 | 59 | .summoner-info-level { 60 | color: white; 61 | align-self: center; 62 | margin-top: 10%; 63 | } 64 | 65 | .summoner-info-items { 66 | color: white; 67 | font-size: 100%; 68 | display: flex; 69 | flex-direction: column; 70 | justify-content: space-around; 71 | } 72 | 73 | .summoner-info-name { 74 | text-align: center; 75 | } 76 | 77 | .summoner-info-name:after, .summoner-info-level:after { 78 | display: block; 79 | content: ""; 80 | width: 100%; 81 | height: 1px; 82 | background-color: #CDB470; 83 | opacity: 0.7; 84 | } 85 | 86 | .summoner-info-match { 87 | display: grid; 88 | grid-template: 50% 50% / 40% auto; 89 | padding: 1%; 90 | color: white; 91 | } 92 | 93 | .match-champion-icon-cut { 94 | display: none; 95 | } 96 | 97 | .match-champion-icon-cut img { 98 | max-width: 100%; 99 | } 100 | 101 | .match-icons { 102 | display: flex; 103 | justify-content: space-evenly; 104 | align-items: center; 105 | grid-row: 1 / 2; 106 | grid-column: 1 / 2; 107 | } 108 | 109 | .match-summoner-spells, .match-items { 110 | display: flex; 111 | flex-direction: column; 112 | flex-wrap: nowrap; 113 | justify-content: space-between; 114 | } 115 | 116 | .match-items { 117 | width: 45%; 118 | } 119 | 120 | .match-items div { 121 | display: flex; 122 | justify-content: space-between; 123 | } 124 | 125 | .match-items div img, .match-no-item, .match-summoner-spells img { 126 | background-color: rgba(0, 0, 0, 0.3); 127 | border-radius: 12px; 128 | height: 24px; 129 | width: 24px; 130 | flex-shrink: 0; 131 | } 132 | 133 | .match-stats { 134 | font-size: 12pt; 135 | display: flex; 136 | flex-direction: column; 137 | justify-content: center; 138 | align-items: center; 139 | grid-row: 2 / 3; 140 | grid-column: 1 / 2; 141 | } 142 | 143 | .match-participants { 144 | display: flex; 145 | justify-content: space-around; 146 | flex-wrap: nowrap; 147 | } 148 | 149 | .match-participant-name, .match-participant-nameCurrent { 150 | background: linear-gradient(to right, white 50%, rgba(0, 0, 0, 0) 50%); 151 | background-size: 201% 100%; 152 | background-position: right bottom; 153 | color: white; 154 | margin-left: 2.5%; 155 | text-decoration: none; 156 | transition: all .15s ease-out; 157 | white-space: nowrap; 158 | font-size: 2vmin; 159 | } 160 | 161 | .match-participants { 162 | grid-row: 1 / 3; 163 | grid-column: 2 / 3; 164 | } 165 | 166 | .match-participant-imageContainer { 167 | display: flex; 168 | align-items: center; 169 | justify-content: flex-start; 170 | 171 | } 172 | 173 | .match-participant-nameCurrent:after { 174 | display: block; 175 | content: ""; 176 | width: 100%; 177 | height: 1px; 178 | background-color: #CDB470; 179 | } 180 | 181 | .match-participant-name:hover { 182 | background-position: left bottom; 183 | color: black; 184 | } 185 | 186 | .match-participant-image { 187 | border-radius: 12px; 188 | height: 24px; 189 | width: 24px; 190 | } 191 | 192 | @media only screen and (min-width: 961px) { 193 | .summoner-info-rank { 194 | display: flex; 195 | margin-right: 0%; 196 | } 197 | 198 | .match-champion-icon-cut { 199 | width: 100px; 200 | height: 100px; 201 | } 202 | 203 | .match-champion-icon-cut { 204 | font-family: 'optimusprincepsregular'; 205 | font-size: 100%; 206 | width: 50px; 207 | height: 50px; 208 | overflow: hidden; 209 | border-radius: 50%; 210 | border: 1px #CDB470 solid; 211 | color: white; 212 | } 213 | 214 | .summoner-info-main { 215 | height: 10%; 216 | padding: 1% 0 1% 0; 217 | } 218 | 219 | .match-items, .match-summoner-spells { 220 | height: 100%; 221 | } 222 | 223 | .match-items div img, .match-no-item, .match-summoner-spells img { 224 | height: 64px; 225 | width: 64px; 226 | } 227 | 228 | .match-stats { 229 | font-size: 14pt; 230 | } 231 | 232 | .summoner-info-match { 233 | border-radius: 12px; 234 | grid-template: 144px / 45% 10% 45%; 235 | margin: 2.5% 20% 2.5% 20%; 236 | } 237 | 238 | .match-participant-name, .match-participant-nameCurrent { 239 | background: linear-gradient(to right, white 50%, rgba(0, 0, 0, 0) 50%); 240 | background-size: 201% 100%; 241 | background-position: right bottom; 242 | color: white; 243 | margin-left: 2.5%; 244 | text-decoration: none; 245 | transition: all .15s ease-out; 246 | white-space: nowrap; 247 | font-size: 12pt; 248 | } 249 | 250 | .match-participant-nameCurrent { 251 | font-weight: 700; 252 | } 253 | 254 | .match-participants-t0, .match-participants-t1 { 255 | display: flex; 256 | flex-direction: column; 257 | justify-content: space-evenly; 258 | } 259 | 260 | .match-icons { 261 | display: flex; 262 | justify-content: space-evenly; 263 | align-items: center; 264 | grid-row: 1 / 2; 265 | grid-column: 1 / 2; 266 | } 267 | 268 | .match-stats { 269 | grid-row: 1 / 2; 270 | grid-column: 2 / 3; 271 | } 272 | 273 | .match-participants { 274 | grid-row: 1 / 2; 275 | grid-column: 3 / 4; 276 | } 277 | 278 | } -------------------------------------------------------------------------------- /src/components/styles/NavBar.css: -------------------------------------------------------------------------------- 1 | /* Hide normal navbar div */ 2 | .navbar { 3 | display: none; 4 | } 5 | 6 | /* Grid Stuff */ 7 | .mobile-nav-container, .navbar-container { 8 | height: 100%; 9 | grid-row: 1 / 2; 10 | } 11 | #body-container { 12 | height: 100%; 13 | margin-top: 10vh; 14 | } 15 | 16 | /* Bar at top of page */ 17 | .mobile-nav-bar { 18 | height: 10%; 19 | width: 100%; 20 | background-color: #111111; 21 | display: grid; 22 | grid-template-columns: 10% 1fr 10%; 23 | grid-template-rows: 1fr; 24 | position: fixed; 25 | z-index: 1; 26 | } 27 | 28 | /* Mobile home button */ 29 | .mobile-button-home { 30 | grid-column: 2 / 3; 31 | align-self: center; 32 | justify-self: center; 33 | font-family: 'optimusprincepsregular'; 34 | font-weight:normal; 35 | font-style:normal; 36 | font-size: 7vmin; 37 | color: white; 38 | margin: 0; 39 | text-decoration: none; 40 | } 41 | 42 | .mobile-button-home:after { 43 | display: block; 44 | content: ""; 45 | width: 100%; 46 | height: 1px; 47 | background-color: #CDB470; 48 | } 49 | 50 | /* Open nav panel button */ 51 | .mobile-button-nav { 52 | grid-column: 3 / 4; 53 | background-color: #111111; 54 | border: none; 55 | color: white; 56 | text-rendering: optimizeLegibility; 57 | } 58 | 59 | .mobile-button-nav:hover, .mobile-button-nav-active { 60 | color: #CDB470; 61 | } 62 | .mobile-button-nav:active, .mobile-button-nav:focus { 63 | outline: 0; 64 | border: none; 65 | --moz-outline-style: none; 66 | } 67 | 68 | /* Mobile Nav Panel */ 69 | #mobile-nav{ 70 | height: 100%; 71 | width: 0; 72 | right: 0; 73 | position: fixed; 74 | z-index: 10; 75 | background-color: #111111; 76 | overflow-x: hidden; 77 | transition: 0.5s; 78 | display: grid; 79 | grid-template-columns: 1fr; 80 | grid-template-rows: 20vh 1fr; 81 | will-change: width; 82 | } 83 | 84 | /* Mobile Nav Links */ 85 | .mobile-nav-right { 86 | align-items: center; 87 | display: flex; 88 | justify-content: space-around; 89 | flex-direction: column; 90 | } 91 | 92 | .mobile-nav-right a { 93 | font-family: 'optimusprincepsregular'; 94 | font-weight:normal; 95 | font-style:normal; 96 | font-size: 5vmin; 97 | color: white; 98 | margin: 0; 99 | text-decoration: none; 100 | align-self: center; 101 | justify-self: center; 102 | } 103 | 104 | /* Kindred Logo */ 105 | .logo-mobile-kindred { 106 | height: 25vh; 107 | width: 25vh; 108 | top: 50px; 109 | align-self: start; 110 | justify-self: center; 111 | } 112 | 113 | .mobile-nav-bar { 114 | transition: all .5s; 115 | will-change: right; 116 | } 117 | 118 | .mobile-nav-right a:after, .nav-link-search:after { 119 | display: block; 120 | content: ""; 121 | width: 100%; 122 | height: 1px; 123 | background-color: #CDB470; 124 | visibility: hidden; 125 | transform: scaleX(0); 126 | transition: all 0.15s ease-in-out 0s; 127 | will-change: visibility, transform; 128 | } 129 | 130 | a.nav-link-active:after { 131 | visibility: visible; 132 | transform: scaleX(1); 133 | } 134 | 135 | .nav-link-search:hover { 136 | cursor: pointer; 137 | } 138 | 139 | a.nav-link-leaderboard:hover:after, 140 | a.nav-link-home:hover:after, 141 | a.nav-link-rotation:hover:after, 142 | a.nav-link-about:hover:after, 143 | a.nav-link-search:hover:after, 144 | .nav-link-search:hover:after { 145 | visibility: visible; 146 | transform: scaleX(1); 147 | } 148 | 149 | 150 | /* Remove outlines on links and buttons */ 151 | a.nav-link-home:active, a.nav-link-home:focus, 152 | a.nav-link-rotation:active, a.nav-link-rotation:focus, 153 | a.nav-link-about:active, a.nav-link-about:focus, 154 | a.nav-link-search:active, a.nav-link-search:focus, 155 | .mobile-button-home:active, .mobile-button-home:focus, 156 | .mobile-button-nav:active, .mobile-button-nav:focus { 157 | outline: 0; 158 | border: none; 159 | --moz-outline-style: none; 160 | padding: 0; 161 | } 162 | 163 | button::-moz-focus-inner { 164 | border: 0; 165 | } 166 | 167 | /* Landscape Mobile */ 168 | @media only screen and (orientation: landscape) { 169 | .mobile-button-home { 170 | font-size: 5vmin; 171 | } 172 | } 173 | 174 | /* Larger Display */ 175 | @media only screen and (min-width: 961px) { 176 | #mobile-nav, .mobile-nav-bar { 177 | display: none; 178 | } 179 | .navbar-container { 180 | display: block; 181 | } 182 | .navbar, #body-container { 183 | margin-right: 0; 184 | } 185 | 186 | .logo-kindred { 187 | height: 100%; 188 | } 189 | 190 | #root { 191 | grid-template-rows: 8vh auto 3vh; 192 | } 193 | 194 | .navbar { 195 | display: flex; 196 | height: 10%; 197 | background-color: #111111; 198 | grid-template-columns: 2vw 4vw 2vw 10vw auto 8vw 8vw; 199 | grid-template-rows: 1fr; 200 | width: 100%; 201 | position: fixed; 202 | z-index: 5; 203 | } 204 | 205 | .nav-left, .nav-right { 206 | display: flex; 207 | width: 50%; 208 | } 209 | 210 | .nav-left { 211 | align-items: center; 212 | margin-left: 1.5%; 213 | } 214 | 215 | .nav-right { 216 | align-items: center; 217 | justify-content: flex-end; 218 | margin-right: 2.5%; 219 | } 220 | 221 | .nav-right a, .nav-left a, .nav-right button { 222 | background-color: black; 223 | border: none; 224 | font-family: 'optimusprincepsregular'; 225 | font-weight:normal; 226 | font-style:normal; 227 | font-size: 1.5vw; 228 | color: white; 229 | margin-left: 5%; 230 | outline: none; 231 | text-decoration: none; 232 | } 233 | 234 | .nav-left * { 235 | margin-right: 2.5%; 236 | } 237 | 238 | .nav-left img { 239 | margin-right: 0; 240 | } 241 | 242 | .nav-left a { 243 | margin-left: 0; 244 | } 245 | 246 | /* Global Nav Changes */ 247 | .nav-right a:after, .nav-left a:after { 248 | display: block; 249 | content: ""; 250 | width: 100%; 251 | height: 1px; 252 | background-color: #CDB470; 253 | visibility: hidden; 254 | transform: scaleX(0); 255 | transition: all 0.15s ease-in-out 0s; 256 | z-index: 100; 257 | will-change: visibility, transform; 258 | } 259 | 260 | a.nav-link-active:after { 261 | visibility: visible; 262 | transform: scaleX(1); 263 | } 264 | 265 | 266 | /* Home Button */ 267 | a.nav-link-home { 268 | font-size: 1.75vw; 269 | } 270 | 271 | /* Seperator */ 272 | .nav-link-seperator { 273 | content: ""; 274 | width: 2px; 275 | height: 1.5vw; 276 | background-color: #525252; 277 | grid-column: 3 / 4; 278 | justify-self: center; 279 | align-self: center; 280 | } 281 | 282 | /* NavBar Buttons */ 283 | #body-container { 284 | margin-top: 10vh; 285 | } 286 | } -------------------------------------------------------------------------------- /src/components/NavBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink, withRouter } from 'react-router-dom'; 3 | import { connect } from 'react-redux'; 4 | 5 | import { setLoadStateTrue, setLoadStateFalse } from '../redux/app'; 6 | import Kindred_pixel from '../assets/custom/Kindred_pixel.png'; 7 | import Kindred_sf_pixel from '../assets/custom/Kindred_sf_pixel.png'; 8 | 9 | import './styles/NavBar.css' 10 | import Search from './Search'; 11 | 12 | class NavBar extends React.Component { 13 | constructor(props) { 14 | super(props); 15 | 16 | this.location = props.location; 17 | this.setWrapperRef = this.setWrapperRef.bind(this); 18 | this.hideNav = this.hideNav.bind(this); 19 | this.toggleSearch = this.toggleSearch.bind(this); 20 | this.updateNav = this.updateNav.bind(this); 21 | 22 | // Not sure if I really want to use redux for these 23 | this.nav = null; 24 | this.body = null; 25 | this.navbar = null; 26 | this.footer = null; 27 | this.button = null; 28 | } 29 | 30 | componentDidMount() { 31 | const nav = document.getElementById('mobile-nav'); 32 | const body = document.querySelector('.body-content'); 33 | const navbar = document.querySelector('.mobile-nav-bar'); 34 | const footer = document.getElementById('footer-container'); 35 | const button = document.querySelector('.mobile-button-nav'); 36 | 37 | if (nav && body && navbar && footer && button) { 38 | this.nav = nav; 39 | this.body = body; 40 | this.navbar = navbar; 41 | this.footer = footer; 42 | this.button = button; 43 | } 44 | 45 | document.addEventListener('mousedown', this.hideNav); 46 | window.addEventListener('orientationchange', () => { 47 | if (this.button.classList.contains('mobile-button-nav-active')) { 48 | this.updateNav(); 49 | } 50 | }) 51 | } 52 | 53 | componentDidUpdate() { 54 | // wait for elements to load 55 | const body = document.querySelector('.body-content'); 56 | 57 | if (body !== this.body) this.body = body; 58 | 59 | window.addEventListener('resize', () => { 60 | if (window.innerWidth > 960) { 61 | this.nav.style.width = '0'; 62 | this.body.style.right = '0'; 63 | this.navbar.style.right = '0'; 64 | this.footer.style.right = '0'; 65 | return; 66 | } 67 | }) 68 | } 69 | 70 | componentWillUnmount() { 71 | document.removeEventListener('mousedown', this.hideNav); 72 | window.removeEventListener('resize', this.hideNav); 73 | } 74 | 75 | setWrapperRef(node) { 76 | this.wrapperRef = node; 77 | } 78 | 79 | hideNav(event) { 80 | if (this.button) { 81 | if ( 82 | ( 83 | this.wrapperRef && 84 | !this.wrapperRef.contains(event.target) && 85 | this.button.classList.contains('mobile-button-nav-active') 86 | ) || 87 | event.target.classList.contains('mobile-button-home') 88 | ) { 89 | this.button.classList.remove('mobile-button-nav-active'); 90 | this.nav.style.width = '0'; 91 | this.body.style.right = '0'; 92 | this.navbar.style.right = '0'; 93 | this.footer.style.right = '0'; 94 | return; 95 | } 96 | } 97 | } 98 | 99 | toggleSearch() { 100 | let searchBar = document.querySelector('.search-container'); 101 | 102 | if (searchBar && searchBar.style.visibility === 'hidden') { 103 | searchBar.style.visibility = 'visible'; 104 | searchBar.style.height = '7.5vh'; 105 | searchBar.style.opacity = '100'; 106 | } else { 107 | searchBar.style.visibility = 'hidden'; 108 | searchBar.style.height = '0px'; 109 | searchBar.style.opacity = '0'; 110 | } 111 | } 112 | 113 | updateNav() { 114 | if (!this.button.classList.contains('mobile-button-nav-active')) { // Show nav 115 | this.button.classList.add('mobile-button-nav-active'); 116 | if (window.matchMedia("(orientation: landscape)").matches) { 117 | this.nav.style.width = '35%'; 118 | this.body.style.right = '35%'; 119 | this.navbar.style.right = '35%'; 120 | this.footer.style.right = '35%'; 121 | return; 122 | } 123 | this.nav.style.width = '50%'; 124 | this.body.style.right = '50%'; 125 | this.navbar.style.right = '50%'; 126 | this.footer.style.right = '50%'; 127 | return; 128 | } else { // Hide Nav 129 | this.button.classList.remove('mobile-button-nav-active'); 130 | this.nav.style.width = '0'; 131 | this.body.style.right = '0'; 132 | this.navbar.style.right = '0'; 133 | this.footer.style.right = '0'; 134 | return; 135 | } 136 | } 137 | 138 | render() { 139 | return ( 140 | <> 141 | 174 | 208 |
209 | 214 | LoL Finder 215 | 216 | 219 |
220 | 221 | 222 | ) 223 | } 224 | } 225 | 226 | const mapStateToProps = state => { 227 | return { 228 | loading: state.app.loading, 229 | }; 230 | }; 231 | 232 | const mapDispatchToProps = dispatch => { 233 | return { 234 | setLoadStateFalse: () => dispatch(setLoadStateFalse()), 235 | setLoadStateTrue: () => dispatch(setLoadStateTrue()) 236 | }; 237 | }; 238 | 239 | export default withRouter(connect( 240 | mapStateToProps, 241 | mapDispatchToProps 242 | )( 243 | NavBar 244 | )); -------------------------------------------------------------------------------- /src/components/SummonerInfo.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { NavLink } from 'react-router-dom'; 4 | import { css } from "@emotion/core"; 5 | import ClipLoader from "react-spinners/ClipLoader"; 6 | 7 | import { getSummonerInfo } from '../redux/summonerInfo'; 8 | import './styles/SummonerInfo.css'; 9 | import bronzeEmblem from '../assets/RankedEmblems/Emblem_Bronze.png'; 10 | import challengerEmblem from '../assets/RankedEmblems/Emblem_Challenger.png'; 11 | import diamondEmblem from '../assets/RankedEmblems/Emblem_Diamond.png'; 12 | import goldEmblem from '../assets/RankedEmblems/Emblem_Gold.png'; 13 | import grandmasterEmblem from '../assets/RankedEmblems/Emblem_Grandmaster.png'; 14 | import ironEmblem from '../assets/RankedEmblems/Emblem_Iron.png'; 15 | import masterEmblem from '../assets/RankedEmblems/Emblem_Master.png'; 16 | import platinumEmblem from '../assets/RankedEmblems/Emblem_Platinum.png'; 17 | import silverEmblem from '../assets/RankedEmblems/Emblem_Silver.png'; 18 | 19 | const override = css` 20 | border-color: #C44017; 21 | `; 22 | 23 | const summonerSpells = { 24 | 21: 'SummonerBarrier', 25 | 1: 'SummonerBoost', 26 | 14: 'SummonerDot', 27 | 3: 'SummonerExhaust', 28 | 4: 'SummonerFlash', 29 | 6: 'SummonerHaste', 30 | 7: 'SummonerHeal', 31 | 13: 'SummonerMana', 32 | 30: 'SummonerPoroRecall', 33 | 31: 'SummonerPoroThrow', 34 | 11: 'SummonerSmite', 35 | 39: 'SummonerSnowURFSnowball_Mark', 36 | 32: 'SummonerSnowball', 37 | 12: 'SummonerTeleport', 38 | } 39 | 40 | class SummonerInfo extends React.Component { 41 | constructor(props) { 42 | super(props); 43 | 44 | this.state = { 45 | hasMoreItems: true, 46 | } 47 | 48 | this.getRankImg = this.getRankImg.bind(this); 49 | } 50 | 51 | componentDidMount() { 52 | this.props.getSummonerInfo(window.location.pathname.split("/").pop(), this.props.region); 53 | } 54 | 55 | getRankImg() { 56 | if (!this.props.summonerInfo.rank) return; 57 | const tier = this.props.summonerInfo.rank.tier; 58 | 59 | if (tier === 'IRON') return ironEmblem; 60 | if (tier === 'BRONZE') return bronzeEmblem; 61 | if (tier === 'SILVER') return silverEmblem; 62 | if (tier === 'GOLD') return goldEmblem; 63 | if (tier === 'PLATINUM') return platinumEmblem; 64 | if (tier === 'DIAMOND') return diamondEmblem; 65 | if (tier === 'MASTER') return masterEmblem; 66 | if (tier === 'GRANDMASTER') return grandmasterEmblem; 67 | if (tier === 'CHALLENGER') return challengerEmblem; 68 | } 69 | 70 | // Send back win ratio, total games, etc. 71 | summonerGameCalc() { 72 | const wins = this.props.summonerInfo.rank.wins; 73 | const losses = this.props.summonerInfo.rank.losses; 74 | 75 | const totalGames = wins + losses; 76 | const winRatio = (Math.floor(((wins / totalGames) * 100) * 100) / 100); 77 | 78 | return [totalGames, winRatio]; 79 | } 80 | 81 | convertRatio(k, d, a) { 82 | if (d === 0) return 'Perfect'; 83 | return (Math.floor(((k + a) / d) * 100) / 100); 84 | } 85 | 86 | render() { 87 | return ( 88 |
89 | {this.props.summonerInfo && !this.props.loading ? ( 90 |
91 |
92 |
93 |
94 |
95 | ProfileIco 101 |
102 |
Level {this.props.summonerInfo.summonerLevel}
103 |
104 | {this.props.summonerInfo.rank != null ? (
105 | Unranked 106 |
107 | {this.props.summonerInfo.rank.tier} 108 | {this.props.summonerInfo.rank.rank} 109 | ({this.props.summonerInfo.rank.leaguePoints} LP) 110 |
111 |
) : 112 | (
113 | Unranked 114 |
)} 115 |
116 |
{this.props.summonerInfo.summonerName}
117 | {this.props.summonerInfo.rank != null ? ( 118 | <> 119 |
Total Games: {this.summonerGameCalc()[0]}
120 |
W: {this.props.summonerInfo.rank.wins} L: {this.props.summonerInfo.rank.losses}
121 |
Win Rate: {this.summonerGameCalc()[1]}%
122 | ) 123 | : 124 |
No Rank Info
125 | } 126 |
127 |
128 |
129 |
130 | { 131 | Object.keys(this.props.summonerInfo.matchHistory).map(matchIdx => { 132 | const match = this.props.summonerInfo.matchHistory[matchIdx]; 133 | const champImgSrc = `https://lol-finder.s3-us-west-1.amazonaws.com/DataDragon/img/champion/tiles/${match.champion}_0.jpg`; 134 | let currentPlayerId; 135 | let currentPlayer; 136 | 137 | match.matchData.participantIdentities.forEach(player => { 138 | if (player.summoner.summonerName === this.props.summonerInfo.summonerName) currentPlayerId = player.participantId; 139 | }) 140 | 141 | match.matchData.participants.forEach(player => { 142 | if (player.participantId === currentPlayerId) currentPlayer = player; 143 | }) 144 | 145 | // p = player | i = identity 146 | const [p0i, p1i, p2i, p3i, p4i, p5i, p6i, p7i, p8i, p9i] = match.matchData.participantIdentities; 147 | const [p0, p1, p2, p3, p4, p5, p6, p7, p8, p9] = match.matchData.participants; 148 | 149 | const t0 = [[p0i, p0], [p1i, p1], [p2i, p2], [p3i, p3], [p4i, p4]]; 150 | const t1 = [[p5i, p5], [p6i, p6], [p7i, p7], [p8i, p8], [p9i, p9]]; 151 | 152 | const divWinColor = { 153 | backgroundColor: 'rgba(80, 170, 255, 0.20)', 154 | }; 155 | 156 | const divLoseColor = { 157 | backgroundColor: 'rgba(180, 0, 0, 0.20)', 158 | }; 159 | 160 | return ( 161 |
162 |
163 |
164 |
165 | ChampIco 166 |
167 |
168 |
169 | summonerSpellIco 170 | summonerSpellIco 171 |
172 |
173 |
174 | { 175 | currentPlayer.stats.item0 !== 0 ? 176 | itemIco 178 | : 179 |
180 | } 181 | { 182 | currentPlayer.stats.item1 !== 0 ? 183 | itemIco 185 | : 186 |
187 | } 188 | { 189 | currentPlayer.stats.item2 !== 0 ? 190 | itemIco 192 | : 193 |
194 | } 195 |
196 |
197 | { 198 | currentPlayer.stats.item3 !== 0 ? 199 | itemIco 201 | : 202 |
203 | } 204 | { 205 | currentPlayer.stats.item4 !== 0 ? 206 | itemIco 208 | : 209 |
210 | } 211 | { 212 | currentPlayer.stats.item5 !== 0 ? 213 | itemIco 215 | : 216 |
217 | } 218 |
219 |
220 |
221 |
222 |
{currentPlayer.stats.kills} / {currentPlayer.stats.deaths} / {currentPlayer.stats.assists}
223 |
{this.convertRatio(currentPlayer.stats.kills, currentPlayer.stats.deaths, currentPlayer.stats.assists)} KDA
224 |
225 |
226 |
227 | {t0.map((player, idx) => { 228 | const champImgSrc = `https://lol-finder.s3-us-west-1.amazonaws.com/DataDragon/img/champion/tiles/${player[1].championName}_0.jpg` 229 | const encodedSummonerName = encodeURI(player[0].summoner.summonerName); 230 | 231 | if (player[0].participantId === currentPlayerId) return ( 232 |
233 |
234 | champIco 235 |
{player[0].summoner.summonerName}
236 |
237 |
238 | ) 239 | 240 | return ( 241 |
242 |
243 | champIco 244 | {player[0].summoner.summonerName} 245 |
246 |
247 | ) 248 | })} 249 |
250 |
251 | {t1.map((player, idx) => { 252 | const champImgSrc = `https://lol-finder.s3-us-west-1.amazonaws.com/DataDragon/img/champion/tiles/${player[1].championName}_0.jpg` 253 | const encodedSummonerName = encodeURI(player[0].summoner.summonerName); 254 | 255 | if (player[0].participantId === currentPlayerId) return ( 256 |
257 |
258 | champIco 259 |
{player[0].summoner.summonerName}
260 |
261 |
262 | ) 263 | 264 | return ( 265 |
266 |
267 | champIco 268 | {player[0].summoner.summonerName} 269 |
270 |
271 | ) 272 | })} 273 |
274 |
275 |
276 | ) 277 | }) 278 | } 279 |
280 |
281 | ) : 282 | ( 283 |
284 | 290 |
291 | )} 292 |
293 | ); 294 | } 295 | } 296 | 297 | const mapStateToProps = state => { 298 | return { 299 | loading: state.app.loading, 300 | region: state.search.region, 301 | summonerInfo: state.summonerInfo.summonerInfo, 302 | }; 303 | }; 304 | 305 | const mapDispatchToProps = dispatch => { 306 | return { 307 | getSummonerInfo: (...args) => dispatch(getSummonerInfo(...args)), 308 | }; 309 | }; 310 | 311 | export default connect( 312 | mapStateToProps, 313 | mapDispatchToProps 314 | )( 315 | SummonerInfo 316 | ); 317 | --------------------------------------------------------------------------------