diff --git a/desk/app/contacts.hoon b/desk/app/contacts.hoon index 189fcb7..74c7b1f 100644 --- a/desk/app/contacts.hoon +++ b/desk/app/contacts.hoon @@ -1,3 +1,4 @@ +/- activity /+ default-agent, dbug, verb, neg=negotiate /+ *contacts :: @@ -100,6 +101,12 @@ :: +| %operations :: + ++ pass-activity + |= [who=ship field=(pair @tas value)] + ^- card + =/ =cage activity-action+!>(`action:activity`[%add %contact who field]) + [%pass /activity %agent [our.bowl %activity] %poke cage] + :: :: +pub: publication management :: :: - /v1/news: local updates to our profile and rolodex @@ -344,15 +351,43 @@ cor =. cor (p-news-0:pub who (contact:to-0 con.u)) =/ page=(unit page) (~(get by book) who) - :: update peer contact page + :: update contact book and send notification :: =? cor ?=(^ page) ?: =(con.u.page con.u) cor =. book (~(put by book) who u.page(con con.u)) + =. cor (emil (send-activity u con.u.page)) (p-response:pub %page who con.u mod.u.page) (p-response:pub %peer who con.u) == :: + ++ send-activity + |= [u=update con=contact] + ^- (list card) + ?. .^(? %gu /(scot %p our.bowl)/activity/(scot %da now.bowl)/$) + ~ + %- ~(rep by con.u) + |= [field=(pair @tas value) cards=(list card)] + ?> ?=(^ q.field) + :: do not broadcast empty changes + :: + ?: (is-value-empty q.field) + cards + :: + =/ val=(unit value) (~(get by con) p.field) + ?~ val + [(pass-activity who field) cards] + ?< ?=(~ u.val) + ::NOTE currently shouldn't happen in practice + ?. =(-.q.field -.u.val) cards + ?: =(p.q.field p.u.val) cards + ?. ?=(%set -.q.field) + [(pass-activity who field) cards] + =/ diff=(set value) + (~(dif in p.q.field) ?>(?=(%set -.u.val) p.u.val)) + ?~ diff cards + [(pass-activity who p.field set+diff) cards] + :: ++ si-meet ^+ si-cor :: @@ -735,7 +770,7 @@ ++ agent |= [=wire =sign:agent:gall] ^+ cor - ?+ wire ~|(evil-agent+wire !!) + ?+ wire ~|(evil-agent+wire !!) [%contact ~] si-abet:(si-take:(sub src.bowl) wire sign) :: @@ -744,6 +779,9 @@ ?~ p.sign cor %- (slog leaf/"{} failed" u.p.sign) cor + :: + [%activity ~] + cor :: [%epic ~] cor diff --git a/desk/lib/contacts.hoon b/desk/lib/contacts.hoon index 853b619..be6afe1 100644 --- a/desk/lib/contacts.hoon +++ b/desk/lib/contacts.hoon @@ -2,6 +2,16 @@ |% :: +| %contact +:: +is-value-empty: is value considered empty +:: +++ is-value-empty + |= val=value + ^- ? + ?+ -.val | + %text =('' p.val) + %look =('' p.val) + %set ?=(~ p.val) + == :: +cy: contact map engine :: ++ cy diff --git a/desk/lib/contacts/json-1.hoon b/desk/lib/contacts/json-1.hoon index cbf368e..a65fc33 100644 --- a/desk/lib/contacts/json-1.hoon +++ b/desk/lib/contacts/json-1.hoon @@ -112,8 +112,7 @@ ++ value ^- $-(json value:c) |= jon=json - :: XX is there a way to do it in one go? - :: + ?~ jon ~ =/ [type=@tas val=json] %. jon (ot type+(se %tas) value+json ~) diff --git a/desk/sur/activity.hoon b/desk/sur/activity.hoon new file mode 100644 index 0000000..8f5d1f0 --- /dev/null +++ b/desk/sur/activity.hoon @@ -0,0 +1,368 @@ +/- c=channels, t=contacts, ch=chat, g=groups +/+ mp=mop-extensions +|% ++| %collections +:: $stream: the activity stream comprised of events from various agents ++$ stream ((mop time event) lte) +:: $indices: the stream and its read data split into various indices ++$ indices + $~ [[[%base ~] *index] ~ ~] + (map source index) +:: $volume-settings: the volume settings for each source ++$ volume-settings (map source volume-map) +:: $activity: the current state of activity for each source ++$ activity (map source activity-summary) +:: $full-info: the full state of the activity stream ++$ full-info [=indices =activity =volume-settings] +:: $volume-map: how to badge and notify for each event type ++$ volume-map + $~ default-volumes + (map event-type volume) +:: $feed: a set of grouped events and the summaries of their sources ++$ feed + $: feed=(list activity-bundle) + summaries=activity + == ++$ feed-init + $: all=(list activity-bundle) + mentions=(list activity-bundle) + replies=(list activity-bundle) + summaries=activity + == ++| %actions +:: $action: how to interact with our activity stream +:: +:: actions are only ever performed for and by our selves +:: +:: %add: add an event to the stream +:: %bump: mark a source as having new activity from myself +:: %del: remove a source and all its activity +:: %read: mark an event as read +:: %adjust: adjust the volume of an source +:: %allow-notifications: change which notifications are allowed +:: ++$ action + $% [%add =incoming-event] + [%bump =source] + [%del =source] + [%del-event =source event=incoming-event] + [%read =source =read-action] + [%adjust =source =(unit volume-map)] + [%allow-notifications allow=notifications-allowed] + == +:: +:: $read-action: mark activity read +:: +:: %item: (DEPRECATED) mark an individual activity as read, indexed by id +:: %event: (DEPRECATED) mark an individual activity as read, indexed by the event itself +:: %all: mark _everything_ as read for this source, and possibly children +:: ++$ read-action + $% [%item id=time-id] + [%event event=incoming-event] + [%all time=(unit time) deep=?] + == +:: ++| %updates +:: +:: $update: what we hear after an action +:: +:: %add: an event was added to the stream +:: %del: a source and its activity were removed +:: %read: a source's activity state was updated +:: %activity: the activity state was updated +:: %adjust: the volume of a source was adjusted +:: %allow-notifications: the allowed notifications were changed +:: ++$ update + $% [%add =source time-event] + [%del =source] + [%read =source =activity-summary] + [%activity =activity] + [%adjust =source volume-map=(unit volume-map)] + [%allow-notifications allow=notifications-allowed] + == +:: ++| %basics +:: $event: a single point of activity, from one of our sources +:: +:: $incoming-event: the event that was sent to us +:: .notified: if this event has been notified +:: .child: if this event is from a child source +:: ++$ event + $: incoming-event + notified=? + child=? + == ++$ incoming-event + $% [%post post-event] + [%reply reply-event] + [%dm-invite =whom] + [%dm-post dm-post-event] + [%dm-reply dm-reply-event] + [%group-ask group=flag:g =ship] + [%group-kick group=flag:g =ship] + [%group-join group=flag:g =ship] + [%group-invite group=flag:g =ship] + [%chan-init channel=nest:c group=flag:g] + [%group-role group=flag:g =ship roles=(set sect:g)] + [%flag-post key=message-key channel=nest:c group=flag:g] + [%flag-reply key=message-key parent=message-key channel=nest:c group=flag:g] + [%contact contact-event] + == +:: ++$ post-event + $: key=message-key + channel=nest:c + group=flag:g + content=story:c + mention=? + == +:: ++$ reply-event + $: key=message-key + parent=message-key + channel=nest:c + group=flag:g + content=story:c + mention=? + == +:: ++$ dm-post-event + $: key=message-key + =whom + content=story:c + mention=? + == +:: ++$ dm-reply-event + $: key=message-key + parent=message-key + =whom + content=story:c + mention=? + == +:: ++$ contact-event + $: who=ship + update=(pair @tas value:t) + == +:: +:: $source: where the activity is happening ++$ source + $% [%base ~] + [%group =flag:g] + [%channel =nest:c group=flag:g] + [%thread key=message-key channel=nest:c group=flag:g] + [%dm =whom] + [%dm-thread key=message-key =whom] + [%contact who=ship] + == +:: +:: $index: the stream of activity and read state for a source ++$ index [=stream =reads bump=time] +:: +:: $reads: the read state for a source +:: +:: $floor: the time of the latest event that was read +:: $items: the set of events above the floor that have been read +:: ++$ reads + $: floor=time + items=read-items + == ++$ read-items ((mop time-id ,~) lte) +:: $activity-summary: the summary of activity for a source +:: +:: $newest: the time of the latest activity read or unread +:: $count: the total number of unread events including children +:: $notify-count: the number of unreads that are notifications +:: including children +:: $notify: if there are any notifications here or in children +:: $unread: if the main stream of source is unread: which starting +:: message, how many there are, and if any are notifications +:: $children: the sources nested under this source +:: ++$ activity-summary + $~ [*@da 0 0 | ~ ~ ~] + $: newest=time + count=@ud + notify-count=@ud + notify=_| + unread=(unit unread-point) + children=(set source) + reads=* :: DO NOT USE, 🚨 ⚠️ REMOVE + == ++$ unread-point [message-key count=@ud notify=_|] ++$ volume [unreads=? notify=?] ++$ notifications-allowed ?(%all %some %none) ++$ activity-bundle + $: =source + latest=time + events=(list time-event) + == +:: ++| %primitives ++$ whom + $% [%ship p=ship] + [%club p=id:club:ch] + == ++$ time-id time ++$ message-id (pair ship time-id) ++$ message-key [id=message-id =time] +:: ++$ event-type + $? %chan-init + %post + %post-mention + %reply + %reply-mention + %dm-invite + %dm-post + %dm-post-mention + %dm-reply + %dm-reply-mention + %group-invite + %group-kick + %group-join + %group-ask + %group-role + %flag-post + %flag-reply + %contact + == ++| %helpers ++$ time-event [=time =event] +++ on-event ((on time event) lte) +++ ex-event ((mp time event) lte) +++ on-read-items ((on time ,~) lte) ++| %constants +++ default-volumes + ^~ + ^- (map event-type volume) + %- my + :~ [%post & &] + [%reply & |] + [%dm-reply & &] + [%post-mention & &] + [%reply-mention & &] + [%dm-invite & &] + [%dm-post & &] + [%dm-post-mention & &] + [%dm-reply-mention & &] + [%group-invite & &] + [%group-ask & &] + [%flag-post & &] + [%flag-reply & &] + [%group-kick & |] + [%group-join & |] + [%group-role & |] + ::XX remove notify? + [%contact & &] + == +++ old-volumes + ^~ + %- my + :~ [%soft (~(put by default-volumes) %post [& |])] + [%loud (~(run by default-volumes) |=([u=? *] [u &]))] + [%hush (~(run by default-volumes) |=([u=? *] [u |]))] + == +++ mute + ^~ + (~(run by default-volumes) |=(* [| |])) +:: ++| %old-types +++ old + |% + ++ v7 + |% + +$ stream ((mop time event) lte) + +$ event + $: incoming-event + notified=? + child=? + == + +$ incoming-event + $% [%post post-event] + [%reply reply-event] + [%dm-invite =whom] + [%dm-post dm-post-event] + [%dm-reply dm-reply-event] + [%group-ask group=flag:g =ship] + [%group-kick group=flag:g =ship] + [%group-join group=flag:g =ship] + [%group-invite group=flag:g =ship] + [%chan-init channel=nest:c group=flag:g] + [%group-role group=flag:g =ship roles=(set sect:g)] + [%flag-post key=message-key channel=nest:c group=flag:g] + [%flag-reply key=message-key parent=message-key channel=nest:c group=flag:g] + == + +$ source + $% [%base ~] + [%group =flag:g] + [%channel =nest:c group=flag:g] + [%thread key=message-key channel=nest:c group=flag:g] + [%dm =whom] + [%dm-thread key=message-key =whom] + == + +$ index [=stream =reads bump=time] + -- + ++ v4 + |% + +$ feed (list activity-bundle) + -- + ++ v3 + |% + +$ index [=stream =reads] + +$ indices (map source index) + +$ update + $% [%add =source time-event] + [%del =source] + [%read =source =activity-summary] + [%adjust =source volume-map=(unit volume-map)] + [%allow-notifications allow=notifications-allowed] + == + +$ full-info + $: =indices + =activity + =volume-settings + == + +$ activity (map source activity-summary) + +$ activity-summary + $~ [*@da 0 0 | ~ ~ [*@da ~]] + $: newest=time + count=@ud + notify-count=@ud + notify=_| + unread=(unit unread-point) + children=(unit activity) + =reads + == + -- + ++ v2 + |% + +$ update + $% [%add =source time-event] + [%del =source] + [%read =source =activity-summary] + [%adjust =source volume-map=(unit volume-map)] + [%allow-notifications allow=notifications-allowed] + == + +$ full-info + $: =indices:v3 + activity=activity + =volume-settings + == + +$ activity (map source activity-summary) + +$ activity-summary + $~ [*@da 0 | ~ ~] + $: newest=time + count=@ud + notify=_| + unread=(unit unread-point) + children=(unit activity) + == + -- + -- +-- diff --git a/desk/sur/contacts-0.hoon b/desk/sur/contacts-0.hoon index a019da8..91ba0d3 100644 --- a/desk/sur/contacts-0.hoon +++ b/desk/sur/contacts-0.hoon @@ -57,4 +57,19 @@ :: +$ news-0 [who=ship con=$@(~ contact-0)] +:: +++ get-contact + |= [=bowl:gall who=@p] + => :_ ..get-contact + [who=who our=our.bowl now=now.bowl] + ~+ ^- (unit contact-0) + =/ base=path /(scot %p our)/contacts/(scot %da now) + ?. ~+ .^(? %gu (weld base /$)) + ~ + =+ ~+ .^(rol=rolodex %gx (weld base /all/contact-rolodex)) + ?~ for=(~(get by rol) who) + ~ + ?. ?=([[@ ^] *] u.for) + ~ + `con.for.u.for --