diff --git a/bin/solid.pill b/bin/solid.pill index 77837f3f4b..da53eec279 100644 --- a/bin/solid.pill +++ b/bin/solid.pill @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f639b862842609c64aa387f8414f9f8bcf1db5d89ae3e54e64aa1b040f21f7e0 -size 6315993 +oid sha256:4d0b09d1f033b59f626ae8727f5122db0dc03f86863f6b6b1ee02d9ec912d35e +size 6316479 diff --git a/pkg/arvo/app/aqua.hoon b/pkg/arvo/app/aqua.hoon index 748a2ee2f5..3eec9a21df 100644 --- a/pkg/arvo/app/aqua.hoon +++ b/pkg/arvo/app/aqua.hoon @@ -21,11 +21,15 @@ :: We get ++unix-event and ++pill from /-aquarium :: /- aquarium -/+ pill, default-agent +/+ pill, default-agent, aqua-azimuth, dbug, verb =, pill-lib=pill =, aquarium => $~ |% - +$ state + +$ versioned-state + $% state-0 + state-1 + == + +$ state-0 $: %0 pil=pill assembled=* @@ -33,7 +37,16 @@ fleet-snaps=(map term (map ship pier)) piers=(map ship pier) == + +$ state-1 + $: %1 + pil=pill + assembled=* + tym=@da + fleet-snaps=(map term fleet) + piers=fleet + == :: + +$ fleet [ships=(map ship pier) azi=az-state] +$ pier $: snap=* event-log=(list unix-timed-event) @@ -42,9 +55,11 @@ == -- :: -=| state -=* all-state - +=| state-1 +=* state - =< + %- agent:dbug + %+ verb | ^- agent:gall |_ =bowl:gall +* this . @@ -52,24 +67,37 @@ ac ~(. aqua-core bowl) def ~(. (default-agent this %|) bowl) ++ on-init `this - ++ on-save !>(all-state) + ++ on-save !>(state) ++ on-load - |= old-state=vase + |= old-vase=vase ^- step:agent:gall ~& prep=%aqua - =+ new=((soft state) !<(* old-state)) - ?~ new - `this - `this(all-state u.new) + =+ !<(old=versioned-state old-vase) + =| cards=(list card:agent:gall) + |- + ?- -.old + :: wipe fleets and piers rather than give them falsely nulled azimuth state + :: + %0 + %_ $ + -.old %1 + fleet-snaps.old *(map term fleet) + piers.old *fleet + == + :: + %1 + [cards this(state old)] + == :: ++ on-poke |= [=mark =vase] ^- step:agent:gall - =^ cards all-state + =^ cards state ?+ mark ~|([%aqua-bad-mark mark] !!) - %aqua-events (poke-aqua-events:ac !<((list aqua-event) vase)) - %pill (poke-pill:ac !<(pill vase)) - %noun (poke-noun:ac !<(* vase)) + %aqua-events (poke-aqua-events:ac !<((list aqua-event) vase)) + %pill (poke-pill:ac !<(pill vase)) + %noun (poke-noun:ac !<(* vase)) + %azimuth-action (poke-azimuth-action:ac !<(azimuth-action vase)) == [cards this] :: @@ -92,7 +120,18 @@ ++ on-peek peek:ac :: ++ on-agent on-agent:def - ++ on-arvo on-arvo:def + :: + ++ on-arvo + |= [=wire sign=sign-arvo] + ^- step:agent:gall + ?+ wire (on-arvo:def wire sign) + [%wait @ ~] + ?> ?=(%wake +<.sign) + =/ wen=@da (slav %da i.t.wire) + =^ cards state + (handle-wake:ac wen) + [cards this] + == ++ on-fail on-fail:def -- :: @@ -110,7 +149,7 @@ :: ++ pe |= who=ship - =+ (~(gut by piers) who *pier) + =+ (~(gut by ships.piers) who *pier) =* pier-data - |% :: @@ -118,7 +157,7 @@ :: ++ abet-pe ^+ this - =. piers (~(put by piers) who pier-data) + =. ships.piers (~(put by ships.piers) who pier-data) this :: :: Initialize new ship @@ -248,7 +287,19 @@ this :: ++ abet-aqua - ^- (quip card:agent:gall state) + ^- (quip card:agent:gall _state) + :: + :: interecept %request effects to handle azimuth subscription + :: + =. this + %- emit-cards + %- zing + %+ turn ~(tap by unix-effects) + |= [=ship ufs=(list unix-effect)] + %+ murn ufs + |= uf=unix-effect + (router:aqua-azimuth our.hid ship uf azi.piers) + :: =. this =/ =path /effect %- emit-cards @@ -300,20 +351,19 @@ =/ =path /boths/(scot %p ship) [%give %fact ~[path] %aqua-boths !>(`aqua-boths`[ship (flop bo)])] :: - [(flop cards) all-state] + [(flop cards) state] :: ++ emit-cards |= ms=(list card:agent:gall) =. cards (weld ms cards) this :: -:: :: Run all events on all ships until all queues are empty :: ++ plow-all |- ^+ this =/ who - =/ pers ~(tap by piers) + =/ pers ~(tap by ships.piers) |- ^- (unit ship) ?~ pers ~ @@ -331,7 +381,7 @@ :: ++ poke-pill |= p=pill - ^- (quip card:agent:gall state) + ^- (quip card:agent:gall _state) =. this apex-aqua =< abet-aqua =. pil p ~& lent=(met 3 (jam boot-ova.pil)) @@ -362,7 +412,7 @@ :: ++ poke-noun |= val=* - ^- (quip card:agent:gall state) + ^- (quip card:agent:gall _state) =. this apex-aqua =< abet-aqua ^+ this :: Could potentially factor out the three lines of turn-ships @@ -391,7 +441,7 @@ =/ txt .^(@ %cx (weld pax /hoon)) [/vane/[vane] [%veer v pax txt]] => .(this ^+(this this)) - =^ ms all-state (poke-pill pil) + =^ ms state (poke-pill pil) (emit-cards ms) :: [%swap-files ~] @@ -402,7 +452,7 @@ %- unix-event %- %*(. file-ovum:pill-lib directories slim-dirs) /(scot %p our.hid)/work/(scot %da now.hid) - =^ ms all-state (poke-pill pil) + =^ ms state (poke-pill pil) (emit-cards ms) :: [%wish hers=* p=@t] @@ -412,12 +462,14 @@ (wish:(pe who) p.val) :: [%unpause-events hers=*] + =. this start-azimuth-timer %+ turn-ships ((list ship) hers.val) |= [who=ship thus=_this] =. this thus start-processing-events:(pe who) :: [%pause-events hers=*] + =. this stop-azimuth-timer %+ turn-ships ((list ship) hers.val) |= [who=ship thus=_this] =. this thus @@ -428,17 +480,47 @@ this == :: +:: Make changes to azimuth state for the current fleet +:: +++ poke-azimuth-action + |= act=azimuth-action + ^- (quip card:agent:gall _state) + =. this apex-aqua =< abet-aqua + ^+ this + ?- -.act + :: + %init-azimuth + =. azi.piers *az-state + start-azimuth-timer + :: + %spawn + =. state (spawn who.act) + this + :: + %breach + :: should we remove the pier from state here? + =. state (breach who.act) + this + :: + == +:: :: Apply a list of events tagged by ship :: ++ poke-aqua-events |= events=(list aqua-event) - ^- (quip card:agent:gall state) + ^- (quip card:agent:gall _state) =. this apex-aqua =< abet-aqua %+ turn-events events |= [ae=aqua-event thus=_this] =. this thus ?- -.ae + :: %init-ship + :: XX Note that the keys that get passed in are unused. The keys field + :: should be deleted now that aqua is capable of managing azimuth state + :: internally. Its been left this way for now until all the ph tests + :: can be rewritten + =/ keys=dawn-event:able:jael (dawn who.ae) =. this abet-pe:(publish-effect:(pe who.ae) [/ %sleep ~]) =/ initted =< plow @@ -451,7 +533,7 @@ :^ //term/1 %boot & ?~ keys.ae [%fake who.ae] - [%dawn u.keys.ae] + [%dawn keys] -.userspace-ova.pil [//http-client/0v1n.2m9vh %born ~] [//http-server/0v1n.2m9vh %born ~] @@ -464,27 +546,36 @@ stop-processing-events:(pe who.ae) :: %snap-ships + =. this + %+ turn-ships (turn ~(tap by ships.piers) head) + |= [who=ship thus=_this] + =. this thus + (publish-effect:(pe who) [/ %kill ~]) =. fleet-snaps %+ ~(put by fleet-snaps) lab.ae + :_ azi.piers %- malt %+ murn hers.ae |= her=ship ^- (unit (pair ship pier)) - =+ per=(~(get by piers) her) + =+ per=(~(get by ships.piers) her) ?~ per ~ `[her u.per] + =. this stop-azimuth-timer + =. piers *fleet (pe -.hers.ae) :: %restore-snap =. this - %+ turn-ships (turn ~(tap by piers) head) + %+ turn-ships (turn ~(tap by ships.piers) head) |= [who=ship thus=_this] =. this thus - (publish-effect:(pe who) [/ %sleep ~]) - =. piers (~(uni by piers) (~(got by fleet-snaps) lab.ae)) + (publish-effect:(pe who) [/ %kill ~]) + =. piers (~(got by fleet-snaps) lab.ae) + =. this start-azimuth-timer =. this - %+ turn-ships (turn ~(tap by piers) head) + %+ turn-ships (turn ~(tap by ships.piers) head) |= [who=ship thus=_this] =. this thus (publish-effect:(pe who) [/ %restore ~]) @@ -537,18 +628,163 @@ ^- (unit (unit cage)) ?+ path ~ [%x %fleet-snap @ ~] ``noun+!>((~(has by fleet-snaps) i.t.t.path)) - [%x %ships ~] ``noun+!>((turn ~(tap by piers) head)) + [%x %fleets ~] ``noun+!>((turn ~(tap by fleet-snaps) head)) + [%x %ships ~] ``noun+!>((turn ~(tap by ships.piers) head)) [%x %pill ~] ``pill+!>(pil) [%x %i @ @ @ @ @ *] =/ who (slav %p i.t.t.path) - =/ pier (~(get by piers) who) + =/ pier (~(get by ships.piers) who) ?~ pier ~ :^ ~ ~ %noun !> (peek:(pe who) t.t.t.path) + [%x %log-info ~] + ``noun+!>([lives.azi.piers (lent logs.azi.piers) tym.azi.piers]) == :: :: Trivial scry for mock :: ++ scry |=([* *] ~) +:: +++ handle-wake + |= wen=@da + ^- (quip card:agent:gall _state) + =. this apex-aqua =< abet-aqua + ?. =(wen tym.azi.piers) + this + =. state (spam-logs 10) + start-azimuth-timer +:: +++ start-azimuth-timer + ^+ this + =? this !=(tym.azi.piers *@da) + stop-azimuth-timer + =/ until=@da (add now.hid ~s40) + =. tym.azi.piers until + %- emit-cards + [%pass /wait/(scot %da until) %arvo %b %wait until]~ +:: +++ stop-azimuth-timer + ^+ this + =* tym tym.azi.piers + ?: =(tym *@da) + this + %- emit-cards + [%pass /wait/(scot %da tym) %arvo %b %rest tym]~ +:: +++ spam-logs + |= n=@ + ^- _state + =* loop $ + ?: =(n 0) + state + =/ new-state=_state + ?. (~(has by lives.azi.piers) ~fes) + (spawn ~fes) + (cycle-keys ~fes) + =. state new-state + loop(n (dec n)) +:: +++ spawn + |= who=@p + ^- _state + ?< (~(has by lives.azi.piers) who) + =. lives.azi.piers (~(put by lives.azi.piers) who [1 0]) + =. logs.azi.piers + %+ weld logs.azi.piers + :_ ~ + %- changed-keys:lo:aqua-azimuth + :* who + (get-public:aqua-azimuth who 1 %crypt) + (get-public:aqua-azimuth who 1 %auth) + 1 + 1 + == + (spam-logs 10) +:: +++ cycle-keys + |= who=@p + ^- _state + =/ prev + ~| no-such-ship+who + (~(got by lives.azi.piers) who) + =/ lyfe +(lyfe.prev) + =. lives.azi.piers (~(put by lives.azi.piers) who [lyfe rut.prev]) + =. logs.azi.piers + %+ weld logs.azi.piers + :_ ~ + %- changed-keys:lo:aqua-azimuth + :* who + (get-public:aqua-azimuth who lyfe %crypt) + (get-public:aqua-azimuth who lyfe %auth) + 1 + lyfe + == + state +:: +++ breach + |= who=@p + ^- _state + =. state (cycle-keys who) + =/ prev (~(got by lives.azi.piers) who) + =/ rut +(rut.prev) + =. lives.azi.piers (~(put by lives.azi.piers) who [lyfe.prev rut]) + =. logs.azi.piers + %+ weld logs.azi.piers + [(broke-continuity:lo:aqua-azimuth who rut) ~] + (spam-logs 10) +:: +++ dawn + |= who=ship + ^- dawn-event:able:jael + ?> ?=(?(%czar %king %duke) (clan:title who)) + =/ spon=(list [ship point:azimuth]) + %- flop + |- ^- (list [ship point:azimuth]) + =/ =ship (^sein:title who) + =/ a-point=[^ship point:azimuth] + =/ spon-spon [& (^sein:title ship)] + =/ life-rift ~|([ship lives.azi.piers] (~(got by lives.azi.piers) ship)) + =/ =life lyfe.life-rift + =/ =rift rut.life-rift + =/ =pass + %^ pass-from-eth:azimuth + (as-octs:mimes:html (get-public:aqua-azimuth ship life %crypt)) + (as-octs:mimes:html (get-public:aqua-azimuth ship life %auth)) + 1 + :^ ship + *[address address address address]:azimuth + `[life=life pass rift spon-spon ~] + ~ + ?: ?=(%czar (clan:title ship)) + [a-point]~ + [a-point $(who ship)] + =/ =seed:able:jael + =/ life-rift (~(got by lives.azi.piers) who) + =/ =life lyfe.life-rift + [who life sec:ex:(get-keys:aqua-azimuth who life) ~] + :* seed + spon + get-czars + ~[~['arvo' 'netw' 'ork']] + 0 + `(need (de-purl:html 'http://localhost:8545')) + == +:: +:: Should only do galaxies +:: +++ get-czars + ^- (map ship [rift life pass]) + %- malt + %+ murn + ~(tap by lives.azi.piers) + |= [who=ship lyfe=life rut=rift] + ?. =(%czar (clan:title who)) + ~ + %- some + :^ who rut lyfe + %^ pass-from-eth:azimuth + (as-octs:mimes:html (get-public:aqua-azimuth who lyfe %crypt)) + (as-octs:mimes:html (get-public:aqua-azimuth who lyfe %auth)) + 1 -- diff --git a/pkg/arvo/app/contact-view.hoon b/pkg/arvo/app/contact-view.hoon index 2abbbb0f67..4350e9d439 100644 --- a/pkg/arvo/app/contact-view.hoon +++ b/pkg/arvo/app/contact-view.hoon @@ -181,22 +181,7 @@ ~[(add-pending rid ship.act)] :: %delete - =/ rid=resource - (de-path:resource path.act) - =/ group-pokes=(list card) - ?: =(our.bol entity.rid) - ~[(group-push-poke %remove rid)] - :~ (group-proxy-poke %remove-members rid (sy our.bol ~)) - (group-pull-poke %remove rid) - == - ;: weld - group-pokes - :~ (contact-hook-poke [%remove path.act]) - (group-poke [%remove-group rid ~]) - (contact-poke [%delete path.act]) - == - (delete-metadata path.act) - == + ~ :: %remove =/ rid=resource @@ -357,13 +342,6 @@ (metadata-hook-poke [%add-owned path]) == :: -++ delete-metadata - |= =path - ^- (list card) - :~ (metadata-poke [%remove path [%contacts path]]) - (metadata-hook-poke [%remove path]) - == -:: ++ all-scry ^- rolodex .^(rolodex %gx /(scot %p our.bol)/contact-store/(scot %da now.bol)/all/noun) diff --git a/pkg/arvo/app/glob.hoon b/pkg/arvo/app/glob.hoon index 472f938331..de1514d5a3 100644 --- a/pkg/arvo/app/glob.hoon +++ b/pkg/arvo/app/glob.hoon @@ -5,7 +5,7 @@ /- glob /+ default-agent, verb, dbug |% -++ hash 0v4.fpa4r.s6dtc.h8tps.62jv0.qn0fj +++ hash 0v5.0umdn.af5hq.bp84b.66eao.q0b98 +$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))] +$ all-states $% state-0 diff --git a/pkg/arvo/app/graph-store.hoon b/pkg/arvo/app/graph-store.hoon index 27a2c82232..fce8193bde 100644 --- a/pkg/arvo/app/graph-store.hoon +++ b/pkg/arvo/app/graph-store.hoon @@ -807,12 +807,14 @@ [%x %peek-update-log @ @ ~] =/ =ship (slav %p i.t.t.path) =/ =term i.t.t.t.path - =/ update-log=(unit update-log:store) (~(get by update-logs) [ship term]) - ?~ update-log [~ ~] - =/ result=(unit [time update:store]) - (peek:orm-log:store u.update-log) - ?~ result [~ ~] - ``noun+!>([~ -.u.result]) + =/ m-update-log=(unit update-log:store) (~(get by update-logs) [ship term]) + :- ~ :- ~ :- %noun + !> ^- (unit time) + %+ biff m-update-log + |= =update-log:store + =/ result=(unit [=time =update:store]) + (peek:orm-log:store update-log) + (bind result |=([=time update:store] time)) == ++ get-node-children |= [=ship =term =index:store] diff --git a/pkg/arvo/app/hark-store.hoon b/pkg/arvo/app/hark-store.hoon index 1b692a29cd..95496008f2 100644 --- a/pkg/arvo/app/hark-store.hoon +++ b/pkg/arvo/app/hark-store.hoon @@ -223,12 +223,16 @@ ?~ existing-notif notification (merge-notification:ha u.existing-notif notification) + =/ new-read=? + ?~ existing-notif + %.y + read.u.existing-notif =. read.new %.n =/ new-timebox=timebox:store (~(put by timebox) index new) :- (give:ha [/updates]~ %added current-timebox index new) %_ state - + ?~(existing-notif (upd-unreads:ha index current-timebox %.n) +.state) + + ?.(new-read +.state (upd-unreads:ha index current-timebox %.n)) notifications (put:orm notifications current-timebox new-timebox) == :: diff --git a/pkg/arvo/app/landscape/index.html b/pkg/arvo/app/landscape/index.html index a2a06dea17..bd6b1828d2 100644 --- a/pkg/arvo/app/landscape/index.html +++ b/pkg/arvo/app/landscape/index.html @@ -24,6 +24,6 @@
- + diff --git a/pkg/arvo/app/observe-hook.hoon b/pkg/arvo/app/observe-hook.hoon index 174b90b13c..9f93de49a1 100644 --- a/pkg/arvo/app/observe-hook.hoon +++ b/pkg/arvo/app/observe-hook.hoon @@ -10,10 +10,13 @@ +$ card card:agent:gall +$ versioned-state $% state-0 + state-1 == :: +$ serial @uv +$ state-0 [%0 observers=(map serial observer:sur)] ++$ state-1 [%1 observers=(map serial observer:sur)] +:: ++ got-by-val |= [a=(map serial observer:sur) b=observer:sur] ^- serial @@ -24,7 +27,7 @@ -- :: %- agent:dbug -=| state-0 +=| state-1 =* state - :: ^- agent:gall @@ -35,8 +38,14 @@ ++ on-init |^ ^- (quip card _this) :_ this - :_ ~ - (act /inv-gra [%watch %invite-store /invitatory/graph %invite-accepted-graph]) + :~ %+ act + /inv-gra + [%watch %invite-store /invitatory/graph %invite-accepted-graph] + :: + %+ act + /grp-gra + [%watch %group-store /groups %group-on-leave] + == :: ++ act |= [=wire =action:sur] @@ -56,7 +65,17 @@ ++ on-load |= old-vase=vase ^- (quip card _this) - `this(state !<(state-0 old-vase)) + =/ old-state !<(versioned-state old-vase) + ?- -.old-state + %1 `this(state old-state) + :: + %0 + =. state [%1 observers.old-state] + %+ on-poke + %observe-action + !> ^- action:sur + [%watch %group-store /groups %group-on-leave] + == :: ++ on-poke |= [=mark =vase] diff --git a/pkg/arvo/app/weather.hoon b/pkg/arvo/app/weather.hoon index 684956b151..31e0b00d27 100644 --- a/pkg/arvo/app/weather.hoon +++ b/pkg/arvo/app/weather.hoon @@ -83,7 +83,7 @@ ?. ?=(%s -.jon) [~ state] =/ str=@t +.jon - =/ req=request:http (request-darksky str) + =/ req=request:http (request-wttr str) =/ out *outbound-config:iris =/ lismov=(list card) [%pass /[(scot %da now.bol)] %arvo %i %request req out]~ @@ -102,11 +102,11 @@ ^- (list card) [%give %fact ~[/all] %json !>((frond:enjs:format %location jon))]~ :: -++ request-darksky +++ request-wttr |= location=@t ^- request:http - =/ base 'https://api.darksky.net/forecast/634639c10670c7376dc66b6692fe57ca/' - =/ url=@t (cat 3 (cat 3 base location) '?units=auto') + =/ base 'https://wttr.in/' + =/ url=@t (cat 3 (cat 3 base location) '?format=j1') =/ hed [['Accept' 'application/json']]~ [%'GET' url hed *(unit octs)] :: @@ -133,8 +133,9 @@ =/ jon=json %+ frond:enjs:format %weather %- pairs:enjs:format - :~ [%currently (~(got by p.u.ujon) 'currently')] - [%daily (~(got by p.u.ujon) 'daily')] + :~ [%current-condition (~(got by p.u.ujon) 'current_condition')] + [%weather (~(got by p.u.ujon) 'weather')] + [%nearest-area (~(got by p.u.ujon) 'nearest_area')] == :- [%give %fact ~[/all] %json !>(jon)]~ %= state @@ -146,7 +147,7 @@ |= [wir=wire err=(unit tang)] ^- (quip card _state) ?~ err - =/ req/request:http (request-darksky location) + =/ req/request:http (request-wttr location) =/ out *outbound-config:iris :_ state(timer `(add now.bol ~h3)) :~ [%pass /[(scot %da now.bol)] %arvo %i %request req out] diff --git a/pkg/arvo/gen/aqua/breach.hoon b/pkg/arvo/gen/aqua/breach.hoon new file mode 100644 index 0000000000..78de7b8b8d --- /dev/null +++ b/pkg/arvo/gen/aqua/breach.hoon @@ -0,0 +1,4 @@ +:- %say +|= [* [her=ship ~] ~] +:- %azimuth-action +[%breach her] diff --git a/pkg/arvo/gen/aqua/init-azimuth.hoon b/pkg/arvo/gen/aqua/init-azimuth.hoon new file mode 100644 index 0000000000..9d29181e7b --- /dev/null +++ b/pkg/arvo/gen/aqua/init-azimuth.hoon @@ -0,0 +1,4 @@ +:- %say +|= [* ~ ~] +:- %azimuth-action +[%init-azimuth ~] diff --git a/pkg/arvo/gen/aqua/init.hoon b/pkg/arvo/gen/aqua/init.hoon index 3aced4c2a4..a9777c1923 100644 --- a/pkg/arvo/gen/aqua/init.hoon +++ b/pkg/arvo/gen/aqua/init.hoon @@ -3,4 +3,4 @@ :- %say |= [* [her=ship ~] ~] :- %aqua-events -[%init-ship her ~]~ +[%init-ship her `*dawn-event:able:jael]~ diff --git a/pkg/arvo/gen/aqua/restore-fleet.hoon b/pkg/arvo/gen/aqua/restore-fleet.hoon index 3a38cdaee1..3fde261aff 100644 --- a/pkg/arvo/gen/aqua/restore-fleet.hoon +++ b/pkg/arvo/gen/aqua/restore-fleet.hoon @@ -1,6 +1,6 @@ /- aquarium =, aquarium :- %say -|= [* [label=@ta] ~] +|= [* [label=@ta ~] ~] :- %aqua-events -[%snap-ships label]~ +[%restore-snap label]~ diff --git a/pkg/arvo/gen/aqua/snap-fleet.hoon b/pkg/arvo/gen/aqua/snap-fleet.hoon index 6fcdf0ab35..208fc0d01b 100644 --- a/pkg/arvo/gen/aqua/snap-fleet.hoon +++ b/pkg/arvo/gen/aqua/snap-fleet.hoon @@ -1,7 +1,7 @@ /- aquarium =, aquarium :- %say -|= [[now=@da eny=@uvJ bec=beak] [label=@ta] ships=(list ship)] +|= [[now=@da eny=@uvJ bec=beak] [label=@ta ships=(list ship)] ~] :- %aqua-events =? ships ?=(~ ships) .^((list ship) %gx /(scot %p p.bec)/aqua/(scot %da now)/ships/noun) diff --git a/pkg/arvo/gen/aqua/spawn.hoon b/pkg/arvo/gen/aqua/spawn.hoon new file mode 100644 index 0000000000..fc7a679992 --- /dev/null +++ b/pkg/arvo/gen/aqua/spawn.hoon @@ -0,0 +1,4 @@ +:- %say +|= [* [her=ship ~] ~] +:- %azimuth-action +[%spawn her] diff --git a/pkg/arvo/gen/sponsor.hoon b/pkg/arvo/gen/sponsor.hoon new file mode 100644 index 0000000000..f2a211f2ef --- /dev/null +++ b/pkg/arvo/gen/sponsor.hoon @@ -0,0 +1,5 @@ +:: Print the sponsor of this ship +:- %say +|= [[now=time @ our=ship ^] * ~] +:- %ship +(sein:title our now our) diff --git a/pkg/arvo/lib/aqua-azimuth.hoon b/pkg/arvo/lib/aqua-azimuth.hoon new file mode 100644 index 0000000000..7120757527 --- /dev/null +++ b/pkg/arvo/lib/aqua-azimuth.hoon @@ -0,0 +1,241 @@ +/- *aquarium +:: +|% +:: +++ extract-request + |= [uf=unix-effect dest=@t] + ^- (unit [num=@ud =request:http]) + ?. ?=(%request -.q.uf) ~ + ?. =(dest url.request.q.uf) ~ + `[id.q.uf request.q.uf] +:: +++ router + |= [our=ship her=ship uf=unix-effect azi=az-state] + ^- (unit card:agent:gall) + =, enjs:format + =/ ask (extract-request uf 'http://localhost:8545/') + ?~ ask + ~ + ?~ body.request.u.ask + ~ + =/ req q.u.body.request.u.ask + |^ ^- (unit card:agent:gall) + =/ method (get-method req) + ?: =(method 'eth_blockNumber') + :- ~ + %+ answer-request req + s+(crip (num-to-hex:ethereum latest-block)) + ?: =(method 'eth_getBlockByNumber') + :- ~ + %+ answer-request req + :- %o + =/ number (hex-to-num:ethereum (get-first-param req)) + =/ hash (number-to-hash number) + =/ parent-hash (number-to-hash ?~(number number (dec number))) + %- malt + ^- (list (pair term json)) + :~ hash+s+(crip (prefix-hex:ethereum (render-hex-bytes:ethereum 32 hash))) + number+s+(crip (num-to-hex:ethereum number)) + 'parentHash'^s+(crip (num-to-hex:ethereum parent-hash)) + == + ?: =(method 'eth_getLogs') + :- ~ + %+ answer-request req + ?^ (get-param-obj-maybe req 'blockHash') + %- logs-by-hash + (get-param-obj req 'blockHash') + %+ logs-by-range + (get-param-obj req 'fromBlock') + (get-param-obj req 'toBlock') + ~& [%ph-azimuth-miss req] + ~ + :: + ++ latest-block + (add launch:contracts:azimuth (dec (lent logs.azi))) + :: + ++ get-single-req + |= req=@t + =/ batch + ((ar:dejs:format same) (need (de-json:html req))) + ?> ?=([* ~] batch) + i.batch + :: + ++ get-id + |= req=@t + =, dejs:format + %. (get-single-req req) + (ot id+so ~) + :: + ++ get-method + |= req=@t + =, dejs:format + ~| req=req + %. (get-single-req req) + (ot method+so ~) + :: + ++ get-param-obj + |= [req=@t param=@t] + =, dejs:format + %- hex-to-num:ethereum + =/ array + %. (get-single-req req) + (ot params+(ar (ot param^so ~)) ~) + ?> ?=([* ~] array) + i.array + :: + ++ get-param-obj-maybe + |= [req=@t param=@t] + ^- (unit @ud) + =, dejs-soft:format + =/ array + %. (get-single-req req) + (ot params+(ar (ot param^so ~)) ~) + ?~ array + ~ + :- ~ + ?> ?=([* ~] u.array) + %- hex-to-num:ethereum + i.u.array + :: + ++ get-first-param + |= req=@t + =, dejs:format + =/ id + %. (get-single-req req) + (ot params+(at so bo ~) ~) + -.id + :: + ++ answer-request + |= [req=@t result=json] + ^- card:agent:gall + =/ resp + %- crip + %- en-json:html + :- %a :_ ~ + %- pairs + :~ id+s+(get-id req) + jsonrpc+s+'2.0' + result+result + == + =/ events=(list aqua-event) + :_ ~ + :* %event + her + //http-client/0v1n.2m9vh + %receive + num.u.ask + [%start [200 ~] `(as-octs:mimes:html resp) &] + == + :* %pass /aqua-events + %agent [our %aqua] + %poke %aqua-events + !>(events) + == + :: + ++ number-to-hash + |= =number:block:able:jael + ^- @ + ?: (lth number launch:contracts:azimuth) + (cat 3 0x5364 (sub launch:contracts:azimuth number)) + (cat 3 0x5363 (sub number launch:contracts:azimuth)) + :: + ++ hash-to-number + |= =hash:block:able:jael + (add launch:contracts:azimuth (div hash 0x1.0000)) + :: + ++ logs-by-range + |= [from-block=@ud to-block=@ud] + %+ logs-to-json (max launch:contracts:azimuth from-block) + ?: (lth to-block launch:contracts:azimuth) + ~ + %+ swag + ?: (lth from-block launch:contracts:azimuth) + [0 +((sub to-block launch:contracts:azimuth))] + :- (sub from-block launch:contracts:azimuth) + +((sub to-block from-block)) + logs.azi + :: + ++ logs-by-hash + |= =hash:block:able:jael + =/ =number:block:able:jael (hash-to-number hash) + (logs-by-range number number) + :: + ++ logs-to-json + |= [count=@ud selected-logs=(list az-log)] + ^- json + :- %a + |- ^- (list json) + ?~ selected-logs + ~ + :_ $(selected-logs t.selected-logs, count +(count)) + %- pairs + :~ 'logIndex'^s+'0x0' + 'transactionIndex'^s+'0x0' + :+ 'transactionHash' %s + (crip (prefix-hex:ethereum (render-hex-bytes:ethereum 32 `@`0x5362))) + :: + :+ 'blockHash' %s + =/ hash (number-to-hash count) + (crip (prefix-hex:ethereum (render-hex-bytes:ethereum 32 hash))) + :: + :+ 'blockNumber' %s + (crip (num-to-hex:ethereum count)) + :: + :+ 'address' %s + (crip (address-to-hex:ethereum azimuth:contracts:azimuth)) + :: + 'type'^s+'mined' + :: + 'data'^s+data.i.selected-logs + :+ 'topics' %a + %+ turn topics.i.selected-logs + |= topic=@ux + ^- json + :- %s + %- crip + %- prefix-hex:ethereum + (render-hex-bytes:ethereum 32 `@`topic) + == + -- +:: +++ get-keys + |= [who=@p lyfe=life] + ^- acru:ames + %+ pit:nu:crub:crypto 32 + (can 5 [1 (scot %p who)] [1 (scot %ud lyfe)] ~) +:: +++ get-public + |= [who=@p lyfe=life typ=?(%auth %crypt)] + =/ bod (rsh 3 1 pub:ex:(get-keys who lyfe)) + =+ [enc=(rsh 8 1 bod) aut=(end 8 1 bod)] + ?: =(%auth typ) + aut + enc +:: +:: Generate logs +:: +++ lo + =, azimuth-events:azimuth + |% + ++ broke-continuity + |= [who=ship rut=rift] + ^- az-log + :- ~[^broke-continuity who] + %- crip + %- prefix-hex:ethereum + (render-hex-bytes:ethereum 32 `@`rut) + :: + ++ changed-keys + |= [who=ship enc=@ux aut=@ux crypto=@ud lyfe=life] + ^- az-log + :- ~[^changed-keys who] + %- crip + %- prefix-hex:ethereum + ;: welp + (render-hex-bytes:ethereum 32 `@`enc) + (render-hex-bytes:ethereum 32 `@`aut) + (render-hex-bytes:ethereum 32 `@`crypto) + (render-hex-bytes:ethereum 32 `@`lyfe) + == + -- +-- diff --git a/pkg/arvo/lib/group-store.hoon b/pkg/arvo/lib/group-store.hoon index 56e663301f..e2a14576b7 100644 --- a/pkg/arvo/lib/group-store.hoon +++ b/pkg/arvo/lib/group-store.hoon @@ -417,6 +417,7 @@ :: ++ remove-group |= =json + ^- [resource ~] ?> ?=(%o -.json) =/ rid=resource (dejs:resource (~(got by p.json) 'resource')) diff --git a/pkg/arvo/lib/ph/io.hoon b/pkg/arvo/lib/ph/io.hoon index 58400579d8..4433670ca3 100644 --- a/pkg/arvo/lib/ph/io.hoon +++ b/pkg/arvo/lib/ph/io.hoon @@ -8,6 +8,12 @@ ^- form:m (poke-our %aqua %aqua-events !>(events)) :: +++ send-azimuth-action + |= =azimuth-action + =/ m (strand ,~) + ^- form:m + (poke-our %aqua %azimuth-action !>(azimuth-action)) +:: ++ take-unix-effect =/ m (strand ,[ship unix-effect]) ^- form:m @@ -34,7 +40,7 @@ =/ m (strand ,~) ^- form:m ~& > "starting" - ;< ~ bind:m (start-threads vane-threads) + ;< tids=(map term tid:spider) bind:m (start-threads vane-threads) ;< ~ bind:m (watch-our /effect %aqua /effect) :: Get our very own event with no mistakes in it... yet. :: @@ -64,16 +70,20 @@ :: ++ start-threads |= threads=(list term) - =/ m (strand ,~) + =/ m (strand ,(map term tid:spider)) ^- form:m ;< =bowl:spider bind:m get-bowl + =| tids=(map term tid:spider) |- ^- form:m =* loop $ ?~ threads - (pure:m ~) + (pure:m tids) + =/ tid + %+ scot %ta + (cat 3 (cat 3 'strand_' i.threads) (scot %uv (sham i.threads eny.bowl))) =/ poke-vase !>([`tid.bowl ~ i.threads *vase]) ;< ~ bind:m (poke-our %spider %spider-start poke-vase) - loop(threads t.threads) + loop(threads t.threads, tids (~(put by tids) i.threads tid)) :: ++ stop-threads |= threads=(list term) @@ -81,6 +91,29 @@ ^- form:m (pure:m ~) :: +:: XX +spawn-aqua and +breach-aqua mean do these actions using aqua's internal +:: azimuth management system, eventually these should just replace +spawn +:: +breach +:: +++ init-azimuth + =/ m (strand ,~) + ^- form:m + (send-azimuth-action %init-azimuth ~) +:: +++ spawn-aqua + |= =ship + ~& > "spawning {}" + =/ m (strand ,~) + ^- form:m + (send-azimuth-action %spawn ship) +:: +++ breach-aqua + |= =ship + ~& > "breaching {}" + =/ m (strand ,~) + ^- form:m + (send-azimuth-action %breach ship) +:: ++ spawn |= [=tid:spider =ship] ~& > "spawning {}" @@ -127,6 +160,39 @@ (pure:m ~) loop :: +++ breach-and-hear-aqua + |= [who=ship her=ship] + =/ m (strand ,~) + ;< =bowl:spider bind:m get-bowl + =/ aqua-pax + :- %i + /(scot %p her)/j/(scot %p her)/rift/(scot %da now.bowl)/(scot %p who)/noun + =/ old-rut ;;((unit @) (scry-aqua:util noun our.bowl now.bowl aqua-pax)) + =/ new-rut + ?~ old-rut + 1 + +(+.old-rut) + ;< ~ bind:m (send-azimuth-action %breach who) + |- ^- form:m + =* loop $ + ;< ~ bind:m (sleep ~s1) + ;< =bowl:spider bind:m get-bowl + =/ aqua-pax + :- %i + /(scot %p her)/j/(scot %p her)/rift/(scot %da now.bowl)/(scot %p who)/noun + =/ rut (scry-aqua:util noun our.bowl now.bowl aqua-pax) + ?: =([~ new-rut] rut) + (pure:m ~) + loop +:: +++ init-ship + |= =ship + =/ m (strand ,~) + ^- form:m + ~& > "starting {}" + ;< ~ bind:m (send-events (init:util ship `*dawn-event:able:jael)) + (check-ship-booted ship) +:: ++ real-ship |= [=tid:spider =ship] ~& > "booting real {}" diff --git a/pkg/arvo/lib/strandio.hoon b/pkg/arvo/lib/strandio.hoon index 80c4178a1e..58bc830b78 100644 --- a/pkg/arvo/lib/strandio.hoon +++ b/pkg/arvo/lib/strandio.hoon @@ -219,6 +219,25 @@ ;< ~ bind:m (send-raw-card card) (take-poke-ack /poke) :: +++ raw-poke + |= [=dock =cage] + =/ m (strand ,~) + ^- form:m + =/ =card:agent:gall [%pass /poke %agent dock %poke cage] + ;< ~ bind:m (send-raw-card card) + =/ m (strand ,~) + ^- form:m + |= tin=strand-input:strand + ?+ in.tin `[%skip ~] + ~ + `[%wait ~] + :: + [~ %agent * %poke-ack *] + ?. =(/poke wire.u.in.tin) + `[%skip ~] + `[%done ~] + == +:: ++ poke-our |= [=term =cage] =/ m (strand ,~) @@ -654,7 +673,8 @@ =/ m (strand ,tid:spider) ^- form:m ;< =bowl:spider bind:m get-bowl - =/ tid (scot %ta (cat 3 'strand_' (scot %uv (sham file eny.bowl)))) + =/ tid + (scot %ta (cat 3 (cat 3 'strand_' file) (scot %uv (sham file eny.bowl)))) =/ poke-vase !>([`tid.bowl `tid file *vase]) ;< ~ bind:m (poke-our %spider %spider-start poke-vase) ;< ~ bind:m (sleep ~s0) :: wait for thread to start diff --git a/pkg/arvo/mar/css.hoon b/pkg/arvo/mar/css.hoon index 39e9bc9450..f41e3390d2 100644 --- a/pkg/arvo/mar/css.hoon +++ b/pkg/arvo/mar/css.hoon @@ -14,7 +14,7 @@ -- ++ grab |% :: convert from - ++ mime |=({p/mite q/octs} (@t q.q)) + ++ mime |=([p=mite q=octs] (@t q.q)) ++ noun @t :: clam from %noun -- ++ grad %mime diff --git a/pkg/arvo/mar/graph/validator/link.hoon b/pkg/arvo/mar/graph/validator/link.hoon index dd8911d8b2..c276a8c3e0 100644 --- a/pkg/arvo/mar/graph/validator/link.hoon +++ b/pkg/arvo/mar/graph/validator/link.hoon @@ -5,8 +5,9 @@ ++ noun i ++ notification-kind ?+ index.p.i ~ - [@ ~] `[%link 0 %each] - [@ @ @ ~] `[%comment 1 %count] + [@ ~] `[%link 0 %each] + [@ @ %1 ~] `[%comment 1 %count] + [@ @ @ ~] `[%edit-comment 1 %count] == -- ++ grab diff --git a/pkg/arvo/mar/graph/validator/publish.hoon b/pkg/arvo/mar/graph/validator/publish.hoon index f043c4a8e4..c4c494475a 100644 --- a/pkg/arvo/mar/graph/validator/publish.hoon +++ b/pkg/arvo/mar/graph/validator/publish.hoon @@ -8,8 +8,10 @@ :: ++ notification-kind ?+ index.p.i ~ - [@ %1 @ ~] `[%note 0 %each] - [@ %2 @ @ ~] `[%comment 1 %count] + [@ %1 %1 ~] `[%note 0 %each] + [@ %1 @ ~] `[%edit-note 0 %each] + [@ %2 @ %1 ~] `[%comment 1 %count] + [@ %2 @ @ ~] `[%edit-comment 1 %count] == -- ++ grab diff --git a/pkg/arvo/mar/hoon.hoon b/pkg/arvo/mar/hoon.hoon index cd582f8a7b..3b4201aecf 100644 --- a/pkg/arvo/mar/hoon.hoon +++ b/pkg/arvo/mar/hoon.hoon @@ -41,7 +41,7 @@ -- ++ grab |% :: convert from - ++ mime |=({p/mite q/octs} q.q) + ++ mime |=([p=mite q=octs] q.q) ++ noun @t :: clam from %noun ++ txt of-wain:format -- diff --git a/pkg/arvo/mar/html.hoon b/pkg/arvo/mar/html.hoon index c6aeef3bc0..8c89e536d8 100644 --- a/pkg/arvo/mar/html.hoon +++ b/pkg/arvo/mar/html.hoon @@ -17,7 +17,7 @@ ++ grab ^? |% :: convert from ++ noun @t :: clam from %noun - ++ mime |=({p/mite q/octs} q.q) :: retrieve form $mime + ++ mime |=([p=mite q=octs] q.q) :: retrieve form $mime -- ++ grad %mime -- diff --git a/pkg/arvo/mar/js.hoon b/pkg/arvo/mar/js.hoon index 1c997c7ada..4b06573c7f 100644 --- a/pkg/arvo/mar/js.hoon +++ b/pkg/arvo/mar/js.hoon @@ -15,7 +15,7 @@ -- ++ grab |% :: convert from - ++ mime |=({p/mite q/octs} (@t q.q)) + ++ mime |=([p=mite q=octs] (@t q.q)) ++ noun cord :: clam from %noun -- ++ grad %mime diff --git a/pkg/arvo/mar/json.hoon b/pkg/arvo/mar/json.hoon index 967b60868d..e79bc7cd88 100644 --- a/pkg/arvo/mar/json.hoon +++ b/pkg/arvo/mar/json.hoon @@ -17,7 +17,7 @@ -- ++ grab |% :: convert from - ++ mime |=({p/mite q/octs} (fall (rush (@t q.q) apex:de-json) *json)) + ++ mime |=([p=mite q=octs] (fall (rush (@t q.q) apex:de-json) *json)) ++ noun json :: clam from %noun ++ numb numb:enjs ++ time time:enjs diff --git a/pkg/arvo/mar/mime.hoon b/pkg/arvo/mar/mime.hoon index f5b1850a00..222047545d 100644 --- a/pkg/arvo/mar/mime.hoon +++ b/pkg/arvo/mar/mime.hoon @@ -16,7 +16,7 @@ |% +$ noun mime :: clam from %noun ++ tape - |=(a/_"" [/application/x-urb-unknown (as-octt:mimes:html a)]) + |=(a=^tape [/application/x-urb-unknown (as-octt:mimes:html a)]) -- ++ grad ^? diff --git a/pkg/arvo/mar/png.hoon b/pkg/arvo/mar/png.hoon index 4bc5b6b3d9..145b213850 100644 --- a/pkg/arvo/mar/png.hoon +++ b/pkg/arvo/mar/png.hoon @@ -5,7 +5,7 @@ -- ++ grab |% - ++ mime |=({p/mite q/octs} q.q) + ++ mime |=([p=mite q=octs] q.q) ++ noun @t -- ++ grad %mime diff --git a/pkg/arvo/mar/snip.hoon b/pkg/arvo/mar/snip.hoon index e6eb3225ee..302cf0b792 100644 --- a/pkg/arvo/mar/snip.hoon +++ b/pkg/arvo/mar/snip.hoon @@ -7,7 +7,7 @@ ++ words 1 ++ hedtal =| met/marl - |= a/marl ^- {hed/marl tal/marl} + |= a=marl ^- {hed/marl tal/marl} ?~ a [~ ~] ?. ?=($h1 n.g.i.a) ?: ?=($meta n.g.i.a) @@ -18,7 +18,7 @@ [c.i.a (weld (flop met) (limit words t.a))] :: ++ limit - |= {lim/@u mal/marl} + |= [lim=@u mal=marl] =< res |- ^- {rem/@u res/marl} ?~ mal [lim ~] @@ -30,7 +30,7 @@ [rem - res]:[hed $(lim lam, mal t.mal)] :: ++ deword - |= {lim/@u tay/tape} ^- {lim/@u tay/tape} + |= [lim=@u tay=tape] ^- {lim/@u tay/tape} ?~ tay [lim tay] ?~ lim [0 ~] =+ wer=(dot 1^1 tay) @@ -58,5 +58,5 @@ -- ++ grab |% :: convert from ++ noun {marl marl} :: clam from $noun - ++ elem |=(a/manx (hedtal +.a)) + ++ elem |=(a=manx (hedtal +.a)) -- -- diff --git a/pkg/arvo/mar/tang.hoon b/pkg/arvo/mar/tang.hoon index 6847515430..2e212b13d9 100644 --- a/pkg/arvo/mar/tang.hoon +++ b/pkg/arvo/mar/tang.hoon @@ -12,11 +12,11 @@ ++ elem =- ;pre:code:"{(of-wall -)}" ^- wall %- zing ^- (list wall) - (turn (flop tan) |=(a/tank (wash 0^160 a))) + (turn (flop tan) |=(a=tank (wash 0^160 a))) -- ++ grab :: convert from |% ++ noun (list ^tank) :: clam from %noun - ++ tank |=(a/^tank [a]~) + ++ tank |=(a=^tank [a]~) -- -- diff --git a/pkg/arvo/mar/txt.hoon b/pkg/arvo/mar/txt.hoon index 20ca73547b..4ea82b7e98 100644 --- a/pkg/arvo/mar/txt.hoon +++ b/pkg/arvo/mar/txt.hoon @@ -24,18 +24,18 @@ |% ++ form %txt-diff ++ diff - |= tyt/wain + |= tyt=wain ^- (urge cord) (lusk txt tyt (loss txt tyt)) :: ++ pact - |= dif/(urge cord) + |= dif=(urge cord) ~| [%pacting dif] ^- wain (lurk txt dif) :: ++ join - |= {ali/(urge cord) bob/(urge cord)} + |= [ali=(urge cord) bob=(urge cord)] ^- (unit (urge cord)) |^ =. ali (clean ali) @@ -49,20 +49,20 @@ %& ?: =(p.i.ali p.i.bob) %+ bind $(ali t.ali, bob t.bob) - |=(cud/(urge cord) [i.ali cud]) + |=(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]) + |=(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]) + |=(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]) + |=(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]) + |=(cud=(urge cord) [i.bob cud]) ~ == :: @@ -77,15 +77,15 @@ %& ?: =(p.i.bob (lent p.i.ali)) %+ bind $(ali t.ali, bob t.bob) - |=(cud/(urge cord) [i.ali cud]) + |=(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]) + |=(cud=(urge cord) [i.ali cud]) ~ == == ++ clean :: clean - |= wig/(urge cord) + |= wig=(urge cord) ^- (urge cord) ?~ wig ~ ?~ t.wig wig @@ -179,7 +179,7 @@ ~ :: ++ clean :: clean - |= wig/(urge cord) + |= wig=(urge cord) ^- (urge cord) ?~ wig ~ ?~ t.wig wig @@ -192,7 +192,7 @@ [i.wig $(wig t.wig)] :: ++ resolve - |= {ali/(urge cord) bob/(urge cord)} + |= [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) diff --git a/pkg/arvo/mar/xml.hoon b/pkg/arvo/mar/xml.hoon index 01ac140fbf..b9705e5a1d 100644 --- a/pkg/arvo/mar/xml.hoon +++ b/pkg/arvo/mar/xml.hoon @@ -17,5 +17,5 @@ -- :: ++ grab |% :: convert from ++ noun @t :: clam from %noun - ++ mime |=({p/mite q/octs} q.q) :: retrieve form $mime + ++ mime |=([p=mite q=octs] q.q) :: retrieve form $mime -- -- diff --git a/pkg/arvo/sur/aquarium.hoon b/pkg/arvo/sur/aquarium.hoon index db760b355f..d9fa4703eb 100644 --- a/pkg/arvo/sur/aquarium.hoon +++ b/pkg/arvo/sur/aquarium.hoon @@ -13,6 +13,12 @@ /+ pill =, pill-lib=pill |% ++$ az-log [topics=(lest @) data=@t] ++$ az-state + $: logs=(list az-log) + lives=(map ship [lyfe=life rut=rift]) + tym=@da + == ++ ph-event $% [%test-done p=?] aqua-event @@ -29,6 +35,12 @@ [%event who=ship ue=unix-event] == :: ++$ azimuth-action + $% [%init-azimuth ~] + [%spawn who=ship] + [%breach who=ship] + == +:: +$ aqua-effects [who=ship ufs=(list unix-effect)] :: @@ -57,6 +69,7 @@ [%ergo p=@tas q=mode:clay] [%sleep ~] [%restore ~] + [%kill ~] [%init ~] [%request id=@ud request=request:http] == diff --git a/pkg/arvo/ted/aqua/behn.hoon b/pkg/arvo/ted/aqua/behn.hoon index 274c45d0e9..9b7e477384 100644 --- a/pkg/arvo/ted/aqua/behn.hoon +++ b/pkg/arvo/ted/aqua/behn.hoon @@ -85,7 +85,7 @@ -- -- :: -%+ aqua-vane-thread ~[%sleep %restore %doze] +%+ aqua-vane-thread ~[%sleep %restore %doze %kill] |_ =bowl:spider +* this . ++ handle-unix-effect @@ -96,6 +96,7 @@ %sleep abet-pe:handle-sleep:(pe bowl who) %restore abet-pe:handle-restore:(pe bowl who) %doze abet-pe:(handle-doze:(pe bowl who) ue) + %kill `(~(del by piers) who) == [cards this] :: diff --git a/pkg/arvo/ted/aqua/dill.hoon b/pkg/arvo/ted/aqua/dill.hoon index adc03dc41d..e56120a64d 100644 --- a/pkg/arvo/ted/aqua/dill.hoon +++ b/pkg/arvo/ted/aqua/dill.hoon @@ -25,6 +25,7 @@ %sag ~& [%save-jamfile-to p.b] line %sav ~& [%save-file-to p.b] line %url ~& [%activate-url p.b] line + %klr ~& %unhandled-case-klr "" == ~? !=(~ last-line) last-line ~ diff --git a/pkg/arvo/ted/aqua/eyre.hoon b/pkg/arvo/ted/aqua/eyre.hoon index 9cfa5c285b..baf512012e 100644 --- a/pkg/arvo/ted/aqua/eyre.hoon +++ b/pkg/arvo/ted/aqua/eyre.hoon @@ -100,7 +100,7 @@ -- -- :: -%+ aqua-vane-thread ~[%sleep %restore %thus] +%+ aqua-vane-thread ~[%sleep %restore %thus %kill] |_ =bowl:spider +* this . ++ handle-unix-effect @@ -111,6 +111,7 @@ %sleep abet-pe:handle-sleep:(pe bowl who) %restore abet-pe:handle-restore:(pe bowl who) %thus abet-pe:(handle-thus:(pe bowl who) ue) + %kill `(~(del by piers) who) == [cards this] :: diff --git a/pkg/arvo/ted/group/on-leave.hoon b/pkg/arvo/ted/group/on-leave.hoon new file mode 100644 index 0000000000..aa3fcad4e2 --- /dev/null +++ b/pkg/arvo/ted/group/on-leave.hoon @@ -0,0 +1,94 @@ +/- spider, grp=group-store, gra=graph-store, met=metadata-store, con=contact-store +/+ strandio, res=resource +:: +=* strand strand:spider +=* raw-poke raw-poke:strandio +=* scry scry:strandio +:: +^- thread:spider +|= arg=vase +=/ m (strand ,vase) +^- form:m +=+ !<([=update:grp ~] arg) +?. ?=(%remove-group -.update) + (pure:m !>(~)) +;< =bowl:spider bind:m get-bowl:strandio +:: tell group host to remove us as member +:: +;< ~ bind:m + %+ raw-poke + [entity.resource.update %group-push-hook] + :- %group-update + !> ^- update:grp + [%remove-members resource.update (silt [our.bowl ~])] +:: stop serving or syncing group updates +:: +;< ~ bind:m + %+ raw-poke + [our.bowl %group-push-hook] + :- %push-hook-action + !>([%remove resource.update]) +;< ~ bind:m + %+ raw-poke + [our.bowl %group-pull-hook] + :- %pull-hook-action + !>([%remove resource.update]) +:: stop serving or syncing contacts associated with group +:: +;< ~ bind:m + %+ raw-poke + [our.bowl %contact-hook] + :- %contact-hook-action + !>([%remove (en-path:res resource.update)]) +:: remove contact data associated with group +:: +;< ~ bind:m + %+ raw-poke + [our.bowl %contact-store] + :- %contact-action + !> ^- contact-action:con + [%delete (en-path:res resource.update)] +:: stop serving or syncing metadata associated with group +:: +;< ~ bind:m + %+ raw-poke + [our.bowl %metadata-hook] + :- %metadata-hook-action + !>([%remove (en-path:res resource.update)]) +:: get metadata associated with group +:: +;< =associations:met bind:m + %+ scry associations:met + ;: weld + /gx/metadata-store/group + (en-path:res resource.update) + /noun + == +=/ entries=(list [g=group-path:met m=md-resource:met]) + ~(tap in ~(key by associations)) +|- ^- form:m +=* loop $ +?~ entries + (pure:m !>(~)) +:: remove metadata associated with group +:: +;< ~ bind:m + %+ raw-poke + [our.bowl %metadata-store] + :- %metadata-action + !> ^- metadata-action:met + [%remove g.i.entries m.i.entries] +:: archive graph associated with group +:: +;< ~ bind:m + %+ raw-poke + [our.bowl %graph-store] + :- %graph-update + !> ^- update:gra + [%0 now.bowl [%archive-graph (de-path:res app-path.m.i.entries)]] +;< ~ bind:m + %+ raw-poke + [our.bowl %graph-pull-hook] + :- %pull-hook-action + !>([%remove (de-path:res app-path.m.i.entries)]) +loop(entries t.entries) diff --git a/pkg/arvo/ted/ph/breach-hi-aqua.hoon b/pkg/arvo/ted/ph/breach-hi-aqua.hoon new file mode 100644 index 0000000000..473ddb846b --- /dev/null +++ b/pkg/arvo/ted/ph/breach-hi-aqua.hoon @@ -0,0 +1,19 @@ +/- spider +/+ *ph-io, *ph-util +=, strand=strand:spider +^- thread:spider +|= vase +=/ m (strand ,vase) +;< =bowl:spider bind:m get-bowl +;< ~ bind:m start-simple +;< ~ bind:m init-azimuth +;< ~ bind:m (spawn-aqua ~bud) +;< ~ bind:m (spawn-aqua ~dev) +;< ~ bind:m (init-ship ~bud) +;< ~ bind:m (init-ship ~dev) +;< ~ bind:m (send-hi ~bud ~dev) +;< ~ bind:m (breach-and-hear-aqua ~dev ~bud) +;< ~ bind:m (send-hi-not-responding ~bud ~dev) +;< ~ bind:m (init-ship ~dev) +;< ~ bind:m (wait-for-output ~bud "hi ~dev successful") +(pure:m *vase) diff --git a/pkg/arvo/ted/ph/start-drivers.hoon b/pkg/arvo/ted/ph/start-drivers.hoon new file mode 100644 index 0000000000..825a2b814a --- /dev/null +++ b/pkg/arvo/ted/ph/start-drivers.hoon @@ -0,0 +1,13 @@ +/- spider +/+ *ph-io, *ph-util +=, strand=strand:spider +^- thread:spider +|= vase +=/ m (strand ,vase) +;< =bowl:spider bind:m get-bowl +;< ~ bind:m start-simple +|- +=* loop $ +~& >> %looping +;< ~ bind:m (sleep ~s5) +loop diff --git a/pkg/interface/package-lock.json b/pkg/interface/package-lock.json index 1adb54bcd5..09d985c69c 100644 --- a/pkg/interface/package-lock.json +++ b/pkg/interface/package-lock.json @@ -1693,9 +1693,9 @@ "integrity": "sha512-3OPSdf9cejP/TSzWXuBaYbzLtAfBzQnc75SlPLkoPfwpxnv1Bvy9hiWngLY0WnKRR6lMOldnkYQCCuNWeDibYQ==" }, "@tlon/indigo-react": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.2.13.tgz", - "integrity": "sha512-6qYLjVcGZtDjI+BqS2PRrfAh9mUCDtYwDOHuYuPyV87mdVRAhduBlQ/3tDVlTNWICF9DeAhozeClxalACs5Ipw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.2.5.tgz", + "integrity": "sha512-NOQTwH74l/XXMIfQ4ZzymvZuk1WK1nmO552TmXrQxBUSb7HmdlA8anG5oRrvnLJTkajLCY59McLkDca+lCcvwg==", "requires": { "@reach/menu-button": "^0.10.5", "react": "^16.13.1", diff --git a/pkg/interface/package.json b/pkg/interface/package.json index 5786f7494e..af7d355078 100644 --- a/pkg/interface/package.json +++ b/pkg/interface/package.json @@ -9,7 +9,7 @@ "@reach/menu-button": "^0.10.5", "@reach/tabs": "^0.10.5", "@tlon/indigo-light": "^1.0.3", - "@tlon/indigo-react": "1.2.13", + "@tlon/indigo-react": "1.2.5", "@tlon/sigil-js": "^1.4.2", "aws-sdk": "^2.726.0", "big-integer": "^1.6.48", diff --git a/pkg/interface/src/logic/api/contacts.ts b/pkg/interface/src/logic/api/contacts.ts index 27f8faf25c..3049442f5a 100644 --- a/pkg/interface/src/logic/api/contacts.ts +++ b/pkg/interface/src/logic/api/contacts.ts @@ -32,10 +32,6 @@ export default class ContactsApi extends BaseApi { }); } - delete(path: Path) { - return this.viewAction({ delete: { path } }); - } - remove(path: Path, ship: Patp) { return this.viewAction({ remove: { path, ship } }); } diff --git a/pkg/interface/src/logic/api/groups.ts b/pkg/interface/src/logic/api/groups.ts index cdd1514ecc..3901149fd2 100644 --- a/pkg/interface/src/logic/api/groups.ts +++ b/pkg/interface/src/logic/api/groups.ts @@ -26,6 +26,10 @@ export default class GroupsApi extends BaseApi { return this.proxyAction({ addMembers: { resource, ships } }); } + removeGroup(resource: Resource) { + return this.storeAction({ removeGroup: { resource } }); + } + changePolicy(resource: Resource, diff: Enc) { return this.proxyAction({ changePolicy: { resource, diff } }); } @@ -35,6 +39,7 @@ export default class GroupsApi extends BaseApi { } private storeAction(action: GroupAction) { - return this.action('group-store', 'group-action', action); + console.log(action); + return this.action('group-store', 'group-update', action); } } diff --git a/pkg/interface/src/logic/api/launch.ts b/pkg/interface/src/logic/api/launch.ts index bbe49db730..ce0a09a1c0 100644 --- a/pkg/interface/src/logic/api/launch.ts +++ b/pkg/interface/src/logic/api/launch.ts @@ -18,8 +18,8 @@ export default class LaunchApi extends BaseApi { return this.launchAction({ 'change-is-shown': { name, isShown }}); } - weather(latlng: any) { - return this.action('weather', 'json', latlng); + weather(location: string) { + return this.action('weather', 'json', location); } private launchAction(data) { diff --git a/pkg/interface/src/logic/lib/publish.ts b/pkg/interface/src/logic/lib/publish.ts index e564cb068d..10c64aecf3 100644 --- a/pkg/interface/src/logic/lib/publish.ts +++ b/pkg/interface/src/logic/lib/publish.ts @@ -102,13 +102,13 @@ export function getLatestCommentRevision(node: GraphNode): [number, Post] { export function getComments(node: GraphNode): GraphNode { const comments = node.children.get(bigInt(2)); if(!comments) { - return { post: buntPost(), children: new BigIntOrderedMap() } + return { post: buntPost(), children: new BigIntOrderedMap() } } return comments; } export function getSnippet(body: string) { - const start = body.slice(0, 400); - return start === body ? start : `${start}...`; + const start = body.slice(0, body.indexOf('\n', 2)); + return (start === body || start.startsWith("![")) ? start : `${start}...`; } - + diff --git a/pkg/interface/src/views/App.js b/pkg/interface/src/views/App.js index f3315809ce..c44ed52914 100644 --- a/pkg/interface/src/views/App.js +++ b/pkg/interface/src/views/App.js @@ -14,6 +14,8 @@ import './css/fonts.css'; import light from './themes/light'; import dark from './themes/old-dark'; +import { Text, Anchor, Row } from '@tlon/indigo-react'; + import { Content } from './landscape/components/Content'; import StatusBar from './components/StatusBar'; import Omnibox from './components/leap/Omnibox'; @@ -25,6 +27,7 @@ import GlobalApi from '~/logic/api/global'; import { uxToHex } from '~/logic/lib/util'; import { foregroundFromBackground } from '~/logic/lib/sigil'; + const Root = styled.div` font-family: ${p => p.theme.fonts.sans}; height: 100%; @@ -128,6 +131,9 @@ class App extends React.Component { const notificationsCount = state.notificationsCount || 0; const doNotDisturb = state.doNotDisturb || false; + const showBanner = localStorage.getItem("2020BreachBanner") || "flex"; + let banner = null; + return ( @@ -136,6 +142,21 @@ class App extends React.Component { : null} + banner = e} + display={showBanner} + justifyContent="space-between" + width='100%' + p='2' + backgroundColor='yellow'> + + A network-wide breach is scheduled for early December 2020. Please visit urbit.org/breach for more information. + + { + banner.style.display = "none"; + localStorage.setItem("2020BreachBanner", "none"); + }}>Dismiss + { contact={contact} color={color} sigilClass={sigilClass} - association={association} group={group} hideAvatars={hideAvatars} hideNicknames={hideNicknames} scrollWindow={scrollWindow} history={history} api={api} - className="fl pr3 v-top pt1 flex-shrink-0" + bg="white" + className="fl pr3 v-top pt1" /> - + diff --git a/pkg/interface/src/views/apps/chat/components/overlay-sigil.js b/pkg/interface/src/views/apps/chat/components/overlay-sigil.js deleted file mode 100644 index 6194b16310..0000000000 --- a/pkg/interface/src/views/apps/chat/components/overlay-sigil.js +++ /dev/null @@ -1,100 +0,0 @@ -import React, { PureComponent } from 'react'; -import { Sigil } from '~/logic/lib/sigil'; -import { - ProfileOverlay, - OVERLAY_HEIGHT -} from './profile-overlay'; -import { Box, BaseImage } from '@tlon/indigo-react'; - -export class OverlaySigil extends PureComponent { - constructor() { - super(); - this.state = { - clicked: false, - captured: false, - topSpace: 0, - bottomSpace: 0 - }; - - this.containerRef = React.createRef(); - - this.profileShow = this.profileShow.bind(this); - this.profileHide = this.profileHide.bind(this); - this.updateContainerOffset = this.updateContainerOffset.bind(this); - this.updateContainerInterval = null; - } - - profileShow() { - this.updateContainerOffset(); - this.setState({ profileClicked: true }); - this.props.scrollWindow.addEventListener('scroll', this.updateContainerOffset); - } - - profileHide() { - this.setState({ profileClicked: false }); - this.props.scrollWindow.removeEventListener('scroll', this.updateContainerOffset, true); - } - - updateContainerOffset() { - if (this.containerRef && this.containerRef.current) { - const container = this.containerRef.current; - const scrollWindow = this.props.scrollWindow; - - const bottomSpace = scrollWindow.scrollHeight - container.offsetTop - scrollWindow.scrollTop; - const topSpace = scrollWindow.offsetHeight - bottomSpace - OVERLAY_HEIGHT; - - this.setState({ - topSpace, - bottomSpace - }); - } - } - - componentWillUnmount() { - this.props.scrollWindow?.removeEventListener('scroll', this.updateContainerOffset, true); - } - - render() { - const { props, state } = this; - const { hideAvatars } = props; - - const img = (props.contact && (props.contact.avatar !== null) && !hideAvatars) - ? - : ; - - return ( - - {state.profileClicked && ( - - )} - {img} - - ); - } -} diff --git a/pkg/interface/src/views/apps/launch/app.js b/pkg/interface/src/views/apps/launch/app.js index 4d7eea7e34..bd1877a1cd 100644 --- a/pkg/interface/src/views/apps/launch/app.js +++ b/pkg/interface/src/views/apps/launch/app.js @@ -10,7 +10,10 @@ import Tiles from './components/tiles'; import Tile from './components/tiles/tile'; import Welcome from './components/welcome'; import Groups from './components/Groups'; +import ModalButton from './components/ModalButton'; import { writeText } from '~/logic/lib/util'; +import { NewGroup } from "~/views/landscape/components/NewGroup"; +import { JoinGroup } from "~/views/landscape/components/JoinGroup"; const ScrollbarLessBox = styled(Box)` scrollbar-width: none !important; @@ -39,19 +42,18 @@ export default function LaunchApp(props) { pt={0} > - + - DMs + Drafts + DMs + Drafts @@ -62,7 +64,23 @@ export default function LaunchApp(props) { location={props.userLocation} weather={props.weather} /> - + + + + + + diff --git a/pkg/interface/src/views/apps/launch/components/Groups.tsx b/pkg/interface/src/views/apps/launch/components/Groups.tsx index 952e18c27b..bf6a55d856 100644 --- a/pkg/interface/src/views/apps/launch/components/Groups.tsx +++ b/pkg/interface/src/views/apps/launch/components/Groups.tsx @@ -44,7 +44,7 @@ export default function Groups(props: GroupsProps & Parameters[0]) { return ( <> - {groups.map((group) => { + {groups.map((group, index) => { const path = group?.["group-path"]; const unreadCount = graphUnreads(path) const notCount = graphNotifications(path); @@ -52,6 +52,7 @@ export default function Groups(props: GroupsProps & Parameters[0]) { return ( + {title} diff --git a/pkg/interface/src/views/apps/launch/components/ModalButton.tsx b/pkg/interface/src/views/apps/launch/components/ModalButton.tsx new file mode 100644 index 0000000000..aa17ae40c7 --- /dev/null +++ b/pkg/interface/src/views/apps/launch/components/ModalButton.tsx @@ -0,0 +1,81 @@ +import React, { useState, useEffect } from "react" +import { Box, Button, Icon, Text } from "@tlon/indigo-react" +import { NewGroup } from "~/views/landscape/components/NewGroup"; +import { JoinGroup } from "~/views/landscape/components/JoinGroup"; + +const ModalButton = (props) => { + const { + childen, + icon, + text, + bg, + color, + ...rest + } = props; + const [modalShown, setModalShown] = useState(false); + + const handleKeyDown = (event) => { + if (event.key === 'Escape') { + setModalShown(false); + } + } + + useEffect(() => { + window.addEventListener('keydown', handleKeyDown); + + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, [modalShown]); + + return ( + <> + {modalShown && ( + setModalShown(false)} + > + e.stopPropagation()} + display="flex" + alignItems="stretch" + flexDirection="column" + > + {props.children} + + + )} + setModalShown(true)} + display="flex" + alignItems="center" + cursor="pointer" + bg={bg} + p={2} + borderRadius={2} + boxShadow="0 0 0px 1px inset" + color="scales.black20" + {...rest} + > + {props.text} + + + ); +} + +export default ModalButton; \ No newline at end of file diff --git a/pkg/interface/src/views/apps/launch/components/tiles/tile.js b/pkg/interface/src/views/apps/launch/components/tiles/tile.js index 348234d5e1..d83862a358 100644 --- a/pkg/interface/src/views/apps/launch/components/tiles/tile.js +++ b/pkg/interface/src/views/apps/launch/components/tiles/tile.js @@ -18,12 +18,13 @@ const SquareBox = styled(Box)` position: absolute; top: 0; } + position: relative; `; const routeList = defaultApps.map(a => `/~${a}`); export default class Tile extends React.Component { render() { - const { bg, to, href, p, boxShadow, ...props } = this.props; + const { bg, to, href, p, boxShadow, gridColumnStart, ...props } = this.props; let childElement = ( @@ -32,7 +33,7 @@ export default class Tile extends React.Component { ); if (to) { - if (routeList.indexOf(to) !== -1 || to === '/~landscape/home' || to === '/~profile' || to.startsWith('/~landscape/ship')) { + if (routeList.indexOf(to) !== -1 || to === '/~profile' || to.startsWith('/~landscape/')) { childElement= ({childElement}); } else { childElement= ({childElement}); @@ -48,6 +49,7 @@ export default class Tile extends React.Component { bg={bg || "white"} color={props?.color || 'scales.black20'} boxShadow={boxShadow || '0 0 0px 1px inset'} + style={{ gridColumnStart }} > { - const latlng = `${res.coords.latitude},${res.coords.longitude}`; + const location = `${res.coords.latitude},${res.coords.longitude}`; this.setState({ - latlng + location }, (err) => { console.log(err); }, { maximumAge: Infinity, timeout: 10000 }); - this.props.api.launch.weather(latlng); + this.props.api.launch.weather(location); this.setState({ manualEntry: !this.state.manualEntry }); }); } - manualLocationSubmit() { + manualLocationSubmit(event) { event.preventDefault(); - const gpsInput = document.getElementById('gps'); - const latlngNoSpace = gpsInput.value.replace(/\s+/g, ''); - const latlngParse = /-?[0-9]+(?:\.[0-9]*)?,-?[0-9]+(?:\.[0-9]*)?/g; - if (latlngParse.test(latlngNoSpace)) { - const latlng = latlngNoSpace; - this.setState({ latlng }, (err) => { + const location = document.getElementById('location').value; + this.setState({ location }, (err) => { console.log(err); }, { maximumAge: Infinity, timeout: 10000 }); - this.props.api.launch.weather(latlng); + this.props.api.launch.weather(location); this.setState({ manualEntry: !this.state.manualEntry }); - } else { - this.setState({ error: true }); - return false; - } } - // set appearance based on weather - setColors(data) { - let weatherStyle = { - bg: '', - text: '' - }; - switch (data.currently.icon) { - case 'clear-day': - weatherStyle = { bg: '#E9F5FF', text: '#333' }; - break; - case 'clear-night': - weatherStyle = { bg: '#14263C', text: '#fff' }; - break; - case 'rain': - weatherStyle = { bg: '#2E1611', text: '#fff' }; - break; - case 'snow': - weatherStyle = { bg: '#F9F9FB', text: '#333' }; - break; - case 'sleet': - weatherStyle = { bg: '#EFF1F3', text: '#333' }; - break; - case 'wind': - weatherStyle = { bg: '#F7FEF6', text: '#333' }; - break; - case 'fog': - weatherStyle = { bg: '#504D44', text: '#fff' }; - break; - case 'cloudy': - weatherStyle = { bg: '#EEF1F5', text: '#333' }; - break; - case 'partly-cloudy-day': - weatherStyle = { bg: '#F3F6FA', text: '#333' }; - break; - case 'partly-cloudy-night': - weatherStyle = { bg: '#283442', text: '#fff' }; - break; - default: - weatherStyle = { bg: 'white', text: 'black' }; - } - return weatherStyle; + // set appearance based on weather + colorFromCondition(data) { + let weatherDesc = data['current-condition'][0].weatherDesc[0].value; + return weatherStyleMap[weatherDesc] || weatherStyleMap.default; } + // all tile views - renderWrapper(child, - weatherStyle = { bg: 'white', text: 'black' } - ) { + renderWrapper(child, backgroundColor = 'white') { return ( - - {child} - + + + {child} + + ); } - renderManualEntry() { + renderManualEntry(data) { let secureCheck; let error; if (this.state.error === true) { @@ -114,6 +99,10 @@ export default class WeatherTile extends React.Component { ); } + let locationName; + if ('nearest-area' in data) { + locationName = data['nearest-area'][0].areaName[0].value; + } return this.renderWrapper( {secureCheck} - Please enter your{' '} - - latitude and longitude - - . + Please enter your location. + {locationName ? ` Current location is near ${locationName}.` : ''} {error} { if (e.key === 'Enter') { - e.preventDefault(); - this.manualLocationSubmit(e.target.value); + this.manualLocationSubmit(e); } }} /> @@ -171,7 +152,7 @@ export default class WeatherTile extends React.Component { fontSize='0' border='0' type="submit" - onClick={() => this.manualLocationSubmit()} + onClick={this.manualLocationSubmit.bind(this)} value="->" /> @@ -182,7 +163,6 @@ export default class WeatherTile extends React.Component { renderNoData() { return this.renderWrapper( 24) { @@ -220,6 +202,10 @@ export default class WeatherTile extends React.Component { ? `Sun sets in ${sunsetDiff}h` : `Sun rises in ${sunriseDiff}h`; + const temp = data['nearest-area'] && imperialCountries.includes(data['nearest-area'][0].country[0].value) + ? `${Math.round(c.temp_F)}℉` + : `${Math.round(c.temp_C)}℃`; + return this.renderWrapper( - - + + Weather this.setState({ manualEntry: !this.state.manualEntry }) @@ -248,42 +234,56 @@ export default class WeatherTile extends React.Component { display="flex" flexDirection="column" > - {c.summary} - {Math.round(c.temperature)}° - {nextSolarEvent} + {c.weatherDesc[0].value.replace(/([a-z])([A-Z])/g, '$1 $2')} + {temp} + {nextSolarEvent} - - , weatherStyle); + , bg); } render() { const data = this.props.weather ? this.props.weather : {}; if (this.state.manualEntry === true) { - return this.renderManualEntry(); + return this.renderManualEntry(data); } - if ('currently' in data && 'daily' in data) { - const weatherStyle = this.setColors(data); - return this.renderWithData(data, weatherStyle); + if ('currently' in data) { // Old weather source + this.props.api.launch.weather(this.props.location); + } + + if ('current-condition' in data && 'weather' in data) { + return this.renderWithData(data); } if (this.props.location) { - return this.renderWrapper(( + return this.renderWrapper( - - Weather - - Loading, please check again later... + Weather + + Loading, please check again later... + + + Set new location{' '} + + this.setState({ manualEntry: !this.state.manualEntry }) + } + > + -> + - )); + ); } return this.renderNoData(); } diff --git a/pkg/interface/src/views/apps/links/LinkResource.tsx b/pkg/interface/src/views/apps/links/LinkResource.tsx index b3764d108b..98797e2aa3 100644 --- a/pkg/interface/src/views/apps/links/LinkResource.tsx +++ b/pkg/interface/src/views/apps/links/LinkResource.tsx @@ -1,15 +1,14 @@ import React, { useEffect } from "react"; -import { Box, Row, Col, Center, LoadingSpinner } from "@tlon/indigo-react"; +import { Box, Row, Col, Center, LoadingSpinner, Text } from "@tlon/indigo-react"; import { Switch, Route, Link } from "react-router-dom"; import bigInt from 'big-integer'; import GlobalApi from "~/logic/api/global"; import { StoreState } from "~/logic/store/type"; import { uxToHex } from '~/logic/lib/util'; -import { Association, GraphNode } from "~/types"; import { RouteComponentProps } from "react-router-dom"; -import { LinkItem } from "./components/link-item"; +import { LinkItem } from "./components/LinkItem"; import { LinkSubmit } from "./components/link-submit"; import { LinkPreview } from "./components/link-preview"; import { Comments } from "~/views/components/comments"; @@ -79,18 +78,21 @@ export function LinkResource(props: LinkResourceProps) { const contact = contactDetails[node.post.author]; return ( ); })} @@ -99,7 +101,7 @@ export function LinkResource(props: LinkResourceProps) { }} /> { const index = bigInt(props.match.params.index); const editCommentId = props.match.params.commentId || null; @@ -117,17 +119,21 @@ export function LinkResource(props: LinkResourceProps) { const contact = contactDetails[node.post.author]; return ( - - {"<- Back"} - + {"<- Back"} + ); diff --git a/pkg/interface/src/views/apps/links/components/LinkItem.tsx b/pkg/interface/src/views/apps/links/components/LinkItem.tsx new file mode 100644 index 0000000000..c2d3315283 --- /dev/null +++ b/pkg/interface/src/views/apps/links/components/LinkItem.tsx @@ -0,0 +1,164 @@ +import React, { useState } from 'react'; +import { Link } from 'react-router-dom'; +import { Row, Col, Anchor, Box, Text, BaseImage, Icon, Action } from '@tlon/indigo-react'; + +import { Sigil } from '~/logic/lib/sigil'; +import { writeText } from '~/logic/lib/util'; +import Author from '~/views/components/Author'; + +import { roleForShip } from '~/logic/lib/group'; +import { Contacts, GraphNode, Group, LocalUpdateRemoteContentPolicy, Rolodex } from '~/types'; +import GlobalApi from '~/logic/api/global'; +import { Dropdown } from '~/views/components/Dropdown'; +import RemoteContent from '~/views/components/RemoteContent'; + +interface LinkItemProps { + node: GraphNode; + resource: string; + hideAvatars: boolean; + hideNicknames: boolean; + remoteContentPolicy: LocalUpdateRemoteContentPolicy; + api: GlobalApi; + group: Group; + path: string; + contacts: Rolodex[]; +} + +export const LinkItem = (props: LinkItemProps) => { + const { + node, + resource, + hideAvatars, + hideNicknames, + remoteContentPolicy, + api, + group, + path, + contacts, + ...rest + } = props; + + const URLparser = new RegExp( + /((?:([\w\d\.-]+)\:\/\/?){1}(?:(www)\.?){0,1}(((?:[\w\d-]+\.)*)([\w\d-]+\.[\w\d]+))){1}(?:\:(\d+)){0,1}((\/(?:(?:[^\/\s\?]+\/)*))(?:([^\?\/\s#]+?(?:.[^\?\s]+){0,1}){0,1}(?:\?([^\s#]+)){0,1})){0,1}(?:#([^#\s]+)){0,1}/ + ); + + const author = node.post.author; + const index = node.post.index.split('/')[1]; + const size = node.children ? node.children.size : 0; + const contents = node.post.contents; + const hostname = URLparser.exec(contents[1].url) ? URLparser.exec(contents[1].url)[4] : null; + + const baseUrl = props.baseUrl || `/~404/${resource}`; + + const ourRole = group ? roleForShip(group, window.ship) : undefined; + const [ship, name] = resource.split('/'); + + const [locationText, setLocationText] = useState('Copy Link Location'); + + const copyLocation = () => { + setLocationText('Copied'); + writeText(contents[1].url); + setTimeout(() => { + setLocationText('Copy Link Location'); + }, 2000); + }; + + const deleteLink = () => { + if (confirm('Are you sure you want to delete this link?')) { + api.graph.removeNodes(`~${ship}`, name, [node.post.index]); + } + }; + + return ( + + + + + + + + {hostname} + + + + + + + + + + + + + + {node.children.size} + + + + + + + {locationText} + + {(ourRole === 'admin' || node.post.author === window.ship) && + + Delete Link + + } + + } + > + + + + + ); +}; + diff --git a/pkg/interface/src/views/apps/notifications/graph.tsx b/pkg/interface/src/views/apps/notifications/graph.tsx index 971bc4c783..02351e6455 100644 --- a/pkg/interface/src/views/apps/notifications/graph.tsx +++ b/pkg/interface/src/views/apps/notifications/graph.tsx @@ -42,8 +42,12 @@ function describeNotification(description: string, plural: boolean) { return `added ${pluralize("new link", plural)} to`; case "comment": return `left ${pluralize("comment", plural)} on`; + case "edit-comment": + return `updated ${pluralize("comment", plural)} on`; case "note": return `posted ${pluralize("note", plural)} to`; + case "edit-note": + return `updated ${pluralize("note", plural)} in`; case "mention": return "mentioned you on"; default: diff --git a/pkg/interface/src/views/apps/publish/components/Note.tsx b/pkg/interface/src/views/apps/publish/components/Note.tsx index 33ae9ec795..db344ceec6 100644 --- a/pkg/interface/src/views/apps/publish/components/Note.tsx +++ b/pkg/interface/src/views/apps/publish/components/Note.tsx @@ -9,7 +9,7 @@ import { Comments } from "~/views/components/Comments"; import { NoteNavigation } from "./NoteNavigation"; import GlobalApi from "~/logic/api/global"; import { getLatestRevision, getComments } from '~/logic/lib/publish'; -import { Author } from "./Author"; +import Author from "~/views/components/Author"; import { Contacts, GraphNode, Graph, LocalUpdateRemoteContentPolicy, Association, Unreads } from "~/types"; interface NoteProps { diff --git a/pkg/interface/src/views/apps/publish/components/NotePreview.tsx b/pkg/interface/src/views/apps/publish/components/NotePreview.tsx index de06b5d9f0..ec2667fea6 100644 --- a/pkg/interface/src/views/apps/publish/components/NotePreview.tsx +++ b/pkg/interface/src/views/apps/publish/components/NotePreview.tsx @@ -1,28 +1,31 @@ -import React from "react"; -import { Col, Box } from "@tlon/indigo-react"; -import { cite } from "~/logic/lib/util"; -import { Note } from "~/types/publish-update"; -import { Contact } from "~/types/contact-update"; -import ReactMarkdown from "react-markdown"; -import moment from "moment"; -import { Link } from "react-router-dom"; -import styled from "styled-components"; -import { GraphNode } from "~/types/graph-update"; +import React from 'react'; +import { Link } from 'react-router-dom'; +import styled from 'styled-components'; +import { Col, Row, Box, Text, Icon, Image } from '@tlon/indigo-react'; + +import Author from '~/views/components/Author'; +import { GraphNode } from '~/types/graph-update'; +import { Contacts, Group } from '~/types'; import { getComments, getLatestRevision, getSnippet, } from "~/logic/lib/publish"; import {Unreads} from "~/types"; +import GlobalApi from '~/logic/api/global'; +import ReactMarkdown from 'react-markdown'; interface NotePreviewProps { host: string; book: string; node: GraphNode; - contact?: Contact; + hideAvatars?: boolean; hideNicknames?: boolean; baseUrl: string; unreads: Unreads; + contacts: Contacts; + api: GlobalApi; + group: Group; } const WrappedBox = styled(Box)` @@ -30,29 +33,14 @@ const WrappedBox = styled(Box)` `; export function NotePreview(props: NotePreviewProps) { - const { node, contact } = props; + const { node, contacts, hideAvatars, hideNicknames, group } = props; const { post } = node; if (!post) { return null; } - let name = post?.author; - if (contact && !props.hideNicknames) { - name = contact.nickname.length > 0 ? contact.nickname : post?.author; - } - if (name === post?.author) { - name = cite(post?.author); - } - const numComments = getComments(node).children.size; - const commentDesc = - numComments === 0 - ? "No Comments" - : numComments === 1 - ? "1 Comment" - : `${numComments} Comments`; - const date = moment(post["time-sent"]).fromNow(); - const url = `${props.baseUrl}/note/${post.index.split("/")[1]}`; + const url = `${props.baseUrl}/note/${post.index.split('/')[1]}`; const [rev, title, body, content] = getLatestRevision(node); const isUnread = props.unreads.graph?.[`/ship/${props.host}/${props.book}`]?.['/']?.unreads?.has(content.index); @@ -60,32 +48,54 @@ export function NotePreview(props: NotePreviewProps) { const snippet = getSnippet(body); return ( - - - {title} - - - - - - {name} - - - {date} - - {commentDesc} - {rev.valueOf() === 1 ? `1 Revision` : `${rev} Revisions`} + + + + {title} + + + + }} + source={snippet} + /> + + + + + + + + + + + {numComments} + + - - + + ); } diff --git a/pkg/interface/src/views/apps/publish/components/NoteRoutes.tsx b/pkg/interface/src/views/apps/publish/components/NoteRoutes.tsx index bba8172290..e4e895bc6e 100644 --- a/pkg/interface/src/views/apps/publish/components/NoteRoutes.tsx +++ b/pkg/interface/src/views/apps/publish/components/NoteRoutes.tsx @@ -6,7 +6,7 @@ import { RouteComponentProps } from "react-router-dom"; import Note from "./Note"; import { EditPost } from "./EditPost"; -import { GraphNode, Graph, Contacts, LocalUpdateRemoteContentPolicy, Association } from "~/types"; +import { GraphNode, Graph, Contacts, LocalUpdateRemoteContentPolicy, Association, Group } from "~/types"; interface NoteRoutesProps { ship: string; @@ -25,8 +25,6 @@ interface NoteRoutesProps { } export function NoteRoutes(props: NoteRoutesProps & RouteComponentProps) { - const { ship, book, noteId } = props; - const baseUrl = props.baseUrl || '/~404'; const rootUrl = props.rootUrl || '/~404'; diff --git a/pkg/interface/src/views/apps/publish/components/Notebook.tsx b/pkg/interface/src/views/apps/publish/components/Notebook.tsx index 998e8beae6..f83236ed86 100644 --- a/pkg/interface/src/views/apps/publish/components/Notebook.tsx +++ b/pkg/interface/src/views/apps/publish/components/Notebook.tsx @@ -3,11 +3,9 @@ import { Link, RouteComponentProps, Route, Switch } from "react-router-dom"; import { NotebookPosts } from "./NotebookPosts"; import { roleForShip } from "~/logic/lib/group"; import { Box, Button, Text, Row, Col } from "@tlon/indigo-react"; -import { Groups } from "~/types/group-update"; -import { Contacts, Rolodex } from "~/types/contact-update"; import GlobalApi from "~/logic/api/global"; import styled from "styled-components"; -import { Associations, Graph, Association, Unreads } from "~/types"; +import { Contacts, Rolodex, Groups, Associations, Graph, Association, Unreads } from "~/types"; import { deSig } from "~/logic/lib/util"; import { StatelessAsyncButton } from "~/views/components/StatelessAsyncButton"; @@ -22,6 +20,7 @@ interface NotebookProps { contacts: Rolodex; groups: Groups; hideNicknames: boolean; + hideAvatars: boolean; baseUrl: string; rootUrl: string; unreads: Unreads; @@ -39,39 +38,42 @@ export function Notebook(props: NotebookProps & RouteComponentProps) { notebookContacts, groups, hideNicknames, + hideAvatars, association, - graph, + graph } = props; const { metadata } = association; - const group = groups[association?.["group-path"]]; - if (!group) return null; // Waitin on groups to populate + const group = groups[association?.['group-path']]; + if (!group) { + return null; // Waitin on groups to populate + } const relativePath = (p: string) => props.baseUrl + p; const contact = notebookContacts?.[ship]; - const role = group ? roleForShip(group, window.ship) : undefined; const isOwn = `~${window.ship}` === ship; + let isWriter = true; - const isWriter = - isOwn || group.tags?.publish?.[`writers-${book}`]?.has(window.ship); + if (group.tags?.publish?.[`writers-${book}`]) { + isWriter = isOwn || group.tags?.publish?.[`writers-${book}`]?.has(window.ship); + } const showNickname = contact?.nickname && !hideNicknames; return ( - + - {metadata?.title} -
+ {metadata?.title} by - + {showNickname ? contact?.nickname : ship}
{isWriter && ( - - @@ -82,10 +84,13 @@ export function Notebook(props: NotebookProps & RouteComponentProps) { graph={graph} host={ship} book={book} - contacts={!!notebookContacts ? notebookContacts : {}} + contacts={notebookContacts ? notebookContacts : {}} hideNicknames={hideNicknames} unreads={props.unreads} + hideAvatars={hideAvatars} baseUrl={props.baseUrl} + api={props.api} + group={group} /> ); diff --git a/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx b/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx index 91dbaf2184..639cf5330c 100644 --- a/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx +++ b/pkg/interface/src/views/apps/publish/components/NotebookPosts.tsx @@ -1,16 +1,19 @@ import React, { Component } from "react"; import { Col } from "@tlon/indigo-react"; import { NotePreview } from "./NotePreview"; -import { Contacts, Graph, Unreads } from "~/types"; +import { Contacts, Graph, Unreads, Group } from "~/types"; interface NotebookPostsProps { contacts: Contacts; graph: Graph; host: string; book: string; - hideNicknames?: boolean; baseUrl: string; unreads: Unreads; + hideAvatars?: boolean; + hideNicknames?: boolean; + api: GlobalApi; + group: Group; } export function NotebookPosts(props: NotebookPostsProps) { @@ -25,9 +28,11 @@ export function NotebookPosts(props: NotebookPostsProps) { book={props.book} unreads={props.unreads} contact={props.contacts[node.post.author]} + contacts={props.contacts} node={node} - hideNicknames={props.hideNicknames} baseUrl={props.baseUrl} + api={props.api} + group={props.group} /> ) )} diff --git a/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx b/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx index 385684d36b..69c608eb52 100644 --- a/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx +++ b/pkg/interface/src/views/apps/publish/components/NotebookRoutes.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from "react"; import { RouteComponentProps, Route, Switch } from "react-router-dom"; import GlobalApi from "~/logic/api/global"; -import { +import { Association, Associations, Graphs, @@ -57,15 +57,18 @@ export function NotebookRoutes( ( - { + if (!graph) { + return
; + } + return - )} + baseUrl={baseUrl} />; + }} /> { const { noteId } = routeProps.match.params; - const noteIdNum = bigInt(noteId) + const noteIdNum = bigInt(noteId); if(!graph) { return
; diff --git a/pkg/interface/src/views/apps/publish/components/Writers.js b/pkg/interface/src/views/apps/publish/components/Writers.js index e613a2a3c2..c80f980188 100644 --- a/pkg/interface/src/views/apps/publish/components/Writers.js +++ b/pkg/interface/src/views/apps/publish/components/Writers.js @@ -4,6 +4,7 @@ import { ShipSearch } from '~/views/components/ShipSearch'; import { Formik, Form, FormikHelpers } from 'formik'; import { resourceFromPath } from '~/logic/lib/group'; import { AsyncButton } from '~/views/components/AsyncButton'; +import { cite } from '~/logic/lib/util'; export class Writers extends Component { render() { @@ -27,6 +28,7 @@ export class Writers extends Component { actions.setStatus({ error: e.message }); } }; + const writers = Array.from(groups?.[association?.['group-path']]?.tags.publish?.[`writers-${name}`] || new Set()).map(e => cite(`~${e}`)).join(', '); return ( @@ -49,6 +51,10 @@ export class Writers extends Component { + {writers.length > 0 && <> + Current writers: + {writers} + } ); } diff --git a/pkg/interface/src/views/apps/publish/css/custom.css b/pkg/interface/src/views/apps/publish/css/custom.css index 4f81edb24f..dc0feb6131 100644 --- a/pkg/interface/src/views/apps/publish/css/custom.css +++ b/pkg/interface/src/views/apps/publish/css/custom.css @@ -187,7 +187,7 @@ color:var(--gray); } -.md p { +.md { line-height: 1.5; } .md code, .md pre { @@ -201,7 +201,7 @@ border-bottom-width: 1px; } -md img { +.md img { margin-bottom: 8px; } diff --git a/pkg/interface/src/views/apps/publish/components/Author.tsx b/pkg/interface/src/views/components/Author.tsx similarity index 58% rename from pkg/interface/src/views/apps/publish/components/Author.tsx rename to pkg/interface/src/views/components/Author.tsx index bc38987a4d..6bb732c2da 100644 --- a/pkg/interface/src/views/apps/publish/components/Author.tsx +++ b/pkg/interface/src/views/components/Author.tsx @@ -1,10 +1,14 @@ import React, {ReactNode} from "react"; import moment from "moment"; -import { Sigil } from "~/logic/lib/sigil" -import { uxToHex, cite } from "~/logic/lib/util"; -import { Contacts } from "~/types/contact-update"; import { Row, Box } from "@tlon/indigo-react"; +import { uxToHex, cite } from "~/logic/lib/util"; +import { Contacts, Rolodex } from "~/types/contact-update"; +import OverlaySigil from "./OverlaySigil"; +import { Group, Association } from "~/types"; +import GlobalApi from "~/logic/api/global"; +import { useHistory } from "react-router-dom"; + interface AuthorProps { contacts: Contacts; ship: string; @@ -14,16 +18,18 @@ interface AuthorProps { hideNicknames: boolean; children?: ReactNode; unread?: boolean; + group: Group; + api: GlobalApi; } -export function Author(props: AuthorProps) { - const { contacts, ship = '', date, showImage } = props; - let contact = null; +export default function Author(props: AuthorProps) { + const { contacts, ship = '', date, showImage, hideAvatars, hideNicknames, group, api } = props; + const history = useHistory(); + let contact; if (contacts) { contact = ship in contacts ? contacts[ship] : null; } const color = contact?.color ? `#${uxToHex(contact?.color)}` : "#000000"; - const showAvatar = !props.hideAvatars && contact?.avatar; const showNickname = !props.hideNicknames && contact?.nickname; const name = showNickname ? contact?.nickname : cite(ship); @@ -32,18 +38,19 @@ export function Author(props: AuthorProps) { {showImage && ( - {showAvatar ? ( - - ) : ( - - )} + )} {!disabled && ( diff --git a/pkg/interface/src/views/components/Comments.tsx b/pkg/interface/src/views/components/Comments.tsx index 4f338cd9bf..b0bf4547f1 100644 --- a/pkg/interface/src/views/components/Comments.tsx +++ b/pkg/interface/src/views/components/Comments.tsx @@ -9,6 +9,7 @@ import { FormikHelpers } from 'formik'; import { GraphNode, LocalUpdateRemoteContentPolicy, Unreads, Association } from '~/types'; import { createPost, createBlankNodeWithChildPost } from '~/logic/api/graph'; import { getLatestCommentRevision } from '~/logic/lib/publish'; +import { LocalUpdateRemoteContentPolicy, Group } from '~/types'; import { scanForMentions } from '~/logic/lib/graph'; import { getUnreadCount } from '~/logic/lib/hark'; @@ -24,10 +25,11 @@ interface CommentsProps { hideAvatars: boolean; hideNicknames: boolean; remoteContentPolicy: LocalUpdateRemoteContentPolicy; + group: Group; } export function Comments(props: CommentsProps) { -const { association, comments, ship, name, api, history } = props; + const { association, comments, ship, name, api, history, baseUrl, group } = props; const onSubmit = async ( { comment }, diff --git a/pkg/interface/src/views/components/DropdownSearch.tsx b/pkg/interface/src/views/components/DropdownSearch.tsx index 9829058916..6a12e5acdd 100644 --- a/pkg/interface/src/views/components/DropdownSearch.tsx +++ b/pkg/interface/src/views/components/DropdownSearch.tsx @@ -35,6 +35,8 @@ interface DropdownSearchExtraProps { onSelect: (c: C) => void; disabled?: boolean; placeholder?: string; + onChange?: (e: ChangeEvent) => void; + onBlur?: (e: any) => void; } type DropdownSearchProps = PropFunc & @@ -51,6 +53,8 @@ export function DropdownSearch(props: DropdownSearchProps) { renderCandidate, disabled, placeholder, + onChange = () => {}, + onBlur = () => {}, ...rest } = props; @@ -101,8 +105,9 @@ export function DropdownSearch(props: DropdownSearchProps) { }; }, [textarea.current, next, back, onEnter]); - const onChange = useCallback( + const changeCallback = useCallback( (e: ChangeEvent) => { + onChange(e); search(e.target.value); setQuery(e.target.value); }, @@ -128,11 +133,12 @@ export function DropdownSearch(props: DropdownSearchProps) { {dropdown.length !== 0 && query.length !== 0 && ( { + public containerRef: React.Ref; + + constructor(props) { + super(props); + this.state = { + clicked: false, + topSpace: 0, + bottomSpace: 0 + }; + + this.containerRef = React.createRef(); + + this.profileShow = this.profileShow.bind(this); + this.profileHide = this.profileHide.bind(this); + this.updateContainerOffset = this.updateContainerOffset.bind(this); + } + + profileShow() { + this.updateContainerOffset(); + this.setState({ clicked: true }); + this.props.scrollWindow?.addEventListener('scroll', this.updateContainerOffset); + } + + profileHide() { + this.setState({ clicked: false }); + this.props.scrollWindow?.removeEventListener('scroll', this.updateContainerOffset, true); + } + + updateContainerOffset() { + if (this.containerRef && this.containerRef.current) { + const container = this.containerRef.current; + const scrollWindow = this.props.scrollWindow; + + const bottomSpace = scrollWindow + ? scrollWindow.scrollHeight - container.offsetTop - scrollWindow.scrollTop + : 'auto'; + const topSpace = scrollWindow + ? scrollWindow.offsetHeight - bottomSpace - OVERLAY_HEIGHT + : 0; + + this.setState({ + topSpace, + bottomSpace + }); + } + } + + componentWillUnmount() { + this.props.scrollWindow?.removeEventListener('scroll', this.updateContainerOffset, true); + } + + render() { + const { + className, + ship, + contact, + color, + group, + hideAvatars, + hideNicknames, + history, + api, + sigilClass, + ...rest + } = this.props; + + const { state } = this; + + const img = (contact && (contact.avatar !== null) && !hideAvatars) + ? + : ; + + return ( + + {state.clicked && ( + + )} + {img} + + ); + } +} diff --git a/pkg/interface/src/views/apps/chat/components/profile-overlay.js b/pkg/interface/src/views/components/ProfileOverlay.tsx similarity index 78% rename from pkg/interface/src/views/apps/chat/components/profile-overlay.js rename to pkg/interface/src/views/components/ProfileOverlay.tsx index ef6303c149..c1d310af5a 100644 --- a/pkg/interface/src/views/apps/chat/components/profile-overlay.js +++ b/pkg/interface/src/views/components/ProfileOverlay.tsx @@ -1,14 +1,32 @@ import React, { PureComponent } from 'react'; + +import { Contact, Group } from '~/types'; import { cite } from '~/logic/lib/util'; import { Sigil } from '~/logic/lib/sigil'; -import { Box, Col, Button, Text, BaseImage } from '@tlon/indigo-react'; +import { Box, Col, Button, Text, BaseImage, ColProps } from '@tlon/indigo-react'; export const OVERLAY_HEIGHT = 250; -export class ProfileOverlay extends PureComponent { - constructor() { - super(); +type ProfileOverlayProps = ColProps & { + ship: string; + contact?: Contact; + color: string; + topSpace: number | 'auto'; + bottomSpace: number | 'auto'; + group?: Group; + onDismiss(): void; + hideAvatars: boolean; + hideNicknames: boolean; + history: any; + api: any; +} + +export class ProfileOverlay extends PureComponent { + public popoverRef: React.Ref; + + constructor(props) { + super(props); this.popoverRef = React.createRef(); this.onDocumentClick = this.onDocumentClick.bind(this); @@ -35,7 +53,19 @@ export class ProfileOverlay extends PureComponent { } render() { - const { contact, ship, color, topSpace, bottomSpace, group, hideNicknames, hideAvatars, history } = this.props; + const { + contact, + ship, + color, + topSpace, + bottomSpace, + group = false, + hideNicknames, + hideAvatars, + history, + onDismiss, + ...rest + } = this.props; let top, bottom; if (topSpace < OVERLAY_HEIGHT / 2) { @@ -66,7 +96,7 @@ export class ProfileOverlay extends PureComponent { /* if (!group.hidden) { }*/ - const isHidden = group.hidden; + const isHidden = group ? group.hidden : false; return ( {img} diff --git a/pkg/interface/src/views/components/RemoteContent.tsx b/pkg/interface/src/views/components/RemoteContent.tsx index 1d8965a908..8e71376099 100644 --- a/pkg/interface/src/views/components/RemoteContent.tsx +++ b/pkg/interface/src/views/components/RemoteContent.tsx @@ -1,12 +1,13 @@ import React, { PureComponent, Fragment } from 'react'; import { LocalUpdateRemoteContentPolicy } from "~/types/local-update"; -import { BaseAnchor, BaseImage, Box, Button } from '@tlon/indigo-react'; +import { BaseAnchor, BaseImage, Box, Button, Text } from '@tlon/indigo-react'; import { hasProvider } from 'oembed-parser'; import EmbedContainer from 'react-oembed-container'; import { memoize } from 'lodash'; interface RemoteContentProps { url: string; + text?: string; remoteContentPolicy: LocalUpdateRemoteContentPolicy; unfold?: boolean; renderUrl?: boolean; @@ -14,6 +15,7 @@ interface RemoteContentProps { audioProps?: any; videoProps?: any; oembedProps?: any; + textProps?: any; style?: any; onLoad?(): void; } @@ -27,8 +29,6 @@ const IMAGE_REGEX = new RegExp(/(jpg|img|png|gif|tiff|jpeg|webp|webm|svg)$/i); const AUDIO_REGEX = new RegExp(/(mp3|wav|ogg)$/i); const VIDEO_REGEX = new RegExp(/(mov|mp4|ogv)$/i); -const memoizedFetch = memoize(fetch); - export default class RemoteContent extends PureComponent { private fetchController: AbortController | undefined; constructor(props) { @@ -48,7 +48,8 @@ export default class RemoteContent extends PureComponent response.clone().json()) @@ -70,12 +71,14 @@ export default class RemoteContent extends PureComponent {contents} @@ -86,12 +89,14 @@ export default class RemoteContent extends PureComponent {}, ...props @@ -115,7 +120,9 @@ export default class RemoteContent extends PureComponent - {renderUrl ? this.wrapInLink(url) : null} + {renderUrl + ? this.wrapInLink({text || url}) + : null}