diff --git a/Server/.eslintrc.js b/Server/.eslintrc.js index a5b82de..23c1463 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..d6ad977 --- /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: false, +} diff --git a/Server/Roleypoly.js b/Server/Roleypoly.js index 9be93fe..5c7442c 100644 --- a/Server/Roleypoly.js +++ b/Server/Roleypoly.js @@ -4,13 +4,13 @@ const fetchModels = require('./models') const fetchApis = require('./api') class Roleypoly { - constructor (router, io, app) { + 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 @@ -21,12 +21,14 @@ class Roleypoly { this.__initialized = this._mountServices() } - async awaitServices () { + async awaitServices() { await this.__initialized } - async _mountServices () { - const sequelize = new Sequelize(process.env.DB_URL, { logging: log.sql.bind(log, log) }) + 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 @@ -46,7 +48,7 @@ class Roleypoly { this.ctx.P = new (require('./services/presentation'))(this.ctx) } - async mountRoutes () { + async mountRoutes() { fetchApis(this.router, this.ctx) this.__app.use(this.router.middleware()) } diff --git a/Server/api/auth.js b/Server/api/auth.js index 71e13f8..516d26e 100644 --- a/Server/api/auth.js +++ b/Server/api/auth.js @@ -1,5 +1,5 @@ module.exports = (R, $) => { - R.post('/api/auth/token', async (ctx) => { + R.post('/api/auth/token', async ctx => { const { token } = ctx.request.body if (token == null || token === '') { @@ -23,7 +23,7 @@ module.exports = (R, $) => { id: user.id, avatar: user.avatar, username: user.username, - discriminator: user.discriminator + discriminator: user.discriminator, } }) @@ -42,7 +42,7 @@ module.exports = (R, $) => { id: user.id, avatar: user.avatar, username: user.username, - discriminator: user.discriminator + discriminator: user.discriminator, } }) @@ -70,7 +70,6 @@ module.exports = (R, $) => { ctx.redirect(url) }) - R.get('/api/oauth/bot/callback', ctx => { console.log(ctx.request) }) diff --git a/Server/api/servers.js b/Server/api/servers.js index 3861c0a..8c2f511 100644 --- a/Server/api/servers.js +++ b/Server/api/servers.js @@ -1,5 +1,5 @@ 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) @@ -11,7 +11,7 @@ module.exports = (R, $) => { } }) - R.get('/api/server/:id', async (ctx) => { + R.get('/api/server/:id', async ctx => { const { userId } = ctx.session const { id } = ctx.params @@ -38,7 +38,7 @@ module.exports = (R, $) => { ctx.body = server }) - R.get('/api/server/:id/slug', async (ctx) => { + R.get('/api/server/:id/slug', async ctx => { const { userId } = ctx.session const { id } = ctx.params @@ -55,7 +55,7 @@ module.exports = (R, $) => { ctx.body = await $.P.serverSlug(srv) }) - R.patch('/api/server/:id', async (ctx) => { + R.patch('/api/server/:id', async ctx => { const { userId } = ctx.session const { id } = ctx.params @@ -75,8 +75,8 @@ module.exports = (R, $) => { // todo make less nasty await $.server.update(id, { - ...((message != null) ? { message } : {}), - ...((categories != null) ? { categories } : {}) + ...(message != null ? { message } : {}), + ...(categories != null ? { categories } : {}), }) ctx.body = { ok: true } @@ -88,7 +88,12 @@ module.exports = (R, $) => { 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 => { diff --git a/Server/index.js b/Server/index.js index 2268c7e..d56dd59 100644 --- a/Server/index.js +++ b/Server/index.js @@ -1,4 +1,4 @@ -require('dotenv').config({silent: true}) +require('dotenv').config({ silent: true }) const log = new (require('./logger'))('index') const http = require('http') @@ -12,7 +12,8 @@ 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) } @@ -20,9 +21,11 @@ Array.prototype.areduce = async function (predicate, acc = []) { // eslint-disab 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()) @@ -30,11 +33,11 @@ const io = _io(server, { transports: ['websocket'], path: '/api/socket.io' }) 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' -async function start () { +async function start() { await M.awaitServices() // body parser @@ -95,7 +98,6 @@ async function start () { // } catch (e) { // send(ctx, 'index.html', { root: pub }) // } - }) // const sendOpts = {root: pub, index: 'index.html'} // // const sendOpts = {} @@ -131,24 +133,33 @@ async function start () { ctx.body = ctx.body || e.stack } else { ctx.body = { - err: 'something terrible happened.' + err: 'something terrible happened.', } } } 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)) + app.use( + session( + { + key: 'roleypoly:sess', + maxAge: 'session', + siteOnly: true, + store: M.ctx.sessions, + genid: () => { + return ksuid.randomSync().string + }, + }, + app + ) + ) await M.mountRoutes() diff --git a/Server/logger.js b/Server/logger.js index 58513da..262a17f 100644 --- a/Server/logger.js +++ b/Server/logger.js @@ -5,12 +5,13 @@ const chalk = require('chalk') // const log = new (require('../logger'))('server/thing') class Logger { - constructor (name, debugOverride = false) { + constructor(name, debugOverride = false) { this.name = name - this.debugOn = (process.env.DEBUG === 'true' || process.env.DEBUG === '*') || debugOverride + this.debugOn = + process.env.DEBUG === 'true' || process.env.DEBUG === '*' || debugOverride } - fatal (text, ...data) { + fatal(text, ...data) { this.error(text, data) if (typeof data[data.length - 1] === 'number') { @@ -22,33 +23,33 @@ class Logger { throw text } - error (text, ...data) { + error(text, ...data) { console.error(chalk.red.bold(`ERR ${this.name}:`) + `\n ${text}`, data) } - warn (text, ...data) { + warn(text, ...data) { console.warn(chalk.yellow.bold(`WARN ${this.name}:`) + `\n ${text}`, data) } - notice (text, ...data) { + notice(text, ...data) { console.log(chalk.cyan.bold(`NOTICE ${this.name}:`) + `\n ${text}`, data) } - info (text, ...data) { + info(text, ...data) { console.info(chalk.blue.bold(`INFO ${this.name}:`) + `\n ${text}`, data) } - request (text, ...data) { + 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) } } - sql (logger, ...data) { + sql(logger, ...data) { if (logger.debugOn) { console.log(chalk.bold('DEBUG SQL:\n '), data) } diff --git a/Server/models/Server.js b/Server/models/Server.js index d93bd74..fd5f238 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..55c1b80 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..a492a77 100644 --- a/Server/models/index.js +++ b/Server/models/index.js @@ -3,12 +3,12 @@ const glob = require('glob') const path = require('path') const util = require('../util/model-methods') -module.exports = (sql) => { +module.exports = sql => { const models = {} const modelFiles = glob.sync('./models/**/!(index).js') log.debug('found models', modelFiles) - modelFiles.forEach((v) => { + modelFiles.forEach(v => { let name = path.basename(v).replace('.js', '') if (v === './models/index.js') { log.debug('index.js hit, skipped') @@ -24,7 +24,7 @@ module.exports = (sql) => { } }) - Object.keys(models).forEach((v) => { + Object.keys(models).forEach(v => { if (models[v].hasOwnProperty('__associations')) { models[v].__associations(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..1039a85 100644 --- a/Server/services/Service.js +++ b/Server/services/Service.js @@ -1,7 +1,7 @@ const Logger = require('../logger') class Service { - constructor (ctx) { + constructor(ctx) { this.ctx = ctx this.log = new Logger(this.constructor.name) } diff --git a/Server/services/discord-rpc.js b/Server/services/discord-rpc.js new file mode 100644 index 0000000..fb142d4 --- /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..d6ca528 100644 --- a/Server/services/discord.js +++ b/Server/services/discord.js @@ -3,7 +3,7 @@ const discord = require('discord.js') const superagent = require('superagent') class DiscordService extends Service { - constructor (ctx) { + constructor(ctx) { super(ctx) this.botToken = process.env.DISCORD_BOT_TOKEN @@ -13,7 +13,7 @@ class DiscordService extends Service { 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.rootUsers = new Set((process.env.ROOT_USERS || '').split(',')) this.client = new discord.Client() this.client.options.disableEveryone = true @@ -23,19 +23,29 @@ class DiscordService extends Service { this.startBot() } - ownGm (server) { + 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) } - async startBot () { + async startBot() { await this.client.login(this.botToken) // not all roleypolys are bots. @@ -50,52 +60,51 @@ class DiscordService extends Service { } } - 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) { + gm(serverId, userId) { return this.client.guilds.get(serverId).members.get(userId) } - getRoles (server) { + 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) { + 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) { + 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 } catch (e) { @@ -104,17 +113,14 @@ class DiscordService extends Service { } } - async getUser (authToken) { + async getUser(authToken) { const url = 'https://discordapp.com/api/v6/users/@me' try { if (authToken == null || authToken === '') { throw new Error('not logged in') } - const rsp = - await superagent - .get(url) - .set('Authorization', `Bearer ${authToken}`) + const rsp = await superagent.get(url).set('Authorization', `Bearer ${authToken}`) return rsp.body } catch (e) { this.log.error('getUser error', e) @@ -146,57 +152,69 @@ 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) { + 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 () { + 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) { + handler(message, matches, r) { r(matches[0]) - } + }, }, { regex: /set username (.*)/, - async handler (message, matches) { + 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)}` + `💮 **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 } - async handleCommand (message) { + 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) { @@ -218,8 +236,9 @@ class DiscordService extends Service { this.mentionResponse(message) } - handleMessage (message) { - if (message.author.bot && message.channel.type !== 'text') { // drop bot messages and dms + handleMessage(message) { + if (message.author.bot && message.channel.type !== 'text') { + // drop bot messages and dms return } @@ -232,10 +251,9 @@ class DiscordService extends Service { } } - async handleJoin (guild) { + async handleJoin(guild) { await this.ctx.server.ensure(guild) } - } module.exports = DiscordService diff --git a/Server/services/presentation.js b/Server/services/presentation.js index 0a45d6c..28d3390 100644 --- a/Server/services/presentation.js +++ b/Server/services/presentation.js @@ -2,7 +2,7 @@ const Service = require('./Service') const LRU = require('lru-cache') class PresentationService extends Service { - constructor (ctx) { + constructor(ctx) { super(ctx) this.M = ctx.M this.discord = ctx.discord @@ -10,16 +10,16 @@ class PresentationService extends Service { 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) { + async oldPresentableServers(collection, userId) { let servers = [] for (let server of collection.array()) { @@ -31,7 +31,7 @@ class PresentationService extends Service { 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 })) @@ -39,24 +39,29 @@ class PresentationService extends Service { }) } - async presentableServer (server, gm, { incRoles = true } = {}) { + 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,7 +69,7 @@ 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), })) } } diff --git a/Server/services/server.js b/Server/services/server.js index fef8284..cec9f8a 100644 --- a/Server/services/server.js +++ b/Server/services/server.js @@ -1,46 +1,44 @@ const Service = require('./Service') class ServerService extends Service { - constructor (ctx) { + constructor(ctx) { super(ctx) this.Server = ctx.M.Server this.P = ctx.P } - async ensure (server) { + async ensure(server) { let srv try { srv = await this.get(server.id) - } catch (e) { - - } + } catch (e) {} if (srv == null) { return this.create({ id: server.id, message: '', - categories: {} + categories: {}, }) } } - create ({ id, message, categories }) { + create({ id, message, categories }) { const srv = this.Server.build({ id, message, categories }) return srv.save() } - async update (id, newData) { + async update(id, newData) { const srv = await this.get(id, false) 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) { @@ -50,7 +48,7 @@ class ServerService extends Service { return s.get({ plain: true }) } - async getAllowedRoles (id) { + async getAllowedRoles(id) { const server = await this.get(id) return Object.values(server.categories).reduce((acc, c) => { diff --git a/Server/services/sessions.js b/Server/services/sessions.js index 5f924f1..e03d3bc 100644 --- a/Server/services/sessions.js +++ b/Server/services/sessions.js @@ -1,12 +1,12 @@ const Service = require('./Service') class SessionsService extends Service { - constructor (ctx) { + constructor(ctx) { super(ctx) this.Session = ctx.M.Session } - async get (id, {rolling}) { + async get(id, { rolling }) { const user = await this.Session.findOne({ where: { id } }) if (user === null) { @@ -16,7 +16,7 @@ class SessionsService extends Service { return user.data } - async set (id, data, {maxAge, rolling, changed}) { + async set(id, data, { maxAge, rolling, changed }) { let session = await this.Session.findOne({ where: { id } }) if (session === null) { session = this.Session.build({ id }) @@ -28,7 +28,7 @@ class SessionsService extends Service { return session.save() } - async destroy (id) { + async destroy(id) { const sess = await this.Session.findOne({ where: { id } }) if (sess != null) { diff --git a/Server/util/model-methods.js b/Server/util/model-methods.js index 6ce720e..9e52fa6 100644 --- a/Server/util/model-methods.js +++ b/Server/util/model-methods.js @@ -1,10 +1,10 @@ const ksuid = require('ksuid') module.exports = { - ksuid (field = 'id') { - return async function () { + 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" 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..d6ad977 --- /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: false, +} 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..8f3b809 100644 --- a/UI/src/App.js +++ b/UI/src/App.js @@ -19,11 +19,11 @@ window.__APP_STORE__ = store @DragDropContext(HTML5Backend) class App extends Component { - componentWillMount () { + componentWillMount() { store.dispatch(userInit) } - render () { + render() { return ( diff --git a/UI/src/App.test.js b/UI/src/App.test.js index b84af98..76d121e 100644 --- a/UI/src/App.test.js +++ b/UI/src/App.test.js @@ -1,8 +1,8 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; +import React from 'react' +import ReactDOM from 'react-dom' +import App from './App' it('renders without crashing', () => { - const div = document.createElement('div'); - ReactDOM.render(, div); -}); + const div = document.createElement('div') + ReactDOM.render(, div) +}) diff --git a/UI/src/actions/index.js b/UI/src/actions/index.js index 47dbe67..b9c331e 100644 --- a/UI/src/actions/index.js +++ b/UI/src/actions/index.js @@ -6,11 +6,11 @@ export const fetchServers = async dispatch => { dispatch({ type: Symbol.for('update servers'), - data: rsp.body + data: rsp.body, }) dispatch({ - type: Symbol.for('app ready') + type: Symbol.for('app ready'), }) } @@ -21,19 +21,19 @@ export const userInit = async dispatch => { dispatch({ type: Symbol.for('set user'), - data: rsp.body + 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'), }) } } @@ -41,11 +41,10 @@ export const userInit = async dispatch => { export const userLogout = async dispatch => { try { await superagent.post('/api/auth/logout') - } catch (e) { - } + } catch (e) {} dispatch({ - type: Symbol.for('reset user') + type: Symbol.for('reset user'), }) window.location.href = '/' @@ -58,7 +57,9 @@ export const startServerPolling = dispatch => { const poll = (dispatch, getState) => { const { servers } = getState() let stop = false - const stopPolling = () => { stop = true } + const stopPolling = () => { + stop = true + } const pollFunc = async () => { if (stop) { return @@ -76,7 +77,7 @@ const poll = (dispatch, getState) => { } else { const old = servers.keySeq().toSet() const upd = newServers.keySeq().toSet() - const newSrv = upd.subtract(old) + const newSrv = upd.subtract(old) stopPolling() dispatch(push(`/s/${newSrv.toJS()[0]}/edit`)) } diff --git a/UI/src/actions/ui.js b/UI/src/actions/ui.js index 2182bea..fd564f9 100644 --- a/UI/src/actions/ui.js +++ b/UI/src/actions/ui.js @@ -1,7 +1,7 @@ export const fadeOut = cb => dispatch => { dispatch({ type: Symbol.for('app fade'), - data: true + data: true, }) setTimeout(cb, 300) @@ -9,5 +9,5 @@ export const fadeOut = cb => dispatch => { 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..c529ae7 100644 --- a/UI/src/components/add-server/index.js +++ b/UI/src/components/add-server/index.js @@ -8,34 +8,57 @@ import discordLogo from '../../pages/images/discord-logo.svg' export default class AddServer extends Component { polling = null - - componentDidMount () { + + componentDidMount() { if (this.props.match.params.server !== undefined) { this.pollingStop = Actions.startServerPolling(this.props.dispatch) } } - componentWillUnmount () { + componentWillUnmount() { if (this.pollingStop != null) { 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. 🌈 💖 +
+
+ +
+
+
- -
+ ) } } diff --git a/UI/src/components/demos/roleypoly.js b/UI/src/components/demos/roleypoly.js index a58f58a..c1de9b6 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' -const RoleypolyDemo = () =>
- - - - - -
+const RoleypolyDemo = () => ( +
+ + + + + +
+) export default RoleypolyDemo diff --git a/UI/src/components/demos/typing.js b/UI/src/components/demos/typing.js index d8778ab..130bd30 100644 --- a/UI/src/components/demos/typing.js +++ b/UI/src/components/demos/typing.js @@ -3,27 +3,29 @@ 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 diff --git a/UI/src/components/dev-tools/index.js b/UI/src/components/dev-tools/index.js index a7d540a..d5f2da2 100644 --- a/UI/src/components/dev-tools/index.js +++ b/UI/src/components/dev-tools/index.js @@ -4,8 +4,7 @@ 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..c906b16 100644 --- a/UI/src/components/logotype/index.js +++ b/UI/src/components/logotype/index.js @@ -1,16 +1,42 @@ 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, +}) => ( + - + - + - + ) diff --git a/UI/src/components/oauth-bot-flow/index.js b/UI/src/components/oauth-bot-flow/index.js index 7786b01..b62d451 100644 --- a/UI/src/components/oauth-bot-flow/index.js +++ b/UI/src/components/oauth-bot-flow/index.js @@ -10,17 +10,25 @@ 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=✔️') + 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. + + ) } } diff --git a/UI/src/components/oauth-callback/index.js b/UI/src/components/oauth-callback/index.js index e895f5f..a1ee984 100644 --- a/UI/src/components/oauth-callback/index.js +++ b/UI/src/components/oauth-callback/index.js @@ -9,25 +9,25 @@ class OauthCallback extends Component { state = { notReady: true, message: 'chotto matte kudasai...', - redirect: '/s' + redirect: '/s', } stopped = false - componentDidUnmount () { + 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') - + 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') @@ -36,7 +36,7 @@ class OauthCallback extends Component { } this.props.history.replace(this.props.location.pathname) - + let counter = 0 const retry = async () => { if (this.stopped) return @@ -44,7 +44,7 @@ class OauthCallback extends Component { const rsp = await superagent.get('/api/auth/user') this.props.dispatch({ type: Symbol.for('set user'), - data: rsp.body + data: rsp.body, }) this.props.dispatch(fetchServers) this.setState({ notReady: false }) @@ -53,7 +53,9 @@ class OauthCallback extends Component { if (counter > 10) { this.setState({ message: "i couldn't log you in. :c" }) } else { - setTimeout(() => { retry() }, 250) + setTimeout(() => { + retry() + }, 250) } } } @@ -62,21 +64,23 @@ class OauthCallback extends Component { try { 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 - } - + } // update user stuff here } - render () { - return (this.state.notReady) ? this.state.message : + render() { + return this.state.notReady ? ( + this.state.message + ) : ( + + ) } } diff --git a/UI/src/components/oauth-flow/index.js b/UI/src/components/oauth-flow/index.js index 60a9fd6..df212d5 100644 --- a/UI/src/components/oauth-flow/index.js +++ b/UI/src/components/oauth-flow/index.js @@ -5,42 +5,41 @@ import { connect } from 'react-redux' import uuidv4 from 'uuid/v4' import { fetchServers } from '../../actions' - @connect() class OauthCallback extends Component { state = { notReady: true, message: 'chotto matte kudasai...', redirect: '/s', - url: null + url: null, } - async fetchUser () { + 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 () { + setupUser() { const userUpdateTime = sessionStorage.getItem('user.update') || 0 - if (+userUpdateTime + (1000 * 60 * 10) > Date.now()) { + 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 () { + async componentDidMount() { const state = uuidv4() const oUrl = new URL(window.location.href) @@ -48,7 +47,10 @@ class OauthCallback extends Component { 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() @@ -56,7 +58,9 @@ class OauthCallback extends Component { this.props.dispatch(fetchServers) this.setState({ notReady: false }) } catch (e) { - const { body: { url } } = await superagent.get('/api/auth/redirect?url=✔️') + const { + body: { url }, + } = await superagent.get('/api/auth/redirect?url=✔️') const nUrl = new URL(url) nUrl.searchParams.set('state', state) @@ -65,8 +69,17 @@ class OauthCallback extends Component { } } - 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. + + + ) } } diff --git a/UI/src/components/role-editor/Category.js b/UI/src/components/role-editor/Category.js index c615685..4328a9e 100644 --- a/UI/src/components/role-editor/Category.js +++ b/UI/src/components/role-editor/Category.js @@ -4,40 +4,66 @@ import { DropTarget } from 'react-dnd' 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 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 diff --git a/UI/src/components/role-editor/CategoryEditor.js b/UI/src/components/role-editor/CategoryEditor.js index 40e292f..0bc88e9 100644 --- a/UI/src/components/role-editor/CategoryEditor.js +++ b/UI/src/components/role-editor/CategoryEditor.js @@ -1,8 +1,7 @@ import React, { Component } from 'react' export default class CategoryEditor extends Component { - - onKeyPress = (e) => { + onKeyPress = e => { const { onSave } = this.props switch (e.key) { @@ -12,75 +11,99 @@ export default class CategoryEditor extends Component { } } - 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..f8f59a1 100644 --- a/UI/src/components/role-editor/actions.js +++ b/UI/src/components/role-editor/actions.js @@ -16,20 +16,20 @@ export const constructView = id => async (dispatch, getState) => { data: { hasSafeRoles, viewMap, - originalSnapshot: viewMap - } + originalSnapshot: viewMap, + }, }) 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) { @@ -37,13 +37,13 @@ export const addRoleToCategory = (id, oldId, role, flip = true) => (dispatch) => } } -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) { @@ -57,12 +57,12 @@ 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 } @@ -71,17 +71,17 @@ export const saveCategory = (id, category) => (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) => { @@ -99,13 +99,13 @@ 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, }) } @@ -133,14 +133,14 @@ 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 vm = roleEditor.get('viewMap') const position = category.get('position') const nextPos = position + move @@ -152,8 +152,8 @@ export const bumpCategory = (category, name) => move => async (dispatch, getStat data: { id: name, key: 'position', - value: nextPos - } + value: nextPos, + }, }) if (!!replaceThisOne) { @@ -162,25 +162,34 @@ 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 } - 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() }) diff --git a/UI/src/components/role-editor/index.js b/UI/src/components/role-editor/index.js index d6d0e0d..1d09677 100644 --- a/UI/src/components/role-editor/index.js +++ b/UI/src/components/role-editor/index.js @@ -16,38 +16,51 @@ 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 + 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)))) + dispatch( + UIActions.fadeOut(() => + dispatch(Actions.constructView(nextProps.match.params.server)) + ) + ) } } - dropRole = (category, name) => ({role, category}) => { + dropRole = (category, name) => ({ role, category }) => { const { dispatch } = this.props console.log(role) dispatch(Actions.addRoleToCategory(name, category, role)) @@ -55,17 +68,17 @@ class RoleEditor extends Component { createCategory = () => { const { dispatch } = this.props - dispatch(Actions.createCategory) + dispatch(Actions.createCategory) } saveCategory = (category, name) => () => { const { dispatch } = this.props - dispatch(Actions.saveCategory(name, category)) + dispatch(Actions.saveCategory(name, category)) } deleteCategory = (category, id) => () => { const { dispatch } = this.props - dispatch(Actions.deleteCategory(id, category)) + dispatch(Actions.deleteCategory(id, category)) } openEditor = (category, name) => () => { @@ -85,11 +98,11 @@ class RoleEditor extends Component { case Symbol.for('edit: bool'): value = event.target.checked break - + case Symbol.for('edit: select'): value = event.target.value break - + default: value = null } @@ -103,17 +116,26 @@ class RoleEditor extends Component { } saveServer = () => { - const { dispatch, match: { params: { server } } } = this.props + const { + dispatch, + match: { + params: { server }, + }, + } = this.props dispatch(Actions.saveServer(server)) } - onBump = (category, name) => (move) => () => this.props.dispatch(Actions.bumpCategory(category, name)(move)) + 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() + get hasChanged() { + return ( + this.props.editor.get('originalSnapshot').hashCode() !== + this.props.editor.get('viewMap').hashCode() + ) } - render () { + render() { const { server } = this.props if (server == null) { @@ -125,72 +147,91 @@ class RoleEditor extends Component { } 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() - } -
- + 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} +
+
+
+ )} +
-
+ ) } } diff --git a/UI/src/components/role-picker/Category.js b/UI/src/components/role-picker/Category.js index 0bcf855..62d9ce6 100644 --- a/UI/src/components/role-picker/Category.js +++ b/UI/src/components/role-picker/Category.js @@ -4,28 +4,34 @@ import { Map } from 'immutable' import Role from '../role' class Category extends Component { - - toggleRoleMulti (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') switch (type) { - case 'single': return this.toggleRoleSingle(id, next) - case 'multi': return this.toggleRoleMulti(id, next) - default: + 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 () { + render() { const { category, name, isSelected } = this.props if (category.get('hidden')) { return null @@ -35,19 +41,28 @@ class Category extends Component { 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 diff --git a/UI/src/components/role-picker/actions.js b/UI/src/components/role-picker/actions.js index 2b59857..13d6c91 100644 --- a/UI/src/components/role-picker/actions.js +++ b/UI/src/components/role-picker/actions.js @@ -10,8 +10,8 @@ export const setup = id => async dispatch => { type: Symbol.for('server: set'), data: { id, - ...data - } + ...data, + }, }) dispatch(constructView(id)) } @@ -21,48 +21,62 @@ export const getViewMap = server => { 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 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 return { viewMap, selected, - hasSafeRoles + hasSafeRoles, } } @@ -79,16 +93,16 @@ export const constructView = id => (dispatch, getState) => { originalRolesSelected: selected, hidden: false, isEditingMessage: false, - messageBuffer: '' - } + messageBuffer: '', + }, }) dispatch(UIActions.fadeIn) } -export const resetSelected = (dispatch) => { +export const resetSelected = dispatch => { dispatch({ - type: Symbol.for('rp: reset selected') + type: Symbol.for('rp: reset selected'), }) } @@ -113,13 +127,13 @@ export const submitSelected = serverId => async (dispatch, getState) => { 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) => { @@ -127,7 +141,7 @@ export const openMessageEditor = id => (dispatch, getState) => { dispatch(editServerMessage(id, message)) dispatch({ type: Symbol.for('rp: set message editor state'), - data: true + data: true, }) } @@ -141,17 +155,17 @@ export const saveServerMessage = id => async (dispatch, getState) => { 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..2c91607 100644 --- a/UI/src/components/role-picker/index.js +++ b/UI/src/components/role-picker/index.js @@ -8,49 +8,56 @@ import { msgToReal } from '../../utils' import './RolePicker.sass' import Category from './Category' -import { Scrollbars } from 'react-custom-scrollbars'; -import { Link } from 'react-router-dom'; +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 + 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)))) + dispatch( + UIActions.fadeOut(() => dispatch(Actions.setup(nextProps.match.params.server))) + ) } } - get serverId () { + 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 () { + get rolesHaveChanged() { const { data } = this.props return !data.get('rolesSelected').equals(data.get('originalRolesSelected')) } - editServerMessage = (e) => { + editServerMessage = e => { const { dispatch } = this.props dispatch(Actions.editServerMessage(this.serverId, e.target.value)) } - saveServerMessage = (e) => { + saveServerMessage = e => { const { dispatch } = this.props dispatch(Actions.saveServerMessage(this.serverId)) } @@ -62,10 +69,10 @@ class RolePicker extends Component { closeMessageEditor = () => { const { dispatch } = this.props - dispatch(Actions.closeMessageEditor) + dispatch(Actions.closeMessageEditor) } - renderServerMessage (server) { + renderServerMessage(server) { const isEditing = this.props.data.get('isEditingMessage') const roleManager = server.getIn(['perms', 'canManageRoles']) const msg = server.get('message') @@ -74,71 +81,133 @@ class RolePicker extends Component { 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

-
-
-
-