urbit/pkg/arvo/app/hark-store.hoon

746 lines
19 KiB
Plaintext
Raw Normal View History

:: hark-store: notifications and unread counts [landscape]
2020-10-21 08:54:59 +03:00
::
:: hark-store can store unread counts differently, depending on the
:: resource.
:: - last seen. This way, hark-store simply stores an index into
:: graph-store, which represents the last "seen" item, useful for
:: high-volume applications which are intrinsically time-ordered. i.e.
:: chats, comments
:: - each. Hark-store will store an index for each item that is unread.
:: Usefull for non-linear, low-volume applications, i.e. blogs,
:: collections
::
2020-12-16 10:37:10 +03:00
/- post, group-store, metadata-store, store=hark-store
2020-12-01 08:46:28 +03:00
/+ resource, metadata, default-agent, dbug, graph-store, graphl=graph, verb, store=hark-store
2020-10-21 08:54:59 +03:00
::
::
~% %hark-store-top ..part ~
2020-10-21 08:54:59 +03:00
|%
+$ card card:agent:gall
+$ versioned-state
$% state:state-zero:store
2020-12-16 07:22:23 +03:00
state:state-one:store
state-2
state-3
state-4
2021-03-02 05:43:51 +03:00
state-5
2021-03-18 10:02:39 +03:00
state-6
state-7
2020-10-21 08:54:59 +03:00
==
+$ unread-stats
[indices=(set index:graph-store) last=@da]
2020-10-21 08:54:59 +03:00
::
2021-03-18 10:02:39 +03:00
+$ base-state
$: unreads-each=(jug stats-index:store index:graph-store)
2020-12-16 07:22:23 +03:00
unreads-count=(map stats-index:store @ud)
2021-05-17 06:35:00 +03:00
timeboxes=(map stats-index:store @da)
unread-notes=timebox:store
2020-12-16 07:22:23 +03:00
last-seen=(map stats-index:store @da)
2020-10-21 08:54:59 +03:00
=notifications:store
archive=notifications:store
current-timebox=@da
2020-11-12 03:34:51 +03:00
dnd=_|
2020-10-21 08:54:59 +03:00
==
2020-12-16 07:22:23 +03:00
::
+$ state-2
[%2 state-two:store]
::
+$ state-3
[%3 state-two:store]
::
+$ state-4
2021-03-18 10:02:39 +03:00
[%4 state-three:store]
::
2021-03-02 05:43:51 +03:00
+$ state-5
2021-03-18 10:02:39 +03:00
[%5 state-three:store]
::
+$ state-6
[%6 state-four:store]
::
+$ state-7
[%7 base-state]
2021-03-02 05:43:51 +03:00
::
2020-10-21 08:54:59 +03:00
::
2020-11-16 06:52:45 +03:00
++ orm ((ordered-map @da timebox:store) gth)
2020-10-21 08:54:59 +03:00
--
::
2021-05-17 06:35:00 +03:00
=| state-7
2020-10-21 08:54:59 +03:00
=* state -
::
=<
2020-12-01 08:46:28 +03:00
%+ verb |
2020-10-21 08:54:59 +03:00
%- agent:dbug
^- agent:gall
~% %hark-store-agent ..card ~
|_ =bowl:gall
+* this .
ha ~(. +> bowl)
def ~(. (default-agent this %|) bowl)
met ~(. metadata bowl)
2020-12-01 08:46:28 +03:00
gra ~(. graphl bowl)
2020-10-21 08:54:59 +03:00
::
++ on-init
:_ this
~[autoseen-timer]
::
2021-05-17 06:35:00 +03:00
++ on-save !>(state)
2020-10-21 08:54:59 +03:00
++ on-load
|= =old=vase
2020-10-21 08:54:59 +03:00
^- (quip card _this)
=/ old
!<(versioned-state old-vase)
=| cards=(list card)
|^
^- (quip card _this)
?- -.old
%7
2021-01-12 01:15:14 +03:00
:- (flop cards)
2021-05-17 06:35:00 +03:00
this(state old)
::
2021-05-04 07:17:55 +03:00
%6
%_ $
-.old %7
2021-03-18 10:02:39 +03:00
::
2021-05-17 06:35:00 +03:00
+.old
%* . *base-state
notifications (notifications:to-five:upgrade:store notifications.old)
archive ~
unreads-each unreads-each.old
unreads-count unreads-count.old
last-seen last-seen.old
current-timebox current-timebox
dnd dnd.old
==
2021-05-04 07:17:55 +03:00
==
::
%5
2021-03-18 10:02:39 +03:00
%_ $
-.old %6
2021-05-04 07:17:55 +03:00
notifications.old (notifications:to-four:upgrade:store notifications.old)
archive.old *notifications:state-four:store
2021-03-18 10:02:39 +03:00
==
2021-05-04 07:17:55 +03:00
::
2021-03-02 05:43:51 +03:00
%4
%_ $
-.old %5
::
last-seen.old
%- ~(run by last-seen.old)
|=(old=@da (min old now.bowl))
==
2021-05-04 07:17:55 +03:00
::
%3
%_ $
-.old %4
2021-05-04 08:21:49 +03:00
notifications.old (notifications:to-three:upgrade:store notifications.old)
archive.old *notifications:state-three:store
==
2021-05-04 07:17:55 +03:00
::
%2
%_ $
-.old %3
2021-01-12 01:15:14 +03:00
::
cards
:_ cards
[%pass / %agent [our dap]:bowl %poke noun+!>(%fix-dangling)]
==
2021-05-04 07:17:55 +03:00
::
%1
2020-12-16 10:37:10 +03:00
%_ $
::
old
%* . *state-2
unreads-each ((convert-unread ,(set index:graph-store)) uni-by unreads-each.old)
unreads-count ((convert-unread ,@ud) add unreads-count.old)
last-seen ((convert-unread ,@da) max last-seen.old)
notifications notifications.old
archive archive.old
current-timebox current-timebox.old
dnd dnd.old
==
==
2021-05-04 07:17:55 +03:00
::
%0
%_ $
::
old
2020-12-16 10:37:10 +03:00
%* . *state:state-one:store
notifications (convert-notifications-1 notifications.old)
archive (convert-notifications-1 archive.old)
current-timebox current-timebox.old
dnd dnd.old
==
==
==
::
2020-12-16 10:37:10 +03:00
++ uni-by
|= [a=(set index:graph-store) b=(set index:graph-store)]
=/ merged
(~(uni in a) b)
%- ~(gas in *(set index:graph-store))
%+ skip ~(tap in merged)
|=(=index:graph-store &(=((lent index) 3) !=(-:(flop index) 1)))
::
++ convert-unread
|* value=mold
|= [combine=$-([value value] value) unreads=(map index:store value)]
^- (map stats-index:store value)
%+ roll
~(tap in unreads)
|= [[=index:store val=value] out=(map stats-index:store value)]
=/ old=value
(~(gut by unreads) index (combine))
=/ =stats-index:store
(to-stats-index:store index)
(~(put by out) stats-index (combine old val))
2020-12-16 07:22:23 +03:00
::
++ convert-notifications-1
|= old=notifications:state-zero:store
%+ gas:orm:state-two:store *notifications:state-two:store
^- (list [@da timebox:state-two:store])
%+ murn
(tap:orm:state-zero:store old)
|= [time=@da =timebox:state-zero:store]
^- (unit [@da timebox:state-two:store])
=/ new-timebox=timebox:state-two:store
(convert-timebox-1 timebox)
?: =(0 ~(wyt by new-timebox))
~
`[time new-timebox]
::
++ convert-timebox-1
|= =timebox:state-zero:store
^- timebox:state-two:store
%- ~(gas by *timebox:state-two:store)
^- (list [index:state-two:store notification:state-two:store])
%+ murn
~(tap by timebox)
|= [=index:state-zero:store =notification:state-zero:store]
^- (unit [index:state-two:store notification:state-two:store])
=/ new-index=(unit index:state-two:store)
(convert-index-1 index)
=/ new-notification=(unit notification:state-two:store)
(convert-notification-1 notification)
?~ new-index ~
?~ new-notification ~
`[u.new-index u.new-notification]
::
++ convert-index-1
|= =index:state-zero:store
^- (unit index:state-two:store)
?+ -.index `index
%chat ~
::
%graph
=, index
2021-05-04 07:17:55 +03:00
`[%graph graph *resource module description ~]
==
::
++ convert-notification-1
|= =notification:state-zero:store
^- (unit notification:state-two:store)
?: ?=(%chat -.contents.notification)
~
`notification
--
::
++ on-watch
|= =path
^- (quip card _this)
?> (team:title [src our]:bowl)
|^
?+ path (on-watch:def path)
::
[%updates ~]
:_ this
[%give %fact ~ hark-update+!>(initial-updates)]~
==
2020-11-10 06:32:48 +03:00
::
++ initial-updates
^- update:store
:- %more
^- (list update:store)
2021-05-18 03:57:25 +03:00
:~ give-unreads
[%set-dnd dnd]
give-notifications
==
2021-05-17 06:35:00 +03:00
::
++ give-notifications
^- update:store
2021-05-18 03:57:25 +03:00
[%timebox ~ ~(tap by unread-notes)]
::
++ give-since-unreads
2020-12-16 10:37:10 +03:00
^- (list [stats-index:store stats:store])
%+ turn
2020-12-01 08:46:28 +03:00
~(tap by unreads-count)
2020-12-16 10:37:10 +03:00
|= [=stats-index:store count=@ud]
:* stats-index
2020-12-01 08:46:28 +03:00
[%count count]
2020-12-16 10:37:10 +03:00
(~(gut by last-seen) stats-index *time)
==
::
++ give-each-unreads
2020-12-16 10:37:10 +03:00
^- (list [stats-index:store stats:store])
%+ turn
~(tap by unreads-each)
2020-12-16 10:37:10 +03:00
|= [=stats-index:store indices=(set index:graph-store)]
:* stats-index
[%each indices]
2020-12-16 10:37:10 +03:00
(~(gut by last-seen) stats-index *time)
==
::
++ give-unreads
2020-11-16 05:22:42 +03:00
^- update:store
:- %unreads
;: weld
give-each-unreads
give-since-unreads
==
--
2020-10-21 08:54:59 +03:00
::
++ on-peek
|= =path
^- (unit (unit cage))
?+ path (on-peek:def path)
::
2020-11-16 06:53:18 +03:00
[%x %recent ?(%archive %inbox) @ @ ~]
=/ is-archive
=(%archive i.t.t.path)
=/ offset=@ud
(slav %ud i.t.t.t.path)
2020-11-16 06:53:18 +03:00
=/ length=@ud
(slav %ud i.t.t.t.t.path)
:^ ~ ~ %hark-update
!> ^- update:store
:- %more
%+ turn
%+ scag length
%+ slag offset
2020-11-16 06:53:18 +03:00
%- tap-nonempty:ha
?:(is-archive archive notifications)
|= [time=@da =timebox:store]
^- update:store
2021-05-18 03:57:25 +03:00
[%timebox `time ~(tap by timebox)]
==
2020-11-10 06:32:48 +03:00
::
2020-10-21 08:54:59 +03:00
++ 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))
%noun (poke-noun !<(* vase))
2020-10-21 08:54:59 +03:00
==
[cards this]
::
++ poke-noun
|= val=*
?+ val ~|(%bad-noun-poke !!)
%fix-dangling fix-dangling
%print ~&(+.state [~ state])
==
::
++ fix-dangling
2021-01-12 01:15:14 +03:00
=/ graphs get-keys:gra
:_ state
%+ roll
~(tap by unreads-each)
|= $: [=stats-index:store indices=(set index:graph-store)]
out=(list card)
==
?. ?=(%graph -.stats-index) out
?. (~(has in graphs) graph.stats-index)
:_(out (poke-us %remove-graph graph.stats-index))
2021-01-12 01:15:14 +03:00
%+ welp out
%+ turn
%+ skip
2021-01-12 01:15:14 +03:00
~(tap in indices)
|= =index:graph-store
(check-node-existence:gra graph.stats-index index)
|=(=index:graph-store (poke-us %read-each stats-index index))
::
++ poke-us
|= =action:store
^- card
[%pass / %agent [our dap]:bowl %poke hark-action+!>(action)]
::
2020-10-21 08:54:59 +03:00
++ hark-action
|= =action:store
^- (quip card _state)
abet:translate:(abed:poke-engine:ha action)
2020-10-21 08:54:59 +03:00
--
::
++ 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)
2021-05-17 06:35:00 +03:00
`this
2020-10-21 08:54:59 +03:00
::
++ on-fail on-fail:def
--
|_ =bowl:gall
+* met ~(. metadata bowl)
++ poke-engine
|_ [in=action:store out=(list update:store) cards=(list card)]
++ poke-core .
::
++ abed
|= =action:store poke-core(in action)
::
++ abet
^- (quip card _state)
:_ state
%+ snoc (flop cards)
[%give %fact ~[/updates] %hark-update !>([%more (flop out)])]
::
++ give
|= =update:store poke-core(out [update out])
::
++ emit
|= =card poke-core(cards [card cards])
::
++ translate
^+ poke-core
?- -.in
::
%add-note (add-note +.in)
%archive (do-archive +.in)
::
%unread-count (unread-count +.in)
%read-count (read-count +.in)
::
%read-each (read-each +.in)
%unread-each (unread-each +.in)
::
%read-note (read-note +.in)
::
%seen-index (seen-index +.in)
::
2020-12-17 09:10:26 +03:00
%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
::
==
::
:: +| %note
::
:: notification tracking
++ put-notifs
|= [time=@da =timebox:store]
poke-core(notifications (put:orm notifications time timebox))
::
++ add-note
|= [=index:store =notification:store]
^+ poke-core
=/ existing-notif
2021-05-17 06:35:00 +03:00
(~(get by unread-notes) index)
=/ new=notification:store
(merge-notification existing-notif notification)
2021-05-17 06:35:00 +03:00
=. unread-notes
(~(put by unread-notes) index new)
=/ timebox=@da
(~(gut by timeboxes) (to-stats-index:store index) current-timebox)
(give %added index new)
::
++ do-archive
2021-05-17 06:35:00 +03:00
|= [time=(unit @da) =index:store]
^+ poke-core
2021-05-17 06:35:00 +03:00
|^
?~(time archive-unread (archive-read u.time))
::
++ archive-unread
=. unread-notes
(~(del by unread-notes) index)
(give %archive ~ index)
::
++ archive-read
|= time=@da
=/ =timebox:store
(gut-orm notifications time)
=/ =notification:store
(~(got by timebox) index)
=/ new-timebox=timebox:store
(~(del by timebox) index)
=. poke-core
(put-notifs time new-timebox)
(give %archive `time index)
--
::
++ read-note
|= =index:store
=/ =notification:store
(~(got by unread-notes) index)
=. unread-notes
(~(del by unread-notes) index)
=/ =time
(~(gut by timeboxes) (to-stats-index:store index) current-timebox)
=/ =timebox:store
(gut-orm notifications time)
2021-05-17 06:35:00 +03:00
=/ existing-notif
(~(get by timebox) index)
=/ new=notification:store
(merge-notification existing-notif notification)
=. timebox
(~(put by timebox) index new)
=. notifications
2021-05-17 06:35:00 +03:00
(put:orm notifications time timebox)
(give %note-read time index)
::
::
:: +| %each
::
:: each unread tracking
::
++ unread-each
2020-12-16 10:37:10 +03:00
|= [=stats-index:store unread=index:graph-store time=@da]
=. poke-core (seen-index time stats-index)
%+ jub-unreads-each:(give %unread-each stats-index unread time)
stats-index
|= indices=(set index:graph-store)
(~(put ^in indices) unread)
::
++ read-index-each
2020-12-16 07:22:23 +03:00
|= [=stats-index:store ref=index:graph-store]
%- read-indices
%+ skim
2021-05-17 06:35:00 +03:00
~(tap ^in ~(key by unread-notes))
|= =index:store
?. (stats-index-is-index:store stats-index index) %.n
=/ not=notification:store
2021-05-17 06:35:00 +03:00
(~(got by unread-notes) index)
?. ?=(%graph -.index) %.n
?. ?=(%graph -.contents.not) %.n
(lien list.contents.not |=(p=post:post =(index.p ref)))
::
++ read-each
2020-12-16 10:37:10 +03:00
|= [=stats-index:store ref=index:graph-store]
2021-05-17 06:35:00 +03:00
=. timeboxes (~(put by timeboxes) stats-index now.bowl)
2020-12-16 10:37:10 +03:00
=. poke-core (read-index-each stats-index ref)
%+ jub-unreads-each:(give %read-each stats-index ref)
stats-index
|= indices=(set index:graph-store)
(~(del ^in indices) ref)
::
++ jub-unreads-each
2020-12-16 10:37:10 +03:00
|= $: =stats-index:store
f=$-((set index:graph-store) (set index:graph-store))
==
2020-12-16 10:37:10 +03:00
poke-core(unreads-each (jub stats-index f))
::
++ unread-count
2020-12-16 07:22:23 +03:00
|= [=stats-index:store time=@da]
=/ new-count
2020-12-16 07:22:23 +03:00
+((~(gut by unreads-count) stats-index 0))
=. unreads-count
2020-12-16 07:22:23 +03:00
(~(put by unreads-count) stats-index new-count)
2020-12-16 10:37:10 +03:00
(seen-index:(give %unread-count stats-index time) time stats-index)
::
++ read-count
2020-12-16 10:37:10 +03:00
|= =stats-index:store
=. unreads-count (~(put by unreads-count) stats-index 0)
2021-05-17 06:35:00 +03:00
=/ times=(list index:store)
(unread-for-stats-index stats-index)
2021-05-18 03:57:25 +03:00
=? timeboxes !(~(has by timeboxes) stats-index) (~(put by timeboxes) stats-index now.bowl)
(give:(read-indices times) %read-count stats-index)
::
++ read-indices
2021-05-17 06:35:00 +03:00
|= times=(list =index:store)
2020-12-16 10:37:10 +03:00
|-
?~ times poke-core
=/ core
(read-note i.times)
2020-12-16 10:37:10 +03:00
$(poke-core core, times t.times)
::
++ seen-index
2020-12-16 10:37:10 +03:00
|= [time=@da =stats-index:store]
=/ new-time=@da
2020-12-16 10:37:10 +03:00
(max time (~(gut by last-seen) stats-index 0))
=. last-seen
2020-12-16 10:37:10 +03:00
(~(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
2021-05-26 07:10:46 +03:00
=/ core=_poke-core (read-graph i.graphs)
$(graphs t.graphs, poke-core core)
::
2020-12-17 09:10:26 +03:00
++ remove-graph
|= rid=resource
|^
=/ indices (get-stats-indices rid)
2020-12-17 09:10:26 +03:00
=. poke-core
(give %remove-graph rid)
=. poke-core
(remove-notifications indices)
=. unreads-count
((dif-map-by-key ,@ud) unreads-count indices)
=. unreads-each
%+ (dif-map-by-key ,(set index:graph-store))
unreads-each indices
=. last-seen
((dif-map-by-key ,@da) last-seen indices)
poke-core
::
++ dif-map-by-key
|* value=mold
|= [=(map stats-index:store value) =(set stats-index:store)]
=/ to-remove ~(tap ^in set)
|-
?~ to-remove map
=. map
(~(del by map) i.to-remove)
$(to-remove t.to-remove)
::
++ remove-notifications
|= =(set stats-index:store)
^+ poke-core
=/ indices
~(tap ^in set)
|-
?~ indices poke-core
2021-05-17 06:35:00 +03:00
=/ times=(list =index:store)
(unread-for-stats-index i.indices)
2020-12-17 09:10:26 +03:00
=. poke-core
(read-indices times)
2020-12-17 09:10:26 +03:00
$(indices t.indices)
--
::
++ seen
2021-05-17 06:35:00 +03:00
=. poke-core
(read-indices ~(tap ^in ~(key by unread-notes)))
poke-core(current-timebox now.bowl, timeboxes ~)
::
++ read-all
=: unreads-count (~(run by unreads-count) _0)
unreads-each (~(run by unreads-each) _~)
notifications (~(run by notifications) _~)
==
2021-05-17 06:35:00 +03:00
(give:seen %read-all ~)
::
++ set-dnd
|= d=?
(give:poke-core(dnd d) %set-dnd d)
--
2020-10-21 08:54:59 +03:00
::
2021-05-17 06:35:00 +03:00
++ unread-for-stats-index
|= =stats-index:store
%+ skim ~(tap in ~(key by unread-notes))
(cury stats-index-is-index:store stats-index)
::
2020-10-21 08:54:59 +03:00
++ merge-notification
|= [existing=(unit notification:store) new=notification:store]
2020-10-21 08:54:59 +03:00
^- notification:store
?~ existing new
?- -.contents.u.existing
2020-10-21 08:54:59 +03:00
::
%graph
?> ?=(%graph -.contents.new)
2021-05-17 06:35:00 +03:00
u.existing(list.contents (weld list.contents.u.existing list.contents.new))
2020-10-21 08:54:59 +03:00
::
%group
?> ?=(%group -.contents.new)
2021-05-17 06:35:00 +03:00
u.existing(list.contents (weld list.contents.u.existing list.contents.new))
2020-10-21 08:54:59 +03:00
==
::
:: +key-orm: +key:by for ordered maps
++ key-orm
|= =notifications:store
^- (list @da)
(turn (tap:orm notifications) |=([@da *] +<-))
2020-10-21 08:54:59 +03:00
:: +jub-orm: combo +jab/+gut for ordered maps
2020-10-22 04:30:37 +03:00
:: TODO: move to zuse.hoon
2020-10-21 08:54:59 +03:00
++ 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)
++ jub
2020-12-16 10:37:10 +03:00
|= [=stats-index:store f=$-((set index:graph-store) (set index:graph-store))]
^- (jug stats-index:store index:graph-store)
=/ val=(set index:graph-store)
2020-12-16 10:37:10 +03:00
(~(gut by unreads-each) stats-index ~)
(~(put by unreads-each) stats-index (f val))
2020-10-22 04:30:37 +03:00
:: +gut-orm: +gut:by for ordered maps
:: TODO: move to zuse.hoon
2020-10-21 08:54:59 +03:00
++ 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 current-timebox autoseen-interval)]
2020-10-21 08:54:59 +03:00
::
++ autoseen-timer
^- card
[%pass /autoseen %arvo %b %wait (add now.bowl autoseen-interval)]
::
++ scry
|* [=mold p=path]
?> ?=(^ p)
?> ?=(^ t.p)
.^(mold i.p (scot %p our.bowl) i.t.p (scot %da now.bowl) t.t.p)
::
2020-10-21 08:54:59 +03:00
++ give
|= [paths=(list path) update=update:store]
^- (list card)
[%give %fact paths [%hark-update !>(update)]]~
::
++ tap-nonempty
|= =notifications:store
^- (list [@da timebox:store])
%+ skim (tap:orm notifications)
|=([@da =timebox:store] !=(~(wyt by timebox) 0))
2020-10-21 08:54:59 +03:00
--