Merge pull request #2286 from urbit/ixv/publish-members-settings

OS1: added members and settings page to publish
This commit is contained in:
ixv 2020-02-14 14:34:43 -08:00 committed by GitHub
commit d49987bcc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 485 additions and 171 deletions

View File

@ -1,6 +1,7 @@
:: ::
/- *publish, /- *publish,
*group-store, *group-store,
*group-hook,
*permission-hook, *permission-hook,
*permission-group-hook, *permission-group-hook,
*permission-store, *permission-store,
@ -295,7 +296,9 @@
[~ this] [~ this]
=/ who=@p (slav %p i.t.wir) =/ who=@p (slav %p i.t.wir)
=/ book=@tas i.t.t.wir =/ book=@tas i.t.t.wir
[~ this(subs (~(del by subs) who book))] =/ del [%del-book who book]
:_ this(subs (~(del by subs) who book))
[%give %fact [/primary]~ %publish-primary-delta !>(del)]~
:: Resubscribe to any subscription we get kicked from. The case of actually :: Resubscribe to any subscription we get kicked from. The case of actually
:: getting banned from a notebook is handled by %watch-ack :: getting banned from a notebook is handled by %watch-ack
:: ::
@ -703,49 +706,31 @@
++ handle-permission-update ++ handle-permission-update
|= upd=permission-update |= upd=permission-update
^- (quip card _state) ^- (quip card _state)
?+ -.upd ?. ?=(?(%remove %add) -.upd)
[~ state] [~ state]
:: =/ book=(unit @tas)
%remove %+ roll ~(tap by books)
=/ book=(unit @tas) |= [[nom=@tas book=notebook] out=(unit @tas)]
%+ roll ~(tap by books) ?: =(path.upd subscribers.book)
|= [[nom=@tas book=notebook] out=(unit @tas)] `nom
?: =(path.upd subscribers.book) out
`nom ?~ book
out [~ state]
?~ book :_ state
[~ state] %- zing
:_ state %+ turn ~(tap in who.upd)
%- zing |= who=@p
%+ turn ~(tap in who.upd) ?. (allowed who %read u.book)
|= who=@p
?: (allowed who %read u.book)
~
[%give %kick [/notebook/[u.book]]~ `who]~ [%give %kick [/notebook/[u.book]]~ `who]~
:: ?: ?=(%remove -.upd)
%add ~
=/ book=(unit @tas) =/ uid (sham %publish who u.book eny.bol)
%+ roll ~(tap by books) =/ inv=invite
|= [[nom=@tas book=notebook] out=(unit @tas)] :* our.bol %publish /notebook/[u.book] who
?: =(path.upd subscribers.book) (crip "invite for notebook {<our.bol>}/{(trip u.book)}")
`nom ==
out =/ act=invite-action [%invite /publish uid inv]
?~ book [%pass / %agent [our.bol %invite-hook] %poke %invite-action !>(act)]~
[~ state]
:_ state
%- zing
%+ turn ~(tap in who.upd)
|= who=@p
?. (allowed who %read u.book)
~
=/ uid (sham %publish who u.book eny.bol)
=/ inv=invite
:* our.bol %publish /notebook/[u.book] who
(crip "invite for notebook {<our.bol>}/{(trip u.book)}")
==
=/ act=invite-action [%invite /publish uid inv]
[%pass / %agent [our.bol %invite-hook] %poke %invite-action !>(act)]~
==
:: ::
++ handle-invite-update ++ handle-invite-update
|= upd=invite-update |= upd=invite-update
@ -870,6 +855,11 @@
^- card ^- card
[%pass / %agent [our.bol %group-store] %poke %group-action !>(act)] [%pass / %agent [our.bol %group-store] %poke %group-action !>(act)]
:: ::
++ group-hook-poke
|= act=group-hook-action
^- card
[%pass / %agent [our.bol %group-hook] %poke %group-hook-action !>(act)]
::
++ contact-view-create ++ contact-view-create
|= act=[%create path (set ship)] |= act=[%create path (set ship)]
^- card ^- card
@ -989,8 +979,9 @@
%- zing %- zing
:~ [(group-poke [%bundle write-path])]~ :~ [(group-poke [%bundle write-path])]~
[(group-poke [%bundle read-path])]~ [(group-poke [%bundle read-path])]~
[(group-hook-poke [%add our.bol write-path])]~
[(group-hook-poke [%add our.bol read-path])]~
[(group-poke [%add (sy our.bol ~) write-path])]~ [(group-poke [%add (sy our.bol ~) write-path])]~
[(group-poke [%add (sy our.bol ~) read-path])]~
(create-security read-path write-path %journal) (create-security read-path write-path %journal)
[(perm-hook-poke [%add-owned write-path write-path])]~ [(perm-hook-poke [%add-owned write-path write-path])]~
[(perm-hook-poke [%add-owned read-path read-path])]~ [(perm-hook-poke [%add-owned read-path read-path])]~
@ -1148,11 +1139,22 @@
%del-book %del-book
?. (team:title our.bol src.bol) ?. (team:title our.bol src.bol)
~|("action not permitted" !!) ~|("action not permitted" !!)
?. (~(has by books) book.act) =/ book=(unit notebook) (~(get by books) book.act)
?~ book
~|("nonexistent notebook {<book.act>}" !!) ~|("nonexistent notebook {<book.act>}" !!)
=/ pax=path /app/publish/notebooks/[book.act] =/ pax=path /app/publish/notebooks/[book.act]
:_ state ?> ?=(^ writers.u.book)
[(delete-dir pax)]~ ?> ?=(^ subscribers.u.book)
=/ cards=(list card)
:~ (delete-dir pax)
(perm-hook-poke [%remove writers.u.book])
(perm-hook-poke [%remove subscribers.u.book])
==
=? cards =('~' i.writers.u.book)
[(group-poke [%unbundle writers.u.book]) cards]
=? cards =('~' i.subscribers.u.book)
[(group-poke [%unbundle subscribers.u.book]) cards]
[cards state]
:: ::
%del-note %del-note
?: &(=(src.bol our.bol) !=(our.bol who.act)) ?: &(=(src.bol our.bol) !=(our.bol who.act))
@ -1279,7 +1281,17 @@
?- -.del ?- -.del
%add-book %add-book
=. tile-num (add tile-num (get-unread data.del)) =. tile-num (add tile-num (get-unread data.del))
(emit-updates-and-state host.del book.del data.del del sty) ?: =(our.bol host.del)
(emit-updates-and-state host.del book.del data.del del sty)
=/ write-pax writers.data.del
=/ read-pax subscribers.data.del
=^ cards state
(emit-updates-and-state host.del book.del data.del del sty)
:_ state
:* (group-hook-poke [%add host.del write-pax])
(group-hook-poke [%add host.del read-pax])
cards
==
:: ::
%add-note %add-note
=/ book=(unit notebook) =/ book=(unit notebook)
@ -1347,14 +1359,14 @@
(emit-updates-and-state host.del book.del u.book del sty) (emit-updates-and-state host.del book.del u.book del sty)
:: ::
%del-book %del-book
=. tile-num
%+ sub tile-num
(get-unread (~(got by books) book.del))
?: =(our.bol host.del) ?: =(our.bol host.del)
:_ sty(books (~(del by books.sty) book.del)) :_ sty(books (~(del by books.sty) book.del))
:~ [%give %fact [/notebook/[book.del]]~ %publish-notebook-delta !>(del)] :~ [%give %fact [/notebook/[book.del]]~ %publish-notebook-delta !>(del)]
[%give %fact [/primary]~ %publish-primary-delta !>(del)] [%give %fact [/primary]~ %publish-primary-delta !>(del)]
== ==
=. tile-num
%+ sub tile-num
(get-unread (~(got by subs) host.del book.del))
=/ jon=json =/ jon=json
(frond:enjs:format %notifications (numb:enjs:format tile-num.sty)) (frond:enjs:format %notifications (numb:enjs:format tile-num.sty))
:_ sty(subs (~(del by subs.sty) host.del book.del)) :_ sty(subs (~(del by subs.sty) host.del book.del))

View File

@ -195,6 +195,15 @@ a {
color: #7F7F7F; color: #7F7F7F;
} }
.options::after {
content: "⌃";
transform: rotate(180deg);
position: absolute;
right: 4px;
top: 6px;
color: #7f7f7f;
}
[contenteditable]:focus { [contenteditable]:focus {
outline: 0px solid transparent; outline: 0px solid transparent;
} }

View File

@ -32,6 +32,9 @@ export class Comments extends Component {
} }
render() { render() {
if (!this.props.enabled) {
return null;
}
let commentArray = this.props.comments.map((com, i) => { let commentArray = this.props.comments.map((com, i) => {
return ( return (
<CommentItem <CommentItem

View File

@ -0,0 +1,73 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
export class Dropdown extends Component {
constructor(props) {
super(props);
this.toggleDropdown = this.toggleDropdown.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
this.collapseAndDispatch = this.collapseAndDispatch.bind(this);
this.state = {
open: false
}
}
componentDidMount() {
document.addEventListener('mousedown', this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClickOutside);
}
handleClickOutside(evt) {
if (this.optsList && !this.optsList.contains(evt.target) &&
this.optsButton && !this.optsButton.contains(evt.target)) {
this.setState({open: false});
}
}
toggleDropdown() {
this.setState({open: !this.state.open});
}
collapseAndDispatch(action){
this.setState({open: false}, action);
}
render() {
let display = (this.state.open)
? "block" : "none";
let optionsColor = (this.state.open)
? '#e6e6e6' : 'white';
let optionsList = this.props.options.map((val, i) => {
return (
<button key={i} className={val.cls}
onClick={() => this.collapseAndDispatch(val.action)}>
{val.txt}
</button>
);
});
return (
<div className="options relative dib"
ref={(el) => {this.optsButton = el}}>
<button className="pr3 mb1 pointer br2 pa2 pr4"
style={{backgroundColor: optionsColor}}
onClick={this.toggleDropdown}>
{this.props.buttonText}
</button>
<div className="absolute flex flex-column pa4 ba b--gray4 br2 z-1 bg-white"
ref={(el) => {this.optsList = el}}
style={{right:0, width:this.props.width, display: display}}>
{optionsList}
</div>
</div>
)
}
}
export default Dropdown

View File

@ -98,7 +98,7 @@ export class NewPost extends Component {
popout={props.popout} popout={props.popout}
/> />
<button <button
className="v-mid w-100 mw7 tl pl4 h1" className={"v-mid w-100 mw7 tl h1 pl4"}
disabled={!state.submit} disabled={!state.submit}
style={submitStyle} style={submitStyle}
onClick={this.postSubmit}> onClick={this.postSubmit}>

View File

@ -15,7 +15,8 @@ export class NewScreen extends Component {
groups: [], groups: [],
ships: [] ships: []
}, },
createGroup: false createGroup: false,
awaiting: false,
}; };
this.idChange = this.idChange.bind(this); this.idChange = this.idChange.bind(this);
@ -27,9 +28,8 @@ export class NewScreen extends Component {
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { props, state } = this; const { props, state } = this;
if (props.notebooks && (("~" + window.ship) in props.notebooks)) { if (props.notebooks && (("~" + window.ship) in props.notebooks)) {
let notebookId = stringToSymbol(state.idName) if (state.awaiting in props.notebooks["~" + window.ship]) {
if (notebookId in props.notebooks["~" + window.ship]) { let notebook = `/~${window.ship}/${state.awaiting}`;
let notebook = `/~${window.ship}/${notebookId}`;
props.history.push("/~publish/notebook" + notebook); props.history.push("/~publish/notebook" + notebook);
} }
} }
@ -92,7 +92,9 @@ export class NewScreen extends Component {
} }
} }
props.api.action("publish", "publish-action", action); this.setState({awaiting: bookId}, () => {
props.api.action("publish", "publish-action", action);
});
} }
render() { render() {

View File

@ -5,10 +5,7 @@ import { Comments } from './comments';
import { NoteNavigation } from './note-navigation'; import { NoteNavigation } from './note-navigation';
import moment from 'moment'; import moment from 'moment';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
//TODO ask for note if we don't have it
//TODO initialise note if no state
//TODO if comments are disabled on the notebook, don't render comments
export class Note extends Component { export class Note extends Component {
constructor(props){ constructor(props){
super(props); super(props);
@ -38,6 +35,14 @@ export class Note extends Component {
} }
componentWillMount() { componentWillMount() {
let readAction = {
read: {
who: this.props.ship.slice(1),
book: this.props.book,
note: this.props.note,
}
}
window.api.action("publish", "publish-action", readAction);
window.api.fetchNote(this.props.ship, this.props.book, this.props.note); window.api.fetchNote(this.props.ship, this.props.book, this.props.note);
} }
@ -189,7 +194,7 @@ export class Note extends Component {
ship={props.ship} ship={props.ship}
book={props.book} book={props.book}
/> />
<Comments <Comments enabled={notebook.comments}
ship={props.ship} ship={props.ship}
book={props.book} book={props.book}
note={props.note} note={props.note}

View File

@ -6,8 +6,6 @@ import { Subscribers } from './subscribers';
import { Settings } from './settings'; import { Settings } from './settings';
import Sidebar from './sidebar'; import Sidebar from './sidebar';
//TODO subcomponent logic for subscribers, settings
export class Notebook extends Component { export class Notebook extends Component {
constructor(props){ constructor(props){
super(props); super(props);
@ -46,13 +44,15 @@ export class Notebook extends Component {
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if (!this.props.notebooks[this.props.ship][this.props.book].notes) { let notebook = this.props.notebooks[this.props.ship][this.props.book];
if (!notebook.subscribers) {
window.api.fetchNotebook(this.props.ship, this.props.book); window.api.fetchNotebook(this.props.ship, this.props.book);
} }
} }
componentDidMount() { componentDidMount() {
if (this.props.notebooks[this.props.ship][this.props.book].notes) { let notebook = this.props.notebooks[this.props.ship][this.props.book];
if (notebook.notes) {
this.onScroll(); this.onScroll();
} }
} }
@ -82,9 +82,9 @@ export class Notebook extends Component {
let tabStyles = { let tabStyles = {
posts: "bb b--gray4 gray2 pv4 ph2", posts: "bb b--gray4 gray2 pv4 ph2",
about: "bb b--gray4 gray2 pv4 ph2" about: "bb b--gray4 gray2 pv4 ph2",
// subscribers: "bb b--gray4 gray2 pv4 ph2", subscribers: "bb b--gray4 gray2 pv4 ph2",
// settings: "bb b--gray4 pr2 gray2 pv4 ph2", settings: "bb b--gray4 pr2 gray2 pv4 ph2",
}; };
tabStyles[props.view] = "bb b--black black pv4 ph2"; tabStyles[props.view] = "bb b--black black pv4 ph2";
@ -104,12 +104,22 @@ export class Notebook extends Component {
case "about": case "about":
inner = <p className="f8 lh-solid">{notebook.about}</p> inner = <p className="f8 lh-solid">{notebook.about}</p>
break; break;
// case "subscribers": case "subscribers":
// inner = <Subscribers/> inner = <Subscribers
// break; host={this.props.ship}
// case "settings": book={this.props.book}
// inner = <Settings/> notebook={notebook}
// break; permissions={this.props.permissions}
groups={this.props.groups}/>
break;
case "settings":
inner = <Settings
host={this.props.ship}
book={this.props.book}
notebook={notebook}
groups={this.props.groups}
history={this.props.history}/>
break;
default: default:
break; break;
} }
@ -148,6 +158,18 @@ export class Notebook extends Component {
Unsubscribe Unsubscribe
</button> </button>
let subsComponent = (this.props.ship.slice(1) !== window.ship)
? null
: <Link to={subs} className={tabStyles.subscribers}>
Subscribers
</Link>;
let settingsComponent = (this.props.ship.slice(1) !== window.ship)
? null
: <Link to={settings} className={tabStyles.settings}>
Settings
</Link>;
return ( return (
<div <div
className="center mw6 f9 h-100" className="center mw6 f9 h-100"
@ -200,8 +222,9 @@ export class Notebook extends Component {
<Link to={about} className={tabStyles.about}> <Link to={about} className={tabStyles.about}>
About About
</Link> </Link>
<div {subsComponent}
className="bb b--gray4 gray2 pv4 ph2" {settingsComponent}
<div className="bb b--gray4 gray2 pv4 ph2"
style={{ flexGrow: 1 }}></div> style={{ flexGrow: 1 }}></div>
</div> </div>

View File

@ -1,41 +1,39 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
//TODO Settings for owned notebooks
export class Settings extends Component { export class Settings extends Component {
render() { constructor(props){
return ( super(props)
<div> this.deleteNotebook = this.deleteNotebook.bind(this);
<div className="flex flex-column mb8"> }
<label for="name" className="f9">Share</label>
<small id="name-desc" className="f9 mb2 gray3">Share a link to this notebook</small>
<div className="flex">
<input style={{flex: "1"}} id="name" placeholder="dopzod.arvo.network/4f5hsS" className="input-reset bt bl bb pa3 gray4" type="text" aria-describedby="name-desc"/>
<button className="bt br bb pa3 b--gray4">Copy</button>
</div>
</div>
<div className="flex flex-column mb8"> deleteNotebook(){
<label for="name" className="f9">Rename</label> let action = {
<small id="name-desc" className="f9 mb2 gray3">Change the name of this notebook</small> "del-book": {
<div className="flex"> book: this.props.book
<input style={{flex: "1"}} id="name" placeholder="Notebook Name" className="input-reset ba pa3 gray4" type="text" aria-describedby="name-desc"/> }
</div> }
</div> window.api.action("publish", "publish-action", action);
<div className="flex flex-column"> this.props.history.push('/~publish');
<label for="name" className="f9">Export</label> }
<small id="name-desc" className="f9 mb2 gray3">Change the name of this notebook</small>
<button className="bg-black white pa3"> render() {
<div className="flex justify-between"> if (this.props.host.slice(1) === window.ship) {
<div>Export Notebook</div> return (
<div></div> <div className="flex-column">
</div> <p className="f9 mt3 lh-copy db">Delete Notebook</p>
<p className="f9 gray2 db mb4">
Permanently delete this notebook. (All current members will no longer see this notebook)
</p>
<button className="b--red2 red2 pointer dib f9 ba pa2"
onClick={this.deleteNotebook}>
Delete this notebook
</button> </button>
</div> </div>
)
</div> } else {
return null;
) }
} }
} }

View File

@ -1,15 +0,0 @@
import React, { Component } from 'react'
//TODO fill sigil/avatar + name from props
export class SubscriberItem extends Component {
render() {
return (
<div>
</div>
)
}
}
export default SubscriberItem

View File

@ -1,53 +1,184 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { SubscriberItem } from './subscriber-item'; import { Dropdown } from './dropdown';
//TODO map list of subscriber-items from props
export class Subscribers extends Component { export class Subscribers extends Component {
constructor(props){
super(props);
this.redirect = this.redirect.bind(this);
this.addUser = this.addUser.bind(this);
this.removeUser = this.removeUser.bind(this);
}
addUser(who, path) {
let action = {
add: {
members: [who],
path: path,
}
}
window.api.action("group-store", "group-action", action);
}
removeUser(who, path) {
let action = {
remove: {
members: [who],
path: path,
}
}
window.api.action("group-store", "group-action", action);
}
redirect(url) {
window.location.href = url;
}
render() { render() {
let readPath = this.props.notebook["subscribers-group-path"]
let readPerms = (readPath)
? this.props.permissions[readPath]
: null;
let writePath = this.props.notebook["writers-group-path"]
let writePerms = (writePath)
? this.props.permissions[writePath]
: null;
let writers = [];
if (writePerms && writePerms.kind === 'white') {
let withoutUs = new Set(writePerms.who)
withoutUs.delete(window.ship);
writers = Array.from(withoutUs).map((who, i) => {
let width = 0;
let options = [];
if (readPath === writePath) {
width = 258;
let url = `/~contacts${writePath}`;
options = [{
cls: "tl pointer",
txt: "Manage this group in the contacts view",
action: () => {this.redirect(url)}
}];
} else {
width = 157;
options = [{
cls: "tl pointer",
txt: "Demote to subscriber",
action: () => {this.removeUser(`~${who}`, writePath)}
}];
}
return (
<div className="flex justify-between" key={i}>
<div className="f9 mono mr2">{`~${who}`}</div>
<Dropdown
options={options}
width={width}
buttonText={"Options"}
/>
</div>
)
});
}
if (writers.length === 0) {
writers =
<div className="f9">
There are no participants on this notebook.
</div>
}
let subscribers = null;
if (readPath !== writePath) {
if (this.props.notebook.subscribers){
let width = 162;
subscribers = this.props.notebook.subscribers.map((who, i) => {
let options = [
{ cls: "tl mb2 pointer",
txt: "Promote to participant",
action: () => {this.addUser(who, writePath)}
},
{ cls: "tl red2 pointer",
txt: "Ban",
action: () => {this.addUser(who, readPath)}
},
];
return (
<div className="flex justify-between" key={i}>
<div className="f9 mono mr2">{who}</div>
<Dropdown
options={options}
width={width}
buttonText={"Options"}
/>
</div>
)
});
}
if (subscribers.length === 0) {
subscribers =
<div className="f9">
There are no subscribers to this notebook.
</div>
}
}
let subsContainer = (readPath === writePath)
? null
: <div className="flex flex-column">
<div className="f9 gray2 mt6 mb3">Subscribers (read access only)</div>
{subscribers}
</div>;
let bannedContainer = null;
if (readPerms && readPerms.kind === 'black') {
let width = 72;
let banned = Array.from(readPerms.who).map((who, i) => {
let options = [{
cls: "tl red2 pointer",
txt: "Unban",
action: () => {this.removeUser(`~${who}`, readPath)}
}];
return (
<div className="flex justify-between" key={i}>
<div className="f9 mono mr2">{`~${who}`}</div>
<Dropdown
options={options}
width={width}
buttonText={"Options"}
/>
</div>
)
});
if (banned.length === 0) {
banned =
<div className="f9">
There are no users banned from this notebook.
</div>
}
bannedContainer =
<div className="flex flex-column">
<div className="f9 gray2 mt6 mb3">Banned</div>
{banned}
</div>;
}
return ( return (
<div> <div>
<div className="flex flex-column"> <div className="flex flex-column">
<div className="f9 gray2">Host</div> <div className="f9 gray2">Host</div>
<div className="flex justify-between mt3"> <div className="flex justify-between mt3">
<div className="flex"> <div className="f9 mono mr2">{this.props.host}</div>
<div className="f9 mono mr2">~fabled-faster</div>
<div className="f9 gray2">Last active</div>
</div> </div>
<div className="f9">Options </div>
</div> </div>
</div> <div className="flex flex-column">
<div className="flex flex-column"> <div className="f9 gray2 mt6 mb3">
<div className="f9 gray2 mt6">Participants (read and write access)</div> Participants (read and write access)
<div className="f9 mt3">There are no paticipants in this notebook.</div>
<div className="flex justify-between mt3">
<div className="flex">
<div className="f9 mono mr2">~fabled-faster</div>
<div className="f9 gray2">Last active</div>
</div> </div>
<div className="f9">Options </div> {writers}
</div> </div>
</div> {subsContainer}
<div className="flex flex-column"> {bannedContainer}
<div className="f9 gray2 mt6 mb3">Subscribers (read access only)</div>
<div className="flex justify-between">
<div className="flex">
<div className="f9 mono mr2">~fabled-faster</div>
<div className="f9 gray2">Last active</div>
</div>
<div className="f9">Options </div>
</div>
</div>
<div className="flex flex-column">
<div className="f9 gray2 mt6 mb3">Banned</div>
<div className="flex justify-between">
<div className="flex">
<div className="f9 mono mr2">~fabled-faster</div>
<div className="f9 gray2">Last active</div>
</div>
<div className="f9">Options </div>
</div>
</div>
</div> </div>
) )
} }

View File

@ -143,6 +143,7 @@ export class Root extends Component {
contacts={notebookContacts} contacts={notebookContacts}
sidebarShown={state.sidebarShown} sidebarShown={state.sidebarShown}
popout={popout} popout={popout}
permissions={state.permissions}
{...props} {...props}
/> />
</Skeleton> </Skeleton>

View File

@ -0,0 +1,58 @@
import _ from 'lodash';
export class PermissionReducer {
reduce(json, state) {
let data = _.get(json, 'permission-initial', false);
if (data) {
for (let perm in data) {
state.permissions[perm] = {
who: new Set(data[perm].who),
kind: data[perm].kind
}
}
}
data = _.get(json, 'permission-update', false);
if (data) {
this.create(data, state);
this.delete(data, state);
this.add(data, state);
this.remove(data, state);
}
}
create(json, state) {
let data = _.get(json, 'create', false);
if (data) {
state.permissions[data.path] = {
kind: data.kind,
who: new Set(data.who)
};
}
}
delete(json, state) {
let data = _.get(json, 'delete', false);
if (data) {
delete state.permissions[data.path];
}
}
add(json, state) {
let data = _.get(json, 'add', false);
if (data) {
for (let member of data.who) {
state.permissions[data.path].who.add(member);
}
}
}
remove(json, state) {
let data = _.get(json, 'remove', false);
if (data) {
for (let member of data.who) {
state.permissions[data.path].who.delete(member);
}
}
}
}

View File

@ -64,6 +64,14 @@ export class ResponseReducer {
if (state.notebooks[json.host][json.notebook]) { if (state.notebooks[json.host][json.notebook]) {
state.notebooks[json.host][json.notebook]["notes-by-date"] = state.notebooks[json.host][json.notebook]["notes-by-date"] =
json.data.notebook["notes-by-date"]; json.data.notebook["notes-by-date"];
state.notebooks[json.host][json.notebook].subscribers =
json.data.notebook.subscribers;
state.notebooks[json.host][json.notebook].comments =
json.data.notebook.comments;
state.notebooks[json.host][json.notebook]["subscribers-group-path"] =
json.data.notebook["subscribers-group-path"];
state.notebooks[json.host][json.notebook]["writers-group-path"] =
json.data.notebook["writers-group-path"];
if (state.notebooks[json.host][json.notebook].notes) { if (state.notebooks[json.host][json.notebook].notes) {
for (var key in json.data.notebook.notes) { for (var key in json.data.notebook.notes) {
let oldNote = state.notebooks[json.host][json.notebook].notes[key]; let oldNote = state.notebooks[json.host][json.notebook].notes[key];

View File

@ -1,8 +1,9 @@
import { InitialReducer } from '/reducers/initial'; import { InitialReducer } from '/reducers/initial';
import { PrimaryReducer } from '/reducers/primary'; import { PrimaryReducer } from '/reducers/primary';
import { ResponseReducer } from '/reducers/response'; import { ResponseReducer } from '/reducers/response';
import { GroupReducer } from '/reducers/group'; import { GroupReducer } from '/reducers/group';
import { InviteReducer } from '/reducers/invite'; import { InviteReducer } from '/reducers/invite';
import { PermissionReducer } from '/reducers/permission';
class Store { class Store {
constructor() { constructor() {
@ -16,11 +17,12 @@ class Store {
sidebarShown: true sidebarShown: true
} }
this.initialReducer = new InitialReducer(); this.initialReducer = new InitialReducer();
this.primaryReducer = new PrimaryReducer(); this.primaryReducer = new PrimaryReducer();
this.responseReducer = new ResponseReducer(); this.responseReducer = new ResponseReducer();
this.groupReducer = new GroupReducer(); this.groupReducer = new GroupReducer();
this.inviteReducer = new InviteReducer(); this.inviteReducer = new InviteReducer();
this.permissionReducer = new PermissionReducer();
this.setState = () => {}; this.setState = () => {};
this.initialReducer.reduce(window.injectedState, this.state); this.initialReducer.reduce(window.injectedState, this.state);
@ -33,6 +35,7 @@ class Store {
handleEvent(evt) { handleEvent(evt) {
if (evt.from && evt.from.path === '/all') { if (evt.from && evt.from.path === '/all') {
this.groupReducer.reduce(evt.data, this.state); this.groupReducer.reduce(evt.data, this.state);
this.permissionReducer.reduce(evt.data, this.state);
} }
else if (evt.from && evt.from.path === '/primary'){ else if (evt.from && evt.from.path === '/primary'){
this.primaryReducer.reduce(evt.data, this.state); this.primaryReducer.reduce(evt.data, this.state);

View File

@ -24,6 +24,9 @@ export class Subscription {
api.bind('/primary', 'PUT', api.authTokens.ship, 'invite-view', api.bind('/primary', 'PUT', api.authTokens.ship, 'invite-view',
this.handleEvent.bind(this), this.handleEvent.bind(this),
this.handleError.bind(this)); this.handleError.bind(this));
api.bind('/all', 'PUT', api.authTokens.ship, 'permission-store',
this.handleEvent.bind(this),
this.handleError.bind(this));
} }
handleEvent(diff) { handleEvent(diff) {