rotations first working version

This commit is contained in:
Sendou 2019-05-13 23:51:44 +03:00
parent 7a5801d9ef
commit 2207683e78
35 changed files with 299 additions and 4 deletions

View File

@ -2,6 +2,7 @@ require('dotenv').config()
const { ApolloServer, UserInputError, AuthenticationError, gql } = require('apollo-server-express')
const mongoose = require('mongoose')
const express = require('express')
const axios = require('axios')
const cors = require('cors')
const Placement = require('./models/placement')
const Player = require('./models/player')
@ -13,6 +14,8 @@ const path = require('path')
mongoose.set('useFindAndModify', false)
mongoose.set('useCreateIndex', true)
let rotationData = {timestamp: 0}
console.log('connecting to MongoDB')
mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, dbName: "production" })
@ -110,6 +113,7 @@ const typeDefs = gql`
playerInfo(uid: String!): PlayerWithPlacements!
searchForPlayers(name: String! exact: Boolean): [Placement]!
maplists: [Maplist!]!
rotationData: String
}
type Mutation {
@ -392,6 +396,16 @@ const resolvers = {
invalidArgs: args,
})
})
},
rotationData: async (root, args) => {
if (Math.floor(Date.now() / 1000) - rotationData.timestamp > 7200) { //only refetching data if two hours have passed
const result = await axios.get('https://splatoon2.ink/data/schedules.json', { headers: { 'User-Agent': 'sendou.ink - owner: @Sendouc on Twitter'}})
rotationData = result.data
rotationData.timestamp = Math.floor(Date.now() / 1000)
return JSON.stringify(rotationData)
} else {
return JSON.stringify(rotationData)
}
}
}
}

30
package-lock.json generated
View File

@ -497,6 +497,15 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
"axios": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz",
"integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=",
"requires": {
"follow-redirects": "^1.3.0",
"is-buffer": "^1.1.5"
}
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
@ -1432,6 +1441,24 @@
}
}
},
"follow-redirects": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
"integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==",
"requires": {
"debug": "^3.2.6"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"requires": {
"ms": "^2.1.1"
}
}
}
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@ -2252,8 +2279,7 @@
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
},
"is-callable": {
"version": "1.1.4",

View File

@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js",
"watch": "nodemon index.js"
},
"author": "Sendou",
@ -12,6 +12,7 @@
"dependencies": {
"apollo-server": "^2.4.8",
"apollo-server-express": "^2.4.8",
"axios": "^0.18.0",
"cors": "^2.8.5",
"dotenv": "^7.0.0",
"graphql": "^14.2.1",

4
react-ui/src/App.js vendored
View File

@ -11,6 +11,7 @@ import InfoPlayer from './components/XSearch/InfoPlayer'
import InfoWeapon from './components/XSearch/InfoWeapon'
import ScrollToTop from './utils/ScrollToTop'
import MapListGenerator from './components/Tools/MapListGenerator'
import Rotations from './components/Tools/Rotations'
const App = () => {
const [menuSelection, setMenuSelection] = useState('home')
@ -34,6 +35,9 @@ const App = () => {
<Route exact path="/maps" render={() =>
<MapListGenerator setMenuSelection={setMenuSelection} />
} />
<Route exact path="/rotation" render={() =>
<Rotations setMenuSelection={setMenuSelection} />
} />
<Route path="/404" render={() => <NotFound />} />
<Route path="*" render={() => <NotFound />} />
</Switch>

View File

@ -19,6 +19,14 @@ const MainMenu = withRouter(({ history, menuSelection, setMenuSelection }) => {
setMenuSelection('maplists')
}}
/>
<Menu.Item
name='rotations'
active={menuSelection === 'rotations'}
onClick={() => {
history.push('/rotation')
setMenuSelection('rotations')
}}
/>
<Menu.Item
name='leaderboards'
active={menuSelection === 'leaderboards'}

View File

@ -0,0 +1,224 @@
import React, { useEffect, useState } from 'react'
import { useQuery } from 'react-apollo-hooks'
import { rotationData } from '../../graphql/queries/rotationData'
import { Loader, Segment, Header, Grid, Image, Popup } from 'semantic-ui-react'
import arowana_mall from '../img/mapIcons/arowana_mall.png'
import anchov_games from '../img/mapIcons/ancho-v_games.png'
import blackbelly_skatepark from '../img/mapIcons/blackbelly_skatepark.png'
import camp_triggerfish from '../img/mapIcons/camp_triggerfish.png'
import goby_arena from '../img/mapIcons/goby_arena.png'
import humpback_pump_track from '../img/mapIcons/humpback_pump_track.png'
import inkblot_art_academy from '../img/mapIcons/inkblot_art_academy.png'
import kelp_dome from '../img/mapIcons/kelp_dome.png'
import makomart from '../img/mapIcons/makomart.png'
import manta_maria from '../img/mapIcons/manta_maria.png'
import moray_towers from '../img/mapIcons/moray_towers.png'
import musselforge_fitness from '../img/mapIcons/musselforge_fitness.png'
import new_albacore_hotel from '../img/mapIcons/new_albacore_hotel.png'
import piranha_pit from '../img/mapIcons/piranha_pit.png'
import port_mackerel from '../img/mapIcons/port_mackerel.png'
import shellendorf_institute from '../img/mapIcons/shellendorf_institute.png'
import skipper_pavilion from '../img/mapIcons/skipper_pavilion.png'
import snapper_canal from '../img/mapIcons/snapper_canal.png'
import starfish_mainstage from '../img/mapIcons/starfish_mainstage.png'
import sturgeon_shipyard from '../img/mapIcons/sturgeon_shipyard.png'
import the_reef from '../img/mapIcons/the_reef.png'
import wahoo_world from '../img/mapIcons/wahoo_world.png'
import walleye_warehouse from '../img/mapIcons/walleye_warehouse.png'
import rankedIcon from '../img/modeIcons/ranked.png'
import regularIcon from '../img/modeIcons/regular.png'
import leagueIcon from '../img/modeIcons/league.png'
import szIcon from '../img/modeIcons/sz.png'
import tcIcon from '../img/modeIcons/tc.png'
import rmIcon from '../img/modeIcons/rm.png'
import cbIcon from '../img/modeIcons/cb.png'
const mapIcons = {
"Arowana Mall": arowana_mall,
"Ancho-V Games": anchov_games,
"Blackbelly Skatepark": blackbelly_skatepark,
"Camp Triggerfish": camp_triggerfish,
"Goby Arena": goby_arena,
"Humpback Pump Track": humpback_pump_track,
"Inkblot Art Academy": inkblot_art_academy,
"Kelp Dome": kelp_dome,
"MakoMart": makomart,
"Manta Maria": manta_maria,
"Moray Towers": moray_towers,
"Musselforge Fitness": musselforge_fitness,
"New Albacore Hotel": new_albacore_hotel,
"Piranha Pit": piranha_pit,
"Port Mackerel": port_mackerel,
"Shellendorf Institute": shellendorf_institute,
"Skipper Pavilion": skipper_pavilion,
"Snapper Canal": snapper_canal,
"Starfish Mainstage": starfish_mainstage,
"Sturgeon Shipyard": sturgeon_shipyard,
"The Reef": the_reef,
"Wahoo World": wahoo_world,
"Walleye Warehouse": walleye_warehouse
}
const modeIcons = {
"Splat Zones": szIcon,
"Tower Control": tcIcon,
"Rainmaker": rmIcon,
"Clam Blitz": cbIcon
}
const modeShort = {
"Splat Zones": "SZ",
"Tower Control": "TC",
"Rainmaker": "RM",
"Clam Blitz": "CB"
}
const Rotations = ({ setMenuSelection }) => {
const { data, error, loading } = useQuery(rotationData)
const [ rotation, setRotation ] = useState([])
const [ currentTime, setCurrentTime ] = useState(new Date(Math.floor(Date.now() / 1000)))
useEffect(() => {
if (loading) {
return
}
setMenuSelection('rotations')
document.title = 'Maplist Generator - sendou.ink'
setRotation(JSON.parse(data.rotationData))
}, [data, loading, setMenuSelection])
useEffect(() => {
const timeout = setTimeout(() => {
setCurrentTime(new Date(Math.floor(Date.now() / 1000)))
}, 1000)
return () => {
clearTimeout(timeout)
}
}, [currentTime])
if (loading || rotation.length === 0) {
return <div style={{"paddingTop": "25px", "paddingBottom": "20000px"}} ><Loader active inline='centered' /></div>
}
if (error) {
return <div style={{"color": "red"}}>{error.message}</div>
}
console.log(rotation)
console.log(currentTime)
return (
<div>
{rotation.gachi.map((r, i) => { //league maps references like so: rotation.league[i] - turf: rotation.normal[i]
if (r.end_time < currentTime) {
return null
}
const timeUntil = r.start_time - currentTime
const hours = Math.floor(timeUntil / 3600)
const minutes = Math.floor((timeUntil % 3600) / 60)
const seconds = timeUntil % 3600 % 60
let header = 'Current'
if (hours >= 2) {
header = `In ${hours} hours ${minutes} minutes (${new Date(r.start_time * 1000).toLocaleString('en-GB')})`
} else if (hours > 0) {
header = `In ${hours} hours ${minutes} minutes ${seconds} seconds`
} else if (minutes > 0) {
header = `In ${minutes} minutes ${seconds} seconds`
}
return (
<div key={r.start_time} style={{"paddingTop": "25px"}}>
<Header size='small' disabled={r.rule.name === 'Clam Blitz'}>{header}</Header>
<Segment inverted disabled={r.rule.name === 'Clam Blitz'}>
<Grid columns={3} stackable>
<Grid.Row>
<Grid.Column>
<Header textAlign='center' inverted>
<Image src={leagueIcon} style={{"paddingBottom": "10px"}}/> LEAGUE
<Header.Subheader style={{"paddingTop": "10px"}}><Image size="mini" src={modeIcons[rotation.league[i].rule.name]} avatar/>{rotation.league[i].rule.name}</Header.Subheader>
</Header>
</Grid.Column>
<Grid.Column>
<Header textAlign='center' inverted>
<Image src={rankedIcon} style={{"paddingBottom": "10px"}}/> RANKED
<Header.Subheader style={{"paddingTop": "10px"}}><Image size="mini" src={modeIcons[r.rule.name]} avatar/>{r.rule.name}</Header.Subheader>
</Header>
</Grid.Column>
<Grid.Column>
<Header textAlign='center' inverted>
<Image src={regularIcon} style={{"paddingBottom": "10px"}}/>REGULAR
<Header.Subheader style={{"paddingTop": "10px"}}></Header.Subheader> {/*this is needed for formatting reasons.*/}
</Header>
</Grid.Column>
</Grid.Row>
<Grid.Row>
<Grid.Column>
<div style={{display: 'flex', justifyContent:'center', alignItems:'center'}}>
<Popup
trigger={<Image
src={mapIcons[rotation.league[i].stage_a.name]}
rounded
/>}
content={rotation.league[i].stage_a.name}
basic
/>
<Popup
trigger={<Image
src={mapIcons[rotation.league[i].stage_b.name]}
rounded
/>}
content={rotation.league[i].stage_b.name}
basic
/>
</div>
</Grid.Column>
<Grid.Column>
<div style={{display: 'flex', justifyContent:'center', alignItems:'center'}}>
<Popup
trigger={<Image
src={mapIcons[r.stage_a.name]}
rounded
/>}
content={r.stage_a.name}
basic
/>
<Popup
trigger={<Image
src={mapIcons[r.stage_b.name]}
rounded
/>}
content={r.stage_b.name}
basic
/>
</div>
</Grid.Column>
<Grid.Column>
<div style={{display: 'flex', justifyContent:'center', alignItems:'center'}}>
<Popup
trigger={<Image
src={mapIcons[rotation.regular[i].stage_a.name]}
rounded
/>}
content={rotation.regular[i].stage_a.name}
basic
/>
<Popup
trigger={<Image
src={mapIcons[rotation.regular[i].stage_b.name]}
rounded
/>}
content={rotation.regular[i].stage_b.name}
basic
/>
</div>
</Grid.Column>
</Grid.Row>
</Grid>
</Segment>
</div>
)
})}
</div>
)
}
export default Rotations

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -0,0 +1,7 @@
import { gql } from 'apollo-boost'
export const rotationData = gql`
{
rotationData
}
`

View File

@ -12,4 +12,15 @@ addFlex: async () => {
}
] )
return "ok"
return "ok"
-- React timer --
useEffect(() => {
const timeout = setTimeout(() => {
setCurrentTime(new Date(Math.floor(Date.now() / 1000)))
}, 1000)
return () => {
clearTimeout(timeout)
}
}, [currentTime])