diff --git a/ui/pages/_internal/_server.js b/ui/pages/_internal/_server.js index efc3009..b372321 100644 --- a/ui/pages/_internal/_server.js +++ b/ui/pages/_internal/_server.js @@ -6,19 +6,31 @@ import SocialCards from '../../components/social-cards' import redirect from '../../lib/redirect' import { connect } from 'react-redux' import { fetchServerIfNeed, getCurrentServerState, type ServerState } from '../../stores/currentServer' -import { renderRoles, getCurrentRoles } from '../../stores/roles' +import { renderRoles, getCategoryViewState, toggleRole, type ViewState } from '../../stores/roles' +import styled from 'styled-components' +import Role from '../../components/role' type ServerPageProps = PageProps & { - currentServer: ServerState + currentServer: ServerState, + view: ViewState } const mapStateToProps = (state, { router: { query: { id } } }) => { return { currentServer: getCurrentServerState(state, id), - roles: getCurrentRoles(state, id) + view: getCategoryViewState(state) } } +const Category = styled.div`` + +const Hider = styled.div` + /* opacity: ${(props: any) => props.visible ? '1' : '0'}; */ + /* opacity: 1; */ + /* transition: opacity 0.15s ease-out; */ + /* ${(props: any) => props.visible ? '' : 'display: none;'} */ +` + class Server extends React.Component { static async getInitialProps (ctx: *, rpc: *, router: *) { if (ctx.user == null) { @@ -26,29 +38,29 @@ class Server extends React.Component { } ctx.robots = 'NOINDEX, NOFOLLOW' - try { - if (router.query.id == null) { - console.warn({ query: router.query }) - } - ctx.store.dispatch(fetchServerIfNeed(router.query.id, rpc)) - ctx.store.dispatch(renderRoles(router.query.id)) - } catch (e) { - - } + await ctx.store.dispatch(fetchServerIfNeed(router.query.id, rpc)) + await ctx.store.dispatch(renderRoles(router.query.id)) } - componentDidMount () { + async componentDidMount () { const { currentServer, router: { query: { id } }, dispatch } = this.props if (currentServer == null) { this.props.router.push('/s/add') } - dispatch(fetchServerIfNeed(id)) + await dispatch(fetchServerIfNeed(id)) + await dispatch(renderRoles(id)) + } + + onToggle = (role) => (nextState) => { + if (role.safe) { + this.props.dispatch(toggleRole(role.id, nextState)) + } } render () { - const { currentServer } = this.props - console.log({ currentServer }) + const { currentServer, view } = this.props + // console.log({ currentServer }) if (currentServer == null) { return null } @@ -59,7 +71,17 @@ class Server extends React.Component { {currentServer.server.name} - Roleypoly - hello {currentServer.gm.nickname} on {currentServer.server.name} + hello {currentServer.gm.nickname} on {currentServer.server.name} ({ view.dirty ? 'dirty' : 'clean' }) + + { !view.invalidated && view.categories.map(c => +
{ c.name }
+
+ { + c._roles && c._roles.map(r => ) + } +
+
) } +
) } diff --git a/ui/stores/currentServer.js b/ui/stores/currentServer.js index 1285b0f..724db59 100644 --- a/ui/stores/currentServer.js +++ b/ui/stores/currentServer.js @@ -2,6 +2,7 @@ import { dynamicPropertyConfig } from 'fast-redux' // import { Map } from 'immutable' import type { PresentableServer } from '../../services/presentation' +import type { Category } from '../../models/Server' import RPC from '../config/rpc' import { action } from './servers' @@ -15,7 +16,8 @@ const DEFAULT_STATE: $Shape | { id: ?string } = { }, gm: { nickname: 'person', - color: '#ff00ff' + color: '#ff00ff', + roles: ['0000'] }, categories: {}, perms: { @@ -46,7 +48,7 @@ export const fetchServerIfNeed = (id: string, rpc?: typeof RPC) => async (dispat if (state.id == null || state.id !== id) { const server = await rpc.getServer(id) dispatch(updateCurrentServer(id, server)) - console.log({ state, server, fullStore: getState() }) + // console.log({ state, server, fullStore: getState() }) } else { console.log('did not update') } diff --git a/ui/stores/roles.js b/ui/stores/roles.js index d2c5ad9..4e49cd5 100644 --- a/ui/stores/roles.js +++ b/ui/stores/roles.js @@ -1,10 +1,105 @@ +// @flow +// import { action } from './servers' import { namespaceConfig } from 'fast-redux' -import { getCurrentServerState } from './currentServer' +import { OrderedMap, OrderedSet, Set } from 'immutable' +import { getCurrentServerState, type ServerState } from './currentServer' -export const { action, getState: getCurrentRoles } = namespaceConfig('roles', {}) - -export const updateCurrentRoles = action('updateCurrentRoles', (state, data) => data) - -export const renderRoles = (dispatch, getState) => { - const server = getCurrentServerState(getState()) +type RenderedRole = { + id: string, + name: string, + color: string +} + +type RenderedCategory = { + id: string, + name: string, + _roles?: RenderedRole[], + roles: string[], + type: 'single' | 'multi', + hidden: boolean, + position?: number, +} + +const MOCK_DATA: RenderedCategory = { + id: '00000', + name: 'Placeholder Category', + hidden: false, + type: 'multi', + roles: [ '00000' ], + _roles: OrderedSet([ + { id: '00000', name: 'Placeholder Role', color: '#ff00ff' } + ]) +} + +const DEFAULT_VIEW_STATE = { server: '000', invalidated: true, categories: [ MOCK_DATA ], selected: Set([]), originalSelected: Set([]), dirty: false } +export type ViewState = typeof DEFAULT_VIEW_STATE + +export const { action, getState: getCategoryViewState } = namespaceConfig('currentCategoryView', DEFAULT_VIEW_STATE) + +export const toggleRole = action('toggleRole', (state: ViewState, role: string, nextState: boolean) => { + let selected = state.selected + + if (nextState === true) { + selected = selected.add(role) + } else { + selected = selected.delete(role) + } + + const dirty = !selected.equals(state.originalSelected) + + return { + ...state, + selected, + dirty + } +}) + +const getUncategorized = (roleMap: OrderedMap, allCategories): RenderedCategory => { + const roles = Set(roleMap.keys()) + const knownRoles = Set([...Object.values(allCategories).map((c: any) => c.roles)]) + const rolesLeft = roles.subtract(knownRoles) + + // console.log({ roles, knownRoles, rolesLeft }) + return { + id: 'Uncategorized', + name: 'Uncategorized', + position: -1, + roles: rolesLeft, + _roles: OrderedSet(rolesLeft.map(r => roleMap.get(r))).sortBy(v => -v.position), + hidden: true, + type: 'multi' + } +} + +export const updateCurrentView = action('updateCurrentView', (state, data) => ({ ...state, ...data })) +export const invalidateView = action('invalidateView', (state, data) => ({ ...state, invalidated: true })) + +export const renderRoles = (id: string) => (dispatch: *, getState: *) => { + const active = getCategoryViewState(getState()) + const current: ServerState = getCurrentServerState(getState(), id) + if (!active.invalidated && current.id === active.id && active.id === id) { + return + } + + const { roles, categories } = current + if (roles == null) { + return + } + + // console.log({ roles, categories }) + + const roleMap: OrderedMap = roles.reduce((acc: OrderedMap, r) => acc.set(r.id, r), OrderedMap()) + + let render = OrderedSet() + for (let catId in categories) { + const category = categories[catId] + category._roles = OrderedSet(category.roles.map(r => roleMap.get(r))).sortBy(v => -v.position) + render = render.add(category) + } + // console.log({id}) + render = render.add(getUncategorized(roleMap, categories)) + + render = render.sortBy(h => h.position || 0) + + dispatch(updateCurrentView({ server: id, categories: render, invalidated: false, selected: Set(current.gm.roles), originalSelected: Set(current.gm.roles) })) }