+
+
{ category.get('name') }
+
+
{
category.get('roles_map')
.sortBy(r => r.get('position'))
diff --git a/UI/src/components/role-editor/CategoryEditor.js b/UI/src/components/role-editor/CategoryEditor.js
index ba58882..9847b41 100644
--- a/UI/src/components/role-editor/CategoryEditor.js
+++ b/UI/src/components/role-editor/CategoryEditor.js
@@ -1,18 +1,28 @@
import React, { Component } from 'react'
export default class CategoryEditor extends Component {
+
+ onKeyPress = (e) => {
+ const { onSave } = this.props
+
+ switch (e.key) {
+ case 'Enter':
+ case 'Escape':
+ return onSave()
+ }
+ }
+
render () {
const {
- name,
category
} = this.props
- return
}
}
diff --git a/UI/src/components/role-editor/RoleEditor.sass b/UI/src/components/role-editor/RoleEditor.sass
index 0b577c8..74a5b85 100644
--- a/UI/src/components/role-editor/RoleEditor.sass
+++ b/UI/src/components/role-editor/RoleEditor.sass
@@ -7,7 +7,7 @@
&__actions
display: flex
- margin-top: 10px
+ margin: 10px 0
button
padding: 0
@@ -19,12 +19,16 @@
&_save
flex: 4
+ &__uncat-zone
+ padding: 10px
+
.role-editor__category
box-sizing: border-box
background-color: var(--c-1)
padding: 15px
margin: 10px
min-width: 220px - 30px
+ position: relative
&.add-button
height: 100px
@@ -43,4 +47,44 @@
transform: scale(1.1)
color: var(--c-7)
-
\ No newline at end of file
+ &-header
+ display: flex
+ align-items: center
+ justify-content: left
+ margin-bottom: 10px
+
+ color: var(--c-7)
+
+ svg
+ transition: transform 0.05s ease-in-out
+ transform: translateZ(0)
+ &:hover
+ transform: translateZ(0) scale(1.1)
+ color: var(--c-9)
+
+ h4
+ margin: 0
+ flex: 1
+ margin-right: 10px
+
+ .drop-zone
+ position: relative
+ &::after
+ content: ""
+ background-color: var(--c-7)
+ box-sizing: border-box
+ position: absolute
+ top: 0
+ right: 0
+ left: 0
+ bottom: 0
+ border: 5px dashed var(--c-3)
+ transition: opacity 0.15s ease-in-out
+ opacity: 0
+ pointer-events: none
+
+ &.is-over::after
+ opacity: 0.5
+
+ &.can-drop::after
+ opacity: 0.2
\ No newline at end of file
diff --git a/UI/src/components/role-editor/actions.js b/UI/src/components/role-editor/actions.js
index 26106fd..3a5deb0 100644
--- a/UI/src/components/role-editor/actions.js
+++ b/UI/src/components/role-editor/actions.js
@@ -1,6 +1,8 @@
import { Set } from 'immutable'
import * as UIActions from '../../actions/ui'
import { getViewMap } from '../role-picker/actions'
+import ksuid from 'ksuid'
+import superagent from 'superagent'
export const constructView = id => (dispatch, getState) => {
const server = getState().servers.get(id)
@@ -11,78 +13,133 @@ export const constructView = id => (dispatch, getState) => {
dispatch({
type: Symbol.for('re: setup'),
data: {
- viewMap: viewMap
+ viewMap: viewMap,
+ originalSnapshot: viewMap
}
})
dispatch(UIActions.fadeIn)
}
-export const addRoleToCategory = (name, oldName, role, flip = true) => (dispatch) => {
+export const addRoleToCategory = (id, oldId, role, flip = true) => (dispatch) => {
dispatch({
type: Symbol.for('re: add role to category'),
data: {
- name,
+ id,
role
}
})
if (flip) {
- dispatch(removeRoleFromCategory(oldName, name, role, false))
+ dispatch(removeRoleFromCategory(oldId, id, role, false))
}
}
-export const removeRoleFromCategory = (name, oldName, role, flip = true) => (dispatch) => {
+export const removeRoleFromCategory = (id, oldId, role, flip = true) => (dispatch) => {
dispatch({
type: Symbol.for('re: remove role from category'),
data: {
- name,
+ id,
role
}
})
-
+
if (flip) {
- dispatch(addRoleToCategory(oldName, name, role, false))
+ dispatch(addRoleToCategory(oldId, id, role, false))
}
}
+export const editCategory = ({ id, key, value }) => dispatch => {
+ dispatch({
+ type: Symbol.for('re: edit category'),
+ data: {
+ id,
+ key,
+ value
+ }
+ })
+}
-export const editCategory = (stuff) => dispatch => {
+export const saveCategory = (id, category) => (dispatch) => {
+ if (category.get('name') === '') {
+ return
+ }
+
+ dispatch({
+ type: Symbol.for('re: switch category mode'),
+ data: {
+ id,
+ mode: Symbol.for('drop')
+ }
+ })
}
-export const saveCategory = (name) => ({
+export const openEditor = (id) => ({
type: Symbol.for('re: switch category mode'),
data: {
- name,
- mode: Symbol.for('drop')
+ id,
+ mode: Symbol.for('edit')
}
})
-export const deleteCategory = (name) => ({
- type: Symbol.for('re: delete category'),
- data: name
-})
+export const deleteCategory = (id, category) => (dispatch, getState) => {
+ const roles = category.get('roles')
+ const rolesMap = category.get('roles_map')
-export const createCategory = (dispatch, getState) => {
+ let uncategorized = getState().roleEditor.getIn(['viewMap', 'Uncategorized'])
+
+ dispatch({
+ type: Symbol.for('re: set category'),
+ data: {
+ id: 'Uncategorized',
+ name: '',
+ roles: uncategorized.get('roles').union(roles),
+ roles_map: uncategorized.get('roles_map').union(rolesMap),
+ hidden: true,
+ type: 'multi',
+ mode: null
+ }
+ })
+
+ dispatch({
+ type: Symbol.for('re: delete category'),
+ data: id
+ })
+}
+
+export const createCategory = async (dispatch, getState) => {
const { roleEditor } = getState()
const vm = roleEditor.get('viewMap')
let name = 'New Category'
let idx = 1
- while (vm.has(name)) {
+ while (vm.find(c => c.get('name') === name) !== undefined) {
idx++
name = `New Category ${idx}`
}
+ const id = (await ksuid.random()).string
+
dispatch({
type: Symbol.for('re: set category'),
data: {
+ id,
name,
roles: Set([]),
roles_map: Set([]),
hidden: true,
+ type: 'multi',
mode: Symbol.for('edit')
}
})
}
+
+export const saveServer = id => async (dispatch, getState) => {
+ const viewMap = getState().roleEditor.get('viewMap')
+ .filterNot((_, k) => k === 'Uncategorized')
+ .map(v => v.delete('roles_map').delete('mode').delete('id'))
+
+ 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 3f98c2f..9ad6133 100644
--- a/UI/src/components/role-editor/index.js
+++ b/UI/src/components/role-editor/index.js
@@ -22,6 +22,9 @@ const mapState = ({ rolePicker, roleEditor, servers }, ownProps) => ({
@DropTarget(Symbol.for('dnd: role'), {
drop (props, monitor, element) {
element.dropRole({}, 'Uncategorized')(monitor.getItem())
+ },
+ canDrop (props, monitor) {
+ return (monitor.getItem().category !== 'Uncategorized')
}
}, (connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
@@ -56,15 +59,20 @@ class RoleEditor extends Component {
saveCategory = (category, name) => () => {
const { dispatch } = this.props
- dispatch(Actions.saveCategory(name))
+ dispatch(Actions.saveCategory(name, category))
}
- deleteCategory = (category, name) => () => {
+ deleteCategory = (category, id) => () => {
const { dispatch } = this.props
- dispatch(Actions.deleteCategory(name))
+ dispatch(Actions.deleteCategory(id, category))
}
- editCategory = (category, name) => (key, type) => event => {
+ openEditor = (category, name) => () => {
+ const { dispatch } = this.props
+ dispatch(Actions.openEditor(name))
+ }
+
+ editCategory = (category, id) => (key, type) => event => {
const { dispatch } = this.props
let value
@@ -77,16 +85,46 @@ class RoleEditor extends Component {
value = event.target.checked
break
+ case Symbol.for('edit: select'):
+ value = event.target.value
+ break
+
default:
value = null
}
- dispatch(Actions.editCategory({ category, name, key, type, value }))
+ dispatch(Actions.editCategory({ category, id, key, type, value }))
+ }
+
+ resetServer = () => {
+ const { dispatch } = this.props
+ dispatch({ type: Symbol.for('re: reset') })
+ }
+
+ saveServer = () => {
+ const { dispatch, match: { params: { server } } } = this.props
+ dispatch(Actions.saveServer(server))
+ }
+
+ get hasChanged () {
+ return this.props.editor.get('originalSnapshot').hashCode() !== this.props.editor.get('viewMap').hashCode()
}
render () {
const vm = this.props.editor.get('viewMap')
return
+
+
{this.props.server.getIn(['server','name'])}
+
+
+
+
+
+
@@ -100,7 +138,7 @@ class RoleEditor extends Component {
mode={c.get('mode')}
onDrop={this.dropRole(c, name)}
onEdit={this.editCategory(c, name)}
- // onEditOpen={this.openEditor(c, name)}
+ onEditOpen={this.openEditor(c, name)}
onSave={this.saveCategory(c, name)}
onDelete={this.deleteCategory(c, name)}
/>)
@@ -113,14 +151,18 @@ class RoleEditor extends Component {
{
this.props.connectDropTarget(
-
- {
- (vm.getIn(['Uncategorized', 'roles_map']) || Set())
- .sortBy(r => r.get('position'))
- .reverse()
- .map((r, k) =>
)
- .toArray()
- }
+
+
+
+ {
+ (vm.getIn(['Uncategorized', 'roles_map']) || Set())
+ .sortBy(r => r.get('position'))
+ .reverse()
+ .map((r, k) => )
+ .toArray()
+ }
+
+
)
}
diff --git a/UI/src/components/role-picker/Category.js b/UI/src/components/role-picker/Category.js
index 4d04969..0bcf855 100644
--- a/UI/src/components/role-picker/Category.js
+++ b/UI/src/components/role-picker/Category.js
@@ -17,9 +17,11 @@ class Category extends Component {
const type = this.props.category.get('type')
switch (type) {
- case 'multi': return this.toggleRoleMulti(id, next)
case 'single': return this.toggleRoleSingle(id, next)
- default: console.warn('NOT SURE')
+ case 'multi': return this.toggleRoleMulti(id, next)
+ default:
+ console.warn('DEFAULTING TO MULTI', id, next, old)
+ return this.toggleRoleMulti(id, next)
}
}
@@ -34,7 +36,7 @@ class Category extends Component {
}
return
-
{ name }
+
{ category.get('name') }
{
category.get('roles_map')
.sortBy(r => r.get('position'))
diff --git a/UI/src/components/role-picker/RolePicker.sass b/UI/src/components/role-picker/RolePicker.sass
index 873e93e..7bf7191 100644
--- a/UI/src/components/role-picker/RolePicker.sass
+++ b/UI/src/components/role-picker/RolePicker.sass
@@ -49,4 +49,15 @@
&.hidden
opacity: 0
- // display: none
\ No newline at end of file
+ // display: none
+
+ &__msg-editor
+ background-color: rgba(0,0,0,0.2)
+ border-color: rgba(0,0,0,0.1)
+ color: var(--c-white)
+ margin: 10px 0
+
+ &:focus, &:active
+ color: var(--c-white)
+ background-color: rgba(0,0,0,0.2)
+ border-color: var(--c-7)
\ No newline at end of file
diff --git a/UI/src/components/role-picker/actions.js b/UI/src/components/role-picker/actions.js
index 85ea257..9bfd25e 100644
--- a/UI/src/components/role-picker/actions.js
+++ b/UI/src/components/role-picker/actions.js
@@ -3,16 +3,18 @@ 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('update server roles'),
- // data: {
- // id,
- // roles: data
- // }
- // })
+ console.log(data)
+
+ dispatch({
+ type: Symbol.for('server: set'),
+ data: {
+ id,
+ ...data
+ }
+ })
dispatch(constructView(id))
}
@@ -29,15 +31,19 @@ export const getViewMap = server => {
const viewMap = categories.set('Uncategorized', fromJS({
roles: unaccountedRoles,
- hidden: false,
- type: 'multi'
+ hidden: true,
+ type: 'multi',
+ name: 'Uncategorized'
})).map(c => {
const roles = c.get('roles')
+ // fill in roles_map
.map(r =>
server.get('roles').find(sr => sr.get('id') === r)
)
+ // sort by server position, backwards.
.sort((a, b) => a.position > b.position)
- return c.set('roles_map', roles)
+ // 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())
@@ -54,12 +60,13 @@ export const constructView = id => (dispatch, getState) => {
const { viewMap, selected } = getViewMap(server)
dispatch({
- type: Symbol.for('setup role picker'),
+ type: Symbol.for('rp: setup role picker'),
data: {
viewMap: viewMap,
rolesSelected: selected,
originalRolesSelected: selected,
- hidden: false
+ hidden: false,
+ isEditingMessage: false
}
})
@@ -68,7 +75,7 @@ export const constructView = id => (dispatch, getState) => {
export const resetSelected = (dispatch) => {
dispatch({
- type: Symbol.for('reset selected')
+ type: Symbol.for('rp: reset selected')
})
}
@@ -93,11 +100,35 @@ export const submitSelected = serverId => async (dispatch, getState) => {
await superagent.patch(`/api/servers/${serverId}/roles`).send(diff.toJS())
dispatch({
- type: Symbol.for('sync selected roles')
+ type: Symbol.for('rp: sync selected roles')
})
}
export const updateRoles = roles => ({
- type: Symbol.for('update selected roles'),
+ type: Symbol.for('rp: update selected roles'),
data: roles
})
+
+export const openMessageEditor = ({
+ type: Symbol.for('rp: set message editor state'),
+ data: true
+})
+
+export const saveServerMessage = id => async (dispatch, getState) => {
+ const message = getState().servers.getIn([id, 'message'])
+
+ await superagent.patch(`/api/server/${id}`).send({ message })
+
+ dispatch({
+ type: Symbol.for('rp: set message editor state'),
+ data: false
+ })
+}
+
+export const editServerMessage = (id, message) => ({
+ type: Symbol.for('server: edit message'),
+ data: {
+ id,
+ message
+ }
+})
diff --git a/UI/src/components/role-picker/index.js b/UI/src/components/role-picker/index.js
index e2b556d..049d059 100644
--- a/UI/src/components/role-picker/index.js
+++ b/UI/src/components/role-picker/index.js
@@ -39,9 +39,28 @@ class RolePicker extends Component {
return !data.get('rolesSelected').equals(data.get('originalRolesSelected'))
}
+ editServerMessage = (e) => {
+ const { dispatch } = this.props
+ dispatch(Actions.editServerMessage(this.props.server.get('id'), e.target.value))
+ }
+
+ saveServerMessage = (e) => {
+ const { dispatch } = this.props
+ dispatch(Actions.saveServerMessage(this.props.server.get('id')))
+ }
+
+ openMessageEditor = () => {
+ const { dispatch } = this.props
+ dispatch(Actions.openMessageEditor)
+ }
+
renderServerMessage (server) {
+ const isEditing = this.props.data.get('isEditingMessage')
const roleManager = server.getIn(['perms', 'canManageRoles'])
const msg = server.get('message')
+
+ console.log(msg, roleManager, isEditing, this.props.data.toJS())
+
if (!roleManager && msg !== '') {
return
Server Message
@@ -49,16 +68,26 @@ class RolePicker extends Component {
}
- if (roleManager) {
+ if (roleManager && !isEditing) {
return
{msg || no server message}
}
+ if (roleManager && isEditing) {
+ return
+ }
+
return null
}
@@ -75,6 +104,7 @@ class RolePicker extends Component {
Roles
+