sync: full redux retrofit done

This commit is contained in:
41666 2017-12-11 18:06:25 -06:00
parent cfc623b228
commit f5220aa6dc
14 changed files with 206 additions and 122 deletions

View file

@ -2,7 +2,9 @@ module.exports = (R, $) => {
R.get('/api/servers', async (ctx) => {
const { userId } = ctx.session
const srv = $.discord.getRelevantServers(userId)
ctx.body = $.discord.presentableServers(srv, userId)
const presentable = $.discord.presentableServers(srv, userId)
ctx.body = presentable
})
R.get('/api/server/:id', async (ctx) => {
@ -18,8 +20,8 @@ module.exports = (R, $) => {
}
const gm = srv.members.get(userId)
const roles = $.discord.presentableRoles(id, gm)
const server = $.discord.presentableRoles(id, gm)
ctx.body = roles
ctx.body = server
})
}

View file

@ -41,22 +41,26 @@ class DiscordService extends Service {
ownerID: server.ownerID,
icon: server.icon
},
roles: this.presentableRoles(server.id, gm),
message: 'moe moe kyuuuuuuuuun~',
perms: this.getPermissions(gm)
}
})
}
presentableRoles (serverId, gm) {
return this.client.guilds.get(serverId).roles.filter(r => r.id !== serverId).map((role) => {
return {
color: role.hexColor,
position: role.position,
calculatedPosition: role.calculatedPosition,
id: role.id,
name: role.name,
selected: gm.roles.has(role.id)
}
})
return this.client.guilds
.get(serverId)
.roles
.filter(r => r.id !== serverId)
.map((role) => ({
color: role.hexColor,
position: role.position,
calculatedPosition: role.calculatedPosition,
id: role.id,
name: role.name,
selected: gm.roles.has(role.id)
}))
}
getPermissions (gm) {

View file

@ -8,7 +8,6 @@
"eslint": "^4.13.0",
"immutable": "^3.8.2",
"prop-types": "^15.6.0",
"radium": "^0.19.6",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-immutable-proptypes": "^2.1.0",

View file

@ -7,6 +7,10 @@ export const fetchServers = async dispatch => {
type: Symbol.for('update servers'),
data: rsp.body
})
dispatch({
type: Symbol.for('app ready')
})
}
export const userInit = async dispatch => {

View file

@ -0,0 +1,47 @@
import { Map, Set } from 'immutable'
import superagent from 'superagent'
export const roleUpdate = (id, oldState) => (dispatch, getState) => {
dispatch({
type: Symbol.for('update selected roles'),
data: {
id,
state: !oldState
}
})
}
export const setup = id => async dispatch => {
const rsp = await superagent.get(`/api/server/${id}`)
const data = rsp.body
dispatch({
type: Symbol.for('update server roles'),
data: {
id,
roles: data
}
})
dispatch(constructView(id))
}
export const constructView = id => (dispatch, getState) => {
const server = getState().servers.get(id)
const roles = server.get('roles')
const categories = roles.groupBy(x => x.get('category'))
const selected = roles.reduce((acc, r) => {
return acc.set(r.id, r.selected)
}, Map())
console.log(categories, selected)
dispatch({
type: Symbol.for('setup role picker'),
data: {
viewMap: categories,
rolesSelected: selected,
originalRolesSelected: selected
}
})
}

View file

@ -1,62 +1,47 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import superagent from 'superagent'
import * as Actions from './actions'
import './RolePicker.sass'
import Role from '../role'
const mapState = ({ rolePicker, servers }, ownProps) => {
console.log(servers)
return {
data: rolePicker,
server: servers.get(ownProps.match.params.server)
}
}
@connect(mapState)
class RolePicker extends Component {
state = {
roles: [],
categories: {},
hidden: true,
serverMessage: `Hey there. This is a cool UI you can pick your roles from. If you have any questions, ask the mods on Discord!`
}
async componentDidUpdate (prevProps) {
if (prevProps.match.params.server !== this.props.match.params.server) {
this.setState({ hidden: true })
setTimeout(async () => {
this.setState({ roles: [] })
this.setState({ roles: await this.getRoles(this.props.match.params.server), hidden: false })
}, 350)
}
}
async componentWillMount () {
this.setState({ roles: await this.getRoles(this.props.match.params.server), hidden: false })
}
async getRoles (id) {
const rsp = await superagent.get(`/api/server/${id}`)
return rsp.body//.map(r => { r.selected = false; return r })
}
onChange = k => (newVal, oldVal) => {
this.setState((prevState) => {
return { roles: prevState.roles.map(r => {
if (r.id === k) {
r.selected = newVal
}
return r
})}
})
componentWillMount () {
const { dispatch, match: { params: { server } } } = this.props
dispatch(Actions.setup(server))
}
render () {
return <div className={`role-picker ${(this.state.hidden) ? 'hidden' : ''}`}>
<section>
<h3>Server Message</h3>
<p>{this.state.serverMessage}</p>
</section>
console.log(this.props)
if (this.props.server === undefined) {
return null
}
return <div className={`role-picker ${(this.props.data.hidden) ? 'hidden' : ''}`}>
{ (this.props.server.get('message') !== '')
? <section>
<h3>Server Message</h3>
<p>{this.props.server.get('message')}</p>
</section>
: null
}
<section>
<h3>Roles</h3>
{
this.state.roles.map((r, k) => {
return <Role key={k} role={r} onToggle={this.onChange(r.id)} />
{/* {
this.props.data.roles.map((r, k) => {
return <Role key={k} role={r} onToggle={this.dispatch(Actions.roleUpdate(r.id, r.selected))} />
})
}
} */}
</section>
</div>
}

View file

@ -8,7 +8,7 @@ class ServersNavigation extends Component {
static propTypes = {
user: ImmutablePropTypes.map.isRequired,
servers: ImmutablePropTypes.setOf(ImmutablePropTypes.map).isRequired,
servers: ImmutablePropTypes.setOf(ImmutablePropTypes.orderedMap).isRequired,
className: PropTypes.string
}
@ -17,7 +17,12 @@ class ServersNavigation extends Component {
return <Fragment>
<UserCard user={this.props.user} />
<div className={this.props.className}>
{ this.props.servers.map((s, i) => <ServerCard server={s} user={this.props.user} key={i} />) }
{
this.props.servers.reduce((acc, s, i) => {
acc.push(<ServerCard server={s} user={this.props.user} key={i} />)
return acc
}, [])
}
</div>
</Fragment>
}

View file

@ -15,28 +15,29 @@ class ServerCard extends Component {
let icon = ''
console.log(__filename, server)
const s = server.get('server')
const gm = server.get('gm')
const perms = server.get('perms')
if (perms.canManageRoles) {
if (perms.get('canManageRoles')) {
icon = <span title='Role Manager' uk-tooltip='' role='img' aria-label='Role Manager'>🔰</span>
}
if (perms.isAdmin) {
if (perms.get('isAdmin')) {
icon = <span title='Server Admin' uk-tooltip='' role='img' aria-label='Server Admin'>🔰</span>
}
return <NavLink className='server-list__item' activeClassName='active' to={`/s/${s.id}`}>
return <NavLink className='server-list__item' activeClassName='active' to={`/s/${s.get('id')}`}>
<div className='server-list__item__icon'>
<img src={`https://cdn.discordapp.com/icons/${s.id}/${s.icon}.png`} alt={s.name} />
<img src={`https://cdn.discordapp.com/icons/${s.get('id')}/${s.get('icon')}.png`} alt={s.name} />
</div>
<div className='server-list__item__info'>
<b>{s.name}</b><br />
<span style={{ color: gm.color }}>{ gm.nickname || user.get('username') }</span> { icon }
<b>{s.get('name')}</b><br />
<span style={{ color: gm.get('color') }}>{ gm.get('nickname') || user.get('username') }</span> { icon }
</div>
</NavLink>
}
}
export default Radium(ServerCard)
export default ServerCard

View file

@ -16,7 +16,7 @@ class UserCard extends Component {
const avatar = user.get('avatar')
if (avatar === '' || avatar == null) {
return `https://cdn.discordapp.com/embed/avatars/${Math.ceil(Math.random() * 9999) % 4}.png`
return `https://cdn.discordapp.com/embed/avatars/${Math.ceil(Math.random() * 9999) % 5}.png`
}
return `https://cdn.discordapp.com/avatars/${user.get('id')}/${avatar}.png`

View file

@ -2,10 +2,31 @@ import { combineReducers } from 'redux'
import servers from './servers'
import user from './user'
import rolePicker from './role-picker'
// import roles from './roles'
const initialState = {
ready: false
}
const appState = (state = initialState, { type }) => {
switch (type) {
case Symbol.for('app ready'):
return {
ready: true
}
default:
return state
}
}
const rootReducer = combineReducers({
appState,
servers,
user
user,
// roles,
rolePicker
})
export default rootReducer

View file

@ -0,0 +1,37 @@
import { Map, Set } from 'immutable'
const initialState = Map({
hidden: true, // should the view be hidden?
emptyRoles: true, // helps derender roles so there's no visible element state change
viewMap: Set([]), // roles in categories
originalRolesSelected: Map({}), // Map<role id, bool> -- original roles for diffing against selected
rolesSelected: Map({}) // Map<role id, bool> -- new roles for diffing
})
export default (state = initialState, { type, data }) => {
switch (type) {
case Symbol.for('setup role picker'):
return state.merge(data)
case Symbol.for('hide role picker ui'):
return {
...state,
hidden: data
}
case Symbol.for('reset role picker ui'):
return {
...state,
emptyRoles: data
}
case Symbol.for('zero role picker'):
return initialState
case Symbol.for('update selected roles'):
return state.set('rolesSelected', state.get('rolesSelected').set(data.id, data.state))
default:
return state
}
}

View file

@ -1,34 +1,39 @@
import { Set, Map } from 'immutable'
import { Set, OrderedMap, Map, fromJS } from 'immutable'
const blankServer = Map({
'id': '386659935687147521',
'gm': {
'nickname': null,
'color': '#cca1a1'
id: '386659935687147521',
gm: {
nickname: null,
color: '#cca1a1'
},
'server': {
'id': '386659935687147521',
'name': 'Roleypoly',
'ownerID': '62601275618889728',
'icon': '4fa0c1063649a739f3fe1a0589aa2c03'
message: 'Hey hey!',
server: {
id: '386659935687147521',
name: 'Roleypoly',
ownerID: '62601275618889728',
icon: '4fa0c1063649a739f3fe1a0589aa2c03'
},
'perms': {
'isAdmin': true,
'canManageRoles': true
roles: Set([]),
perms: {
isAdmin: true,
canManageRoles: true
}
})
const initialState = Set([])
const initialState = OrderedMap({})
export default (state = initialState, { type, data }) => {
console.log(__filename, type, data)
switch (type) {
case Symbol.for('update servers'):
return data.reduce((acc, s) => acc.add(Map(s)), Set())
return data.reduce((acc, s) => acc.set(s.id, fromJS(s)), OrderedMap())
// case Symbol.for('update server roles'):
// return state.set(data.id,
// state.get(data.id).set('roles', Set(data.roles))
// )
case Symbol.for('add debug server'):
return Set([blankServer])
return state.set('0', blankServer)
default:
return state

View file

@ -1,5 +1,6 @@
import React, { Component, Fragment } from 'react'
import { Route } from 'react-router-dom'
import { connect } from 'react-redux'
import Servers from '../components/servers'
import OauthCallback from '../components/oauth-callback'
@ -7,8 +8,13 @@ import OauthFlow from '../components/oauth-flow'
const aaa = (props) => (<div>{ JSON.stringify(props) }</div>)
@connect(({ appState }) => ({ ready: appState.ready }))
export default class AppRouter extends Component {
render () {
if (!this.props.ready) {
return null
}
return <Fragment>
<Route exact path='/' component={aaa} />
<Route path='/s' component={Servers} />

View file

@ -214,10 +214,6 @@ array-find-index@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
array-find@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8"
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
@ -1298,10 +1294,6 @@ boom@5.x.x:
dependencies:
hoek "4.x.x"
bowser@^1.0.0:
version "1.8.1"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.8.1.tgz#49785777e7302febadb1a5b71d9a646520ed310d"
boxen@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-0.6.0.tgz#8364d4248ac34ff0ef1b2f2bf49a6c60ce0d81b6"
@ -2884,10 +2876,6 @@ execa@^0.7.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
exenv@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
expand-brackets@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
@ -3704,10 +3692,6 @@ https-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
hyphenate-style-name@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz#31160a36930adaf1fc04c6074f7eb41465d4ec4b"
iconv-lite@0.4.19, iconv-lite@^0.4.17, iconv-lite@~0.4.13:
version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
@ -3779,13 +3763,6 @@ ini@^1.3.4, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
inline-style-prefixer@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-2.0.5.tgz#c153c7e88fd84fef5c602e95a8168b2770671fe7"
dependencies:
bowser "^1.0.0"
hyphenate-style-name "^1.0.1"
inquirer@3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.2.1.tgz#06ceb0f540f45ca548c17d6840959878265fa175"
@ -5977,15 +5954,6 @@ querystringify@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb"
radium@^0.19.6:
version "0.19.6"
resolved "https://registry.yarnpkg.com/radium/-/radium-0.19.6.tgz#b86721d08dbd303b061a4ae2ebb06cc6e335ae72"
dependencies:
array-find "^1.0.0"
exenv "^1.2.1"
inline-style-prefixer "^2.0.5"
prop-types "^15.5.8"
randomatic@^1.1.3:
version "1.1.7"
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"