Merge remote-tracking branch 'origin/release/next-userspace' into lf/npm-typings

This commit is contained in:
Liam Fitzgerald 2021-06-14 11:59:22 +10:00
commit 926da94604
No known key found for this signature in database
GPG Key ID: D390E12C61D1CFFB
43 changed files with 590 additions and 399 deletions

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:61e583dd7db795dac4a7c31bfd3ee8b240e679bb882e35d4e7d1acb5f9f2f3d6
size 8270131
oid sha256:e0af91e5c51359719aaa943f37a1e953989c786412616b18fbaa0addb2cf0740
size 10272514

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:185ea5e76dc48695e55efc543377e0682e485f81b16e3b443f9be881d026d4f2
size 2616564
oid sha256:23d8235b19a3404e0bfbed54aa56a018255beb1f33457e37f521bc0763b4d0eb
size 6245506

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:28c1ce6409721365a8c24d77af767550bb6596260833af21fa96964b740b3cf8
size 12381649
oid sha256:64d7cd93dbdf650e390b9c5780ee8bee22cf925fa31f47a0a1a3b17ce7443302
size 11566105

View File

@ -918,7 +918,7 @@
^- (quip card _state)
:_ state
=- (turn - print:sh-out)
:~ ";view ~host/chat to print messages for a chat you've already jonied."
:~ ";view ~host/chat to print messages for a chat you've already joined."
";flee ~host/chat to stop printing messages for a chat."
"For more details:"
"https://urbit.org/using/operations/using-your-ship/#messaging"

View File

@ -131,10 +131,9 @@
=^ allowed cards (is-allowed-add:hc rid nodes.q.update)
?. allowed
[cards ~]
=/ mark-cached (~(has by graph-to-mark) rid)
=/ mark
?: mark-cached
(~(got by graph-to-mark) rid)
%+ fall
(~(get by graph-to-mark) rid)
(get-mark:gra rid)
?~ mark
[cards `vas]
@ -143,15 +142,12 @@
|%
++ $
^- (quip card (unit vase))
=/ transform-cached (~(has by transform-marks) u.mark)
=/ transform=cached-transform
?: transform-cached
(~(got by transform-marks) u.mark)
%+ fall
(~(get by transform-marks) u.mark)
=/ =tube:clay
.^(tube:clay (scry:hc %cc %home /[u.mark]/transform-add-nodes))
!< cached-transform
%. !>(*indexed-post:store)
tube
!<(cached-transform (tube !>(*indexed-post:store)))
=/ [* result=(list [index:store node:store])]
%+ roll
(flatten-node-map ~(tap by nodes.q.update))
@ -164,13 +160,15 @@
update
%+ weld cards
%- zing
:~ ?: mark-cached ~
:~ ?: (~(has by graph-to-mark) rid)
~
:_ ~
%+ poke-self:pass:io %graph-cache-hook
!> ^- cache-action
[%graph-to-mark rid mark]
::
?: transform-cached ~
?: (~(has by transform-marks) u.mark)
~
:_ ~
%+ poke-self:pass:io %graph-cache-hook
!> ^- cache-action
@ -316,28 +314,27 @@
|= [=resource:res perm=@t vip=vip-metadata:metadata =indexed-post:store]
^- [permissions:store (list card)]
|^
=/ mark-cached (~(has by graph-to-mark.cache) resource)
=/ mark
?: mark-cached
(~(got by graph-to-mark.cache) resource)
%+ fall
(~(get by graph-to-mark.cache) resource)
(get-mark:gra resource)
?~ mark
[[%no %no %no] ~]
=/ key [u.mark (perm-mark-name perm)]
=/ perms-cached (~(has by perm-marks.cache) key)
=/ convert
?: perms-cached
(~(got by perm-marks.cache) key)
%+ fall
(~(get by perm-marks.cache) key)
.^(cached-permission (scry %cf %home /[u.mark]/(perm-mark-name perm)))
:- ((convert indexed-post) vip)
%- zing
:~ ?: mark-cached ~
:~ ?: (~(has by graph-to-mark.cache) resource)
~
:_ ~
%+ poke-self:pass:io %graph-cache-hook
!> ^- cache-action
[%graph-to-mark resource mark]
::
?: perms-cached ~
?: (~(has by perm-marks.cache) key) ~
:_ ~
%+ poke-self:pass:io %graph-cache-hook
!> ^- cache-action

View File

@ -5,8 +5,8 @@
|%
+$ card card:agent:gall
+$ versioned-state
$% [%0 network:zero:store]
[%1 network:zero:store]
$% [%0 *]
[%1 *]
[%2 network:zero:store]
[%3 network:one:store]
[%4 network:store]
@ -16,7 +16,6 @@
+$ state-5 [%5 network:store]
++ orm orm:store
++ orm-log orm-log:store
+$ debug-input [%validate-graph =resource:store]
::
+$ cache
$: validators=(map mark $-(indexed-post:store indexed-post:store))
@ -50,34 +49,8 @@
=| cards=(list card)
|-
?- -.old
%0
=* zro zero-load:upgrade:store
%_ $
-.old %1
::
graphs.old
%- ~(run by graphs.old)
|= [=graph:zero:store q=(unit mark)]
^- [graph:zero:store (unit mark)]
:- (convert-unix-timestamped-graph:zro graph)
?^ q q
`%graph-validator-link
::
update-logs.old
%- ~(run by update-logs.old)
|=(a=* *update-log:zero:store)
==
::
%1
=* zro zero-load:upgrade:store
%_ $
-.old %2
graphs.old (~(run by graphs.old) change-revision-graph:zro)
::
update-logs.old
%- ~(run by update-logs.old)
|=(a=* *update-log:zero:store)
==
%0 !!
%1 !!
::
%2
=* upg upgrade:store
@ -138,7 +111,7 @@
++ give
|= =action:store
^- (list card)
[%give %fact ~ [%graph-update-2 !>([now.bowl action])]]~
[%give %fact ~ [%graph-update-2 !>(`update:store`[now.bowl action])]]~
--
::
++ on-poke
@ -148,10 +121,9 @@
|^
?> (team:title our.bowl src.bowl)
=^ cards state
?+ mark (on-poke:def mark vase)
%graph-update-2 (graph-update !<(update:store vase))
%noun (debug !<(debug-input vase))
%import (poke-import q.vase)
?+ mark (on-poke:def mark vase)
%graph-update-2 (graph-update !<(update:store vase))
%import (poke-import q.vase)
==
[cards this]
::
@ -204,7 +176,7 @@
==
%- zing
:~ (give [/keys ~] %keys (~(put in ~(key by graphs)) resource))
(give [/updates ~] %add-graph resource *graph:store mark overwrite)
(give [/updates ~] %add-graph resource ~ mark overwrite)
==
::
++ remove-graph
@ -274,7 +246,7 @@
?~ index
?=(^ node)
?~ t.index
?=(^ (get:orm graph i.index))
(has:orm graph i.index)
=. node (get:orm graph i.index)
?~ node %.n
?- -.children.u.node
@ -330,7 +302,8 @@
~| "cannot add deleted post"
?> ?=(%& -.post.node)
=* p p.post.node
?~ hash.p node(signatures.p.post *signatures:store)
?~ hash.p
node(signatures.p.post ~)
=/ =validated-portion:store
[parent-hash author.p time-sent.p contents.p]
=/ =hash:store `@ux`(sham validated-portion)
@ -343,24 +316,23 @@
::
=/ parent=node:store
~| "index does not exist to add a node to!"
(need (get:orm graph atom))
(got:orm graph atom)
%_ parent
children
^- internal-graph:store
:- %graph
%_ $
index t.index
index t.index
::
parent-hash
?- -.post.parent
%| `p.post.parent
%& hash.p.post.parent
==
?: ?=(%| -.post.parent)
`p.post.parent
hash.p.post.parent
::
graph
?: ?=(%graph -.children.parent)
p.children.parent
(gas:orm ~ ~)
?. ?=(%graph -.children.parent)
~
p.children.parent
==
==
--
@ -416,7 +388,7 @@
?~ t.index
=/ =node:store
~| "cannot remove index that does not exist {<index>}"
(need (get:orm graph atom))
(got:orm graph atom)
%_ node
post
~| "cannot remove post that has already been removed"
@ -434,7 +406,7 @@
::
=/ parent=node:store
~| "parent index does not exist to remove a node from!"
(need (get:orm graph atom))
(got:orm graph atom)
~| "child index does not exist to remove a node from!"
?> ?=(%graph -.children.parent)
%_ parent
@ -444,10 +416,9 @@
graph p.children.parent
::
parent-hash
?- -.post.parent
%| `p.post.parent
%& hash.p.post.parent
==
?: ?=(%| -.post.parent)
`p.post.parent
hash.p.post.parent
==
==
--
@ -478,7 +449,7 @@
=* atom i.index
=/ =node:store
~| "node does not exist to add signatures to!"
(need (get:orm graph atom))
(got:orm graph atom)
:: last index in list
::
%^ put:orm
@ -490,7 +461,10 @@
~| "cannot add signatures to a node missing a hash"
?> ?=(^ hash.p.post.node)
~| "signatures did not match public keys!"
?> (are-signatures-valid:sigs our.bowl signatures u.hash.p.post.node now.bowl)
?> %: are-signatures-valid:sigs
our.bowl signatures
u.hash.p.post.node now.bowl
==
node(signatures.p.post (~(uni in signatures) signatures.p.post.node))
~| "child graph does not exist to add signatures to!"
?> ?=(%graph -.children.node)
@ -525,7 +499,7 @@
=* atom i.index
=/ =node:store
~| "node does not exist to add signatures to!"
(need (get:orm graph atom))
(got:orm graph atom)
:: last index in list
::
%^ put:orm
@ -578,7 +552,7 @@
%_ 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 ~ ~))
update-logs (~(put by update-logs) resource ~)
==
::
++ run-updates
@ -599,38 +573,28 @@
%- graph-update
^- update:store
?- -.q.update
%add-graph update(resource.q resource)
%add-nodes update(resource.q resource)
%remove-posts update(resource.q resource)
%add-signatures update(resource.uid.q resource)
%remove-signatures update(resource.uid.q resource)
%add-graph update(resource.q resource)
%add-nodes update(resource.q resource)
%remove-posts update(resource.q resource)
%add-signatures update(resource.uid.q resource)
%remove-signatures update(resource.uid.q resource)
==
$(cards (weld cards crds), updates t.updates)
::
++ give
|= [paths=(list path) update=action:store]
^- (list card)
[%give %fact paths [%graph-update-2 !>([now.bowl update])]]~
[%give %fact paths [%graph-update-2 !>(`update:store`[now.bowl update])]]~
--
::
++ debug
|= =debug-input
^- (quip card _state)
=/ [=graph:store mark=(unit mark:store)]
(~(got by graphs) resource.debug-input)
=^ is-valid state
(validate-graph graph mark)
?> is-valid
[~ state]
::
++ validate-graph
|= [=graph:store mark=(unit mark:store)]
^- [? _state]
?~ mark [%.y state]
=/ has-validator (~(has by validators) u.mark)
?~ mark
[%.y state]
=/ validate=$-(indexed-post:store indexed-post:store)
?: has-validator
(~(got by validators) u.mark)
%+ fall
(~(get by validators) u.mark)
.^ $-(indexed-post:store indexed-post:store)
%cf
(scot %p our.bowl)
@ -640,11 +604,13 @@
%graph-indexed-post
~
==
:_ state(validators (~(put by validators) u.mark validate))
=? validators !(~(has by validators) u.mark)
(~(put by validators) u.mark validate)
:_ state
|- ^- ?
?~ graph %.y
%+ roll (tap:orm graph)
|= [[=atom =node:store] out=?]
%+ all:orm graph
|= [=atom =node:store]
^- ?
?& ?| ?=(%| -.post.node)
?=(^ (validate [atom p.post.node]))
@ -762,6 +728,7 @@
(~(gas by *(map index:store node:store)) [index u.node] ~)
::
[%x %node-siblings ?(%older %younger) @ @ @ *]
|^
=/ older ?=(%older i.t.t.path)
=/ =ship (slav %p i.t.t.t.path)
=/ =term i.t.t.t.t.path
@ -779,21 +746,28 @@
:+ %add-nodes
[ship term]
%- ~(gas by *(map index:store node:store))
:: TODO time complexity not desirable
:: replace with custom ordered map functions
%+ turn
=- ?.(older (slag (safe-sub (lent -) count) -) (scag count -))
?: older
(tab:orm u.graph ~ count)
:: TODO time complexity not desirable for %younger case
::
%+ slag (safe-sub (lent -) count)
%- tap:orm
%+ lot:orm u.graph
=/ idx
(snag (dec (lent index)) index)
?:(older [`idx ~] [~ `idx])
[~ `(snag (dec (lent index)) index)]
|= [=atom =node:store]
^- [index:store node:store]
[(snoc parent atom) node]
::
++ safe-sub
|= [a=@ b=@]
^- @
?: (gte b a)
0
(sub a b)
--
::
[%x %shallow-children @ @ *]
=/ newest ?=(%newest i.t.path)
=/ =ship (slav %p i.t.t.path)
=/ =term i.t.t.t.path
=/ =index:store
@ -831,8 +805,9 @@
%- ~(gas by *(map index:store node:store))
%+ turn
%+ scag count
%- ?:(newest same flop)
(tap:orm u.children)
?: newest
(tap:orm u.children)
(bap:orm u.children)
|= [=atom =node:store]
^- [index:store node:store]
[(snoc index atom) node]
@ -923,7 +898,8 @@
::
++ collect-parents
|= [=graph:store =index:store =ship =term]
^- (unit [node:store index:store (map index:store node:store) ^ship ^term])
^- %- unit
[node:store index:store (map index:store node:store) ^ship ^term]
=| =(map index:store node:store)
=| =node:store
=| ind=index:store
@ -953,16 +929,16 @@
==
::
++ collect-firstborn
|= [=node:store =index:store map=(map index:store node:store) =ship =term]
|= [=node:store =index:store mp=(map index:store node:store) =ship =term]
^- (unit (unit cage))
?: ?=(%empty -.children.node)
:- ~ :- ~ :- %graph-update-2
!> ^- update:store
[now.bowl [%add-nodes [ship term] map]]
[now.bowl [%add-nodes [ship term] mp]]
=/ item=[k=atom v=node:store]
(need (ram:orm p.children.node))
=. index (snoc index k.item)
$(map (~(put by map) index v.item(children empty+~)), node v.item)
$(mp (~(put by mp) index v.item(children empty+~)), node v.item)
--
::
[%x %update-log-subset @ @ @ @ ~]
@ -985,23 +961,17 @@
[%x %peek-update-log @ @ ~]
=/ =ship (slav %p i.t.t.path)
=/ =term i.t.t.t.path
=/ m-update-log=(unit update-log:store) (~(get by update-logs) [ship term])
=/ m-update-log=(unit update-log:store)
(~(get by update-logs) [ship term])
:- ~ :- ~ :- %noun
!> ^- (unit time)
%+ biff m-update-log
|= =update-log:store
=/ result=(unit [=time =update:store])
(pry:orm-log:store update-log)
(bind result |=([=time update:store] time))
(bind result head)
==
::
++ safe-sub
|= [a=@ b=@]
^- @
?: (gte b a)
0
(sub a b)
::
++ get-node-children
|= [=ship =term =index:store]
^- (unit graph:store)
@ -1044,40 +1014,12 @@
?+ wire (on-arvo:def wire sign-arvo)
::
:: old wire, do nothing
[%graph *] [~ this]
[%validator @ ~] [~ this]
::
[%try-rejoin @ *]
=/ rid=resource:store (de-path:res t.t.wire)
=/ nack-count (slav %ud i.t.wire)
?> ?=([%behn %wake *] sign-arvo)
~? ?=(^ error.sign-arvo)
"behn errored in backoff timers, continuing anyway"
=/ new=^wire [%try-rejoin (scot %ud +(nack-count)) t.t.wire]
:_ this
[%pass new %agent [entity.rid %graph-push-hook] %watch resource+t.t.wire]~
[%graph *] [~ this]
[%validator @ ~] [~ this]
[%try-rejoin @ *] [~ this]
==
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
?. ?=([%try-rejoin @ *] wire)
(on-agent:def wire sign)
?. ?=(%watch-ack -.sign)
[~ this]
=/ rid=resource:store (de-path:res t.t.wire)
?~ p.sign
=/ =cage [%pull-hook-action !>([%add entity.rid rid])]
:_ this
:~ [%pass / %agent [our.bowl %graph-pull-hook] %poke cage]
[%pass wire %agent [entity.rid %graph-push-hook] %leave ~]
==
=/ nack-count=@ud (slav %ud i.t.wire)
=/ wakeup=@da
(add now.bowl (mul ~s1 (bex (min 19 nack-count))))
:_ this
[%pass wire %arvo %b %wait wakeup]~
::
++ on-agent on-agent:def
++ on-leave on-leave:def
++ on-fail on-fail:def
--

View File

@ -30,12 +30,12 @@
?> ?=(^ t.p)
.^(mold i.p (scot %p our) i.t.p (scot %da now) t.t.p)
::
++ scry-conversion
++ scry-notif-conversion
|= [[our=@p now=@da] desk=term =mark]
~+
^- $-(indexed-post:graph-store (unit notif-kind:hook))
%^ scry [our now]
tube:clay
/cc/[desk]/[mark]/notification-kind
$-(indexed-post:graph-store (unit notif-kind:hook))
/cf/[desk]/[mark]/notification-kind
--
::
=| state-1
@ -87,7 +87,7 @@
|= =mark
^- card
=/ =wire /validator/[mark]
=/ =rave:clay [%sing %c [%da now.bowl] /[mark]/notification-kind]
=/ =rave:clay [%sing %f [%da now.bowl] /[mark]/notification-kind]
[%pass wire %arvo %c %warp our.bowl [%home `rave]]
::
++ on-watch
@ -214,19 +214,18 @@
%- ~(gas by *(set [resource index:graph-store]))
(turn ~(tap in indices) (lead rid))
:_ state(watching (~(dif in watching) to-remove))
=/ =tube:clay
(get-conversion:ha rid)
=/ convert (get-conversion:ha rid)
%+ roll
~(tap in indices)
|= [=index:graph-store out=(list card)]
=| =indexed-post:graph-store
=. index.p.indexed-post index
=+ !<(u-notif-kind=(unit notif-kind:hook) (tube !>(indexed-post)))
?~ u-notif-kind out
=* notif-kind u.u-notif-kind
=/ notif-kind=(unit notif-kind:hook)
(convert indexed-post)
?~ notif-kind out
=/ =stats-index:store
[%graph rid (scag parent.index-len.notif-kind index)]
?. ?=(%each mode.notif-kind) out
[%graph rid (scag parent.index-len.u.notif-kind index)]
?. ?=(%each mode.u.notif-kind) out
:_ out
(poke-hark %read-each stats-index index)
::
@ -285,7 +284,7 @@
[%validator @ ~]
:_ this
=* validator i.t.wire
=/ =rave:clay [%next %c [%da now.bowl] /[validator]/notification-kind]
=/ =rave:clay [%next %f [%da now.bowl] /[validator]/notification-kind]
[%pass wire %arvo %c %warp our.bowl [%home `rave]]~
==
++ on-fail on-fail:def
@ -298,13 +297,13 @@
::
++ get-conversion
|= rid=resource
^- tube:clay
^- $-(indexed-post:graph-store (unit notif-kind:hook))
=+ %^ scry [our now]:bowl
,mark=(unit mark)
/gx/graph-store/graph-mark/(scot %p entity.rid)/[name.rid]/noun
?~ mark
|=(v=vase !>(~))
(scry-conversion [our now]:bowl q.byk.bowl u.mark)
|=(=indexed-post:graph-store ~)
(scry-notif-conversion [our now]:bowl q.byk.bowl u.mark)
::
++ give
|= [paths=(list path) =update:hook]
@ -355,8 +354,6 @@
update-core(rid r, updates upds, mark m)
::
++ get-conversion
:: LA: this tube should be cached in %hark-graph-hook state
:: instead of just trying to keep it warm, as the scry overhead is large
~+ (^get-conversion rid)
::
++ abet
@ -410,9 +407,8 @@
?: ?=(%| -.post.node)
update-core
=* pos p.post.node
=+ !< notif-kind=(unit notif-kind:hook)
%- get-conversion
!>(`indexed-post:graph-store`[0 pos])
=/ notif-kind=(unit notif-kind:hook)
(get-conversion [0 pos])
?~ notif-kind
update-core
=/ desc=@t

View File

@ -422,7 +422,11 @@
%read-note (read-note +.in)
::
%seen-index (seen-index +.in)
::
%remove-graph (remove-graph +.in)
%read-graph (read-graph +.in)
%read-group (read-group +.in)
::
%set-dnd (set-dnd +.in)
%seen seen
%read-all read-all
@ -566,10 +570,53 @@
(~(put by last-seen) stats-index new-time)
(give %seen-index new-time stats-index)
::
++ get-stats-indices
|= rid=resource
%- ~(gas ^in *(set stats-index:store))
%+ skim
;: weld
~(tap ^in ~(key by unreads-count))
~(tap ^in ~(key by last-seen))
~(tap ^in ~(key by unreads-each))
==
|= =stats-index:store
?. ?=(%graph -.stats-index) %.n
=(graph.stats-index rid)
::
++ read-all-each
|= =stats-index:store
=/ refs=(list index:graph-store)
~(tap ^in (~(get ju unreads-each) stats-index))
|-
?~ refs poke-core
$(refs t.refs, poke-core (read-each stats-index i.refs))
::
++ read-graph
|= rid=resource
=/ indices=(list stats-index:store)
~(tap ^in (get-stats-indices rid))
|-
?~ indices poke-core
=* index i.indices
=? poke-core (~(has by unreads-count) index)
(read-count i.indices)
=? poke-core (~(has by unreads-each) index)
(read-all-each i.indices)
$(indices t.indices)
::
++ read-group
|= rid=resource
=/ graphs=(list resource)
(graphs-of-group:met rid)
|-
?~ graphs poke-core
=/ core=_poke-core (read-graph i.graphs)
$(graphs t.graphs, poke-core core)
::
++ remove-graph
|= rid=resource
|^
=/ indices get-stats-indices
=/ indices (get-stats-indices rid)
=. poke-core
(give %remove-graph rid)
=. poke-core
@ -583,18 +630,6 @@
((dif-map-by-key ,@da) last-seen indices)
poke-core
::
++ get-stats-indices
%- ~(gas ^in *(set stats-index:store))
%+ skim
;: weld
~(tap ^in ~(key by unreads-count))
~(tap ^in ~(key by last-seen))
~(tap ^in ~(key by unreads-each))
==
|= =stats-index:store
?. ?=(%graph -.stats-index) %.n
=(graph.stats-index rid)
::
++ dif-map-by-key
|* value=mold
|= [=(map stats-index:store value) =(set stats-index:store)]

View File

@ -28,8 +28,8 @@
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)
++ orm ((on atom node) gth)
++ orm-log ((on time logged-update) gth)
::
++ enjs
=, enjs:format
@ -311,7 +311,7 @@
++ graph
|= a=json
^- ^graph
=/ or-mp ((ordered-map atom ^node) gth)
=/ or-mp ((on atom ^node) gth)
%+ gas:or-mp ~
%+ turn ~(tap by ((om node) a))
|* [b=cord c=*]
@ -559,7 +559,7 @@
=>
|%
++ in-orm
((ordered-map atom in-node) gth)
((on atom in-node) gth)
+$ in-node
[post=in-pst children=in-internal-graph]
+$ in-graph
@ -571,7 +571,7 @@
==
::
++ out-orm
((ordered-map atom out-node) gth)
((on atom out-node) gth)
+$ out-node
[post=out-pst children=out-internal-graph]
+$ out-graph
@ -823,7 +823,7 @@
++ remake-update-log
|= t=tree-update-log
^- update-log
=/ ulm ((ordered-map time logged-update) gth)
=/ ulm ((on time logged-update) gth)
%+ gas:ulm *update-log
%+ turn ~(tap by t)
|= [=time tlu=tree-logged-update]

View File

@ -314,6 +314,8 @@
add-note+add
set-dnd+bo
read-count+stats-index
read-graph+dejs-path:resource
read-group+dejs-path:resource
read-each+read-graph-index
read-all+ul
==

View File

@ -100,4 +100,13 @@
^- (unit resource)
%+ bind (peek-association md-resource)
|=(association:store group)
::
++ graphs-of-group
|= group=resource
=/ =associations:store
(metadata-for-group group)
%+ murn ~(tap in ~(key by associations))
|= [=app-name:store rid=resource]
?.(=(%graph app-name) ~ `rid)
--

View File

@ -73,8 +73,8 @@
::
++ one
|%
++ orm ((ordered-map atom node) gth)
++ orm-log ((ordered-map time logged-update) gth)
++ orm ((on atom node) gth)
++ orm-log ((on time logged-update) gth)
::
+$ graph ((mop atom node) gth)
+$ marked-graph [p=graph q=(unit mark)]

View File

@ -44,6 +44,9 @@
[%read-note =index]
::
[%seen-index time=@da =stats-index]
::
[%read-graph =resource]
[%read-group =resource]
[%remove-graph =resource]
::
[%read-all ~]
@ -281,6 +284,7 @@
[%unread-note time=@da index]
::
[%seen-index time=@da =stats-index]
::
[%remove-graph =resource]
::
[%read-all ~]

View File

@ -252,11 +252,13 @@
:: %what: update from files
:: %whey: produce $mass :: XX remove, scry
:: %verb: toggle laconicity
:: %whiz: prime vane caches
::
$% [%trim p=@ud]
[%what p=(list (pair path (cask)))]
[%whey ~]
[%verb p=(unit ?)]
[%whiz ~]
==
+$ wasp
:: %crud: reroute $ovum with $goof
@ -291,14 +293,23 @@
|=(b=beam =*(s scot `path`[(s %p p.b) q.b (s r.b) s.b]))
::
++ de-beam
~/ %de-beam
|= p=path
^- (unit beam)
?. ?=([@ @ @ *] p) ~
?~ who=(slaw %p i.p) ~
?~ des=?~(i.t.p (some %$) (slaw %tas i.t.p)) ~ :: XX +sym ;~(pose low (easy %$))
?~ ved=(slay i.t.t.p) ~
?. ?=([%$ case] u.ved) ~
`(unit beam)`[~ [`ship`u.who `desk`u.des `case`p.u.ved] t.t.t.p]
?~ ved=(de-case i.t.t.p) ~
`[[`ship`u.who `desk`u.des u.ved] t.t.t.p]
::
++ de-case
~/ %de-case
|= =knot
^- (unit case)
?^ num=(slaw %ud knot) `[%ud u.num]
?^ wen=(slaw %da knot) `[%da u.wen]
?~ lab=(slaw %tas knot) ~
`[%tas u.lab]
::
++ en-omen
|= [vis=view bem=beam]
@ -308,6 +319,7 @@
~(rent co [%many $/tas/way.vis $/tas/car.vis ~])
::
++ de-omen
~/ %de-omen
|= pax=path
^- (unit [vis=view bem=beam])
?~ pax ~
@ -1000,8 +1012,11 @@
++ settle
|= van=vase
^- (pair vase worm)
=/ [rig=vase wor=worm] (~(slym wa *worm) van *vane-sample)
[van +:(~(slap wa wor) rig [%limb %scry])]
=| sac=worm
=^ rig=vase sac (~(slym wa sac) van *vane-sample)
=^ gat=vase sac (~(slap wa sac) rig [%limb %scry])
=^ pro=vase sac (~(slap wa sac) gat [%limb %$])
[van +:(~(mint wa sac) p.pro [%$ 7])]
::
:: XX pass identity to preserve behavior?
::
@ -1470,6 +1485,9 @@
%verb ..pith(lac.fad ?~(p.waif !lac.fad u.p.waif))
%what ~(kel what p.waif)
%whey ..pith(out [[//arvo mass/whey] out])
::
%whiz
..pith(van.mod (~(run by van.mod) |=(vane (settle:va:part vase))))
==
::
++ peek

View File

@ -259,6 +259,8 @@
++ head |*(^ ,:+<-) :: get head
++ same |*(* +<) :: identity
::
++ succ |=(@ +(+<)) :: successor
::
++ tail |*(^ ,:+<+) :: get tail
++ test |=(^ =(+<- +<+)) :: equality
::
@ -8764,6 +8766,7 @@
%peek peek
%repo repo
%rest rest
%sink sink
%tack tack
%toss toss
%wrap wrap
@ -10838,7 +10841,7 @@
|- ^- type
?~ lov sut
$(lov t.lov, sut (face i.lov sut))
:: ::
::
++ sint :: reduce by reference
|= $: :: hod: expand holds
::
@ -10911,6 +10914,39 @@
%- ~(gas in *(set type))
(turn leg |=([p=type q=hoon] (play(sut p) q)))
::
++ sink
~/ %sink
|^ ^- cord
?- sut
%void 'void'
%noun 'noun'
[%atom *] (rap 3 'atom ' p.sut ' ' ?~(q.sut '~' u.q.sut) ~)
[%cell *] (rap 3 'cell ' (mup p.sut) ' ' (mup q.sut) ~)
[%face *] (rap 3 'face ' ?@(p.sut p.sut (mup p.sut)) ' ' (mup q.sut) ~)
[%fork *] (rap 3 'fork ' (mup p.sut) ~)
[%hint *] (rap 3 'hint ' (mup p.sut) ' ' (mup q.sut) ~)
[%hold *] (rap 3 'hold ' (mup p.sut) ' ' (mup q.sut) ~)
::
[%core *]
%+ rap 3
:~ 'core '
(mup p.sut)
' '
?~(p.p.q.sut '~' u.p.p.q.sut)
' '
q.p.q.sut
' '
r.p.q.sut
' '
(mup q.q.sut)
' '
(mup p.r.q.sut)
==
==
::
++ mup |=(* (scot %p (mug +<)))
--
::
++ take
|= [vit=vein duz=$-(type type)]
^- (pair axis type)

View File

@ -1069,14 +1069,23 @@
:::: :: (1d2)
::
+$ blew [p=@ud q=@ud] :: columns rows
+$ belt :: old belt
+$ belt :: client input
$? bolt :: simple input
$% [%mod mod=?(%ctl %met %hyp) key=bolt] :: w/ modifier
[%txt p=(list @c)] :: utf32 text
::TODO consider moving %hey, %rez, %yow here ::
::TMP forward backwards-compatibility ::
:: ::
[%ctl p=@c] ::
[%met p=@c] ::
== == ::
+$ bolt :: simple input
$@ @c :: simple keystroke
$% [%aro p=?(%d %l %r %u)] :: arrow key
[%bac ~] :: true backspace
[%ctl p=@c] :: control-key
[%del ~] :: true delete
[%met p=@c] :: meta-key
[%hit r=@ud c=@ud] :: mouse click
[%ret ~] :: return
[%txt p=(list @c)] :: utf32 text
== ::
+$ blit :: old blit
$% [%bel ~] :: make a noise
@ -2102,6 +2111,7 @@
[%g task:gall]
[%i task:iris]
[%j task:jael]
[%$ %whiz ~]
[@tas %meta vase]
==
:: full vane names are required in vanes

View File

@ -3660,7 +3660,7 @@
=/ lower=@ud 1
|-
:: a should be excluded, so wait until we're past it
?: =(lower +(a))
?: (gte lower +(a))
acc
=/ res=(set tako) (reachable-takos (~(got by hit.dom) lower))
$(acc (~(uni in acc) res), lower +(lower))
@ -3668,7 +3668,7 @@
=| acc=(set tako)
=/ upper=@ud b
|-
?: =(upper a)
?: (lte upper a)
acc
=/ res=(set tako) (reachable-takos (~(got by hit.dom) upper))
$(acc (~(uni in acc) res), upper (dec upper))

View File

@ -106,7 +106,6 @@
%flow +>
%harm +>
%hail (send %hey ~)
%belt (send `dill-belt`p.kyz)
%text (from %out (tuba p.kyz))
%crud :: (send `dill-belt`[%cru p.kyz q.kyz])
(crud p.kyz q.kyz)
@ -116,6 +115,18 @@
%pack (dump kyz)
%crop (dump trim+p.kyz)
%verb (pass /verb %$ kyz)
::
%belt
%- send
::TMP forwards compatibility with next-dill
::
?@ p.kyz [%txt p.kyz ~]
?: ?=(%hit -.p.kyz) [%txt ~]
?. ?=(%mod -.p.kyz) p.kyz
=/ =@c
?@ key.p.kyz key.p.kyz
?:(?=(?(%bac %del %ret) -.key.p.kyz) `@`-.key.p.kyz ~-)
?:(?=(%met mod.p.kyz) [%met c] [%ctl c])
==
::
++ crud

View File

@ -30,6 +30,9 @@
$% [%rest p=@da]
[%wait p=@da]
== ==
$: %c
$>(%warp task:clay)
==
:: %d: to dill
::
$: %d
@ -53,6 +56,12 @@
$: %gall
gift:gall
:: $>(%unto gift:gall)
::
==
$: %clay
gift:clay
:: $>(%writ gift:clay)
::
== ==
--
:: more structures
@ -1231,9 +1240,9 @@
::NOTE these will only fail if the mark and/or json types changed,
:: since conversion failure also gets caught during first receive.
:: we can't do anything about this, so consider it unsupported.
?~ sign=(channel-event-to-sign channel-event) $
?~ json=(sign-to-json request-id u.sign) $
$(events [(event-json-to-wall id u.json) events])
?~ sign=(channel-event-to-sign channel-event) $
?~ jive=(sign-to-json request-id u.sign) $
$(events [(event-json-to-wall id +.u.jive) events])
:: send the start event to the client
::
=^ http-moves state
@ -1499,8 +1508,12 @@
:: if conversion succeeds, we *can* send it. if the client is actually
:: connected, we *will* send it immediately.
::
=/ json=(unit json)
=/ jive=(unit (quip move json))
(sign-to-json request-id sign)
=/ json=(unit json)
?~(jive ~ `+.u.jive)
=? moves ?=(^ jive)
(weld moves -.u.jive)
=* sending &(?=([%| *] state.u.channel) ?=(^ json))
::
=/ next-id next-id.u.channel
@ -1578,7 +1591,7 @@
^= data
%- wall-to-octs
%+ event-json-to-wall next-id
(need (sign-to-json request-id %kick ~))
+:(need (sign-to-json request-id %kick ~))
::
complete=%.n
==
@ -1619,32 +1632,33 @@
:: +sign-to-json: render sign from request-id as json channel event
::
++ sign-to-json
~% %sign-to-json ..part ~
|= [request-id=@ud =sign:agent:gall]
^- (unit json)
^- (unit (quip move json))
:: for facts, we try to convert the result to json
::
=/ jsyn=(unit sign:agent:gall)
?. ?=(%fact -.sign) `sign
?: ?=(%json p.cage.sign) `sign
=/ [from=(unit mark) jsyn=(unit sign:agent:gall)]
?. ?=(%fact -.sign) [~ `sign]
?: ?=(%json p.cage.sign) [~ `sign]
:: find and use tube from fact mark to json
::
=* have=mark p.cage.sign
=* desc=tape "from {(trip have)} to json"
=/ tube=(unit tube:clay)
=/ tuc=(unit (unit cage))
(rof ~ %cc [our %home da+now] /[have]/json)
?. ?=([~ ~ *] tuc) ~
`!<(tube:clay q.u.u.tuc)
?~ tube
((slog leaf+"eyre: no tube {desc}" ~) ~)
::
=/ res (mule |.((u.tube q.cage.sign)))
?: ?=(%& -.res)
`[%fact %json p.res]
((slog leaf+"eyre: failed tube {desc}" ~) ~)
::
=/ convert=(unit vase)
=/ cag=(unit (unit cage))
(rof ~ %cf [our %home da+now] /[have]/json)
?. ?=([~ ~ *] cag) ~
`q.u.u.cag
?~ convert
((slog leaf+"eyre: no convert {desc}" ~) [~ ~])
~| "conversion failed {desc}"
[`have `[%fact %json (slym u.convert q.q.cage.sign)]]
?~ jsyn ~
%- some
:- ?~ from ~
:_ ~
:^ duct %pass /conversion-cache/[u.from]
[%c %warp our %home `[%sing %f da+now /[u.from]/json]]
=* sign u.jsyn
=, enjs:format
%- pairs
@ -1665,7 +1679,7 @@
:- 'json'
~| [%unexpected-fact-mark p.cage.sign]
?> =(%json p.cage.sign)
;;(json q.q.cage.sign)
!<(json q.cage.sign)
==
::
%kick
@ -2320,14 +2334,15 @@
::
|^ ^- [(list move) _http-server-gate]
::
?+ i.wire
~|([%bad-take-wire wire] !!)
?+ i.wire
~|([%bad-take-wire wire] !!)
::
%run-app-request run-app-request
%watch-response watch-response
%sessions sessions
%channel channel
%acme acme-ack
%run-app-request run-app-request
%watch-response watch-response
%sessions sessions
%channel channel
%acme acme-ack
%conversion-cache `http-server-gate
==
::
++ run-app-request

View File

@ -159,7 +159,10 @@
~< %slog.[0 leaf+"gall: molted"]
:: +molt should never notify its client about agent changes
::
=- [(skip -< |=(move ?=([* %give %onto *] +<))) ->]
=- :_ ->
%+ welp
(skip -< |=(move ?=([* %give %onto *] +<)))
[^duct %pass /whiz/gall %$ %whiz ~]~
=/ adult adult-core
=. state.adult
[%7 system-duct outstanding contacts yokes=~ blocked]:spore

View File

@ -3391,6 +3391,11 @@
:: :: ++no:dejs:format
++ no :: number as cord
|=(jon=json ?>(?=([%n *] jon) p.jon))
:: :: ++nu:dejs:format
++ nu :: parse number as hex
|= jon=json
?> ?=([%s *] jon)
(rash p.jon hex)
:: :: ++of:dejs:format
++ of :: object as frond
|* wer=(pole [cord fist])
@ -3440,6 +3445,11 @@
=/ ten ~|(key+key.wer (wit.wer (~(get by jom) key.wer)))
?~(t.wer ten [ten ((ou-raw t.wer) jom)])
==
:: :: ++oj:dejs:format
++ oj :: object as jug
|* =fist
^- $-(json (jug cord _(fist *json)))
(om (as fist))
:: :: ++om:dejs:format
++ om :: object as map
|* wit=fist
@ -3466,6 +3476,12 @@
:: :: ++sa:dejs:format
++ sa :: string as tape
|=(jon=json ?>(?=([%s *] jon) (trip p.jon)))
:: :: ++sd:dejs:format
++ sd :: string @ud as date
|= jon=json
^- @da
?> ?=(%s -.jon)
`@da`(rash p.jon dem:ag)
:: :: ++se:dejs:format
++ se :: string as aura
|= aur=@tas
@ -3580,6 +3596,15 @@
?. ?=([%s *] jon) ~
(bind (stud:chrono:userlib p.jon) |=(a=date (year a)))
::
++ dank :: tank
^- $-(json (unit tank))
%+ 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) ~)
==
::
++ di :: millisecond date
(cu from-unix-ms:chrono:userlib ni)
::
@ -3653,6 +3678,12 @@
|* [pre=* wit=fist]
(cu |*(* [pre +<]) wit)
::
++ re :: recursive reparsers
|* [gar=* sef=_|.(fist)]
|= jon=json
^- (unit _gar)
((sef) jon)
::
++ sa :: string as tape
|= jon=json
?.(?=([%s *] jon) ~ (some (trip p.jon)))

View File

@ -49,7 +49,7 @@
^- hexb:bc
%- flip:byt:bc
%- sha256:bc
(script-pubkey:bc a)
(to-script-pubkey:adr:bc a)
::
++ parse-json-rpc
|= =json

View File

@ -48,7 +48,7 @@ export default class Send extends Component {
showModal: false,
note: '',
choosingSignMethod: false,
signMethod: 'Sign Transaction',
signMethod: 'bridge',
};
this.initPayment = this.initPayment.bind(this);
@ -86,7 +86,7 @@ export default class Send extends Component {
}
setSignMethod(signMethod) {
this.setState({signMethod});
this.setState({signMethod, choosingSignMethod: false});
}
checkPayee(e){
@ -204,7 +204,7 @@ export default class Send extends Component {
const signReady = (this.state.ready && (parseInt(this.state.satsAmount) > 0)) && !signing;
let invoice = null;
if (signMethod === 'Sign Transaction') {
if (signMethod === 'masterTicket') {
invoice =
<Invoice
network={network}
@ -218,7 +218,7 @@ export default class Send extends Component {
satsAmount={satsAmount}
state={this.props.state}
/>
} else if (signMethod === 'Sign with Bridge') {
} else if (signMethod === 'bridge') {
invoice =
<BridgeInvoice
state={this.props.state}
@ -439,6 +439,17 @@ export default class Send extends Component {
border='none'
style={{cursor: signReady ? 'pointer' : 'default'}} />
</Row>
{signMethod === 'masterTicket' &&
<Row
mt={4}
alignItems='center'
>
<Icon icon='Info' color='yellow' height={4} width={4}/>
<Text fontSize="14px" fontWeight="regular" color="gray" ml={2}>
We recommend that you sign transactions using Bridge to protect your master ticket.
</Text>
</Row>
}
</Col>
}
</>

View File

@ -19,26 +19,26 @@ export default function Signer(props) {
backgroundColor='transparent'
fontWeight='bold'
cursor='pointer'
color={(signMethod === 'Sign Transaction') ? 'blue' : 'lightBlue'}
color={(signMethod === 'masterTicket') ? 'blue' : 'lightBlue'}
py='24px'
px='24px'
onClick={() => setSignMethod('Sign Transaction')}
children='Sign Transaction' />
onClick={() => setSignMethod('masterTicket')}
children='Sign with Master Ticket' />
<Button
border='none'
backgroundColor='transparent'
fontWeight='bold'
cursor='pointer'
color={(signMethod === 'Sign with Bridge') ? 'blue' : 'lightBlue'}
color={(signMethod === 'bridge') ? 'blue' : 'lightBlue'}
py='24px'
px='24px'
onClick={() => setSignMethod('Sign with Bridge')}
onClick={() => setSignMethod('bridge')}
children='Sign with Bridge' />
</Box>
:
<Button
primary
children={signMethod}
children={signMethod === 'bridge' ? 'Sign with Bridge' : 'Sign with Master Ticket'}
fontSize={1}
fontWeight='bold'
borderRadius='24px'

View File

@ -22,7 +22,7 @@ export default class WalletModal extends Component {
super(props);
this.state = {
mode: 'masterTicket',
mode: 'xpub',
masterTicket: '',
confirmedMasterTicket: '',
xpub: '',
@ -103,13 +103,15 @@ export default class WalletModal extends Component {
Step 2 of 2: Import your extended public key
</Text>
</Row>
<Box mt={3}>
<Text fontSize="14px" fontWeight="regular" color="gray">
To begin, you'll need to derive an XPub Bitcoin address using your
master ticket.
<a fontSize="14px" target="_blank" href="https://urbit.org/bitcoin-wallet"> Learn More</a>
<Row
mt={3}
alignItems='center'
>
<Icon icon='Info' color='yellow' height={4} width={4}/>
<Text fontSize="14px" fontWeight="regular" color="gray" ml={2}>
We recommend that you import your wallet using Bridge to protect your master ticket.
</Text>
</Box>
</Row>
<Box
display='flex'
alignItems='center'
@ -126,7 +128,7 @@ export default class WalletModal extends Component {
error: false
})}/>}
<Text fontSize="14px" fontWeight="500">
{this.state.confirmingMasterTicket ? 'Confirm Master Key' : 'Master Key'}
{this.state.confirmingMasterTicket ? 'Confirm Master Ticket' : 'Master Ticket'}
</Text>
</Box>
<Row alignItems="center">
@ -155,37 +157,38 @@ export default class WalletModal extends Component {
</Text>
</Row>
}
<Box mt={3} mb={3}>
<Text fontSize="14px" fontWeight="regular"
color={(inputDisabled) ? "lighterGray" : "gray"}
style={{cursor: (inputDisabled) ? "default" : "pointer"}}
<Row mt={3}>
<Button
primary
color="black"
backgroundColor="veryLightGray"
borderColor="veryLightGray"
children="Cancel"
fontSize="14px"
mr={2}
style={{cursor: "pointer"}}
onClick={() => {this.setState({mode: 'xpub', masterTicket: '', xpub: '', readyToSubmit: false})}}
/>
<Button
primary
disabled={buttonDisabled}
children="Next Step"
fontSize="14px"
style={{cursor: buttonDisabled ? "default" : "pointer"}}
onClick={() => {
if (inputDisabled) return;
this.setState({mode: 'xpub', xpub: '', masterTicket: '', readyToSubmit: false})
}}
>
Manually import your extended public key ->
</Text>
</Box>
<Button
primary
disabled={buttonDisabled}
children="Next Step"
fontSize="14px"
style={{cursor: buttonDisabled ? "default" : "pointer"}}
onClick={() => {
if (!this.state.confirmingMasterTicket) {
this.setState({confirmingMasterTicket: true});
} else {
if (this.state.masterTicket === this.state.confirmedMasterTicket) {
this.setState({error: false});
this.submitMasterTicket(this.state.masterTicket);
if (!this.state.confirmingMasterTicket) {
this.setState({confirmingMasterTicket: true});
} else {
this.setState({error: true});
if (this.state.masterTicket === this.state.confirmedMasterTicket) {
this.setState({error: false});
this.submitMasterTicket(this.state.masterTicket);
} else {
this.setState({error: true});
}
}
}
}}
/>
}}
/>
</Row>
</Box>
);
} else if (this.state.mode === 'xpub') {
@ -203,7 +206,7 @@ export default class WalletModal extends Component {
</Row>
<Box mt={3}>
<Text fontSize="14px" fontWeight="regular" color="gray">
Visit bridge.urbit.org to obtain your key
Visit <a href='https://bridge.urbit.org/?kind=xpub' target='_blank' style={{color: 'black'}} >bridge.urbit.org</a> to obtain your key
</Text>
</Box>
<Box mt={3} mb={2}>
@ -221,27 +224,27 @@ export default class WalletModal extends Component {
autoCorrect="off"
onChange={this.checkXPub}
/>
<Row mt={3}>
<Button
primary
color="black"
backgroundColor="veryLightGray"
borderColor="veryLightGray"
children="Cancel"
fontSize="14px"
mr={2}
style={{cursor: "pointer"}}
onClick={() => {this.setState({mode: 'masterTicket', masterTicket: '', xpub: '', readyToSubmit: false})}}
/>
<Button
primary
disabled={buttonDisabled}
children="Next Step"
fontSize="14px"
style={{cursor: this.state.ready ? "pointer" : "default"}}
onClick={() => { this.submitXPub(this.state.xpub) }}
/>
</Row>
<Box mt={3} mb={3}>
<Text fontSize="14px" fontWeight="regular"
color={(inputDisabled) ? "lighterGray" : "gray"}
style={{cursor: (inputDisabled) ? "default" : "pointer"}}
onClick={() => {
if (inputDisabled) return;
this.setState({mode: 'masterTicket', xpub: '', masterTicket: '', readyToSubmit: false})
}}
>
Import using master ticket ->
</Text>
</Box>
<Button
primary
mt={3}
disabled={buttonDisabled}
children="Next Step"
fontSize="14px"
style={{cursor: this.state.ready ? "pointer" : "default"}}
onClick={() => { this.submitXPub(this.state.xpub) }}
/>
</Box>
);
}

View File

@ -1,10 +1,9 @@
import { Association, GraphNotifDescription, IndexedNotification, NotifIndex } from '@urbit/api';
import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
import { BigInteger } from 'big-integer';
import { getParentIndex } from '../lib/notification';
import { dateToDa, decToUd } from '../lib/util';
import {reduce} from '../reducers/hark-update';
import {doOptimistically, optReduceState} from '../state/base';
import { reduce } from '../reducers/hark-update';
import { doOptimistically } from '../state/base';
import useHarkState from '../state/hark';
import { StoreState } from '../store/type';
import BaseApi from './base';
@ -62,7 +61,7 @@ export class HarkApi extends BaseApi<StoreState> {
index
}
};
await doOptimistically(useHarkState, action, this.harkAction.bind(this), [reduce])
await doOptimistically(useHarkState, action, this.harkAction.bind(this), [reduce]);
}
read(time: BigInteger, index: NotifIndex) {
@ -81,6 +80,18 @@ export class HarkApi extends BaseApi<StoreState> {
return this.actOnNotification('unread-note', time, index);
}
readGroup(group: string) {
return this.harkAction({
'read-group': group
});
}
readGraph(graph: string) {
return this.harkAction({
'read-graph': graph
});
}
dismissReadCount(graph: string, index: string) {
return this.harkAction({
'read-count': {

View File

@ -5,7 +5,7 @@ import React, {
useEffect,
useMemo,
useRef,
useState,
useState
} from 'react';
import _ from 'lodash';
import { getChord } from '~/logic/lib/util';
@ -13,10 +13,9 @@ import { getChord } from '~/logic/lib/util';
type Handler = (e: KeyboardEvent) => void;
const fallback: ShortcutContextProps = {
add: () => {},
remove: () => {},
remove: () => {}
};
export const ShortcutContext = createContext(fallback);
export interface ShortcutContextProps {
add: (cb: (e: KeyboardEvent) => void, key: string) => void;
@ -27,19 +26,19 @@ export function ShortcutContextProvider({ children }) {
const handlerRef = useRef<Handler>(() => {});
const add = useCallback((cb: Handler, key: string) => {
setShortcuts((s) => ({ ...s, [key]: cb }));
setShortcuts(s => ({ ...s, [key]: cb }));
}, []);
const remove = useCallback((cb: Handler, key: string) => {
setShortcuts((s) => (key in s ? _.omit(s, key) : s));
setShortcuts(s => (key in s ? _.omit(s, key) : s));
}, []);
useEffect(() => {
function onKeypress(e: KeyboardEvent) {
handlerRef.current(e);
}
document.addEventListener('keypress', onKeypress);
document.addEventListener('keydown', onKeypress);
return () => {
document.removeEventListener('keypress', onKeypress);
document.removeEventListener('keydown', onKeypress);
};
}, []);
@ -50,7 +49,7 @@ export function ShortcutContextProvider({ children }) {
};
}, [shortcuts]);
const value = useMemo(() => ({ add, remove }), [add, remove])
const value = useMemo(() => ({ add, remove }), [add, remove]);
return (
<ShortcutContext.Provider value={value}>

View File

@ -1,7 +1,7 @@
import urbitOb from 'urbit-ob';
import { parsePermalink, permalinkToReference } from '~/logic/lib/permalinks';
const URL_REGEX = new RegExp(String(/^([^[\]]*?)(([\w\-\+]+:\/\/)[-a-zA-Z0-9:@;?&=\/%\+\.\*!'\(\),\$_\{\}\^~\[\]`#|]+[\w/])([\s\S]*)/.source));
const URL_REGEX = new RegExp(String(/^([^[\]]*?)(([\w\-\+]+:\/\/)[-a-zA-Z0-9:@;?&=\/%\+\.\*!'\(\),\$_\{\}\^~\[\]`#|]+[-a-zA-Z0-9:@;?&=\/%\+\*!'\(\)\$_\{\}\^~\[\]`#|])([\s\S]*)/.source));
const PATP_REGEX = /^([\s\S]*?)(~[a-z_-]+)([\s\S]*)/;

View File

@ -93,4 +93,24 @@ describe('tokenizeMessage', () => {
expect(three).toEqual(' test ');
expect(hastuc).toEqual('~hastuc-dibtux');
});
it('should tokenize a url with a par', () => {
const example = 'test https://en.wikipedia.org/wiki/Turbo_(gastropod)';
const [{ text }, { url }] = tokenizeMessage(example);
expect(text).toBe('test ');
expect(url).toBe('https://en.wikipedia.org/wiki/Turbo_(gastropod)');
});
it('should ignore ending commas', () => {
const example = 'https://tlon.io/test, foo';
const [{ url }, { text }] = tokenizeMessage(example);
expect(text).toBe(', foo');
expect(url).toBe('https://tlon.io/test');
});
it('should ignore ending dots', () => {
const example = 'https://tlon.io/test. foo';
const [{ url }, { text }] = tokenizeMessage(example);
expect(text).toBe('. foo');
expect(url).toBe('https://tlon.io/test');
});
});

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from 'react';
import { DragEvent, useCallback, useEffect, useState } from 'react';
function validateDragEvent(e: DragEvent): FileList | File[] | true | null {
const files: File[] = [];
@ -37,7 +37,7 @@ export function useFileDrag(dragged: (f: FileList | File[], e: DragEvent) => voi
const [dragging, setDragging] = useState(false);
const onDragEnter = useCallback(
(e: DragEvent) => {
(e: DragEvent<HTMLDivElement>) => {
if (!validateDragEvent(e)) {
return;
}
@ -47,7 +47,7 @@ export function useFileDrag(dragged: (f: FileList | File[], e: DragEvent) => voi
);
const onDrop = useCallback(
(e: DragEvent) => {
(e: DragEvent<HTMLDivElement>) => {
setDragging(false);
const files = validateDragEvent(e);
if (!files || files === true) {
@ -60,7 +60,7 @@ export function useFileDrag(dragged: (f: FileList | File[], e: DragEvent) => voi
);
const onDragOver = useCallback(
(e: DragEvent) => {
(e: DragEvent<HTMLDivElement>) => {
if (!validateDragEvent(e)) {
return;
}

View File

@ -1,9 +1,9 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import _ from 'lodash';
import { patp2dec } from 'urbit-ob';
import f, { compose, memoize } from 'lodash/fp';
import f from 'lodash/fp';
import { Association, Contact, Patp } from '@urbit/api';
import produce, { enableMapSet } from 'immer';
import { enableMapSet } from 'immer';
import useSettingsState from '../state/settings';
/* eslint-disable max-lines */
import anyAscii from 'any-ascii';
@ -50,7 +50,7 @@ export function parentPath(path: string) {
return _.dropRight(path.split('/'), 1).join('/');
}
/**
/*
* undefined -> initial
* null -> disabled feed
* string -> enabled feed
@ -67,23 +67,26 @@ export function getFeedPath(association: Association): string | null | undefined
}
export const getChord = (e: KeyboardEvent) => {
let chord = [e.key];
const chord = [e.key];
if(e.metaKey) {
chord.unshift('meta');
}
if(e.ctrlKey) {
chord.unshift('ctrl');
}
if(e.shiftKey) {
chord.unshift('shift');
}
return chord.join('+');
}
};
export function getResourcePath(workspace: Workspace, path: string, joined: boolean, mod: string) {
const base = workspace.type === 'group'
? `/~landscape${workspace.group}`
: workspace.type === 'home'
? `/~landscape/home`
: `/~landscape/messages`;
return `${base}/${joined ? 'resource' : 'join'}/${mod}${path}`
? '/~landscape/home'
: '/~landscape/messages';
return `${base}/${joined ? 'resource' : 'join'}/${mod}${path}`;
}
const DA_UNIX_EPOCH = bigInt('170141184475152167957503069145530368000'); // `@ud` ~1970.1.1
@ -135,14 +138,14 @@ export function decToUd(str: string): string {
);
}
/**
/*
* Clamp a number between a min and max
*/
export function clamp(x: number, min: number, max: number) {
return Math.max(min, Math.min(max, x));
}
/**
/*
* Euclidean modulo
*/
export function modulo(x: number, mod: number) {
@ -355,6 +358,7 @@ export function stringToTa(str: string) {
add = '~~';
break;
default:
// eslint-disable-next-line
const charCode = str.charCodeAt(i);
if (
(charCode >= 97 && charCode <= 122) || // a-z
@ -413,7 +417,7 @@ export function stringToSymbol(str: string) {
}
return result;
}
/**
/*
* Formats a numbers as a `@ud` inserting dot where needed
*/
export function numToUd(num: number) {
@ -428,7 +432,7 @@ export function numToUd(num: number) {
}
export function patpToUd(patp: Patp) {
return numToUd(patp2dec(patp))
return numToUd(patp2dec(patp));
}
export function usePreventWindowUnload(shouldPreventDefault: boolean, message = 'You have unsaved changes. Are you sure you want to exit?') {
@ -443,7 +447,7 @@ export function usePreventWindowUnload(shouldPreventDefault: boolean, message =
window.onbeforeunload = handleBeforeUnload;
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
// @ts-ignore
// @ts-ignore need better window typings
window.onbeforeunload = undefined;
};
}, [shouldPreventDefault]);
@ -484,8 +488,8 @@ export function withHovering<T>(Component: React.ComponentType<T>) {
return React.forwardRef((props, ref) => {
const { hovering, bind } = useHovering();
// @ts-ignore needs type signature on return?
return <Component ref={ref} hovering={hovering} bind={bind} {...props} />
})
return <Component ref={ref} hovering={hovering} bind={bind} {...props} />;
});
}
const DM_REGEX = /ship\/~([a-z]|-)*\/dm--/;
@ -500,14 +504,14 @@ export function getItemTitle(association: Association): string {
return association.metadata.title ?? association.resource ?? '';
}
export const svgDataURL = (svg) => 'data:image/svg+xml;base64,' + btoa(svg);
export const svgDataURL = svg => 'data:image/svg+xml;base64,' + btoa(svg);
export const svgBlobURL = (svg) => URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
export const svgBlobURL = svg => URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
export const favicon = () => {
let background = '#ffffff';
const contacts = useContactState.getState().contacts;
if (contacts.hasOwnProperty(`~${window.ship}`)) {
if (Object.prototype.hasOwnProperty.call(contacts, `~${window.ship}`)) {
background = `#${uxToHex(contacts[`~${window.ship}`].color)}`;
}
const foreground = foregroundFromBackground(background);
@ -518,4 +522,4 @@ export const favicon = () => {
colors: [background, foreground]
});
return svg;
}
};

View File

@ -23,6 +23,7 @@ export interface ShortcutMapping {
navForward: string;
navBack: string;
hideSidebar: string;
readGroup: string;
}
export interface SettingsState {
@ -93,7 +94,8 @@ const useSettingsState = createState<SettingsState>(
cycleBack: 'ctrl+;',
navForward: 'ctrl+]',
navBack: 'ctrl+[',
hideSidebar: 'ctrl+\\'
hideSidebar: 'ctrl+\\',
readGroup: 'shift+Escape'
},
getAll: async () => {
const { all } = await airlock.scry(getAll);

View File

@ -75,8 +75,6 @@ class App extends React.Component {
super(props);
this.ship = window.ship;
// eslint-disable-next-line
this.updateTheme = this.updateTheme.bind(this);
this.updateMobile = this.updateMobile.bind(this);
}

View File

@ -1,6 +1,6 @@
import { BaseInput, Box, Button, LoadingSpinner, Text } from '@tlon/indigo-react';
import { hasProvider } from 'oembed-parser';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useState, DragEvent, useEffect } from 'react';
import { createPost } from '~/logic/api/graph';
import { parsePermalink, permalinkToReference } from '~/logic/lib/permalinks';
import { useFileDrag } from '~/logic/lib/useDrag';
@ -94,6 +94,10 @@ const LinkSubmit = (props: LinkSubmitProps) => {
return link;
};
useEffect(() => {
setLinkValid(validateLink(linkValue));
}, [linkValue]);
const onFileDrag = useCallback(
(files: FileList | File[], e: DragEvent): void => {
if (!canUpload) {

View File

@ -5,7 +5,7 @@ import 'codemirror/addon/edit/continuelist';
import 'codemirror/lib/codemirror.css';
import 'codemirror/mode/markdown/markdown';
import { useFormikContext } from 'formik';
import React, { useCallback, useRef } from 'react';
import React, { useCallback, useRef, DragEvent } from 'react';
import { UnControlled as CodeEditor } from 'react-codemirror2';
import { Prompt } from 'react-router-dom';
import { useFileDrag } from '~/logic/lib/useDrag';
@ -61,13 +61,6 @@ export function MarkdownEditor(
[onChange]
);
const handleBlur = useCallback(
(_i, e: any) => {
onBlur && onBlur(e);
},
[onBlur]
);
const { uploadDefault, canUpload } = useStorage();
const onFileDrag = useCallback(
@ -110,10 +103,10 @@ export function MarkdownEditor(
value={value}
options={options}
onChange={handleChange}
onDragLeave={(editor, e: DragEvent) => bind.onDragLeave(e)}
onDragOver={(editor, e: DragEvent) => bind.onDragOver(e)}
onDrop={(editor, e: DragEvent) => bind.onDrop(e)}
onDragEnter={(editor, e: DragEvent) => bind.onDragEnter(e)}
onDragLeave={(editor, e: any) => bind.onDragLeave(e)}
onDragOver={(editor, e: any) => bind.onDragOver(e)}
onDrop={(editor, e: any) => bind.onDrop(e)}
onDragEnter={(editor, e: any) => bind.onDragEnter(e)}
/>
{dragging && <SubmitDragger />}
</Box>

View File

@ -1,10 +1,11 @@
import { Box, Col, Row, Text } from '@tlon/indigo-react';
import { Association, Graph } from '@urbit/api';
import React, { ReactElement } from 'react';
import { Box, Button, Col, Row, Text } from '@tlon/indigo-react';
import { Association, Graph, readGraph } from '@urbit/api';
import React, { ReactElement, useCallback } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { useShowNickname } from '~/logic/lib/util';
import useContactState from '~/logic/state/contact';
import useGroupState from '~/logic/state/group';
import airlock from '~/logic/api';
import { NotebookPosts } from './NotebookPosts';
interface NotebookProps {
@ -28,12 +29,15 @@ export function Notebook(props: NotebookProps & RouteComponentProps): ReactEleme
const contacts = useContactState(state => state.contacts);
const group = groups[association?.group];
const relativePath = (p: string) => props.baseUrl + p;
const contact = contacts?.[`~${ship}`];
const showNickname = useShowNickname(contact);
const readBook = useCallback(() => {
airlock.poke(readGraph(association.resource));
}, [association.resource]);
if (!group) {
return null; // Waiting on groups to populate
}
@ -48,6 +52,7 @@ export function Notebook(props: NotebookProps & RouteComponentProps): ReactEleme
{showNickname ? contact?.nickname : ship}
</Text>
</Box>
<Button onClick={readBook}>Mark all as Read</Button>
</Row>
<Box borderBottom={1} borderBottomColor="lightGray" />
<NotebookPosts

View File

@ -32,7 +32,9 @@ const StoreDebugger = (props: StoreDebuggerProps) => {
let output: any = false;
try {
output = _.get(state, filterToTry, undefined);
} catch (e) { }
} catch (e) {
console.log('filter failed');
}
if (output) {
console.log(output);
setText(objectToString(output));

View File

@ -103,6 +103,7 @@ export default function ShortcutSettings() {
label="Cycle backward through channel list"
/>
<ChordInput id="hideSidebar" label="Show/hide group sidebar" />
<ChordInput id="readGroup" label="Read all in a group" />
</Box>
<AsyncButton primary width="fit-content">Save Changes</AsyncButton>
</Col>

View File

@ -244,7 +244,7 @@ const renderers = {
return (
<Text
mono
fontWeight='inherit'
fontWeight="inherit"
p={1}
backgroundColor="washedGray"
fontSize={0}
@ -284,7 +284,11 @@ const renderers = {
paragraph: ({ children }) => {
return (
<Box display="block">
<Text fontSize={1} lineHeight="tall" style={{ 'overflowWrap': 'break-word' }}>
<Text
fontSize={1}
lineHeight="tall"
style={{ overflowWrap: 'break-word' }}
>
{children}
</Text>
</Box>
@ -309,7 +313,7 @@ const renderers = {
className="clamp-message"
display="block"
borderRadius={1}
fontWeight='inherit'
fontWeight="inherit"
mono
fontSize={0}
backgroundColor="washedGray"
@ -342,20 +346,12 @@ const renderers = {
'graph-mention': ({ ship }) => <Mention ship={ship} />,
image: ({ url, tall }) => (
<Box mt="1" mb="2" flexShrink={0}>
<RemoteContent
key={url}
url={url}
tall={tall}
/>
<RemoteContent key={url} url={url} tall={tall} />
</Box>
),
'graph-url': ({ url, tall }) => (
<Box mt={1} mb={2} flexShrink={0}>
<RemoteContent
key={url}
url={url}
tall={tall}
/>
<RemoteContent key={url} url={url} tall={tall} />
</Box>
),
'graph-reference': ({ reference, transcluded }) => {

View File

@ -1,11 +1,12 @@
import { AppName } from '@urbit/api';
import { AppName, readGroup } from '@urbit/api';
import _ from 'lodash';
import React, { useEffect } from 'react';
import React, { useCallback, useEffect } from 'react';
import Helmet from 'react-helmet';
import {
Route,
RouteComponentProps, Switch
} from 'react-router-dom';
import { useShortcut } from '~/logic/state/settings';
import { useLocalStorageState } from '~/logic/lib/useLocalStorageState';
import { getGroupFromWorkspace } from '~/logic/lib/workspace';
import useGroupState from '~/logic/state/group';
@ -25,6 +26,7 @@ import { NewChannel } from './NewChannel';
import { PopoverRoutes } from './PopoverRoutes';
import { Resource } from './Resource';
import { Skeleton } from './Skeleton';
import airlock from '~/logic/api';
type GroupsPaneProps = StoreState & {
baseUrl: string;
@ -40,6 +42,12 @@ export function GroupsPane(props: GroupsPaneProps) {
const groupPath = getGroupFromWorkspace(workspace);
const groups = useGroupState(state => state.groups);
useShortcut('readGroup', useCallback(() => {
if(groupPath) {
airlock.poke(readGroup(groupPath));
}
}, [groupPath]));
const groupAssociation =
(groupPath && associations.groups[groupPath]) || undefined;
const group = (groupPath && groups[groupPath]) || undefined;

View File

@ -102,8 +102,10 @@ export function SidebarDmItem(props: {
}) {
const { ship, selected = false } = props;
const contact = useContact(ship);
const title = contact?.nickname || (cite(ship) ?? ship);
const hideAvatars = false;
const { hideAvatars, hideNicknames } = useSettingsState(s => s.calm);
const title = (!hideNicknames && contact?.nickname)
? contact?.nickname
: (cite(ship) ?? ship);
const { unreads } = useHarkDm(ship) || { unreads: 0 };
const img =
contact?.avatar && !hideAvatars ? (
@ -131,7 +133,7 @@ export function SidebarDmItem(props: {
hasUnread={(unreads as number) > 0}
to={`/~landscape/messages/dm/${ship}`}
title={title}
mono={!contact?.nickname}
mono={hideAvatars || !contact?.nickname}
isSynced
>
{img}

View File

@ -20,7 +20,7 @@ function sidebarSort(
const alphabetical = (a: string, b: string) => {
const aAssoc = associations[a];
const bAssoc = associations[b];
const aTitle = aAssoc?.metadata?.title || b;
const aTitle = aAssoc?.metadata?.title || a;
const bTitle = bAssoc?.metadata?.title || b;
return alphabeticalOrder(aTitle, bTitle);
@ -33,8 +33,9 @@ function sidebarSort(
const bAppName = bAssoc?.['app-name'];
const aUpdated = a.startsWith('~')
? (inboxUnreads?.[`/${patp2dec(a)}`]?.last)
? (inboxUnreads?.[`/${patp2dec(a)}`]?.last || 0)
: (apps[aAppName]?.lastUpdated(a) || 0);
const bUpdated = b.startsWith('~')
? (inboxUnreads?.[`/${patp2dec(b)}`]?.last || 0)
: (apps[bAppName]?.lastUpdated(b) || 0);
@ -108,14 +109,19 @@ export function SidebarList(props: {
const offset = backward ? -1 : 1;
const newIdx = modulo(idx+offset, ordered.length - 1);
const { metadata, resource } = associations[ordered[newIdx]];
const joined = graphKeys.has(resource.slice(7));
let path = '/~landscape/home';
if ('graph' in metadata.config) {
path = getResourcePath(workspace, resource, joined, metadata.config.graph);
const newChannel = ordered[newIdx];
let path = '';
if(newChannel.startsWith('~')) {
path = `/~landscape/messages/dm/${newChannel}`;
} else {
const { metadata, resource } = associations.graph[ordered[newIdx]];
const joined = graphKeys.has(resource.slice(7));
if ('graph' in metadata.config) {
path = getResourcePath(workspace, resource, joined, metadata.config.graph);
}
}
history.push(path);
}, [selected, history.push]);
}, [ordered, selected, history.push]);
useShortcut('cycleForward', useCallback((e: KeyboardEvent) => {
cycleChannels(false);

View File

@ -243,3 +243,20 @@ export const getNotificationCount = (
.map(index => unread[index]?.notifications as number || 0)
.reduce(f.add, 0);
}
/**
* Read all graphs belonging to a particular group
*/
export const readGroup = (group: string) =>
harkAction({
'read-group': group
});
/**
* Read all unreads in a graph
*/
export const readGraph = (graph: string) =>
harkAction({
'read-graph': graph
});