player pages first vesrion

This commit is contained in:
Sendou 2019-05-09 00:37:00 +03:00
parent f92f7c2407
commit 6a6e0dad69
69 changed files with 659 additions and 88 deletions

View File

@ -1,16 +1,23 @@
{
"files": {
"main.js": "/static/js/main.0702caf0.chunk.js",
"main.js.map": "/static/js/main.0702caf0.chunk.js.map",
"main.js": "/static/js/main.74965c76.chunk.js",
"main.js.map": "/static/js/main.74965c76.chunk.js.map",
"runtime~main.js": "/static/js/runtime~main.a8a9905a.js",
"runtime~main.js.map": "/static/js/runtime~main.a8a9905a.js.map",
"static/js/2.0537ac8b.chunk.js": "/static/js/2.0537ac8b.chunk.js",
"static/js/2.0537ac8b.chunk.js.map": "/static/js/2.0537ac8b.chunk.js.map",
"static/js/2.8c043a76.chunk.js": "/static/js/2.8c043a76.chunk.js",
"static/js/2.8c043a76.chunk.js.map": "/static/js/2.8c043a76.chunk.js.map",
"index.html": "/index.html",
"precache-manifest.473bef15a24678fc09ea90998c7fb45b.js": "/precache-manifest.473bef15a24678fc09ea90998c7fb45b.js",
"precache-manifest.9954408c5a89de688655e3bfb5fa3345.js": "/precache-manifest.9954408c5a89de688655e3bfb5fa3345.js",
"service-worker.js": "/service-worker.js",
"static/media/bridge.png": "/static/media/bridge.fba72008.png",
"static/media/depot.png": "/static/media/depot.c5459318.png",
"static/media/heights.png": "/static/media/heights.7e33477e.png",
"static/media/mahi.png": "/static/media/mahi.0e9369da.png",
"static/media/museum.png": "/static/media/museum.176394c2.png",
"static/media/rig.png": "/static/media/rig.87b5e71c.png",
"static/media/rm.png": "/static/media/rm.ee623ae2.png",
"static/media/sz.png": "/static/media/sz.563a7566.png",
"static/media/tc.png": "/static/media/tc.bd2d39a4.png"
"static/media/tc.png": "/static/media/tc.bd2d39a4.png",
"static/media/underpass.png": "/static/media/underpass.c55c527a.png"
}
}

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><link rel="stylesheet" href="//cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>sendou.ink</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={1:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/2.0537ac8b.chunk.js"></script><script src="/static/js/main.0702caf0.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><link rel="stylesheet" href="//cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>sendou.ink</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={1:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="/static/js/2.8c043a76.chunk.js"></script><script src="/static/js/main.74965c76.chunk.js"></script></body></html>

View File

@ -1,30 +0,0 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "157c583ec55a8e3c581675beedebc20a",
"url": "/index.html"
},
{
"revision": "d8b9b385bad8565d4100",
"url": "/static/js/2.0537ac8b.chunk.js"
},
{
"revision": "068403f6ae99724cd502",
"url": "/static/js/main.0702caf0.chunk.js"
},
{
"revision": "42ac5946195a7306e2a5",
"url": "/static/js/runtime~main.a8a9905a.js"
},
{
"revision": "ee623ae225822d4a257120c58516eb86",
"url": "/static/media/rm.ee623ae2.png"
},
{
"revision": "563a75668da994dc8c3a42e75daf038c",
"url": "/static/media/sz.563a7566.png"
},
{
"revision": "bd2d39a43fc0bcfae9955e647842571d",
"url": "/static/media/tc.bd2d39a4.png"
}
]);

View File

@ -0,0 +1,58 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "42d9bfc9bb2df76ed31e8a543805b852",
"url": "/index.html"
},
{
"revision": "a27da5d1625a4331441e",
"url": "/static/js/2.8c043a76.chunk.js"
},
{
"revision": "9416f0e806a96e221630",
"url": "/static/js/main.74965c76.chunk.js"
},
{
"revision": "42ac5946195a7306e2a5",
"url": "/static/js/runtime~main.a8a9905a.js"
},
{
"revision": "fba720089b3644e959ae4d7d7f87e328",
"url": "/static/media/bridge.fba72008.png"
},
{
"revision": "c5459318010bbec7ac0f9b4ed0625967",
"url": "/static/media/depot.c5459318.png"
},
{
"revision": "7e33477e6270d5436d0e31069ce6ba75",
"url": "/static/media/heights.7e33477e.png"
},
{
"revision": "0e9369da56634efad39d159a7275d993",
"url": "/static/media/mahi.0e9369da.png"
},
{
"revision": "176394c281c9142a8ac75444e740dd00",
"url": "/static/media/museum.176394c2.png"
},
{
"revision": "87b5e71c29a70bc15a501002b4192517",
"url": "/static/media/rig.87b5e71c.png"
},
{
"revision": "ee623ae225822d4a257120c58516eb86",
"url": "/static/media/rm.ee623ae2.png"
},
{
"revision": "563a75668da994dc8c3a42e75daf038c",
"url": "/static/media/sz.563a7566.png"
},
{
"revision": "bd2d39a43fc0bcfae9955e647842571d",
"url": "/static/media/tc.bd2d39a4.png"
},
{
"revision": "c55c527ab9b1f079f606848b880b5f4c",
"url": "/static/media/underpass.c55c527a.png"
}
]);

View File

@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.0/workbox-sw.js");
importScripts(
"/precache-manifest.473bef15a24678fc09ea90998c7fb45b.js"
"/precache-manifest.9954408c5a89de688655e3bfb5fa3345.js"
);
self.addEventListener('message', (event) => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

Before

Width:  |  Height:  |  Size: 851 KiB

After

Width:  |  Height:  |  Size: 851 KiB

View File

@ -73,6 +73,11 @@ const typeDefs = gql`
modeCount: [Int!]!
}
type PlayerWithPlacements {
player: Player!
placements: [Placement!]!
}
type Token {
value: String!
}
@ -90,7 +95,7 @@ const typeDefs = gql`
topBrellaPlayers (amount: Int): [Player!]!
topPlayers (weapon: String!): topPlayer!
weaponPlacementStats(weapon: String!): [Int!]!
playerInfo(uid: String!): [Placement]
playerInfo(uid: String!): PlayerWithPlacements!
}
type Mutation {
@ -293,16 +298,30 @@ const resolvers = {
return {placements: placements.slice(0, 101), modeCount: [m.sz+m.tc+m.rm+m.cb, m.sz, m.tc, m.rm, m.cb]}
},
playerInfo: (root, args) => { //tee tästä uus tyyppi joka palauttaa kaikki placementit sekä Player objektin
console.log('args', args)
return Placement
.find({ unique_id: args.uid })
.sort({ "month": "asc", "year": "asc"})
playerInfo: async (root, args) => {
const player = await Player
.findOne({ unique_id: args.uid })
.catch(e => {
throw new UserInputError(e.message, {
invalidArgs: args,
})
})
if (!player) {
throw new UserInputError('player not found with the id given', {
invalidArgs: args,
})
}
const placements = await Placement
.find({ unique_id: args.uid })
.sort({ "year": "asc", "month": "asc" })
.catch(e => {
throw new UserInputError(e.message, {
invalidArgs: args,
})
})
return { player, placements }
}
}
}

View File

@ -6,7 +6,7 @@ const placementSchema = new mongoose.Schema({
rank: {type: Number, min: 1, max: 500, required: true},
mode: {type: Number, min: 1, max: 4, required: true},
x_power: {type: Number, required: true},
unique_id: {type: Number, required: true},
unique_id: {type: String, required: true},
month: {type: Number, min: 1, max: 12, required: true},
year: {type: Number, min: 2017, required: true}
})

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

@ -3,10 +3,11 @@ import { Container } from 'semantic-ui-react'
import Footer from './components/Footer'
import MainMenu from './components/MainMenu'
import NotFound from './components/NotFound'
import { BrowserRouter as Router, Route, Redirect, Switch } from 'react-router-dom'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import WeaponLeaderboardSelector from './components/WeaponLeaderboardSelector'
import XSearch from './components/XSearch'
import ScrollToTop from './utils/ScrollToTop'
const App = () => {
const [menuSelection, setMenuSelection] = useState('home')
@ -16,6 +17,7 @@ const App = () => {
<Container>
<MainMenu menuSelection={menuSelection} setMenuSelection={setMenuSelection} />
<div>
<ScrollToTop />
<Switch>
<Route exact path="/" render={() => <div>This is home.</div>} />
<Route path="/xleaderboard" render={() => <WeaponLeaderboardSelector setMenuSelection={setMenuSelection} />} />

View File

@ -2,7 +2,7 @@ import React from 'react'
const Footer = () => {
return (
<div style={{"fontSize": "small"}} >
<div style={{"fontSize": "small", "paddingTop": "25px"}} >
<hr />
Website by <a href="https://twitter.com/sendouc">Sendou</a>. Data for X Rank Leaderboards provided by <a href="https://twitter.com/LeanYoshi">Lean</a>.
</div>

View File

@ -1,15 +1,9 @@
import React from 'react'
import { Popup } from 'semantic-ui-react'
import weaponDict from '../utils/english_internal.json'
import { modes, months } from '../utils/lists'
import { modes, months, getNumberWithOrdinal } from '../utils/lists'
const FourWeapons = ({ weapons }) => {
function getNumberWithOrdinal(n) {
var s=["th","st","nd","rd"],
v=n%100;
return n+(s[(v-20)%10]||s[v]||s[0]);
}
return (
<>
<Popup

View File

@ -1,7 +1,72 @@
import React from 'react'
import React, { useEffect, useState } from 'react'
import { useQuery } from 'react-apollo-hooks'
import { Loader, Header, Image, Icon } from 'semantic-ui-react'
import { playerInfo } from '../graphql/queries/playerInfo'
import TopPlacementTable from '../components/TopPlacementsTable'
import WpnPlayedTable from '../components/WpnPlayedTable'
import MonthsTable from '../components/MonthsTable'
const InfoPlayer = ({ uid }) => {
const { data, error, loading } = useQuery(playerInfo, {variables: { uid: uid }})
const [top, setTop] = useState([])
useEffect(() => {
if (loading) {
return
}
document.title = `${data.playerInfo.player.alias ? data.playerInfo.player.alias : data.playerInfo.player.name} Top 500 X Rank - sendou.ink`
const placements = data.playerInfo.placements
//reducing placements to top sz, tc etc. rank and x power
const tops = ["", "szTop", "tcTop", "rmTop", "cbTop"]
const exs = ["", "szX", "tcX", "rmX", "cbX"]
setTop(placements.reduce((acc, cur) => {
const topKey = tops[cur.mode]
const xKey = exs[cur.mode]
if (!acc[xKey]) {
acc[xKey] = cur
acc[topKey] = cur
return acc
}
if (acc[xKey].x_power < cur.x_power) {
acc[xKey] = cur
}
if (acc[topKey].rank > cur.rank) {
acc[topKey] = cur
}
return acc
}, {
szX: null, szTop: null,
tcX: null, tcTop: null,
rmX: null, rmTop: null,
cbX: null, cbTop: null
}))
}, [data, loading])
if (loading || top.length === 0) {
return <div style={{"paddingTop": "25px", "paddingBottom": "20000px"}} ><Loader active inline='centered' /></div>
}
if (error) {
return <div style={{"color": "red"}}>{error.message}</div>
}
const playerData = data.playerInfo.player
return (
<div style={{"paddingTop": "20px"}}>
<Header as='h2'>
{playerData.twitter ? <Image circular src={`https://avatars.io/twitter/${playerData.twitter}`} /> : null} {playerData.alias ? playerData.alias : playerData.name}
{playerData.twitter ? <a href={`https://twitter.com/${playerData.twitter}`}><Icon style={{"paddingLeft": "5px"}} size='small' name="twitter"/></a> : null}
</Header>
<TopPlacementTable top={top} />
<Header dividing style={{"paddingTop": "5px"}}>All Top 500 placements</Header>
<MonthsTable placements={data.playerInfo.placements} />
<Header dividing style={{"paddingTop": "5px"}}>Weapons reached Top 500 with</Header>
<WpnPlayedTable weapons={playerData.weapons} />
</div>
)
}
export default InfoPlayer

View File

@ -3,17 +3,17 @@ import { useQuery } from 'react-apollo-hooks'
import { topPlayersOfWeapon } from '../graphql/queries/topPlayersOfWeapon'
import weaponDictReversed from '../utils/internal_english.json'
import { Loader, Header, Table, Checkbox } from 'semantic-ui-react'
import { withRouter } from 'react-router-dom'
import { withRouter, Link } from 'react-router-dom'
import szIcon from './modeIcons/sz.png'
import tcIcon from './modeIcons/tc.png'
import rmIcon from './modeIcons/rm.png'
import cbIcon from './modeIcons/cb.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'
import { months, modes } from '../utils/lists'
const modeIcons = ["", szIcon, tcIcon, rmIcon, cbIcon]
const InfoWeapon = withRouter(({ history, wpn }) => { //todo - handle error (404?) //new weapons not shown?
const InfoWeapon = withRouter(({ history, wpn }) => {
const { data, error, loading } = useQuery(topPlayersOfWeapon, {variables: {weapon: weaponDictReversed[wpn] }})
const [uniqueId, setUniqueId] = useState('')
const [allPlayers, setAllPlayers] = useState(true)
@ -107,7 +107,7 @@ const InfoWeapon = withRouter(({ history, wpn }) => { //todo - handle error (404
{leaderboard.map((p, i) =>
<Table.Row key={p.id} active={p.unique_id === uniqueId} onClick={() => setUniqueId(p.unique_id)}>
<Table.Cell>{i + 1}</Table.Cell>
<Table.Cell>{p.name}</Table.Cell>
<Table.Cell><Link to={`/xsearch/p/${p.unique_id}`} style={{"color": "black"}}><u>{p.alias ? p.alias : p.name}</u></Link></Table.Cell>
<Table.Cell>{p.x_power}</Table.Cell>
<Table.Cell>{months[p.month]}</Table.Cell>
<Table.Cell>{p.year}</Table.Cell>

View File

@ -7,7 +7,7 @@ const MainMenu = withRouter(({ history, menuSelection, setMenuSelection }) => {
const [memCakePic, setMemCakePic] = useState(memCakes[Math.floor(Math.random()*memCakes.length)])
return (
<Segment inverted>
<Menu inverted secondary>
<Menu inverted secondary stackable>
<Menu.Item>
<img src={process.env.PUBLIC_URL + `/memCakes/${memCakePic}`} alt="mem cake logo" onClick={() => setMemCakePic(memCakes[Math.floor(Math.random()*memCakes.length)])}/>
</Menu.Item>

57
react-ui/src/components/MonthsTable.js vendored Normal file
View File

@ -0,0 +1,57 @@
import React from 'react'
import { Table, Header, Image } from 'semantic-ui-react'
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'
import { months } from '../utils/lists'
import weaponDict from '../utils/english_internal.json'
const MonthsTable = ({ placements }) => { //data received is ordered chronologically and sz->tc->rm->cb
const modeIcons = [null, szIcon, tcIcon, rmIcon, cbIcon]
let lastMonth = placements[0].month
let toggle = false
return (
<Table basic='very' celled collapsing>
<Table.Header>
<Table.Row>
<Table.HeaderCell></Table.HeaderCell>
<Table.HeaderCell>Name</Table.HeaderCell>
<Table.HeaderCell>X Power</Table.HeaderCell>
<Table.HeaderCell>Placement</Table.HeaderCell>
<Table.HeaderCell>Weapon</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{placements.map(p => {
if (lastMonth !== p.month) {
lastMonth = p.month
toggle = !toggle
}
return (
<Table.Row key={p.id} active={toggle}>
<Table.Cell>
<Header as='h4' image>
<Image src={modeIcons[p.mode]} rounded size='mini' />
<Header.Content>
{months[p.month]}
<Header.Subheader>{p.year}</Header.Subheader>
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{p.name}</Table.Cell>
<Table.Cell>{p.x_power}</Table.Cell>
<Table.Cell>{p.rank}</Table.Cell>
<Table.Cell><img src={process.env.PUBLIC_URL + `/wpnSmall/Wst_${weaponDict[p.weapon]}.png`} alt={p.weapon} /></Table.Cell>
</Table.Row>
)
})}
</Table.Body>
</Table>
)
}
export default MonthsTable

View File

@ -1,13 +1,13 @@
import React from 'react'
import { Header } from 'semantic-ui-react'
import bridge from './s1Maps/bridge.png'
import depot from './s1Maps/depot.png'
import heights from './s1Maps/heights.png'
import mahi from './s1Maps/mahi.png'
import museum from './s1Maps/museum.png'
import rig from './s1Maps/rig.png'
import underpass from './s1Maps/underpass.png'
import bridge from './img/s1Maps/bridge.png'
import depot from './img/s1Maps/depot.png'
import heights from './img/s1Maps/heights.png'
import mahi from './img/s1Maps/mahi.png'
import museum from './img/s1Maps/museum.png'
import rig from './img/s1Maps/rig.png'
import underpass from './img/s1Maps/underpass.png'
const NotFound = () => {
const maps = [

View File

@ -0,0 +1,217 @@
import React from 'react'
import { Header, Image, Table } from 'semantic-ui-react'
import weaponDict from '../utils/english_internal.json'
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'
import { months } from '../utils/lists'
const TopPlacementTable = ({ top }) => {
console.log(top)
return (
<Table basic='very' celled collapsing>
<Table.Header>
<Table.Row>
<Table.HeaderCell></Table.HeaderCell>
<Table.HeaderCell>X Power</Table.HeaderCell>
<Table.HeaderCell>Placement</Table.HeaderCell>
<Table.HeaderCell>Weapon</Table.HeaderCell>
<Table.HeaderCell>Month</Table.HeaderCell>
<Table.HeaderCell>Year</Table.HeaderCell>
</Table.Row>
</Table.Header>
{top.szX ?
<Table.Body>
<Table.Row>
<Table.Cell>
<Header as='h4' image>
<Image src={szIcon} size='mini' />
<Header.Content>
Splat Zones
<Header.Subheader>Highest X Power</Header.Subheader>
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{top.szX.x_power}</Table.Cell>
<Table.Cell>{top.szX.rank}</Table.Cell>
<Table.Cell>
<img
src={process.env.PUBLIC_URL + `/wpnSmall/Wst_${weaponDict[top.szX.weapon]}.png`}
alt={top.szX.weapon}
/>
</Table.Cell>
<Table.Cell>{months[top.szX.month]}</Table.Cell>
<Table.Cell>{top.szX.year}</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Header as='h4' image>
<Image src={szIcon} size='mini' />
<Header.Content>
Splat Zones
<Header.Subheader>Highest Placement</Header.Subheader>
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{top.szTop.x_power}</Table.Cell>
<Table.Cell>{top.szTop.rank}</Table.Cell>
<Table.Cell>
<img
src={process.env.PUBLIC_URL + `/wpnSmall/Wst_${weaponDict[top.szTop.weapon]}.png`}
alt={top.szTop.weapon}
/>
</Table.Cell>
<Table.Cell>{months[top.szTop.month]}</Table.Cell>
<Table.Cell>{top.szTop.year}</Table.Cell>
</Table.Row>
</Table.Body>
: null
}
{top.tcX ?
<Table.Body>
<Table.Row>
<Table.Cell>
<Header as='h4' image>
<Image src={tcIcon} size='mini' />
<Header.Content>
Tower Control
<Header.Subheader>Highest X Power</Header.Subheader>
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{top.tcX.x_power}</Table.Cell>
<Table.Cell>{top.tcX.rank}</Table.Cell>
<Table.Cell>
<img
src={process.env.PUBLIC_URL + `/wpnSmall/Wst_${weaponDict[top.tcX.weapon]}.png`}
alt={top.tcX.weapon}
/>
</Table.Cell>
<Table.Cell>{months[top.tcX.month]}</Table.Cell>
<Table.Cell>{top.tcX.year}</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Header as='h4' image>
<Image src={tcIcon} size='mini' />
<Header.Content>
Tower Control
<Header.Subheader>Highest Placement</Header.Subheader>
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{top.tcTop.x_power}</Table.Cell>
<Table.Cell>{top.tcTop.rank}</Table.Cell>
<Table.Cell>
<img
src={process.env.PUBLIC_URL + `/wpnSmall/Wst_${weaponDict[top.tcTop.weapon]}.png`}
alt={top.tcTop.weapon}
/>
</Table.Cell>
<Table.Cell>{months[top.tcTop.month]}</Table.Cell>
<Table.Cell>{top.tcTop.year}</Table.Cell>
</Table.Row>
</Table.Body>
: null
}
{top.rmX ?
<Table.Body>
<Table.Row>
<Table.Cell>
<Header as='h4' image>
<Image src={rmIcon} size='mini' />
<Header.Content>
Rainmaker
<Header.Subheader>Highest X Power</Header.Subheader>
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{top.rmX.x_power}</Table.Cell>
<Table.Cell>{top.rmX.rank}</Table.Cell>
<Table.Cell>
<img
src={process.env.PUBLIC_URL + `/wpnSmall/Wst_${weaponDict[top.rmX.weapon]}.png`}
alt={top.rmX.weapon}
/>
</Table.Cell>
<Table.Cell>{months[top.rmX.month]}</Table.Cell>
<Table.Cell>{top.rmX.year}</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Header as='h4' image>
<Image src={rmIcon} size='mini' />
<Header.Content>
Rainmaker
<Header.Subheader>Highest Placement</Header.Subheader>
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{top.rmTop.x_power}</Table.Cell>
<Table.Cell>{top.rmTop.rank}</Table.Cell>
<Table.Cell>
<img
src={process.env.PUBLIC_URL + `/wpnSmall/Wst_${weaponDict[top.rmTop.weapon]}.png`}
alt={top.rmTop.weapon}
/>
</Table.Cell>
<Table.Cell>{months[top.rmTop.month]}</Table.Cell>
<Table.Cell>{top.rmTop.year}</Table.Cell>
</Table.Row>
</Table.Body>
: null
}
{top.cbX ?
<Table.Body>
<Table.Row>
<Table.Cell>
<Header as='h4' image>
<Image src={cbIcon} size='mini' />
<Header.Content>
Clam Blitz
<Header.Subheader>Highest X Power</Header.Subheader>
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{top.cbX.x_power}</Table.Cell>
<Table.Cell>{top.cbX.rank}</Table.Cell>
<Table.Cell>
<img
src={process.env.PUBLIC_URL + `/wpnSmall/Wst_${weaponDict[top.cbX.weapon]}.png`}
alt={top.cbX.weapon}
/>
</Table.Cell>
<Table.Cell>{months[top.cbX.month]}</Table.Cell>
<Table.Cell>{top.cbX.year}</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Header as='h4' image>
<Image src={cbIcon} size='mini' />
<Header.Content>
Clam Blitz
<Header.Subheader>Highest Placement</Header.Subheader>
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{top.cbTop.x_power}</Table.Cell>
<Table.Cell>{top.cbTop.rank}</Table.Cell>
<Table.Cell>
<img
src={process.env.PUBLIC_URL + `/wpnSmall/Wst_${weaponDict[top.cbTop.weapon]}.png`}
alt={top.cbTop.weapon}
/>
</Table.Cell>
<Table.Cell>{months[top.cbTop.month]}</Table.Cell>
<Table.Cell>{top.cbTop.year}</Table.Cell>
</Table.Row>
</Table.Body>
: null
}
</Table>
)
}
export default TopPlacementTable

View File

@ -2,6 +2,7 @@ import React from 'react'
import { Table, Icon, Popup } from 'semantic-ui-react'
import { useQuery } from 'react-apollo-hooks'
import { Loader } from 'semantic-ui-react'
import { Link } from 'react-router-dom'
import FourWeapons from '../components/FourWeapons'
@ -40,7 +41,7 @@ const WeaponLeaderboard = ({ query, queryName, scoreField, weaponsField, setActi
{leaderboard.map((player, index) =>
<Table.Row key={player.id}>
<Table.Cell>{index + 1}</Table.Cell>
<Table.Cell>{player.alias ? player.alias : player.name}
<Table.Cell><Link to={`/xsearch/p/${player.unique_id}`} style={{"color": "black"}}><u>{player.alias ? player.alias : player.name}</u></Link>
{player.twitter ?
<a href={`https://twitter.com/${player.twitter}`}><Icon style={{"paddingLeft": "5px"}} name="twitter"/></a>
: null}

View File

@ -11,15 +11,15 @@ import { topDualiesPlayers } from '../graphql/queries/topDualies'
import { topRollerPlayers } from '../graphql/queries/topRollers'
import { topSlosherPlayers } from '../graphql/queries/topSloshers'
import { topSplatlingPlayers } from '../graphql/queries/topSplatlings'
import allIcon from './wpnIcons/all.png'
import blasterIcon from './wpnIcons/blasters.png'
import brellaIcon from './wpnIcons/brellas.png'
import chargerIcon from './wpnIcons/chargers.png'
import dualieIcon from './wpnIcons/dualies.png'
import rollerIcon from './wpnIcons/rollers.png'
import shooterIcon from './wpnIcons/shooters.png'
import slosherIcon from './wpnIcons/sloshers.png'
import splatlingIcon from './wpnIcons/splatlings.png'
import allIcon from './img/wpnIcons/all.png'
import blasterIcon from './img/wpnIcons/blasters.png'
import brellaIcon from './img/wpnIcons/brellas.png'
import chargerIcon from './img/wpnIcons/chargers.png'
import dualieIcon from './img/wpnIcons/dualies.png'
import rollerIcon from './img/wpnIcons/rollers.png'
import shooterIcon from './img/wpnIcons/shooters.png'
import slosherIcon from './img/wpnIcons/sloshers.png'
import splatlingIcon from './img/wpnIcons/splatlings.png'
const WeaponLeaderboardSelector = withRouter(({ history, setMenuSelection }) => {
const [activeItem, setActiveItem] = useState('')

View File

@ -0,0 +1,57 @@
import React from 'react'
import { weaponsByCategory } from '../utils/lists'
import { categoryKeys } from '../utils/lists'
import weaponDict from '../utils/english_internal.json'
import { Table, Header, Popup } from 'semantic-ui-react'
const WpnPlayedTable = ({ weapons }) => {
const weaponStyle = (wpnName) => {
const activeStyle = {}
const inactiveStyle = {"filter": "grayscale(1)", "opacity": "0.3"}
return weapons.includes(wpnName) ? activeStyle : inactiveStyle
}
return (
<Table basic='very' celled collapsing>
{categoryKeys.map(c => {
return (
<Table.Body key={c}>
<Table.Row>
<Table.Cell>
<Header as='h4'>
<Header.Content>
{c}
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{weaponsByCategory[c].map(w => {
return (
<Popup
key={w}
trigger={<img key={w} style={weaponStyle(w)} src={process.env.PUBLIC_URL + `/wpnSmall/Wst_${weaponDict[w]}.png`} alt={w} />}
content={w}
/>
)
})}</Table.Cell>
</Table.Row>
</Table.Body>
)})}
<Table.Body>
<Table.Row>
<Table.Cell>
<Header as='h4'>
<Header.Content>
Total
</Header.Content>
</Header>
</Table.Cell>
<Table.Cell>{weapons.length} / 129</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
)
}
export default WpnPlayedTable

View File

@ -3,6 +3,7 @@ import { Form, Message, Button } from 'semantic-ui-react'
import { Route, withRouter } from 'react-router-dom'
import WeaponForm from '../components/WeaponForm'
import InfoWeapon from '../components/InfoWeapon'
import InfoPlayer from '../components/InfoPlayer'
import weaponDict from '../utils/english_internal.json'
const XSearch = withRouter(({ history, setMenuSelection }) => {
@ -32,6 +33,9 @@ const XSearch = withRouter(({ history, setMenuSelection }) => {
<Route exact path="/xsearch/w/:wpn" render={({ match }) =>
<InfoWeapon wpn={match.params.wpn.replace(/-/g, '_')} setWeaponForm={setWeaponForm} />
} />
<Route exact path="/xsearch/p/:uid" render={({ match }) =>
<InfoPlayer uid={match.params.uid} />
} />
</div>
)
}) //alemmalle buttonille onChange uncontrolled?

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,33 @@
import { gql } from 'apollo-boost'
export const playerInfo = gql`
query playerInfo($uid: String!) {
playerInfo(uid: $uid) {
player {
name
weapons
alias
twitter
topTotalScore
topShooterScore
topBlasterScore
topRollerScore
topChargerScore
topSlosherScore
topSplatlingScore
topDualiesScore
topBrellaScore
}
placements {
id
name
weapon
rank
mode
x_power
month
year
}
}
}
`

View File

@ -4,6 +4,7 @@ export const topBlasterPlayers = gql`
{
topBlasterPlayers {
id
unique_id
name
alias
twitter

View File

@ -4,6 +4,7 @@ export const topBrellaPlayers = gql`
{
topBrellaPlayers {
id
unique_id
name
alias
twitter

View File

@ -4,6 +4,7 @@ export const topChargerPlayers = gql`
{
topChargerPlayers {
id
unique_id
name
alias
twitter

View File

@ -4,6 +4,7 @@ export const topDualiesPlayers = gql`
{
topDualiesPlayers {
id
unique_id
name
alias
twitter

View File

@ -4,6 +4,7 @@ export const topTotalPlayers = gql`
{
topTotalPlayers {
id
unique_id
name
alias
twitter

View File

@ -4,6 +4,7 @@ export const topRollerPlayers = gql`
{
topRollerPlayers {
id
unique_id
name
alias
twitter

View File

@ -4,6 +4,7 @@ export const topShooterPlayers = gql`
{
topShooterPlayers {
id
unique_id
name
alias
twitter

View File

@ -4,6 +4,7 @@ export const topSlosherPlayers = gql`
{
topSlosherPlayers {
id
unique_id
name
alias
twitter

View File

@ -4,6 +4,7 @@ export const topSplatlingPlayers = gql`
{
topSplatlingPlayers {
id
unique_id
name
alias
twitter

18
react-ui/src/utils/ScrollToTop.js vendored Normal file
View File

@ -0,0 +1,18 @@
import { useEffect } from 'react';
import { withRouter } from 'react-router-dom';
function ScrollToTop({ history }) {
useEffect(() => {
const unlisten = history.listen(() => {
window.scrollTo(0, 0)
});
return () => {
unlisten()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (null)
}
export default withRouter(ScrollToTop)

View File

@ -1,3 +1,9 @@
export function getNumberWithOrdinal(n) {
var s=["th","st","nd","rd"],
v=n%100;
return n+(s[(v-20)%10]||s[v]||s[0]);
}
export const modes = ["", "Splat Zones", "Tower Control", "Rainmaker", "Clam Blitz"]
export const months = ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
@ -38,4 +44,58 @@ export const weapons = ["Sploosh-o-matic", "Neo Sploosh-o-matic", "Sploosh-o-mat
"Splat Brella", "Sorella Brella", "Tenta Brella", "Tenta Sorella Brella",
"Tenta Camo Brella", "Undercover Brella", "Undercover Sorella Brella", "Kensa Undercover Brella"]
export const memCakes = ['moon.png', 'S2_Gear_Headgear_Fresh_Fish_Head.png', 'S2_Gear_Headgear_Anglerfish_Mask.png', 'nstc.png', 'chimera.png', 'olive.png', 'pulla.png', 'qr.png', 'OctCollectIcon_00.png', 'OctCollectIcon_01.png', 'OctCollectIcon_02.png', 'OctCollectIcon_03.png', 'OctCollectIcon_04.png', 'OctCollectIcon_05.png', 'OctCollectIcon_06.png', 'OctCollectIcon_07.png', 'OctCollectIcon_08.png', 'OctCollectIcon_09.png', 'OctCollectIcon_10.png', 'OctCollectIcon_11.png', 'OctCollectIcon_12.png', 'OctCollectIcon_13.png', 'OctCollectIcon_14.png', 'OctCollectIcon_15.png', 'OctCollectIcon_16.png', 'OctCollectIcon_17.png', 'OctCollectIcon_18.png', 'OctCollectIcon_19.png', 'OctCollectIcon_20.png', 'OctCollectIcon_21.png', 'OctCollectIcon_22.png', 'OctCollectIcon_23.png', 'OctCollectIcon_24.png', 'OctCollectIcon_25.png', 'OctCollectIcon_26.png', 'OctCollectIcon_27.png', 'OctCollectIcon_28.png', 'OctCollectIcon_29.png', 'OctCollectIcon_30.png', 'OctCollectIcon_31.png', 'OctCollectIcon_32.png', 'OctCollectIcon_33.png', 'OctCollectIcon_34.png', 'OctCollectIcon_35.png', 'OctCollectIcon_36.png', 'OctCollectIcon_37.png', 'OctCollectIcon_38.png', 'OctCollectIcon_39.png', 'OctCollectIcon_40.png', 'OctCollectIcon_41.png', 'OctCollectIcon_42.png', 'OctCollectIcon_43.png', 'OctCollectIcon_44.png', 'OctCollectIcon_45.png', 'OctCollectIcon_46.png', 'OctCollectIcon_47.png', 'OctCollectIcon_48.png', 'OctCollectIcon_49.png', 'OctCollectIcon_50.png', 'OctCollectIcon_51.png', 'OctCollectIcon_52.png', 'OctCollectIcon_53.png', 'OctCollectIcon_54.png', 'OctCollectIcon_55.png', 'OctCollectIcon_56.png', 'OctCollectIcon_57.png', 'OctCollectIcon_59.png', 'OctCollectIcon_60.png', 'OctCollectIcon_61.png', 'OctCollectIcon_62.png', 'OctCollectIcon_63.png', 'OctCollectIcon_64.png', 'OctCollectIcon_65.png', 'OctCollectIcon_66.png', 'OctCollectIcon_67.png', 'OctCollectIcon_68.png', 'OctCollectIcon_69.png', 'OctCollectIcon_70.png', 'OctCollectIcon_71.png', 'OctCollectIcon_72.png', 'OctCollectIcon_73.png', 'OctCollectIcon_74.png', 'OctCollectIcon_75.png', 'OctCollectIcon_76.png', 'OctCollectIcon_77.png', 'OctCollectIcon_78.png', 'OctCollectIcon_79.png', 'lanista.png', 'heart.png']
export const memCakes = ['moon.png', 'S2_Gear_Headgear_Fresh_Fish_Head.png', 'S2_Gear_Headgear_Anglerfish_Mask.png', 'nstc.png', 'chimera.png', 'olive.png', 'pulla.png', 'qr.png', 'OctCollectIcon_00.png', 'OctCollectIcon_01.png', 'OctCollectIcon_02.png', 'OctCollectIcon_03.png', 'OctCollectIcon_04.png', 'OctCollectIcon_05.png', 'OctCollectIcon_06.png', 'OctCollectIcon_07.png', 'OctCollectIcon_08.png', 'OctCollectIcon_09.png', 'OctCollectIcon_10.png', 'OctCollectIcon_11.png', 'OctCollectIcon_12.png', 'OctCollectIcon_13.png', 'OctCollectIcon_14.png', 'OctCollectIcon_15.png', 'OctCollectIcon_16.png', 'OctCollectIcon_17.png', 'OctCollectIcon_18.png', 'OctCollectIcon_19.png', 'OctCollectIcon_20.png', 'OctCollectIcon_21.png', 'OctCollectIcon_22.png', 'OctCollectIcon_23.png', 'OctCollectIcon_24.png', 'OctCollectIcon_25.png', 'OctCollectIcon_26.png', 'OctCollectIcon_27.png', 'OctCollectIcon_28.png', 'OctCollectIcon_29.png', 'OctCollectIcon_30.png', 'OctCollectIcon_31.png', 'OctCollectIcon_32.png', 'OctCollectIcon_33.png', 'OctCollectIcon_34.png', 'OctCollectIcon_35.png', 'OctCollectIcon_36.png', 'OctCollectIcon_37.png', 'OctCollectIcon_38.png', 'OctCollectIcon_39.png', 'OctCollectIcon_40.png', 'OctCollectIcon_41.png', 'OctCollectIcon_42.png', 'OctCollectIcon_43.png', 'OctCollectIcon_44.png', 'OctCollectIcon_45.png', 'OctCollectIcon_46.png', 'OctCollectIcon_47.png', 'OctCollectIcon_48.png', 'OctCollectIcon_49.png', 'OctCollectIcon_50.png', 'OctCollectIcon_51.png', 'OctCollectIcon_52.png', 'OctCollectIcon_53.png', 'OctCollectIcon_54.png', 'OctCollectIcon_55.png', 'OctCollectIcon_56.png', 'OctCollectIcon_57.png', 'OctCollectIcon_59.png', 'OctCollectIcon_60.png', 'OctCollectIcon_61.png', 'OctCollectIcon_62.png', 'OctCollectIcon_63.png', 'OctCollectIcon_64.png', 'OctCollectIcon_65.png', 'OctCollectIcon_66.png', 'OctCollectIcon_67.png', 'OctCollectIcon_68.png', 'OctCollectIcon_69.png', 'OctCollectIcon_70.png', 'OctCollectIcon_71.png', 'OctCollectIcon_72.png', 'OctCollectIcon_73.png', 'OctCollectIcon_74.png', 'OctCollectIcon_75.png', 'OctCollectIcon_76.png', 'OctCollectIcon_77.png', 'OctCollectIcon_78.png', 'OctCollectIcon_79.png', 'lanista.png', 'heart.png']
export const shooters = ["Sploosh-o-matic", "Neo Sploosh-o-matic", "Sploosh-o-matic 7",
"Splattershot Jr.", "Custom Splattershot Jr.", "Kensa Splattershot Jr.",
"Splash-o-matic", "Neo Splash-o-matic", "Aerospray MG", "Aerospray RG",
"Aerospray PG", "Splattershot", "Tentatek Splattershot", "Kensa Splattershot",
".52 Gal", ".52 Gal Deco", "Kensa .52 Gal", "N-ZAP '85", "N-ZAP '89",
"N-ZAP '83", "Splattershot Pro", "Forge Splattershot Pro", "Kensa Splattershot Pro",
".96 Gal", ".96 Gal Deco", "Jet Squelcher", "Custom Jet Squelcher"]
export const semiauto = ["L-3 Nozzlenose", "L-3 Nozzlenose D", "Kensa L-3 Nozzlenose",
"H-3 Nozzlenose", "H-3 Nozzlenose D", "Cherry H-3 Nozzlenose", "Squeezer",
"Foil Squeezer"]
export const blasters = ["Luna Blaster", "Luna Blaster Neo", "Kensa Luna Blaster",
"Blaster", "Custom Blaster", "Range Blaster", "Custom Range Blaster",
"Grim Range Blaster", "Rapid Blaster", "Rapid Blaster Deco", "Kensa Rapid Blaster",
"Rapid Blaster Pro", "Rapid Blaster Pro Deco", "Clash Blaster", "Clash Blaster Neo"]
export const rollers = ["Carbon Roller", "Carbon Roller Deco", "Splat Roller", "Krak-On Splat Roller",
"Kensa Splat Roller", "Dynamo Roller", "Gold Dynamo Roller", "Kensa Dynamo Roller",
"Flingza Roller", "Foil Flingza Roller"]
export const brushes = ["Inkbrush", "Inkbrush Nouveau",
"Permanent Inkbrush", "Octobrush", "Octobrush Nouveau", "Kensa Octobrush"]
export const chargers = ["Classic Squiffer", "New Squiffer", "Fresh Squiffer", "Splat Charger",
"Firefin Splat Charger", "Kensa Charger", "Splatterscope", "Firefin Splatterscope",
"Kensa Splatterscope", "E-liter 4K", "Custom E-liter 4K", "E-liter 4K Scope",
"Custom E-liter 4K Scope", "Bamboozler 14 Mk I", "Bamboozler 14 Mk II",
"Bamboozler 14 Mk III", "Goo Tuber", "Custom Goo Tuber"]
export const sloshers = ["Slosher", "Slosher Deco", "Soda Slosher", "Tri-Slosher",
"Tri-Slosher Nouveau", "Sloshing Machine", "Sloshing Machine Neo",
"Kensa Sloshing Machine", "Bloblobber", "Bloblobber Deco", "Explosher",
"Custom Explosher"]
export const splatlings = ["Mini Splatling", "Zink Mini Splatling", "Kensa Mini Splatling",
"Heavy Splatling", "Heavy Splatling Deco", "Heavy Splatling Remix",
"Hydra Splatling", "Custom Hydra Splatling", "Ballpoint Splatling",
"Ballpoint Splatling Nouveau", "Nautilus 47", "Nautilus 79"]
export const dualies = ["Dapple Dualies", "Dapple Dualies Nouveau", "Clear Dapple Dualies",
"Splat Dualies", "Enperry Splat Dualies", "Kensa Splat Dualies", "Glooga Dualies",
"Glooga Dualies Deco", "Kensa Glooga Dualies", "Dualie Squelchers",
"Custom Dualie Squelchers", "Dark Tetra Dualies", "Light Tetra Dualies"]
export const brellas = ["Splat Brella", "Sorella Brella", "Tenta Brella", "Tenta Sorella Brella",
"Tenta Camo Brella", "Undercover Brella", "Undercover Sorella Brella", "Kensa Undercover Brella"]
export const weaponsByCategory = {"Shooters": shooters, "Semi-automatic Shooters": semiauto,
"Blasters": blasters, "Rollers": rollers, "Brushes": brushes, "Chargers": chargers, "Sloshers": sloshers,
"Splatlings": splatlings, "Dualies": dualies, "Brellas": brellas}
export const categoryKeys = ["Shooters", "Semi-automatic Shooters", "Blasters", "Rollers", "Brushes", "Chargers", "Sloshers", "Splatlings", "Dualies", "Brellas"]