From 912b40c383c5c654f75aa5e143eff997d90f2f8b Mon Sep 17 00:00:00 2001 From: Katie Thornhill Date: Tue, 19 Nov 2019 23:02:54 -0500 Subject: [PATCH 1/3] chore: prettier on server --- Server/.eslintrc.js | 4 +- Server/.prettierrc.js | 9 ++ Server/Roleypoly.js | 62 ++++---- Server/api/auth.js | 79 +++++------ Server/api/index.js | 22 +-- Server/api/servers.js | 133 +++++++++--------- Server/api/servers_test.js | 28 ++-- Server/index.js | 167 ++++++++++++---------- Server/logger.js | 49 +++---- Server/models/Server.js | 15 +- Server/models/Session.js | 6 +- Server/models/index.js | 48 +++---- Server/package.json | 5 + Server/services/Service.js | 10 +- Server/services/discord-rpc.js | 4 + Server/services/discord.js | 242 +++++++++++++++++--------------- Server/services/presentation.js | 65 +++++---- Server/services/server.js | 60 ++++---- Server/services/sessions.js | 36 ++--- Server/util/model-methods.js | 16 +-- Server/yarn.lock | 30 ++++ 21 files changed, 589 insertions(+), 501 deletions(-) create mode 100644 Server/.prettierrc.js create mode 100644 Server/services/discord-rpc.js diff --git a/Server/.eslintrc.js b/Server/.eslintrc.js index a5b82de..b0a06a6 100644 --- a/Server/.eslintrc.js +++ b/Server/.eslintrc.js @@ -1,3 +1,3 @@ module.exports = { - "extends": "standard" -}; \ No newline at end of file + extends: 'standard', +}; diff --git a/Server/.prettierrc.js b/Server/.prettierrc.js new file mode 100644 index 0000000..d58c0ce --- /dev/null +++ b/Server/.prettierrc.js @@ -0,0 +1,9 @@ +module.exports = { + printWidth: 90, + useTabs: false, + tabWidth: 2, + singleQuote: true, + trailingComma: 'es5', + bracketSpacing: true, + semi: true, +}; diff --git a/Server/Roleypoly.js b/Server/Roleypoly.js index 9be93fe..4856a79 100644 --- a/Server/Roleypoly.js +++ b/Server/Roleypoly.js @@ -1,36 +1,38 @@ -const log = new (require('./logger'))('Roleypoly') -const Sequelize = require('sequelize') -const fetchModels = require('./models') -const fetchApis = require('./api') +const log = new (require('./logger'))('Roleypoly'); +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 = {} + constructor(router, io, app) { + this.router = router; + this.io = io; + this.ctx = {}; this.ctx.config = { - appUrl: process.env.APP_URL - } + appUrl: process.env.APP_URL, + }; - this.ctx.io = io - this.__app = app + this.ctx.io = io; + this.__app = app; - if (log.debugOn) log.warn('debug mode is on') + if (log.debugOn) log.warn('debug mode is on'); - this.__initialized = this._mountServices() + this.__initialized = this._mountServices(); } - async awaitServices () { - await this.__initialized + 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() + 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', @@ -40,16 +42,16 @@ class Roleypoly { // enableReadyCheck: true, // enableOfflineQueue: true // }) - this.ctx.server = new (require('./services/server'))(this.ctx) - this.ctx.discord = new (require('./services/discord'))(this.ctx) - this.ctx.sessions = new (require('./services/sessions'))(this.ctx) - this.ctx.P = new (require('./services/presentation'))(this.ctx) + this.ctx.server = new (require('./services/server'))(this.ctx); + this.ctx.discord = new (require('./services/discord'))(this.ctx); + this.ctx.sessions = new (require('./services/sessions'))(this.ctx); + this.ctx.P = new (require('./services/presentation'))(this.ctx); } - async mountRoutes () { - fetchApis(this.router, this.ctx) - this.__app.use(this.router.middleware()) + async mountRoutes() { + fetchApis(this.router, this.ctx); + this.__app.use(this.router.middleware()); } } -module.exports = Roleypoly +module.exports = Roleypoly; diff --git a/Server/api/auth.js b/Server/api/auth.js index 71e13f8..f163c42 100644 --- a/Server/api/auth.js +++ b/Server/api/auth.js @@ -1,77 +1,76 @@ module.exports = (R, $) => { - R.post('/api/auth/token', async (ctx) => { - const { token } = ctx.request.body + R.post('/api/auth/token', async ctx => { + const { token } = ctx.request.body; if (token == null || token === '') { - ctx.body = { err: 'token_missing' } - ctx.status = 400 - return + ctx.body = { err: 'token_missing' }; + ctx.status = 400; + return; } if (ctx.session.accessToken === undefined || ctx.session.expiresAt < Date.now()) { - const data = await $.discord.getAuthToken(token) - ctx.session.accessToken = data.access_token - ctx.session.refreshToken = data.refresh_token - ctx.session.expiresAt = Date.now() + (ctx.expires_in || 1000 * 60 * 60 * 24) + const data = await $.discord.getAuthToken(token); + ctx.session.accessToken = data.access_token; + ctx.session.refreshToken = data.refresh_token; + ctx.session.expiresAt = Date.now() + (ctx.expires_in || 1000 * 60 * 60 * 24); } - const user = await $.discord.getUser(ctx.session.accessToken) - ctx.session.userId = user.id - ctx.session.avatarHash = user.avatar + const user = await $.discord.getUser(ctx.session.accessToken); + ctx.session.userId = user.id; + ctx.session.avatarHash = user.avatar; ctx.body = { id: user.id, avatar: user.avatar, username: user.username, - discriminator: user.discriminator - } - }) + discriminator: user.discriminator, + }; + }); R.get('/api/auth/user', async ctx => { if (ctx.session.accessToken === undefined) { - ctx.body = { err: 'not_logged_in' } - ctx.status = 401 - return + ctx.body = { err: 'not_logged_in' }; + ctx.status = 401; + return; } - const user = await $.discord.getUser(ctx.session.accessToken) - ctx.session.userId = user.id - ctx.session.avatarHash = user.avatar + const user = await $.discord.getUser(ctx.session.accessToken); + ctx.session.userId = user.id; + ctx.session.avatarHash = user.avatar; ctx.body = { id: user.id, avatar: user.avatar, username: user.username, - discriminator: user.discriminator - } - }) + discriminator: user.discriminator, + }; + }); R.get('/api/auth/redirect', ctx => { - const url = $.discord.getAuthUrl() + const url = $.discord.getAuthUrl(); if (ctx.query.url === '✔️') { - ctx.body = { url } - return + ctx.body = { url }; + return; } - ctx.redirect(url) - }) + ctx.redirect(url); + }); R.post('/api/auth/logout', ctx => { - ctx.session = null - }) + ctx.session = null; + }); R.get('/api/oauth/bot', ctx => { - const url = $.discord.getBotJoinUrl() + const url = $.discord.getBotJoinUrl(); if (ctx.query.url === '✔️') { - ctx.body = { url } - return + ctx.body = { url }; + return; } - ctx.redirect(url) - }) - + ctx.redirect(url); + }); R.get('/api/oauth/bot/callback', ctx => { - console.log(ctx.request) - }) -} + console.log(ctx.request); + }); +}; diff --git a/Server/api/index.js b/Server/api/index.js index ca717b0..139f044 100644 --- a/Server/api/index.js +++ b/Server/api/index.js @@ -1,22 +1,22 @@ -const log = new (require('../logger'))('api/index') -const glob = require('glob') +const log = new (require('../logger'))('api/index'); +const glob = require('glob'); -const PROD = process.env.NODE_ENV === 'production' +const PROD = process.env.NODE_ENV === 'production'; module.exports = async (router, ctx) => { - const apis = glob.sync(`./api/**/!(index).js`) - log.debug('found apis', apis) + const apis = glob.sync(`./api/**/!(index).js`); + log.debug('found apis', apis); for (let a of apis) { if (a.endsWith('_test.js') && PROD) { - log.debug(`skipping ${a}`) - continue + log.debug(`skipping ${a}`); + continue; } - log.debug(`mounting ${a}`) + log.debug(`mounting ${a}`); try { - require(a.replace('api/', ''))(router, ctx) + require(a.replace('api/', ''))(router, ctx); } catch (e) { - log.error(`couldn't mount ${a}`, e) + log.error(`couldn't mount ${a}`, e); } } -} +}; diff --git a/Server/api/servers.js b/Server/api/servers.js index 3861c0a..384ff18 100644 --- a/Server/api/servers.js +++ b/Server/api/servers.js @@ -1,103 +1,108 @@ module.exports = (R, $) => { - R.get('/api/servers', async (ctx) => { + R.get('/api/servers', async ctx => { try { - const { userId } = ctx.session - const srv = $.discord.getRelevantServers(userId) - const presentable = await $.P.presentableServers(srv, userId) + const { userId } = ctx.session; + const srv = $.discord.getRelevantServers(userId); + const presentable = await $.P.presentableServers(srv, userId); - ctx.body = presentable + ctx.body = presentable; } catch (e) { - console.error(e.trace || e.stack) + console.error(e.trace || e.stack); } - }) + }); - R.get('/api/server/:id', async (ctx) => { - const { userId } = ctx.session - const { id } = ctx.params + R.get('/api/server/:id', async ctx => { + const { userId } = ctx.session; + const { id } = ctx.params; - const srv = $.discord.client.guilds.get(id) + const srv = $.discord.client.guilds.get(id); if (srv == null) { - ctx.body = { err: 'not found' } - ctx.status = 404 - return + ctx.body = { err: 'not found' }; + ctx.status = 404; + return; } - let gm + let gm; if (srv.members.has(userId)) { - gm = $.discord.gm(id, userId) + gm = $.discord.gm(id, userId); } else if ($.discord.isRoot(userId)) { - gm = $.discord.fakeGm({ id: userId }) + gm = $.discord.fakeGm({ id: userId }); } else { - ctx.body = { err: 'not_a_member' } - ctx.status = 400 - return + ctx.body = { err: 'not_a_member' }; + ctx.status = 400; + return; } - const server = await $.P.presentableServer(srv, gm) + const server = await $.P.presentableServer(srv, gm); - ctx.body = server - }) + ctx.body = server; + }); - R.get('/api/server/:id/slug', async (ctx) => { - const { userId } = ctx.session - const { id } = ctx.params + R.get('/api/server/:id/slug', async ctx => { + const { userId } = ctx.session; + const { id } = ctx.params; - const srv = $.discord.client.guilds.get(id) + const srv = $.discord.client.guilds.get(id); - console.log(srv) + console.log(srv); if (srv == null) { - ctx.body = { err: 'not found' } - ctx.status = 404 - return + ctx.body = { err: 'not found' }; + ctx.status = 404; + return; } - ctx.body = await $.P.serverSlug(srv) - }) + ctx.body = await $.P.serverSlug(srv); + }); - R.patch('/api/server/:id', async (ctx) => { - const { userId } = ctx.session - const { id } = ctx.params + R.patch('/api/server/:id', async ctx => { + const { userId } = ctx.session; + const { id } = ctx.params; - let gm = $.discord.gm(id, userId) + let gm = $.discord.gm(id, userId); if (gm == null && $.discord.isRoot(userId)) { - gm = $.discord.fakeGm({ id: userId }) + gm = $.discord.fakeGm({ id: userId }); } // check perms if (!$.discord.getPermissions(gm).canManageRoles) { - ctx.status = 403 - ctx.body = { err: 'cannot_manage_roles' } - return + ctx.status = 403; + ctx.body = { err: 'cannot_manage_roles' }; + return; } - const { message = null, categories = null } = ctx.request.body + const { message = null, categories = null } = ctx.request.body; // todo make less nasty await $.server.update(id, { - ...((message != null) ? { message } : {}), - ...((categories != null) ? { categories } : {}) - }) + ...(message != null ? { message } : {}), + ...(categories != null ? { categories } : {}), + }); - ctx.body = { ok: true } - }) + ctx.body = { ok: true }; + }); R.get('/api/admin/servers', async ctx => { - const { userId } = ctx.session + const { userId } = ctx.session; if (!$.discord.isRoot(userId)) { - return + return; } - ctx.body = $.discord.client.guilds.map(g => ({ url: `${process.env.APP_URL}/s/${g.id}`, name: g.name, members: g.members.array().length, roles: g.roles.array().length })) - }) + ctx.body = $.discord.client.guilds.map(g => ({ + url: `${process.env.APP_URL}/s/${g.id}`, + name: g.name, + members: g.members.array().length, + roles: g.roles.array().length, + })); + }); R.patch('/api/servers/:server/roles', async ctx => { - const { userId } = ctx.session - const { server } = ctx.params + const { userId } = ctx.session; + const { server } = ctx.params; - let gm = $.discord.gm(server, userId) + let gm = $.discord.gm(server, userId); if (gm == null && $.discord.isRoot(userId)) { - gm = $.discord.fakeGm({ id: userId }) + gm = $.discord.fakeGm({ id: userId }); } // check perms @@ -107,24 +112,24 @@ module.exports = (R, $) => { // return // } - const { added, removed } = ctx.request.body + const { added, removed } = ctx.request.body; - const allowedRoles = await $.server.getAllowedRoles(server) + const allowedRoles = await $.server.getAllowedRoles(server); - const pred = r => $.discord.safeRole(server, r) && allowedRoles.indexOf(r) !== -1 + const pred = r => $.discord.safeRole(server, r) && allowedRoles.indexOf(r) !== -1; if (added.length > 0) { - gm = await gm.addRoles(added.filter(pred)) + gm = await gm.addRoles(added.filter(pred)); } setTimeout(() => { if (removed.length > 0) { - gm.removeRoles(removed.filter(pred)) + gm.removeRoles(removed.filter(pred)); } - }, 1000) + }, 1000); // console.log('role patch', { added, removed, allowedRoles, addedFiltered: added.filterNot(pred), removedFiltered: removed.filterNot(pred) }) - ctx.body = { ok: true } - }) -} + ctx.body = { ok: true }; + }); +}; diff --git a/Server/api/servers_test.js b/Server/api/servers_test.js index 605c021..67b766b 100644 --- a/Server/api/servers_test.js +++ b/Server/api/servers_test.js @@ -1,26 +1,26 @@ module.exports = (R, $) => { R.get('/api/~/relevant-servers/:user', (ctx, next) => { // ctx.body = 'ok' - const srv = $.discord.getRelevantServers(ctx.params.user) - ctx.body = $.discord.presentableServers(srv, ctx.params.user) - return - }) + const srv = $.discord.getRelevantServers(ctx.params.user); + ctx.body = $.discord.presentableServers(srv, ctx.params.user); + return; + }); R.get('/api/~/roles/:id/:userId', (ctx, next) => { // ctx.body = 'ok' - const { id, userId } = ctx.params + const { id, userId } = ctx.params; - const srv = $.discord.client.guilds.get(id) + const srv = $.discord.client.guilds.get(id); if (srv === undefined) { - ctx.body = { err: 'not found' } - ctx.status = 404 - return + ctx.body = { err: 'not found' }; + ctx.status = 404; + return; } - const gm = srv.members.get(userId) - const roles = $.discord.presentableRoles(id, gm) + const gm = srv.members.get(userId); + const roles = $.discord.presentableRoles(id, gm); - ctx.boy = roles - }) -} + ctx.boy = roles; + }); +}; diff --git a/Server/index.js b/Server/index.js index 2268c7e..728974d 100644 --- a/Server/index.js +++ b/Server/index.js @@ -1,93 +1,96 @@ -require('dotenv').config({silent: true}) -const log = new (require('./logger'))('index') +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 fs = require('fs') -const path = require('path') -const router = require('koa-better-router')().loadMethods() -const Roleypoly = require('./Roleypoly') -const ksuid = require('ksuid') +const http = require('http'); +const Koa = require('koa'); +const app = new Koa(); +const _io = require('socket.io'); +const fs = require('fs'); +const path = require('path'); +const router = require('koa-better-router')().loadMethods(); +const Roleypoly = require('./Roleypoly'); +const ksuid = require('ksuid'); // monkey patch async-reduce because F U T U R E -Array.prototype.areduce = async function (predicate, acc = []) { // eslint-disable-line +Array.prototype.areduce = async function(predicate, acc = []) { + // eslint-disable-line for (let i of this) { - acc = await predicate(acc, i) + acc = await predicate(acc, i); } - return acc -} + return acc; +}; -Array.prototype.filterNot = Array.prototype.filterNot || function (predicate) { - return this.filter(v => !predicate(v)) -} +Array.prototype.filterNot = + Array.prototype.filterNot || + function(predicate) { + return this.filter(v => !predicate(v)); + }; // Create the server and socket.io server -const server = http.createServer(app.callback()) -const io = _io(server, { transports: ['websocket'], path: '/api/socket.io' }) +const server = http.createServer(app.callback()); +const io = _io(server, { transports: ['websocket'], path: '/api/socket.io' }); -const M = new Roleypoly(router, io, app) // eslint-disable-line no-unused-vars +const M = new Roleypoly(router, io, app); // eslint-disable-line no-unused-vars -app.keys = [ process.env.APP_KEY ] +app.keys = [process.env.APP_KEY]; -const DEVEL = process.env.NODE_ENV === 'development' +const DEVEL = process.env.NODE_ENV === 'development'; -async function start () { - await M.awaitServices() +async function start() { + await M.awaitServices(); // body parser - const bodyParser = require('koa-bodyparser') - app.use(bodyParser({ types: ['json'] })) + const bodyParser = require('koa-bodyparser'); + app.use(bodyParser({ types: ['json'] })); // Compress - const compress = require('koa-compress') - app.use(compress()) + const compress = require('koa-compress'); + app.use(compress()); // SPA + Static if (process.env.NODE_ENV === 'production') { - const pub = path.resolve(path.join(__dirname, 'public')) - log.info('public path', pub) + const pub = path.resolve(path.join(__dirname, 'public')); + log.info('public path', pub); - const staticFiles = require('koa-static') + const staticFiles = require('koa-static'); // app.use(staticFiles(pub, { defer: true, gzip: true, br: true })) - const send = require('koa-send') + const send = require('koa-send'); app.use(async (ctx, next) => { if (ctx.path.startsWith('/api')) { - log.info('api breakout') - return next() + log.info('api breakout'); + return next(); } - const chkPath = path.resolve(path.join(pub, ctx.path)) - log.info('chkPath', chkPath) + const chkPath = path.resolve(path.join(pub, ctx.path)); + log.info('chkPath', chkPath); if (!chkPath.startsWith(pub)) { - return next() + return next(); } try { - fs.statSync(chkPath) - log.info('sync pass') - ctx.body = fs.readFileSync(chkPath) - ctx.type = path.extname(ctx.path) - log.info('body sent') - ctx.status = 200 - return next() + fs.statSync(chkPath); + log.info('sync pass'); + ctx.body = fs.readFileSync(chkPath); + ctx.type = path.extname(ctx.path); + log.info('body sent'); + ctx.status = 200; + return next(); } catch (e) { - log.warn('failed') + log.warn('failed'); if (ctx.path.startsWith('/static/')) { - return next() + return next(); } try { - ctx.body = fs.readFileSync(path.join(pub, 'index.html'), { encoding: 'utf-8' }) + ctx.body = fs.readFileSync(path.join(pub, 'index.html'), { encoding: 'utf-8' }); } catch (e) { - ctx.body = e.stack || e.trace - ctx.status = 500 + ctx.body = e.stack || e.trace; + ctx.status = 500; } - log.info('index sent') - ctx.status = 200 - return next() + log.info('index sent'); + ctx.status = 200; + return next(); } // try { @@ -95,8 +98,7 @@ async function start () { // } catch (e) { // send(ctx, 'index.html', { root: pub }) // } - - }) + }); // const sendOpts = {root: pub, index: 'index.html'} // // const sendOpts = {} // app.use(async (ctx, next) => { @@ -121,43 +123,52 @@ async function start () { // Request logger app.use(async (ctx, next) => { - let timeStart = new Date() + let timeStart = new Date(); try { - await next() + await next(); } catch (e) { - log.error(e) - ctx.status = ctx.status || 500 + log.error(e); + ctx.status = ctx.status || 500; if (DEVEL) { - ctx.body = ctx.body || e.stack + ctx.body = ctx.body || e.stack; } else { ctx.body = { - err: 'something terrible happened.' - } + err: 'something terrible happened.', + }; } } - let timeElapsed = new Date() - timeStart + let timeElapsed = new Date() - timeStart; - log.request(`${ctx.status} ${ctx.method} ${ctx.url} - ${ctx.ip} - took ${timeElapsed}ms`) + log.request( + `${ctx.status} ${ctx.method} ${ctx.url} - ${ctx.ip} - took ${timeElapsed}ms` + ); // return null - }) + }); - const session = require('koa-session') - app.use(session({ - key: 'roleypoly:sess', - maxAge: 'session', - siteOnly: true, - store: M.ctx.sessions, - genid: () => { return ksuid.randomSync().string } - }, app)) + const session = require('koa-session'); + app.use( + session( + { + key: 'roleypoly:sess', + maxAge: 'session', + siteOnly: true, + store: M.ctx.sessions, + genid: () => { + return ksuid.randomSync().string; + }, + }, + app + ) + ); - await M.mountRoutes() + await M.mountRoutes(); // SPA server - log.info(`starting HTTP server on ${process.env.APP_PORT || 6769}`) - server.listen(process.env.APP_PORT || 6769) + log.info(`starting HTTP server on ${process.env.APP_PORT || 6769}`); + server.listen(process.env.APP_PORT || 6769); } start().catch(e => { - log.fatal('app failed to start', e) -}) + log.fatal('app failed to start', e); +}); diff --git a/Server/logger.js b/Server/logger.js index 58513da..c01c86c 100644 --- a/Server/logger.js +++ b/Server/logger.js @@ -1,58 +1,59 @@ -const chalk = require('chalk') +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 + constructor(name, debugOverride = false) { + this.name = name; + this.debugOn = + process.env.DEBUG === 'true' || process.env.DEBUG === '*' || debugOverride; } - fatal (text, ...data) { - this.error(text, data) + fatal(text, ...data) { + this.error(text, data); if (typeof data[data.length - 1] === 'number') { - process.exit(data[data.length - 1]) + process.exit(data[data.length - 1]); } else { - process.exit(1) + process.exit(1); } - throw text + throw text; } - error (text, ...data) { - console.error(chalk.red.bold(`ERR ${this.name}:`) + `\n ${text}`, data) + 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) + 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) + 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) + 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}`) + request(text, ...data) { + console.info(chalk.green.bold(`HTTP ${this.name}:`) + `\n ${text}`); } - debug (text, ...data) { + debug(text, ...data) { if (this.debugOn) { - console.log(chalk.gray.bold(`DEBUG ${this.name}:`) + `\n ${text}`, data) + console.log(chalk.gray.bold(`DEBUG ${this.name}:`) + `\n ${text}`, data); } } - sql (logger, ...data) { + sql(logger, ...data) { if (logger.debugOn) { - console.log(chalk.bold('DEBUG SQL:\n '), data) + console.log(chalk.bold('DEBUG SQL:\n '), data); } } } -module.exports = Logger +module.exports = Logger; diff --git a/Server/models/Server.js b/Server/models/Server.js index d93bd74..6269f17 100644 --- a/Server/models/Server.js +++ b/Server/models/Server.js @@ -1,14 +1,15 @@ module.exports = (sql, DataTypes) => { return sql.define('server', { - id: { // discord snowflake + id: { + // discord snowflake type: DataTypes.TEXT, - primaryKey: true + primaryKey: true, }, categories: { - type: DataTypes.JSON + type: DataTypes.JSON, }, message: { - type: DataTypes.TEXT - } - }) -} + type: DataTypes.TEXT, + }, + }); +}; diff --git a/Server/models/Session.js b/Server/models/Session.js index b7f6c8a..38fe049 100644 --- a/Server/models/Session.js +++ b/Server/models/Session.js @@ -2,6 +2,6 @@ module.exports = (sequelize, DataTypes) => { return sequelize.define('session', { id: { type: DataTypes.TEXT, primaryKey: true }, maxAge: DataTypes.BIGINT, - data: DataTypes.JSONB - }) -} + data: DataTypes.JSONB, + }); +}; diff --git a/Server/models/index.js b/Server/models/index.js index cdd176e..253d328 100644 --- a/Server/models/index.js +++ b/Server/models/index.js @@ -1,37 +1,37 @@ -const log = new (require('../logger'))('models/index') -const glob = require('glob') -const path = require('path') -const util = require('../util/model-methods') +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) +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', '') + modelFiles.forEach(v => { + let name = path.basename(v).replace('.js', ''); if (v === './models/index.js') { - log.debug('index.js hit, skipped') - return + log.debug('index.js hit, skipped'); + return; } try { - log.debug('importing..', v.replace('models/', '')) - let model = sql.import(v.replace('models/', '')) - models[name] = model + 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) + log.fatal('error importing model ' + v, err); + process.exit(-1); } - }) + }); - Object.keys(models).forEach((v) => { + Object.keys(models).forEach(v => { if (models[v].hasOwnProperty('__associations')) { - models[v].__associations(models) + models[v].__associations(models); } if (models[v].hasOwnProperty('__instanceMethods')) { - models[v].__instanceMethods(models[v]) + models[v].__instanceMethods(models[v]); } - }) + }); - return models -} + return models; +}; diff --git a/Server/package.json b/Server/package.json index 5a7d8c4..fcde5aa 100644 --- a/Server/package.json +++ b/Server/package.json @@ -6,10 +6,12 @@ "start": "standard && node index.js", "fix": "standard --fix", "dev": "pm2 start index.js --watch", + "lint:prettier": "prettier -c '**/*.{ts,tsx,css,yml,yaml,md,json,js,jsx}'", "pm2": "pm2" }, "dependencies": { "@discordjs/uws": "^11.149.1", + "@roleypoly/rpc": "^3.0.0-alpha.12", "chalk": "^2.4.2", "discord.js": "^11.4.2", "dotenv": "^7.0.0", @@ -34,5 +36,8 @@ "socket.io": "^2.2.0", "superagent": "^5.0.2", "uuid": "^3.3.2" + }, + "devDependencies": { + "prettier": "^1.19.1" } } diff --git a/Server/services/Service.js b/Server/services/Service.js index e020af4..4ea5aa6 100644 --- a/Server/services/Service.js +++ b/Server/services/Service.js @@ -1,10 +1,10 @@ -const Logger = require('../logger') +const Logger = require('../logger'); class Service { - constructor (ctx) { - this.ctx = ctx - this.log = new Logger(this.constructor.name) + constructor(ctx) { + this.ctx = ctx; + this.log = new Logger(this.constructor.name); } } -module.exports = Service +module.exports = Service; diff --git a/Server/services/discord-rpc.js b/Server/services/discord-rpc.js new file mode 100644 index 0000000..61bce10 --- /dev/null +++ b/Server/services/discord-rpc.js @@ -0,0 +1,4 @@ +const Service = require('./Service'); +const DiscordRPC = require('@roleypoly/rpc/discord'); + +class DiscordRPCService extends Service {} diff --git a/Server/services/discord.js b/Server/services/discord.js index 214f3e6..c3b9d72 100644 --- a/Server/services/discord.js +++ b/Server/services/discord.js @@ -1,124 +1,130 @@ -const Service = require('./Service') -const discord = require('discord.js') -const superagent = require('superagent') +const Service = require('./Service'); +const discord = require('discord.js'); +const superagent = require('superagent'); class DiscordService extends Service { - constructor (ctx) { - super(ctx) + 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}/api/oauth/bot/callback` - this.appUrl = process.env.APP_URL - this.isBot = process.env.IS_BOT === 'true' || false - this.rootUsers = new Set((process.env.ROOT_USERS||'').split(',')) + 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}/api/oauth/bot/callback`; + this.appUrl = process.env.APP_URL; + this.isBot = process.env.IS_BOT === 'true' || false; + this.rootUsers = new Set((process.env.ROOT_USERS || '').split(',')); - this.client = new discord.Client() - this.client.options.disableEveryone = true + this.client = new discord.Client(); + this.client.options.disableEveryone = true; - this.cmds = this._cmds() + this.cmds = this._cmds(); - this.startBot() + this.startBot(); } - ownGm (server) { - return this.gm(server, this.client.user.id) + ownGm(server) { + return this.gm(server, this.client.user.id); } - fakeGm({id = 0, nickname = '[none]', displayHexColor = '#ffffff'}) { - return { id, nickname, displayHexColor, __faked: true, roles: { has() {return false} } } + fakeGm({ id = 0, nickname = '[none]', displayHexColor = '#ffffff' }) { + return { + id, + nickname, + displayHexColor, + __faked: true, + roles: { + has() { + return false; + }, + }, + }; } isRoot(id) { - return this.rootUsers.has(id) + return this.rootUsers.has(id); } - async startBot () { - await this.client.login(this.botToken) + async startBot() { + await this.client.login(this.botToken); // not all roleypolys are bots. if (this.isBot) { - this.log.info('this roleypoly is a bot') - this.client.on('message', this.handleMessage.bind(this)) - this.client.on('guildCreate', this.handleJoin.bind(this)) + this.log.info('this roleypoly is a bot'); + this.client.on('message', this.handleMessage.bind(this)); + this.client.on('guildCreate', this.handleJoin.bind(this)); } for (let server of this.client.guilds.array()) { - await this.ctx.server.ensure(server) + await this.ctx.server.ensure(server); } } - getRelevantServers (userId) { - return this.client.guilds.filter((g) => g.members.has(userId)) + getRelevantServers(userId) { + return this.client.guilds.filter(g => g.members.has(userId)); } - gm (serverId, userId) { - return this.client.guilds.get(serverId).members.get(userId) + gm(serverId, userId) { + return this.client.guilds.get(serverId).members.get(userId); } - getRoles (server) { - return this.client.guilds.get(server).roles + getRoles(server) { + return this.client.guilds.get(server).roles; } - getPermissions (gm) { + getPermissions(gm) { if (this.isRoot(gm.id)) { return { isAdmin: true, - canManageRoles: true - } + canManageRoles: true, + }; } return { isAdmin: gm.permissions.hasPermission('ADMINISTRATOR'), - canManageRoles: gm.permissions.hasPermission('MANAGE_ROLES', false, true) - } + canManageRoles: gm.permissions.hasPermission('MANAGE_ROLES', false, true), + }; } - safeRole (server, role) { - const r = this.getRoles(server).get(role) - return r.editable && !r.hasPermission('MANAGE_ROLES', false, true) + safeRole(server, role) { + const r = this.getRoles(server).get(role); + return r.editable && !r.hasPermission('MANAGE_ROLES', false, true); } // oauth step 2 flow, grab the auth token via code - async getAuthToken (code) { - const url = 'https://discordapp.com/api/oauth2/token' + async getAuthToken(code) { + const url = 'https://discordapp.com/api/oauth2/token'; try { - const rsp = - await superagent - .post(url) - .set('Content-Type', 'application/x-www-form-urlencoded') - .send({ - client_id: this.clientId, - client_secret: this.clientSecret, - grant_type: 'authorization_code', - code: code, - redirect_uri: this.oauthCallback - }) + const rsp = await superagent + .post(url) + .set('Content-Type', 'application/x-www-form-urlencoded') + .send({ + client_id: this.clientId, + client_secret: this.clientSecret, + grant_type: 'authorization_code', + code: code, + redirect_uri: this.oauthCallback, + }); - return rsp.body + return rsp.body; } catch (e) { - this.log.error('getAuthToken failed', e) - throw e + this.log.error('getAuthToken failed', e); + throw e; } } - async getUser (authToken) { - const url = 'https://discordapp.com/api/v6/users/@me' + async getUser(authToken) { + const url = 'https://discordapp.com/api/v6/users/@me'; try { if (authToken == null || authToken === '') { - throw new Error('not logged in') + throw new Error('not logged in'); } - const rsp = - await superagent - .get(url) - .set('Authorization', `Bearer ${authToken}`) - return rsp.body + const rsp = await superagent.get(url).set('Authorization', `Bearer ${authToken}`); + return rsp.body; } catch (e) { - this.log.error('getUser error', e) - throw e + this.log.error('getUser error', e); + throw e; } } @@ -146,96 +152,108 @@ class DiscordService extends Service { // 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&state=${state}` + getAuthUrl(state) { + return `https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&redirect_uri=${this.oauthCallback}&response_type=code&scope=identify&state=${state}`; } // 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` + getBotJoinUrl() { + return `https://discordapp.com/oauth2/authorize?client_id=${this.clientId}&scope=bot&permissions=268435456`; } - mentionResponse (message) { - message.channel.send(`🔰 Assign your roles here! <${this.appUrl}/s/${message.guild.id}>`, { disableEveryone: true }) + mentionResponse(message) { + message.channel.send( + `🔰 Assign your roles here! <${this.appUrl}/s/${message.guild.id}>`, + { disableEveryone: true } + ); } - _cmds () { + _cmds() { const cmds = [ { regex: /say (.*)/, - handler (message, matches, r) { - r(matches[0]) - } + handler(message, matches, r) { + r(matches[0]); + }, }, { regex: /set username (.*)/, - async handler (message, matches) { - const { username } = this.client.user - await this.client.user.setUsername(matches[0]) - message.channel.send(`Username changed from ${username} to ${matches[0]}`) - } + async handler(message, matches) { + const { username } = this.client.user; + await this.client.user.setUsername(matches[0]); + message.channel.send(`Username changed from ${username} to ${matches[0]}`); + }, }, { regex: /stats/, - async handler (message, matches) { + async handler(message, matches) { const t = [ `**Stats** 📈`, '', - `👩‍❤️‍👩 **Users Served:** ${this.client.guilds.reduce((acc, g) => acc + g.memberCount, 0)}`, + `👩‍❤️‍👩 **Users Served:** ${this.client.guilds.reduce( + (acc, g) => acc + g.memberCount, + 0 + )}`, `🔰 **Servers:** ${this.client.guilds.size}`, - `💮 **Roles Seen:** ${this.client.guilds.reduce((acc, g) => acc + g.roles.size, 0)}` - ] - message.channel.send(t.join('\n')) - } - } + `💮 **Roles Seen:** ${this.client.guilds.reduce( + (acc, g) => acc + g.roles.size, + 0 + )}`, + ]; + message.channel.send(t.join('\n')); + }, + }, ] // prefix regex with ^ for ease of code - .map(({regex, ...rest}) => ({ regex: new RegExp(`^${regex.source}`, regex.flags), ...rest })) + .map(({ regex, ...rest }) => ({ + regex: new RegExp(`^${regex.source}`, regex.flags), + ...rest, + })); - return cmds + return cmds; } - async handleCommand (message) { - const cmd = message.content.replace(`<@${this.client.user.id}> `, '') - this.log.debug(`got command from ${message.author.username}`, cmd) + async handleCommand(message) { + const cmd = message.content.replace(`<@${this.client.user.id}> `, ''); + this.log.debug(`got command from ${message.author.username}`, cmd); for (let { regex, handler } of this.cmds) { - const match = regex.exec(cmd) + const match = regex.exec(cmd); if (match !== null) { - this.log.debug('command accepted', { cmd, match }) + this.log.debug('command accepted', { cmd, match }); try { - await handler.call(this, message, match.slice(1)) - return + await handler.call(this, message, match.slice(1)); + return; } catch (e) { - this.log.error('command errored', { e, cmd, message }) - message.channel.send(`❌ **An error occured.** ${e}`) - return + this.log.error('command errored', { e, cmd, message }); + message.channel.send(`❌ **An error occured.** ${e}`); + return; } } } // nothing matched? - this.mentionResponse(message) + this.mentionResponse(message); } - handleMessage (message) { - if (message.author.bot && message.channel.type !== 'text') { // drop bot messages and dms - return + handleMessage(message) { + if (message.author.bot && message.channel.type !== 'text') { + // drop bot messages and dms + return; } if (message.mentions.users.has(this.client.user.id)) { if (this.rootUsers.has(message.author.id)) { - this.handleCommand(message) + this.handleCommand(message); } else { - this.mentionResponse(message) + this.mentionResponse(message); } } } - async handleJoin (guild) { - await this.ctx.server.ensure(guild) + async handleJoin(guild) { + await this.ctx.server.ensure(guild); } - } -module.exports = DiscordService +module.exports = DiscordService; diff --git a/Server/services/presentation.js b/Server/services/presentation.js index 0a45d6c..b3114ac 100644 --- a/Server/services/presentation.js +++ b/Server/services/presentation.js @@ -1,62 +1,67 @@ -const Service = require('./Service') -const LRU = require('lru-cache') +const Service = require('./Service'); +const LRU = require('lru-cache'); class PresentationService extends Service { - constructor (ctx) { - super(ctx) - this.M = ctx.M - this.discord = ctx.discord + constructor(ctx) { + super(ctx); + this.M = ctx.M; + this.discord = ctx.discord; - this.cache = new LRU({ max: 500, maxAge: 100 * 60 * 5 }) + this.cache = new LRU({ max: 500, maxAge: 100 * 60 * 5 }); } - serverSlug (server) { + serverSlug(server) { return { id: server.id, name: server.name, ownerID: server.ownerID, - icon: server.icon - } + icon: server.icon, + }; } - async oldPresentableServers (collection, userId) { - let servers = [] + async oldPresentableServers(collection, userId) { + let servers = []; for (let server of collection.array()) { - const gm = server.members.get(userId) + const gm = server.members.get(userId); - servers.push(await this.presentableServer(server, gm)) + servers.push(await this.presentableServer(server, gm)); } - return servers + return servers; } - async presentableServers (collection, userId) { + async presentableServers(collection, userId) { return collection.array().areduce(async (acc, server) => { - const gm = server.members.get(userId) - acc.push(await this.presentableServer(server, gm, { incRoles: false })) - return acc - }) + const gm = server.members.get(userId); + acc.push(await this.presentableServer(server, gm, { incRoles: false })); + return acc; + }); } - async presentableServer (server, gm, { incRoles = true } = {}) { - const sd = await this.ctx.server.get(server.id) + async presentableServer(server, gm, { incRoles = true } = {}) { + const sd = await this.ctx.server.get(server.id); return { id: server.id, gm: { nickname: gm.nickname, - color: gm.displayHexColor + color: gm.displayHexColor, }, server: this.serverSlug(server), - roles: (incRoles) ? (await this.rolesByServer(server, sd)).map(r => ({ ...r, selected: gm.roles.has(r.id) })) : [], + roles: incRoles + ? (await this.rolesByServer(server, sd)).map(r => ({ + ...r, + selected: gm.roles.has(r.id), + })) + : [], message: sd.message, categories: sd.categories, - perms: this.discord.getPermissions(gm) - } + perms: this.discord.getPermissions(gm), + }; } - async rolesByServer (server) { + async rolesByServer(server) { return server.roles .filter(r => r.id !== server.id) // get rid of @everyone .map(r => ({ @@ -64,9 +69,9 @@ class PresentationService extends Service { color: r.color, name: r.name, position: r.position, - safe: this.discord.safeRole(server.id, r.id) - })) + safe: this.discord.safeRole(server.id, r.id), + })); } } -module.exports = PresentationService +module.exports = PresentationService; diff --git a/Server/services/server.js b/Server/services/server.js index fef8284..45709b6 100644 --- a/Server/services/server.js +++ b/Server/services/server.js @@ -1,66 +1,64 @@ -const Service = require('./Service') +const Service = require('./Service'); class ServerService extends Service { - constructor (ctx) { - super(ctx) - this.Server = ctx.M.Server - this.P = ctx.P + constructor(ctx) { + super(ctx); + this.Server = ctx.M.Server; + this.P = ctx.P; } - async ensure (server) { - let srv + async ensure(server) { + let srv; try { - srv = await this.get(server.id) - } catch (e) { - - } + srv = await this.get(server.id); + } catch (e) {} if (srv == null) { return this.create({ id: server.id, message: '', - categories: {} - }) + categories: {}, + }); } } - create ({ id, message, categories }) { - const srv = this.Server.build({ id, message, categories }) + create({ id, message, categories }) { + const srv = this.Server.build({ id, message, categories }); - return srv.save() + return srv.save(); } - async update (id, newData) { - const srv = await this.get(id, false) + async update(id, newData) { + const srv = await this.get(id, false); - return srv.update(newData) + return srv.update(newData); } - async get (id, plain = true) { + async get(id, plain = true) { const s = await this.Server.findOne({ where: { - id - } - }) + id, + }, + }); if (!plain) { - return s + return s; } - return s.get({ plain: true }) + return s.get({ plain: true }); } - async getAllowedRoles (id) { - const server = await this.get(id) + async getAllowedRoles(id) { + const server = await this.get(id); return Object.values(server.categories).reduce((acc, c) => { if (c.hidden !== true) { - return acc.concat(c.roles) + return acc.concat(c.roles); } - return acc - }, []) + return acc; + }, []); } } -module.exports = ServerService +module.exports = ServerService; diff --git a/Server/services/sessions.js b/Server/services/sessions.js index 5f924f1..05d5894 100644 --- a/Server/services/sessions.js +++ b/Server/services/sessions.js @@ -1,40 +1,40 @@ -const Service = require('./Service') +const Service = require('./Service'); class SessionsService extends Service { - constructor (ctx) { - super(ctx) - this.Session = ctx.M.Session + constructor(ctx) { + super(ctx); + this.Session = ctx.M.Session; } - async get (id, {rolling}) { - const user = await this.Session.findOne({ where: { id } }) + async get(id, { rolling }) { + const user = await this.Session.findOne({ where: { id } }); if (user === null) { - return null + return null; } - return user.data + return user.data; } - async set (id, data, {maxAge, rolling, changed}) { - let session = await this.Session.findOne({ where: { id } }) + async set(id, data, { maxAge, rolling, changed }) { + let session = await this.Session.findOne({ where: { id } }); if (session === null) { - session = this.Session.build({ id }) + session = this.Session.build({ id }); } - session.data = data - session.maxAge = maxAge + session.data = data; + session.maxAge = maxAge; - return session.save() + return session.save(); } - async destroy (id) { - const sess = await this.Session.findOne({ where: { id } }) + async destroy(id) { + const sess = await this.Session.findOne({ where: { id } }); if (sess != null) { - return sess.destroy() + return sess.destroy(); } } } -module.exports = SessionsService +module.exports = SessionsService; diff --git a/Server/util/model-methods.js b/Server/util/model-methods.js index 6ce720e..18abb18 100644 --- a/Server/util/model-methods.js +++ b/Server/util/model-methods.js @@ -1,10 +1,10 @@ -const ksuid = require('ksuid') +const ksuid = require('ksuid'); module.exports = { - ksuid (field = 'id') { - return async function () { - this.id = await ksuid.random() - return this - } - } -} + ksuid(field = 'id') { + return async function() { + this.id = await ksuid.random(); + return this; + }; + }, +}; diff --git a/Server/yarn.lock b/Server/yarn.lock index 42e8fd0..4043689 100644 --- a/Server/yarn.lock +++ b/Server/yarn.lock @@ -23,6 +23,13 @@ resolved "https://registry.yarnpkg.com/@discordjs/uws/-/uws-11.149.1.tgz#2e86f2825f43bed43d2e69eaf30a07d2dd8e8946" integrity sha512-TmbwZaeXDSCq0ckmf2q10Fkt1220gu9AZJ/UvtQjsi2jyJDjy0i0OwL4/eb3vc9Cwr0mpC9EbfzltQ2si0qUiQ== +"@improbable-eng/grpc-web@0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@improbable-eng/grpc-web/-/grpc-web-0.11.0.tgz#c9d4097b4a947384e3dc0234299d910668a6e7ad" + integrity sha512-SS2YP6iHyZ7TSSuCShnSo9xJyUkNZHEhEPJpTSUcNoULe1LuLEk52OKHY+VW9XB0qXstejpHgZq2Hx+69PThiw== + dependencies: + browser-headers "^0.4.0" + "@opencensus/core@^0.0.8": version "0.0.8" resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.8.tgz#df01f200c2d2fbfe14dae129a1a86fb87286db92" @@ -108,6 +115,14 @@ eventemitter2 "^4.1.0" ws "^3.0.0" +"@roleypoly/rpc@^3.0.0-alpha.12": + version "3.0.0-alpha.12" + resolved "https://registry.yarnpkg.com/@roleypoly/rpc/-/rpc-3.0.0-alpha.12.tgz#61e519755ae5103a191f253e63302bed252e9ea8" + integrity sha512-Tl7G/yGF/3FmoF3GoYFZ2JdpSH7Fv5NGGbDB7KbVum+GOmnIoG7D8ETUb5Ge2YHpTYLoJSAmVhDGiuI8o3QC6A== + dependencies: + "@improbable-eng/grpc-web" "0.11.0" + google-protobuf "3.10.0" + "@types/node@*": version "10.12.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.1.tgz#da61b64a2930a80fa708e57c45cd5441eb379d5b" @@ -434,6 +449,11 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +browser-headers@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/browser-headers/-/browser-headers-0.4.1.tgz#4308a7ad3b240f4203dbb45acedb38dc2d65dd02" + integrity sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg== + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -1427,6 +1447,11 @@ globals@^11.7.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.8.0.tgz#c1ef45ee9bed6badf0663c5cb90e8d1adec1321d" integrity sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA== +google-protobuf@3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.10.0.tgz#f36171128090615049f9b0e42252b1a10a4bc534" + integrity sha512-d0cMO8TJ6xtB/WrVHCv5U81L2ulX+aCD58IljyAN6mHwdHHJ2jbcauX5glvivi3s3hx7EYEo7eUA9WftzamMnw== + graceful-fs@^4.1.11: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -2785,6 +2810,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + prism-media@^0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-0.0.3.tgz#8842d4fae804f099d3b48a9a38e3c2bab6f4855b" From 4b75eaa0ab07d6dd54b37a48c6475640e623cc2a Mon Sep 17 00:00:00 2001 From: Katie Thornhill Date: Tue, 19 Nov 2019 23:04:09 -0500 Subject: [PATCH 2/3] chore: prettier on UI --- UI/config-overrides.js | 6 +- UI/package.json | 4 +- UI/src/.prettierrc.js | 9 + UI/src/App.css | 8 +- UI/src/App.js | 40 +-- UI/src/actions/index.js | 91 ++--- UI/src/actions/ui.js | 12 +- UI/src/components/add-server/index.js | 81 +++-- UI/src/components/demos/roleypoly.js | 22 +- UI/src/components/demos/typing.js | 54 +-- UI/src/components/dev-tools/index.js | 13 +- UI/src/components/logotype/index.js | 42 ++- UI/src/components/oauth-bot-flow/index.js | 40 ++- UI/src/components/oauth-callback/index.js | 98 +++--- UI/src/components/oauth-flow/index.js | 89 +++-- UI/src/components/role-editor/Category.js | 88 +++-- .../components/role-editor/CategoryEditor.js | 155 +++++---- UI/src/components/role-editor/actions.js | 175 +++++----- UI/src/components/role-editor/index.js | 325 ++++++++++-------- UI/src/components/role-picker/Category.js | 81 +++-- UI/src/components/role-picker/actions.js | 186 +++++----- UI/src/components/role-picker/index.js | 263 ++++++++------ UI/src/components/role/demo.js | 26 +- UI/src/components/role/draggable.js | 34 +- UI/src/components/role/index.js | 75 ++-- UI/src/components/servers/Navigation.js | 63 ++-- UI/src/components/servers/ServerCard.js | 83 +++-- UI/src/components/servers/ServerLanding.js | 74 ++-- UI/src/components/servers/UserCard.js | 85 +++-- UI/src/components/servers/index.js | 82 +++-- UI/src/components/wrapper/index.js | 83 +++-- UI/src/index.css | 18 +- UI/src/index.js | 14 +- UI/src/pages/Error404.js | 17 +- UI/src/pages/Landing.js | 29 +- UI/src/pages/WhyNoRoles.js | 46 ++- UI/src/pages/index.js | 46 +-- UI/src/reducers/index.js | 34 +- UI/src/reducers/role-editor.js | 56 +-- UI/src/reducers/role-picker.js | 30 +- UI/src/reducers/servers.js | 32 +- UI/src/reducers/user.js | 16 +- UI/src/registerServiceWorker.js | 4 +- UI/src/router/index.js | 82 ++--- UI/src/store/configureStore.dev.js | 27 +- UI/src/store/configureStore.js | 4 +- UI/src/store/configureStore.prod.js | 21 +- UI/src/utils.js | 2 +- UI/yarn.lock | 5 + 49 files changed, 1703 insertions(+), 1267 deletions(-) create mode 100644 UI/src/.prettierrc.js diff --git a/UI/config-overrides.js b/UI/config-overrides.js index 760dc75..9e93b5a 100644 --- a/UI/config-overrides.js +++ b/UI/config-overrides.js @@ -1,4 +1,2 @@ -const { override, addDecoratorsLegacy } = require('customize-cra') -module.exports = override( - addDecoratorsLegacy() -) \ No newline at end of file +const { override, addDecoratorsLegacy } = require("customize-cra"); +module.exports = override(addDecoratorsLegacy()); diff --git a/UI/package.json b/UI/package.json index 2ac5324..27b3d92 100644 --- a/UI/package.json +++ b/UI/package.json @@ -31,7 +31,8 @@ "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", - "eject": "react-app-rewired eject" + "eject": "react-app-rewired eject", + "lint:prettier": "prettier -c '**/*.{ts,tsx,css,yml,yaml,md,json,js,jsx}'" }, "eslintConfig": { "extends": "react-app" @@ -53,6 +54,7 @@ "eslint-plugin-react": "^7.11.1", "eslint-plugin-standard": "^4.0.0", "node-sass-chokidar": "^1.3.4", + "prettier": "^1.19.1", "react-app-rewire-scss": "^1.0.2", "react-app-rewired": "^2.1.1", "redux-devtools": "^3.4.1", diff --git a/UI/src/.prettierrc.js b/UI/src/.prettierrc.js new file mode 100644 index 0000000..d58c0ce --- /dev/null +++ b/UI/src/.prettierrc.js @@ -0,0 +1,9 @@ +module.exports = { + printWidth: 90, + useTabs: false, + tabWidth: 2, + singleQuote: true, + trailingComma: 'es5', + bracketSpacing: true, + semi: true, +}; diff --git a/UI/src/App.css b/UI/src/App.css index c5c6e8a..31be39d 100644 --- a/UI/src/App.css +++ b/UI/src/App.css @@ -23,6 +23,10 @@ } @keyframes App-logo-spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } diff --git a/UI/src/App.js b/UI/src/App.js index f3d83ec..cbaabca 100644 --- a/UI/src/App.js +++ b/UI/src/App.js @@ -1,29 +1,29 @@ -import React, { Component } from 'react' -import { Provider } from 'react-redux' -import { ConnectedRouter } from 'react-router-redux' -import { DragDropContext } from 'react-dnd' -import HTML5Backend from 'react-dnd-html5-backend' -import createHistory from 'history/createBrowserHistory' -import configureStore from './store/configureStore' -import './App.css' -import './generic.sass' +import React, { Component } from 'react'; +import { Provider } from 'react-redux'; +import { ConnectedRouter } from 'react-router-redux'; +import { DragDropContext } from 'react-dnd'; +import HTML5Backend from 'react-dnd-html5-backend'; +import createHistory from 'history/createBrowserHistory'; +import configureStore from './store/configureStore'; +import './App.css'; +import './generic.sass'; -import Wrapper from './components/wrapper' -import AppRouter from './router' -import { userInit } from './actions' +import Wrapper from './components/wrapper'; +import AppRouter from './router'; +import { userInit } from './actions'; -const history = createHistory() -const store = configureStore(undefined, history) +const history = createHistory(); +const store = configureStore(undefined, history); -window.__APP_STORE__ = store +window.__APP_STORE__ = store; @DragDropContext(HTML5Backend) class App extends Component { - componentWillMount () { - store.dispatch(userInit) + componentWillMount() { + store.dispatch(userInit); } - render () { + render() { return ( @@ -32,8 +32,8 @@ class App extends Component { - ) + ); } } -export default App +export default App; diff --git a/UI/src/actions/index.js b/UI/src/actions/index.js index 47dbe67..0fae6fb 100644 --- a/UI/src/actions/index.js +++ b/UI/src/actions/index.js @@ -1,87 +1,88 @@ -import superagent from 'superagent' -import { push } from 'react-router-redux' +import superagent from 'superagent'; +import { push } from 'react-router-redux'; export const fetchServers = async dispatch => { - const rsp = await superagent.get('/api/servers') + const rsp = await superagent.get('/api/servers'); dispatch({ type: Symbol.for('update servers'), - data: rsp.body - }) + data: rsp.body, + }); dispatch({ - type: Symbol.for('app ready') - }) -} + type: Symbol.for('app ready'), + }); +}; export const userInit = async dispatch => { if (!window.location.pathname.startsWith('/oauth')) { try { - const rsp = await superagent.get('/api/auth/user') + const rsp = await superagent.get('/api/auth/user'); dispatch({ type: Symbol.for('set user'), - data: rsp.body - }) - - dispatch(fetchServers) + data: rsp.body, + }); + + dispatch(fetchServers); } catch (e) { dispatch({ - type: Symbol.for('app ready') - }) + type: Symbol.for('app ready'), + }); // window.location.href = '/oauth/flow' } } else { dispatch({ - type: Symbol.for('app ready') - }) + type: Symbol.for('app ready'), + }); } -} +}; export const userLogout = async dispatch => { try { - await superagent.post('/api/auth/logout') - } catch (e) { - } + await superagent.post('/api/auth/logout'); + } catch (e) {} dispatch({ - type: Symbol.for('reset user') - }) + type: Symbol.for('reset user'), + }); - window.location.href = '/' -} + window.location.href = '/'; +}; export const startServerPolling = dispatch => { - return poll(window.__APP_STORE__.dispatch, window.__APP_STORE__.getState) // let's not cheat... :c -} + return poll(window.__APP_STORE__.dispatch, window.__APP_STORE__.getState); // let's not cheat... :c +}; const poll = (dispatch, getState) => { - const { servers } = getState() - let stop = false - const stopPolling = () => { stop = true } + const { servers } = getState(); + let stop = false; + const stopPolling = () => { + stop = true; + }; const pollFunc = async () => { if (stop) { - return + return; } try { - await fetchServers(dispatch) + await fetchServers(dispatch); } catch (e) { - console.error(e) - setTimeout(pollFunc, 5000) + console.error(e); + setTimeout(pollFunc, 5000); } - const newServers = getState().servers + const newServers = getState().servers; if (servers.size >= newServers.size) { - setTimeout(pollFunc, 5000) + setTimeout(pollFunc, 5000); } else { - const old = servers.keySeq().toSet() - const upd = newServers.keySeq().toSet() - const newSrv = upd.subtract(old) - stopPolling() - dispatch(push(`/s/${newSrv.toJS()[0]}/edit`)) + const old = servers.keySeq().toSet(); + const upd = newServers.keySeq().toSet(); + const newSrv = upd.subtract(old); + stopPolling(); + dispatch(push(`/s/${newSrv.toJS()[0]}/edit`)); } - } + }; - pollFunc() - return stopPolling -} + pollFunc(); + return stopPolling; +}; diff --git a/UI/src/actions/ui.js b/UI/src/actions/ui.js index 2182bea..7076c2f 100644 --- a/UI/src/actions/ui.js +++ b/UI/src/actions/ui.js @@ -1,13 +1,13 @@ export const fadeOut = cb => dispatch => { dispatch({ type: Symbol.for('app fade'), - data: true - }) + data: true, + }); - setTimeout(cb, 300) -} + setTimeout(cb, 300); +}; export const fadeIn = { type: Symbol.for('app fade'), - data: false -} + data: false, +}; diff --git a/UI/src/components/add-server/index.js b/UI/src/components/add-server/index.js index 7e4bd74..a74165b 100644 --- a/UI/src/components/add-server/index.js +++ b/UI/src/components/add-server/index.js @@ -1,41 +1,64 @@ -import React, { Component } from 'react' -import { Link } from 'react-router-dom' -import TypingDemo from '../demos/typing' -import RoleypolyDemo from '../demos/roleypoly' -import * as Actions from '../../actions' -import './styles.sass' -import discordLogo from '../../pages/images/discord-logo.svg' +import React, { Component } from 'react'; +import { Link } from 'react-router-dom'; +import TypingDemo from '../demos/typing'; +import RoleypolyDemo from '../demos/roleypoly'; +import * as Actions from '../../actions'; +import './styles.sass'; +import discordLogo from '../../pages/images/discord-logo.svg'; export default class AddServer extends Component { - polling = null - - componentDidMount () { + polling = null; + + componentDidMount() { if (this.props.match.params.server !== undefined) { - this.pollingStop = Actions.startServerPolling(this.props.dispatch) + this.pollingStop = Actions.startServerPolling(this.props.dispatch); } } - componentWillUnmount () { + componentWillUnmount() { if (this.pollingStop != null) { - this.pollingStop() + this.pollingStop(); } } - - render () { - return
-

What is Roleypoly?

-

- Roleypoly is a helper bot to help server members assign themselves roles on Discord. -

-
-
-
Could you easily remember 250 role names? You'd use images or bot commands to tell everyone what they can assign. This kinda limits how many roles you can reasonably have. And don't even start with emojis. 💢
-
Just click. 🌈 💖
-
+ + render() { + return ( +
+

What is Roleypoly?

+

+ Roleypoly is a helper bot to help server members assign themselves roles on + Discord. +

+
+
+ +
+
+ Could you easily remember 250 role names? You'd use images or bot commands to + tell everyone what they can assign. This kinda limits how many roles + you can reasonably have. And don't even start with emojis.{' '} + + 💢 + +
+
+ Just click. 🌈 💖 +
+
+ +
+
+
-
- Authorize via Discord -
-
+ ); } } diff --git a/UI/src/components/demos/roleypoly.js b/UI/src/components/demos/roleypoly.js index a58f58a..5646fd2 100644 --- a/UI/src/components/demos/roleypoly.js +++ b/UI/src/components/demos/roleypoly.js @@ -1,12 +1,14 @@ -import React from 'react' -import RoleDemo from '../role/demo' +import React from 'react'; +import RoleDemo from '../role/demo'; -const RoleypolyDemo = () =>
- - - - - -
+const RoleypolyDemo = () => ( +
+ + + + + +
+); -export default RoleypolyDemo +export default RoleypolyDemo; diff --git a/UI/src/components/demos/typing.js b/UI/src/components/demos/typing.js index d8778ab..11e1373 100644 --- a/UI/src/components/demos/typing.js +++ b/UI/src/components/demos/typing.js @@ -1,29 +1,31 @@ -import React from 'react' -import moment from 'moment' -import Typist from 'react-typist' -import './typing.sass' +import React from 'react'; +import moment from 'moment'; +import Typist from 'react-typist'; +import './typing.sass'; -const Typing = () =>
-
- {moment().format('LT')} - Kata カタ - Hey, I want some roles! +const Typing = () => ( +
+
+ {moment().format('LT')} + Kata カタ + Hey, I want some roles! +
+
+ + .iam a cute role ♡ + + .iam a vanity role ♡ + + .iam a brave role ♡ + + .iam a proud role ♡ + + .iam a wonderful role ♡ + + i have too many roles. + +
-
- - .iam a cute role ♡ - - .iam a vanity role ♡ - - .iam a brave role ♡ - - .iam a proud role ♡ - - .iam a wonderful role ♡ - - i have too many roles. - -
-
+); -export default Typing +export default Typing; diff --git a/UI/src/components/dev-tools/index.js b/UI/src/components/dev-tools/index.js index a7d540a..f437a20 100644 --- a/UI/src/components/dev-tools/index.js +++ b/UI/src/components/dev-tools/index.js @@ -1,11 +1,10 @@ -import React from 'react' -import { createDevTools } from 'redux-devtools' -import LogMonitor from 'redux-devtools-log-monitor' -import DockMonitor from 'redux-devtools-dock-monitor' +import React from 'react'; +import { createDevTools } from 'redux-devtools'; +import LogMonitor from 'redux-devtools-log-monitor'; +import DockMonitor from 'redux-devtools-dock-monitor'; export default createDevTools( - + -) \ No newline at end of file +); diff --git a/UI/src/components/logotype/index.js b/UI/src/components/logotype/index.js index 9cecf84..5be9230 100644 --- a/UI/src/components/logotype/index.js +++ b/UI/src/components/logotype/index.js @@ -1,18 +1,44 @@ -import React from 'react' +import React from 'react'; -const Logotype = ({fill = 'var(--c-7)', width, height, circleFill, typeFill, style, className}) => ( - +const Logotype = ({ + fill = 'var(--c-7)', + width, + height, + circleFill, + typeFill, + style, + className, +}) => ( + - + - + - + -) +); -export default Logotype +export default Logotype; diff --git a/UI/src/components/oauth-bot-flow/index.js b/UI/src/components/oauth-bot-flow/index.js index 7786b01..a166610 100644 --- a/UI/src/components/oauth-bot-flow/index.js +++ b/UI/src/components/oauth-bot-flow/index.js @@ -1,27 +1,35 @@ -import React, { Component } from 'react' -import { Redirect } from 'react-router-dom' -import superagent from 'superagent' -import { connect } from 'react-redux' -import { push } from 'react-router-redux' -import { fetchServers } from '../../actions' +import React, { Component } from 'react'; +import { Redirect } from 'react-router-dom'; +import superagent from 'superagent'; +import { connect } from 'react-redux'; +import { push } from 'react-router-redux'; +import { fetchServers } from '../../actions'; @connect() class OauthCallback extends Component { state = { notReady: true, message: 'chotto matte kudasai...', - url: null + url: null, + }; + + async componentDidMount() { + const { + body: { url }, + } = await superagent.get('/api/oauth/bot?url=✔️'); + this.setState({ url, notReady: false }); + window.location.href = url; } - async componentDidMount () { - const { body: { url } } = await superagent.get('/api/oauth/bot?url=✔️') - this.setState({ url, notReady: false }) - window.location.href = url - } - - render () { - return (this.state.notReady) ? this.state.message : Something oopsed, click me to get to where you meant. + render() { + return this.state.notReady ? ( + this.state.message + ) : ( + + Something oopsed, click me to get to where you meant. + + ); } } -export default OauthCallback +export default OauthCallback; diff --git a/UI/src/components/oauth-callback/index.js b/UI/src/components/oauth-callback/index.js index e895f5f..4da3423 100644 --- a/UI/src/components/oauth-callback/index.js +++ b/UI/src/components/oauth-callback/index.js @@ -1,83 +1,87 @@ -import React, { Component } from 'react' -import { Redirect } from 'react-router-dom' -import superagent from 'superagent' -import { connect } from 'react-redux' -import { fetchServers } from '../../actions' +import React, { Component } from 'react'; +import { Redirect } from 'react-router-dom'; +import superagent from 'superagent'; +import { connect } from 'react-redux'; +import { fetchServers } from '../../actions'; @connect() class OauthCallback extends Component { state = { notReady: true, message: 'chotto matte kudasai...', - redirect: '/s' + redirect: '/s', + }; + + stopped = false; + + componentDidUnmount() { + this.stopped = true; } - stopped = false - - componentDidUnmount () { - this.stopped = true - } - - async componentDidMount () { + async componentDidMount() { // handle stuff in the url - const sp = new URLSearchParams(this.props.location.search) - const token = sp.get('code') - + 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 - } - - const stateToken = sp.get('state') - const state = JSON.parse(window.sessionStorage.getItem('state') || 'null') + this.setState({ message: 'token missing, what are you trying to do?!' }); + return; + } + + const stateToken = sp.get('state'); + const state = JSON.parse(window.sessionStorage.getItem('state') || 'null'); if (state !== null && state.state === stateToken && state.redirect != null) { - this.setState({ redirect: state.redirect }) + this.setState({ redirect: state.redirect }); } - this.props.history.replace(this.props.location.pathname) - - let counter = 0 + this.props.history.replace(this.props.location.pathname); + + let counter = 0; const retry = async () => { - if (this.stopped) return + if (this.stopped) return; try { - const rsp = await superagent.get('/api/auth/user') + const rsp = await superagent.get('/api/auth/user'); this.props.dispatch({ type: Symbol.for('set user'), - data: rsp.body - }) - this.props.dispatch(fetchServers) - this.setState({ notReady: false }) + data: rsp.body, + }); + this.props.dispatch(fetchServers); + this.setState({ notReady: false }); } catch (e) { - counter++ + counter++; if (counter > 10) { - this.setState({ message: "i couldn't log you in. :c" }) + this.setState({ message: "i couldn't log you in. :c" }); } else { - setTimeout(() => { retry() }, 250) + setTimeout(() => { + retry(); + }, 250); } } - } + }; // pass token to backend, await it to finish it's business. try { - await superagent.post('/api/auth/token').send({ token }) + await superagent.post('/api/auth/token').send({ token }); // this.props.onLogin(rsp.body) - - retry() + retry(); } catch (e) { - console.error('token pass error', e) - this.setState({ message: 'g-gomen nasai... i broke it...' }) - return - } - + console.error('token pass error', e); + this.setState({ message: 'g-gomen nasai... i broke it...' }); + return; + } // update user stuff here } - render () { - return (this.state.notReady) ? this.state.message : + render() { + return this.state.notReady ? ( + this.state.message + ) : ( + + ); } } -export default OauthCallback +export default OauthCallback; diff --git a/UI/src/components/oauth-flow/index.js b/UI/src/components/oauth-flow/index.js index 60a9fd6..ea0dd57 100644 --- a/UI/src/components/oauth-flow/index.js +++ b/UI/src/components/oauth-flow/index.js @@ -1,10 +1,9 @@ -import React, { Component } from 'react' -import { Redirect } from 'react-router-dom' -import superagent from 'superagent' -import { connect } from 'react-redux' -import uuidv4 from 'uuid/v4' -import { fetchServers } from '../../actions' - +import React, { Component } from 'react'; +import { Redirect } from 'react-router-dom'; +import superagent from 'superagent'; +import { connect } from 'react-redux'; +import uuidv4 from 'uuid/v4'; +import { fetchServers } from '../../actions'; @connect() class OauthCallback extends Component { @@ -12,62 +11,76 @@ class OauthCallback extends Component { notReady: true, message: 'chotto matte kudasai...', redirect: '/s', - url: null - } + url: null, + }; - async fetchUser () { - const rsp = await superagent.get('/api/auth/user') - sessionStorage.setItem('user', JSON.stringify(rsp.body)) - sessionStorage.setItem('user.update', JSON.stringify(Date.now())) + async fetchUser() { + const rsp = await superagent.get('/api/auth/user'); + sessionStorage.setItem('user', JSON.stringify(rsp.body)); + sessionStorage.setItem('user.update', JSON.stringify(Date.now())); this.props.dispatch({ type: Symbol.for('set user'), - data: rsp.body - }) + data: rsp.body, + }); } - setupUser () { - const userUpdateTime = sessionStorage.getItem('user.update') || 0 - if (+userUpdateTime + (1000 * 60 * 10) > Date.now()) { - const user = sessionStorage.getItem('user') + setupUser() { + const userUpdateTime = sessionStorage.getItem('user.update') || 0; + if (+userUpdateTime + 1000 * 60 * 10 > Date.now()) { + const user = sessionStorage.getItem('user'); if (user != null && user !== '') { this.props.dispatch({ type: Symbol.for('set user'), - data: JSON.parse(user) - }) + data: JSON.parse(user), + }); } } - return this.fetchUser() + return this.fetchUser(); } - async componentDidMount () { - const state = uuidv4() + async componentDidMount() { + const state = uuidv4(); - const oUrl = new URL(window.location.href) + const oUrl = new URL(window.location.href); if (oUrl.searchParams.has('r')) { - this.setState({ redirect: oUrl.searchParams.get('r') }) + this.setState({ redirect: oUrl.searchParams.get('r') }); } - window.sessionStorage.setItem('state', JSON.stringify({ state, redirect: oUrl.searchParams.get('r') })) + window.sessionStorage.setItem( + 'state', + JSON.stringify({ state, redirect: oUrl.searchParams.get('r') }) + ); try { - await this.setupUser() + await this.setupUser(); - this.props.dispatch(fetchServers) - this.setState({ notReady: false }) + this.props.dispatch(fetchServers); + this.setState({ notReady: false }); } catch (e) { - const { body: { url } } = await superagent.get('/api/auth/redirect?url=✔️') - const nUrl = new URL(url) + const { + body: { url }, + } = await superagent.get('/api/auth/redirect?url=✔️'); + const nUrl = new URL(url); - nUrl.searchParams.set('state', state) - this.setState({ url: nUrl.toString() }) - window.location.href = nUrl.toString() + nUrl.searchParams.set('state', state); + this.setState({ url: nUrl.toString() }); + window.location.href = nUrl.toString(); } } - render () { - return (this.state.notReady) ? this.state.message : <>Something oopsed, click me to get to where you meant. + render() { + return this.state.notReady ? ( + this.state.message + ) : ( + <> + + + Something oopsed, click me to get to where you meant. + + + ); } } -export default OauthCallback +export default OauthCallback; diff --git a/UI/src/components/role-editor/Category.js b/UI/src/components/role-editor/Category.js index c615685..2e8172d 100644 --- a/UI/src/components/role-editor/Category.js +++ b/UI/src/components/role-editor/Category.js @@ -1,43 +1,69 @@ -import React, { Component } from 'react' -import { DropTarget } from 'react-dnd' +import React, { Component } from 'react'; +import { DropTarget } from 'react-dnd'; -import Role from '../role/draggable' -import CategoryEditor from './CategoryEditor' +import Role from '../role/draggable'; +import CategoryEditor from './CategoryEditor'; -@DropTarget(Symbol.for('dnd: role'), { - drop (props, monitor, element) { - props.onDrop(monitor.getItem()) +@DropTarget( + Symbol.for('dnd: role'), + { + drop(props, monitor, element) { + props.onDrop(monitor.getItem()); + }, + canDrop(props, monitor) { + return ( + props.mode !== Symbol.for('edit') && monitor.getItem().category !== props.name + ); + }, }, - canDrop (props, monitor) { - return (props.mode !== Symbol.for('edit') && monitor.getItem().category !== props.name) - } -}, (connect, monitor) => ({ - connectDropTarget: connect.dropTarget(), - isOver: monitor.isOver(), - isOverCurrent: monitor.isOver({ shallow: true }), - canDrop: monitor.canDrop(), - itemType: monitor.getItemType() -})) + (connect, monitor) => ({ + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver(), + isOverCurrent: monitor.isOver({ shallow: true }), + canDrop: monitor.canDrop(), + itemType: monitor.getItemType(), + }) +) class Category extends Component { - render () { - const { category, name, isOver, canDrop, connectDropTarget, mode, onEditOpen, ...rest } = this.props + render() { + const { + category, + name, + isOver, + canDrop, + connectDropTarget, + mode, + onEditOpen, + ...rest + } = this.props; if (mode === Symbol.for('edit')) { - return + return ; } - return connectDropTarget(
-
-

{ category.get('name') }

-
-
- { - category.get('roles_map') + return connectDropTarget( +
+
+

{category.get('name')}

+
+
+ {category + .get('roles_map') .reverse() .map((r, k) => ) - .toArray() - } -
) + .toArray()} +
+ ); } } -export default Category +export default Category; diff --git a/UI/src/components/role-editor/CategoryEditor.js b/UI/src/components/role-editor/CategoryEditor.js index 40e292f..1926cd9 100644 --- a/UI/src/components/role-editor/CategoryEditor.js +++ b/UI/src/components/role-editor/CategoryEditor.js @@ -1,86 +1,109 @@ -import React, { Component } from 'react' +import React, { Component } from 'react'; export default class CategoryEditor extends Component { - - onKeyPress = (e) => { - const { onSave } = this.props + onKeyPress = e => { + const { onSave } = this.props; switch (e.key) { case 'Enter': case 'Escape': - return onSave() + return onSave(); } - } + }; - render () { - const { - category - } = this.props + render() { + const { category } = this.props; - return
-
-
- -
- -
-
-
- -
-
- -
-
-
-
-
-
-
+ ); } } diff --git a/UI/src/components/role-editor/actions.js b/UI/src/components/role-editor/actions.js index 3be1789..1a1f414 100644 --- a/UI/src/components/role-editor/actions.js +++ b/UI/src/components/role-editor/actions.js @@ -1,55 +1,55 @@ -import { Set } from 'immutable' -import * as UIActions from '../../actions/ui' -import { getViewMap, setup } from '../role-picker/actions' -import uuidv4 from 'uuid/v4' -import superagent from 'superagent' +import { Set } from 'immutable'; +import * as UIActions from '../../actions/ui'; +import { getViewMap, setup } from '../role-picker/actions'; +import uuidv4 from 'uuid/v4'; +import superagent from 'superagent'; export const constructView = id => async (dispatch, getState) => { - await setup(id)(dispatch) - const server = getState().servers.get(id) + await setup(id)(dispatch); + const server = getState().servers.get(id); - let { viewMap, hasSafeRoles } = getViewMap(server) - viewMap = viewMap.map((c, idx) => c.set('mode', Symbol.for('drop'))) + let { viewMap, hasSafeRoles } = getViewMap(server); + viewMap = viewMap.map((c, idx) => c.set('mode', Symbol.for('drop'))); dispatch({ type: Symbol.for('re: setup'), data: { hasSafeRoles, viewMap, - originalSnapshot: viewMap - } - }) + originalSnapshot: viewMap, + }, + }); - dispatch(UIActions.fadeIn) -} + dispatch(UIActions.fadeIn); +}; -export const addRoleToCategory = (id, oldId, role, flip = true) => (dispatch) => { +export const addRoleToCategory = (id, oldId, role, flip = true) => dispatch => { dispatch({ type: Symbol.for('re: add role to category'), data: { id, - role - } - }) + role, + }, + }); if (flip) { - dispatch(removeRoleFromCategory(oldId, id, role, false)) + dispatch(removeRoleFromCategory(oldId, id, role, false)); } -} +}; -export const removeRoleFromCategory = (id, oldId, role, flip = true) => (dispatch) => { +export const removeRoleFromCategory = (id, oldId, role, flip = true) => dispatch => { dispatch({ type: Symbol.for('re: remove role from category'), data: { id, - role - } - }) + role, + }, + }); if (flip) { - dispatch(addRoleToCategory(oldId, id, role, false)) + dispatch(addRoleToCategory(oldId, id, role, false)); } -} +}; export const editCategory = ({ id, key, value }) => dispatch => { dispatch({ @@ -57,38 +57,38 @@ export const editCategory = ({ id, key, value }) => dispatch => { data: { id, key, - value - } - }) -} + value, + }, + }); +}; -export const saveCategory = (id, category) => (dispatch) => { +export const saveCategory = (id, category) => dispatch => { if (category.get('name') === '') { - return + return; } dispatch({ type: Symbol.for('re: switch category mode'), data: { id, - mode: Symbol.for('drop') - } - }) -} + mode: Symbol.for('drop'), + }, + }); +}; -export const openEditor = (id) => ({ +export const openEditor = id => ({ type: Symbol.for('re: switch category mode'), data: { id, - mode: Symbol.for('edit') - } -}) + mode: Symbol.for('edit'), + }, +}); export const deleteCategory = (id, category) => (dispatch, getState) => { - const roles = category.get('roles') - const rolesMap = category.get('roles_map') + const roles = category.get('roles'); + const rolesMap = category.get('roles_map'); - let uncategorized = getState().roleEditor.getIn(['viewMap', 'Uncategorized']) + let uncategorized = getState().roleEditor.getIn(['viewMap', 'Uncategorized']); dispatch({ type: Symbol.for('re: set category'), @@ -99,29 +99,29 @@ export const deleteCategory = (id, category) => (dispatch, getState) => { roles_map: uncategorized.get('roles_map').union(rolesMap), hidden: true, type: 'multi', - mode: null - } - }) + mode: null, + }, + }); dispatch({ type: Symbol.for('re: delete category'), - data: id - }) -} + data: id, + }); +}; export const createCategory = (dispatch, getState) => { - const { roleEditor } = getState() - const vm = roleEditor.get('viewMap') + const { roleEditor } = getState(); + const vm = roleEditor.get('viewMap'); - let name = 'New Category' - let idx = 1 - const pred = c => c.get('name') === name + let name = 'New Category'; + let idx = 1; + const pred = c => c.get('name') === name; while (vm.find(pred) !== undefined) { - idx++ - name = `New Category ${idx}` + idx++; + name = `New Category ${idx}`; } - const id = uuidv4() + const id = uuidv4(); dispatch({ type: Symbol.for('re: set category'), @@ -133,28 +133,28 @@ export const createCategory = (dispatch, getState) => { hidden: true, type: 'multi', position: idx, - mode: Symbol.for('edit') - } - }) -} + mode: Symbol.for('edit'), + }, + }); +}; export const bumpCategory = (category, name) => move => async (dispatch, getState) => { - const { roleEditor } = getState() - const vm = roleEditor.get('viewMap') + const { roleEditor } = getState(); + const vm = roleEditor.get('viewMap'); - const position = category.get('position') - const nextPos = position + move + const position = category.get('position'); + const nextPos = position + move; - const replaceThisOne = vm.findKey(category => category.get('position') === nextPos) + const replaceThisOne = vm.findKey(category => category.get('position') === nextPos); dispatch({ type: Symbol.for('re: edit category'), data: { id: name, key: 'position', - value: nextPos - } - }) + value: nextPos, + }, + }); if (!!replaceThisOne) { dispatch({ @@ -162,27 +162,36 @@ export const bumpCategory = (category, name) => move => async (dispatch, getStat data: { id: replaceThisOne, key: 'position', - value: position - } - }) + value: position, + }, + }); } - -} +}; export const saveServer = id => async (dispatch, getState) => { - const viewMap = getState().roleEditor.get('viewMap') + const viewMap = getState() + .roleEditor.get('viewMap') .filterNot((_, k) => k === 'Uncategorized') - .map(v => v.delete('roles_map').delete('mode').delete('id')) + .map(v => + v + .delete('roles_map') + .delete('mode') + .delete('id') + ); viewMap.map((v, idx) => { if (v.has('position')) { - return v + return v; } - console.warn('category position wasnt set, so fake ones are being made', {cat: v.toJS(), idx, position: viewMap.count()+idx}) - return v.set('position', viewMap.count()+idx) - }) + console.warn('category position wasnt set, so fake ones are being made', { + cat: v.toJS(), + idx, + position: viewMap.count() + idx, + }); + return v.set('position', viewMap.count() + idx); + }); - await superagent.patch(`/api/server/${id}`).send({ categories: viewMap.toJS() }) - dispatch({ type: Symbol.for('re: swap original state') }) -} + await superagent.patch(`/api/server/${id}`).send({ categories: viewMap.toJS() }); + dispatch({ type: Symbol.for('re: swap original state') }); +}; diff --git a/UI/src/components/role-editor/index.js b/UI/src/components/role-editor/index.js index d6d0e0d..6c8d640 100644 --- a/UI/src/components/role-editor/index.js +++ b/UI/src/components/role-editor/index.js @@ -1,197 +1,238 @@ -import React, { Component } from 'react' -import { Set } from 'immutable' -import { connect } from 'react-redux' -import { DropTarget } from 'react-dnd' -import { Link, Prompt, Redirect } from 'react-router-dom' -import { Scrollbars } from 'react-custom-scrollbars' -import * as Actions from './actions' -import * as PickerActions from '../role-picker/actions' -import * as UIActions from '../../actions/ui' -import './RoleEditor.sass' +import React, { Component } from 'react'; +import { Set } from 'immutable'; +import { connect } from 'react-redux'; +import { DropTarget } from 'react-dnd'; +import { Link, Prompt, Redirect } from 'react-router-dom'; +import { Scrollbars } from 'react-custom-scrollbars'; +import * as Actions from './actions'; +import * as PickerActions from '../role-picker/actions'; +import * as UIActions from '../../actions/ui'; +import './RoleEditor.sass'; -import Category from './Category' -import CategoryEditor from './CategoryEditor' -import Role from '../role/draggable' +import Category from './Category'; +import CategoryEditor from './CategoryEditor'; +import Role from '../role/draggable'; const mapState = ({ rolePicker, roleEditor, servers }, ownProps) => ({ rp: rolePicker, editor: roleEditor, - server: servers.get(ownProps.match.params.server) -}) + server: servers.get(ownProps.match.params.server), +}); @connect(mapState) -@DropTarget(Symbol.for('dnd: role'), { - drop (props, monitor, element) { - element.dropRole({}, 'Uncategorized')(monitor.getItem()) +@DropTarget( + Symbol.for('dnd: role'), + { + drop(props, monitor, element) { + element.dropRole({}, 'Uncategorized')(monitor.getItem()); + }, + canDrop(props, monitor) { + return monitor.getItem().category !== 'Uncategorized'; + }, }, - canDrop (props, monitor) { - return (monitor.getItem().category !== 'Uncategorized') - } -}, (connect, monitor) => ({ - connectDropTarget: connect.dropTarget(), - isOver: monitor.isOver(), - isOverCurrent: monitor.isOver({ shallow: true }), - canDrop: monitor.canDrop(), - itemType: monitor.getItemType() -})) + (connect, monitor) => ({ + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver(), + isOverCurrent: monitor.isOver({ shallow: true }), + canDrop: monitor.canDrop(), + itemType: monitor.getItemType(), + }) +) class RoleEditor extends Component { - componentWillMount () { - const { dispatch, match: { params: { server } } } = this.props - dispatch(Actions.constructView(server)) + componentWillMount() { + const { + dispatch, + match: { + params: { server }, + }, + } = this.props; + dispatch(Actions.constructView(server)); } - componentWillReceiveProps (nextProps) { + componentWillReceiveProps(nextProps) { if (this.props.match.params.server !== nextProps.match.params.server) { - const { dispatch } = this.props - dispatch(UIActions.fadeOut(() => dispatch(Actions.constructView(nextProps.match.params.server)))) + const { dispatch } = this.props; + dispatch( + UIActions.fadeOut(() => + dispatch(Actions.constructView(nextProps.match.params.server)) + ) + ); } } - dropRole = (category, name) => ({role, category}) => { - const { dispatch } = this.props - console.log(role) - dispatch(Actions.addRoleToCategory(name, category, role)) - } + dropRole = (category, name) => ({ role, category }) => { + const { dispatch } = this.props; + console.log(role); + dispatch(Actions.addRoleToCategory(name, category, role)); + }; createCategory = () => { - const { dispatch } = this.props - dispatch(Actions.createCategory) - } + const { dispatch } = this.props; + dispatch(Actions.createCategory); + }; saveCategory = (category, name) => () => { - const { dispatch } = this.props - dispatch(Actions.saveCategory(name, category)) - } + const { dispatch } = this.props; + dispatch(Actions.saveCategory(name, category)); + }; deleteCategory = (category, id) => () => { - const { dispatch } = this.props - dispatch(Actions.deleteCategory(id, category)) - } + const { dispatch } = this.props; + dispatch(Actions.deleteCategory(id, category)); + }; openEditor = (category, name) => () => { - const { dispatch } = this.props - dispatch(Actions.openEditor(name)) - } + const { dispatch } = this.props; + dispatch(Actions.openEditor(name)); + }; editCategory = (category, id) => (key, type) => event => { - const { dispatch } = this.props - let value + const { dispatch } = this.props; + let value; switch (type) { case Symbol.for('edit: text'): - value = event.target.value - break + value = event.target.value; + break; case Symbol.for('edit: bool'): - value = event.target.checked - break - + value = event.target.checked; + break; + case Symbol.for('edit: select'): - value = event.target.value - break - + value = event.target.value; + break; + default: - value = null + value = null; } - dispatch(Actions.editCategory({ category, id, key, type, value })) - } + dispatch(Actions.editCategory({ category, id, key, type, value })); + }; resetServer = () => { - const { dispatch } = this.props - dispatch({ type: Symbol.for('re: reset') }) - } + const { dispatch } = this.props; + dispatch({ type: Symbol.for('re: reset') }); + }; saveServer = () => { - const { dispatch, match: { params: { server } } } = this.props - dispatch(Actions.saveServer(server)) + const { + dispatch, + match: { + params: { server }, + }, + } = this.props; + dispatch(Actions.saveServer(server)); + }; + + onBump = (category, name) => move => () => + this.props.dispatch(Actions.bumpCategory(category, name)(move)); + + get hasChanged() { + return ( + this.props.editor.get('originalSnapshot').hashCode() !== + this.props.editor.get('viewMap').hashCode() + ); } - onBump = (category, name) => (move) => () => this.props.dispatch(Actions.bumpCategory(category, name)(move)) - - get hasChanged () { - return this.props.editor.get('originalSnapshot').hashCode() !== this.props.editor.get('viewMap').hashCode() - } - - render () { - const { server } = this.props + render() { + const { server } = this.props; if (server == null) { - return null + return null; } if (server.getIn(['perms', 'canManageRoles']) !== true) { - return + return ; } - const vm = this.props.editor.get('viewMap') - return
- -
-

{this.props.server.getIn(['server','name'])}

-
-
- - -
-
-
-
- - { - vm - .filter((_, k) => k !== 'Uncategorized') - .sortBy(c => c.get('position')) - .map((c, name, arr) => ) - .toArray() - } -
- + const vm = this.props.editor.get('viewMap'); + return ( +
+ +
+

{this.props.server.getIn(['server', 'name'])}

+
+
+ +
-
- { - this.props.connectDropTarget( -
- -
- { - (vm.getIn(['Uncategorized', 'roles_map']) || Set()) - .sortBy(r => r.get('position')) - .reverse() - .map((r, k) => ) - .toArray() - } - { - (this.props.editor.get('hasSafeRoles') !== true) - ?
- Why are there no roles here? -
- : null - } +
+
+ + {vm + .filter((_, k) => k !== 'Uncategorized') + .sortBy(c => c.get('position')) + .map((c, name, arr) => ( + + )) + .toArray()} +
+
-
) - } +
+ {this.props.connectDropTarget( +
+ +
+ {(vm.getIn(['Uncategorized', 'roles_map']) || Set()) + .sortBy(r => r.get('position')) + .reverse() + .map((r, k) => ) + .toArray()} + {this.props.editor.get('hasSafeRoles') !== true ? ( +
+ + Why are there no roles here? + +
+ ) : null} +
+
+
+ )} +
-
+ ); } } -export default RoleEditor +export default RoleEditor; diff --git a/UI/src/components/role-picker/Category.js b/UI/src/components/role-picker/Category.js index 0bcf855..5e403e4 100644 --- a/UI/src/components/role-picker/Category.js +++ b/UI/src/components/role-picker/Category.js @@ -1,53 +1,68 @@ -import React, { Component } from 'react' -import { Map } from 'immutable' +import React, { Component } from 'react'; +import { Map } from 'immutable'; -import Role from '../role' +import Role from '../role'; class Category extends Component { - - toggleRoleMulti (id, next) { - this.props.onChange(Map({ [id]: next })) + toggleRoleMulti(id, next) { + this.props.onChange(Map({ [id]: next })); } - toggleRoleSingle (id, next) { - this.props.onChange(this.props.category.get('roles').reduce((acc, i) => acc.set(i, false), Map()).set(id, next)) + toggleRoleSingle(id, next) { + this.props.onChange( + this.props.category + .get('roles') + .reduce((acc, i) => acc.set(i, false), Map()) + .set(id, next) + ); } onRoleToggle = id => (next, old) => { - const type = this.props.category.get('type') + const type = this.props.category.get('type'); switch (type) { - case 'single': return this.toggleRoleSingle(id, next) - case 'multi': return this.toggleRoleMulti(id, next) - default: - console.warn('DEFAULTING TO MULTI', id, next, old) - return this.toggleRoleMulti(id, next) + case 'single': + return this.toggleRoleSingle(id, next); + case 'multi': + return this.toggleRoleMulti(id, next); + default: + console.warn('DEFAULTING TO MULTI', id, next, old); + return this.toggleRoleMulti(id, next); } - } + }; - render () { - const { category, name, isSelected } = this.props + render() { + const { category, name, isSelected } = this.props; if (category.get('hidden')) { - return null + return null; } if (category.get('roles').count() === 0) { - return null + return null; } - return
-

{ category.get('name') }

- { - category.get('roles_map') - .sortBy(r => r.get('position')) - .reverse() - .map((r, k) => { - const id = r.get('id') - return - }) - .toArray() - } -
+ return ( +
+

{category.get('name')}

+ {category + .get('roles_map') + .sortBy(r => r.get('position')) + .reverse() + .map((r, k) => { + const id = r.get('id'); + return ( + + ); + }) + .toArray()} +
+ ); } } -export default Category +export default Category; diff --git a/UI/src/components/role-picker/actions.js b/UI/src/components/role-picker/actions.js index 2b59857..24b8653 100644 --- a/UI/src/components/role-picker/actions.js +++ b/UI/src/components/role-picker/actions.js @@ -1,75 +1,89 @@ -import { Map, Set, fromJS } from 'immutable' -import superagent from 'superagent' -import * as UIActions from '../../actions/ui' +import { Map, Set, fromJS } from 'immutable'; +import superagent from 'superagent'; +import * as UIActions from '../../actions/ui'; export const setup = id => async dispatch => { - const rsp = await superagent.get(`/api/server/${id}`) - const data = rsp.body + const rsp = await superagent.get(`/api/server/${id}`); + const data = rsp.body; dispatch({ type: Symbol.for('server: set'), data: { id, - ...data - } - }) - dispatch(constructView(id)) -} + ...data, + }, + }); + dispatch(constructView(id)); +}; export const getViewMap = server => { - const roles = server.get('roles') - const categories = server.get('categories') - const categoriesIds = server.get('categories').keySeq() + const roles = server.get('roles'); + const categories = server.get('categories'); + const categoriesIds = server.get('categories').keySeq(); - const allRoles = server.get('roles').filter(v => v.get('safe')).map(r => r.get('id')).toSet() - const accountedRoles = categories.map(c => c.get('roles')).toSet().flatten() - const unaccountedRoles = allRoles.subtract(accountedRoles) + const allRoles = server + .get('roles') + .filter(v => v.get('safe')) + .map(r => r.get('id')) + .toSet(); + const accountedRoles = categories + .map(c => c.get('roles')) + .toSet() + .flatten(); + const unaccountedRoles = allRoles.subtract(accountedRoles); // console.log('roles', allRoles.toJS(), accountedRoles.toJS(), unaccountedRoles.toJS()) - const viewMap = categories.set('Uncategorized', fromJS({ - roles: unaccountedRoles, - hidden: true, - type: 'multi', - name: 'Uncategorized' - })) - .map( - (cat, idx) => + const viewMap = categories + .set( + 'Uncategorized', + fromJS({ + roles: unaccountedRoles, + hidden: true, + type: 'multi', + name: 'Uncategorized', + }) + ) + .map((cat, idx) => cat.set( - 'position', - cat.get('position', categoriesIds.findIndex(v => v === idx) + 'position', + cat.get( + 'position', + categoriesIds.findIndex(v => v === idx) + ) ) ) - ) - // .sortBy(cat => cat.get('position')) - .map(c => { - const roles = c.get('roles') - // fill in roles_map - .map(r => - server.get('roles').find(sr => sr.get('id') === r) - ) - .filter(r => r != null) - // sort by server position, backwards. - .sort((a, b) => a.position > b.position) - // force data to sets - return c.set('roles_map', Set(roles)).set('roles', Set(c.get('roles'))) - }) + // .sortBy(cat => cat.get('position')) + .map(c => { + const roles = c + .get('roles') + // fill in roles_map + .map(r => server.get('roles').find(sr => sr.get('id') === r)) + .filter(r => r != null) + // sort by server position, backwards. + .sort((a, b) => a.position > b.position); + // force data to sets + return c.set('roles_map', Set(roles)).set('roles', Set(c.get('roles'))); + }); - const selected = roles.reduce((acc, r) => acc.set(r.get('id'), r.get('selected')), Map()) + const selected = roles.reduce( + (acc, r) => acc.set(r.get('id'), r.get('selected')), + Map() + ); - const hasSafeRoles = allRoles.size > 0 + const hasSafeRoles = allRoles.size > 0; return { viewMap, selected, - hasSafeRoles - } -} + hasSafeRoles, + }; +}; export const constructView = id => (dispatch, getState) => { - const server = getState().servers.get(id) + const server = getState().servers.get(id); - const { viewMap, selected } = getViewMap(server) + const { viewMap, selected } = getViewMap(server); dispatch({ type: Symbol.for('rp: setup role picker'), @@ -79,79 +93,79 @@ export const constructView = id => (dispatch, getState) => { originalRolesSelected: selected, hidden: false, isEditingMessage: false, - messageBuffer: '' - } - }) + messageBuffer: '', + }, + }); - dispatch(UIActions.fadeIn) -} + dispatch(UIActions.fadeIn); +}; -export const resetSelected = (dispatch) => { +export const resetSelected = dispatch => { dispatch({ - type: Symbol.for('rp: reset selected') - }) -} + type: Symbol.for('rp: reset selected'), + }); +}; export const submitSelected = serverId => async (dispatch, getState) => { - const { rolePicker } = getState() - const original = rolePicker.get('originalRolesSelected') - const current = rolePicker.get('rolesSelected') + const { rolePicker } = getState(); + const original = rolePicker.get('originalRolesSelected'); + const current = rolePicker.get('rolesSelected'); const diff = original.reduce((acc, v, k) => { if (current.get(k) !== v) { // if original value is false, then we know we're adding, otherwise removing. if (v !== true) { - return acc.set('added', acc.get('added').add(k)) + return acc.set('added', acc.get('added').add(k)); } else { - return acc.set('removed', acc.get('removed').add(k)) + return acc.set('removed', acc.get('removed').add(k)); } } - return acc - }, Map({ added: Set(), removed: Set() })) + return acc; + }, Map({ added: Set(), removed: Set() })); - await superagent.patch(`/api/servers/${serverId}/roles`).send(diff.toJS()) + await superagent.patch(`/api/servers/${serverId}/roles`).send(diff.toJS()); dispatch({ - type: Symbol.for('rp: sync selected roles') - }) -} + type: Symbol.for('rp: sync selected roles'), + }); +}; export const updateRoles = roles => ({ type: Symbol.for('rp: update selected roles'), - data: roles -}) + data: roles, +}); export const openMessageEditor = id => (dispatch, getState) => { - const message = getState().servers.getIn([id, 'message']) - dispatch(editServerMessage(id, message)) + const message = getState().servers.getIn([id, 'message']); + dispatch(editServerMessage(id, message)); dispatch({ type: Symbol.for('rp: set message editor state'), - data: true - }) -} + data: true, + }); +}; export const saveServerMessage = id => async (dispatch, getState) => { - const message = getState().rolePicker.get('messageBuffer') + const message = getState().rolePicker.get('messageBuffer'); - await superagent.patch(`/api/server/${id}`).send({ message }) + await superagent.patch(`/api/server/${id}`).send({ message }); - dispatch(closeMessageEditor) + dispatch(closeMessageEditor); dispatch({ type: Symbol.for('server: edit message'), data: { id, - message - } - }) -} + message, + }, + }); +}; export const editServerMessage = (id, message) => ({ type: Symbol.for('rp: edit message buffer'), - data: message -}) + data: message, +}); -export const closeMessageEditor = ({ +export const closeMessageEditor = { type: Symbol.for('rp: set message editor state'), - data: false -}) + data: false, +}; diff --git a/UI/src/components/role-picker/index.js b/UI/src/components/role-picker/index.js index 3e105d4..22852ff 100644 --- a/UI/src/components/role-picker/index.js +++ b/UI/src/components/role-picker/index.js @@ -1,145 +1,214 @@ -import React, { Component, Fragment } from 'react' -import { connect } from 'react-redux' -import { Prompt } from 'react-router-dom' -import superagent from 'superagent' -import * as Actions from './actions' -import * as UIActions from '../../actions/ui' -import { msgToReal } from '../../utils' -import './RolePicker.sass' +import React, { Component, Fragment } from 'react'; +import { connect } from 'react-redux'; +import { Prompt } from 'react-router-dom'; +import superagent from 'superagent'; +import * as Actions from './actions'; +import * as UIActions from '../../actions/ui'; +import { msgToReal } from '../../utils'; +import './RolePicker.sass'; -import Category from './Category' +import Category from './Category'; import { Scrollbars } from 'react-custom-scrollbars'; import { Link } from 'react-router-dom'; const mapState = ({ rolePicker, servers }, ownProps) => { return { data: rolePicker, - server: servers.get(ownProps.match.params.server) - } -} + server: servers.get(ownProps.match.params.server), + }; +}; @connect(mapState) class RolePicker extends Component { - componentWillMount () { - const { dispatch, match: { params: { server } } } = this.props - dispatch(Actions.setup(server)) + componentWillMount() { + const { + dispatch, + match: { + params: { server }, + }, + } = this.props; + dispatch(Actions.setup(server)); } - componentWillReceiveProps (nextProps) { + componentWillReceiveProps(nextProps) { if (this.props.match.params.server !== nextProps.match.params.server) { - const { dispatch } = this.props - dispatch(UIActions.fadeOut(() => dispatch(Actions.setup(nextProps.match.params.server)))) + const { dispatch } = this.props; + dispatch( + UIActions.fadeOut(() => dispatch(Actions.setup(nextProps.match.params.server))) + ); } } - get serverId () { - return this.props.server.get('id') + get serverId() { + return this.props.server.get('id'); } isSelected = id => { - return this.props.data.getIn([ 'rolesSelected', id ]) + return this.props.data.getIn(['rolesSelected', id]); + }; + + get rolesHaveChanged() { + const { data } = this.props; + return !data.get('rolesSelected').equals(data.get('originalRolesSelected')); } - get rolesHaveChanged () { - const { data } = this.props - return !data.get('rolesSelected').equals(data.get('originalRolesSelected')) - } + editServerMessage = e => { + const { dispatch } = this.props; + dispatch(Actions.editServerMessage(this.serverId, e.target.value)); + }; - editServerMessage = (e) => { - const { dispatch } = this.props - dispatch(Actions.editServerMessage(this.serverId, e.target.value)) - } - - saveServerMessage = (e) => { - const { dispatch } = this.props - dispatch(Actions.saveServerMessage(this.serverId)) - } + saveServerMessage = e => { + const { dispatch } = this.props; + dispatch(Actions.saveServerMessage(this.serverId)); + }; openMessageEditor = () => { - const { dispatch } = this.props - dispatch(Actions.openMessageEditor(this.serverId)) - } + const { dispatch } = this.props; + dispatch(Actions.openMessageEditor(this.serverId)); + }; closeMessageEditor = () => { - const { dispatch } = this.props - dispatch(Actions.closeMessageEditor) - } + const { dispatch } = this.props; + dispatch(Actions.closeMessageEditor); + }; - renderServerMessage (server) { - const isEditing = this.props.data.get('isEditingMessage') - const roleManager = server.getIn(['perms', 'canManageRoles']) - const msg = server.get('message') - const msgBuffer = this.props.data.get('messageBuffer') + renderServerMessage(server) { + const isEditing = this.props.data.get('isEditingMessage'); + const roleManager = server.getIn(['perms', 'canManageRoles']); + const msg = server.get('message'); + const msgBuffer = this.props.data.get('messageBuffer'); - console.log(msg, roleManager, isEditing, this.props.data.toJS()) + console.log(msg, roleManager, isEditing, this.props.data.toJS()); if (!roleManager && msg !== '') { - return
-

Server Message

-

-
+ return ( +
+

Server Message

+

+
+ ); } if (roleManager && !isEditing) { - return
-
-

Server Message

-
-
-

no server message'}}>

-
+ return ( +
+
+

Server Message

+
+
+

no server message', + }} + >

+
+ ); } if (roleManager && isEditing) { - return
-
-

Server Message

-
-
-
-