summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-11-12 07:11:07 +0700
committerpolwex <polwex@sortug.com>2025-11-12 07:11:07 +0700
commit284ce9ce7d9f81e54e91f917329d48926487fbf4 (patch)
tree7a156986323fd799e1457c8b7663806e32b2af7d
parent7305d67ff7f9e78b73326ef0e1f68a9613d34205 (diff)
fixes to engagement handling
-rw-r--r--.gitignore2
-rw-r--r--app/app/nostrill.hoon16
-rw-r--r--app/lib/bip/b173.hoon145
-rw-r--r--app/lib/bitcoin-utils.hoon177
-rw-r--r--app/lib/json/nostrill.hoon14
-rw-r--r--app/lib/mutations/trill.hoon51
-rw-r--r--app/lib/nostr/client.hoon6
-rw-r--r--app/lib/nostrill.hoon3
-rw-r--r--app/lib/nostrill/comms.hoon62
-rw-r--r--app/lib/nostrill/follows.hoon3
-rw-r--r--app/lib/trill/feed.hoon31
-rw-r--r--app/mar/txt.hoon274
-rw-r--r--app/sur/bitcoin.hoon84
-rw-r--r--app/sur/nostrill.hoon11
-rw-r--r--app/sur/nostrill/comms.hoon11
-rw-r--r--arvo/iris.hoon3
-rw-r--r--gui/src/components/feed/PostList.tsx2
-rw-r--r--gui/src/components/post/Footer.tsx3
-rw-r--r--gui/src/components/post/Quote.tsx3
-rw-r--r--gui/src/logic/trill/helpers.ts24
-rw-r--r--gui/src/pages/Feed.tsx17
-rw-r--r--gui/src/pages/Thread.tsx52
-rw-r--r--gui/src/state/state.ts6
23 files changed, 960 insertions, 40 deletions
diff --git a/.gitignore b/.gitignore
index aece2ac..5366ec3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,5 +5,5 @@ AGENTS.md
CLAUDE.md
NOTES.md
run.sh
-pier
+piers
urbit
diff --git a/app/app/nostrill.hoon b/app/app/nostrill.hoon
index 15a20a3..52cb8bb 100644
--- a/app/app/nostrill.hoon
+++ b/app/app/nostrill.hoon
@@ -1,6 +1,7 @@
/- sur=nostrill, nsur=nostr, tf=trill-feed, comms=nostrill-comms
/+ lib=nostrill, nostr-keys, sr=sortug, scri,
ws=websockets,
+ bip-b173,
nreq=nostr-req,
nostr-client,
dbug,
@@ -140,6 +141,7 @@
?- -.pok
%req (handle-req:coms +.pok)
%res (handle-res:coms +.pok)
+ %eng (handle-eng:coms +.pok)
==
[cs this]
:: DEPRECATED
@@ -281,6 +283,10 @@
=/ key i.ks
~& pub=(scow:sr %ux -.key)
~& priv=(scow:sr %ux +.key)
+ =/ npub (encode-pubkey:bip-b173 %main [33 -.key])
+ ~& npub=npub
+ :: =/ nsec
+ :: ~& nsec=nsec
$(ks t.ks)
%feed
:: =/ lol debug-own-feed:mutat
@@ -357,6 +363,16 @@
:_ this =/ subs ~(tap by sup.bowl)
%+ turn subs |= [* p=@p pat=path]
[%give %kick ~[pat] ~]
+ %leave
+ :_ this =/ subs ~(tap by wex.bowl)
+ %+ turn subs |= [[wire sip=@p term] q=*]
+ (urbit-leave:fols sip)
+ %comms
+ :_ this
+ :~ (urbit-watch:fols ~zod)
+ [%pass /foldbug %agent [~zod dap.bowl] %poke %bitch !>(~)]
+ ==
+
==
diff --git a/app/lib/bip/b173.hoon b/app/lib/bip/b173.hoon
new file mode 100644
index 0000000..e554597
--- /dev/null
+++ b/app/lib/bip/b173.hoon
@@ -0,0 +1,145 @@
+:: BIP173: Bech32 Addresses
+:: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
+::
+:: Heavily copies:
+:: https://github.com/bitcoinjs/bech32/blob/master/index.js
+::
+:: TODO not really working at generating npub/nsec keys from hex
+/- sur=bitcoin
+/+ bcu=bitcoin-utils
+=, sur
+=, bcu
+|%
+++ prefixes
+ ^- (map network tape)
+ (my [[%main "bc"] [%testnet "tb"] [%regtest "bcrt"] ~])
+++ charset "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
++$ raw-decoded [hrp=tape data=(list @) checksum=(list @)]
+:: below is a port of: https://github.com/bitcoinjs/bech32/blob/master/index.js
+::
+++ polymod
+ |= values=(list @)
+ |^ ^- @
+ =/ gen=(list @ux)
+ ~[0x3b6a.57b2 0x2650.8e6d 0x1ea1.19fa 0x3d42.33dd 0x2a14.62b3]
+ =/ chk=@ 1
+ |- ?~ values chk
+ =/ top (rsh [0 25] chk)
+ =. chk
+ (mix i.values (lsh [0 5] (dis chk 0x1ff.ffff)))
+ $(values t.values, chk (update-chk chk top gen))
+::
+ ++ update-chk
+ |= [chk=@ top=@ gen=(list @ux)]
+ =/ is (gulf 0 4)
+ |- ?~ is chk
+ ?: =(1 (dis 1 (rsh [0 i.is] top)))
+ $(is t.is, chk (mix chk (snag i.is gen)))
+ $(is t.is)
+ --
+::
+++ expand-hrp
+ |= hrp=tape
+ ^- (list @)
+ =/ front (turn hrp |=(p=@tD (rsh [0 5] p)))
+ =/ back (turn hrp |=(p=@tD (dis 31 p)))
+ (zing ~[front ~[0] back])
+::
+++ verify-checksum
+ |= [hrp=tape data-and-checksum=(list @)]
+ ^- ?
+ %- |=(a=@ =(1 a))
+ %- polymod
+ (weld (expand-hrp hrp) data-and-checksum)
+::
+++ checksum
+ |= [hrp=tape data=(list @)]
+ ^- (list @)
+ :: xor 1 with the polymod
+ ::
+ =/ pmod=@
+ %+ mix 1
+ %- polymod
+ (zing ~[(expand-hrp hrp) data (reap 6 0)])
+ %+ turn (gulf 0 5)
+ |=(i=@ (dis 31 (rsh [0 (mul 5 (sub 5 i))] pmod)))
+::
+++ charset-to-value
+ |= c=@tD
+ ^- (unit @)
+ (find ~[c] charset)
+++ value-to-charset
+ |= value=@
+ ^- (unit @tD)
+ ?: (gth value 31) ~
+ `(snag value charset)
+::
+++ is-valid
+ |= [bech=tape last-1-pos=@] ^- ?
+ ?& ?|(=((cass bech) bech) =((cuss bech) bech)) :: to upper or to lower is same as bech
+ (gte last-1-pos 1)
+ (lte (add last-1-pos 7) (lent bech))
+ (lte (lent bech) 90)
+ (levy bech |=(c=@tD (gte c 33)))
+ (levy bech |=(c=@tD (lte c 126)))
+ ==
+:: data should be 5bit words
+::
+++ encode-raw
+ |= [hrp=tape data=(list @)]
+ ^- cord
+ =/ combined=(list @)
+ (weld data (checksum hrp data))
+ %- crip
+ (zing ~[hrp "1" (tape (murn combined value-to-charset))])
+++ decode-raw
+ |= body=cord
+ ^- (unit raw-decoded)
+ =/ bech (cass (trip body)) :: to lowercase
+ =/ pos (flop (fand "1" bech))
+ ?~ pos ~
+ =/ last-1=@ i.pos
+ ?. (is-valid bech last-1) :: check bech32 validity (not segwit validity or checksum)
+ ~
+ =/ hrp (scag last-1 bech)
+ =/ encoded-data-and-checksum=(list @)
+ (slag +(last-1) bech)
+ =/ data-and-checksum=(list @)
+ %+ murn encoded-data-and-checksum
+ charset-to-value
+ ?. =((lent encoded-data-and-checksum) (lent data-and-checksum)) :: ensure all were in CHARSET
+ ~
+ ?. (verify-checksum hrp data-and-checksum)
+ ~
+ =/ checksum-pos (sub (lent data-and-checksum) 6)
+ `[hrp (scag checksum-pos data-and-checksum) (slag checksum-pos data-and-checksum)]
+:: +from-address: BIP173 bech32 address encoding to hex
+:: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
+:: expects to drop a leading 5-bit 0 (the witness version)
+::
+++ from-address
+ |= body=cord
+ ^- hexb
+ ~| "Invalid bech32 address"
+ =/ d=(unit raw-decoded) (decode-raw body)
+ ?> ?=(^ d)
+ =/ bs=bits (from-atoms:bit 5 data.u.d)
+ =/ byt-len=@ (div (sub wid.bs 5) 8)
+ ?> =(5^0b0 (take:bit 5 bs))
+ ?> ?| =(20 byt-len)
+ =(32 byt-len)
+ ==
+ [byt-len `@ux`dat:(take:bit (mul 8 byt-len) (drop:bit 5 bs))]
+:: pubkey is the 33 byte ECC compressed public key
+::
+++ encode-pubkey
+ |= [=network pubkey=byts]
+ ^- (unit cord)
+ ?. =(33 wid.pubkey)
+ ~|('pubkey must be a 33 byte ECC compressed public key' !!)
+ =/ prefix (~(get by prefixes) network)
+ ?~ prefix ~
+ :- ~
+ %+ encode-raw u.prefix
+ [0v0 (to-atoms:bit 5 [160 `@ub`dat:(hash-160 pubkey)])]
+--
diff --git a/app/lib/bitcoin-utils.hoon b/app/lib/bitcoin-utils.hoon
new file mode 100644
index 0000000..a23354c
--- /dev/null
+++ b/app/lib/bitcoin-utils.hoon
@@ -0,0 +1,177 @@
+:: lib/bitcoin-utils.hoon
+:: Utilities for working with BTC data types and transactions
+::
+/- *bitcoin
+~% %bitcoin-utils-lib ..part ~
+|%
+::
+:: TODO: move this bit/byt stuff to zuse
+:: bit/byte utilities
+::
+::
+:: +blop: munge bit and byt sequences (cat, flip, take, drop)
+::
+++ blop
+ ~/ %blop
+ |_ =bloq
+ +$ biyts [wid=@ud dat=@]
+ ++ cat
+ |= bs=(list biyts)
+ ^- biyts
+ :- (roll (turn bs |=(b=biyts -.b)) add)
+ (can bloq (flop bs))
+ :: +flip: flip endianness while preserving lead/trail zeroes
+ ::
+ ++ flip
+ |= b=biyts
+ ^- biyts
+ [wid.b (rev bloq b)]
+ :: +take: take n bloqs from front
+ :: pads front with extra zeroes if n is longer than input
+ ::
+ ++ take
+ |= [n=@ b=biyts]
+ ^- biyts
+ ?: (gth n wid.b)
+ [n dat.b]
+ [n (rsh [bloq (sub wid.b n)] dat.b)]
+ :: +drop: drop n bloqs from front
+ :: returns 0^0 if n >= width
+ ::
+ ++ drop
+ |= [n=@ b=biyts]
+ ^- biyts
+ ?: (gte n wid.b)
+ 0^0x0
+ =+ n-take=(sub wid.b n)
+ [n-take (end [bloq n-take] dat.b)]
+ --
+++ byt ~(. blop 3)
+::
+++ bit
+ ~/ %bit
+ =/ bl ~(. blop 0)
+ |%
+ ++ cat cat:bl:bit
+ ++ flip flip:bl:bit
+ ++ take take:bl:bit
+ ++ drop drop:bl:bit
+ ++ from-atoms
+ |= [bitwidth=@ digits=(list @)]
+ ^- bits
+ %- cat:bit
+ %+ turn digits
+ |= a=@
+ ?> (lte (met 0 a) bitwidth)
+ [bitwidth `@ub`a]
+ :: +to-atoms: convert bits to atoms of bitwidth
+ ::
+ ++ to-atoms
+ |= [bitwidth=@ bs=bits]
+ ^- (list @)
+ =| res=(list @)
+ ?> =(0 (mod wid.bs bitwidth))
+ |-
+ ?: =(0 wid.bs) res
+ %= $
+ res (snoc res dat:(take:bit bitwidth bs))
+ bs (drop:bit bitwidth bs)
+ ==
+ --
+:: big endian sha256: input and output are both MSB first (big endian)
+::
+++ sha256
+ ~/ %sha256
+ |= =byts
+ ^- hexb
+ %- flip:byt
+ [32 (shay (flip:byt byts))]
+::
+++ dsha256
+ ~/ %dsha256
+ |= =byts
+ (sha256 (sha256 byts))
+::
+++ hash-160
+ ~/ %hash-160
+ |= val=byts
+ ^- hexb
+ =, ripemd:crypto
+ :- 20
+ %- ripemd-160
+ (sha256 val)
+
+::
+:: hxb: hex parsing utilities
+::
+++ hxb
+ ~% %hxb ..blop ~
+ |%
+ ++ from-cord
+ ~/ %from-cord
+ |= h=@t
+ ^- hexb
+ ?: =('' h) 1^0x0
+ :: Add leading 00
+ ::
+ =+ (lsh [3 2] h)
+ :: Group by 4-size block
+ ::
+ =+ (rsh [3 2] -)
+ :: Parse hex to atom
+ ::
+ =/ a (need (de:base16:mimes:html -))
+ [-.a `@ux`+.a]
+ ::
+ ++ to-cord
+ ~/ %to-cord
+ |= =hexb
+ ^- cord
+ (en:base16:mimes:html hexb)
+ --
+::
+:: +csiz: CompactSize integers (a Bitcoin-specific datatype)
+:: https://btcinformation.org/en/developer-reference#compactsize-unsigned-integers
+:: - encode: big endian to little endian
+:: - decode: little endian to big endian
+::
+++ csiz
+ ~% %csiz ..blop ~
+ |%
+ ++ en
+ ~/ %en
+ |= a=@
+ ^- hexb
+ =/ l=@ (met 3 a)
+ ?: =(l 0) 1^a
+ ?: =(l 1) 1^a
+ ?: =(l 2) (cat:byt ~[1^0xfd (flip:byt 2^a)])
+ ?: (lte l 4) (cat:byt ~[1^0xfe (flip:byt 4^a)])
+ ?: (lte l 8) (cat:byt ~[1^0xff (flip:byt 8^a)])
+ ~|("Cannot encode CompactSize longer than 8 bytes" !!)
+ ::
+ ++ de
+ ~/ %de
+ |= h=hexb
+ ^- [n=hexb rest=hexb]
+ =/ s=@ux dat:(take:byt 1 h)
+ ?: (lth s 0xfd) [1^s (drop:byt 1 h)]
+ ~| "Invalid compact-size at start of {<h>}"
+ =/ len=bloq
+ ?+ s !!
+ %0xfd 1
+ %0xfe 2
+ %0xff 3
+ ==
+ :_ (drop:byt (add 1 (bex len)) h)
+ %- flip:byt
+ (take:byt (bex len) (drop:byt 1 h))
+ :: +dea: atom instead of hexb for parsed CompactSize
+ ::
+ ++ dea
+ |= h=hexb
+ ^- [a=@ rest=hexb]
+ => (de h)
+ [dat.n rest]
+ --
+--
diff --git a/app/lib/json/nostrill.hoon b/app/lib/json/nostrill.hoon
index b07de61..266a6c8 100644
--- a/app/lib/json/nostrill.hoon
+++ b/app/lib/json/nostrill.hoon
@@ -15,6 +15,7 @@
feed+(feed-with-cursor:en:trill feed ~ ~)
nostr+(en-nostr-feed nostr-feed)
following+(enfollowing following)
+ following2+(feed-with-cursor:en:trill following2 ~ ~)
['followGraph' (engraph follow-graph)]
~
==
@@ -190,9 +191,10 @@
add+postadd
reply+reply
quote+quote
- rp+rp
+ rp+pid
:: rt+de-rt
- :: del+hex:de:common
+ reaction+reaction
+ del+pid
==
++ postadd
%- ot :~
@@ -211,11 +213,17 @@
host+(se:de:common %p)
id+de-atom-id
==
-++ rp
+++ pid
%- ot :~
host+(se:de:common %p)
id+de-atom-id
==
+++ reaction
+ %- ot :~
+ host+(se:de:common %p)
+ id+de-atom-id
+ reaction+so
+ ==
++ rt
%- ot :~
id+hex:de:common
diff --git a/app/lib/mutations/trill.hoon b/app/lib/mutations/trill.hoon
index 40cce1e..cfedecf 100644
--- a/app/lib/mutations/trill.hoon
+++ b/app/lib/mutations/trill.hoon
@@ -3,6 +3,7 @@
/+ appjs=json-nostrill,
lib=nostrill,
+ trill-feed,
trill=trill-post,
njs=json-nostr,
postlib=trill-post,
@@ -22,11 +23,26 @@
++ add-to-feed |= p=post:post
=. feed.state (put:orm:feed feed.state id.p p)
state
+++ headsup-poke
+ |= [poke=post-poke:ui:sur p=post:post] ^- (unit engagement:comms)
+ ?- -.poke
+ %add ~
+ ::: TODO del-reply
+ %del ~
+ %quote `[%quote id.poke p]
+ %reply `[%reply id.poke p]
+ %rp `[%rp id.poke id.p]
+ %reaction `[%reaction id.poke reaction.poke]
+ ==
++ handle-post |= poke=post-poke:ui:sur
^- (quip card _state)
=/ profile (~(get by profiles.state) [%urbit our.bowl])
=/ pubkey pub.i.keys.state
+ ?: ?=(%del -.poke)
+ =. feed.state =< + (del:orm:feed feed.state id.poke)
+ :: TODO
+ `state
=/ p=post:post
?- -.poke
%add
@@ -45,23 +61,36 @@
=/ sp (build-sp:trill host.poke our.bowl '' ~ ~)
=. contents.sp ~[quote]
(build-post:trill now.bowl pubkey sp)
+ %reaction
+ =/ p (got:orm:feed feed.state id.poke)
+ =. reacts.engagement.p %+ ~(put by reacts.engagement.p)
+ our.bowl [reaction.poke *signature:post]
+ p
==
+ =. state (add-to-feed p)
=/ pw [p (some pubkey) ~ ~ profile]
=/ jfact=fact:ui:sur [%post %add pw]
=/ ui-card (update-ui:cards:lib jfact)
- :: only update followers when we are updating our own feed
- ?. .=(our.bowl host.p) [~[ui-card] state]
- =. state (add-to-feed p)
- =/ =fact:comms [%post %add p]
- =/ fact-card (update-followers:cards:lib fact)
+ =/ crds ~(. cards:lib bowl)
+ =/ engagement-poke (headsup-poke poke p)
+ =/ base-cards
+ ?~ engagement-poke :~(ui-card)
+ =/ poke [%eng u.engagement-poke]
+ =/ eng-card (poke-host:crds host.p poke)
+ :~(ui-card eng-card)
+ :: if our own post we update followers, if someone elses post we send an engagement poke
:_ state
- :~ ui-card
- fact-card
- ==
-
+ ?: .=(our.bowl host.p)
+ ::
+ =/ =fact:comms [%post %add p]
+ =/ fact-card (update-followers:cards:lib fact)
+ :- fact-card base-cards
+ ::
+ base-cards
++ handle-post-fact |= pf=post-fact:comms
^- (quip card _state)
+ ~& handle-post-fact=pf
=/ =user:sur [%urbit src.bowl]
=/ fed (~(get by following.state) user)
?~ fed ~& "emmm not following ya" `state
@@ -72,6 +101,10 @@
:: =/ =user:nsur [%urbit host.p.pdf]
(put:orm:feed u.fed id.p.pf p.pf)
=. following.state (~(put by following.state) user nf)
+ =. following2.state
+ ?: ?=(%del -.pf)
+ =< + (del:orm:feed following2.state id.pf)
+ (insert-to-global:trill-feed nf p.pf)
:: TODO update the ui with the changes
:_ state ~
--
diff --git a/app/lib/nostr/client.hoon b/app/lib/nostr/client.hoon
index 2438a47..c258258 100644
--- a/app/lib/nostr/client.hoon
+++ b/app/lib/nostr/client.hoon
@@ -48,9 +48,9 @@
++ get-posts
^- (quip card _state)
=/ kinds (silt ~[1])
- =/ since (sub now.bowl ~m1)
+ =/ last-week (sub now.bowl ~d1)
:: =/ since (to-unix-secs:jikan:sr last-week)
- =/ =filter:nsur [~ ~ `kinds ~ `since ~ ~]
+ =/ =filter:nsur [~ ~ `kinds ~ `last-week ~ ~]
(send-req ~[filter])
++ get-user-feed
@@ -92,7 +92,7 @@
++ test-connection
|= relay-url=@t
=/ kinds (silt ~[1])
- =/ since (sub now.bowl ~m1)
+ =/ since (sub now.bowl ~m10)
=/ =filter:nsur [~ ~ `kinds ~ `since ~ ~]
=/ sub-id (gen-sub-id:nostr-keys eny.bowl)
=/ req=client-msg:nsur [%req sub-id ~[filter]]
diff --git a/app/lib/nostrill.hoon b/app/lib/nostrill.hoon
index d46d090..1f5db40 100644
--- a/app/lib/nostrill.hoon
+++ b/app/lib/nostrill.hoon
@@ -55,5 +55,8 @@
:: ++ update-followers |= =fact:comms ^- card:agent:gall
++ update-followers |= =fact:comms ^- card:agent:gall
[%give %fact ~[/follow] %noun !>(fact)]
+ ::
+ ++ poke-host |= [sip=@p =poke:comms] ^- card:agent:gall
+ [%pass /heads-up %agent [sip dap.bowl] %poke %noun !>(poke)]
--
--
diff --git a/app/lib/nostrill/comms.hoon b/app/lib/nostrill/comms.hoon
index 5f9b0a9..a386c0e 100644
--- a/app/lib/nostrill/comms.hoon
+++ b/app/lib/nostrill/comms.hoon
@@ -1,5 +1,5 @@
-/- sur=nostrill, nsur=nostr, comms=nostrill-comms, feed=trill-feed
-/+ js=json-nostr, sr=sortug,constants, gatelib=trill-gate, feedlib=trill-feed, jsonlib=json-nostrill
+/- sur=nostrill, nsur=nostr, comms=nostrill-comms, feed=trill-feed, post=trill-post
+/+ js=json-nostr, sr=sortug,constants, gatelib=trill-gate, feedlib=trill-feed, jsonlib=json-nostrill, lib=nostrill
|_ [=state:sur =bowl:gall]
++ cast-poke
|= raw=* ^- poke:comms
@@ -87,6 +87,64 @@
=/ c1 [%give %fact paths cage]
:~(c1)
+:: engagement pokes, heads up when replying etc. to a post on your feed
+++ handle-eng
+ |= e=engagement:comms
+ ?- -.e
+ %reply
+ =/ poast (get:orm:feed feed.state parent.e)
+ ?~ poast `state
+ =. children.u.poast (~(put in children.u.poast) id.child.e)
+ =. feed.state (put:orm:feed feed.state parent.e u.poast)
+ =. feed.state (put:orm:feed feed.state id.child.e child.e)
+ =/ f=fact:comms [%post %add child.e]
+ =/ f2=fact:comms [%post %changes u.poast]
+ :_ state
+ :~ (update-followers:cards:lib f)
+ (update-followers:cards:lib f2)
+ ==
+ %del-reply
+ =. feed.state =< + (del:orm:feed feed.state child.e)
+ =/ poast (get:orm:feed feed.state parent.e)
+ ?~ poast `state
+ =. children.u.poast (~(del in children.u.poast) child.e)
+ =. feed.state (put:orm:feed feed.state parent.e u.poast)
+ :_ state
+ :~ (update-followers:cards:lib [%post %changes u.poast])
+ (update-followers:cards:lib [%post %del child.e])
+ ==
+ :: TODO ideally we want the full quote to display it within the post engagement. So do we change quoted.engagement.post? What if the quoter edits the quote down the line, etc.
+ %quote
+ =/ poast (get:orm:feed feed.state src.e)
+ ?~ poast `state
+ =/ spid [*signature:post src.bowl id.post.e]
+ =. quoted.engagement.u.poast (~(put in quoted.engagement.u.poast) spid)
+ =. feed.state (put:orm:feed feed.state src.e u.poast)
+ =/ f=fact:comms [%post %changes u.poast]
+ :_ state
+ :~ (update-followers:cards:lib f)
+ ==
+ %rp
+ =/ poast (get:orm:feed feed.state src.e)
+ ?~ poast `state
+ =/ spid [*signature:post src.bowl rt.e]
+ =. shared.engagement.u.poast (~(put in shared.engagement.u.poast) spid)
+ =. feed.state (put:orm:feed feed.state src.e u.poast)
+ =/ f=fact:comms [%post %changes u.poast]
+ :_ state
+ :~ (update-followers:cards:lib f)
+ ==
+ %reaction
+ =/ poast (get:orm:feed feed.state post.e)
+ ?~ poast `state
+ :: TODO signatures et al.
+ =. reacts.engagement.u.poast (~(put by reacts.engagement.u.poast) src.bowl [reaction.e *signature:post])
+ =. feed.state (put:orm:feed feed.state post.e u.poast)
+ =/ f=fact:comms [%post %changes u.poast]
+ :_ state
+ :~ (update-followers:cards:lib f)
+ ==
+ ==
--
diff --git a/app/lib/nostrill/follows.hoon b/app/lib/nostrill/follows.hoon
index 34c1d19..942a876 100644
--- a/app/lib/nostrill/follows.hoon
+++ b/app/lib/nostrill/follows.hoon
@@ -48,7 +48,8 @@
++ handle-follow-ok |= [=user:sur =fc:feed profile=(unit user-meta:nsur)]
^- (quip card:agent:gall _state)
- =. following.state (~(put by following.state) user feed.fc)
+ =. following.state (~(put by following.state) user feed.fc)
+ =. following2.state (add-new-feed:feedlib following2.state feed.fc)
=/ graph (~(get by follow-graph.state) [%urbit our.bowl])
=/ follows ?~ graph (silt ~[user]) (~(put in u.graph) user)
=. follow-graph.state (~(put by follow-graph.state) [%urbit our.bowl] follows)
diff --git a/app/lib/trill/feed.hoon b/app/lib/trill/feed.hoon
index 721a596..31fcd64 100644
--- a/app/lib/trill/feed.hoon
+++ b/app/lib/trill/feed.hoon
@@ -67,4 +67,35 @@
(put:form:post acc id full-node)
?~ children [%empty ~]
:- %full g
+::
+++ add-new-feed
+|= [global=feed:feed new=feed:feed] ^- feed:feed
+ =/ poasts (tap:orm:feed new)
+ |- ?~ poasts global
+ =/ poast +.i.poasts
+ =. global (insert-to-global global poast)
+ $(poasts t.poasts)
+
+++ consolidate-feeds
+|= feeds=(list [* feed:feed]) ^- feed:feed
+ =| nf=feed:feed
+ |- ?~ feeds nf
+ =/ poasts (tap:orm:feed +.i.feeds)
+ =. nf |- ?~ poasts nf
+ =/ poast +.i.poasts
+ =. nf (insert-to-global nf poast)
+ $(poasts t.poasts)
+ $(feeds t.feeds)
+
+++ find-available-id
+=| tries=@ud
+|= [f=feed:feed id=@da] ^- @da
+ ?: (gte tries 20) ~|('find-available-id stack overflow' !!)
+ ?. (has:orm:feed f id) id
+ $(id +(id), tries +(tries))
+
+++ insert-to-global
+|= [f=feed:feed p=post:post] ^- feed:feed
+ =/ nid (find-available-id f id.p)
+ (put:orm:feed f nid p)
--
diff --git a/app/mar/txt.hoon b/app/mar/txt.hoon
new file mode 100644
index 0000000..982dce9
--- /dev/null
+++ b/app/mar/txt.hoon
@@ -0,0 +1,274 @@
+::
+:::: /hoon/txt/mar
+ ::
+/? 310
+::
+=, clay
+=, differ
+=, format
+=, mimes:html
+|_ txt=wain
+::
+++ grab :: convert from
+ |%
+ ++ mime |=((pair mite octs) (to-wain q.q))
+ ++ noun wain :: clam from %noun
+ --
+++ grow
+ => v=.
+ |%
+ ++ mime => v [/text/plain (as-octs (of-wain txt))]
+ --
+++ grad
+ |%
+ ++ form %txt-diff
+ ++ diff
+ |= tyt=wain
+ ^- (urge cord)
+ (lusk txt tyt (loss txt tyt))
+ ::
+ ++ pact
+ |= dif=(urge cord)
+ ~| [%pacting dif]
+ ^- wain
+ (lurk txt dif)
+ ::
+ ++ join
+ |= [ali=(urge cord) bob=(urge cord)]
+ ^- (unit (urge cord))
+ |^
+ =. ali (clean ali)
+ =. bob (clean bob)
+ |- ^- (unit (urge cord))
+ ?~ ali `bob
+ ?~ bob `ali
+ ?- -.i.ali
+ %&
+ ?- -.i.bob
+ %&
+ ?: =(p.i.ali p.i.bob)
+ %+ bind $(ali t.ali, bob t.bob)
+ |=(cud=(urge cord) [i.ali cud])
+ ?: (gth p.i.ali p.i.bob)
+ %+ bind $(p.i.ali (sub p.i.ali p.i.bob), bob t.bob)
+ |=(cud=(urge cord) [i.bob cud])
+ %+ bind $(ali t.ali, p.i.bob (sub p.i.bob p.i.ali))
+ |=(cud=(urge cord) [i.ali cud])
+ ::
+ %|
+ ?: =(p.i.ali (lent p.i.bob))
+ %+ bind $(ali t.ali, bob t.bob)
+ |=(cud=(urge cord) [i.bob cud])
+ ?: (gth p.i.ali (lent p.i.bob))
+ %+ bind $(p.i.ali (sub p.i.ali (lent p.i.bob)), bob t.bob)
+ |=(cud=(urge cord) [i.bob cud])
+ ~
+ ==
+ ::
+ %|
+ ?- -.i.bob
+ %|
+ ?. =(i.ali i.bob)
+ ~
+ %+ bind $(ali t.ali, bob t.bob)
+ |=(cud=(urge cord) [i.ali cud])
+ ::
+ %&
+ ?: =(p.i.bob (lent p.i.ali))
+ %+ bind $(ali t.ali, bob t.bob)
+ |=(cud=(urge cord) [i.ali cud])
+ ?: (gth p.i.bob (lent p.i.ali))
+ %+ bind $(ali t.ali, p.i.bob (sub p.i.bob (lent p.i.ali)))
+ |=(cud=(urge cord) [i.ali cud])
+ ~
+ ==
+ ==
+ ++ clean :: clean
+ |= wig=(urge cord)
+ ^- (urge cord)
+ ?~ wig ~
+ ?~ t.wig wig
+ ?: ?=(%& -.i.wig)
+ ?: ?=(%& -.i.t.wig)
+ $(wig [[%& (add p.i.wig p.i.t.wig)] t.t.wig])
+ [i.wig $(wig t.wig)]
+ ?: ?=(%| -.i.t.wig)
+ $(wig [[%| (welp p.i.wig p.i.t.wig) (welp q.i.wig q.i.t.wig)] t.t.wig])
+ [i.wig $(wig t.wig)]
+ --
+ ::
+ ++ mash
+ |= $: [als=ship ald=desk ali=(urge cord)]
+ [bos=ship bod=desk bob=(urge cord)]
+ ==
+ ^- (urge cord)
+ |^
+ =. ali (clean ali)
+ =. bob (clean bob)
+ |- ^- (urge cord)
+ ?~ ali bob
+ ?~ bob ali
+ ?- -.i.ali
+ %&
+ ?- -.i.bob
+ %&
+ ?: =(p.i.ali p.i.bob)
+ [i.ali $(ali t.ali, bob t.bob)]
+ ?: (gth p.i.ali p.i.bob)
+ [i.bob $(p.i.ali (sub p.i.ali p.i.bob), bob t.bob)]
+ [i.ali $(ali t.ali, p.i.bob (sub p.i.bob p.i.ali))]
+ ::
+ %|
+ ?: =(p.i.ali (lent p.i.bob))
+ [i.bob $(ali t.ali, bob t.bob)]
+ ?: (gth p.i.ali (lent p.i.bob))
+ [i.bob $(p.i.ali (sub p.i.ali (lent p.i.bob)), bob t.bob)]
+ =/ [fic=(unce cord) ali=(urge cord) bob=(urge cord)]
+ (resolve ali bob)
+ [fic $(ali ali, bob bob)]
+ :: ~ :: here, alice is good for a while, but not for the whole
+ == :: length of bob's changes
+ ::
+ %|
+ ?- -.i.bob
+ %|
+ =/ [fic=(unce cord) ali=(urge cord) bob=(urge cord)]
+ (resolve ali bob)
+ [fic $(ali ali, bob bob)]
+ ::
+ %&
+ ?: =(p.i.bob (lent p.i.ali))
+ [i.ali $(ali t.ali, bob t.bob)]
+ ?: (gth p.i.bob (lent p.i.ali))
+ [i.ali $(ali t.ali, p.i.bob (sub p.i.bob (lent p.i.ali)))]
+ =/ [fic=(unce cord) ali=(urge cord) bob=(urge cord)]
+ (resolve ali bob)
+ [fic $(ali ali, bob bob)]
+ ==
+ ==
+ ::
+ ++ annotate :: annotate conflict
+ |= $: ali=(list @t)
+ bob=(list @t)
+ bas=(list @t)
+ ==
+ ^- (list @t)
+ %- zing
+ ^- (list (list @t))
+ %- flop
+ ^- (list (list @t))
+ :- :_ ~
+ %^ cat 3 '<<<<<<<<<<<<'
+ %^ cat 3 ' '
+ %^ cat 3 `@t`(scot %p bos)
+ %^ cat 3 '/'
+ bod
+
+ :- bob
+ :- ~['------------']
+ :- bas
+ :- ~['++++++++++++']
+ :- ali
+ :- :_ ~
+ %^ cat 3 '>>>>>>>>>>>>'
+ %^ cat 3 ' '
+ %^ cat 3 `@t`(scot %p als)
+ %^ cat 3 '/'
+ ald
+ ~
+ ::
+ ++ clean :: clean
+ |= wig=(urge cord)
+ ^- (urge cord)
+ ?~ wig ~
+ ?~ t.wig wig
+ ?: ?=(%& -.i.wig)
+ ?: ?=(%& -.i.t.wig)
+ $(wig [[%& (add p.i.wig p.i.t.wig)] t.t.wig])
+ [i.wig $(wig t.wig)]
+ ?: ?=(%| -.i.t.wig)
+ $(wig [[%| (welp p.i.wig p.i.t.wig) (welp q.i.wig q.i.t.wig)] t.t.wig])
+ [i.wig $(wig t.wig)]
+ ::
+ ++ resolve
+ |= [ali=(urge cord) bob=(urge cord)]
+ ^- [fic=[%| p=(list cord) q=(list cord)] ali=(urge cord) bob=(urge cord)]
+ =- [[%| bac (annotate alc boc bac)] ali bob]
+ |- ^- $: $: bac=(list cord)
+ alc=(list cord)
+ boc=(list cord)
+ ==
+ ali=(urge cord)
+ bob=(urge cord)
+ ==
+ ?~ ali [[~ ~ ~] ali bob]
+ ?~ bob [[~ ~ ~] ali bob]
+ ?- -.i.ali
+ %&
+ ?- -.i.bob
+ %& [[~ ~ ~] ali bob] :: no conflict
+ %|
+ =+ lob=(lent p.i.bob)
+ ?: =(lob p.i.ali)
+ [[p.i.bob p.i.bob q.i.bob] t.ali t.bob]
+ ?: (lth lob p.i.ali)
+ [[p.i.bob p.i.bob q.i.bob] [[%& (sub p.i.ali lob)] t.ali] t.bob]
+ =+ wat=(scag (sub lob p.i.ali) p.i.bob)
+ =+ ^= res
+ %= $
+ ali t.ali
+ bob [[%| (scag (sub lob p.i.ali) p.i.bob) ~] t.bob]
+ ==
+ :* :* (welp bac.res wat)
+ (welp alc.res wat)
+ (welp boc.res q.i.bob)
+ ==
+ ali.res
+ bob.res
+ ==
+ ==
+ ::
+ %|
+ ?- -.i.bob
+ %&
+ =+ loa=(lent p.i.ali)
+ ?: =(loa p.i.bob)
+ [[p.i.ali q.i.ali p.i.ali] t.ali t.bob]
+ ?: (lth loa p.i.bob)
+ [[p.i.ali q.i.ali p.i.ali] t.ali [[%& (sub p.i.bob loa)] t.bob]]
+ =+ wat=(slag (sub loa p.i.bob) p.i.ali)
+ =+ ^= res
+ %= $
+ ali [[%| (scag (sub loa p.i.bob) p.i.ali) ~] t.ali]
+ bob t.bob
+ ==
+ :* :* (welp bac.res wat)
+ (welp alc.res q.i.ali)
+ (welp boc.res wat)
+ ==
+ ali.res
+ bob.res
+ ==
+ ::
+ %|
+ =+ loa=(lent p.i.ali)
+ =+ lob=(lent p.i.bob)
+ ?: =(loa lob)
+ [[p.i.ali q.i.ali q.i.bob] t.ali t.bob]
+ =+ ^= res
+ ?: (gth loa lob)
+ $(ali [[%| (scag (sub loa lob) p.i.ali) ~] t.ali], bob t.bob)
+ ~& [%scagging loa=loa pibob=p.i.bob slag=(scag loa p.i.bob)]
+ $(ali t.ali, bob [[%| (scag (sub lob loa) p.i.bob) ~] t.bob])
+ :* :* (welp bac.res ?:((gth loa lob) p.i.bob p.i.ali))
+ (welp alc.res q.i.ali)
+ (welp boc.res q.i.bob)
+ ==
+ ali.res
+ bob.res
+ ==
+ ==
+ ==
+ --
+ --
+--
diff --git a/app/sur/bitcoin.hoon b/app/sur/bitcoin.hoon
new file mode 100644
index 0000000..4b83b5b
--- /dev/null
+++ b/app/sur/bitcoin.hoon
@@ -0,0 +1,84 @@
+:: sur/btc.hoon
+:: Utilities for working with BTC data types and transactions
+::
+:: chyg: whether account is (non-)change. 0 or 1
+:: bytc: "btc-byts" with dat cast to @ux
+|%
++$ network ?(%main %testnet %regtest)
++$ hexb [wid=@ dat=@ux] :: hex byts
++$ bits [wid=@ dat=@ub]
++$ xpub @ta
++$ address
+ $% [%base58 @uc]
+ [%bech32 cord]
+ ==
++$ fprint hexb
++$ bipt $?(%44 %49 %84)
++$ chyg $?(%0 %1)
++$ idx @ud
++$ hdkey [=fprint pubkey=hexb =network =bipt =chyg =idx]
++$ sats @ud
++$ vbytes @ud
++$ txid hexb
++$ utxo [pos=@ =txid height=@ value=sats recvd=(unit @da)]
+++ address-info
+ $: =address
+ confirmed-value=sats
+ unconfirmed-value=sats
+ utxos=(set utxo)
+ ==
+++ tx
+ |%
+ +$ data
+ $: is=(list input)
+ os=(list output)
+ locktime=@ud
+ nversion=@ud
+ segwit=(unit @ud)
+ ==
+ +$ val
+ $: =txid
+ pos=@ud
+ =address
+ value=sats
+ ==
+ :: included: whether tx is in the mempool or blockchain
+ ::
+ +$ info
+ $: included=?
+ =txid
+ confs=@ud
+ recvd=(unit @da)
+ inputs=(list val)
+ outputs=(list val)
+ ==
+ +$ input
+ $: =txid
+ pos=@ud
+ sequence=hexb
+ script-sig=(unit hexb)
+ pubkey=(unit hexb)
+ value=sats
+ ==
+ +$ output
+ $: script-pubkey=hexb
+ value=sats
+ ==
+ --
+++ psbt
+ |%
+ +$ base64 cord
+ +$ in [=utxo rawtx=hexb =hdkey]
+ +$ out [=address hk=(unit hdkey)]
+ +$ target $?(%input %output)
+ +$ keyval [key=hexb val=hexb]
+ +$ map (list keyval)
+ --
+++ ops
+ |%
+ ++ op-dup 118
+ ++ op-equalverify 136
+ ++ op-hash160 169
+ ++ op-checksig 172
+ --
+--
diff --git a/app/sur/nostrill.hoon b/app/sur/nostrill.hoon
index c5c5ae8..92a0876 100644
--- a/app/sur/nostrill.hoon
+++ b/app/sur/nostrill.hoon
@@ -15,6 +15,7 @@
:: profiles
profiles=(map user user-meta:nostr)
following=(map user =feed:trill)
+ following2=feed:trill
follow-graph=(map user (set user))
:: TODO global feed somehow?
@@ -39,7 +40,6 @@ $: pub=(unit @ux)
$% [%fols fols-poke]
[%begs begs-poke]
[%post post-poke]
- :: [%reac reac-poke]
[%prof prof-poke]
[%keys ~] :: cycle-keys
[%rela relay-poke]
@@ -50,11 +50,12 @@ $: pub=(unit @ux)
==
+$ post-poke
$% [%add content=@t]
- [%reply content=@t host=@p id=@ thread=@]
- [%quote content=@t host=@p id=@]
- [%rp host=@p id=@] :: NIP-18
+ [%reply content=@t host=@p id=@da thread=@da]
+ [%quote content=@t host=@p id=@da]
+ [%rp host=@p id=@da] :: NIP-18
+ [%reaction host=@p id=@da reaction=@t]
:: [%rt id=@ux pubkey=@ux relay=@t] :: NIP-18
- :: [%del pubkey=@ux]
+ [%del host=@p id=@da]
==
+$ fols-poke
$% [%add =user]
diff --git a/app/sur/nostrill/comms.hoon b/app/sur/nostrill/comms.hoon
index 42ea1ba..fa60989 100644
--- a/app/sur/nostrill/comms.hoon
+++ b/app/sur/nostrill/comms.hoon
@@ -3,12 +3,15 @@
+$ poke
$% [%req req]
[%res res]
+ [%eng engagement]
[%dbug *]
==
-+$ emgagement
- $% [%reply host=@p id=@da]
- [%del-reply host=@p id=@da]
- [%reaction host=@p id=@da reaction=@t]
++$ engagement
+ $% [%reply parent=@da child=post:post]
+ [%del-reply parent=@da child=@da]
+ [%quote src=@da =post:post]
+ [%rp src=@da rt=@da]
+ [%reaction post=@da reaction=@t]
==
+$ req
$% [%feed ~]
diff --git a/arvo/iris.hoon b/arvo/iris.hoon
index c55c0ae..ead7a5b 100644
--- a/arvo/iris.hoon
+++ b/arvo/iris.hoon
@@ -557,7 +557,8 @@
|- ?~ sockets ~
=/ socket=websocket-connection:iris q.i.sockets
?. .=(app.socket caller) $(sockets t.sockets)
- ?: .=(url.socket i.t.s.bem) `socket
+ ?: .=(url.socket i.t.s.bem) `sock
+ et
$(sockets t.sockets)
==
=* ren car
diff --git a/gui/src/components/feed/PostList.tsx b/gui/src/components/feed/PostList.tsx
index b09a0e9..0d01bd2 100644
--- a/gui/src/components/feed/PostList.tsx
+++ b/gui/src/components/feed/PostList.tsx
@@ -20,6 +20,8 @@ function TrillFeed({ data, refetch }: { data: FC; refetch: Function }) {
return (
<>
{Object.keys(data.feed)
+ // omit replies
+ .filter((i) => !data.feed[i].parent)
.sort()
.reverse()
.slice(0, 50)
diff --git a/gui/src/components/post/Footer.tsx b/gui/src/components/post/Footer.tsx
index 5b79da0..a87c1f8 100644
--- a/gui/src/components/post/Footer.tsx
+++ b/gui/src/components/post/Footer.tsx
@@ -41,6 +41,7 @@ function Footer({ poast, refetch }: PostProps) {
// Scroll to top where composer is located
window.scrollTo({ top: 0, behavior: "smooth" });
}
+ console.log({ poast });
const childrenCount = poast.children
? poast.children.length
? poast.children.length
@@ -52,7 +53,7 @@ function Footer({ poast, refetch }: PostProps) {
e.preventDefault();
const r = await api!.deletePost(our);
if (r) toast.success("Repost deleted");
- refetch();
+ // refetch();
if (location.includes(poast.id)) navigate("/");
}
async function sendRP(e: React.MouseEvent) {
diff --git a/gui/src/components/post/Quote.tsx b/gui/src/components/post/Quote.tsx
index 28149f0..98720ea 100644
--- a/gui/src/components/post/Quote.tsx
+++ b/gui/src/components/post/Quote.tsx
@@ -49,12 +49,11 @@ function Quote({
return (
<div onMouseUp={gotoQuote} className="quote-in-post">
<header className="btw">
- (
<div className="quote-author flex">
<Sigil patp={data.author} size={20} />
{data.author}
</div>
- )<span>{date_diff(data.time, "short")}</span>
+ <span>{date_diff(data.time, "short")}</span>
</header>
<Body poast={data} nest={nest} refetch={refetch!} />
</div>
diff --git a/gui/src/logic/trill/helpers.ts b/gui/src/logic/trill/helpers.ts
index 6b5a138..8bd1b0c 100644
--- a/gui/src/logic/trill/helpers.ts
+++ b/gui/src/logic/trill/helpers.ts
@@ -8,3 +8,27 @@ export function toFlat(n: FullNode): Poast {
: Object.keys(n.children).map((c) => n.children[c].id),
};
}
+
+type res = { threadChildren: FullNode[]; replies: FullNode[] };
+const bunt: res = { threadChildren: [], replies: [] };
+export function extractThread(node: FullNode): res {
+ if (!node.children) return bunt;
+ const r = Object.keys(node.children)
+ .sort()
+ .reduce((acc, index) => {
+ const n = node.children[index];
+ // if (typeof n.post === "string") return acc;
+ const nn = n as FullNode;
+ return n.author !== node.author
+ ? { ...acc, replies: [...acc.replies, nn] }
+ : {
+ ...acc,
+ threadChildren: [
+ ...acc.threadChildren,
+ nn,
+ ...extractThread(nn).threadChildren,
+ ],
+ };
+ }, bunt);
+ return r;
+}
diff --git a/gui/src/pages/Feed.tsx b/gui/src/pages/Feed.tsx
index 66acc66..ac596dd 100644
--- a/gui/src/pages/Feed.tsx
+++ b/gui/src/pages/Feed.tsx
@@ -57,7 +57,7 @@ function FeedPage({ t }: { t: FeedType }) {
{active === "global" ? (
<Global />
) : active === "following" ? (
- <Global />
+ <Following />
) : active === "nostr" ? (
<Nostr />
) : null}
@@ -87,6 +87,21 @@ function Global() {
// else return <Inner data={data} refetch={refetch} />;
return <p>Error</p>;
}
+function Following() {
+ const following = useLocalState((s) => s.following2);
+ console.log({ following });
+
+ // console.log(data, "scry feed data");
+ // if (isPending) return <img className="x-center" src={spinner} />;
+ // else if ("bucun" in data) return <p>Error</p>;
+ // else return <Inner data={data} refetch={refetch} />;
+
+ return (
+ <div>
+ <PostList data={following} refetch={() => {}} />
+ </div>
+ );
+}
function Nostr() {
const { nostrFeed, api } = useLocalState((s) => ({
nostrFeed: s.nostrFeed,
diff --git a/gui/src/pages/Thread.tsx b/gui/src/pages/Thread.tsx
index 8296f07..dec8946 100644
--- a/gui/src/pages/Thread.tsx
+++ b/gui/src/pages/Thread.tsx
@@ -1,15 +1,15 @@
import { useParams } from "wouter";
import { useQuery } from "@tanstack/react-query";
import useLocalState from "@/state/state";
-import PostList from "@/components/feed/PostList";
-import Composer from "@/components/composer/Composer";
import Icon from "@/components/Icon";
import spinner from "@/assets/triangles.svg";
import { ErrorPage } from "@/Router";
import "@/styles/trill.css";
import "@/styles/feed.css";
import Post from "@/components/post/Post";
-import { toFlat } from "@/logic/trill/helpers";
+import { extractThread, toFlat } from "@/logic/trill/helpers";
+import type { FullNode } from "@/types/trill";
+import Composer from "@/components/composer/Composer";
export default function Thread() {
const params = useParams<{ host: string; id: string }>();
@@ -19,7 +19,7 @@ export default function Thread() {
async function fetchThread() {
return await api!.scryThread(host, id);
}
- const { isPending, data, error, refetch } = useQuery({
+ const { isPending, data, error } = useQuery({
queryKey: ["thread", params.host, params.id],
queryFn: fetchThread,
enabled: !!api && !!params.host && !!params.id,
@@ -66,7 +66,8 @@ export default function Thread() {
</main>
);
}
-
+ console.log({ data });
+ // TODO make Composer a modal when in Thread mode
return (
<main>
<div className="thread-header">
@@ -90,13 +91,52 @@ export default function Thread() {
<div id="feed-proper">
<Composer />
- <div className="thread-content">
+ <div id="thread-head">
<Post poast={toFlat(data.ok)} />
</div>
+ <div id="thread-children">
+ <ChildTree node={data.ok} />
+ </div>
</div>
</main>
);
}
+
+function ChildTree({ node }: { node: FullNode }) {
+ const { threadChildren, replies } = extractThread(node);
+ return (
+ <>
+ <div id="tail">
+ {threadChildren.map((n) => {
+ return <Post key={n.id} poast={toFlat(n)} />;
+ })}
+ </div>
+ <div id="replies">
+ {replies.map((n) => (
+ <ReplyThread key={n.id} node={n} />
+ ))}
+ </div>
+ </>
+ );
+}
+
+function ReplyThread({ node }: { node: FullNode }) {
+ // const { threadChildren, replies } = extractThread(node);
+ const { replies } = extractThread(node);
+ return (
+ <div className="trill-reply-thread">
+ <div className="head">
+ <Post poast={toFlat(node)} />
+ </div>
+ <div className="tail">
+ {replies.map((r) => (
+ <Post key={r.id} poast={toFlat(r)} />
+ ))}
+ </div>
+ </div>
+ );
+}
+
// function OwnData(props: Props) {
// const { api } = useLocalState((s) => ({
// api: s.api,
diff --git a/gui/src/state/state.ts b/gui/src/state/state.ts
index f329145..9bd5e0e 100644
--- a/gui/src/state/state.ts
+++ b/gui/src/state/state.ts
@@ -26,6 +26,7 @@ export type LocalState = {
profiles: Map<string, UserProfile>; // pubkey key
addProfile: (key: string, u: UserProfile) => void;
following: Map<string, FC>;
+ following2: FC;
followers: string[];
// Notifications
notifications: Notification[];
@@ -50,7 +51,8 @@ export const useStore = creator((set, get) => ({
await api.subscribeStore((data) => {
console.log("store sub", data);
if ("state" in data) {
- const { feed, nostr, following, relays, profiles, pubkey } = data.state;
+ const { feed, nostr, following, following2, relays, profiles, pubkey } =
+ data.state;
const flwing = new Map(Object.entries(following as Record<string, FC>));
flwing.set(api!.airlock.our!, feed);
set({
@@ -58,6 +60,7 @@ export const useStore = creator((set, get) => ({
nostrFeed: nostr,
profiles: new Map(Object.entries(profiles)),
following: flwing,
+ following2,
pubkey,
});
} else if ("fact" in data) {
@@ -103,6 +106,7 @@ export const useStore = creator((set, get) => ({
nostrFeed: [],
following: new Map(),
followers: [],
+ following2: { feed: {}, start: "", end: "" },
UISettings: {},
modal: null,
setModal: (modal) => set({ modal }),