From 7eea3de7bb7faaebadc895c505919e5a186d6bfa Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 2 Sep 2020 15:17:34 +1000 Subject: [PATCH 01/82] lib/strandio: add +scry --- pkg/arvo/lib/strandio.hoon | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/arvo/lib/strandio.hoon b/pkg/arvo/lib/strandio.hoon index 71299cb8bc..8da72d0bea 100644 --- a/pkg/arvo/lib/strandio.hoon +++ b/pkg/arvo/lib/strandio.hoon @@ -241,6 +241,15 @@ ;< our=@p bind:m get-our (watch wire [our term] path) :: +++ scry + |* [=mold =path] + =/ m (strand mold) + ?> ?=(^ path) + ?> ?=(^ t.path) + ;< =bowl:spider bind:m get-bowl + %- pure:m + .^(mold i.path (scot %p our.bowl) i.t.path (scot %da now.bowl) t.t.path) +:: ++ leave |= [=wire =dock] =/ m (strand ,~) From 18755b63ce20193b30d86bc154ff3c61047f474e Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Wed, 2 Sep 2020 15:18:07 +1000 Subject: [PATCH 02/82] graph-view: added create,join threads --- pkg/arvo/sur/graph-view.hoon | 25 +++++++++++++++ pkg/arvo/ted/graph/create.hoon | 57 ++++++++++++++++++++++++++++++++++ pkg/arvo/ted/graph/join.hoon | 52 +++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 pkg/arvo/sur/graph-view.hoon create mode 100644 pkg/arvo/ted/graph/create.hoon create mode 100644 pkg/arvo/ted/graph/join.hoon diff --git a/pkg/arvo/sur/graph-view.hoon b/pkg/arvo/sur/graph-view.hoon new file mode 100644 index 0000000000..0c0bfdc271 --- /dev/null +++ b/pkg/arvo/sur/graph-view.hoon @@ -0,0 +1,25 @@ +/- group +/+ resource +^? +|% +++ error + $% + [%offline =ship] + [%no-permissions ~] + == +++ action + $% + $: %create + rid=resource + title=@t + description=@t + mark=(unit mark) + associated=(each resource policy:group) + == + [%delete rid=resource ~] + [%join rid=resource =ship] + [%invite rid=resource ships=(set ship)] + [%groupify rid=resource title=@t description=@t] + == +-- + diff --git a/pkg/arvo/ted/graph/create.hoon b/pkg/arvo/ted/graph/create.hoon new file mode 100644 index 0000000000..54fca38522 --- /dev/null +++ b/pkg/arvo/ted/graph/create.hoon @@ -0,0 +1,57 @@ +/- spider, graph-view, graph=graph-store, *metadata-store, *group, group-store +/+ strandio, resource +=> +|% +++ strand strand:spider +++ poke poke:strandio +++ poke-our poke-our:strandio +++ handle-group + |= [rid=resource associated=(each resource policy)] + =/ m (strand ,resource) + ?: ?=(%& -.associated) + (pure:m p.associated) + =/ =action:group-store + [%add-group rid p.associated %&] + ;< ~ bind:m (poke-our %group-store %group-action !>(action)) + ;< ~ bind:m + (poke-our %group-push-hook %push-hook-action !>([%add rid])) + (pure:m rid) + +-- +=, strand=strand:spider +^- thread:spider +|= arg=vase +=/ m (strand ,vase) +^- form:m +=+ !<([=action:graph-view ~] arg) +?> ?=(%create -.action) +;< =bowl:spider bind:m get-bowl:strandio +:: Add graph to graph-store +:: +=/ =update:graph + [%0 now.bowl %add-graph rid.action *graph:graph mark.action] +;< ~ bind:m (poke-our %graph-store %graph-update !>(update)) +::;< ~ bind:m +:: (poke-our %graph-push-hook %push-hook-action !>([%add rid.action])) +:: Add group, if graph is unmanaged +:: +;< group=resource bind:m + (handle-group rid.action associated.action) +=/ group-path=path + (en-path:resource group) +:: Setup metadata +:: +=/ =metadata + %* . *metadata + title title.action + description description.action + date-created now.bowl + creator our.bowl + == +=/ act=metadata-action + [%add group-path [%graph (en-path:resource rid.action)] metadata] +;< ~ bind:m (poke-our %metadata-hook %metadata-action !>(act)) +;< ~ bind:m + (poke-our %metadata-hook %metadata-hook-action !>([%add-owned group-path])) +(pure:m !>(~)) + diff --git a/pkg/arvo/ted/graph/join.hoon b/pkg/arvo/ted/graph/join.hoon new file mode 100644 index 0000000000..968e842120 --- /dev/null +++ b/pkg/arvo/ted/graph/join.hoon @@ -0,0 +1,52 @@ +/- spider, graph-view, graph=graph-store, *metadata-store, *group +/+ strandio, resource +=> +|% +++ strand strand:spider +++ poke poke:strandio +++ poke-our poke-our:strandio +++ scry-metadata + |= rid=resource + =/ m (strand ,(unit resource)) + ;< pax=(unit path) bind:m + %+ scry:strandio ,(unit path) + ;: weld + /gx/metadata-store/resource/graph + (en-path:resource rid) + /noun + == + (pure:m (bind pax de-path:resource)) +-- +^- thread:spider +|= arg=vase +=/ m (strand ,vase) +^- form:m +=+ !<(=action:graph-view arg) +?> ?=(%join -.action) +;< =bowl:spider bind:m get-bowl:strandio +?< =(our.bowl entity.rid.action) +;< group=(unit resource) bind:m + (scry-metadata rid.action) +?^ group + :: We have group, graph is managed + :: ;< ~ bind:m + :: %+ poke-our %graph-pull-hook + :: pull-hook-action+!>([%add ship.action rid.action]) + (pure:m !>(~)) +:: Else, add group then join +;< ~ bind:m + %+ poke + [ship.action %group-push-hook] + group-update+!>([%add-members rid.action (sy our.bowl ~)]) +:: +;< ~ bind:m + %+ poke-our %group-pull-hook + pull-hook-action+!>([%add ship.action rid.action]) +:: +;< ~ bind:m + %+ poke-our %metadata-hook + metadata-hook-action+!>([%add-synced ship.action rid.action]) +;< ~ bind:m + (poke-our %graph-pull-hook %pull-hook-action !>([%add ship.action rid.action])) +(pure:m !>(~)) + From 24658275d52b668df807733e80b8424c53b2b809 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 3 Sep 2020 14:47:34 +1000 Subject: [PATCH 03/82] spider: add HTTP interface --- pkg/arvo/app/spider.hoon | 79 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/pkg/arvo/app/spider.hoon b/pkg/arvo/app/spider.hoon index a6286ba8cb..051c66bfa0 100644 --- a/pkg/arvo/app/spider.hoon +++ b/pkg/arvo/app/spider.hoon @@ -1,5 +1,5 @@ /- spider -/+ libstrand=strand, default-agent, verb +/+ libstrand=strand, default-agent, verb, server =, strand=strand:libstrand |% +$ card card:agent:gall @@ -17,6 +17,7 @@ $: starting=(map yarn [=trying =vase]) running=trie tid=(map tid yarn) + serving=(map tid [@ta =mark]) == :: +$ clean-slate-any @@ -30,18 +31,21 @@ starting=(map yarn [=trying =vase]) running=(list yarn) tid=(map tid yarn) + serving=(map tid [@ta =mark]) == :: +$ clean-slate-ket $: starting=(map yarn [trying=?(%build %find %none) =vase]) running=(list yarn) tid=(map tid yarn) + serving=(map tid [@ta =mark]) == :: +$ clean-slate-sig $: starting=~ running=(list yarn) tid=(map tid yarn) + serving=(map tid [@ta =mark]) == :: +$ start-args @@ -133,7 +137,10 @@ sc ~(. spider-core bowl) def ~(. (default-agent this %|) bowl) :: - ++ on-init on-init:def + ++ on-init + ^- (quip card _this) + :_ this + [%pass /bind %arvo %e %connect [~ /spider] %spider]~ ++ on-save clean-state:sc ++ on-load |^ @@ -172,6 +179,9 @@ %spider-input (on-poke-input:sc !<(input vase)) %spider-start (handle-start-thread:sc !<(start-args vase)) %spider-stop (handle-stop-thread:sc !<([tid ?] vase)) + :: + %handle-http-request + (handle-http-request:sc !<([@ta =inbound-request:eyre] vase)) == [cards this] :: @@ -182,6 +192,7 @@ ?+ path (on-watch:def path) [%thread @ *] (on-watch:sc t.path) [%thread-result @ ~] (on-watch-result:sc i.t.path) + [%http-response *] `state == [cards this] :: @@ -228,6 +239,36 @@ -- :: |_ =bowl:gall +++ handle-http-request + |= [eyre-id=@ta =inbound-request:eyre] + ^- (quip card _state) + ?> authenticated.inbound-request + =/ url + (parse-request-line:server url.request.inbound-request) + ?> ?=([%spider @t @t @t ~] site.url) + =* input-mark i.t.site.url + =* thread i.t.t.site.url + =* output-mark i.t.t.t.site.url + =/ =tid + (scot %uv (sham eny.bowl)) + =. serving.state + (~(put by serving.state) tid [eyre-id output-mark]) + =+ .^ + =tube:clay + %cc + /(scot %p our.bowl)/[q.byk.bowl]/(scot %da now.bowl)/json/[input-mark] + == + ?> ?=(^ body.request.inbound-request) + =/ body=json + (need (de-json:html q.u.body.request.inbound-request)) + =/ input=vase + (tube !>(body)) + =/ =start-args + [~ `tid thread input] + =^ cards state + (handle-start-thread start-args) + [cards state] +:: ++ on-poke-input |= input =/ yarn (~(got by tid.state) tid) @@ -394,6 +435,17 @@ :~ [%give %fact ~[/thread-result/[tid]] %thread-fail !>([term tang])] [%give %kick ~[/thread-result/[tid]] ~] == +++ thread-http-fail + |= [=tid =term =tang] + ^- (quip card ^state) + =- (fall - `state) + %+ bind + (~(get by serving.state) tid) + |= [eyre-id=@ta output=mark] + :_ state(serving (~(del by serving.state) tid)) + %+ give-simple-payload:app:server eyre-id + ^- simple-payload:http + [[500 ~] ~] :: ++ thread-fail |= [=yarn =term =tang] @@ -402,7 +454,24 @@ =/ =tid (yarn-to-tid yarn) =/ fail-cards (thread-say-fail tid term tang) =^ cards state (thread-clean yarn) - [(weld fail-cards cards) state] + =^ http-cards state (thread-http-fail tid term tang) + [:(weld fail-cards cards http-cards) state] +:: +++ thread-http-response + |= [=tid =vase] + ^- (quip card ^state) + =- (fall - `state) + %+ bind + (~(get by serving.state) tid) + |= [eyre-id=@ta output=mark] + =+ .^ + =tube:clay + %cc + /(scot %p our.bowl)/[q.byk.bowl]/(scot %da now.bowl)/[output]/json + == + :_ state(serving (~(del by serving.state) tid)) + %+ give-simple-payload:app:server eyre-id + (json-response:gen:server !<(json (tube vase))) :: ++ thread-done |= [=yarn =vase] @@ -413,8 +482,10 @@ :~ [%give %fact ~[/thread-result/[tid]] %thread-done vase] [%give %kick ~[/thread-result/[tid]] ~] == + =^ http-cards state + (thread-http-response tid vase) =^ cards state (thread-clean yarn) - [(weld done-cards cards) state] + [:(weld done-cards cards http-cards) state] :: ++ thread-clean |= =yarn From b1093f0749a31947ef6242107a58d7c5bf8d3a36 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 3 Sep 2020 15:04:11 +1000 Subject: [PATCH 04/82] spider: add state adapters for HTTP interface --- pkg/arvo/app/spider.hoon | 44 ++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/pkg/arvo/app/spider.hoon b/pkg/arvo/app/spider.hoon index 051c66bfa0..51e6c60cf2 100644 --- a/pkg/arvo/app/spider.hoon +++ b/pkg/arvo/app/spider.hoon @@ -23,29 +23,35 @@ +$ clean-slate-any $^ clean-slate-ket $% clean-slate-sig + clean-slate-1 clean-slate == :: +$ clean-slate - $: %1 + $: %2 starting=(map yarn [=trying =vase]) running=(list yarn) tid=(map tid yarn) serving=(map tid [@ta =mark]) == :: ++$ clean-slate-1 + $: %1 + starting=(map yarn [=trying =vase]) + running=(list yarn) + tid=(map tid yarn) + == +:: +$ clean-slate-ket $: starting=(map yarn [trying=?(%build %find %none) =vase]) running=(list yarn) tid=(map tid yarn) - serving=(map tid [@ta =mark]) == :: +$ clean-slate-sig $: starting=~ running=(list yarn) tid=(map tid yarn) - serving=(map tid [@ta =mark]) == :: +$ start-args @@ -140,7 +146,7 @@ ++ on-init ^- (quip card _this) :_ this - [%pass /bind %arvo %e %connect [~ /spider] %spider]~ + ~[bind-eyre:sc] ++ on-save clean-state:sc ++ on-load |^ @@ -148,7 +154,9 @@ =+ !<(any=clean-slate-any old-state) =? any ?=(^ -.any) (old-to-1 any) =? any ?=(~ -.any) (old-to-1 any) - ?> ?=(%1 -.any) + =^ upgrade-cards any + (old-to-2 any) + ?> ?=(%2 -.any) :: =. tid.state tid.any =/ yarns=(list yarn) @@ -161,12 +169,26 @@ (handle-stop-thread:sc (yarn-to-tid i.yarns) |) =^ cards-2 this $(yarns t.yarns) - [(weld cards-1 cards-2) this] + [:(weld upgrade-cards cards-1 cards-2) this] :: ++ old-to-1 |= old=clean-slate-ket - ^- clean-slate + ^- clean-slate-1 1+old(starting (~(run by starting.old) |=([* v=vase] none+v))) + :: + ++ old-to-2 + |= old=clean-slate-any + ^- (quip card clean-slate) + ?> ?=(?(%1 %2) -.old) + ?: ?=(%2 -.old) + `old + :- ~[bind-eyre:sc] + :* %2 + starting.old + running.old + tid.old + ~ + == -- :: ++ on-poke @@ -227,6 +249,7 @@ ?+ wire (on-arvo:def wire sign-arvo) [%thread @ *] (handle-sign:sc i.t.wire t.t.wire sign-arvo) [%build @ ~] (handle-build:sc i.t.wire sign-arvo) + [%bind ~] `state == [cards this] :: On unexpected failure, kill all outstanding strands @@ -239,6 +262,11 @@ -- :: |_ =bowl:gall +:: +++ bind-eyre + ^- card + [%pass /bind %arvo %e %connect [~ /spider] %spider] +:: ++ handle-http-request |= [eyre-id=@ta =inbound-request:eyre] ^- (quip card _state) @@ -545,5 +573,5 @@ :: ++ clean-state !> ^- clean-slate - 1+state(running (turn (tap-yarn running.state) head)) + 2+state(running (turn (tap-yarn running.state) head)) -- From 9aeabe515a1f2156ea7580987956bd95240c9dc2 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 3 Sep 2020 15:07:18 +1000 Subject: [PATCH 05/82] graph-view: add delete, leave threads --- pkg/arvo/sur/graph-view.hoon | 23 +++++++++---- pkg/arvo/ted/graph/delete.hoon | 61 ++++++++++++++++++++++++++++++++++ pkg/arvo/ted/graph/join.hoon | 6 ++-- pkg/arvo/ted/graph/leave.hoon | 61 ++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 pkg/arvo/ted/graph/delete.hoon create mode 100644 pkg/arvo/ted/graph/leave.hoon diff --git a/pkg/arvo/sur/graph-view.hoon b/pkg/arvo/sur/graph-view.hoon index 0c0bfdc271..00cf4fe94c 100644 --- a/pkg/arvo/sur/graph-view.hoon +++ b/pkg/arvo/sur/graph-view.hoon @@ -2,12 +2,23 @@ /+ resource ^? |% -++ error - $% - [%offline =ship] - [%no-permissions ~] - == -++ action +:: $error: An error from a graph-view poke +:: +:: %offline: Ship is offline +:: %bad-perms: Not permitted +:: %unknown: Anything not described above +:: ++$ error + ?(%offline %bad-perms %unknown) +:: $action: A semantic action on graphs +:: +:: %create: Create a graph and associated metadata +:: %delete: Delete a graph +:: %join: Join a graph +:: %invite: Invite users to a graph +:: %groupify: Make graph into managed group +:: ++$ action $% $: %create rid=resource diff --git a/pkg/arvo/ted/graph/delete.hoon b/pkg/arvo/ted/graph/delete.hoon new file mode 100644 index 0000000000..0df982b677 --- /dev/null +++ b/pkg/arvo/ted/graph/delete.hoon @@ -0,0 +1,61 @@ +/- spider, graph-view, graph=graph-store, *metadata-store, *group +/+ strandio, resource +=> +|% +++ strand strand:spider +++ poke poke:strandio +++ poke-our poke-our:strandio +++ scry-metadata + |= rid=resource + =/ m (strand ,(unit resource)) + ;< pax=(unit path) bind:m + %+ scry:strandio ,(unit path) + ;: weld + /gx/metadata-store/resource/graph + (en-path:resource rid) + /noun + == + (pure:m (bind pax de-path:resource)) +++ scry-group + |= rid=resource + =/ m (strand ,(unit resource)) + ;< ugroup=(unit group) bind:m + %+ scry:strandio ,(unit group) + ;: weld + /gx/group-store/resource/graph + (en-path:resource rid) + /noun + == + (pure:m (need ugroup)) +++ delete-graph + |= rid=resource + =/ m (strand ,~) + ;< ~ bind:m + (poke-our %graph-push-hook %push-hook-action [%remove rid.action]) + ;< ~ bind:m + (poke-our %graph-store %graph-update [%archive-graph rid.action]) + (pure:m ~) +-- +^- thread:spider +|= arg=vase +=/ m (strand ,vase) +^- form:m +=+ !<([=action:graph-view ~] arg) +?> ?=(%delete -.action) +;< =bowl:spider bind:m get-bowl:strandio +?> =(our.bowl entity.rid.action) +;< ugroup-rid=(unit resource) bind:m + (scry-metadata rid.action) +?~ ugroup-rid (fail:m %nonexistent) +;< ugroup=(unit group) + (scry-group u.ugroup-rid) +?~ ugroup (fail:m %nonexistent) +?. hidden.u.ugroup + (delete-graph rid.action) +;< ~ bind:m + (poke-our %group-push-hook %pull-hook-action [%remove rid.action]) +;< ~ bind:m + (poke-our %group-store %group-action !>([%remove-group rid.action]) +;< ~ bind:m (delete-graph rid.action) +(pure:m !>(~)) + diff --git a/pkg/arvo/ted/graph/join.hoon b/pkg/arvo/ted/graph/join.hoon index 968e842120..5882785678 100644 --- a/pkg/arvo/ted/graph/join.hoon +++ b/pkg/arvo/ted/graph/join.hoon @@ -29,9 +29,9 @@ (scry-metadata rid.action) ?^ group :: We have group, graph is managed - :: ;< ~ bind:m - :: %+ poke-our %graph-pull-hook - :: pull-hook-action+!>([%add ship.action rid.action]) + ;< ~ bind:m + %+ poke-our %graph-pull-hook + pull-hook-action+!>([%add ship.action rid.action]) (pure:m !>(~)) :: Else, add group then join ;< ~ bind:m diff --git a/pkg/arvo/ted/graph/leave.hoon b/pkg/arvo/ted/graph/leave.hoon new file mode 100644 index 0000000000..aa2fe45e36 --- /dev/null +++ b/pkg/arvo/ted/graph/leave.hoon @@ -0,0 +1,61 @@ +/- spider, graph-view, graph=graph-store, *metadata-store, *group +/+ strandio, resource +=> +|% +++ strand strand:spider +++ poke poke:strandio +++ poke-our poke-our:strandio +++ scry-metadata + |= rid=resource + =/ m (strand ,(unit resource)) + ;< pax=(unit path) bind:m + %+ scry:strandio ,(unit path) + ;: weld + /gx/metadata-store/resource/graph + (en-path:resource rid) + /noun + == + (pure:m (bind pax de-path:resource)) +++ scry-group + |= rid=resource + =/ m (strand ,(unit resource)) + ;< ugroup=(unit group) bind:m + %+ scry:strandio ,(unit group) + ;: weld + /gx/group-store/resource/graph + (en-path:resource rid) + /noun + == + (pure:m (need ugroup)) +++ delete-graph + |= rid=resource + =/ m (strand ,~) + ;< ~ bind:m + (poke-our %graph-pull-hook %pull-hook-action [%remove rid.action]) + ;< ~ bind:m + (poke-our %graph-store %graph-update [%archive-graph rid.action]) + (pure:m ~) +-- +^- thread:spider +|= arg=vase +=/ m (strand ,vase) +^- form:m +=+ !<([=action:graph-view ~] arg) +?> ?=(%leave -.action) +;< =bowl:spider bind:m get-bowl:strandio +?< =(our.bowl entity.rid.action) +;< ugroup-rid=(unit resource) bind:m + (scry-metadata rid.action) +?~ ugroup-rid (fail:m %nonexistent) +;< ugroup=(unit group) + (scry-group u.ugroup-rid) +?~ ugroup (fail:m %nonexistent) +?. hidden.u.ugroup + (delete-graph rid.action) +;< ~ bind:m + (poke-our %group-push-hook %pull-hook-action [%remove rid.action]) +;< ~ bind:m + (poke-our %group-store %group-action !>([%remove-group rid.action]) +;< ~ bind:m (delete-graph rid.action) +(pure:m !>(~)) + From ab145928cac3fae84d2bc80049e93edd1006382e Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 3 Sep 2020 15:07:35 +1000 Subject: [PATCH 06/82] Revert "graph-hooks: deleted for now until we can test them with a UI" This reverts commit 738481ff71a50dc9011ab37b6ee6241500bbf01e. --- pkg/arvo/app/graph-pull-hook.hoon | 44 ++++++++++++ pkg/arvo/app/graph-push-hook.hoon | 115 ++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 pkg/arvo/app/graph-pull-hook.hoon create mode 100644 pkg/arvo/app/graph-push-hook.hoon diff --git a/pkg/arvo/app/graph-pull-hook.hoon b/pkg/arvo/app/graph-pull-hook.hoon new file mode 100644 index 0000000000..c6429c036e --- /dev/null +++ b/pkg/arvo/app/graph-pull-hook.hoon @@ -0,0 +1,44 @@ +/- *resource +/+ store=graph-store, graph, default-agent, verb, dbug, pull-hook +~% %graph-pull-hook-top ..is ~ +|% ++$ card card:agent:gall +++ config + ^- config:pull-hook + :* %graph-store + update:store + %graph-update + %graph-push-hook + == +-- +:: +%- agent:dbug +^- agent:gall +%- (agent:pull-hook config) +^- (pull-hook:pull-hook config) +|_ =bowl:gall ++* this . + def ~(. (default-agent this %|) bowl) + dep ~(. (default:pull-hook this config) bowl) +:: +++ on-init on-init:def +++ on-save !>(~) +++ on-load on-load:def +++ on-poke on-poke:def +++ on-peek on-peek:def +++ on-arvo on-arvo:def +++ on-fail on-fail:def +++ on-agent on-agent:def +++ on-watch on-watch:def +++ on-leave on-leave:def +++ on-pull-nack + :: TODO: archive the graph + on-pull-nack:dep +:: +++ on-pull-kick + |= =resource + ^- (unit path) + =/ maybe-time (peek-log:graph resource) + ?~ maybe-time `/ + `/(scot %da u.maybe-time) +-- diff --git a/pkg/arvo/app/graph-push-hook.hoon b/pkg/arvo/app/graph-push-hook.hoon new file mode 100644 index 0000000000..08d36c8cfe --- /dev/null +++ b/pkg/arvo/app/graph-push-hook.hoon @@ -0,0 +1,115 @@ +/+ store=graph-store +/+ res=resource +/+ graph +/+ group +/+ default-agent +/+ dbug +/+ push-hook +~% %graph-push-hook-top ..is ~ +|% ++$ card card:agent:gall +++ config + ^- config:push-hook + :* %graph-store + /updates + update:store + %graph-update + %graph-pull-hook + == +:: ++$ agent (push-hook:push-hook config) +-- +:: +%- agent:dbug +^- agent:gall +%- (agent:push-hook config) +^- agent +|_ =bowl:gall ++* this . + def ~(. (default-agent this %|) bowl) + grp ~(. group bowl) + gra ~(. graph bowl) +:: +++ on-init on-init:def +++ on-save !>(~) +++ on-load on-load:def +++ on-poke on-poke:def +++ on-agent on-agent:def +++ on-watch on-watch:def +++ on-leave on-leave:def +++ on-peek on-peek:def +++ on-arvo on-arvo:def +++ on-fail on-fail:def +:: +++ should-proxy-update + |= =vase + ^- ? + =/ =update:store !<(update:store vase) + ?- -.q.update + %add-graph %.y + %remove-graph %.y + %add-nodes %.y + %remove-nodes %.y + %add-signatures %.y + %remove-signatures %.y + %archive-graph %.y + %unarchive-graph %.n + %add-tag %.n + %remove-tag %.n + %keys %.n + %tags %.n + %tag-queries %.n + %run-updates %.y + == +:: +++ resource-for-update + |= =vase + ^- (unit resource:res) + =/ =update:store !<(update:store vase) + ?- -.q.update + %add-graph `resource.q.update + %remove-graph `resource.q.update + %add-nodes `resource.q.update + %remove-nodes `resource.q.update + %add-signatures `resource.uid.q.update + %remove-signatures `resource.uid.q.update + %archive-graph `resource.q.update + %unarchive-graph ~ + %add-tag ~ + %remove-tag ~ + %run-updates `resource.q.update + %keys ~ + %tags ~ + %tag-queries ~ + == +:: +++ initial-watch + |= [=path =resource:res] + ^- vase + ?> (can-join:grp resource src.bowl) + ?~ path + :: new subscribe + =/ [=graph:store mark=(unit mark:store)] + (get-graph:gra resource) + !> ^- update:store + [%0 now.bowl [%add-graph resource graph mark]] + :: resubscribe + :: + :: TODO: use action-log + :: + !! +:: +++ take-update + |= =vase + ^- [(list card) agent] + =/ =update:store !<(update:store vase) + ?+ -.q.update [~ this] + %remove-graph + :_ this + [%give %kick ~[resource+(en-path:res resource.q.update)] ~]~ + :: + %archive-graph + :_ this + [%give %kick ~[resource+(en-path:res resource.q.update)] ~]~ + == +-- From 7174f3281b8aec427dba9ad937646540c74a7f8f Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 4 Sep 2020 00:12:12 +1000 Subject: [PATCH 07/82] marks: add graph-view marks --- pkg/arvo/lib/graph-view.hoon | 62 +++++++++++++++++++++++++++++ pkg/arvo/lib/strandio.hoon | 3 +- pkg/arvo/mar/graph/update.hoon | 2 + pkg/arvo/mar/graph/view-action.hoon | 13 ++++++ pkg/arvo/sur/graph-view.hoon | 22 +++++++--- 5 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 pkg/arvo/lib/graph-view.hoon create mode 100644 pkg/arvo/mar/graph/view-action.hoon diff --git a/pkg/arvo/lib/graph-view.hoon b/pkg/arvo/lib/graph-view.hoon new file mode 100644 index 0000000000..e5a83018b9 --- /dev/null +++ b/pkg/arvo/lib/graph-view.hoon @@ -0,0 +1,62 @@ +/- sur=graph-view +/+ resource, group-store +^? +=< [sur .] +=, sur +|% +++ app-to-mark + |= app=app-name + ?+ app !! + %chat %graph-validator-chat + == +++ dejs + =, dejs:format + |% + ++ action + |^ + ^- $-(json ^action) + %- of + :~ create+create + delete+delete + join+join + ::groupify+groupify + ::invite+invite + == + :: + ++ create + %- ot + :~ resource+dejs:resource + title+so + description+so + app+app + associated+associated + == + ++ app + |= jon=json + ^- app-name + ?> ?=(%s -.jon) + ~! app-name + %chat + :: + ++ delete + %- ot + :~ resource+dejs:resource + == + :: + ++ join + %- ot + :~ resource+dejs:resource + ship+(su ;~(pfix sig fed:ag)) + == + :: + ++ groupify !! + ++ invite !! + ++ associated + %- of + :~ group+dejs:resource + policy+policy:dejs:group-store + == + + -- + -- +-- diff --git a/pkg/arvo/lib/strandio.hoon b/pkg/arvo/lib/strandio.hoon index 8da72d0bea..7eaa8c288d 100644 --- a/pkg/arvo/lib/strandio.hoon +++ b/pkg/arvo/lib/strandio.hoon @@ -243,7 +243,8 @@ :: ++ scry |* [=mold =path] - =/ m (strand mold) + =/ m (strand ,mold) + ^- form:m ?> ?=(^ path) ?> ?=(^ t.path) ;< =bowl:spider bind:m get-bowl diff --git a/pkg/arvo/mar/graph/update.hoon b/pkg/arvo/mar/graph/update.hoon index d5f0f4abec..1cd2a20686 100644 --- a/pkg/arvo/mar/graph/update.hoon +++ b/pkg/arvo/mar/graph/update.hoon @@ -1,7 +1,9 @@ /+ *graph-store |_ upd=update +++ grad %noun ++ grow |% + ++ noun upd ++ json (update:enjs upd) -- :: diff --git a/pkg/arvo/mar/graph/view-action.hoon b/pkg/arvo/mar/graph/view-action.hoon new file mode 100644 index 0000000000..ab8b0ef12e --- /dev/null +++ b/pkg/arvo/mar/graph/view-action.hoon @@ -0,0 +1,13 @@ +/+ *graph-view +|_ act=action +++ grad %noun +++ grow + |% + ++ noun act + -- +++ grab + |% + ++ noun action + ++ json action:dejs + -- +-- diff --git a/pkg/arvo/sur/graph-view.hoon b/pkg/arvo/sur/graph-view.hoon index 00cf4fe94c..1d54897600 100644 --- a/pkg/arvo/sur/graph-view.hoon +++ b/pkg/arvo/sur/graph-view.hoon @@ -1,7 +1,17 @@ -/- group +/- *group /+ resource ^? |% +:: $associated: A group to associate, or a policy if it is unmanaged +:: ++$ associated + $% [%group rid=resource] + [%policy =policy] + == +:: $app: An app that is associated to a graph-store mark +:: ++$ app-name ?(%chat %publish %links) +:: :: $error: An error from a graph-view poke :: :: %offline: Ship is offline @@ -24,13 +34,13 @@ rid=resource title=@t description=@t - mark=(unit mark) - associated=(each resource policy:group) + app=app-name + =associated == - [%delete rid=resource ~] + [%delete rid=resource] [%join rid=resource =ship] - [%invite rid=resource ships=(set ship)] - [%groupify rid=resource title=@t description=@t] + ::[%invite rid=resource ships=(set ship)] + :: [%groupify rid=resource title=@t description=@t] == -- From fe17e48713d11deb0429e11f0ae7c99316745fa6 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 4 Sep 2020 17:42:20 +1000 Subject: [PATCH 08/82] lib/graph: fix scries --- pkg/arvo/lib/graph.hoon | 10 ++++++++-- pkg/arvo/ted/graph/create.hoon | 26 +++++++++++++------------- pkg/arvo/ted/graph/join.hoon | 24 +++++++++++++++++++----- 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/pkg/arvo/lib/graph.hoon b/pkg/arvo/lib/graph.hoon index 395c0554fa..ae347b73fa 100644 --- a/pkg/arvo/lib/graph.hoon +++ b/pkg/arvo/lib/graph.hoon @@ -13,10 +13,16 @@ :: ++ get-graph |= res=resource - ^- marked-graph:store - %+ scry-for marked-graph:store + ^- update:store + %+ scry-for update:store /graph/(scot %p entity.res)/[name.res] :: +++ get-update-log + |= rid=resource + ^- update-log:store + %+ scry-for update-log:store + /update-log/(scot %p entity.rid)/[name.rid] +:: ++ peek-log |= res=resource ^- (unit time) diff --git a/pkg/arvo/ted/graph/create.hoon b/pkg/arvo/ted/graph/create.hoon index 54fca38522..43fcabc5c3 100644 --- a/pkg/arvo/ted/graph/create.hoon +++ b/pkg/arvo/ted/graph/create.hoon @@ -1,38 +1,38 @@ -/- spider, graph-view, graph=graph-store, *metadata-store, *group, group-store -/+ strandio, resource +/- spider, graph=graph-store, *metadata-store, *group, group-store +/+ strandio, resource, graph-view => |% ++ strand strand:spider ++ poke poke:strandio ++ poke-our poke-our:strandio ++ handle-group - |= [rid=resource associated=(each resource policy)] + |= [rid=resource =associated:graph-view] =/ m (strand ,resource) - ?: ?=(%& -.associated) - (pure:m p.associated) + ?: ?=(%group -.associated) + (pure:m rid.associated) =/ =action:group-store - [%add-group rid p.associated %&] + [%add-group rid policy.associated %&] ;< ~ bind:m (poke-our %group-store %group-action !>(action)) ;< ~ bind:m (poke-our %group-push-hook %push-hook-action !>([%add rid])) (pure:m rid) - -- =, strand=strand:spider ^- thread:spider |= arg=vase =/ m (strand ,vase) ^- form:m -=+ !<([=action:graph-view ~] arg) +=+ !<(=action:graph-view arg) ?> ?=(%create -.action) ;< =bowl:spider bind:m get-bowl:strandio :: Add graph to graph-store :: =/ =update:graph - [%0 now.bowl %add-graph rid.action *graph:graph mark.action] -;< ~ bind:m (poke-our %graph-store %graph-update !>(update)) -::;< ~ bind:m -:: (poke-our %graph-push-hook %push-hook-action !>([%add rid.action])) + [%0 now.bowl %add-graph rid.action *graph:graph `(app-to-mark:graph-view app.action)] +;< ~ bind:m + (poke-our %graph-store graph-update+!>(update)) +;< ~ bind:m + (poke-our %graph-push-hook %push-hook-action !>([%add rid.action])) :: Add group, if graph is unmanaged :: ;< group=resource bind:m @@ -49,7 +49,7 @@ creator our.bowl == =/ act=metadata-action - [%add group-path [%graph (en-path:resource rid.action)] metadata] + [%add group-path [app.action (en-path:resource rid.action)] metadata] ;< ~ bind:m (poke-our %metadata-hook %metadata-action !>(act)) ;< ~ bind:m (poke-our %metadata-hook %metadata-hook-action !>([%add-owned group-path])) diff --git a/pkg/arvo/ted/graph/join.hoon b/pkg/arvo/ted/graph/join.hoon index 5882785678..13c57cc35e 100644 --- a/pkg/arvo/ted/graph/join.hoon +++ b/pkg/arvo/ted/graph/join.hoon @@ -5,17 +5,29 @@ ++ strand strand:spider ++ poke poke:strandio ++ poke-our poke-our:strandio +++ check-live + |= who=ship + =/ m (strand ,~) + ^- form:m + %+ (set-timeout:strandio ,~) ~s10 + ;< ~ bind:m + (poke [who %hood] %helm-hi !>(~)) + (pure:m ~) ++ scry-metadata |= rid=resource =/ m (strand ,(unit resource)) - ;< pax=(unit path) bind:m - %+ scry:strandio ,(unit path) + ^- form:m + ;< pax=(unit (set path)) bind:m + %+ scry:strandio ,(unit (set path)) ;: weld - /gx/metadata-store/resource/graph + /gx/metadata-store/resource/chat (en-path:resource rid) /noun == - (pure:m (bind pax de-path:resource)) + %- pure:m + ?~ pax ~ + ?~ u.pax ~ + `(de-path:resource n.u.pax) -- ^- thread:spider |= arg=vase @@ -27,6 +39,7 @@ ?< =(our.bowl entity.rid.action) ;< group=(unit resource) bind:m (scry-metadata rid.action) +;< ~ bind:m (check-live entity.rid.action) ?^ group :: We have group, graph is managed ;< ~ bind:m @@ -47,6 +60,7 @@ %+ poke-our %metadata-hook metadata-hook-action+!>([%add-synced ship.action rid.action]) ;< ~ bind:m - (poke-our %graph-pull-hook %pull-hook-action !>([%add ship.action rid.action])) + %+ poke-our %graph-pull-hook + pull-hook-action+!>([%add ship.action rid.action])) (pure:m !>(~)) From 881146e47678b70cace44f0e78913459e2a264b4 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Fri, 4 Sep 2020 17:51:28 +1000 Subject: [PATCH 09/82] graph-view: address review comments --- pkg/arvo/lib/graph-view.hoon | 10 ++++++++-- pkg/arvo/sur/graph-view.hoon | 1 + pkg/arvo/ted/graph/create.hoon | 3 ++- pkg/arvo/ted/graph/delete.hoon | 7 +++++-- pkg/arvo/ted/graph/join.hoon | 6 ++++-- pkg/arvo/ted/graph/leave.hoon | 5 ++++- 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/pkg/arvo/lib/graph-view.hoon b/pkg/arvo/lib/graph-view.hoon index e5a83018b9..f208accf31 100644 --- a/pkg/arvo/lib/graph-view.hoon +++ b/pkg/arvo/lib/graph-view.hoon @@ -19,6 +19,7 @@ :~ create+create delete+delete join+join + leave+leave ::groupify+groupify ::invite+invite == @@ -31,13 +32,18 @@ app+app associated+associated == + :: ++ app |= jon=json ^- app-name ?> ?=(%s -.jon) - ~! app-name %chat :: + ++ leave + %- ot + :~ resource+dejs:resource + == + :: ++ delete %- ot :~ resource+dejs:resource @@ -51,12 +57,12 @@ :: ++ groupify !! ++ invite !! + :: ++ associated %- of :~ group+dejs:resource policy+policy:dejs:group-store == - -- -- -- diff --git a/pkg/arvo/sur/graph-view.hoon b/pkg/arvo/sur/graph-view.hoon index 1d54897600..d465c48ab8 100644 --- a/pkg/arvo/sur/graph-view.hoon +++ b/pkg/arvo/sur/graph-view.hoon @@ -38,6 +38,7 @@ =associated == [%delete rid=resource] + [%leave rid=resource] [%join rid=resource =ship] ::[%invite rid=resource ships=(set ship)] :: [%groupify rid=resource title=@t description=@t] diff --git a/pkg/arvo/ted/graph/create.hoon b/pkg/arvo/ted/graph/create.hoon index 43fcabc5c3..bb86d14db1 100644 --- a/pkg/arvo/ted/graph/create.hoon +++ b/pkg/arvo/ted/graph/create.hoon @@ -5,6 +5,7 @@ ++ strand strand:spider ++ poke poke:strandio ++ poke-our poke-our:strandio +:: ++ handle-group |= [rid=resource =associated:graph-view] =/ m (strand ,resource) @@ -17,6 +18,7 @@ (poke-our %group-push-hook %push-hook-action !>([%add rid])) (pure:m rid) -- +:: =, strand=strand:spider ^- thread:spider |= arg=vase @@ -54,4 +56,3 @@ ;< ~ bind:m (poke-our %metadata-hook %metadata-hook-action !>([%add-owned group-path])) (pure:m !>(~)) - diff --git a/pkg/arvo/ted/graph/delete.hoon b/pkg/arvo/ted/graph/delete.hoon index 0df982b677..54de93faab 100644 --- a/pkg/arvo/ted/graph/delete.hoon +++ b/pkg/arvo/ted/graph/delete.hoon @@ -5,6 +5,7 @@ ++ strand strand:spider ++ poke poke:strandio ++ poke-our poke-our:strandio +:: ++ scry-metadata |= rid=resource =/ m (strand ,(unit resource)) @@ -16,6 +17,7 @@ /noun == (pure:m (bind pax de-path:resource)) +:: ++ scry-group |= rid=resource =/ m (strand ,(unit resource)) @@ -27,6 +29,7 @@ /noun == (pure:m (need ugroup)) +:: ++ delete-graph |= rid=resource =/ m (strand ,~) @@ -36,6 +39,7 @@ (poke-our %graph-store %graph-update [%archive-graph rid.action]) (pure:m ~) -- +:: ^- thread:spider |= arg=vase =/ m (strand ,vase) @@ -53,9 +57,8 @@ ?. hidden.u.ugroup (delete-graph rid.action) ;< ~ bind:m - (poke-our %group-push-hook %pull-hook-action [%remove rid.action]) + (poke-our %group-push-hook %push-hook-action [%remove rid.action]) ;< ~ bind:m (poke-our %group-store %group-action !>([%remove-group rid.action]) ;< ~ bind:m (delete-graph rid.action) (pure:m !>(~)) - diff --git a/pkg/arvo/ted/graph/join.hoon b/pkg/arvo/ted/graph/join.hoon index 13c57cc35e..a61d20c91c 100644 --- a/pkg/arvo/ted/graph/join.hoon +++ b/pkg/arvo/ted/graph/join.hoon @@ -5,14 +5,16 @@ ++ strand strand:spider ++ poke poke:strandio ++ poke-our poke-our:strandio +:: ++ check-live |= who=ship =/ m (strand ,~) ^- form:m - %+ (set-timeout:strandio ,~) ~s10 + %+ (set-timeout:strandio ,~) ~s20 ;< ~ bind:m (poke [who %hood] %helm-hi !>(~)) (pure:m ~) +:: ++ scry-metadata |= rid=resource =/ m (strand ,(unit resource)) @@ -29,6 +31,7 @@ ?~ u.pax ~ `(de-path:resource n.u.pax) -- +:: ^- thread:spider |= arg=vase =/ m (strand ,vase) @@ -63,4 +66,3 @@ %+ poke-our %graph-pull-hook pull-hook-action+!>([%add ship.action rid.action])) (pure:m !>(~)) - diff --git a/pkg/arvo/ted/graph/leave.hoon b/pkg/arvo/ted/graph/leave.hoon index aa2fe45e36..84660d109e 100644 --- a/pkg/arvo/ted/graph/leave.hoon +++ b/pkg/arvo/ted/graph/leave.hoon @@ -5,6 +5,7 @@ ++ strand strand:spider ++ poke poke:strandio ++ poke-our poke-our:strandio +:: ++ scry-metadata |= rid=resource =/ m (strand ,(unit resource)) @@ -16,6 +17,7 @@ /noun == (pure:m (bind pax de-path:resource)) +:: ++ scry-group |= rid=resource =/ m (strand ,(unit resource)) @@ -27,6 +29,7 @@ /noun == (pure:m (need ugroup)) +:: ++ delete-graph |= rid=resource =/ m (strand ,~) @@ -36,6 +39,7 @@ (poke-our %graph-store %graph-update [%archive-graph rid.action]) (pure:m ~) -- +:: ^- thread:spider |= arg=vase =/ m (strand ,vase) @@ -58,4 +62,3 @@ (poke-our %group-store %group-action !>([%remove-group rid.action]) ;< ~ bind:m (delete-graph rid.action) (pure:m !>(~)) - From d1a1cfccad7818f29d05877d2e1e5ce4644867d5 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 8 Sep 2020 14:18:21 +1000 Subject: [PATCH 10/82] publish: first draft of graph-store migration --- pkg/arvo/app/publish.hoon | 92 ++++++++++++++++++++++- pkg/arvo/mar/graph/validator/publish.hoon | 17 +++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 pkg/arvo/mar/graph/validator/publish.hoon diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon index 9761db7837..7f1451e7f7 100644 --- a/pkg/arvo/app/publish.hoon +++ b/pkg/arvo/app/publish.hoon @@ -18,6 +18,7 @@ /+ verb /+ grpl=group /+ group-store +/+ graph-store /+ resource :: ~% %publish ..is ~ @@ -55,6 +56,7 @@ [%4 state-three] [%5 state-three] [%6 state-three] + [%7 state-three] == :: +$ metadata-delta @@ -70,7 +72,7 @@ == -- :: -=| [%6 state-three] +=| [%7 state-three] =* state - %- agent:dbug %+ verb | @@ -238,8 +240,96 @@ == :: %6 + %_ $ + -.p.old-state %7 + :: + cards + %+ weld cards + %+ roll ~(tap by books.p.old-state) + |= [[[who=@p book=@tas] nb=notebook] out=(list card)] + ^- (list card) + =/ =resource + [who book] + ?. =(who our.bol) + :_ out + (poke-graph-pull %add who resource) + =/ =graph:graph-store + (notebook-to-graph nb) + %+ weld out + ^- (list card) + :~ + %- poke-graph-store + :* %0 date-created.nb %add-graph + resource + graph + `%graph-validator-publish + == + (poke-graph-push %add resource) + == + == + :: + %7 [cards this(state p.old-state)] == + ++ notebook-to-graph + |= =notebook + ^- graph:graph-store + %+ gas:orm:graph-store *graph:graph-store + %+ turn ~(tap by notes.notebook) + |= [@ta =note] + ^- [atom node:graph-store] + :- date-created.note + (note-to-node note) + :: + ++ comments-to-children + |= [=index:graph-store =note] + ^- internal-graph:graph-store + ?: =(~ comments.note) + [%empty ~] + :- %graph + %+ gas:orm:graph-store *graph:graph-store + %+ turn ~(tap by comments.note) + |= [when=@da =comment] + ^- [atom node:graph-store] + :- when + :_ [%empty ~] + ^- post:graph-store + :* author.comment + (snoc index when) + when + [%text content.comment]~ + ~ ~ + == + :: + ++ note-to-node + |= =note + ^- node:graph-store + =/ =index:graph-store + ~[date-created.note] + :_ (comments-to-children index note) + ^- post:graph-store + :* author.note + index + date-created.note + [%text file.note]~ + ~ ~ + == + :: + ++ poke-our + |= [app=term =cage] + [%pass / %agent [our.bol app] %poke cage] + :: + ++ poke-graph-pull + |= =action:pull-hook + (poke-our %graph-pull-hook pull-hook-action+!>(action)) + :: + ++ poke-graph-store + |= =update:graph-store + (poke-our %graph-store graph-update+!>(update)) + :: + ++ poke-graph-push + |= =action:push-hook + (poke-our %graph-push-hook push-hook-action+!>(action)) ++ convert-notebook-3-4 |= prev=notebook-3 ^- notebook-3 diff --git a/pkg/arvo/mar/graph/validator/publish.hoon b/pkg/arvo/mar/graph/validator/publish.hoon new file mode 100644 index 0000000000..5dfcbd6fd9 --- /dev/null +++ b/pkg/arvo/mar/graph/validator/publish.hoon @@ -0,0 +1,17 @@ +/- *post +|_ i=indexed-post +++ grow + |% + ++ noun i + -- +++ grab + |% + ++ noun + |= p=* + =/ ip ;;(indexed-post p) + ?> (gte 2 (lent index.p.ip)) + ip + -- +:: +++ grad %noun +-- From 502792a73497b1899c051842a645fac13424c069 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 8 Sep 2020 16:08:52 +1000 Subject: [PATCH 11/82] publish: store title in content --- pkg/arvo/app/publish.hoon | 6 +++++- pkg/arvo/mar/graph/validator/publish.hoon | 11 +++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon index 7f1451e7f7..421440aac8 100644 --- a/pkg/arvo/app/publish.hoon +++ b/pkg/arvo/app/publish.hoon @@ -306,12 +306,16 @@ ^- node:graph-store =/ =index:graph-store ~[date-created.note] + =/ contents=(list content:graph-store) + :~ [%text title.note] + [%text file.note] + == :_ (comments-to-children index note) ^- post:graph-store :* author.note index date-created.note - [%text file.note]~ + contents ~ ~ == :: diff --git a/pkg/arvo/mar/graph/validator/publish.hoon b/pkg/arvo/mar/graph/validator/publish.hoon index 5dfcbd6fd9..849044ef67 100644 --- a/pkg/arvo/mar/graph/validator/publish.hoon +++ b/pkg/arvo/mar/graph/validator/publish.hoon @@ -9,8 +9,15 @@ ++ noun |= p=* =/ ip ;;(indexed-post p) - ?> (gte 2 (lent index.p.ip)) - ip + |^ + ?+ index.p.ip !! + [@ @ ~] ip + :: top level post must have title as first content + [@ ~] + ?> ?=(^ contents.p.ip) + ?> ?=(%text -.i.contents.p.ip) + ip + == -- :: ++ grad %noun From 22b6277abe22ec19febf5074425ec598e6432ee4 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 10 Sep 2020 20:05:11 +1000 Subject: [PATCH 12/82] publish: rewrite top-level for graph-store --- pkg/arvo/mar/graph/validator/publish.hoon | 1 - pkg/interface/src/logic/api/base.ts | 12 ++ pkg/interface/src/logic/api/graph.ts | 63 +++++- pkg/interface/src/logic/api/publish.ts | 185 ------------------ pkg/interface/src/logic/lib/group.ts | 5 + .../src/logic/reducers/graph-update.js | 21 +- pkg/interface/src/logic/store/type.ts | 3 +- pkg/interface/src/types/index.ts | 1 + pkg/interface/src/types/metadata-update.ts | 2 +- pkg/interface/src/views/apps/publish/app.tsx | 26 ++- .../apps/publish/components/lib/GroupItem.tsx | 62 +++--- .../apps/publish/components/lib/Join.tsx | 14 +- .../apps/publish/components/lib/Sidebar.tsx | 100 +++------- .../apps/publish/components/lib/new-post.tsx | 36 ++-- .../views/apps/publish/components/lib/new.tsx | 29 ++- .../apps/publish/components/skeleton.tsx | 29 +-- 16 files changed, 214 insertions(+), 375 deletions(-) delete mode 100644 pkg/interface/src/logic/api/publish.ts diff --git a/pkg/arvo/mar/graph/validator/publish.hoon b/pkg/arvo/mar/graph/validator/publish.hoon index 849044ef67..d4437478b3 100644 --- a/pkg/arvo/mar/graph/validator/publish.hoon +++ b/pkg/arvo/mar/graph/validator/publish.hoon @@ -9,7 +9,6 @@ ++ noun |= p=* =/ ip ;;(indexed-post p) - |^ ?+ index.p.ip !! [@ @ ~] ip :: top level post must have title as first content diff --git a/pkg/interface/src/logic/api/base.ts b/pkg/interface/src/logic/api/base.ts index b18a345162..42019ba41d 100644 --- a/pkg/interface/src/logic/api/base.ts +++ b/pkg/interface/src/logic/api/base.ts @@ -57,4 +57,16 @@ export default class BaseApi { scry(app: string, path: Path): Promise { return fetch(`/~/scry/${app}${path}.json`).then(r => r.json() as Promise); } + + + async spider(inputMark: string, outputMark: string, threadName: string, body: any): Promise { + + const res = await fetch(`/spider/${inputMark}/${threadName}/${outputMark}.json`, { + method: 'POST', + body: JSON.stringify(body) + }); + + return res.json(); + } + } diff --git a/pkg/interface/src/logic/api/graph.ts b/pkg/interface/src/logic/api/graph.ts index d583322021..4e64d50f46 100644 --- a/pkg/interface/src/logic/api/graph.ts +++ b/pkg/interface/src/logic/api/graph.ts @@ -1,7 +1,9 @@ import BaseApi from './base'; import { StoreState } from '../store/type'; import { Patp, Path, PatpNoSig } from '~/types/noun'; - +import _ from 'lodash'; +import {makeResource, resourceFromPath} from '../lib/group'; +import {GroupPolicy, Enc} from '~/types'; export const createPost = (contents: Object[], parentIndex: string = '') => { return { @@ -20,6 +22,58 @@ export default class GraphApi extends BaseApi { return this.action('graph-store', 'graph-update', action) } + private viewAction(threadName: string, action: any) { + return this.spider('graph-view-action', 'json', threadName, action); + } + + createManagedGraph(name: string, title: string, description: string, app: string, group: Path) { + const associated = { group: resourceFromPath(group) }; + + const resource = makeResource(`~${window.ship}`, name); + return this.viewAction('graph-create', { + "create": { + resource, + title, + description, + app, + associated + } + }); + } + + createUnmanagedGraph(name: string, title: string, description: string, app: string, policy: Enc) { + + const resource = makeResource(`~${window.ship}`, name); + return this.viewAction('graph-create', { + "create": { + resource, + title, + description, + app, + associated: { policy } + } + }); + } + + joinGraph(ship: Patp, name: string) { + const resource = makeResource(ship, name); + return this.viewAction('graph-join', { + join: { + resource, + ship + } + }); + } + + deleteGraph(name: string) { + const resource = makeResource(`~${window.ship}`, name); + return this.viewAction('graph-delete', { + "delete": { + resource + } + }); + } + addGraph(ship: Patp, name: string, graph: any, mark: any) { this.storeAction({ 'add-graph': { @@ -40,14 +94,15 @@ export default class GraphApi extends BaseApi { addPost(ship: Patp, name: string, post: Object) { let nodes = {}; + const resource = { ship, name }; nodes[post.index] = { post, children: { empty: null } }; - this.storeAction({ + return this.storeAction({ 'add-nodes': { - resource: { ship, name }, + resource, nodes } }); @@ -63,7 +118,7 @@ export default class GraphApi extends BaseApi { } removeNodes(ship: Patp, name: string, indices: string[]) { - this.storeAction({ + return this.storeAction({ 'remove-nodes': { resource: { ship, name }, indices diff --git a/pkg/interface/src/logic/api/publish.ts b/pkg/interface/src/logic/api/publish.ts deleted file mode 100644 index 02648f651b..0000000000 --- a/pkg/interface/src/logic/api/publish.ts +++ /dev/null @@ -1,185 +0,0 @@ -import BaseApi from './base'; - -import { PublishResponse } from '~/types/publish-response'; -import { PatpNoSig, Path } from '~/types/noun'; -import { BookId, NoteId } from '~/types/publish-update'; - -export default class PublishApi extends BaseApi { - handleEvent(data: PublishResponse) { - this.store.handleEvent({ data: { 'publish-response' : data } }); - } - - fetchNotebooks() { - fetch('/publish-view/notebooks.json') - .then(response => response.json()) - .then((json) => { - this.handleEvent({ - type: 'notebooks', - data: json - }); - }); - } - - fetchNotebook(host: PatpNoSig, book: BookId) { - fetch(`/publish-view/${host}/${book}.json`) - .then(response => response.json()) - .then((json) => { - this.handleEvent({ - type: 'notebook', - data: json, - host: host, - notebook: book - }); - }); - } - - fetchNote(host: PatpNoSig, book: BookId, note: NoteId) { - fetch(`/publish-view/${host}/${book}/${note}.json`) - .then(response => response.json()) - .then((json) => { - this.handleEvent({ - type: 'note', - data: json, - host: host, - notebook: book, - note: note - }); - }); - } - - fetchNotesPage(host: PatpNoSig, book: BookId, start: number, length: number) { - fetch(`/publish-view/notes/${host}/${book}/${start}/${length}.json`) - .then(response => response.json()) - .then((json) => { - this.handleEvent({ - type: 'notes-page', - data: json, - host: host, - notebook: book, - startIndex: start, - length: length - }); - }); - } - - fetchCommentsPage(host: PatpNoSig, book: BookId, note: NoteId, start: number, length: number) { - fetch(`/publish-view/comments/${host}/${book}/${note}/${start}/${length}.json`) - .then(response => response.json()) - .then((json) => { - this.handleEvent({ - type: 'comments-page', - data: json, - host: host, - notebook: book, - note: note, - startIndex: start, - length: length - }); - }); - } - - publishAction(act: any) { - return this.action('publish', 'publish-action', act); - } - - newBook(bookId: string, title: string, description: string, group?: Path) { - const groupInfo = group ? { 'group-path': group, - invitees: [], - 'use-preexisting': true, - 'make-managed': true - } : { - 'group-path': `/ship/~${window.ship}/${bookId}`, - invitees: [], - 'use-preexisting': false, - 'make-managed': false - }; - return this.publishAction({ - "new-book": { - book: bookId, - title: title, - about: description, - coms: true, - group: groupInfo - } - }); - } - - editBook(bookId: string, title: string, description: string, coms: boolean) { - return this.publishAction({ - "edit-book": { - book: bookId, - title: title, - about: description, - coms, - group: null - } - }); - } - - delBook(book: string) { - return this.publishAction({ - "del-book": { - book - } - }); - } - - newNote(who: PatpNoSig, book: string, note: string, title: string, body: string) { - return this.publishAction({ - 'new-note': { - who, - book, - note, - title, - body - } - }); - } - - editNote(who: PatpNoSig, book: string, note: string, title: string, body: string) { - return this.publishAction({ - 'edit-note': { - who, - book, - note, - title, - body - } - }); - } - - delNote(who: PatpNoSig, book: string, note: string) { - return this.publishAction({ - 'del-note': { - who, - book, - note - } - }); - } - - updateComment(who: PatpNoSig, book: string, note: string, comment: Path, body: string) { - return this.publishAction({ - 'edit-comment': { - who, - book, - note, - comment, - body - } - }); - } - - deleteComment(who: PatpNoSig, book: string, note: string, comment: Path ) { - return this.publishAction({ - "del-comment": { - who, - book, - note, - comment - }, - }); - } - -} - diff --git a/pkg/interface/src/logic/lib/group.ts b/pkg/interface/src/logic/lib/group.ts index 64b035b51d..bd4939c2f5 100644 --- a/pkg/interface/src/logic/lib/group.ts +++ b/pkg/interface/src/logic/lib/group.ts @@ -13,3 +13,8 @@ export function resourceFromPath(path: Path): Resource { const [, , ship, name] = path.split('/'); return { ship, name } } + +export function makeResource(ship: string, name:string) { + return { ship, name }; +} + diff --git a/pkg/interface/src/logic/reducers/graph-update.js b/pkg/interface/src/logic/reducers/graph-update.js index c8253f229c..54047e72a5 100644 --- a/pkg/interface/src/logic/reducers/graph-update.js +++ b/pkg/interface/src/logic/reducers/graph-update.js @@ -140,10 +140,21 @@ const addNodes = (json, state) => { }; const removeNodes = (json, state) => { + const _remove = (graph, index) => { + if (index.length === 1) { + graph.delete(index[0]); + } else { + const child = graph.get(index[0]); + _remove(child.children, index.slice(1)); + graph.set(index[0], child); + } + }; const data = _.get(json, 'remove-nodes', false); if (data) { console.log(data); - if (!(data.resource in state.graphs)) { return; } + const { ship, name } = data.resource; + const res = `${ship}/${name}`; + if (!(res in state.graphs)) { return; } data.indices.forEach((index) => { console.log(index); @@ -151,13 +162,7 @@ const removeNodes = (json, state) => { let indexArr = index.split('/').slice(1).map((ind) => { return parseInt(ind, 10); }); - - if (indexArr.length === 1) { - state.graphs[data.resource].delete(indexArr[0]); - } else { - // TODO: recursive - } - + _remove(state.graphs[res], indexArr); }); } }; diff --git a/pkg/interface/src/logic/store/type.ts b/pkg/interface/src/logic/store/type.ts index 525ec23a23..92e8e1f422 100644 --- a/pkg/interface/src/logic/store/type.ts +++ b/pkg/interface/src/logic/store/type.ts @@ -12,6 +12,7 @@ import { LaunchState, WeatherState } from '~/types/launch-update'; import { LinkComments, LinkCollections, LinkSeen } from '~/types/link-update'; import { ConnectionStatus } from '~/types/connection'; import { BackgroundConfig } from '~/types/local-update'; +import {Graphs} from '~/types/graph-update'; export interface StoreState { // local state @@ -35,7 +36,7 @@ export interface StoreState { groupKeys: Set; permissions: Permissions; s3: S3State; - graphs: Object; + graphs: Graphs; graphKeys: Set; diff --git a/pkg/interface/src/types/index.ts b/pkg/interface/src/types/index.ts index 3f7cda54c5..44a608d9c2 100644 --- a/pkg/interface/src/types/index.ts +++ b/pkg/interface/src/types/index.ts @@ -5,6 +5,7 @@ export * from './connection'; export * from './contact-update'; export * from './global'; export * from './group-update'; +export * from './graph-update'; export * from './invite-update'; export * from './launch-update'; export * from './link-listen-update'; diff --git a/pkg/interface/src/types/metadata-update.ts b/pkg/interface/src/types/metadata-update.ts index 1489201f39..d6b5bb6e73 100644 --- a/pkg/interface/src/types/metadata-update.ts +++ b/pkg/interface/src/types/metadata-update.ts @@ -31,7 +31,7 @@ type MetadataUpdateRemove = { export type Associations = Record; -type AppAssociations = { +export type AppAssociations = { [p in Path]: Association; } diff --git a/pkg/interface/src/views/apps/publish/app.tsx b/pkg/interface/src/views/apps/publish/app.tsx index 59aafff85e..a74a15c674 100644 --- a/pkg/interface/src/views/apps/publish/app.tsx +++ b/pkg/interface/src/views/apps/publish/app.tsx @@ -34,10 +34,11 @@ export default function PublishApp(props: PublishAppProps) { useEffect(() => { props.subscription.startApp("publish"); - props.api.publish.fetchNotebooks(); + props.subscription.startApp("graph"); return () => { props.subscription.stopApp("publish"); + props.subscription.stopApp("graph"); }; }, []); @@ -69,6 +70,9 @@ export default function PublishApp(props: PublishAppProps) { ? "sidebar" : "rightPanel"; + const appAssociations = props.associations?.publish || {}; + const graphs = props.graphs || {} + return ( <> @@ -76,9 +80,9 @@ export default function PublishApp(props: PublishAppProps) { @@ -88,6 +92,7 @@ export default function PublishApp(props: PublishAppProps) { sidebarShown={sidebarShown} invites={invites} notebooks={notebooks} + graphs={graphs} associations={associations} contacts={contacts} api={api} @@ -117,7 +122,7 @@ export default function PublishApp(props: PublishAppProps) { /> { const ship = props.match.params.ship || ""; const notebook = props.match.params.notebook || ""; @@ -133,7 +138,7 @@ export default function PublishApp(props: PublishAppProps) { }} /> { const view = props.match.params.view ? props.match.params.view @@ -141,9 +146,11 @@ export default function PublishApp(props: PublishAppProps) { const ship = props.match.params.ship || ""; const book = props.match.params.notebook || ""; + + const association = appAssociations?.[`/ship/${ship}/${book}`]; - const bookGroupPath = - notebooks?.[ship]?.[book]?.["subscribers-group-path"]; + + const bookGroupPath = association?.['group-path']; const notebookContacts = bookGroupPath in contacts ? contacts[bookGroupPath] : {}; @@ -157,6 +164,9 @@ export default function PublishApp(props: PublishAppProps) { groups={groups} contacts={contacts} notebookContacts={notebookContacts} + association={association} + associations={associations} + graphs={graphs} sidebarShown={sidebarShown} api={api} hideNicknames={hideNicknames} diff --git a/pkg/interface/src/views/apps/publish/components/lib/GroupItem.tsx b/pkg/interface/src/views/apps/publish/components/lib/GroupItem.tsx index dd5ffb731c..3a0f81ad6d 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/GroupItem.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/GroupItem.tsx @@ -1,42 +1,38 @@ -import React, { Component } from 'react'; +import React, { Component } from "react"; import { Box, Text } from "@tlon/indigo-react"; -import { NotebookItem } from './NotebookItem'; +import { NotebookItem } from "./NotebookItem"; +import { Association, AppAssociations } from "~/types"; -export class GroupItem extends Component { +interface GroupItemProps { + groupAssociation: Association; + appAssociations: AppAssociations; + appPaths: string[]; + path?: string; +} + +export class GroupItem extends Component { render() { const { props } = this; - const association = props.association ? props.association : {}; - let title = association['app-path'] ? association['app-path'] : 'Unmanaged Notebooks'; - if (association.metadata && association.metadata.title) { - title = association.metadata.title !== '' - ? association.metadata.title : title; - } - - const groupedBooks = props.groupedBooks ? props.groupedBooks : []; - const first = (props.index === 0) ? 'pt1' : 'pt6'; - - const notebookItems = groupedBooks.map((each, i) => { - const unreads = props.notebooks[each]['num-unread'] || 0; - let title = each.substr(1); - if (props.notebooks[each].title) { - title = (props.notebooks[each].title !== '') - ? props.notebooks[each].title : title; - } - return ( - - ); - }); + const title = + props.groupAssociation?.metadata?.title || "Unmanaged Notebooks"; return ( - - {title} - {notebookItems} + + + {title} + + {props.appPaths.map((appPath, i) => { + const { metadata } = props.appAssociations?.[appPath] || {}; + return ( + + ); + })} ); } diff --git a/pkg/interface/src/views/apps/publish/components/lib/Join.tsx b/pkg/interface/src/views/apps/publish/components/lib/Join.tsx index 3c57d3f08a..c72dfe8f43 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Join.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Join.tsx @@ -4,9 +4,10 @@ import { Spinner } from "~/views/components/Spinner"; import { Notebooks } from "~/types/publish-update"; import { useWaitForProps } from "~/logic/lib/useWaitForProps"; import { RouteComponentProps } from "react-router-dom"; +import GlobalApi from "~/logic/api/global"; interface JoinScreenProps { - api: any; // GlobalApi; + api: GlobalApi; ship: string; book: string; notebooks: Notebooks; @@ -21,17 +22,10 @@ export function JoinScreen(props: JoinScreenProps & RouteComponentProps) { const onJoin = useCallback(async () => { joining.current = true; - const action = { - subscribe: { - who: ship.replace("~", ""), - book, - }, - }; try { - await api.publish.publishAction(action); - await waiter((p) => !!p.notebooks?.[ship]?.[book]); - props.history.push(`/~publish/notebook/${ship}/${book}`); + await api.graph.joinGraph(ship, book); + props.history.push(`/~publish/notebook/ship/${ship}/${book}`); } catch (e) { console.error(e); setError(true); diff --git a/pkg/interface/src/views/apps/publish/components/lib/Sidebar.tsx b/pkg/interface/src/views/apps/publish/components/lib/Sidebar.tsx index 615b3122dd..167e7a1396 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Sidebar.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Sidebar.tsx @@ -1,10 +1,11 @@ -import React, { Component } from "react"; +import React from "react"; import { Box, Text, Col } from "@tlon/indigo-react"; import { Link } from "react-router-dom"; import SidebarInvite from "~/views/components/SidebarInvite"; import { Welcome } from "./Welcome"; import { GroupItem } from "./GroupItem"; import { alphabetiseAssociations } from "~/logic/lib/util"; +import _ from "lodash"; export function Sidebar(props: any) { const sidebarInvites = !(props.invites && props.invites["/publish"]) @@ -23,80 +24,37 @@ export function Sidebar(props: any) { props.associations && "contacts" in props.associations ? alphabetiseAssociations(props.associations.contacts) : {}; + const appAssociations = + props.associations && "publish" in props.associations + ? props.associations.publish + : {}; - const notebooks = {}; - Object.keys(props.notebooks).map((host) => { - Object.keys(props.notebooks[host]).map((notebook) => { - const title = `${host}/${notebook}`; - notebooks[title] = props.notebooks[host][notebook]; - }); - }); + const groups = props.groups || {}; - const groupedNotebooks = {}; - Object.keys(notebooks).map((book) => { - const path = notebooks[book]["subscribers-group-path"] - ? notebooks[book]["subscribers-group-path"] - : book; - if (path in associations) { - if (groupedNotebooks[path]) { - const array = groupedNotebooks[path]; - array.push(book); - groupedNotebooks[path] = array; - } else { - groupedNotebooks[path] = [book]; - } - } else { - if (groupedNotebooks["/~/"]) { - const array = groupedNotebooks["/~/"]; - array.push(book); - groupedNotebooks["/~/"] = array; - } else { - groupedNotebooks["/~/"] = [book]; - } - } - }); - - const selectedGroups = props.selectedGroups ? props.selectedGroups : []; - const groupedItems = Object.keys(associations) - .map((each, i) => { - const books = groupedNotebooks[each] || []; - if (books.length === 0) return; - if ( - selectedGroups.length === 0 && - groupedNotebooks["/~/"] && - groupedNotebooks["/~/"].length !== 0 - ) { - i = i + 1; - } - return ( - - ); - }); - if ( - selectedGroups.length === 0 && - groupedNotebooks["/~/"] && - groupedNotebooks["/~/"].length !== 0 - ) { - groupedItems.unshift( + const groupedItems = _.chain(props.graphs) + .reduce((acc, g, path) => { + const appPath = `/ship/~${path}`; + return appPath in appAssociations + ? [...acc, appAssociations[appPath]] + : acc; + }, [] as any[]) + .groupBy((association) => + groups?.[association["group-path"]]?.hidden + ? "unmanaged" + : association["group-path"] + ) + .map((appPaths, groupPath) => ( - ); - } + )) + .value(); - const display = props.path ? ['none', 'block'] : 'block'; + const display = props.path ? ["none", "block"] : "block"; return ( New Notebook - + {sidebarInvites} {groupedItems} diff --git a/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx b/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx index c0e26905e7..f7124bdd91 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx @@ -1,21 +1,23 @@ import React from "react"; -import { stringToSymbol } from "~/logic/lib/util"; import { FormikHelpers } from "formik"; import GlobalApi from "~/logic/api/global"; import { useWaitForProps } from "~/logic/lib/useWaitForProps"; -import { Notebook } from "~/types/publish-update"; import { RouteComponentProps } from "react-router-dom"; import { PostForm, PostFormSchema } from "./NoteForm"; +import {createPost} from "~/logic/api/graph"; +import {Graph} from "~/types/graph-update"; +import {Association} from "~/types"; interface NewPostProps { api: GlobalApi; book: string; ship: string; - notebook: Notebook; + graph: Graph; + association: Association; } export default function NewPost(props: NewPostProps & RouteComponentProps) { - const { api, book, notebook, ship, history } = props; + const { api, book, association, ship, history } = props; const waiter = useWaitForProps(props, 20000); @@ -23,26 +25,16 @@ export default function NewPost(props: NewPostProps & RouteComponentProps) { values: PostFormSchema, actions: FormikHelpers ) => { - let noteId = stringToSymbol(values.title); const { title, body } = values; - const host = ship.slice(1); - try { - try { - await api.publish.newNote(host, book, noteId, title, body); - } catch (e) { - if (e.includes("note already exists")) { - const timestamp = Math.floor(Date.now() / 1000); - noteId = `${noteId}-${timestamp}`; - await api.publish.newNote(host, book, noteId, title, body); - } else { - throw e; - } - } - await waiter((p) => { - return !!p?.notebook?.notes[noteId]; + const post = createPost([{ text: title }, { text: body }]) + const noteId = parseInt(post.index.split('/')[1], 10); + await api.graph.addPost(ship, book, post) + await waiter(p => { + const { graph } = p; + return graph.has(noteId); }); - history.push(`/~publish/notebook/${ship}/${book}/note/${noteId}`); + history.push(`/~publish/notebook/ship/${ship}/${book}/note/${noteId}`); } catch (e) { console.error(e); actions.setStatus({ error: "Posting note failed" }); @@ -58,7 +50,7 @@ export default function NewPost(props: NewPostProps & RouteComponentProps) { ); diff --git a/pkg/interface/src/views/apps/publish/components/lib/new.tsx b/pkg/interface/src/views/apps/publish/components/lib/new.tsx index cd061b68c0..896ebddbf9 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/new.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/new.tsx @@ -35,19 +35,30 @@ interface NewScreenProps { export function NewScreen(props: NewScreenProps & RouteComponentProps) { const { history } = props; - const waiter = useWaitForProps(props, 5000); - const onSubmit = async (values: FormSchema, actions) => { const bookId = stringToSymbol(values.name); try { const { name, description, group } = values; - await props.api.publish.newBook(bookId, name, description, group); - await waiter((p) => !!p?.notebooks?.[`~${window.ship}`]?.[bookId]); - if (!group) { - await waiter((p) => !!p?.groups?.[`/ship/~${window.ship}/${bookId}`]); + if (group) { + await props.api.graph.createManagedGraph( + bookId, + name, + description, + "publish", + group + ); + } else { + await props.api.graph.createUnmanagedGraph( + bookId, + name, + description, + "publish", + { open: { banned: [], banRanks: [] } } + ); } + actions.setStatus({ success: null }); - history.push(`/~publish/notebook/~${window.ship}/${bookId}`); + history.push(`/~publish/notebook/ship/~${window.ship}/${bookId}`); } catch (e) { console.error(e); actions.setStatus({ error: "Notebook creation failed" }); @@ -55,7 +66,9 @@ export function NewScreen(props: NewScreenProps & RouteComponentProps) { }; return ( - New Notebook + + New Notebook + ) => { @@ -45,25 +47,13 @@ export function Skeleton(props: SkeletonProps) { const fullyLoaded = note["num-comments"] === loadedComments; if (distanceFromBottom < 40) { if (!fullyLoaded) { - api.publish.fetchCommentsPage( - ship, - notebook, - noteId, - loadedComments, - 30 - ); } if (!note.read) { - api.publish.publishAction({ - read: { - who: ship.slice(1), - book: notebook, - note: noteId, - }, - }); + // TODO: stubbed for notification-store } } } else if (notebook && ship) { + // TODO: lazy load notebooks } }; @@ -77,13 +67,7 @@ export function Skeleton(props: SkeletonProps) { const { clientHeight, scrollHeight } = scrollRef.current; const isScrolling = clientHeight < scrollHeight; if(!isScrolling && note) { - api.publish.publishAction({ - read: { - who: ship.slice(1), - book: notebook, - note: noteId, - }, - }); + // TODO: stubbed for notification-store } }, 1500); @@ -103,6 +87,7 @@ export function Skeleton(props: SkeletonProps) { > Date: Thu, 10 Sep 2020 20:10:07 +1000 Subject: [PATCH 13/82] publish: rewrite notebook components for graph-store --- .../publish/components/lib/NotePreview.tsx | 52 ++++++++++++------- .../apps/publish/components/lib/Notebook.tsx | 30 ++++++----- .../publish/components/lib/NotebookItem.tsx | 2 +- .../publish/components/lib/NotebookPosts.tsx | 36 ++++++------- .../publish/components/lib/NotebookRoutes.tsx | 42 ++++++++------- .../apps/publish/components/lib/Settings.tsx | 41 +++++++-------- .../publish/components/lib/Subscribers.tsx | 24 +++++---- 7 files changed, 123 insertions(+), 104 deletions(-) diff --git a/pkg/interface/src/views/apps/publish/components/lib/NotePreview.tsx b/pkg/interface/src/views/apps/publish/components/lib/NotePreview.tsx index 17aa2d9d8a..e944989913 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/NotePreview.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/NotePreview.tsx @@ -7,11 +7,12 @@ import ReactMarkdown from "react-markdown"; import moment from "moment"; import { Link } from "react-router-dom"; import styled from "styled-components"; +import { GraphNode } from "~/types/graph-update"; interface NotePreviewProps { host: string; book: string; - note: Note; + node: GraphNode; contact?: Contact; hideNicknames?: boolean; } @@ -21,47 +22,60 @@ const WrappedBox = styled(Box)` `; export function NotePreview(props: NotePreviewProps) { - const { note, contact } = props; + const { node, contact } = props; + const { post } = node; + if (!post) { + return null; + } - let name = note.author; + let name = post?.author; if (contact && !props.hideNicknames) { - name = contact.nickname.length > 0 ? contact.nickname : note.author; + name = contact.nickname.length > 0 ? contact.nickname : post?.author; } - if (name === note.author) { - name = cite(note.author); + if (name === post?.author) { + name = cite(post?.author); } - let comment = "No Comments"; - if (note["num-comments"] == 1) { - comment = "1 Comment"; - } else if (note["num-comments"] > 1) { - comment = `${note["num-comments"]} Comments`; - } - const date = moment(note["date-created"]).fromNow(); + + const numComments = node.children.size; + const commentDesc = + numComments === 0 + ? "No Comments" + : numComments === 1 + ? "1 Comment" + : `${numComments} Comments`; + const date = moment(post["time-sent"]).fromNow(); //const popout = props.popout ? "popout/" : ""; - const url = `/~publish/notebook/${props.host}/${props.book}/note/${note["note-id"]}`; + const url = `/~publish/notebook/ship/${props.host}/${props.book}/note/${ + post.index.split("/")[1] + }`; + + // stubbing pending notification-store + const isRead = true; return ( - {note.title} + {post.contents[0]?.text} {name} - + {date} - {comment} + {commentDesc} diff --git a/pkg/interface/src/views/apps/publish/components/lib/Notebook.tsx b/pkg/interface/src/views/apps/publish/components/lib/Notebook.tsx index 86c94dabc9..43a0899f88 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Notebook.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Notebook.tsx @@ -20,6 +20,8 @@ import { Groups } from "~/types/group-update"; import { Contacts, Rolodex } from "~/types/contact-update"; import GlobalApi from "~/logic/api/global"; import styled from "styled-components"; +import {Association, Associations} from "~/types"; +import {Graph} from "~/types/graph-update"; const TabList = styled(_TabList)` margin-bottom: ${(p) => p.theme.space[4]}px; @@ -33,18 +35,20 @@ interface NotebookProps { api: GlobalApi; ship: string; book: string; - notebook: INotebook; + graph: Graph; notebookContacts: Contacts; + association: Association; + associations: Associations; contacts: Rolodex; groups: Groups; hideNicknames: boolean; } export function Notebook(props: NotebookProps & RouteComponentProps) { - const { api, ship, book, notebook, notebookContacts, groups } = props; + const { api, ship, book, association, notebookContacts, groups } = props; const contact = notebookContacts[ship]; - const group = groups[notebook?.["writers-group-path"]]; + const group = groups[association['group-path']]; const role = group ? roleForShip(group, window.ship) : undefined; const isOwn = `~${window.ship}` === ship; const isAdmin = role === "admin" || isOwn; @@ -52,10 +56,10 @@ export function Notebook(props: NotebookProps & RouteComponentProps) { const isWriter = isOwn || group.tags?.publish?.[`writers-${book}`]?.has(window.ship); - const notesList = notebook?.["notes-by-date"] || []; - const notes = notebook?.notes || {}; const showNickname = contact?.nickname && !props.hideNicknames; + const { metadata } = props.association || {}; + return ( {"<- All Notebooks"} - {notebook?.title} + {metadata?.title}
by @@ -80,7 +84,7 @@ export function Notebook(props: NotebookProps & RouteComponentProps) {
{isWriter && ( - + @@ -103,8 +107,7 @@ export function Notebook(props: NotebookProps & RouteComponentProps) { - {notebook?.about} + {metadata?.description} @@ -128,7 +132,7 @@ export function Notebook(props: NotebookProps & RouteComponentProps) { host={ship} book={book} api={api} - notebook={notebook} + association={props.association} contacts={notebookContacts} /> diff --git a/pkg/interface/src/views/apps/publish/components/lib/NotebookItem.tsx b/pkg/interface/src/views/apps/publish/components/lib/NotebookItem.tsx index 4a03475e78..b7622ded64 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/NotebookItem.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/NotebookItem.tsx @@ -29,7 +29,7 @@ function UnreadCount(props: { unread: number }) { export function NotebookItem(props: NotebookItemProps) { return ( - + - {props.list.map((noteId: NoteId) => { - const note = props.notes[noteId]; - if (!note) { - console.log(noteId); - return null; - } - return ( - - ); - })} + {Array.from(props.graph || []).map( + ([date, node]) => + node && ( + + ) + )} ); } diff --git a/pkg/interface/src/views/apps/publish/components/lib/NotebookRoutes.tsx b/pkg/interface/src/views/apps/publish/components/lib/NotebookRoutes.tsx index ff0dc905b7..766725088f 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/NotebookRoutes.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/NotebookRoutes.tsx @@ -1,40 +1,37 @@ import React, { useEffect } from "react"; -import { RouteComponentProps, Link, Route, Switch } from "react-router-dom"; -import { Box, Text } from "@tlon/indigo-react"; -import GlobalApi from "../../../../api/global"; -import { PublishContent } from "./PublishContent"; -import { Notebook as INotebook } from "../../../../types/publish-update"; -import { Groups } from "../../../../types/group-update"; -import { Contacts, Rolodex } from "../../../../types/contact-update"; +import { RouteComponentProps, Route, Switch } from "react-router-dom"; +import GlobalApi from "~/logic/api/global"; import Notebook from "./Notebook"; import NewPost from "./new-post"; import { NoteRoutes } from './NoteRoutes'; +import { Association, Associations, Graphs, Groups, Contacts, Rolodex } from "~/types"; interface NotebookRoutesProps { api: GlobalApi; ship: string; book: string; - notes: any; - notebook: INotebook; + graphs: Graphs; notebookContacts: Contacts; contacts: Rolodex; groups: Groups; hideAvatars: boolean; hideNicknames: boolean; + association: Association; + associations: Associations; } export function NotebookRoutes( props: NotebookRoutesProps & RouteComponentProps ) { - const { ship, book, api, notebook, notebookContacts } = props; + const { ship, book, api, notebookContacts } = props; useEffect(() => { - api.publish.fetchNotesPage(ship, book, 1, 50); - api.publish.fetchNotebook(ship, book); + ship && book && api.graph.getGraph(ship, book); }, [ship, book]); + const graph = props.graphs[`${ship.slice(1)}/${book}`]; - const baseUrl = `/~publish/notebook/${ship}/${book}`; + const baseUrl = `/~publish/notebook/ship/${ship}/${book}`; const relativePath = (path: string) => `${baseUrl}${path}`; return ( @@ -43,7 +40,7 @@ export function NotebookRoutes( path={baseUrl} exact render={(routeProps) => { - return ; + return ; }} /> )} /> @@ -62,15 +60,23 @@ export function NotebookRoutes( path={relativePath("/note/:noteId")} render={(routeProps) => { const { noteId } = routeProps.match.params; - const note = notebook?.notes[noteId]; + const noteIdNum = parseInt(noteId, 10); + + if(!graph) { + return null; + } + const note = graph.get(noteIdNum); + if(!note) { + return null; + } return ( { @@ -48,12 +45,12 @@ const ResetOnPropsChange = (props: { init: FormSchema; book: string }) => { }; export function Settings(props: SettingsProps) { - const { host, notebook, api, book } = props; + const { api, book } = props; const history = useHistory(); + const { metadata } = props.association || {}; const initialValues: FormSchema = { - name: notebook?.title, - description: notebook?.about, - comments: notebook?.comments, + name: metadata?.title, + description: metadata?.description, }; const onSubmit = async ( @@ -61,9 +58,16 @@ export function Settings(props: SettingsProps) { actions: FormikHelpers ) => { try { - const { name, description, comments } = values; - await api.publish.editBook(book, name, description, comments); - api.publish.fetchNotebook(host, book); + const { name, description } = values; + await api.metadata.metadataAdd( + "publish", + props.association["app-path"], + props.association["group-path"], + name, + description, + props.association.metadata["date-created"], + uxToHex(props.association.metadata.color) + ); actions.setStatus({ success: null }); } catch (e) { console.log(e); @@ -72,7 +76,7 @@ export function Settings(props: SettingsProps) { }; const onDelete = async () => { - await api.publish.delBook(book); + await api.graph.deleteGraph(book); history.push("/~publish"); }; @@ -96,7 +100,7 @@ export function Settings(props: SettingsProps) { Permanently delete this notebook. (All current members will no longer see this notebook.) - @@ -110,11 +114,6 @@ export function Settings(props: SettingsProps) { label="Change description" caption="Change the description of this notebook" /> - Save diff --git a/pkg/interface/src/views/apps/publish/components/lib/Subscribers.tsx b/pkg/interface/src/views/apps/publish/components/lib/Subscribers.tsx index 4ff86c82bf..fa744a58bb 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Subscribers.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Subscribers.tsx @@ -4,22 +4,22 @@ import { resourceFromPath } from '~/logic/lib/group'; import {Notebook} from '~/types/publish-update'; import GlobalApi from '~/logic/api/global'; import {Groups} from '~/types/group-update'; -import {Associations} from '~/types/metadata-update'; +import {Associations, Association} from '~/types/metadata-update'; import {Rolodex} from '~/types/contact-update'; +import {GraphNode} from '~/types/graph-update'; interface SubscribersProps { - notebook: Notebook; api: GlobalApi; groups: Groups; book: string; associations: Associations; + association: Association; contacts: Rolodex; } export class Subscribers extends Component { - constructor(props) { + constructor(props: SubscribersProps) { super(props); - this.redirect = this.redirect.bind(this); this.addUser = this.addUser.bind(this); this.removeUser = this.removeUser.bind(this); this.addAll = this.addAll.bind(this); @@ -33,13 +33,12 @@ export class Subscribers extends Component { this.props.api.groups.remove(path, [who]); } - redirect(url) { - window.location.href = url; - } - addAll() { - const path = this.props.notebook['writers-group-path']; + const path = this.props.association['group-path']; const group = path ? this.props.groups[path] : null; + if(!group) { + return; + } const resource = resourceFromPath(path); this.props.api.groups.addTag( resource, @@ -50,7 +49,7 @@ export class Subscribers extends Component { render() { - const path = this.props.notebook['writers-group-path']; + const path = this.props.association['group-path']; const group = path ? this.props.groups[path] : null; @@ -71,7 +70,10 @@ export class Subscribers extends Component { addDesc: 'Allow user to write to this notebook' }, ]; - + + if(!group) { + return null; + } return (
From 2160bed3ca44bf3f734720f134650b53b429e72a Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 10 Sep 2020 20:10:29 +1000 Subject: [PATCH 14/82] publish: rewrite note components for graph-store --- pkg/interface/src/logic/api/global.ts | 2 - .../publish/components/lib/CommentItem.tsx | 48 +++--------- .../apps/publish/components/lib/Comments.tsx | 61 +++------------- .../apps/publish/components/lib/EditPost.tsx | 23 +++--- .../apps/publish/components/lib/Note.tsx | 73 ++++++++----------- .../publish/components/lib/NoteNavigation.tsx | 46 ++++++++---- .../publish/components/lib/NoteRoutes.tsx | 14 ++-- 7 files changed, 101 insertions(+), 166 deletions(-) diff --git a/pkg/interface/src/logic/api/global.ts b/pkg/interface/src/logic/api/global.ts index d724e0073c..842cb93793 100644 --- a/pkg/interface/src/logic/api/global.ts +++ b/pkg/interface/src/logic/api/global.ts @@ -10,7 +10,6 @@ import ContactsApi from './contacts'; import GroupsApi from './groups'; import LaunchApi from './launch'; import LinksApi from './links'; -import PublishApi from './publish'; import GraphApi from './graph'; import S3Api from './s3'; @@ -23,7 +22,6 @@ export default class GlobalApi extends BaseApi { groups = new GroupsApi(this.ship, this.channel, this.store); launch = new LaunchApi(this.ship, this.channel, this.store); links = new LinksApi(this.ship, this.channel, this.store); - publish = new PublishApi(this.ship, this.channel, this.store); s3 = new S3Api(this.ship, this.channel, this.store); graph = new GraphApi(this.ship, this.channel, this.store); diff --git a/pkg/interface/src/views/apps/publish/components/lib/CommentItem.tsx b/pkg/interface/src/views/apps/publish/components/lib/CommentItem.tsx index a8e9f68083..63d36eb14b 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/CommentItem.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/CommentItem.tsx @@ -9,6 +9,7 @@ import GlobalApi from "~/logic/api/global"; import { Button, Box, Row, Text } from "@tlon/indigo-react"; import styled from "styled-components"; import { Author } from "./Author"; +import {GraphNode, TextContent} from "~/types/graph-update"; const ClickBox = styled(Box)` cursor: pointer; @@ -17,48 +18,36 @@ const ClickBox = styled(Box)` interface CommentItemProps { pending?: boolean; - comment: Comment; + comment: GraphNode; contacts: Contacts; book: string; ship: string; api: GlobalApi; - note: NoteId; hideNicknames: boolean; hideAvatars: boolean; } export function CommentItem(props: CommentItemProps) { - const { ship, contacts, book, note, api } = props; + const { ship, contacts, book, api } = props; const [editing, setEditing] = useState(false); - const commentPath = Object.keys(props.comment)[0]; - const commentData = props.comment[commentPath]; - const content = commentData.content.split("\n").map((line, i) => { + const commentData = props.comment?.post; + const comment = commentData.contents[0] as TextContent; + const content = comment.text.split("\n").map((line, i) => { return ( - + {line} ); }); - const disabled = props.pending || window.ship !== commentData.author.slice(1); - - const onUpdate = async ({ comment }) => { - await api.publish.updateComment( - ship.slice(1), - book, - note, - commentPath, - comment - ); - setEditing(false); - }; + const disabled = props.pending || window.ship !== commentData.author; const onDelete = async () => { - await api.publish.deleteComment(ship.slice(1), book, note, commentPath); + await api.graph.removeNodes(ship, book, [commentData?.index]); }; return ( - + - {!disabled && !editing && ( + {!disabled && ( <> - setEditing(true)}> - Edit - Delete )} - {editing && ( - setEditing(false)} color="red"> - Cancel - - )} {!editing && content} - {editing && ( - - )} ); diff --git a/pkg/interface/src/views/apps/publish/components/lib/Comments.tsx b/pkg/interface/src/views/apps/publish/components/lib/Comments.tsx index e3f17749a9..3a3ef605f6 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Comments.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Comments.tsx @@ -8,50 +8,34 @@ import { Contacts } from "~/types/contact-update"; import _ from "lodash"; import GlobalApi from "~/logic/api/global"; import { FormikHelpers } from "formik"; +import {GraphNode, Graph} from "~/types/graph-update"; +import {createPost} from "~/logic/api/graph"; interface CommentsProps { - comments: Comment[]; + comments: Graph; book: string; - noteId: NoteId; - note: Note; + note: GraphNode; ship: string; contacts: Contacts; api: GlobalApi; - numComments: number; - enabled: boolean; hideAvatars: boolean; hideNicknames: boolean; } export function Comments(props: CommentsProps) { - const { comments, ship, book, note, api, noteId, numComments } = props; - const [pending, setPending] = useState([]); - - useEffect(() => { - _.forEach(comments, (comment: Comment) => { - const { content } = comment[Object.keys(comment)[0]]; - setPending((p) => p.filter((p) => p !== content)); - }); - }, [numComments]); + const { comments, ship, book, note, api } = props; const onSubmit = async ( { comment }, actions: FormikHelpers<{ comment: string }> ) => { - setPending((p) => [...p, comment]); - const action = { - "new-comment": { - who: ship.slice(1), - book: book, - note: noteId, - body: comment, - }, - }; try { - await api.publish.publishAction(action); + const post = createPost([{ text: comment }], note?.post?.index); + await api.graph.addPost(ship, book, post) actions.resetForm(); actions.setStatus({ success: null }); } catch (e) { + console.error(e); actions.setStatus({ error: e.message }); } }; @@ -59,37 +43,14 @@ export function Comments(props: CommentsProps) { return ( - {Array.from(pending).map((com, i) => { - const da = dateToDa(new Date()); - const ship = `~${window.ship}`; - const comment = { - [da]: { - author: ship, - content: com, - "date-created": Math.round(new Date().getTime()), - }, - } as Comment; - return ( - - ); - })} - {props.comments.map((com, i) => ( + {Array.from(comments).reverse().map(([idx, comment]) => ( diff --git a/pkg/interface/src/views/apps/publish/components/lib/EditPost.tsx b/pkg/interface/src/views/apps/publish/components/lib/EditPost.tsx index dde8d8d5a4..fa801ba995 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/EditPost.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/EditPost.tsx @@ -1,22 +1,23 @@ -import React, { Component } from "react"; +import React from "react"; import { PostFormSchema, PostForm } from "./NoteForm"; -import { Note } from "../../../../types/publish-update"; import { FormikHelpers } from "formik"; -import GlobalApi from "../../../../api/global"; +import GlobalApi from "~/logic/api/global"; import { RouteComponentProps } from "react-router-dom"; +import { GraphNode, TextContent } from "~/types"; interface EditPostProps { ship: string; - noteId: string; - note: Note; + noteId: number; + note: GraphNode; api: GlobalApi; book: string; } export function EditPost(props: EditPostProps & RouteComponentProps) { const { note, book, noteId, api, ship, history } = props; - const body = note.file.slice(note.file.indexOf(";>") + 2); + const [title, file] = note.post.contents as TextContent[]; + const body = file.text.slice(file.text.indexOf(";>") + 2); const initial: PostFormSchema = { - title: note.title, + title: title.text, body, }; @@ -25,11 +26,11 @@ export function EditPost(props: EditPostProps & RouteComponentProps) { actions: FormikHelpers ) => { const { title, body } = values; - const host = ship.slice(1); try { - await api.publish.editNote(host, book, noteId, title, body); - history.push(`/~publish/notebook/${ship}/${book}/note/${noteId}`); + // graph-store does not support editing nodes + throw new Error("Unsupported"); } catch (e) { + console.error(e); actions.setStatus({ error: "Failed to edit notebook" }); } }; @@ -38,7 +39,7 @@ export function EditPost(props: EditPostProps & RouteComponentProps) { ); diff --git a/pkg/interface/src/views/apps/publish/components/lib/Note.tsx b/pkg/interface/src/views/apps/publish/components/lib/Note.tsx index d3cf8d9285..7e00886ce9 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Note.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Note.tsx @@ -5,21 +5,15 @@ import { Link, RouteComponentProps } from "react-router-dom"; import { Spinner } from "~/views/components/Spinner"; import { Comments } from "./Comments"; import { NoteNavigation } from "./NoteNavigation"; -import { - NoteId, - Note as INote, - Notebook, -} from "~/types/publish-update"; -import { Contacts } from "~/types/contact-update"; import GlobalApi from "~/logic/api/global"; import { Author } from "./Author"; +import { Contacts, GraphNode, Graph} from "~/types"; interface NoteProps { ship: string; book: string; - noteId: NoteId; - note: INote; - notebook: Notebook; + note: GraphNode; + notebook: Graph; contacts: Contacts; api: GlobalApi; hideAvatars: boolean; @@ -28,31 +22,27 @@ interface NoteProps { export function Note(props: NoteProps & RouteComponentProps) { const [deleting, setDeleting] = useState(false); - const { notebook, note, contacts, ship, book, noteId, api } = props; - useEffect(() => { - api.publish.fetchNote(ship, book, noteId); - }, [ship, book, noteId]); + const { contacts, ship, notebook, book, api, note } = props; - const baseUrl = `/~publish/notebook/${props.ship}/${props.book}`; + const baseUrl = `/~publish/notebook/ship/${props.ship}/${props.book}`; const deletePost = async () => { setDeleting(true); - await api.publish.delNote(ship.slice(1), book, noteId); + const indices = [note.post.index] + await api.graph.removeNodes(ship, book, indices); props.history.push(baseUrl); }; - const comments = note?.comments || []; - const file = note?.file; + const comments = note?.children + const file = note?.post?.contents[1]?.text || ""; const newfile = file ? file.slice(file.indexOf(";>") + 2) : ""; - let editPost: JSX.Element | null = null; - const editUrl = props.location.pathname + "/edit"; - if (`~${window.ship}` === note?.author) { - editPost = ( + const noteId = parseInt(note.post.index.split('/')[1], 10); + + let adminLinks: JSX.Element | null = null; + if (window.ship === note?.post?.author) { + adminLinks = ( - - Edit - {"<- Notebook Index"} - {note?.title || ""} + {note?.post?.contents[0]?.text || ""} - {editPost} + {adminLinks} @@ -98,25 +88,20 @@ export function Note(props: NoteProps & RouteComponentProps) { - {notebook.comments && ( - - )} + index === child); + const target = children[backwards ? i-1 : i+1]; + return target?.[0] || null; +} + + interface NoteNavigationProps { book: string; - nextId?: string; - prevId?: string; + noteId: number; ship: string; - notebook: Notebook; + notebook: Graph; } export function NoteNavigation(props: NoteNavigationProps) { @@ -43,32 +50,39 @@ export function NoteNavigation(props: NoteNavigationProps) { let prevComponent = ; let nextUrl = ""; let prevUrl = ""; - const { nextId, prevId, notebook } = props; - const next = - nextId && nextId in notebook?.notes ? notebook?.notes[nextId] : null; + const { noteId, notebook } = props; + if(!notebook) { + return null; + } + const nextId = getAdjacentId(notebook, noteId); + const prevId = getAdjacentId(notebook, noteId, true); + const next = nextId && notebook.get(nextId); + const prev = prevId && notebook.get(prevId); - const prev = - prevId && prevId in notebook?.notes ? notebook?.notes[prevId] : null; if (!next && !prev) { return null; } if (next) { - nextUrl = `/~publish/notebook/${props.ship}/${props.book}/note/${props.nextId}`; + nextUrl = `/~publish/notebook/ship/${props.ship}/${props.book}/note/${nextId}`; + const title = next.post.contents[0]?.text; + const date = next.post['time-sent']; nextComponent = ( ); } if (prev) { - prevUrl = `/~publish/notebook/${props.ship}/${props.book}/note/${props.prevId}`; + prevUrl = `/~publish/notebook/ship/${props.ship}/${props.book}/note/${prevId}`; + const title = prev.post.contents[0]?.text; + const date = prev.post['time-sent']; prevComponent = ( diff --git a/pkg/interface/src/views/apps/publish/components/lib/NoteRoutes.tsx b/pkg/interface/src/views/apps/publish/components/lib/NoteRoutes.tsx index e97105ab1f..1f45b526e5 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/NoteRoutes.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/NoteRoutes.tsx @@ -1,27 +1,29 @@ import React from "react"; import { Route, Switch } from "react-router-dom"; -import { NoteId, Note as INote, Notebook } from "~/types/publish-update"; -import { Contacts } from "~/types/contact-update"; import GlobalApi from "~/logic/api/global"; import { RouteComponentProps } from "react-router-dom"; import Note from "./Note"; import { EditPost } from "./EditPost"; +import { GraphNode, Graph, Contacts } from "~/types"; + interface NoteRoutesProps { ship: string; book: string; - noteId: NoteId; - note: INote; - notebook: Notebook; + note: GraphNode; + noteId: number; + notebook: Graph; contacts: Contacts; api: GlobalApi; + hideAvatars: boolean; + hideNicknames: boolean; } export function NoteRoutes(props: NoteRoutesProps & RouteComponentProps) { const { ship, book, noteId } = props; - const baseUrl = `/~publish/notebook/${ship}/${book}/note/${noteId}`; + const baseUrl = `/~publish/notebook/ship/${ship}/${book}/note/${noteId}`; const relativePath = (path: string) => `${baseUrl}${path}`; return ( From 03807619ed54ae91c2fdd81ebaae08493abefa98 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 10 Sep 2020 20:20:00 +1000 Subject: [PATCH 15/82] graph-view: refine threads --- pkg/arvo/lib/graph-view.hoon | 9 ++++- pkg/arvo/sur/graph-view.hoon | 2 +- pkg/arvo/ted/graph/delete.hoon | 40 ++++++++++--------- pkg/arvo/ted/graph/join.hoon | 7 ++-- pkg/arvo/ted/graph/leave.hoon | 4 +- pkg/interface/src/logic/api/graph.ts | 11 ++--- pkg/interface/src/types/graph-update.ts | 30 ++++++++++++++ .../apps/publish/components/lib/Join.tsx | 2 +- 8 files changed, 73 insertions(+), 32 deletions(-) create mode 100644 pkg/interface/src/types/graph-update.ts diff --git a/pkg/arvo/lib/graph-view.hoon b/pkg/arvo/lib/graph-view.hoon index f208accf31..9127bf0906 100644 --- a/pkg/arvo/lib/graph-view.hoon +++ b/pkg/arvo/lib/graph-view.hoon @@ -6,8 +6,9 @@ |% ++ app-to-mark |= app=app-name - ?+ app !! + ?+ app ~|("nonexistent app" !!) %chat %graph-validator-chat + %publish %graph-validator-publish == ++ dejs =, dejs:format @@ -37,7 +38,10 @@ |= jon=json ^- app-name ?> ?=(%s -.jon) - %chat + ?+ p.jon !! + %chat %chat + %publish %publish + == :: ++ leave %- ot @@ -53,6 +57,7 @@ %- ot :~ resource+dejs:resource ship+(su ;~(pfix sig fed:ag)) + app+app == :: ++ groupify !! diff --git a/pkg/arvo/sur/graph-view.hoon b/pkg/arvo/sur/graph-view.hoon index d465c48ab8..0397cf9bd2 100644 --- a/pkg/arvo/sur/graph-view.hoon +++ b/pkg/arvo/sur/graph-view.hoon @@ -39,7 +39,7 @@ == [%delete rid=resource] [%leave rid=resource] - [%join rid=resource =ship] + [%join rid=resource =ship app=app-name] ::[%invite rid=resource ships=(set ship)] :: [%groupify rid=resource title=@t description=@t] == diff --git a/pkg/arvo/ted/graph/delete.hoon b/pkg/arvo/ted/graph/delete.hoon index 54de93faab..d9ff09fc8e 100644 --- a/pkg/arvo/ted/graph/delete.hoon +++ b/pkg/arvo/ted/graph/delete.hoon @@ -9,34 +9,37 @@ ++ scry-metadata |= rid=resource =/ m (strand ,(unit resource)) - ;< pax=(unit path) bind:m - %+ scry:strandio ,(unit path) + ;< paxs=(unit (set path)) bind:m + %+ scry:strandio ,(unit (set path)) ;: weld - /gx/metadata-store/resource/graph + /gx/metadata-store/resource/publish (en-path:resource rid) /noun == - (pure:m (bind pax de-path:resource)) + ?~ paxs (pure:m ~) + ?~ u.paxs (pure:m ~) + (pure:m `(de-path:resource n.u.paxs)) :: ++ scry-group |= rid=resource - =/ m (strand ,(unit resource)) + =/ m (strand ,group) ;< ugroup=(unit group) bind:m %+ scry:strandio ,(unit group) ;: weld - /gx/group-store/resource/graph + /gx/group-store/groups (en-path:resource rid) /noun == (pure:m (need ugroup)) :: ++ delete-graph - |= rid=resource + |= [group=resource rid=resource app=app-name:graph-view] =/ m (strand ,~) + ::;< ~ bind:m + :: (poke-our %graph-push-hook %push-hook-action [%remove rid.action]) + ;< =bowl:spider bind:m get-bowl:strandio ;< ~ bind:m - (poke-our %graph-push-hook %push-hook-action [%remove rid.action]) - ;< ~ bind:m - (poke-our %graph-store %graph-update [%archive-graph rid.action]) + (poke-our %graph-store %graph-update !>([%0 now.bowl %archive-graph rid])) (pure:m ~) -- :: @@ -44,21 +47,22 @@ |= arg=vase =/ m (strand ,vase) ^- form:m -=+ !<([=action:graph-view ~] arg) +=+ !<(=action:graph-view arg) ?> ?=(%delete -.action) ;< =bowl:spider bind:m get-bowl:strandio ?> =(our.bowl entity.rid.action) ;< ugroup-rid=(unit resource) bind:m (scry-metadata rid.action) -?~ ugroup-rid (fail:m %nonexistent) -;< ugroup=(unit group) +?~ ugroup-rid !! +;< =group bind:m (scry-group u.ugroup-rid) -?~ ugroup (fail:m %nonexistent) -?. hidden.u.ugroup - (delete-graph rid.action) +?. hidden.group + ;< ~ bind:m + (delete-graph rid.action) + (pure:m !>(~)) ;< ~ bind:m - (poke-our %group-push-hook %push-hook-action [%remove rid.action]) + (poke-our %group-push-hook %push-hook-action !>([%remove rid.action])) ;< ~ bind:m - (poke-our %group-store %group-action !>([%remove-group rid.action]) + (poke-our %group-store %group-action !>([%remove-group rid.action])) ;< ~ bind:m (delete-graph rid.action) (pure:m !>(~)) diff --git a/pkg/arvo/ted/graph/join.hoon b/pkg/arvo/ted/graph/join.hoon index a61d20c91c..976c0ef635 100644 --- a/pkg/arvo/ted/graph/join.hoon +++ b/pkg/arvo/ted/graph/join.hoon @@ -16,13 +16,13 @@ (pure:m ~) :: ++ scry-metadata - |= rid=resource + |= [app=app-name:graph-view rid=resource] =/ m (strand ,(unit resource)) ^- form:m ;< pax=(unit (set path)) bind:m %+ scry:strandio ,(unit (set path)) ;: weld - /gx/metadata-store/resource/chat + /gx/metadata-store/resource/[app] (en-path:resource rid) /noun == @@ -62,7 +62,8 @@ ;< ~ bind:m %+ poke-our %metadata-hook metadata-hook-action+!>([%add-synced ship.action rid.action]) +:: ;< ~ bind:m %+ poke-our %graph-pull-hook - pull-hook-action+!>([%add ship.action rid.action])) + pull-hook-action+!>([%add ship.action rid.action]) (pure:m !>(~)) diff --git a/pkg/arvo/ted/graph/leave.hoon b/pkg/arvo/ted/graph/leave.hoon index 84660d109e..60b1957781 100644 --- a/pkg/arvo/ted/graph/leave.hoon +++ b/pkg/arvo/ted/graph/leave.hoon @@ -50,14 +50,14 @@ ?< =(our.bowl entity.rid.action) ;< ugroup-rid=(unit resource) bind:m (scry-metadata rid.action) -?~ ugroup-rid (fail:m %nonexistent) +?~ ugroup-rid !! ;< ugroup=(unit group) (scry-group u.ugroup-rid) ?~ ugroup (fail:m %nonexistent) ?. hidden.u.ugroup (delete-graph rid.action) ;< ~ bind:m - (poke-our %group-push-hook %pull-hook-action [%remove rid.action]) + (poke-our %group-push-hook %pull-hook-action !>([%remove rid.action])) ;< ~ bind:m (poke-our %group-store %group-action !>([%remove-group rid.action]) ;< ~ bind:m (delete-graph rid.action) diff --git a/pkg/interface/src/logic/api/graph.ts b/pkg/interface/src/logic/api/graph.ts index 4e64d50f46..da4cc56e01 100644 --- a/pkg/interface/src/logic/api/graph.ts +++ b/pkg/interface/src/logic/api/graph.ts @@ -3,7 +3,7 @@ import { StoreState } from '../store/type'; import { Patp, Path, PatpNoSig } from '~/types/noun'; import _ from 'lodash'; import {makeResource, resourceFromPath} from '../lib/group'; -import {GroupPolicy, Enc} from '~/types'; +import {GroupPolicy, Enc, Post} from '~/types'; export const createPost = (contents: Object[], parentIndex: string = '') => { return { @@ -55,12 +55,13 @@ export default class GraphApi extends BaseApi { }); } - joinGraph(ship: Patp, name: string) { + joinGraph(ship: Patp, name: string, app: string) { const resource = makeResource(ship, name); return this.viewAction('graph-join', { join: { resource, - ship + ship, + app } }); } @@ -92,7 +93,7 @@ export default class GraphApi extends BaseApi { }); } - addPost(ship: Patp, name: string, post: Object) { + addPost(ship: Patp, name: string, post: Post) { let nodes = {}; const resource = { ship, name }; nodes[post.index] = { @@ -162,7 +163,7 @@ export default class GraphApi extends BaseApi { }); } - getGraphSubset(ship: string, resource: string, start: string, end: start) { + getGraphSubset(ship: string, resource: string, start: string, end: string) { this.scry( 'graph-store', `/graph-subset/${ship}/${resource}/${end}/${start}` diff --git a/pkg/interface/src/types/graph-update.ts b/pkg/interface/src/types/graph-update.ts new file mode 100644 index 0000000000..c9fb6a19c9 --- /dev/null +++ b/pkg/interface/src/types/graph-update.ts @@ -0,0 +1,30 @@ +import {Patp} from "./noun"; + + +export interface TextContent { text: string; }; +export interface UrlContent { url: string; } +export interface CodeContent { expresssion: string; output: string; }; +export interface ReferenceContent { uid: string; } +export type Content = TextContent | UrlContent | CodeContent | ReferenceContent; + +export interface Post { + author: Patp; + contents: Content[]; + hash?: string; + index: string; + pending?: boolean; + signatures: string[]; + 'time-sent': number; +} + + +export interface GraphNode { + children: Graph; + post: Post; +} + +export type Graph = Map; + +export type Graphs = { [rid: string]: Graph }; + + diff --git a/pkg/interface/src/views/apps/publish/components/lib/Join.tsx b/pkg/interface/src/views/apps/publish/components/lib/Join.tsx index c72dfe8f43..c4d7019d47 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Join.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Join.tsx @@ -24,7 +24,7 @@ export function JoinScreen(props: JoinScreenProps & RouteComponentProps) { joining.current = true; try { - await api.graph.joinGraph(ship, book); + await api.graph.joinGraph(ship, book, 'publish'); props.history.push(`/~publish/notebook/ship/${ship}/${book}`); } catch (e) { console.error(e); From cae2ccd707807ca0f76e61bba71c1d7fd2cdaab3 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 10 Sep 2020 21:36:00 +1000 Subject: [PATCH 16/82] graph-view: revived groupify --- pkg/arvo/app/graph-pull-hook.hoon | 44 ------- pkg/arvo/app/graph-push-hook.hoon | 115 ------------------ pkg/arvo/lib/graph-view.hoon | 9 +- pkg/arvo/sur/graph-view.hoon | 2 +- pkg/arvo/ted/graph/groupify.hoon | 72 +++++++++++ pkg/interface/src/logic/api/graph.ts | 13 ++ .../publish/components/lib/GroupifyForm.tsx | 5 +- 7 files changed, 96 insertions(+), 164 deletions(-) delete mode 100644 pkg/arvo/app/graph-pull-hook.hoon delete mode 100644 pkg/arvo/app/graph-push-hook.hoon create mode 100644 pkg/arvo/ted/graph/groupify.hoon diff --git a/pkg/arvo/app/graph-pull-hook.hoon b/pkg/arvo/app/graph-pull-hook.hoon deleted file mode 100644 index c6429c036e..0000000000 --- a/pkg/arvo/app/graph-pull-hook.hoon +++ /dev/null @@ -1,44 +0,0 @@ -/- *resource -/+ store=graph-store, graph, default-agent, verb, dbug, pull-hook -~% %graph-pull-hook-top ..is ~ -|% -+$ card card:agent:gall -++ config - ^- config:pull-hook - :* %graph-store - update:store - %graph-update - %graph-push-hook - == --- -:: -%- agent:dbug -^- agent:gall -%- (agent:pull-hook config) -^- (pull-hook:pull-hook config) -|_ =bowl:gall -+* this . - def ~(. (default-agent this %|) bowl) - dep ~(. (default:pull-hook this config) bowl) -:: -++ on-init on-init:def -++ on-save !>(~) -++ on-load on-load:def -++ on-poke on-poke:def -++ on-peek on-peek:def -++ on-arvo on-arvo:def -++ on-fail on-fail:def -++ on-agent on-agent:def -++ on-watch on-watch:def -++ on-leave on-leave:def -++ on-pull-nack - :: TODO: archive the graph - on-pull-nack:dep -:: -++ on-pull-kick - |= =resource - ^- (unit path) - =/ maybe-time (peek-log:graph resource) - ?~ maybe-time `/ - `/(scot %da u.maybe-time) --- diff --git a/pkg/arvo/app/graph-push-hook.hoon b/pkg/arvo/app/graph-push-hook.hoon deleted file mode 100644 index 08d36c8cfe..0000000000 --- a/pkg/arvo/app/graph-push-hook.hoon +++ /dev/null @@ -1,115 +0,0 @@ -/+ store=graph-store -/+ res=resource -/+ graph -/+ group -/+ default-agent -/+ dbug -/+ push-hook -~% %graph-push-hook-top ..is ~ -|% -+$ card card:agent:gall -++ config - ^- config:push-hook - :* %graph-store - /updates - update:store - %graph-update - %graph-pull-hook - == -:: -+$ agent (push-hook:push-hook config) --- -:: -%- agent:dbug -^- agent:gall -%- (agent:push-hook config) -^- agent -|_ =bowl:gall -+* this . - def ~(. (default-agent this %|) bowl) - grp ~(. group bowl) - gra ~(. graph bowl) -:: -++ on-init on-init:def -++ on-save !>(~) -++ on-load on-load:def -++ on-poke on-poke:def -++ on-agent on-agent:def -++ on-watch on-watch:def -++ on-leave on-leave:def -++ on-peek on-peek:def -++ on-arvo on-arvo:def -++ on-fail on-fail:def -:: -++ should-proxy-update - |= =vase - ^- ? - =/ =update:store !<(update:store vase) - ?- -.q.update - %add-graph %.y - %remove-graph %.y - %add-nodes %.y - %remove-nodes %.y - %add-signatures %.y - %remove-signatures %.y - %archive-graph %.y - %unarchive-graph %.n - %add-tag %.n - %remove-tag %.n - %keys %.n - %tags %.n - %tag-queries %.n - %run-updates %.y - == -:: -++ resource-for-update - |= =vase - ^- (unit resource:res) - =/ =update:store !<(update:store vase) - ?- -.q.update - %add-graph `resource.q.update - %remove-graph `resource.q.update - %add-nodes `resource.q.update - %remove-nodes `resource.q.update - %add-signatures `resource.uid.q.update - %remove-signatures `resource.uid.q.update - %archive-graph `resource.q.update - %unarchive-graph ~ - %add-tag ~ - %remove-tag ~ - %run-updates `resource.q.update - %keys ~ - %tags ~ - %tag-queries ~ - == -:: -++ initial-watch - |= [=path =resource:res] - ^- vase - ?> (can-join:grp resource src.bowl) - ?~ path - :: new subscribe - =/ [=graph:store mark=(unit mark:store)] - (get-graph:gra resource) - !> ^- update:store - [%0 now.bowl [%add-graph resource graph mark]] - :: resubscribe - :: - :: TODO: use action-log - :: - !! -:: -++ take-update - |= =vase - ^- [(list card) agent] - =/ =update:store !<(update:store vase) - ?+ -.q.update [~ this] - %remove-graph - :_ this - [%give %kick ~[resource+(en-path:res resource.q.update)] ~]~ - :: - %archive-graph - :_ this - [%give %kick ~[resource+(en-path:res resource.q.update)] ~]~ - == --- diff --git a/pkg/arvo/lib/graph-view.hoon b/pkg/arvo/lib/graph-view.hoon index 9127bf0906..c8d7f6ae91 100644 --- a/pkg/arvo/lib/graph-view.hoon +++ b/pkg/arvo/lib/graph-view.hoon @@ -21,7 +21,7 @@ delete+delete join+join leave+leave - ::groupify+groupify + groupify+groupify ::invite+invite == :: @@ -60,7 +60,12 @@ app+app == :: - ++ groupify !! + ++ groupify + %- ou + :~ resource+(un dejs:resource) + app+(un app) + to+(uf ~ (mu dejs:resource)) + == ++ invite !! :: ++ associated diff --git a/pkg/arvo/sur/graph-view.hoon b/pkg/arvo/sur/graph-view.hoon index 0397cf9bd2..df9d70fcf6 100644 --- a/pkg/arvo/sur/graph-view.hoon +++ b/pkg/arvo/sur/graph-view.hoon @@ -41,7 +41,7 @@ [%leave rid=resource] [%join rid=resource =ship app=app-name] ::[%invite rid=resource ships=(set ship)] - :: [%groupify rid=resource title=@t description=@t] + [%groupify rid=resource app=app-name to=(unit resource)] == -- diff --git a/pkg/arvo/ted/graph/groupify.hoon b/pkg/arvo/ted/graph/groupify.hoon new file mode 100644 index 0000000000..c3f2dda03d --- /dev/null +++ b/pkg/arvo/ted/graph/groupify.hoon @@ -0,0 +1,72 @@ +/- spider, graph-view, graph=graph-store, *metadata-store, *group, *metadata-store +/+ strandio, resource +=> +|% +++ strand strand:spider +++ poke poke:strandio +++ poke-our poke-our:strandio +:: +++ check-live + |= who=ship + =/ m (strand ,~) + ^- form:m + %+ (set-timeout:strandio ,~) ~s20 + ;< ~ bind:m + (poke [who %hood] %helm-hi !>(~)) + (pure:m ~) +:: +++ scry-group + |= rid=resource + =/ m (strand ,group) + ^- form:m + ;< ugroup=(unit group) bind:m + %+ scry:strandio (unit group) + %+ weld /gx/group-store/groups + (snoc (en-path:resource rid) %noun) + ?> ?=(^ ugroup) + (pure:m u.ugroup) +:: +++ scry-metadatum + |= [app=app-name:graph-view rid=resource] + =/ m (strand ,metadata) + ^- form:m + =/ enc-path=@t + (scot %t (spat (en-path:resource rid))) + ;< umeta=(unit metadata) bind:m + %+ scry:strandio (unit metadata) + %+ weld /gx/metadata-store/metadata + /[enc-path]/[app]/[enc-path]/noun + ?> ?=(^ umeta) + (pure:m u.umeta) +-- +:: +^- thread:spider +|= arg=vase +=/ m (strand ,vase) +^- form:m +=+ !<(=action:graph-view arg) +?> ?=(%groupify -.action) +;< =group bind:m (scry-group rid.action) +?> hidden.group +;< =metadata bind:m + (scry-metadatum app.action rid.action) +?~ to.action + ;< ~ bind:m + %+ poke-our %contact-view + contact-view-action+!>([%groupify rid.action title.metadata description.metadata]) + (pure:m !>(~)) +;< new=^group bind:m (scry-group u.to.action) +?< hidden.new +=/ new-path + (en-path:resource u.to.action) +=/ app-path + (en-path:resource rid.action) +=/ add-md=metadata-action + [%add new-path [app.action app-path] metadata] +;< ~ bind:m + (poke-our %metadata-store metadata-action+!>(add-md)) +;< ~ bind:m + (poke-our %metadata-store metadata-action+!>([%remove app-path [app.action app-path]])) +;< ~ bind:m + (poke-our %group-store %group-update !>([%remove-group rid.action])) +(pure:m !>(~)) diff --git a/pkg/interface/src/logic/api/graph.ts b/pkg/interface/src/logic/api/graph.ts index da4cc56e01..4b9791c727 100644 --- a/pkg/interface/src/logic/api/graph.ts +++ b/pkg/interface/src/logic/api/graph.ts @@ -75,6 +75,19 @@ export default class GraphApi extends BaseApi { }); } + groupifyGraph(ship: Patp, name: string, app: string, toPath?: string) { + const resource = makeResource(ship, name); + const to = toPath && resourceFromPath(toPath); + + return this.viewAction('graph-groupify', { + groupify: { + resource, + app, + to + } + }); + } + addGraph(ship: Patp, name: string, graph: any, mark: any) { this.storeAction({ 'add-graph': { diff --git a/pkg/interface/src/views/apps/publish/components/lib/GroupifyForm.tsx b/pkg/interface/src/views/apps/publish/components/lib/GroupifyForm.tsx index 8d8f78cbe1..7f23959f07 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/GroupifyForm.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/GroupifyForm.tsx @@ -34,10 +34,11 @@ export function GroupifyForm(props: GroupifyFormProps) { actions: FormikHelpers ) => { try { - // TODO - // await props.api.publish.groupify(props.book, values.group); + await props.api.graph.groupifyGraph( + `~${window.ship}`, props.book, 'publish', values.group || undefined); actions.setStatus({ success: null }); } catch (e) { + console.error(e); actions.setStatus({ error: e.message }); } }; From dd3fd3b6a9699b7bf9cdccb31c4d26a6ca41ca93 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 15 Sep 2020 15:32:04 +1000 Subject: [PATCH 17/82] graph-store: allow recursive add-nodes from JSON --- pkg/arvo/app/graph-store.hoon | 2 +- pkg/arvo/lib/graph-store.hoon | 10 +++++++++- pkg/interface/src/logic/api/graph.ts | 10 +++++----- pkg/interface/src/logic/reducers/graph-update.js | 12 ++++++++++-- pkg/interface/src/types/graph-update.ts | 7 ++++++- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/pkg/arvo/app/graph-store.hoon b/pkg/arvo/app/graph-store.hoon index b30c6f7b4d..917d848154 100644 --- a/pkg/arvo/app/graph-store.hoon +++ b/pkg/arvo/app/graph-store.hoon @@ -497,7 +497,7 @@ =/ =ship (slav %p i.t.t.path) =/ =term i.t.t.t.path =/ =index:store - (turn t.t.t.t.path |=(=cord (slav %ud cord))) + (turn t.t.t.t.path |=(=cord (rash cord dem:ag))) =/ node=(unit node:store) (get-node ship term index) ?~ node [~ ~] :- ~ :- ~ :- %graph-update diff --git a/pkg/arvo/lib/graph-store.hoon b/pkg/arvo/lib/graph-store.hoon index 4ed56c4a72..b1ee831262 100644 --- a/pkg/arvo/lib/graph-store.hoon +++ b/pkg/arvo/lib/graph-store.hoon @@ -302,7 +302,15 @@ :~ [%post post] :: TODO: support adding nodes with children by supporting the :: graph key - [%children (of [%empty ul]~)] + [%children internal-graph] + :: [%children (of [%empty ul]~)] + == + :: + ++ internal-graph + ^- $-(json ^internal-graph) + %- of + :~ [%empty ul] + [%graph graph] == :: ++ post diff --git a/pkg/interface/src/logic/api/graph.ts b/pkg/interface/src/logic/api/graph.ts index 4b9791c727..85ad503303 100644 --- a/pkg/interface/src/logic/api/graph.ts +++ b/pkg/interface/src/logic/api/graph.ts @@ -3,7 +3,7 @@ import { StoreState } from '../store/type'; import { Patp, Path, PatpNoSig } from '~/types/noun'; import _ from 'lodash'; import {makeResource, resourceFromPath} from '../lib/group'; -import {GroupPolicy, Enc, Post} from '~/types'; +import {GroupPolicy, Enc, Post, NodeMap} from '~/types'; export const createPost = (contents: Object[], parentIndex: string = '') => { return { @@ -122,8 +122,8 @@ export default class GraphApi extends BaseApi { }); } - addNodes(ship: Patp, name: string, nodes: Object) { - this.storeAction({ + addNodes(ship: Patp, name: string, nodes: NodeMap) { + return this.storeAction({ 'add-nodes': { resource: { ship, name }, nodes @@ -188,9 +188,9 @@ export default class GraphApi extends BaseApi { } getNode(ship: string, resource: string, index: string) { - this.scry( + return this.scry( 'graph-store', - `/node/${ship}/${resource}/${index}` + `/node/${ship}/${resource}${index}` ).then((node) => { this.store.handleEvent({ data: node diff --git a/pkg/interface/src/logic/reducers/graph-update.js b/pkg/interface/src/logic/reducers/graph-update.js index 54047e72a5..5d6d20afbf 100644 --- a/pkg/interface/src/logic/reducers/graph-update.js +++ b/pkg/interface/src/logic/reducers/graph-update.js @@ -91,6 +91,14 @@ const removeGraph = (json, state) => { } }; +const mapifyChildren = (children) => { + return new Map( + children.map(([idx, node]) => { + const nd = {...node, children: mapifyChildren(node.children || []) }; + return [parseInt(idx.slice(1), 10), nd]; + })); +}; + const addNodes = (json, state) => { const _addNode = (graph, index, node) => { // set child of graph @@ -127,8 +135,8 @@ const addNodes = (json, state) => { if (index.length === 0) { return; } - // TODO: support adding nodes with children - item[1].children = new Map(); + item[1].children = mapifyChildren(item[1].children || []); + state.graphs[resource] = _addNode( state.graphs[resource], diff --git a/pkg/interface/src/types/graph-update.ts b/pkg/interface/src/types/graph-update.ts index c9fb6a19c9..4c019ddf34 100644 --- a/pkg/interface/src/types/graph-update.ts +++ b/pkg/interface/src/types/graph-update.ts @@ -10,7 +10,7 @@ export type Content = TextContent | UrlContent | CodeContent | ReferenceContent export interface Post { author: Patp; contents: Content[]; - hash?: string; + hash?: string | null; index: string; pending?: boolean; signatures: string[]; @@ -27,4 +27,9 @@ export type Graph = Map; export type Graphs = { [rid: string]: Graph }; +/** + * `(map index node)` to be sent over the wire + */ +export type NodeMap = Record; + From de99436bc5beddb572883a4c86c01d0bfbb14e80 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 15 Sep 2020 15:49:14 +1000 Subject: [PATCH 18/82] graph-store: order map in JS --- pkg/interface/src/logic/lib/OrderedMap.ts | 21 +++++++++++++++++++ .../src/logic/reducers/graph-update.js | 11 +++++----- 2 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 pkg/interface/src/logic/lib/OrderedMap.ts diff --git a/pkg/interface/src/logic/lib/OrderedMap.ts b/pkg/interface/src/logic/lib/OrderedMap.ts new file mode 100644 index 0000000000..d66346ff61 --- /dev/null +++ b/pkg/interface/src/logic/lib/OrderedMap.ts @@ -0,0 +1,21 @@ + +export class OrderedMap extends Map + implements Iterable<[number, V]> { + + [Symbol.iterator](): IterableIterator<[number, V]> { + const sorted = Array.from(super[Symbol.iterator]()).sort( + ([a], [b]) => b - a + ); + let index = 0; + return { + [Symbol.iterator]: this[Symbol.iterator], + next: (): IteratorResult<[number, V]> => { + if (index < sorted.length) { + return { value: sorted[index++], done: false }; + } else { + return { done: true, value: null }; + } + }, + }; + } +} diff --git a/pkg/interface/src/logic/reducers/graph-update.js b/pkg/interface/src/logic/reducers/graph-update.js index 5d6d20afbf..2000421904 100644 --- a/pkg/interface/src/logic/reducers/graph-update.js +++ b/pkg/interface/src/logic/reducers/graph-update.js @@ -1,4 +1,5 @@ import _ from 'lodash'; +import { OrderedMap } from "~/logic/lib/OrderedMap"; export const GraphReducer = (json, state) => { @@ -19,7 +20,7 @@ const keys = (json, state) => { let resource = res.ship + '/' + res.name; if (!(resource in state.graphs)) { - state.graphs[resource] = new Map(); + state.graphs[resource] = new OrderedMap(); } return resource; @@ -32,12 +33,12 @@ const addGraph = (json, state) => { const _processNode = (node) => { // is empty if (!node.children) { - node.children = new Map(); + node.children = new OrderedMap(); return node; } // is graph - let converted = new Map(); + let converted = new OrderedMap(); for (let i in node.children) { let item = node.children[i]; let index = item[0].split('/').slice(1).map((ind) => { @@ -62,7 +63,7 @@ const addGraph = (json, state) => { } let resource = data.resource.ship + '/' + data.resource.name; - state.graphs[resource] = new Map(); + state.graphs[resource] = new OrderedMap(); for (let i in data.graph) { let item = data.graph[i]; @@ -92,7 +93,7 @@ const removeGraph = (json, state) => { }; const mapifyChildren = (children) => { - return new Map( + return new OrderedMap( children.map(([idx, node]) => { const nd = {...node, children: mapifyChildren(node.children || []) }; return [parseInt(idx.slice(1), 10), nd]; From a81c41b9426110d6834a012f621a365d80917fb2 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 15 Sep 2020 15:51:46 +1000 Subject: [PATCH 19/82] publish: update validator mark to allow for revisions --- pkg/arvo/mar/graph/validator/publish.hoon | 26 ++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/pkg/arvo/mar/graph/validator/publish.hoon b/pkg/arvo/mar/graph/validator/publish.hoon index d4437478b3..fc7f7baa8f 100644 --- a/pkg/arvo/mar/graph/validator/publish.hoon +++ b/pkg/arvo/mar/graph/validator/publish.hoon @@ -6,16 +6,36 @@ -- ++ grab |% + :: +noun: Validate publish post + :: ++ noun |= p=* =/ ip ;;(indexed-post p) ?+ index.p.ip !! - [@ @ ~] ip - :: top level post must have title as first content - [@ ~] + :: container for revisions + :: + [@ %1 ~] + ?> ?=(~ contents.p.ip) + ip + :: specific revision + :: first content is the title + :: revisions are numbered by the revision count + :: starting at one + [@ %1 @ ~] ?> ?=(^ contents.p.ip) ?> ?=(%text -.i.contents.p.ip) ip + + :: container for comments + [@ %2 ~] + ?> ?=(~ contents.p.ip) + ip + :: comment + [@ %2 @ ~] ip + :: top level post must have no content + [@ ~] + ?> ?=(~ contents.p.ip) + ip == -- :: From 80f5747cd5c8e5d5a8f2232b2449fade3a5cd963 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 15 Sep 2020 15:53:20 +1000 Subject: [PATCH 20/82] publish: update FE for versioned posts --- pkg/interface/src/logic/lib/publish.ts | 117 ++++++++++++++++++ .../apps/publish/components/lib/Comments.tsx | 6 +- .../apps/publish/components/lib/EditPost.tsx | 21 +++- .../apps/publish/components/lib/Note.tsx | 25 ++-- .../publish/components/lib/NoteNavigation.tsx | 23 ++-- .../publish/components/lib/NotePreview.tsx | 18 ++- .../apps/publish/components/lib/new-post.tsx | 10 +- 7 files changed, 181 insertions(+), 39 deletions(-) create mode 100644 pkg/interface/src/logic/lib/publish.ts diff --git a/pkg/interface/src/logic/lib/publish.ts b/pkg/interface/src/logic/lib/publish.ts new file mode 100644 index 0000000000..d38f7238bc --- /dev/null +++ b/pkg/interface/src/logic/lib/publish.ts @@ -0,0 +1,117 @@ +import { Post, GraphNode, TextContent, Graph, NodeMap } from "~/types"; + +const buntPost = (): Post => ({ + author: '', + contents: [], + hash: null, + index: '', + signatures: [], + 'time-sent': 0 +}); + +export function makeNodeMap(posts: Post[]): Record { + let nodes = {}; + posts.forEach((p) => { + nodes[p.index] = { children: { empty: null }, post: p }; + }); + return nodes; +} + +export function newPost( + title: string, + body: string +): [number, NodeMap] { + const now = Date.now(); + const root: Post = { + author: `~${window.ship}`, + index: "/" + now, + "time-sent": now, + contents: [], + hash: null, + signatures: [], + }; + + const revContainer: Post = { ...root, index: root.index + "/1" }; + const commentsContainer = { ...root, index: root.index + "/2" }; + + const firstRevision: Post = { + ...revContainer, + index: revContainer.index + "/1", + contents: [{ text: title }, { text: body }], + }; + + const nodes = { + [root.index]: { + post: root, + children: { + graph: { + 1: { + post: revContainer, + children: { + graph: { + 1: { + post: firstRevision, + children: { empty: null }, + }, + }, + }, + }, + 2: { + post: commentsContainer, + children: { empty: null }, + }, + }, + }, + }, + }; + + return [now, nodes]; +} + +export function editPost(rev: number, noteId: number, title: string, body: string) { + const now = Date.now(); + const newRev: Post = { + author: `~${window.ship}`, + index: `/${noteId}/1/${rev}`, + "time-sent": now, + contents: [{ text: title }, { text: body }], + hash: null, + signatures: [], + }; + const nodes = { + [newRev.index]: { + post: newRev, + children: { empty: null } + } + }; + + return nodes; +} + +export function getLatestRevision(node: GraphNode): [number, string, string, Post] { + const revs = node.children.get(1); + const empty = [1, "", "", buntPost()] as [number, string, string, Post]; + if(!revs) { + return empty; + } + const [revNum, rev] = [...revs.children][0]; + if(!rev) { + return empty + } + const [title, body] = rev.post.contents as TextContent[]; + return [revNum, title.text, body.text, rev.post]; +} + +export function getComments(node: GraphNode): GraphNode { + const comments = node.children.get(2); + if(!comments) { + return { post: buntPost(), children: new Map() } + } + return comments; +} + +export function getSnippet(body: string) { + const start = body.slice(0, 400); + return start === body ? start : `${start}...`; +} + diff --git a/pkg/interface/src/views/apps/publish/components/lib/Comments.tsx b/pkg/interface/src/views/apps/publish/components/lib/Comments.tsx index 94ab89dafc..9a2dcc1da1 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Comments.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Comments.tsx @@ -13,7 +13,7 @@ import {createPost} from "~/logic/api/graph"; import { LocalUpdateRemoteContentPolicy } from "~/types"; interface CommentsProps { - comments: Graph; + comments: GraphNode; book: string; note: GraphNode; ship: string; @@ -32,7 +32,7 @@ export function Comments(props: CommentsProps) { actions: FormikHelpers<{ comment: string }> ) => { try { - const post = createPost([{ text: comment }], note?.post?.index); + const post = createPost([{ text: comment }], comments?.post?.index); await api.graph.addPost(ship, book, post) actions.resetForm(); actions.setStatus({ success: null }); @@ -45,7 +45,7 @@ export function Comments(props: CommentsProps) { return ( - {Array.from(comments).reverse().map(([idx, comment]) => ( + {Array.from(comments.children).reverse().map(([idx, comment]) => ( ") + 2); + const [revNum, title, body] = getLatestRevision(note); + + const waiter = useWaitForProps(props); const initial: PostFormSchema = { - title: title.text, + title, body, }; @@ -27,8 +30,14 @@ export function EditPost(props: EditPostProps & RouteComponentProps) { ) => { const { title, body } = values; try { - // graph-store does not support editing nodes - throw new Error("Unsupported"); + const newRev = revNum + 1; + const nodes = editPost(newRev, noteId, title, body); + await api.graph.addNodes(ship, book, nodes); + await waiter(p => { + const [rev] = getLatestRevision(note); + return rev === newRev; + }); + history.push(`/~publish/notebook/ship/${ship}/${book}/note/${noteId}`); } catch (e) { console.error(e); actions.setStatus({ error: "Failed to edit notebook" }); @@ -39,7 +48,7 @@ export function EditPost(props: EditPostProps & RouteComponentProps) { ); diff --git a/pkg/interface/src/views/apps/publish/components/lib/Note.tsx b/pkg/interface/src/views/apps/publish/components/lib/Note.tsx index ffe9e012c6..3689ad9d28 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Note.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Note.tsx @@ -6,6 +6,7 @@ import { Spinner } from "~/views/components/Spinner"; import { Comments } from "./Comments"; import { NoteNavigation } from "./NoteNavigation"; import GlobalApi from "~/logic/api/global"; +import { getLatestRevision, getComments } from '~/logic/lib/publish'; import { Author } from "./Author"; import { Contacts, GraphNode, Graph, LocalUpdateRemoteContentPolicy } from "~/types"; @@ -34,18 +35,24 @@ export function Note(props: NoteProps & RouteComponentProps) { props.history.push(baseUrl); }; - const comments = note?.children - const file = note?.post?.contents[1]?.text || ""; - const newfile = file ? file.slice(file.indexOf(";>") + 2) : ""; - + const comments = getComments(note); + const [revNum, title, body, post] = getLatestRevision(note); + const noteId = parseInt(note.post.index.split('/')[1], 10); let adminLinks: JSX.Element | null = null; if (window.ship === note?.post?.author) { adminLinks = ( + + + Update + + {"<- Notebook Index"} - {note?.post?.contents[0]?.text || ""} + {title || ""} {adminLinks} - + ; let prevComponent = ; - let nextUrl = ""; - let prevUrl = ""; const { noteId, notebook } = props; if(!notebook) { return null; @@ -63,10 +66,10 @@ export function NoteNavigation(props: NoteNavigationProps) { return null; } - if (next) { - nextUrl = `/~publish/notebook/ship/${props.ship}/${props.book}/note/${nextId}`; - const title = next.post.contents[0]?.text; - const date = next.post['time-sent']; + if (next && nextId) { + const nextUrl = makeNoteUrl(props.ship, props.book, nextId); + const [,title,, post] = getLatestRevision(next); + const date = post['time-sent']; nextComponent = ( ); } - if (prev) { - prevUrl = `/~publish/notebook/ship/${props.ship}/${props.book}/note/${prevId}`; - const title = prev.post.contents[0]?.text; - const date = prev.post['time-sent']; + if (prev && prevId) { + const prevUrl = makeNoteUrl(props.ship, props.book, prevId); + const [,title,, post] = getLatestRevision(prev); + const date = post['time-sent']; prevComponent = ( - {post.contents[0]?.text} + {title} @@ -74,7 +83,8 @@ export function NotePreview(props: NotePreviewProps) { {date} - {commentDesc} + {commentDesc} + {rev === 1 ? `1 Revision` : `${rev} Revisions`} diff --git a/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx b/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx index f7124bdd91..75b781cf8d 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx @@ -7,6 +7,7 @@ import { PostForm, PostFormSchema } from "./NoteForm"; import {createPost} from "~/logic/api/graph"; import {Graph} from "~/types/graph-update"; import {Association} from "~/types"; +import {newPost} from "~/logic/lib/publish"; interface NewPostProps { api: GlobalApi; @@ -27,13 +28,8 @@ export default function NewPost(props: NewPostProps & RouteComponentProps) { ) => { const { title, body } = values; try { - const post = createPost([{ text: title }, { text: body }]) - const noteId = parseInt(post.index.split('/')[1], 10); - await api.graph.addPost(ship, book, post) - await waiter(p => { - const { graph } = p; - return graph.has(noteId); - }); + const [noteId, nodes] = newPost(title, body) + await api.graph.addNodes(ship, book, nodes) history.push(`/~publish/notebook/ship/${ship}/${book}/note/${noteId}`); } catch (e) { console.error(e); From 6264091148a252497014775056c45aa3e7f8b33f Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 1 Oct 2020 11:15:46 +1000 Subject: [PATCH 21/82] publish: fix breakage from merge --- pkg/arvo/sur/graph-view.hoon | 21 ------------- pkg/interface/src/logic/api/graph.ts | 2 +- pkg/interface/src/logic/store/type.ts | 2 +- pkg/interface/src/types/noun.ts | 2 +- pkg/interface/src/views/apps/publish/app.tsx | 15 +++++----- .../apps/publish/components/lib/EditPost.tsx | 2 +- .../publish/components/lib/NoteNavigation.tsx | 2 +- .../apps/publish/components/lib/Notebook.tsx | 30 +++++++++---------- .../apps/publish/components/lib/Sidebar.tsx | 8 ++--- .../apps/publish/components/lib/new-post.tsx | 1 + .../views/apps/publish/components/lib/new.tsx | 4 +-- .../apps/publish/components/skeleton.tsx | 2 ++ 12 files changed, 36 insertions(+), 55 deletions(-) diff --git a/pkg/arvo/sur/graph-view.hoon b/pkg/arvo/sur/graph-view.hoon index f3451834a0..ee166334a2 100644 --- a/pkg/arvo/sur/graph-view.hoon +++ b/pkg/arvo/sur/graph-view.hoon @@ -1,8 +1,4 @@ -<<<<<<< HEAD -/- *group -======= /- *group, store=graph-store ->>>>>>> release/next-userspace /+ resource ^? |% @@ -12,12 +8,6 @@ $% [%group rid=resource] [%policy =policy] == -<<<<<<< HEAD -:: $app: An app that is associated to a graph-store mark -:: -+$ app-name ?(%chat %publish %links) -======= ->>>>>>> release/next-userspace :: :: $error: An error from a graph-view poke :: @@ -41,16 +31,6 @@ rid=resource title=@t description=@t -<<<<<<< HEAD - app=app-name - =associated - == - [%delete rid=resource] - [%leave rid=resource] - [%join rid=resource =ship app=app-name] - ::[%invite rid=resource ships=(set ship)] - [%groupify rid=resource app=app-name to=(unit resource)] -======= mark=(unit mark) =associated module=@t @@ -61,7 +41,6 @@ ::[%invite rid=resource ships=(set ship)] [%groupify rid=resource to=(unit resource)] [%forward rid=resource =update:store] ->>>>>>> release/next-userspace == -- diff --git a/pkg/interface/src/logic/api/graph.ts b/pkg/interface/src/logic/api/graph.ts index 7bf862be5a..6b08115bec 100644 --- a/pkg/interface/src/logic/api/graph.ts +++ b/pkg/interface/src/logic/api/graph.ts @@ -138,7 +138,7 @@ export default class GraphApi extends BaseApi { } addNodes(ship: Patp, name: string, nodes: Object) { - this.hookAction(ship, { + return this.hookAction(ship, { 'add-nodes': { resource: { ship, name }, nodes diff --git a/pkg/interface/src/logic/store/type.ts b/pkg/interface/src/logic/store/type.ts index 84d7b273f6..681315a53b 100644 --- a/pkg/interface/src/logic/store/type.ts +++ b/pkg/interface/src/logic/store/type.ts @@ -39,7 +39,7 @@ export interface StoreState { permissions: Permissions; s3: S3State; graphs: Graphs; - graphKeys: Set; + graphKeys: Set; // App specific states diff --git a/pkg/interface/src/types/noun.ts b/pkg/interface/src/types/noun.ts index 8a85697906..95dedea823 100644 --- a/pkg/interface/src/types/noun.ts +++ b/pkg/interface/src/types/noun.ts @@ -18,7 +18,7 @@ export type Serial = string; export type Jug = Map>; // name of app -export type AppName = 'chat' | 'link' | 'contacts' | 'publish'; +export type AppName = 'chat' | 'link' | 'contacts' | 'publish' | 'graph'; export function getTagFromFrond(frond: O): keyof O { const tags = Object.keys(frond) as Array; diff --git a/pkg/interface/src/views/apps/publish/app.tsx b/pkg/interface/src/views/apps/publish/app.tsx index 264664096e..6b7d99c452 100644 --- a/pkg/interface/src/views/apps/publish/app.tsx +++ b/pkg/interface/src/views/apps/publish/app.tsx @@ -71,13 +71,13 @@ export default function PublishApp(props: PublishAppProps) { ? "sidebar" : "rightPanel"; - const appAssociations = props.associations?.publish || {}; + const appAssociations = props.associations?.graph || {}; const graphs = props.graphs || {} return ( <> - - {unreadTotal > 0 ? `(${unreadTotal}) ` : ""}OS1 - Publish + + OS1 - Publish @@ -156,10 +156,10 @@ export default function PublishApp(props: PublishAppProps) { const notebookContacts = bookGroupPath in contacts ? contacts[bookGroupPath] : {}; - const notebook = notebooks?.[ship]?.[book]; + const graph = graphs[`${ship.slice(1)}/${book}`]; + return ( ); diff --git a/pkg/interface/src/views/apps/publish/components/lib/EditPost.tsx b/pkg/interface/src/views/apps/publish/components/lib/EditPost.tsx index 37fdad0e81..e781fc6c06 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/EditPost.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/EditPost.tsx @@ -34,7 +34,7 @@ export function EditPost(props: EditPostProps & RouteComponentProps) { const nodes = editPost(newRev, noteId, title, body); await api.graph.addNodes(ship, book, nodes); await waiter(p => { - const [rev] = getLatestRevision(note); + const [rev] = getLatestRevision(p.note); return rev === newRev; }); history.push(`/~publish/notebook/ship/${ship}/${book}/note/${noteId}`); diff --git a/pkg/interface/src/views/apps/publish/components/lib/NoteNavigation.tsx b/pkg/interface/src/views/apps/publish/components/lib/NoteNavigation.tsx index c402015285..4beea9ae0d 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/NoteNavigation.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/NoteNavigation.tsx @@ -34,7 +34,7 @@ function NavigationItem(props: { function getAdjacentId(graph: Graph, child: number, backwards = false): number | null { const children = Array.from(graph); const i = children.findIndex(([index]) => index === child); - const target = children[backwards ? i-1 : i+1]; + const target = children[backwards ? i+1 : i-1]; return target?.[0] || null; } diff --git a/pkg/interface/src/views/apps/publish/components/lib/Notebook.tsx b/pkg/interface/src/views/apps/publish/components/lib/Notebook.tsx index 3065bbf468..c36a336471 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Notebook.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Notebook.tsx @@ -12,7 +12,7 @@ import { Groups } from "~/types/group-update"; import { Contacts, Rolodex } from "~/types/contact-update"; import GlobalApi from "~/logic/api/global"; import styled from "styled-components"; -import { Associations } from "~/types"; +import { Associations, Graph, Association } from "~/types"; import { deSig } from "~/logic/lib/util"; interface NotebookProps { @@ -55,16 +55,18 @@ export class Notebook extends PureComponent< api, ship, book, - notebook, notebookContacts, groups, history, hideNicknames, associations, + association, + graph } = this.props; const { state } = this; + const { metadata } = association; - const group = groups[notebook?.["writers-group-path"]]; + const group = groups[association?.['group-path']]; if (!group) return null; // Waitin on groups to populate const contact = notebookContacts[ship]; @@ -75,8 +77,6 @@ export class Notebook extends PureComponent< const isWriter = isOwn || group.tags?.publish?.[`writers-${book}`]?.has(window.ship); - const notesList = notebook?.["notes-by-date"] || []; - const notes = notebook?.notes || {}; const showNickname = contact?.nickname && !hideNicknames; return ( @@ -94,7 +94,7 @@ export class Notebook extends PureComponent< {"<- All Notebooks"} - {notebook?.title} + {metadata?.title}
by @@ -103,7 +103,7 @@ export class Notebook extends PureComponent<
{isWriter && ( - + )} @@ -120,8 +120,8 @@ export class Notebook extends PureComponent< destructive onClick={() => { this.setState({ isUnsubscribing: true }); - api.publish - .unsubscribeNotebook(deSig(ship), book) + + api.graph.leaveGraph(ship, book) .then(() => { history.push("/~publish"); }) @@ -168,8 +168,7 @@ export class Notebook extends PureComponent< {state.tab === "all" && ( - {notebook?.about} + {metadata?.description}
)} {state.tab === "subscribers" && ( )} {state.tab === "settings" && ( @@ -195,9 +195,9 @@ export class Notebook extends PureComponent< host={ship} book={book} api={api} - notebook={notebook} contacts={notebookContacts} associations={associations} + association={association} groups={groups} /> )} diff --git a/pkg/interface/src/views/apps/publish/components/lib/Sidebar.tsx b/pkg/interface/src/views/apps/publish/components/lib/Sidebar.tsx index b710d40507..a050e65ae1 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/Sidebar.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/Sidebar.tsx @@ -25,14 +25,14 @@ export function Sidebar(props: any) { ? alphabetiseAssociations(props.associations.contacts) : {}; const appAssociations = - props.associations && "publish" in props.associations - ? props.associations.publish + props.associations && "graph" in props.associations + ? props.associations.graph : {}; const groups = props.groups || {}; - const groupedItems = _.chain(props.graphs) - .reduce((acc, g, path) => { + const groupedItems = _.chain(Array.from(props.graphKeys)) + .reduce((acc, path) => { const appPath = `/ship/~${path}`; return appPath in appAssociations ? [...acc, appAssociations[appPath]] diff --git a/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx b/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx index e83d6ea027..a94b293eed 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/new-post.tsx @@ -30,6 +30,7 @@ export default function NewPost(props: NewPostProps & RouteComponentProps) { try { const [noteId, nodes] = newPost(title, body) await api.graph.addNodes(ship, book, nodes) + await waiter(p => p.graph.has(noteId)); history.push(`/~publish/notebook/ship/${ship}/${book}/note/${noteId}`); } catch (e) { console.error(e); diff --git a/pkg/interface/src/views/apps/publish/components/lib/new.tsx b/pkg/interface/src/views/apps/publish/components/lib/new.tsx index ecdc5e8e3b..8ecc8b0558 100644 --- a/pkg/interface/src/views/apps/publish/components/lib/new.tsx +++ b/pkg/interface/src/views/apps/publish/components/lib/new.tsx @@ -44,16 +44,16 @@ export function NewScreen(props: NewScreenProps & RouteComponentProps) { bookId, name, description, + group, "publish", - group ); } else { await props.api.graph.createUnmanagedGraph( bookId, name, description, + { open: { banned: [], banRanks: [] } }, "publish", - { open: { banned: [], banRanks: [] } } ); } diff --git a/pkg/interface/src/views/apps/publish/components/skeleton.tsx b/pkg/interface/src/views/apps/publish/components/skeleton.tsx index 2cebcf709d..468cfdf924 100644 --- a/pkg/interface/src/views/apps/publish/components/skeleton.tsx +++ b/pkg/interface/src/views/apps/publish/components/skeleton.tsx @@ -20,6 +20,7 @@ type SkeletonProps = RouteComponentProps<{ associations: Associations; contacts: Rolodex; graphs: Graphs; + graphKeys: Set; api: GlobalApi; children: React.ReactNode; }; @@ -90,6 +91,7 @@ export function Skeleton(props: SkeletonProps) {
); diff --git a/pkg/interface/src/views/apps/publish/components/NoteNavigation.tsx b/pkg/interface/src/views/apps/publish/components/NoteNavigation.tsx index 4beea9ae0d..e350126312 100644 --- a/pkg/interface/src/views/apps/publish/components/NoteNavigation.tsx +++ b/pkg/interface/src/views/apps/publish/components/NoteNavigation.tsx @@ -38,16 +38,15 @@ function getAdjacentId(graph: Graph, child: number, backwards = false): number | return target?.[0] || null; } -function makeNoteUrl(ship: string, book: string, noteId: number) { - return `/~publish/notebook/ship/${ship}/${book}/note/${noteId}`; +function makeNoteUrl(noteId: number) { + return noteId.toString(); } interface NoteNavigationProps { - book: string; noteId: number; - ship: string; notebook: Graph; + baseUrl: string; } export function NoteNavigation(props: NoteNavigationProps) { @@ -67,7 +66,7 @@ export function NoteNavigation(props: NoteNavigationProps) { } if (next && nextId) { - const nextUrl = makeNoteUrl(props.ship, props.book, nextId); + const nextUrl = makeNoteUrl(nextId); const [,title,, post] = getLatestRevision(next); const date = post['time-sent']; nextComponent = ( @@ -79,7 +78,7 @@ export function NoteNavigation(props: NoteNavigationProps) { ); } if (prev && prevId) { - const prevUrl = makeNoteUrl(props.ship, props.book, prevId); + const prevUrl = makeNoteUrl(prevId); const [,title,, post] = getLatestRevision(prev); const date = post['time-sent']; prevComponent = ( diff --git a/pkg/interface/src/views/apps/publish/components/NotePreview.tsx b/pkg/interface/src/views/apps/publish/components/NotePreview.tsx index f805f380d1..1f0c114acb 100644 --- a/pkg/interface/src/views/apps/publish/components/NotePreview.tsx +++ b/pkg/interface/src/views/apps/publish/components/NotePreview.tsx @@ -20,6 +20,7 @@ interface NotePreviewProps { node: GraphNode; contact?: Contact; hideNicknames?: boolean; + baseUrl: string; } const WrappedBox = styled(Box)` @@ -49,9 +50,7 @@ export function NotePreview(props: NotePreviewProps) { ? "1 Comment" : `${numComments} Comments`; const date = moment(post["time-sent"]).fromNow(); - const url = `/~publish/notebook/ship/${props.host}/${props.book}/note/${ - post.index.split("/")[1] - }`; + const url = `${props.baseUrl}/note/${post.index.split("/")[1]}`; // stubbing pending notification-store const isRead = true; diff --git a/pkg/interface/src/views/apps/publish/components/Notebook.tsx b/pkg/interface/src/views/apps/publish/components/Notebook.tsx index ff10ed873f..6cef04ae11 100644 --- a/pkg/interface/src/views/apps/publish/components/Notebook.tsx +++ b/pkg/interface/src/views/apps/publish/components/Notebook.tsx @@ -1,19 +1,15 @@ import React, { PureComponent } from "react"; import { Link, RouteComponentProps, Route, Switch } from "react-router-dom"; import { NotebookPosts } from "./NotebookPosts"; -import { Subscribers } from "./Subscribers"; -import { Settings } from "./Settings"; -import { Spinner } from "~/views/components/Spinner"; -import { Tabs, Tab } from "~/views/components/Tab"; import { roleForShip } from "~/logic/lib/group"; -import { Box, Button, Text, Row } from "@tlon/indigo-react"; -import { Notebook as INotebook } from "~/types/publish-update"; +import { Box, Button, Text, Row, Col } from "@tlon/indigo-react"; import { Groups } from "~/types/group-update"; import { Contacts, Rolodex } from "~/types/contact-update"; import GlobalApi from "~/logic/api/global"; import styled from "styled-components"; import { Associations, Graph, Association } from "~/types"; import { deSig } from "~/logic/lib/util"; +import { StatelessAsyncButton } from "~/views/components/StatelessAsyncButton"; interface NotebookProps { api: GlobalApi; @@ -36,69 +32,35 @@ interface NotebookState { tab: string; } -export class Notebook extends PureComponent< - NotebookProps & RouteComponentProps, - NotebookState -> { - constructor(props) { - super(props); - this.state = { - isUnsubscribing: false, - tab: "all", - }; - this.setTab = this.setTab.bind(this); - } +export function Notebook(props: NotebookProps & RouteComponentProps) { + const { + ship, + book, + notebookContacts, + groups, + hideNicknames, + association, + graph, + } = props; + const { metadata } = association; - setTab(tab: string) { - this.setState({ tab }); - } + const group = groups[association?.["group-path"]]; + if (!group) return null; // Waitin on groups to populate - render() { - const { - api, - ship, - book, - notebookContacts, - groups, - history, - hideNicknames, - associations, - association, - graph - } = this.props; - const { state } = this; - const { metadata } = association; + const relativePath = (p: string) => props.baseUrl + p; - const group = groups[association?.['group-path']]; - if (!group) return null; // Waitin on groups to populate + const contact = notebookContacts?.[ship]; + const role = group ? roleForShip(group, window.ship) : undefined; + const isOwn = `~${window.ship}` === ship; - const relativePath = (p: string) => this.props.baseUrl + p; + const isWriter = + isOwn || group.tags?.publish?.[`writers-${book}`]?.has(window.ship); - const contact = notebookContacts?.[ship]; - const role = group ? roleForShip(group, window.ship) : undefined; - const isOwn = `~${window.ship}` === ship; - const isAdmin = role === "admin" || isOwn; + const showNickname = contact?.nickname && !hideNicknames; - const isWriter = - isOwn || group.tags?.publish?.[`writers-${book}`]?.has(window.ship); - - const showNickname = contact?.nickname && !hideNicknames; - - return ( - - - {"<- All Notebooks"} - + return ( + + {metadata?.title}
@@ -107,114 +69,25 @@ export class Notebook extends PureComponent< {showNickname ? contact?.nickname : ship}
- - {isWriter && ( - - - - )} - {!isOwn ? ( - this.state.isUnsubscribing ? ( - - ) : ( - - ) - ) : null} - - - - - - {isAdmin && ( - <> - - - - )} - - {state.tab === "all" && ( - - )} - {state.tab === "about" && ( - - {metadata?.description} - - )} - {state.tab === "subscribers" && ( - - )} - {state.tab === "settings" && ( - - )} - - - ); - } + {isWriter && ( + + + + )} + + + + + ); } + export default Notebook; diff --git a/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx b/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx index b2fb5902c7..2fc8ae908b 100644 --- a/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx +++ b/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx @@ -8,8 +8,8 @@ interface NotebookPostsProps { graph: Graph; host: string; book: string; - baseUrl: string; hideNicknames?: boolean; + baseUrl: string; } export function NotebookPosts(props: NotebookPostsProps) { @@ -25,6 +25,7 @@ export function NotebookPosts(props: NotebookPostsProps) { contact={props.contacts[node.post.author]} node={node} hideNicknames={props.hideNicknames} + baseUrl={props.baseUrl} /> ) )} diff --git a/pkg/interface/src/views/apps/publish/components/Settings.tsx b/pkg/interface/src/views/apps/publish/components/Settings.tsx deleted file mode 100644 index 05d073af26..0000000000 --- a/pkg/interface/src/views/apps/publish/components/Settings.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React, { useEffect } from "react"; -import { Box, Col, Button, Label } from "@tlon/indigo-react"; -import GlobalApi from "~/logic/api/global"; -import { MetadataForm } from "./MetadataForm"; -import { Groups, Associations, Association, Contacts } from "~/types"; -import GroupifyForm from "./GroupifyForm"; -import { useHistory } from "react-router-dom"; - -interface SettingsProps { - host: string; - book: string; - association: Association; - contacts: Contacts; - groups: Groups; - api: GlobalApi; - associations: Associations; - baseUrl: string; -} - -const Divider = (props) => ( - -); - -export function Settings(props: SettingsProps) { - const history = useHistory(); - const onDelete = async () => { - await props.api.graph.deleteGraph(props.book) - history.push(props.baseUrl || '/~404'); - }; - const groupPath = props.association?.['group-path']; - - const isUnmanaged = props.groups?.[groupPath]?.hidden || false; - - return ( - - {isUnmanaged && ( - <> - - - - )} - - - - - - - - - ); -} - -export default Settings; diff --git a/pkg/interface/src/views/apps/publish/components/Subscribers.tsx b/pkg/interface/src/views/apps/publish/components/Subscribers.tsx deleted file mode 100644 index ac74938f7f..0000000000 --- a/pkg/interface/src/views/apps/publish/components/Subscribers.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React, { Component } from 'react'; -import { GroupView } from '~/views/components/Group'; -import { resourceFromPath, roleForShip } from '~/logic/lib/group'; -import GlobalApi from '~/logic/api/global'; -import {Groups} from '~/types/group-update'; -import {Associations, Association} from '~/types/metadata-update'; -import {Rolodex} from '~/types/contact-update'; -import {GraphNode} from '~/types/graph-update'; -import {Box, Button} from '@tlon/indigo-react'; - -interface SubscribersProps { - api: GlobalApi; - groups: Groups; - book: string; - associations: Associations; - association: Association; - contacts: Rolodex; -} - -export class Subscribers extends Component { - constructor(props: SubscribersProps) { - super(props); - this.addUser = this.addUser.bind(this); - this.removeUser = this.removeUser.bind(this); - this.addAll = this.addAll.bind(this); - } - - addUser(who, path) { - this.props.api.groups.add(path, [who]); - } - - removeUser(who, path) { - this.props.api.groups.remove(path, [who]); - } - - addAll() { - const path = this.props.association['group-path']; - const group = path ? this.props.groups[path] : null; - if(!group) { - return; - } - const resource = resourceFromPath(path); - this.props.api.groups.addTag( - resource, - { app: 'publish', tag: `writers-${this.props.book}` }, - [...group.members].map(m => `~${m}`) - ); - } - - - render() { - const path = this.props.association['group-path']; - const group = path ? this.props.groups[path] : null; - - - const tags = [ - { - description: 'Writer', - tag: `writers-${this.props.book}`, - addDescription: 'Make Writer', - app: 'publish', - }, - ]; - - const appTags = [ - { - app: 'publish', - tag: `writers-${this.props.book}`, - desc: `Writer`, - addDesc: 'Allow user to write to this notebook' - }, - ]; - - if(!group) { - return null; - } - - const role = roleForShip(group, window.ship) - - return ( - - { role === 'admin' && ( - - )} - - - ); - } -} - -export default Subscribers; diff --git a/pkg/interface/src/views/apps/publish/components/new-post.tsx b/pkg/interface/src/views/apps/publish/components/new-post.tsx index 52548f3af0..b269425f4b 100644 --- a/pkg/interface/src/views/apps/publish/components/new-post.tsx +++ b/pkg/interface/src/views/apps/publish/components/new-post.tsx @@ -32,7 +32,7 @@ export default function NewPost(props: NewPostProps & RouteComponentProps) { const [noteId, nodes] = newPost(title, body) await api.graph.addNodes(ship, book, nodes) await waiter(p => p.graph.has(noteId)); - history.push(`/${props.baseUrl}/note/${noteId}`); + history.push(`${props.baseUrl}/note/${noteId}`); } catch (e) { console.error(e); actions.setStatus({ error: "Posting note failed" }); diff --git a/pkg/interface/src/views/components/UnjoinedResource.tsx b/pkg/interface/src/views/components/UnjoinedResource.tsx index 34e4eafec9..33ef74c17f 100644 --- a/pkg/interface/src/views/components/UnjoinedResource.tsx +++ b/pkg/interface/src/views/components/UnjoinedResource.tsx @@ -22,16 +22,13 @@ interface UnjoinedResourceProps { function isJoined(app: string, path: string) { return function ( - props: Pick + props: Pick ) { - let ship, name; const graphKey = path.substr(7); switch (app) { case "link": - return props.graphKeys.has(graphKey); case "publish": - [, ship, name] = path.split("/"); - return !!props.notebooks[ship]?.[name]; + return props.graphKeys.has(graphKey); case "chat": return !!props.inbox[path]; default: @@ -53,14 +50,11 @@ export function UnjoinedResource(props: UnjoinedResourceProps) { const onJoin = async () => { let ship, name; switch (app) { + case "publish": case "link": [, , ship, name] = appPath.split("/"); await api.graph.joinGraph(ship, name); break; - case "publish": - [, ship, name] = appPath.split("/"); - await api.publish.subscribeNotebook(ship.slice(1), name); - break; case "chat": [, ship, name] = appPath.split("/"); await api.chat.join(ship, appPath, true); @@ -73,7 +67,7 @@ export function UnjoinedResource(props: UnjoinedResourceProps) { }; useEffect(() => { - if (isJoined(app, appPath)({ inbox, graphKeys, notebooks })) { + if (isJoined(app, appPath)({ inbox, graphKeys })) { history.push(`${props.baseUrl}/resource/${app}${appPath}`); } }, [props.association, inbox, graphKeys, notebooks]); diff --git a/pkg/interface/src/views/landscape/components/GroupsPane.tsx b/pkg/interface/src/views/landscape/components/GroupsPane.tsx index 8ce6784d91..100bb4db4e 100644 --- a/pkg/interface/src/views/landscape/components/GroupsPane.tsx +++ b/pkg/interface/src/views/landscape/components/GroupsPane.tsx @@ -96,11 +96,11 @@ export function GroupsPane(props: GroupsPaneProps) { string >; const appName = app as AppName; - const isShip = app === "link"; + const isGraph = app === "link" || app === 'publish'; - const resource = `${isShip ? "/ship" : ""}/${host}/${name}`; + const resource = `${isGraph ? "/ship" : ""}/${host}/${name}`; const association = - appName === "link" + isGraph ? associations.graph[resource] : associations[appName][resource]; const resourceUrl = `${baseUrl}/resource/${app}${resource}`; @@ -134,9 +134,9 @@ export function GroupsPane(props: GroupsPaneProps) { render={(routeProps) => { const { app, host, name } = routeProps.match.params; const appName = app as AppName; - const isShip = app === "link"; - const appPath = `${isShip ? '/ship/' : '/'}${host}/${name}`; - const association = isShip ? associations.graph[appPath] : associations[appName][appPath]; + const isGraph = app === "link" || app === 'publish'; + const appPath = `${isGraph ? '/ship/' : '/'}${host}/${name}`; + const association = isGraph ? associations.graph[appPath] : associations[appName][appPath]; const resourceUrl = `${baseUrl}/join/${app}${appPath}`; if (!association) { @@ -195,7 +195,7 @@ export function GroupsPane(props: GroupsPaneProps) { justifyContent="center" display={["none", "flex"]} > - + {description} diff --git a/pkg/interface/src/views/landscape/components/NewChannel.tsx b/pkg/interface/src/views/landscape/components/NewChannel.tsx index 7bf1b373b5..1f52e8892f 100644 --- a/pkg/interface/src/views/landscape/components/NewChannel.tsx +++ b/pkg/interface/src/views/landscape/components/NewChannel.tsx @@ -71,16 +71,15 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { ); break; case "publish": - await props.api.publish.newBook(resId, name, description, group); - break; case "links": + const module = type === 'links' ? 'link' : type; if (group) { await api.graph.createManagedGraph( resId, name, description, group, - "link" + module ); } else { await api.graph.createUnmanagedGraph( @@ -88,7 +87,7 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { name, description, EMPTY_INVITE_POLICY, - "link" + module ); } break; diff --git a/pkg/interface/src/views/landscape/components/Sidebar/Apps.tsx b/pkg/interface/src/views/landscape/components/Sidebar/Apps.tsx index ace7d3ef3f..6910982398 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/Apps.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/Apps.tsx @@ -38,43 +38,10 @@ export function useChat( return { lastUpdated, getStatus }; } -export function usePublish(notebooks: Notebooks): SidebarAppConfig { - const getStatus = useCallback( - (s: string) => { - const [, host, name] = s.split("/"); - const notebook = notebooks?.[host]?.[name]; - if (!notebook) { - return "unsubscribed"; - } - if (notebook["num-unread"]) { - return "unread"; - } - return undefined; - }, - [notebooks] - ); - const lastUpdated = useCallback( - (s: string) => { - // we can't get publish timestamps without loading posts - // so we just return the number of unreads, this ensures - // that unread notebooks don't get lost on the bottom - const [, host, name] = s.split("/"); - const notebook = notebooks?.[host]?.[name]; - if(!notebook) { - return 0; - } - return notebook?.["num-unread"]+1; - }, - [notebooks] - ); - - return { getStatus, lastUpdated }; -} - -export function useLinks( +export function useGraphModule( graphKeys: Set, - graphs: Graphs + graphs: Graphs, ): SidebarAppConfig { const getStatus = useCallback( (s: string) => { diff --git a/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx b/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx index 7c4f55e6b3..210829fc4b 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx @@ -61,7 +61,7 @@ export function SidebarItem(props: { const module = association?.metadata?.module || appName; const appPath = association?.["app-path"]; const groupPath = association?.["group-path"]; - const app = apps[module]; + const app = apps[appName]; const isUnmanaged = groups?.[groupPath]?.hidden || false; if (!app) { return null; diff --git a/pkg/interface/src/views/landscape/components/Skeleton.tsx b/pkg/interface/src/views/landscape/components/Skeleton.tsx index 00149053f2..23e8a5f722 100644 --- a/pkg/interface/src/views/landscape/components/Skeleton.tsx +++ b/pkg/interface/src/views/landscape/components/Skeleton.tsx @@ -13,7 +13,7 @@ import { LinkCollections } from "~/types/link-update"; import styled from "styled-components"; import GlobalSubscription from "~/logic/subscription/global"; import { Workspace, Groups, Graphs, Invites } from "~/types"; -import { useChat, usePublish, useLinks } from "./Sidebar/Apps"; +import { useChat, useGraphModule } from "./Sidebar/Apps"; import { Body } from "~/views/components/Body"; interface SkeletonProps { @@ -42,15 +42,13 @@ interface SkeletonProps { export function Skeleton(props: SkeletonProps) { const chatConfig = useChat(props.inbox, props.chatSynced); - const publishConfig = usePublish(props.notebooks); - const linkConfig = useLinks(props.graphKeys, props.graphs); + const graphConfig = useGraphModule(props.graphKeys, props.graphs); const config = useMemo( () => ({ - publish: publishConfig, - link: linkConfig, + graph: graphConfig, chat: chatConfig, }), - [publishConfig, linkConfig, chatConfig] + [graphConfig, chatConfig] ); return ( diff --git a/pkg/interface/src/views/landscape/index.tsx b/pkg/interface/src/views/landscape/index.tsx index 39ba46789f..b4844e6ae0 100644 --- a/pkg/interface/src/views/landscape/index.tsx +++ b/pkg/interface/src/views/landscape/index.tsx @@ -31,10 +31,7 @@ export default class Landscape extends Component { this.props.subscription.startApp('groups') this.props.subscription.startApp('chat') - this.props.subscription.startApp('publish'); this.props.subscription.startApp('graph'); - this.props.api.publish.fetchNotebooks(); - } render() { From b610153b24eb1963996717e98b62ef6144ff3b2b Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 19 Oct 2020 15:39:36 +1000 Subject: [PATCH 28/82] publish: address comments --- pkg/arvo/lib/graph-store.hoon | 3 --- pkg/interface/src/logic/lib/util.js | 4 ++++ .../src/views/landscape/components/GroupsPane.tsx | 9 +++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/pkg/arvo/lib/graph-store.hoon b/pkg/arvo/lib/graph-store.hoon index b1ee831262..daf63fda3b 100644 --- a/pkg/arvo/lib/graph-store.hoon +++ b/pkg/arvo/lib/graph-store.hoon @@ -300,10 +300,7 @@ ++ node %- ot :~ [%post post] - :: TODO: support adding nodes with children by supporting the - :: graph key [%children internal-graph] - :: [%children (of [%empty ul]~)] == :: ++ internal-graph diff --git a/pkg/interface/src/logic/lib/util.js b/pkg/interface/src/logic/lib/util.js index 9283a5bc12..77a6380b1d 100644 --- a/pkg/interface/src/logic/lib/util.js +++ b/pkg/interface/src/logic/lib/util.js @@ -3,6 +3,10 @@ import f from 'lodash/fp'; export const MOBILE_BROWSER_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i; +export function appIsGraph(app) { + return app === 'link' || app === 'publish'; +} + export function clamp(x,min,max) { return Math.max(min, Math.min(max, x)); } diff --git a/pkg/interface/src/views/landscape/components/GroupsPane.tsx b/pkg/interface/src/views/landscape/components/GroupsPane.tsx index ab2c8d0ec7..0adcd7e041 100644 --- a/pkg/interface/src/views/landscape/components/GroupsPane.tsx +++ b/pkg/interface/src/views/landscape/components/GroupsPane.tsx @@ -14,11 +14,8 @@ import { Skeleton } from "./Skeleton"; import { InvitePopover } from "./InvitePopover"; import { NewChannel } from "./NewChannel"; -import { Resource as IResource, Groups } from "~/types/group-update"; -import { Associations } from "~/types/metadata-update"; -import { resourceAsPath } from "~/logic/lib/util"; +import { appIsGraph } from "~/logic/lib/util"; import { AppName } from "~/types/noun"; -import { Contacts, Rolodex } from "~/types/contact-update"; import GlobalApi from "~/logic/api/global"; import { StoreState } from "~/logic/store/type"; import { UnjoinedResource } from "~/views/components/UnjoinedResource"; @@ -96,7 +93,7 @@ export function GroupsPane(props: GroupsPaneProps) { string >; const appName = app as AppName; - const isGraph = app === "link" || app === 'publish'; + const isGraph = appIsGraph(app); const resource = `${isGraph ? "/ship" : ""}/${host}/${name}`; const association = @@ -134,7 +131,7 @@ export function GroupsPane(props: GroupsPaneProps) { render={(routeProps) => { const { app, host, name } = routeProps.match.params; const appName = app as AppName; - const isGraph = app === "link" || app === 'publish'; + const isGraph = appIsGraph(app); const appPath = `${isGraph ? '/ship/' : '/'}${host}/${name}`; const association = isGraph ? associations.graph[appPath] : associations[appName][appPath]; const resourceUrl = `${baseUrl}/join/${app}${appPath}`; From 3ee0f13e41449e5ad4fc252b9acd5a468959c835 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Mon, 19 Oct 2020 16:11:06 +1000 Subject: [PATCH 29/82] publish: tombstone --- pkg/arvo/app/publish.hoon | 2051 +---------------- pkg/arvo/lib/publish.hoon | 238 +- pkg/arvo/mar/publish/action.hoon | 118 +- pkg/arvo/mar/publish/info.hoon | 15 +- pkg/arvo/mar/publish/primary-delta.hoon | 68 - pkg/interface/src/logic/api/publish.ts | 224 -- .../src/logic/reducers/publish-response.ts | 205 -- .../src/logic/reducers/publish-update.ts | 269 --- pkg/interface/src/logic/store/store.ts | 4 - pkg/interface/src/logic/store/type.ts | 2 - pkg/interface/src/types/publish-response.ts | 48 - pkg/interface/src/types/publish-update.ts | 158 -- 12 files changed, 57 insertions(+), 3343 deletions(-) delete mode 100644 pkg/interface/src/logic/api/publish.ts delete mode 100644 pkg/interface/src/logic/reducers/publish-response.ts delete mode 100644 pkg/interface/src/logic/reducers/publish-update.ts delete mode 100644 pkg/interface/src/types/publish-response.ts delete mode 100644 pkg/interface/src/types/publish-update.ts diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon index 1faf9b54a5..f4094a69bf 100644 --- a/pkg/arvo/app/publish.hoon +++ b/pkg/arvo/app/publish.hoon @@ -89,23 +89,7 @@ :: ++ on-init ^- (quip card _this) - =/ rav [%sing %t [%da now.bol] /app/publish/notebooks] - :_ this - :~ [%pass /view-bind %arvo %e %connect [~ /'publish-view'] %publish] - [%pass /read/paths %arvo %c %warp our.bol q.byk.bol `rav] - (invite-poke:main [%create /publish]) - :* %pass /invites %agent [our.bol %invite-store] %watch - /invitatory/publish - == - :* %pass / %agent [our.bol %invite-store] %poke %invite-action - !>([%create /publish]) - == - :* %pass /srv %agent [our.bol %file-server] - %poke %file-server-action - !>([%serve-dir /'~publish' /app/landscape %.n %.y]) - == - [%pass /groups %agent [our.bol %group-store] %watch /groups] - == + `this :: ++ on-save !>(state) :: @@ -248,27 +232,35 @@ -.p.old-state %7 :: cards - %+ weld cards - %+ roll ~(tap by books.p.old-state) - |= [[[who=@p book=@tas] nb=notebook] out=(list card)] - ^- (list card) - =/ =resource - [who book] - ?. =(who our.bol) - :_ out - (poke-graph-pull %add who resource) - =/ =graph:graph-store - (notebook-to-graph nb) - %+ weld out - ^- (list card) - :~ - %- poke-graph-store - :* %0 date-created.nb %add-graph - resource - graph - `%graph-validator-publish + ;: weld + cards + :: move to graph-store + %+ roll ~(tap by books.p.old-state) + |= [[[who=@p book=@tas] nb=notebook] out=(list card)] + ^- (list card) + =/ =resource + [who book] + ?. =(who our.bol) + :_ out + (poke-graph-pull %add who resource) + =/ =graph:graph-store + (notebook-to-graph nb) + %+ weld out + ^- (list card) + :~ + %- poke-graph-store + :* %0 date-created.nb %add-graph + resource + graph + `%graph-validator-publish + == + (poke-graph-push %add resource) == - (poke-graph-push %add resource) + :: leave all subscriptions + %+ turn ~(tap in ~(key by wex.bol)) + |= [=wire =ship app=term] + ^- card + [%pass wire %agent [ship app] %leave ~] == == :: @@ -488,227 +480,12 @@ [[%pass /move-files %arvo %c %info q.byk.bol %& sob] cards] -- :: - ++ on-poke - |= [mar=mark vas=vase] - ^- (quip card _this) - ?+ mar (on-poke:def mar vas) - :: - %noun - ?+ q.vas - [~ this] - :: - %flush-limbo [~ this(limbo [~ ~])] - :: - %reset-warp - =/ rav [%sing %t [%da now.bol] /app/publish/notebooks] - :_ this - [%pass /read/paths %arvo %c %warp our.bol q.byk.bol `rav]~ - == - :: - %handle-http-request - =+ !<([id=@ta req=inbound-request:eyre] vas) - :_ this - %+ give-simple-payload:app id - %+ require-authorization:app req - handle-http-request:main - :: - %publish-action - =^ cards state - (poke-publish-action:main !<(action vas)) - [cards this] - == - :: - ++ on-watch - |= pax=path - ^- (quip card _this) - ?+ pax (on-watch:def pax) - [%http-response *] [~ this] - [%primary ~] [~ this] - [%notebook @ ~] - =^ cards state - (watch-notebook:main pax) - [cards this] - == - :: + ++ on-poke on-poke:def + ++ on-watch on-watch:def ++ on-leave on-leave:def - ++ on-peek - |= pax=path - ^- (unit (unit cage)) - ?+ pax (on-peek:def pax) - [%t %limbo ~] - :^ ~ ~ %noun - !> ^- (list path) - %+ weld - %+ turn ~(tap by notes.limbo) - |= [[who=@p book=@tas note=@tas] *] - ^- path - /(scot %p who)/[book]/[note] - %+ turn ~(tap by comments.limbo) - |= [[who=@p book=@tas note=@tas comment=@da] *] - ^- path - /(scot %p who)/[book]/[note]/(scot %ds comment) - :: - [%x %limbo @ @ @ ~] - =/ host=(unit @p) (slaw %p i.t.t.pax) - ?~ host [~ ~] - =/ book-name i.t.t.t.pax - =/ note-name i.t.t.t.t.pax - =/ note (~(get by notes.limbo) u.host book-name note-name) - ?~ note ~ - ``noun+!>(u.note) - :: - [%x %limbo @ @ @ @ ~] - =/ host=(unit @p) (slaw %p i.t.t.pax) - =/ comment-date=(unit @da) (slaw %da i.t.t.t.t.t.pax) - ?~ host [~ ~] - ?~ comment-date [~ ~] - =/ book-name i.t.t.t.pax - =/ note-name i.t.t.t.t.pax - =/ comment - (~(get by comments.limbo) u.host book-name note-name u.comment-date) - ?~ comment ~ - ``noun+!>(u.comment) - :: - [%x %book @ @ ~] - =/ host=(unit @p) (slaw %p i.t.t.pax) - =/ book-name i.t.t.t.pax - ?~ host [~ ~] - =/ book (~(get by books) u.host book-name) - ?~ book ~ - ``noun+!>(u.book) - == - :: - ++ on-agent - |= [wir=wire sin=sign:agent:gall] - ^- (quip card _this) - ?- -.sin - %poke-ack - ?: ?=([%join-group @ @ ~] wir) - ?^ p.sin - (on-agent:def wir sin) - =/ =ship - (slav %p i.t.wir) - =^ cards state - (subscribe-notebook ship i.t.t.wir) - [cards this] - ?~ p.sin - [~ this] - =^ cards state - (handle-poke-fail:main wir) - [cards this] - :: If our subscribe failed, delete notebook associated with subscription if - :: it exists - :: - %watch-ack - ?. ?=([%subscribe @ @ ~] wir) - (on-agent:def wir sin) - ?~ p.sin - [~ this] - =/ who=@p (slav %p i.t.wir) - =/ book=@tas i.t.t.wir - =/ del [%del-book who book] - :_ this(books (~(del by books) who book)) - [%give %fact [/primary]~ %publish-primary-delta !>(del)]~ - :: Resubscribe to any subscription we get kicked from. The case of actually - :: getting banned from a notebook is handled by %watch-ack - :: - %kick - ?+ wir - [~ this] - :: - [%subscribe @ @ ~] - =/ who=@p (slav %p i.t.wir) - =/ book=@tas i.t.t.wir - =/ wen=(unit @da) (get-last-update:main who book) - =/ pax=path - ?~ wen - /notebook/[book] - /notebook/[book]/(scot %da u.wen) - :_ this - [%pass wir %agent [who %publish] %watch pax]~ - :: - [%permissions ~] - :_ this - [%pass /permissions %agent [our.bol %permission-store] %watch /updates]~ - :: - [%groups ~] - :_ this - [%pass /groups %agent [our.bol %group-store] %watch /groups]~ - :: - [%invites ~] - :_ this - :_ ~ - :* %pass /invites %agent [our.bol %invite-store] %watch - /invitatory/publish - == - == - :: - %fact - ?+ wir (on-agent:def wir sin) - [%subscribe @ @ ~] - =/ who=@p (slav %p i.t.wir) - =/ book-name i.t.t.wir - ?> ?=(%publish-notebook-delta p.cage.sin) - =^ cards state - (handle-notebook-delta:main !<(notebook-delta q.cage.sin) state) - [cards this] - :: - [%groups ~] - =^ cards state - (handle-group-update:main !<(update:group-store q.cage.sin)) - [cards this] - :: - [%invites ~] - =^ cards state - (handle-invite-update:main !<(invite-update q.cage.sin)) - [cards this] - :: - [%collection *] - [~ this] - == - == - :: - ++ on-arvo - |= [wir=wire sin=sign-arvo] - ^- (quip card _this) - ?+ wir - (on-arvo:def wir sin) - :: - [%read %paths ~] - ?> ?=([?(%b %c) %writ *] sin) - =/ rot=riot:clay +>.sin - ?> ?=(^ rot) - =^ cards state - (read-paths:main u.rot) - [cards this] - :: - [%read %info *] - ?> ?=([?(%b %c) %writ *] sin) - =/ rot=riot:clay +>.sin - =^ cards state - (read-info:main t.t.wir rot) - [cards this] - :: - [%read %note *] - ?> ?=([?(%b %c) %writ *] sin) - =/ rot=riot:clay +>.sin - =^ cards state - (read-note:main t.t.wir rot) - [cards this] - :: - [%read %comment *] - ?> ?=([?(%b %c) %writ *] sin) - =/ rot=riot:clay +>.sin - =^ cards state - (read-comment:main t.t.wir rot) - [cards this] - :: - [%bind ~] - [~ this] - :: - [%view-bind ~] - [~ this] - == + ++ on-peek on-peek:def + ++ on-agent on-agent:def + ++ on-arvo on-arvo:def :: ++ on-fail on-fail:def -- @@ -716,624 +493,8 @@ |_ bol=bowl:gall ++ grup ~(. grpl bol) :: -++ metadata-store-poke - |= act=metadata-action - ^- card - [%pass / %agent [our.bol %metadata-store] %poke %metadata-action !>(act)] - :: -:: -++ get-last-update - |= [host=@p book-name=@tas] - ^- (unit @da) - =/ book (~(get by books) host book-name) - ?~ book ~ - =/ wen date-created.u.book - %- some - %- ~(rep by notes.u.book) - |= [[@tas =note] out=_wen] - ^- @da - %+ max out - %+ max last-edit.note - %- ~(rep by comments.note) - |= [[@da =comment] out=_out] - (max date-created.comment out) -:: -++ get-notebook-from-date - |= [host=@p book-name=@tas wen=@da] - ^- notebook - =/ book (~(got by books) host book-name) - %= book - notes - %- ~(rep by notes.book) - |= [[nom=@tas not=note] out=(map @tas note)] - ^- (map @tas note) - ?: (gth last-edit.not wen) - (~(put by out) nom not) - =. comments.not - %- ~(rep by comments.not) - |= [[nam=@da com=comment] out=(map @da comment)] - ?: (gth date-created.com wen) - (~(put by out) nam com) - out - ?~ comments.not - out - (~(put by out) nom not) - == -:: -++ merge-notebooks - |= [base=notebook diff=notebook] - ^- notebook - %= diff - notes - %- ~(rep by notes.diff) - |= [[nom=@tas not=note] out=_notes.base] - =/ base-note=(unit note) (~(get by out) nom) - ?~ base-note - (~(put by out) nom not) - =. comments.u.base-note - (~(uni by comments.u.base-note) comments.not) - (~(put by out) nom u.base-note) - == -:: -++ read-paths - |= ran=rant:clay - ^- (quip card _state) - =/ rav [%next %t [%da now.bol] /app/publish/notebooks] - =/ new (filter-and-sort-paths !<((list path) q.r.ran)) - =/ dif (diff-paths our-paths new) - =^ del-moves state (del-paths del.dif) - =^ add-moves state (add-paths add.dif) - :: - =/ cards=(list card) - ;: weld - [%pass /read/paths %arvo %c %warp our.bol q.byk.bol `rav]~ - del-moves - add-moves - == - [cards state(our-paths new)] -:: -++ read-info - |= [pax=path rot=riot:clay] - ^- (quip card _state) - ?> ?=([%app %publish %notebooks @ %publish-info ~] pax) - =/ book-name i.t.t.t.pax - ?~ rot - [~ state] - =/ info=notebook-info !<(notebook-info q.r.u.rot) - =/ new-book=notebook - :* title.info - description.info - comments.info - writers.info - subscribers.info - now.bol - ~ ~ ~ - == - =/ rif=riff:clay [q.byk.bol `[%next %x [%da now.bol] pax]] - =/ delta=notebook-delta - [%edit-book our.bol book-name new-book] - =^ cards state - (handle-notebook-delta delta state) - :_ state - :* [%pass (welp /read/info pax) %arvo %c %warp our.bol rif] - cards - == -:: -++ read-note - |= [pax=path rot=riot:clay] - ^- (quip card _state) - ?> ?=([%app %publish %notebooks @ @ %udon ~] pax) - =/ book-name i.t.t.t.pax - =/ note-name i.t.t.t.t.pax - =/ book (~(get by books) our.bol book-name) - ?~ book - [~ state] - =/ old-note (~(get by notes.u.book) note-name) - ?~ old-note - [~ state] - ?~ rot - [~ state] - =/ udon !<(@t q.r.u.rot) - =/ new-note=note (form-note note-name udon) - =/ rif=riff:clay [q.byk.bol `[%next %x [%da now.bol] pax]] - =/ delta=notebook-delta - [%edit-note our.bol book-name note-name new-note] - =^ cards state - (handle-notebook-delta delta state) - :_ state - :* [%pass (welp /read/note pax) %arvo %c %warp our.bol rif] - cards - == -:: -++ read-comment - |= [pax=path rot=riot:clay] - ^- (quip card _state) - ?> ?=([%app %publish %notebooks @ @ @ %publish-comment ~] pax) - ?~ rot - [~ state] - =/ comment-date (slaw %da i.t.t.t.t.t.pax) - ?~ comment-date - [~ state] - =/ book-name i.t.t.t.pax - =/ note-name i.t.t.t.t.pax - =/ com-2-3 !<(?(comment-2 comment-3) q.r.u.rot) - =/ new-comment=comment-3 - ?: ?=(comment-2 com-2-3) - [author.com-2-3 date-created.com-2-3 content.com-2-3 %.n] - com-2-3 - =/ rif=riff:clay [q.byk.bol `[%next %x [%da now.bol] pax]] - =/ delta=notebook-delta - [%edit-comment our.bol book-name note-name u.comment-date new-comment] - =^ cards state - (handle-notebook-delta delta state) - :_ state - :* [%pass (welp /read/comment pax) %arvo %c %warp our.bol rif] - cards - == -:: -++ filter-and-sort-paths - |= paths=(list path) - ^- (list path) - %+ sort - %+ skim paths - |= pax=path - ?| ?=([%app %publish %notebooks @ %publish-info ~] pax) - ?=([%app %publish %notebooks @ @ %udon ~] pax) - ?=([%app %publish %notebooks @ @ @ %publish-comment ~] pax) - == - |= [a=path b=path] - ^- ? - (lte (lent a) (lent b)) -:: -++ diff-paths - |= [old=(list path) new=(list path)] - ^- [del=(list path) add=(list path)] - =/ del=(list path) (skim old |=(p=path ?=(~ (find [p]~ new)))) - =/ add=(list path) (skim new |=(p=path ?=(~ (find [p]~ old)))) - [del add] -:: -++ del-paths - |= paths=(list path) - ^- (quip card _state) - %+ roll paths - |= [pax=path cad=(list card) sty=_state] - ?+ pax !! - [%app %publish %notebooks @ %publish-info ~] - =/ book-name i.t.t.t.pax - =/ delta=notebook-delta [%del-book our.bol book-name] - =^ cards sty (handle-notebook-delta delta sty) - [(weld cards cad) sty] - :: - [%app %publish %notebooks @ @ %udon ~] - =/ book-name i.t.t.t.pax - =/ note-name i.t.t.t.t.pax - =/ book (~(get by books.sty) our.bol book-name) - ?~ book - [cad sty] - =. notes.u.book (~(del by notes.u.book) note-name) - =/ delta=notebook-delta [%del-note our.bol book-name note-name] - =^ cards sty (handle-notebook-delta delta sty) - [(weld cards cad) sty] - :: - [%app %publish %notebooks @ @ @ %publish-comment ~] - =/ book-name i.t.t.t.pax - =/ note-name i.t.t.t.t.pax - =/ comment-date (slaw %da i.t.t.t.t.t.pax) - ?~ comment-date - [cad sty] - =/ delta=notebook-delta - [%del-comment our.bol book-name note-name u.comment-date] - =^ cards sty (handle-notebook-delta delta sty) - [(weld cards cad) sty] - == -:: -++ add-paths - |= paths=(list path) - ^- (quip card _state) - %+ roll paths - |= [pax=path cad=(list card) sty=_state] - ^- (quip card _state) - ?+ pax !! - [%app %publish %notebooks @ %publish-info ~] - =/ book-name i.t.t.t.pax - =/ info=notebook-info .^(notebook-info %cx (welp our-beak pax)) - =* title title.info - =* description description.info - =/ new-book=notebook - :* title - description - comments.info - writers.info - subscribers.info - now.bol - ~ ~ ~ - == - =+ ^- [grp-car=(list card) write-pax=path read-pax=path] - ?: =(writers.new-book /) - =/ group-path /~/(scot %p our.bol)/[book-name] - (make-groups book-name [group-path ~ %.n %.n] title description) - [~ writers.info subscribers.info] - =. writers.new-book write-pax - =. subscribers.new-book read-pax - =+ ^- [read-cards=(list card) notes=(map @tas note)] - (watch-notes /app/publish/notebooks/[book-name]) - =. notes.new-book notes - =/ delta=notebook-delta [%add-book our.bol book-name new-book] - =/ rif=riff:clay [q.byk.bol `[%next %x [%da now.bol] pax]] - =^ update-cards sty (handle-notebook-delta delta sty) - :_ sty - ;: weld - grp-car - [%pass (welp /read/info pax) %arvo %c %warp our.bol rif]~ - read-cards - update-cards - cad - == - :: - [%app %publish %notebooks @ @ %udon ~] - =/ book-name i.t.t.t.pax - =/ note-name i.t.t.t.t.pax - =/ new-note=note (scry-note pax) - =+ ^- [read-cards=(list card) comments=(map @da comment)] - (watch-comments /app/publish/notebooks/[book-name]/[note-name]) - =. comments.new-note comments - =/ rif=riff:clay [q.byk.bol `[%next %x [%da now.bol] pax]] - =/ delta=notebook-delta - [%add-note our.bol book-name note-name new-note] - =^ update-cards sty (handle-notebook-delta delta sty) - :_ sty - ;: weld - [%pass (welp /read/note pax) %arvo %c %warp our.bol rif]~ - read-cards - update-cards - cad - == - :: - [%app %publish %notebooks @ @ @ %publish-comment ~] - =/ book-name i.t.t.t.pax - =/ note-name i.t.t.t.t.pax - =/ comment-name (slaw %da i.t.t.t.t.t.pax) - ?~ comment-name - [~ sty] - =/ com-2-3 .^(?(comment-2 comment-3) %cx (welp our-beak pax)) - =/ new-com=comment-3 - ?: ?=(comment-2 com-2-3) - [author.com-2-3 date-created.com-2-3 content.com-2-3 %.n] - com-2-3 - =/ rif=riff:clay [q.byk.bol `[%next %x [%da now.bol] pax]] - :: - =/ delta=notebook-delta - [%add-comment our.bol book-name note-name u.comment-name new-com] - =^ update-cards sty (handle-notebook-delta delta sty) - :_ sty - ;: weld - [%pass (welp /read/comment pax) %arvo %c %warp our.bol rif]~ - update-cards - cad - == - == -:: -++ watch-notes - |= pax=path - ^- [(list card) (map @tas note)] - =/ paths .^((list path) %ct (weld our-beak pax)) - %+ roll paths - |= [pax=path cards=(list card) notes=(map @tas note)] - ?. ?=([%app %publish %notebooks @ @ %udon ~] pax) - [cards notes] - =/ book-name i.t.t.t.pax - =/ note-name i.t.t.t.t.pax - =/ new-note (scry-note pax) - =^ comment-cards comments.new-note - (watch-comments /app/publish/notebooks/[book-name]/[note-name]) - =/ rif=riff:clay [q.byk.bol `[%next %x [%da now.bol] pax]] - :_ (~(put by notes) note-name new-note) - ;: weld - [%pass (welp /read/note pax) %arvo %c %warp our.bol rif]~ - comment-cards - cards - == -:: -++ watch-comments - |= pax=path - ^- [(list card) (map @da comment)] - =/ paths .^((list path) %ct (weld our-beak pax)) - %+ roll paths - |= [pax=path cards=(list card) comments=(map @da comment)] - ?. ?=([%app %publish %notebooks @ @ @ %publish-comment ~] pax) - [cards comments] - =/ comment-name (slaw %da i.t.t.t.t.t.pax) - ?~ comment-name - [cards comments] - =/ new-com .^(comment %cx (welp our-beak pax)) - =/ rif=riff:clay [q.byk.bol `[%next %x [%da now.bol] pax]] - :_ (~(put by comments) u.comment-name new-com) - [[%pass (welp /read/comment pax) %arvo %c %warp our.bol rif] cards] -:: -++ scry-note - |= pax=path - ^- note - ?> ?=([%app %publish %notebooks @ @ %udon ~] pax) - =/ note-name i.t.t.t.t.pax - =/ udon=@t .^(@t %cx (welp our-beak pax)) - (form-note note-name udon) -:: -++ form-snippet - |= file=@t - ^- @t - =/ front-idx (add 3 (need (find ";>" (trip file)))) - =/ front-matter (cat 3 (end 3 front-idx file) 'dummy text\0a') - =/ body (cut 3 [front-idx (met 3 file)] file) - (of-wain:format (scag 1 (to-wain:format body))) -:: -++ form-note - |= [note-name=@tas file=@t] - ^- note - =/ snippet=@t (form-snippet file) - =/ front-idx (add 3 (need (find ";>" (trip file)))) - =/ front-matter (cat 3 (end 3 front-idx file) 'dummy text\0a') - =/ meta=(each (map term knot) tang) - %- mule |. - %- ~(run by inf:(static:cram (ream front-matter))) - |= a=dime ^- cord - ?+ (end 3 1 p.a) (scot a) - %t q.a - == - :: - =/ author=@p our.bol - =? author ?=(%.y -.meta) - %+ fall - (biff (~(get by p.meta) %author) (slat %p)) - our.bol - :: - =/ title=@t note-name - =? title ?=(%.y -.meta) - (fall (~(get by p.meta) %title) note-name) - :: - =/ date-created=@da now.bol - =? date-created ?=(%.y -.meta) - %+ fall - (biff (~(get by p.meta) %date-created) (slat %da)) - now.bol - :: - =/ last-modified=@da now.bol - =? last-modified ?=(%.y -.meta) - %+ fall - (biff (~(get by p.meta) %last-modified) (slat %da)) - now.bol - :: - :* author - title - note-name - date-created - last-modified - %.y - file - snippet - ~ - %.n - == -:: -++ handle-group-update - |= =update:group-store - ^- (quip card _state) - ?. ?=(?(%remove-members %add-members) -.update) - [~ state] - =* ships ships.update - =/ =path - (en-path:resource resource.update) - =/ book=(unit @tas) - %+ roll ~(tap by books) - |= [[[who=@p nom=@tas] book=notebook] out=(unit @tas)] - ?. =(who our.bol) - out - ?. =(path subscribers.book) - out - `nom - ?~ book - [~ state] - :_ state - %- zing - :- ^- (list card) - %+ roll ~(tap by books) - |= [[[who=@p book=@tas] nb=notebook] out=(list card)] - ^- (list card) - ?. =(who our.bol) - out - ?. =(writers.nb path) - out - =/ rid (de-path:resource writers.nb) - =/ grp=(unit group) (scry-group:grup rid) - ?~ grp out - ?: hidden.u.grp - out - =/ =tag [%publish (cat 3 'writers-' book)] - :_ out - (group-proxy-poke entity.rid %add-tag rid tag members.u.grp) - %+ turn ~(tap in ships) - |= who=@p - ?. (allowed who %read u.book) - [%give %kick [/notebook/[u.book]]~ `who]~ - ?: ?|(?=(%remove-members -.update) (is-managed-path:grup path)) - ~ - =/ uid (sham %publish who u.book eny.bol) - =/ inv=invite - :* our.bol %publish /notebook/[u.book] who - (crip "invite for notebook {}/{(trip u.book)}") - == - =/ act=invite-action [%invite /publish uid inv] - [%pass / %agent [our.bol %invite-hook] %poke %invite-action !>(act)]~ -:: -++ handle-invite-update - |= upd=invite-update - ^- (quip card _state) - ?+ -.upd - [~ state] - :: - %delete - [~ state] - :: - %invite - [~ state] - :: - %decline - [~ state] - :: - %accepted - ?> ?=([@ @ *] path.invite.upd) - =/ book i.t.path.invite.upd - =/ group - (group-from-book notebook+book^~) - ?^ group - (subscribe-notebook ship.invite.upd book) - =/ rid=resource - (de-path:resource ship+path.invite.upd) - =/ join-wire=wire - /join-group/[(scot %p ship.invite.upd)]/[book] - =/ =cage - :- %group-update - !> ^- action:group-store - [%add-members rid (sy our.bol ~)] - :_ state - [%pass join-wire %agent [entity.rid %group-push-hook] %poke cage]~ - == -:: -++ subscribe-notebook - |= [=ship book=@tas] - ^- (quip card _state) - =/ pax=path /notebook/[book] - =/ wir=wire /subscribe/[(scot %p ship)]/[book] - =? tile-num (gth tile-num 0) - (dec tile-num) - =/ jon=json (frond:enjs:format %notifications (numb:enjs:format tile-num)) - :_ state - :~ [%pass wir %agent [ship %publish] %watch pax] - [%give %fact [/publishtile]~ %json !>(jon)] - == -:: -++ watch-notebook - |= pax=path - ?> ?=([%notebook @ *] pax) - =/ book-name i.t.pax - ?. (allowed src.bol %read book-name) - ~|("not permitted" !!) - =/ book - ?: ?=([%notebook @ @ ~] pax) - =/ wen=@da (slav %da i.t.t.pax) - (get-notebook-from-date our.bol book-name wen) - (~(got by books) our.bol book-name) - =/ delta=notebook-delta - [%add-book our.bol book-name book] - :_ state - [%give %fact ~ %publish-notebook-delta !>(delta)]~ -:: ++ our-beak /(scot %p our.bol)/[q.byk.bol]/(scot %da now.bol) :: -++ book-writers - |= [host=@p book=@tas] - ^- (set ship) - =/ =notebook (~(got by books) host book) - =/ rid=resource - (de-path:resource writers.notebook) - %- ~(uni in (fall (scry-tag:grup rid %admin) ~)) - %+ fall - (scry-tag:grup rid `tag`[%publish (cat 3 %writers- book)]) - ~ -:: -++ allowed - |= [who=@p mod=?(%read %write) book=@tas] - ^- ? - =/ =notebook (~(got by books) our.bol book) - =/ rid=resource - (de-path:resource writers.notebook) - ?: ?=(%read mod) - (~(has in (members:grup rid)) who) - (~(has in (book-writers our.bol book)) who) -:: -++ write-file - |= [pax=path cay=cage] - ^- card - =. pax (weld our-beak pax) - [%pass (weld /write pax) %arvo %c %info (foal:space:userlib pax cay)] -:: -++ delete-file - |= pax=path - ^- card - =. pax (weld our-beak pax) - [%pass (weld /delete pax) %arvo %c %info (fray:space:userlib pax)] -:: -++ delete-dir - |= pax=path - ^- card - =/ nor=nori:clay - :- %& - %+ turn .^((list path) %ct (weld our-beak pax)) - |= pax=path - ^- [path miso:clay] - [pax %del ~] - [%pass (weld /delete pax) %arvo %c %info q.byk.bol nor] -:: -++ add-front-matter - |= [fro=(map knot cord) udon=@t] - ^- @t - %- of-wain:format - =/ tum (trip udon) - =/ id (find ";>" tum) - ?~ id - %+ weld (front-to-wain fro) - (to-wain:format (crip :(weld ";>\0a" tum))) - %+ weld (front-to-wain fro) - (to-wain:format (crip (slag u.id tum))) -:: -++ front-to-wain - |= a=(map knot cord) - ^- wain - =/ entries=wain - %+ turn ~(tap by a) - |= b=[knot cord] - =/ c=[term cord] (,[term cord] b) - (crip " [{<-.c>} {<+.c>}]") - :: - ?~ entries ~ - ;: weld - [':- :~' ~] - entries - [' ==' ~] - == -:: -++ give-primary-delta - |= del=primary-delta - ^- card - [%give %fact [/primary]~ %publish-primary-delta !>(del)] -:: -++ group-poke - |= act=action:group-store - ^- card - [%pass / %agent [our.bol %group-store] %poke %group-action !>(act)] -:: -++ group-proxy-poke - |= [who=ship act=action:group-store] - ^- card - [%pass / %agent [who %group-push-hook] %poke %group-update !>(act)] -:: -++ group-pull-hook-poke - |= act=action:pull-hook - ^- card - [%pass / %agent [our.bol %group-pull-hook] %poke %pull-hook-action !>(act)] -:: -++ contact-view-poke - |= act=contact-view-action:contact-view - ^- card - [%pass / %agent [our.bol %contact-view] %poke %contact-view-action !>(act)] -:: -++ contact-view-create - |= [=path ships=(set ship) =policy title=@t description=@t] - =/ rid=resource - (de-path:resource path) - =/ act=contact-view-action:contact-view - [%create name.rid policy title description] - (contact-view-poke act) -:: ++ perm-hook-poke |= act=permission-hook-action ^- card @@ -1412,1135 +573,31 @@ :- (group-poke %add-group rid policy %.y) (generate-invites book (~(del in invitees.group) our.bol)) :: -++ handle-poke-fail - |= wir=wire - ^- (quip card _state) - ?+ wir - [~ state] - :: new note failed, stash it in limbo - :: - [%forward %new-note @ @ @ ~] - =/ host=@p (slav %p i.t.t.wir) - =/ book-name i.t.t.t.wir - =/ note-name i.t.t.t.t.wir - =/ book (~(get by books) [host book-name]) - ?~ book - [~ state] - =/ note (~(get by notes.u.book) note-name) - ?~ note - [~ state] - =. notes.limbo (~(put by notes.limbo) [host book-name note-name] u.note) - =. notes.u.book (~(del by notes.u.book) note-name) - =/ del [%del-note host book-name note-name] - :- [(give-primary-delta del)]~ - state(books (~(put by books) [host book-name] u.book)) - :: new comment failed, stash it in limbo - :: - [%forward %new-comment @ @ @ @ ~] - =/ host=@p (slav %p i.t.t.wir) - =/ book-name i.t.t.t.wir - =/ note-name i.t.t.t.t.wir - =/ comment-date=@da (slav %da i.t.t.t.t.t.wir) - =/ book (~(get by books) [host book-name]) - ?~ book - [~ state] - =/ note (~(get by notes.u.book) note-name) - ?~ note - [~ state] - =/ comment (~(get by comments.u.note) comment-date) - ?~ comment - [~ state] - =. comments.limbo - %+ ~(put by comments.limbo) - [host book-name note-name comment-date] - u.comment - =. comments.u.note (~(del by comments.u.note) comment-date) - =. notes.u.book (~(put by notes.u.book) note-name u.note) - =/ del [%del-comment host book-name note-name comment-date] - :- [(give-primary-delta del)]~ - state(books (~(put by books) [host book-name] u.book)) - :: edit note failed, restore old version - :: - [%forward %edit-note @ @ @ ~] - =/ host=@p (slav %p i.t.t.wir) - =/ book-name i.t.t.t.wir - =/ note-name i.t.t.t.t.wir - =/ book (~(get by books) [host book-name]) - ?~ book - [~ state] - =/ note (~(get by notes.limbo) host book-name note-name) - ?~ note - [~ state] - =. notes.u.book (~(put by notes.u.book) note-name u.note) - =/ del [%edit-note host book-name note-name u.note] - :- [(give-primary-delta del)]~ - %= state - books (~(put by books) [host book-name] u.book) - notes.limbo (~(del by notes.limbo) host book-name note-name) - == - :: edit comment failed, restore old version - :: - [%forward %new-comment @ @ @ @ ~] - =/ host=@p (slav %p i.t.t.wir) - =/ book-name i.t.t.t.wir - =/ note-name i.t.t.t.t.wir - =/ comment-date=@da (slav %da i.t.t.t.t.t.wir) - =/ book (~(get by books) [host book-name]) - ?~ book - [~ state] - =/ note (~(get by notes.u.book) note-name) - ?~ note - [~ state] - =/ comment - (~(get by comments.limbo) host book-name note-name comment-date) - ?~ comment - [~ state] - =. comments.u.note (~(put by comments.u.note) comment-date u.comment) - =. notes.u.book (~(put by notes.u.book) note-name u.note) - =/ del [%edit-comment host book-name note-name comment-date u.comment] - :- [(give-primary-delta del)]~ - %= state - books (~(put by books) [host book-name] u.book) - :: - comments.limbo - %+ ~(del by comments.limbo) - [host book-name note-name comment-date] - u.comment - == - :: delete note failed, restore old version - :: - [%forward %del-note @ @ @ ~] - =/ host=@p (slav %p i.t.t.wir) - =/ book-name i.t.t.t.wir - =/ note-name i.t.t.t.t.wir - =/ book (~(get by books) [host book-name]) - ?~ book - [~ state] - =/ note (~(get by notes.limbo) host book-name note-name) - ?~ note - [~ state] - =. notes.u.book (~(put by notes.u.book) note-name u.note) - =/ del [%add-note host book-name note-name u.note] - :- [(give-primary-delta del)]~ - %= state - books (~(put by books) [host book-name] u.book) - notes.limbo (~(del by notes.limbo) host book-name note-name) - == - :: delete comment failed, restore old version - :: - [%forward %del-comment @ @ @ @ ~] - =/ host=@p (slav %p i.t.t.wir) - =/ book-name i.t.t.t.wir - =/ note-name i.t.t.t.t.wir - =/ comment-date=@da (slav %da i.t.t.t.t.t.wir) - =/ book (~(get by books) [host book-name]) - ?~ book - [~ state] - =/ note (~(get by notes.u.book) note-name) - ?~ note - [~ state] - =/ comment - (~(get by comments.limbo) host book-name note-name comment-date) - ?~ comment - [~ state] - =. comments.u.note (~(put by comments.u.note) comment-date u.comment) - =. notes.u.book (~(put by notes.u.book) note-name u.note) - =/ del [%add-comment host book-name note-name comment-date u.comment] - :- [(give-primary-delta del)]~ - %= state - books (~(put by books) [host book-name] u.book) - :: - comments.limbo - %+ ~(del by comments.limbo) - [host book-name note-name comment-date] - u.comment - == - == -:: -++ poke-publish-action - |= act=action - ^- (quip card _state) - ?- -.act - :: %new-book: Make groups and save publish info file. - :: - %new-book - ?. (team:title our.bol src.bol) - ~|("action not permitted" !!) - ?: (~(has by books) our.bol book.act) - ~|("notebook already exists: {}" !!) - =+ ^- [cards=(list card) write-pax=path read-pax=path] - (make-groups book.act group.act title.act about.act) - =/ new-book=notebook-info - :* title.act - about.act - coms.act - write-pax - read-pax - == - =/ pax=path /app/publish/notebooks/[book.act]/publish-info - :_ state - [(write-file pax %publish-info !>(new-book)) cards] - :: %new-note: - :: If poke is from us, eagerly store new note in books. If poke is to us, - :: save file, otherwise forward the poke. If forwarded poke fails, note is - :: removed from books and stored in limbo. - :: - %new-note - =/ book=(unit notebook) (~(get by books) who.act book.act) - ?~ book - ~|("nonexistent notebook {}" !!) - ?: (~(has by notes.u.book) note.act) - ~|("note already exists: {}" !!) - =/ front=(map knot cord) - %- my - :~ title+title.act - author+(scot %p src.bol) - date-created+(scot %da now.bol) - last-modified+(scot %da now.bol) - == - =/ file=@t (add-front-matter front body.act) - :: - =^ cards books - ?. =(src.bol our.bol) - [~ books] - =/ new-note=note - :* src.bol - title.act - note.act - now.bol - now.bol - %.y - file - (form-snippet file) - ~ - %.y - == - =/ del=primary-delta [%add-note who.act book.act note.act new-note] - :- [(give-primary-delta del)]~ - %+ ~(put by books) - [who.act book.act] - u.book(notes (~(put by notes.u.book) note.act new-note)) - :: - :_ state - ?. =(who.act our.bol) - =/ poke-wir=wire - /forward/new-note/(scot %p who.act)/[book.act]/[note.act] - :_ cards - [%pass poke-wir %agent [who.act %publish] %poke %publish-action !>(act)] - ?. ?| (team:title our.bol src.bol) - (allowed src.bol %write book.act) - == - ~|("action not permitted" !!) - =/ pax=path /app/publish/notebooks/[book.act]/[note.act]/udon - :_ cards - [(write-file pax %udon !>(file))] - :: %new-comment - :: If poke is from us, eagerly store new comment in books. If poke is to - :: us, save file, otherwise forward the poke. If forwarded poke fails, - :: comment is removed from books and stored in limbo. - :: - %new-comment - =/ book=(unit notebook) (~(get by books) who.act book.act) - ?~ book - ~|("nonexistent notebook {}" !!) - =/ note=(unit note) (~(get by notes.u.book) note.act) - ?~ note - ~|("nonexistent note {}" !!) - =/ new-comment=comment - :* author=src.bol - date-created=now.bol - content=body.act - %.y - == - :: - =^ cards books - ?. =(src.bol our.bol) - [~ books] - =/ new-note - %= u.note - comments (~(put by comments.u.note) now.bol new-comment) - == - =/ del=primary-delta - [%add-comment who.act book.act note.act now.bol new-comment] - :- [(give-primary-delta del)]~ - %+ ~(put by books) - [who.act book.act] - u.book(notes (~(put by notes.u.book) note.act new-note)) - :_ state - ?. =(who.act our.bol) - =/ poke-wir=wire - :~ %forward - %new-comment - (scot %p who.act) - book.act - note.act - (scot %da now.bol) - == - :_ cards - [%pass poke-wir %agent [who.act %publish] %poke %publish-action !>(act)] - ?. ?& ?| (team:title our.bol src.bol) - (allowed src.bol %read book.act) - == - comments.u.book - == - ~|("action not permitted" !!) - =/ pax=path - %+ weld /app/publish/notebooks - /[book.act]/[note.act]/(scot %da now.bol)/publish-comment - [(write-file pax %publish-comment !>(new-comment(pending %.n)))]~ - :: %edit-book: Make groups and save publish-info file - :: - %edit-book - ?. (team:title our.bol src.bol) - ~|("action not permitted" !!) - =/ book (~(get by books) our.bol book.act) - ?~ book - ~|("nonexistent notebook" !!) - =+ ^- [cards=(list card) write-pax=path read-pax=path] - ?~ group.act - [~ writers.u.book subscribers.u.book] - (make-groups book.act u.group.act title.act about.act) - =/ new-info=notebook-info - :* title.act - about.act - coms.act - write-pax - read-pax - == - =/ pax=path /app/publish/notebooks/[book.act]/publish-info - :_ state - [(write-file pax %publish-info !>(new-info)) cards] - :: %edit-note: - :: If poke is from us, eagerly store new note in books, and place the old - :: note in limbo. If poke is to us, save file, otherwise forward the poke. - :: If forwarded poke fails, old note is restored from limbo. - :: - %edit-note - =/ book=(unit notebook) (~(get by books) who.act book.act) - ?~ book - ~|("nonexistent notebook {}" !!) - =/ note=(unit note) (~(get by notes.u.book) note.act) - ?~ note - ~|("nonexistent note: {}" !!) - =/ front=(map knot cord) - %- my - :~ title+title.act - author+(scot %p src.bol) - date-created+(scot %da date-created.u.note) - last-modified+(scot %da now.bol) - == - =/ file=@t (add-front-matter front body.act) - :: - =^ cards state - ?. =(src.bol our.bol) - [~ state] - =/ new-note - %= u.note - author src.bol - title title.act - last-edit now.bol - file file - snippet (form-snippet file) - pending %.y - == - =/ del=primary-delta [%edit-note who.act book.act note.act new-note] - :- [(give-primary-delta del)]~ - %= state - notes.limbo - (~(put by notes.limbo) [who.act book.act note.act] u.note) - :: - books - %+ ~(put by books) - [who.act book.act] - u.book(notes (~(put by notes.u.book) note.act new-note)) - == - :: - :_ state - ?. =(who.act our.bol) - =/ poke-wir=wire - /forward/edit-note/(scot %p who.act)/[book.act]/[note.act] - :_ cards - [%pass poke-wir %agent [who.act %publish] %poke %publish-action !>(act)] - ?. ?| (team:title our.bol src.bol) - ?& =(author.u.note src.bol) - (allowed src.bol %write book.act) - == - == - ~|("action not permitted" !!) - =/ pax=path /app/publish/notebooks/[book.act]/[note.act]/udon - [(write-file pax %udon !>(file))]~ - :: %edit-comment: - :: If poke is from us, eagerly store new comment in books, and place the - :: old note in limbo. If poke is to us, save file, otherwise forward the - :: poke. If forwarded poke fails, old comment is restored from limbo. - :: - %edit-comment - =/ book=(unit notebook) (~(get by books) who.act book.act) - ?~ book - ~|("nonexistent notebook {}" !!) - =/ note=(unit note) (~(get by notes.u.book) note.act) - ?~ note - ~|("nonexistent note {}" !!) - =/ comment-date (slav %da comment.act) - =/ comment=(unit comment) (~(get by comments.u.note) comment-date) - ?~ comment - ~|("nonexistent comment {}" !!) - =/ new-comment - u.comment(content body.act, pending %.y) - :: - =^ cards state - ?. =(src.bol our.bol) - [~ state] - =/ new-note - %= u.note - comments - (~(put by comments.u.note) comment-date new-comment) - == - =/ del=primary-delta - [%edit-comment who.act book.act note.act comment-date new-comment] - :- [(give-primary-delta del)]~ - %= state - books - %+ ~(put by books) - [who.act book.act] - u.book(notes (~(put by notes.u.book) note.act new-note)) - :: - comments.limbo - %+ ~(put by comments.limbo) - [who.act book.act note.act comment-date] - u.comment - == - :: - :_ state - ?. =(who.act our.bol) - =/ poke-wir - :~ %forward - %edit-comment - (scot %p who.act) - book.act - note.act - comment.act - == - :_ cards - [%pass poke-wir %agent [who.act %publish] %poke %publish-action !>(act)] - ?. ?| (team:title our.bol src.bol) - ?& =(author.u.comment src.bol) - (allowed src.bol %read book.act) - == - == - ~|("action not permitted" !!) - =/ pax=path - %+ weld /app/publish/notebooks - /[book.act]/[note.act]/[comment.act]/publish-comment - [(write-file pax %publish-comment !>(new-comment(pending %.n)))]~ - :: %del-book: Delete whole notebook directory, delete groups and permissions - :: - %del-book - ?. (team:title our.bol src.bol) - ~|("action not permitted" !!) - =/ book=(unit notebook) (~(get by books) our.bol book.act) - ?~ book - ~|("nonexistent notebook {}" !!) - =/ pax=path /app/publish/notebooks/[book.act] - ?> ?=(^ writers.u.book) - ?> ?=(^ subscribers.u.book) - =/ cards=(list card) - ~[(delete-dir pax)] - =/ rid=resource - (de-path:resource writers.u.book) - =? cards !(is-managed:grup rid) - [(group-poke %remove-group rid ~) cards] - [cards state] - :: %del-note: - :: If poke is from us, eagerly remove note from books, and place the - :: old note in limbo. If poke is to us, save file, otherwise forward the - :: poke. If forwarded poke fails, old note is restored from limbo. - :: - %del-note - =/ book=(unit notebook) (~(get by books) who.act book.act) - ?~ book - ~|("nonexistent notebook {}" !!) - =/ note=(unit note) (~(get by notes.u.book) note.act) - ?~ note - ~|("nonexistent note: {}" !!) - :: - =^ cards state - ?. =(src.bol our.bol) - [~ state] - =/ del=primary-delta [%del-note who.act book.act note.act] - =. notes.u.book (~(del by notes.u.book) note.act) - :- [(give-primary-delta del)]~ - %= state - books (~(put by books) [who.act book.act] u.book) - notes.limbo (~(put by notes.limbo) [who.act book.act note.act] u.note) - == - :: - :_ state - ?. =(who.act our.bol) - =/ poke-wir=wire - /forward/del-note/(scot %p who.act)/[book.act]/[note.act] - :_ cards - [%pass poke-wir %agent [who.act %publish] %poke %publish-action !>(act)] - ?. ?| (team:title our.bol src.bol) - ?& =(author.u.note src.bol) - (allowed src.bol %write book.act) - == - == - ~|("action not permitted" !!) - =/ pax=path /app/publish/notebooks/[book.act]/[note.act]/udon - [(delete-file pax)]~ - :: %del-comment: - :: If poke is from us, eagerly remove comment from books, and place the - :: old note in limbo. If poke is to us, save file, otherwise forward the - :: poke. If forwarded poke fails, old comment is restored from limbo. - :: - %del-comment - =/ book=(unit notebook) (~(get by books) who.act book.act) - ?~ book - ~|("nonexistent notebook {}" !!) - =/ note=(unit note) (~(get by notes.u.book) note.act) - ?~ note - ~|("nonexistent note {}" !!) - =/ comment-date (slav %da comment.act) - =/ comment=(unit comment) (~(get by comments.u.note) comment-date) - ?~ comment - ~|("nonexistent comment {}" !!) - :: - =^ cards state - ?. =(src.bol our.bol) - [~ state] - =/ del=primary-delta - [%del-comment who.act book.act note.act comment-date] - =. comments.u.note (~(del by comments.u.note) comment-date) - =. notes.u.book (~(put by notes.u.book) note.act u.note) - :- [(give-primary-delta del)]~ - %= state - books - (~(put by books) [who.act book.act] u.book) - :: - comments.limbo - %+ ~(put by comments.limbo) - [who.act book.act note.act comment-date] - u.comment - == - :: - :_ state - ?. =(who.act our.bol) - =/ poke-wir=wire - :~ %forward - %del-comment - (scot %p who.act) - book.act - note.act - comment.act - == - :_ cards - [%pass poke-wir %agent [who.act %publish] %poke %publish-action !>(act)] - ?. ?| (team:title our.bol src.bol) - ?& =(author.u.comment src.bol) - (allowed src.bol %read book.act) - == - == - ~|("action not permitted" !!) - =/ pax=path - %+ weld /app/publish/notebooks - /[book.act]/[note.act]/[comment.act]/publish-comment - [(delete-file pax)]~ - :: %subscribe - :: - %subscribe - ?> (team:title our.bol src.bol) - ?: =(our.bol who.act) - [~ state] - =/ join-wire=wire - /join-group/[(scot %p who.act)]/[book.act] - =/ meta=(unit (set path)) - (metadata-resource-scry %publish /(scot %p who.act)/[book.act]) - ?^ meta - (subscribe-notebook who.act book.act) - =/ rid=resource - [who.act book.act] - =/ =cage - :- %group-update - !> ^- action:group-store - [%add-members rid (sy our.bol ~)] - :_ state - [%pass join-wire %agent [who.act %group-push-hook] %poke cage]~ - :: %unsubscribe - :: - %unsubscribe - ?> (team:title our.bol src.bol) - =/ wir=wire /subscribe/(scot %p who.act)/[book.act] - =/ del=primary-delta [%del-book who.act book.act] - =/ book=notebook - (~(got by books) who.act book.act) - =/ rid=resource - (de-path:resource writers.book) - =/ =group - (need (scry-group:grup rid)) - =/ cards=(list card) - :~ [%pass wir %agent [who.act %publish] %leave ~] - [%give %fact [/primary]~ %publish-primary-delta !>(del)] - == - =? cards hidden.group - %+ weld cards - :~ (group-proxy-poke who.act %remove-members rid (sy our.bol ~)) - (group-poke %remove-group rid ~) - == - [cards state(books (~(del by books) who.act book.act))] - :: %read - :: - %read - ?> (team:title our.bol src.bol) - =/ book=(unit notebook) - (~(get by books) who.act book.act) - ?~ book - ~|("nonexistent notebook: {}" !!) - =/ not=(unit note) (~(get by notes.u.book) note.act) - ?~ not - ~|("nonexistent note: {}" !!) - =? tile-num &(!read.u.not (gth tile-num 0)) - (dec tile-num) - =. read.u.not %.y - =. notes.u.book (~(put by notes.u.book) note.act u.not) - =. books (~(put by books) [who.act book.act] u.book) - :_ state - [%give %fact [/primary]~ %publish-primary-delta !>(act)]~ - :: %groupify - :: - %groupify - ?. (team:title our.bol src.bol) - ~|("action not permitted" !!) - =/ book (~(get by books) our.bol book.act) - ?~ book - ~|("nonexistent notebook: {}" !!) - :: - =* old-group-path writers.u.book - =/ app-path /[(scot %p our.bol)]/[book.act] - =/ =metadata - (need (metadata-scry old-group-path app-path)) - =/ old-rid=resource - (de-path:resource old-group-path) - ?< (is-managed:grup old-rid) - ?~ target.act - :: just create contacts object for group - :_ state - ~[(contact-view-poke %groupify old-rid title.metadata description.metadata)] - :: change associations - =* group-path u.target.act - =/ rid=resource - (de-path:resource group-path) - =/ old-group=group - (need (scry-group:grup old-rid)) - =/ =group - (need (scry-group:grup rid)) - =/ ships=(set ship) - (~(dif in members.old-group) members.group) - =. subscribers.u.book - group-path - =. writers.u.book - group-path - =. books - (~(put by books) [our.bol book.act] u.book) - =/ del - [%edit-book our.bol book.act u.book] - :_ state - :* [%give %fact [/primary]~ %publish-primary-delta !>(del)] - [%give %fact [/notebook/[book.act]]~ %publish-notebook-delta !>(del)] - (metadata-store-poke %remove app-path %publish app-path) - (metadata-store-poke %add group-path [%publish app-path] metadata) - (group-poke %remove-group old-rid ~) - ?. inclusive.act - ~ - :- (group-poke %add-members rid ships) - %+ turn - ~(tap in ships) - |= =ship - =/ =invite - :* our.bol - %contact-hook - group-path - ship '' - == - =/ act=invite-action [%invite /contacts (shaf %msg-uid eny.bol) invite] - [%pass / %agent [our.bol %invite-hook] %poke %invite-action !>(act)] - == - == -:: -++ get-subscribers - |= book=@tas - ^- (set @p) - %+ roll ~(val by sup.bol) - |= [[who=@p pax=path] out=(set @p)] - ^- (set @p) - ?. ?=([%notebook @ ~] pax) out - ?. =(book i.t.pax) out - (~(put in out) who) -:: -++ get-notebook - |= [host=@p book-name=@tas sty=_state] - ^- (unit notebook) - (~(get by books.sty) host book-name) -:: -++ get-unread - |= book=notebook - ^- @ud - %+ roll ~(tap by notes.book) - |= [[nom=@tas not=note] out=@ud] - ?: read.not - out - +(out) -:: -++ emit-updates-and-state - |= [host=@p book-name=@tas book=notebook del=notebook-delta sty=_state] - ^- (quip card _state) - :_ sty(books (~(put by books.sty) [host book-name] book)) - ?: =(our.bol host) - :~ [%give %fact [/notebook/[book-name]]~ %publish-notebook-delta !>(del)] - [%give %fact [/primary]~ %publish-primary-delta !>(del)] - == - [%give %fact [/primary]~ %publish-primary-delta !>(del)]~ -:: -++ metadata-poke - |= act=metadata-action - ^- card - [%pass / %agent [our.bol %metadata-hook] %poke %metadata-action !>(act)] -:: -:: -++ metadata-scry - |= [group-path=path app-path=path] - ^- (unit metadata) - ?. .^(? %gu (scot %p our.bol) %metadata-store (scot %da now.bol) ~) ~ - .^ (unit metadata) - %gx - (scot %p our.bol) - %metadata-store - (scot %da now.bol) - %metadata - (scot %t (spat group-path)) - %publish - (scot %t (spat app-path)) - /noun - == -:: -++ metadata-resource-scry - |= [app=@tas app-path=path] - ^- (unit (set path)) - ?. .^(? %gu (scot %p our.bol) %metadata-store (scot %da now.bol) ~) ~ - .^ (unit (set path)) - %gx - ;: weld - /(scot %p our.bol)/metadata-store/(scot %da now.bol)/resource/[app] - app-path - /noun - == - == -:: -++ emit-metadata - |= del=metadata-delta - ^- (list card) - |^ - ?- -.del - %add - =/ preexisting (metadata-scry group-path.del app-path.del) - =/ meta=metadata - %* . *metadata - title title.del - description desc.del - date-created created.del - creator author.del - == - ?~ preexisting - (add group-path.del app-path.del meta) - =. color.meta color.u.preexisting - (add group-path.del app-path.del meta) - :: - %remove - =/ app-path [(scot %p author.del) /[book.del]] - =/ group-path=(unit path) (group-from-book app-path) - ?~ group-path ~ - [(metadata-poke [%remove u.group-path [%publish app-path]])]~ - == - :: - ++ add - |= [group-path=path app-path=path =metadata] - ^- (list card) - [(metadata-poke [%add group-path [%publish app-path] metadata])]~ - -- -:: -++ group-from-book - |= app-path=path - ^- (unit path) - ?. .^(? %gu (scot %p our.bol) %metadata-store (scot %da now.bol) ~) - ?: ?=([@ ^] app-path) - ~& [%assuming-ported-legacy-publish app-path] - `[%'~' app-path] - ~&([%weird-publish app-path] ~) - =/ resource-indices - .^ (jug md-resource group-path) - %gy - (scot %p our.bol) - %metadata-store - (scot %da now.bol) - /resource-indices - == - =/ groups=(unit (set path)) - (~(get by resource-indices) [%publish app-path]) - ?~ groups ~ - =/ group-paths ~(tap in u.groups) - ?~ group-paths ~ - `i.group-paths -:: -++ metadata-hook-poke - |= act=metadata-hook-action - ^- card - :* %pass / %agent - [our.bol %metadata-hook] - %poke %metadata-hook-action - !>(act) - == -:: -++ handle-notebook-delta - |= [del=notebook-delta sty=_state] - ^- (quip card _state) - ?- -.del - %add-book - ?: =(our.bol host.del) - =^ cards state - (emit-updates-and-state host.del book.del data.del del sty) - :_ state - %- zing - :~ cards - [(metadata-hook-poke [%add-owned writers.data.del])]~ - %- emit-metadata - :* %add - writers.data.del - [(scot %p host.del) /[book.del]] - title.data.del - description.data.del - host.del - date-created.data.del - == - == - =? data.del (~(has by books) host.del book.del) - (merge-notebooks (~(got by books) host.del book.del) data.del) - =^ cards state - (emit-updates-and-state host.del book.del data.del del sty) - =/ rid=resource - (de-path:resource writers.data.del) - =? cards !=(our.bol entity.rid) - :_ cards - (group-pull-hook-poke [%add host.del rid]) - :_ state - :* (metadata-hook-poke [%add-synced host.del writers.data.del]) - cards - == - :: - %add-note - =/ book=(unit notebook) - (get-notebook host.del book.del sty) - ?~ book - [~ sty] - =. read.data.del =(our.bol author.data.del) - =. notes.u.book (~(put by notes.u.book) note.del data.del) - (emit-updates-and-state host.del book.del u.book del sty) - :: - %add-comment - =/ book=(unit notebook) - (get-notebook host.del book.del sty) - ?~ book - [~ sty] - =/ note (~(get by notes.u.book) note.del) - ?~ note - [~ sty] - =/ limbo-comment=(unit @da) - %- ~(rep by comments.u.note) - |= [[date=@da com=comment] out=(unit @da)] - ?: ?& =(author.com author.data.del) - =(content.com content.data.del) - =(%.y pending.com) - == - `date - out - =? comments.u.note ?=(^ limbo-comment) - (~(del by comments.u.note) u.limbo-comment) - =. comments.u.note (~(put by comments.u.note) comment-date.del data.del) - =. notes.u.book (~(put by notes.u.book) note.del u.note) - (emit-updates-and-state host.del book.del u.book del sty) - :: - %edit-book - =/ old-book=(unit notebook) - (get-notebook host.del book.del sty) - ?~ old-book - [~ sty] - =/ new-book=notebook - %= data.del - date-created date-created.u.old-book - notes notes.u.old-book - order order.u.old-book - == - =^ cards state - (emit-updates-and-state host.del book.del new-book del sty) - :_ state - %+ weld cards - %- emit-metadata - :* %add - writers.new-book - [(scot %p host.del) /[book.del]] - title.new-book - description.new-book - host.del - date-created.new-book - == - :: - %edit-note - =. notes.limbo.sty (~(del by notes.limbo.sty) host.del book.del note.del) - =/ book=(unit notebook) - (get-notebook host.del book.del sty) - ?~ book - [~ sty] - =/ old-note (~(get by notes.u.book) note.del) - ?~ old-note - [~ sty] - ?: =(our.bol author.u.old-note) - [~ sty] - =/ new-note=note - %= data.del - date-created date-created.u.old-note - comments comments.u.old-note - read read.u.old-note - == - =. notes.u.book (~(put by notes.u.book) note.del new-note) - (emit-updates-and-state host.del book.del u.book del sty) - :: - %edit-comment - =. comments.limbo.sty - %- ~(del by comments.limbo.sty) - [host.del book.del note.del comment-date.del] - =/ book=(unit notebook) - (get-notebook host.del book.del sty) - ?~ book - [~ sty] - =/ note (~(get by notes.u.book) note.del) - ?~ note - [~ sty] - =/ old-comment (~(get by comments.u.note) comment-date.del) - ?~ old-comment - [~ sty] - =. comments.u.note (~(put by comments.u.note) comment-date.del data.del) - =. notes.u.book (~(put by notes.u.book) note.del u.note) - (emit-updates-and-state host.del book.del u.book del sty) - :: - %del-book - =/ book=(unit notebook) - (get-notebook host.del book.del sty) - ?~ book [~ sty] - :_ sty(books (~(del by books.sty) host.del book.del)) - ?. =(our.bol host.del) - %+ welp - [%give %fact [/primary]~ %publish-primary-delta !>(del)]~ - ?: (is-managed writers.u.book) ~ - [(metadata-hook-poke [%remove writers.u.book])]~ - %- zing - :~ [%give %fact [/notebook/[book.del]]~ %publish-notebook-delta !>(del)]~ - [%give %fact [/primary]~ %publish-primary-delta !>(del)]~ - (emit-metadata %remove host.del book.del) - :: - ?: (is-managed writers.u.book) ~ - [(metadata-hook-poke [%remove writers.u.book])]~ - == - :: - %del-note - =. notes.limbo.sty (~(del by notes.limbo.sty) host.del book.del note.del) - =/ book=(unit notebook) - (get-notebook host.del book.del sty) - ?~ book - [~ sty] - =/ not=(unit note) (~(get by notes.u.book) note.del) - ?~ not - [~ sty] - =. notes.u.book (~(del by notes.u.book) note.del) - (emit-updates-and-state host.del book.del u.book del sty) - :: - %del-comment - =. comments.limbo.sty - %- ~(del by comments.limbo.sty) - [host.del book.del note.del comment.del] - =/ book=(unit notebook) - (get-notebook host.del book.del sty) - ?~ book - [~ sty] - =/ note (~(get by notes.u.book) note.del) - ?~ note - [~ sty] - =. comments.u.note (~(del by comments.u.note) comment.del) - =. notes.u.book (~(put by notes.u.book) note.del u.note) - (emit-updates-and-state host.del book.del u.book del sty) - == -:: -++ get-subscribers-json - |= book=@tas - ^- json - :- %a - %+ roll ~(val by sup.bol) - |= [[who=@p pax=path] out=(list json)] - ^- (list json) - ?. ?=([%notebook @ ~] pax) out - ?. =(book i.t.pax) out - [[%s (scot %p who)] out] -:: -++ get-writers-json - |= [host=@p book=@tas] - =/ =tag - [%publish (cat 3 %writers- book)] - ^- json - =/ writers=(list ship) - ~(tap in (book-writers host book)) - :- %a - %+ turn writers - |= who=@p - ^- json - [%s (scot %p who)] -:: -++ get-notebook-json - |= [host=@p book-name=@tas] - ^- (unit json) - =, enjs:format - =/ book=(unit notebook) (~(get by books) host book-name) - ?~ book - ~ - =/ notebook-json (notebook-full:enjs host book-name u.book) - ?> ?=(%o -.notebook-json) - =. p.notebook-json - (~(uni by p.notebook-json) (notes-page:enjs notes.u.book 0 50)) - =. p.notebook-json - (~(put by p.notebook-json) %subscribers (get-subscribers-json book-name)) - =/ notebooks-json (notebooks-map:enjs our.bol books) - =. p.notebook-json - (~(put by p.notebook-json) %writers (get-writers-json host book-name)) - ?> ?=(%o -.notebooks-json) - =/ host-books-json (~(got by p.notebooks-json) (scot %p host)) - ?> ?=(%o -.host-books-json) - =. p.host-books-json (~(put by p.host-books-json) book-name notebook-json) - =. p.notebooks-json - (~(put by p.notebooks-json) (scot %p host) host-books-json) - `(pairs notebooks+notebooks-json ~) -:: -++ get-note-json - |= [host=@p book-name=@tas note-name=@tas] - ^- (unit json) - =, enjs:format - =/ book=(unit notebook) (~(get by books) host book-name) - ?~ book - ~ - =/ note=(unit note) (~(get by notes.u.book) note-name) - ?~ note - ~ - =/ notebook-json (notebook-full:enjs host book-name u.book) - ?> ?=(%o -.notebook-json) - =/ note-json (note-presentation:enjs u.book note-name u.note) - =. p.notebook-json (~(uni by p.notebook-json) note-json) - =/ notebooks-json (notebooks-map:enjs our.bol books) - ?> ?=(%o -.notebooks-json) - =/ host-books-json (~(got by p.notebooks-json) (scot %p host)) - ?> ?=(%o -.host-books-json) - =. p.host-books-json (~(put by p.host-books-json) book-name notebook-json) - =. p.notebooks-json - (~(put by p.notebooks-json) (scot %p host) host-books-json) - `(pairs notebooks+notebooks-json ~) -:: ++ is-managed |= =path ^- ? ?> ?=(^ path) !=(i.path '~') :: -++ handle-http-request - |= req=inbound-request:eyre - ^- simple-payload:http - =/ url (parse-request-line url.request.req) - ?+ url not-found:gen - :: - :: pagination endpoints - :: - :: all notebooks, short form - [[[~ %json] [%'publish-view' %notebooks ~]] ~] - %- json-response:gen - (notebooks-map:enjs our.bol books) - :: - :: notes pagination - [[[~ %json] [%'publish-view' %notes @ @ @ @ ~]] ~] - =/ host=(unit @p) (slaw %p i.t.t.site.url) - ?~ host - not-found:gen - =/ book-name i.t.t.t.site.url - =/ book=(unit notebook) (~(get by books) u.host book-name) - ?~ book - not-found:gen - =/ start (rush i.t.t.t.t.site.url dem) - ?~ start - not-found:gen - =/ length (rush i.t.t.t.t.t.site.url dem) - ?~ length - not-found:gen - %- json-response:gen - :- %o - (notes-page:enjs notes.u.book u.start u.length) - :: - :: comments pagination - [[[~ %json] [%'publish-view' %comments @ @ @ @ @ ~]] ~] - =/ host=(unit @p) (slaw %p i.t.t.site.url) - ?~ host - not-found:gen - =/ book-name i.t.t.t.site.url - =/ book=(unit notebook) (~(get by books) u.host book-name) - ?~ book - not-found:gen - =/ note-name i.t.t.t.t.site.url - =/ note=(unit note) (~(get by notes.u.book) note-name) - ?~ note - not-found:gen - =/ start (rush i.t.t.t.t.t.site.url dem) - ?~ start - not-found:gen - =/ length (rush i.t.t.t.t.t.t.site.url dem) - ?~ length - not-found:gen - %- json-response:gen - (comments-page:enjs comments.u.note u.start u.length) - :: - :: single notebook with initial 50 notes in short form, as json - [[[~ %json] [%'publish-view' @ @ ~]] ~] - =, enjs:format - =/ host=(unit @p) (slaw %p i.t.site.url) - ?~ host not-found:gen - =/ book-name i.t.t.site.url - =/ book=(unit notebook) (~(get by books) u.host book-name) - ?~ book not-found:gen - =/ notebook-json (notebook-full:enjs u.host book-name u.book) - ?> ?=(%o -.notebook-json) - =. p.notebook-json - (~(uni by p.notebook-json) (notes-page:enjs notes.u.book 0 50)) - =. p.notebook-json - (~(put by p.notebook-json) %subscribers (get-subscribers-json book-name)) - =. p.notebook-json - (~(put by p.notebook-json) %writers (get-writers-json u.host book-name)) - (json-response:gen (pairs notebook+notebook-json ~)) - :: - :: single note, with initial 50 comments, as json - [[[~ %json] [%'publish-view' @ @ @ ~]] ~] - =, enjs:format - =/ host=(unit @p) (slaw %p i.t.site.url) - ?~ host not-found:gen - =/ book-name i.t.t.site.url - =/ book=(unit notebook) (~(get by books) u.host book-name) - ?~ book not-found:gen - =/ note-name i.t.t.t.site.url - =/ note=(unit note) (~(get by notes.u.book) note-name) - ?~ note not-found:gen - =/ jon=json - o+(note-presentation:enjs u.book note-name u.note) - (json-response:gen jon) - == +++ group-poke + |= =update:group-store + ^- card + [%pass / %agent [our.bol %group-store] %poke %group-update !>(update)] :: +++ group-proxy-poke + |= [=ship =update:group-store] + ^- card + [%pass / %agent [ship %group-push-hook] %poke %group-update !>(update)] +++ contact-view-poke + |= act=contact-view-action:contact-view + ^- card + [%pass / %agent [our.bol %contact-view] %poke %contact-view-action !>(act)] +:: +++ contact-view-create + |= [=path ships=(set ship) =policy title=@t description=@t] + =/ rid=resource + (de-path:resource path) + =/ act=contact-view-action:contact-view + [%create name.rid policy title description] + (contact-view-poke act) -- diff --git a/pkg/arvo/lib/publish.hoon b/pkg/arvo/lib/publish.hoon index 2a035aad5a..2db1066ad4 100644 --- a/pkg/arvo/lib/publish.hoon +++ b/pkg/arvo/lib/publish.hoon @@ -1,240 +1,4 @@ /- sur=publish /+ elem-to-react-json ^? -=< [. sur] -=, sur -|% -:: -++ enjs - =, enjs:format - |% - :: - ++ tang - |= tan=^tang - %- wall - %- zing - %+ turn tan - |= a=^tank - (wash [0 80] a) - :: - ++ note-build - |= build=(each manx ^tang) - ^- json - ?: ?=(%.y -.build) - %- pairs - :~ success+b+%.y - result+(elem-to-react-json p.build) - == - %- pairs - :~ success+b+%.n - result+(tang p.build) - == - :: - ++ notebooks-list - |= [our=@p books=(map @tas notebook) subs=(map [@p @tas] notebook)] - ^- json - :- %a - %+ weld - %+ turn ~(tap by books) - |= [name=@tas book=notebook] - (notebook-short book) - %+ turn ~(tap by subs) - |= [[host=@p name=@tas] book=notebook] - (notebook-short book) - :: - ++ notebooks-map - |= [our=@p books=(map [@p @tas] notebook)] - ^- json - =/ notebooks-map=json - %- ~(rep by books) - |= [[[host=@p book-name=@tas] book=notebook] out=json] - ^- json - =/ host-ta (scot %p host) - ?~ out - (frond host-ta (frond book-name (notebook-short book))) - ?> ?=(%o -.out) - =/ books (~(get by p.out) host-ta) - ?~ books - :- %o - (~(put by p.out) host-ta (frond book-name (notebook-short book))) - ?> ?=(%o -.u.books) - =. p.u.books (~(put by p.u.books) book-name (notebook-short book)) - :- %o - (~(put by p.out) host-ta u.books) - =? notebooks-map ?=(~ notebooks-map) - [%o ~] - notebooks-map - :: - ++ notebook-short - |= book=notebook - ^- json - %- pairs - :~ title+s+title.book - date-created+(time date-created.book) - about+s+description.book - num-notes+(numb ~(wyt by notes.book)) - num-unread+(numb (count-unread notes.book)) - comments+b+comments.book - writers-group-path+s+(spat writers.book) - subscribers-group-path+s+(spat subscribers.book) - == - :: - ++ notebook-full - |= [host=@p book-name=@tas book=notebook] - ^- json - %- pairs - :~ title+s+title.book - about+s+description.book - date-created+(time date-created.book) - num-notes+(numb ~(wyt by notes.book)) - num-unread+(numb (count-unread notes.book)) - notes-by-date+(notes-by-date notes.book) - comments+b+comments.book - writers-group-path+s+(spat writers.book) - subscribers-group-path+s+(spat subscribers.book) - == - :: - ++ note-presentation - |= [book=notebook note-name=@tas not=note] - ^- (map @t json) - =/ notes-list=(list [@tas note]) - %+ sort ~(tap by notes.book) - |= [[@tas n1=note] [@tas n2=note]] - (gte date-created.n1 date-created.n2) - =/ idx=@ (need (find [note-name not]~ notes-list)) - =/ next=(unit [name=@tas not=note]) - ?: =(idx 0) ~ - `(snag (dec idx) notes-list) - =/ prev=(unit [name=@tas not=note]) - ?: =(+(idx) (lent notes-list)) ~ - `(snag +(idx) notes-list) - =/ current=json (note-full note-name not) - ?> ?=(%o -.current) - =. p.current (~(put by p.current) %prev-note ?~(prev ~ s+name.u.prev)) - =. p.current (~(put by p.current) %next-note ?~(next ~ s+name.u.next)) - =/ notes=(list [@t json]) [note-name current]~ - =? notes ?=(^ prev) - [[name.u.prev (note-short name.u.prev not.u.prev)] notes] - =? notes ?=(^ next) - [[name.u.next (note-short name.u.next not.u.next)] notes] - %- my - :~ notes+(pairs notes) - notes-by-date+a+(turn notes-list |=([name=@tas *] s+name)) - == - :: - ++ note-full - |= [note-name=@tas =note] - ^- json - %- pairs - :~ note-id+s+note-name - author+s+(scot %p author.note) - title+s+title.note - date-created+(time date-created.note) - snippet+s+snippet.note - file+s+file.note - num-comments+(numb ~(wyt by comments.note)) - comments+(comments-page:enjs comments.note 0 50) - read+b+read.note - pending+b+pending.note - == - :: - ++ notes-by-date - |= notes=(map @tas note) - ^- json - =/ notes-list=(list [@tas note]) - %+ sort ~(tap by notes) - |= [[@tas n1=note] [@tas n2=note]] - (gte date-created.n1 date-created.n2) - :- %a - %+ turn notes-list - |= [name=@tas note] - ^- json - [%s name] - :: - ++ note-short - |= [note-name=@tas =note] - ^- json - %- pairs - :~ note-id+s+note-name - author+s+(scot %p author.note) - title+s+title.note - date-created+(time date-created.note) - num-comments+(numb ~(wyt by comments.note)) - read+b+read.note - snippet+s+snippet.note - pending+b+pending.note - == - :: - ++ notes-page - |= [notes=(map @tas note) start=@ud length=@ud] - ^- (map @t json) - =/ notes-list=(list [@tas note]) - %+ sort ~(tap by notes) - |= [[@tas n1=note] [@tas n2=note]] - (gte date-created.n1 date-created.n2) - %- my - :~ notes-by-date+a+(turn notes-list |=([name=@tas *] s+name)) - notes+o+(^notes-list (scag length (slag start notes-list))) - == - :: - ++ notes-list - |= notes=(list [@tas note]) - ^- (map @t json) - %+ roll notes - |= [[name=@tas not=note] out-map=(map @t json)] - ^- (map @t json) - (~(put by out-map) name (note-short name not)) - :: - ++ comments-page - |= [comments=(map @da ^comment) start=@ud end=@ud] - ^- json - =/ coms=(list [@da ^comment]) - %+ sort ~(tap by comments) - |= [[d1=@da ^comment] [d2=@da ^comment]] - (gte d1 d2) - %- comments-list - (scag end (slag start coms)) - :: - ++ comments-list - |= comments=(list [@da ^comment]) - ^- json - :- %a - (turn comments comment) - :: - ++ comment - |= [date=@da com=^comment] - ^- json - %+ frond - (scot %da date) - %- pairs - :~ author+s+(scot %p author.com) - date-created+(time date-created.com) - content+s+content.com - pending+b+pending.com - == - -- -:: -++ string-to-symbol - |= tap=tape - ^- @tas - %- crip - %+ turn tap - |= a=@ - ?: ?| &((gte a 'a') (lte a 'z')) - &((gte a '0') (lte a '9')) - == - a - ?: &((gte a 'A') (lte a 'Z')) - (add 32 a) - '-' -:: -++ count-unread - |= notes=(map @tas note) - ^- @ud - %- ~(rep by notes) - |= [[key=@tas val=note] count=@ud] - ?: read.val - count - +(count) -:: --- +sur diff --git a/pkg/arvo/mar/publish/action.hoon b/pkg/arvo/mar/publish/action.hoon index 2e5c1ce3c1..f346ed8161 100644 --- a/pkg/arvo/mar/publish/action.hoon +++ b/pkg/arvo/mar/publish/action.hoon @@ -1,6 +1,6 @@ :: :::: /hoon/action/publish/mar - :: + :: tombstoned, now unused /- *publish =, format :: @@ -16,121 +16,5 @@ ++ grab |% ++ noun action - ++ json - |= jon=^json - =, dejs:format - ;; action - |^ %. jon - %- of - :~ new-book+new-book - new-note+new-note - new-comment+new-comment - edit-book+edit-book - edit-note+edit-note - edit-comment+edit-comment - del-book+del-book - del-note+del-note - del-comment+del-comment - subscribe+subscribe - unsubscribe+unsubscribe - read+read - groupify+groupify - == - :: - ++ new-book - %- ot - :~ book+so - title+so - about+so - coms+bo - group+group-info - == - :: - ++ new-note - %- ot - :~ who+(su fed:ag) - book+so - note+so - title+so - body+so - == - :: - ++ new-comment - %- ot - :~ who+(su fed:ag) - book+so - note+so - body+so - == - :: - ++ edit-book - %- ot - :~ book+so - title+so - about+so - coms+bo - group+(mu group-info) - == - :: - ++ edit-note - %- ot - :~ who+(su fed:ag) - book+so - note+so - title+so - body+so - == - :: - ++ edit-comment - %- ot - :~ who+(su fed:ag) - book+so - note+so - comment+so - body+so - == - :: - ++ del-book (ot book+so ~) - :: - ++ del-note (ot who+(su fed:ag) book+so note+so ~) - :: - ++ del-comment - %- ot - :~ who+(su fed:ag) - book+so - note+so - comment+so - == - ++ subscribe - %- ot - :~ who+(su fed:ag) - book+so - == - ++ unsubscribe - %- ot - :~ who+(su fed:ag) - book+so - == - ++ read - %- ot - :~ who+(su fed:ag) - book+so - note+so - == - ++ groupify - %- ot - :~ book+so - target+(mu pa) - inclusive+bo - == - ++ group-info - %- ot - :~ group-path+pa - invitees+set-ship - use-preexisting+bo - make-managed+bo - == - ++ set-ship (as (su fed:ag)) - -- -- -- diff --git a/pkg/arvo/mar/publish/info.hoon b/pkg/arvo/mar/publish/info.hoon index eb8d7cbcc1..92d917b0c1 100644 --- a/pkg/arvo/mar/publish/info.hoon +++ b/pkg/arvo/mar/publish/info.hoon @@ -1,25 +1,12 @@ :: :::: /hoon/info/publish/mar + :: tombstoned, now unused :: /- *publish !: |_ info=notebook-info :: :: -++ grow - |% - ++ mime - :- /text/x-publish-info - (as-octs:mimes:html (of-wain:format txt)) - ++ txt - ^- wain - :~ (cat 3 'title: ' title.info) - (cat 3 'description: ' description.info) - (cat 3 'comments: ' ?:(comments.info 'on' 'off')) - (cat 3 'writers: ' (spat writers.info)) - (cat 3 'subscribers: ' (spat subscribers.info)) - == - -- ++ grab |% ++ mime diff --git a/pkg/arvo/mar/publish/primary-delta.hoon b/pkg/arvo/mar/publish/primary-delta.hoon index d2b01052d3..41b331c080 100644 --- a/pkg/arvo/mar/publish/primary-delta.hoon +++ b/pkg/arvo/mar/publish/primary-delta.hoon @@ -13,73 +13,5 @@ ++ grow |% ++ noun del - ++ json - %+ frond:enjs:format %publish-update - %+ frond:enjs:format -.del - ?- -.del - %add-book - %+ frond:enjs:format (scot %p host.del) - %+ frond:enjs:format book.del - (notebook-short:enjs data.del) - :: - %add-note - %+ frond:enjs:format (scot %p host.del) - %+ frond:enjs:format book.del - (note-full:enjs note.del data.del) - :: - %add-comment - %- pairs:enjs:format - :~ host+s+(scot %p host.del) - book+s+book.del - note+s+note.del - comment+(comment:enjs comment-date.del data.del) - == - :: - %edit-book - %+ frond:enjs:format (scot %p host.del) - %+ frond:enjs:format book.del - (notebook-short:enjs data.del) - :: - %edit-note - %+ frond:enjs:format (scot %p host.del) - %+ frond:enjs:format book.del - (note-full:enjs note.del data.del) - :: - %edit-comment - %- pairs:enjs:format - :~ host+s+(scot %p host.del) - book+s+book.del - note+s+note.del - comment+(comment:enjs comment-date.del data.del) - == - :: - %del-book - %- pairs:enjs:format - :~ host+s+(scot %p host.del) - book+s+book.del - == - :: - %del-note - %- pairs:enjs:format - :~ host+s+(scot %p host.del) - book+s+book.del - note+s+note.del - == - :: - %del-comment - %- pairs:enjs:format - :~ host+s+(scot %p host.del) - book+s+book.del - note+s+note.del - comment+s+(scot %da comment.del) - == - :: - %read - %- pairs:enjs:format - :~ host+s+(scot %p who.del) - book+s+book.del - note+s+note.del - == - == -- -- diff --git a/pkg/interface/src/logic/api/publish.ts b/pkg/interface/src/logic/api/publish.ts deleted file mode 100644 index 27d4bfdadc..0000000000 --- a/pkg/interface/src/logic/api/publish.ts +++ /dev/null @@ -1,224 +0,0 @@ -import BaseApi from './base'; - -import { PublishResponse } from '~/types/publish-response'; -import { PatpNoSig, Path } from '~/types/noun'; -import { BookId, NoteId } from '~/types/publish-update'; - -export default class PublishApi extends BaseApi { - handleEvent(data: PublishResponse) { - this.store.handleEvent({ data: { 'publish-response' : data } }); - } - - fetchNotebooks() { - return fetch('/publish-view/notebooks.json') - .then(response => response.json()) - .then((json) => { - this.handleEvent({ - type: 'notebooks', - data: json - }); - }); - } - - fetchNotebook(host: PatpNoSig, book: BookId) { - return fetch(`/publish-view/${host}/${book}.json`) - .then(response => response.json()) - .then((json) => { - this.handleEvent({ - type: 'notebook', - data: json, - host: host, - notebook: book - }); - }); - } - - fetchNote(host: PatpNoSig, book: BookId, note: NoteId) { - return fetch(`/publish-view/${host}/${book}/${note}.json`) - .then(response => response.json()) - .then((json) => { - this.handleEvent({ - type: 'note', - data: json, - host: host, - notebook: book, - note: note - }); - }); - } - - fetchNotesPage(host: PatpNoSig, book: BookId, start: number, length: number) { - return fetch(`/publish-view/notes/${host}/${book}/${start}/${length}.json`) - .then(response => response.json()) - .then((json) => { - this.handleEvent({ - type: 'notes-page', - data: json, - host: host, - notebook: book, - startIndex: start, - length: length - }); - }); - } - - fetchCommentsPage(host: PatpNoSig, book: BookId, note: NoteId, start: number, length: number) { - return fetch(`/publish-view/comments/${host}/${book}/${note}/${start}/${length}.json`) - .then(response => response.json()) - .then((json) => { - this.handleEvent({ - type: 'comments-page', - data: json, - host: host, - notebook: book, - note: note, - startIndex: start, - length: length - }); - }); - } - - subscribeNotebook(who: PatpNoSig, book: BookId) { - return this.publishAction({ - subscribe: { - who, - book - } - }); - } - - unsubscribeNotebook(who: PatpNoSig, book: BookId) { - return this.publishAction({ - unsubscribe: { - who, - book - } - }); - } - - publishAction(act: any) { - return this.action('publish', 'publish-action', act); - } - - groupify(bookId: string, group: Path | null) { - return this.publishAction({ - groupify: { - book: bookId, - target: group, - inclusive: false - } - }); - } - - - newBook(bookId: string, title: string, description: string, group?: Path) { - const groupInfo = group ? { 'group-path': group, - invitees: [], - 'use-preexisting': true, - 'make-managed': true - } : { - 'group-path': `/ship/~${window.ship}/${bookId}`, - invitees: [], - 'use-preexisting': false, - 'make-managed': false - }; - return this.publishAction({ - "new-book": { - book: bookId, - title: title, - about: description, - coms: true, - group: groupInfo - } - }); - } - - editBook(bookId: string, title: string, description: string, coms: boolean) { - return this.publishAction({ - "edit-book": { - book: bookId, - title: title, - about: description, - coms, - group: null - } - }); - } - - delBook(book: string) { - return this.publishAction({ - "del-book": { - book - } - }); - } - - newNote(who: PatpNoSig, book: string, note: string, title: string, body: string) { - return this.publishAction({ - 'new-note': { - who, - book, - note, - title, - body - } - }); - } - - editNote(who: PatpNoSig, book: string, note: string, title: string, body: string) { - return this.publishAction({ - 'edit-note': { - who, - book, - note, - title, - body - } - }); - } - - delNote(who: PatpNoSig, book: string, note: string) { - return this.publishAction({ - 'del-note': { - who, - book, - note - } - }); - } - - readNote(who: PatpNoSig, book: string, note: string) { - return this.publishAction({ - read: { - who, - book, - note - } - }); - } - - updateComment(who: PatpNoSig, book: string, note: string, comment: Path, body: string) { - return this.publishAction({ - 'edit-comment': { - who, - book, - note, - comment, - body - } - }); - } - - deleteComment(who: PatpNoSig, book: string, note: string, comment: Path ) { - return this.publishAction({ - "del-comment": { - who, - book, - note, - comment - }, - }); - } - -} - diff --git a/pkg/interface/src/logic/reducers/publish-response.ts b/pkg/interface/src/logic/reducers/publish-response.ts deleted file mode 100644 index 073efa3997..0000000000 --- a/pkg/interface/src/logic/reducers/publish-response.ts +++ /dev/null @@ -1,205 +0,0 @@ -import _ from 'lodash'; -import { StoreState } from '../../store/type'; -import { Cage } from '~/types/cage'; - -type PublishState = Pick; - -export default class PublishResponseReducer { - reduce(json: Cage, state: S) { - const data = _.get(json, 'publish-response', false); - if (!data) { return; } - switch(data.type) { - case "notebooks": - this.handleNotebooks(data, state); - break; - case "notebook": - this.handleNotebook(data, state); - break; - case "note": - this.handleNote(data, state); - break; - case "notes-page": - this.handleNotesPage(data, state); - break; - case "comments-page": - this.handleCommentsPage(data, state); - break; - default: - break; - } - } - - handleNotebooks(json, state) { - for (var host in state.notebooks) { - if (json.data[host]) { - for (var book in state.notebooks[host]) { - if (!json.data[host][book]) { - delete state.notebooks[host][book]; - } - } - } else { - delete state.notebooks[host]; - } - } - - for (var host in json.data) { - if (state.notebooks[host]) { - for (var book in json.data[host]) { - if (state.notebooks[host][book]) { - state.notebooks[host][book]["title"] = json.data[host][book]["title"]; - state.notebooks[host][book]["date-created"] = - json.data[host][book]["date-created"]; - state.notebooks[host][book]["num-notes"] = - json.data[host][book]["num-notes"]; - state.notebooks[host][book]["num-unread"] = - json.data[host][book]["num-unread"]; - } else { - state.notebooks[host][book] = json.data[host][book]; - } - } - } else { - state.notebooks[host] = json.data[host]; - } - } - } - - handleNotebook(json, state) { - if (state.notebooks[json.host]) { - if (state.notebooks[json.host][json.notebook]) { - state.notebooks[json.host][json.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].writers = - json.data.notebook.writers; - 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"]; - state.notebooks[json.host][json.notebook].about = - json.data.notebook.about; - if (state.notebooks[json.host][json.notebook].notes) { - for (var key in json.data.notebook.notes) { - let oldNote = state.notebooks[json.host][json.notebook].notes[key]; - if (!(oldNote)) { - state.notebooks[json.host][json.notebook].notes[key] = - json.data.notebook.notes[key]; - } else if (!(oldNote.build)) { - state.notebooks[json.host][json.notebook].notes[key]["author"] = - json.data.notebook.notes[key]["author"]; - state.notebooks[json.host][json.notebook].notes[key]["date-created"] = - json.data.notebook.notes[key]["date-created"]; - state.notebooks[json.host][json.notebook].notes[key]["note-id"] = - json.data.notebook.notes[key]["note-id"]; - state.notebooks[json.host][json.notebook].notes[key]["num-comments"] = - json.data.notebook.notes[key]["num-comments"]; - state.notebooks[json.host][json.notebook].notes[key]["title"] = - json.data.notebook.notes[key]["title"]; - } - } - } else { - state.notebooks[json.host][json.notebook].notes = - json.data.notebook.notes; - } - } else { - state.notebooks[json.host][json.notebook] = json.data.notebook; - } - } else { - state.notebooks[json.host] = {[json.notebook]: json.data.notebook}; - } - } - - handleNote(json, state) { - if (state.notebooks[json.host] && - state.notebooks[json.host][json.notebook]) { - state.notebooks[json.host][json.notebook]["notes-by-date"] = - json.data["notes-by-date"]; - if (state.notebooks[json.host][json.notebook].notes) { - for (var key in json.data.notes) { - let oldNote = state.notebooks[json.host][json.notebook].notes[key]; - if (!(oldNote && oldNote.build && key !== json.note)) { - state.notebooks[json.host][json.notebook].notes[key] = - json.data.notes[key]; - } - } - } else { - state.notebooks[json.host][json.notebook].notes = json.data.notes; - } - } else { - throw Error("tried to fetch note, but we don't have the notebook"); - } - } - - handleNotesPage(json, state) { - if (state.notebooks[json.host] && state.notebooks[json.host][json.notebook]) { - state.notebooks[json.host][json.notebook]["notes-by-date"] = - json.data["notes-by-date"]; - if (state.notebooks[json.host][json.notebook].notes) { - for (var key in json.data.notes) { - let oldNote = state.notebooks[json.host][json.notebook].notes[key]; - if (!(oldNote)) { - state.notebooks[json.host][json.notebook].notes[key] = - json.data.notes[key]; - } else if (!(oldNote.build)) { - state.notebooks[json.host][json.notebook].notes[key]["author"] = - json.data.notes[key]["author"]; - state.notebooks[json.host][json.notebook].notes[key]["date-created"] = - json.data.notes[key]["date-created"]; - state.notebooks[json.host][json.notebook].notes[key]["note-id"] = - json.data.notes[key]["note-id"]; - state.notebooks[json.host][json.notebook].notes[key]["num-comments"] = - json.data.notes[key]["num-comments"]; - state.notebooks[json.host][json.notebook].notes[key]["title"] = - json.data.notes[key]["title"]; - } - } - } else { - state.notebooks[json.host][json.notebook].notes = - json.data.notes; - } - } else { - throw Error("tried to fetch paginated notes, but we don't have the notebook"); - } - } - - handleCommentsPage(json, state) { - if (state.notebooks[json.host] && - state.notebooks[json.host][json.notebook] && - state.notebooks[json.host][json.notebook].notes[json.note]) - { - if (state.notebooks[json.host][json.notebook].notes[json.note].comments) { - json.data.forEach((val, i) => { - let newKey = Object.keys(val)[0]; - let newDate = val[newKey]["date-created"] - let oldComments = state.notebooks[json.host][json.notebook].notes[json.note].comments; - let insertIdx = -1; - - for (var j=0; j newDate) && - (j === oldComments.length-1)){ - insertIdx = j+1; - } - } - if (insertIdx !== -1) { - state.notebooks[json.host][json.notebook].notes[json.note].comments - .splice(insertIdx, 0, val); - } - }); - } else { - state.notebooks[json.host][json.notebook].notes[json.note].comments = - json.data; - } - } else { - throw Error("tried to fetch paginated comments, but we don't have the note"); - } - } -} diff --git a/pkg/interface/src/logic/reducers/publish-update.ts b/pkg/interface/src/logic/reducers/publish-update.ts deleted file mode 100644 index 3be2977372..0000000000 --- a/pkg/interface/src/logic/reducers/publish-update.ts +++ /dev/null @@ -1,269 +0,0 @@ -import _ from 'lodash'; - -import { PublishUpdate } from '~/types/publish-update'; -import { Cage } from '~/types/cage'; -import { StoreState } from '../../store/type'; -import { getTagFromFrond } from '~/types/noun'; - -type PublishState = Pick; - - -export default class PublishUpdateReducer { - reduce(data: Cage, state: S){ - let json = data["publish-update"]; - if(!json) { - return; - } - const tag = getTagFromFrond(json); - switch(tag){ - case "add-book": - this.addBook(json["add-book"], state); - break; - case "add-note": - this.addNote(json["add-note"], state); - break; - case "add-comment": - this.addComment(json["add-comment"], state); - break; - case "edit-book": - this.editBook(json["edit-book"], state); - break; - case "edit-note": - this.editNote(json["edit-note"], state); - break; - case "edit-comment": - this.editComment(json["edit-comment"], state); - break; - case "del-book": - this.delBook(json["del-book"], state); - break; - case "del-note": - this.delNote(json["del-note"], state); - break; - case "del-comment": - this.delComment(json["del-comment"], state); - break; - case "read": - this.read(json["read"], state); - break; - default: - break; - } - } - - addBook(json, state: S) { - let host = Object.keys(json)[0]; - let book = Object.keys(json[host])[0]; - if (state.notebooks[host]) { - state.notebooks[host][book] = json[host][book]; - } else { - state.notebooks[host] = json[host]; - } - } - - addNote(json, state: S) { - let host = Object.keys(json)[0]; - let book = Object.keys(json[host])[0]; - let noteId = json[host][book]["note-id"]; - if (state.notebooks[host] && state.notebooks[host][book]) { - if (state.notebooks[host][book].notes) { - if (state.notebooks[host][book].notes[noteId] && - state.notebooks[host][book].notes[noteId].pending) - { - state.notebooks[host][book].notes[noteId].pending = false; - return; - } - if (state.notebooks[host][book]["notes-by-date"]) { - state.notebooks[host][book]["notes-by-date"].unshift(noteId); - } else { - state.notebooks[host][book]["notes-by-date"] = [noteId]; - } - state.notebooks[host][book].notes[noteId] = json[host][book]; - } else { - state.notebooks[host][book].notes = {[noteId]: json[host][book]}; - } - state.notebooks[host][book]["num-notes"] += 1; - if (!json[host][book].read) { - state.notebooks[host][book]["num-unread"] += 1; - } - let prevNoteId = state.notebooks[host][book]["notes-by-date"][1] || null; - state.notebooks[host][book].notes[noteId]["prev-note"] = prevNoteId - state.notebooks[host][book].notes[noteId]["next-note"] = null; - if (prevNoteId && state.notebooks[host][book].notes[prevNoteId]) { - state.notebooks[host][book].notes[prevNoteId]["next-note"] = noteId; - } - } - } - - addComment(json, state: S) { - let host = json.host - let book = json.book - let note = json.note - let comment = json.comment; - if (state.notebooks[host] && - state.notebooks[host][book] && - state.notebooks[host][book].notes && - state.notebooks[host][book].notes[note]) - { - - if (state.notebooks[host][book].notes[note].comments) { - let limboCommentIdx = - _.findIndex(state.notebooks[host][book].notes[note].comments, (o) => { - let oldVal = o[getTagFromFrond(o)]; - let newVal = comment[Object.keys(comment)[0]]; - return (oldVal.pending && - (oldVal.author === newVal.author) && - (oldVal.content === newVal.content) - ); - }); - if (limboCommentIdx === -1) { - state.notebooks[host][book].notes[note]["num-comments"] += 1; - state.notebooks[host][book].notes[note].comments.unshift(comment); - } else { - state.notebooks[host][book].notes[note].comments[limboCommentIdx] = - comment; - } - } else if (state.notebooks[host][book].notes[note]["num-comments"] === 1) { - state.notebooks[host][book].notes[note]["num-comments"] += 1; - state.notebooks[host][book].notes[note].comments = [comment]; - } - } - } - - editBook(json, state) { - let host = Object.keys(json)[0]; - let book = Object.keys(json[host])[0]; - if (state.notebooks[host] && state.notebooks[host][book]) { - state.notebooks[host][book]["comments"] = json[host][book]["comments"]; - state.notebooks[host][book]["date-created"] = json[host][book]["date-created"]; - state.notebooks[host][book]["num-notes"] = json[host][book]["num-notes"]; - state.notebooks[host][book]["num-unread"] = json[host][book]["num-unread"]; - state.notebooks[host][book]["title"] = json[host][book]["title"]; - state.notebooks[host][book]["writers-group-path"] = - json[host][book]["writers-group-path"]; - state.notebooks[host][book]["subscribers-group-path"] = - json[host][book]["subscribers-group-path"]; - } - } - - editNote(json, state) { - let host = Object.keys(json)[0]; - let book = Object.keys(json[host])[0]; - let noteId = json[host][book]["note-id"]; - let note = json[host][book]; - if (state.notebooks[host] && - state.notebooks[host][book] && - state.notebooks[host][book].notes && - state.notebooks[host][book].notes[noteId]) - { - state.notebooks[host][book].notes[noteId]["author"] = note["author"]; - state.notebooks[host][book].notes[noteId]["build"] = note["build"]; - state.notebooks[host][book].notes[noteId]["file"] = note["file"]; - state.notebooks[host][book].notes[noteId]["title"] = note["title"]; - } - } - - editComment(json, state) { - let host = json.host - let book = json.book - let note = json.note - let comment = json.comment; - let commentId = Object.keys(comment)[0] - if (state.notebooks[host] && - state.notebooks[host][book] && - state.notebooks[host][book].notes && - state.notebooks[host][book].notes[note] && - state.notebooks[host][book].notes[note].comments) - { - let keys = state.notebooks[host][book].notes[note].comments.map((com) => { - return Object.keys(com)[0]; - }); - let index = keys.indexOf(commentId); - if (index > -1) { - state.notebooks[host][book].notes[note].comments[index] = comment; - } - } - } - - delBook(json, state) { - let host = json.host; - let book = json.book; - if (state.notebooks[host]) { - if (state.notebooks[host][book]) { - delete state.notebooks[host][book]; - } - if (Object.keys(state.notebooks[host]).length === 0) { - delete state.notebooks[host]; - } - } - } - - delNote(json, state) { - let host = json.host; - let book = json.book; - let note = json.note; - if (state.notebooks[host] && - state.notebooks[host][book] && - state.notebooks[host][book].notes) - { - if (state.notebooks[host][book].notes[note]) { - state.notebooks[host][book]["num-notes"] -= 1; - if (!state.notebooks[host][book].notes[note].read) { - state.notebooks[host][book]["num-unread"] -= 1; - } - - delete state.notebooks[host][book].notes[note]; - let index = state.notebooks[host][book]["notes-by-date"].indexOf(note); - if (index > -1) { - state.notebooks[host][book]["notes-by-date"].splice(index, 1); - } - - } - if (Object.keys(state.notebooks[host][book].notes).length === 0) { - delete state.notebooks[host][book].notes; - delete state.notebooks[host][book]["notes-by-date"]; - } - } - } - - delComment(json, state) { - let host = json.host - let book = json.book - let note = json.note - let comment = json.comment; - if (state.notebooks[host] && - state.notebooks[host][book] && - state.notebooks[host][book].notes && - state.notebooks[host][book].notes[note]) - { - state.notebooks[host][book].notes[note]["num-comments"] -= 1; - if (state.notebooks[host][book].notes[note].comments) { - let keys = state.notebooks[host][book].notes[note].comments.map((com) => { - return Object.keys(com)[0]; - }); - - let index = keys.indexOf(comment); - if (index > -1) { - state.notebooks[host][book].notes[note].comments.splice(index, 1); - } - } - } - } - - read(json, state){ - let host = json.host; - let book = json.book; - let noteId = json.note - if (state.notebooks[host] && - state.notebooks[host][book] && - state.notebooks[host][book].notes && - state.notebooks[host][book].notes[noteId]) - { - if (!state.notebooks[host][book].notes[noteId]["read"]) { - state.notebooks[host][book].notes[noteId]["read"] = true; - state.notebooks[host][book]["num-unread"] -= 1; - } - } - } - -} diff --git a/pkg/interface/src/logic/store/store.ts b/pkg/interface/src/logic/store/store.ts index 14f523e31b..504a1cf6d0 100644 --- a/pkg/interface/src/logic/store/store.ts +++ b/pkg/interface/src/logic/store/store.ts @@ -10,8 +10,6 @@ import ContactReducer from '../reducers/contact-update'; import S3Reducer from '../reducers/s3-update'; import { GraphReducer } from '../reducers/graph-update'; import GroupReducer from '../reducers/group-update'; -import PublishUpdateReducer from '../reducers/publish-update'; -import PublishResponseReducer from '../reducers/publish-response'; import LaunchReducer from '../reducers/launch-update'; import ConnectionReducer from '../reducers/connection'; @@ -37,8 +35,6 @@ export default class GlobalStore extends BaseStore { contactReducer = new ContactReducer(); s3Reducer = new S3Reducer(); groupReducer = new GroupReducer(); - publishUpdateReducer = new PublishUpdateReducer(); - publishResponseReducer = new PublishResponseReducer(); launchReducer = new LaunchReducer(); connReducer = new ConnectionReducer(); diff --git a/pkg/interface/src/logic/store/type.ts b/pkg/interface/src/logic/store/type.ts index a18336b5fb..de3f51e72d 100644 --- a/pkg/interface/src/logic/store/type.ts +++ b/pkg/interface/src/logic/store/type.ts @@ -4,14 +4,12 @@ import { Path } from '~/types/noun'; import { Invites } from '~/types/invite-update'; import { Associations } from '~/types/metadata-update'; import { Rolodex } from '~/types/contact-update'; -import { Notebooks } from '~/types/publish-update'; import { Groups } from '~/types/group-update'; import { S3State } from '~/types/s3-update'; import { LaunchState, WeatherState } from '~/types/launch-update'; import { ConnectionStatus } from '~/types/connection'; import {Graphs} from '~/types/graph-update'; import { BackgroundConfig, LocalUpdateRemoteContentPolicy } from '~/types/local-update'; -import {Graphs} from '~/types/graph-update'; export interface StoreState { // local state diff --git a/pkg/interface/src/types/publish-response.ts b/pkg/interface/src/types/publish-response.ts deleted file mode 100644 index addc559211..0000000000 --- a/pkg/interface/src/types/publish-response.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Notebooks, Notebook, Note, BookId, NoteId } from './publish-update'; -import { Patp } from './noun'; - -export type PublishResponse = - NotebooksResponse -| NotebookResponse -| NoteResponse -| NotesPageResponse -| CommentsPageResponse; - -interface NotebooksResponse { - type: 'notebooks'; - data: Notebooks; -} - -interface NotebookResponse { - type: 'notebook'; - data: Notebook; - host: Patp; - notebook: BookId; -} - -interface NoteResponse { - type: 'note'; - data: Note; - host: Patp; - notebook: BookId; - note: NoteId; -} - -interface NotesPageResponse { - type: 'notes-page'; - data: Note[]; - host: Patp; - notebook: BookId; - startIndex: number; - length: number; -} - -interface CommentsPageResponse { - type: 'comments-page'; - data: Comment[]; - host: Patp; - notebook: BookId; - note: NoteId; - startIndex: number; - length: number; -} diff --git a/pkg/interface/src/types/publish-update.ts b/pkg/interface/src/types/publish-update.ts deleted file mode 100644 index f3f1e3d4d6..0000000000 --- a/pkg/interface/src/types/publish-update.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { Patp, PatpNoSig, Path } from './noun'; - - -export type NoteId = string; -export type BookId = string; - - -export type PublishUpdate = - PublishUpdateAddBook -| PublishUpdateAddNote -| PublishUpdateAddComment -| PublishUpdateEditBook -| PublishUpdateEditNote -| PublishUpdateEditComment -| PublishUpdateDelBook -| PublishUpdateDelNote -| PublishUpdateDelComment; - - -type PublishUpdateBook = { - [s in Patp]: { - [b in BookId]: { - title: string; - 'date-created': number; - about: string; - 'num-notes': number; - 'num-unread': number; - comments: boolean; - 'writers-group-path': Path; - 'subscribers-group-path': Path; - }; - }; -} - -type PublishUpdateNote = { - [s in Patp]: { - [b in BookId]: { - 'note-id': NoteId; - author: Patp; - title: string; - 'date-created': string; - snippet: string; - file: string; - 'num-comments': number; - comments: Comment[]; - read: boolean; - pending: boolean; - }; - }; -}; - -interface PublishUpdateAddBook { - 'add-book': PublishUpdateBook; -} - -interface PublishUpdateEditBook { - 'edit-book': PublishUpdateBook; -} - -interface PublishUpdateDelBook { - 'del-book': { - host: Patp; - book: string; - } -} - -interface PublishUpdateAddNote { - 'add-note': PublishUpdateNote; -} - -interface PublishUpdateEditNote { - 'edit-note': PublishUpdateNote; -} - -interface PublishUpdateDelNote { - 'del-note': { - host: Patp; - book: BookId; - note: NoteId; - } -} - -interface PublishUpdateAddComment { - 'add-comment': { - who: Patp; - host: BookId; - note: NoteId; - body: string; - } -} - -interface PublishUpdateEditComment { - 'edit-comment': { - host: Patp; - book: BookId; - note: NoteId; - body: string; - comment: Comment; - } -} - -interface PublishUpdateDelComment { - 'del-comment': { - host: Patp; - book: BookId; - note: NoteId; - comment: string; - } -} - -export type Notebooks = { - [host in Patp]: { - [book in BookId]: Notebook; - } -} - - -export interface Notebook { - about: string; - comments: boolean; - 'date-created': number; - notes: Notes; - 'notes-by-date': NoteId[]; - 'num-notes': number; - 'num-unread': number; - subscribers: PatpNoSig[]; - 'subscribers-group-path': Path; - title: string; - 'writers-group-path': Path; -} - -export type Notes = { - [id in NoteId]: Note; -}; - -export interface Note { - author: Patp; - comments: Comment[]; - 'date-created': number; - file: string; - 'next-note': NoteId | null; - 'note-id': NoteId; - 'num-comments': number; - pending: boolean; - 'prev-note': NoteId | null; - read: boolean; - snippet: string; - title: string; -} - -export interface Comment { - [date: string]: { - author: Patp; - content: string; - 'date-created': number; - pending: boolean; - }; -} From f8fcc161a16b6217a7835754f708ed56977966ea Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Tue, 20 Oct 2020 10:39:14 +1000 Subject: [PATCH 30/82] publish: fix graph-store migration Previously, if a subscriber received the OTA migration before the host, then it would attempt to subscribe to a non-existent resource, and receive a nack, thus unsubscribing the ship from the notebook. To rectify this, the tombstoned publish has been changed to resubscribe in a loop with exponential back off. Once it receives a positive watch-ack (i.e. the host completed the OTA), it will then migrate the notebook to graph-store by adding it to graph-pull-hook --- pkg/arvo/app/publish.hoon | 110 +++++++++++++++++++++++++++++++------- 1 file changed, 92 insertions(+), 18 deletions(-) diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon index f4094a69bf..cdf6731dd1 100644 --- a/pkg/arvo/app/publish.hoon +++ b/pkg/arvo/app/publish.hoon @@ -53,6 +53,15 @@ == == :: ++$ state-four + [state-three migrate=migration-state] +:: +:: $migration-state: resources that are unavailable because their host +:: has not processed the ota, and number of times we've tried to reach +:: the host ++$ migration-state + (map resource @ud) +:: +$ versioned-state $% [%1 state-two] [%2 state-two] @@ -60,7 +69,7 @@ [%4 state-three] [%5 state-three] [%6 state-three] - [%7 state-three] + [%7 state-four] == :: +$ metadata-delta @@ -76,7 +85,7 @@ == -- :: -=| [%7 state-three] +=| [%7 state-four] =* state - %- agent:dbug %+ verb | @@ -228,35 +237,48 @@ == :: %6 - %_ $ - -.p.old-state %7 + =/ [ours=(set [rid=resource nb=notebook]) theirs=(set resource)] + %+ roll ~(tap by books.p.old-state) + |= [[[who=@p book=@tas] nb=notebook] [ours=(set [resource notebook]) theirs=(set resource)]] + ^- [(set [resource notebook]) (set resource)] + =/ =resource + [who book] + ?. =(who our.bol) + ours^(~(put in theirs) resource) + :_ theirs + (~(put in ours) [resource nb]) + :: + %_ $ + p.old-state + :+ %7 +.p.old-state + %- ~(gas by *(map resource @ud)) + (turn ~(tap in theirs) (late 0)) :: cards ;: weld cards - :: move to graph-store - %+ roll ~(tap by books.p.old-state) - |= [[[who=@p book=@tas] nb=notebook] out=(list card)] + :: move our books to graph-store + ^- (list card) + %- zing + %+ turn ~(tap in ours) + |= [rid=resource nb=notebook] ^- (list card) - =/ =resource - [who book] - ?. =(who our.bol) - :_ out - (poke-graph-pull %add who resource) =/ =graph:graph-store (notebook-to-graph nb) - %+ weld out - ^- (list card) :~ %- poke-graph-store :* %0 date-created.nb %add-graph - resource + rid graph `%graph-validator-publish == - (poke-graph-push %add resource) + (poke-graph-push %add rid) == + :: for their books, subscribe to graph-pull-hook, to see if host has migrated + ^- (list card) + (turn ~(tap in theirs) check-host-migrate:main) :: leave all subscriptions + ^- (list card) %+ turn ~(tap in ~(key by wex.bol)) |= [=wire =ship app=term] ^- card @@ -484,8 +506,41 @@ ++ on-watch on-watch:def ++ on-leave on-leave:def ++ on-peek on-peek:def - ++ on-agent on-agent:def - ++ on-arvo on-arvo:def + ++ on-agent + |= [=wire =sign:agent:gall] + ^- (quip card _this) + ?. ?=([%graph-migrate *] wire) + (on-agent:def wire sign) + =/ rid=resource + (de-path:resource t.wire) + ?. ?=(%watch-ack -.sign) + (on-agent:def wire sign) + ?~ p.sign + :: if watch acked successfully, then host has completed OTA, and + :: we are safe to add it to the pull-hook + :_ this(migrate (~(del by migrate) rid)) + ~[(poke-graph-pull:main %add entity.rid rid)] + :: if nacked, then set a exponential backoff and retry + =/ nack-count=@ud + +((~(gut by migrate) rid 0)) + :_ this(migrate (~(put by migrate) rid nack-count)) + :: (bex 19) is roughly 6 days + =/ wakeup=@da + (add now.bol (bex (min 19 nack-count))) + [%pass wire %arvo %b %wait wakeup]~ + :: + ++ on-arvo + |= [=wire =sign-arvo] + ^- (quip card _this) + ?. ?=([%graph-migrate *] wire) + (on-arvo:def wire sign-arvo) + =/ rid=resource + (de-path:resource t.wire) + ?> ?=([%b %wake *] sign-arvo) + ~? ?=(^ error.sign-arvo) + "behn errored in backoff timers, continuing anyway" + :_ this + ~[(check-host-migrate:main rid)] :: ++ on-fail on-fail:def -- @@ -600,4 +655,23 @@ =/ act=contact-view-action:contact-view [%create name.rid policy title description] (contact-view-poke act) +:: +++ check-host-migrate + |= rid=resource + ^- card + =/ res-path + (en-path:resource rid) + =- [%pass graph-migrate+res-path %agent -] + [[entity.rid %graph-push-hook] %watch resource+res-path] +:: + +:: +++ poke-our + |= [app=term =cage] + [%pass / %agent [our.bol app] %poke cage] +:: +++ poke-graph-pull + |= =action:pull-hook + (poke-our %graph-pull-hook pull-hook-action+!>(action)) +:: -- From 803f430c185bf31b2df870ac93efff57f74240e1 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 20 Oct 2020 14:27:54 -0500 Subject: [PATCH 31/82] interface: makes leaving and deleting a publish resource work, fixes store --- pkg/interface/src/logic/store/store.ts | 3 --- .../src/views/landscape/components/ChannelMenu.tsx | 6 ++---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/pkg/interface/src/logic/store/store.ts b/pkg/interface/src/logic/store/store.ts index 504a1cf6d0..f1441bb076 100644 --- a/pkg/interface/src/logic/store/store.ts +++ b/pkg/interface/src/logic/store/store.ts @@ -69,7 +69,6 @@ export default class GlobalStore extends BaseStore { chat: {}, contacts: {}, graph: {}, - publish: {} }, groups: {}, groupKeys: new Set(), @@ -105,8 +104,6 @@ export default class GlobalStore extends BaseStore { this.contactReducer.reduce(data, this.state); this.s3Reducer.reduce(data, this.state); this.groupReducer.reduce(data, this.state); - this.publishUpdateReducer.reduce(data, this.state); - this.publishResponseReducer.reduce(data, this.state); this.launchReducer.reduce(data, this.state); this.connReducer.reduce(data, this.state); GraphReducer(data, this.state); diff --git a/pkg/interface/src/views/landscape/components/ChannelMenu.tsx b/pkg/interface/src/views/landscape/components/ChannelMenu.tsx index d532e67764..6687f32845 100644 --- a/pkg/interface/src/views/landscape/components/ChannelMenu.tsx +++ b/pkg/interface/src/views/landscape/components/ChannelMenu.tsx @@ -51,9 +51,7 @@ export function ChannelMenu(props: ChannelMenuProps) { await api.chat.delete(appPath); break; case "publish": - await api.publish.unsubscribeNotebook(ship.slice(1), name); - await api.publish.fetchNotebooks(); - + await api.graph.leaveGraph(ship, name); break; case "link": await api.graph.leaveGraph(ship, name); @@ -71,7 +69,7 @@ export function ChannelMenu(props: ChannelMenuProps) { await api.chat.delete(appPath); break; case "publish": - await api.publish.delBook(name); + await api.graph.deleteGraph(name); break; case "link": await api.graph.deleteGraph(name); From 96d4c9d1e1e3820b15b8567bafbd1329e2f1e802 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 20 Oct 2020 15:06:44 -0500 Subject: [PATCH 32/82] interface: fix main notebook route and remove update processing --- pkg/interface/src/logic/reducers/graph-update.js | 2 +- .../src/views/apps/publish/components/Notebook.tsx | 2 +- .../views/apps/publish/components/NotebookRoutes.tsx | 12 +++++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pkg/interface/src/logic/reducers/graph-update.js b/pkg/interface/src/logic/reducers/graph-update.js index 00a5153a08..1b2f8cd703 100644 --- a/pkg/interface/src/logic/reducers/graph-update.js +++ b/pkg/interface/src/logic/reducers/graph-update.js @@ -82,7 +82,7 @@ const removeGraph = (json, state) => { if (!('graphs' in state)) { state.graphs = {}; } - let resource = data.resource.ship + '/' + data.resource.name; + let resource = data.ship + '/' + data.name; delete state.graphs[resource]; } }; diff --git a/pkg/interface/src/views/apps/publish/components/Notebook.tsx b/pkg/interface/src/views/apps/publish/components/Notebook.tsx index 6cef04ae11..7c9c4a721d 100644 --- a/pkg/interface/src/views/apps/publish/components/Notebook.tsx +++ b/pkg/interface/src/views/apps/publish/components/Notebook.tsx @@ -82,7 +82,7 @@ export function Notebook(props: NotebookProps & RouteComponentProps) { graph={graph} host={ship} book={book} - contacts={notebookContacts} + contacts={!!notebookContacts ? notebookContacts : {}} hideNicknames={hideNicknames} baseUrl={props.baseUrl} /> diff --git a/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx b/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx index 204c31b90e..ad30091fd5 100644 --- a/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx +++ b/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx @@ -54,9 +54,15 @@ export function NotebookRoutes( { - return ; - }} + render={(routeProps) => ( + + )} /> Date: Tue, 20 Oct 2020 15:15:58 -0500 Subject: [PATCH 33/82] graph-threads: delete thread gets rid of metadata properly --- pkg/arvo/ted/graph/delete.hoon | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/arvo/ted/graph/delete.hoon b/pkg/arvo/ted/graph/delete.hoon index 7e95d492bc..1ffcdd97dc 100644 --- a/pkg/arvo/ted/graph/delete.hoon +++ b/pkg/arvo/ted/graph/delete.hoon @@ -41,6 +41,15 @@ (poke-our %graph-store %graph-update !>([%0 now.bowl %remove-graph rid])) ;< ~ bind:m (poke-our %graph-push-hook %push-hook-action !>([%remove rid])) + ;< ~ bind:m + %+ poke-our %metadata-hook + metadata-hook-action+!>([%remove (en-path:resource rid)]) + ;< ~ bind:m + %+ poke-our %metadata-store + :- %metadata-action + !> :+ %remove + (en-path:resource rid) + [%graph (en-path:resource rid)] (pure:m ~) -- :: From 5b8087b72a5d5dc90b99de230dcf57f92f79710e Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 22 Oct 2020 10:30:04 +1000 Subject: [PATCH 34/82] publish: refine backoff behaviour --- pkg/arvo/app/publish.hoon | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon index cdf6731dd1..5792ad08bf 100644 --- a/pkg/arvo/app/publish.hoon +++ b/pkg/arvo/app/publish.hoon @@ -523,6 +523,9 @@ :: if nacked, then set a exponential backoff and retry =/ nack-count=@ud +((~(gut by migrate) rid 0)) + ?: (gte 24 nack-count) + ~& >>> "failed to migrate notebook {} to graph-store" + [~ this] :_ this(migrate (~(put by migrate) rid nack-count)) :: (bex 19) is roughly 6 days =/ wakeup=@da From 7a4d61fe69e167a1f63531409094098ef516023b Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 22 Oct 2020 10:31:10 +1000 Subject: [PATCH 35/82] publish-js: fix updating posts --- .../src/views/apps/publish/components/EditPost.tsx | 9 ++++++--- .../src/views/apps/publish/components/MarkdownEditor.tsx | 2 +- pkg/interface/src/views/apps/publish/components/Note.tsx | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pkg/interface/src/views/apps/publish/components/EditPost.tsx b/pkg/interface/src/views/apps/publish/components/EditPost.tsx index e781fc6c06..1b5cae127c 100644 --- a/pkg/interface/src/views/apps/publish/components/EditPost.tsx +++ b/pkg/interface/src/views/apps/publish/components/EditPost.tsx @@ -1,9 +1,10 @@ import React from "react"; +import _ from 'lodash'; import { PostFormSchema, PostForm } from "./NoteForm"; import { FormikHelpers } from "formik"; import GlobalApi from "~/logic/api/global"; -import { RouteComponentProps } from "react-router-dom"; -import { GraphNode, TextContent } from "~/types"; +import { RouteComponentProps, useLocation } from "react-router-dom"; +import { GraphNode, TextContent, Association } from "~/types"; import { getLatestRevision, editPost } from "~/logic/lib/publish"; import {useWaitForProps} from "~/logic/lib/useWaitForProps"; interface EditPostProps { @@ -17,6 +18,7 @@ interface EditPostProps { export function EditPost(props: EditPostProps & RouteComponentProps) { const { note, book, noteId, api, ship, history } = props; const [revNum, title, body] = getLatestRevision(note); + const location = useLocation(); const waiter = useWaitForProps(props); const initial: PostFormSchema = { @@ -37,7 +39,8 @@ export function EditPost(props: EditPostProps & RouteComponentProps) { const [rev] = getLatestRevision(p.note); return rev === newRev; }); - history.push(`/~publish/notebook/ship/${ship}/${book}/note/${noteId}`); + const noteUrl = _.dropRight(location.pathname.split('/'), 1).join('/'); + history.push(noteUrl); } catch (e) { console.error(e); actions.setStatus({ error: "Failed to edit notebook" }); diff --git a/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx b/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx index c31b62913a..ab231b5064 100644 --- a/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx +++ b/pkg/interface/src/views/apps/publish/components/MarkdownEditor.tsx @@ -53,7 +53,7 @@ export function MarkdownEditor( return ( - + Date: Sat, 24 Oct 2020 11:45:21 +0200 Subject: [PATCH 36/82] eyre: ignore facts directly after clog When an application would send multiple facts during a single event, it was possible for the first fact to trigger a clog, removing the subscription and sending a quit, but then the second fact still getting sent out at normal. Here, we drop any facts for subscriptions we don't have registered in state, which should only happen in the described case. --- pkg/arvo/sys/vane/eyre.hoon | 9 +++++++++ pkg/arvo/tests/sys/vane/eyre.hoon | 22 ++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/pkg/arvo/sys/vane/eyre.hoon b/pkg/arvo/sys/vane/eyre.hoon index 8744d142a2..386cdd4251 100644 --- a/pkg/arvo/sys/vane/eyre.hoon +++ b/pkg/arvo/sys/vane/eyre.hoon @@ -1500,6 +1500,15 @@ ?~ channel :_ state :_ ~ [duct %pass /flog %d %flog %crud %eyre-no-channel >id=channel-id< ~] + :: it's possible that this is a fact emitted directly alongside a fact + :: that triggered a clog & closed the subscription. in that case, just + :: drop the fact. + :: + ?: ?& ?=(%fact -.sign) + !(~(has by subscriptions.u.channel) request-id) + == + ~& [%e %fact-without-subscription channel-id request-id] + [~ state] :: attempt to convert the sign to json. :: if conversion succeeds, we *can* send it. if the client is actually :: connected, we *will* send it immediately. diff --git a/pkg/arvo/tests/sys/vane/eyre.hoon b/pkg/arvo/tests/sys/vane/eyre.hoon index b5323038fe..0efc360c4f 100644 --- a/pkg/arvo/tests/sys/vane/eyre.hoon +++ b/pkg/arvo/tests/sys/vane/eyre.hoon @@ -1875,7 +1875,7 @@ loop-fact(cur +(cur)) :: the next subscription result should trigger a clog :: - =^ results eyre-gate + =^ results1 eyre-gate %: eyre-take eyre-gate now @@ -1922,7 +1922,25 @@ == == == - results + :: subsequent subscription updates, which might have gotten sent out during + :: the same event in which a clog triggered, should be silently ignored + :: + =^ results2 eyre-gate + %: eyre-take + eyre-gate + now + scry=scry-provides-code + ^= take-args + :* wire=/channel/subscription/'0123456789abcdef'/'1'/~nul/two + duct=~[/http-put-request] + ^- (hypo sign:eyre-gate) + :- *type + [%g %unto %fact %json !>(`json`[%a [%n '1'] ~])] + == + ^= moves + ~ + == + (weld results1 results2) :: ++ test-born-sends-pending-cancels :: From 93475aa7560d4eea36273481f107258c5f0141b8 Mon Sep 17 00:00:00 2001 From: fang Date: Mon, 26 Oct 2020 15:52:39 +0100 Subject: [PATCH 37/82] eyre: remove fact-without-subscription printf --- pkg/arvo/sys/vane/eyre.hoon | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/arvo/sys/vane/eyre.hoon b/pkg/arvo/sys/vane/eyre.hoon index 386cdd4251..cd6bb219c8 100644 --- a/pkg/arvo/sys/vane/eyre.hoon +++ b/pkg/arvo/sys/vane/eyre.hoon @@ -1507,7 +1507,6 @@ ?: ?& ?=(%fact -.sign) !(~(has by subscriptions.u.channel) request-id) == - ~& [%e %fact-without-subscription channel-id request-id] [~ state] :: attempt to convert the sign to json. :: if conversion succeeds, we *can* send it. if the client is actually From 047b11a4451875233cfd3b1f5b0a1e85c75708f4 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Tue, 27 Oct 2020 21:24:25 -0400 Subject: [PATCH 38/82] landscape: 'home' workspace in switcher Fixes #3805. --- .../views/landscape/components/GroupSwitcher.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx b/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx index 004ab97b39..ecfe50f387 100644 --- a/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx +++ b/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx @@ -102,7 +102,8 @@ export function GroupSwitcher(props: { width="100%" alignItems="stretch" > - + {(props.baseUrl === '/~landscape/home') ? + All Groups + : + + + Home + } Date: Tue, 27 Oct 2020 21:43:58 -0400 Subject: [PATCH 39/82] links: add link deletion Fixes #3045. --- pkg/interface/src/views/apps/links/LinkResource.tsx | 1 + pkg/interface/src/views/apps/links/components/link-item.js | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/interface/src/views/apps/links/LinkResource.tsx b/pkg/interface/src/views/apps/links/LinkResource.tsx index 571f9b2d18..e2158d5774 100644 --- a/pkg/interface/src/views/apps/links/LinkResource.tsx +++ b/pkg/interface/src/views/apps/links/LinkResource.tsx @@ -83,6 +83,7 @@ export function LinkResource(props: LinkResourceProps) { hideNicknames={hideNicknames} baseUrl={resourceUrl} color={uxToHex(contact?.color || '0x0')} + api={api} /> ); })} diff --git a/pkg/interface/src/views/apps/links/components/link-item.js b/pkg/interface/src/views/apps/links/components/link-item.js index 068d69cdc2..a803b8d984 100644 --- a/pkg/interface/src/views/apps/links/components/link-item.js +++ b/pkg/interface/src/views/apps/links/components/link-item.js @@ -13,7 +13,8 @@ export const LinkItem = (props) => { avatar, resource, hideAvatars, - hideNicknames + hideNicknames, + api } = props; const URLparser = new RegExp( @@ -35,6 +36,8 @@ export const LinkItem = (props) => { const baseUrl = props.baseUrl || `/~404/${resource}`; + const [ship, name] = resource.split("/"); + return ( {img} @@ -58,6 +61,7 @@ export const LinkItem = (props) => { {size} comments + {(author === window.ship) && api.graph.removeNodes(`~${ship}`, name, [node.post.index])}>Delete} From e1bbb25c6c5f2e335fc793190209e278613ef200 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Tue, 27 Oct 2020 21:56:51 -0400 Subject: [PATCH 40/82] links: use admin role for deletion check --- pkg/interface/src/views/apps/links/LinkResource.tsx | 2 ++ .../src/views/apps/links/components/link-item.js | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/interface/src/views/apps/links/LinkResource.tsx b/pkg/interface/src/views/apps/links/LinkResource.tsx index e2158d5774..8704bbcbfb 100644 --- a/pkg/interface/src/views/apps/links/LinkResource.tsx +++ b/pkg/interface/src/views/apps/links/LinkResource.tsx @@ -48,6 +48,7 @@ export function LinkResource(props: LinkResourceProps) { ? associations.graph[appPath] : { metadata: {} }; const contactDetails = contacts[resource["group-path"]] || {}; + const group = groups[resource["group-path"]] || {}; const graph = graphs[resourcePath] || null; useEffect(() => { @@ -83,6 +84,7 @@ export function LinkResource(props: LinkResourceProps) { hideNicknames={hideNicknames} baseUrl={resourceUrl} color={uxToHex(contact?.color || '0x0')} + group={group} api={api} /> ); diff --git a/pkg/interface/src/views/apps/links/components/link-item.js b/pkg/interface/src/views/apps/links/components/link-item.js index a803b8d984..0d7d777f0e 100644 --- a/pkg/interface/src/views/apps/links/components/link-item.js +++ b/pkg/interface/src/views/apps/links/components/link-item.js @@ -5,6 +5,8 @@ import { Sigil } from '~/logic/lib/sigil'; import { Link } from 'react-router-dom'; import { cite } from '~/logic/lib/util'; +import { roleForShip } from "~/logic/lib/group"; + export const LinkItem = (props) => { const { node, @@ -14,7 +16,8 @@ export const LinkItem = (props) => { resource, hideAvatars, hideNicknames, - api + api, + group } = props; const URLparser = new RegExp( @@ -36,6 +39,7 @@ export const LinkItem = (props) => { const baseUrl = props.baseUrl || `/~404/${resource}`; + const ourRole = group ? roleForShip(group, window.ship) : undefined; const [ship, name] = resource.split("/"); return ( @@ -61,7 +65,7 @@ export const LinkItem = (props) => { {size} comments - {(author === window.ship) && api.graph.removeNodes(`~${ship}`, name, [node.post.index])}>Delete} + {(ourRole === "admin") && api.graph.removeNodes(`~${ship}`, name, [node.post.index])}>Delete} From 3b5d3c6ff6a288cbf0d659cdb84183baa3bd8d6e Mon Sep 17 00:00:00 2001 From: Tyler Brown Cifu Shuster Date: Mon, 26 Oct 2020 16:59:14 -0700 Subject: [PATCH 41/82] chat: fixed embedded scroll item disability Fixes #3801 --- .../src/views/components/VirtualScroller.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/interface/src/views/components/VirtualScroller.tsx b/pkg/interface/src/views/components/VirtualScroller.tsx index 6410fa6140..ead595f594 100644 --- a/pkg/interface/src/views/components/VirtualScroller.tsx +++ b/pkg/interface/src/views/components/VirtualScroller.tsx @@ -244,7 +244,17 @@ export default class VirtualScroller extends PureComponent { event.preventDefault(); const normalized = normalizeWheel(event); - element.scrollBy(0, normalized.pixelY * -1); + if ( + (event.target.scrollHeight > event.target.clientHeight && event.target.clientHeight > 0) // If we're scrolling something with a scrollbar + && ( + (event.target.scrollTop > 0 && event.deltaY < 0) // Either we're not at the top and scrolling up + || (event.target.scrollTop < event.target.scrollHeight - event.target.clientHeight && event.deltaY > 0) // Or we're not at the bottom and scrolling down + ) + ) { + event.target.scrollBy(0, normalized.pixelY); + } else { + element.scrollBy(0, normalized.pixelY * -1); + } return false; }, { passive: false }); window.addEventListener('keydown', this.invertedKeyHandler, { passive: false }); From e357a365775924010b7327e216b00a1618198446 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Wed, 28 Oct 2020 13:42:44 -0500 Subject: [PATCH 42/82] graph-create: fix creation in edge cases --- pkg/arvo/ted/graph/create.hoon | 4 +-- .../views/landscape/components/NewChannel.tsx | 29 ++++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/pkg/arvo/ted/graph/create.hoon b/pkg/arvo/ted/graph/create.hoon index 6af8d306d8..2871218327 100644 --- a/pkg/arvo/ted/graph/create.hoon +++ b/pkg/arvo/ted/graph/create.hoon @@ -70,10 +70,10 @@ :: :: Send invites :: -?: ?=(%group -.associated) +?: ?=(%group -.associated.action) (pure:m !>(~)) ?- -.policy.associated.action - %group (pure:m !>(~)) + %open (pure:m !>(~)) %invite =/ inv-action=action:inv :^ %invites %graph (shaf %graph-uid eny.bowl) diff --git a/pkg/interface/src/views/landscape/components/NewChannel.tsx b/pkg/interface/src/views/landscape/components/NewChannel.tsx index 509a2a43d5..1ebcfc6eea 100644 --- a/pkg/interface/src/views/landscape/components/NewChannel.tsx +++ b/pkg/interface/src/views/landscape/components/NewChannel.tsx @@ -24,14 +24,14 @@ interface FormSchema { name: string; description: string; ships: string[]; - type: "chat" | "publish" | "link"; + moduleType: "chat" | "publish" | "link"; } const formSchema = Yup.object({ name: Yup.string().required('Channel must have a name'), description: Yup.string(), ships: Yup.array(Yup.string()), - type: Yup.string().required('Must choose channel type') + moduleType: Yup.string().required('Must choose channel type') }); interface NewChannelProps { @@ -51,8 +51,8 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { const onSubmit = async (values: FormSchema, actions) => { const resId: string = stringToSymbol(values.name); try { - const { name, description, type, ships } = values; - switch (type) { + const { name, description, moduleType, ships } = values; + switch (moduleType) { case 'chat': const appPath = `/~${window.ship}/${resId}`; const groupPath = group || `/ship${appPath}`; @@ -69,15 +69,14 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { ); break; case "publish": - case "links": - const module = type === 'links' ? 'link' : type; + case "link": if (group) { await api.graph.createManagedGraph( resId, name, description, group, - module + moduleType ); } else { await api.graph.createUnmanagedGraph( @@ -85,11 +84,10 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { name, description, { invite: { pending: ships.map((s) => `~${s}`) } }, - module, + moduleType ); } break; - default: console.log('fallthrough'); } @@ -99,7 +97,10 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { } actions.setStatus({ success: null }); const resourceUrl = parentPath(location.pathname); - history.push(`${resourceUrl}/resource/${type}${type === 'link' ? '/ship' : ''}/~${window.ship}/${resId}`); + history.push( + `${resourceUrl}/resource/${moduleType}` + + `${moduleType !== 'chat' ? '/ship' : ''}/~${window.ship}/${resId}` + ); } catch (e) { console.error(e); actions.setStatus({ error: 'Channel creation failed' }); @@ -113,7 +114,7 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) { Channel Type - - - + + + Date: Wed, 28 Oct 2020 14:37:26 -0500 Subject: [PATCH 43/82] graph-store: if null timestamp, replace with now --- pkg/arvo/app/graph-store.hoon | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/arvo/app/graph-store.hoon b/pkg/arvo/app/graph-store.hoon index 37712dde37..5794e5659a 100644 --- a/pkg/arvo/app/graph-store.hoon +++ b/pkg/arvo/app/graph-store.hoon @@ -68,6 +68,7 @@ ^- (quip card _state) |^ ?> ?=(%0 -.update) + =? p.update =(p.update *time) now.bowl ?- -.q.update %add-graph (add-graph +.q.update) %remove-graph (remove-graph +.q.update) From db457b504a35261089c9fe0de6edd28320a95c3a Mon Sep 17 00:00:00 2001 From: Tyler Brown Cifu Shuster Date: Thu, 22 Oct 2020 13:39:08 -0700 Subject: [PATCH 44/82] launch: fluid layout fixes https://github.com/urbit/landscape/issues/107 --- pkg/interface/src/views/apps/launch/app.js | 10 +++------ .../views/apps/launch/components/Groups.tsx | 14 ++---------- .../apps/launch/components/tiles/tile.js | 22 ++++++++++++++++--- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/pkg/interface/src/views/apps/launch/app.js b/pkg/interface/src/views/apps/launch/app.js index a0ba6285f7..cc234d231b 100644 --- a/pkg/interface/src/views/apps/launch/app.js +++ b/pkg/interface/src/views/apps/launch/app.js @@ -37,8 +37,7 @@ export default class LaunchApp extends React.Component { @@ -75,12 +74,9 @@ export default class LaunchApp extends React.Component { location={props.userLocation} weather={props.weather} /> + + - [0]) { }; return ( - + <> {incomingGroups.map((invite) => ( [0]) { {group.metadata.title} ))} - + ); } diff --git a/pkg/interface/src/views/apps/launch/components/tiles/tile.js b/pkg/interface/src/views/apps/launch/components/tiles/tile.js index 3d6d8d966e..64124351fd 100644 --- a/pkg/interface/src/views/apps/launch/components/tiles/tile.js +++ b/pkg/interface/src/views/apps/launch/components/tiles/tile.js @@ -1,8 +1,24 @@ import React from 'react'; import { Link } from 'react-router-dom'; +import styled from 'styled-components'; + import defaultApps from '~/logic/lib/default-apps'; -import { Box, DisclosureBox } from "@tlon/indigo-react"; +import { Box } from "@tlon/indigo-react"; + +const SquareBox = styled(Box)` + &::before { + content: ""; + display: inline-block; + width: 1px; + height: 0; + padding-bottom: 100%; + } + & > * { + position: absolute; + top: 0; + } +`; const routeList = defaultApps.map(a => `/~${a}`); export default class Tile extends React.Component { @@ -26,7 +42,7 @@ export default class Tile extends React.Component { return ( - {childElement} - + ); } } From 358af000ecf8e5d3bffbe3207f8283a520372ba5 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Wed, 28 Oct 2020 21:54:02 +0000 Subject: [PATCH 45/82] invite-store: fix %graph %create --- pkg/arvo/app/invite-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/app/invite-store.hoon b/pkg/arvo/app/invite-store.hoon index 2ca51f6f05..258f61a4c6 100644 --- a/pkg/arvo/app/invite-store.hoon +++ b/pkg/arvo/app/invite-store.hoon @@ -45,7 +45,7 @@ =/ old !<(versioned-state old-vase) ?: ?=(%1 -.old) `this(state old) - :- =- [%pass / %agent [our.bowl %invite-store] %poke %invite-action !>(-)]~ + :- =- [%pass / %agent [our.bowl %invite-store] %poke %invite-action -]~ !> ^- action:store [%create %graph] %= this From e86a87086859b12561e0ecd072d2a056b5c97c5a Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 09:21:54 +1000 Subject: [PATCH 46/82] lib/util: remove _.chain --- pkg/interface/src/logic/lib/util.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/interface/src/logic/lib/util.js b/pkg/interface/src/logic/lib/util.js index 63afc42a8b..9fda4c6e8e 100644 --- a/pkg/interface/src/logic/lib/util.js +++ b/pkg/interface/src/logic/lib/util.js @@ -298,11 +298,12 @@ export function scrollIsAtBottom(container) { * Formats a numbers as a `@ud` inserting dot where needed */ export function numToUd(num) { - return _.chain(num.toString()) - .split('') - .reverse() - .chunk(3) - .reverse() - .map(s => s.join('')).join('.') - .value(); + return f.flow( + f.split(''), + f.reverse, + f.chunk(3), + f.reverse, + f.map(s => s.join('')), + f.join('.') + )(num.toString()) } From 7e61bb9a6d9bf7c54e64f2670185bde1e359488c Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 28 Oct 2020 19:25:57 -0400 Subject: [PATCH 47/82] glob: update to 0v5.d2ut4.t1kqo.2mdr9.mnc4i.vgb53 --- pkg/arvo/app/glob.hoon | 2 +- pkg/arvo/app/landscape/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/arvo/app/glob.hoon b/pkg/arvo/app/glob.hoon index c031faa9bd..3239920c03 100644 --- a/pkg/arvo/app/glob.hoon +++ b/pkg/arvo/app/glob.hoon @@ -5,7 +5,7 @@ /- glob /+ default-agent, verb, dbug |% -++ hash 0v2.1vtfh.0l23v.30s7f.n57l9.dpjvi +++ hash 0v5.d2ut4.t1kqo.2mdr9.mnc4i.vgb53 +$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))] +$ all-states $% state-0 diff --git a/pkg/arvo/app/landscape/index.html b/pkg/arvo/app/landscape/index.html index cab16a4502..a79317788e 100644 --- a/pkg/arvo/app/landscape/index.html +++ b/pkg/arvo/app/landscape/index.html @@ -24,6 +24,6 @@
- + From 4079de05f6bf48bc3af02cb0cba7189d77629776 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 09:32:15 +1000 Subject: [PATCH 48/82] publish: fix graph-validator mark --- pkg/arvo/mar/graph/validator/publish.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/mar/graph/validator/publish.hoon b/pkg/arvo/mar/graph/validator/publish.hoon index 2b11cfdc9a..56ad626961 100644 --- a/pkg/arvo/mar/graph/validator/publish.hoon +++ b/pkg/arvo/mar/graph/validator/publish.hoon @@ -24,7 +24,7 @@ [@ %1 @ ~] ?> ?=([* * ~] contents.p.ip) ?> ?=(%text -.i.contents.p.ip) - ?> ?=(%text -.t.i.contents.p.ip) + ?> ?=(%text -.i.t.contents.p.ip) ip :: container for comments [@ %2 ~] From aabd890b6df9438585457088865d00524225ab1a Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 09:34:15 +1000 Subject: [PATCH 49/82] publish: fix backoff --- pkg/arvo/app/publish.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon index 330fc732c6..d70c8c7d32 100644 --- a/pkg/arvo/app/publish.hoon +++ b/pkg/arvo/app/publish.hoon @@ -523,7 +523,7 @@ :: if nacked, then set a exponential backoff and retry =/ nack-count=@ud +((~(gut by migrate) rid 0)) - ?: (gte 24 nack-count) + ?: (gte nack-count 24) ~& >>> "failed to migrate notebook {} to graph-store" [~ this] :_ this(migrate (~(put by migrate) rid nack-count)) From ec1c4339c5a091f63f15f65f6fb3b313fa6d28e2 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 09:35:43 +1000 Subject: [PATCH 50/82] publish: loosen validator --- pkg/arvo/mar/graph/validator/publish.hoon | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/arvo/mar/graph/validator/publish.hoon b/pkg/arvo/mar/graph/validator/publish.hoon index 56ad626961..64023f9a48 100644 --- a/pkg/arvo/mar/graph/validator/publish.hoon +++ b/pkg/arvo/mar/graph/validator/publish.hoon @@ -22,9 +22,8 @@ :: revisions are numbered by the revision count :: starting at one [@ %1 @ ~] - ?> ?=([* * ~] contents.p.ip) + ?> ?=([* * *] contents.p.ip) ?> ?=(%text -.i.contents.p.ip) - ?> ?=(%text -.i.t.contents.p.ip) ip :: container for comments [@ %2 ~] From 410d193f5aa604d24c0ae2be16b9345b4d253b99 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 09:37:47 +1000 Subject: [PATCH 51/82] publish: loosen comment validation --- pkg/arvo/mar/graph/validator/publish.hoon | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/arvo/mar/graph/validator/publish.hoon b/pkg/arvo/mar/graph/validator/publish.hoon index 64023f9a48..7688b4a281 100644 --- a/pkg/arvo/mar/graph/validator/publish.hoon +++ b/pkg/arvo/mar/graph/validator/publish.hoon @@ -31,8 +31,7 @@ ip :: comment [@ %2 @ ~] - ?> ?=([* ~] contents.p.ip) - ?> ?=(%text -.i.contents.p.ip) + ?> ?=(^ contents.p.ip) ip :: top level post must have no content [@ ~] From d5683fb6314dd43f31fde1974fa2d352ffdd031f Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 09:49:05 +1000 Subject: [PATCH 52/82] publish: fix time handling in publish --- pkg/arvo/app/publish.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon index d70c8c7d32..64baca3143 100644 --- a/pkg/arvo/app/publish.hoon +++ b/pkg/arvo/app/publish.hoon @@ -529,7 +529,7 @@ :_ this(migrate (~(put by migrate) rid nack-count)) :: (bex 19) is roughly 6 days =/ wakeup=@da - (add now.bol (bex (min 19 nack-count))) + (add now.bol (mul ~s1 (bex (min 19 nack-count)))) [%pass wire %arvo %b %wait wakeup]~ :: ++ on-arvo From 225718b8cfeaf3d917a5368925d6a5154a5fe662 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 10:25:16 +1000 Subject: [PATCH 53/82] metadata-store: fix path handling in migration --- pkg/arvo/app/metadata-store.hoon | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/pkg/arvo/app/metadata-store.hoon b/pkg/arvo/app/metadata-store.hoon index 0abde921d3..ec32cf8182 100644 --- a/pkg/arvo/app/metadata-store.hoon +++ b/pkg/arvo/app/metadata-store.hoon @@ -57,6 +57,7 @@ +$ state-3 [%3 base-state-1] +$ state-4 [%4 base-state-1] +$ state-5 [%5 base-state-1] ++$ state-6 [%6 base-state-1] +$ versioned-state $% state-0 state-1 @@ -64,10 +65,11 @@ state-3 state-4 state-5 + state-6 == -- :: -=| state-5 +=| state-6 =* state - %+ verb | %- agent:dbug @@ -86,13 +88,13 @@ =/ old !<(versioned-state vase) =| cards=(list card) |^ - ?: ?=(%5 -.old) + ?: ?=(%6 -.old) [cards this(state old)] - ?: ?=(%4 -.old) + ?: ?=(%5 -.old) =/ =^associations (migrate-app-to-graph-store %publish associations.old) %_ $ - -.old %5 + -.old %6 associations.old associations :: resource-indices.old @@ -104,9 +106,10 @@ group-indices.old (rebuild-group-indices associations) == - ?: ?=(%3 -.old) + + ?: ?=(%4 -.old) %_ $ - -.old %4 + -.old %5 :: resource-indices.old (rebuild-resource-indices associations.old) @@ -117,6 +120,8 @@ group-indices.old (rebuild-group-indices associations.old) == + ?: ?=(%3 -.old) + $(old [%4 +.old]) ?: ?=(%2 -.old) =/ new-state=state-3 %* . *state-3 @@ -178,11 +183,16 @@ |= [app=@tas =^associations] ^+ associations %- malt - %+ murn ~(tap by associations) + %+ turn ~(tap by associations) |= [[=group-path =md-resource] m=metadata] - ^- (unit [[^group-path ^md-resource] metadata]) - ?. =(app-name.md-resource app) ~ - `[[group-path [%graph app-path.md-resource]] m] + ^- [[^group-path ^md-resource] metadata] + ?. =(app-name.md-resource app) + [[group-path md-resource] m] + =/ new-app-path=path + ?. ?=([@ @ ~] app-path.md-resource) + app-path.md-resource + ship+app-path.md-resource + [[group-path [%graph new-app-path]] m(module app)] :: ++ poke-md-hook |= act=metadata-hook-action From c6dc485d24678cc8ebb36ebc5cdadcab7bbd5802 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 12:52:13 +1000 Subject: [PATCH 54/82] graph-js: useBigIntOrderedMap --- pkg/interface/package-lock.json | 93 ++++++--- pkg/interface/package.json | 1 + .../src/logic/lib/BigIntOrderedMap.ts | 193 ++++++++++++++++++ pkg/interface/src/logic/lib/publish.ts | 8 +- .../src/logic/reducers/graph-update.js | 61 ++---- pkg/interface/src/types/graph-update.ts | 5 +- .../src/views/apps/links/LinkResource.tsx | 5 +- .../views/apps/publish/components/Note.tsx | 5 +- .../publish/components/NoteNavigation.tsx | 41 ++-- .../apps/publish/components/NotebookPosts.tsx | 2 +- .../publish/components/NotebookRoutes.tsx | 3 +- .../components/Sidebar/SidebarItem.tsx | 14 +- 12 files changed, 324 insertions(+), 107 deletions(-) create mode 100644 pkg/interface/src/logic/lib/BigIntOrderedMap.ts diff --git a/pkg/interface/package-lock.json b/pkg/interface/package-lock.json index 5c7b0ada66..24e099e090 100644 --- a/pkg/interface/package-lock.json +++ b/pkg/interface/package-lock.json @@ -2666,6 +2666,11 @@ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, + "big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -10112,7 +10117,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -10133,12 +10139,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10153,17 +10161,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -10280,7 +10291,8 @@ "inherits": { "version": "2.0.4", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -10292,6 +10304,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -10306,6 +10319,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -10313,12 +10327,14 @@ "minimist": { "version": "1.2.5", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -10337,6 +10353,7 @@ "version": "0.5.3", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -10398,7 +10415,8 @@ "npm-normalize-package-bin": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "npm-packlist": { "version": "1.4.8", @@ -10426,7 +10444,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -10438,6 +10457,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -10515,7 +10535,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -10551,6 +10572,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -10570,6 +10592,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -10613,12 +10636,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -11099,7 +11124,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -11120,12 +11146,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11140,17 +11168,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -11267,7 +11298,8 @@ "inherits": { "version": "2.0.4", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -11279,6 +11311,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -11293,6 +11326,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -11300,12 +11334,14 @@ "minimist": { "version": "1.2.5", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -11324,6 +11360,7 @@ "version": "0.5.3", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -11385,7 +11422,8 @@ "npm-normalize-package-bin": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "npm-packlist": { "version": "1.4.8", @@ -11413,7 +11451,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -11425,6 +11464,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -11502,7 +11542,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -11538,6 +11579,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -11557,6 +11599,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -11600,12 +11643,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/pkg/interface/package.json b/pkg/interface/package.json index 1e33fd4fb0..30a4b698ae 100644 --- a/pkg/interface/package.json +++ b/pkg/interface/package.json @@ -12,6 +12,7 @@ "@tlon/indigo-react": "urbit/indigo-react#lf/1.2.9", "@tlon/sigil-js": "^1.4.2", "aws-sdk": "^2.726.0", + "big-integer": "^1.6.48", "classnames": "^2.2.6", "codemirror": "^5.55.0", "css-loader": "^3.5.3", diff --git a/pkg/interface/src/logic/lib/BigIntOrderedMap.ts b/pkg/interface/src/logic/lib/BigIntOrderedMap.ts new file mode 100644 index 0000000000..dbb06b9c5e --- /dev/null +++ b/pkg/interface/src/logic/lib/BigIntOrderedMap.ts @@ -0,0 +1,193 @@ +import bigInt, { BigInteger } from "big-integer"; + +interface NonemptyNode { + n: [BigInteger, V]; + l: MapNode; + r: MapNode; +} + +type MapNode = NonemptyNode | null; + +/** + * An implementation of ordered maps for JS + * Plagiarised wholesale from sys/zuse + */ +export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { + private root: MapNode = null; + size: number = 0; + + constructor() {} + + /** + * Retrieve an value for a key + */ + get(key: BigInteger): V | null { + const inner = (node: MapNode) => { + if (!node) { + return node; + } + const [k, v] = node.n; + if (key.eq(k)) { + return v; + } + if (key.gt(k)) { + return inner(node.l); + } else { + return inner(node.r); + } + }; + + return inner(this.root); + } + + /** + * Put an item by a key + */ + set(key: BigInteger, value: V): void { + + const inner = (node: MapNode) => { + if (!node) { + return { + n: [key, value], + l: null, + r: null, + }; + } + const [k] = node.n; + if (key.eq(k)) { + this.size--; + return { + ...node, + n: [k, value], + }; + } + if (key.gt(k)) { + const l = inner(node.l); + if (!l) { + throw new Error("invariant violation"); + } + return { + ...node, + l, + }; + } + const r = inner(node.r); + if (!r) { + throw new Error("invariant violation"); + } + + return { ...node, r }; + }; + this.size++; + this.root = inner(this.root); + } + + /** + * Remove all entries + */ + clear() { + this.root = null; + } + + /** + * Predicate testing if map contains key + */ + has(key: BigInteger): boolean { + const inner = (node: MapNode) => { + if (!node) { + return false; + } + const [k] = node.n; + + if (k.eq(key)) { + return true; + } + if (key.gt(k)) { + return inner(node.l); + } + return inner(node.r); + }; + return inner(this.root); + } + + /** + * Remove value associated with key, returning whether that key + * existed in the first place + */ + delete(key: BigInteger) { + const inner = (node: MapNode): [boolean, MapNode] => { + if (!node) { + return [false, null]; + } + const [k] = node.n; + if (k.eq(key)) { + return [true, this.nip(node)]; + } + if (key.gt(k)) { + const [bool, l] = inner(node.l); + return [ + bool, + { + ...node, + l, + }, + ]; + } + + const [bool, r] = inner(node.r); + return [ + bool, + { + ...node, + r, + }, + ]; + }; + const [ret, newRoot] = inner(this.root); + if(ret) { + this.size--; + } + this.root = newRoot; + return ret; + } + + private nip(nod: NonemptyNode): MapNode { + const inner = (node: NonemptyNode) => { + if (!node.l) { + return node.r; + } + if (!node.r) { + return node.l; + } + return { + ...node.l, + r: inner(node.r), + }; + }; + return inner(nod); + } + + [Symbol.iterator](): IterableIterator<[BigInteger, V]> { + let result: [BigInteger, V][] = []; + const inner = (node: MapNode) => { + if (!node) { + return; + } + inner(node.l); + result.push(node.n); + inner(node.r); + }; + inner(this.root); + + let idx = 0; + return { + [Symbol.iterator]: this[Symbol.iterator], + next: (): IteratorResult<[BigInteger, V]> => { + if (idx < result.length) { + return { value: result[idx++], done: false }; + } + return { done: true, value: null }; + }, + }; + } +} diff --git a/pkg/interface/src/logic/lib/publish.ts b/pkg/interface/src/logic/lib/publish.ts index 6d639d55ff..71689e1198 100644 --- a/pkg/interface/src/logic/lib/publish.ts +++ b/pkg/interface/src/logic/lib/publish.ts @@ -1,5 +1,7 @@ import { Post, GraphNode, TextContent, Graph, NodeMap } from "~/types"; import { buntPost } from '~/logic/lib/post'; +import {BigIntOrderedMap} from "./BigIntOrderedMap"; +import bigInt from 'big-integer'; export function newPost( title: string, @@ -73,7 +75,7 @@ export function editPost(rev: number, noteId: number, title: string, body: strin } export function getLatestRevision(node: GraphNode): [number, string, string, Post] { - const revs = node.children.get(1); + const revs = node.children.get(bigInt(1)); const empty = [1, "", "", buntPost()] as [number, string, string, Post]; if(!revs) { return empty; @@ -87,9 +89,9 @@ export function getLatestRevision(node: GraphNode): [number, string, string, Pos } export function getComments(node: GraphNode): GraphNode { - const comments = node.children.get(2); + const comments = node.children.get(bigInt(2)); if(!comments) { - return { post: buntPost(), children: new Map() } + return { post: buntPost(), children: new BigIntOrderedMap() } } return comments; } diff --git a/pkg/interface/src/logic/reducers/graph-update.js b/pkg/interface/src/logic/reducers/graph-update.js index dcdd5d9eea..8dd21783e3 100644 --- a/pkg/interface/src/logic/reducers/graph-update.js +++ b/pkg/interface/src/logic/reducers/graph-update.js @@ -1,16 +1,6 @@ import _ from 'lodash'; -import { OrderedMap } from "~/logic/lib/OrderedMap"; - -const DA_UNIX_EPOCH = 170141184475152167957503069145530368000; -const normalizeKey = (key) => { - if(key > DA_UNIX_EPOCH) { - // new links uses milliseconds since unix epoch - // old (pre-graph-store) use @da - // ported from +time:enjs:format in hoon.hoon - return Math.round((1000 * (9223372036854775 + (key - DA_UNIX_EPOCH))) / 18446744073709551616); - } - return key; -} +import { BigIntOrderedMap } from "~/logic/lib/BigIntOrderedMap"; +import bigInt, { BigInteger } from "big-integer"; export const GraphReducer = (json, state) => { const data = _.get(json, 'graph-update', false); @@ -38,33 +28,26 @@ const addGraph = (json, state) => { const _processNode = (node) => { // is empty if (!node.children) { - node.children = new OrderedMap(); - node.post.originalIndex = node.post.index; - node.post.index = node.post.index.split('/').map(x => x.length === 0 ? '' : normalizeKey(parseInt(x, 10))).join('/'); + node.children = new BigIntOrderedMap(); return node; } // is graph - let converted = new OrderedMap(); + let converted = new BigIntOrderedMap(); for (let i in node.children) { let item = node.children[i]; let index = item[0].split('/').slice(1).map((ind) => { - return parseInt(ind, 10); + return bigInt(ind); }); if (index.length === 0) { break; } - - const normalKey = normalizeKey(index[index.length - 1]); - item[1].post.originalKey = index[index.length - 1]; converted.set( - normalKey, + index[index.length - 1], _processNode(item[1]) ); } node.children = converted; - node.post.originalIndex = node.post.index; - node.post.index = node.post.index.split('/').map(x => x.length === 0 ? '' : normalizeKey(parseInt(x, 10))).join('/'); return node; }; @@ -75,21 +58,22 @@ const addGraph = (json, state) => { } let resource = data.resource.ship + '/' + data.resource.name; - state.graphs[resource] = new OrderedMap(); + state.graphs[resource] = new BigIntOrderedMap(); for (let i in data.graph) { let item = data.graph[i]; let index = item[0].split('/').slice(1).map((ind) => { - return parseInt(ind, 10); + return bigInt(ind); }); if (index.length === 0) { break; } let node = _processNode(item[1]); - const normalKey = normalizeKey(index[index.length - 1]) - node.post.originalKey = index[index.length - 1]; - state.graphs[resource].set(normalKey, node); + state.graphs[resource].set( + index[index.length - 1], + node + ); } state.graphKeys.add(resource); } @@ -108,7 +92,7 @@ const removeGraph = (json, state) => { }; const mapifyChildren = (children) => { - return new OrderedMap( + return new BigIntOrderedMap( children.map(([idx, node]) => { const nd = {...node, children: mapifyChildren(node.children || []) }; return [normalizeKey(parseInt(idx.slice(1), 10)), nd]; @@ -119,23 +103,18 @@ const addNodes = (json, state) => { const _addNode = (graph, index, node) => { // set child of graph if (index.length === 1) { - node.post.originalIndex = node.post.index; - node.post.index = node.post.index.split('/').map(x => x.length === 0 ? '' : normalizeKey(parseInt(x, 10))).join('/'); - - const normalKey = normalizeKey(index[0]) - node.post.originalKey = index[0]; - graph.set(normalKey, node); + graph.set(index[0], node); return graph; } // set parent of graph - let parNode = graph.get(normalizeKey(index[0])); + let parNode = graph.get(index[0]); if (!parNode) { console.error('parent node does not exist, cannot add child'); return; } parNode.children = _addNode(parNode.children, index.slice(1), node); - graph.set(normalizeKey(index[0]), parNode); + graph.set(index[0], parNode); return graph; }; @@ -151,7 +130,7 @@ const addNodes = (json, state) => { if (item[0].split('/').length === 0) { return; } let index = item[0].split('/').slice(1).map((ind) => { - return parseInt(ind, 10); + return bigInt(ind); }); if (index.length === 0) { return; } @@ -174,9 +153,9 @@ const removeNodes = (json, state) => { if (index.length === 1) { graph.delete(index[0]); } else { - const child = graph.get(normalizeKey(index[0])); + const child = graph.get(index[0]); _remove(child.children, index.slice(1)); - graph.set(normalizeKey(index[0]), child); + graph.set(index[0], child); } }; const data = _.get(json, 'remove-nodes', false); @@ -188,7 +167,7 @@ const removeNodes = (json, state) => { data.indices.forEach((index) => { if (index.split('/').length === 0) { return; } let indexArr = index.split('/').slice(1).map((ind) => { - return parseInt(ind, 10); + return bigInt(ind); }); _remove(state.graphs[res], indexArr); }); diff --git a/pkg/interface/src/types/graph-update.ts b/pkg/interface/src/types/graph-update.ts index bd258e7410..cdcb1585e9 100644 --- a/pkg/interface/src/types/graph-update.ts +++ b/pkg/interface/src/types/graph-update.ts @@ -1,4 +1,5 @@ -import {Patp} from "./noun"; +import { Patp } from "./noun"; +import { BigIntOrderedMap } from "~/logic/lib/BigIntOrderedMap"; export interface TextContent { text: string; }; @@ -23,7 +24,7 @@ export interface GraphNode { post: Post; } -export type Graph = Map; +export type Graph = BigIntOrderedMap; export type Graphs = { [rid: string]: Graph }; diff --git a/pkg/interface/src/views/apps/links/LinkResource.tsx b/pkg/interface/src/views/apps/links/LinkResource.tsx index 571f9b2d18..b535dd83eb 100644 --- a/pkg/interface/src/views/apps/links/LinkResource.tsx +++ b/pkg/interface/src/views/apps/links/LinkResource.tsx @@ -1,6 +1,7 @@ import React, { useEffect } from "react"; import { Box, Row, Col, Center, LoadingSpinner } from "@tlon/indigo-react"; import { Switch, Route, Link } from "react-router-dom"; +import bigInt from 'big-integer'; import GlobalApi from "~/logic/api/global"; import { StoreState } from "~/logic/store/type"; @@ -99,7 +100,7 @@ export function LinkResource(props: LinkResourceProps) { return
Malformed URL
; } - const index = parseInt(indexArr[1], 10); + const index = bigInt(indexArr[1]); const node = !!graph ? graph.get(index) : null; if (!node) { @@ -124,7 +125,7 @@ export function LinkResource(props: LinkResourceProps) { name={name} ship={ship} api={api} - parentIndex={node.post.originalIndex} + parentIndex={node.post.index} /> index === child); - const target = children[backwards ? i+1 : i-1]; + const i = children.findIndex(([index]) => index.eq(child)); + const target = children[backwards ? i + 1 : i - 1]; return target?.[0] || null; } function makeNoteUrl(noteId: number) { return noteId.toString(); } - interface NoteNavigationProps { noteId: number; @@ -53,7 +57,7 @@ export function NoteNavigation(props: NoteNavigationProps) { let nextComponent = ; let prevComponent = ; const { noteId, notebook } = props; - if(!notebook) { + if (!notebook) { return null; } const nextId = getAdjacentId(notebook, noteId); @@ -67,27 +71,16 @@ export function NoteNavigation(props: NoteNavigationProps) { if (next && nextId) { const nextUrl = makeNoteUrl(nextId); - const [,title,, post] = getLatestRevision(next); - const date = post['time-sent']; - nextComponent = ( - - ); + const [, title, , post] = getLatestRevision(next); + const date = post["time-sent"]; + nextComponent = ; } if (prev && prevId) { const prevUrl = makeNoteUrl(prevId); - const [,title,, post] = getLatestRevision(prev); - const date = post['time-sent']; + const [, title, , post] = getLatestRevision(prev); + const date = post["time-sent"]; prevComponent = ( - + ); } diff --git a/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx b/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx index 2fc8ae908b..d44905a3b3 100644 --- a/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx +++ b/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx @@ -19,7 +19,7 @@ export function NotebookPosts(props: NotebookPostsProps) { ([date, node]) => node && ( { const { noteId } = routeProps.match.params; - const noteIdNum = parseInt(noteId, 10); + const noteIdNum = bigInt(noteId) if(!graph) { return
; diff --git a/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx b/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx index 210829fc4b..05dddb4d87 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/SidebarItem.tsx @@ -24,12 +24,12 @@ function SidebarItemIndicator(props: { status?: SidebarItemStatus }) { } } -const getAppIcon = (app: string, module: string) => { +const getAppIcon = (app: string, mod: string) => { if (app === "graph") { - if (module === "link") { + if (mod === "link") { return "Links"; } - return _.capitalize(module); + return _.capitalize(mod); } return _.capitalize(app); }; @@ -58,7 +58,7 @@ export function SidebarItem(props: { const { association, path, selected, apps, groups } = props; const title = getItemTitle(association); const appName = association?.["app-name"]; - const module = association?.metadata?.module || appName; + const mod = association?.metadata?.module || appName; const appPath = association?.["app-path"]; const groupPath = association?.["group-path"]; const app = apps[appName]; @@ -74,8 +74,8 @@ export function SidebarItem(props: { const baseUrl = isUnmanaged ? `/~landscape/home` : `/~landscape${groupPath}`; const to = isSynced - ? `${baseUrl}/resource/${module}${appPath}` - : `${baseUrl}/join/${module}${appPath}`; + ? `${baseUrl}/resource/${mod}${appPath}` + : `${baseUrl}/join/${mod}${appPath}`; const color = selected ? "black" : isSynced ? "gray" : "lightGray"; @@ -101,7 +101,7 @@ export function SidebarItem(props: { Date: Thu, 29 Oct 2020 12:56:41 +1000 Subject: [PATCH 55/82] publish: migrate note body correctly --- pkg/arvo/app/publish.hoon | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon index 64baca3143..f1639c4861 100644 --- a/pkg/arvo/app/publish.hoon +++ b/pkg/arvo/app/publish.hoon @@ -335,9 +335,16 @@ ^- graph:graph-store %^ put:orm:graph-store *graph:graph-store %1 + =/ body=@t + =/ idx + (find ";>" file.note) + ?~ idx + file.note + %- crip + (slag (add 2 u.idx) (trip file.note)) %* . (blank-note-node note) index.post ~[date-created.note %1 %1] - contents.post ~[text+title.note text+filename.note] + contents.post ~[text+title.note text+body] == :: ++ comments-to-internal-graph From fbf505c0d9ae416bcee7514598efd728968071b9 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 13:11:18 +1000 Subject: [PATCH 56/82] graph-store: mark unmarked graphs --- pkg/arvo/app/graph-store.hoon | 27 ++++++++++++++++++++++++--- pkg/arvo/app/publish.hoon | 4 +++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/pkg/arvo/app/graph-store.hoon b/pkg/arvo/app/graph-store.hoon index 5794e5659a..f619939b9e 100644 --- a/pkg/arvo/app/graph-store.hoon +++ b/pkg/arvo/app/graph-store.hoon @@ -7,14 +7,17 @@ +$ card card:agent:gall +$ versioned-state $% state-0 + state-1 == :: +$ state-0 [%0 network:store] ++$ state-1 [%1 network:store] +:: ++ orm orm:store ++ orm-log orm-log:store -- :: -=| state-0 +=| state-1 =* state - :: %- agent:dbug @@ -27,9 +30,27 @@ ++ on-init [~ this] ++ on-save !>(state) ++ on-load - |= old=vase + |= =old=vase ^- (quip card _this) - [~ this(state !<(state-0 old))] + =+ !<(old=versioned-state old-vase) + =| cards=(list card) + |- + ?- -.old + %0 + %_ $ + -.old %1 + :: + graphs.old + %- ~(run by graphs.old) + |= [=graph:store q=(unit mark)] + ^- [graph:store (unit mark)] + :- graph + ?^ q q + `%graph-validator-link + == + :: + %1 [cards this(state old)] + == :: ++ on-watch ~/ %graph-store-watch diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon index f1639c4861..d907840c3f 100644 --- a/pkg/arvo/app/publish.hoon +++ b/pkg/arvo/app/publish.hoon @@ -336,8 +336,10 @@ %^ put:orm:graph-store *graph:graph-store %1 =/ body=@t + =/ file + (trip file.note) =/ idx - (find ";>" file.note) + (find ";>" file) ?~ idx file.note %- crip From b768cd3dd0c4ab4dd2aeeeaed5de9ff39425aca7 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 14:07:47 +1000 Subject: [PATCH 57/82] graph-store: normalize @da in indices As links is the only application currently using graph-store, we migrate all indices to @da instead of a @da or a unix timestamp. Rolls over current graphs and update log to rectify this. Additionally, applies the link validator mark to graphs, as this was missing. Fixes a latent bug in graph-store where the removal of a graph that triggered a mark build would prevent the mark from being rebuilt. Fixes mark input and index formatting from the frontend as well. --- pkg/arvo/app/graph-store.hoon | 103 ++++++++++++++++++++++++--- pkg/interface/src/logic/api/graph.ts | 20 ++++-- pkg/interface/src/logic/lib/util.js | 18 +++++ 3 files changed, 126 insertions(+), 15 deletions(-) diff --git a/pkg/arvo/app/graph-store.hoon b/pkg/arvo/app/graph-store.hoon index f619939b9e..516abd003f 100644 --- a/pkg/arvo/app/graph-store.hoon +++ b/pkg/arvo/app/graph-store.hoon @@ -34,23 +34,104 @@ ^- (quip card _this) =+ !<(old=versioned-state old-vase) =| cards=(list card) - |- + |^ ?- -.old %0 %_ $ -.old %1 + :: + validators.old + (~(put in validators.old) %graph-validator-link) + :: + cards + %+ weld cards + %+ turn + ~(tap in (~(put in validators.old) %graph-validator-link)) + |= validator=@t + ^- card + =/ =wire /validator/[validator] + =/ =rave:clay [%sing %b [%da now.bowl] /[validator]] + [%pass wire %arvo %c %warp our.bowl [%home `rave]] :: graphs.old %- ~(run by graphs.old) |= [=graph:store q=(unit mark)] ^- [graph:store (unit mark)] - :- graph + :- (convert-unix-timestamped-graph graph) ?^ q q `%graph-validator-link + :: + update-logs.old + %- ~(run by update-logs.old) + convert-unix-timestamped-log == :: %1 [cards this(state old)] == + :: + ++ convert-unix-timestamped-log + |= =update-log:store + ^- update-log:store + %+ gas:orm-log *update-log:store + %+ turn + (tap:orm-log update-log) + |= [=time =logged-update:store] + :- time + |^ ^- logged-update:store + :+ %0 p.logged-update + ?+ -.q.logged-update q.logged-update + %add-nodes (add-nodes +.q.logged-update) + %remove-nodes (remove-nodes +.q.logged-update) + == + :: + ++ add-nodes + |= [rid=res nodes=(map index:store node:store)] + ^- logged-update-0:store + :+ %add-nodes rid + %- ~(gas by *(map index:store node:store)) + %+ turn + ~(tap by nodes) + |= [=index:store =node:store] + ^- [index:store node:store] + :- (turn index maybe-unix-to-da) + (convert-unix-timestamped-node node) + :: + ++ remove-nodes + |= [rid=res indices=(set index:store)] + ^- logged-update-0:store + :+ %remove-nodes rid + %- ~(gas in *(set index:store)) + %+ turn + ~(tap in indices) + |=(=index:store (turn index maybe-unix-to-da)) + -- + :: + ++ maybe-unix-to-da + |= =atom + ^- @ + :: (bex 127) is roughly 226AD + ?. (lte atom (bex 127)) + atom + (add ~1970.1.1 (div (mul ~s1 atom) 1.000)) + :: + ++ convert-unix-timestamped-node + |= =node:store + ^- node:store + ?. ?=(%graph -.children.node) + node + :+ post.node %graph + (convert-unix-timestamped-graph p.children.node) + :: + ++ convert-unix-timestamped-graph + |= =graph:store + %+ gas:orm *graph:store + %+ turn + (tap:orm graph) + |= [=atom =node:store] + ^- [^atom node:store] + :- (maybe-unix-to-da atom) + (convert-unix-timestamped-node node) + -- :: ++ on-watch ~/ %graph-store-watch @@ -124,7 +205,7 @@ :~ (give [/updates /keys ~] [%add-graph resource graph mark]) ?~ mark ~ ?: (~(has in validators) u.mark) ~ - =/ wire (weld /graph (en-path:res resource)) + =/ wire /validator/[u.mark] =/ =rave:clay [%sing %b [%da now.bowl] /[u.mark]] [%pass wire %arvo %c %warp our.bowl [%home `rave]]~ == @@ -622,15 +703,15 @@ ++ on-arvo |= [=wire =sign-arvo] ^- (quip card _this) - ?+ -.sign-arvo (on-arvo:def wire sign-arvo) - %c + ?+ wire (on-arvo:def wire sign-arvo) + :: + :: old wire, do nothing + [%graph *] [~ this] + :: + [%validator @ ~] :_ this - ?> ?=([%graph @ *] wire) - =/ =resource:store (de-path:res t.wire) - =/ gra=(unit marked-graph:store) (~(get by graphs) resource) - ?~ gra ~ - ?~ q.u.gra ~ - =/ =rave:clay [%next %b [%da now.bowl] /[u.q.u.gra]] + =* validator i.t.wire + =/ =rave:clay [%sing %b [%da now.bowl] /[validator]] [%pass wire %arvo %c %warp our.bowl [%home `rave]]~ == :: diff --git a/pkg/interface/src/logic/api/graph.ts b/pkg/interface/src/logic/api/graph.ts index 725ca7006f..fe73cac0bd 100644 --- a/pkg/interface/src/logic/api/graph.ts +++ b/pkg/interface/src/logic/api/graph.ts @@ -4,12 +4,12 @@ import { Patp, Path, PatpNoSig } from '~/types/noun'; import _ from 'lodash'; import {makeResource, resourceFromPath} from '../lib/group'; import {GroupPolicy, Enc, Post, NodeMap} from '~/types'; -import { numToUd } from '~/logic/lib/util'; +import { numToUd, unixToDa } from '~/logic/lib/util'; export const createPost = (contents: Object[], parentIndex: string = '') => { return { author: `~${window.ship}`, - index: parentIndex + '/' + Date.now(), + index: parentIndex + '/' + unixToDa(Date.now()).toString(), 'time-sent': Date.now(), contents, hash: null, @@ -17,6 +17,16 @@ export const createPost = (contents: Object[], parentIndex: string = '') => { }; }; +function moduleToMark(mod: string): string | undefined { + if(mod === 'link') { + return 'graph-validator-link'; + } + if(mod === 'publish') { + return 'graph-validator-publish'; + } + return undefined; +} + export default class GraphApi extends BaseApi { private storeAction(action: any): Promise { @@ -47,7 +57,8 @@ export default class GraphApi extends BaseApi { title, description, associated, - "module": mod + "module": mod, + mark: moduleToMark(mod) } }); } @@ -67,7 +78,8 @@ export default class GraphApi extends BaseApi { title, description, associated: { policy }, - "module": mod + "module": mod, + mark: moduleToMark(mod) } }); } diff --git a/pkg/interface/src/logic/lib/util.js b/pkg/interface/src/logic/lib/util.js index 9fda4c6e8e..8edeae38ae 100644 --- a/pkg/interface/src/logic/lib/util.js +++ b/pkg/interface/src/logic/lib/util.js @@ -1,8 +1,26 @@ import _ from 'lodash'; import f from 'lodash/fp'; +import bigInt from 'big-integer'; export const MOBILE_BROWSER_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i; +const DA_UNIX_EPOCH = bigInt("170141184475152167957503069145530368000"); // `@ud` ~1970.1.1 +const DA_SECOND = bigInt("18446744073709551616"); // `@ud` ~s1 +export function daToUnix(da) { + // ported from +time:enjs:format in hoon.hoon + const offset = DA_SECOND.divide(bigInt(2000)); + const epochAdjusted = offset.add(da.subtract(DA_UNIX_EPOCH)); + + return Math.round( + epochAdjusted.multiply(bigInt(1000)).divide(DA_SECOND).toJSNumber() + ); +} + +export function unixToDa(unix) { + const timeSinceEpoch = bigInt(unix).multiply(DA_SECOND).divide(bigInt(1000)); + return DA_UNIX_EPOCH.add(timeSinceEpoch); +} + export function appIsGraph(app) { return app === 'link' || app === 'publish'; } From 5ebbb26f416b4458ff15476e7f9b4d65ecb923e0 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 15:18:39 +1000 Subject: [PATCH 58/82] groups-js: add ourselves to admin in group-update reducer Fixes #3825 --- pkg/interface/src/logic/reducers/group-update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/interface/src/logic/reducers/group-update.ts b/pkg/interface/src/logic/reducers/group-update.ts index 5862aaca1d..0b0ad8ec04 100644 --- a/pkg/interface/src/logic/reducers/group-update.ts +++ b/pkg/interface/src/logic/reducers/group-update.ts @@ -103,7 +103,7 @@ export default class GroupReducer { const resourcePath = resourceAsPath(resource); state.groups[resourcePath] = { members: new Set(), - tags: { role: {} }, + tags: { role: { admin: new Set([window.ship]) } }, policy: decodePolicy(policy), hidden, }; From 66ad349c5cea599b25fc7bbf91575703b10df575 Mon Sep 17 00:00:00 2001 From: Liam Fitzgerald Date: Thu, 29 Oct 2020 15:35:12 +1000 Subject: [PATCH 59/82] Profile: use ColorInput for background Fixes #3384 --- .../views/apps/profile/components/lib/BackgroundPicker.tsx | 3 ++- .../src/views/apps/profile/components/lib/DisplayForm.tsx | 7 ++++--- pkg/interface/src/views/components/ColorInput.tsx | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/pkg/interface/src/views/apps/profile/components/lib/BackgroundPicker.tsx b/pkg/interface/src/views/apps/profile/components/lib/BackgroundPicker.tsx index df4e8d617a..43d7d19487 100644 --- a/pkg/interface/src/views/apps/profile/components/lib/BackgroundPicker.tsx +++ b/pkg/interface/src/views/apps/profile/components/lib/BackgroundPicker.tsx @@ -11,6 +11,7 @@ import { import GlobalApi from "~/logic/api/global"; import { S3State } from "~/types"; import { ImageInput } from "~/views/components/ImageInput"; +import {ColorInput} from "~/views/components/ColorInput"; export type BgType = "none" | "url" | "color"; @@ -48,7 +49,7 @@ export function BackgroundPicker({ {bgType === "color" && ( - + )} diff --git a/pkg/interface/src/views/apps/profile/components/lib/DisplayForm.tsx b/pkg/interface/src/views/apps/profile/components/lib/DisplayForm.tsx index 9ae53a29c3..4655e26cc4 100644 --- a/pkg/interface/src/views/apps/profile/components/lib/DisplayForm.tsx +++ b/pkg/interface/src/views/apps/profile/components/lib/DisplayForm.tsx @@ -9,6 +9,7 @@ import { Formik, Form } from 'formik'; import * as Yup from 'yup'; import GlobalApi from '~/logic/api/global'; +import { uxToHex } from '~/logic/lib/util'; import { S3State, BackgroundConfig } from '~/types'; import { BackgroundPicker, BgType } from './BackgroundPicker'; @@ -17,7 +18,7 @@ const formSchema = Yup.object().shape({ .oneOf(['none', 'color', 'url'], 'invalid') .required('Required'), bgUrl: Yup.string().url(), - bgColor: Yup.string().matches(/#([A-F]|[a-f]|[0-9]){6}/, 'Invalid color'), + bgColor: Yup.string(), avatars: Yup.boolean(), nicknames: Yup.boolean() }); @@ -57,7 +58,7 @@ export default function DisplayForm(props: DisplayFormProps) { initialValues={ { bgType, - bgColor, + bgColor: bgColor || '', bgUrl, avatars: hideAvatars, nicknames: hideNicknames @@ -66,7 +67,7 @@ export default function DisplayForm(props: DisplayFormProps) { onSubmit={(values, actions) => { const bgConfig: BackgroundConfig = values.bgType === 'color' - ? { type: 'color', color: values.bgColor || '' } + ? { type: 'color', color: `#${uxToHex(values.bgColor || '0x0')}` } : values.bgType === 'url' ? { type: 'url', url: values.bgUrl || '' } : undefined; diff --git a/pkg/interface/src/views/components/ColorInput.tsx b/pkg/interface/src/views/components/ColorInput.tsx index 1139e9d0ba..d2a4459034 100644 --- a/pkg/interface/src/views/components/ColorInput.tsx +++ b/pkg/interface/src/views/components/ColorInput.tsx @@ -15,7 +15,7 @@ import { uxToHex, hexToUx } from "~/logic/lib/util"; type ColorInputProps = Parameters[0] & { id: string; label: string; - disabled: boolean; + disabled?: boolean; }; export function ColorInput(props: ColorInputProps) { @@ -36,9 +36,10 @@ export function ColorInput(props: ColorInputProps) { const result = hexToUx(newValue); setValue(result); }; + return ( - + {caption ? ( +