mirror of
https://github.com/urbit/shrub.git
synced 2024-11-28 05:22:27 +03:00
Merge pull request #2241 from urbit/mp/os1/publish-new-screen
OS1: publish new screen
This commit is contained in:
commit
5b4cff7f61
@ -173,7 +173,7 @@
|
|||||||
=/ uid (sham %publish who book eny.bol)
|
=/ uid (sham %publish who book eny.bol)
|
||||||
=/ inv=invite
|
=/ inv=invite
|
||||||
:* our.bol %publish /notebook/[book] who
|
:* our.bol %publish /notebook/[book] who
|
||||||
'invite for notebook {<who>}/{<book>}'
|
(crip "invite for notebook {<who>}/{<book>}")
|
||||||
==
|
==
|
||||||
=/ act=invite-action [%invite /publish uid inv]
|
=/ act=invite-action [%invite /publish uid inv]
|
||||||
[%pass /invite %agent [who %invite-hook] %poke %invite-action !>(act)]
|
[%pass /invite %agent [who %invite-hook] %poke %invite-action !>(act)]
|
||||||
@ -191,12 +191,13 @@
|
|||||||
[%web %publish @ %publish-info ~]
|
[%web %publish @ %publish-info ~]
|
||||||
=/ book-name i.t.t.pax
|
=/ book-name i.t.t.pax
|
||||||
=/ old=old-info .^(old-info %cx (welp our-beak:main pax))
|
=/ old=old-info .^(old-info %cx (welp our-beak:main pax))
|
||||||
=/ write-pax /~/publish/(scot %p our.bol)/[book-name]/write
|
=/ group-pax /~/publish/(scot %p our.bol)/[book-name]
|
||||||
=/ read-pax /~/publish/(scot %p our.bol)/[book-name]/read
|
|
||||||
=/ book=notebook-info
|
=/ book=notebook-info
|
||||||
[title.old '' =(%open comments.old) write-pax read-pax]
|
[title.old '' =(%open comments.old) / /]
|
||||||
=/ grp-car=(list card)
|
=+ ^- [grp-car=(list card) write-pax=path read-pax=path]
|
||||||
(make-groups ~ writers.book ~ subscribers.book %journal)
|
(make-groups book-name group-pax ~ %.n %.n)
|
||||||
|
=. writers.book write-pax
|
||||||
|
=. subscribers.book read-pax
|
||||||
=/ inv-car (send-invites book-name (~(get ju old-subs) book-name))
|
=/ inv-car (send-invites book-name (~(get ju old-subs) book-name))
|
||||||
:- :(weld car grp-car inv-car)
|
:- :(weld car grp-car inv-car)
|
||||||
^- soba:clay
|
^- soba:clay
|
||||||
@ -522,12 +523,14 @@
|
|||||||
now.bol
|
now.bol
|
||||||
~ ~ ~
|
~ ~ ~
|
||||||
==
|
==
|
||||||
=? writers.new-book =(writers.new-book /)
|
=/ group-path=path
|
||||||
/~/publish/(scot %p our.bol)/[book-name]/write
|
?: =(writers.new-book /)
|
||||||
=? subscribers.new-book =(writers.new-book /)
|
/~/publish/(scot %p our.bol)/[book-name]
|
||||||
/~/publish/(scot %p our.bol)/[book-name]/read
|
writers.new-book
|
||||||
=/ grp-car=(list card)
|
=+ ^- [grp-car=(list card) write-pax=path read-pax=path]
|
||||||
(make-groups ~ writers.new-book ~ subscribers.new-book %journal)
|
(make-groups book-name group-path ~ %.n %.n)
|
||||||
|
=. writers.new-book write-pax
|
||||||
|
=. subscribers.new-book read-pax
|
||||||
=+ ^- [read-cards=(list card) notes=(map @tas note)]
|
=+ ^- [read-cards=(list card) notes=(map @tas note)]
|
||||||
(watch-notes /app/publish/notebooks/[book-name])
|
(watch-notes /app/publish/notebooks/[book-name])
|
||||||
=. notes.new-book notes
|
=. notes.new-book notes
|
||||||
@ -810,6 +813,11 @@
|
|||||||
^- card
|
^- card
|
||||||
[%pass / %agent [our.bol %group-store] %poke %group-action !>(act)]
|
[%pass / %agent [our.bol %group-store] %poke %group-action !>(act)]
|
||||||
::
|
::
|
||||||
|
++ contact-view-create
|
||||||
|
|= act=[%create path (set ship)]
|
||||||
|
^- card
|
||||||
|
[%pass / %agent [our.bol %contact-view] %poke %contact-view-action !>(act)]
|
||||||
|
::
|
||||||
++ perm-hook-poke
|
++ perm-hook-poke
|
||||||
|= act=permission-hook-action
|
|= act=permission-hook-action
|
||||||
^- card
|
^- card
|
||||||
@ -853,22 +861,80 @@
|
|||||||
(perm-group-hook-poke [%associate write [[write write-type] ~ ~]])
|
(perm-group-hook-poke [%associate write [[write write-type] ~ ~]])
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ make-groups
|
++ create-managed-group
|
||||||
|= $: write-grp=(set ship) write-pax=path
|
|= [pax=path security=rw-security ships=(set ship)]
|
||||||
read-grp=(set ship) read-pax=path
|
|
||||||
sec=rw-security
|
|
||||||
==
|
|
||||||
^- (list card)
|
^- (list card)
|
||||||
;: weld
|
=/ grp
|
||||||
:~ (group-poke [%bundle write-pax])
|
.^((unit group) %gx ;:(weld /=group-store/(scot %da now.bol) pax /noun))
|
||||||
(group-poke [%bundle read-pax])
|
?^ grp
|
||||||
(group-poke [%add write-grp write-pax])
|
~
|
||||||
(group-poke [%add read-grp read-pax])
|
?> ?=(^ pax)
|
||||||
|
?: |(=('~' i.pax) !=(%village security))
|
||||||
|
[(group-poke [%bundle pax])]~
|
||||||
|
[(contact-view-create [%create pax ships])]~
|
||||||
|
::
|
||||||
|
++ generate-invites
|
||||||
|
|= [book=@tas invitees=(set ship)]
|
||||||
|
^- (list card)
|
||||||
|
%+ turn ~(tap in invitees)
|
||||||
|
|= who=ship
|
||||||
|
=/ uid (sham %publish who book eny.bol)
|
||||||
|
=/ inv=invite
|
||||||
|
:* our.bol %publish /notebook/[book] who
|
||||||
|
(crip "invite for notebook {<who>}/{<book>}")
|
||||||
==
|
==
|
||||||
(create-security read-pax write-pax sec)
|
=/ act=invite-action [%invite /publish uid inv]
|
||||||
:~ (perm-hook-poke [%add-owned write-pax write-pax])
|
[%pass / %agent [our.bol %invite-hook] %poke %invite-action !>(act)]
|
||||||
(perm-hook-poke [%add-owned read-pax read-pax])
|
::
|
||||||
|
++ make-groups
|
||||||
|
|= [book=@tas group=group-info]
|
||||||
|
^- [(list card) write=path read=path]
|
||||||
|
?> ?=(^ group-path.group)
|
||||||
|
?: use-preexisting.group
|
||||||
|
=/ scry-path
|
||||||
|
;:(weld /=group-store/(scot %da now.bol) group-path.group /noun)
|
||||||
|
=/ grp .^((unit ^group) %gx scry-path)
|
||||||
|
?~ grp !!
|
||||||
|
?> ?=(^ group-path.group)
|
||||||
|
?< =('~' i.group-path.group)
|
||||||
|
:_ [group-path.group group-path.group]
|
||||||
|
%- zing
|
||||||
|
:~ (create-security group-path.group group-path.group %channel)
|
||||||
|
[(perm-hook-poke [%add-owned group-path.group group-path.group])]~
|
||||||
|
(generate-invites book (~(del in u.grp) our.bol))
|
||||||
==
|
==
|
||||||
|
::
|
||||||
|
?: make-managed.group
|
||||||
|
=/ scry-path
|
||||||
|
;:(weld /=group-store/(scot %da now.bol) group-path.group /noun)
|
||||||
|
=/ grp .^((unit ^group) %gx scry-path)
|
||||||
|
?^ grp [~ group-path.group group-path.group]
|
||||||
|
?> ?=(^ group-path.group)
|
||||||
|
?< =('~' i.group-path.group)
|
||||||
|
:_ [group-path.group group-path.group]
|
||||||
|
%- zing
|
||||||
|
:~ [(contact-view-create [%create group-path.group invitees.group])]~
|
||||||
|
(create-security group-path.group group-path.group %channel)
|
||||||
|
[(perm-hook-poke [%add-owned group-path.group group-path.group])]~
|
||||||
|
(generate-invites book (~(del in invitees.group) our.bol))
|
||||||
|
==
|
||||||
|
:: make unmanaged group
|
||||||
|
?> ?=(^ group-path.group)
|
||||||
|
?> =('~' i.group-path.group)
|
||||||
|
=* write-path group-path.group
|
||||||
|
=/ read-path (weld write-path /read)
|
||||||
|
=/ scry-path=path
|
||||||
|
;:(weld /=group-store/(scot %da now.bol) group-path.group /noun)
|
||||||
|
=/ grp .^((unit ^group) %gx scry-path)
|
||||||
|
?^ grp [~ write-path read-path]
|
||||||
|
:_ [write-path read-path]
|
||||||
|
%- zing
|
||||||
|
:~ [(group-poke [%bundle write-path])]~
|
||||||
|
[(group-poke [%bundle read-path])]~
|
||||||
|
(create-security read-path write-path %journal)
|
||||||
|
[(perm-hook-poke [%add-owned write-path write-path])]~
|
||||||
|
[(perm-hook-poke [%add-owned read-path read-path])]~
|
||||||
|
(generate-invites book (~(del in invitees.group) our.bol))
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ poke-publish-action
|
++ poke-publish-action
|
||||||
@ -881,14 +947,7 @@
|
|||||||
?: (~(has by books) book.act)
|
?: (~(has by books) book.act)
|
||||||
~|("notebook already exists: {<book.act>}" !!)
|
~|("notebook already exists: {<book.act>}" !!)
|
||||||
=+ ^- [cards=(list card) write-pax=path read-pax=path]
|
=+ ^- [cards=(list card) write-pax=path read-pax=path]
|
||||||
?. ?=(%new -.group.act)
|
(make-groups book.act group.act)
|
||||||
[~ write-pax.group.act read-pax.group.act]
|
|
||||||
:_ [write-pax.group.act read-pax.group.act]
|
|
||||||
%- make-groups
|
|
||||||
:* write-grp.group.act write-pax.group.act
|
|
||||||
read-grp.group.act read-pax.group.act
|
|
||||||
sec.group.act
|
|
||||||
==
|
|
||||||
=/ new-book=notebook-info
|
=/ new-book=notebook-info
|
||||||
:* title.act
|
:* title.act
|
||||||
about.act
|
about.act
|
||||||
@ -959,14 +1018,7 @@
|
|||||||
=+ ^- [cards=(list card) write-pax=path read-pax=path]
|
=+ ^- [cards=(list card) write-pax=path read-pax=path]
|
||||||
?~ group.act
|
?~ group.act
|
||||||
[~ writers.u.book subscribers.u.book]
|
[~ writers.u.book subscribers.u.book]
|
||||||
?. ?=(%new -.u.group.act)
|
(make-groups book.act u.group.act)
|
||||||
[~ write-pax.u.group.act read-pax.u.group.act]
|
|
||||||
:_ [write-pax.u.group.act read-pax.u.group.act]
|
|
||||||
%- make-groups
|
|
||||||
:* write-grp.u.group.act write-pax.u.group.act
|
|
||||||
read-grp.u.group.act read-pax.u.group.act
|
|
||||||
sec.u.group.act
|
|
||||||
==
|
|
||||||
=/ new-info=notebook-info
|
=/ new-info=notebook-info
|
||||||
:* title.act
|
:* title.act
|
||||||
about.act
|
about.act
|
||||||
|
File diff suppressed because one or more lines are too long
BIN
pkg/arvo/app/publish/img/search.png
Normal file
BIN
pkg/arvo/app/publish/img/search.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 951 B |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -115,16 +115,13 @@
|
|||||||
note+so
|
note+so
|
||||||
==
|
==
|
||||||
++ group-info
|
++ group-info
|
||||||
%- of
|
|
||||||
:~ old+(ot write-pax+pa read-pax+pa ~)
|
|
||||||
:- %new
|
|
||||||
%- ot
|
%- ot
|
||||||
:~ write-grp+set-ship write-pax+pa
|
:~ group-path+pa
|
||||||
read-grp+set-ship read-pax+pa
|
invitees+set-ship
|
||||||
sec+so
|
use-preexisting+bo
|
||||||
|
make-managed+bo
|
||||||
==
|
==
|
||||||
==
|
++ set-ship (as (su fed:ag))
|
||||||
++ set-ship (ar (su fed:ag))
|
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
|
@ -2,14 +2,11 @@
|
|||||||
|%
|
|%
|
||||||
::
|
::
|
||||||
+$ group-info
|
+$ group-info
|
||||||
$% [%old write-pax=path read-pax=path]
|
$: group-path=path
|
||||||
$: %new
|
invitees=(set ship)
|
||||||
write-grp=(set ship) write-pax=path
|
use-preexisting=?
|
||||||
read-grp=(set ship) read-pax=path
|
make-managed=?
|
||||||
sec=rw-security
|
|
||||||
==
|
==
|
||||||
==
|
|
||||||
::
|
|
||||||
::
|
::
|
||||||
+$ action
|
+$ action
|
||||||
$% [%new-book book=@tas title=@t about=@t coms=? group=group-info]
|
$% [%new-book book=@tas title=@t about=@t coms=? group=group-info]
|
||||||
|
@ -80,16 +80,20 @@ a {
|
|||||||
background: rgba(42, 167, 121, 0.1);
|
background: rgba(42, 167, 121, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.focus-b--black:focus {
|
||||||
|
border-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mix-blend-diff {
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
}
|
||||||
|
|
||||||
.NotebookButton {
|
.NotebookButton {
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
border-radius:2px;
|
border-radius:2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.NotebookTab {
|
|
||||||
padding: 16px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.NewPost {
|
.NewPost {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 103px);
|
height: calc(100% - 103px);
|
||||||
@ -97,6 +101,33 @@ a {
|
|||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.placeholder-inter::placeholder {
|
||||||
|
font-family: "Inter", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* toggler checkbox */
|
||||||
|
.toggle::after {
|
||||||
|
content: "";
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
background: white;
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle.checked::after {
|
||||||
|
content: "";
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
background: white;
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 14px;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.react-codemirror2 {
|
.react-codemirror2 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@ -109,9 +140,10 @@ a {
|
|||||||
cursor: text;
|
cursor: text;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
* {
|
}
|
||||||
|
|
||||||
|
.CodeMirror * {
|
||||||
font-family: 'Source Code Pro';
|
font-family: 'Source Code Pro';
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-selected { background:#BAE3FE !important; }
|
.CodeMirror-selected { background:#BAE3FE !important; }
|
||||||
@ -175,6 +207,7 @@ a {
|
|||||||
top: 16px;
|
top: 16px;
|
||||||
color: #7f7f7f;
|
color: #7f7f7f;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-scrollbar {
|
.no-scrollbar {
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
|
299
pkg/interface/publish/src/js/components/lib/invite-search.js
Normal file
299
pkg/interface/publish/src/js/components/lib/invite-search.js
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import urbitOb from "urbit-ob";
|
||||||
|
import { Sigil } from "../lib/icons/sigil";
|
||||||
|
|
||||||
|
|
||||||
|
export class InviteSearch extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
groups: [],
|
||||||
|
peers: [],
|
||||||
|
searchValue: "",
|
||||||
|
searchResults: {
|
||||||
|
groups: [],
|
||||||
|
ships: []
|
||||||
|
},
|
||||||
|
inviteError: false
|
||||||
|
}
|
||||||
|
this.search = this.search.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.peerUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (prevProps !== this.props) {
|
||||||
|
this.peerUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
peerUpdate() {
|
||||||
|
let groups = Array.from(Object.keys(this.props.groups));
|
||||||
|
groups = groups.filter(e => !e.startsWith("/~/"));
|
||||||
|
|
||||||
|
let peers = [],
|
||||||
|
peerSet = new Set();
|
||||||
|
Object.keys(this.props.groups).map(group => {
|
||||||
|
if (this.props.groups[group].size > 0) {
|
||||||
|
peerSet.add(...this.props.groups[group]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
peers = Array.from(peerSet);
|
||||||
|
|
||||||
|
this.setState({ groups: groups, peers: peers });
|
||||||
|
}
|
||||||
|
|
||||||
|
search(event) {
|
||||||
|
let searchTerm = event.target.value.toLowerCase().replace("~", "");
|
||||||
|
|
||||||
|
this.setState({searchValue: event.target.value});
|
||||||
|
|
||||||
|
if (searchTerm.length < 2) {
|
||||||
|
this.setState({searchResults: { groups: [], ships: [] }})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchTerm.length > 2) {
|
||||||
|
if (this.state.inviteError === true) {
|
||||||
|
this.setState({inviteError: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
let groupMatches = this.state.groups.filter(e => {
|
||||||
|
return e.includes(searchTerm);
|
||||||
|
});
|
||||||
|
let shipMatches = this.state.peers.filter(e => {
|
||||||
|
return e.includes(searchTerm) && !this.props.invites.ships.includes(e);
|
||||||
|
});
|
||||||
|
this.setState({
|
||||||
|
searchResults: { groups: groupMatches, ships: shipMatches }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteGroup() {
|
||||||
|
let { ships } = this.props.invites;
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
searchValue: "",
|
||||||
|
searchResults: { groups: [], ships: [] }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.props.setInvite({ groups: [], ships: ships });
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteShip(ship) {
|
||||||
|
let { groups, ships } = this.props.invites;
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
searchValue: "",
|
||||||
|
searchResults: { groups: [], ships: [] }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ships = ships.filter(e => {
|
||||||
|
return e !== ship;
|
||||||
|
});
|
||||||
|
this.props.setInvite({ groups: groups, ships: ships });
|
||||||
|
}
|
||||||
|
|
||||||
|
addGroup(group) {
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
searchValue: "",
|
||||||
|
searchResults: { groups: [], ships: [] }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.props.setInvite({ groups: [group], ships: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
addShip(ship) {
|
||||||
|
let { groups, ships } = this.props.invites;
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
searchValue: "",
|
||||||
|
searchResults: { groups: [], ships: [] }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ships.push(ship);
|
||||||
|
if (groups.length > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.props.setInvite({ groups: groups, ships: ships });
|
||||||
|
}
|
||||||
|
|
||||||
|
submitShipToAdd(ship) {
|
||||||
|
let searchTerm = ship.toLowerCase().replace("~", "").trim();
|
||||||
|
let isValid = true;
|
||||||
|
if (!urbitOb.isValidPatp("~" + searchTerm)) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
if (!isValid) {
|
||||||
|
this.setState({ inviteError: true, searchValue: "" });
|
||||||
|
} else if (isValid) {
|
||||||
|
this.addShip(searchTerm);
|
||||||
|
this.setState({ searchValue: "" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { props, state } = this;
|
||||||
|
let searchDisabled = false;
|
||||||
|
if (props.invites.groups) {
|
||||||
|
if (props.invites.groups.length > 0) {
|
||||||
|
searchDisabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let participants = <div/>
|
||||||
|
let searchResults = <div/>
|
||||||
|
|
||||||
|
let invErrElem = <span />;
|
||||||
|
if (state.inviteError) {
|
||||||
|
invErrElem = (
|
||||||
|
<span className="f9 inter red2 db pt2">
|
||||||
|
Invited ships must be validly formatted ship names.
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((state.searchResults.groups.length > 0)
|
||||||
|
|| (state.searchResults.ships.length > 0)) {
|
||||||
|
|
||||||
|
let groupHeader = (state.searchResults.groups.length > 0)
|
||||||
|
? <p className="f9 gray2 ph3">Groups</p> : "";
|
||||||
|
|
||||||
|
let shipHeader = (state.searchResults.ships.length > 0)
|
||||||
|
? <p className="f9 gray2 pv2 ph3">Ships</p> : "";
|
||||||
|
|
||||||
|
let groupResults = state.searchResults.groups.map(group => {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={group}
|
||||||
|
className={
|
||||||
|
"list mono white-d f8 pv2 ph3 pointer" +
|
||||||
|
" hover-bg-gray4 hover-black-d"}
|
||||||
|
onClick={e => this.addGroup(group)}>
|
||||||
|
{group}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
let shipResults = state.searchResults.ships.map(ship => {
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={ship}
|
||||||
|
className={
|
||||||
|
"list mono white-d f8 pv1 ph3 pointer" +
|
||||||
|
" hover-bg-gray4 hover-black-d"
|
||||||
|
}
|
||||||
|
onClick={e => this.addShip(ship)}>
|
||||||
|
<Sigil
|
||||||
|
ship={"~" + ship}
|
||||||
|
size={24}
|
||||||
|
color="#000000"
|
||||||
|
classes="mix-blend-diff v-mid"
|
||||||
|
/>
|
||||||
|
<span className="v-mid ml2">{"~" + ship}</span>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
searchResults =
|
||||||
|
<div className={"absolute bg-white bg-gray0-d white-d" +
|
||||||
|
" pv3 z-1 w-100 mt1 ba b--white-d overflow-y-scroll mh-16"}>
|
||||||
|
{groupHeader}
|
||||||
|
{groupResults}
|
||||||
|
{shipHeader}
|
||||||
|
{shipResults}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
let groupInvites = props.invites.groups || [];
|
||||||
|
let shipInvites = props.invites.ships || [];
|
||||||
|
|
||||||
|
if (groupInvites.length > 0 || shipInvites.length > 0) {
|
||||||
|
let groups = groupInvites.map(group => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
key={group}
|
||||||
|
className={
|
||||||
|
"f9 mono black pa2 bg-gray5 bg-gray1-d" +
|
||||||
|
" ba b--gray4 b--gray2-d white-d dib mr2 mt2 c-default"}>
|
||||||
|
{group}
|
||||||
|
<span className="white-d ml3 mono pointer"
|
||||||
|
onClick={e => this.deleteGroup(group)}>
|
||||||
|
x
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ships = shipInvites.map(ship => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
key={ship}
|
||||||
|
className={"f9 mono black pa2 bg-gray5 bg-gray1-d" +
|
||||||
|
" ba b--gray4 b--gray2-d white-d dib mr2 mt2 c-default"}>
|
||||||
|
{"~" + ship}
|
||||||
|
<span className="white-d ml3 mono pointer"
|
||||||
|
onClick={e => this.deleteShip(ship)}>x</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
participants = (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"f9 gray2 bb bl br b--gray3 b--gray2-d bg-gray0-d " +
|
||||||
|
"white-d pa3 db w-100 inter"
|
||||||
|
}>
|
||||||
|
<span className="db gray2">Participants</span>
|
||||||
|
{groups} {ships}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<img
|
||||||
|
src="/~publish/search.png"
|
||||||
|
className="absolute invert-d"
|
||||||
|
style={{
|
||||||
|
height: 16,
|
||||||
|
width: 16,
|
||||||
|
top: 14,
|
||||||
|
left: 12
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<textarea
|
||||||
|
ref={e => {
|
||||||
|
this.textarea = e;
|
||||||
|
}}
|
||||||
|
className={"f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 w-100"
|
||||||
|
+ " db focus-b--black focus-b--white-d mono placeholder-inter"}
|
||||||
|
placeholder="Search for ships or existing groups"
|
||||||
|
disabled={searchDisabled}
|
||||||
|
rows={1}
|
||||||
|
spellCheck={false}
|
||||||
|
style={{
|
||||||
|
resize: "none",
|
||||||
|
paddingLeft: 36
|
||||||
|
}}
|
||||||
|
onKeyPress={e => {
|
||||||
|
if (e.key === "Enter" || e.key === ",") {
|
||||||
|
e.preventDefault();
|
||||||
|
this.submitShipToAdd(this.state.searchValue);
|
||||||
|
}}}
|
||||||
|
onChange={this.search}
|
||||||
|
value={state.searchValue}
|
||||||
|
/>
|
||||||
|
{searchResults}
|
||||||
|
{participants}
|
||||||
|
{invErrElem}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InviteSearch;
|
@ -64,8 +64,6 @@ export class JoinScreen extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('actionData', actionData);
|
|
||||||
|
|
||||||
// TODO: askHistory setting
|
// TODO: askHistory setting
|
||||||
window.api.action("publish","publish-action", actionData);
|
window.api.action("publish","publish-action", actionData);
|
||||||
|
|
||||||
|
@ -1,16 +1,191 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react';
|
||||||
|
import { InviteSearch } from './invite-search';
|
||||||
|
import { Route, Link } from 'react-router-dom';
|
||||||
|
import { uuid, isPatTa, deSig, stringToSymbol } from "/lib/util";
|
||||||
|
import urbitOb from 'urbit-ob';
|
||||||
|
|
||||||
//TODO textarea fields for title/description
|
|
||||||
//TODO add component for ship / group search using props.groups
|
|
||||||
// (integrate props.contacts as well once contact-view is bound)
|
|
||||||
export class NewScreen extends Component {
|
export class NewScreen extends Component {
|
||||||
render() {
|
constructor(props) {
|
||||||
return (
|
super(props);
|
||||||
<div>
|
|
||||||
|
|
||||||
</div>
|
this.state = {
|
||||||
)
|
idName: '',
|
||||||
|
description: '',
|
||||||
|
invites: {
|
||||||
|
groups: [],
|
||||||
|
ships: []
|
||||||
|
},
|
||||||
|
createGroup: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.idChange = this.idChange.bind(this);
|
||||||
|
this.descriptionChange = this.descriptionChange.bind(this);
|
||||||
|
this.setInvite = this.setInvite.bind(this);
|
||||||
|
this.createGroupChange = this.createGroupChange.bind(this);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default NewScreen
|
componentDidUpdate(prevProps) {
|
||||||
|
const { props, state } = this;
|
||||||
|
if (props.notebooks && (("~" + window.ship) in props.notebooks)) {
|
||||||
|
let notebookId = stringToSymbol(state.idName)
|
||||||
|
if (notebookId in props.notebooks["~" + window.ship]) {
|
||||||
|
let notebook = `/~${window.ship}/${notebookId}`;
|
||||||
|
props.history.push("/~publish/notebook" + notebook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
idChange(event) {
|
||||||
|
this.setState({
|
||||||
|
idName: event.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionChange(event) {
|
||||||
|
this.setState({
|
||||||
|
description: event.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createGroupChange(event) {
|
||||||
|
this.setState({createGroup: !!event.target.checked});
|
||||||
|
}
|
||||||
|
|
||||||
|
setInvite(value) {
|
||||||
|
this.setState({invites: value})
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickCreate() {
|
||||||
|
const { props, state } = this;
|
||||||
|
let bookId = stringToSymbol(state.idName);
|
||||||
|
let groupInfo = (state.invites.groups.length > 0)
|
||||||
|
? {
|
||||||
|
"group-path": state.invites.groups[0],
|
||||||
|
"invitees": [],
|
||||||
|
"use-preexisting": true,
|
||||||
|
"make-managed": false,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
// TODO remove /~ and set make-managed on toggle
|
||||||
|
"group-path": `/~/publish/~${window.ship}/${bookId}`,
|
||||||
|
"invitees": state.invites.ships,
|
||||||
|
"use-preexisting": false,
|
||||||
|
"make-managed": false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let action = {
|
||||||
|
"new-book": {
|
||||||
|
book: bookId,
|
||||||
|
title: state.idName,
|
||||||
|
about: state.description,
|
||||||
|
coms: true,
|
||||||
|
group: groupInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
props.api.action("publish", "publish-action", action);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// let createGroupClasses = this.state.createGroup
|
||||||
|
// ? "relative checked bg-green2 br3 h1 toggle v-mid z-0"
|
||||||
|
// : "relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0";
|
||||||
|
|
||||||
|
let createClasses = "pointer db f9 green2 bg-gray0-d ba pv3 ph4 mv7 b--green2";
|
||||||
|
if (!this.state.idName) {
|
||||||
|
createClasses = "pointer db f9 gray2 ba bg-gray0-d pa2 pv3 ph4 mv7 b--gray3";
|
||||||
|
}
|
||||||
|
|
||||||
|
// let createGroupToggle = <div/>
|
||||||
|
// if ((this.state.invites.ships.length > 0) && (this.state.invites.groups.length === 0)) {
|
||||||
|
// createGroupToggle = (
|
||||||
|
// <div className="mv7">
|
||||||
|
// <input
|
||||||
|
// type="checkbox"
|
||||||
|
// style={{ WebkitAppearance: "none", width: 28 }}
|
||||||
|
// className={createGroupClasses}
|
||||||
|
// onChange={this.createGroupChange}
|
||||||
|
// />
|
||||||
|
// <span className="dib f9 white-d inter ml3">Create Group</span>
|
||||||
|
// <p className="f9 gray2 pt1" style={{ paddingLeft: 40 }}>
|
||||||
|
// Participants will share this group across applications
|
||||||
|
// </p>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
let idErrElem = <span />;
|
||||||
|
if (this.state.idError) {
|
||||||
|
idErrElem = (
|
||||||
|
<span className="f9 inter red2 db pt2">
|
||||||
|
Notebook must have a valid name.
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"h-100 w-100 mw6 pa3 pt4 overflow-x-hidden flex flex-column"
|
||||||
|
}>
|
||||||
|
<div className="w-100 dn-m dn-l dn-xl inter pt1 pb6 f8">
|
||||||
|
<Link to="/~publish/">{"⟵ All Notebooks"}</Link>
|
||||||
|
</div>
|
||||||
|
<h2 className="mb3 f8">New Notebook</h2>
|
||||||
|
<div className="w-100">
|
||||||
|
<p className="f8 mt3 lh-copy db">Name</p>
|
||||||
|
<p className="f9 gray2 db mb2 pt1">
|
||||||
|
Provide a name for your notebook
|
||||||
|
</p>
|
||||||
|
<textarea
|
||||||
|
className="f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 db w-100"
|
||||||
|
placeholder="eg. My Journal"
|
||||||
|
rows={1}
|
||||||
|
style={{
|
||||||
|
resize: "none"
|
||||||
|
}}
|
||||||
|
onChange={this.idChange}
|
||||||
|
value={this.state.idName}
|
||||||
|
/>
|
||||||
|
{idErrElem}
|
||||||
|
<p className="f8 mt4 lh-copy db">
|
||||||
|
Description
|
||||||
|
<span className="gray3 ml1">(Optional)</span>
|
||||||
|
</p>
|
||||||
|
<p className="f9 gray2 db mb2 pt1">What's your notebook about?</p>
|
||||||
|
<textarea
|
||||||
|
className="f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 db w-100"
|
||||||
|
placeholder="Notebook description"
|
||||||
|
rows={1}
|
||||||
|
style={{
|
||||||
|
resize: "none"
|
||||||
|
}}
|
||||||
|
onChange={this.descriptionChange}
|
||||||
|
value={this.state.description}
|
||||||
|
/>
|
||||||
|
<p className="f8 mt4 lh-copy db">
|
||||||
|
Invite
|
||||||
|
<span className="gray3 ml1">
|
||||||
|
(Optional)
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="f9 gray2 db mb2 pt1">Select an initial read-only audience for your notebook</p>
|
||||||
|
<InviteSearch
|
||||||
|
groups={this.props.groups}
|
||||||
|
invites={this.state.invites}
|
||||||
|
setInvite={this.setInvite}
|
||||||
|
/>
|
||||||
|
{/* {createGroupToggle} */}
|
||||||
|
<button
|
||||||
|
onClick={this.onClickCreate.bind(this)}
|
||||||
|
className={createClasses}>
|
||||||
|
Create Notebook
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewScreen;
|
||||||
|
@ -60,12 +60,12 @@ export class Notebook extends Component {
|
|||||||
render() {
|
render() {
|
||||||
let notebook = this.props.notebooks[this.props.ship][this.props.book];
|
let notebook = this.props.notebooks[this.props.ship][this.props.book];
|
||||||
let tabStyles = {
|
let tabStyles = {
|
||||||
posts: "bb b--gray4 gray2 NotebookTab",
|
posts: "bb b--gray4 gray2 pv4 ph2",
|
||||||
about: "bb b--gray4 gray2 NotebookTab",
|
about: "bb b--gray4 gray2 pv4 ph2"
|
||||||
// subscribers: "bb b--gray4 gray2 NotebookTab",
|
// subscribers: "bb b--gray4 gray2 pv4 ph2",
|
||||||
// settings: "bb b--gray4 pr2 gray2 NotebookTab",
|
// settings: "bb b--gray4 pr2 gray2 pv4 ph2",
|
||||||
}
|
};
|
||||||
tabStyles[this.props.view] = "bb b--black black NotebookTab";
|
tabStyles[this.props.view] = "bb b--black black pv4 ph2";
|
||||||
|
|
||||||
let inner = null;
|
let inner = null;
|
||||||
switch (this.props.view) {
|
switch (this.props.view) {
|
||||||
@ -111,19 +111,23 @@ export class Notebook extends Component {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="center mw6 f9 h-100"
|
<div
|
||||||
style={{paddingLeft:16, paddingRight:16}}>
|
className="center mw6 f9 h-100"
|
||||||
<div className="h-100 overflow-container no-scrollbar"
|
style={{ paddingLeft: 16, paddingRight: 16 }}>
|
||||||
|
<div
|
||||||
|
className="h-100 overflow-container no-scrollbar"
|
||||||
onScroll={this.onScroll}
|
onScroll={this.onScroll}
|
||||||
ref={(el) => {this.scrollElement = el}}>
|
ref={el => {
|
||||||
<div className="flex justify-between"
|
this.scrollElement = el;
|
||||||
style={{marginTop: 56, marginBottom: 32}}>
|
}}>
|
||||||
|
<div
|
||||||
|
className="flex justify-between"
|
||||||
|
style={{ marginTop: 56, marginBottom: 32 }}>
|
||||||
<div className="flex-col">
|
<div className="flex-col">
|
||||||
<div className="mb1">{notebook.title}</div>
|
<div className="mb1">{notebook.title}</div>
|
||||||
<span><span className="gray3 mr1">by</span>
|
<span>
|
||||||
<span className="mono">
|
<span className="gray3 mr1">by</span>
|
||||||
{this.props.ship}
|
<span className="mono">{this.props.ship}</span>
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
@ -132,23 +136,24 @@ export class Notebook extends Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex" style={{marginBottom:24}}>
|
<div className="flex" style={{ marginBottom: 24 }}>
|
||||||
<Link to={base} className={tabStyles.posts}>
|
<Link to={base} className={tabStyles.posts}>
|
||||||
All Posts
|
All Posts
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={about} className={tabStyles.about}>
|
<Link to={about} className={tabStyles.about}>
|
||||||
About
|
About
|
||||||
</Link>
|
</Link>
|
||||||
<div className="bb b--gray4 gray2 NotebookTab" style={{flexGrow:1}}>
|
<div
|
||||||
</div>
|
className="bb b--gray4 gray2 pv4 ph2"
|
||||||
|
style={{ flexGrow: 1 }}></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{height:"calc(100% - 188px)"}} className="f9 lh-solid">
|
<div style={{ height: "calc(100% - 188px)" }} className="f9 lh-solid">
|
||||||
{inner}
|
{inner}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
key:{}
|
|
||||||
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { BrowserRouter, Route, Link } from "react-router-dom";
|
import { BrowserRouter, Route, Link } from "react-router-dom";
|
||||||
import { store } from '/store';
|
import { store } from '/store';
|
||||||
|
import { api } from '/api';
|
||||||
import { Skeleton } from '/components/skeleton';
|
import { Skeleton } from '/components/skeleton';
|
||||||
import { NewScreen } from '/components/lib/new';
|
import { NewScreen } from '/components/lib/new';
|
||||||
import { JoinScreen } from '/components/lib/join';
|
import { JoinScreen } from '/components/lib/join';
|
||||||
@ -8,6 +9,7 @@ import { Notebook } from '/components/lib/notebook';
|
|||||||
import { Note } from '/components/lib/note';
|
import { Note } from '/components/lib/note';
|
||||||
import { NewPost } from '/components/lib/new-post';
|
import { NewPost } from '/components/lib/new-post';
|
||||||
|
|
||||||
|
|
||||||
export class Root extends Component {
|
export class Root extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -52,7 +54,11 @@ export class Root extends Component {
|
|||||||
sidebarShown={true}
|
sidebarShown={true}
|
||||||
notebooks={state.notebooks}>
|
notebooks={state.notebooks}>
|
||||||
<NewScreen
|
<NewScreen
|
||||||
groups={state.groups}/>
|
notebooks={state.notebooks}
|
||||||
|
groups={state.groups}
|
||||||
|
api={api}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
)
|
)
|
||||||
}}/>
|
}}/>
|
||||||
|
68
pkg/interface/publish/src/js/reducers/group.js
Normal file
68
pkg/interface/publish/src/js/reducers/group.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
|
export class GroupReducer {
|
||||||
|
reduce(json, state) {
|
||||||
|
let data = _.get(json, "group-initial", false);
|
||||||
|
if (data) {
|
||||||
|
for (let group in data) {
|
||||||
|
state.groups[group] = new Set(data[group]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data = _.get(json, "group-update", false);
|
||||||
|
if (data) {
|
||||||
|
this.add(data, state);
|
||||||
|
this.remove(data, state);
|
||||||
|
this.bundle(data, state);
|
||||||
|
this.unbundle(data, state);
|
||||||
|
this.keys(data, state);
|
||||||
|
this.path(data, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add(json, state) {
|
||||||
|
let data = _.get(json, "add", false);
|
||||||
|
if (data) {
|
||||||
|
for (let member of data.members) {
|
||||||
|
state.groups[data.path].add(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(json, state) {
|
||||||
|
let data = _.get(json, "remove", false);
|
||||||
|
if (data) {
|
||||||
|
for (let member of data.members) {
|
||||||
|
state.groups[data.path].delete(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle(json, state) {
|
||||||
|
let data = _.get(json, "bundle", false);
|
||||||
|
if (data) {
|
||||||
|
state.groups[data.path] = new Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unbundle(json, state) {
|
||||||
|
let data = _.get(json, "unbundle", false);
|
||||||
|
if (data) {
|
||||||
|
delete state.groups[data.path];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys(json, state) {
|
||||||
|
let data = _.get(json, "keys", false);
|
||||||
|
if (data) {
|
||||||
|
state.groupKeys = new Set(data.keys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path(json, state) {
|
||||||
|
let data = _.get(json, "path", false);
|
||||||
|
if (data) {
|
||||||
|
state.groups[data.path] = new Set([data.members]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
pkg/interface/publish/src/js/reducers/invite.js
Normal file
58
pkg/interface/publish/src/js/reducers/invite.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export class InviteReducer {
|
||||||
|
reduce(json, state) {
|
||||||
|
let initial = _.get(json, 'invite-initial', false);
|
||||||
|
if (initial) {
|
||||||
|
this.initial(initial, state);
|
||||||
|
}
|
||||||
|
let update = _.get(json, 'invite-update', false);
|
||||||
|
if (update) {
|
||||||
|
this.create(update, state);
|
||||||
|
this.delete(update, state);
|
||||||
|
this.invite(update, state);
|
||||||
|
this.accepted(update, state);
|
||||||
|
this.decline(update, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initial(json, state) {
|
||||||
|
state.invites = json;;
|
||||||
|
}
|
||||||
|
|
||||||
|
create(json, state) {
|
||||||
|
let data = _.get(json, 'create', false);
|
||||||
|
if (data) {
|
||||||
|
state.invites[data.path] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(json, state) {
|
||||||
|
let data = _.get(json, 'delete', false);
|
||||||
|
if (data) {
|
||||||
|
delete state.invites[data.path];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invite(json, state) {
|
||||||
|
let data = _.get(json, 'invite', false);
|
||||||
|
if (data) {
|
||||||
|
state.invites[data.path][data.uid] = data.invite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accepted(json, state) {
|
||||||
|
let data = _.get(json, 'accepted', false);
|
||||||
|
if (data) {
|
||||||
|
delete state.invites[data.path][data.uid];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decline(json, state) {
|
||||||
|
let data = _.get(json, 'decline', false);
|
||||||
|
if (data) {
|
||||||
|
delete state.invites[data.path][data.uid];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import _ from 'lodash';
|
|||||||
export class PrimaryReducer {
|
export class PrimaryReducer {
|
||||||
reduce(json, state){
|
reduce(json, state){
|
||||||
switch(Object.keys(json)[0]){
|
switch(Object.keys(json)[0]){
|
||||||
|
//publish actions
|
||||||
case "add-book":
|
case "add-book":
|
||||||
this.addBook(json["add-book"], state);
|
this.addBook(json["add-book"], state);
|
||||||
break;
|
break;
|
||||||
@ -33,6 +34,12 @@ export class PrimaryReducer {
|
|||||||
case "read":
|
case "read":
|
||||||
this.read(json["read"], state);
|
this.read(json["read"], state);
|
||||||
break;
|
break;
|
||||||
|
// contacts actions
|
||||||
|
case "contact-initial":
|
||||||
|
this.contactInitial(json["contact-initial"], state);
|
||||||
|
break;
|
||||||
|
case "contact-update":
|
||||||
|
this.contactUpdate(json["contact-update"], state);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -225,4 +232,64 @@ export class PrimaryReducer {
|
|||||||
state.notebooks[host][book].notes[noteId]["read"] = true;
|
state.notebooks[host][book].notes[noteId]["read"] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contactInitial(json, state) {
|
||||||
|
state.contacts = json;
|
||||||
|
}
|
||||||
|
|
||||||
|
contactUpdate(json, state) {
|
||||||
|
this.createContact(json, state);
|
||||||
|
this.deleteContact(json, state);
|
||||||
|
this.addContact(json, state);
|
||||||
|
this.removeContact(json, state);
|
||||||
|
this.editContact(json, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
createContact(json, state) {
|
||||||
|
let data = _.get(json, "create", false);
|
||||||
|
if (data) {
|
||||||
|
state.contacts[data.path] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteContact(json, state) {
|
||||||
|
let data = _.get(json, "delete", false);
|
||||||
|
if (data) {
|
||||||
|
delete state.contacts[data.path];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addContact(json, state) {
|
||||||
|
let data = _.get(json, "add", false);
|
||||||
|
if (data && data.path in state.contacts) {
|
||||||
|
state.contacts[data.path][data.ship] = data.contact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeContact(json, state) {
|
||||||
|
let data = _.get(json, "remove", false);
|
||||||
|
if (
|
||||||
|
data &&
|
||||||
|
data.path in state.contacts &&
|
||||||
|
data.ship in state.contacts[data.path]
|
||||||
|
) {
|
||||||
|
delete state.contacts[data.path][data.ship];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editContact(json, state) {
|
||||||
|
let data = _.get(json, "edit", false);
|
||||||
|
if (
|
||||||
|
data &&
|
||||||
|
data.path in state.contacts &&
|
||||||
|
data.ship in state.contacts[data.path]
|
||||||
|
) {
|
||||||
|
let edit = Object.keys(data["edit-field"]);
|
||||||
|
if (edit.length !== 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.contacts[data.path][data.ship][edit[0]] =
|
||||||
|
data["edit-field"][edit[0]];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
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 { InviteReducer } from '/reducers/invite';
|
||||||
|
|
||||||
class Store {
|
class Store {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.state = {
|
this.state = {
|
||||||
notebooks: {},
|
notebooks: {},
|
||||||
groups: {},
|
groups: {},
|
||||||
|
contacts: {},
|
||||||
permissions: {},
|
permissions: {},
|
||||||
invites: {},
|
invites: {},
|
||||||
spinner: false,
|
spinner: false,
|
||||||
@ -16,6 +19,8 @@ class Store {
|
|||||||
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.inviteReducer = new InviteReducer();
|
||||||
this.setState = () => {};
|
this.setState = () => {};
|
||||||
|
|
||||||
this.initialReducer.reduce(window.injectedState, this.state);
|
this.initialReducer.reduce(window.injectedState, this.state);
|
||||||
@ -26,8 +31,12 @@ class Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(evt) {
|
handleEvent(evt) {
|
||||||
if (evt.from && evt.from.path === '/primary'){
|
if (evt.from && evt.from.path === '/all') {
|
||||||
|
this.groupReducer.reduce(evt.data, this.state);
|
||||||
|
}
|
||||||
|
else if (evt.from && evt.from.path === '/primary'){
|
||||||
this.primaryReducer.reduce(evt.data, this.state);
|
this.primaryReducer.reduce(evt.data, this.state);
|
||||||
|
this.inviteReducer.reduce(evt.data, this.state);
|
||||||
} else if (evt.type) {
|
} else if (evt.type) {
|
||||||
this.responseReducer.reduce(evt, this.state);
|
this.responseReducer.reduce(evt, this.state);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,15 @@ export class Subscription {
|
|||||||
api.bind(`/primary`, "PUT", api.authTokens.ship, 'publish',
|
api.bind(`/primary`, "PUT", api.authTokens.ship, 'publish',
|
||||||
this.handleEvent.bind(this),
|
this.handleEvent.bind(this),
|
||||||
this.handleError.bind(this));
|
this.handleError.bind(this));
|
||||||
|
api.bind('/all', 'PUT', api.authTokens.ship, 'group-store',
|
||||||
|
this.handleEvent.bind(this),
|
||||||
|
this.handleError.bind(this));
|
||||||
|
api.bind('/primary', 'PUT', api.authTokens.ship, 'contact-view',
|
||||||
|
this.handleEvent.bind(this),
|
||||||
|
this.handleError.bind(this));
|
||||||
|
api.bind('/primary', 'PUT', api.authTokens.ship, 'invite-view',
|
||||||
|
this.handleEvent.bind(this),
|
||||||
|
this.handleError.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(diff) {
|
handleEvent(diff) {
|
||||||
|
Loading…
Reference in New Issue
Block a user