first sync

This commit is contained in:
41666 2017-12-04 21:25:13 -06:00
parent 5d6f382c8a
commit a4acc441ea
52 changed files with 28315 additions and 0 deletions

21
Server/.editorconfig Normal file
View file

@ -0,0 +1,21 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# Change these settings to your own preference
indent_style = space
indent_size = 2
# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

6
Server/.env Normal file
View file

@ -0,0 +1,6 @@
DB_URL=postgres://roleypoly:19216801@localhost:5432/roleypoly
DEBUG=true
NODE_ENV=development
DISCORD_CLIENT_ID=363916474127220739
DISCORD_CLIENT_SECRET=PZd3u4Rkok200o-5UcG3hnB8MVdALPz5
APP_URL=http://localhost:6769

2
Server/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules
.data

53
Server/Roleypoly.js Normal file
View file

@ -0,0 +1,53 @@
const log = new (require('./logger'))('World')
const Sequelize = require('sequelize')
const fetchModels = require('./models')
const fetchApis = require('./api')
class Roleypoly {
constructor (router, io, app) {
this.router = router
this.io = io
this.ctx = {}
this.ctx.config = {
appUrl: process.env.APP_URL
}
this.ctx.io = io
this.__app = app
if (log.debugOn) log.warn('debug mode is on')
this.__initialized = this._mountServices()
}
async awaitServices () {
await this.__initialized
}
async _mountServices () {
const sequelize = new Sequelize(process.env.DB_URL, { logging: log.sql.bind(log, log) })
this.ctx.sql = sequelize
this.M = fetchModels(sequelize)
this.ctx.M = this.M
await sequelize.sync()
// this.ctx.redis = new (require('ioredis'))({
// port: process.env.REDIS_PORT || '6379',
// host: process.env.REDIS_HOST || 'localhost',
// parser: 'hiredis',
// dropBufferSupport: true,
// enableReadyCheck: true,
// enableOfflineQueue: true
// })
this.ctx.discord = new (require('./services/discord'))(this.ctx)
}
async mountRoutes () {
fetchApis(this.router, this.ctx)
this.__app.use(this.router.middleware())
}
}
module.exports = Roleypoly

3
Server/api/auth.js Normal file
View file

@ -0,0 +1,3 @@
module.exports = R => {
}

20
Server/api/index.js Normal file
View file

@ -0,0 +1,20 @@
const log = new (require('../logger'))('api/index')
const glob = require('glob')
module.exports = async (router, ctx) => {
const apis = glob.sync('./api/**/!(index).js')
log.debug('found apis', apis)
for (let a of apis) {
if (a.endsWith('_test.js') !== null && process.env.NODE_ENV !== 'development') {
log.debug(`skipping ${a}`)
continue
}
log.debug(`mounting ${a}`)
try {
require(a.replace('api/', ''))(router, ctx)
} catch (e) {
log.error(`couldn't mount ${a}`, e)
}
}
}

6
Server/api/servers.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = (R, $) => {
R.get('/api/servers', (ctx) => {
ctx.body = 'hi'
console.log($)
})
}

44
Server/index.js Normal file
View file

@ -0,0 +1,44 @@
require('dotenv').config({silent: true})
const log = new (require('./logger'))('index')
const http = require('http')
const Koa = require('koa')
const app = new Koa()
const _io = require('socket.io')
const router = require('koa-better-router')().loadMethods()
const Roleypoly = require('./Roleypoly')
// Create the server and socket.io server
const server = http.createServer(app.callback())
const io = _io(server, { transports: ['websocket'], path: '/api/socket.io', wsEngine: 'uws' })
const M = new Roleypoly(router, io, app) // eslint-disable-line no-unused-vars
async function start () {
// body parser
const bodyParser = require('koa-bodyparser')
app.use(bodyParser({ types: ['json'] }))
// Request logger
app.use(async (ctx, next) => {
let timeStart = new Date()
await next()
let timeElapsed = new Date() - timeStart
log.request(`${ctx.status} ${ctx.method} ${ctx.url} - ${ctx.ip} - took ${timeElapsed}ms`)
return null
})
// Construct the Roleypoly!
await M.mountRoutes()
// Start it!
await M.awaitServices()
log.info(`starting HTTP server on ${process.env.APP_PORT || 6769}`)
server.listen(process.env.APP_PORT || 6769)
}
start().catch(e => {
console.trace(e)
})

56
Server/logger.js Normal file
View file

@ -0,0 +1,56 @@
const chalk = require('chalk')
// const { debug } = require('yargs').argv
// process.env.DEBUG = process.env.DEBUG || debug
// logger template//
// const log = new (require('../logger'))('server/thing')
class Logger {
constructor (name, debugOverride = false) {
this.name = name
this.debugOn = (process.env.DEBUG === 'true' || process.env.DEBUG === '*') || debugOverride
}
fatal (text, ...data) {
this.error(text, data)
if (typeof data[data.length - 1] === 'number') {
process.exit(data[data.length - 1])
}
throw text
}
error (text, ...data) {
console.error(chalk.red.bold(`ERR ${this.name}:`) + `\n ${text}`, data)
}
warn (text, ...data) {
console.warn(chalk.yellow.bold(`WARN ${this.name}:`) + `\n ${text}`, data)
}
notice (text, ...data) {
console.log(chalk.cyan.bold(`NOTICE ${this.name}:`) + `\n ${text}`, data)
}
info (text, ...data) {
console.info(chalk.blue.bold(`INFO ${this.name}:`) + `\n ${text}`, data)
}
request (text, ...data) {
console.info(chalk.green.bold(`HTTP ${this.name}:`) + `\n ${text}`)
}
debug (text, ...data) {
if (this.debugOn) {
console.log(chalk.gray.bold(`DEBUG ${this.name}:`) + `\n ${text}`, data)
}
}
sql (logger, ...data) {
if (logger.debugOn) {
console.log(chalk.bold('DEBUG SQL:\n '), data)
}
}
}
module.exports = Logger

14
Server/models/Server.js Normal file
View file

@ -0,0 +1,14 @@
module.exports = (sql, DataTypes) => {
return sql.define('server', {
id: { // discord snowflake
type: DataTypes.TEXT,
primaryKey: true
},
categories: {
type: DataTypes.JSON
},
note: {
type: DataTypes.TEXT
}
})
}

7
Server/models/Session.js Normal file
View file

@ -0,0 +1,7 @@
module.exports = (sequelize, DataTypes) => {
return sequelize.define('session', {
id: { type: DataTypes.TEXT, primaryKey: true },
maxAge: DataTypes.BIGINT,
data: DataTypes.JSONB
})
}

37
Server/models/index.js Normal file
View file

@ -0,0 +1,37 @@
const log = new (require('../logger'))('models/index')
const glob = require('glob')
const path = require('path')
const util = require('../util/model-methods')
module.exports = (sql) => {
const models = {}
const modelFiles = glob.sync('./models/**/!(index).js')
log.debug('found models', modelFiles)
modelFiles.forEach((v) => {
let name = path.basename(v).replace('.js', '')
if (v === './models/index.js') {
log.debug('index.js hit, skipped')
return
}
try {
log.debug('importing..', v.replace('models/', ''))
let model = sql.import(v.replace('models/', ''))
models[name] = model
} catch (err) {
log.fatal('error importing model ' + v, err)
process.exit(-1)
}
})
Object.keys(models).forEach((v) => {
if (models[v].hasOwnProperty('__associations')) {
models[v].__associations(models)
}
if (models[v].hasOwnProperty('__instanceMethods')) {
models[v].__instanceMethods(models[v])
}
})
return models
}

4669
Server/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

40
Server/package.json Normal file
View file

@ -0,0 +1,40 @@
{
"name": "backend",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "standard && node index.js",
"fix": "standard --fix",
"dev": "pm2 start index.js --watch",
"pm2": "pm2"
},
"dependencies": {
"chalk": "^2.3.0",
"commander": "^2.12.2",
"discord.js": "^11.2.1",
"dotenv": "^4.0.0",
"erlpack": "github:discordapp/erlpack",
"eventemitter3": "^3.0.0",
"glob": "^7.1.2",
"immutable": "^3.8.2",
"inquirer": "^4.0.1",
"koa": "^2.4.1",
"koa-better-router": "^2.1.1",
"koa-bodyparser": "^4.2.0",
"koa-passport": "^4.0.1",
"koa-session": "^5.5.1",
"ksuid": "^0.4.0",
"passport-discord": "^0.1.3",
"passport-oauth2-refresh": "^1.0.0",
"pg": "^7.4.0",
"pg-hstore": "^2.3.2",
"pm2": "^2.8.0",
"sequelize": "^4.25.1",
"socket.io": "^2.0.4",
"standard": "^10.0.3",
"superagent": "^3.8.1",
"uuid": "^3.1.0",
"uws": "^9.14.0",
"yargs": "^10.0.3"
}
}

View file

@ -0,0 +1,10 @@
const Logger = require('../logger')
class Service {
constructor (ctx) {
this.ctx = ctx
this.log = new Logger(this.constructor.name)
}
}
module.exports = Service

View file

@ -0,0 +1,80 @@
const Service = require('./Service')
const discord = require('discord.js')
const superagent = require('superagent')
class DiscordService extends Service {
constructor (ctx) {
super(ctx)
this.botToken = process.env.DISCORD_BOT_TOKEN
this.clientId = process.env.DISCORD_CLIENT_ID
this.clientSecret = process.env.DISCORD_CLIENT_SECRET
this.oauthCallback = process.env.OAUTH_AUTH_CALLBACK
this.botCallback = `${ctx.config.appUrl}/oauth/bot/callback`
this.startBot()
}
async startBot () {
await discord.login(this.botToken)
}
// oauth step 2 flow, grab the auth token via code
async getAuthToken (code) {
const url = 'https://discordapp.com/api/oauth2/token'
try {
const rsp =
await superagent
.post(url)
.send({
client_id: this.clientId,
client_secret: this.clientSecret,
grant_type: 'authorization_code',
code: code,
redirect_uri: this.oauthCallback
})
return rsp.body
} catch (e) {
this.log.error('getAuthToken failed', e)
throw e
}
}
// on sign out, we revoke the token we had.
async revokeAuthToken (code, state) {
const url = 'https://discordapp.com/api/oauth2/revoke'
try {
const rsp =
await superagent
.post(url)
.send({
client_id: this.clientId,
client_secret: this.clientSecret,
grant_type: 'authorization_code',
code: code,
redirect_uri: this.oauthCallback
})
return rsp.body
} catch (e) {
this.log.error('getAuthToken failed', e)
throw e
}
}
// returns oauth authorize url with IDENTIFY permission
// we only need IDENTIFY because we only use it for matching IDs from the bot
getAuthUrl (state) {
return `https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&redirect_uri=${this.oauthCallback}&response_type=code&scope=identify`
}
// returns the bot join url with MANAGE_ROLES permission
// MANAGE_ROLES is the only permission we really need.
getBotJoinUrl () {
return `https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&scope=bot&permissions=268435456&redirect_uri=${this.botCallback}`
}
}
module.exports = DiscordService

View file

@ -0,0 +1,10 @@
const ksuid = require('ksuid')
module.exports = {
ksuid (field = 'id') {
return async function () {
this.id = await ksuid.random()
return this
}
}
}

3294
Server/yarn.lock Normal file

File diff suppressed because it is too large Load diff

3
UI/.eslintrc.js Normal file
View file

@ -0,0 +1,3 @@
module.exports = {
"extends": "standard"
};

21
UI/.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

2229
UI/README.md Normal file

File diff suppressed because it is too large Load diff

10472
UI/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

32
UI/package.json Normal file
View file

@ -0,0 +1,32 @@
{
"name": "roleypoly",
"version": "0.1.0",
"private": true,
"dependencies": {
"color": "^2.0.1",
"eslint": "^4.12.1",
"prop-types": "^15.6.0",
"radium": "^0.19.6",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-scripts": "1.0.17",
"superagent": "^3.8.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:6769",
"devDependencies": {
"eslint-config-standard": "^10.2.1",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-node": "^5.2.1",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-react": "^7.5.1",
"eslint-plugin-standard": "^3.0.1"
}
}

BIN
UI/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
UI/public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

24
UI/public/index.html Normal file
View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" sizes="any" href="%PUBLIC_URL%/favicon.png"/>
<title>Roleypoly</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.35/css/uikit.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.35/js/uikit.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.35/js/uikit-icons.min.js"></script>
<style>body{ background-color: #453F3E; }</style>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->

15
UI/public/manifest.json Normal file
View file

@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

28
UI/src/App.css Normal file
View file

@ -0,0 +1,28 @@
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}

20
UI/src/App.js Normal file
View file

@ -0,0 +1,20 @@
import React, { Component } from 'react'
import { BrowserRouter } from 'react-router-dom'
import './App.css'
import Wrapper from './components/wrapper'
import AppRouter from './router'
class App extends Component {
render () {
return (
<BrowserRouter>
<Wrapper>
<AppRouter />
</Wrapper>
</BrowserRouter>
)
}
}
export default App

8
UI/src/App.test.js Normal file
View file

@ -0,0 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
});

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="350px" height="350px" viewBox="0 0 350 350" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 44.1 (41455) - http://www.bohemiancoding.com/sketch -->
<title>Slice</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M95.5351562,425.050781 C180.587008,425.050781 249.535156,356.102633 249.535156,271.050781 C249.535156,185.99893 172.535156,117.050781 95.5351562,117.050781 L95.5351562,425.050781 Z M95.5351563,387.050781 C159.600187,387.050781 211.535156,334.668097 211.535156,270.050781 C211.535156,205.433466 153.535156,153.050781 95.5351563,153.050781 C95.5351562,203.905895 95.5351563,337.260095 95.5351563,387.050781 Z" id="path-1"></path>
</defs>
<g id="Logomark" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group" transform="translate(35.000000, -46.000000)">
<text id="R" font-family="HelveticaNeue-Medium, Helvetica Neue" font-size="288" font-weight="400" fill="#000000">
<tspan x="0" y="281">R</tspan>
</text>
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Oval" fill="#000000" transform="translate(172.535156, 271.050781) rotate(45.000000) translate(-172.535156, -271.050781) " xlink:href="#path-1"></use>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,20 @@
import React from 'react'
const Logotype = ({fill = 'var(--c-7)', width, height, circleFill, typeFill, style, className}) => (
<svg style={style} className={className} viewBox='0 0 1566 298' version='1.1' xmlns='http://www.w3.org/2000/svg'>
<g id='Page-1' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'>
<text id='Roleypoly' font-family='HelveticaNeue-Medium, Helvetica Neue' font-size='288' font-weight='400' fill={typeFill || fill}>
<tspan x='249' y='238'>Roleypoly</tspan>
</text>
<defs>
<path d='M86.5351562,318.050781 C171.587008,318.050781 240.535156,249.102633 240.535156,164.050781 C240.535156,78.9989298 163.535156,10.0507812 86.5351562,10.0507812 L86.5351562,318.050781 Z M86.5351563,280.050781 C150.600187,280.050781 202.535156,227.668097 202.535156,163.050781 C202.535156,98.4334655 144.535156,46.0507812 86.5351563,46.0507812 C86.5351562,96.9058949 86.5351563,230.260095 86.5351563,280.050781 Z' id='path-1' />
</defs>
<mask id='mask-2' fill='white'>
<use href='#path-1' />
</mask>
<use id='Oval' fill={circleFill || fill} transform='translate(163.535156, 164.050781) rotate(45.000000) translate(-163.535156, -164.050781) ' href='#path-1' />
</g>
</svg>
)
export default Logotype

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1566px" height="298px" viewBox="0 0 1566 298" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 44.1 (41455) - http://www.bohemiancoding.com/sketch -->
<title>Untitled</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M86.5351562,318.050781 C171.587008,318.050781 240.535156,249.102633 240.535156,164.050781 C240.535156,78.9989298 163.535156,10.0507812 86.5351562,10.0507812 L86.5351562,318.050781 Z M86.5351563,280.050781 C150.600187,280.050781 202.535156,227.668097 202.535156,163.050781 C202.535156,98.4334655 144.535156,46.0507812 86.5351563,46.0507812 C86.5351562,96.9058949 86.5351563,230.260095 86.5351563,280.050781 Z" id="path-1"></path>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<text id="Roleypoly" font-family="HelveticaNeue-Medium, Helvetica Neue" font-size="288" font-weight="400" fill="#AB9C9A">
<tspan x="249" y="238">Roleypoly</tspan>
</text>
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Oval" fill="#AB9C9A" transform="translate(163.535156, 164.050781) rotate(45.000000) translate(-163.535156, -164.050781) " xlink:href="#path-1"></use>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,42 @@
import React, { Component, Fragment } from 'react'
import { Redirect } from 'react-router-dom'
import superagent from 'superagent'
class OauthCallback extends Component {
state = {
notReady: true,
message: 'chotto matte kudasai...'
}
async componentDidMount () {
// handle stuff in the url
const sp = new URLSearchParams(this.props.location.search)
const token = sp.get('code')
if (token === '' || token == null) {
this.setState({ message: 'token missing, what are you trying to do?!' })
return
}
this.props.history.replace(this.props.location.pathname)
// pass token to backend, await it to finish it's business.
try {
await superagent.post('/api/auth/token').send({ token })
} catch (e) {
console.error('token pass error', e)
this.setState({ message: 'g-gomen nasai... i broke it...' })
return
}
this.setState({ notReady: false })
// update user stuff here
}
render () {
return (this.state.notReady) ? this.state.message : <Redirect to='/s' />
}
}
export default OauthCallback

View file

@ -0,0 +1,19 @@
import React, { Component, Fragment } from 'react'
import Radium from 'radium'
import serverStyles, { navigation as styles } from './styles'
import ServerCard from './ServerCard'
import UserCard from './UserCard'
class ServersNavigation extends Component {
render () {
return <Fragment>
<UserCard user={this.props.user} />
<div className={this.props.className}>
{ this.props.servers.map((s, i) => <ServerCard server={s} key={i} />) }
</div>
</Fragment>
}
}
export default Radium(ServersNavigation)

View file

@ -0,0 +1,43 @@
.server-list__item {
display: flex;
border-bottom: 1px solid var(--c-3);
padding: 25px 15px;
padding-right: 0;
}
a.server-list__item {
color: var(--c-white);
text-decoration: none;
}
a.server-list__item.active {
cursor: default
}
.server-list__item.active {
background-color: var(--c-3);
}
.server-list__item:hover:not(.active) {
background-color: rgba(0,0,0,0.25) !important;
cursor: pointer;
}
.server-list__item:last-of-type {
border: 0
}
.server-list__item:nth-of-type(even):not(.active) {
background-color: rgba(0,0,0,0.1);
}
.server-list__item__icon img {
border-radius: 100%;
width: 50px;
height: 50px;
border: 2px solid transparent;
}
.server-list__item__info {
padding: 0 10px
}

View file

@ -0,0 +1,21 @@
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
import Radium from 'radium'
import './ServerCard.css'
class ServerCard extends Component {
render () {
const { server } = this.props
return <NavLink className='server-list__item' activeClassName='active' to={`/s/${server.id}`}>
<div className='server-list__item__icon'>
<img src={`https://cdn.discordapp.com/icons/${server.id}/${server.icon}.png`} />
</div>
<div className='server-list__item__info'>
<b>{server.name}</b><br />
<span>{server.name}</span>
</div>
</NavLink>
}
}
export default Radium(ServerCard)

View file

@ -0,0 +1,45 @@
.user-card {
position: relative;
display: flex;
background-color: rgba(0,0,0,0.3);
border-bottom: 1px solid var(--c-3);
padding: 25px 15px;
grid-area: user;
}
.user-card__icon img {
width: 50px;
height: 50px;
border-radius: 100%;
border: 2px solid #5fc66d;
}
.user-card__info {
padding: 0 10px;
line-height: 50px
}
.user-card__info__discrim {
font-weight: 100;
font-size: 0.7rem;
color: var(--c-7);
}
.user-card__info__name {
/* font-size */
/* font-weight: ; */
}
.user-card__actions {
position: absolute;
right: 5px;
top: 5px;
bottom: 5px;
display: flex;
align-items: center;
justify-content: center;
}
.user-card__actions polygon {
fill: var(--c-7);
}

View file

@ -0,0 +1,31 @@
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
import Radium from 'radium'
import './UserCard.css'
class UserCard extends Component {
render () {
const { user } = this.props
return <div className='user-card'>
<div className='user-card__icon'>
<img src={`https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`} />
</div>
<div className='user-card__info'>
<span className='user-card__info__name'>{user.username}</span><span className='user-card__info__discrim'>#{user.discriminator}</span>
</div>
<div className='user-card__actions'>
<ul className='uk-iconnav uk-iconnav-vertical'>
<li><NavLink uk-tooltip='' title='Sign out' uk-icon='icon: sign-out' to='/auth/signout' activeClassName='uk-active' /></li>
{
(this.props.user.isRoot === true)
? <li><NavLink uk-tooltip='' title='Root' uk-icon='icon: bolt' to='/root/' activeClassName='uk-active' /></li>
: null
}
</ul>
</div>
</div>
}
}
export default Radium(UserCard)

View file

@ -0,0 +1,20 @@
.servers {
display: grid;
grid-template-rows: 100px calc(100vh - 75px);
grid-template-columns: 300px 1fr;
grid-template-areas: "user content"
"listing content";
}
.servers__nav {
grid-area: listing;
overflow-y: scroll;
height: calc(100vh - 175px);
}
.servers__content {
grid-area: content;
background-color: var(--c-3);
padding: 15px;
overflow-y: scroll;
}

View file

@ -0,0 +1,20 @@
import React, { Component } from 'react'
import Radium from 'radium'
import './index.css'
import Navigation from './Navigation'
import mockData from './mockData'
class Servers extends Component {
render () {
return <div className="servers">
<Navigation className="servers__nav" servers={mockData.servers} user={mockData.user} />
<div className="servers__content">
{/* another router probably. */}
</div>
</div>
}
}
export default Radium(Servers)

View file

@ -0,0 +1,13 @@
export default {
servers: [
{'id': '318647068782755840', 'name': 'jumpystick / かちい', 'ownerId': '62601275618889728', 'icon': 'f729111314c86f09ec7b421455dde383', 'splash': null, 'features': [], 'roles': {'318647823845425162': {'position': 14, 'permissions': 2146954495, 'name': 'nee-chan', 'mentionable': false, 'managed': false, 'id': '318647823845425162', 'hoist': true, 'color': 16371117, 'originalPosition': 14, 'colorString': '#f9cdad'}, '318648103152648202': {'position': 13, 'permissions': 536341575, 'name': 'mods', 'mentionable': true, 'managed': false, 'id': '318648103152648202', 'hoist': true, 'color': 8630171, 'originalPosition': 13, 'colorString': '#83af9b'}, '318649514745397248': {'position': 12, 'permissions': 372632641, 'name': 'botty mcbotface', 'mentionable': false, 'managed': false, 'id': '318649514745397248', 'hoist': false, 'color': 8630171, 'originalPosition': 12, 'colorString': '#83af9b'}, '329358102975741955': {'position': 11, 'permissions': 104188993, 'name': 'cute person 🎀', 'mentionable': false, 'managed': false, 'id': '329358102975741955', 'hoist': false, 'color': 15844367, 'originalPosition': 11, 'colorString': '#f1c40f'}, '343121679150743555': {'position': 10, 'permissions': 268697665, 'name': 'Patreon', 'mentionable': false, 'managed': true, 'id': '343121679150743555', 'hoist': false, 'color': 0, 'originalPosition': 10, 'colorString': null}, '343121980477800458': {'position': 9, 'permissions': 104188993, 'name': 'patreon supporter', 'mentionable': false, 'managed': false, 'id': '343121980477800458', 'hoist': true, 'color': 16555418, 'originalPosition': 9, 'colorString': '#fc9d9a'}, '330588237670121472': {'position': 8, 'permissions': 104188993, 'name': 'subscriber', 'mentionable': false, 'managed': true, 'id': '330588237670121472', 'hoist': true, 'color': 16663397, 'originalPosition': 8, 'colorString': '#fe4365'}, '344565996411027456': {'position': 7, 'permissions': 104188993, 'name': 'ninja sub', 'mentionable': false, 'managed': false, 'id': '344565996411027456', 'hoist': false, 'color': 16663397, 'originalPosition': 7, 'colorString': '#fe4365'}, '318648703311151104': {'position': 6, 'permissions': 104320065, 'name': 'community member', 'mentionable': false, 'managed': false, 'id': '318648703311151104', 'hoist': false, 'color': 9807270, 'originalPosition': 6, 'colorString': '#95a5a6'}, '318652392142929920': {'position': 5, 'permissions': 519232, 'name': 'MuxyBot', 'mentionable': false, 'managed': true, 'id': '318652392142929920', 'hoist': false, 'color': 0, 'originalPosition': 5, 'colorString': null}, '318850773918285824': {'position': 4, 'permissions': 67437569, 'name': 'muted', 'mentionable': false, 'managed': false, 'id': '318850773918285824', 'hoist': false, 'color': 6323595, 'originalPosition': 4, 'colorString': '#607d8b'}, '346731968873889803': {'position': 3, 'permissions': 12921935, 'name': 'Tatsumaki', 'mentionable': false, 'managed': true, 'id': '346731968873889803', 'hoist': false, 'color': 0, 'originalPosition': 3, 'colorString': null}, '360585645405503488': {'position': 2, 'permissions': 104188993, 'name': 'バーバーバカ お兄ちゃん!', 'mentionable': false, 'managed': false, 'id': '360585645405503488', 'hoist': false, 'color': 10378846, 'originalPosition': 2, 'colorString': '#9e5e5e'}, '383433060747575296': {'position': 1, 'permissions': 104188993, 'name': 'heart of pure gold 🧡', 'mentionable': false, 'managed': false, 'id': '383433060747575296', 'hoist': false, 'color': 12429145, 'originalPosition': 1, 'colorString': '#bda759'}, '318647068782755840': {'position': 0, 'permissions': 104188993, 'name': '@everyone', 'mentionable': false, 'managed': false, 'id': '318647068782755840', 'hoist': false, 'color': 0, 'originalPosition': 0, 'colorString': null}}, 'afkChannelId': null, 'afkTimeout': 300, 'systemChannelId': null, 'verificationLevel': 2, 'region': 'us-south', 'joinedAt': '2017-05-29T07:09:41.428Z', 'large': true, 'defaultMessageNotifications': 1, 'mfaLevel': 0, 'application_id': null, 'explicitContentFilter': 2},
{'id': '318647068782755840', 'name': 'jumpystick / かちい', 'ownerId': '62601275618889728', 'icon': 'f729111314c86f09ec7b421455dde383', 'splash': null, 'features': [], 'roles': {'318647823845425162': {'position': 14, 'permissions': 2146954495, 'name': 'nee-chan', 'mentionable': false, 'managed': false, 'id': '318647823845425162', 'hoist': true, 'color': 16371117, 'originalPosition': 14, 'colorString': '#f9cdad'}, '318648103152648202': {'position': 13, 'permissions': 536341575, 'name': 'mods', 'mentionable': true, 'managed': false, 'id': '318648103152648202', 'hoist': true, 'color': 8630171, 'originalPosition': 13, 'colorString': '#83af9b'}, '318649514745397248': {'position': 12, 'permissions': 372632641, 'name': 'botty mcbotface', 'mentionable': false, 'managed': false, 'id': '318649514745397248', 'hoist': false, 'color': 8630171, 'originalPosition': 12, 'colorString': '#83af9b'}, '329358102975741955': {'position': 11, 'permissions': 104188993, 'name': 'cute person 🎀', 'mentionable': false, 'managed': false, 'id': '329358102975741955', 'hoist': false, 'color': 15844367, 'originalPosition': 11, 'colorString': '#f1c40f'}, '343121679150743555': {'position': 10, 'permissions': 268697665, 'name': 'Patreon', 'mentionable': false, 'managed': true, 'id': '343121679150743555', 'hoist': false, 'color': 0, 'originalPosition': 10, 'colorString': null}, '343121980477800458': {'position': 9, 'permissions': 104188993, 'name': 'patreon supporter', 'mentionable': false, 'managed': false, 'id': '343121980477800458', 'hoist': true, 'color': 16555418, 'originalPosition': 9, 'colorString': '#fc9d9a'}, '330588237670121472': {'position': 8, 'permissions': 104188993, 'name': 'subscriber', 'mentionable': false, 'managed': true, 'id': '330588237670121472', 'hoist': true, 'color': 16663397, 'originalPosition': 8, 'colorString': '#fe4365'}, '344565996411027456': {'position': 7, 'permissions': 104188993, 'name': 'ninja sub', 'mentionable': false, 'managed': false, 'id': '344565996411027456', 'hoist': false, 'color': 16663397, 'originalPosition': 7, 'colorString': '#fe4365'}, '318648703311151104': {'position': 6, 'permissions': 104320065, 'name': 'community member', 'mentionable': false, 'managed': false, 'id': '318648703311151104', 'hoist': false, 'color': 9807270, 'originalPosition': 6, 'colorString': '#95a5a6'}, '318652392142929920': {'position': 5, 'permissions': 519232, 'name': 'MuxyBot', 'mentionable': false, 'managed': true, 'id': '318652392142929920', 'hoist': false, 'color': 0, 'originalPosition': 5, 'colorString': null}, '318850773918285824': {'position': 4, 'permissions': 67437569, 'name': 'muted', 'mentionable': false, 'managed': false, 'id': '318850773918285824', 'hoist': false, 'color': 6323595, 'originalPosition': 4, 'colorString': '#607d8b'}, '346731968873889803': {'position': 3, 'permissions': 12921935, 'name': 'Tatsumaki', 'mentionable': false, 'managed': true, 'id': '346731968873889803', 'hoist': false, 'color': 0, 'originalPosition': 3, 'colorString': null}, '360585645405503488': {'position': 2, 'permissions': 104188993, 'name': 'バーバーバカ お兄ちゃん!', 'mentionable': false, 'managed': false, 'id': '360585645405503488', 'hoist': false, 'color': 10378846, 'originalPosition': 2, 'colorString': '#9e5e5e'}, '383433060747575296': {'position': 1, 'permissions': 104188993, 'name': 'heart of pure gold 🧡', 'mentionable': false, 'managed': false, 'id': '383433060747575296', 'hoist': false, 'color': 12429145, 'originalPosition': 1, 'colorString': '#bda759'}, '318647068782755840': {'position': 0, 'permissions': 104188993, 'name': '@everyone', 'mentionable': false, 'managed': false, 'id': '318647068782755840', 'hoist': false, 'color': 0, 'originalPosition': 0, 'colorString': null}}, 'afkChannelId': null, 'afkTimeout': 300, 'systemChannelId': null, 'verificationLevel': 2, 'region': 'us-south', 'joinedAt': '2017-05-29T07:09:41.428Z', 'large': true, 'defaultMessageNotifications': 1, 'mfaLevel': 0, 'application_id': null, 'explicitContentFilter': 2},
{'id': '318647068782755840', 'name': 'jumpystick / かちい', 'ownerId': '62601275618889728', 'icon': 'f729111314c86f09ec7b421455dde383', 'splash': null, 'features': [], 'roles': {'318647823845425162': {'position': 14, 'permissions': 2146954495, 'name': 'nee-chan', 'mentionable': false, 'managed': false, 'id': '318647823845425162', 'hoist': true, 'color': 16371117, 'originalPosition': 14, 'colorString': '#f9cdad'}, '318648103152648202': {'position': 13, 'permissions': 536341575, 'name': 'mods', 'mentionable': true, 'managed': false, 'id': '318648103152648202', 'hoist': true, 'color': 8630171, 'originalPosition': 13, 'colorString': '#83af9b'}, '318649514745397248': {'position': 12, 'permissions': 372632641, 'name': 'botty mcbotface', 'mentionable': false, 'managed': false, 'id': '318649514745397248', 'hoist': false, 'color': 8630171, 'originalPosition': 12, 'colorString': '#83af9b'}, '329358102975741955': {'position': 11, 'permissions': 104188993, 'name': 'cute person 🎀', 'mentionable': false, 'managed': false, 'id': '329358102975741955', 'hoist': false, 'color': 15844367, 'originalPosition': 11, 'colorString': '#f1c40f'}, '343121679150743555': {'position': 10, 'permissions': 268697665, 'name': 'Patreon', 'mentionable': false, 'managed': true, 'id': '343121679150743555', 'hoist': false, 'color': 0, 'originalPosition': 10, 'colorString': null}, '343121980477800458': {'position': 9, 'permissions': 104188993, 'name': 'patreon supporter', 'mentionable': false, 'managed': false, 'id': '343121980477800458', 'hoist': true, 'color': 16555418, 'originalPosition': 9, 'colorString': '#fc9d9a'}, '330588237670121472': {'position': 8, 'permissions': 104188993, 'name': 'subscriber', 'mentionable': false, 'managed': true, 'id': '330588237670121472', 'hoist': true, 'color': 16663397, 'originalPosition': 8, 'colorString': '#fe4365'}, '344565996411027456': {'position': 7, 'permissions': 104188993, 'name': 'ninja sub', 'mentionable': false, 'managed': false, 'id': '344565996411027456', 'hoist': false, 'color': 16663397, 'originalPosition': 7, 'colorString': '#fe4365'}, '318648703311151104': {'position': 6, 'permissions': 104320065, 'name': 'community member', 'mentionable': false, 'managed': false, 'id': '318648703311151104', 'hoist': false, 'color': 9807270, 'originalPosition': 6, 'colorString': '#95a5a6'}, '318652392142929920': {'position': 5, 'permissions': 519232, 'name': 'MuxyBot', 'mentionable': false, 'managed': true, 'id': '318652392142929920', 'hoist': false, 'color': 0, 'originalPosition': 5, 'colorString': null}, '318850773918285824': {'position': 4, 'permissions': 67437569, 'name': 'muted', 'mentionable': false, 'managed': false, 'id': '318850773918285824', 'hoist': false, 'color': 6323595, 'originalPosition': 4, 'colorString': '#607d8b'}, '346731968873889803': {'position': 3, 'permissions': 12921935, 'name': 'Tatsumaki', 'mentionable': false, 'managed': true, 'id': '346731968873889803', 'hoist': false, 'color': 0, 'originalPosition': 3, 'colorString': null}, '360585645405503488': {'position': 2, 'permissions': 104188993, 'name': 'バーバーバカ お兄ちゃん!', 'mentionable': false, 'managed': false, 'id': '360585645405503488', 'hoist': false, 'color': 10378846, 'originalPosition': 2, 'colorString': '#9e5e5e'}, '383433060747575296': {'position': 1, 'permissions': 104188993, 'name': 'heart of pure gold 🧡', 'mentionable': false, 'managed': false, 'id': '383433060747575296', 'hoist': false, 'color': 12429145, 'originalPosition': 1, 'colorString': '#bda759'}, '318647068782755840': {'position': 0, 'permissions': 104188993, 'name': '@everyone', 'mentionable': false, 'managed': false, 'id': '318647068782755840', 'hoist': false, 'color': 0, 'originalPosition': 0, 'colorString': null}}, 'afkChannelId': null, 'afkTimeout': 300, 'systemChannelId': null, 'verificationLevel': 2, 'region': 'us-south', 'joinedAt': '2017-05-29T07:09:41.428Z', 'large': true, 'defaultMessageNotifications': 1, 'mfaLevel': 0, 'application_id': null, 'explicitContentFilter': 2},
{'id': '318647068782755840', 'name': 'jumpystick / かちい', 'ownerId': '62601275618889728', 'icon': 'f729111314c86f09ec7b421455dde383', 'splash': null, 'features': [], 'roles': {'318647823845425162': {'position': 14, 'permissions': 2146954495, 'name': 'nee-chan', 'mentionable': false, 'managed': false, 'id': '318647823845425162', 'hoist': true, 'color': 16371117, 'originalPosition': 14, 'colorString': '#f9cdad'}, '318648103152648202': {'position': 13, 'permissions': 536341575, 'name': 'mods', 'mentionable': true, 'managed': false, 'id': '318648103152648202', 'hoist': true, 'color': 8630171, 'originalPosition': 13, 'colorString': '#83af9b'}, '318649514745397248': {'position': 12, 'permissions': 372632641, 'name': 'botty mcbotface', 'mentionable': false, 'managed': false, 'id': '318649514745397248', 'hoist': false, 'color': 8630171, 'originalPosition': 12, 'colorString': '#83af9b'}, '329358102975741955': {'position': 11, 'permissions': 104188993, 'name': 'cute person 🎀', 'mentionable': false, 'managed': false, 'id': '329358102975741955', 'hoist': false, 'color': 15844367, 'originalPosition': 11, 'colorString': '#f1c40f'}, '343121679150743555': {'position': 10, 'permissions': 268697665, 'name': 'Patreon', 'mentionable': false, 'managed': true, 'id': '343121679150743555', 'hoist': false, 'color': 0, 'originalPosition': 10, 'colorString': null}, '343121980477800458': {'position': 9, 'permissions': 104188993, 'name': 'patreon supporter', 'mentionable': false, 'managed': false, 'id': '343121980477800458', 'hoist': true, 'color': 16555418, 'originalPosition': 9, 'colorString': '#fc9d9a'}, '330588237670121472': {'position': 8, 'permissions': 104188993, 'name': 'subscriber', 'mentionable': false, 'managed': true, 'id': '330588237670121472', 'hoist': true, 'color': 16663397, 'originalPosition': 8, 'colorString': '#fe4365'}, '344565996411027456': {'position': 7, 'permissions': 104188993, 'name': 'ninja sub', 'mentionable': false, 'managed': false, 'id': '344565996411027456', 'hoist': false, 'color': 16663397, 'originalPosition': 7, 'colorString': '#fe4365'}, '318648703311151104': {'position': 6, 'permissions': 104320065, 'name': 'community member', 'mentionable': false, 'managed': false, 'id': '318648703311151104', 'hoist': false, 'color': 9807270, 'originalPosition': 6, 'colorString': '#95a5a6'}, '318652392142929920': {'position': 5, 'permissions': 519232, 'name': 'MuxyBot', 'mentionable': false, 'managed': true, 'id': '318652392142929920', 'hoist': false, 'color': 0, 'originalPosition': 5, 'colorString': null}, '318850773918285824': {'position': 4, 'permissions': 67437569, 'name': 'muted', 'mentionable': false, 'managed': false, 'id': '318850773918285824', 'hoist': false, 'color': 6323595, 'originalPosition': 4, 'colorString': '#607d8b'}, '346731968873889803': {'position': 3, 'permissions': 12921935, 'name': 'Tatsumaki', 'mentionable': false, 'managed': true, 'id': '346731968873889803', 'hoist': false, 'color': 0, 'originalPosition': 3, 'colorString': null}, '360585645405503488': {'position': 2, 'permissions': 104188993, 'name': 'バーバーバカ お兄ちゃん!', 'mentionable': false, 'managed': false, 'id': '360585645405503488', 'hoist': false, 'color': 10378846, 'originalPosition': 2, 'colorString': '#9e5e5e'}, '383433060747575296': {'position': 1, 'permissions': 104188993, 'name': 'heart of pure gold 🧡', 'mentionable': false, 'managed': false, 'id': '383433060747575296', 'hoist': false, 'color': 12429145, 'originalPosition': 1, 'colorString': '#bda759'}, '318647068782755840': {'position': 0, 'permissions': 104188993, 'name': '@everyone', 'mentionable': false, 'managed': false, 'id': '318647068782755840', 'hoist': false, 'color': 0, 'originalPosition': 0, 'colorString': null}}, 'afkChannelId': null, 'afkTimeout': 300, 'systemChannelId': null, 'verificationLevel': 2, 'region': 'us-south', 'joinedAt': '2017-05-29T07:09:41.428Z', 'large': true, 'defaultMessageNotifications': 1, 'mfaLevel': 0, 'application_id': null, 'explicitContentFilter': 2},
{'id': '318647068782755840', 'name': 'jumpystick / かちい', 'ownerId': '62601275618889728', 'icon': 'f729111314c86f09ec7b421455dde383', 'splash': null, 'features': [], 'roles': {'318647823845425162': {'position': 14, 'permissions': 2146954495, 'name': 'nee-chan', 'mentionable': false, 'managed': false, 'id': '318647823845425162', 'hoist': true, 'color': 16371117, 'originalPosition': 14, 'colorString': '#f9cdad'}, '318648103152648202': {'position': 13, 'permissions': 536341575, 'name': 'mods', 'mentionable': true, 'managed': false, 'id': '318648103152648202', 'hoist': true, 'color': 8630171, 'originalPosition': 13, 'colorString': '#83af9b'}, '318649514745397248': {'position': 12, 'permissions': 372632641, 'name': 'botty mcbotface', 'mentionable': false, 'managed': false, 'id': '318649514745397248', 'hoist': false, 'color': 8630171, 'originalPosition': 12, 'colorString': '#83af9b'}, '329358102975741955': {'position': 11, 'permissions': 104188993, 'name': 'cute person 🎀', 'mentionable': false, 'managed': false, 'id': '329358102975741955', 'hoist': false, 'color': 15844367, 'originalPosition': 11, 'colorString': '#f1c40f'}, '343121679150743555': {'position': 10, 'permissions': 268697665, 'name': 'Patreon', 'mentionable': false, 'managed': true, 'id': '343121679150743555', 'hoist': false, 'color': 0, 'originalPosition': 10, 'colorString': null}, '343121980477800458': {'position': 9, 'permissions': 104188993, 'name': 'patreon supporter', 'mentionable': false, 'managed': false, 'id': '343121980477800458', 'hoist': true, 'color': 16555418, 'originalPosition': 9, 'colorString': '#fc9d9a'}, '330588237670121472': {'position': 8, 'permissions': 104188993, 'name': 'subscriber', 'mentionable': false, 'managed': true, 'id': '330588237670121472', 'hoist': true, 'color': 16663397, 'originalPosition': 8, 'colorString': '#fe4365'}, '344565996411027456': {'position': 7, 'permissions': 104188993, 'name': 'ninja sub', 'mentionable': false, 'managed': false, 'id': '344565996411027456', 'hoist': false, 'color': 16663397, 'originalPosition': 7, 'colorString': '#fe4365'}, '318648703311151104': {'position': 6, 'permissions': 104320065, 'name': 'community member', 'mentionable': false, 'managed': false, 'id': '318648703311151104', 'hoist': false, 'color': 9807270, 'originalPosition': 6, 'colorString': '#95a5a6'}, '318652392142929920': {'position': 5, 'permissions': 519232, 'name': 'MuxyBot', 'mentionable': false, 'managed': true, 'id': '318652392142929920', 'hoist': false, 'color': 0, 'originalPosition': 5, 'colorString': null}, '318850773918285824': {'position': 4, 'permissions': 67437569, 'name': 'muted', 'mentionable': false, 'managed': false, 'id': '318850773918285824', 'hoist': false, 'color': 6323595, 'originalPosition': 4, 'colorString': '#607d8b'}, '346731968873889803': {'position': 3, 'permissions': 12921935, 'name': 'Tatsumaki', 'mentionable': false, 'managed': true, 'id': '346731968873889803', 'hoist': false, 'color': 0, 'originalPosition': 3, 'colorString': null}, '360585645405503488': {'position': 2, 'permissions': 104188993, 'name': 'バーバーバカ お兄ちゃん!', 'mentionable': false, 'managed': false, 'id': '360585645405503488', 'hoist': false, 'color': 10378846, 'originalPosition': 2, 'colorString': '#9e5e5e'}, '383433060747575296': {'position': 1, 'permissions': 104188993, 'name': 'heart of pure gold 🧡', 'mentionable': false, 'managed': false, 'id': '383433060747575296', 'hoist': false, 'color': 12429145, 'originalPosition': 1, 'colorString': '#bda759'}, '318647068782755840': {'position': 0, 'permissions': 104188993, 'name': '@everyone', 'mentionable': false, 'managed': false, 'id': '318647068782755840', 'hoist': false, 'color': 0, 'originalPosition': 0, 'colorString': null}}, 'afkChannelId': null, 'afkTimeout': 300, 'systemChannelId': null, 'verificationLevel': 2, 'region': 'us-south', 'joinedAt': '2017-05-29T07:09:41.428Z', 'large': true, 'defaultMessageNotifications': 1, 'mfaLevel': 0, 'application_id': null, 'explicitContentFilter': 2},
{'id': '318647068782755840', 'name': 'jumpystick / かちい', 'ownerId': '62601275618889728', 'icon': 'f729111314c86f09ec7b421455dde383', 'splash': null, 'features': [], 'roles': {'318647823845425162': {'position': 14, 'permissions': 2146954495, 'name': 'nee-chan', 'mentionable': false, 'managed': false, 'id': '318647823845425162', 'hoist': true, 'color': 16371117, 'originalPosition': 14, 'colorString': '#f9cdad'}, '318648103152648202': {'position': 13, 'permissions': 536341575, 'name': 'mods', 'mentionable': true, 'managed': false, 'id': '318648103152648202', 'hoist': true, 'color': 8630171, 'originalPosition': 13, 'colorString': '#83af9b'}, '318649514745397248': {'position': 12, 'permissions': 372632641, 'name': 'botty mcbotface', 'mentionable': false, 'managed': false, 'id': '318649514745397248', 'hoist': false, 'color': 8630171, 'originalPosition': 12, 'colorString': '#83af9b'}, '329358102975741955': {'position': 11, 'permissions': 104188993, 'name': 'cute person 🎀', 'mentionable': false, 'managed': false, 'id': '329358102975741955', 'hoist': false, 'color': 15844367, 'originalPosition': 11, 'colorString': '#f1c40f'}, '343121679150743555': {'position': 10, 'permissions': 268697665, 'name': 'Patreon', 'mentionable': false, 'managed': true, 'id': '343121679150743555', 'hoist': false, 'color': 0, 'originalPosition': 10, 'colorString': null}, '343121980477800458': {'position': 9, 'permissions': 104188993, 'name': 'patreon supporter', 'mentionable': false, 'managed': false, 'id': '343121980477800458', 'hoist': true, 'color': 16555418, 'originalPosition': 9, 'colorString': '#fc9d9a'}, '330588237670121472': {'position': 8, 'permissions': 104188993, 'name': 'subscriber', 'mentionable': false, 'managed': true, 'id': '330588237670121472', 'hoist': true, 'color': 16663397, 'originalPosition': 8, 'colorString': '#fe4365'}, '344565996411027456': {'position': 7, 'permissions': 104188993, 'name': 'ninja sub', 'mentionable': false, 'managed': false, 'id': '344565996411027456', 'hoist': false, 'color': 16663397, 'originalPosition': 7, 'colorString': '#fe4365'}, '318648703311151104': {'position': 6, 'permissions': 104320065, 'name': 'community member', 'mentionable': false, 'managed': false, 'id': '318648703311151104', 'hoist': false, 'color': 9807270, 'originalPosition': 6, 'colorString': '#95a5a6'}, '318652392142929920': {'position': 5, 'permissions': 519232, 'name': 'MuxyBot', 'mentionable': false, 'managed': true, 'id': '318652392142929920', 'hoist': false, 'color': 0, 'originalPosition': 5, 'colorString': null}, '318850773918285824': {'position': 4, 'permissions': 67437569, 'name': 'muted', 'mentionable': false, 'managed': false, 'id': '318850773918285824', 'hoist': false, 'color': 6323595, 'originalPosition': 4, 'colorString': '#607d8b'}, '346731968873889803': {'position': 3, 'permissions': 12921935, 'name': 'Tatsumaki', 'mentionable': false, 'managed': true, 'id': '346731968873889803', 'hoist': false, 'color': 0, 'originalPosition': 3, 'colorString': null}, '360585645405503488': {'position': 2, 'permissions': 104188993, 'name': 'バーバーバカ お兄ちゃん!', 'mentionable': false, 'managed': false, 'id': '360585645405503488', 'hoist': false, 'color': 10378846, 'originalPosition': 2, 'colorString': '#9e5e5e'}, '383433060747575296': {'position': 1, 'permissions': 104188993, 'name': 'heart of pure gold 🧡', 'mentionable': false, 'managed': false, 'id': '383433060747575296', 'hoist': false, 'color': 12429145, 'originalPosition': 1, 'colorString': '#bda759'}, '318647068782755840': {'position': 0, 'permissions': 104188993, 'name': '@everyone', 'mentionable': false, 'managed': false, 'id': '318647068782755840', 'hoist': false, 'color': 0, 'originalPosition': 0, 'colorString': null}}, 'afkChannelId': null, 'afkTimeout': 300, 'systemChannelId': null, 'verificationLevel': 2, 'region': 'us-south', 'joinedAt': '2017-05-29T07:09:41.428Z', 'large': true, 'defaultMessageNotifications': 1, 'mfaLevel': 0, 'application_id': null, 'explicitContentFilter': 2}
],
user: {
'id': '62601275618889728', 'username': 'Kata カタ', 'usernameLowerCase': 'kata カタ', 'discriminator': '0975', 'avatar': 'f60031ba7d3ea518d25f5a85900e6fb0'
}
}

View file

@ -0,0 +1,20 @@
const styles = {
outer: {
},
navigation: {
},
serverCard: {
},
userCard: {
},
card: {}
}
module.exports = styles

View file

@ -0,0 +1,31 @@
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import Radium from 'radium'
import Logotype from '../logotype'
import styles from './styles'
class Wrapper extends Component {
render () {
return <div style={styles.root}>
<div style={styles.background} />
<div style={styles.container}>
<nav uk-navbar='' style={styles.nav} className='uk-navbar-transparent'>
<div className='uk-navbar-left'>
<Logotype style={{ height: '2rem' }} />
</div>
<div class='uk-navbar-right'>
<ul class='uk-navbar-nav'>
<li><Link to='/start'>Get Started</Link></li>
<li><a href='https://discord.gg/PWQUVsd'>Support Discord</a></li>
</ul>
</div>
</nav>
<main>
{this.props.children}
</main>
</div>
</div>
}
}
export default Radium(Wrapper)

View file

@ -0,0 +1,25 @@
export default {
root: {
backgroundColor: 'var(--c-1)'
},
container: {
width: 960,
margin: '0 auto'
},
background: {
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'var(--c-1)',
zIndex: -1000
},
nav: {
padding: '0 20px'
}
}

46
UI/src/index.css Normal file
View file

@ -0,0 +1,46 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
:root {
--c-white: #efefef;
--c-9: #EBD6D4;
--c-7: #AB9C9A;
--c-5: #6B6261;
--c-3: #5E5655;
--c-1: #453F3E;
--c-dark: #5E5655;
}
::selection {
background: var(--c-9);
color: var(--c-1);
}
::-moz-selection {
background: var(--c-9);
color: var(--c-1);
}
html {
overflow: hidden;
height: 100%;
}
body {
height: 100%;
overflow: auto;
color: var(--c-white);
background-color: var(--c-1);
overflow-y: hidden;
}
h1,h2,h3,h4,h5,h6 {
color: var(--c-9);
}
.uk-navbar-nav>li>a {
color: var(--c-7);
}

8
UI/src/index.js Normal file
View file

@ -0,0 +1,8 @@
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import registerServiceWorker from './registerServiceWorker'
ReactDOM.render(<App />, document.getElementById('root'))
registerServiceWorker()

7
UI/src/logo.svg Normal file
View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1,108 @@
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}

18
UI/src/router/index.js Normal file
View file

@ -0,0 +1,18 @@
import React, { Component, Fragment } from 'react'
import { Route } from 'react-router-dom'
import Servers from '../components/servers'
import OauthCallback from '../components/oauth-callback'
const aaa = (props) => (<div>{ JSON.stringify(props) }</div>)
export default class AppRouter extends Component {
render () {
return <Fragment>
<Route exact path='/' component={aaa} />
<Route path='/s' component={Servers} />
<Route path='/root' component={aaa} />
<Route path='/oauth/callback' component={OauthCallback} />
</Fragment>
}
}

6503
UI/yarn.lock Normal file

File diff suppressed because it is too large Load diff

13
docker-compose.yml Normal file
View file

@ -0,0 +1,13 @@
version: '2.1'
services:
pg:
image: postgres:10-alpine
container_name: roleypoly-pg
ports:
- 5432:5432
environment:
POSTGRES_PASSWORD: 19216801
POSTGRES_DB: roleypoly
POSTGRES_USER: roleypoly
POSTGRES_INITDB_ARGS: -A trust