diff --git a/pkg/arvo/app/glob.hoon b/pkg/arvo/app/glob.hoon index f8685a267..90b0ab225 100644 --- a/pkg/arvo/app/glob.hoon +++ b/pkg/arvo/app/glob.hoon @@ -5,7 +5,7 @@ /- glob /+ default-agent, verb, dbug |% -++ hash 0v3.29n7b.04srk.3pcv0.1ld5v.vl1io +++ hash 0v6.9vk2h.hr87m.nn63p.8kmo5.k4ljt +$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))] +$ all-states $% state-0 @@ -89,7 +89,7 @@ =+ .^(=map=tube:clay %cc (weld home /map/mime)) =+ .^(arch %cy (weld home /app/landscape/js/bundle)) =/ bundle-hash=@t - %- need + %- need ^- (unit @t) %- ~(rep by dir) |= [[file=@t ~] out=(unit @t)] diff --git a/pkg/arvo/app/graph-store.hoon b/pkg/arvo/app/graph-store.hoon index 6fce9e517..1c6abbefc 100644 --- a/pkg/arvo/app/graph-store.hoon +++ b/pkg/arvo/app/graph-store.hoon @@ -560,6 +560,14 @@ |^ ?> (team:title our.bowl src.bowl) ?+ path (on-peek:def path) + [%x %graph-mark @ @ ~] + =/ =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+!>(q.u.result) + :: [%x %keys ~] :- ~ :- ~ :- %graph-update !>(`update:store`[%0 now.bowl [%keys ~(key by graphs)]]) diff --git a/pkg/arvo/app/hark-chat-hook.hoon b/pkg/arvo/app/hark-chat-hook.hoon new file mode 100644 index 000000000..472bcb7dc --- /dev/null +++ b/pkg/arvo/app/hark-chat-hook.hoon @@ -0,0 +1,180 @@ +:: hark-chat-hook: notifications for chat-store [landscape] +:: +/- store=hark-store, post, group-store, metadata-store, hook=hark-chat-hook +/+ resource, metadata, default-agent, dbug, chat-store +:: +~% %hark-chat-hook-top ..is ~ +|% ++$ card card:agent:gall ++$ versioned-state + $% state-0 + == +:: ++$ state-0 + $: %0 + watching=(set path) + mentions=_& + == +:: +-- +:: +=| state-0 +=* state - +:: +=> + |_ =bowl:gall + :: + ++ give + |= [paths=(list path) =update:hook] + ^- (list card) + [%give %fact paths hark-chat-hook-update+!>(update)]~ + :: + ++ watch-chat + ^- card + [%pass /chat %agent [our.bowl %chat-store] %watch /updates] + -- +%- agent:dbug +^- agent:gall +~% %hark-chat-hook-agent ..card ~ +|_ =bowl:gall ++* this . + ha ~(. +> bowl) + def ~(. (default-agent this %|) bowl) + met ~(. metadata bowl) +:: +++ on-init + :_ this + ~[watch-chat:ha] +:: +++ on-save !>(state) +++ on-load + |= old=vase + ^- (quip card _this) + `this(state !<(state-0 old)) +:: +++ on-watch + |= =path + ^- (quip card _this) + =^ cards state + ?+ path (on-watch:def path) + :: + [%updates ~] + :_ state + %+ give:ha ~ + :* %initial + watching + == + == + [cards this] +:: +++ on-poke + ~/ %hark-chat-hook-poke + |= [=mark =vase] + ^- (quip card _this) + |^ + ?> (team:title our.bowl src.bowl) + =^ cards state + ?+ mark (on-poke:def mark vase) + %hark-chat-hook-action + (hark-chat-hook-action !<(action:hook vase)) + == + [cards this] + :: + ++ hark-chat-hook-action + |= =action:hook + ^- (quip card _state) + |^ + :- (give:ha ~[/updates] action) + ?- -.action + %listen (listen +.action) + %ignore (ignore +.action) + %set-mentions (set-mentions +.action) + == + ++ listen + |= chat=path + ^+ state + state(watching (~(put in watching) chat)) + :: + ++ ignore + |= chat=path + ^+ state + state(watching (~(del in watching) chat)) + :: + ++ set-mentions + |= ment=? + ^+ state + state(mentions ment) + -- + -- +:: +++ on-agent + ~/ %hark-chat-hook-agent + |= [=wire =sign:agent:gall] + ^- (quip card _this) + |^ + ?+ -.sign (on-agent:def wire sign) + %kick + :_ this + ?. ?=([%chat ~] wire) + ~ + ~[watch-chat:ha] + :: + %fact + ?. ?=(%chat-update p.cage.sign) + (on-agent:def wire sign) + =^ cards state + (chat-update !<(update:chat-store q.cage.sign)) + [cards this] + == + :: + ++ chat-update + |= =update:chat-store + ^- (quip card _state) + :_ state + ?+ -.update ~ + %message (process-envelope path.update envelope.update) + :: + %messages + %- zing + (turn envelopes.update (cury process-envelope path.update)) + == + ++ is-mention + |= =envelope:chat-store + ?. ?=(%text -.letter.envelope) %.n + ?& mentions + ?= ^ + (find (scow %p our.bowl) (trip text.letter.envelope)) + == + :: + ++ is-notification + |= [=path =envelope:chat-store] + ?& (~(has in watching) path) + !=(author.envelope our.bowl) + == + :: + ++ process-envelope + |= [=path =envelope:chat-store] + ^- (list card) + =/ mention=? + (is-mention envelope) + ?. ?|(mention (is-notification path envelope)) + ~ + =/ =index:store + [%chat path mention] + =/ =contents:store + [%chat ~[envelope]] + ~[(poke-store %add index when.envelope %.n contents)] + :: + ++ poke-store + |= =action:store + ^- card + =- [%pass /store %agent [our.bowl %hark-store] %poke -] + hark-action+!>(action) + -- +:: +++ on-peek on-peek:def +:: +++ on-leave on-leave:def +++ on-arvo on-arvo:def +++ on-fail on-fail:def +-- diff --git a/pkg/arvo/app/hark-graph-hook.hoon b/pkg/arvo/app/hark-graph-hook.hoon new file mode 100644 index 000000000..d9d1b7f71 --- /dev/null +++ b/pkg/arvo/app/hark-graph-hook.hoon @@ -0,0 +1,256 @@ +:: hark-graph-hook: notifications for graph-store [landscape] +:: +/- store=hark-store, post, group-store, metadata-store, hook=hark-graph-hook +/+ resource, metadata, default-agent, dbug, graph-store +:: +~% %hark-graph-hook-top ..is ~ +|% ++$ card card:agent:gall ++$ versioned-state + $% state-0 + == +:: ++$ state-0 + $: %0 + watching=(set resource) + mentions=_& + watch-on-self=_& + == +:: +-- +:: +=| state-0 +=* state - +:: +=> + |_ =bowl:gall + :: + ++ scry + |* [=mold p=path] + ?> ?=(^ p) + ?> ?=(^ t.p) + .^(mold i.p (scot %p our.bowl) i.t.p (scot %da now.bowl) t.t.p) + :: + ++ give + |= [paths=(list path) =update:hook] + ^- (list card) + [%give %fact paths hark-graph-hook-update+!>(update)]~ + :: + ++ watch-graph + ^- card + [%pass /graph %agent [our.bowl %graph-store] %watch /updates] + -- +%- agent:dbug +^- agent:gall +~% %hark-graph-hook-agent ..card ~ +|_ =bowl:gall ++* this . + ha ~(. +> bowl) + def ~(. (default-agent this %|) bowl) + met ~(. metadata bowl) +:: +++ on-init + :_ this + ~[watch-graph:ha] +:: +++ on-save !>(state) +++ on-load + |= old=vase + ^- (quip card _this) + `this(state !<(state-0 old)) +:: +++ on-watch + |= =path + ^- (quip card _this) + =^ cards state + ?+ path (on-watch:def path) + :: + [%updates ~] + :_ state + %+ give:ha ~ + :* %initial + watching + mentions + watch-on-self + == + == + [cards this] +:: +++ on-poke + ~/ %hark-graph-hook-poke + |= [=mark =vase] + ^- (quip card _this) + |^ + ?> (team:title our.bowl src.bowl) + =^ cards state + ?+ mark (on-poke:def mark vase) + %hark-graph-hook-action + (hark-graph-hook-action !<(action:hook vase)) + == + [cards this] + :: + ++ hark-graph-hook-action + |= =action:hook + ^- (quip card _state) + |^ + :- (give:ha ~[/updates] action) + ?- -.action + %listen (listen +.action) + %ignore (ignore +.action) + %set-mentions (set-mentions +.action) + %set-watch-on-self (set-watch-on-self +.action) + == + ++ listen + |= graph=resource + ^+ state + state(watching (~(put in watching) graph)) + :: + ++ ignore + |= graph=resource + ^+ state + state(watching (~(del in watching) graph)) + :: + ++ set-mentions + |= ment=? + ^+ state + state(mentions ment) + :: + ++ set-watch-on-self + |= self=? + ^+ state + state(watch-on-self self) + -- + -- +:: +++ on-agent + ~/ %hark-graph-hook-agent + |= [=wire =sign:agent:gall] + ^- (quip card _this) + |^ + ?+ -.sign (on-agent:def wire sign) + %kick + :_ this + ?. ?=([%graph ~] wire) + ~ + ~[watch-graph:ha] + :: + %fact + ?. ?=(%graph-update p.cage.sign) + (on-agent:def wire sign) + =^ cards state + (graph-update !<(update:graph-store q.cage.sign)) + [cards this] + == + :: + ++ graph-update + |= =update:graph-store + ^- (quip card _state) + ?. ?=(%add-nodes -.q.update) + [~ state] + =/ group=resource + (need (group-from-app-resource:met %graph resource.q.update)) + =/ =metadata:metadata-store + (need (peek-metadata:met %graph group resource.q.update)) + =* rid resource.q.update + =+ %+ scry:ha + ,mark=(unit mark) + /gx/graph-store/graph-mark/(scot %p entity.rid)/[name.rid]/noun + =+ %+ scry:ha + ,=tube:clay + /cc/[q.byk.bowl]/[(fall mark %graph-validator-link)]/notification-kind + =/ nodes=(list [p=index:graph-store q=node:graph-store]) + ~(tap by nodes.q.update) + =| cards=(list card) + |^ + ?~ nodes + [cards state] + =* index p.i.nodes + =* node q.i.nodes + =^ node-cards state + (check-node node tube) + %_ $ + nodes t.nodes + cards (weld node-cards cards) + == + :: + ++ check-node-children + |= [=node:graph-store =tube:clay] + ^- (quip card _state) + ?: ?=(%empty -.children.node) + [~ state] + =/ children=(list [=atom =node:graph-store]) + (tap:orm:graph-store p.children.node) + =| cards=(list card) + |- ^- (quip card _state) + ?~ children + [cards state] + =^ new-cards state + (check-node node.i.children tube) + %_ $ + cards (weld cards new-cards) + children t.children + == + :: + ++ check-node + |= [=node:graph-store =tube:clay] + ^- (quip card _state) + =^ child-cards state + (check-node-children node tube) + ?: =(our.bowl author.post.node) + =^ self-cards state + (self-post node) + :_ state + (weld child-cards self-cards) + =+ !<(notif-kind=(unit @t) (tube !>([0 post.node]))) + ?~ notif-kind + [child-cards state] + =/ desc=@t + ?: (is-mention contents.post.node) + %mention + u.notif-kind + ?. ?| =(desc %mention) + (~(has in watching) rid) + == + [child-cards state] + =/ notif-index=index:store + [%graph group rid module.metadata desc] + =/ =contents:store + [%graph (limo post.node ~)] + :_ state + %+ snoc child-cards + (add-unread notif-index [time-sent.post.node %.n contents]) + :: + ++ is-mention + |= contents=(list content:post) + ^- ? + ?. mentions %.n + ?~ contents %.n + ?. ?=(%mention -.i.contents) + $(contents t.contents) + ?: =(our.bowl ship.i.contents) + %.y + $(contents t.contents) + :: + ++ self-post + |= =node:graph-store + ^- (quip card _state) + ?. ?=(%.y watch-on-self) + [~ state] + `state(watching (~(put in watching) rid)) + :: + ++ add-unread + |= [=index:store =notification:store] + ^- card + =- [%pass / %agent [our.bowl %hark-store] %poke -] + hark-action+!>([%add index notification]) + :: + -- + -- +:: +++ on-peek on-peek:def +:: +++ on-leave on-leave:def +++ on-arvo on-arvo:def +++ on-fail on-fail:def +-- + diff --git a/pkg/arvo/app/hark-group-hook.hoon b/pkg/arvo/app/hark-group-hook.hoon new file mode 100644 index 000000000..62a7fb5f3 --- /dev/null +++ b/pkg/arvo/app/hark-group-hook.hoon @@ -0,0 +1,169 @@ +:: hark-group-hook: notifications for groups [landscape] +:: +/- store=hark-store, post, group-store, metadata-store, hook=hark-group-hook +/+ resource, metadata, default-agent, dbug, graph-store +:: +~% %hark-group-hook-top ..is ~ +|% ++$ card card:agent:gall ++$ versioned-state + $% state-0 + == +:: ++$ state-0 + $: %0 + watching=(set resource) + == +:: +-- +:: +=| state-0 +=* state - +:: +=< +%- agent:dbug +^- agent:gall +~% %hark-group-hook-agent ..card ~ +|_ =bowl:gall ++* this . + ha ~(. +> bowl) + def ~(. (default-agent this %|) bowl) + met ~(. metadata bowl) +:: +++ on-init + :_ this + :~ watch-metadata:ha + watch-groups:ha + == +:: +++ on-save !>(state) +++ on-load + |= old=vase + ^- (quip card _this) + `this(state !<(state-0 old)) +:: +++ on-watch + |= =path + ?. ?=([%updates ~] path) + (on-watch:def path) + :_ this + =; =cage + [%give %fact ~[/updates] cage]~ + :- %hark-group-hook-update + !> ^- update:hook + [%initial watching] +:: +++ on-poke + ~/ %hark-group-hook-poke + |= [=mark =vase] + ^- (quip card _this) + |^ + ?> (team:title our.bowl src.bowl) + =^ cards state + ?+ mark (on-poke:def mark vase) + %hark-group-hook-action + (hark-group-hook-action !<(action:hook vase)) + == + [cards this] + :: + ++ hark-group-hook-action + |= =action:hook + ^- (quip card _state) + |^ + ?- -.action + %listen (listen +.action) + %ignore (ignore +.action) + == + ++ listen + |= group=resource + ^- (quip card _state) + :- (give %listen group) + state(watching (~(put in watching) group)) + :: + ++ ignore + |= group=resource + ^- (quip card _state) + :- (give %ignore group) + state(watching (~(del in watching) group)) + :: + ++ give + |= =update:hook + ^- (list card) + [%give %fact ~[/updates] %hark-group-hook-update !>(update)]~ + -- + -- +:: +++ on-agent + ~/ %hark-group-hook-agent + |= [=wire =sign:agent:gall] + ^- (quip card _this) + |^ + ?+ -.sign (on-agent:def wire sign) + %kick + :_ this + ?+ wire ~ + [%group ~] ~[watch-groups:ha] + [%metadata ~] ~[watch-metadata:ha] + == + :: + %fact + ?+ p.cage.sign (on-agent:def wire sign) + %group-update + =^ cards state + (group-update !<(update:group-store q.cage.sign)) + [cards this] + :: + %metadata-update + =^ cards state + (metadata-update !<(metadata-update:metadata-store q.cage.sign)) + [cards this] + == + == + :: + ++ group-update + |= =update:group-store + ^- (quip card _state) + ?. ?=(?(%add-members %remove-members) -.update) + [~ state] + ?. (~(has in watching) resource.update) + [~ state] + =/ =contents:store + [%group ~[update]] + =/ =notification:store [now.bowl %.n contents] + =/ =index:store + [%group resource.update -.update] + :_ state + ~[(add-unread index notification)] + :: +metadata-update is stubbed for now, for the following reasons + :: - There's no semantic difference in metadata-store between + :: adding and editing a channel + :: - We have no way of retrieving old metadata to e.g. get a + :: channel's old name when it is renamed + ++ metadata-update + |= update=metadata-update:metadata-store + ^- (quip card _state) + [~ state] + :: + ++ add-unread + |= [=index:store =notification:store] + ^- card + =- [%pass / %agent [our.bowl %hark-store] %poke -] + hark-action+!>([%add index notification]) + -- +:: +++ on-peek on-peek:def +++ on-leave on-leave:def +++ on-arvo on-arvo:def +++ on-fail on-fail:def +-- +|_ =bowl:gall ++* met ~(. metadata bowl) +:: +++ watch-groups + ^- card + [%pass /group %agent [our.bowl %group-store] %watch /groups] +:: +++ watch-metadata + ^- card + [%pass /metadata %agent [our.bowl %metadata-store] %watch /updates] +-- diff --git a/pkg/arvo/app/hark-store.hoon b/pkg/arvo/app/hark-store.hoon new file mode 100644 index 000000000..f1d3905ac --- /dev/null +++ b/pkg/arvo/app/hark-store.hoon @@ -0,0 +1,313 @@ +:: hark-store: notifications [landscape] +:: +/- store=hark-store, post, group-store, metadata-store +/+ resource, metadata, default-agent, dbug, graph-store +:: +~% %hark-store-top ..is ~ +|% ++$ card card:agent:gall ++$ versioned-state + $% state-0 + == +:: ++$ state-0 + $: %0 + =notifications:store + archive=notifications:store + last-seen=@da + dnd=? + == ++$ inflated-state + $: state-0 + cache + == +:: $cache: useful to have precalculated, but can be derived from state +:: albeit expensively ++$ cache + $: unread-count=@ud + ~ + == +:: +++ orm ((ordered-map @da timebox:store) lth) +-- +:: +=| inflated-state +=* state - +:: +=< +%- agent:dbug +^- agent:gall +~% %hark-store-agent ..card ~ +|_ =bowl:gall ++* this . + ha ~(. +> bowl) + def ~(. (default-agent this %|) bowl) + met ~(. metadata bowl) +:: +++ on-init + :_ this + ~[autoseen-timer] +:: +++ on-save !>(-.state) +++ on-load + |= =old=vase + ^- (quip card _this) + =/ old + !<(state-0 old-vase) + `this(-.state old, +.state (inflate-cache old)) +:: +++ on-watch + |= =path + ^- (quip card _this) + |^ + ?+ path (on-watch:def path) + :: + [%updates ~] + :_ this + [%give %fact ~ hark-update+!>(initial-updates)]~ + == + :: + ++ initial-updates + ^- update:store + :- %more + ^- (list update:store) + :+ [%set-dnd dnd] + [%count unread-count] + %+ weld + %+ turn + (tap-nonempty archive) + (timebox-update &) + %+ turn + (tap-nonempty notifications) + (timebox-update |) + :: + ++ timebox-update + |= archived=? + |= [time=@da =timebox:store] + ^- update:store + [%timebox time archived ~(tap by timebox)] + :: + ++ tap-nonempty + |= =notifications:store + ^- (list [@da timebox:store]) + %+ skip (tap:orm notifications) + |=([@da =timebox:store] =(0 ~(wyt by timebox))) + -- +:: +++ on-peek + |= =path + ^- (unit (unit cage)) + ?+ path (on-peek:def path) + :: + [%x %recent @ @ ~] + =/ offset=@ud + (slav %ud i.t.t.path) + =/ length=@ud + (slav %ud i.t.t.t.path) + :^ ~ ~ %noun + !> ^- update:store + :- %more + %+ turn + (scag length (slag offset (tap:orm notifications))) + |= [time=@da =timebox:store] + ^- update:store + :^ %timebox time %.n + ~(tap by timebox) + == +:: +++ on-poke + ~/ %hark-store-poke + |= [=mark =vase] + ^- (quip card _this) + |^ + ?> (team:title our.bowl src.bowl) + =^ cards state + ?+ mark (on-poke:def mark vase) + %hark-action (hark-action !<(action:store vase)) + == + [cards this] + :: + ++ hark-action + |= =action:store + ^- (quip card _state) + |^ + ?- -.action + %add (add +.action) + %archive (do-archive +.action) + %seen seen + %read (read +.action) + %unread (unread +.action) + %set-dnd (set-dnd +.action) + == + ++ add + |= [=index:store =notification:store] + ^- (quip card _state) + =/ =timebox:store + (gut-orm:ha notifications last-seen) + =/ existing-notif + (~(get by timebox) index) + =/ new=notification:store + ?~ existing-notif + notification + (merge-notification:ha u.existing-notif notification) + =/ new-timebox=timebox:store + (~(put by timebox) index new) + :- (give:ha [/updates]~ %added last-seen index new) + %_ state + notifications (put:orm notifications last-seen new-timebox) + unread-count ?~(existing-notif +(unread-count) unread-count) + == + :: + ++ do-archive + |= [time=@da =index:store] + ^- (quip card _state) + =/ =timebox:store + (gut-orm:ha notifications time) + =/ =notification:store + (~(got by timebox) index) + =/ new-timebox=timebox:store + (~(del by timebox) index) + :- (give:ha [/updates]~ %archive time index) + %_ state + unread-count ?.(read.notification (dec unread-count) unread-count) + :: + notifications + (put:orm notifications time new-timebox) + :: + archive + %^ jub-orm:ha archive time + |= archive-box=timebox:store + ^- timebox:store + (~(put by archive-box) index notification(read %.y)) + == + :: + ++ read + |= [time=@da =index:store] + ^- (quip card _state) + :- (give:ha [/updates]~ %read time index) + %_ state + unread-count (dec unread-count) + notifications (change-read-status:ha time index %.y) + == + :: + ++ unread + |= [time=@da =index:store] + ^- (quip card _state) + :- (give:ha [/updates]~ %unread time index) + %_ state + unread-count +(unread-count) + notifications (change-read-status:ha time index %.n) + == + :: + ++ seen + ^- (quip card _state) + :_ state(last-seen now.bowl) + :~ cancel-autoseen:ha + autoseen-timer:ha + == + :: + ++ set-dnd + |= d=? + ^- (quip card _state) + :_ state(dnd d) + (give:ha [/updates]~ %set-dnd d) + -- + -- +:: +++ on-agent on-agent:def +:: +++ on-leave on-leave:def +++ on-arvo + |= [=wire =sign-arvo] + ^- (quip card _this) + ?. ?=([%autoseen ~] wire) + (on-arvo:def wire sign-arvo) + ?> ?=([%b %wake *] sign-arvo) + :_ this(last-seen now.bowl) + ~[autoseen-timer:ha] +:: +++ on-fail on-fail:def +-- +|_ =bowl:gall ++* met ~(. metadata bowl) +:: +++ merge-notification + |= [existing=notification:store new=notification:store] + ^- notification:store + ?- -.contents.existing + :: + %chat + ?> ?=(%chat -.contents.new) + existing(list.contents (weld list.contents.existing list.contents.new)) + :: + %graph + ?> ?=(%graph -.contents.new) + existing(list.contents (weld list.contents.existing list.contents.new)) + :: + %group + ?> ?=(%group -.contents.new) + existing(list.contents (weld list.contents.existing list.contents.new)) + == +:: +++ change-read-status + |= [time=@da =index:store read=?] + ^+ notifications + %^ jub-orm notifications time + |= =timebox:store + %+ ~(jab by timebox) index + |= =notification:store + ?> !=(read read.notification) + notification(read read) +:: +key-orm: +key:by for ordered maps +++ key-orm + |= =notifications:store + ^- (list @da) + (turn (tap:orm notifications) |=([key=@da =timebox:store] key)) +:: +jub-orm: combo +jab/+gut for ordered maps +:: TODO: move to zuse.hoon +++ jub-orm + |= [=notifications:store time=@da fun=$-(timebox:store timebox:store)] + ^- notifications:store + =/ =timebox:store + (fun (gut-orm notifications time)) + (put:orm notifications time timebox) +:: +gut-orm: +gut:by for ordered maps +:: TODO: move to zuse.hoon +++ gut-orm + |= [=notifications:store time=@da] + ^- timebox:store + (fall (get:orm notifications time) ~) +:: +++ autoseen-interval ~h3 +++ cancel-autoseen + ^- card + [%pass /autoseen %arvo %b %rest (add last-seen autoseen-interval)] +:: +++ autoseen-timer + ^- card + [%pass /autoseen %arvo %b %wait (add now.bowl autoseen-interval)] +:: +++ give + |= [paths=(list path) update=update:store] + ^- (list card) + [%give %fact paths [%hark-update !>(update)]]~ +:: +++ inflate-cache + |= state-0 + ^- cache + :_ ~ + %+ roll + (tap:orm notifications) + |= [[time=@da =timebox:store] out=@ud] + =/ unreads ~(tap by timebox) + |- + ?~ unreads out + =* notification q.i.unreads + ?: read.notification + out + %_ $ + unreads t.unreads + :: + out +(out) + == +-- diff --git a/pkg/arvo/app/landscape/index.html b/pkg/arvo/app/landscape/index.html index 0f258bd90..fb74f53b6 100644 --- a/pkg/arvo/app/landscape/index.html +++ b/pkg/arvo/app/landscape/index.html @@ -24,6 +24,6 @@
- +