mirror of
https://github.com/roleypoly/roleypoly-v1.git
synced 2025-04-24 19:59:12 +00:00
feat(rolepicker): report on status passed up from discord service
This commit is contained in:
parent
690671317f
commit
0c321e8b1e
9 changed files with 116 additions and 43 deletions
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
}
|
|
@ -134,18 +134,27 @@ module.exports = (R, $) => {
|
||||||
// last, add new roles
|
// last, add new roles
|
||||||
newRoles = [...newRoles, ...sanitizedAdded]
|
newRoles = [...newRoles, ...sanitizedAdded]
|
||||||
|
|
||||||
if (!arrayMatches(currentRoles, newRoles)) {
|
let status = null
|
||||||
if (process.env.FF_TransactionalRoles !== '0') {
|
|
||||||
await $.discord.updateRolesTx(gm, {
|
try {
|
||||||
added: sanitizedAdded,
|
if (!arrayMatches(currentRoles, newRoles)) {
|
||||||
removed: sanitizedRemoved,
|
if (process.env.FF_TransactionalRoles !== '0') {
|
||||||
})
|
const ret = await $.discord.updateRolesTx(gm, {
|
||||||
} else {
|
added: sanitizedAdded,
|
||||||
await $.discord.updateRoles(gm, newRoles)
|
removed: sanitizedRemoved,
|
||||||
|
})
|
||||||
|
status = ret.toObject().status
|
||||||
|
} else {
|
||||||
|
await $.discord.updateRoles(gm, newRoles)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ctx.status = 400
|
||||||
|
ctx.body = { ok: false }
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = { ok: true }
|
ctx.body = { ok: true, status }
|
||||||
|
|
||||||
$.P.invalidate(userId)
|
$.P.invalidate(userId)
|
||||||
$.discord.invalidate(userId)
|
$.discord.invalidate(userId)
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
const Service = require('./Service')
|
const Service = require('./Service')
|
||||||
const superagent = require('superagent')
|
const superagent = require('superagent')
|
||||||
const { DiscordClient, Member, RoleTransaction, TxDelta } = require('@roleypoly/rpc/discord')
|
const {
|
||||||
|
DiscordClient,
|
||||||
|
Member,
|
||||||
|
RoleTransaction,
|
||||||
|
TxDelta,
|
||||||
|
} = require('@roleypoly/rpc/discord')
|
||||||
const { IDQuery, DiscordUser } = require('@roleypoly/rpc/shared')
|
const { IDQuery, DiscordUser } = require('@roleypoly/rpc/shared')
|
||||||
const { Empty } = require('google-protobuf/google/protobuf/empty_pb')
|
const { Empty } = require('google-protobuf/google/protobuf/empty_pb')
|
||||||
const { NodeHttpTransport } = require('@improbable-eng/grpc-web-node-http-transport')
|
const { NodeHttpTransport } = require('@improbable-eng/grpc-web-node-http-transport')
|
||||||
|
@ -158,7 +163,7 @@ class DiscordService extends Service {
|
||||||
async updateRolesTx(memberObj, { added, removed }) {
|
async updateRolesTx(memberObj, { added, removed }) {
|
||||||
const roleTx = new RoleTransaction()
|
const roleTx = new RoleTransaction()
|
||||||
roleTx.setMember(this.memberToQueryProto(memberObj))
|
roleTx.setMember(this.memberToQueryProto(memberObj))
|
||||||
|
|
||||||
for (let toAdd of added) {
|
for (let toAdd of added) {
|
||||||
const delta = new TxDelta()
|
const delta = new TxDelta()
|
||||||
delta.setAction(TxDelta.Action.ADD)
|
delta.setAction(TxDelta.Action.ADD)
|
||||||
|
@ -173,7 +178,7 @@ class DiscordService extends Service {
|
||||||
roleTx.addDelta(delta)
|
roleTx.addDelta(delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.rpc.updateMemberRoles(roleTx, this.sharedHeaders)
|
return this.rpc.updateMemberRoles(roleTx, this.sharedHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
memberToQueryProto(member) {
|
memberToQueryProto(member) {
|
||||||
|
|
5
UI/package-lock.json
generated
5
UI/package-lock.json
generated
|
@ -15935,6 +15935,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||||
},
|
},
|
||||||
|
"uikit": {
|
||||||
|
"version": "3.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/uikit/-/uikit-3.4.4.tgz",
|
||||||
|
"integrity": "sha512-VTDqtTUTH2XiGw0MudWki4oPl+Bxt3JEm5S/CE1B9zUmt9/FqKRYxv/7s5GSeNMnRMSQa2evQTkLSGsdBmKXdA=="
|
||||||
|
},
|
||||||
"unicode-canonical-property-names-ecmascript": {
|
"unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"superagent": "^5.2.2",
|
"superagent": "^5.2.2",
|
||||||
|
"uikit": "^3.4.4",
|
||||||
"uuid": "^3.3.3"
|
"uuid": "^3.3.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -1,16 +1,34 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" type="image/png" sizes="any" href="%PUBLIC_URL%/favicon.png"/>
|
<link rel="icon" type="image/png" sizes="any" href="%PUBLIC_URL%/favicon.png" />
|
||||||
<title>Roleypoly</title>
|
<title>Roleypoly</title>
|
||||||
<script src="https://use.typekit.net/bck0pci.js"></script>
|
<script src="https://use.typekit.net/bck0pci.js"></script>
|
||||||
<script>try{Typekit.load({ async: true });}catch(e){}</script>
|
<script>
|
||||||
<link rel="stylesheet" defer href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-rc.24/css/uikit.min.css" />
|
try {
|
||||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-rc.24/js/uikit.min.js"></script>
|
Typekit.load({ async: true })
|
||||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-rc.24/js/uikit-icons.min.js"></script>
|
} catch (e) {}
|
||||||
<style>body{ background-color: #453F3E; }</style>
|
</script>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
defer
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.4.4/css/uikit.min.css"
|
||||||
|
/>
|
||||||
|
<script
|
||||||
|
defer
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.4.4/js/uikit.min.js"
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
defer
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.4.4/js/uikit-icons.min.js"
|
||||||
|
></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #453f3e;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<noscript>
|
<noscript>
|
||||||
You need to enable JavaScript to run this app.
|
You need to enable JavaScript to run this app.
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { Map, Set, fromJS } from 'immutable'
|
import { Map, Set, fromJS } from 'immutable'
|
||||||
import superagent from 'superagent'
|
import superagent from 'superagent'
|
||||||
import * as UIActions from '../../actions/ui'
|
import * as UIActions from '../../actions/ui'
|
||||||
|
import uikit from 'uikit'
|
||||||
|
|
||||||
export const setup = id => async dispatch => {
|
export const setup = (id) => async (dispatch) => {
|
||||||
const rsp = await superagent.get(`/api/server/${id}`)
|
const rsp = await superagent.get(`/api/server/${id}`)
|
||||||
const data = rsp.body
|
const data = rsp.body
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ export const setup = id => async dispatch => {
|
||||||
dispatch(constructView(id))
|
dispatch(constructView(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getViewMap = server => {
|
export const getViewMap = (server) => {
|
||||||
const gmRoles = server.get('gm').get('rolesList')
|
const gmRoles = server.get('gm').get('rolesList')
|
||||||
const roles = server.get('roles')
|
const roles = server.get('roles')
|
||||||
const categories = server.get('categories')
|
const categories = server.get('categories')
|
||||||
|
@ -24,11 +25,11 @@ export const getViewMap = server => {
|
||||||
|
|
||||||
const allRoles = server
|
const allRoles = server
|
||||||
.get('roles')
|
.get('roles')
|
||||||
.filter(v => v.get('safety') === 0)
|
.filter((v) => v.get('safety') === 0)
|
||||||
.map(r => r.get('id'))
|
.map((r) => r.get('id'))
|
||||||
.toSet()
|
.toSet()
|
||||||
const accountedRoles = categories
|
const accountedRoles = categories
|
||||||
.map(c => c.get('roles'))
|
.map((c) => c.get('roles'))
|
||||||
.toSet()
|
.toSet()
|
||||||
.flatten()
|
.flatten()
|
||||||
const unaccountedRoles = allRoles.subtract(accountedRoles)
|
const unaccountedRoles = allRoles.subtract(accountedRoles)
|
||||||
|
@ -48,17 +49,17 @@ export const getViewMap = server => {
|
||||||
'position',
|
'position',
|
||||||
cat.get(
|
cat.get(
|
||||||
'position',
|
'position',
|
||||||
categoriesIds.findIndex(v => v === idx)
|
categoriesIds.findIndex((v) => v === idx)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// .sortBy(cat => cat.get('position'))
|
// .sortBy(cat => cat.get('position'))
|
||||||
.map(c => {
|
.map((c) => {
|
||||||
const roles = c
|
const roles = c
|
||||||
.get('roles')
|
.get('roles')
|
||||||
// fill in roles_map
|
// fill in roles_map
|
||||||
.map(r => server.get('roles').find(sr => sr.get('id') === r))
|
.map((r) => server.get('roles').find((sr) => sr.get('id') === r))
|
||||||
.filter(r => r != null)
|
.filter((r) => r != null)
|
||||||
// sort by server position, backwards.
|
// sort by server position, backwards.
|
||||||
.sort((a, b) => a.position > b.position)
|
.sort((a, b) => a.position > b.position)
|
||||||
// force data to sets
|
// force data to sets
|
||||||
|
@ -79,7 +80,7 @@ export const getViewMap = server => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const constructView = id => (dispatch, getState) => {
|
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)
|
||||||
|
@ -99,13 +100,17 @@ export const constructView = id => (dispatch, getState) => {
|
||||||
dispatch(UIActions.fadeIn)
|
dispatch(UIActions.fadeIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const resetSelected = dispatch => {
|
export const resetSelected = (dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: Symbol.for('rp: reset selected'),
|
type: Symbol.for('rp: reset selected'),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const submitSelected = serverId => async (dispatch, getState) => {
|
export const submitSelected = (serverId) => async (dispatch, getState) => {
|
||||||
|
dispatch({
|
||||||
|
type: Symbol.for('rp: lock'),
|
||||||
|
})
|
||||||
|
|
||||||
const { rolePicker } = getState()
|
const { rolePicker } = getState()
|
||||||
const original = rolePicker.get('originalRolesSelected')
|
const original = rolePicker.get('originalRolesSelected')
|
||||||
const current = rolePicker.get('rolesSelected')
|
const current = rolePicker.get('rolesSelected')
|
||||||
|
@ -123,19 +128,39 @@ export const submitSelected = serverId => async (dispatch, getState) => {
|
||||||
return acc
|
return acc
|
||||||
}, Map({ added: Set(), removed: Set() }))
|
}, Map({ added: Set(), removed: Set() }))
|
||||||
|
|
||||||
await superagent.patch(`/api/servers/${serverId}/roles`).send(diff.toJS())
|
const response = await superagent
|
||||||
|
.patch(`/api/servers/${serverId}/roles`)
|
||||||
|
.send(diff.toJS())
|
||||||
|
|
||||||
|
if (!response.ok || !response.body.ok) {
|
||||||
|
uikit.notification('Failed to save roles. Please contact us.', {
|
||||||
|
pos: 'top-center',
|
||||||
|
status: 'danger',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uikit.notification(
|
||||||
|
response.body.status === 1
|
||||||
|
? 'This Discord server is active.<br/>Your role updates will take a moment.'
|
||||||
|
: 'Roles saved!',
|
||||||
|
{ pos: 'top-center', status: 'primary' }
|
||||||
|
)
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: Symbol.for('rp: unlock'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: Symbol.for('rp: sync selected roles'),
|
type: Symbol.for('rp: sync selected roles'),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateRoles = roles => ({
|
export const updateRoles = (roles) => ({
|
||||||
type: Symbol.for('rp: update selected roles'),
|
type: Symbol.for('rp: update selected roles'),
|
||||||
data: roles,
|
data: roles,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const openMessageEditor = id => (dispatch, getState) => {
|
export const openMessageEditor = (id) => (dispatch, getState) => {
|
||||||
const message = getState().servers.getIn([id, 'message'])
|
const message = getState().servers.getIn([id, 'message'])
|
||||||
dispatch(editServerMessage(id, message))
|
dispatch(editServerMessage(id, message))
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -144,7 +169,7 @@ export const openMessageEditor = id => (dispatch, getState) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const saveServerMessage = id => async (dispatch, getState) => {
|
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 })
|
||||||
|
|
|
@ -38,7 +38,7 @@ class RolePicker extends Component {
|
||||||
return this.props.server.get('id')
|
return this.props.server.get('id')
|
||||||
}
|
}
|
||||||
|
|
||||||
isSelected = id => {
|
isSelected = (id) => {
|
||||||
return this.props.data.getIn(['rolesSelected', id])
|
return this.props.data.getIn(['rolesSelected', id])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +47,12 @@ class RolePicker extends Component {
|
||||||
return !data.get('rolesSelected').equals(data.get('originalRolesSelected'))
|
return !data.get('rolesSelected').equals(data.get('originalRolesSelected'))
|
||||||
}
|
}
|
||||||
|
|
||||||
editServerMessage = e => {
|
editServerMessage = (e) => {
|
||||||
const { dispatch } = this.props
|
const { dispatch } = this.props
|
||||||
dispatch(Actions.editServerMessage(this.serverId, e.target.value))
|
dispatch(Actions.editServerMessage(this.serverId, e.target.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
saveServerMessage = e => {
|
saveServerMessage = (e) => {
|
||||||
const { dispatch } = this.props
|
const { dispatch } = this.props
|
||||||
dispatch(Actions.saveServerMessage(this.serverId))
|
dispatch(Actions.saveServerMessage(this.serverId))
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,7 @@ class RolePicker extends Component {
|
||||||
Reset
|
Reset
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
disabled={!this.rolesHaveChanged}
|
disabled={!this.rolesHaveChanged || data.get('lockedInteractions')}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
dispatch(Actions.submitSelected(this.props.match.params.server))
|
dispatch(Actions.submitSelected(this.props.match.params.server))
|
||||||
}
|
}
|
||||||
|
@ -186,14 +186,14 @@ class RolePicker extends Component {
|
||||||
</div>
|
</div>
|
||||||
<div className="role-picker__categories">
|
<div className="role-picker__categories">
|
||||||
{vm
|
{vm
|
||||||
.sortBy(v => v.get('position'))
|
.sortBy((v) => v.get('position'))
|
||||||
.map((c, name) => (
|
.map((c, name) => (
|
||||||
<Category
|
<Category
|
||||||
key={name}
|
key={name}
|
||||||
name={name}
|
name={name}
|
||||||
category={c}
|
category={c}
|
||||||
isSelected={this.isSelected}
|
isSelected={this.isSelected}
|
||||||
onChange={roles => dispatch(Actions.updateRoles(roles))}
|
onChange={(roles) => dispatch(Actions.updateRoles(roles))}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
.toArray()}
|
.toArray()}
|
||||||
|
|
|
@ -8,6 +8,7 @@ const initialState = Map({
|
||||||
viewMap: OrderedMap({}), // roles in categories
|
viewMap: OrderedMap({}), // roles in categories
|
||||||
originalRolesSelected: Map({}), // Map<role id, bool> -- original roles for diffing against selected
|
originalRolesSelected: Map({}), // Map<role id, bool> -- original roles for diffing against selected
|
||||||
rolesSelected: Map({}), // Map<role id, bool> -- new roles for diffing
|
rolesSelected: Map({}), // Map<role id, bool> -- new roles for diffing
|
||||||
|
lockedInterations: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
export default (state = initialState, { type, data }) => {
|
export default (state = initialState, { type, data }) => {
|
||||||
|
@ -35,6 +36,12 @@ export default (state = initialState, { type, data }) => {
|
||||||
|
|
||||||
case Symbol.for('rp: edit message buffer'):
|
case Symbol.for('rp: edit message buffer'):
|
||||||
return state.set('messageBuffer', data)
|
return state.set('messageBuffer', data)
|
||||||
|
|
||||||
|
case Symbol.for('rp: lock'):
|
||||||
|
return state.set('lockedInteractions', true)
|
||||||
|
|
||||||
|
case Symbol.for('rp: unlock'):
|
||||||
|
return state.set('lockedInteractions', false)
|
||||||
// case Symbol.for('rp: zero role picker'):
|
// case Symbol.for('rp: zero role picker'):
|
||||||
// return initialState
|
// return initialState
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue