diff --git a/.github/ISSUE_TEMPLATE/kernel-or-runtime-bug-report.md b/.github/ISSUE_TEMPLATE/kernel-or-runtime-bug-report.md index 081d5def7..e913f8502 100644 --- a/.github/ISSUE_TEMPLATE/kernel-or-runtime-bug-report.md +++ b/.github/ISSUE_TEMPLATE/kernel-or-runtime-bug-report.md @@ -30,7 +30,7 @@ If applicable, add screenshots to help explain your problem. **System (please supply the following information, if relevant):** - OS: [e.g. macOS, linux64, FreeBSD] - Vere and Urbit OS versions - - Your ship's `%base` hash (use `.^(@uv %cz /=base=)` to check) + - Your ship's `%base` hash (use `+trouble` to check) **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/os1-bug-report.md b/.github/ISSUE_TEMPLATE/os1-bug-report.md index 3f12e2523..974acf045 100644 --- a/.github/ISSUE_TEMPLATE/os1-bug-report.md +++ b/.github/ISSUE_TEMPLATE/os1-bug-report.md @@ -27,13 +27,13 @@ If applicable, add screenshots to help explain your problem. If possible, please **Desktop (please complete the following information):** - OS: [e.g. MacOS 10.15.3] - Browser [e.g. chrome, safari] - - Base hash of your urbit ship. Run ` .^(@uv %cz /=base=)` in Dojo to see this. + - Base hash of your urbit ship. Run `+trouble` in Dojo to see this. **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - Browser [e.g. stock browser, safari] - - Base hash of your urbit ship. Run ` .^(@uv %cz /=base=)` in Dojo to see this. + - Base hash of your urbit ship. Run `+trouble` in Dojo to see this. **Additional context** Add any other context about the problem here. diff --git a/bin/solid.pill b/bin/solid.pill index 0ca93db1f..b4a336f3e 100644 --- a/bin/solid.pill +++ b/bin/solid.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ecf3f8593815742e409008421f318b664124e672b1eecd131e4a1e49864a1c2a -size 6175676 +oid sha256:d7088528dbfd54a913921ade093251d678c4ccebfd0ad85ef2022520266b3954 +size 16451173 diff --git a/pkg/arvo/app/chat-cli.hoon b/pkg/arvo/app/chat-cli.hoon index 1f298cf3d..6ff7667a5 100644 --- a/pkg/arvo/app/chat-cli.hoon +++ b/pkg/arvo/app/chat-cli.hoon @@ -72,7 +72,7 @@ +$ glyph char ++ glyphs "!@#$%^&()-=_+[]\{}'\\:\",.<>?" :: -+$ nu-security ?(%channel %village %village-with-group) ++$ nu-security ?(%channel %village) :: +$ command $% [%target (set target)] :: set messaging target @@ -81,7 +81,7 @@ :: :: :: create chat - [%create nu-security path (unit glyph) (unit ?)] + [%create nu-security path (unit resource) (unit glyph) (unit ?)] [%delete path] :: delete chat [%invite [? path] (set ship)] :: allow [%banish [? path] (set ship)] :: disallow @@ -293,8 +293,6 @@ :: ++ target-to-path |= target - %+ weld - ?:(in-group ~ /~) [(scot %p ship) path] :: +path-to-target: deduces a target from a mailbox path :: @@ -464,6 +462,7 @@ security ;~ plug path + (punt ;~(pfix ace group)) (punt ;~(pfix ace glyph)) (punt ;~(pfix ace (fuss 'y' 'n'))) == @@ -535,16 +534,15 @@ :: ;~(pfix ace ;~(plug i.opt $(opt t.opt))) :: -- :: + ++ group ;~((glue net) ship sym) ++ tag |*(a=@tas (cold a (jest a))) ::TODO into stdlib ++ ship ;~(pfix sig fed:ag) ++ path ;~(pfix net ;~(plug urs:ab (easy ~))) ::NOTE short only, tmp :: +mang: un/managed indicator prefix :: - ++ mang - ;~ pose - (cold %| (jest '~/')) - (cold %& (easy ~)) - == + :: deprecated, as sig prefix is no longer used + :: + ++ mang (cold %& (easy ~)) :: +tarl: local target, as /path :: ++ tarl (stag our-self path) @@ -585,7 +583,7 @@ :: +security: security mode :: ++ security - (perk %channel %village-with-group %village ~) + (perk %channel %village ~) :: :: +glyph: shorthand character :: @@ -741,15 +739,21 @@ :: +create: new local mailbox :: ++ create - |= [security=nu-security =path gyf=(unit char) allow-history=(unit ?)] + |= $: security=nu-security + =path + ugroup=(unit resource) + gyf=(unit char) + allow-history=(unit ?) + == ^- (quip card _state) - =/ with-group=? ?=(%village-with-group security) + =/ with-group=? ?=(^ ugroup) =/ =target [with-group our-self path] =/ real-path=^path (target-to-path target) + =/ group-path=^path ?~(ugroup ship+real-path (en-path:resource u.ugroup)) =/ =policy ?- security - %channel *open:policy - ?(%village %village-with-group) *invite:policy + %channel *open:policy + %village *invite:policy == ?^ (scry-for (unit mailbox:store) %chat-store [%mailbox real-path]) =- [[- ~] state] @@ -767,7 +771,7 @@ (rsh 3 1 (spat path)) '' real-path :: chat - real-path :: group + group-path :: group policy ~ (fall allow-history %.y) diff --git a/pkg/arvo/app/chat-hook.hoon b/pkg/arvo/app/chat-hook.hoon index cdc102c9f..fc8d546ec 100644 --- a/pkg/arvo/app/chat-hook.hoon +++ b/pkg/arvo/app/chat-hook.hoon @@ -18,17 +18,19 @@ state-1 state-2 state-3 + state-4 + state-5 + state-6 + state-7 == :: -+$ state-3 - $: %3 - state-base - == ++$ state-7 [%7 state-base] ++$ state-6 [%6 state-base] ++$ state-5 [%5 state-base] ++$ state-4 [%4 state-base] ++$ state-3 [%3 state-base] ++$ state-2 [%2 state-base] :: -+$ state-2 - $: %2 - state-base - == +$ state-1 $: %1 loaded-cards=* @@ -52,7 +54,7 @@ $% [%chat-update update:store] == -- -=| state-3 +=| state-7 =* state - :: %- agent:dbug @@ -81,8 +83,20 @@ =/ old !<(versioned-state old-vase) =| cards=(list card) |- - ?: ?=(%3 -.old) + ?: ?=(%7 -.old) [cards this(state old)] + ?: ?=(%6 -.old) + =. cards + %+ weld cards + ^- (list card) + [%pass /s %agent [our.bol %chat-hook] %poke %noun !>(%fix-out-of-sync)]~ + $(-.old %7) + ?: ?=(?(%3 %4 %5) -.old) + =. cards + %+ weld cards + ^- (list card) + [%pass /pokeme %agent [our.bol %chat-hook] %poke %noun !>(%fix-dm)]~ + $(-.old %6) ?: ?=(%2 -.old) =. cards %+ weld cards @@ -319,9 +333,9 @@ ^- (quip card _this) =^ cards state ?+ mark (on-poke:def mark vase) - %json (poke-json:cc !<(json vase)) - %chat-action (poke-chat-action:cc !<(action:store vase)) - %noun [~ state] + %json (poke-json:cc !<(json vase)) + %chat-action (poke-chat-action:cc !<(action:store vase)) + %noun (poke-noun:cc !<(?(%fix-dm %fix-out-of-sync) vase)) :: %chat-hook-action (poke-chat-hook-action:cc !<(action:hook vase)) @@ -383,6 +397,81 @@ |_ bol=bowl:gall ++ grp ~(. grpl bol) :: +++ poke-noun + |= a=?(%fix-dm %fix-out-of-sync) + ^- (quip card _state) + |^ + :_ state + ?- a + %fix-dm (fix-dm %fix-dm) + %fix-out-of-sync (fix-out-of-sync %fix-out-of-sync) + == + :: + ++ fix-out-of-sync + |= b=%fix-out-of-sync + ^- (list card) + %- zing + %+ turn ~(tap by synced) + |= [=path host=ship] + ^- (list card) + ?: =(host our.bol) ~ + ?> ?=([@ @ ~] path) + =/ =ship (slav %p i.path) + :~ =- [%pass / %agent [our.bol %chat-hook] %poke %chat-hook-action -] + !> ^- action:hook + [%remove path] + :: + =- [%pass / %agent [our.bol %chat-hook] %poke %chat-hook-action -] + !> ^- action:hook + [%add-synced ship path %.y] + == + :: + ++ fix-dm + |= b=%fix-dm + ^- (list card) + %- zing + %+ turn + ~(tap by synced) + |= [=path host=ship] + ^- (list card) + ?> ?=([@ @ *] path) + =/ =ship (slav %p i.path) + ?: =(ship our.bol) + :: local dm, no need to do cleanup + ~ + ?: ?=(^ (groups-of-chat path)) + :: correctly initialized, no need to do cleanup + :: + ~ + ?. =((end 3 4 i.t.path) 'dm--') + ~ + :- =- [%pass /fixdm %agent [our.bol %chat-view] %poke %chat-view-action -] + !> ^- action:view + [%delete path] + =/ new-dm /(scot %p our.bol)/(crip (weld "dm--" (trip (scot %p ship)))) + =/ mailbox=(unit mailbox:store) (chat-scry path) + ?~ mailbox + ~ + :~ =- [%pass /fixdm %agent [our.bol %chat-view] %poke %chat-view-action -] + !> ^- action:view + :* %create + %- crip + (zing [(trip (scot %p our.bol)) " <-> " (trip (scot %p ship)) ~]) + '' + new-dm + ship+new-dm + [%invite (silt ~[ship])] + (silt ~[ship]) + %.y + %.n + == + :: + =- [%pass /fixdm %agent [our.bol %chat-store] %poke %chat-action -] + !> ^- action:store + [%messages new-dm envelopes.u.mailbox] + == + -- +:: ++ poke-json |= jon=json ^- (quip card _state) diff --git a/pkg/arvo/app/file-server.hoon b/pkg/arvo/app/file-server.hoon index 6732fd30a..fdcf7bfd4 100644 --- a/pkg/arvo/app/file-server.hoon +++ b/pkg/arvo/app/file-server.hoon @@ -269,7 +269,7 @@ == :: ++ on-leave on-leave:def -++ on-peek +++ on-peek |= =path ^- (unit (unit cage)) |^ @@ -287,10 +287,9 @@ *@uv =/ parent (scot %p ship.u.ota) =+ .^(=cass:clay %cs /[parent]/[desk.u.ota]/1/late/foo) - %^ end 3 3 + %^ end 0 25 .^(@uv %cz /[parent]/[desk.u.ota]/(scot %ud ud.cass)) -- - ++ on-agent on-agent:def ++ on-fail on-fail:def -- diff --git a/pkg/arvo/app/glob.hoon b/pkg/arvo/app/glob.hoon index 953a99bbd..328264bb7 100644 --- a/pkg/arvo/app/glob.hoon +++ b/pkg/arvo/app/glob.hoon @@ -1,7 +1,7 @@ /- glob /+ default-agent, verb, dbug |% -++ hash 0v5.knd3c.vvtvt.h0gg0.8qcau.8iii4 +++ hash 0v3.cus8h.vc64c.rfb3t.22oji.b529a +$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))] +$ all-states $% state-0 @@ -58,7 +58,6 @@ ++ on-load |= old-state=vase ^- (quip card _this) - ~& > %initting =+ !<(old=all-states old-state) ?> ?=(%0 -.old) ?~ glob.old diff --git a/pkg/arvo/app/graph-store.hoon b/pkg/arvo/app/graph-store.hoon new file mode 100644 index 000000000..ab9a6b971 --- /dev/null +++ b/pkg/arvo/app/graph-store.hoon @@ -0,0 +1,572 @@ +/+ store=graph-store, sigs=signatures, res=resource, default-agent, dbug +~% %graph-store-top ..is ~ +|% ++$ card card:agent:gall ++$ versioned-state + $% state-0 + == +:: ++$ state-0 [%0 network:store] +++ orm orm:store +++ orm-log orm-log:store +-- +:: +=| state-0 +=* state - +:: +%- agent:dbug +^- agent:gall +~% %graph-store-agent ..card ~ +|_ =bowl:gall ++* this . + def ~(. (default-agent this %|) bowl) +:: +++ on-init [~ this] +++ on-save !>(state) +++ on-load + |= old=vase + ^- (quip card _this) + [~ this(state !<(state-0 old))] +:: +++ on-watch + ~/ %graph-store-watch + |= =path + ^- (quip card _this) + |^ + ?> (team:title our.bowl src.bowl) + =/ cards=(list card) + ?+ path (on-watch:def path) + [%updates ~] ~ + [%keys ~] (give [%keys ~(key by graphs)]) + [%tags ~] (give [%tags ~(key by tag-queries)]) + == + [cards this] + :: + ++ give + |= =update-0:store + ^- (list card) + [%give %fact ~ [%graph-update !>([%0 now.bowl update-0])]]~ + -- +:: +++ on-poke + ~/ %graph-store-poke + |= [=mark =vase] + ^- (quip card _this) + |^ + ?> (team:title our.bowl src.bowl) + =^ cards state + ?+ mark (on-poke:def mark vase) + %graph-update (graph-update !<(update:store vase)) + == + [cards this] + :: + ++ graph-update + |= =update:store + ^- (quip card _state) + |^ + ?> ?=(%0 -.update) + ?- -.q.update + %add-graph (add-graph +.q.update) + %remove-graph (remove-graph +.q.update) + %add-nodes (add-nodes p.update +.q.update) + %remove-nodes (remove-nodes p.update +.q.update) + %add-signatures (add-signatures p.update +.q.update) + %remove-signatures (remove-signatures p.update +.q.update) + %add-tag (add-tag +.q.update) + %remove-tag (remove-tag +.q.update) + %archive-graph (archive-graph +.q.update) + %unarchive-graph (unarchive-graph +.q.update) + %run-updates (run-updates +.q.update) + %keys ~|('cannot send %keys as poke' !!) + %tags ~|('cannot send %tags as poke' !!) + %tag-queries ~|('cannot send %tag-queries as poke' !!) + == + :: + ++ add-graph + |= [=resource:store =graph:store mark=(unit mark:store)] + ^- (quip card _state) + ?< (~(has by archive) resource) + ?< (~(has by graphs) resource) + ?> (validate-graph graph mark) + :_ %_ state + graphs (~(put by graphs) resource [graph mark]) + update-logs (~(put by update-logs) resource (gas:orm-log ~ ~)) + validators + ?~ mark validators + (~(put in validators) u.mark) + == + %- zing + :~ (give [/updates /keys ~] [%add-graph resource graph mark]) + ?~ mark ~ + ?: (~(has in validators) u.mark) ~ + =/ wire (weld /graph (en-path:res resource)) + =/ =rave:clay [%sing %b [%da now.bowl] /[u.mark]] + [%pass wire %arvo %c %warp our.bowl [%home `rave]]~ + == + :: + ++ remove-graph + |= =resource:store + ^- (quip card _state) + ?< (~(has by archive) resource) + ?> (~(has by graphs) resource) + :- (give [/updates /keys ~] [%remove-graph resource]) + %_ state + graphs (~(del by graphs) resource) + update-logs (~(del by update-logs) resource) + == + :: + ++ add-nodes + |= $: =time + =resource:store + nodes=(map index:store node:store) + == + ^- (quip card _state) + |^ + =/ [=graph:store mark=(unit mark:store)] + (~(got by graphs) resource) + =/ =update-log:store (~(got by update-logs) resource) + =. update-log + (put:orm-log update-log time [%0 time [%add-nodes resource nodes]]) + :: + :- (give [/updates]~ [%add-nodes resource nodes]) + %_ state + update-logs (~(put by update-logs) resource update-log) + graphs + %+ ~(put by graphs) + resource + :_ mark + (add-node-list resource graph mark (sort-nodes nodes)) + == + :: + ++ sort-nodes + |= nodes=(map index:store node:store) + ^- (list [index:store node:store]) + %+ sort ~(tap by nodes) + |= [p=[=index:store *] q=[=index:store *]] + ^- ? + (lth (lent index.p) (lent index.q)) + :: + ++ add-node-list + |= $: =resource:store + =graph:store + mark=(unit mark:store) + node-list=(list [index:store node:store]) + == + ^- graph:store + ?~ node-list graph + =* index -.i.node-list + =* node +.i.node-list + %_ $ + node-list t.node-list + graph (add-node-at-index graph index node mark) + == + :: + ++ add-node-at-index + =| parent-hash=(unit hash:store) + |= $: =graph:store + =index:store + =node:store + mark=(unit mark:store) + == + ^- graph:store + ?< ?=(~ index) + ~| "validation of node failed using mark {}" + ?> (validate-graph (gas:orm ~ [i.index node]~) mark) + =* atom i.index + %^ put:orm + graph + atom + :: add child + :: + ?~ t.index + =* p post.node + =/ =validated-portion:store + [parent-hash author.p time-sent.p contents.p] + =/ =hash:store `@ux`(sham validated-portion) + ?~ hash.p node(signatures.post *signatures:store) + ~| "signatures do not match the calculated hash" + ?> (are-signatures-valid:sigs signatures.p hash now.bowl) + ~| "hash of post does not match calculated hash" + ?> =(hash u.hash.p) + node + :: recurse children + :: + =/ parent=node:store + ~| "index does not exist to add a node to!" + (need (get:orm graph atom)) + %_ parent + children + ^- internal-graph:store + :- %graph + %_ $ + index t.index + parent-hash hash.post.parent + graph + ?: ?=(%graph -.children.parent) + p.children.parent + (gas:orm ~ ~) + == + == + -- + :: + ++ remove-nodes + |= [=time =resource:store indices=(set index:store)] + ^- (quip card _state) + |^ + =/ [=graph:store mark=(unit mark:store)] + (~(got by graphs) resource) + =/ =update-log:store (~(got by update-logs) resource) + =. update-log + (put:orm-log update-log time [%0 time [%remove-nodes resource indices]]) + :: + :- (give [/updates]~ [%remove-nodes resource indices]) + %_ state + update-logs (~(put by update-logs) resource update-log) + graphs + %+ ~(put by graphs) + resource + [(remove-indices resource graph ~(tap in indices)) mark] + == + :: + ++ remove-indices + |= [=resource:store =graph:store indices=(list index:store)] + ^- graph:store + ?~ indices graph + %_ $ + indices t.indices + graph (remove-index graph i.indices) + == + :: + ++ remove-index + |= [=graph:store =index:store] + ^- graph:store + ?~ index graph + =* atom i.index + :: last index in list + :: + ?~ t.index + +:`[* graph:store]`(del:orm graph atom) + =/ =node:store + ~| "parent index does not exist to remove a node from!" + (need (get:orm graph atom)) + ~| "child index does not exist to remove a node from!" + ?> ?=(%graph -.children.node) + %^ put:orm + graph + atom + node(p.children $(graph p.children.node, index t.index)) + -- + :: + ++ add-signatures + |= [=time =uid:store =signatures:store] + ^- (quip card _state) + |^ + =* resource resource.uid + =/ [=graph:store mark=(unit mark:store)] + (~(got by graphs) resource) + =/ =update-log:store (~(got by update-logs) resource) + =. update-log + (put:orm-log update-log time [%0 time [%add-signatures uid signatures]]) + :: + :- (give [/updates]~ [%add-signatures uid signatures]) + %_ state + update-logs (~(put by update-logs) resource update-log) + graphs + %+ ~(put by graphs) resource + [(add-at-index graph index.uid signatures) mark] + == + :: + ++ add-at-index + |= [=graph:store =index:store =signatures:store] + ^- graph:store + ?~ index graph + =* atom i.index + =/ =node:store + ~| "node does not exist to add signatures to!" + (need (get:orm graph atom)) + :: last index in list + :: + %^ put:orm + graph + atom + ?~ t.index + ~| "cannot add signatures to a node missing a hash" + ?> ?=(^ hash.post.node) + ~| "signatures did not match public keys!" + ?> (are-signatures-valid:sigs signatures u.hash.post.node now.bowl) + node(signatures.post (~(uni in signatures) signatures.post.node)) + ~| "child graph does not exist to add signatures to!" + ?> ?=(%graph -.children.node) + node(p.children $(graph p.children.node, index t.index)) + -- + :: + ++ remove-signatures + |= [=time =uid:store =signatures:store] + ^- (quip card _state) + |^ + =* resource resource.uid + =/ [=graph:store mark=(unit mark:store)] + (~(got by graphs) resource) + =/ =update-log:store (~(got by update-logs) resource) + =. update-log + %^ put:orm-log update-log + time + [%0 time [%remove-signatures uid signatures]] + :: + :- (give [/updates]~ [%remove-signatures uid signatures]) + %_ state + update-logs (~(put by update-logs) resource update-log) + graphs + %+ ~(put by graphs) resource + [(remove-at-index graph index.uid signatures) mark] + == + :: + ++ remove-at-index + |= [=graph:store =index:store =signatures:store] + ^- graph:store + ?~ index graph + =* atom i.index + =/ =node:store + ~| "node does not exist to add signatures to!" + (need (get:orm graph atom)) + :: last index in list + :: + %^ put:orm + graph + atom + ?~ t.index + node(signatures.post (~(dif in signatures) signatures.post.node)) + ~| "child graph does not exist to add signatures to!" + ?> ?=(%graph -.children.node) + node(p.children $(graph p.children.node, index t.index)) + -- + :: + ++ add-tag + |= [=term =resource:store] + ^- (quip card _state) + ?> (~(has by graphs) resource) + :- (give [/updates /tags ~] [%add-tag term resource]) + %_ state + tag-queries (~(put ju tag-queries) term resource) + == + :: + ++ remove-tag + |= [=term =resource:store] + ^- (quip card _state) + ?> (~(has by graphs) resource) + :- (give [/updates /tags ~] [%remove-tag term resource]) + %_ state + tag-queries (~(del ju tag-queries) term resource) + == + :: + ++ archive-graph + |= =resource:store + ^- (quip card _state) + ?< (~(has by archive) resource) + ?> (~(has by graphs) resource) + :- (give [/updates /keys /tags ~] [%archive-graph resource]) + %_ state + archive (~(put by archive) resource (~(got by graphs) resource)) + graphs (~(del by graphs) resource) + update-logs (~(del by update-logs) resource) + tag-queries + %- ~(run by tag-queries) + |= =resources:store + (~(del in resources) resource) + == + :: + ++ unarchive-graph + |= =resource:store + ^- (quip card _state) + ?> (~(has by archive) resource) + ?< (~(has by graphs) resource) + :- (give [/updates /keys ~] [%unarchive-graph resource]) + %_ state + archive (~(del by archive) resource) + graphs (~(put by graphs) resource (~(got by archive) resource)) + update-logs (~(put by update-logs) resource (gas:orm-log ~ ~)) + == + :: + ++ run-updates + |= [=resource:store =update-log:store] + ^- (quip card _state) + ?< (~(has by archive) resource) + ?> (~(has by graphs) resource) + :_ state + %+ turn (tap:orm-log update-log) + |= [=time update=logged-update:store] + ^- card + ?> ?=(%0 -.update) + :* %pass + /run-updates/(scot %da time) + %agent + [our.bowl %graph-store] + %poke + :- %graph-update + !> + ^- update:store + ?- -.q.update + %add-nodes update(resource.q resource) + %remove-nodes update(resource.q resource) + %add-signatures update(resource.uid.q resource) + %remove-signatures update(resource.uid.q resource) + == + == + :: + ++ validate-graph + |= [=graph:store mark=(unit mark:store)] + ^- ? + ?~ mark %.y + ?~ graph %.y + =/ =dais:clay + .^ =dais:clay + %cb + /(scot %p our.bowl)/[q.byk.bowl]/(scot %da now.bowl)/[u.mark] + == + %+ roll (tap:orm graph) + |= [[=atom =node:store] out=?] + ?& out + =(%& -:(mule |.((vale:dais [atom post.node])))) + ?- -.children.node + %empty %.y + %graph ^$(graph p.children.node) + == + == + :: + ++ give + |= [paths=(list path) update=update-0:store] + ^- (list card) + [%give %fact paths [%graph-update !>([%0 now.bowl update])]]~ + -- + -- +:: +++ on-peek + ~/ %graph-store-peek + |= =path + ^- (unit (unit cage)) + |^ + ?> (team:title our.bowl src.bowl) + ?+ path (on-peek:def path) + [%x %keys ~] ``noun+!>(~(key by graphs)) + [%x %tags ~] ``noun+!>(~(key by tag-queries)) + [%x %tag-queries ~] ``noun+!>(tag-queries) + [%x %graph @ @ ~] + =/ =ship (slav %p i.t.t.path) + =/ =term i.t.t.t.path + =/ result=(unit marked-graph:store) + (~(get by graphs) [ship term]) + ?~ result [~ ~] + ``noun+!>(u.result) + :: + [%x %graph-subset @ @ @ @ ~] + =/ =ship (slav %p i.t.t.path) + =/ =term i.t.t.t.path + =/ start=(unit atom) (rush i.t.t.t.t.path dem:ag) + =/ end=(unit atom) (rush i.t.t.t.t.t.path dem:ag) + =/ graph=(unit marked-graph:store) + (~(get by graphs) [ship term]) + ?~ graph [~ ~] + ``noun+!>(`graph:store`(subset:orm p.u.graph start end)) + :: + [%x %node @ @ @ *] + =/ =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))) + =/ node=(unit node:store) (get-node ship term index) + ?~ node [~ ~] + ``noun+!>(u.node) + :: + [%x %post @ @ @ *] + =/ =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))) + =/ node=(unit node:store) (get-node ship term index) + ?~ node [~ ~] + ``noun+!>(post.u.node) + :: + [%x %node-children @ @ @ *] + =/ =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))) + =/ node=(unit node:store) (get-node ship term index) + ?~ node [~ ~] + ?- -.children.u.node + %empty [~ ~] + %graph ``noun+!>(p.children.u.node) + == + :: + [%x %node-children-subset @ @ @ @ @ *] + =/ =ship (slav %p i.t.t.path) + =/ =term i.t.t.t.path + =/ start=(unit atom) (rush i.t.t.t.t.path dem:ag) + =/ end=(unit atom) (rush i.t.t.t.t.t.path dem:ag) + =/ =index:store + (turn t.t.t.t.t.t.path |=(=cord (slav %ud cord))) + =/ node=(unit node:store) (get-node ship term index) + ?~ node [~ ~] + ?- -.children.u.node + %empty [~ ~] + %graph ``noun+!>(`graph:store`(subset:orm p.children.u.node start end)) + == + :: + [%x %update-log @ @ ~] + =/ =ship (slav %p i.t.t.path) + =/ =term i.t.t.t.path + =/ update-log=(unit update-log:store) (~(get by update-logs) [ship term]) + ?~ update-log [~ ~] + ``noun+!>(u.update-log) + :: + [%x %peek-update-log @ @ ~] + =/ =ship (slav %p i.t.t.path) + =/ =term i.t.t.t.path + =/ update-log=(unit update-log:store) (~(get by update-logs) [ship term]) + ?~ update-log [~ ~] + =/ result=(unit [time update:store]) + (peek:orm-log:store u.update-log) + ?~ result [~ ~] + ``noun+!>([~ -.u.result]) + == + :: + ++ get-node + |= [=ship =term =index:store] + ^- (unit node:store) + =/ parent-graph=(unit marked-graph:store) + (~(get by graphs) [ship term]) + ?~ parent-graph ~ + =/ node=(unit node:store) ~ + =/ =graph:store p.u.parent-graph + |- + ?~ index + node + ?~ t.index + (get:orm graph i.index) + =. node (get:orm graph i.index) + ?~ node ~ + ?- -.children.u.node + %empty ~ + %graph $(graph p.children.u.node, index t.index) + == + -- +:: +++ on-arvo + |= [=wire =sign-arvo] + ^- (quip card _this) + ?+ -.sign-arvo (on-arvo:def wire sign-arvo) + %c + :_ 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]] + [%pass wire %arvo %c %warp our.bowl [%home `rave]]~ + == +:: +++ on-agent on-agent:def +++ on-leave on-leave:def +++ on-fail on-fail:def +-- diff --git a/pkg/arvo/app/hood.hoon b/pkg/arvo/app/hood.hoon index e83b30b15..1bc70ed80 100644 --- a/pkg/arvo/app/hood.hoon +++ b/pkg/arvo/app/hood.hoon @@ -2,22 +2,16 @@ /+ drum=hood-drum, helm=hood-helm, kiln=hood-kiln |% +$ state - $: %8 - drum=state:drum - helm=state:helm - kiln=state:kiln - == -+$ state-7 - $: %7 + $: %9 drum=state:drum helm=state:helm kiln=state:kiln == +$ any-state $% state - state-7 [ver=?(%1 %2 %3 %4 %5 %6) lac=(map @tas fin-any-state)] [%7 drum=state:drum helm=state:helm kiln=state:kiln] + [%8 drum=state:drum helm=state:helm kiln=state:kiln] == +$ any-state-tuple $: drum=any-state:drum diff --git a/pkg/arvo/app/metadata-store.hoon b/pkg/arvo/app/metadata-store.hoon index bf052800d..a0e88e2e6 100644 --- a/pkg/arvo/app/metadata-store.hoon +++ b/pkg/arvo/app/metadata-store.hoon @@ -85,15 +85,16 @@ old [%2 +.old] :: cards - %+ turn + %+ murn ~(tap in ~(key by group-indices.old)) |= =group-path - ^- card - =/ rid=resource - (de-path:resource group-path) - ?: =(our.bowl entity.rid) - (poke-md-hook %add-owned group-path) - (poke-md-hook %add-synced entity.rid group-path) + ^- (unit card) + =/ rid=(unit resource) + (de-path-soft:resource group-path) + ?~ rid ~ + ?: =(our.bowl entity.u.rid) + `(poke-md-hook %add-owned group-path) + `(poke-md-hook %add-synced entity.u.rid group-path) == =/ new-state=state-one %* . *state-one @@ -254,6 +255,11 @@ =/ =group-path (stab (slav %t i.t.t.path)) =/ =md-resource [`@tas`i.t.t.t.path (stab (slav %t i.t.t.t.t.path))] ``noun+!>((~(get by associations) [group-path md-resource])) + :: + [%x %resource @ *] + =/ app=@tas i.t.t.path + =/ app-path=^path t.t.t.path + ``noun+!>((~(get by resource-indices) app app-path)) == :: ++ on-agent on-agent:def diff --git a/pkg/arvo/app/publish.hoon b/pkg/arvo/app/publish.hoon index d4c5b470c..17470e7e5 100644 --- a/pkg/arvo/app/publish.hoon +++ b/pkg/arvo/app/publish.hoon @@ -54,6 +54,7 @@ [%3 state-three] [%4 state-three] [%5 state-three] + [%6 state-three] == :: +$ metadata-delta @@ -69,7 +70,7 @@ == -- :: -=| [%5 state-three] +=| [%6 state-three] =* state - %- agent:dbug %+ verb | @@ -86,7 +87,6 @@ :_ this :~ [%pass /view-bind %arvo %e %connect [~ /'publish-view'] %publish] [%pass /read/paths %arvo %c %warp our.bol q.byk.bol `rav] - [%pass /permissions %agent [our.bol %permission-store] %watch /updates] (invite-poke:main [%create /publish]) :* %pass /invites %agent [our.bol %invite-store] %watch /invitatory/publish @@ -218,6 +218,26 @@ == :: %5 + %= $ + -.p.old-state %6 + cards + %+ weld cards + %+ roll ~(tap by books.p.old-state) + |= [[[who=@p book=@tas] nb=notebook] out=(list card)] + ^- (list card) + ?. =(who our.bol) + out + =/ rid (de-path:resource writers.nb) + =/ grp=(unit group) (scry-group:grup:main 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) + == + :: + %6 [cards this(state p.old-state)] == ++ convert-notebook-3-4 @@ -995,6 +1015,22 @@ [~ 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) @@ -1226,12 +1262,19 @@ ^- [(list card) write=path read=path] ?> ?=(^ group-path.group) =/ scry-path - ;:(welp /(scot %p our.bol)/group-store/(scot %da now.bol) [%groups group-path.group] /noun) - =/ grp .^((unit ^group) %gx scry-path) + ;: welp + /(scot %p our.bol)/group-store/(scot %da now.bol) + [%groups group-path.group] + /noun + == + =/ rid=resource (de-path:resource group-path.group) + =/ grp=(unit ^group) (scry-group:grup rid) ?: use-preexisting.group ?~ grp !! ?. (is-managed group-path.group) !! - `[group-path.group group-path.group] + =/ =tag [%publish (cat 3 'writers-' book)] + :_ [group-path.group group-path.group] + [(group-proxy-poke entity.rid %add-tag rid tag members.u.grp)]~ :: =/ =policy *open:policy @@ -1684,10 +1727,9 @@ ?> ?=(^ subscribers.u.book) =/ cards=(list card) ~[(delete-dir pax)] - =/ rid=resource (de-path:resource writers.u.book) - =? cards (is-managed:grup rid) + =? cards !(is-managed:grup rid) [(group-poke %remove-group rid ~) cards] [cards state] :: %del-note: @@ -1791,6 +1833,10 @@ ?> (team:title our.bol src.bol) =/ 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 @@ -1811,12 +1857,16 @@ (de-path:resource writers.book) =/ =group (need (scry-group:grup rid)) - :_ state(books (~(del by books) who.act book.act)) - :~ `card`[%pass wir %agent [who.act %publish] %leave ~] - `card`[%give %fact [/primary]~ %publish-primary-delta !>(del)] - (group-proxy-poke who.act %remove-members rid (sy our.bol ~)) - (group-poke %remove-group 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 @@ -1952,6 +2002,19 @@ /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) @@ -2044,9 +2107,11 @@ (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 - :* (group-pull-hook-poke [%add host.del rid]) - (metadata-hook-poke [%add-synced host.del writers.data.del]) + :* (metadata-hook-poke [%add-synced host.del writers.data.del]) cards == :: diff --git a/pkg/arvo/gen/graph-store/add-graph.hoon b/pkg/arvo/gen/graph-store/add-graph.hoon new file mode 100644 index 000000000..a95468928 --- /dev/null +++ b/pkg/arvo/gen/graph-store/add-graph.hoon @@ -0,0 +1,10 @@ +:: graph-store|add-graph: add new graph +:: +/+ *graph-store +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[=resource mark=(unit mark) ~] ~] + == +:- %graph-update +^- update +[%0 now [%add-graph resource (gas:orm ~ ~) mark]] diff --git a/pkg/arvo/gen/graph-store/add-post.hoon b/pkg/arvo/gen/graph-store/add-post.hoon new file mode 100644 index 000000000..643e073c0 --- /dev/null +++ b/pkg/arvo/gen/graph-store/add-post.hoon @@ -0,0 +1,20 @@ +:: graph-store|add-post: add post to a graph +:: +/- *graph-store +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[[our=ship name=term] contents=(list content) ~] ~] + == +=/ =post *post +=: author.post our + index.post [now]~ + time-sent.post now + contents.post contents +== +:: +:- %graph-update +^- update +:+ %0 now +:+ %add-nodes [our name] +%- ~(gas by *(map index node)) +~[[[now]~ [post [%empty ~]]]] diff --git a/pkg/arvo/gen/graph-store/add-signatures.hoon b/pkg/arvo/gen/graph-store/add-signatures.hoon new file mode 100644 index 000000000..9a536d6d5 --- /dev/null +++ b/pkg/arvo/gen/graph-store/add-signatures.hoon @@ -0,0 +1,10 @@ +:: graph-store|add-signatures: add signatures to a node at a particular uid +:: +/- *graph-store +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[[=resource =index] =signatures ~] ~] + == +:- %graph-update +^- update +[%0 now [%add-signatures [resource index] signatures]] diff --git a/pkg/arvo/gen/graph-store/add-tag.hoon b/pkg/arvo/gen/graph-store/add-tag.hoon new file mode 100644 index 000000000..8c83f1c4e --- /dev/null +++ b/pkg/arvo/gen/graph-store/add-tag.hoon @@ -0,0 +1,10 @@ +:: graph-store|add-tag: tag a particular graph +:: +/- *graph-store +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[=term =resource ~] ~] + == +:- %graph-update +^- update +[%0 now [%add-tag term resource]] diff --git a/pkg/arvo/gen/graph-store/archive-graph.hoon b/pkg/arvo/gen/graph-store/archive-graph.hoon new file mode 100644 index 000000000..a40a7a533 --- /dev/null +++ b/pkg/arvo/gen/graph-store/archive-graph.hoon @@ -0,0 +1,10 @@ +:: graph-store|archive-graph: archive graph +:: +/- *graph-store +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[=resource ~] ~] + == +:- %graph-update +^- update +[%0 now [%archive-graph resource]] diff --git a/pkg/arvo/gen/graph-store/remove-graph.hoon b/pkg/arvo/gen/graph-store/remove-graph.hoon new file mode 100644 index 000000000..e06305981 --- /dev/null +++ b/pkg/arvo/gen/graph-store/remove-graph.hoon @@ -0,0 +1,10 @@ +:: graph-store|remove-graph: remove graph +:: +/- *graph-store +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[=resource ~] ~] + == +:- %graph-update +^- update +[%0 now [%remove-graph resource]] diff --git a/pkg/arvo/gen/graph-store/remove-nodes.hoon b/pkg/arvo/gen/graph-store/remove-nodes.hoon new file mode 100644 index 000000000..8c5e13603 --- /dev/null +++ b/pkg/arvo/gen/graph-store/remove-nodes.hoon @@ -0,0 +1,10 @@ +:: graph-store|remove-nodes: remove nodes from a graph at indices +:: +/- *graph-store +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[=resource indices=(set index) ~] ~] + == +:- %graph-update +^- update +[%0 now [%remove-nodes resource indices]] diff --git a/pkg/arvo/gen/graph-store/remove-signatures.hoon b/pkg/arvo/gen/graph-store/remove-signatures.hoon new file mode 100644 index 000000000..b9bd658fa --- /dev/null +++ b/pkg/arvo/gen/graph-store/remove-signatures.hoon @@ -0,0 +1,11 @@ +:: graph-store|remove-signatures: remove signatures from a node at a +:: particular uid +:: +/- *graph-store +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[[=resource =index] =signatures ~] ~] + == +:- %graph-update +^- update +[%0 now [%remove-signatures [resource index] signatures]] diff --git a/pkg/arvo/gen/graph-store/remove-tag.hoon b/pkg/arvo/gen/graph-store/remove-tag.hoon new file mode 100644 index 000000000..722d4af5c --- /dev/null +++ b/pkg/arvo/gen/graph-store/remove-tag.hoon @@ -0,0 +1,10 @@ +:: graph-store|remove-tag: remove a tag from a particular graph +:: +/- *graph-store +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[=term =resource ~] ~] + == +:- %graph-update +^- update +[%0 now [%remove-tag term resource]] diff --git a/pkg/arvo/gen/graph-store/unarchive-graph.hoon b/pkg/arvo/gen/graph-store/unarchive-graph.hoon new file mode 100644 index 000000000..1d684de6a --- /dev/null +++ b/pkg/arvo/gen/graph-store/unarchive-graph.hoon @@ -0,0 +1,10 @@ +:: graph-store|unarchive-graph: unarchive graph +:: +/- *graph-store +:- %say +|= $: [now=@da eny=@uvJ =beak] + [[=resource ~] ~] + == +:- %graph-update +^- update +[%0 now [%unarchive-graph resource]] diff --git a/pkg/arvo/gen/hood/moon-cycle-keys.hoon b/pkg/arvo/gen/hood/moon-cycle-keys.hoon index e9be8a32f..38ca3d364 100644 --- a/pkg/arvo/gen/hood/moon-cycle-keys.hoon +++ b/pkg/arvo/gen/hood/moon-cycle-keys.hoon @@ -36,7 +36,7 @@ public-key =/ cub (pit:nu:crub:crypto 512 (shaz (jam mon life eny))) =/ =seed:able:jael - [mon 1 sec:ex:cub ~] + [mon life sec:ex:cub ~] %- %- slog :~ leaf+"moon: {(scow %p mon)}" leaf+(scow %uw (jam seed)) diff --git a/pkg/arvo/gen/hood/ota.hoon b/pkg/arvo/gen/hood/ota.hoon index c91f5b9f3..fcccb68a8 100644 --- a/pkg/arvo/gen/hood/ota.hoon +++ b/pkg/arvo/gen/hood/ota.hoon @@ -8,7 +8,11 @@ :: :- %say |= $: [now=@da eny=@uvJ bec=beak] - [arg=?(~ [her=@p sud=@tas ~]) ~] + [arg=?(~ [%disable ~] [her=@p sud=@tas ~]) ~] == +?~ arg + :- %kiln-ota-info ~ :- %kiln-ota -?~(arg ~ `[her sud]:arg) +?: ?=([%disable ~] arg) + ~ +`[her sud]:arg diff --git a/pkg/arvo/gen/timers.hoon b/pkg/arvo/gen/timers.hoon index b016fd41c..d448fd6ac 100644 --- a/pkg/arvo/gen/timers.hoon +++ b/pkg/arvo/gen/timers.hoon @@ -5,5 +5,5 @@ [%tang >timers< ~] .^ (list [date=@da =duct]) %bx - (en-beam:format [p.bec %$ r.bec] /debug/timers) + (en-beam:format [p.bec %$ r.bec] /timers/debug) == diff --git a/pkg/arvo/lib/graph-store.hoon b/pkg/arvo/lib/graph-store.hoon new file mode 100644 index 000000000..bf099c89f --- /dev/null +++ b/pkg/arvo/lib/graph-store.hoon @@ -0,0 +1,411 @@ +/- sur=graph-store, pos=post +/+ res=resource +=< [sur .] +=< [pos .] +=, sur +=, pos +|% +:: NOTE: move these functions to zuse +++ nu :: parse number as hex + |= jon/json + ?> ?=({$s *} jon) + (rash p.jon hex) +:: +++ re :: recursive reparsers + |* {gar/* sef/_|.(fist:dejs-soft:format)} + |= jon/json + ^- (unit _gar) + =- ~! gar ~! (need -) - + ((sef) jon) +:: +++ dank :: tank + ^- $-(json (unit tank)) + =, ^? dejs-soft:format + %+ re *tank |. ~+ + %- of :~ + leaf+sa + palm+(ot style+(ot mid+sa cap+sa open+sa close+sa ~) lines+(ar dank) ~) + rose+(ot style+(ot mid+sa open+sa close+sa ~) lines+(ar dank) ~) + == +:: +++ orm ((ordered-map atom node) gth) +++ orm-log ((ordered-map time logged-update) gth) +:: +++ enjs + =, enjs:format + |% + ++ update + |= upd=^update + ^- json + ?> ?=(%0 -.upd) + |^ (frond %graph-update (pairs ~[(encode q.upd)])) + :: + ++ encode + |= upd=update-0 + ^- [cord json] + ?- -.upd + %add-graph + :- %add-graph + %- pairs + :~ [%resource (enjs:res resource.upd)] + [%graph (graph graph.upd)] + [%mark ?~(mark.upd ~ s+u.mark.upd)] + == + :: + %remove-graph + [%remove-graph (enjs:res resource.upd)] + :: + %add-nodes + :- %add-nodes + %- pairs + :~ [%resource (enjs:res resource.upd)] + [%nodes (nodes nodes.upd)] + == + :: + %remove-nodes + :- %remove-nodes + %- pairs + :~ [%resource (enjs:res resource.upd)] + [%indices (indices indices.upd)] + == + :: + %add-signatures + :- %add-signatures + %- pairs + :~ [%uid (uid uid.upd)] + [%signatures (signatures signatures.upd)] + == + :: + %remove-signatures + :- %remove-signatures + %- pairs + :~ [%uid (uid uid.upd)] + [%signatures (signatures signatures.upd)] + == + :: + %add-tag + :- %add-tag + %- pairs + :~ [%term s+term.upd] + [%resource (enjs:res resource.upd)] + == + :: + %remove-tag + :- %remove-tag + %- pairs + :~ [%term s+term.upd] + [%resource (enjs:res resource.upd)] + == + :: + %archive-graph + [%archive-graph (enjs:res resource.upd)] + :: + %unarchive-graph + [%unarchive-graph (enjs:res resource.upd)] + :: + %keys + [%keys [%a (turn ~(tap in resources.upd) enjs:res)]] + :: + %tags + [%tags [%a (turn ~(tap in tags.upd) |=(=term s+term))]] + :: + %run-updates + [%run-updates ~] + :: + %tag-queries + :- %tag-queries + %- pairs + %+ turn ~(tap by tag-queries.upd) + |= [=term =resources] + ^- [cord json] + [term [%a (turn ~(tap in resources) enjs:res)]] + == + :: + ++ graph + |= g=^graph + ^- json + :- %a + %+ turn (tap:orm g) + |= [a=atom n=^node] + ^- json + :- %a + :~ (index [a]~) + (node n) + == + :: + ++ index + |= i=^index + ^- json + =/ j=^tape "" + |- + ?~ i [%s (crip j)] + =/ k=json (numb i.i) + ?> ?=(%n -.k) + %_ $ + i t.i + j (weld j (weld "/" (trip +.k))) + == + :: + ++ node + |= n=^node + ^- json + %- pairs + :~ [%post (post post.n)] + :- %children + ?- -.children.n + %empty ~ + %graph (graph +.children.n) + == + == + :: + ++ post + |= p=^post + ^- json + %- pairs + :~ [%author (ship author.p)] + [%index (index index.p)] + [%time-sent (time time-sent.p)] + [%contents [%a (turn contents.p content)]] + [%hash ?~(hash.p ~ s+(scot %ux u.hash.p))] + [%signatures (signatures signatures.p)] + == + :: + ++ content + |= c=^content + ^- json + ?- -.c + %text (frond %text s+text.c) + %url (frond %url s+url.c) + %reference (frond %reference (uid uid.c)) + %code + %+ frond %code + %- pairs + :- [%expression s+expression.c] + :_ ~ + :- %output + :: virtualize output rendering, +tank:enjs:format might crash + :: + =/ result=(each (list json) tang) + (mule |.((turn output.c tank))) + ?- -.result + %& a+p.result + %| a+[a+[%s '[[output rendering error]]']~]~ + == + == + :: + ++ nodes + |= m=(map ^index ^node) + ^- json + :- %a + %+ turn ~(tap by m) + |= [n=^index o=^node] + ^- json + :- %a + :~ (index n) + (node o) + == + :: + ++ indices + |= i=(set ^index) + ^- json + [%a (turn ~(tap in i) index)] + :: + ++ uid + |= u=^uid + ^- json + %- pairs + :~ [%resource (enjs:res resource.u)] + [%index (index index.u)] + == + :: + ++ signatures + |= s=^signatures + ^- json + [%a (turn ~(tap in s) signature)] + :: + ++ signature + |= s=^signature + ^- json + %- pairs + :~ [%signature s+(scot %ux p.s)] + [%ship (ship q.s)] + [%life (numb r.s)] + == + -- + -- +:: +++ dejs + =, dejs:format + |% + ++ update + |= jon=json + ^- ^update + :- %0 + :- *time + ^- update-0 + =< (decode jon) + |% + ++ decode + %- of + :~ [%add-graph add-graph] + [%remove-graph remove-graph] + [%add-nodes add-nodes] + [%remove-nodes remove-nodes] + [%add-signatures add-signatures] + [%remove-signatures remove-signatures] + [%add-tag add-tag] + [%remove-tag remove-tag] + [%archive-graph archive-graph] + [%unarchive-graph unarchive-graph] + [%keys keys] + [%tags tags] + [%tag-queries tag-queries] + [%run-updates run-updates] + == + :: + ++ add-graph + %- ot + :~ [%resource dejs:res] + [%graph graph] + [%mark (mu so)] + == + :: + ++ graph + |= a=json + ^- ^graph + =/ or-mp ((ordered-map atom ^node) gth) + %+ gas:or-mp ~ + %+ turn ~(tap by ((om node) a)) + |* [b=cord c=*] + ^- [atom ^node] + => .(+< [b c]=+<) + [(rash b dem) c] + :: + ++ remove-graph (ot [%resource dejs:res]~) + ++ archive-graph (ot [%resource dejs:res]~) + ++ unarchive-graph (ot [%resource dejs:res]~) + :: + ++ add-nodes + %- ot + :~ [%resource dejs:res] + [%nodes nodes] + == + :: + ++ nodes (op ;~(pfix net (more net dem)) node) + :: + ++ node + %- ot + :~ [%post post] + :: TODO: support adding nodes with children by supporting the + :: graph key + [%children (of [%empty ul]~)] + == + :: + ++ post + %- ot + :~ [%author (su ;~(pfix sig fed:ag))] + [%index index] + [%time-sent di] + [%contents (ar content)] + [%hash (mu nu)] + [%signatures (as signature)] + == + :: + ++ content + %- of + :~ [%text so] + [%url so] + [%reference uid] + [%code eval] + == + :: + ++ eval + |= a=^json + ^- [cord (list tank)] + =, ^? dejs-soft:format + =+ exp=((ot expression+so ~) a) + %- need + ?~ exp [~ '' ~] + :+ ~ u.exp + :: NOTE: when sending, if output is an empty list, + :: graph-store will evaluate + (fall ((ot output+(ar dank) ~) a) ~) + :: + ++ remove-nodes + %- ot + :~ [%resource dejs:res] + [%indices (as index)] + == + :: + ++ add-signatures + %- ot + :~ [%uid uid] + [%signatures (as signature)] + == + :: + ++ remove-signatures + %- ot + :~ [%uid uid] + [%signatures (as signature)] + == + :: + ++ signature + %- ot + :~ [%hash nu] + [%ship (su ;~(pfix sig fed:ag))] + [%life ni] + == + :: + ++ uid + %- ot + :~ [%resource dejs:res] + [%index index] + == + :: + ++ index (su ;~(pfix net (more net dem))) + :: + ++ add-tag + %- ot + :~ [%term so] + [%resource dejs:res] + == + :: + ++ remove-tag + %- ot + :~ [%term so] + [%resource dejs:res] + == + :: + ++ keys + |= =json + *resources + :: + ++ tags + |= =json + *(set term) + :: + ++ tag-queries + |= =json + *^tag-queries + :: + ++ run-updates + |= a=json + ^- [resource update-log] + [*resource *update-log] + -- + -- +:: +++ create + |_ [our=ship now=time] + ++ post + |= [=index contents=(list content)] + ^- ^post + :* our + index + now + contents + ~ + *signatures + == + -- +-- diff --git a/pkg/arvo/lib/graph.hoon b/pkg/arvo/lib/graph.hoon new file mode 100644 index 000000000..395c0554f --- /dev/null +++ b/pkg/arvo/lib/graph.hoon @@ -0,0 +1,24 @@ +/- *resource +/+ store=graph-store +|_ =bowl:gall +++ scry-for + |* [=mold =path] + .^ mold + %gx + (scot %p our.bowl) + %graph-store + (scot %da now.bowl) + (snoc `^path`path %noun) + == +:: +++ get-graph + |= res=resource + ^- marked-graph:store + %+ scry-for marked-graph:store + /graph/(scot %p entity.res)/[name.res] +:: +++ peek-log + |= res=resource + ^- (unit time) + (scry-for (unit time) /peek-update-log/(scot %p entity.res)/[name.res]) +-- diff --git a/pkg/arvo/lib/hood/drum.hoon b/pkg/arvo/lib/hood/drum.hoon index 0afec46ce..e7b91cfbe 100644 --- a/pkg/arvo/lib/hood/drum.hoon +++ b/pkg/arvo/lib/hood/drum.hoon @@ -104,6 +104,7 @@ %s3-store %file-server %glob + %graph-store == :: ++ deft-fish :: default connects @@ -206,7 +207,7 @@ == :: ++ on-load - |= [hood-version=?(%1 %2 %3 %4 %5 %6 %7 %8) old=any-state] + |= [hood-version=?(%1 %2 %3 %4 %5 %6 %7 %8 %9) old=any-state] =< se-abet =< se-view =. sat old =. dev (~(gut by bin) ost *source) @@ -233,6 +234,8 @@ =? ..on-load (lte hood-version %8) => (se-born | %home %group-push-hook) (se-born | %home %group-pull-hook) + =? ..on-load (lte hood-version %9) + (se-born | %home %graph-store) ..on-load :: ++ reap-phat :: ack connect diff --git a/pkg/arvo/lib/hood/kiln.hoon b/pkg/arvo/lib/hood/kiln.hoon index 07f04de83..21273f827 100644 --- a/pkg/arvo/lib/hood/kiln.hoon +++ b/pkg/arvo/lib/hood/kiln.hoon @@ -208,7 +208,7 @@ :: ++ get-germ |= =desk - =+ .^(=cass:clay %cw /(scot %p our)/home/(scot %da now)) + =+ .^(=cass:clay %cw /(scot %p our)/[desk]/(scot %da now)) ?- ud.cass %0 %init %1 %that @@ -341,13 +341,22 @@ abet:(spam (render "already syncing" [sud her syd]:hos) ~) abet:abet:start-sync:(auto hos) :: +++ ota-info + ?~ ota + "OTAs disabled" + "OTAs enabled from {} on {}" +:: +++ poke-ota-info + |= * + =< abet %- spam + :~ [%leaf ota-info] + [%leaf "use |ota %disable or |ota ~sponsor %kids to reset it"] + == +:: ++ poke-syncs :: print sync config |= ~ =< abet %- spam - :- :- %leaf - ?~ ota - "OTAs disabled" - "OTAs from {} on {}" + :- [%leaf ota-info] ?: =(0 ~(wyt by syn)) [%leaf "no other syncs configured"]~ %+ turn ~(tap in ~(key by syn)) @@ -416,6 +425,7 @@ %kiln-merge =;(f (f !<(_+<.f vase)) poke-merge) %kiln-mount =;(f (f !<(_+<.f vase)) poke-mount) %kiln-ota =;(f (f !<(_+<.f vase)) poke:update) + %kiln-ota-info =;(f (f !<(_+<.f vase)) poke-ota-info) %kiln-permission =;(f (f !<(_+<.f vase)) poke-permission) %kiln-rm =;(f (f !<(_+<.f vase)) poke-rm) %kiln-schedule =;(f (f !<(_+<.f vase)) poke-schedule) diff --git a/pkg/arvo/lib/push-hook.hoon b/pkg/arvo/lib/push-hook.hoon index 4958085a2..776aa0f59 100644 --- a/pkg/arvo/lib/push-hook.hoon +++ b/pkg/arvo/lib/push-hook.hoon @@ -59,7 +59,6 @@ |~ [term tang] *[(list card) _^|(..on-init)] :: +resource-for-update: get affected resource from an update - ++ resource-for-update |~ vase *(unit resource) diff --git a/pkg/arvo/lib/signatures.hoon b/pkg/arvo/lib/signatures.hoon new file mode 100644 index 000000000..12b58a644 --- /dev/null +++ b/pkg/arvo/lib/signatures.hoon @@ -0,0 +1,43 @@ +/- post +^? +=< [post .] +=, post +|% +++ sign + |= [our=ship now=time =hash] + ^- signature + =/ =life .^(life %j /=life/(scot %da now)/(scot %p our)) + =/ =ring .^(ring %j /=vein/(scot %da now)/(scot %ud life)) + :+ `@ux`(sign:as:(nol:nu:crub:crypto ring) hash) + our + life +:: +++ is-signature-valid + |= [=signature =hash now=time] + ^- ? + =/ deed=(unit [a=life b=pass c=(unit @ux)]) + .^ (unit [life pass (unit @ux)]) + %j + /=deed/(scot %da now)/(scot %p q.signature)/(scot %ud p.signature) + == + :: we do not have a public key from ship + :: + ?~ deed %.y + :: we do not have a public key from ship at this life + :: + ?. =(a.u.deed r.signature) %.y + :: verify signature from ship at life + :: + =(`hash (tear:as:crub:crypto b.u.deed p.signature)) +:: +++ are-signatures-valid + |= [=signatures =hash now=time] + ^- ? + =/ signature-list ~(tap in signatures) + |- + ?~ signature-list + %.y + ?: (is-signature-valid i.signature-list hash now) + $(signature-list t.signature-list) + %.n +-- diff --git a/pkg/arvo/mar/graph/update.hoon b/pkg/arvo/mar/graph/update.hoon new file mode 100644 index 000000000..d5f0f4abe --- /dev/null +++ b/pkg/arvo/mar/graph/update.hoon @@ -0,0 +1,13 @@ +/+ *graph-store +|_ upd=update +++ grow + |% + ++ json (update:enjs upd) + -- +:: +++ grab + |% + ++ noun update + ++ json update:dejs + -- +-- diff --git a/pkg/arvo/mar/graph/validator/chat.hoon b/pkg/arvo/mar/graph/validator/chat.hoon new file mode 100644 index 000000000..c49373514 --- /dev/null +++ b/pkg/arvo/mar/graph/validator/chat.hoon @@ -0,0 +1,17 @@ +/- *post +|_ i=indexed-post +++ grow + |% + ++ noun i + -- +++ grab + |% + ++ noun + |= p=* + =/ ip ;;(indexed-post p) + ?> ?=([@ ~] index.p.ip) + ip + -- +:: +++ grad %noun +-- diff --git a/pkg/arvo/sur/graph-store.hoon b/pkg/arvo/sur/graph-store.hoon new file mode 100644 index 000000000..edcfb4135 --- /dev/null +++ b/pkg/arvo/sur/graph-store.hoon @@ -0,0 +1,61 @@ +/- *post +|% ++$ graph ((mop atom node) gth) ++$ marked-graph [p=graph q=(unit mark)] +:: ++$ node [=post children=internal-graph] ++$ graphs (map resource marked-graph) +:: ++$ tag-queries (jug term resource) +:: ++$ update-log ((mop time logged-update) gth) ++$ update-logs (map resource update-log) +:: ++$ internal-graph + $~ [%empty ~] + $% [%graph p=graph] + [%empty ~] + == +:: ++$ network + $: =graphs + =tag-queries + =update-logs + archive=graphs + validators=(set mark) + == +:: ++$ update + $% [%0 p=time q=update-0] + == +:: ++$ logged-update + $% [%0 p=time q=logged-update-0] + == +:: ++$ logged-update-0 + $% [%add-nodes =resource nodes=(map index node)] + [%remove-nodes =resource indices=(set index)] + [%add-signatures =uid =signatures] + [%remove-signatures =uid =signatures] + == +:: ++$ update-0 + $% logged-update-0 + [%add-graph =resource =graph mark=(unit mark)] + [%remove-graph =resource] + :: + [%add-tag =term =resource] + [%remove-tag =term =resource] + :: + [%archive-graph =resource] + [%unarchive-graph =resource] + [%run-updates =resource =update-log] + :: + :: NOTE: cannot be sent as pokes + :: + [%keys =resources] + [%tags tags=(set term)] + [%tag-queries =tag-queries] + == +-- diff --git a/pkg/arvo/sur/post.hoon b/pkg/arvo/sur/post.hoon new file mode 100644 index 000000000..c5c354615 --- /dev/null +++ b/pkg/arvo/sur/post.hoon @@ -0,0 +1,37 @@ +/- *resource +|% ++$ index (list atom) ++$ uid [=resource =index] +:: +:: +sham (half sha-256) hash of +validated-portion ++$ hash @ux +:: ++$ signature [p=@ux q=ship r=life] ++$ signatures (set signature) ++$ post + $: author=ship + =index + time-sent=time + contents=(list content) + hash=(unit hash) + =signatures + == +:: ++$ indexed-post [a=atom p=post] +:: ++$ validated-portion + $: parent-hash=(unit hash) + author=ship + time-sent=time + contents=(list content) + == +:: ++$ content + $% [%text text=cord] + [%url url=cord] + [%code expression=cord output=(list tank)] + [%reference =uid] + :: TODO: maybe use a cask? + ::[%cage =cage] + == +-- diff --git a/pkg/arvo/sur/pull-hook.hoon b/pkg/arvo/sur/pull-hook.hoon index 81655a181..1c66648c1 100644 --- a/pkg/arvo/sur/pull-hook.hoon +++ b/pkg/arvo/sur/pull-hook.hoon @@ -8,5 +8,4 @@ +$ update $% [%tracking tracking=(map resource ship)] == -:: -- diff --git a/pkg/arvo/sys/hoon.hoon b/pkg/arvo/sys/hoon.hoon index 824b3a2cd..369977f8a 100644 --- a/pkg/arvo/sys/hoon.hoon +++ b/pkg/arvo/sys/hoon.hoon @@ -7762,11 +7762,13 @@ ++ teal |= mod/spec ^- spec + ?: ?=(%& -.tik) mod [%over [%& 3]~ mod] :: ++ tele |= syn/skin ^- skin + ?: ?=(%& -.tik) syn [%over [%& 3]~ syn] :: ++ gray diff --git a/pkg/arvo/sys/vane/ames.hoon b/pkg/arvo/sys/vane/ames.hoon index cbf7485b8..74b3c64b0 100644 --- a/pkg/arvo/sys/vane/ames.hoon +++ b/pkg/arvo/sys/vane/ames.hoon @@ -1121,17 +1121,32 @@ ?> =(rcvr-life.shut-packet our-life.channel) :: non-galaxy: update route with heard lane or forwarded lane :: - =? route.peer-state - ?: =(%czar (clan:title her.channel)) - %.n - =/ is-old-direct=? ?=([~ %& *] route.peer-state) - =/ is-new-direct=? ?=(~ origin.packet) - :: old direct takes precedence over new indirect - :: - |(is-new-direct !is-old-direct) + =? route.peer-state !=(%czar (clan:title her.channel)) + :: if new packet is direct, use that. otherwise, if the new new + :: and old lanes are indirect, use the new one. if the new lane + :: is indirect but the old lane is direct, then if the lanes are + :: identical, don't mark it indirect; if they're not identical, + :: use the new lane and mark it indirect. :: - ?~ origin.packet + :: if you mark lane as indirect because you got an indirect + :: packet even though you already had a direct identical lane, + :: then delayed forwarded packets will come later and reset to + :: indirect, so you're unlikely to get a stable direct route + :: (unless the forwarder goes offline for a while). + :: + :: conversely, if you don't accept indirect routes with different + :: lanes, then if your lane is stale and they're trying to talk + :: to you, your acks will go to the stale lane, and you'll never + :: time it out unless you reach out to them. this manifests as + :: needing to |hi or dotpost to get a response when the other + :: ship has changed lanes. + :: + ?: ?=(~ origin.packet) `[direct=%.y lane] + ?: ?=([~ %& *] route.peer-state) + ?: =(lane.u.route.peer-state u.origin.packet) + route.peer-state + `[direct=%.n u.origin.packet] `[direct=%.n u.origin.packet] :: perform peer-specific handling of packet :: diff --git a/pkg/arvo/sys/vane/clay.hoon b/pkg/arvo/sys/vane/clay.hoon index e60c47b00..14d23c8d4 100644 --- a/pkg/arvo/sys/vane/clay.hoon +++ b/pkg/arvo/sys/vane/clay.hoon @@ -109,6 +109,10 @@ mut/(list (trel path lobe cage)) :: mutations == :: :: +:: Over-the-wire backfill request +:: ++$ fill [=desk =lobe] +:: :: Ford cache :: +$ ford-cache @@ -214,18 +218,29 @@ :: requests, and a possible nako if we've received data from the other ship and :: are in the process of validating it. :: -++ rind :: request manager - $: nix/@ud :: request index - bom/(map @ud {p/duct q/rave}) :: outstanding - fod/(map duct @ud) :: current requests - haw/(map mood (unit cage)) :: simple cache - == :: ++$ rind :: request manager + $: nix=@ud :: request index + bom=(map @ud update-state) :: outstanding + fod=(map duct @ud) :: current requests + haw=(map mood (unit cage)) :: simple cache + == :: +:: +:: Active downloads +:: ++$ update-state + $: =duct + =rave + have=(map lobe blob) + need=(list lobe) + nako=(qeu (unit nako)) + busy=_| + == :: :: Result of a subscription :: ++ sub-result $% [%blab =mood data=(each cage lobe)] - [%bleb ins=@ud range=(unit (pair aeon aeon))] + [%bleb ver=@ud ins=@ud range=(unit (pair aeon aeon))] [%balk cage=(unit (each cage lobe)) =mood] [%blas moods=(set mood)] [%blub ~] @@ -246,7 +261,7 @@ :: Generally used when we store a request in our state somewhere. :: ++ cach (unit (unit (each cage lobe))) :: cached result -+$ wove [for=(unit ship) =rove] :: stored source + req ++$ wove [for=(unit [=ship ver=@ud]) =rove] :: stored source + req ++ rove :: stored request $% [%sing =mood] :: single request [%next =mood aeon=(unit aeon) =cach] :: next version of one @@ -1134,13 +1149,13 @@ :: Give next step in a subscription. :: ++ bleb - |= {hen/duct ins/@ud hip/(unit (pair aeon aeon))} + |= [hen=duct ver=@ud ins=@ud hip=(unit (pair aeon aeon))] ^+ +> %^ blab hen [%w [%ud ins] ~] :- %& ?~ hip [%null [%atom %n ~] ~] - [%nako !>((make-nako:ze u.hip))] + [%nako !>((make-nako:ze ver u.hip))] :: :: Tell subscriber that subscription is done. :: @@ -1183,7 +1198,7 @@ =/ =desk p.riff =/ =wire /warp-index/(scot %p ship)/(scot %tas desk)/(scot %ud index) =/ =path [%question desk (scot %ud index) ~] - (emit duct %pass wire %a %plea ship %c path riff) + (emit duct %pass wire %a %plea ship %c path [[%1 ~] riff]) :: :: Create a request that cannot be filled immediately. :: @@ -1210,7 +1225,7 @@ (send-over-ames hen her inx syd `rave) %= +>+.$ nix.u.ref +(nix.u.ref) - bom.u.ref (~(put by bom.u.ref) inx [hen rave]) + bom.u.ref (~(put by bom.u.ref) inx [hen rave ~ ~ ~ |]) fod.u.ref (~(put by fod.u.ref) hen inx) == :: @@ -2003,6 +2018,7 @@ :: bob's. :: ?: ?=(%init germ) + ?> ?=(~ bob-yaki) &+`[conflicts=~ new=|+ali-yaki lat=~] :: =/ bob-yaki (need bob-yaki) @@ -2589,7 +2605,7 @@ :: and then waiting if the subscription range extends into the future. :: ++ start-request - |= [for=(unit ship) rav=rave] + |= [for=(unit [ship @ud]) rav=rave] ^+ ..start-request =^ [new-sub=(unit rove) sub-results=(list sub-result)] fod.dom (try-fill-sub for (rave-to-rove rav)) @@ -2612,9 +2628,9 @@ ?> ?=(^ ref) =+ ruv=(~(get by bom.u.ref) inx) ?~ ruv +>.$ - =/ rav=rave q.u.ruv + =/ rav=rave rave.u.ruv ?: ?=(%many -.rav) - (take-foreign-update inx rut) + abet:(apex:(foreign-update inx) rut) ?~ rut :: nothing here, so cache that :: @@ -2689,36 +2705,138 @@ !>(;;(@uvI q.page)) -- :: - :: A full foreign update. Validate and apply to our local cache of - :: their state. + :: Respond to backfill request :: - ++ take-foreign-update - |= [inx=@ud rut=(unit rand)] - ^+ ..take-foreign-update + :: Maybe should verify the requester is allowed to access this blob? + :: + ++ give-backfill + |= =lobe + ^+ ..give-backfill + (emit hen %give %boon (~(got by lat.ran) lobe)) + :: + :: Ingest foreign update, requesting missing blobs if necessary + :: + ++ foreign-update + |= inx=@ud ?> ?=(^ ref) - =/ ruv (~(get by bom.u.ref) inx) - ?~ ruv - ~& [%clay-foreign-update-lost her syd inx] - ..take-foreign-update - =. hen p.u.ruv - =/ =rave q.u.ruv - ?> ?=(%many -.rave) - |^ - ?~ rut - done - =. lim ?.(?=(%da -.to.moat.rave) lim p.to.moat.rave) - ?> ?=(%nako p.r.u.rut) - =/ nako ;;(nako q.r.u.rut) - =. ..take-foreign-update - =< ?>(?=(^ ref) .) - (apply-foreign-update nako) - done + =/ [sat=update-state lost=?] + =/ ruv (~(get by bom.u.ref) inx) + ?~ ruv + ~& [%clay-foreign-update-lost her syd inx] + [*update-state &] + [u.ruv |] + =/ done=? | + =. hen duct.sat + |% + ++ abet + ^+ ..foreign-update + ?: lost + ..foreign-update + ?: done + =: bom.u.ref (~(del by bom.u.ref) inx) + fod.u.ref (~(del by fod.u.ref) hen) + == + =<(?>(?=(^ ref) .) wake) + =. bom.u.ref (~(put by bom.u.ref) inx sat) + ..foreign-update :: - ++ done - =: bom.u.ref (~(del by bom.u.ref) inx) - bom.u.ref (~(del by bom.u.ref) hen) - == - =<(?>(?=(^ ref) .) wake) + ++ apex + |= rut=(unit rand) + ^+ ..abet + ?: lost ..abet + ?~ rut + =. nako.sat (~(put to nako.sat) ~) + work + ?> ?=(%nako p.r.u.rut) + =/ nako ;;(nako q.r.u.rut) + =/ missing (missing-blobs nako) + =. need.sat `(list lobe)`(welp need.sat ~(tap in missing)) + =. nako.sat (~(put to nako.sat) ~ nako) + work + :: + ++ missing-blobs + |= =nako + ^- (set lobe) + =/ yakis ~(tap in lar.nako) + |- ^- (set lobe) + =* yaki-loop $ + ?~ yakis + ~ + =/ lobes=(list [=path =lobe]) ~(tap by q.i.yakis) + |- ^- (set lobe) + =* blob-loop $ + ?~ lobes + yaki-loop(yakis t.yakis) + ?: (~(has by lat.ran) lobe.i.lobes) + blob-loop(lobes t.lobes) + (~(put in blob-loop(lobes t.lobes)) lobe.i.lobes) + :: + :: Receive backfill response + :: + ++ take-backfill + |= =blob + ^+ ..abet + ?: lost ..abet + =? need.sat + ?& ?=(%delta -.blob) + !(~(has by lat.ran) q.q.blob) + !(~(has by have.sat) q.q.blob) + == + [q.q.blob need.sat] + :: We can't put a blob in lat.ran if its parent isn't already + :: there. Unions are in reverse order so we don't overwrite + :: existing blobs. + :: + =. ..abet + ?: &(?=(%delta -.blob) !(~(has by lat.ran) q.q.blob)) + ..abet(have.sat (~(uni by (malt [p.blob `^blob`blob] ~)) have.sat)) + ..abet(lat.ran (~(uni by (malt [p.blob blob] ~)) lat.ran)) + work(busy.sat |) + :: + :: Fetch next blob + :: + ++ work + ^+ ..abet + ?: busy.sat + ..abet + |- ^+ ..abet + ?: =(~ need.sat) + :: NB: if you change to release nakos as we get enough blobs + :: for them instead of all at the end, you *must* store the + :: `lim` that should be applied after the nako is complete and + :: not use the one in the rave, since that will apply to the + :: end of subscription. + :: + =. lat.ran (~(uni by have.sat) lat.ran) + |- ^+ ..abet + ?: =(~ nako.sat) + ..abet + =^ next=(unit nako) nako.sat ~(get to nako.sat) + ?~ next + ..abet(done &) + =. ..abet (apply-foreign-update u.next) + =. ..foreign-update =<(?>(?=(^ ref) .) wake) + $ + ?> ?=(^ need.sat) + :: This is what removes an item from `need`. This happens every + :: time we take a backfill response, but it could happen more than + :: once if we somehow got this data in the meantime (maybe from + :: another desk updating concurrently, or a previous update on this + :: same desk). + :: + ?: ?| (~(has by lat.ran) i.need.sat) + (~(has by have.sat) i.need.sat) + == + $(need.sat t.need.sat) + :: Otherwise, fetch the next blob + :: + =/ =fill [syd i.need.sat] + =/ =wire /back-index/(scot %p her)/[syd]/(scot %ud inx) + =/ =path [%backfill syd (scot %ud inx) ~] + =. ..foreign-update + =< ?>(?=(^ ref) .) + (emit hen %pass wire %a %plea her %c path fill) + ..abet(busy.sat &) :: :: When we get a %w foreign update, store this in our state. :: @@ -2728,7 +2846,7 @@ :: ++ apply-foreign-update |= =nako - ^+ ..take-foreign-update + ^+ ..abet :: hit: updated commit-hashes by @ud case :: nut: new commit-hash/commit pairs :: hut: updated commits by hash @@ -2765,12 +2883,19 @@ $(aeon +(aeon)) :: produce updated state :: + =/ =rave rave:(~(got by bom.u.ref) inx) + ?> ?=(%many -.rave) =: let.dom (max let.nako let.dom) hit.dom hit hut.ran hut lat.ran lat + :: Is this correct? Seeems like it should only go to `to` if + :: we've gotten all the way to the end. Leaving this + :: behavior unchanged for now, but I believe it's wrong. + :: + lim ?.(?=(%da -.to.moat.rave) lim p.to.moat.rave) == - ..take-foreign-update + ..abet -- :: :: fire function if request is in future @@ -2862,8 +2987,9 @@ :: Try to fill a subscription :: ++ try-fill-sub - |= [for=(unit ship) rov=rove] + |= [far=(unit [=ship ver=@ud]) rov=rove] ^- [[new-sub=(unit rove) (list sub-result)] ford-cache] + =/ for=(unit ship) ?~(far ~ `ship.u.far) ?- -.rov %sing =/ cache-value=(unit (unit cage)) @@ -3075,6 +3201,7 @@ :: [`rov ~] =/ to-aeon (case-to-aeon to.moat.rov) + =/ ver ?~(far %1 ver.u.far) ?~ to-aeon :: we're in the middle of the range, so produce what we can, :: but don't end the subscription @@ -3092,7 +3219,7 @@ ~ :: else changes, so produce them :: - [%bleb let.dom ?:(track.rov ~ `[u.from-aeon let.dom])]~ + [%bleb ver let.dom ?:(track.rov ~ `[u.from-aeon let.dom])]~ :: we're past the end of the range, so end subscription :: :- ~ @@ -3103,7 +3230,7 @@ =/ bleb=(list sub-result) ?: =(lobes.rov new-lobes) ~ - [%bleb +(u.from-aeon) ?:(track.rov ~ `[u.from-aeon u.to-aeon])]~ + [%bleb ver +(u.from-aeon) ?:(track.rov ~ `[u.from-aeon u.to-aeon])]~ :: end subscription :: =/ blub=(list sub-result) @@ -3111,17 +3238,6 @@ (weld bleb blub) == :: - ++ drop-me - ^+ . - ~| %clay-drop-me-not-implemented - !! - :: ?~ mer - :: . - :: %- emit(mer ~) ^- move :* - :: hen.u.mer %give %mere %| %user-interrupt - :: >sor.u.mer< >our< >cas.u.mer< >gem.u.mer< ~ - :: == - :: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: :: :: This core has no additional state, and the distinction exists purely for @@ -3209,7 +3325,7 @@ :: Creates a nako of all the changes between a and b. :: ++ make-nako - |= {a/aeon b/aeon} + |= [ver=@ud a=aeon b=aeon] ^- nako :+ ?> (lte b let.dom) |- @@ -3219,7 +3335,7 @@ b ?: =(0 b) [~ ~] - (data-twixt-takos (~(get by hit.dom) a) (aeon-to-tako b)) + (data-twixt-takos =(0 ver) (~(get by hit.dom) a) (aeon-to-tako b)) :: :: Traverse parentage and find all ancestor hashes :: @@ -3245,16 +3361,21 @@ :: ones we found before `a`. Then convert the takos to yakis and also get :: all the data in all the yakis. :: + :: What happens if you run an %init merge on a desk that already + :: had a commit? + :: ++ data-twixt-takos - |= {a/(unit tako) b/tako} - ^- {(set yaki) (set plop)} + |= [plops=? a=(unit tako) b=tako] + ^- [(set yaki) (set plop)] =+ old=?~(a ~ (reachable-takos u.a)) - =/ yal/(set tako) + =/ yal=(set tako) %- silt %+ skip ~(tap in (reachable-takos b)) - |=(tak/tako (~(has in old) tak)) + |=(tak=tako (~(has in old) tak)) :- (silt (turn ~(tap in yal) tako-to-yaki)) + ?. plops + ~ (silt (turn ~(tap in (new-lobes (new-lobes ~ old) yal)) lobe-to-blob)) :: :: Get all the lobes that are referenced in `a` except those that are @@ -3728,7 +3849,7 @@ :: :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: =| :: instrument state - $: ver=%3 :: vane version + $: ver=%4 :: vane version ruf=raft :: revision tree == :: |= [our=ship now=@da eny=@uvJ ski=sley] :: current invocation @@ -3939,7 +4060,14 @@ =^ for req ?: ?=(%warp -.req) [~ req] - :- ?:(=(our who.req) ~ `who.req) + :: ?: =(our who.req) + :: [~ [%warp wer.req rif.req]] + =^ ver rif.req + ?@ -.rif.req + [%0 rif.req] + [-<.rif.req +.rif.req] + ?> ?=(@ -.rif.req) + :- ?:(=(our who.req) ~ `[who.req ver]) [%warp wer.req rif.req] :: ?> ?=(%warp -.req) @@ -3957,8 +4085,14 @@ =* pax path.plea.req =* res payload.plea.req :: - ?> ?=({%question *} pax) - =+ ryf=;;(riff res) + ?: ?=([%backfill *] pax) + =+ ;;(=fill res) + =^ mos ruf + =/ den ((de our now ski hen ruf) our desk.fill) + abet:(give-backfill:den +.fill) + [[[hen %give %done ~] mos] ..^$] + ?> ?=([%question *] pax) + =+ ryf=;;(riff-any res) :_ ..^$ :~ [hen %give %done ~] =/ =wire @@ -3971,11 +4105,58 @@ !: |^ |= old=any-state - ~! [old=old new=*state-3] + ~! [old=old new=*state-4] =? old ?=(%2 -.old) (load-2-to-3 old) - ?> ?=(%3 -.old) + =? old ?=(%3 -.old) (load-3-to-4 old) + ?> ?=(%4 -.old) ..^^$(ruf +.old) :: + ++ load-3-to-4 + |= =state-3 + ^- state-4 + |^ + =- state-3(- %4, hoy hoy.-, rom (room-3-to-4 rom.state-3)) + ^- hoy=(map ship rung) + %- ~(run by hoy.state-3) + |= =rung-3 + ^- rung + %- ~(run by rus.rung-3) + |= =rede-3 + ^- rede + =- rede-3(ref ref.-, qyx (cult-3-to-4 qyx.rede-3)) + ^- ref=(unit rind) + ?~ ref.rede-3 + ~ + =- `u.ref.rede-3(bom bom.-) + ^- bom=(map @ud update-state) + %- ~(run by bom.u.ref.rede-3) + |= [=duct =rave] + ^- update-state + [duct rave ~ ~ ~ |] + :: + ++ room-3-to-4 + |= =room-3 + ^- room + =- room-3(dos dos.-) + ^- dos=(map desk dojo) + %- ~(run by dos.room-3) + |= =dojo-3 + ^- dojo + dojo-3(qyx (cult-3-to-4 qyx.dojo-3)) + :: + ++ cult-3-to-4 + |= =cult-3 + ^- cult + %- malt + %+ turn ~(tap by cult-3) + |= [=wove-3 ducts=(set duct)] + ^- [wove (set duct)] + :_ ducts :_ rove.wove-3 + ?~ for.wove-3 + ~ + `[u.for.wove-3 %0] + -- + :: ++ load-2-to-3 |= =state-2 ^- state-3 @@ -4005,11 +4186,11 @@ :- %ford-fusion [leaf+"queued merge canceled due to upgrade to ford fusion" ~] `[duct %slip %b %drip !>([%mere %| err])] - ^- rom=room + ^- rom=room-3 :- hun.rom.state-2 %- ~(urn by dos.rom.state-2) |= [=desk =dojo-2] - ^- dojo + ^- dojo-3 =- dojo-2(dom -) ^- dome =/ fer=(unit reef-cache) @@ -4019,23 +4200,22 @@ (~(got by hut.ran.state-2) (~(got by hit.dom.dojo-2) let.dom.dojo-2)) `(build-reef desk q.yaki) [ank let hit lab mim fod=*ford-cache fer=fer]:[dom.dojo-2 .] - ^- hoy=(map ship rung) + ^- hoy=(map ship rung-3) %- ~(run by hoy.state-2) |= =rung-2 - ^- rung + ^- rung-3 %- ~(run by rus.rung-2) |= =rede-2 - ^- rede + ^- rede-3 =- rede-2(ref ref.-, dom dom.-) :- ^- dom=dome [ank let hit lab mim fod=*ford-cache fer=~]:[dom.rede-2 .] - ^- ref=(unit rind) + ^- ref=(unit rind-3) ?~ ref.rede-2 ~ - :: TODO: somehow call +wake later to notify subscribers :- ~ - ^- rind - =/ rin=rind [nix bom fod haw]:u.ref.rede-2 + ^- rind-3 + =/ rin=rind-3 [nix bom fod haw]:u.ref.rede-2 =. rin =/ pur=(list [inx=@ud =rand *]) ~(tap by pur.u.ref.rede-2) |- ^+ rin @@ -4138,8 +4318,46 @@ -- -- :: - +$ any-state $%(state-3 state-2) - +$ state-3 [%3 raft] + +$ any-state $%(state-4 state-3 state-2) + +$ state-4 [%4 raft] + +$ state-3 + $: %3 + rom=room-3 + hoy=(map ship rung-3) + ran=rang + mon=(map term beam) + hez=(unit duct) + cez=(map @ta crew) + pud=(unit [=desk =yoki]) + pun=(list move) + == + +$ rung-3 rus=(map desk rede-3) + +$ rede-3 + $: lim/@da + ref/(unit rind-3) + qyx/cult-3 + dom/dome + per/regs + pew/regs + == + +$ rind-3 + $: nix/@ud + bom/(map @ud {p/duct q/rave}) + fod/(map duct @ud) + haw/(map mood (unit cage)) + == + +$ room-3 + $: hun/duct + dos/(map desk dojo-3) + == + ++ dojo-3 + $: qyx/cult-3 + dom/dome + per/regs + pew/regs + == + +$ cult-3 (jug wove-3 duct) + +$ wove-3 [for=(unit ship) =rove] +$ state-2 $: %2 rom=room-2 :: domestic @@ -4156,7 +4374,7 @@ dos/(map desk dojo-2) :: native desk == :: +$ dojo-2 - $: qyx/cult :: subscribers + $: qyx/cult-3 :: subscribers dom/dome-2 :: desk state per/regs :: read perms per path pew/regs :: write perms per path @@ -4172,7 +4390,7 @@ +$ rede-2 $: lim/@da :: complete to ref/(unit rind-2) :: outgoing requests - qyx/cult :: subscribers + qyx/cult-3 :: subscribers dom/dome-2 :: revision state per/regs :: read perms per path pew/regs :: write perms per path @@ -4303,6 +4521,35 @@ [mos ..^$] == :: + ?: ?=([%back-index @ @ @ ~] tea) + ?+ +<.q.hin ~| %clay-backfill-index-strange !! + %done + ?~ error.q.hin + [~ ..^$] + :: TODO better error handling + :: + ~& %clay-take-backfill-index-error^our^tea^tag.u.error.q.hin + %- (slog tang.u.error.q.hin) + [~ ..^$] + :: + %lost + ~| %clay-take-backfill-lost^our + :: TODO better error handling + !! + :: + %boon + =+ ;; =blob payload.q.hin + :: + =/ her=ship (slav %p i.t.tea) + =/ =desk (slav %tas i.t.t.tea) + =/ index=@ud (slav %ud i.t.t.t.tea) + :: + =^ mos ruf + =/ den ((de our now ski hen ruf) her desk) + abet:abet:(take-backfill:(foreign-update:den index) blob) + [mos ..^$] + == + :: ?: ?=([%sinks ~] tea) ?> ?=(%public-keys +<.q.hin) ?. ?=(%breach -.public-keys-result.q.hin) @@ -4396,7 +4643,9 @@ :+ desk %| :~ ankh+&+ank.dom.dojo mime+&+mim.dom.dojo - ford+&+fod.dom.dojo + ford-vases+&+vases.fod.dom.dojo + ford-marks+&+marks.fod.dom.dojo + ford-casts+&+casts.fod.dom.dojo == :~ domestic+|+domestic foreign+&+hoy.ruf diff --git a/pkg/arvo/sys/zuse.hoon b/pkg/arvo/sys/zuse.hoon index e1b3998a0..74a2f7990 100644 --- a/pkg/arvo/sys/zuse.hoon +++ b/pkg/arvo/sys/zuse.hoon @@ -861,7 +861,7 @@ $>(%trim vane-task) :: trim state $>(%vega vane-task) :: report upgrade {$warp wer/ship rif/riff} :: internal file req - {$werp who/ship wer/ship rif/riff} :: external file req + {$werp who/ship wer/ship rif/riff-any} :: external file req $>(%plea vane-task) :: ames request == :: -- ::able @@ -967,7 +967,10 @@ who/(pair (set ship) (map @ta crew)) :: == :: ++ regs (map path rule) :: rules for paths - ++ riff {p/desk q/(unit rave)} :: request+desist + +$ riff [p=desk q=(unit rave)] :: request+desist + +$ riff-any + $^ [[%1 ~] riff] + riff ++ rite :: new permissions $% {$r red/(unit rule)} :: for read {$w wit/(unit rule)} :: for write diff --git a/pkg/arvo/ted/ph/breach-multiple.hoon b/pkg/arvo/ted/ph/breach-multiple.hoon index 4c76bc8a6..d486f9d43 100644 --- a/pkg/arvo/ted/ph/breach-multiple.hoon +++ b/pkg/arvo/ted/ph/breach-multiple.hoon @@ -11,14 +11,14 @@ ;< ~ bind:m (spawn az ~marbud) ;< ~ bind:m (real-ship az ~bud) ;< ~ bind:m (real-ship az ~marbud) -;< file=@t bind:m (touch-file ~bud %base %foo) +;< file=@t bind:m (touch-file ~bud %kids %foo) ;< ~ bind:m (check-file-touched ~marbud %home file) ;< ~ bind:m (breach-and-hear az ~bud ~marbud) ;< ~ bind:m (real-ship az ~bud) ;< ~ bind:m (breach-and-hear az ~marbud ~bud) ;< ~ bind:m (real-ship az ~marbud) -;< file=@t bind:m (touch-file ~bud %base %bar) -;< file=@t bind:m (touch-file ~bud %base %baz) +;< file=@t bind:m (touch-file ~bud %kids %bar) +;< file=@t bind:m (touch-file ~bud %kids %baz) ;< ~ bind:m (check-file-touched ~marbud %home file) ;< ~ bind:m end-azimuth (pure:m *vase) diff --git a/pkg/arvo/ted/ph/breach-sudden.hoon b/pkg/arvo/ted/ph/breach-sudden.hoon index ae6af62dd..5491c84c4 100644 --- a/pkg/arvo/ted/ph/breach-sudden.hoon +++ b/pkg/arvo/ted/ph/breach-sudden.hoon @@ -13,13 +13,13 @@ ;< ~ bind:m (spawn az ~marbud) ;< ~ bind:m (real-ship az ~bud) ;< ~ bind:m (real-ship az ~marbud) -;< file=@t bind:m (touch-file ~bud %base %foo) +;< file=@t bind:m (touch-file ~bud %kids %foo) ;< ~ bind:m (check-file-touched ~marbud %home file) ;< ~ bind:m (breach az ~bud) ;< ~ bind:m (real-ship az ~bud) -;< ~ bind:m (dojo ~bud "|merge %base ~marbud %kids, =gem %this") -;< file=@t bind:m (touch-file ~bud %base %bar) -;< file=@t bind:m (touch-file ~bud %base %baz) +;< ~ bind:m (dojo ~bud "|merge %home ~marbud %kids, =gem %this") +;< file=@t bind:m (touch-file ~bud %kids %bar) +;< file=@t bind:m (touch-file ~bud %kids %baz) ;< ~ bind:m (check-file-touched ~marbud %home file) ;< ~ bind:m end-azimuth (pure:m *vase) diff --git a/pkg/arvo/ted/ph/breach-sync.hoon b/pkg/arvo/ted/ph/breach-sync.hoon index 833ececd1..a603c779f 100644 --- a/pkg/arvo/ted/ph/breach-sync.hoon +++ b/pkg/arvo/ted/ph/breach-sync.hoon @@ -13,6 +13,10 @@ ;< ~ bind:m (real-ship az ~marbud) ;< file=@t bind:m (touch-file ~bud %kids %foo) ;< ~ bind:m (check-file-touched ~marbud %home file) +:: Merge so that when we unify history with the %this merge later, we +:: don't get a spurious conflict in %home +:: +;< ~ bind:m (dojo ~marbud "|merge %kids our %home") ;< ~ bind:m (breach-and-hear az ~bud ~marbud) ;< ~ bind:m (real-ship az ~bud) ;< ~ bind:m (dojo ~bud "|merge %kids ~marbud %kids, =gem %this") diff --git a/pkg/arvo/ted/ph/child-sync.hoon b/pkg/arvo/ted/ph/child-sync.hoon index 291ce598e..381a67064 100644 --- a/pkg/arvo/ted/ph/child-sync.hoon +++ b/pkg/arvo/ted/ph/child-sync.hoon @@ -7,7 +7,8 @@ ;< ~ bind:m start-simple ;< ~ bind:m (raw-ship ~bud ~) ;< ~ bind:m (raw-ship ~marbud ~) -;< file=@t bind:m (touch-file ~bud %base %foo) +;< file=@t bind:m (touch-file ~bud %home %foo) +;< ~ bind:m (dojo ~bud "|merge %kids our %home") ;< ~ bind:m (check-file-touched ~marbud %home file) ;< ~ bind:m end-simple (pure:m *vase) diff --git a/pkg/arvo/ted/ph/child-update.hoon b/pkg/arvo/ted/ph/child-update.hoon index fc0878ad2..d505e74ea 100644 --- a/pkg/arvo/ted/ph/child-update.hoon +++ b/pkg/arvo/ted/ph/child-update.hoon @@ -29,15 +29,15 @@ %^ cat 3 (get-val /mar/js/hoon) ' ~& > new-val=new-val .' =/ js-contents - %^ cat 3 (get-val /app/publish/js/index/js) + %^ cat 3 (get-val /app/landscape/js/channel/js) 'extra' =/ files :~ [/sys/zuse/hoon zuse-contents] [/mar/js/hoon mar-contents] - [/app/publish/js/index/js js-contents] + [/app/landscape/js/channel/js js-contents] == ;< ~ bind:m (send-events (insert-files:util her desk files)) - (pure:m /app/publish/js/index/js js-contents) + (pure:m /app/landscape/js/channel/js js-contents) :: ++ aqua-path |= =path diff --git a/pkg/interface/package-lock.json b/pkg/interface/package-lock.json index c4c1df3b4..27a526c50 100644 --- a/pkg/interface/package-lock.json +++ b/pkg/interface/package-lock.json @@ -1393,6 +1393,42 @@ "tslib": "^1.11.1" } }, + "@reach/disclosure": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@reach/disclosure/-/disclosure-0.10.5.tgz", + "integrity": "sha512-DCae28vcL7wXJNt8hySI2uaowEJ6KPDJ9U14xQMkMs0/lH7Tz8PoAO3llf7csEXk/4kzjnDpkyobDiEV3pz05g==", + "requires": { + "@reach/auto-id": "0.10.5", + "@reach/utils": "0.10.5", + "tslib": "^2.0.0" + }, + "dependencies": { + "@reach/auto-id": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@reach/auto-id/-/auto-id-0.10.5.tgz", + "integrity": "sha512-we4/bwjFxJ3F+2eaddQ1HltbKvJ7AB8clkN719El7Zugpn/vOjfPMOVUiBqTmPGLUvkYrq4tpuFwLvk2HyOVHg==", + "requires": { + "@reach/utils": "0.10.5", + "tslib": "^2.0.0" + } + }, + "@reach/utils": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.10.5.tgz", + "integrity": "sha512-5E/xxQnUbmpI/LrufBAOXjunl96DnqX6B4zC2MO2KH/dRzLug5gM5VuOwV26egsp0jvsSPxojwciOhS43px3qw==", + "requires": { + "@types/warning": "^3.0.0", + "tslib": "^2.0.0", + "warning": "^4.0.3" + } + }, + "tslib": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", + "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" + } + } + }, "@reach/menu-button": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@reach/menu-button/-/menu-button-0.10.1.tgz", @@ -1443,6 +1479,53 @@ "tslib": "^1.11.1" } }, + "@reach/tabs": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@reach/tabs/-/tabs-0.10.5.tgz", + "integrity": "sha512-oQJxQ9FwFsXo2HxEzJxFU/wP31bPVh4VU54NlhHW9f49uofyYkIKBbAhdF0Zb3TnaFp4cGKPHX39pXBYGPDkAQ==", + "requires": { + "@reach/auto-id": "0.10.5", + "@reach/descendants": "0.10.5", + "@reach/utils": "0.10.5", + "prop-types": "^15.7.2", + "tslib": "^2.0.0" + }, + "dependencies": { + "@reach/auto-id": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@reach/auto-id/-/auto-id-0.10.5.tgz", + "integrity": "sha512-we4/bwjFxJ3F+2eaddQ1HltbKvJ7AB8clkN719El7Zugpn/vOjfPMOVUiBqTmPGLUvkYrq4tpuFwLvk2HyOVHg==", + "requires": { + "@reach/utils": "0.10.5", + "tslib": "^2.0.0" + } + }, + "@reach/descendants": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@reach/descendants/-/descendants-0.10.5.tgz", + "integrity": "sha512-8HhN4DwS/HsPQ+Ym/Ft/XJ1spXBYdE8hqpnbYR9UcU7Nx3oDbTIdhjA6JXXt23t5avYIx2jRa8YHCtVKSHuiwA==", + "requires": { + "@reach/utils": "0.10.5", + "tslib": "^2.0.0" + } + }, + "@reach/utils": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.10.5.tgz", + "integrity": "sha512-5E/xxQnUbmpI/LrufBAOXjunl96DnqX6B4zC2MO2KH/dRzLug5gM5VuOwV26egsp0jvsSPxojwciOhS43px3qw==", + "requires": { + "@types/warning": "^3.0.0", + "tslib": "^2.0.0", + "warning": "^4.0.3" + } + }, + "tslib": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz", + "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==" + } + } + }, "@reach/utils": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@reach/utils/-/utils-0.10.1.tgz", @@ -1555,15 +1638,15 @@ "@styled-system/css": "^5.1.5" } }, + "@tlon/indigo-light": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tlon/indigo-light/-/indigo-light-1.0.3.tgz", + "integrity": "sha512-3OPSdf9cejP/TSzWXuBaYbzLtAfBzQnc75SlPLkoPfwpxnv1Bvy9hiWngLY0WnKRR6lMOldnkYQCCuNWeDibYQ==" + }, "@tlon/indigo-react": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.1.12.tgz", - "integrity": "sha512-XBJjHwaslEwZA2r09qnoh84BeVLnd/jwZRkhq71KNABnRD+QRtg/dYNvtswueML4Km89Vx9QBtCIEIeujzrblw==", - "requires": { - "@reach/menu-button": "^0.10.0", - "@styled-system/css": "^5.1.5", - "@types/styled-system__css": "^5.0.5" - } + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.1.15.tgz", + "integrity": "sha512-Ao+1hAJjN5y1gDyT7GIUgXORPXTIpZKVVtrS++ZGYBemYMSq3oJFMIZertsSZbDHuh/TsVPenJrMUZBpV60law==" }, "@types/anymatch": { "version": "1.3.1", @@ -1667,14 +1750,6 @@ "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", "dev": true }, - "@types/styled-system__css": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@types/styled-system__css/-/styled-system__css-5.0.8.tgz", - "integrity": "sha512-skv+daDje8vWQ8wnqVV0GCzgWVKx4gI9lJpAxWE77s52Ne6k/SCPP8HGE4BFbWDvK+qi5O3p89BGWVOQ1VHjMg==", - "requires": { - "csstype": "^2.6.6" - } - }, "@types/tapable": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.5.tgz", @@ -3328,7 +3403,8 @@ "csstype": { "version": "2.6.10", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", - "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==" + "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==", + "dev": true }, "cyclist": { "version": "1.0.1", @@ -5949,6 +6025,15 @@ "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==" }, + "markdown-to-jsx": { + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.4.tgz", + "integrity": "sha512-3lRCD5Sh+tfA52iGgfs/XZiw33f7fFX9Bn55aNnVNUd2GzLDkOWyKYYD8Yju2B1Vn+feiEdgJs8T6Tg0xNokPw==", + "requires": { + "prop-types": "^15.6.2", + "unquote": "^1.1.0" + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -5985,6 +6070,11 @@ "p-is-promise": "^2.0.0" } }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -7491,6 +7581,15 @@ "tiny-warning": "^1.0.0" } }, + "react-window": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.5.tgz", + "integrity": "sha512-HeTwlNa37AFa8MDZFZOKcNEkuF2YflA0hpGPiTT9vR7OawEt+GZbfM6wqkBahD3D3pUjIabQYzsnY/BSJbgq6Q==", + "requires": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -9257,6 +9356,11 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", "dev": true }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + }, "unset-value": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-0.1.2.tgz", @@ -9565,7 +9669,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -9586,12 +9691,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" @@ -9606,17 +9713,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", @@ -9733,7 +9843,8 @@ "inherits": { "version": "2.0.4", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -9745,6 +9856,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -9759,6 +9871,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -9766,12 +9879,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" @@ -9790,6 +9905,7 @@ "version": "0.5.3", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -9851,7 +9967,8 @@ "npm-normalize-package-bin": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "npm-packlist": { "version": "1.4.8", @@ -9879,7 +9996,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -9891,6 +10009,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -9968,7 +10087,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -10004,6 +10124,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", @@ -10023,6 +10144,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -10066,12 +10188,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 } } }, @@ -10552,7 +10676,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -10573,12 +10698,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" @@ -10593,17 +10720,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", @@ -10720,7 +10850,8 @@ "inherits": { "version": "2.0.4", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -10732,6 +10863,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -10746,6 +10878,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -10753,12 +10886,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" @@ -10777,6 +10912,7 @@ "version": "0.5.3", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -10838,7 +10974,8 @@ "npm-normalize-package-bin": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "npm-packlist": { "version": "1.4.8", @@ -10866,7 +11003,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -10878,6 +11016,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -10955,7 +11094,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -10991,6 +11131,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", @@ -11010,6 +11151,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -11053,12 +11195,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 2aef19ded..1e21e1322 100644 --- a/pkg/interface/package.json +++ b/pkg/interface/package.json @@ -5,13 +5,17 @@ "main": "index.js", "dependencies": { "@babel/runtime": "^7.10.5", + "@reach/disclosure": "^0.10.5", "@reach/menu-button": "^0.10.1", - "@tlon/indigo-react": "^1.1.10", + "@reach/tabs": "^0.10.5", + "@tlon/indigo-light": "^1.0.3", + "@tlon/indigo-react": "^1.1.15", "classnames": "^2.2.6", "codemirror": "^5.51.0", "css-loader": "^3.5.3", "formik": "^2.1.4", "lodash": "^4.17.15", + "markdown-to-jsx": "^6.11.4", "moment": "^2.20.1", "mousetrap": "^1.6.5", "prop-types": "^15.7.2", @@ -20,6 +24,7 @@ "react-dom": "^16.8.6", "react-markdown": "^4.3.1", "react-router-dom": "^5.0.0", + "react-window": "^1.8.5", "remark-disable-tokenizers": "^1.0.24", "style-loader": "^1.2.1", "styled-components": "^5.1.0", diff --git a/pkg/interface/src/App.js b/pkg/interface/src/App.js index 5d057f9cb..94dc9d076 100644 --- a/pkg/interface/src/App.js +++ b/pkg/interface/src/App.js @@ -6,7 +6,8 @@ import styled, { ThemeProvider, createGlobalStyle } from 'styled-components'; import './css/indigo-static.css'; import './css/fonts.css'; -import { light, dark, inverted, paperDark } from '@tlon/indigo-react'; +import light from './themes/light'; +import dark from './themes/old-dark'; import LaunchApp from './apps/launch/app'; import ChatApp from './apps/chat/app'; @@ -16,7 +17,7 @@ import LinksApp from './apps/links/app'; import PublishApp from './apps/publish/app'; import StatusBar from './components/StatusBar'; -import NotFound from './components/404'; +import ErrorComponent from './components/Error'; import GlobalStore from './store/store'; import GlobalSubscription from './subscription/global'; @@ -85,7 +86,7 @@ class App extends React.Component { const associations = this.state.associations ? this.state.associations : { contacts: {} }; const selectedGroups = this.state.selectedGroups ? this.state.selectedGroups : []; const { state } = this; - const theme = state.dark ? paperDark : light; + const theme = state.dark ? dark : light; return ( @@ -161,7 +162,11 @@ class App extends React.Component { /> )} /> - + ( + + )} + /> diff --git a/pkg/interface/src/apps/chat/components/chat.tsx b/pkg/interface/src/apps/chat/components/chat.tsx index 05ed6923c..a8a94e62b 100644 --- a/pkg/interface/src/apps/chat/components/chat.tsx +++ b/pkg/interface/src/apps/chat/components/chat.tsx @@ -94,6 +94,7 @@ interface ChatScreenState { scrollLocked: boolean; read: number; active: boolean; + messages: Map; lastScrollHeight: number | null; } @@ -118,6 +119,7 @@ export class ChatScreen extends Component { scrollLocked: false, read: props.read, active: true, + messages: new Map(), // only for FF lastScrollHeight: null, }; @@ -594,8 +596,12 @@ export class ChatScreen extends Component { envelopes={props.envelopes} contacts={props.contacts} onEnter={() => this.setState({ scrollLocked: false })} + onUnmount={(msg: string) => this.setState({ + messages: this.state.messages.set(props.station, msg) + })} s3={props.s3} placeholder="Message..." + message={this.state.messages.get(props.station) || ""} /> ); diff --git a/pkg/interface/src/apps/chat/components/lib/chat-input.js b/pkg/interface/src/apps/chat/components/lib/chat-input.js index d84a8c6e6..b507d1114 100644 --- a/pkg/interface/src/apps/chat/components/lib/chat-input.js +++ b/pkg/interface/src/apps/chat/components/lib/chat-input.js @@ -40,7 +40,7 @@ export class ChatInput extends Component { super(props); this.state = { - message: '', + message: props.message, patpSearch: null }; @@ -57,24 +57,6 @@ export class ChatInput extends Component { this.editor = null; - // perf testing: - /* let closure = () => { - let x = 0; - for (var i = 0; i < 30; i++) { - x++; - props.api.chat.message( - props.station, - `~${window.ship}`, - Date.now(), - { - text: `${x}` - } - ); - } - setTimeout(closure, 1000); - }; - this.closure = closure.bind(this);*/ - moment.updateLocale('en', { relativeTime : { past: function(input) { @@ -99,6 +81,10 @@ export class ChatInput extends Component { }); } + componentWillUnmount() { + this.props.onUnmount(this.state.message); + } + nextAutocompleteSuggestion(backward = false) { const { patpSuggestions } = this.state; let idx = patpSuggestions.findIndex(s => s === this.state.selectedSuggestion); @@ -150,6 +136,9 @@ export class ChatInput extends Component { if(patpSearch !== null) { this.patpAutocomplete(value, false); } + this.setState({ + message: value + }); } getLetterType(letter) { @@ -209,29 +198,60 @@ export class ChatInput extends Component { return; } let message = []; - editorMessage.split(' ').map((each) => { - if (this.isUrl(each)) { - if (message.length > 0) { - message = message.join(' '); - message = this.getLetterType(message); - props.api.chat.message( - props.station, - `~${window.ship}`, - Date.now(), - message - ); - message = []; - } - const URL = this.getLetterType(each); - props.api.chat.message( - props.station, - `~${window.ship}`, - Date.now(), - URL - ); + let isInCodeBlock = false; + let endOfCodeBlock = false; + editorMessage.split(/\r?\n/).forEach((line) => { + // A line of backticks enters and exits a codeblock + if (line.startsWith('```')) { + // But we need to check if we've ended a codeblock + endOfCodeBlock = isInCodeBlock; + isInCodeBlock = (!isInCodeBlock); } else { - return message.push(each); + endOfCodeBlock = false; } + if (isInCodeBlock) { + message.push(`\n${line}`); + } else if (endOfCodeBlock) { + message.push(`\n${line}\n`); + } else { + line.split(/\s/).forEach((str) => { + if ( + (str.startsWith('`') && str !== '`') + || (str === '`' && !isInCodeBlock) + ) { + isInCodeBlock = true; + } else if ( + (str.endsWith('`') && str !== '`') + || (str === '`' && isInCodeBlock) + ) { + isInCodeBlock = false; + } + if (this.isUrl(str) && !isInCodeBlock) { + if (message.length > 0) { + message = message.join(' '); + message = this.getLetterType(message); + props.api.chat.message( + props.station, + `~${window.ship}`, + Date.now(), + message + ); + message = []; + } + const URL = this.getLetterType(str); + props.api.chat.message( + props.station, + `~${window.ship}`, + Date.now(), + URL + ); + } else { + message.push(str); + } + }); + + } + }); if (message.length > 0) { @@ -246,8 +266,24 @@ export class ChatInput extends Component { message = []; } - // perf: - // setTimeout(this.closure, 2000); + // perf testing: + /*let closure = () => { + let x = 0; + for (var i = 0; i < 30; i++) { + x++; + props.api.chat.message( + props.station, + `~${window.ship}`, + Date.now(), + { + text: `${x}` + } + ); + } + setTimeout(closure, 1000); + }; + this.closure = closure.bind(this); + setTimeout(this.closure, 2000);*/ this.editor.setValue(''); } @@ -364,6 +400,7 @@ export class ChatInput extends Component { style={{ flexGrow: 1, maxHeight: '224px', width: 'calc(100% - 72px)' }} > { this.editor = editor; diff --git a/pkg/interface/src/apps/chat/components/lib/content/code.js b/pkg/interface/src/apps/chat/components/lib/content/code.js new file mode 100644 index 000000000..99ca099d8 --- /dev/null +++ b/pkg/interface/src/apps/chat/components/lib/content/code.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react'; + + +export default class CodeContent extends Component { + + render() { + const { props } = this; + const content = props.content; + + const outputElement = + (Boolean(content.code.output) && + content.code.output.length && content.code.output.length > 0) ? + ( +
+          {content.code.output[0].join('\n')}
+        
+ ) : null; + + return ( +
+
+          {content.code.expression}
+        
+ {outputElement} +
+ ); + } +} diff --git a/pkg/interface/src/apps/chat/components/lib/content/text.js b/pkg/interface/src/apps/chat/components/lib/content/text.js new file mode 100644 index 000000000..ec1153b8d --- /dev/null +++ b/pkg/interface/src/apps/chat/components/lib/content/text.js @@ -0,0 +1,65 @@ +import React, { Component } from 'react'; +import ReactMarkdown from 'react-markdown'; +import RemarkDisableTokenizers from 'remark-disable-tokenizers'; +import urbitOb from 'urbit-ob'; +import { Link } from 'react-router-dom'; + +const DISABLED_BLOCK_TOKENS = [ + 'indentedCode', + 'blockquote', + 'atxHeading', + 'thematicBreak', + 'list', + 'setextHeading', + 'html', + 'definition', + 'table' +]; + +const DISABLED_INLINE_TOKENS = [ + 'autoLink', + 'url', + 'email', + 'link', + 'reference' +]; + +const MessageMarkdown = React.memo(props => ( + +)); + + +export default class TextContent extends Component { + + render() { + const { props } = this; + const content = props.content; + + const group = content.text.match( + /([~][/])?(~[a-z]{3,6})(-[a-z]{6})?([/])(([a-z])+([/-])?)+/ + ); + if ((group !== null) // matched possible chatroom + && (group[2].length > 2) // possible ship? + && (urbitOb.isValidPatp(group[2]) // valid patp? + && (group[0] === content.text))) { // entire message is room name? + return ( + + {content.text} + + ); + } else { + return ( +
+ +
+ ); + } + } +} diff --git a/pkg/interface/src/apps/chat/components/lib/content/url.js b/pkg/interface/src/apps/chat/components/lib/content/url.js new file mode 100644 index 000000000..e1f59dd5f --- /dev/null +++ b/pkg/interface/src/apps/chat/components/lib/content/url.js @@ -0,0 +1,101 @@ +import React, { Component } from 'react'; + + +const IMAGE_REGEX = + /(jpg|img|png|gif|tiff|jpeg|JPG|IMG|PNG|TIFF|GIF|webp|WEBP|webm|WEBM|svg|SVG)$/; + +const YOUTUBE_REGEX = + new RegExp( + String(/(?:https?:\/\/(?:[a-z]+.)?)/.source) // protocol + + /(?:youtu\.?be(?:\.com)?\/)(?:embed\/)?/.source // short and long-links + + /(?:(?:(?:(?:watch\?)?(?:time_continue=(?:[0-9]+))?.+v=)?([a-zA-Z0-9_-]+))(?:\?t\=(?:[0-9a-zA-Z]+))?)/.source // id + ); + +export default class UrlContent extends Component { + constructor() { + super(); + this.state = { + unfold: false, + copied: false + }; + this.unfoldEmbed = this.unfoldEmbed.bind(this); + } + + unfoldEmbed(id) { + let unfoldState = this.state.unfold; + unfoldState = !unfoldState; + this.setState({ unfold: unfoldState }); + const iframe = this.refs.iframe; + iframe.setAttribute('src', iframe.getAttribute('data-src')); + } + + render() { + const { props } = this; + const content = props.content; + const imgMatch = IMAGE_REGEX.exec(props.content.url); + const ytMatch = YOUTUBE_REGEX.exec(props.content.url); + + let contents = content.url; + if (imgMatch) { + contents = ( + + ); + return ( + + {contents} + + ); + } else if (ytMatch) { + contents = ( +
+ +
+ ); + return ( + + ); + } else { + return ( + + {contents} + + ); + } + } +} diff --git a/pkg/interface/src/apps/chat/components/lib/message-content.js b/pkg/interface/src/apps/chat/components/lib/message-content.js new file mode 100644 index 000000000..865ab3af2 --- /dev/null +++ b/pkg/interface/src/apps/chat/components/lib/message-content.js @@ -0,0 +1,33 @@ +import React, { Component } from 'react'; + +import TextContent from './content/text'; +import CodeContent from './content/code'; +import UrlContent from './content/url'; + + +export default class MessageContent extends Component { + + render() { + const { props } = this; + + const content = props.letter; + + if ('code' in content) { + return ; + } else if ('url' in content) { + return ; + } else if ('me' in content) { + return ( +

+ {content.me} +

+ ); + } + else if ('text' in content) { + return ; + } else { + return null; + } + } + +} diff --git a/pkg/interface/src/apps/chat/components/lib/message.js b/pkg/interface/src/apps/chat/components/lib/message.js index 1c61a9eeb..898b8a8ee 100644 --- a/pkg/interface/src/apps/chat/components/lib/message.js +++ b/pkg/interface/src/apps/chat/components/lib/message.js @@ -1,275 +1,127 @@ import React, { Component } from 'react'; -import { Link } from 'react-router-dom'; import { OverlaySigil } from './overlay-sigil'; +import MessageContent from './message-content'; import { uxToHex, cite, writeText } from '../../../../lib/util'; import moment from 'moment'; -import ReactMarkdown from 'react-markdown'; -import RemarkDisableTokenizers from 'remark-disable-tokenizers'; -import urbitOb from 'urbit-ob'; -const DISABLED_BLOCK_TOKENS = [ - 'indentedCode', - 'blockquote', - 'atxHeading', - 'thematicBreak', - 'list', - 'setextHeading', - 'html', - 'definition', - 'table' -]; - -const DISABLED_INLINE_TOKENS = [ - 'autoLink', - 'url', - 'email', - 'link', - 'reference' -]; - -const MessageMarkdown = React.memo( - props => ()); export class Message extends Component { constructor() { super(); this.state = { - unfold: false, copied: false }; - this.unFoldEmbed = this.unFoldEmbed.bind(this); } - unFoldEmbed(id) { - let unfoldState = this.state.unfold; - unfoldState = !unfoldState; - this.setState({ unfold: unfoldState }); - const iframe = this.refs.iframe; - iframe.setAttribute('src', iframe.getAttribute('data-src')); - } - - renderContent() { - const { props } = this; - const letter = props.msg.letter; - - if ('code' in letter) { - const outputElement = - (Boolean(letter.code.output) && - letter.code.output.length && letter.code.output.length > 0) ? - ( -
-            {letter.code.output[0].join('\n')}
-          
- ) : null; - return ( -
-
-            {letter.code.expression}
-          
- {outputElement} -
- ); - } else if ('url' in letter) { - const imgMatch = - /(jpg|img|png|gif|tiff|jpeg|JPG|IMG|PNG|TIFF|GIF|webp|WEBP|svg|SVG)$/ - .exec(letter.url); - const youTubeRegex = new RegExp(String(/(?:https?:\/\/(?:[a-z]+.)?)/.source) // protocol - + /(?:youtu\.?be(?:\.com)?\/)(?:embed\/)?/.source // short and long-links - + /(?:(?:(?:(?:watch\?)?(?:time_continue=(?:[0-9]+))?.+v=)?([a-zA-Z0-9_-]+))(?:\?t\=(?:[0-9a-zA-Z]+))?)/.source // id - ); - const ytMatch = - youTubeRegex.exec(letter.url); - let contents = letter.url; - if (imgMatch) { - contents = ( - - ); - return ( - - {contents} - - ); - } else if (ytMatch) { - contents = ( -
- -
- ); - return ( - - ); - } else { - return ( - - {contents} - - ); - } - } else if ('me' in letter) { - return ( -

- {letter.me} -

- ); - } else { - const group = letter.text.match( - /([~][/])?(~[a-z]{3,6})(-[a-z]{6})?([/])(([a-z])+([/-])?)+/ - ); - if ((group !== null) // matched possible chatroom - && (group[2].length > 2) // possible ship? - && (urbitOb.isValidPatp(group[2]) // valid patp? - && (group[0] === letter.text))) { // entire message is room name? - return ( - - {letter.text} - - ); - } else { - return ( -
- -
- ); - } - } -} - render() { const { props, state } = this; + const pending = props.msg.pending ? ' o-40' : ''; - const datestamp = '~' + moment.unix(props.msg.when / 1000).format('YYYY.M.D'); + const containerClass = + props.renderSigil ? + `w-100 f7 pl3 pt4 pr3 cf flex lh-copy ` + pending : + 'w-100 pr3 cf hide-child flex' + pending; + const timestamp = + moment.unix(props.msg.when / 1000).format( + props.renderSigil ? 'hh:mm a' : 'hh:mm' + ); + + return ( +
+ { + props.renderSigil ? ( + this.renderWithSigil(timestamp) + ) : ( + this.renderWithoutSigil(timestamp) + ) + } +
+ ); + } + + renderWithSigil(timestamp) { + const { props, state } = this; const paddingTop = props.paddingTop ? { 'paddingTop': '6px' } : ''; + const datestamp = + '~' + moment.unix(props.msg.when / 1000).format('YYYY.M.D'); - if (props.renderSigil) { - const timestamp = moment.unix(props.msg.when / 1000).format('hh:mm a'); - - const contact = props.msg.author in props.contacts - ? props.contacts[props.msg.author] : false; - let name = `~${props.msg.author}`; - let color = '#000000'; - let sigilClass = 'mix-blend-diff'; - if (contact) { - name = (contact.nickname.length > 0) - ? contact.nickname : `~${props.msg.author}`; - color = `#${uxToHex(contact.color)}`; - sigilClass = ''; - } - - if (`~${props.msg.author}` === name) { - name = cite(props.msg.author); - } - - return ( -
- -
-
-

- { - writeText(props.msg.author); - this.setState({ copied: true }); - setTimeout(() => { - this.setState({ copied: false }); - }, 800); - }} - title={`~${props.msg.author}`} - > - {state.copied && 'Copied' || name} - -

-

{timestamp}

-

{datestamp}

-
- {this.renderContent()} -
-
- ); - } else { - const timestamp = moment.unix(props.msg.when / 1000).format('hh:mm'); - - return ( -
-

{timestamp}

-
- {this.renderContent()} -
-
- ); + const contact = props.msg.author in props.contacts + ? props.contacts[props.msg.author] : false; + let name = `~${props.msg.author}`; + let color = '#000000'; + let sigilClass = 'mix-blend-diff'; + if (contact) { + name = (contact.nickname.length > 0) + ? contact.nickname : `~${props.msg.author}`; + color = `#${uxToHex(contact.color)}`; + sigilClass = ''; } + + if (`~${props.msg.author}` === name) { + name = cite(props.msg.author); + } + + return ( +
+ +
+
+

+ { + writeText(props.msg.author); + this.setState({ copied: true }); + setTimeout(() => { + this.setState({ copied: false }); + }, 800); + }} + title={`~${props.msg.author}`} + > + {state.copied && 'Copied' || name} + +

+

{timestamp}

+

+ {datestamp} +

+
+ +
+
+ ); + } + + renderWithoutSigil(timestamp) { + const { props } = this; + + return ( +
+

{timestamp}

+
+ +
+
+ ); } } diff --git a/pkg/interface/src/apps/chat/components/lib/profile-overlay.js b/pkg/interface/src/apps/chat/components/lib/profile-overlay.js index e679e9e38..b9a690bd7 100644 --- a/pkg/interface/src/apps/chat/components/lib/profile-overlay.js +++ b/pkg/interface/src/apps/chat/components/lib/profile-overlay.js @@ -46,7 +46,7 @@ export class ProfileOverlay extends Component { if (!(top || bottom)) { bottom = `-${Math.round(OVERLAY_HEIGHT / 2)}px`; } - const containerStyle = { top, bottom, left: '100%' }; + const containerStyle = { top, bottom, left: '100%', maxWidth: '160px' }; const isOwn = window.ship === ship; @@ -79,7 +79,7 @@ export class ProfileOverlay extends Component {
{contact && contact.nickname && ( -
{contact.nickname}
+
{contact.nickname}
)}
{cite(`~${ship}`)}
{!isOwn && ( diff --git a/pkg/interface/src/apps/chat/components/settings.js b/pkg/interface/src/apps/chat/components/settings.js index 6109cff9b..5471d42e8 100644 --- a/pkg/interface/src/apps/chat/components/settings.js +++ b/pkg/interface/src/apps/chat/components/settings.js @@ -6,6 +6,7 @@ import { Spinner } from '../../../components/Spinner'; import { ChatTabBar } from './lib/chat-tabbar'; import { InviteSearch } from '../../../components/InviteSearch'; import SidebarSwitcher from '../../../components/SidebarSwitch'; +import Toggle from '../../../components/toggle'; export class SettingsScreen extends Component { constructor(props) { @@ -195,18 +196,12 @@ export class SettingsScreen extends Component { } else { let inclusiveToggle =
; if (state.targetGroup) { - // TODO toggle component into /lib - const inclusiveClasses = state.inclusive - ? 'relative checked bg-green2 br3 h1 toggle v-mid z-0' - : 'relative bg-gray4 bg-gray1-d br3 h1 toggle v-mid z-0'; inclusiveToggle = (
- + Add all members to group diff --git a/pkg/interface/src/apps/chat/components/sidebar.js b/pkg/interface/src/apps/chat/components/sidebar.js index b94e62c12..d7d63a984 100644 --- a/pkg/interface/src/apps/chat/components/sidebar.js +++ b/pkg/interface/src/apps/chat/components/sidebar.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import Welcome from './lib/welcome'; import { alphabetiseAssociations } from '../../../lib/util'; -import { SidebarInvite } from './lib/sidebar-invite'; +import SidebarInvite from '../../../components/SidebarInvite'; import { GroupItem } from './lib/group-item'; export class Sidebar extends Component { @@ -51,10 +51,10 @@ export class Sidebar extends Component { .map((uid) => { return ( props.api.invite.accept('/chat', uid)} + onDecline={() => props.api.invite.decline('/chat', uid)} /> ); }); diff --git a/pkg/interface/src/apps/chat/components/skeleton.js b/pkg/interface/src/apps/chat/components/skeleton.js index 4c31e26e2..8fa175712 100644 --- a/pkg/interface/src/apps/chat/components/skeleton.js +++ b/pkg/interface/src/apps/chat/components/skeleton.js @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import classnames from 'classnames'; import { Link } from 'react-router-dom'; +import ErrorBoundary from '../../../components/ErrorBoundary'; export class Skeleton extends Component { render() { @@ -61,7 +62,9 @@ export class Skeleton extends Component { width: 'calc(100% - 300px)' }} > - {this.props.children} + + {this.props.children} +
diff --git a/pkg/interface/src/apps/groups/components/join.js b/pkg/interface/src/apps/groups/components/join.js index a9a2e4992..a092dd749 100644 --- a/pkg/interface/src/apps/groups/components/join.js +++ b/pkg/interface/src/apps/groups/components/join.js @@ -6,7 +6,6 @@ import urbitOb from 'urbit-ob'; export class JoinScreen extends Component { constructor(props) { super(props); - this.state = { group: '', error: false, @@ -21,12 +20,11 @@ export class JoinScreen extends Component { this.componentDidUpdate(); } - componentDidUpdate(prevProps) { + componentDidUpdate() { const { props, state } = this; // autojoin by URL, waits for group information if ((props.ship && props.name) && - (prevProps && (prevProps.groups !== props.groups))) { - console.log('autojoining'); + (props.contacts && (Object.keys(props.contacts).length > 0) && !state.group)) { const incomingGroup = `${props.ship}/${props.name}`; // push to group if already exists if (`/ship/${incomingGroup}` in props.groups) { @@ -48,10 +46,8 @@ export class JoinScreen extends Component { } } - onClickJoin() { const { props, state } = this; - console.log('i am joining'); const { group } = state; const [ship, name] = group.split('/'); @@ -101,14 +97,12 @@ export class JoinScreen extends Component {

Enter a ~ship/group-name

Group names use lowercase, hyphens, and slashes.