diff --git a/app/app/n-contacts.hoon b/app/app/n-contacts.hoon
new file mode 100644
index 0000000..506b90a
--- /dev/null
+++ b/app/app/n-contacts.hoon
@@ -0,0 +1,856 @@
+/+ default-agent, dbug
+/+ *contacts-contacts, kol
+::
+:: performance, keep warm
+:: /+ j0=contacts-json-0, j1=contacts-json-1, mark-warmer
+/+ j0=contacts-json-0, j1=contacts-json-1
+::
+|%
+:: conventions
+::
+:: .con: a contact
+:: .rof: our profile
+:: .rol: our full rolodex (legacy)
+:: .far: foreign peer
+:: .for: foreign profile
+:: .sag: foreign subscription state
+::
++| %types
++$ card card:agent:gall
++$ state-4
+ $: %4
+ rof=profile
+ =book
+ =peers
+ last-updated=(list [=kip =time])
+ retry=(map ship @da) :: retry sub at time
+ ==
+--
+%- agent:dbug
+^- agent:gall
+=| state-4
+=* state -
+=< |_ =bowl:gall
+ +* this .
+ def ~(. (default-agent this %|) bowl)
+ cor ~(. raw bowl)
+ ::
+ ++ on-init
+ ^- (quip card _this)
+ =^ cards state abet:init:cor
+ [cards this]
+ ::
+ ++ on-save !>([state ~])
+ ::
+ ++ on-load
+ |= old=vase
+ ^- (quip card _this)
+ =^ cards state abet:(load:cor old)
+ [cards this]
+ ::
+ ++ on-watch
+ |= =path
+ ^- (quip card _this)
+ =^ cards state abet:(peer:cor path)
+ [cards this]
+ ::
+ ++ on-poke
+ |= [=mark =vase]
+ ^- (quip card _this)
+ =^ cards state abet:(poke:cor mark vase)
+ [cards this]
+ ::
+ ++ on-peek peek:cor
+ ++ on-leave on-leave:def
+ ::
+ ++ on-agent
+ |= [=wire =sign:agent:gall]
+ ^- (quip card _this)
+ =^ cards state abet:(agent:cor wire sign)
+ [cards this]
+ ::
+ ++ on-arvo
+ |= [=wire sign=sign-arvo]
+ =^ cards state abet:(arvo:cor wire sign)
+ [cards this]
+ ::
+ ++ on-fail on-fail:def
+ --
+
+|%
+::
++| %state
+::
+:: namespaced to avoid accidental direct reference
+::
+++ raw
+ =| out=(list card)
+ |_ =bowl:gall
+ +* ol (kol gte)
+ ::
+ +| %generic
+ ::
+ ++ abet [(flop out) state]
+ ++ cor .
+ ++ emit |=(c=card cor(out [c out]))
+ ++ emil |=(c=(list card) cor(out (weld (flop c) out)))
+ ++ give |=(=gift:agent:gall (emit %give gift))
+ ++ pass |=([=wire =note:agent:gall] (emit %pass wire note))
+ ::
+ +| %operations
+ ::
+ ::
+ ++ pub
+ => |%
+ :: if this proves to be too slow, the set of paths
+ :: should be maintained statefully: put on +p-init:pub,
+ :: filtered at some interval (on +load?) to avoid a space leak.
+ ::
+ :: XX number of peers is usually around 5.000.
+ :: this means that the number of subscribers is about the
+ :: same. Thus on each contact update we need to filter
+ :: over 5.000 elements: do some benchmarking.
+ ::
+ ++ subs
+ ^- (set path)
+ %- ~(rep by sup.bowl)
+ :: default .acc prevents invalid empty fact path in the case
+ :: of no subscribers
+ ::
+ |= [[duct ship pat=path] acc=_(sy `path`/v1/contact ~)]
+ ?.(?=([%v1 %contact *] pat) acc (~(put in acc) pat))
+ ++ fact
+ |= [pat=(set path) u=update]
+ ^- gift:agent:gall
+ [%fact ~(tap in pat) %contact-update-1 !>(u)]
+ --
+ ::
+ |%
+ :: +p-anon: delete our profile
+ ::
+ ++ p-anon ?.(?=([@ ^] rof) cor (p-commit-self ~))
+ :: +p-self: edit our profile
+ ::
+ ++ p-self
+ |= con=(map @tas value)
+ =/ old=contact
+ ?.(?=([@ ^] rof) *contact con.rof)
+ =/ new=contact
+ (do-edit old con)
+ ?: =(old new)
+ cor
+ ?> (sane-contact `our.bowl new)
+ (p-commit-self new)
+ :: +p-page-spot: add ship as a contact
+ ::
+ ++ p-page-spot
+ |= [who=ship mod=contact]
+ ?: (~(has by book) who)
+ ~| "peer {} is already a contact" !!
+ =/ con=contact
+ ~| "peer {} not found"
+ =/ far=foreign
+ (~(got by peers) who)
+ ?~ for.far *contact
+ con.for.far
+ ?> (sane-contact ~ mod)
+ (p-commit-page who con mod)
+ :: +p-page: create new contact page
+ ::
+ ++ p-page
+ |= [=kip mod=contact]
+ ?@ kip
+ (p-page-spot kip mod)
+ ?: (~(has by book) kip)
+ ~| "contact page {} already exists" !!
+ ?> (sane-contact ~ mod)
+ (p-commit-page kip ~ mod)
+ :: +p-edit: edit contact page overlay
+ ::
+ ++ p-edit
+ |= [=kip mod=contact]
+ =/ =page
+ ~| "contact page {} does not exist"
+ (~(got by book) kip)
+ =/ old=contact
+ mod.page
+ =/ new=contact
+ (do-edit old mod)
+ ?: =(old new)
+ cor
+ ?> (sane-contact ?@(kip `kip ~) new)
+ (p-commit-edit kip con.page new)
+ :: +p-wipe: delete a contact page
+ ::
+ ++ p-wipe
+ |= wip=(list kip)
+ %+ roll wip
+ |= [=kip acc=_cor]
+ (p-commit-wipe kip)
+ :: +p-commit-self: publish modified profile
+ ::
+ ++ p-commit-self
+ |= con=contact
+ =/ p=profile [(mono wen.rof now.bowl) con]
+ =. rof p
+ =. cor
+ (p-news-0 our.bowl (contact:to-0 con))
+ =. cor
+ (p-response [%self con])
+ (give (fact subs [%full p]))
+ :: +p-commit-page: publish new contact page
+ ::
+ ++ p-commit-page
+ |= [=kip =page]
+ =. book (~(put by book) kip page)
+ (p-response [%page kip page])
+ :: +p-commit-edit: publish contact page update
+ ::
+ ++ p-commit-edit
+ |= [=kip =page]
+ =. book
+ (~(put by book) kip page)
+ (p-response [%page kip page])
+ :: +p-commit-wipe: publish contact page wipe
+ ::
+ ++ p-commit-wipe
+ |= =kip
+ =. book
+ (~(del by book) kip)
+ (p-response [%wipe kip])
+ :: +p-init: publish our profile
+ ::
+ ++ p-init
+ |= wen=(unit @da)
+ ?~ wen (give (fact ~ full+rof))
+ ?: =(u.wen wen.rof) cor
+ ::
+ :: no future subs
+ ?>((lth u.wen wen.rof) (give (fact ~ full+rof)))
+ :: +p-news-0: [legacy] publish news
+ ::
+ ++ p-news-0
+ |= n=news-0:c0
+ (give %fact ~[/news] %contact-news !>(n))
+ :: +p-response: publish response
+ ::
+ ++ p-response
+ |= r=response
+ (give %fact ~[/v1/news] %contact-response-0 !>(r))
+ --
+ ::
+ :: +sub: subscription mgmt
+ ::
+ :: /contact/*: foreign profiles, _s-impl
+ ::
+ :: subscription state is tracked per peer in .sag
+ ::
+ :: ~: no subscription
+ :: %want: /contact/* requested
+ ::
+ :: for a given peer, we always have at most one subscription,
+ :: to /contact/*
+ ::
+ ++ sub
+ |^ |= who=ship
+ ^+ s-impl
+ ?< =(our.bowl who)
+ =/ old (~(get by peers) who)
+ ~(. s-impl who %live ?=(~ old) (fall old *foreign))
+ ::
+ ++ s-many
+ |= [l=(list ship) f=$-(_s-impl _s-impl)]
+ ^+ cor
+ %+ roll l
+ |= [who=@p acc=_cor]
+ ?: =(our.bowl who) acc
+ si-abet:(f (sub:acc who))
+ ::
+ ++ s-impl
+ |_ [who=ship sas=?(%dead %live) new=? foreign]
+ ::
+ ++ si-cor .
+ ::
+ ++ si-abet
+ ^+ cor
+ ?- sas
+ %live =. peers (~(put by peers) who [for sag])
+ ?. new cor
+ :: NB: this assumes con.for is only set in +si-hear
+ ::
+ =. cor (p-news-0:pub who ~)
+ (p-response:pub [%peer who ~])
+ ::
+ %dead ?: new cor
+ =. peers (~(del by peers) who)
+ ::
+ :: this is not quite right, reflecting *total* deletion
+ :: as *contact* deletion. but it's close, and keeps /news simpler
+ ::
+ =. cor (p-news-0:pub who ~)
+ (p-response:pub [%peer who ~])
+ ==
+ ::
+ ++ si-take
+ |= [=wire =sign:agent:gall]
+ ^+ si-cor
+ ?- -.sign
+ %poke-ack ~|(strange-poke-ack+wire !!)
+ ::
+ %watch-ack ~| strange-watch-ack+wire
+ ?> ?=(%want sag)
+ ?~ p.sign si-cor
+ %- (slog 'contact-fail' u.p.sign)
+ =/ wake=@da (add now.bowl ~m30)
+ =. retry (~(put by retry) who wake)
+ %_ si-cor cor
+ (pass /retry/(scot %p who) %arvo %b %wait wake)
+ ==
+ ::
+ %kick si-meet(sag ~)
+ ::
+ %fact ?+ p.cage.sign ~|(strange-fact+wire !!)
+ %contact-update-1
+ (si-hear !<(update q.cage.sign))
+ == ==
+ ::
+ ++ si-hear
+ |= u=update
+ ^+ si-cor
+ ?. (sane-contact `src.bowl con.u)
+ si-cor
+ ?: &(?=(^ for) (lte wen.u wen.for))
+ si-cor
+ =. last-updated (~(put ol last-updated) who now.bowl)
+ %_ si-cor
+ for +.u
+ cor =. cor
+ (p-news-0:pub who (contact:to-0 con.u))
+ =/ page=(unit page) (~(get by book) who)
+ :: update contact book and send notification
+ ::
+ =? cor ?=(^ page)
+ ?: =(con.u.page con.u) cor
+ =. book (~(put by book) who u.page(con con.u))
+ (p-response:pub %page who con.u mod.u.page)
+ (p-response:pub %peer who con.u)
+ ==
+ ::
+ ::
+ ++ si-meet
+ ^+ si-cor
+ ::
+ :: already subscribed
+ ?: ?=(%want sag)
+ si-cor
+ =/ pat [%v1 %contact ?~(for / /at/(scot %da wen.for))]
+ %_ si-cor
+ cor (pass /contact %agent [who dap.bowl] %watch pat)
+ sag %want
+ ==
+ ::
+ ++ si-retry
+ ^+ si-cor
+ ::
+ ::XX this works around a gall/behn bug:
+ :: the timer is identified by the duct.
+ :: it needn't be the same when gall passes our
+ :: card to behn.
+ ::
+ ?. (~(has by retry) who)
+ si-cor
+ =. retry (~(del by retry) who)
+ si-meet(sag ~)
+ ::
+ ++ si-drop si-snub(sas %dead)
+ ::
+ ++ si-snub
+ %_ si-cor
+ sag ~
+ cor ?. ?=(%want sag) cor
+ :: retry is scheduled, cancel the timer
+ ::
+ ?^ when=(~(get by retry) who)
+ =. retry (~(del by retry) who)
+ (pass /retry/(scot %p who)/cancel %arvo %b %rest u.when)
+ (pass /contact %agent [who dap.bowl] %leave ~)
+ ==
+ --
+ --
+ ::
+ :: +migrate: from :contact-store
+ ::
+ :: all known ships, non-default profiles, no subscriptions
+ ::
+ ++ migrate
+ => |%
+ ++ legacy
+ |%
+ +$ rolodex (map ship contact)
+ +$ resource [=entity name=term]
+ +$ entity ship
+ +$ contact
+ $: nickname=@t
+ bio=@t
+ status=@t
+ color=@ux
+ avatar=(unit @t)
+ cover=(unit @t)
+ groups=(set resource)
+ last-updated=@da
+ ==
+ --
+ --
+ ::
+ ^+ cor
+ =/ bas /(scot %p our.bowl)/contact-store/(scot %da now.bowl)
+ ?. .^(? gu+(weld bas /$)) cor
+ =/ ful .^(rolodex:legacy gx+(weld bas /all/noun))
+ ::
+ |^
+ cor(rof us, peers them)
+ ++ us
+ %+ fall
+ (bind (~(get by ful) our.bowl) convert)
+ *profile
+ ::
+ ++ them
+ ^- ^peers
+ %- ~(rep by (~(del by ful) our.bowl))
+ |= [[who=ship con=contact:legacy] =^peers]
+ (~(put by peers) who (convert con) ~)
+ ::
+ ++ convert
+ |= con=contact:legacy
+ ^- profile
+ %- profile:from-0
+ [last-updated.con con(|6 groups.con)]
+ --
+ ::
+ +| %implementation
+ ::
+ ++ init
+ =. wen.rof now.bowl
+ (emit %pass /migrate %agent [our dap]:bowl %poke noun+!>(%migrate))
+ ::
+ ++ load
+ |= old-vase=vase
+ ^+ cor
+ |^ =+ !<([old=versioned-state *] old-vase)
+ =^ caz-0=(list card) old
+ ?. ?=(%0 -.old) [~ old]
+ (state-0-to-1 old)
+ =. cor (emil caz-0)
+ =? old ?=(%1 -.old) (state-1-to-2 old)
+ =? cor ?=(%2 -.old)
+ :: fix incorrectly bunted timestamp for an empty profile
+ :: migrated from v0.
+ ::
+ ?: &(=(*@da wen.rof) ?=(~ con.rof))
+ (p-commit-self:pub ~)
+ cor
+ =? old ?=(%2 -.old) (state-2-to-3 old)
+ =? old ?=(%3 -.old) (state-3-to-4 old)
+ ?> ?=(%4 -.old)
+ =. state old
+ inflate-io
+ ::
+ +$ state-0 [%0 rof=$@(~ profile-0:c0) rol=rolodex:c0]
+ +$ state-1
+ $: %1
+ rof=profile
+ =^book
+ =^peers
+ retry=(map ship @da) :: retry sub at time
+ ==
+ +$ state-2
+ $: %2
+ rof=profile
+ =^book
+ =^peers
+ retry=(map ship @da) :: retry sub at time
+ ==
+ +$ state-3
+ $: %3
+ rof=profile
+ =^book
+ =^peers
+ last-updated=(list [=kip =time])
+ retry=(map ship @da) :: retry sub at time
+ ==
+ +$ versioned-state
+ $% state-4
+ state-3
+ state-2
+ state-1
+ state-0
+ ==
+ ::
+ ++ state-3-to-4
+ |= state-3
+ ^- state-4
+ =* state +<
+ :: sanitize our nickname
+ ::
+ =+ nick=(~(get cy con.rof) %nickname %text)
+ =? con.rof &(?=(^ nick) !(sane-nickname `our.bowl u.nick))
+ %+ ~(put by con.rof) %nickname
+ text+(sani-nickname u.nick)
+ :: sanitize peer nicknames
+ ::
+ =. state
+ %+ roll ~(tap in ~(key by peers))
+ |= [her=ship =_state]
+ =+ far=(~(got by peers) her)
+ :: examine peer nickname and sanitize it if needed
+ ::
+ ?~ for.far state
+ =+ nick=(~(get cy con.for.far) %nickname %text)
+ ?~ nick state
+ ?: (sane-nickname `her u.nick) state
+ =. u.nick (sani-nickname u.nick)
+ =. con.for.far
+ %+ ~(put by con.for.far) %nickname
+ text+u.nick
+ =. peers.state
+ (~(put by peers.state) her far)
+ :: update the corresponding entry in the contact book, if any.
+ ::
+ ?~ page=(~(get by book) her) state
+ =. con.u.page
+ %+ ~(put by con.u.page) %nickname
+ text+u.nick
+ =. book.state
+ (~(put by book.state) her u.page)
+ state
+ state(- %4)
+ ::
+ ++ state-2-to-3
+ |= =state-2
+ ^- state-3
+ [%3 rof book peers ~ retry]:state-2
+ ::
+ ++ state-1-to-2
+ |= =state-1
+ ^- state-2
+ state-1(- %2)
+ ::
+ ++ state-0-to-1
+ |= state-0
+ ^- [(list card) state-1]
+ =| =state-1
+ =. rof.state-1
+ ?~(rof *profile (profile:from-0 rof))
+ :: migrate peers. for each peer
+ :: 1. leave /epic, if any
+ :: 2. subscribe if desired
+ :: 3. put into peers
+ ::
+ =^ caz=(list card) peers.state-1
+ %+ roll ~(tap by rol)
+ |= [[who=ship foreign-0:c0] caz=(list card) =_peers.state-1]
+ :: leave /epic if any
+ ::
+ =? caz (~(has by wex.bowl) [/epic who dap.bowl])
+ :_ caz
+ [%pass /epic %agent [who dap.bowl] %leave ~]
+ =/ fir=$@(~ profile)
+ ?~ for ~
+ (profile:from-0 for)
+ :: no intent to connect
+ ::
+ ?: =(~ sag)
+ :- caz
+ (~(put by peers) who fir ~)
+ :_ (~(put by peers) who fir %want)
+ ?: (~(has by wex.bowl) [/contact who dap.bowl])
+ caz
+ =/ =path [%v1 %contact ?~(fir / /at/(scot %da wen.fir))]
+ :_ caz
+ [%pass /contact %agent [who dap.bowl] %watch path]
+ [caz state-1]
+ ::
+ ++ inflate-io
+ ^+ cor
+ =/ cards
+ %+ roll ~(tap by peers)
+ |= [[who=ship foreign] caz=(list card)]
+ :: intent to connect, resubscribe
+ ::
+ ?: ?& =(%want sag)
+ !(~(has by wex.bowl) [/contact who dap.bowl])
+ ==
+ =/ =path [%v1 %contact ?~(for / /at/(scot %da wen.for))]
+ :_ caz
+ [%pass /contact %agent [who dap.bowl] %watch path]
+ caz
+ (emil cards)
+ --
+ ::
+ ++ poke
+ |= [=mark =vase]
+ ^+ cor
+ ?+ mark ~|(bad-mark+mark !!)
+ %noun
+ ?+ q.vase !!
+ %migrate migrate
+ ==
+ $? %contact-action
+ %contact-action-0
+ %contact-action-1
+ ==
+ ?> =(our src):bowl
+ =/ act=action
+ ?- mark
+ ::
+ %contact-action-1
+ !<(action vase)
+ :: upconvert legacy %contact-action
+ ::
+ ?(%contact-action %contact-action-0)
+ =/ act-0 !<(action-0:c0 vase)
+ ?. ?=(%edit -.act-0)
+ (to-action act-0)
+ :: v0 %edit needs special handling to evaluate
+ :: groups edit
+ ::
+ =/ groups=(set $>(%flag value))
+ ?~ con.rof ~
+ =+ set=(~(ges cy con.rof) groups+%flag)
+ (fall set ~)
+ [%self (to-self-edit p.act-0 groups)]
+ ==
+ =^ kis=(list kip) cor
+ ?- -.act
+ %anon [[our.bowl]~ p-anon:pub]
+ %self [[our.bowl]~ (p-self:pub p.act)]
+ :: if we add a page for someone who is not a peer,
+ :: we meet them first
+ ::
+ %page :- [p.act]~
+ =? cor &(?=(ship p.act) !(~(has by peers) p.act))
+ si-abet:si-meet:(sub p.act)
+ (p-page:pub p.act q.act)
+ %edit [[p.act]~ (p-edit:pub p.act q.act)]
+ %wipe [p.act (p-wipe:pub p.act)]
+ %meet [~ (s-many:sub p.act |=(s=_s-impl:sub si-meet:s))]
+ %drop [~ (s-many:sub p.act |=(s=_s-impl:sub si-drop:s))]
+ %snub [~ (s-many:sub p.act |=(s=_s-impl:sub si-snub:s))]
+ ==
+ =- cor(last-updated -)
+ %+ roll kis
+ |= [=kip =_last-updated]
+ (~(put ol last-updated) kip now.bowl)
+ ==
+ :: +peek: scry
+ ::
+ :: v0 scries
+ ::
+ :: /x/all -> $rolodex:c0
+ :: /x/contact/her=@ -> $@(~ contact-0:c0)
+ ::
+ :: v1 scries
+ ::
+ :: /x/v1/self -> $contact
+ :: /x/v1/book -> $book
+ :: /x/v1/book/her=@p -> $page
+ :: /x/v1/book/id/cid=@uv -> $page
+ :: /x/v1/all -> $directory
+ :: /x/v1/contact/her=@p -> $contact
+ :: /x/v1/peer/her=@p -> $contact
+ ::
+ ++ peek
+ |= pat=(pole knot)
+ ^- (unit (unit cage))
+ ?+ pat [~ ~]
+ ::
+ [%x %all ~]
+ =/ rol-0=rolodex:c0
+ %- ~(urn by peers)
+ |= [who=ship far=foreign]
+ ^- foreign-0:c0
+ =/ mod=contact
+ ?~ page=(~(get by book) who)
+ ~
+ mod.u.page
+ (foreign:to-0 (foreign-mod far mod))
+ =/ lor-0=rolodex:c0
+ ?: ?=(~ con.rof) rol-0
+ (~(put by rol-0) our.bowl (profile:to-0 rof) ~)
+ ``contact-rolodex+!>(lor-0)
+ ::
+ [%x %contact her=@ ~]
+ ?~ who=(slaw %p her.pat)
+ [~ ~]
+ =/ tac=?(~ contact-0:c0)
+ ?: =(our.bowl u.who)
+ ?~(con.rof ~ (contact:to-0 con.rof))
+ =+ far=(~(get by peers) u.who)
+ ?: |(?=(~ far) ?=(~ for.u.far)) ~
+ (contact:to-0 con.for.u.far)
+ ?~ tac [~ ~]
+ ``contact+!>(`contact-0:c0`tac)
+ ::
+ [%x %v1 %self ~]
+ ``contact-1+!>(`contact`con.rof)
+ ::
+ [%x %v1 %book ~]
+ ``contact-book-0+!>(book)
+ ::
+ [%u %v1 %book her=@p ~]
+ ?~ who=(slaw %p her.pat)
+ [~ ~]
+ ``loob+!>((~(has by book) u.who))
+ ::
+ [%x %v1 %book her=@p ~]
+ ?~ who=(slaw %p her.pat)
+ [~ ~]
+ =/ page=(unit page)
+ (~(get by book) u.who)
+ ``contact-page-0+!>(`^page`(fall page *^page))
+ ::
+ [%u %v1 %book %id =cid ~]
+ ?~ id=(slaw %uv cid.pat)
+ [~ ~]
+ ``loob+!>((~(has by book) id+u.id))
+ ::
+ [%x %v1 %book %id =cid ~]
+ ?~ id=(slaw %uv cid.pat)
+ [~ ~]
+ =/ page=(unit page)
+ (~(get by book) id+u.id)
+ ``contact-page-0+!>(`^page`(fall page *^page))
+ ::
+ [%x %v1 %all ~]
+ =| dir=directory
+ :: export all ship contacts
+ ::
+ =. dir
+ %- ~(rep by book)
+ |= [[=kip =page] =_dir]
+ ?^ kip
+ dir
+ (~(put by dir) kip (contact-uni page))
+ :: export all peers
+ ::
+ =. dir
+ %- ~(rep by peers)
+ |= [[who=ship far=foreign] =_dir]
+ ?~ for.far dir
+ ?: (~(has by dir) who) dir
+ (~(put by dir) who con.for.far)
+ ``contact-directory-0+!>(dir)
+ ::
+ [%x %v1 %changes since=@ ~]
+ =+ since=(slav %da since.pat)
+ :^ ~ ~
+ %contact-changed-contacts
+ !> ^- (map ship profile)
+ %- ~(rep by peers)
+ |= [[who=ship foreign] out=(map ship profile)]
+ ?~ for out
+ ?: (lte wen.for since) out
+ (~(put by out) who for)
+ ::
+ [%x %v2 %changes since=@ ~]
+ =+ since=(slav %da since.pat)
+ :^ ~ ~
+ %contact-changed-pages
+ !> %- ~(gas by *(map kip [(unit contact) (unit contact)]))
+ %+ turn (~(top ol last-updated) since)
+ |= [=kip @da]
+ ^- [_kip con=(unit contact) mod=(unit contact)]
+ :- kip
+ ?: =(our.bowl kip) [`con.rof ~]
+ :_ (bind (~(get by book) kip) tail)
+ ?^ kip ~
+ =+ per=(~(get by peers) kip)
+ ?:(?=([~ ^ *] per) `con.for.u.per ~)
+ ::
+ [%u %v1 %contact her=@p ~]
+ ?~ who=(slaw %p her.pat)
+ [~ ~]
+ ?: (~(has by book) u.who)
+ ``loob+!>(&)
+ =- ``loob+!>(-)
+ ?~ far=(~(get by peers) u.who)
+ |
+ ?~ for.u.far
+ |
+ &
+ ::
+ [%x %v1 %contact her=@p ~]
+ ?~ who=(slaw %p her.pat)
+ [~ ~]
+ ?^ page=(~(get by book) u.who)
+ ``contact-1+!>((contact-uni u.page))
+ ?~ far=(~(get by peers) u.who)
+ [~ ~]
+ ?~ for.u.far
+ [~ ~]
+ ``contact-1+!>(con.for.u.far)
+ ::
+ [%u %v1 %peer her=@p ~]
+ ?~ who=(slaw %p her.pat)
+ [~ ~]
+ ``loob+!>((~(has by peers) u.who))
+ ::
+ [%x %v1 %peer her=@p ~]
+ ?~ who=(slaw %p her.pat)
+ [~ ~]
+ ?~ far=(~(get by peers) u.who)
+ [~ ~]
+ ``contact-foreign-0+!>(`foreign`u.far)
+ ==
+ ::
+ ++ peer
+ |= pat=(pole knot)
+ ^+ cor
+ ?+ pat ~|(bad-watch-path+pat !!)
+ ::
+ :: v0
+ [%news ~] ~|(local-news+src.bowl ?>(=(our src):bowl cor))
+ ::
+ :: v1
+ [%v1 %contact ~] (p-init:pub ~)
+ [%v1 %contact %at wen=@ ~] (p-init:pub `(slav %da wen.pat))
+ [%v1 %news ~] ~|(local-news+src.bowl ?>(=(our src):bowl cor))
+ ::
+ [%epic ~] (give %fact ~ epic+!>(okay))
+ ==
+ ::
+ ++ agent
+ |= [=wire =sign:agent:gall]
+ ^+ cor
+ ?+ wire ~|(evil-agent+wire !!)
+ [%contact ~]
+ si-abet:(si-take:(sub src.bowl) wire sign)
+ ::
+ [%migrate ~]
+ ?> ?=(%poke-ack -.sign)
+ ?~ p.sign cor
+ %- (slog leaf/"{} failed" u.p.sign)
+ cor
+ ::
+ [%activity ~]
+ cor
+ ::
+ [%epic ~]
+ cor
+ ==
+ ::
+ ++ arvo
+ |= [=wire sign=sign-arvo]
+ ^+ cor
+ ?+ wire ~|(evil-vane+wire !!)
+ ::
+ [%retry her=@p ~]
+ :: XX technically, the timer could fail.
+ :: it should be ok to still retry.
+ ::
+ ?> ?=([%behn %wake *] sign)
+ =+ who=(slav %p i.t.wire)
+ si-abet:si-retry:(sub who)
+ ==
+ --
+--
diff --git a/app/app/nostrill.hoon b/app/app/nostrill.hoon
index 81f67f1..0192e7c 100644
--- a/app/app/nostrill.hoon
+++ b/app/app/nostrill.hoon
@@ -60,7 +60,6 @@
|~ [=mark =vase]
^- (quip card:agent:gall agent:gall)
|^
- ~& nostrill-on-poke=mark
?+ mark `this
%noun handle-comms
%json on-ui
@@ -73,7 +72,6 @@
==
++ on-ted
=/ pok !<(ted:ui vase)
- :: =/ poke !<()
=^ cards state (handle-ted:mutan pok)
:_ this cards
@@ -704,6 +702,23 @@
:~ (urbit-watch:fols ~zod)
[%pass /foldbug %agent [~zod dap.bowl] %poke %bitch !>(~)]
==
+ %uinf
+ :_ this
+ :~ (update-ui:cards [%nostr %feed nostr-feed])
+ ==
+ %tedt
+ =/ teds .^((list path) %gx /(scot %p our.bowl)/spider/(scot %da now.bowl)/tree/noun)
+ ~& teds
+ =/ flt=(list @t) (zing teds)
+ |- ?~ flt ~& 'no ted' `this
+ ?. .=('sync-' (cut 3 [0 5] i.flt)) $(flt t.flt)
+ =/ tid i.flt
+
+ :_ this
+ =/ payload [%res 'lmao']
+ :~
+ [%pass /fuck/this %agent [our.bowl %spider] %poke %spider-input !>([tid %nostrill-ted !>(payload)])]
+ ==
:: :: TODO refactoring into mutations
:: ::
:: %rt0
@@ -779,21 +794,25 @@
?+ pole `this
[%websocket-client wids=@ ~]
:: reconnect logic requires knowing which side dropped the connection, which is... tricky
- `this
- :: ~& websocket-client-connection-dropped=`@t`wids.pole
- :: =/ wid (slav %ud wids.pole)
+ ~& websocket-client-connection-dropped=`@t`wids.pole
+ =/ uwid (slaw %ud wids.pole)
+ ?~ uwid `this
+ =/ wid u.uwid
+ =. relays (~(del by relays) wid)
:: :: check if it's the global relay
:: =/ is-global .= `wid global-relay-conn
:: ?: is-global
:: ~& reconnecting-to-global=wid
:: :_ this :~((connect:ws global-relay:constants bowl))
:: ::
- :: =/ relay (~(get by relays) wid)
- :: ?~ relay `this :: that would be weird
:: :_ this
:: ~& reconnecting=url.u.relay
:: :~ (connect:ws url.u.relay bowl)
:: ==
+ :_ this
+ :: TODO more minute disconnected note
+ :~ (update-ui:cards [%nostr %relays relays])
+ ==
==
::
@@ -843,7 +862,7 @@
++ on-arvo
|~ [wire=(pole knot) =sign-arvo]
^- (quip card:agent:gall agent:gall)
- ~& >> on-arvo=[`path`wire -.sign-arvo +<.sign-arvo]
+ :: ~& >> on-arvo=[`path`wire -.sign-arvo +<.sign-arvo]
?: ?=(%iris -.sign-arvo)
:: ~& > +.sign-arvo
`this
diff --git a/app/desk.bill b/app/desk.bill
index ddea07e..b8d37c7 100644
--- a/app/desk.bill
+++ b/app/desk.bill
@@ -1,2 +1,3 @@
:~ %nostrill
+ %n-contacts
==
diff --git a/app/desk.docket-0 b/app/desk.docket-0
index e2e0735..692be88 100644
--- a/app/desk.docket-0
+++ b/app/desk.docket-0
@@ -2,10 +2,10 @@
title+'Trill 2'
info+'Pater Nostr'
color+0xff.d400
- version+[0 1 1]
+ version+[0 1 2]
website+'https://groundwire.dev'
license+'Noli me tangere'
image+'https://s3.sortug.com/img/nostrill-tile.png'
base+'nostrill'
- glob-http+['https://s3.sortug.com/globs/glob-0v2.0n7m8.g69ms.uejn8.dkget.1jm7v.glob' 0v2.0n7m8.g69ms.uejn8.dkget.1jm7v]
+ glob-http+['https://s3.sortug.com/globs/glob-0vjp0je.6lheq.e8r6b.5e483.59v2p.glob' 0vjp0je.6lheq.e8r6b.5e483.59v2p]
==
diff --git a/app/lib/contacts/contacts.hoon b/app/lib/contacts/contacts.hoon
new file mode 100644
index 0000000..82bf8f8
--- /dev/null
+++ b/app/lib/contacts/contacts.hoon
@@ -0,0 +1,536 @@
+/- *contacts, c0=contacts-0
+/+ unicode
+|%
+::
++| %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
+ |_ c=contact
+ :: +typ: enforce type if value exists
+ ::
+ ++ typ
+ |* [key=@tas typ=value-type]
+ ^- ?
+ =/ val=(unit value) (~(get by c) key)
+ ?~ val &
+ ?~ u.val |
+ ?- typ
+ %text ?=(%text -.u.val)
+ %numb ?=(%numb -.u.val)
+ %date ?=(%date -.u.val)
+ %tint ?=(%tint -.u.val)
+ %ship ?=(%ship -.u.val)
+ %look ?=(%look -.u.val)
+ %flag ?=(%flag -.u.val)
+ %set ?=(%set -.u.val)
+ ==
+ :: +get: typed get
+ ::
+ ++ get
+ |* [key=@tas typ=value-type]
+ ^- (unit _p:*$>(_typ value))
+ =/ val=(unit value) (~(get by c) key)
+ ?~ val ~
+ ?~ u.val !!
+ ~| "{} expected at {}"
+ ?- typ
+ %text ?>(?=(%text -.u.val) (some p.u.val))
+ %numb ?>(?=(%numb -.u.val) (some p.u.val))
+ %date ?>(?=(%date -.u.val) (some p.u.val))
+ %tint ?>(?=(%tint -.u.val) (some p.u.val))
+ %ship ?>(?=(%ship -.u.val) (some p.u.val))
+ %look ?>(?=(%look -.u.val) (some p.u.val))
+ %flag ?>(?=(%flag -.u.val) (some p.u.val))
+ %set ?>(?=(%set -.u.val) (some p.u.val))
+ ==
+ :: +ges: get specialized to typed set
+ ::
+ ::TODO introduce more legible names, ges -> get-set etc.
+ ++ ges
+ |* [key=@tas typ=value-type]
+ ^- (unit (set $>(_typ value)))
+ =/ val=(unit value) (~(get by c) key)
+ ?~ val ~
+ ?. ?=(%set -.u.val)
+ ~| "set expected at {}" !!
+ %- some
+ %- ~(run in p.u.val)
+ ?- typ
+ %text |=(v=value ?>(?=(%text -.v) v))
+ %numb |=(v=value ?>(?=(%numb -.v) v))
+ %date |=(v=value ?>(?=(%date -.v) v))
+ %tint |=(v=value ?>(?=(%tint -.v) v))
+ %ship |=(v=value ?>(?=(%ship -.v) v))
+ %look |=(v=value ?>(?=(%look -.v) v))
+ %flag |=(v=value ?>(?=(%flag -.v) v))
+ %set |=(v=value ?>(?=(%set -.v) v))
+ ==
+ :: +gos: got specialized to typed set
+ ::
+ ++ gos
+ |* [key=@tas typ=value-type]
+ ^- (set $>(_typ value))
+ =/ val=value (~(got by c) key)
+ ?. ?=(%set -.val)
+ ~| "set expected at {}" !!
+ %- ~(run in p.val)
+ ?- typ
+ %text |=(v=value ?>(?=(%text -.v) v))
+ %numb |=(v=value ?>(?=(%numb -.v) v))
+ %date |=(v=value ?>(?=(%date -.v) v))
+ %tint |=(v=value ?>(?=(%tint -.v) v))
+ %ship |=(v=value ?>(?=(%ship -.v) v))
+ %look |=(v=value ?>(?=(%look -.v) v))
+ %flag |=(v=value ?>(?=(%flag -.v) v))
+ %set |=(v=value ?>(?=(%set -.v) v))
+ ==
+ :: +gut: typed gut with default
+ ::
+ ++ gut
+ |* [key=@tas def=value]
+ ^+ +.def
+ =/ val=value (~(gut by c) key ~)
+ ?~ val
+ +.def
+ ~| "{<-.def>} expected at {}"
+ ?- -.val
+ %text ?>(?=(%text -.def) p.val)
+ %numb ?>(?=(%numb -.def) p.val)
+ %date ?>(?=(%date -.def) p.val)
+ %tint ?>(?=(%tint -.def) p.val)
+ %ship ?>(?=(%ship -.def) p.val)
+ %look ?>(?=(%look -.def) p.val)
+ %flag ?>(?=(%flag -.def) p.val)
+ %set ?>(?=(%set -.def) p.val)
+ ==
+ :: +gub: typed gut with bunt default
+ ::
+ ++ gub
+ |* [key=@tas typ=value-type]
+ ^+ +:*$>(_typ value)
+ =/ val=value (~(gut by c) key ~)
+ ?~ val
+ ?+ typ !!
+ %text *@t
+ %numb *@ud
+ %date *@da
+ %tint *@ux
+ %ship *@p
+ %look *@t
+ %flag *flag
+ %set *(set value)
+ ==
+ ~| "{} expected at {}"
+ ?- typ
+ %text ?>(?=(%text -.val) p.val)
+ %numb ?>(?=(%numb -.val) p.val)
+ %date ?>(?=(%date -.val) p.val)
+ %tint ?>(?=(%tint -.val) p.val)
+ %ship ?>(?=(%ship -.val) p.val)
+ %look ?>(?=(%look -.val) p.val)
+ %flag ?>(?=(%flag -.val) p.val)
+ %set ?>(?=(%set -.val) p.val)
+ ==
+ --
+::
+++ do-edit-0
+ |= [c=contact-0:c0 f=field-0:c0]
+ ^+ c
+ ?- -.f
+ %nickname c(nickname nickname.f)
+ %bio c(bio bio.f)
+ %status c(status status.f)
+ %color c(color color.f)
+ ::
+ %avatar ~| "cannot add a data url to avatar!"
+ ?> ?| ?=(~ avatar.f)
+ !=('data:' (end 3^5 u.avatar.f))
+ ==
+ c(avatar avatar.f)
+ ::
+ %cover ~| "cannot add a data url to cover!"
+ ?> ?| ?=(~ cover.f)
+ !=('data:' (end 3^5 u.cover.f))
+ ==
+ c(cover cover.f)
+ ::
+ %add-group c(groups (~(put in groups.c) flag.f))
+ ::
+ %del-group c(groups (~(del in groups.c) flag.f))
+ ==
+:: +sane-contact: verify contact sanity
+::
+:: - restrict size of the jammed noun to 10kB
+:: - prohibit 'data:' URLs in image data
+:: - nickname and bio must be a %text
+:: - avatar and cover must be a %look
+:: - groups must be a %set of %flags
+::
+++ sane-contact
+ |= [src=(unit @p) con=contact]
+ ^- ?
+ ?~ ((soft contact) con)
+ |
+ :: 10kB contact ought to be enough for anybody
+ ::
+ ?: (gth (met 3 (jam con)) 10.000)
+ |
+ :: field restrictions
+ ::
+ :: 1. %nickname field: max 64 characters
+ :: 2. %bio field: max 2048 characters
+ :: 3. data URLs in %avatar and %cover
+ :: are forbidden
+ ::
+ ?. (~(typ cy con) %nickname %text) |
+ =+ nickname=(~(get cy con) %nickname %text)
+ ?: ?& ?=(^ nickname)
+ ?| (gth (met 3 u.nickname) 64)
+ !(sane-nickname src u.nickname)
+ ==
+ ==
+ |
+ ?. (~(typ cy con) %bio %text) |
+ =+ bio=(~(get cy con) %bio %text)
+ ?: ?& ?=(^ bio)
+ (gth (met 3 u.bio) 2.048)
+ ==
+ |
+ ?. (~(typ cy con) %avatar %look) |
+ =+ avatar=(~(get cy con) %avatar %look)
+ ?: ?& ?=(^ avatar)
+ =('data:' (end 3^5 u.avatar))
+ ==
+ |
+ ?. (~(typ cy con) %cover %look) |
+ =+ cover=(~(get cy con) %cover %look)
+ ?: ?& ?=(^ cover)
+ =('data:' (end 3^5 u.cover))
+ ==
+ |
+ ?. (~(typ cy con) %groups %set) |
+ =+ groups=(~(get cy con) %groups %set)
+ :: verifying the type of the first set element is enough,
+ :: set uniformity is verified by +soft above.
+ ::
+ ?: ?& ?=(^ groups)
+ ?=(^ u.groups)
+ !?=(%flag -.n.u.groups)
+ ==
+ |
+ &
+:: +sane-nickname: validate a nickname against network id
+::
+++ sane-nickname
+ |= [src=(unit @p) txt=@t]
+ ^- ?
+ ?~ src &
+ =+ nom=(norm-p:confusable:unicode txt)
+ :: TODO for better performance, should probably operate on
+ :: tapes.
+ |-
+ ?: =('' nom) &
+ =+ len=(teff nom)
+ =* next $(nom (rsh [3 len] nom))
+ =+ c=(end [3 len] nom)
+ ?. =('~' c) next
+ :: could be the beginning of a pat-p, attempt to parse
+ ::
+ =/ vex (;~(pfix sig fed:ag) [[1 1] (trip nom)])
+ ?~ q.vex next
+ =/ ship=@p p.u.q.vex
+ ?. =(ship u.src) |
+ =+ pos=q.p.vex
+ $(nom (rsh [3 (dec pos)] nom))
+ :: q.p.vex
+:: +sani-nickname: sanitize a nickname
+::
+:: assume nickname is insane and delete all characters
+:: that normalize to a sig.
+::
+++ sani-nickname
+ |= txt=@t
+ ^- @t
+ =| norm=@t
+ |-
+ ?: =('' txt) norm
+ =+ len=(teff txt)
+ =+ c=(end [3 len] txt)
+ =* next
+ $(norm (cat 3 norm c), txt (rsh [3 len] txt))
+ =* skip
+ $(txt (rsh [3 len] txt))
+ ?: (~(has in con-sig:confusable:unicode) c)
+ skip
+ next
+:: +do-edit: edit contact
+::
+:: edit .con with .mod contact map.
+:: unifies the two maps, and deletes any resulting fields
+:: that are null.
+::
+++ do-edit
+ |= [con=contact mod=(map @tas value)]
+ ^+ con
+ =/ don (~(uni by con) mod)
+ =/ del=(list @tas)
+ %- ~(rep by don)
+ |= [[key=@tas val=value] acc=(list @tas)]
+ ?. ?=(~ val) acc
+ [key acc]
+ =? don !=(~ del)
+ %+ roll del
+ |= [key=@tas acc=_don]
+ (~(del by acc) key)
+ don
+:: +from-0: legacy to new type
+::
+++ from-0
+ |%
+ :: +contact: convert legacy to contact
+ ::
+ ++ contact
+ |= o=contact-0:c0
+ ^- ^contact
+ =/ c=^contact
+ %- malt
+ ^- (list (pair @tas value))
+ :~ nickname+text/nickname.o
+ bio+text/bio.o
+ status+text/status.o
+ color+tint/color.o
+ ==
+ =? c ?=(^ avatar.o)
+ (~(put by c) %avatar look/u.avatar.o)
+ =? c ?=(^ cover.o)
+ (~(put by c) %cover look/u.cover.o)
+ =? c !?=(~ groups.o)
+ %+ ~(put by c) %groups
+ :- %set
+ %- ~(run in groups.o)
+ |= =flag
+ flag/flag
+ c
+ :: +profile: convert legacy to profile
+ ::
+ ++ profile
+ |= o=profile-0:c0
+ ^- ^profile
+ [wen.o ?~(con.o ~ (contact con.o))]
+ ::
+ --
+:: +from: legacy from new type
+::
+++ to-0
+ |%
+ :: +contact: convert contact to legacy
+ ::
+ ++ contact
+ |= c=^contact
+ ^- $@(~ contact-0:c0)
+ ?~ c ~
+ =| o=contact-0:c0
+ %_ o
+ nickname
+ (~(gub cy c) %nickname %text)
+ bio
+ (~(gub cy c) %bio %text)
+ status
+ (~(gub cy c) %status %text)
+ color
+ (~(gub cy c) %color %tint)
+ avatar
+ (~(get cy c) %avatar %look)
+ cover
+ (~(get cy c) %cover %look)
+ groups
+ =/ groups
+ (~(get cy c) %groups %set)
+ ?~ groups ~
+ ^- (set flag)
+ %- ~(run in u.groups)
+ |= val=value
+ ?> ?=(%flag -.val)
+ p.val
+ ==
+ :: +profile: convert profile to legacy
+ ::
+ ++ profile
+ |= p=^profile
+ ^- profile-0:c0
+ [wen.p (contact:to-0 con.p)]
+ :: +profile-0-mod: convert profile with contact overlay
+ :: to legacy
+ ::
+ ++ profile-mod
+ |= [p=^profile mod=^contact]
+ ^- profile-0:c0
+ [wen.p (contact:to-0 (contact-uni con.p mod))]
+ :: +foreign: convert foreign to legacy
+ ::
+ ++ foreign
+ |= f=^foreign
+ ^- foreign-0:c0
+ [?~(for.f ~ (profile:to-0 for.f)) sag.f]
+ :: foreign-mod: convert foreign with contact overlay
+ :: to legacy
+ ::
+ ++ foreign-mod
+ |= [f=^foreign mod=^contact]
+ ^- foreign-0:c0
+ [?~(for.f ~ (profile-mod:to-0 for.f mod)) sag.f]
+ --
+:: +contact-uni: merge contacts
+::
+++ contact-uni
+ |= [c=contact mod=contact]
+ ^- contact
+ (~(uni by c) mod)
+:: +foreign-contact: get foreign contact
+::
+++ foreign-contact
+ |= far=foreign
+ ^- contact
+ ?~(for.far ~ con.for.far)
+:: +foreign-mod: modify foreign profile with user overlay
+::
+++ foreign-mod
+ |= [far=foreign mod=contact]
+ ^- foreign
+ ?~ for.far
+ far
+ far(con.for (contact-uni con.for.far mod))
+:: +sole-field-0: sole field is a field that does
+:: not modify the groups set
+::
++$ sole-field-0
+ $~ nickname+''
+ $<(?(%add-group %del-group) field-0:c0)
+:: +to-sole-edit: convert legacy sole field to contact edit
+::
+:: modify any field except for groups
+::
+++ to-sole-edit
+ |= edit-0=(list sole-field-0)
+ ^- contact
+ %+ roll edit-0
+ |= $: fed=sole-field-0
+ acc=(map @tas value)
+ ==
+ ^+ acc
+ ?- -.fed
+ ::
+ %nickname
+ %+ ~(put by acc)
+ %nickname
+ text/nickname.fed
+ ::
+ %bio
+ %+ ~(put by acc)
+ %bio
+ text/bio.fed
+ ::
+ %status
+ %+ ~(put by acc)
+ %status
+ text/status.fed
+ ::
+ %color
+ %+ ~(put by acc)
+ %color
+ tint/color.fed
+ ::
+ %avatar
+ ?~ avatar.fed acc
+ %+ ~(put by acc)
+ %avatar
+ look/u.avatar.fed
+ ::
+ %cover
+ ?~ cover.fed acc
+ %+ ~(put by acc)
+ %cover
+ look/u.cover.fed
+ ==
+:: +to-self-edit: convert legacy to self edit
+::
+++ to-self-edit
+ |= [edit-0=(list field-0:c0) groups=(set value)]
+ ^- contact
+ :: converting v0 profile edit to v1 is non-trivial.
+ :: for field edits other than groups, we derive a contact
+ :: edition map. for group operations (%add-group, %del-group)
+ :: we need to operate directly on (existing?) groups field in
+ :: the profile.
+ ::
+ :: .sed: sole field edits, no group edits
+ :: .ged: only group edit actions
+ ::
+ =* group-type ?(%add-group %del-group)
+ =* sole-edits (list $<(group-type field-0:c0))
+ =* group-edits (list $>(group-type field-0:c0))
+ :: sift edits
+ ::
+ =/ [sed=sole-edits ged=group-edits]
+ ::
+ :: XX why is casting neccessary here?
+ =- [(flop `sole-edits`-<) (flop `group-edits`->)]
+ %+ roll edit-0
+ |= [f=field-0:c0 sed=sole-edits ged=group-edits]
+ ^+ [sed ged]
+ ?. ?=(group-type -.f)
+ :- [f sed]
+ ged
+ :- sed
+ [f ged]
+ :: edit favourite groups
+ ::
+ =. groups
+ %+ roll ged
+ |= [fav=$>(group-type field-0:c0) =_groups]
+ ?- -.fav
+ %add-group
+ (~(put in groups) flag/flag.fav)
+ %del-group
+ (~(del in groups) flag/flag.fav)
+ ==
+ %+ ~(put by (to-sole-edit sed))
+ %groups
+ set/groups
+:: +to-action: convert legacy to action
+::
+:: convert any action except %edit.
+:: %edit must be handled separately, since we need
+:: access to existing groups to be able to process group edits.
+::
+++ to-action
+ |= o=$<(%edit action-0:c0)
+ ^- action
+ ?- -.o
+ %anon [%anon ~]
+ ::
+ :: old %meet is now a no-op
+ %meet [%meet ~]
+ %heed [%meet p.o]
+ %drop [%drop p.o]
+ %snub [%snub p.o]
+ ==
+:: +mono: tick time
+::
+++ mono
+ |= [old=@da new=@da]
+ ^- @da
+ ?: (lth old new) new
+ (add old ^~((rsh 3^2 ~s1)))
+--
diff --git a/app/lib/contacts/json-0.hoon b/app/lib/contacts/json-0.hoon
new file mode 100644
index 0000000..af69d80
--- /dev/null
+++ b/app/lib/contacts/json-0.hoon
@@ -0,0 +1,172 @@
+/- c=contacts
+/- legacy=contacts-0
+=, legacy
+|%
+++ gj
+|%
+ ++ enjs
+ =, enjs:format
+ |%
+ ++ print-flag
+ |= f=flag:c
+ ^- @t
+ (rap 3 (scot %p p.f) '/' q.f ~)
+ ++ flag
+ |= f=flag:c
+ s+(print-flag f)
+ ++ print-nest
+ |= n=nest:c
+ ^- @t
+ (rap 3 p.n '/' (print-flag q.n) ~)
+ ::
+ ++ nest
+ |= n=nest:c
+ s+(print-nest n)
+--
+++ dejs
+=, dejs:format
+|%
+ ++ p
+ |%
+ ++ rank (perk %czar %king %duke %earl %pawn ~)
+ ++ ship ;~(pfix sig fed:ag)
+ ++ flag ;~((glue fas) ship ^sym)
+ ++ nest ;~((glue fas) ^sym flag)
+ --
+ ++ sym (se %tas)
+ ++ ship (se %p)
+ ++ rank (su rank:p)
+ ++ flag (su flag:p)
+ ++ nest (su nest:p)
+ --
+--
+++ enjs
+ =, enjs:format
+ |%
+ :: XX shadowed for compat, +ship:enjs removes the ~
+ ::
+ ++ ship
+ |=(her=@p n+(rap 3 '"' (scot %p her) '"' ~))
+ ::
+ ++ action
+ |= a=action-0
+ ^- json
+ %+ frond -.a
+ ?- -.a
+ %anon ~
+ %edit a+(turn p.a field)
+ %meet a+(turn p.a ship)
+ %heed a+(turn p.a ship)
+ %drop a+(turn p.a ship)
+ %snub a+(turn p.a ship)
+ ==
+ ::
+ ++ contact
+ |= c=contact-0
+ ^- json
+ %- pairs
+ :~ nickname+s+nickname.c
+ bio+s+bio.c
+ status+s+status.c
+ color+s+(scot %ux color.c)
+ avatar+?~(avatar.c ~ s+u.avatar.c)
+ cover+?~(cover.c ~ s+u.cover.c)
+ ::
+ =- groups+a+-
+ %- ~(rep in groups.c)
+ |=([f=flag j=(list json)] [(flag:enjs:gj f) j])
+ ==
+ ::
+ ++ field
+ |= f=field-0
+ ^- json
+ %+ frond -.f
+ ?- -.f
+ %nickname s+nickname.f
+ %bio s+bio.f
+ %status s+status.f
+ %color s+(rsh 3^2 (scot %ux color.f)) :: XX confirm
+ %avatar ?~(avatar.f ~ s+u.avatar.f)
+ %cover ?~(cover.f ~ s+u.cover.f)
+ %add-group (flag:enjs:gj flag.f)
+ %del-group (flag:enjs:gj flag.f)
+ ==
+ ::
+ ++ rolodex
+ |= r=^rolodex
+ ^- json
+ %- pairs
+ %- ~(rep by r)
+ |= [[who=@p foreign-0] j=(list [@t json])]
+ [[(scot %p who) ?.(?=([@ ^] for) ~ (contact con.for))] j] :: XX stale flag per sub state?
+ ::
+ ++ news
+ |= n=news-0
+ ^- json
+ %- pairs
+ :~ who+(ship who.n)
+ con+?~(con.n ~ (contact con.n))
+ ==
+ --
+::
+++ dejs
+ =, dejs:format
+ |%
+ :: for performance, @p is serialized above to json %n (no escape)
+ :: for mark roundtrips, ships are parsed from either %s or %n
+ :: XX do this elsewhere in groups?
+ ::
+ ++ ship (se-ne %p)
+ ++ se-ne
+ |= aur=@tas
+ |= jon=json
+ ?+ jon !!
+ [%s *] (slav aur p.jon)
+ :: XX this seems wrong: current JSON parser
+ :: would never pass a ship as a number
+ ::
+ [%n *] ~| bad-n+p.jon
+ =/ wyd (met 3 p.jon)
+ ?> ?& =('"' (end 3 p.jon))
+ =('"' (cut 3 [(dec wyd) 1] p.jon))
+ ==
+ (slav aur (cut 3 [1 (sub wyd 2)] p.jon))
+ ==
+ ::
+ ++ action
+ ^- $-(json action-0)
+ %- of
+ :~ anon+ul
+ edit+(ar field)
+ meet+(ar ship)
+ heed+(ar ship)
+ drop+(ar ship)
+ snub+(ar ship)
+ ==
+ ::
+ ++ contact
+ ^- $-(json contact-0)
+ %- ot
+ :~ nickname+so
+ bio+so
+ status+so
+ color+nu
+ avatar+(mu so)
+ cover+(mu so)
+ groups+(as flag:dejs:gj)
+ ==
+ ::
+ ++ field
+ ^- $-(json field-0)
+ %- of
+ :~ nickname+so
+ bio+so
+ status+so
+ color+nu
+ avatar+(mu so)
+ cover+(mu so)
+ add-group+flag:dejs:gj
+ del-group+flag:dejs:gj
+ ==
+ --
+--
diff --git a/app/lib/contacts/json-1.hoon b/app/lib/contacts/json-1.hoon
new file mode 100644
index 0000000..514c303
--- /dev/null
+++ b/app/lib/contacts/json-1.hoon
@@ -0,0 +1,190 @@
+/- c=contacts
+|%
+++ g
+|%
+ ++ enjs
+ =, enjs:format
+ |%
+ ++ print-flag
+ |= f=flag:c
+ ^- @t
+ (rap 3 (scot %p p.f) '/' q.f ~)
+ ++ flag
+ |= f=flag:c
+ s+(print-flag f)
+ ++ print-nest
+ |= n=nest:c
+ ^- @t
+ (rap 3 p.n '/' (print-flag q.n) ~)
+ ::
+ ++ nest
+ |= n=nest:c
+ s+(print-nest n)
+--
+++ dejs
+=, dejs:format
+|%
+ ++ p
+ |%
+ ++ rank (perk %czar %king %duke %earl %pawn ~)
+ ++ ship ;~(pfix sig fed:ag)
+ ++ flag ;~((glue fas) ship ^sym)
+ ++ nest ;~((glue fas) ^sym flag)
+ --
+ ++ sym (se %tas)
+ ++ ship (se %p)
+ ++ rank (su rank:p)
+ ++ flag (su flag:p)
+ ++ nest (su nest:p)
+ --
+--
+++ enjs
+ =, enjs:format
+ |%
+ ::
+ ++ ship
+ |=(her=@p n+(rap 3 '"' (scot %p her) '"' ~))
+ ::
+ ++ cid
+ |= =cid:c
+ ^- json
+ s+(scot %uv cid)
+ ::
+ ++ kip
+ |= =kip:c
+ ^- json
+ ?@ kip
+ (ship kip)
+ (cid +.kip)
+ ::
+ ++ value
+ |= val=value:c
+ ^- json
+ ?@ val
+ (frond type+s/%null)
+ ?- -.val
+ %text (pairs type+s/%text value+s+p.val ~)
+ %numb (pairs type+s/%numb value+(numb p.val) ~)
+ %date (pairs type+s/%date value+s+(scot %da p.val) ~)
+ %tint (pairs type+s/%tint value+s+(rsh 3^2 (scot %ux p.val)) ~)
+ %ship (pairs type+s/%ship value+(ship p.val) ~)
+ %look (pairs type+s/%look value+s/p.val ~)
+ %flag (pairs type+s/%flag value+(flag:enjs:g p.val) ~)
+ %set (pairs type+s/%set value+a+(turn ~(tap in p.val) value) ~)
+ ==
+ ::
+ ++ contact
+ |= con=contact:c
+ ^- json
+ o+(~(run by con) value)
+ ::
+ ++ page
+ |= =page:c
+ ^- json
+ a+[(contact con.page) (contact mod.page) ~]
+ ::
+ ++ book
+ |= =book:c
+ ^- json
+ =| kob=(map @ta json)
+ :- %o
+ %- ~(rep by book)
+ |= [[=kip:c =page:c] acc=_kob]
+ ?^ kip
+ (~(put by acc) (scot %uv +.kip) (^page page))
+ (~(put by acc) (scot %p kip) (^page page))
+ ::
+ ++ directory
+ |= =directory:c
+ ^- json
+ =| dir=(map @ta json)
+ :- %o
+ %- ~(rep by directory)
+ |= [[who=@p con=contact:c] acc=_dir]
+ (~(put by acc) (scot %p who) (contact con))
+ ::
+ ++ response
+ |= n=response:c
+ ^- json
+ %+ frond -.n
+ ?- -.n
+ %self (frond contact+(contact con.n))
+ %page %- pairs
+ :~ kip+(kip kip.n)
+ contact+(contact con.n)
+ mod+(contact mod.n)
+ ==
+ %wipe (frond kip+(kip kip.n))
+ %peer %- pairs
+ :~ who+(ship who.n)
+ contact+(contact con.n)
+ ==
+ ==
+ --
+::
+++ dejs
+ =, dejs:format
+ |%
+ ::
+ ++ ship (se %p)
+ ::
+ ++ cid
+ |= jon=json
+ ^- cid:c
+ ?> ?=(%s -.jon)
+ (slav %uv p.jon)
+ ::
+ ++ kip
+ |= jon=json
+ ^- kip:c
+ ?> ?=(%s -.jon)
+ ?: =('~' (end [3 1] p.jon))
+ (ship jon)
+ id+(cid jon)
+ :: +ta: tag .wit parsed json with .mas
+ ::
+ ++ ta
+ |* [mas=@tas wit=fist]
+ |= jon=json
+ [mas (wit jon)]
+ ::
+ ++ value
+ ^- $-(json value:c)
+ |= jon=json
+ ?~ jon ~
+ =/ [type=@tas val=json]
+ %. jon
+ (ot type+(se %tas) value+json ~)
+ ?+ type !!
+ %text %. val (ta %text so)
+ %numb %. val (ta %numb ni)
+ %date %. val (ta %date (se %da))
+ %tint %. val
+ %+ ta %tint
+ %+ cu
+ |=(s=@t (slav %ux (cat 3 '0x' s)))
+ so
+ %ship %. val (ta %ship ship)
+ %look %. val (ta %look so)
+ %flag %. val (ta %flag flag:dejs:g)
+ %set %. val (ta %set (as value))
+ ==
+ ::
+ ++ contact
+ ^- $-(json contact:c)
+ (om value)
+ ::
+ ++ action
+ ^- $-(json action:c)
+ %- of
+ :~ anon+ul
+ self+contact
+ page+(ot kip+kip contact+contact ~)
+ edit+(ot kip+kip contact+contact ~)
+ wipe+(ar kip)
+ meet+(ar ship)
+ drop+(ar ship)
+ snub+(ar ship)
+ ==
+ --
+--
diff --git a/app/lib/default-agent.hoon b/app/lib/default-agent.hoon
new file mode 100644
index 0000000..319bf95
--- /dev/null
+++ b/app/lib/default-agent.hoon
@@ -0,0 +1,69 @@
+/+ skeleton
+|* [agent=* help=*]
+?: ?=(%& help)
+ ~| %default-agent-helpfully-crashing
+ skeleton
+|_ =bowl:gall
+++ on-init
+ `agent
+::
+++ on-save
+ !>(~)
+::
+++ on-load
+ |= old-state=vase
+ `agent
+::
+++ on-poke
+ |= =cage
+ ~| "unexpected poke to {} with mark {}"
+ !!
+::
+++ on-watch
+ |= =path
+ ~| "unexpected subscription to {} on path {}"
+ !!
+::
+++ on-leave
+ |= path
+ `agent
+::
+++ on-peek
+ |= =path
+ ~| "unexpected scry into {} on path {}"
+ !!
+::
+++ on-agent
+ |= [=wire =sign:agent:gall]
+ ^- (quip card:agent:gall _agent)
+ ?- -.sign
+ %poke-ack
+ ?~ p.sign
+ `agent
+ %- (slog leaf+"poke failed from {} on wire {}" u.p.sign)
+ `agent
+ ::
+ %watch-ack
+ ?~ p.sign
+ `agent
+ =/ =tank leaf+"subscribe failed from {} on wire {}"
+ %- (slog tank u.p.sign)
+ `agent
+ ::
+ %kick `agent
+ %fact
+ ~| "unexpected subscription update to {} on wire {}"
+ ~| "with mark {}"
+ !!
+ ==
+::
+++ on-arvo
+ |= [=wire =sign-arvo]
+ ~| "unexpected system response {<-.sign-arvo>} to {} on wire {}"
+ !!
+::
+++ on-fail
+ |= [=term =tang]
+ %- (slog leaf+"error in {}" >term< tang)
+ `agent
+--
diff --git a/app/lib/json/nostr.hoon b/app/lib/json/nostr.hoon
index 9c90c9d..13ea7f8 100644
--- a/app/lib/json/nostr.hoon
+++ b/app/lib/json/nostr.hoon
@@ -39,11 +39,11 @@
==
++ wevent
|= w=wevent:sur ^- json
- %: pairs
- relays+a+(turn relays.w cord:en:common)
- event+(event +.w)
- ~
- ==
+ =/ base (event +.w)
+ ?> ?=(%o -.base)
+ =. p.base (~(put by p.base) 'relays' [%a (turn relays.w cord:en:common)])
+ base
+
++ event
=/ nostr=? .n
|= e=event:sur ^- json
diff --git a/app/lib/kol.hoon b/app/lib/kol.hoon
new file mode 100644
index 0000000..448c70f
--- /dev/null
+++ b/app/lib/kol.hoon
@@ -0,0 +1,35 @@
+:: kol: key-unique, value-ordered lists
+::
+|* comp=$-([* *] ?)
+=| a=(list (pair)) :: key-value list
+|@
+++ put
+ |* b=(pair)
+ =+ c=| :: inserted b
+ =+ d=| :: dropped old
+ |- ^+ a
+ ?: &(d c) a :: done
+ ?~ a ?:(c ~ [b]~) :: end
+ ?: =(p.i.a p.b) $(a t.a, d &) :: drop old
+ ?: |(c (comp q.i.a q.b)) [i.a $(a t.a)] :: next
+ [b i.a $(a t.a, c &)] :: insert new
+::
+++ del
+ |* b=*
+ |- ^+ a
+ ?~ a ~
+ ?: =(p.i.a b) t.a
+ [i.a $(a t.a)]
+::
+++ top
+ |* b=*
+ |- ^+ a
+ ?~ a ~
+ ?:((comp q.i.a b) [i.a $(a t.a)] ~)
+::
+++ bot
+ |* b=*
+ |- ^+ a
+ ?~ a ~
+ ?:((comp q.i.a b) $(a t.a) a)
+--
diff --git a/app/lib/mutations/nostr.hoon b/app/lib/mutations/nostr.hoon
index 4cca0a7..d554dc7 100644
--- a/app/lib/mutations/nostr.hoon
+++ b/app/lib/mutations/nostr.hoon
@@ -101,7 +101,6 @@
=/ rclient ~(. relay.nclient [wid relay])
|^
=^ cards state
- ~& > handle-ws=-.msg
?- -.msg
:: This gets returned when we post a message to a relay
%ok (handle-ok url.relay +.msg)
@@ -142,9 +141,6 @@
|= [sub-id=@t =event:nsur]
^- (quip card _state)
:: increment event count in relay state
- ~& >> parsing-nostr-event=kind.event
- ~& >> sub-id=sub-id
- :: ~& > relay-subs=~(key by reqs.relay)
=/ req (~(get by reqs.relay) sub-id)
?~ req ~& >>> "sub id not found in relay state" `state
@@ -265,13 +261,12 @@
=/ ureq=(unit req-state:nsur) (~(get by reqs.relay) sub-id)
?~ ureq ~& >>> "sub id not found! on eose" `state
=/ reqs=req-state:nsur u.ureq
- ~& >> eose=reqs
- ~& >>> "**************"
::
:: if there's a queue we setup the next subscription
=^ cards relay
?: (is-feed:evlib filters.reqs)
~& >> "eose on global feed request"
+ :: TODO don't send all at once, stream in chunks
=/ c (update-ui:cardslib [%nostr %feed nostr-feed.state])
=^ mc relay get-profiles:rclient
[[c mc] relay]
@@ -314,7 +309,7 @@
:: if ongoing request we mark it as backlog received and keep it alive, else we cloe it
=^ cards3 relay
?~ ongoing.reqs
- ~& >>> closing-relay-sub=[sub-id filters.reqs]
+ ~& >>> closing-relay-sub=sub-id
=/ d (close-sub-req:nclient sub-id wid relay)
:_ +.d
:~ (send-card:rclient -.d)
@@ -323,10 +318,16 @@
=. reqs.relay (~(put by reqs.relay) sub-id reqs)
[~ relay]
- =/ eose-card (update-ui:cardslib [%nostr %eose sub-id])
- =/ carrds :- eose-card %+ weld cards %+ weld cards2 cards3
-
=. relays.state (~(put by relays.state) wid relay)
+ =/ eose-card (update-ui:cardslib [%nostr %eose sub-id])
+ =/ relays-card (update-ui:cardslib [%nostr %relays relays.state])
+ =/ cs
+ :~ eose-card
+ ==
+ =/ carrds=(list card)
+ :- eose-card :- relays-card
+ %+ weld cards %+ weld cards2 cards3
+
:_ state carrds
--
@@ -337,28 +338,6 @@
(~(put by profiles.state) [%urbit src.bowl] u.prof)
:: TODO kinda wanna send it to the UI
`state
-
-
- ++ call-relay |= [wid=@ud relay-url=@t ro=relay-order:ui]
- ^- (quip card _state)
- =/ nclient ~(. nostr-client [state bowl])
- =/ urelay (~(get by relays.state) wid)
- ?~ urelay
- ~& >>> not-connected-to-relay=wid
- (test-reconnection relay-url ro)
- ::
- =/ relay u.urelay
- =/ rclient ~(. relay.nclient [wid relay])
- ::
- =/ d=[(list card) _relay]
- ?+ -.ro !!
- %user (get-user-feed:rclient +.ro)
- %thread (get-thread:rclient +.ro)
- %sync get-posts:rclient
- %prof get-profiles:rclient
- ==
- =. relays.state (~(put by relays.state) wid +.d)
- [-.d state]
++ test-reconnection |= [relay-url=@t ro=relay-order:ui]
^- (quip card _state)
@@ -374,7 +353,7 @@
++ relay-get |= [tid=@ta wids=(list @ud) rg=relay-get:ui]
^- (quip card _state)
- ~& >> got-tid=tid
+ ~& >> relay-get=[tid wids rg]
=/ nclient ~(. nostr-client [state bowl])
=^ cards state
=| css=(list card)
@@ -390,12 +369,12 @@
=/ d
?+ -.rg !!
- %sync get-posts-ted:rclient
+ %sync ted:get-posts:rclient
==
=. reqs.relay (~(put by reqs.relay) sub-id.d rs.d)
=. relays.state (~(put by relays.state) wid relay)
=/ cs=(list card)
- :~ (poke-ui-thread:cards:lib tid sub-id.d)
+ :~ (poke-ui-thread:cardslib tid sub-id.d)
card.d
==
=/ ncss (weld cs css)
@@ -424,15 +403,18 @@
=/ rclient ~(. relay.nclient [wid relay])
::
=/ d=[(list card) _relay]
+ ~& action=action.rh
?- -.action.rh
%user (get-user-feed:rclient +.action.rh)
%thread (get-thread:rclient +.action.rh)
- %sync get-posts:rclient
+ %sync sub:get-posts:rclient
%prof get-profiles:rclient
==
=. relays.state (~(put by relays.state) wid +.d)
$(wids t.wids, css (weld css -.d))
- [cards state]
+ =/ relays-card (update-ui:cardslib [%nostr %relays relays.state])
+ =/ cs [relays-card cards]
+ [cs state]
++ send-post |= [wids=(list @ud) host=@p id=@da]
diff --git a/app/lib/nostr/client.hoon b/app/lib/nostr/client.hoon
index e10cb08..02368bc 100644
--- a/app/lib/nostr/client.hoon
+++ b/app/lib/nostr/client.hoon
@@ -1,5 +1,5 @@
/- sur=nostrill, nsur=nostr
-/+ js=json-nostr, sr=sortug, seq, nostr-keys, constants, server, ws=websockets, evlib=nostr-events
+/+ js=json-nostr, sr=sortug, seq, nostr-keys, constants, server, ws=websockets, evlib=nostr-events, lib=nostrill
/= web /web/router
|_ [=state:sur =bowl:gall]
+$ card card:agent:gall
@@ -42,9 +42,9 @@
++ set-req
|= [relay=relay-stats:nsur name=@t fs=(list filter:nsur) ongoing=(unit ?) chunked=(list filter:nsur)]
- ^- [client-msg:nsur relay-stats:nsur]
+ ^- [[%req relay-req:nsur] relay-stats:nsur]
=/ sub-id (gen-sub-id:nostr-keys eny.bowl)
- =/ msg=client-msg:nsur [%req sub-id fs]
+ =/ msg [%req sub-id fs]
=/ req=req-state:nsur [name fs 0 ongoing chunked]
=. reqs.relay (~(put by reqs.relay) sub-id req)
[msg relay]
@@ -106,43 +106,39 @@
=/ wmsg (req-to-msg req)
(give-ws-payload-client:ws wid wmsg)
::
- :: TODO temp, replace with one below
- ++ get-posts-ted
- ^- [sub-id=@t rs=req-state:nsur =card]
- =/ kinds (silt ~[1])
- =/ last-week (sub now.bowl ~m1)
- :: =/ since (to-unix-secs:jikan:sr last-week)
- =/ =filter:nsur [~ ~ `kinds ~ `last-week ~ ~]
- =/ req-name 'timeline'
- =/ filters ~[filter]
- =/ req (build-req filters)
- =/ sub-id=@t +<.req
- =/ card (send-card req)
- =/ rs=req-state:nsur (init-req req-name filters ~ ~)
- [sub-id rs card]
-
-
++ get-posts
=/ kinds (silt ~[1])
- =/ last-week (sub now.bowl ~h1)
- :: =/ since (to-unix-secs:jikan:sr last-week)
- =/ =filter:nsur [~ ~ `kinds ~ `last-week ~ ~]
+ =/ from:: look in settings agent
+ ?. .^(? %gx /(scot %p our.bowl)/settings/(scot %da now.bowl)/has-entry/nostrill/relay/backlog-size/noun)
+ ~h1
+ =/ res .^(* %gx /(scot %p our.bowl)/settings/(scot %da now.bowl)/entry/nostrill/relay/backlog-size/noun)
+ =/ hours ?> ?=(@ +>.res) +>.res
+ (mul hours ~h1)
+ ~& >>> from=from
+ :: =/ since (sub now.bowl from)
+ =/ since (sub now.bowl ~m10)
+
+
+ =/ =filter:nsur [~ ~ `kinds ~ `since ~ ~]
+ =/ filters ~[filter]
=/ req-name 'timeline'
- =^ req relay (set-req relay req-name ~[filter] ~ ~)
- :_ relay
- :~ (send-card req)
- ==
- :: ++ get-posts
- :: =/ kinds (silt ~[1])
- :: =/ last-week (sub now.bowl ~d7)
- :: :: =/ since (to-unix-secs:jikan:sr last-week)
- :: =/ =filter:nsur [~ ~ `kinds ~ `last-week ~ ~]
- :: =/ req-name 'timeline'
- :: =^ req relay (set-req relay req-name ~[filter] `.n ~)
- :: :_ relay
- :: :~ (send-card req)
- :: ==
- ::
+ |%
+ ++ ted
+ ^- [sub-id=@t rs=req-state:nsur =card]
+ =/ req (build-req filters)
+ =/ sub-id=@t sub-id.req
+ ~& get-posts-ted=sub-id
+ =/ card (send-card req)
+ =/ rs=req-state:nsur (init-req req-name filters ~ ~)
+ [sub-id rs card]
+ ++ sub
+ =^ req relay (set-req relay req-name ~[filter] ~ ~)
+ ~& get-posts-sub=req
+ :_ relay
+ :~ (send-card req)
+ ==
+ --
+
++ get-user-feed
|= pubkey=@ux
=/ kinds (silt ~[1])
@@ -208,7 +204,6 @@
::
++ get-profile |= pubkey=@ux
=/ kinds (silt ~[0])
- :: =/ since (to-unix-secs:jikan:sr last-week)
=/ pubkeys (silt ~[pubkey])
=/ =filter:nsur [~ `pubkeys `kinds ~ ~ ~ ~]
=/ req-name 'user profile fetch'
@@ -246,6 +241,9 @@
=/ pubkeys=(set @ux) (silt i.chunks)
=/ =filter:nsur [~ `pubkeys `kinds ~ ~ ~ ~]
(set-req relay req-name ~[filter] ~ queue)
+
+ =/ sub-id=@t sub-id.req
+ ~& >>> profiles-sub-id=sub-id
:_ relay
:~ (send-card req)
==
diff --git a/app/lib/nostrill.hoon b/app/lib/nostrill.hoon
index 02df8e9..cc079ef 100644
--- a/app/lib/nostrill.hoon
+++ b/app/lib/nostrill.hoon
@@ -82,6 +82,7 @@
[%pass /ted-res/[ta-now] %agent [our.bowl %spider] %poke %spider-input !>([tid %noun !>(body)])]
++ poke-ui-thread |= [tid=@ta sub-id=@t] ^- card:agent:gall
+ ~& >>> poke-ui-ted=tid
=/ ta-now (scot %ud `@`now.bowl)
=/ payload=ted:ui [%res sub-id]
[%pass /ted-res/[ta-now] %agent [our.bowl %spider] %poke %spider-input !>([tid %nostrill-ted !>(payload)])]
diff --git a/app/lib/skeleton.hoon b/app/lib/skeleton.hoon
new file mode 100644
index 0000000..982c371
--- /dev/null
+++ b/app/lib/skeleton.hoon
@@ -0,0 +1,51 @@
+:: Similar to default-agent except crashes everywhere
+^- agent:gall
+|_ bowl:gall
+++ on-init
+ ^- (quip card:agent:gall agent:gall)
+ !!
+::
+++ on-save
+ ^- vase
+ !!
+::
+++ on-load
+ |~ old-state=vase
+ ^- (quip card:agent:gall agent:gall)
+ !!
+::
+++ on-poke
+ |~ in-poke-data=cage
+ ^- (quip card:agent:gall agent:gall)
+ !!
+::
+++ on-watch
+ |~ path
+ ^- (quip card:agent:gall agent:gall)
+ !!
+::
+++ on-leave
+ |~ path
+ ^- (quip card:agent:gall agent:gall)
+ !!
+::
+++ on-peek
+ |~ path
+ ^- (unit (unit cage))
+ !!
+::
+++ on-agent
+ |~ [wire sign:agent:gall]
+ ^- (quip card:agent:gall agent:gall)
+ !!
+::
+++ on-arvo
+ |~ [wire =sign-arvo]
+ ^- (quip card:agent:gall agent:gall)
+ !!
+::
+++ on-fail
+ |~ [term tang]
+ ^- (quip card:agent:gall agent:gall)
+ !!
+--
diff --git a/app/lib/unicode.hoon b/app/lib/unicode.hoon
new file mode 100644
index 0000000..d7aafda
--- /dev/null
+++ b/app/lib/unicode.hoon
@@ -0,0 +1,956 @@
+:: unicode utilities
+::
+|%
+:: |confusables: unicode confusables characters
+::
+:: unicode is a standard that encompasses all world scripts. some
+:: of these scripts share characters, which thus gives rise to the
+:: problem of semantically distinct characters appearing the same.
+:: this is important if the program relies on these semantic
+:: differences for operation.
+::
+:: this library currently supports the following classes of
+:: confusables, following which can be queried through
+:: - latin alphabet confusables
+:: - ascii confusables
+:: - latin full-width confusables
+::
+:: the lists are based on
+:: https://www.unicode.org/Public/security/revision-03/confusablesSummary.txt,
+:: which can be queried at
+:: https://util.unicode.org/UnicodeJsps/confusables.jsp.
+::
+:: utility arms are provided that aid in normalizing an utf-8 string,
+:: using latin letters and ascii characters as representatives.
+::
+++ confusable
+ |%
+ :: +con-sig: confusable with '~'
+ ++ con-sig ^~
+ ^- (set @t)
+ %- silt
+ :~ '῀'
+ '⁓'
+ '˜'
+ '∼'
+ '~'
+ ==
+ :: +con-hep: confusable '-'
+ ++ con-hep
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '‐'
+ '‑'
+ '‒'
+ '−'
+ '–'
+ '⁃'
+ '۔'
+ '➖'
+ '˗'
+ '﹘'
+ 'Ⲻ'
+ '-'
+ ==
+
+ :: +con-a: confusable with 'a'
+ ++ con-a
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝝰'
+ 'a'
+ 'a'
+ '𝑎'
+ '𝗮'
+ '𝕒'
+ '𝖆'
+ '𝓪'
+ '𝚊'
+ '𝞪'
+ 'а'
+ 'ɑ'
+ 'α'
+ '𝔞'
+ '𝒂'
+ '𝘢'
+ '𝛂'
+ '⍺'
+ '𝒶'
+ '𝙖'
+ '𝜶'
+ '𝛼'
+ '𝐚'
+ '𝖺'
+ ==
+ :: +con-b: confusable with 'b'
+ ++ con-b
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝑏'
+ '𝗯'
+ 'b'
+ 'Ƅ'
+ '𝕓'
+ '𝖇'
+ 'Ь'
+ '𝓫'
+ '𝚋'
+ 'Ꮟ'
+ 'ᖯ'
+ '𝔟'
+ 'ᑲ'
+ '𝒃'
+ '𝘣'
+ '𝒷'
+ '𝙗'
+ '𝐛'
+ '𝖻'
+ ==
+ :: +con-c: confusable with 'c'
+ ++ con-c
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝑐'
+ '𝗰'
+ 'с'
+ 'c'
+ 'c'
+ '𝕔'
+ 'ᴄ'
+ 'ⲥ'
+ '𐐽'
+ '𝖈'
+ '𝓬'
+ '𝚌'
+ 'ꮯ'
+ '𝔠'
+ 'ϲ'
+ '𝒄'
+ '𝘤'
+ '𝒸'
+ '𝙘'
+ '𝐜'
+ '𝖼'
+ 'ⅽ'
+ ==
+ :: +con-d: confusable with 'd'
+ ++ con-d
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ 'ԁ'
+ '𝓭'
+ '𝚍'
+ 'd'
+ 'ⅆ'
+ '𝑑'
+ '𝗱'
+ 'Ꮷ'
+ '𝒅'
+ '𝘥'
+ '𝖉'
+ 'ᑯ'
+ 'ꓒ'
+ '𝐝'
+ '𝖽'
+ '𝔡'
+ '𝕕'
+ 'ⅾ'
+ '𝒹'
+ '𝙙'
+ ==
+ :: +con-e: confusable with 'e'
+ ++ con-e
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝓮'
+ '𝚎'
+ 'e'
+ 'e'
+ '𝑒'
+ '𝗲'
+ 'ⅇ'
+ '𝒆'
+ '𝘦'
+ '℮'
+ '𝖊'
+ 'ℯ'
+ '𝐞'
+ '𝖾'
+ 'ꬲ'
+ 'е'
+ '𝔢'
+ '𝕖'
+ 'ҽ'
+ '𝙚'
+ ==
+ :: +con-f: confusable with 'f'
+ ++ con-f
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝓯'
+ '𝚏'
+ 'ք'
+ '𝑓'
+ '𝗳'
+ 'f'
+ '𝒇'
+ '𝘧'
+ '𝖋'
+ '𝐟'
+ '𝖿'
+ '𝔣'
+ 'ꬵ'
+ '𝕗'
+ 'ꞙ'
+ '𝒻'
+ '𝙛'
+ 'ẝ'
+ 'ſ'
+ ==
+ :: +con-g: confusable with 'g'
+ ++ con-g
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝓰'
+ '𝚐'
+ 'ɡ'
+ 'ց'
+ 'ᶃ'
+ '𝑔'
+ '𝗴'
+ 'g'
+ 'g'
+ '𝒈'
+ '𝘨'
+ 'ℊ'
+ '𝖌'
+ 'ƍ'
+ '𝐠'
+ '𝗀'
+ '𝔤'
+ '𝕘'
+ '𝙜'
+ ==
+ :: +con-h: confusable with 'h'
+ ++ con-h
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ 'Ꮒ'
+ '𝖍'
+ '𝓱'
+ '𝚑'
+ 'h'
+ 'h'
+ '𝔥'
+ 'ℎ'
+ '𝒉'
+ '𝘩'
+ 'հ'
+ '𝒽'
+ '𝙝'
+ '𝐡'
+ '𝗁'
+ '𝗵'
+ 'һ'
+ '𝕙'
+ ==
+ :: +con-i: confusable with 'i'
+ ++ con-i
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝓲'
+ '𝞲'
+ 'ꙇ'
+ 'ⅈ'
+ 'i'
+ '𝔦'
+ '𝘪'
+ 'ӏ'
+ '𝙞'
+ '𝚤'
+ '𝐢'
+ 'і'
+ '𝑖'
+ '˛'
+ '𝕚'
+ '𝖎'
+ 'Ꭵ'
+ '𝚒'
+ '𑣃'
+ 'i'
+ 'ɩ'
+ 'ɪ'
+ '𝒊'
+ '𝛊'
+ 'ⅰ'
+ 'ı'
+ '𝒾'
+ '𝜾'
+ '⍳'
+ '𝜄'
+ 'ꭵ'
+ '𝗂'
+ '𝝸'
+ 'ℹ'
+ 'ι'
+ '𝗶'
+ 'ͺ'
+ ==
+ :: +con-j: confusable with 'j'
+ ++ con-j
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝖏'
+ '𝓳'
+ '𝚓'
+ 'ⅉ'
+ '𝔧'
+ 'j'
+ 'j'
+ '𝒋'
+ '𝘫'
+ '𝒿'
+ '𝙟'
+ 'ϳ'
+ '𝐣'
+ '𝗃'
+ 'ј'
+ '𝑗'
+ '𝗷'
+ '𝕛'
+ ==
+ :: +con-k: confusable with 'k'
+ ++ con-k
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝖐'
+ '𝓴'
+ '𝚔'
+ '𝔨'
+ 'k'
+ '𝒌'
+ '𝘬'
+ '𝓀'
+ '𝙠'
+ '𝐤'
+ '𝗄'
+ '𝑘'
+ '𝗸'
+ '𝕜'
+ ==
+ :: +con-l: confusable with 'l'
+ ++ con-l
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝚰'
+ '𝘭'
+ 'І'
+ '𝖨'
+ '𝐥'
+ 'ﺍ'
+ 'ﺎ'
+ '𝔩'
+ 'ℐ'
+ 'ℑ'
+ '𐊊'
+ 'Ⲓ'
+ '𐌉'
+ 'ℓ'
+ '𝜤'
+ 'Ɩ'
+ '𝞘'
+ 'Ι'
+ '𝚕'
+ '𝟏'
+ '∣'
+ 'ا'
+ 'I'
+ '𝗅'
+ '𝕀'
+ '1'
+ '𝙄'
+ '𝓁'
+ '𐌠'
+ '𝐼'
+ '𞸀'
+ '𞺀'
+ '׀'
+ '𝑰'
+ 'ǀ'
+ 'Ӏ'
+ 'ᛁ'
+ '𝟭'
+ '𝕴'
+ 'I'
+ 'ߊ'
+ 'l'
+ '𝛪'
+ 'ⵏ'
+ '𝝞'
+ '𝕝'
+ '𝟣'
+ 'ו'
+ '𞣇'
+ '𝙡'
+ '𝓘'
+ '𝗜'
+ '𝟙'
+ '𝑙'
+ 'ן'
+ 'Ⅰ'
+ '𝘐'
+ '١'
+ '𝒍'
+ '𝖑'
+ '│'
+ '🯱'
+ '𝐈'
+ 'l'
+ '۱'
+ 'ꓲ'
+ '𖼨'
+ '𝙸'
+ '𝟷'
+ '𝓵'
+ '|'
+ 'ⅼ'
+ '⏽'
+ '𝗹'
+ ==
+ :: +con-m: confusable with 'm'
+ ++ con-m
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𑜀'
+ '𝒎'
+ '𝘮'
+ '𑣣'
+ '𝖒'
+ '𝐦'
+ '𝗆'
+ 'm'
+ '𝔪'
+ '𝕞'
+ '𝓂'
+ '𝙢'
+ '𝓶'
+ '𝚖'
+ 'rn'
+ '𝑚'
+ '𝗺'
+ 'ⅿ'
+ ==
+ :: +con-n: confusable with 'n'
+ ++ con-n
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝒏'
+ '𝘯'
+ '𝖓'
+ '𝐧'
+ '𝗇'
+ '𝔫'
+ 'n'
+ '𝕟'
+ '𝓃'
+ '𝙣'
+ 'ո'
+ '𝓷'
+ '𝚗'
+ 'ռ'
+ '𝑛'
+ '𝗻'
+ ==
+ :: +con-o: confusable with 'o'
+ ++ con-o
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝘰'
+ 'ం'
+ 'ಂ'
+ 'ം'
+ 'ං'
+ 'օ'
+ '𝐨'
+ '𑣗'
+ '𝔬'
+ 'ᴏ'
+ 'ᴑ'
+ '𞹤'
+ '𐓪'
+ '𝚘'
+ '𑣈'
+ 'ဝ'
+ 'ⲟ'
+ '𝛐'
+ 'ഠ'
+ '𝛔'
+ 'ﮦ'
+ 'ﮧ'
+ '𝗈'
+ '𝝈'
+ 'ﮨ'
+ 'ﮩ'
+ 'ﮪ'
+ 'ﮫ'
+ 'ﮬ'
+ 'ﮭ'
+ '𞺄'
+ 'ℴ'
+ '𝝄'
+ '𝞸'
+ '𝞼'
+ 'ꬽ'
+ 'о'
+ 'ھ'
+ 'ο'
+ '၀'
+ 'ہ'
+ 'σ'
+ 'ه'
+ 'o'
+ '๐'
+ '໐'
+ '𝕠'
+ '𐐬'
+ '𞸤'
+ '𝙤'
+ 'ە'
+ '𝑜'
+ '𝒐'
+ 'ס'
+ '𝜎'
+ '𝖔'
+ '٥'
+ '०'
+ '੦'
+ '૦'
+ '௦'
+ '౦'
+ '೦'
+ '൦'
+ 'ﻩ'
+ 'ﻪ'
+ 'ﻫ'
+ 'ﻬ'
+ '𝜊'
+ 'o'
+ '𝝾'
+ '۵'
+ '𝞂'
+ '𝓸'
+ '𝗼'
+ 'ჿ'
+ ==
+ :: +con-p: confusable with 'p'
+ ++ con-p
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ 'р'
+ 'ρ'
+ '𝔭'
+ '𝘱'
+ '𝙥'
+ '𝐩'
+ 'p'
+ '𝛠'
+ '𝑝'
+ '𝕡'
+ '𝖕'
+ '𝜚'
+ '𝚙'
+ '𝞎'
+ 'ⲣ'
+ '𝝔'
+ '𝛒'
+ '𝒑'
+ '𝟈'
+ '𝝆'
+ '𝓅'
+ '𝜌'
+ '𝗉'
+ 'p'
+ '𝞀'
+ 'ϱ'
+ '𝗽'
+ '⍴'
+ '𝞺'
+ '𝓹'
+ ==
+ :: +con-q: confusable with 'q'
+ ++ con-q
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝔮'
+ 'գ'
+ '𝒒'
+ '𝘲'
+ 'զ'
+ '𝓆'
+ '𝙦'
+ '𝐪'
+ '𝗊'
+ 'q'
+ '𝑞'
+ '𝗾'
+ '𝕢'
+ '𝖖'
+ 'ԛ'
+ '𝓺'
+ '𝚚'
+ ==
+ :: +con-r: confusable with 'r'
+ ++ con-r
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝔯'
+ 'ꮁ'
+ '𝒓'
+ '𝘳'
+ 'ⲅ'
+ 'ᴦ'
+ 'ꭇ'
+ 'ꭈ'
+ '𝓇'
+ '𝙧'
+ '𝐫'
+ '𝗋'
+ '𝑟'
+ '𝗿'
+ 'r'
+ 'г'
+ '𝕣'
+ '𝖗'
+ '𝓻'
+ '𝚛'
+ ==
+ :: +con-s: confusable with 's'
+ ++ con-s
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝔰'
+ '𑣁'
+ '𝒔'
+ '𝘴'
+ '𝓈'
+ '𝙨'
+ 'ꮪ'
+ '𝐬'
+ '𝗌'
+ '𝑠'
+ '𝘀'
+ 'ꜱ'
+ 's'
+ 's'
+ '𝕤'
+ 'ѕ'
+ '𝖘'
+ '𝓼'
+ '𝚜'
+ '𐑈'
+ 'ƽ'
+ ==
+ :: +con-t: confusable with 't'
+ ++ con-t
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝐭'
+ '𝗍'
+ '𝔱'
+ '𝕥'
+ '𝓉'
+ '𝙩'
+ '𝓽'
+ '𝚝'
+ 't'
+ '𝑡'
+ '𝘁'
+ '𝒕'
+ '𝘵'
+ '𝖙'
+ ==
+ :: +con-u: confusable with 'u'
+ ++ con-u
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝐮'
+ 'υ'
+ '𝔲'
+ '𑣘'
+ '𝕦'
+ 'ʋ'
+ '𝙪'
+ 'ꭎ'
+ '𐓶'
+ '𝚞'
+ 'ꭒ'
+ '𝑢'
+ '𝒖'
+ '𝛖'
+ 'ᴜ'
+ '𝖚'
+ 'ꞟ'
+ '𝜐'
+ '𝗎'
+ '𝓊'
+ '𝝊'
+ '𝓾'
+ '𝞾'
+ '𝞄'
+ 'u'
+ '𝘂'
+ '𝘶'
+ 'ս'
+ ==
+ :: +con-v: confusable with 'v'
+ ++ con-v
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '⋁'
+ '𝐯'
+ '𝔳'
+ '𝕧'
+ '𝙫'
+ '𝚟'
+ '𝑣'
+ 'v'
+ 'ט'
+ '𝒗'
+ '𝖛'
+ 'ᴠ'
+ '𝗏'
+ '𑣀'
+ '𝛎'
+ '∨'
+ '𝜈'
+ 'ꮩ'
+ '𝓋'
+ '𝓿'
+ 'ⅴ'
+ '𝘃'
+ 'ѵ'
+ 'v'
+ '𝝂'
+ '𝘷'
+ '𝞶'
+ '𑜆'
+ '𝝼'
+ 'ν'
+ ==
+ :: +con-w: confusable with 'w'
+ ++ con-w
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝐰'
+ '𝗐'
+ 'ᴡ'
+ 'ѡ'
+ 'ա'
+ 'ꮃ'
+ '𝔴'
+ '𝕨'
+ '𝓌'
+ '𝙬'
+ 'ɯ'
+ '𝔀'
+ '𝚠'
+ '𑜏'
+ '𑜎'
+ '𝑤'
+ '𝘄'
+ 'w'
+ '𝒘'
+ '𝘸'
+ '𝖜'
+ 'ԝ'
+ '𑜊'
+ ==
+ :: +con-x: confusable with 'x'
+ ++ con-x
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ 'ᕁ'
+ '𝓍'
+ '𝙭'
+ 'х'
+ '𝐱'
+ '𝗑'
+ '⤫'
+ '𝑥'
+ '𝘅'
+ '⤬'
+ '᙮'
+ '⨯'
+ '𝕩'
+ '𝖝'
+ '×'
+ '𝔁'
+ '𝚡'
+ 'x'
+ 'x'
+ 'ⅹ'
+ '𝔵'
+ 'ᕽ'
+ '𝒙'
+ '𝘹'
+ ==
+ :: +con-y: confusable with 'y'
+ ++ con-y
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝙮'
+ 'у'
+ '𝐲'
+ '𝝲'
+ '𝑦'
+ 'ᶌ'
+ '𝞬'
+ '𑣜'
+ '𝕪'
+ 'ʏ'
+ '𝖞'
+ '𝚢'
+ 'y'
+ 'ꭚ'
+ '𝒚'
+ '𝓎'
+ 'ɣ'
+ '𝗒'
+ 'ყ'
+ '𝘆'
+ 'ү'
+ '𝛾'
+ 'γ'
+ '𝛄'
+ '𝔂'
+ '𝜸'
+ 'y'
+ '𝔶'
+ 'ℽ'
+ '𝘺'
+ 'ỿ'
+ ==
+ :: +con-z: confusable with 'z'
+ ++ con-z
+ ^~
+ ^- (set @t)
+ %- silt
+ :~ '𝓏'
+ '𝙯'
+ 'ᴢ'
+ '𝐳'
+ '𝗓'
+ '𑣄'
+ '𝑧'
+ '𝘇'
+ '𝕫'
+ '𝖟'
+ 'ꮓ'
+ '𝔃'
+ '𝚣'
+ '𝔷'
+ 'z'
+ '𝒛'
+ '𝘻'
+ ==
+ :: con-latin: latin confusables
+ ++ con-latin
+ ^~
+ ^- (list (pair @t (set @t)))
+ :~ ['a' con-a]
+ ['b' con-b]
+ ['c' con-c]
+ ['d' con-d]
+ ['e' con-e]
+ ['f' con-f]
+ ['g' con-g]
+ ['h' con-h]
+ ['i' con-i]
+ ['j' con-j]
+ ['k' con-k]
+ ['l' con-l]
+ ['m' con-m]
+ ['n' con-n]
+ ['o' con-o]
+ ['p' con-p]
+ ['q' con-q]
+ ['r' con-r]
+ ['s' con-s]
+ ['t' con-t]
+ ['u' con-u]
+ ['v' con-v]
+ ['w' con-w]
+ ['x' con-x]
+ ['y' con-y]
+ ['z' con-z]
+ ==
+ :: +norm-latin-wide: normalize full-width latin character
+ ::
+ ++ norm-latin-wide
+ |= =@t
+ ^- @t
+ =+ c=(taft t)
+ :: ascii wide characters are respresented in the
+ :: range U+FF01 - U+FF5E.
+ ?. &((gte c 0xff01) (lte c 0xff5e)) t
+ (add (sub c 0xff00) 0x20)
+ :: +norm-latin: normalize latin character
+ ::
+ ++ norm-latin
+ |= c=@t
+ ^- @t
+ :: leave ascii as is
+ ?: (lte c 0x7f) c
+ =/ conf=(list (pair @t (set @t)))
+ con-latin
+ |-
+ ?~ conf c
+ ?: (~(has in q.i.conf) c)
+ p.i.conf
+ $(conf t.conf)
+ :: +norm-p: normalize a possible @p string
+ ::
+ ++ norm-p
+ |= txt=@t
+ ^- @t
+ =| norm=@t
+ |-
+ ?: =('' txt) norm
+ =+ len=(teff txt)
+ =+ c=(end [3 len] txt)
+ :: leave ascii as is
+ ?: (lte c 0x7f)
+ $(norm (cat 3 norm c), txt (rsh [3 len] txt))
+ =/ n=@t
+ ?: (~(has in con-sig) c) '~'
+ ?: (~(has in con-hep) c) '-'
+ (norm-latin-wide (norm-latin c))
+ $(norm (cat 3 norm n), txt (rsh [3 len] txt))
+ --
+--
diff --git a/app/mar/contact/action-0.hoon b/app/mar/contact/action-0.hoon
new file mode 100644
index 0000000..8ea2b57
--- /dev/null
+++ b/app/mar/contact/action-0.hoon
@@ -0,0 +1,15 @@
+/- c=contacts, legacy=contacts-0
+/+ j=contacts-json-0
+|_ action=action-0:legacy
+++ grad %noun
+++ grow
+ |%
+ ++ noun action
+ ++ json (action:enjs:j action)
+ --
+++ grab
+ |%
+ ++ noun action-0:legacy
+ ++ json action:dejs:j
+ --
+--
diff --git a/app/mar/contact/action-1.hoon b/app/mar/contact/action-1.hoon
new file mode 100644
index 0000000..4525792
--- /dev/null
+++ b/app/mar/contact/action-1.hoon
@@ -0,0 +1,14 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ action=action:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun action
+ --
+++ grab
+ |%
+ ++ noun action:c
+ ++ json action:dejs:j
+ --
+--
diff --git a/app/mar/contact/action.hoon b/app/mar/contact/action.hoon
new file mode 100644
index 0000000..f602042
--- /dev/null
+++ b/app/mar/contact/action.hoon
@@ -0,0 +1,2 @@
+/= mark /mar/contact/action-0
+mark
diff --git a/app/mar/contact/book-0.hoon b/app/mar/contact/book-0.hoon
new file mode 100644
index 0000000..2de84aa
--- /dev/null
+++ b/app/mar/contact/book-0.hoon
@@ -0,0 +1,14 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ book=book:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun book
+ ++ json (book:enjs:j book)
+ --
+++ grab
+ |%
+ ++ noun book:c
+ --
+--
diff --git a/app/mar/contact/book.hoon b/app/mar/contact/book.hoon
new file mode 100644
index 0000000..2de84aa
--- /dev/null
+++ b/app/mar/contact/book.hoon
@@ -0,0 +1,14 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ book=book:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun book
+ ++ json (book:enjs:j book)
+ --
+++ grab
+ |%
+ ++ noun book:c
+ --
+--
diff --git a/app/mar/contact/changed-contacts.hoon b/app/mar/contact/changed-contacts.hoon
new file mode 100644
index 0000000..ddc5257
--- /dev/null
+++ b/app/mar/contact/changed-contacts.hoon
@@ -0,0 +1,21 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ dir=(map ship profile:c)
+++ grad %noun
+++ grow
+ |%
+ ++ noun dir
+ ++ json
+ %- pairs:enjs:format
+ %+ turn ~(tap by dir)
+ |= [who=ship pro=profile:c]
+ :- (scot %p who)
+ =+ (contact:enjs:j con.pro)
+ ?> ?=([%o *] -)
+ o+(~(put by p) 'since' s+(scot %da wen.pro))
+ --
+++ grab
+ |%
+ ++ noun (map ship profile:c)
+ --
+--
diff --git a/app/mar/contact/changed-pages.hoon b/app/mar/contact/changed-pages.hoon
new file mode 100644
index 0000000..619b92e
--- /dev/null
+++ b/app/mar/contact/changed-pages.hoon
@@ -0,0 +1,23 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ dir=(map kip:c page=[(unit contact:c) (unit contact:c)])
+++ grad %noun
+++ grow
+ |%
+ ++ noun dir
+ ++ json
+ %- pairs:enjs:format
+ %+ turn ~(tap by dir)
+ |= [=kip:c con=(unit contact:c) mod=(unit contact:c)]
+ ::NOTE shenanigans to extract just the string
+ :- ?@(kip (scot %p kip) =+((cid:enjs:j +.kip) ?>(?=([%s *] -) p)))
+ :- %a
+ :~ ?~(con ~ (contact:enjs:j u.con))
+ ?~(mod ~ (contact:enjs:j u.mod))
+ ==
+ --
+++ grab
+ |%
+ ++ noun (map kip:c (unit page:c))
+ --
+--
diff --git a/app/mar/contact/contact-0.hoon b/app/mar/contact/contact-0.hoon
new file mode 100644
index 0000000..4e355e8
--- /dev/null
+++ b/app/mar/contact/contact-0.hoon
@@ -0,0 +1,14 @@
+/- c=contacts, x=contacts-0
+/+ j=contacts-json-0
+|_ contact=contact-0:x
+++ grad %noun
+++ grow
+ |%
+ ++ noun contact
+ ++ json (contact:enjs:j contact)
+ --
+++ grab
+ |%
+ ++ noun contact-0:x
+ --
+--
diff --git a/app/mar/contact/contact-1.hoon b/app/mar/contact/contact-1.hoon
new file mode 100644
index 0000000..e75a43d
--- /dev/null
+++ b/app/mar/contact/contact-1.hoon
@@ -0,0 +1,16 @@
+/+ c=contacts
+/+ j=contacts-json-1
+|_ contact=contact:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun contact
+ ++ json (contact:enjs:j contact)
+ --
+++ grab
+ |%
+ ++ noun contact:c
+ ++ json contact:dejs:j
+ ++ contact contact:from-0:c
+ --
+--
diff --git a/app/mar/contact/contact.hoon b/app/mar/contact/contact.hoon
new file mode 100644
index 0000000..aa4bd1c
--- /dev/null
+++ b/app/mar/contact/contact.hoon
@@ -0,0 +1,3 @@
+/= contact-0 /mar/contact-0
+contact-0
+
diff --git a/app/mar/contact/directory-0.hoon b/app/mar/contact/directory-0.hoon
new file mode 100644
index 0000000..b7c399c
--- /dev/null
+++ b/app/mar/contact/directory-0.hoon
@@ -0,0 +1,14 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ dir=directory:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun dir
+ ++ json (directory:enjs:j dir)
+ --
+++ grab
+ |%
+ ++ noun directory:c
+ --
+--
diff --git a/app/mar/contact/directory.hoon b/app/mar/contact/directory.hoon
new file mode 100644
index 0000000..b7c399c
--- /dev/null
+++ b/app/mar/contact/directory.hoon
@@ -0,0 +1,14 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ dir=directory:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun dir
+ ++ json (directory:enjs:j dir)
+ --
+++ grab
+ |%
+ ++ noun directory:c
+ --
+--
diff --git a/app/mar/contact/news.hoon b/app/mar/contact/news.hoon
new file mode 100644
index 0000000..19f3bb3
--- /dev/null
+++ b/app/mar/contact/news.hoon
@@ -0,0 +1,14 @@
+/- c=contacts, x=contacts-0
+/+ j=contacts-json-0
+|_ news=news-0:x
+++ grad %noun
+++ grow
+ |%
+ ++ noun news
+ ++ json (news:enjs:j news)
+ --
+++ grab
+ |%
+ ++ noun news-0:x
+ --
+--
diff --git a/app/mar/contact/page-0.hoon b/app/mar/contact/page-0.hoon
new file mode 100644
index 0000000..ca62844
--- /dev/null
+++ b/app/mar/contact/page-0.hoon
@@ -0,0 +1,14 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ =page:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun page
+ ++ json (page:enjs:j page)
+ --
+++ grab
+ |%
+ ++ noun page:c
+ --
+--
diff --git a/app/mar/contact/page-1.hoon b/app/mar/contact/page-1.hoon
new file mode 100644
index 0000000..ca62844
--- /dev/null
+++ b/app/mar/contact/page-1.hoon
@@ -0,0 +1,14 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ =page:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun page
+ ++ json (page:enjs:j page)
+ --
+++ grab
+ |%
+ ++ noun page:c
+ --
+--
diff --git a/app/mar/contact/page.hoon b/app/mar/contact/page.hoon
new file mode 100644
index 0000000..ca62844
--- /dev/null
+++ b/app/mar/contact/page.hoon
@@ -0,0 +1,14 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ =page:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun page
+ ++ json (page:enjs:j page)
+ --
+++ grab
+ |%
+ ++ noun page:c
+ --
+--
diff --git a/app/mar/contact/response-0.hoon b/app/mar/contact/response-0.hoon
new file mode 100644
index 0000000..92c2968
--- /dev/null
+++ b/app/mar/contact/response-0.hoon
@@ -0,0 +1,14 @@
+/- c=contacts
+/+ j=contacts-json-1
+|_ =response:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun response
+ ++ json (response:enjs:j response)
+ --
+++ grab
+ |%
+ ++ noun response:c
+ --
+--
diff --git a/app/mar/contact/rolodex.hoon b/app/mar/contact/rolodex.hoon
new file mode 100644
index 0000000..4992264
--- /dev/null
+++ b/app/mar/contact/rolodex.hoon
@@ -0,0 +1,14 @@
+/- c=contacts, x=contacts-0
+/+ j=contacts-json-0
+|_ rol=rolodex:x
+++ grad %noun
+++ grow
+ |%
+ ++ noun rol
+ ++ json (rolodex:enjs:j rol)
+ --
+++ grab
+ |%
+ ++ noun rolodex:x
+ --
+--
diff --git a/app/mar/contact/update-0.hoon b/app/mar/contact/update-0.hoon
new file mode 100644
index 0000000..519391b
--- /dev/null
+++ b/app/mar/contact/update-0.hoon
@@ -0,0 +1,12 @@
+/- c=contacts, x=contacts-0
+|_ update=update-0:x
+++ grad %noun
+++ grow
+ |%
+ ++ noun update
+ --
+++ grab
+ |%
+ ++ noun update-0:x
+ --
+--
diff --git a/app/mar/contact/update-1.hoon b/app/mar/contact/update-1.hoon
new file mode 100644
index 0000000..f5d9fc5
--- /dev/null
+++ b/app/mar/contact/update-1.hoon
@@ -0,0 +1,12 @@
+/- c=contacts
+|_ update=update:c
+++ grad %noun
+++ grow
+ |%
+ ++ noun update
+ --
+++ grab
+ |%
+ ++ noun update:c
+ --
+--
diff --git a/app/mar/contact/update.hoon b/app/mar/contact/update.hoon
new file mode 100644
index 0000000..f143c73
--- /dev/null
+++ b/app/mar/contact/update.hoon
@@ -0,0 +1,2 @@
+/= mark /mar/contact/update-0
+mark
diff --git a/app/sur/contacts-0.hoon b/app/sur/contacts-0.hoon
new file mode 100644
index 0000000..ef34be2
--- /dev/null
+++ b/app/sur/contacts-0.hoon
@@ -0,0 +1,82 @@
+|%
++$ flag (pair ship term)
++$ saga
+ $~ [%lev ~]
+ $% [%dex ver=@ud]
+ [%lev ~]
+ [%chi ~]
+ ==
+
++$ contact-0
+ $: nickname=@t
+ bio=@t
+ status=@t
+ color=@ux
+ avatar=(unit @t)
+ cover=(unit @t)
+ groups=(set flag)
+ ==
+::
++$ foreign-0 [for=$@(~ profile-0) sag=$@(~ saga-0)]
++$ profile-0 [wen=@da con=$@(~ contact-0)]
++$ rolodex (map ship foreign-0)
+::
++$ saga-0
+ $@ $? %want :: subscribing
+ %fail :: %want failed
+ %lost :: epic %fail
+ ~ :: none intended
+ ==
+ saga
+::
++$ field-0
+ $% [%nickname nickname=@t]
+ [%bio bio=@t]
+ [%status status=@t]
+ [%color color=@ux]
+ [%avatar avatar=(unit @t)]
+ [%cover cover=(unit @t)]
+ [%add-group =flag]
+ [%del-group =flag]
+ ==
+::
++$ action-0
+ :: %anon: delete our profile
+ :: %edit: change our profile
+ :: %meet: track a peer
+ :: %heed: follow a peer
+ :: %drop: discard a peer
+ :: %snub: unfollow a peer
+ ::
+ $% [%anon ~]
+ [%edit p=(list field-0)]
+ [%meet p=(list ship)]
+ [%heed p=(list ship)]
+ [%drop p=(list ship)]
+ [%snub p=(list ship)]
+ ==
+:: network
+::
++$ update-0
+ $% [%full profile-0]
+ ==
+:: local
+::
++$ 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
+--
diff --git a/app/sur/contacts.hoon b/app/sur/contacts.hoon
new file mode 100644
index 0000000..a393cfc
--- /dev/null
+++ b/app/sur/contacts.hoon
@@ -0,0 +1,138 @@
+|%
+::
++| %compat
+::
+++ okay `epic`1
++$ flag (pair ship term)
++$ nest (pair term flag)
+::
++| %types
+:: $value-type: contact field value type
+::
++$ value-type
+ $? %text
+ %numb
+ %date
+ %tint
+ %ship
+ %look
+ %flag
+ %set
+ ==
+:: $value: contact field value
+::
++$ value
+ $+ contact-value
+ $@ ~
+ $% [%text p=@t]
+ [%numb p=@ud]
+ [%date p=@da]
+ ::
+ :: color
+ [%tint p=@ux]
+ [%ship p=ship]
+ ::
+ :: picture
+ [%look p=@ta]
+ ::
+ :: group
+ [%flag p=flag]
+ ::
+ :: uniform set
+ [%set p=$|((set value) unis)]
+ ==
+:: +unis: whether set is uniformly typed
+::
+++ unis
+ |= set=(set value)
+ ^- ?
+ ?~ set &
+ =/ typ -.n.set
+ |-
+ ?& =(typ -.n.set)
+ ?~(l.set & $(set l.set))
+ ?~(r.set & $(set r.set))
+ ==
+:: $contact: contact data
+::
++$ contact (map @tas value)
+:: $profile: contact profile
+::
+:: .wen: last updated
+:: .con: contact
+::
++$ profile [wen=@da con=contact]
+:: $foreign: foreign profile
+::
+:: .for: profile
+:: .sag: connection status
+::
++$ foreign [for=$@(~ profile) sag=saga]
+:: $page: contact page
+::
+:: .con: peer contact
+:: .mod: user overlay
+::
++$ page [con=contact mod=contact]
+:: $cid: contact page id
+::
++$ cid @uvF
+:: $kip: contact book key
+::
++$ kip $@(ship [%id cid])
+:: $book: contact book
+::
++$ book (map kip page)
+:: $directory: merged contacts
+::
++$ directory (map ship contact)
+:: $peers: network peers
+::
++$ peers (map ship foreign)
+::
++$ epic @ud
+::
++$ saga
+ $? %want :: subscribing
+ ~ :: none intended
+ ==
+:: %anon: delete our profile
+:: %self: edit our profile
+:: %page: create a new contact page
+:: %edit: edit a contact overlay
+:: %wipe: delete a contact page
+:: %meet: track a peer
+:: %drop: discard a peer
+:: %snub: unfollow a peer
+::
++$ action
+ $% [%anon ~]
+ [%self p=contact]
+ [%page p=kip q=contact]
+ [%edit p=kip q=contact]
+ [%wipe p=(list kip)]
+ [%meet p=(list ship)]
+ [%drop p=(list ship)]
+ [%snub p=(list ship)]
+ ==
+:: network update
+::
+:: %full: our profile
+::
++$ update
+ $% [%full profile]
+ ==
+:: $response: local update
+::
+:: %self: profile update
+:: %page: contact page update
+:: %wipe: contact page delete
+:: %peer: peer update
+::
++$ response
+ $% [%self con=contact]
+ [%page =kip con=contact mod=contact]
+ [%wipe =kip]
+ [%peer who=ship con=contact]
+ ==
+--
diff --git a/app/ted/sync.hoon b/app/ted/sync.hoon
index fbacef0..4770b56 100644
--- a/app/ted/sync.hoon
+++ b/app/ted/sync.hoon
@@ -12,16 +12,16 @@
=/ ureq (de-relay-do:de:jsonlib u.ujon)
?~ ureq ~& bad-json=(en:json:html u.ujon) bail
=/ req=relay-handling:ui u.ureq
- ~& req=req
+ ~& >> req=req
;< =bowl:spider bind:m get-bowl:strandio
+ ~& >> tid=tid.bowl
?. ?=(relay-get:ui action.req) bail
=/ t=ted:ui [%req tid.bowl relays.req action.req]
;< ~ bind:m (poke-our:strandio %nostrill %nostrill-ted !>(t))
;< v=vase bind:m (take-poke:strandio %nostrill-ted)
- ~& v=v
=/ res !<(ted:ui v)
- ~& sub-id-to-ted=res
+ ~& >>> sync-ted-sub-id-to-ted=res
?. ?=(%res -.res) bail
=/ j=json [%s sub-id.res]
(pure:m !>(j))
diff --git a/gui/devenv.lock b/gui/devenv.lock
index 63e15ea..4a8f02e 100644
--- a/gui/devenv.lock
+++ b/gui/devenv.lock
@@ -3,10 +3,11 @@
"devenv": {
"locked": {
"dir": "src/modules",
- "lastModified": 1770047045,
+ "lastModified": 1774816935,
+ "narHash": "sha256-MJ1kZGA/BZ9Xy+lsppMcw19jCf9zB9lmfwmCDhiC7Dg=",
"owner": "cachix",
"repo": "devenv",
- "rev": "5c0090a96b72ef4a1e1aec3e0abd30fc260062ac",
+ "rev": "a4cf85b989fff0bafe5858c879e5343493a86019",
"type": "github"
},
"original": {
@@ -16,88 +17,49 @@
"type": "github"
}
},
- "flake-compat": {
- "flake": false,
- "locked": {
- "lastModified": 1767039857,
- "owner": "NixOS",
- "repo": "flake-compat",
- "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "repo": "flake-compat",
- "type": "github"
- }
- },
- "git-hooks": {
+ "nixpkgs": {
"inputs": {
- "flake-compat": "flake-compat",
- "gitignore": "gitignore",
- "nixpkgs": [
- "nixpkgs"
- ]
+ "nixpkgs-src": "nixpkgs-src"
},
"locked": {
- "lastModified": 1769939035,
+ "lastModified": 1774287239,
+ "narHash": "sha256-W3krsWcDwYuA3gPWsFA24YAXxOFUL6iIlT6IknAoNSE=",
"owner": "cachix",
- "repo": "git-hooks.nix",
- "rev": "a8ca480175326551d6c4121498316261cbb5b260",
+ "repo": "devenv-nixpkgs",
+ "rev": "fa7125ea7f1ae5430010a6e071f68375a39bd24c",
"type": "github"
},
"original": {
"owner": "cachix",
- "repo": "git-hooks.nix",
- "type": "github"
- }
- },
- "gitignore": {
- "inputs": {
- "nixpkgs": [
- "git-hooks",
- "nixpkgs"
- ]
- },
- "locked": {
- "lastModified": 1762808025,
- "owner": "hercules-ci",
- "repo": "gitignore.nix",
- "rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c",
- "type": "github"
- },
- "original": {
- "owner": "hercules-ci",
- "repo": "gitignore.nix",
+ "ref": "rolling",
+ "repo": "devenv-nixpkgs",
"type": "github"
}
},
- "nixpkgs": {
+ "nixpkgs-src": {
+ "flake": false,
"locked": {
- "lastModified": 1767052823,
- "owner": "cachix",
- "repo": "devenv-nixpkgs",
- "rev": "538a5124359f0b3d466e1160378c87887e3b51a4",
+ "lastModified": 1773840656,
+ "narHash": "sha256-9tpvMGFteZnd3gRQZFlRCohVpqooygFuy9yjuyRL2C0=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "9cf7092bdd603554bd8b63c216e8943cf9b12512",
"type": "github"
},
"original": {
- "owner": "cachix",
- "ref": "rolling",
- "repo": "devenv-nixpkgs",
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
- "git-hooks": "git-hooks",
- "nixpkgs": "nixpkgs",
- "pre-commit-hooks": [
- "git-hooks"
- ]
+ "nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
-}
+}
\ No newline at end of file
diff --git a/gui/src/components/feed/Global.tsx b/gui/src/components/feed/Global.tsx
index 082ae7f..5e7de37 100644
--- a/gui/src/components/feed/Global.tsx
+++ b/gui/src/components/feed/Global.tsx
@@ -30,7 +30,7 @@ export default function Global() {
const relay = relayRef.current;
const req = relay.req([{ kinds: [667] }]);
for await (const msg of req) {
- console.log("relay msg", msg);
+ // console.log("relay msg", msg);
if (msg[0] === "EVENT") {
const event = msg[2];
const wevent = { ...event, relays: [GLOBAL_RELAY_URL] };
diff --git a/gui/src/components/nostr/Feed.tsx b/gui/src/components/nostr/Feed.tsx
index f2c93ea..b857469 100644
--- a/gui/src/components/nostr/Feed.tsx
+++ b/gui/src/components/nostr/Feed.tsx
@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
import Icon from "@/components/Icon";
import toast from "react-hot-toast";
import { Contact, RefreshCw } from "lucide-react";
+import type { RelayReqs, RelayStats } from "@/types/nostrill";
export default function Nostr() {
const { nostrFeed, api, relays, lastEose, setEose } = useLocalState((s) => ({
@@ -14,35 +15,61 @@ export default function Nostr() {
lastEose: s.lastEose,
setEose: s.setEose,
}));
- const [isSyncing, setIsSyncing] = useState(false);
- const [subId, setSubId] = useState("");
+ const [fetchingPosts, setFetchingPosts] = useState("");
+ const [fetchingProfiles, setFetchingProfiles] = useState("");
const refetch = () => nostrFeed;
+ console.log({
+ eose: lastEose,
+ posts: fetchingPosts,
+ profiles: fetchingProfiles,
+ });
useEffect(() => {
- if (!lastEose || !subId) return;
- if (lastEose === subId) {
- setIsSyncing(false);
- setEose("");
+ if (!lastEose) return;
+ if (lastEose === fetchingPosts) {
+ setFetchingPosts("");
toast.success("Nostr feed sync complete");
+ setEose("");
+ setFetchingProfiles("temp");
+ toast.loading("Nostr profile sync initiated");
}
- }, [lastEose, subId]);
+ if (lastEose === fetchingProfiles) {
+ setFetchingProfiles("");
+ setEose("");
+ toast.success("Nostr profiles sync complete");
+ }
+ }, [lastEose, fetchingPosts, fetchingProfiles]);
+
+ useEffect(() => {
+ const allReqs = Object.values(relays).reduce(
+ (acc: RelayReqs, item: RelayStats) => {
+ acc = Object.assign(acc, item.reqs);
+ return acc;
+ },
+ {},
+ );
+ for (const [subId, req] of Object.entries(allReqs)) {
+ if (req.name === "timeline" && !req.ongoing) setFetchingPosts(subId);
+ if (req.name === "user profiles fetch" && !req.ongoing)
+ setFetchingProfiles(subId);
+ }
+ }, [relays]);
const handleResync = async () => {
if (!api) return;
-
- // setIsSyncing(true);
+ toast.loading("Nostr post sync initiated");
+ setFetchingPosts("temp");
// TODO make this configurable
const rels = Object.values(relays).map((r) => r.wid);
try {
- const subId = await api.syncRelays(rels);
- setSubId(subId);
- console.log("syncrelays", subId);
- toast.success("Nostr feed sync initiated");
+ const res = await api.syncRelays(rels);
+ // const subId = await api.syncRelaysThread(rels);
+ // setSubId(subId);
+ // console.log("syncrelays", subId);
+ // toast.success("Nostr feed sync initiated");
} catch (error) {
toast.error("Failed to sync Nostr feed");
console.error("Sync error:", error);
- // } finally {
- // setIsSyncing(false);
}
};
@@ -50,16 +77,18 @@ export default function Nostr() {
if (!api) return;
const rels = Object.values(relays).map((r) => r.wid);
- setIsSyncing(true);
+ setFetchingProfiles("temp");
try {
const subId = await api.nostrProfiles(rels);
- setSubId(subId);
- toast.success("Nostr profile sync initiated");
+ setFetchingProfiles(subId);
+ toast.loading("Nostr profile sync initiated");
} catch (error) {
toast.error("Failed to sync Nostr feed");
console.error("Sync error:", error);
}
}
+ console.log({ nostrFeed });
+ console.log({ relays });
if (Object.keys(relays).length === 0)
return (
@@ -94,10 +123,10 @@ export default function Nostr() {