From 1f7f86f682584dcbd1caeb7ed1008685fc92c2a9 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Thu, 7 Jan 2021 16:49:24 -0600 Subject: [PATCH 01/42] contact-store: first approach at rewrite --- pkg/arvo/app/contact-hook.hoon | 580 +------------------------- pkg/arvo/app/contact-store.hoon | 393 ++++++----------- pkg/arvo/app/contact-view.hoon | 349 +--------------- pkg/arvo/app/group-store.hoon | 14 +- pkg/arvo/app/invite-store.hoon | 63 ++- pkg/arvo/lib/contact-json.hoon | 265 ------------ pkg/arvo/lib/contact-store.hoon | 123 ++++++ pkg/arvo/mar/contact/action.hoon | 15 - pkg/arvo/mar/contact/hook-update.hoon | 15 - pkg/arvo/mar/contact/initial.hoon | 16 - pkg/arvo/mar/contact/update.hoon | 11 +- pkg/arvo/mar/contact/view-action.hoon | 12 - pkg/arvo/sur/contact-hook.hoon | 18 - pkg/arvo/sur/contact-store.hoon | 29 +- pkg/arvo/sur/contact-view.hoon | 27 -- 15 files changed, 337 insertions(+), 1593 deletions(-) delete mode 100644 pkg/arvo/lib/contact-json.hoon create mode 100644 pkg/arvo/lib/contact-store.hoon delete mode 100644 pkg/arvo/mar/contact/action.hoon delete mode 100644 pkg/arvo/mar/contact/hook-update.hoon delete mode 100644 pkg/arvo/mar/contact/initial.hoon delete mode 100644 pkg/arvo/mar/contact/view-action.hoon delete mode 100644 pkg/arvo/sur/contact-hook.hoon delete mode 100644 pkg/arvo/sur/contact-view.hoon diff --git a/pkg/arvo/app/contact-hook.hoon b/pkg/arvo/app/contact-hook.hoon index 56311fa510..fc77f2b1dd 100644 --- a/pkg/arvo/app/contact-hook.hoon +++ b/pkg/arvo/app/contact-hook.hoon @@ -1,571 +1,27 @@ -:: contact-hook [landscape] +:: contact-hook [landscape]: deprecated :: -:: -/- *contact-hook, - *contact-view, - inv=invite-store, - *metadata-hook, - *metadata-store, - *group -/+ *contact-json, - default-agent, - dbug, - group-store, - verb, - resource, - grpl=group, - *migrate -~% %contact-hook-top ..part ~ +/+ default-agent |% +$ card card:agent:gall -:: -+$ versioned-state - $% state-zero - state-one - state-two - state-three - == -:: -+$ state-zero [%0 state-base] -+$ state-one [%1 state-base] -+$ state-two [%2 state-base] -+$ state-three [%3 state-base] -+$ state-base - $: =synced - invite-created=_| - == -- -=| state-three -=* state - -%- agent:dbug -%+ verb | +:: ^- agent:gall -=< - |_ bol=bowl:gall - +* this . - contact-core +> - cc ~(. contact-core bol) - def ~(. (default-agent this %|) bol) - :: - ++ on-init - ^- (quip card _this) - :_ this(invite-created %.y) - :~ (invite-poke:cc [%create %contacts]) - [%pass /inv %agent [our.bol %invite-store] %watch /invitatory/contacts] - [%pass /group %agent [our.bol %group-store] %watch /groups] - == - ++ on-save !>(state) - ++ on-load - |= old-vase=vase - ^- (quip card _this) - =/ old !<(versioned-state old-vase) - =| cards=(list card) - |^ - |- ^- (quip card _this) - ?: ?=(%3 -.old) - [cards this(state old)] - ?: ?=(%2 -.old) - %_ $ - old [%3 +.old] - :: - cards - %+ welp - cards - %- zing - %+ turn - ~(tap by synced.old) - |= [=path =ship] - ^- (list card) - ?. =(ship our.bol) - ~ - ?> ?=([%ship *] path) - :~ (pass-store contacts+t.path %leave ~) - (pass-store contacts+path %watch contacts+path) - == - == - ?: ?=(%1 -.old) - %_ $ - -.old %2 - :: - synced.old - %- malt - %+ turn - ~(tap by synced.old) - |= [=path =ship] - [ship+path ship] - :: - cards - ^- (list card) - ;: welp - :~ [%pass /group %agent [our.bol %group-store] %leave ~] - [%pass /group %agent [our.bol %group-store] %watch /groups] - == - kick-old-subs - cards - == - == - %_ $ - -.old %1 - :: - cards - :_ cards - [%pass /group %agent [our.bol %group-store] %watch /updates] - == - ++ kick-old-subs - =/ paths - %+ turn - ~(val by sup.bol) - |=([=ship =path] path) - ?~ paths ~ - [%give %kick paths ~]~ - :: - ++ pass-store - |= [=wire =task:agent:gall] - ^- card - [%pass wire %agent [our.bol %contact-store] task] - -- - :: - ++ on-poke - |= [=mark =vase] - ^- (quip card _this) - =^ cards state - ?+ mark (on-poke:def mark vase) - %json - (poke-json:cc !<(json vase)) - :: - %contact-action - (poke-contact-action:cc !<(contact-action vase)) - :: - %contact-hook-action - (poke-hook-action:cc !<(contact-hook-action vase)) - :: - %import - ?> (team:title our.bol src.bol) - (poke-import:cc q.vase) - == - [cards this] - :: - ++ on-watch - |= =path - ^- (quip card _this) - ?+ path (on-watch:def path) - [%contacts *] [(watch-contacts:cc t.path) this] - [%synced *] [(watch-synced:cc t.path) this] - == - :: - ++ on-agent - |= [=wire =sign:agent:gall] - ^- (quip card _this) - ?+ -.sign (on-agent:def wire sign) - %kick [(kick:cc wire) this] - %watch-ack - =^ cards state - (watch-ack:cc wire p.sign) - [cards this] - :: - %fact - ?+ p.cage.sign (on-agent:def wire sign) - %contact-update - =^ cards state - (fact-contact-update:cc wire !<(contact-update q.cage.sign)) - [cards this] - :: - %group-update - =^ cards state - (fact-group-update:cc wire !<(update:group-store q.cage.sign)) - [cards this] - :: - %invite-update [~ this] - == - == - :: - ++ on-leave on-leave:def - ++ on-peek - |= =path - ^- (unit (unit cage)) - ?+ path (on-peek:def path) - [%x %export ~] - ``noun+!>(state) - [%x %synced ~] - ``noun+!>(~(key by synced)) - == - ++ on-arvo - |= [=wire =sign-arvo] - ^- (quip card _this) - ?. ?=([%try-rejoin @ @ *] wire) - (on-arvo:def wire sign-arvo) - =/ nack-count=@ud (slav %ud i.t.wire) - =/ who=@p (slav %p i.t.t.wire) - =/ pax t.t.t.wire - ?> ?=([%behn %wake *] sign-arvo) - ~? ?=(^ error.sign-arvo) - "behn errored in backoff timers, continuing anyway" - :_ this - [(try-rejoin:cc who pax +(nack-count))]~ - :: - ++ on-fail on-fail:def - -- -:: |_ bol=bowl:gall -++ grp ~(. grpl bol) ++* this . + def ~(. (default-agent this %|) bol) :: -++ poke-json - |= jon=json - ^- (quip card _state) - (poke-contact-action (json-to-action jon)) +++ on-init on-init:def +++ on-poke on-poke:def +++ on-watch on-watch:def +++ on-agent on-agent:def +++ on-arvo on-arvo:def +++ on-save !>(~) +++ on-load + |= old-vase=vase + ^- (quip card _this) + [~ this] :: -++ poke-contact-action - |= act=contact-action - ^- (quip card _state) - :_ state - ?+ -.act !! - %edit (handle-contact-action path.act ship.act act) - %add (handle-contact-action path.act ship.act act) - %remove (handle-contact-action path.act ship.act act) - == -:: -++ handle-contact-action - |= [=path =ship act=contact-action] - ^- (list card) - :: local - ?: (team:title our.bol src.bol) - ?. |(=(path /~/default) (~(has by synced) path)) ~ - =/ shp ?:(=(path /~/default) our.bol (~(got by synced) path)) - =/ appl ?:(=(shp our.bol) %contact-store %contact-hook) - [%pass / %agent [shp appl] %poke %contact-action !>(act)]~ - :: foreign - =/ shp (~(got by synced) path) - ?. |(=(shp our.bol) =(src.bol ship)) ~ - :: scry group to check if ship is a member - =/ =group (need (group-scry path)) - ?. (~(has in members.group) shp) ~ - [%pass / %agent [our.bol %contact-store] %poke %contact-action !>(act)]~ -:: -++ poke-hook-action - |= act=contact-hook-action - ^- (quip card _state) - ?- -.act - %add-owned - ?> (team:title our.bol src.bol) - =/ contact-path [%contacts path.act] - ?: (~(has by synced) path.act) - [~ state] - =. synced (~(put by synced) path.act our.bol) - :_ state - :~ [%pass contact-path %agent [our.bol %contact-store] %watch contact-path] - [%give %fact [/synced]~ %contact-hook-update !>([%initial synced])] - == - :: - %add-synced - ?> (team:title our.bol src.bol) - ?: (~(has by synced) path.act) [~ state] - =. synced (~(put by synced) path.act ship.act) - =/ contact-path [%contacts path.act] - :_ state - :~ [%pass contact-path %agent [ship.act %contact-hook] %watch contact-path] - [%give %fact [/synced]~ %contact-hook-update !>([%initial synced])] - == - :: - %remove - =/ ship (~(get by synced) path.act) - ?~ ship [~ state] - ?: &(=(u.ship our.bol) (team:title our.bol src.bol)) - :: delete one of our.bol own paths - :_ state(synced (~(del by synced) path.act)) - %- zing - :~ (pull-wire [%contacts path.act]) - [%give %kick ~[[%contacts path.act]] ~]~ - [%give %fact [/synced]~ %contact-hook-update !>([%initial synced])]~ - == - ?. |(=(u.ship src.bol) (team:title our.bol src.bol)) - :: if neither ship = source or source = us, do nothing - [~ state] - :: delete a foreign ship's path - =/ cards - (handle-contact-action path.act our.bol [%remove path.act our.bol]) - :_ state(synced (~(del by synced) path.act)) - %- zing - :~ (pull-wire [%contacts path.act]) - [%give %fact [/synced]~ %contact-hook-update !>([%initial synced])]~ - cards - == - == -:: -++ poke-import - |= arc=* - ^- (quip card _state) - =/ sty=state-three - [%3 (remake-map ;;((tree [path ship]) +<.arc)) ;;(? +>.arc)] - :_ sty - %+ turn ~(tap by synced.sty) - |= [=path =ship] - ^- card - =/ contact-path [%contacts path] - ?: =(our.bol ship) - [%pass contact-path %agent [our.bol %contact-store] %watch contact-path] - (try-rejoin ship contact-path 0) -:: -++ try-rejoin - |= [who=@p pax=path nack-count=@ud] - ^- card - =/ =wire - [%try-rejoin (scot %ud nack-count) (scot %p who) pax] - [%pass wire %agent [who %contact-hook] %watch pax] -:: -++ watch-contacts - |= pax=path - ^- (list card) - ?> ?=(^ pax) - ?> (~(has by synced) pax) - :: scry groups to check if ship is a member - =/ =group (need (group-scry pax)) - ?> (~(has in members.group) src.bol) - =/ contacts (need (contacts-scry pax)) - [%give %fact ~ %contact-update !>([%contacts pax contacts])]~ -:: -++ watch-synced - |= pax=path - ^- (list card) - ?> (team:title our.bol src.bol) - [%give %fact ~ %contact-hook-update !>([%initial synced])]~ -:: -++ watch-ack - |= [wir=wire saw=(unit tang)] - ^- (quip card _state) - ?~ saw - [~ state] - ?: ?=([%try-rejoin @ *] wir) - =/ nack-count=@ud (slav %ud i.t.wir) - =/ wakeup=@da - (add now.bol (mul ~s1 (bex (min 19 nack-count)))) - :_ state - [%pass wir %arvo %b %wait wakeup]~ - :: - ?> ?=(^ wir) - [~ state(synced (~(del by synced) t.wir))] -:: -++ migrate - |= wir=wire - ^- wire - ?> ?=([%contacts @ @ *] wir) - [%contacts %ship t.wir] -:: -++ kick - |= wir=wire - ^- (list card) - ?+ wir !! - [%try-rejoin @ @ *] - $(wir t.t.t.wir) - :: - [%inv ~] - [%pass /inv %agent [our.bol %invite-store] %watch /invitatory/contacts]~ - :: - [%group ~] - [%pass /group %agent [our.bol %group-store] %watch /groups]~ - :: - [%contacts @ *] - =/ wir - ?: =(%ship i.t.wir) - wir - (migrate wir) - ?> ?=([%contacts @ @ *] wir) - ?. (~(has by synced) t.wir) ~ - =/ =ship (~(got by synced) t.wir) - ?: =(ship our.bol) - [%pass wir %agent [our.bol %contact-store] %watch wir]~ - [%pass wir %agent [ship %contact-hook] %watch wir]~ - == -:: -++ fact-contact-update - |= [wir=wire fact=contact-update] - ^- (quip card _state) - |^ - ?: (team:title our.bol src.bol) - (local fact) - :_ state - (foreign fact) - :: - ++ give-fact - |= [=path update=contact-update] - ^- (list card) - [%give %fact ~[[%contacts path]] %contact-update !>(update)]~ - :: - ++ local - |= fact=contact-update - ^- (quip card _state) - ?+ -.fact [~ state] - %add - :_ state - (give-fact path.fact [%add path.fact ship.fact contact.fact]) - :: - %edit - :_ state - (give-fact path.fact [%edit path.fact ship.fact edit-field.fact]) - :: - %delete - =. synced (~(del by synced) path.fact) - `state - == - :: - ++ foreign - |= fact=contact-update - ^- (list card) - ?+ -.fact ~ - %contacts - =/ owner (~(got by synced) path.fact) - ?> =(owner src.bol) - =/ have-contacts=(unit contacts) - (contacts-scry path.fact) - ?~ have-contacts - :: if we don't have any contacts yet, - :: create the entry, and %add every contact - :: - :- (contact-poke [%create path.fact]) - %+ turn ~(tap by contacts.fact) - |= [=ship =contact] - (contact-poke [%add path.fact ship contact]) - :: if we already have some, decide between %add, %remove and recreate - :: on a per-contact basis - :: - %- zing - %+ turn - %~ tap in - %- ~(uni in ~(key by contacts.fact)) - ~(key by u.have-contacts) - |= =ship - ^- (list card) - =/ have=(unit contact) (~(get by u.have-contacts) ship) - =/ want=(unit contact) (~(get by contacts.fact) ship) - ?~ have - [(contact-poke %add path.fact ship (need want))]~ - ?~ want - [(contact-poke %remove path.fact ship)]~ - ?: =(u.want u.have) ~ - ::TODO probably want an %all edit-field that resolves to more granular - :: updates within the contact-store? - :~ (contact-poke %remove path.fact ship) - (contact-poke %add path.fact ship u.want) - == - :: - %add - =/ owner (~(get by synced) path.fact) - ?~ owner ~ - ?> |(=(u.owner src.bol) =(src.bol ship.fact)) - ~[(contact-poke [%add path.fact ship.fact contact.fact])] - :: - %remove - =/ owner (~(get by synced) path.fact) - ?~ owner ~ - ?> |(=(u.owner src.bol) =(src.bol ship.fact)) - ~[(contact-poke [%remove path.fact ship.fact])] - :: - %edit - =/ owner (~(got by synced) path.fact) - ?> |(=(owner src.bol) =(src.bol ship.fact)) - ~[(contact-poke [%edit path.fact ship.fact edit-field.fact])] - == - -- -:: -++ fact-group-update - |= [wir=wire fact=update:group-store] - ^- (quip card _state) - ?: ?=(%initial -.fact) - [~ state] - =/ group=(unit group) - (scry-group:grp resource.fact) - |^ - ?+ -.fact [~ state] - %initial-group (initial-group +.fact) - %remove-members (remove +.fact) - %remove-group (unbundle +.fact) - == - :: - ++ initial-group - |= [rid=resource =^group] - ^- (quip card _state) - ?: hidden.group [~ state] - =/ =path - (en-path:resource rid) - ?: (~(has by synced) path) - [~ state] - (poke-hook-action %add-synced entity.rid path) - :: - ++ unbundle - |= [rid=resource ~] - ^- (quip card _state) - =/ =path - (en-path:resource rid) - ?. (~(has by synced) path) - ?~ (contacts-scry path) - [~ state] - :_ state - [(contact-poke [%delete path])]~ - :_ state(synced (~(del by synced) path)) - :~ [%pass [%contacts path] %agent [our.bol %contact-store] %leave ~] - [(contact-poke [%delete path])] - == - :: - ++ remove - |= [rid=resource ships=(set ship)] - ^- (quip card _state) - :: if pax is synced, remove member from contacts and kick their sub - ?~ group - [~ state] - ?: hidden.u.group [~ state] - =/ =path - (en-path:resource rid) - =/ owner=(unit ship) (~(get by synced) path) - ?~ owner - :_ state - %+ turn ~(tap in ships) - |= =ship - (contact-poke [%remove path ship]) - :_ state - %- zing - %+ turn ~(tap in ships) - |= =ship - :~ [%give %kick ~[[%contacts path]] `ship] - ?: =(ship our.bol) - (contact-poke [%delete path]) - (contact-poke [%remove path ship]) - == - -- -:: -++ invite-poke - |= act=action:inv - ^- card - [%pass / %agent [our.bol %invite-store] %poke %invite-action !>(act)] -:: -++ contact-poke - |= act=contact-action - ^- card - [%pass / %agent [our.bol %contact-store] %poke %contact-action !>(act)] -:: -++ contacts-scry - |= pax=path - ^- (unit contacts) - =. pax - ;: weld - /(scot %p our.bol)/contact-store/(scot %da now.bol)/contacts - pax - /noun - == - .^((unit contacts) %gx pax) -:: -++ group-scry - |= pax=path - .^ (unit group) - %gx - ;:(weld /(scot %p our.bol)/group-store/(scot %da now.bol) /groups pax /noun) - == -:: -++ pull-wire - |= pax=path - ^- (list card) - ?> ?=(^ pax) - =/ shp (~(get by synced) t.pax) - ?~ shp ~ - ?: =(u.shp our.bol) - [%pass pax %agent [our.bol %contact-store] %leave ~]~ - [%pass pax %agent [u.shp %contact-hook] %leave ~]~ +++ on-leave on-leave:def +++ on-peek on-peek:def +++ on-fail on-fail:def -- diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index 7f4323a640..0b748ef0a7 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -1,279 +1,154 @@ :: contact-store [landscape]: :: -:: data store that holds group-based contact data +:: data store that holds individual contact data :: -/+ *contact-json, default-agent, dbug, *migrate +/- store=contact-store +/+ default-agent, dbug, *migrate |% +$ card card:agent:gall ++$ state-4 [%4 =rolodex:store] +$ versioned-state - $% state-zero - state-one - state-two - state-three - == -:: -+$ rolodex-0 (map path contacts-0) -+$ contacts-0 (map ship contact-0) -+$ avatar-0 [content-type=@t octs=[p=@ud q=@t]] -+$ contact-0 - $: nickname=@t - email=@t - phone=@t - website=@t - notes=@t - color=@ux - avatar=(unit avatar-0) - == -:: -+$ state-zero - $: %0 - rolodex=rolodex-0 - == -+$ state-one - $: %1 - =rolodex - == -+$ state-two - $: %2 - =rolodex - == -+$ state-three - $: %3 - =rolodex + $% [%0 *] + [%1 *] + [%2 *] + [%3 *] + state-4 == -- :: -=| state-three +=| state-4 =* state - %- agent:dbug ^- agent:gall -=< - |_ =bowl:gall - +* this . - contact-core +> - cc ~(. contact-core bowl) - def ~(. (default-agent this %|) bowl) - :: - ++ on-init on-init:def - ++ on-save !>(state) - ++ on-load - |= old-vase=vase - =/ old !<(versioned-state old-vase) - =| cards=(list card) - |- - ?: ?=(%3 -.old) - [cards this(state old)] - ?: ?=(%2 -.old) - %_ $ - -.old %3 - :: - rolodex.old - =/ def - (~(get by rolodex.old) /ship/~/default) - ?~ def - rolodex.old - =. rolodex.old - (~(del by rolodex.old) /ship/~/default) - =. rolodex.old - (~(put by rolodex.old) /~/default u.def) - rolodex.old - == - ?: ?=(%1 -.old) - =/ new-rolodex=^rolodex - %- malt - %+ turn - ~(tap by rolodex.old) - |= [=path =contacts] - [ship+path contacts] - %_ $ - old [%2 new-rolodex] - :: - cards - =/ paths - %+ turn - ~(val by sup.bol) - |=([=ship =path] path) - ?~ paths cards - :_ cards - [%give %kick paths ~] - == - - =/ new-rolodex=^rolodex - %- ~(run by rolodex.old) - |= cons=contacts-0 - ^- contacts - %- ~(run by cons) - |= con=contact-0 - ^- contact - :* nickname.con - email.con - phone.con - website.con - notes.con - color.con - ~ - == - $(old [%1 new-rolodex]) - :: - ++ on-poke - |= [=mark =vase] - ^- (quip card _this) - ?> (team:title our.bowl src.bowl) - =^ cards state - ?+ mark (on-poke:def mark vase) - ::%json (poke-json:cc !<(json vase)) - %contact-action - (poke-contact-action:cc !<(contact-action vase)) - :: - %import - (poke-import:cc q.vase) - == - [cards this] - :: - ++ on-watch - |= =path - ^- (quip card _this) - ?> (team:title our.bowl src.bowl) - |^ - =/ cards=(list card) - ?+ path (on-watch:def path) - [%all ~] (give %contact-update !>([%initial rolodex])) - [%updates ~] ~ - [%contacts @ *] - %+ give %contact-update - !>([%contacts t.path (~(got by rolodex) t.path)]) - == - [cards this] - :: - ++ give - |= =cage - ^- (list card) - [%give %fact ~ cage]~ - -- - :: - ++ on-leave on-leave:def - ++ on-peek - |= =path - ^- (unit (unit cage)) - ?+ path (on-peek:def path) - [%x %all ~] ``noun+!>(rolodex) - [%x %contacts *] - ?~ t.t.path - ~ - ``noun+!>((~(get by rolodex) t.t.path)) - :: - [%x %contact *] - :: /:path/:ship - =/ pax `^path`(flop t.t.path) - ?~ pax ~ - =/ =ship (slav %p i.pax) - ?~ t.pax ~ - => .(pax `(list @ta)`(flop t.pax)) - =/ contacts=(unit contacts) (~(get by rolodex) pax) - ?~ contacts - ~ - ``noun+!>((~(get by u.contacts) ship)) - :: - [%x %export ~] - ``noun+!>(state) +|_ =bowl:gall ++* this . + def ~(. (default-agent this %|) bowl) +:: +++ on-init on-init:def +++ on-save !>(state) +++ on-load + |= old-vase=vase + ^- (quip card _this) + =/ old !<(versioned-state old-vase) + ?- -.old + %4 [~ this(state old)] + %3 [~ this] + %2 [~ this] + %1 [~ this] + %0 [~ this] + == +:: +++ on-watch + |= =path + ^- (quip card _this) + ?> (team:title our.bowl src.bowl) + |^ + =/ cards=(list card) + ?+ path (on-watch:def path) + [%updates ~] ~ == + [cards this] :: - ++ on-agent on-agent:def - ++ on-arvo on-arvo:def - ++ on-fail on-fail:def + ++ give + |= =cage + ^- (list card) + [%give %fact ~ cage]~ -- :: +++ on-poke + |= [=mark =vase] + ^- (quip card _this) + ?> (team:title our.bowl src.bowl) + |^ + =^ cards state + ?+ mark (on-poke:def mark vase) + %contact-update (update !<(update:store vase)) + %import (import q.vase) + == + [cards this] + :: + ++ update + |= =update:store + ^- (quip card _state) + |^ + ?- -.update + %initial (handle-initial +.update) + %add (handle-add +.update) + %remove (handle-remove +.update) + %edit (handle-edit +.update) + == + :: + ++ handle-initial + |= rolo=rolodex:store + ^- (quip card _state) + =. rolodex (~(uni by rolodex) rolo) + :_ state(rolodex rolodex) + (send-diff [%initial rolodex]) + :: + ++ handle-add + |= [=ship =contact:store] + ^- (quip card _state) + ?< (~(has by rolodex) ship) + :- (send-diff [%add ship contact]) + state(rolodex (~(put by rolodex) ship contact)) + :: + ++ handle-remove + |= =ship + ^- (quip card _state) + ?> (~(has by rolodex) ship) + :- (send-diff [%remove ship]) + state(rolodex (~(del by rolodex) ship)) + :: + ++ handle-edit + |= [=ship =edit-field:store] + ^- (quip card _state) + =/ contact (~(got by rolodex) ship) + =. contact (edit-contact contact edit-field) + :- (send-diff [%edit ship edit-field]) + state(rolodex (~(put by rolodex) ship contact)) + :: + ++ edit-contact + |= [=contact:store edit=edit-field:store] + ^- contact:store + ?- -.edit + %nickname contact(nickname nickname.edit) + %email contact(email email.edit) + %phone contact(phone phone.edit) + %website contact(website website.edit) + %color contact(color color.edit) + %avatar contact(avatar avatar.edit) + == + :: + ++ send-diff + |= =update:store + ^- (list card) + [%give %fact ~[/updates] %contact-update !>(update)]~ + -- + :: + ++ import + |= arc=* + ^- (quip card _state) + [~ *state-4] + -- :: -|_ bol=bowl:gall -:: -::++ poke-json -:: |= =json -:: ^- (quip move _this) -:: ?> (team:title our.bol src.bol) -:: (poke-contact-action (json-to-action json)) -:: -++ poke-contact-action - |= action=contact-action - ^- (quip card _state) - ?> (team:title our.bol src.bol) - ?- -.action - %create (handle-create +.action) - %delete (handle-delete +.action) - %add (handle-add +.action) - %remove (handle-remove +.action) - %edit (handle-edit +.action) +++ on-peek + |= =path + ^- (unit (unit cage)) + ?+ path (on-peek:def path) + [%x %all ~] ``noun+!>(rolodex) + [%x %export ~] ``noun+!>(state) + :: + [%x %contact @ ~] + =/ =ship (slav %p i.t.t.path) + =/ contact=(unit contact:store) (~(get by rolodex) ship) + ?~ contact ~ + :- ~ :- ~ :- %contact-update + !> ^- update:store + [%add ship u.contact] == :: -++ poke-import - |= arc=* - ^- (quip card _state) - =/ sty=state-three - :- %3 - %- remake-map-of-map - ;;((tree [path (tree [ship contact])]) +.arc) - [~ sty] -:: -++ handle-create - |= =path - ^- (quip card _state) - ?< (~(has by rolodex) path) - :- (send-diff path [%create path]) - state(rolodex (~(put by rolodex) path *contacts)) -:: -++ handle-delete - |= =path - ^- (quip card _state) - ?. (~(has by rolodex) path) [~ state] - :- (send-diff path [%delete path]) - state(rolodex (~(del by rolodex) path)) -:: -++ handle-add - |= [=path =ship =contact] - ^- (quip card _state) - =/ contacts (~(got by rolodex) path) - ?< (~(has by contacts) ship) - =. contacts (~(put by contacts) ship contact) - :- (send-diff path [%add path ship contact]) - state(rolodex (~(put by rolodex) path contacts)) -:: -++ handle-remove - |= [=path =ship] - ^- (quip card _state) - =/ contacts (~(got by rolodex) path) - ?. (~(has by contacts) ship) [~ state] - =. contacts (~(del by contacts) ship) - :- (send-diff path [%remove path ship]) - state(rolodex (~(put by rolodex) path contacts)) -:: -++ handle-edit - |= [=path =ship =edit-field] - ^- (quip card _state) - =/ contacts (~(got by rolodex) path) - =/ contact (~(got by contacts) ship) - =. contact (edit-contact contact edit-field) - =. contacts (~(put by contacts) ship contact) - :- (send-diff path [%edit path ship edit-field]) - state(rolodex (~(put by rolodex) path contacts)) -:: -++ edit-contact - |= [con=contact edit=edit-field] - ^- contact - ?- -.edit - %nickname con(nickname nickname.edit) - %email con(email email.edit) - %phone con(phone phone.edit) - %website con(website website.edit) - %notes con(notes notes.edit) - %color con(color color.edit) - %avatar con(avatar avatar.edit) - == -:: -++ send-diff - |= [pax=path upd=contact-update] - ^- (list card) - :~ :* - %give %fact - ~[/all /updates [%contacts pax]] - %contact-update !>(upd) - == == +++ on-leave on-leave:def +++ on-agent on-agent:def +++ on-arvo on-arvo:def +++ on-fail on-fail:def -- diff --git a/pkg/arvo/app/contact-view.hoon b/pkg/arvo/app/contact-view.hoon index 8b0a0d22cf..68694b7085 100644 --- a/pkg/arvo/app/contact-view.hoon +++ b/pkg/arvo/app/contact-view.hoon @@ -1,342 +1,27 @@ -:: contact-view [landscape]: -:: -:: sets up contact JS client and combines commands -:: into semantic actions for the UI -:: -/- - inv=invite-store, - *contact-hook, - *metadata-store, - *metadata-hook, - pull-hook, - push-hook -/+ *server, *contact-json, default-agent, dbug, verb, - grpl=group, mdl=metadata, resource, - group-store +:: contact-view [landscape]: deprecated :: +/+ default-agent |% -+$ versioned-state - $% state-0 - == -:: -+$ state-0 - $: %0 - ~ - == -:: +$ card card:agent:gall -- -=| state-0 -=* state - :: -%- agent:dbug -%+ verb | ^- agent:gall -=< - |_ =bowl:gall - +* this . - contact-core +> - cc ~(. contact-core bowl) - def ~(. (default-agent this %|) bowl) - :: - ++ on-init - ^- (quip card _this) - :_ this - :~ [%pass /updates %agent [our.bowl %contact-store] %watch /updates] - (contact-poke:cc [%create /~/default]) - (contact-poke:cc [%add /~/default our.bowl *contact]) - :* %pass /srv %agent [our.bol %file-server] - %poke %file-server-action - !>([%serve-dir /'~groups' /app/landscape %.n %.y]) - == - == - :: - ++ on-save !>(state) - ++ on-load - |= old-vase=vase - ^- (quip card _this) - =/ old ((soft state-0) q.old-vase) - ?^ old [~ this] - :_ this(state [%0 ~]) - :~ [%pass / %arvo %e %disconnect [~ /'~groups']] - [%pass / %arvo %e %connect [~ /'contact-view'] %contact-view] - :* %pass /srv %agent [our.bol %file-server] - %poke %file-server-action - !>([%serve-dir /'~groups' /app/landscape %.n %.y]) - == - == - :: - ++ on-poke - |= [=mark =vase] - ^- (quip card _this) - ?> (team:title our.bowl src.bowl) - ?+ mark (on-poke:def mark vase) - %json [(poke-json:cc !<(json vase)) this] - %contact-view-action - [(poke-contact-view-action:cc !<(contact-view-action vase)) this] - :: - %handle-http-request - =+ !<([eyre-id=@ta =inbound-request:eyre] vase) - :_ this - %+ give-simple-payload:app eyre-id - %+ require-authorization:app inbound-request - poke-handle-http-request:cc - == - :: - ++ on-watch - |= =path - ^- (quip card _this) - ?> (team:title our.bowl src.bowl) - ?: ?=([%http-response *] path) [~ this] - ?. =(/primary path) (on-watch:def path) - [[%give %fact ~ %json !>((update-to-json [%initial all-scry:cc]))]~ this] - :: - ++ on-agent - |= [=wire =sign:agent:gall] - ^- (quip card _this) - ?+ -.sign (on-agent:def wire sign) - %poke-ack - ?. ?=([%join-group %ship @ @ ~] wire) - (on-agent:def wire sign) - ?^ p.sign - (on-agent:def wire sign) - :_ this - (joined-group:cc t.wire) - :: - %kick - [[%pass / %agent [our.bol %contact-store] %watch /updates]~ this] - :: - %fact - ?+ p.cage.sign (on-agent:def wire sign) - %contact-update - =/ update=json (update-to-json !<(contact-update q.cage.sign)) - [[%give %fact ~[/primary] %json !>(update)]~ this] - == - == - :: - ++ on-arvo - |= [=wire =sign-arvo] - ^- (quip card _this) - ?. ?=(%bound +<.sign-arvo) - (on-arvo:def wire sign-arvo) - [~ this] - :: - ++ on-leave on-leave:def - ++ on-peek on-peek:def - ++ on-fail on-fail:def - -- -:: |_ bol=bowl:gall -++ grp ~(. grpl bol) -++ md ~(. mdl bol) -++ poke-json - |= jon=json - ^- (list card) - ?> (team:title our.bol src.bol) - (poke-contact-view-action (json-to-view-action jon)) ++* this . + def ~(. (default-agent this %|) bol) :: -++ poke-contact-view-action - |= act=contact-view-action - ^- (list card) - ?> (team:title our.bol src.bol) - ?- -.act - %create - =/ rid=resource - [our.bol name.act] - =/ =path - (en-path:resource rid) - ;: weld - :~ (group-poke [%add-group rid policy.act %.n]) - (group-poke [%add-members rid (sy our.bol ~)]) - (group-push-poke %add rid) - (contact-poke [%create path]) - (contact-hook-poke [%add-owned path]) - == - (create-metadata path title.act description.act) - ?. ?=(%invite -.policy.act) - ~ - %+ turn - ~(tap in pending.policy.act) - |= =ship - (send-invite our.bol %contacts rid ship '') - == - :: - %join - =/ =cage - :- %group-update - !> ^- update:group-store - [%add-members resource.act (sy our.bol ~)] - =/ =wire - [%join-group (en-path:resource resource.act)] - [%pass wire %agent [entity.resource.act %group-push-hook] %poke cage]~ - :: - %invite - =* rid resource.act - =/ =group (need (scry-group:grp rid)) - :- (send-invite entity.rid %contacts rid ship.act text.act) - ?. ?=(%invite -.policy.group) ~ - ~[(add-pending rid ship.act)] - :: - %delete - ~ - :: - %remove - =/ rid=resource - (de-path:resource path.act) - :~ (group-poke %remove-members rid (sy ship.act ~)) - (contact-poke [%remove path.act ship.act]) - == - :: - %share - :: determine whether to send to our contact-hook or foreign - :: send contact-action to contact-hook with %add action - [(share-poke recipient.act [%add path.act ship.act contact.act])]~ - :: - %groupify - =/ =path - (en-path:resource resource.act) - %+ weld - :~ (group-poke %expose resource.act ~) - (contact-poke [%create path]) - (contact-hook-poke [%add-owned path]) - == - (create-metadata path title.act description.act) - == -++ poke-handle-http-request - |= =inbound-request:eyre - ^- simple-payload:http - =+ url=(parse-request-line url.request.inbound-request) - =/ name=@t - =+ back-path=(flop site.url) - ?~ back-path - '' - i.back-path - ?+ site.url not-found:gen - [%'contact-view' @ *] - =/ =path (flop t.t.site.url) - ?~ path not-found:gen - =/ contact (contact-scry `^path`(snoc (flop t.path) name)) - ?~ contact not-found:gen - ?~ avatar.u.contact not-found:gen - ?- -.u.avatar.u.contact - %url [[307 ['location' url.u.avatar.u.contact]~] ~] - %octt - =/ max-3-days ['cache-control' 'max-age=259200'] - =/ content-type ['content-type' content-type.u.avatar.u.contact] - [[200 [content-type max-3-days ~]] `octs.u.avatar.u.contact] - == - == +++ on-init on-init:def +++ on-poke on-poke:def +++ on-watch on-watch:def +++ on-agent on-agent:def +++ on-arvo on-arvo:def +++ on-save !>(~) +++ on-load + |= old-vase=vase + ^- (quip card _this) + [~ this] :: -++ joined-group - |= =path - ^- (list card) - =/ rid=resource - (de-path:resource path) - :~ (group-pull-poke [%add entity.rid rid]) - (contact-hook-poke [%add-synced entity.rid path]) - (sync-metadata entity.rid path) - == -:: -:: +utilities -:: -++ add-pending - |= [rid=resource =ship] - ^- card - =/ app=term - ?: =(our.bol entity.rid) - %group-store - %group-push-hook - =/ =cage - :- %group-update - !> ^- action:group-store - [%change-policy rid %invite %add-invites (sy ship ~)] - [%pass / %agent [entity.rid app] %poke cage] -:: -++ send-invite - |= =invite:inv - ^- card - =/ =cage - :- %invite-action - !> ^- action:inv - [%invite %contacts (shaf %invite-uid eny.bol) invite] - [%pass / %agent [recipient.invite %invite-hook] %poke cage] -:: -++ contact-poke - |= act=contact-action - ^- card - [%pass / %agent [our.bol %contact-store] %poke %contact-action !>(act)] -:: -++ contact-hook-poke - |= act=contact-hook-action - ^- card - [%pass / %agent [our.bol %contact-hook] %poke %contact-hook-action !>(act)] -:: -++ share-poke - |= [=ship act=contact-action] - ^- card - [%pass / %agent [ship %contact-hook] %poke %contact-action !>(act)] -:: -++ group-poke - |= act=action:group-store - ^- card - [%pass / %agent [our.bol %group-store] %poke %group-action !>(act)] -:: -++ group-push-poke - |= act=action:push-hook - ^- card - [%pass / %agent [our.bol %group-push-hook] %poke %push-hook-action !>(act)] -:: -++ group-proxy-poke - |= act=action:group-store - ^- card - [%pass / %agent [entity.resource.act %group-push-hook] %poke %group-update !>(act)] -:: -++ group-pull-poke - |= act=action:pull-hook - ^- card - [%pass / %agent [our.bol %group-pull-hook] %poke %pull-hook-action !>(act)] -:: -++ metadata-poke - |= act=metadata-action - ^- card - [%pass / %agent [our.bol %metadata-store] %poke %metadata-action !>(act)] -:: -++ metadata-hook-poke - |= act=metadata-hook-action - ^- card - [%pass / %agent [our.bol %metadata-hook] %poke %metadata-hook-action !>(act)] -:: -++ sync-metadata - |= [=ship =path] - ^- card - (metadata-hook-poke %add-synced ship path) -:: -++ create-metadata - |= [=path title=@t description=@t] - ^- (list card) - =/ =metadata - %* . *metadata - title title - description description - date-created now.bol - creator our.bol - == - :~ (metadata-poke [%add path [%contacts path] metadata]) - (metadata-hook-poke [%add-owned path]) - == -:: -++ all-scry - ^- rolodex - .^(rolodex %gx /(scot %p our.bol)/contact-store/(scot %da now.bol)/all/noun) -:: -++ contact-scry - |= pax=path - ^- (unit contact) - =. pax - ;: weld - /(scot %p our.bol)/contact-store/(scot %da now.bol)/contact - pax - /noun - == - .^((unit contact) %gx pax) +++ on-leave on-leave:def +++ on-peek on-peek:def +++ on-fail on-fail:def -- diff --git a/pkg/arvo/app/group-store.hoon b/pkg/arvo/app/group-store.hoon index 43bcb67f07..c85a0ad637 100644 --- a/pkg/arvo/app/group-store.hoon +++ b/pkg/arvo/app/group-store.hoon @@ -29,7 +29,7 @@ :: Modify the group. Further documented in /sur/group-store.hoon :: :: -/- *group, *contact-view +/- *group /+ store=group-store, default-agent, verb, dbug, resource, *migrate |% +$ card card:agent:gall @@ -284,11 +284,8 @@ |= [recipient=@p out=(list card)] ?: =(recipient our.bol) out - :_ out - %- poke-contact - :* %invite rid recipient - (crip "Rejoin disconnected group {}/{}") - == + :: TODO: figure out contacts integration + out :_ out (try-rejoin rid 0) :: @@ -610,11 +607,6 @@ |= =action:store ^- card [%pass / %agent [our.bol %group-store] %poke %group-action !>(action)] -:: -++ poke-contact - |= act=contact-view-action - ^- card - [%pass / %agent [our.bol %contact-view] %poke %contact-view-action !>(act)] :: +send-diff: update subscribers of new state :: :: We only allow subscriptions on /groups diff --git a/pkg/arvo/app/invite-store.hoon b/pkg/arvo/app/invite-store.hoon index ae74a4e5f0..47de74ba82 100644 --- a/pkg/arvo/app/invite-store.hoon +++ b/pkg/arvo/app/invite-store.hoon @@ -6,6 +6,7 @@ +$ versioned-state $% state-0 state-1 + state-2 == :: +$ invitatory-0 (map serial:store invite-0) @@ -19,9 +20,10 @@ :: +$ state-0 [%0 invites=(map path invitatory-0)] +$ state-1 [%1 =invites:store] ++$ state-2 [%2 =invites:store] -- :: -=| state-1 +=| state-2 =* state - %- agent:dbug ^- agent:gall @@ -43,37 +45,22 @@ ++ on-load |= old-vase=vase =/ old !<(versioned-state old-vase) + =| cards=(list card) + |- + ?: ?=(%2 -.old) + [cards this(state old)] ?: ?=(%1 -.old) - `this(state old) - :- =- [%pass / %agent [our.bowl %invite-store] %poke %invite-action -]~ - !> ^- action:store - [%create %graph] - %= this - state - :- %1 - %- ~(gas by *invites:store) - %+ murn ~(tap by invites.old) - |= [=path =invitatory-0] - ^- (unit [term invitatory:store]) - ?. ?=([@ ~] path) ~ - :- ~ - :- i.path - %- ~(gas by *invitatory:store) - %+ murn ~(tap by invitatory-0) - |= [=serial:store =invite-0] - ^- (unit [serial:store invite:store]) - =/ resource=(unit resource:res) (de-path-soft:res path.invite-0) - ?~ resource ~ - :- ~ - :- serial - ^- invite:store - :* ship.invite-0 - app.invite-0 - u.resource - recipient.invite-0 - text.invite-0 - == - == + =. cards + :~ =- [%pass / %agent [our.bowl %invite-store] %poke %invite-action -] + !> ^- action:store + [%create %groups] + :: + =- [%pass / %agent [our.bowl %invite-store] %poke %invite-action -] + !> ^- action:store + [%delete %contacts] + == + $(-.old %2) + $(old [%1 (~(gas by *invites:store) [%graph *invitatory:store]~)]) :: ++ on-agent on-agent:def ++ on-arvo on-arvo:def @@ -109,11 +96,19 @@ ++ poke-import |= arc=* ^- (quip card _state) - =/ sty=state-1 - :- %1 + =/ sty=state-2 + :- %2 %- remake-map-of-map ;;((tree [term (tree [serial:store invite:store])]) +.arc) - [~ sty] + :_ sty + :~ =- [%pass / %agent [our.bowl %invite-store] %poke %invite-action -] + !> ^- action:store + [%create %groups] + :: + =- [%pass / %agent [our.bowl %invite-store] %poke %invite-action -] + !> ^- action:store + [%delete %contacts] + == :: ++ poke-invite-action |= =action:store diff --git a/pkg/arvo/lib/contact-json.hoon b/pkg/arvo/lib/contact-json.hoon deleted file mode 100644 index 5485ef4c4b..0000000000 --- a/pkg/arvo/lib/contact-json.hoon +++ /dev/null @@ -1,265 +0,0 @@ -/- *contact-view, *contact-hook -/+ group-store, resource -|% -++ nu :: parse number as hex - |= jon=json - ?> ?=([%s *] jon) - (rash p.jon hex) -:: -++ hook-update-to-json - |= upd=contact-hook-update - =, enjs:format - ^- json - %+ frond %contact-hook-update - %- pairs - %+ turn ~(tap by synced.upd) - |= [pax=^path shp=^ship] - ^- [cord json] - [(spat pax) s+(scot %p shp)] -:: -++ rolodex-to-json - |= rolo=rolodex - =, enjs:format - ^- json - %- pairs - %+ turn ~(tap by rolo) - |= [pax=^path =contacts] - ^- [cord json] - :- (spat pax) - (contacts-to-json pax contacts) -:: -++ contacts-to-json - |= [=path con=contacts] - ^- json - %- pairs:enjs:format - %+ turn ~(tap by con) - |= [=ship =contact] - ^- [cord json] - [(crip (slag 1 (scow %p ship))) (contact-to-json path ship contact)] -:: -++ contact-to-json - |= [=path =ship con=contact] - ^- json - %- pairs:enjs:format - :~ [%nickname s+nickname.con] - [%email s+email.con] - [%phone s+phone.con] - [%website s+website.con] - [%notes s+notes.con] - [%color s+(scot %ux color.con)] - [%avatar (avatar-to-json path ship avatar.con)] - == -:: -++ edit-to-json - |= [=path =ship edit=edit-field] - ^- json - %+ frond:enjs:format -.edit - ?- -.edit - %nickname s+nickname.edit - %email s+email.edit - %phone s+phone.edit - %website s+website.edit - %notes s+notes.edit - %color s+(scot %ux color.edit) - %avatar (avatar-to-json path ship avatar.edit) - == -:: -++ avatar-to-json - |= [=path =ship avat=(unit avatar)] - ^- json - ?~ avat ~ - ?- -.u.avat - %octt - :- %s - %- crip - %- zing - :~ "/contact-view" - (trip (spat path)) - "/" - (trip (scot %p ship)) - == - :: - %url s+url.u.avat - == -:: -++ update-to-json - |= upd=contact-update - =, enjs:format - ^- json - %+ frond %contact-update - %- pairs - :~ - ?: ?=(%initial -.upd) - [%initial (rolodex-to-json rolodex.upd)] - ?: ?=(%create -.upd) - [%create (pairs [%path (path path.upd)]~)] - ?: ?=(%delete -.upd) - [%delete (pairs [%path (path path.upd)]~)] - ?: ?=(%add -.upd) - :- %add - %- pairs - :~ [%path (path path.upd)] - [%ship (ship ship.upd)] - [%contact (contact-to-json path.upd ship.upd contact.upd)] - == - ?: ?=(%remove -.upd) - :- %remove - %- pairs - :~ [%path (path path.upd)] - [%ship (ship ship.upd)] - == - ?: ?=(%edit -.upd) - :- %edit - %- pairs - :~ [%path (path path.upd)] - [%ship (ship ship.upd)] - [%edit-field (edit-to-json path.upd ship.upd edit-field.upd)] - == - [*@t *^json] - == -:: -++ json-to-view-action - |= jon=json - ^- contact-view-action - =, dejs:format - =< (parse-json jon) - |% - ++ parse-json - %- of - :~ [%create create] - [%delete delete] - [%join dejs:resource] - [%invite invite] - [%remove remove] - [%share share] - == - :: - ++ create - %- ot - :~ [%name so] - [%policy policy:dejs:group-store] - [%title so] - [%description so] - == - :: - ++ invite - %- ot - :~ [%resource dejs:resource] - [%ship (su ;~(pfix sig fed:ag))] - [%text so] - == - :: - ++ delete (ot [%path pa]~) - :: - ++ remove - %- ot - :~ [%path pa] - [%ship (su ;~(pfix sig fed:ag))] - == - :: - ++ share - %- ot - :~ [%recipient (su ;~(pfix sig fed:ag))] - [%path pa] - [%ship (su ;~(pfix sig fed:ag))] - [%contact cont] - == - -- -:: -++ json-to-action - |= jon=json - ^- contact-action - =, dejs:format - =< (parse-json jon) - |% - ++ parse-json - %- of - :~ [%create create] - [%delete delete] - [%add add] - [%remove remove] - [%edit edit] - == - :: - ++ create - (ot [%path pa]~) - :: - ++ delete - (ot [%path pa]~) - :: - ++ add - %- ot - :~ [%path pa] - [%ship (su ;~(pfix sig fed:ag))] - [%contact cont] - == - :: - ++ remove - %- ot - :~ [%path pa] - [%ship (su ;~(pfix sig fed:ag))] - == - :: - ++ edit - %- ot - :~ [%path pa] - [%ship (su ;~(pfix sig fed:ag))] - [%edit-field edit-fi] - == - -- -:: -++ octet - %- ot:dejs:format - :~ [%p ni:dejs:format] - [%q so:dejs:format] - == -:: -++ avat - |= jon=json - ^- avatar - |^ - =/ =avatar (parse-json jon) - ?- -.avatar - %url avatar - %octt - =. octs.avatar (need (de:base64:mimes:html q.octs.avatar)) - avatar - == - :: - ++ parse-json - %- of:dejs:format - :~ [%octt octt] - [%url url] - == - :: - ++ octt - %- ot:dejs:format - :~ [%content-type so:dejs:format] - [%octs octet] - == - :: - ++ url so:dejs:format - -- -:: -++ cont - %- ot:dejs:format - :~ [%nickname so:dejs:format] - [%email so:dejs:format] - [%phone so:dejs:format] - [%website so:dejs:format] - [%notes so:dejs:format] - [%color nu] - [%avatar (mu:dejs:format avat)] - == -:: -++ edit-fi - %- of:dejs:format - :~ [%nickname so:dejs:format] - [%email so:dejs:format] - [%phone so:dejs:format] - [%website so:dejs:format] - [%notes so:dejs:format] - [%color nu] - [%avatar (mu:dejs:format avat)] - == --- diff --git a/pkg/arvo/lib/contact-store.hoon b/pkg/arvo/lib/contact-store.hoon new file mode 100644 index 0000000000..f12d2392a5 --- /dev/null +++ b/pkg/arvo/lib/contact-store.hoon @@ -0,0 +1,123 @@ +/- sur=contact-store +=< [sur .] +=, sur +|% +++ nu :: parse number as hex + |= jon=json + ?> ?=([%s *] jon) + (rash p.jon hex) +:: +++ enjs + =, enjs:format + |% + ++ update + |= upd=^update + ^- json + |^ (frond %contact-update (pairs ~[(encode upd)])) + :: + ++ encode + |= upd=update-0 + ^- [cord json] + ?- -.upd + %initial + :: TODO: initial + :- %initial + *json + %add + :- %add + %- pairs + :~ [%path (path path.upd)] + [%ship (ship ship.upd)] + [%contact (contact-to-json path.upd ship.upd contact.upd)] + == + :: + %remove + :- %remove + %- pairs + :~ [%path (path path.upd)] + [%ship (ship ship.upd)] + == + :: + %edit + :- %edit + %- pairs + :~ [%path (path path.upd)] + [%ship (ship ship.upd)] + [%edit-field (edit-to-json edit-field.upd)] + == + == + -- + :: + ++ rolodex-to-json + |= rol=rolodex:store + ^- json + %- pairs:enjs:format + %+ turn ~(tap by rol) + |= [=ship =contact:store] + ^- [cord json] + [(crip (slag 1 (scow %p ship))) (contact-to-json contact)] + :: + ++ contact-to-json + |= con=contact:store + ^- json + %- pairs:enjs:format + :~ [%nickname s+nickname.con] + [%email s+email.con] + [%phone s+phone.con] + [%website s+website.con] + [%color s+(scot %ux color.con)] + [%avatar ?~(avatar.edit ~ s+u.avatar.con)] + == + :: + ++ edit-to-json + |= edit=edit-field + ^- json + %+ frond:enjs:format -.edit + ?- -.edit + %nickname s+nickname.edit + %email s+email.edit + %phone s+phone.edit + %website s+website.edit + %color s+(scot %ux color.edit) + %avatar ?~(avatar.edit ~ s+u.avatar.edit) + == + -- +:: +++ dej + =, dejs:formats + |% + ++ update + |= jon=json + ^- ^update + =< (decode jon) + |% + ++ decode + %- of + :~ [%initial initial] + [%add add-contact] + [%remove remove-contact] + [%edit edit-contact] + == + :: + ++ cont + %- ot + :~ [%nickname so] + [%email so] + [%phone so] + [%website so] + [%color nu] + [%avatar (mu so)] + == + :: + ++ edit-fi + %- of + :~ [%nickname so] + [%email so] + [%phone so] + [%website so] + [%color nu] + [%avatar (mu so)] + == + -- + -- +-- diff --git a/pkg/arvo/mar/contact/action.hoon b/pkg/arvo/mar/contact/action.hoon deleted file mode 100644 index a756fb8102..0000000000 --- a/pkg/arvo/mar/contact/action.hoon +++ /dev/null @@ -1,15 +0,0 @@ -/+ *contact-json -|_ act=contact-action -++ grad %noun -++ grow - |% - ++ noun act - -- -++ grab - |% - ++ noun contact-action - ++ json - |= jon=^json - (json-to-action jon) - -- --- diff --git a/pkg/arvo/mar/contact/hook-update.hoon b/pkg/arvo/mar/contact/hook-update.hoon deleted file mode 100644 index 481582282c..0000000000 --- a/pkg/arvo/mar/contact/hook-update.hoon +++ /dev/null @@ -1,15 +0,0 @@ -/+ *contact-json -|_ upd=contact-hook-update -++ grad %noun -++ grow - |% - ++ noun upd - ++ json (hook-update-to-json upd) - -- -:: -++ grab - |% - ++ noun contact-hook-update - -- -:: --- diff --git a/pkg/arvo/mar/contact/initial.hoon b/pkg/arvo/mar/contact/initial.hoon deleted file mode 100644 index 0bf1d3e8d8..0000000000 --- a/pkg/arvo/mar/contact/initial.hoon +++ /dev/null @@ -1,16 +0,0 @@ -/+ *contact-json -|_ rolo=rolodex -:: -++ grad %noun -++ grow - |% - ++ noun +<.grow - ++ json (rolodex-to-json rolo) - -- -:: -++ grab - |% - ++ noun rolodex - -- -:: --- diff --git a/pkg/arvo/mar/contact/update.hoon b/pkg/arvo/mar/contact/update.hoon index 75e5931255..733d30f48e 100644 --- a/pkg/arvo/mar/contact/update.hoon +++ b/pkg/arvo/mar/contact/update.hoon @@ -1,15 +1,16 @@ -/+ *contact-json -|_ upd=contact-update +/+ *contact-store +:: +|_ upd=update ++ grad %noun ++ grow |% ++ noun upd - ++ json (update-to-json upd) + ++ json (update:enjs upd) -- :: ++ grab |% - ++ noun contact-update + ++ noun update + ++ json update:dejs -- -:: -- diff --git a/pkg/arvo/mar/contact/view-action.hoon b/pkg/arvo/mar/contact/view-action.hoon deleted file mode 100644 index bd386555da..0000000000 --- a/pkg/arvo/mar/contact/view-action.hoon +++ /dev/null @@ -1,12 +0,0 @@ -/- *contact-view -|_ act=contact-view-action -++ grad %noun -++ grow - |% - ++ noun act - -- -++ grab - |% - ++ noun contact-view-action - -- --- diff --git a/pkg/arvo/sur/contact-hook.hoon b/pkg/arvo/sur/contact-hook.hoon deleted file mode 100644 index 5926ec5a5a..0000000000 --- a/pkg/arvo/sur/contact-hook.hoon +++ /dev/null @@ -1,18 +0,0 @@ -|% -+$ contact-hook-action - $% :: %add-owned: make a contacts list accessible to foreign ships - :: who are members of that list - :: - [%add-owned =path] - :: %add-synced: mirror a foreign contacts list to our contact-store - :: - [%add-synced =ship =path] - :: %remove: stop mirroring a foreign contacts list or stop allowing - :: a local contacts list to be mirrored - :: - [%remove =path] - == -:: -+$ synced (map path ship) -+$ contact-hook-update [%initial =synced] --- diff --git a/pkg/arvo/sur/contact-store.hoon b/pkg/arvo/sur/contact-store.hoon index 7bd642b7b9..6c7db48a37 100644 --- a/pkg/arvo/sur/contact-store.hoon +++ b/pkg/arvo/sur/contact-store.hoon @@ -1,20 +1,13 @@ /- *identity |% -+$ rolodex (map path contacts) -+$ contacts (map ship contact) -+$ avatar - $% [%octt content-type=@t octs=[p=@ud q=@t]] - [%url url=@t] - == -:: ++$ rolodex (map ship contact) +$ contact $: nickname=@t email=@t phone=@t website=@t - notes=@t color=@ux - avatar=(unit avatar) + avatar=(unit @t) == :: +$ edit-field @@ -22,22 +15,14 @@ [%email email=@t] [%phone phone=@t] [%website website=@t] - [%notes notes=@t] [%color color=@ux] - [%avatar avatar=(unit avatar)] + [%avatar avatar=(unit @t)] == :: -+$ contact-action - $% [%create =path] - [%delete =path] - [%add =path =ship =contact] - [%remove =path =ship] - [%edit =path =ship =edit-field] - == -:: -+$ contact-update ++$ update $% [%initial =rolodex] - [%contacts =path =contacts] - contact-action + [%add =ship =contact] + [%remove =ship] + [%edit =ship =edit-field] == -- diff --git a/pkg/arvo/sur/contact-view.hoon b/pkg/arvo/sur/contact-view.hoon deleted file mode 100644 index f02a5d3b70..0000000000 --- a/pkg/arvo/sur/contact-view.hoon +++ /dev/null @@ -1,27 +0,0 @@ -/- *contact-store, *group, *resource -:: -|% -+$ contact-view-action - $% :: %create: create in both groups and contacts - :: - [%create name=term =policy title=@t description=@t] - :: %join: join open group in both groups and contacts - :: - [%join =resource] - :: %invite: invite to invite-only group and contacts - :: - [%invite =resource =ship text=cord] - :: %remove: remove from both groups and contacts - :: - [%remove =path =ship] - :: %delete: delete in both groups and contacts - :: - [%delete =path] - :: %share: send %add contact-action to to recipient's contact-hook - :: - [%share recipient=ship =path =ship =contact] - :: %groupify: create contacts object for a preexisting group - :: - [%groupify =resource title=@t description=@t] - == --- From 1eb99bfdcd3e84531b9f491f24d88cae4a160817 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Mon, 11 Jan 2021 13:39:36 -0600 Subject: [PATCH 02/42] contact-store: wrote out all the JSON conversions and store compiles --- pkg/arvo/app/contact-store.hoon | 9 +- pkg/arvo/lib/contact-store.hoon | 139 ++++++++++++++++--------------- pkg/arvo/sur/contact-store.hoon | 1 - pkg/arvo/ted/group/on-leave.hoon | 17 +--- 4 files changed, 78 insertions(+), 88 deletions(-) diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index 0b748ef0a7..ffa066b8e5 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -30,12 +30,8 @@ |= old-vase=vase ^- (quip card _this) =/ old !<(versioned-state old-vase) - ?- -.old - %4 [~ this(state old)] - %3 [~ this] - %2 [~ this] - %1 [~ this] - %0 [~ this] + ?+ -.old [~ this] + %4 [~ this(state old)] == :: ++ on-watch @@ -128,6 +124,7 @@ ++ import |= arc=* ^- (quip card _state) + :: note: we are purposefully wiping all state before state-4 [~ *state-4] -- :: diff --git a/pkg/arvo/lib/contact-store.hoon b/pkg/arvo/lib/contact-store.hoon index f12d2392a5..719886ca1a 100644 --- a/pkg/arvo/lib/contact-store.hoon +++ b/pkg/arvo/lib/contact-store.hoon @@ -13,78 +13,71 @@ ++ update |= upd=^update ^- json - |^ (frond %contact-update (pairs ~[(encode upd)])) - :: - ++ encode - |= upd=update-0 - ^- [cord json] - ?- -.upd - %initial - :: TODO: initial - :- %initial - *json - %add - :- %add - %- pairs - :~ [%path (path path.upd)] - [%ship (ship ship.upd)] - [%contact (contact-to-json path.upd ship.upd contact.upd)] - == - :: - %remove - :- %remove - %- pairs - :~ [%path (path path.upd)] - [%ship (ship ship.upd)] - == - :: - %edit - :- %edit - %- pairs - :~ [%path (path path.upd)] - [%ship (ship ship.upd)] - [%edit-field (edit-to-json edit-field.upd)] - == - == - -- - :: - ++ rolodex-to-json - |= rol=rolodex:store - ^- json - %- pairs:enjs:format - %+ turn ~(tap by rol) - |= [=ship =contact:store] + %+ frond %contact-update + %- pairs + :_ ~ ^- [cord json] - [(crip (slag 1 (scow %p ship))) (contact-to-json contact)] - :: - ++ contact-to-json - |= con=contact:store - ^- json - %- pairs:enjs:format - :~ [%nickname s+nickname.con] - [%email s+email.con] - [%phone s+phone.con] - [%website s+website.con] - [%color s+(scot %ux color.con)] - [%avatar ?~(avatar.edit ~ s+u.avatar.con)] + ?- -.upd + %initial + :- %initial + (pairs [%rolodex (rolo rolodex.upd)]~) + :: + %add + :- %add + %- pairs + :~ [%ship (ship ship.upd)] + [%contact (cont contact.upd)] + == + :: + %remove + :- %remove + (pairs [%ship (ship ship.upd)]~) + :: + %edit + :- %edit + %- pairs + :~ [%ship (ship ship.upd)] + [%edit-field (edit edit-field.upd)] + == == :: - ++ edit-to-json - |= edit=edit-field + ++ rolo + |= =rolodex ^- json - %+ frond:enjs:format -.edit - ?- -.edit - %nickname s+nickname.edit - %email s+email.edit - %phone s+phone.edit - %website s+website.edit - %color s+(scot %ux color.edit) - %avatar ?~(avatar.edit ~ s+u.avatar.edit) + %- pairs + %+ turn ~(tap by rolodex) + |= [=^ship =contact] + ^- [cord json] + [(scot %p ship) (cont contact)] + :: + ++ cont + |= =contact + ^- json + %- pairs + :~ [%nickname s+nickname.contact] + [%email s+email.contact] + [%phone s+phone.contact] + [%website s+website.contact] + [%color s+(scot %ux color.contact)] + [%avatar ?~(avatar.contact ~ s+u.avatar.contact)] + == + :: + ++ edit + |= field=edit-field + ^- json + %+ frond -.field + ?- -.field + %nickname s+nickname.field + %email s+email.field + %phone s+phone.field + %website s+website.field + %color s+(scot %ux color.field) + %avatar ?~(avatar.field ~ s+u.avatar.field) == -- :: ++ dej - =, dejs:formats + =, dejs:format |% ++ update |= jon=json @@ -99,6 +92,22 @@ [%edit edit-contact] == :: + ++ initial (op ;~(pfix sig fed:ag) cont) + :: + ++ add-contact + %- ot + :~ [%ship (su ;~(pfix sig fed:ag))] + [%contact cont] + == + :: + ++ remove-contact (ot [%ship (su ;~(pfix sig fed:ag))]~) + :: + ++ edit-contact + %- ot + :~ [%ship (su ;~(pfix sig fed:ag))] + [%edit-field edit] + == + :: ++ cont %- ot :~ [%nickname so] @@ -109,7 +118,7 @@ [%avatar (mu so)] == :: - ++ edit-fi + ++ edit %- of :~ [%nickname so] [%email so] diff --git a/pkg/arvo/sur/contact-store.hoon b/pkg/arvo/sur/contact-store.hoon index 6c7db48a37..ea118a2e08 100644 --- a/pkg/arvo/sur/contact-store.hoon +++ b/pkg/arvo/sur/contact-store.hoon @@ -1,4 +1,3 @@ -/- *identity |% +$ rolodex (map ship contact) +$ contact diff --git a/pkg/arvo/ted/group/on-leave.hoon b/pkg/arvo/ted/group/on-leave.hoon index b3e02ef355..ed26d6ccc0 100644 --- a/pkg/arvo/ted/group/on-leave.hoon +++ b/pkg/arvo/ted/group/on-leave.hoon @@ -1,4 +1,4 @@ -/- spider, grp=group-store, gra=graph-store, met=metadata-store, con=contact-store +/- spider, grp=group-store, gra=graph-store, met=metadata-store /+ strandio, res=resource :: =* strand strand:spider @@ -33,21 +33,6 @@ [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 From dca2c9ae586809f258a267ec3e52a7e4b700a75d Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Mon, 11 Jan 2021 13:40:40 -0600 Subject: [PATCH 03/42] interface: updated api/logic in reducer and api to reflect new %contact-store --- pkg/interface/src/logic/api/contacts.ts | 59 ++++--------------- .../src/logic/reducers/contact-update.ts | 34 ++--------- .../src/logic/subscription/global.ts | 4 +- 3 files changed, 20 insertions(+), 77 deletions(-) diff --git a/pkg/interface/src/logic/api/contacts.ts b/pkg/interface/src/logic/api/contacts.ts index 3049442f5a..2874ba0ef0 100644 --- a/pkg/interface/src/logic/api/contacts.ts +++ b/pkg/interface/src/logic/api/contacts.ts @@ -5,74 +5,41 @@ import { Contact, ContactEdit } from '~/types/contact-update'; import { GroupPolicy, Resource } from '~/types/group-update'; export default class ContactsApi extends BaseApi { - create( - name: string, - policy: Enc, - title: string, - description: string - ) { - return this.viewAction({ - create: { - name, - policy, - title, - description, - }, - }); + add(ship: Patp, contact: any) { + return this.storeAction({ add: { ship, contact } }); } - share(recipient: Patp, path: Patp, ship: Patp, contact: Contact) { - return this.viewAction({ - share: { - recipient, - path, - ship, - contact, - }, - }); + remove(ship: Patp) { + return this.storeAction({ remove: { ship } }); } - remove(path: Path, ship: Patp) { - return this.viewAction({ remove: { path, ship } }); - } - - edit(path: Path, ship: Patp, editField: ContactEdit) { + edit(ship: Patp, editField: ContactEdit) { /* editField can be... {nickname: ''} {email: ''} {phone: ''} {website: ''} - {notes: ''} {color: 'fff'} // with no 0x prefix {avatar: null} - {avatar: {url: ''}} + {avatar: ''} */ - return this.hookAction({ + return this.storeAction({ edit: { - path, ship, 'edit-field': editField, }, }); } - invite(resource: Resource, ship: Patp, text = '') { - return this.viewAction({ - invite: { resource, ship, text }, - }); + private storeAction(action: any): Promise { + return this.action('contact-store', 'contact-update', action) } - join(resource: Resource) { - return this.viewAction({ - join: resource, - }); + private viewAction(threadName: string, action: any) { + return this.spider('contact-view-action', 'json', threadName, action); } - private hookAction(data) { - return this.action('contact-hook', 'contact-action', data); - } - - private viewAction(data) { - return this.action('contact-view', 'json', data); + private hookAction(ship: Patp, action: any): Promise { + return this.action('contact-push-hook', 'contact-update', action); } } diff --git a/pkg/interface/src/logic/reducers/contact-update.ts b/pkg/interface/src/logic/reducers/contact-update.ts index 88a527ed1e..2ba1da1d0c 100644 --- a/pkg/interface/src/logic/reducers/contact-update.ts +++ b/pkg/interface/src/logic/reducers/contact-update.ts @@ -10,8 +10,6 @@ export default class ContactReducer { const data = _.get(json, 'contact-update', false); if (data) { this.initial(data, state); - this.create(data, state); - this.delete(data, state); this.add(data, state); this.remove(data, state); this.edit(data, state); @@ -25,27 +23,10 @@ export default class ContactReducer { } } - create(json: ContactUpdate, state: S) { - const data = _.get(json, 'create', false); - if (data) { - state.contacts[data.path] = {}; - } - } - - delete(json: ContactUpdate, state: S) { - const data = _.get(json, 'delete', false); - if (data) { - delete state.contacts[data.path]; - } - } - add(json: ContactUpdate, state: S) { const data = _.get(json, 'add', false); - if ( - data && - (data.path in state.contacts) - ) { - state.contacts[data.path][data.ship] = data.contact; + if (data) { + state.contacts[data.ship] = data.contact; } } @@ -53,10 +34,9 @@ export default class ContactReducer { const data = _.get(json, 'remove', false); if ( data && - (data.path in state.contacts) && - (data.ship in state.contacts[data.path]) + (data.ship in state.contacts) ) { - delete state.contacts[data.path][data.ship]; + delete state.contacts[data.ship]; } } @@ -64,15 +44,13 @@ export default class ContactReducer { const data = _.get(json, 'edit', false); if ( data && - (data.path in state.contacts) && - (data.ship in state.contacts[data.path]) + (data.ship in state.contacts) ) { const edit = Object.keys(data['edit-field']); if (edit.length !== 1) { return; } - state.contacts[data.path][data.ship][edit[0]] = - data['edit-field'][edit[0]]; + state.contacts[data.ship][edit[0]] = data['edit-field'][edit[0]]; } } } diff --git a/pkg/interface/src/logic/subscription/global.ts b/pkg/interface/src/logic/subscription/global.ts index ed4a2c94c8..d6af894282 100644 --- a/pkg/interface/src/logic/subscription/global.ts +++ b/pkg/interface/src/logic/subscription/global.ts @@ -10,7 +10,6 @@ import _ from 'lodash'; type AppSubscription = [Path, string]; const groupSubscriptions: AppSubscription[] = [ - ['/synced', 'contact-hook'] ]; const graphSubscriptions: AppSubscription[] = [ @@ -37,8 +36,7 @@ export default class GlobalSubscription extends BaseSubscription { this.subscribe('/groups', 'group-store'); this.clearQueue(); - - this.subscribe('/primary', 'contact-view'); + this.subscribe('/updates', 'contact-store'); this.subscribe('/all', 's3-store'); this.subscribe('/keys', 'graph-store'); this.subscribe('/updates', 'hark-store'); From cdb91291ed393b8fed63783491b2f211aa00981d Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Mon, 11 Jan 2021 15:09:41 -0600 Subject: [PATCH 04/42] contact-store: %allow/%disallow support --- pkg/arvo/app/contact-store.hoon | 67 +++++++++++++++++++++++---------- pkg/arvo/lib/contact-store.hoon | 30 +++++++++++++-- pkg/arvo/sur/contact-store.hoon | 8 ++++ 3 files changed, 82 insertions(+), 23 deletions(-) diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index ffa066b8e5..bcb2897de3 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -2,11 +2,16 @@ :: :: data store that holds individual contact data :: -/- store=contact-store +/- store=contact-store, *resource /+ default-agent, dbug, *migrate |% +$ card card:agent:gall -+$ state-4 [%4 =rolodex:store] ++$ state-4 + $: %4 + =rolodex:store + allowed-groups=(set resource) + allowed-ships=(set ship) + == +$ versioned-state $% [%0 *] [%1 *] @@ -41,14 +46,15 @@ |^ =/ cards=(list card) ?+ path (on-watch:def path) + [%all ~] (give [%initial rolodex]) [%updates ~] ~ == [cards this] :: ++ give - |= =cage + |= =update:store ^- (list card) - [%give %fact ~ cage]~ + [%give %fact ~ [%contact-update !>(update)]]~ -- :: ++ on-poke @@ -68,10 +74,12 @@ ^- (quip card _state) |^ ?- -.update - %initial (handle-initial +.update) - %add (handle-add +.update) - %remove (handle-remove +.update) - %edit (handle-edit +.update) + %initial (handle-initial +.update) + %add (handle-add +.update) + %remove (handle-remove +.update) + %edit (handle-edit +.update) + %allow (handle-allow +.update) + %disallow (handle-disallow +.update) == :: ++ handle-initial @@ -97,22 +105,42 @@ :: ++ handle-edit |= [=ship =edit-field:store] + |^ ^- (quip card _state) =/ contact (~(got by rolodex) ship) =. contact (edit-contact contact edit-field) :- (send-diff [%edit ship edit-field]) state(rolodex (~(put by rolodex) ship contact)) + :: + ++ edit-contact + |= [=contact:store edit=edit-field:store] + ^- contact:store + ?- -.edit + %nickname contact(nickname nickname.edit) + %email contact(email email.edit) + %phone contact(phone phone.edit) + %website contact(website website.edit) + %color contact(color color.edit) + %avatar contact(avatar avatar.edit) + == + -- :: - ++ edit-contact - |= [=contact:store edit=edit-field:store] - ^- contact:store - ?- -.edit - %nickname contact(nickname nickname.edit) - %email contact(email email.edit) - %phone contact(phone phone.edit) - %website contact(website website.edit) - %color contact(color color.edit) - %avatar contact(avatar avatar.edit) + ++ handle-allow + |= =beings:store + ^- (quip card _state) + :- (send-diff [%allow beings]) + ?- -.beings + %group state(allowed-groups (~(put in allowed-groups) resource.beings)) + %ships state(allowed-ships (~(uni in allowed-ships) ships.beings)) + == + :: + ++ handle-disallow + |= =beings:store + ^- (quip card _state) + :- (send-diff [%disallow beings]) + ?- -.beings + %group state(allowed-groups (~(del in allowed-groups) resource.beings)) + %ships state(allowed-ships (~(dif in allowed-ships) ships.beings)) == :: ++ send-diff @@ -133,12 +161,11 @@ ^- (unit (unit cage)) ?+ path (on-peek:def path) [%x %all ~] ``noun+!>(rolodex) - [%x %export ~] ``noun+!>(state) :: [%x %contact @ ~] =/ =ship (slav %p i.t.t.path) =/ contact=(unit contact:store) (~(get by rolodex) ship) - ?~ contact ~ + ?~ contact [~ ~] :- ~ :- ~ :- %contact-update !> ^- update:store [%add ship u.contact] diff --git a/pkg/arvo/lib/contact-store.hoon b/pkg/arvo/lib/contact-store.hoon index 719886ca1a..78e9c39ca2 100644 --- a/pkg/arvo/lib/contact-store.hoon +++ b/pkg/arvo/lib/contact-store.hoon @@ -1,4 +1,5 @@ /- sur=contact-store +/+ res=resource =< [sur .] =, sur |% @@ -19,8 +20,7 @@ ^- [cord json] ?- -.upd %initial - :- %initial - (pairs [%rolodex (rolo rolodex.upd)]~) + [%initial (rolo rolodex.upd)] :: %add :- %add @@ -39,6 +39,14 @@ :~ [%ship (ship ship.upd)] [%edit-field (edit edit-field.upd)] == + :: + %allow + :- %allow + (pairs [%beings (beng beings.upd)]~) + :: + %disallow + :- %disallow + (pairs [%beings (beng beings.upd)]~) == :: ++ rolo @@ -74,9 +82,17 @@ %color s+(scot %ux color.field) %avatar ?~(avatar.field ~ s+u.avatar.field) == + :: + ++ beng + |= =beings + ^- json + ?- -.beings + %ships [%a (turn ~(tap in ships.beings) |=(s=^ship s+(scot %p s)))] + %group (enjs:res resource.beings) + == -- :: -++ dej +++ dejs =, dejs:format |% ++ update @@ -90,6 +106,8 @@ [%add add-contact] [%remove remove-contact] [%edit edit-contact] + [%allow beings] + [%disallow beings] == :: ++ initial (op ;~(pfix sig fed:ag) cont) @@ -108,6 +126,12 @@ [%edit-field edit] == :: + ++ beings + %- of + :~ [%ships (as (su ;~(pfix sig fed:ag)))] + [%group dejs:res] + == + :: ++ cont %- ot :~ [%nickname so] diff --git a/pkg/arvo/sur/contact-store.hoon b/pkg/arvo/sur/contact-store.hoon index ea118a2e08..ad6765bca3 100644 --- a/pkg/arvo/sur/contact-store.hoon +++ b/pkg/arvo/sur/contact-store.hoon @@ -1,3 +1,4 @@ +/- *resource |% +$ rolodex (map ship contact) +$ contact @@ -18,10 +19,17 @@ [%avatar avatar=(unit @t)] == :: ++$ beings + $% [%ships ships=(set ship)] + [%group =resource] + == +:: +$ update $% [%initial =rolodex] [%add =ship =contact] [%remove =ship] [%edit =ship =edit-field] + [%allow =beings] + [%disallow =beings] == -- From 6d95cc76a4d20c9ac8fcb2b5d80fe73ed220a360 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 19 Jan 2021 13:56:17 -0600 Subject: [PATCH 05/42] interface: basic new contact-store support --- .../src/logic/reducers/contact-update.ts | 78 +++++++++---------- pkg/interface/src/logic/store/store.ts | 7 +- .../src/logic/subscription/global.ts | 3 +- 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/pkg/interface/src/logic/reducers/contact-update.ts b/pkg/interface/src/logic/reducers/contact-update.ts index 2ba1da1d0c..0f03c1292e 100644 --- a/pkg/interface/src/logic/reducers/contact-update.ts +++ b/pkg/interface/src/logic/reducers/contact-update.ts @@ -5,52 +5,52 @@ import { ContactUpdate } from '~/types/contact-update'; type ContactState = Pick; -export default class ContactReducer { - reduce(json: Cage, state: S) { - const data = _.get(json, 'contact-update', false); - if (data) { - this.initial(data, state); - this.add(data, state); - this.remove(data, state); - this.edit(data, state); - } +export const ContactReducer = (json, state) => { + const data = _.get(json, 'contact-update', false); + if (data) { + console.log(data); + initial(data, state); + add(data, state); + remove(data, state); + edit(data, state); + console.log(state); } +}; - initial(json: ContactUpdate, state: S) { - const data = _.get(json, 'initial', false); - if (data) { - state.contacts = data; - } +const initial = (json: ContactUpdate, state: S) => { + const data = _.get(json, 'initial', false); + if (data) { + state.contacts = data; } +}; - add(json: ContactUpdate, state: S) { - const data = _.get(json, 'add', false); - if (data) { - state.contacts[data.ship] = data.contact; - } +const add = (json: ContactUpdate, state: S) => { + const data = _.get(json, 'add', false); + if (data) { + state.contacts[data.ship] = data.contact; } +}; - remove(json: ContactUpdate, state: S) { - const data = _.get(json, 'remove', false); - if ( - data && - (data.ship in state.contacts) - ) { - delete state.contacts[data.ship]; - } +const remove = (json: ContactUpdate, state: S) => { + const data = _.get(json, 'remove', false); + if ( + data && + (data.ship in state.contacts) + ) { + delete state.contacts[data.ship]; } +}; - edit(json: ContactUpdate, state: S) { - const data = _.get(json, 'edit', false); - if ( - data && - (data.ship in state.contacts) - ) { - const edit = Object.keys(data['edit-field']); - if (edit.length !== 1) { - return; - } - state.contacts[data.ship][edit[0]] = data['edit-field'][edit[0]]; +const edit = (json: ContactUpdate, state: S) => { + const data = _.get(json, 'edit', false); + if ( + data && + (data.ship in state.contacts) + ) { + const edit = Object.keys(data['edit-field']); + if (edit.length !== 1) { + return; } + state.contacts[data.ship][edit[0]] = data['edit-field'][edit[0]]; } -} +}; diff --git a/pkg/interface/src/logic/store/store.ts b/pkg/interface/src/logic/store/store.ts index 56720d4d97..1229bf0b83 100644 --- a/pkg/interface/src/logic/store/store.ts +++ b/pkg/interface/src/logic/store/store.ts @@ -6,10 +6,10 @@ import LocalReducer from '../reducers/local'; import { StoreState } from './type'; import { Timebox } from '~/types'; import { Cage } from '~/types/cage'; -import ContactReducer from '../reducers/contact-update'; import S3Reducer from '../reducers/s3-update'; import { GraphReducer } from '../reducers/graph-update'; import { HarkReducer } from '../reducers/hark-update'; +import { ContactReducer } from '../reducers/contact-update'; import GroupReducer from '../reducers/group-update'; import LaunchReducer from '../reducers/launch-update'; import ConnectionReducer from '../reducers/connection'; @@ -34,7 +34,6 @@ export default class GlobalStore extends BaseStore { inviteReducer = new InviteReducer(); metadataReducer = new MetadataReducer(); localReducer = new LocalReducer(); - contactReducer = new ContactReducer(); s3Reducer = new S3Reducer(); groupReducer = new GroupReducer(); launchReducer = new LaunchReducer(); @@ -54,7 +53,7 @@ export default class GlobalStore extends BaseStore { baseHash: null, invites: {}, associations: { - contacts: {}, + groups: {}, graph: {}, }, groups: {}, @@ -97,12 +96,12 @@ export default class GlobalStore extends BaseStore { this.inviteReducer.reduce(data, this.state); this.metadataReducer.reduce(data, this.state); this.localReducer.reduce(data, this.state); - this.contactReducer.reduce(data, this.state); this.s3Reducer.reduce(data, this.state); this.groupReducer.reduce(data, this.state); this.launchReducer.reduce(data, this.state); this.connReducer.reduce(data, this.state); GraphReducer(data, this.state); HarkReducer(data, this.state); + ContactReducer(data, this.state); } } diff --git a/pkg/interface/src/logic/subscription/global.ts b/pkg/interface/src/logic/subscription/global.ts index d6af894282..655e66e4ea 100644 --- a/pkg/interface/src/logic/subscription/global.ts +++ b/pkg/interface/src/logic/subscription/global.ts @@ -36,7 +36,8 @@ export default class GlobalSubscription extends BaseSubscription { this.subscribe('/groups', 'group-store'); this.clearQueue(); - this.subscribe('/updates', 'contact-store'); + // TODO: update to get /updates + this.subscribe('/all', 'contact-store'); this.subscribe('/all', 's3-store'); this.subscribe('/keys', 'graph-store'); this.subscribe('/updates', 'hark-store'); From 54b64f56821a80db77b2d6056607ac65f6544f63 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 19 Jan 2021 15:07:11 -0600 Subject: [PATCH 06/42] contacts: write hooks and use permisssioning system --- pkg/arvo/app/contact-pull-hook.hoon | 45 ++++++++++++++++ pkg/arvo/app/contact-push-hook.hoon | 81 +++++++++++++++++++++++++++++ pkg/arvo/app/contact-store.hoon | 36 +++++++++---- pkg/arvo/lib/contact.hoon | 34 ++++++++++++ 4 files changed, 187 insertions(+), 9 deletions(-) create mode 100644 pkg/arvo/app/contact-pull-hook.hoon create mode 100644 pkg/arvo/app/contact-push-hook.hoon create mode 100644 pkg/arvo/lib/contact.hoon diff --git a/pkg/arvo/app/contact-pull-hook.hoon b/pkg/arvo/app/contact-pull-hook.hoon new file mode 100644 index 0000000000..9d66a20c13 --- /dev/null +++ b/pkg/arvo/app/contact-pull-hook.hoon @@ -0,0 +1,45 @@ +/- *resource +/+ store=contact-store, contact, default-agent, verb, dbug, pull-hook +~% %contact-pull-hook-top ..part ~ +|% ++$ card card:agent:gall +++ config + ^- config:pull-hook + :* %contact-store + update:store + %contact-update + %contact-push-hook + == +-- +:: +%- agent:dbug +^- agent:gall +%- (agent:pull-hook config) +^- (pull-hook:pull-hook config) +|_ =bowl:gall ++* this . + def ~(. (default-agent this %|) bowl) + dep ~(. (default:pull-hook this config) bowl) + con ~(. contact bowl) +:: +++ on-init on-init:def +++ on-save !>(~) +++ on-load on-load:def +++ on-poke on-poke:def +++ on-peek on-peek:def +++ on-arvo on-arvo:def +++ on-fail on-fail:def +++ on-agent on-agent:def +++ on-watch on-watch:def +++ on-leave on-leave:def +++ on-pull-nack + |= [=resource =tang] + ^- (quip card _this) + :_ this + ?~ (get-contact:con entity.resource) ~ + =- [%pass /pl-nack %agent [our.bowl %contact-store] %poke %contact-update -]~ + !> ^- update:store + [%remove entity.resource] +:: +++ on-pull-kick |=(=resource `/) +-- diff --git a/pkg/arvo/app/contact-push-hook.hoon b/pkg/arvo/app/contact-push-hook.hoon new file mode 100644 index 0000000000..12b665e611 --- /dev/null +++ b/pkg/arvo/app/contact-push-hook.hoon @@ -0,0 +1,81 @@ +/+ store=contact-store, res=resource, contact, default-agent, dbug, push-hook +~% %contact-push-hook-top ..part ~ +|% ++$ card card:agent:gall +++ config + ^- config:push-hook + :* %contact-store + /our + update:store + %contact-update + %graph-pull-hook + == +:: ++$ agent (push-hook:push-hook config) +-- +:: +%- agent:dbug +^- agent:gall +%- (agent:push-hook config) +^- agent +|_ =bowl:gall ++* this . + def ~(. (default-agent this %|) bowl) + con ~(. contact bowl) +:: +++ on-init on-init:def +++ on-save !>(~) +++ on-load on-load:def +++ on-poke on-poke:def +++ on-agent on-agent:def +++ on-watch on-watch:def +++ on-leave on-leave:def +++ on-peek on-peek:def +++ on-arvo on-arvo:def +++ on-fail on-fail:def +:: +++ should-proxy-update + |= =vase + ^- ? + =/ =update:store !<(update:store vase) + ?- -.update + %initial %.n + %add %.y + %remove %.y + %edit %.y + %allow %.n + %disallow %.n + == +:: +++ resource-for-update + |= =vase + ^- (unit resource:res) + =/ =update:store !<(update:store vase) + ?- -.update + %initial ~ + %add `[our.bowl %our] + %remove `[our.bowl %our] + %edit `[our.bowl %our] + %allow ~ + %disallow `[our.bowl %our] + == +:: +++ initial-watch + |= [=path =resource:res] + ^- vase + ?> (is-allowed:con src.bowl) + !> ^- update:store + =/ contact=(unit contact:store) (get-contact:con our.bowl) + :+ %add + our.bowl + ?^ contact u.contact + *contact:store +:: +++ take-update + |= =vase + ^- [(list card) agent] + =/ =update:store !<(update:store vase) + ?. ?=(%disallow -.update) [~ this] + :_ this + [%give %kick ~[resource+(en-path:res [our.bowl %our])] ~]~ +-- diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index bcb2897de3..c1d7734b0c 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -48,6 +48,14 @@ ?+ path (on-watch:def path) [%all ~] (give [%initial rolodex]) [%updates ~] ~ + :: + [%our ~] + %- give + :+ %add + our.bowl + =/ contact=(unit contact:store) (~(get by rolodex) our.bowl) + ?~ contact *contact:store + u.contact == [cards this] :: @@ -87,20 +95,19 @@ ^- (quip card _state) =. rolodex (~(uni by rolodex) rolo) :_ state(rolodex rolodex) - (send-diff [%initial rolodex]) + (send-diff [%initial rolodex] %.n) :: ++ handle-add |= [=ship =contact:store] ^- (quip card _state) - ?< (~(has by rolodex) ship) - :- (send-diff [%add ship contact]) + :- (send-diff [%add ship contact] =(ship our.bowl)) state(rolodex (~(put by rolodex) ship contact)) :: ++ handle-remove |= =ship ^- (quip card _state) ?> (~(has by rolodex) ship) - :- (send-diff [%remove ship]) + :- (send-diff [%remove ship] =(ship our.bowl)) state(rolodex (~(del by rolodex) ship)) :: ++ handle-edit @@ -109,7 +116,7 @@ ^- (quip card _state) =/ contact (~(got by rolodex) ship) =. contact (edit-contact contact edit-field) - :- (send-diff [%edit ship edit-field]) + :- (send-diff [%edit ship edit-field] =(ship our.bowl)) state(rolodex (~(put by rolodex) ship contact)) :: ++ edit-contact @@ -128,7 +135,7 @@ ++ handle-allow |= =beings:store ^- (quip card _state) - :- (send-diff [%allow beings]) + :- (send-diff [%allow beings] %.n) ?- -.beings %group state(allowed-groups (~(put in allowed-groups) resource.beings)) %ships state(allowed-ships (~(uni in allowed-ships) ships.beings)) @@ -137,16 +144,20 @@ ++ handle-disallow |= =beings:store ^- (quip card _state) - :- (send-diff [%disallow beings]) + :- (send-diff [%disallow beings] %.y) ?- -.beings %group state(allowed-groups (~(del in allowed-groups) resource.beings)) %ships state(allowed-ships (~(dif in allowed-ships) ships.beings)) == :: ++ send-diff - |= =update:store + |= [=update:store our=?] ^- (list card) - [%give %fact ~[/updates] %contact-update !>(update)]~ + =/ paths=(list path) + ?: our + `(list path)`[/updates /our]~ + ~[/updates] + [%give %fact paths %contact-update !>(update)]~ -- :: ++ import @@ -169,6 +180,13 @@ :- ~ :- ~ :- %contact-update !> ^- update:store [%add ship u.contact] + :: + [%x %allowed-ship @ ~] + =/ =ship (slav %p i.t.t.path) + ``noun+!>((~(has in allowed-ships) ship)) + :: + [%x %allowed-groups ~] + ``noun+!>(allowed-groups) == :: ++ on-leave on-leave:def diff --git a/pkg/arvo/lib/contact.hoon b/pkg/arvo/lib/contact.hoon new file mode 100644 index 0000000000..8a86715d51 --- /dev/null +++ b/pkg/arvo/lib/contact.hoon @@ -0,0 +1,34 @@ +/- store=contact-store +/+ group +|_ =bowl:gall +++ scry-for + |* [=mold =path] + .^ mold + %gx + (scot %p our.bowl) + %contact-store + (scot %da now.bowl) + (snoc `^path`path %noun) + == +:: +++ get-contact + |= =ship + ^- (unit contact:store) + =/ upd (scry-for (unit update:store) /contact/(scot %p ship)) + ?~ upd ~ + ?> ?=(%add -.u.upd) + `contact.u.upd +:: +++ is-allowed + |= =ship + ^- ? + =/ shp (scry-for ? /allowed-ship/(scot %p ship)) + ?: shp %.y + =/ allowed-groups ~(tap in (scry-for (set resource) /allowed-groups)) + =/ grp ~(. group bowl) + |- + ?~ allowed-groups %.n + ?: ~(has in (members:grp i.allowed-groups) ship) + %.y + $(allowed-groups t.allowed-groups) +-- From 7ffce92c377c4273e098220f34e051704f2e8f33 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 19 Jan 2021 15:09:30 -0600 Subject: [PATCH 07/42] hood: start %contact-push/pull-hooks --- pkg/arvo/app/contact-store.hoon | 2 +- pkg/arvo/app/hood.hoon | 3 ++- pkg/arvo/lib/contact.hoon | 4 ++-- pkg/arvo/lib/hood/drum.hoon | 5 +++++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index c1d7734b0c..0276821b1a 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -155,7 +155,7 @@ ^- (list card) =/ paths=(list path) ?: our - `(list path)`[/updates /our]~ + [/updates /our ~] ~[/updates] [%give %fact paths %contact-update !>(update)]~ -- diff --git a/pkg/arvo/app/hood.hoon b/pkg/arvo/app/hood.hoon index 1b62fd165f..24a9e524cc 100644 --- a/pkg/arvo/app/hood.hoon +++ b/pkg/arvo/app/hood.hoon @@ -2,7 +2,7 @@ /+ drum=hood-drum, helm=hood-helm, kiln=hood-kiln |% +$ state - $: %11 + $: %12 drum=state:drum helm=state:helm kiln=state:kiln @@ -14,6 +14,7 @@ [%8 drum=state:drum helm=state:helm kiln=state:kiln] [%9 drum=state:drum helm=state:helm kiln=state:kiln] [%10 drum=state:drum helm=state:helm kiln=state:kiln] + [%11 drum=state:drum helm=state:helm kiln=state:kiln] == +$ any-state-tuple $: drum=any-state:drum diff --git a/pkg/arvo/lib/contact.hoon b/pkg/arvo/lib/contact.hoon index 8a86715d51..449478e128 100644 --- a/pkg/arvo/lib/contact.hoon +++ b/pkg/arvo/lib/contact.hoon @@ -1,4 +1,4 @@ -/- store=contact-store +/- store=contact-store, *resource /+ group |_ =bowl:gall ++ scry-for @@ -28,7 +28,7 @@ =/ grp ~(. group bowl) |- ?~ allowed-groups %.n - ?: ~(has in (members:grp i.allowed-groups) ship) + ?: (~(has in (members:grp i.allowed-groups)) ship) %.y $(allowed-groups t.allowed-groups) -- diff --git a/pkg/arvo/lib/hood/drum.hoon b/pkg/arvo/lib/hood/drum.hoon index e3c33eda90..f7676bfddd 100644 --- a/pkg/arvo/lib/hood/drum.hoon +++ b/pkg/arvo/lib/hood/drum.hoon @@ -91,6 +91,8 @@ %herm %contact-store %contact-hook + %contact-push-hook + %contact-pull-hook %contact-view %metadata-store %metadata-hook @@ -248,6 +250,9 @@ => (se-born | %home %hark-store) => (se-born | %home %observe-hook) (se-born | %home %herm) + =? ..on-load (lte hood-version %12) + => (se-born | %home %contact-push-hook) + (se-born | %home %contact-pull-hook) ..on-load :: ++ reap-phat :: ack connect From 058bd69cf146ae1fb478f47e182c3780e4c17ad5 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 19 Jan 2021 16:07:34 -0600 Subject: [PATCH 08/42] interface: broke apart settings and profile, updated StatusBar UI --- .../src/logic/reducers/contact-update.ts | 2 +- pkg/interface/src/views/App.js | 3 + .../profile}/components/ContactCard.tsx | 1 + .../src/views/apps/profile/profile.tsx | 115 +++--------------- .../components/lib/BackgroundPicker.tsx | 0 .../components/lib/BucketList.tsx | 0 .../components/lib/DisplayForm.tsx | 0 .../components/lib/RemoteContent.tsx | 0 .../components/lib/S3Form.tsx | 0 .../components/lib/Security.tsx | 0 .../components/settings.tsx | 0 .../src/views/apps/settings/settings.tsx | 48 ++++++++ .../src/views/components/StatusBar.js | 9 +- .../src/views/landscape/components/Content.js | 9 ++ .../landscape/components/PopoverRoutes.tsx | 10 -- 15 files changed, 82 insertions(+), 115 deletions(-) rename pkg/interface/src/views/{landscape => apps/profile}/components/ContactCard.tsx (99%) rename pkg/interface/src/views/apps/{profile => settings}/components/lib/BackgroundPicker.tsx (100%) rename pkg/interface/src/views/apps/{profile => settings}/components/lib/BucketList.tsx (100%) rename pkg/interface/src/views/apps/{profile => settings}/components/lib/DisplayForm.tsx (100%) rename pkg/interface/src/views/apps/{profile => settings}/components/lib/RemoteContent.tsx (100%) rename pkg/interface/src/views/apps/{profile => settings}/components/lib/S3Form.tsx (100%) rename pkg/interface/src/views/apps/{profile => settings}/components/lib/Security.tsx (100%) rename pkg/interface/src/views/apps/{profile => settings}/components/settings.tsx (100%) create mode 100644 pkg/interface/src/views/apps/settings/settings.tsx diff --git a/pkg/interface/src/logic/reducers/contact-update.ts b/pkg/interface/src/logic/reducers/contact-update.ts index 0f03c1292e..2de64b6ddf 100644 --- a/pkg/interface/src/logic/reducers/contact-update.ts +++ b/pkg/interface/src/logic/reducers/contact-update.ts @@ -13,7 +13,7 @@ export const ContactReducer = (json, state) => { add(data, state); remove(data, state); edit(data, state); - console.log(state); + console.log(state.contacts); } }; diff --git a/pkg/interface/src/views/App.js b/pkg/interface/src/views/App.js index 27b32293e2..f40f633b45 100644 --- a/pkg/interface/src/views/App.js +++ b/pkg/interface/src/views/App.js @@ -137,6 +137,8 @@ class App extends React.Component { const notificationsCount = state.notificationsCount || 0; const doNotDisturb = state.doNotDisturb || false; + const ourContact = this.state.contacts[this.ship] || null; + console.log(ourContact, this.state.contacts, this.ship); const showBanner = localStorage.getItem("2020BreachBanner") || "flex"; let banner = null; @@ -155,6 +157,7 @@ class App extends React.Component { props={this.props} associations={associations} invites={this.state.invites} + ourContact={ourContact} api={this.api} connection={this.state.connection} subscription={this.subscription} diff --git a/pkg/interface/src/views/landscape/components/ContactCard.tsx b/pkg/interface/src/views/apps/profile/components/ContactCard.tsx similarity index 99% rename from pkg/interface/src/views/landscape/components/ContactCard.tsx rename to pkg/interface/src/views/apps/profile/components/ContactCard.tsx index 3738428eb6..b7fe89e296 100644 --- a/pkg/interface/src/views/landscape/components/ContactCard.tsx +++ b/pkg/interface/src/views/apps/profile/components/ContactCard.tsx @@ -135,6 +135,7 @@ export function ContactCard(props: ContactCardProps) { gridTemplateColumns="100%" gridRowGap="5" maxWidth="400px" + width="100%" > { - const selected = current === view; - const icon = (view) => { - switch(view) { - case 'identity': - return 'Smiley'; - case 'settings': - return 'Adjust'; - default: - return 'Circle' - } - } - return ( - - - - - {children} - - - - ); -}; - export default function ProfileScreen(props: any) { const { ship, dark } = props; const hideAvatars = useLocalState(state => state.hideAvatars); @@ -49,101 +18,45 @@ export default function ProfileScreen(props: any) { OS1 - Profile - { - const { view } = match.params; - const contact = props.contacts?.["/~/default"]?.[window.ship]; + const contact = props.contacts?.[window.ship]; const sigilColor = contact?.color ? `#${uxToHex(contact.color)}` : dark ? "#FFFFFF" : "#000000"; + if(!contact) { return null; } - if (!view && !MOBILE_BROWSER_REGEX.test(window.navigator.userAgent)) { - history.replace("/~profile/identity"); - } - - const image = (!hideAvatars && contact?.avatar) - ? - : ; return ( - - - - {image} - - - - - Your Identity - - - Ship Settings - - - - - {"<- Back"} - - {view === "settings" && } - - {view === "identity" && ( - <> - Your identity provides the default information you can optionally share with groups in the group settings panel. - - - )} + ); }} - > - + /> ); } diff --git a/pkg/interface/src/views/apps/profile/components/lib/BackgroundPicker.tsx b/pkg/interface/src/views/apps/settings/components/lib/BackgroundPicker.tsx similarity index 100% rename from pkg/interface/src/views/apps/profile/components/lib/BackgroundPicker.tsx rename to pkg/interface/src/views/apps/settings/components/lib/BackgroundPicker.tsx diff --git a/pkg/interface/src/views/apps/profile/components/lib/BucketList.tsx b/pkg/interface/src/views/apps/settings/components/lib/BucketList.tsx similarity index 100% rename from pkg/interface/src/views/apps/profile/components/lib/BucketList.tsx rename to pkg/interface/src/views/apps/settings/components/lib/BucketList.tsx diff --git a/pkg/interface/src/views/apps/profile/components/lib/DisplayForm.tsx b/pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx similarity index 100% rename from pkg/interface/src/views/apps/profile/components/lib/DisplayForm.tsx rename to pkg/interface/src/views/apps/settings/components/lib/DisplayForm.tsx diff --git a/pkg/interface/src/views/apps/profile/components/lib/RemoteContent.tsx b/pkg/interface/src/views/apps/settings/components/lib/RemoteContent.tsx similarity index 100% rename from pkg/interface/src/views/apps/profile/components/lib/RemoteContent.tsx rename to pkg/interface/src/views/apps/settings/components/lib/RemoteContent.tsx diff --git a/pkg/interface/src/views/apps/profile/components/lib/S3Form.tsx b/pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx similarity index 100% rename from pkg/interface/src/views/apps/profile/components/lib/S3Form.tsx rename to pkg/interface/src/views/apps/settings/components/lib/S3Form.tsx diff --git a/pkg/interface/src/views/apps/profile/components/lib/Security.tsx b/pkg/interface/src/views/apps/settings/components/lib/Security.tsx similarity index 100% rename from pkg/interface/src/views/apps/profile/components/lib/Security.tsx rename to pkg/interface/src/views/apps/settings/components/lib/Security.tsx diff --git a/pkg/interface/src/views/apps/profile/components/settings.tsx b/pkg/interface/src/views/apps/settings/components/settings.tsx similarity index 100% rename from pkg/interface/src/views/apps/profile/components/settings.tsx rename to pkg/interface/src/views/apps/settings/components/settings.tsx diff --git a/pkg/interface/src/views/apps/settings/settings.tsx b/pkg/interface/src/views/apps/settings/settings.tsx new file mode 100644 index 0000000000..bfe331ecd3 --- /dev/null +++ b/pkg/interface/src/views/apps/settings/settings.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { Route, Link, Switch } from "react-router-dom"; +import Helmet from 'react-helmet'; + +import { Box, Text, Row, Col, Icon, BaseImage } from "@tlon/indigo-react"; + +import Settings from "./components/settings"; +import useLocalState from "~/logic/state/local"; + +export default function SettingsScreen(props: any) { + const { ship, dark } = props; + const hideAvatars = useLocalState(state => state.hideAvatars); + return ( + <> + + Landscape - Settings + + { + return ( + + + + + + ); + }} + /> + + ); +} diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 6b32bd4eb2..2f2c5cb741 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -10,6 +10,8 @@ const StatusBar = (props) => { const invites = [].concat(...Object.values(props.invites).map(obj => Object.values(obj))); const metaKey = (window.navigator.platform.includes('Mac')) ? '⌘' : 'Ctrl+'; const toggleOmnibox = useLocalState(state => state.toggleOmnibox); + + const color = !!props.ourContact ? props.ourContact.color : 'black'; return ( { - toggleOmnibox()}> { !props.doNotDisturb && (props.notificationsCount > 0 || invites.length > 0) && ( @@ -59,9 +60,11 @@ const StatusBar = (props) => { > Submit an issue + props.history.push('/~settings')}> + + props.history.push('/~profile')}> - - ~{props.ship} + diff --git a/pkg/interface/src/views/landscape/components/Content.js b/pkg/interface/src/views/landscape/components/Content.js index 3d14f6f1de..a507e4f3b8 100644 --- a/pkg/interface/src/views/landscape/components/Content.js +++ b/pkg/interface/src/views/landscape/components/Content.js @@ -7,6 +7,7 @@ import LaunchApp from '~/views/apps/launch/app'; import TermApp from '~/views/apps/term/app'; import Landscape from '~/views/landscape/index'; import Profile from '~/views/apps/profile/profile'; +import Settings from '~/views/apps/settings/settings'; import ErrorComponent from '~/views/components/Error'; import Notifications from '~/views/apps/notifications/notifications'; import GraphApp from '../../apps/graph/app'; @@ -63,6 +64,14 @@ export const Content = (props) => { /> )} /> + ( + + )} + /> ( diff --git a/pkg/interface/src/views/landscape/components/PopoverRoutes.tsx b/pkg/interface/src/views/landscape/components/PopoverRoutes.tsx index 5b42228ffd..4a50a5d442 100644 --- a/pkg/interface/src/views/landscape/components/PopoverRoutes.tsx +++ b/pkg/interface/src/views/landscape/components/PopoverRoutes.tsx @@ -9,7 +9,6 @@ import { Association } from "~/types/metadata-update"; import GlobalApi from "~/logic/api/global"; import {GroupNotificationsConfig, S3State} from "~/types"; -import { ContactCard } from "./ContactCard"; import { GroupSettings } from "./GroupSettings/GroupSettings"; import { Participants } from "./Participants"; @@ -135,15 +134,6 @@ export function PopoverRoutes( api={props.api} /> )} - {view === "profile" && ( - - )} From 7bb76ba39c2fdddc9d9f333f404bd8fc31694e88 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Thu, 21 Jan 2021 11:36:48 -0600 Subject: [PATCH 09/42] contact-store: change fields in to reflect new profile --- pkg/arvo/app/contact-store.hoon | 8 +++++--- pkg/arvo/lib/contact-store.hoon | 26 ++++++++++++++------------ pkg/arvo/sur/contact-store.hoon | 13 +++++++------ 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index 0276821b1a..8216ee1488 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -100,6 +100,7 @@ ++ handle-add |= [=ship =contact:store] ^- (quip card _state) + =. last-updated.contact now.bowl :- (send-diff [%add ship contact] =(ship our.bowl)) state(rolodex (~(put by rolodex) ship contact)) :: @@ -116,6 +117,7 @@ ^- (quip card _state) =/ contact (~(got by rolodex) ship) =. contact (edit-contact contact edit-field) + =. last-updated.contact now.bowl :- (send-diff [%edit ship edit-field] =(ship our.bowl)) state(rolodex (~(put by rolodex) ship contact)) :: @@ -124,11 +126,11 @@ ^- contact:store ?- -.edit %nickname contact(nickname nickname.edit) - %email contact(email email.edit) - %phone contact(phone phone.edit) - %website contact(website website.edit) + %bio contact(bio bio.edit) + %status contact(status status.edit) %color contact(color color.edit) %avatar contact(avatar avatar.edit) + %cover contact(cover cover.edit) == -- :: diff --git a/pkg/arvo/lib/contact-store.hoon b/pkg/arvo/lib/contact-store.hoon index 78e9c39ca2..67c7ed9e5f 100644 --- a/pkg/arvo/lib/contact-store.hoon +++ b/pkg/arvo/lib/contact-store.hoon @@ -63,11 +63,12 @@ ^- json %- pairs :~ [%nickname s+nickname.contact] - [%email s+email.contact] - [%phone s+phone.contact] - [%website s+website.contact] + [%bio s+bio.contact] + [%status s+status.contact] [%color s+(scot %ux color.contact)] [%avatar ?~(avatar.contact ~ s+u.avatar.contact)] + [%cover ?~(cover.contact ~ s+u.cover.contact)] + [%last-updated (time last-updated.contact)] == :: ++ edit @@ -76,11 +77,11 @@ %+ frond -.field ?- -.field %nickname s+nickname.field - %email s+email.field - %phone s+phone.field - %website s+website.field + %bio s+bio.field + %status s+status.field %color s+(scot %ux color.field) %avatar ?~(avatar.field ~ s+u.avatar.field) + %cover ?~(cover.field ~ s+u.cover.field) == :: ++ beng @@ -135,21 +136,22 @@ ++ cont %- ot :~ [%nickname so] - [%email so] - [%phone so] - [%website so] + [%bio so] + [%status so] [%color nu] [%avatar (mu so)] + [%cover (mu so)] + [%last-updated di] == :: ++ edit %- of :~ [%nickname so] - [%email so] - [%phone so] - [%website so] + [%bio so] + [%status so] [%color nu] [%avatar (mu so)] + [%cover (mu so)] == -- -- diff --git a/pkg/arvo/sur/contact-store.hoon b/pkg/arvo/sur/contact-store.hoon index ad6765bca3..8424c04c5a 100644 --- a/pkg/arvo/sur/contact-store.hoon +++ b/pkg/arvo/sur/contact-store.hoon @@ -3,20 +3,21 @@ +$ rolodex (map ship contact) +$ contact $: nickname=@t - email=@t - phone=@t - website=@t + bio=@t + status=@t color=@ux avatar=(unit @t) + cover=(unit @t) + last-updated=@da == :: +$ edit-field $% [%nickname nickname=@t] - [%email email=@t] - [%phone phone=@t] - [%website website=@t] + [%bio bio=@t] + [%status status=@t] [%color color=@ux] [%avatar avatar=(unit @t)] + [%cover cover=(unit @t)] == :: +$ beings From 8f5efe46be90d101c2576d01dd00c8e124e54776 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Thu, 21 Jan 2021 11:37:30 -0600 Subject: [PATCH 10/42] interface: add blank profile --- .../views/apps/profile/components/Profile.tsx | 76 +++++++++++++++++++ .../src/views/apps/profile/profile.tsx | 20 ++--- .../src/views/components/StatusBar.js | 5 +- 3 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 pkg/interface/src/views/apps/profile/components/Profile.tsx diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx new file mode 100644 index 0000000000..9df092bfad --- /dev/null +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -0,0 +1,76 @@ +import React from "react"; +import { Sigil } from "~/logic/lib/sigil"; + +import { uxToHex } from "~/logic/lib/util"; +import { + Center, + Col, + Box, + Text, + Row, + BaseImage, +} from "@tlon/indigo-react"; +import { AsyncButton } from "~/views/components/AsyncButton"; +import GlobalApi from "~/logic/api/global"; +import useLocalState from "~/logic/state/local"; + + +const emptyContact = { + nickname: '', + bio: '', + status: '', + avatar: null, + cover: null, + 'last-updated': 0 +}; + +export function Profile(props: any) { + const { hideAvatars, hideNicknames } = useLocalState(({ hideAvatars, hideNicknames }) => ({ + hideAvatars, hideNicknames + })); + if (!props.ship) { + return null; + } + const { contact } = props; + const hexColor = contact?.color ? `#${uxToHex(contact.color)}` : "#000000"; + const cover = (contact?.cover) + ? + : ; + + const image = (!hideAvatars && contact?.avatar) + ? + : ; + + const nickname = + (!hideNicknames && contact?.nickname) ? contact.nickname : ""; + + return ( +
+ + + {cover} + + +
+ + {image} + +
+ + {nickname} + +
+
+
+ ); +} diff --git a/pkg/interface/src/views/apps/profile/profile.tsx b/pkg/interface/src/views/apps/profile/profile.tsx index a4efb103aa..48f3141ab0 100644 --- a/pkg/interface/src/views/apps/profile/profile.tsx +++ b/pkg/interface/src/views/apps/profile/profile.tsx @@ -4,14 +4,13 @@ import Helmet from 'react-helmet'; import { Box, Text, Row, Col, Icon, BaseImage } from "@tlon/indigo-react"; -import { Sigil } from "~/logic/lib/sigil"; import { uxToHex } from "~/logic/lib/util"; -import { ContactCard } from "./components/ContactCard"; +import { Profile } from "./components/Profile"; import useLocalState from "~/logic/state/local"; export default function ProfileScreen(props: any) { - const { ship, dark } = props; + const { dark } = props; const hideAvatars = useLocalState(state => state.hideAvatars); return ( <> @@ -19,35 +18,30 @@ export default function ProfileScreen(props: any) { OS1 - Profile { - const contact = props.contacts?.[window.ship]; + const ship = match.params.ship; + const contact = props.contacts?.[ship]; const sigilColor = contact?.color ? `#${uxToHex(contact.color)}` : dark ? "#FFFFFF" : "#000000"; - if(!contact) { - return null; - } return ( - diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 2f2c5cb741..55f73d50cb 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -63,7 +63,10 @@ const StatusBar = (props) => { props.history.push('/~settings')}> - props.history.push('/~profile')}> + props.history.push('/~profile/' + window.ship)}> From 288302a406063afc2e3289e395f612cc31f872ac Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Thu, 21 Jan 2021 11:58:59 -0600 Subject: [PATCH 11/42] wip --- pkg/interface/src/views/App.js | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/interface/src/views/App.js b/pkg/interface/src/views/App.js index f40f633b45..19f4ffc8c9 100644 --- a/pkg/interface/src/views/App.js +++ b/pkg/interface/src/views/App.js @@ -138,7 +138,6 @@ class App extends React.Component { const notificationsCount = state.notificationsCount || 0; const doNotDisturb = state.doNotDisturb || false; const ourContact = this.state.contacts[this.ship] || null; - console.log(ourContact, this.state.contacts, this.ship); const showBanner = localStorage.getItem("2020BreachBanner") || "flex"; let banner = null; From 2ad33fe6a389d444368572acb004473d5877757b Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Thu, 21 Jan 2021 13:39:43 -0600 Subject: [PATCH 12/42] interface: built out profile view screen --- .../views/apps/profile/components/Profile.tsx | 72 ++++++++++++++++--- .../src/views/apps/profile/profile.tsx | 4 +- 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 9df092bfad..65cb87bf9c 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -4,15 +4,17 @@ import { Sigil } from "~/logic/lib/sigil"; import { uxToHex } from "~/logic/lib/util"; import { Center, - Col, Box, Text, Row, BaseImage, + Button, } from "@tlon/indigo-react"; import { AsyncButton } from "~/views/components/AsyncButton"; +import RichText from "~/views/components/RichText"; import GlobalApi from "~/logic/api/global"; import useLocalState from "~/logic/state/local"; +import { useHistory } from "react-router-dom"; const emptyContact = { @@ -25,13 +27,14 @@ const emptyContact = { }; export function Profile(props: any) { + const history = useHistory(); const { hideAvatars, hideNicknames } = useLocalState(({ hideAvatars, hideNicknames }) => ({ hideAvatars, hideNicknames })); if (!props.ship) { return null; } - const { contact } = props; + const { contact, isEdit} = props; const hexColor = contact?.color ? `#${uxToHex(contact.color)}` : "#000000"; const cover = (contact?.cover) ? @@ -41,9 +44,6 @@ export function Profile(props: any) { ? : ; - const nickname = - (!hideNicknames && contact?.nickname) ? contact.nickname : ""; - return (
@@ -66,10 +66,64 @@ export function Profile(props: any) { {image}
- - {nickname} - + +
+ + {(contact?.nickname ? contact.nickname : "")} + +
+
+ +
+ + {`~${ship}`} + +
+
+ +
+ + {(contact?.bio ? contact.bio : "")} + +
+
+ { (ship === window.ship && !isEdit) ? ( + +
+ +
+
+ ) : null + } + +
+ {`~${ship} `} + remains private +
+
); diff --git a/pkg/interface/src/views/apps/profile/profile.tsx b/pkg/interface/src/views/apps/profile/profile.tsx index 48f3141ab0..0c2a7f69dc 100644 --- a/pkg/interface/src/views/apps/profile/profile.tsx +++ b/pkg/interface/src/views/apps/profile/profile.tsx @@ -18,9 +18,10 @@ export default function ProfileScreen(props: any) { OS1 - Profile { const ship = match.params.ship; + const isEdit = match.url.includes('edit'); const contact = props.contacts?.[ship]; const sigilColor = contact?.color ? `#${uxToHex(contact.color)}` @@ -44,6 +45,7 @@ export default function ProfileScreen(props: any) { contact={contact} api={props.api} s3={props.s3} + isEdit={isEdit} />
From 316b86e7fbebb5ac931fa2e7a3bc8465acee198f Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 26 Jan 2021 15:22:33 -0600 Subject: [PATCH 13/42] contact-store: update to add public groups and force us to always keep our own contact --- pkg/arvo/app/contact-store.hoon | 25 ++++++++++++++++++------- pkg/arvo/lib/contact-store.hoon | 18 ++++++++++++------ pkg/arvo/sur/contact-store.hoon | 3 +++ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index 8216ee1488..902ac49168 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -35,7 +35,10 @@ |= old-vase=vase ^- (quip card _this) =/ old !<(versioned-state old-vase) - ?+ -.old [~ this] + ?+ -.old + =. rolodex (~(put by rolodex) our.bowl *contact:store) + [~ this(state state)] + :: %4 [~ this(state old)] == :: @@ -109,6 +112,8 @@ ^- (quip card _state) ?> (~(has by rolodex) ship) :- (send-diff [%remove ship] =(ship our.bowl)) + ?: =(ship our.bowl) + state(rolodex (~(put by rolodex) *contact:store)) state(rolodex (~(del by rolodex) ship)) :: ++ handle-edit @@ -125,12 +130,18 @@ |= [=contact:store edit=edit-field:store] ^- contact:store ?- -.edit - %nickname contact(nickname nickname.edit) - %bio contact(bio bio.edit) - %status contact(status status.edit) - %color contact(color color.edit) - %avatar contact(avatar avatar.edit) - %cover contact(cover cover.edit) + %nickname contact(nickname nickname.edit) + %bio contact(bio bio.edit) + %status contact(status status.edit) + %color contact(color color.edit) + %avatar contact(avatar avatar.edit) + %cover contact(cover cover.edit) + :: + %add-group + contact(groups (~(put in groups.contact) resource.edit)) + :: + %remove-group + contact(groups (~(del in groups.contact) resource.edit)) == -- :: diff --git a/pkg/arvo/lib/contact-store.hoon b/pkg/arvo/lib/contact-store.hoon index 67c7ed9e5f..722b779e9b 100644 --- a/pkg/arvo/lib/contact-store.hoon +++ b/pkg/arvo/lib/contact-store.hoon @@ -68,6 +68,7 @@ [%color s+(scot %ux color.contact)] [%avatar ?~(avatar.contact ~ s+u.avatar.contact)] [%cover ?~(cover.contact ~ s+u.cover.contact)] + [%groups a+(turn ~(tap in groups.contact) |=(r=resource (enjs:res r)))] [%last-updated (time last-updated.contact)] == :: @@ -76,12 +77,14 @@ ^- json %+ frond -.field ?- -.field - %nickname s+nickname.field - %bio s+bio.field - %status s+status.field - %color s+(scot %ux color.field) - %avatar ?~(avatar.field ~ s+u.avatar.field) - %cover ?~(cover.field ~ s+u.cover.field) + %nickname s+nickname.field + %bio s+bio.field + %status s+status.field + %color s+(scot %ux color.field) + %avatar ?~(avatar.field ~ s+u.avatar.field) + %cover ?~(cover.field ~ s+u.cover.field) + %add-group (enjs:res resource.field) + %remove-group (enjs:res resource.field) == :: ++ beng @@ -141,6 +144,7 @@ [%color nu] [%avatar (mu so)] [%cover (mu so)] + [%groups (as dejs:res)] [%last-updated di] == :: @@ -152,6 +156,8 @@ [%color nu] [%avatar (mu so)] [%cover (mu so)] + [%add-group dejs:res] + [%remove-group dejs:res] == -- -- diff --git a/pkg/arvo/sur/contact-store.hoon b/pkg/arvo/sur/contact-store.hoon index 8424c04c5a..37f3af1404 100644 --- a/pkg/arvo/sur/contact-store.hoon +++ b/pkg/arvo/sur/contact-store.hoon @@ -8,6 +8,7 @@ color=@ux avatar=(unit @t) cover=(unit @t) + groups=(set resource) last-updated=@da == :: @@ -17,6 +18,8 @@ [%status status=@t] [%color color=@ux] [%avatar avatar=(unit @t)] + [%add-group =resource] + [%remove-group =resource] [%cover cover=(unit @t)] == :: From e577edfc32ca2045694824c873baaf0a5b6ee2bc Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 26 Jan 2021 15:23:01 -0600 Subject: [PATCH 14/42] interface: split profile into multiple components, beginning Formik work --- .../apps/profile/components/EditProfile.tsx | 127 ++++++++++++++++++ .../views/apps/profile/components/Profile.tsx | 85 ++---------- .../apps/profile/components/ViewProfile.tsx | 82 +++++++++++ .../src/views/components/StatusBar.js | 42 ++++-- 4 files changed, 253 insertions(+), 83 deletions(-) create mode 100644 pkg/interface/src/views/apps/profile/components/EditProfile.tsx create mode 100644 pkg/interface/src/views/apps/profile/components/ViewProfile.tsx diff --git a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx new file mode 100644 index 0000000000..f7216397aa --- /dev/null +++ b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx @@ -0,0 +1,127 @@ +import React from "react"; +import * as Yup from "yup"; + +import { + Center, + Box, + Text, + Row, + Button, +} from "@tlon/indigo-react"; +import { Formik, FormikHelpers } from "formik"; +import { useHistory } from "react-router-dom"; + +import GlobalApi from "~/logic/api/global"; +import { Sigil } from "~/logic/lib/sigil"; +import { AsyncButton } from "~/views/components/AsyncButton"; +import { ColorInput } from "~/views/components/ColorInput"; +import { ImageInput } from "~/views/components/ImageInput"; + + +const formSchema = Yup.object({ + nickname: Yup.string(), + bio: Yup.string(), + color: Yup.string() +}); + +const emptyContact = { + nickname: '', + bio: '', + status: '', + color: '0', + avatar: null, + cover: null, + groups: [], + 'last-updated': 0 +}; + + +export function EditProfile(props: any) { + const { contact, ship } = props; + if (ship !== window.ship) { + return null; + } + const history = useHistory(); + + const onSubmit = async (values: any, actions: FormikHelpers) => { + try { + if(!contact) { + const [,,ship] = props.path.split('/'); + values.color = uxToHex(values.color); + const sharedValues = Object.assign({}, values); + sharedValues.avatar = !!values.avatar ? values.avatar : null; + console.log(values); + await props.api.contacts.share(ship, props.path, us, sharedValues); + actions.setStatus({ success: null }); + return; + } + + await Object.keys(values).reduce((acc, key) => { + const newValue = key !== "color" ? values[key] : uxToHex(values[key]); + if (newValue !== contact[key]) { + if (key === "avatar") { + return acc.then(() => + props.api.contacts.edit(props.path, us, { + avatar: { url: newValue }, + } as any) + ); + } + + return acc.then(() => + props.api.contacts.edit(props.path, us, { + [key]: newValue, + } as any) + ); + } + return acc; + }, Promise.resolve()); + actions.setStatus({ success: null }); + } catch (e) { + console.error(e); + actions.setStatus({ error: e.message }); + } + }; + + + + return ( + +
+ + + {image} + + + {nickname} + + + + + + + + + + + {(contact) ? "Save" : "Share Contact"} + + +
+ ); +} diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 65cb87bf9c..1fe217bf9c 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -1,40 +1,27 @@ import React from "react"; import { Sigil } from "~/logic/lib/sigil"; +import { ViewProfile } from './ViewProfile'; +import { EditProfile } from './EditProfile'; import { uxToHex } from "~/logic/lib/util"; import { Center, Box, - Text, Row, BaseImage, - Button, } from "@tlon/indigo-react"; -import { AsyncButton } from "~/views/components/AsyncButton"; -import RichText from "~/views/components/RichText"; -import GlobalApi from "~/logic/api/global"; import useLocalState from "~/logic/state/local"; import { useHistory } from "react-router-dom"; -const emptyContact = { - nickname: '', - bio: '', - status: '', - avatar: null, - cover: null, - 'last-updated': 0 -}; - export function Profile(props: any) { - const history = useHistory(); - const { hideAvatars, hideNicknames } = useLocalState(({ hideAvatars, hideNicknames }) => ({ - hideAvatars, hideNicknames + const { hideAvatars } = useLocalState(({ hideAvatars }) => ({ + hideAvatars })); if (!props.ship) { return null; } - const { contact, isEdit} = props; + const { contact, isEdit, ship } = props; const hexColor = contact?.color ? `#${uxToHex(contact.color)}` : "#000000"; const cover = (contact?.cover) ? @@ -67,63 +54,11 @@ export function Profile(props: any) {
- -
- - {(contact?.nickname ? contact.nickname : "")} - -
-
- -
- - {`~${ship}`} - -
-
- -
- - {(contact?.bio ? contact.bio : "")} - -
-
- { (ship === window.ship && !isEdit) ? ( - -
- -
-
- ) : null - } - -
- {`~${ship} `} - remains private -
-
+ { isEdit ? ( + + ) : ( + + ) } ); diff --git a/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx b/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx new file mode 100644 index 0000000000..2d766be2c8 --- /dev/null +++ b/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx @@ -0,0 +1,82 @@ +import React from "react"; +import { Sigil } from "~/logic/lib/sigil"; + +import { + Center, + Box, + Text, + Row, + Button, +} from "@tlon/indigo-react"; +import { AsyncButton } from "~/views/components/AsyncButton"; +import RichText from "~/views/components/RichText"; +import { useHistory } from "react-router-dom"; + + +export function ViewProfile(props: any) { + const history = useHistory(); + const { contact, ship } = props; + + return ( + <> + +
+ + {(contact?.nickname ? contact.nickname : "")} + +
+
+ +
+ + {`~${ship}`} + +
+
+ +
+ + {(contact?.bio ? contact.bio : "")} + +
+
+ { (ship === window.ship) ? ( + +
+ +
+
+ ) : null + } + +
+ {`~${ship} `} + remains private +
+
+ + ); +} + diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 55f73d50cb..da85eb23af 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -1,7 +1,8 @@ import React from 'react'; -import { Row, Box, Text, Icon, Button } from '@tlon/indigo-react'; +import { Col, Row, Box, Text, Icon, Button } from '@tlon/indigo-react'; import ReconnectButton from './ReconnectButton'; +import { Dropdown } from './Dropdown'; import { StatusBarItem } from './StatusBarItem'; import { Sigil } from '~/logic/lib/sigil'; import useLocalState from '~/logic/state/local'; @@ -11,7 +12,7 @@ const StatusBar = (props) => { const metaKey = (window.navigator.platform.includes('Mac')) ? '⌘' : 'Ctrl+'; const toggleOmnibox = useLocalState(state => state.toggleOmnibox); - const color = !!props.ourContact ? props.ourContact.color : 'black'; + const color = !!props.ourContact ? props.ourContact.color : '#000'; return ( { props.history.push('/~settings')}> - props.history.push('/~profile/' + window.ship)}> - - + + props.history.push('/~profile/' + window.ship)}> + View Profile + + props.history.push('/~profile/' + window.ship)}> + Set Status + + props.history.push('/~profile/' + window.ship)}> + System Settings + + + }> + + + + ); From 683df1b9a40b6df6a138347b74119684111573aa Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 26 Jan 2021 15:41:34 -0600 Subject: [PATCH 15/42] contact-store: fix crash --- pkg/arvo/app/contact-store.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index 902ac49168..7f5b9389b0 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -113,7 +113,7 @@ ?> (~(has by rolodex) ship) :- (send-diff [%remove ship] =(ship our.bowl)) ?: =(ship our.bowl) - state(rolodex (~(put by rolodex) *contact:store)) + state(rolodex (~(put by rolodex) our.bowl *contact:store)) state(rolodex (~(del by rolodex) ship)) :: ++ handle-edit From f5e623b04abb7490436cdb7fc69bea45a86b38ea Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 26 Jan 2021 15:41:50 -0600 Subject: [PATCH 16/42] interface: get edit closer --- .../apps/profile/components/EditProfile.tsx | 58 ++++++------------- .../views/apps/profile/components/Profile.tsx | 2 +- 2 files changed, 19 insertions(+), 41 deletions(-) diff --git a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx index f7216397aa..e78684076f 100644 --- a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx @@ -2,7 +2,10 @@ import React from "react"; import * as Yup from "yup"; import { + ManagedForm as Form, + ManagedTextInputField as Input, Center, + Col, Box, Text, Row, @@ -21,7 +24,8 @@ import { ImageInput } from "~/views/components/ImageInput"; const formSchema = Yup.object({ nickname: Yup.string(), bio: Yup.string(), - color: Yup.string() + color: Yup.string(), + avatar: Yup.string().nullable() }); const emptyContact = { @@ -58,19 +62,10 @@ export function EditProfile(props: any) { await Object.keys(values).reduce((acc, key) => { const newValue = key !== "color" ? values[key] : uxToHex(values[key]); - if (newValue !== contact[key]) { - if (key === "avatar") { - return acc.then(() => - props.api.contacts.edit(props.path, us, { - avatar: { url: newValue }, - } as any) - ); - } + if (newValue !== contact[key]) { return acc.then(() => - props.api.contacts.edit(props.path, us, { - [key]: newValue, - } as any) + props.api.contacts.edit(ship, { [key]: newValue }) ); } return acc; @@ -90,36 +85,19 @@ export function EditProfile(props: any) { initialValues={contact || emptyContact} onSubmit={onSubmit} > -
- - - {image} - - - {nickname} - + + + + + + + + + + - - - - - - - - {(contact) ? "Save" : "Share Contact"} + Submit diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 1fe217bf9c..8dc409edbb 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -55,7 +55,7 @@ export function Profile(props: any) {
{ isEdit ? ( - + ) : ( ) } From 95d2d6eb088b2eded5b2b01208366b1ebd2155fc Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Tue, 26 Jan 2021 17:13:00 -0500 Subject: [PATCH 17/42] statusbar: style dropdown --- pkg/interface/src/views/components/StatusBar.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index fdfcfcb303..0da0de027c 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -6,7 +6,6 @@ import { Dropdown } from './Dropdown'; import { StatusBarItem } from './StatusBarItem'; import { Sigil } from '~/logic/lib/sigil'; import useLocalState from '~/logic/state/local'; -import { cite } from '~/logic/lib/util'; const StatusBar = (props) => { const invites = [].concat(...Object.values(props.invites).map(obj => Object.values(obj))); @@ -66,28 +65,34 @@ const StatusBar = (props) => { + props.history.push('/~profile/' + window.ship)}> View Profile props.history.push('/~profile/' + window.ship)}> Set Status props.history.push('/~profile/' + window.ship)}> System Settings - + }> Date: Tue, 26 Jan 2021 16:17:41 -0600 Subject: [PATCH 18/42] interface: profile updates work --- pkg/interface/src/logic/api/contacts.ts | 1 + .../src/logic/reducers/contact-update.ts | 5 ++- .../apps/profile/components/EditProfile.tsx | 38 ++++++++----------- .../views/apps/profile/components/Profile.tsx | 2 +- .../src/views/apps/profile/profile.tsx | 3 +- 5 files changed, 22 insertions(+), 27 deletions(-) diff --git a/pkg/interface/src/logic/api/contacts.ts b/pkg/interface/src/logic/api/contacts.ts index 2874ba0ef0..cd9f1cd760 100644 --- a/pkg/interface/src/logic/api/contacts.ts +++ b/pkg/interface/src/logic/api/contacts.ts @@ -23,6 +23,7 @@ export default class ContactsApi extends BaseApi { {avatar: null} {avatar: ''} */ + console.log(ship, editField); return this.storeAction({ edit: { ship, diff --git a/pkg/interface/src/logic/reducers/contact-update.ts b/pkg/interface/src/logic/reducers/contact-update.ts index 2de64b6ddf..ef3031d334 100644 --- a/pkg/interface/src/logic/reducers/contact-update.ts +++ b/pkg/interface/src/logic/reducers/contact-update.ts @@ -43,14 +43,15 @@ const remove = (json: ContactUpdate, state: S) => { const edit = (json: ContactUpdate, state: S) => { const data = _.get(json, 'edit', false); + const ship = `~${data.ship}`; if ( data && - (data.ship in state.contacts) + (ship in state.contacts) ) { const edit = Object.keys(data['edit-field']); if (edit.length !== 1) { return; } - state.contacts[data.ship][edit[0]] = data['edit-field'][edit[0]]; + state.contacts[ship][edit[0]] = data['edit-field'][edit[0]]; } }; diff --git a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx index e78684076f..f7c7b4febb 100644 --- a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx @@ -14,7 +14,7 @@ import { import { Formik, FormikHelpers } from "formik"; import { useHistory } from "react-router-dom"; -import GlobalApi from "~/logic/api/global"; +import { uxToHex } from "~/logic/lib/util"; import { Sigil } from "~/logic/lib/sigil"; import { AsyncButton } from "~/views/components/AsyncButton"; import { ColorInput } from "~/views/components/ColorInput"; @@ -41,31 +41,23 @@ const emptyContact = { export function EditProfile(props: any) { - const { contact, ship } = props; + const { contact, ship, api } = props; if (ship !== window.ship) { return null; } + console.log(contact); const history = useHistory(); - const onSubmit = async (values: any, actions: FormikHelpers) => { + const onSubmit = async (values: any, actions: any) => { + console.log(values); try { - if(!contact) { - const [,,ship] = props.path.split('/'); - values.color = uxToHex(values.color); - const sharedValues = Object.assign({}, values); - sharedValues.avatar = !!values.avatar ? values.avatar : null; - console.log(values); - await props.api.contacts.share(ship, props.path, us, sharedValues); - actions.setStatus({ success: null }); - return; - } - await Object.keys(values).reduce((acc, key) => { + console.log(key); const newValue = key !== "color" ? values[key] : uxToHex(values[key]); - if (newValue !== contact[key]) { + if (newValue !== contact[key] && key !== "groups" && key !== "last-updated") { return acc.then(() => - props.api.contacts.edit(ship, { [key]: newValue }) + api.contacts.edit(`~${ship}`, { [key]: newValue }) ); } return acc; @@ -77,14 +69,13 @@ export function EditProfile(props: any) { } }; - - return ( - + +
@@ -101,5 +92,6 @@ export function EditProfile(props: any) {
+
); } diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 8dc409edbb..9cac884b2e 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -55,7 +55,7 @@ export function Profile(props: any) { { isEdit ? ( - + ) : ( ) } diff --git a/pkg/interface/src/views/apps/profile/profile.tsx b/pkg/interface/src/views/apps/profile/profile.tsx index 0c2a7f69dc..d271f9f9f3 100644 --- a/pkg/interface/src/views/apps/profile/profile.tsx +++ b/pkg/interface/src/views/apps/profile/profile.tsx @@ -22,7 +22,8 @@ export default function ProfileScreen(props: any) { render={({ match, history }) => { const ship = match.params.ship; const isEdit = match.url.includes('edit'); - const contact = props.contacts?.[ship]; + const contact = props.contacts?.[`~${ship}`]; + console.log(props.contacts); const sigilColor = contact?.color ? `#${uxToHex(contact.color)}` : dark From 090946e3289bac186b20131632df0c6d6303da76 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Tue, 26 Jan 2021 16:18:01 -0600 Subject: [PATCH 19/42] contact-store: updates sent to /all path as well --- pkg/arvo/app/contact-store.hoon | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index 7f5b9389b0..ccaa105610 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -29,7 +29,10 @@ +* this . def ~(. (default-agent this %|) bowl) :: -++ on-init on-init:def +++ on-init + =. rolodex (~(put by rolodex) our.bowl *contact:store) + [~ this(state state)] +:: ++ on-save !>(state) ++ on-load |= old-vase=vase @@ -168,8 +171,8 @@ ^- (list card) =/ paths=(list path) ?: our - [/updates /our ~] - ~[/updates] + [/updates /our /all ~] + [/updates /all ~] [%give %fact paths %contact-update !>(update)]~ -- :: From d85214040391e4a429355b51da3cb95d9a0e1b1d Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Tue, 26 Jan 2021 18:42:54 -0500 Subject: [PATCH 20/42] leap: add contacts searching --- pkg/interface/src/logic/lib/omnibox.js | 12 +++++++++- pkg/interface/src/views/App.js | 4 +--- .../src/views/components/leap/Omnibox.js | 5 +++-- .../views/components/leap/OmniboxResult.js | 22 ++++++++++--------- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/pkg/interface/src/logic/lib/omnibox.js b/pkg/interface/src/logic/lib/omnibox.js index 1bc38a4e22..e636d3d6d5 100644 --- a/pkg/interface/src/logic/lib/omnibox.js +++ b/pkg/interface/src/logic/lib/omnibox.js @@ -1,6 +1,7 @@ import { cite } from '~/logic/lib/util'; const indexes = new Map([ + ['ships', []], ['commands', []], ['subscriptions', []], ['groups', []], @@ -18,6 +19,14 @@ const result = function(title, link, app, host) { }; }; +const shipIndex = function(contacts) { + const ships = []; + Object.keys(contacts).map((e) => { + return ships.push(result(e, `/~profile/${e}`, 'profile', contacts[e]?.status)); + }); + return ships; +}; + const commandIndex = function (currentGroup) { // commands are special cased for default suite const commands = []; @@ -62,7 +71,8 @@ const otherIndex = function() { return other; }; -export default function index(associations, apps, currentGroup, groups) { +export default function index(contacts, associations, apps, currentGroup, groups) { + indexes.set('ships', shipIndex(contacts)); // all metadata from all apps is indexed // into subscriptions and landscape const subscriptions = []; diff --git a/pkg/interface/src/views/App.js b/pkg/interface/src/views/App.js index 04a7a37eb2..0e70bce4fe 100644 --- a/pkg/interface/src/views/App.js +++ b/pkg/interface/src/views/App.js @@ -139,9 +139,6 @@ class App extends React.Component { const doNotDisturb = state.doNotDisturb || false; const ourContact = this.state.contacts[this.ship] || null; - const showBanner = localStorage.getItem("2020BreachBanner") || "flex"; - let banner = null; - return ( @@ -170,6 +167,7 @@ class App extends React.Component { associations={state.associations} apps={state.launch} api={this.api} + contacts={state.contacts} notifications={state.notificationsCount} invites={state.invites} groups={state.groups} diff --git a/pkg/interface/src/views/components/leap/Omnibox.js b/pkg/interface/src/views/components/leap/Omnibox.js index 70304381eb..f11eb444dd 100644 --- a/pkg/interface/src/views/components/leap/Omnibox.js +++ b/pkg/interface/src/views/components/leap/Omnibox.js @@ -32,7 +32,7 @@ export class Omnibox extends Component { const { pathname } = this.props.location; const selectedGroup = pathname.startsWith('/~landscape/ship/') ? '/' + pathname.split('/').slice(2,5).join('/') : null; - this.setState({ index: index(this.props.associations, this.props.apps.tiles, selectedGroup, this.props.groups) }); + this.setState({ index: index(this.props.contacts, this.props.associations, this.props.apps.tiles, selectedGroup, this.props.groups) }); } if (prevProps && (prevProps.apps !== this.props.apps) && (this.state.query === '')) { @@ -56,7 +56,7 @@ export class Omnibox extends Component { } getSearchedCategories() { - return ['other', 'commands', 'groups', 'subscriptions', 'apps']; + return ['ships', 'other', 'commands', 'groups', 'subscriptions', 'apps']; } control(evt) { @@ -249,6 +249,7 @@ export class Omnibox extends Component { selected={selected} invites={props.invites} notifications={props.notifications} + contacts={props.contacts} /> ))} diff --git a/pkg/interface/src/views/components/leap/OmniboxResult.js b/pkg/interface/src/views/components/leap/OmniboxResult.js index 9ed37a955e..3dd41aba47 100644 --- a/pkg/interface/src/views/components/leap/OmniboxResult.js +++ b/pkg/interface/src/views/components/leap/OmniboxResult.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import { Box, Row, Icon, Text } from '@tlon/indigo-react'; import defaultApps from '~/logic/lib/default-apps'; import Sigil from '~/logic/lib/sigil'; +import { uxToHex } from '~/logic/lib/util'; export class OmniboxResult extends Component { constructor(props) { @@ -25,9 +26,8 @@ export class OmniboxResult extends Component { } } - getIcon(icon, selected, link, invites, notifications) { + getIcon(icon, selected, link, invites, notifications, text, color) { const iconFill = (this.state.hovered || (selected === link)) ? 'white' : 'black'; - const sigilFill = (this.state.hovered || (selected === link)) ? '#3a8ff7' : '#ffffff'; const bulletFill = (this.state.hovered || (selected === link)) ? 'white' : 'blue'; const inviteCount = [].concat(...Object.values(invites).map(obj => Object.values(obj))); @@ -39,22 +39,23 @@ export class OmniboxResult extends Component { { icon = (icon === 'Link') ? 'Collection' : (icon === 'Terminal') ? 'Dojo' : icon; - graphic = ; + graphic = ; } else if (icon === 'inbox') { graphic = - + {(notifications > 0 || inviteCount.length > 0) && ( )} ; } else if (icon === 'logout') { - graphic = ; + graphic = ; } else if (icon === 'profile') { - graphic = ; + text = text.startsWith('Profile') ? window.ship : text; + graphic = ; } else if (icon === 'home') { - graphic = ; + graphic = ; } else if (icon === 'notifications') { - graphic = ; + graphic = ; } else { graphic = ; } @@ -67,9 +68,10 @@ export class OmniboxResult extends Component { } render() { - const { icon, text, subtext, link, navigate, selected, invites, notifications } = this.props; + const { icon, text, subtext, link, navigate, selected, invites, notifications, contacts } = this.props; - const graphic = this.getIcon(icon, selected, link, invites, notifications); + const color = contacts?.[text] ? `#${uxToHex(contacts[text].color)}` : "#000000"; + const graphic = this.getIcon(icon, selected, link, invites, notifications, text, color); return ( Date: Tue, 26 Jan 2021 18:46:31 -0500 Subject: [PATCH 21/42] profile: assume sig is prepended Our contacts object uses ~patp format. We're prefixing sigs on top of this, causing the profile page to break unless the sig is provided, and then showing patp info with two sigs. This just prefixes sigs in the route and removes the sigs prepended in user-facing strings. --- pkg/interface/src/views/apps/profile/components/Profile.tsx | 2 +- .../src/views/apps/profile/components/ViewProfile.tsx | 6 +++--- pkg/interface/src/views/components/StatusBar.js | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 8dc409edbb..ae444420b0 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -29,7 +29,7 @@ export function Profile(props: any) { const image = (!hideAvatars && contact?.avatar) ? - : ; + : ; return (
- {`~${ship}`} + {`${ship}`}
@@ -63,7 +63,7 @@ export function ViewProfile(props: any) {
- ) : null + ) : null }
- {`~${ship} `} + {`${ship} `} remains private
diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 0da0de027c..0667ce1fd5 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -75,21 +75,21 @@ const StatusBar = (props) => { p={1} color='black' fontSize={0} - onClick={() => props.history.push('/~profile/' + window.ship)}> + onClick={() => props.history.push('/~profile/~' + window.ship)}> View Profile props.history.push('/~profile/' + window.ship)}> + onClick={() => props.history.push('/~profile/~' + window.ship)}> Set Status props.history.push('/~profile/' + window.ship)}> + onClick={() => props.history.push('/~profile/~' + window.ship)}> System Settings From 3c4a62accfcafb0b046026555c6307a5d8092ac0 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Tue, 26 Jan 2021 18:47:44 -0500 Subject: [PATCH 22/42] statusbar: dropdown uses cursor: pointer --- pkg/interface/src/views/components/StatusBar.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 0667ce1fd5..05c33a656e 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -74,6 +74,7 @@ const StatusBar = (props) => { props.history.push('/~profile/~' + window.ship)}> View Profile @@ -81,6 +82,7 @@ const StatusBar = (props) => { props.history.push('/~profile/~' + window.ship)}> Set Status @@ -88,6 +90,7 @@ const StatusBar = (props) => { props.history.push('/~profile/~' + window.ship)}> System Settings From cab2a76004b081c92d6bb507b4c95bab4ed1c3fb Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 27 Jan 2021 16:13:52 -0500 Subject: [PATCH 23/42] leap: mono for ship names --- pkg/interface/src/views/components/leap/OmniboxResult.js | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/interface/src/views/components/leap/OmniboxResult.js b/pkg/interface/src/views/components/leap/OmniboxResult.js index 3dd41aba47..1f3ca309b7 100644 --- a/pkg/interface/src/views/components/leap/OmniboxResult.js +++ b/pkg/interface/src/views/components/leap/OmniboxResult.js @@ -91,6 +91,7 @@ export class OmniboxResult extends Component { Date: Wed, 27 Jan 2021 17:20:58 -0500 Subject: [PATCH 24/42] chat: weave flattened contacts prop --- pkg/interface/src/views/apps/chat/ChatResource.tsx | 4 ++-- .../src/views/apps/chat/components/ChatMessage.tsx | 2 +- pkg/interface/src/views/components/StatusBar.js | 10 +++++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pkg/interface/src/views/apps/chat/ChatResource.tsx b/pkg/interface/src/views/apps/chat/ChatResource.tsx index 9e18ccad62..4c724f2481 100644 --- a/pkg/interface/src/views/apps/chat/ChatResource.tsx +++ b/pkg/interface/src/views/apps/chat/ChatResource.tsx @@ -24,7 +24,7 @@ export function ChatResource(props: ChatResourceProps) { const station = props.association['app-path']; const groupPath = props.association['group-path']; const group = props.groups[groupPath]; - const contacts = props.contacts[groupPath] || {}; + const contacts = props.contacts; const graph = props.graphs[station.slice(7)]; @@ -33,7 +33,7 @@ export function ChatResource(props: ChatResourceProps) { const unreadCount = props.unreads.graph?.[station]?.['/']?.unreads || 0; const [,, owner, name] = station.split('/'); - const ourContact = contacts?.[window.ship]; + const ourContact = contacts?.[`~${window.ship}`]; const chatInput = useRef(); diff --git a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx index 30ed1c2c13..675b54e85f 100644 --- a/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx +++ b/pkg/interface/src/views/apps/chat/components/ChatMessage.tsx @@ -178,7 +178,7 @@ export const MessageWithSigil = (props) => { const dark = useLocalState(state => state.dark); const datestamp = moment.unix(msg['time-sent'] / 1000).format(DATESTAMP_FORMAT); - const contact = msg.author in contacts ? contacts[msg.author] : false; + const contact = `~${msg.author}` in contacts ? contacts[`~${msg.author}`] : false; const showNickname = useShowNickname(contact); const name = showNickname ? contact.nickname : cite(msg.author); const color = contact ? `#${uxToHex(contact.color)}` : dark ? '#000000' :'#FFFFFF' diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 05c33a656e..c836c8fe6f 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -70,7 +70,15 @@ const StatusBar = (props) => { alignY="top" alignX="right" options={ - + Date: Wed, 27 Jan 2021 17:22:38 -0500 Subject: [PATCH 25/42] profileOverlay: new profile style --- .../src/views/components/ProfileOverlay.tsx | 64 ++++++++----------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/pkg/interface/src/views/components/ProfileOverlay.tsx b/pkg/interface/src/views/components/ProfileOverlay.tsx index 0b1ee7f0a8..873abfe48d 100644 --- a/pkg/interface/src/views/components/ProfileOverlay.tsx +++ b/pkg/interface/src/views/components/ProfileOverlay.tsx @@ -4,7 +4,7 @@ import { Contact, Group } from '~/types'; import { cite, useShowNickname } from '~/logic/lib/util'; import { Sigil } from '~/logic/lib/sigil'; -import { Box, Col, Button, Text, BaseImage, ColProps } from '@tlon/indigo-react'; +import { Box, Col, Row, Button, Text, BaseImage, ColProps, Icon } from '@tlon/indigo-react'; import { withLocalState } from '~/logic/state/local'; export const OVERLAY_HEIGHT = 250; @@ -60,7 +60,6 @@ class ProfileOverlay extends PureComponent { color, topSpace, bottomSpace, - group = false, hideAvatars, hideNicknames, history, @@ -78,75 +77,68 @@ class ProfileOverlay extends PureComponent { if (!(top || bottom)) { bottom = `-${Math.round(OVERLAY_HEIGHT / 2)}px`; } - const containerStyle = { top, bottom, left: '100%', maxWidth: '160px' }; + const containerStyle = { top, bottom, left: '100%' }; const isOwn = window.ship === ship; const img = contact?.avatar && !hideAvatars - ? + ? : ; const showNickname = useShowNickname(contact, hideNicknames); - // TODO: we need to rethink this "top-level profile view" of other ships - /* if (!group.hidden) { - }*/ - - const isHidden = group ? group.hidden : false; - const rootSettings = history.location.pathname.slice(0, history.location.pathname.indexOf("/resource")); return ( - + + + {(!isOwn) && ( + history.push(`/~landscape/dm/${ship}`)}/> + )} + + {img} - - {showNickname && ( + - {contact.nickname} + {showNickname ? contact.nickname : cite(ship)} - )} - {cite(`~${ship}`)} - {!isOwn && ( - - )} - {(isOwn) ? ( - - ) :
} - + {contact?.status && ({contact.status})} + ); } } -export default withLocalState(ProfileOverlay, ['hideAvatars', 'hideNicknames']); \ No newline at end of file +export default withLocalState(ProfileOverlay, ['hideAvatars', 'hideNicknames']); From 95a773df2a4da746f214031fa6ebbda1ecb5200e Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 27 Jan 2021 17:41:15 -0500 Subject: [PATCH 26/42] profileOverlay: add stubbed status setting logic Just needs the API call filled in. --- pkg/interface/src/views/components/OverlaySigil.tsx | 1 - pkg/interface/src/views/components/ProfileOverlay.tsx | 9 ++++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/interface/src/views/components/OverlaySigil.tsx b/pkg/interface/src/views/components/OverlaySigil.tsx index b2567596d8..929a8f2603 100644 --- a/pkg/interface/src/views/components/OverlaySigil.tsx +++ b/pkg/interface/src/views/components/OverlaySigil.tsx @@ -110,7 +110,6 @@ class OverlaySigil extends PureComponent { return ( { > {showNickname ? contact.nickname : cite(ship)} - {contact?.status && ({contact.status})} + api.contacts.edit()...} + > + {(!contact?.status && isOwn) ? "Set a status" : contact.status} + ); From 4ce5aafd4f5526b10167a771fe5d2b93b3229706 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Wed, 27 Jan 2021 18:08:41 -0500 Subject: [PATCH 27/42] profileOverlay: add dropdown options --- .../src/views/components/ProfileOverlay.tsx | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/pkg/interface/src/views/components/ProfileOverlay.tsx b/pkg/interface/src/views/components/ProfileOverlay.tsx index 6238ca8e99..011f58a636 100644 --- a/pkg/interface/src/views/components/ProfileOverlay.tsx +++ b/pkg/interface/src/views/components/ProfileOverlay.tsx @@ -4,7 +4,8 @@ import { Contact, Group } from '~/types'; import { cite, useShowNickname } from '~/logic/lib/util'; import { Sigil } from '~/logic/lib/sigil'; -import { Box, Col, Row, Button, Text, BaseImage, ColProps, Icon } from '@tlon/indigo-react'; +import { Box, Col, Row, Text, BaseImage, ColProps, Icon } from '@tlon/indigo-react'; +import { Dropdown } from './Dropdown'; import { withLocalState } from '~/logic/state/local'; export const OVERLAY_HEIGHT = 250; @@ -114,7 +115,44 @@ class ProfileOverlay extends PureComponent { {...rest} > - + + history.push('/~profile/' + window.ship)}> + View Profile + + {(!isOwn) && ( + history.push(`/~landscape/dm/${ship}`)} + > + Send Message + + )} + + }> + + {(!isOwn) && ( history.push(`/~landscape/dm/${ship}`)}/> )} From cb21979d94d0c13841a8014345f975d66391311b Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Wed, 27 Jan 2021 17:20:42 -0600 Subject: [PATCH 28/42] contacts: added %set-public update type --- pkg/arvo/app/contact-push-hook.hoon | 30 ++++++++------------------- pkg/arvo/app/contact-store.hoon | 20 ++++++++++++------ pkg/arvo/lib/contact-store.hoon | 4 ++++ pkg/arvo/mar/contact-hook-action.hoon | 10 --------- pkg/arvo/mar/contact/update.hoon | 16 ++++++++++++++ pkg/arvo/sur/contact-store.hoon | 1 + 6 files changed, 44 insertions(+), 37 deletions(-) delete mode 100644 pkg/arvo/mar/contact-hook-action.hoon diff --git a/pkg/arvo/app/contact-push-hook.hoon b/pkg/arvo/app/contact-push-hook.hoon index 12b665e611..716d33e8b0 100644 --- a/pkg/arvo/app/contact-push-hook.hoon +++ b/pkg/arvo/app/contact-push-hook.hoon @@ -5,10 +5,10 @@ ++ config ^- config:push-hook :* %contact-store - /our + /updates update:store %contact-update - %graph-pull-hook + %contact-pull-hook == :: +$ agent (push-hook:push-hook config) @@ -39,25 +39,13 @@ ^- ? =/ =update:store !<(update:store vase) ?- -.update - %initial %.n - %add %.y - %remove %.y - %edit %.y - %allow %.n - %disallow %.n - == -:: -++ resource-for-update - |= =vase - ^- (unit resource:res) - =/ =update:store !<(update:store vase) - ?- -.update - %initial ~ - %add `[our.bowl %our] - %remove `[our.bowl %our] - %edit `[our.bowl %our] - %allow ~ - %disallow `[our.bowl %our] + %initial %.n + %add %.y + %remove %.y + %edit %.y + %allow %.n + %disallow %.n + %set-public %.n == :: ++ initial-watch diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index ccaa105610..7c10835a60 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -11,6 +11,7 @@ =rolodex:store allowed-groups=(set resource) allowed-ships=(set ship) + is-public=_| == +$ versioned-state $% [%0 *] @@ -88,12 +89,13 @@ ^- (quip card _state) |^ ?- -.update - %initial (handle-initial +.update) - %add (handle-add +.update) - %remove (handle-remove +.update) - %edit (handle-edit +.update) - %allow (handle-allow +.update) - %disallow (handle-disallow +.update) + %initial (handle-initial +.update) + %add (handle-add +.update) + %remove (handle-remove +.update) + %edit (handle-edit +.update) + %allow (handle-allow +.update) + %disallow (handle-disallow +.update) + %set-public (handle-set-public +.update) == :: ++ handle-initial @@ -166,6 +168,12 @@ %ships state(allowed-ships (~(dif in allowed-ships) ships.beings)) == :: + ++ handle-set-public + |= public=? + ^- (quip card _state) + :_ state(is-public public) + (send-diff [%set-public public] %.n) + :: ++ send-diff |= [=update:store our=?] ^- (list card) diff --git a/pkg/arvo/lib/contact-store.hoon b/pkg/arvo/lib/contact-store.hoon index 722b779e9b..731d8a9cdc 100644 --- a/pkg/arvo/lib/contact-store.hoon +++ b/pkg/arvo/lib/contact-store.hoon @@ -47,6 +47,9 @@ %disallow :- %disallow (pairs [%beings (beng beings.upd)]~) + :: + %set-public + [%set-public b+public.upd] == :: ++ rolo @@ -112,6 +115,7 @@ [%edit edit-contact] [%allow beings] [%disallow beings] + [%set-public bo] == :: ++ initial (op ;~(pfix sig fed:ag) cont) diff --git a/pkg/arvo/mar/contact-hook-action.hoon b/pkg/arvo/mar/contact-hook-action.hoon deleted file mode 100644 index c6f27976f1..0000000000 --- a/pkg/arvo/mar/contact-hook-action.hoon +++ /dev/null @@ -1,10 +0,0 @@ -/- *contact-hook -|_ act=contact-hook-action -++ grab |% - ++ noun contact-hook-action - -- -++ grow |% - ++ noun act - -- -++ grad %noun --- diff --git a/pkg/arvo/mar/contact/update.hoon b/pkg/arvo/mar/contact/update.hoon index 733d30f48e..87d3f18a54 100644 --- a/pkg/arvo/mar/contact/update.hoon +++ b/pkg/arvo/mar/contact/update.hoon @@ -6,6 +6,22 @@ |% ++ noun upd ++ json (update:enjs upd) + ++ resource + |^ + ?- -.upd + %initial [nobody %contacts] + %add [nobody %contacts] + %remove [nobody %contacts] + %edit [nobody %contacts] + %allow !! + %disallow !! + %set-public !! + == + :: + ++ nobody + ^- @p + (bex 128) + -- -- :: ++ grab diff --git a/pkg/arvo/sur/contact-store.hoon b/pkg/arvo/sur/contact-store.hoon index 37f3af1404..d9a5109c5a 100644 --- a/pkg/arvo/sur/contact-store.hoon +++ b/pkg/arvo/sur/contact-store.hoon @@ -35,5 +35,6 @@ [%edit =ship =edit-field] [%allow =beings] [%disallow =beings] + [%set-public public=?] == -- From d3c0d92c3a326891312819612441df1b291b7db7 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Wed, 27 Jan 2021 17:21:15 -0600 Subject: [PATCH 29/42] interface: cleaning up integration of profile with mp's branch --- .../src/views/apps/profile/components/EditProfile.tsx | 6 +----- .../src/views/apps/profile/components/Profile.tsx | 2 +- .../src/views/apps/profile/components/ViewProfile.tsx | 8 +++----- pkg/interface/src/views/apps/profile/profile.tsx | 3 +-- pkg/interface/src/views/components/StatusBar.js | 7 ++----- 5 files changed, 8 insertions(+), 18 deletions(-) diff --git a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx index f7c7b4febb..69e350a39f 100644 --- a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx @@ -42,10 +42,6 @@ const emptyContact = { export function EditProfile(props: any) { const { contact, ship, api } = props; - if (ship !== window.ship) { - return null; - } - console.log(contact); const history = useHistory(); const onSubmit = async (values: any, actions: any) => { @@ -57,7 +53,7 @@ export function EditProfile(props: any) { if (newValue !== contact[key] && key !== "groups" && key !== "last-updated") { return acc.then(() => - api.contacts.edit(`~${ship}`, { [key]: newValue }) + api.contacts.edit(ship, { [key]: newValue }) ); } return acc; diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index cb46b35146..0dc07f8d1d 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -29,7 +29,7 @@ export function Profile(props: any) { const image = (!hideAvatars && contact?.avatar) ? - : ; + : ; return (
- - {`${ship}`} - + {ship}
- { (ship === window.ship) ? ( + { (ship === `~${window.ship}`) ? (
- {`${ship} `} + {ship} remains private
diff --git a/pkg/interface/src/views/apps/profile/profile.tsx b/pkg/interface/src/views/apps/profile/profile.tsx index 7baa1724db..2b663ca945 100644 --- a/pkg/interface/src/views/apps/profile/profile.tsx +++ b/pkg/interface/src/views/apps/profile/profile.tsx @@ -22,8 +22,7 @@ export default function ProfileScreen(props: any) { render={({ match, history }) => { const ship = match.params.ship; const isEdit = match.url.includes('edit'); - const contact = props.contacts?.[`~${ship}`]; - console.log(props.contacts); + const contact = props.contacts?.[ship]; const sigilColor = contact?.color ? `#${uxToHex(contact.color)}` : dark diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 05c33a656e..1d86233ddf 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -61,9 +61,6 @@ const StatusBar = (props) => { > Submit an issue - props.history.push('/~settings')}> - - { color='black' cursor='pointer' fontSize={0} - onClick={() => props.history.push('/~profile/~' + window.ship)}> + onClick={() => console.log('TODO, show a modal')}> Set Status { color='black' cursor='pointer' fontSize={0} - onClick={() => props.history.push('/~profile/~' + window.ship)}> + onClick={() => props.history.push('/~settings')}> System Settings From 520a7c2d7c4f2da5358af1869525337b79b9a7ba Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Thu, 28 Jan 2021 13:24:07 -0600 Subject: [PATCH 30/42] interface: fully support profile images in statusbar --- pkg/interface/src/views/App.js | 2 +- .../apps/profile/components/EditProfile.tsx | 6 ++- .../src/views/components/StatusBar.js | 37 ++++++++++++++++--- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/pkg/interface/src/views/App.js b/pkg/interface/src/views/App.js index 0e70bce4fe..684f29486e 100644 --- a/pkg/interface/src/views/App.js +++ b/pkg/interface/src/views/App.js @@ -137,7 +137,7 @@ class App extends React.Component { const notificationsCount = state.notificationsCount || 0; const doNotDisturb = state.doNotDisturb || false; - const ourContact = this.state.contacts[this.ship] || null; + const ourContact = this.state.contacts[`~${this.ship}`] || null; return ( diff --git a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx index 69e350a39f..b34129496f 100644 --- a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx @@ -19,6 +19,7 @@ import { Sigil } from "~/logic/lib/sigil"; import { AsyncButton } from "~/views/components/AsyncButton"; import { ColorInput } from "~/views/components/ColorInput"; import { ImageInput } from "~/views/components/ImageInput"; +import { MarkdownField } from "~/views/apps/publish/components/MarkdownField"; const formSchema = Yup.object({ @@ -74,7 +75,10 @@ export function EditProfile(props: any) { >
- + + Description + + diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 32dbd75475..9a0e906f32 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -1,18 +1,43 @@ import React from 'react'; -import { Col, Row, Box, Text, Icon, Button } from '@tlon/indigo-react'; +import { + Col, + Row, + Box, + Text, + Icon, + Button, + BaseImage +} from '@tlon/indigo-react'; import ReconnectButton from './ReconnectButton'; import { Dropdown } from './Dropdown'; import { StatusBarItem } from './StatusBarItem'; import { Sigil } from '~/logic/lib/sigil'; +import { uxToHex } from "~/logic/lib/util"; + import useLocalState from '~/logic/state/local'; const StatusBar = (props) => { + const { ourContact } = props; const invites = [].concat(...Object.values(props.invites).map(obj => Object.values(obj))); const metaKey = (window.navigator.platform.includes('Mac')) ? '⌘' : 'Ctrl+'; - const toggleOmnibox = useLocalState(state => state.toggleOmnibox); + const { toggleOmnibox, hideAvatars } = + useLocalState(({ toggleOmnibox, hideAvatars }) => + ({ toggleOmnibox, hideAvatars }) + ); + + const color = !!ourContact ? `#${uxToHex(props.ourContact.color)}` : '#000'; + const xPadding = (!hideAvatars && ourContact?.avatar) ? '0' : '2'; + const bgColor = (!hideAvatars && ourContact?.avatar) ? '' : color; + const profileImage = (!hideAvatars && ourContact?.avatar) ? ( + + ) : ; - const color = !!props.ourContact ? props.ourContact.color : '#000'; return ( { }> - + backgroundColor={bgColor}> + {profileImage} From 8ac1a9768d67ce95ca759dcac04409d30e149de6 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Thu, 28 Jan 2021 14:45:28 -0600 Subject: [PATCH 31/42] contacts + interface: hooked up isPublic to interface --- pkg/arvo/app/contact-store.hoon | 8 ++-- pkg/arvo/lib/contact-store.hoon | 12 +++++- pkg/arvo/sur/contact-store.hoon | 2 +- pkg/interface/src/logic/api/contacts.ts | 6 +++ .../src/logic/reducers/contact-update.ts | 11 ++++- pkg/interface/src/logic/store/store.ts | 1 + .../apps/profile/components/EditProfile.tsx | 40 ++++++++++++++----- .../views/apps/profile/components/Profile.tsx | 11 +++-- .../apps/profile/components/ViewProfile.tsx | 27 +++++++------ .../src/views/apps/profile/profile.tsx | 2 + 10 files changed, 86 insertions(+), 34 deletions(-) diff --git a/pkg/arvo/app/contact-store.hoon b/pkg/arvo/app/contact-store.hoon index 7c10835a60..c58b73d02a 100644 --- a/pkg/arvo/app/contact-store.hoon +++ b/pkg/arvo/app/contact-store.hoon @@ -53,7 +53,7 @@ |^ =/ cards=(list card) ?+ path (on-watch:def path) - [%all ~] (give [%initial rolodex]) + [%all ~] (give [%initial rolodex is-public]) [%updates ~] ~ :: [%our ~] @@ -99,11 +99,11 @@ == :: ++ handle-initial - |= rolo=rolodex:store + |= [rolo=rolodex:store is-public=?] ^- (quip card _state) =. rolodex (~(uni by rolodex) rolo) - :_ state(rolodex rolodex) - (send-diff [%initial rolodex] %.n) + :_ state(rolodex rolodex, is-public is-public) + (send-diff [%initial rolodex is-public] %.n) :: ++ handle-add |= [=ship =contact:store] diff --git a/pkg/arvo/lib/contact-store.hoon b/pkg/arvo/lib/contact-store.hoon index 731d8a9cdc..8356d69aab 100644 --- a/pkg/arvo/lib/contact-store.hoon +++ b/pkg/arvo/lib/contact-store.hoon @@ -20,7 +20,11 @@ ^- [cord json] ?- -.upd %initial - [%initial (rolo rolodex.upd)] + :- %initial + %- pairs + :~ [%rolodex (rolo rolodex.upd)] + [%is-public b+is-public.upd] + == :: %add :- %add @@ -118,7 +122,11 @@ [%set-public bo] == :: - ++ initial (op ;~(pfix sig fed:ag) cont) + ++ initial + %- ot + :~ [%rolodex (op ;~(pfix sig fed:ag) cont)] + [%is-public bo] + == :: ++ add-contact %- ot diff --git a/pkg/arvo/sur/contact-store.hoon b/pkg/arvo/sur/contact-store.hoon index d9a5109c5a..fb6e853303 100644 --- a/pkg/arvo/sur/contact-store.hoon +++ b/pkg/arvo/sur/contact-store.hoon @@ -29,7 +29,7 @@ == :: +$ update - $% [%initial =rolodex] + $% [%initial =rolodex is-public=?] [%add =ship =contact] [%remove =ship] [%edit =ship =edit-field] diff --git a/pkg/interface/src/logic/api/contacts.ts b/pkg/interface/src/logic/api/contacts.ts index cd9f1cd760..34db3c773c 100644 --- a/pkg/interface/src/logic/api/contacts.ts +++ b/pkg/interface/src/logic/api/contacts.ts @@ -32,6 +32,12 @@ export default class ContactsApi extends BaseApi { }); } + setPublic(setPublic: any) { + return this.storeAction({ + 'set-public': setPublic + }); + } + private storeAction(action: any): Promise { return this.action('contact-store', 'contact-update', action) } diff --git a/pkg/interface/src/logic/reducers/contact-update.ts b/pkg/interface/src/logic/reducers/contact-update.ts index ef3031d334..87aaf02c49 100644 --- a/pkg/interface/src/logic/reducers/contact-update.ts +++ b/pkg/interface/src/logic/reducers/contact-update.ts @@ -13,6 +13,7 @@ export const ContactReducer = (json, state) => { add(data, state); remove(data, state); edit(data, state); + setPublic(data, state); console.log(state.contacts); } }; @@ -20,7 +21,8 @@ export const ContactReducer = (json, state) => { const initial = (json: ContactUpdate, state: S) => { const data = _.get(json, 'initial', false); if (data) { - state.contacts = data; + state.contacts = data.rolodex; + state.isContactPublic = data['is-public']; } }; @@ -55,3 +57,10 @@ const edit = (json: ContactUpdate, state: S) => { state.contacts[ship][edit[0]] = data['edit-field'][edit[0]]; } }; + +const setPublic = (json: ContactUpdate, state: S) => { + const data = _.get(json, 'set-public', false); + state.isContactPublic = data; +}; + + diff --git a/pkg/interface/src/logic/store/store.ts b/pkg/interface/src/logic/store/store.ts index 1229bf0b83..362489a7e9 100644 --- a/pkg/interface/src/logic/store/store.ts +++ b/pkg/interface/src/logic/store/store.ts @@ -74,6 +74,7 @@ export default class GlobalStore extends BaseStore { }, credentials: null }, + isContactPublic: false, contacts: {}, notifications: new BigIntOrderedMap(), archivedNotifications: new BigIntOrderedMap(), diff --git a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx index b34129496f..cfc8204147 100644 --- a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx @@ -4,6 +4,7 @@ import * as Yup from "yup"; import { ManagedForm as Form, ManagedTextInputField as Input, + ManagedCheckboxField as Checkbox, Center, Col, Box, @@ -37,13 +38,17 @@ const emptyContact = { avatar: null, cover: null, groups: [], - 'last-updated': 0 + 'last-updated': 0, + isPublic: false }; export function EditProfile(props: any) { - const { contact, ship, api } = props; + const { contact, ship, api, isPublic } = props; const history = useHistory(); + if (contact) { + contact.isPublic = isPublic; + } const onSubmit = async (values: any, actions: any) => { console.log(values); @@ -52,14 +57,25 @@ export function EditProfile(props: any) { console.log(key); const newValue = key !== "color" ? values[key] : uxToHex(values[key]); - if (newValue !== contact[key] && key !== "groups" && key !== "last-updated") { - return acc.then(() => - api.contacts.edit(ship, { [key]: newValue }) - ); + if (newValue !== contact[key]) { + if (key === "isPublic") { + return acc.then(() => + api.contacts.setPublic(newValue) + ); + } else if ( + key !== "groups" && + key !== "last-updated" && + key !== "isPublic" + ) { + return acc.then(() => + api.contacts.edit(ship, { [key]: newValue }) + ); + } } return acc; }, Promise.resolve()); - actions.setStatus({ success: null }); + //actions.setStatus({ success: null }); + history.push(`/~profile/${ship}`); } catch (e) { console.error(e); actions.setStatus({ error: e.message }); @@ -79,14 +95,16 @@ export function EditProfile(props: any) { Description + - - + + - - + + + Submit diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 0dc07f8d1d..0e076b4d09 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -21,7 +21,7 @@ export function Profile(props: any) { if (!props.ship) { return null; } - const { contact, isEdit, ship } = props; + const { contact, isPublic, isEdit, ship } = props; const hexColor = contact?.color ? `#${uxToHex(contact.color)}` : "#000000"; const cover = (contact?.cover) ? @@ -55,9 +55,14 @@ export function Profile(props: any) { { isEdit ? ( - + ) : ( - + ) } diff --git a/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx b/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx index efb8f37b91..a84fab7994 100644 --- a/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx @@ -15,7 +15,7 @@ import { useHistory } from "react-router-dom"; export function ViewProfile(props: any) { const history = useHistory(); - const { contact, ship } = props; + const { contact, isPublic, ship } = props; return ( <> @@ -63,17 +63,20 @@ export function ViewProfile(props: any) { ) : null } - -
- {ship} - remains private -
-
+ { !isPublic && ship === `~${window.ship}` ? ( + +
+ {ship} + remains private +
+
+ ) : null + } ); } diff --git a/pkg/interface/src/views/apps/profile/profile.tsx b/pkg/interface/src/views/apps/profile/profile.tsx index 2b663ca945..c4335ea672 100644 --- a/pkg/interface/src/views/apps/profile/profile.tsx +++ b/pkg/interface/src/views/apps/profile/profile.tsx @@ -22,6 +22,7 @@ export default function ProfileScreen(props: any) { render={({ match, history }) => { const ship = match.params.ship; const isEdit = match.url.includes('edit'); + const isPublic = props.isContactPublic; const contact = props.contacts?.[ship]; const sigilColor = contact?.color ? `#${uxToHex(contact.color)}` @@ -46,6 +47,7 @@ export default function ProfileScreen(props: any) { api={props.api} s3={props.s3} isEdit={isEdit} + isPublic={isPublic} /> From 38f2022690b043e70feaa34fe21e7ed712540535 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Thu, 28 Jan 2021 17:09:31 -0600 Subject: [PATCH 32/42] interface: added ability to set status for profile --- .../apps/profile/components/EditProfile.tsx | 6 +- .../views/apps/profile/components/Profile.tsx | 10 +++- .../apps/profile/components/SetStatus.tsx | 57 +++++++++++++++++++ .../src/views/apps/profile/profile.tsx | 4 +- 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 pkg/interface/src/views/apps/profile/components/SetStatus.tsx diff --git a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx index cfc8204147..70b94096d8 100644 --- a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx @@ -83,13 +83,13 @@ export function EditProfile(props: any) { }; return ( - + <> - + Description @@ -110,6 +110,6 @@ export function EditProfile(props: any) { - + ); } diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 0e076b4d09..5c3cf67d76 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -2,6 +2,7 @@ import React from "react"; import { Sigil } from "~/logic/lib/sigil"; import { ViewProfile } from './ViewProfile'; import { EditProfile } from './EditProfile'; +import { SetStatus } from './SetStatus'; import { uxToHex } from "~/logic/lib/util"; import { @@ -9,6 +10,8 @@ import { Box, Row, BaseImage, + StatelessTextInput as Input, + Button } from "@tlon/indigo-react"; import useLocalState from "~/logic/state/local"; import { useHistory } from "react-router-dom"; @@ -35,11 +38,14 @@ export function Profile(props: any) {
+ width="100%"> + { ship === `~${window.ship}` ? ( + + ) : null + } {cover} diff --git a/pkg/interface/src/views/apps/profile/components/SetStatus.tsx b/pkg/interface/src/views/apps/profile/components/SetStatus.tsx new file mode 100644 index 0000000000..a89511f301 --- /dev/null +++ b/pkg/interface/src/views/apps/profile/components/SetStatus.tsx @@ -0,0 +1,57 @@ +import React, { + useState, + useCallback, + useEffect, + ChangeEvent +} from "react"; + +import { + Row, + Button, + StatelessTextInput as Input, +} from "@tlon/indigo-react"; + + +export function SetStatus(props: any) { + const { contact, ship, api } = props; + const [_status, setStatus] = useState(''); + const onStatusChange = useCallback( + (e: ChangeEvent) => { + setStatus(e.target.value); + }, + [setStatus] + ); + + useEffect(() => { + setStatus(!!contact ? contact.status : ''); + }, [contact]); + + const editStatus = () => { + api.contacts.edit(ship, {status: _status}); + }; + + return ( + + { + if (evt.key === 'Enter') { + editStatus(); + } + }} + /> + + + ); +} diff --git a/pkg/interface/src/views/apps/profile/profile.tsx b/pkg/interface/src/views/apps/profile/profile.tsx index c4335ea672..e2fdfed4d0 100644 --- a/pkg/interface/src/views/apps/profile/profile.tsx +++ b/pkg/interface/src/views/apps/profile/profile.tsx @@ -39,8 +39,10 @@ export default function ProfileScreen(props: any) { bg="white" border={1} borderColor="washedGray" + overflowY="auto" + flexGrow > - + Date: Thu, 28 Jan 2021 17:22:59 -0600 Subject: [PATCH 33/42] interface: set-public bug fixed --- pkg/interface/src/logic/reducers/contact-update.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/interface/src/logic/reducers/contact-update.ts b/pkg/interface/src/logic/reducers/contact-update.ts index 87aaf02c49..ac96ae36c1 100644 --- a/pkg/interface/src/logic/reducers/contact-update.ts +++ b/pkg/interface/src/logic/reducers/contact-update.ts @@ -8,13 +8,11 @@ type ContactState = Pick; export const ContactReducer = (json, state) => { const data = _.get(json, 'contact-update', false); if (data) { - console.log(data); initial(data, state); add(data, state); remove(data, state); edit(data, state); setPublic(data, state); - console.log(state.contacts); } }; @@ -59,7 +57,7 @@ const edit = (json: ContactUpdate, state: S) => { }; const setPublic = (json: ContactUpdate, state: S) => { - const data = _.get(json, 'set-public', false); + const data = _.get(json, 'set-public', state.isContactPublic); state.isContactPublic = data; }; From 53d416cf167b0a0a98c3706ba0f11964f7fc094d Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Thu, 28 Jan 2021 18:33:50 -0500 Subject: [PATCH 34/42] profile: allow line breaks in description --- .../views/apps/profile/components/ViewProfile.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx b/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx index a84fab7994..4875d7da1f 100644 --- a/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/ViewProfile.tsx @@ -7,6 +7,7 @@ import { Text, Row, Button, + Col } from "@tlon/indigo-react"; import { AsyncButton } from "~/views/components/AsyncButton"; import RichText from "~/views/components/RichText"; @@ -37,16 +38,17 @@ export function ViewProfile(props: any) { {ship}
- -
- +
+ {(contact?.bio ? contact.bio : "")} -
- +
+ { (ship === `~${window.ship}`) ? ( Date: Thu, 28 Jan 2021 19:04:41 -0500 Subject: [PATCH 35/42] profileOverlay: fix dismiss clobbering dropdown --- pkg/interface/src/views/components/ProfileOverlay.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/interface/src/views/components/ProfileOverlay.tsx b/pkg/interface/src/views/components/ProfileOverlay.tsx index 011f58a636..620123efd9 100644 --- a/pkg/interface/src/views/components/ProfileOverlay.tsx +++ b/pkg/interface/src/views/components/ProfileOverlay.tsx @@ -26,11 +26,13 @@ type ProfileOverlayProps = ColProps & { class ProfileOverlay extends PureComponent { public popoverRef: React.Ref; + public dropdownRef: React.Ref; constructor(props) { super(props); this.popoverRef = React.createRef(); + this.dropdownRef = React.createRef(); this.onDocumentClick = this.onDocumentClick.bind(this); } @@ -45,9 +47,9 @@ class ProfileOverlay extends PureComponent { } onDocumentClick(event) { - const { popoverRef } = this; + const { popoverRef, dropdownRef } = this; // Do nothing if clicking ref's element or descendent elements - if (!popoverRef.current || popoverRef.current.contains(event.target)) { + if (!popoverRef.current || dropdownRef.current.contains(event.target) || popoverRef.current.contains(event.target)) { return; } @@ -129,13 +131,14 @@ class ProfileOverlay extends PureComponent { border={1} borderRadius={2} borderColor="lightGray" + ref={this.dropdownRef} boxShadow="0px 0px 0px 3px"> history.push('/~profile/' + window.ship)}> + onClick={() => history.push('/~profile/~' + window.ship)}> View Profile {(!isOwn) && ( From 8d39763f43d582c280eaa9c1923ce8e3d12d35e1 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Fri, 29 Jan 2021 14:24:57 -0600 Subject: [PATCH 36/42] interface: set status modal works --- .../apps/profile/components/SetStatus.tsx | 6 +- .../src/views/components/SetStatusBarModal.js | 88 +++++++++++++++++++ .../src/views/components/StatusBar.js | 23 ++--- 3 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 pkg/interface/src/views/components/SetStatusBarModal.js diff --git a/pkg/interface/src/views/apps/profile/components/SetStatus.tsx b/pkg/interface/src/views/apps/profile/components/SetStatus.tsx index a89511f301..4674f315c0 100644 --- a/pkg/interface/src/views/apps/profile/components/SetStatus.tsx +++ b/pkg/interface/src/views/apps/profile/components/SetStatus.tsx @@ -13,7 +13,7 @@ import { export function SetStatus(props: any) { - const { contact, ship, api } = props; + const { contact, ship, api, callback } = props; const [_status, setStatus] = useState(''); const onStatusChange = useCallback( (e: ChangeEvent) => { @@ -28,6 +28,10 @@ export function SetStatus(props: any) { const editStatus = () => { api.contacts.edit(ship, {status: _status}); + + if (callback) { + callback(); + } }; return ( diff --git a/pkg/interface/src/views/components/SetStatusBarModal.js b/pkg/interface/src/views/components/SetStatusBarModal.js new file mode 100644 index 0000000000..baa0286e57 --- /dev/null +++ b/pkg/interface/src/views/components/SetStatusBarModal.js @@ -0,0 +1,88 @@ +import React, { + useState, + useEffect +} from 'react'; + +import { + Row, + Box +} from '@tlon/indigo-react'; + +import { SetStatus } from '~/views/apps/profile/components/SetStatus'; + + +export const SetStatusBarModal = (props) => { + const { + ship, + contact, + api, + ...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" + > + + { + setModalShown(false); + }} /> + + + + )} + setModalShown(true)}> + Set Status + + + ); +} + diff --git a/pkg/interface/src/views/components/StatusBar.js b/pkg/interface/src/views/components/StatusBar.js index 9a0e906f32..ca6cd9da6e 100644 --- a/pkg/interface/src/views/components/StatusBar.js +++ b/pkg/interface/src/views/components/StatusBar.js @@ -1,4 +1,7 @@ -import React from 'react'; +import React, { + useState, + useEffect +} from 'react'; import { Col, @@ -14,11 +17,13 @@ import { Dropdown } from './Dropdown'; import { StatusBarItem } from './StatusBarItem'; import { Sigil } from '~/logic/lib/sigil'; import { uxToHex } from "~/logic/lib/util"; +import { SetStatusBarModal } from './SetStatusBarModal'; import useLocalState from '~/logic/state/local'; + const StatusBar = (props) => { - const { ourContact } = props; + const { ourContact, api, ship } = props; const invites = [].concat(...Object.values(props.invites).map(obj => Object.values(obj))); const metaKey = (window.navigator.platform.includes('Mac')) ? '⌘' : 'Ctrl+'; const { toggleOmnibox, hideAvatars } = @@ -106,17 +111,13 @@ const StatusBar = (props) => { color='black' cursor='pointer' fontSize={0} - onClick={() => props.history.push('/~profile/~' + window.ship)}> + onClick={() => props.history.push(`/~profile/~${ship}`)}> View Profile - console.log('TODO, show a modal')}> - Set Status - + Date: Fri, 29 Jan 2021 17:28:48 -0500 Subject: [PATCH 37/42] landscape: "contacts" -> "groups" in associations --- pkg/interface/src/logic/api/contacts.ts | 2 ++ pkg/interface/src/logic/lib/omnibox.js | 2 +- pkg/interface/src/logic/lib/workspace.ts | 2 +- pkg/interface/src/types/noun.ts | 2 +- .../src/views/apps/launch/components/Groups.tsx | 10 +++++----- pkg/interface/src/views/apps/notifications/header.tsx | 2 +- pkg/interface/src/views/apps/notifications/invites.tsx | 2 +- .../src/views/apps/notifications/notifications.tsx | 2 +- .../src/views/landscape/components/GroupSwitcher.tsx | 6 +++--- .../src/views/landscape/components/GroupsPane.tsx | 4 ++-- .../src/views/landscape/components/NewGroup.tsx | 2 +- .../src/views/landscape/components/Resource.tsx | 4 ++-- .../views/landscape/components/Sidebar/SidebarList.tsx | 2 +- 13 files changed, 22 insertions(+), 20 deletions(-) diff --git a/pkg/interface/src/logic/api/contacts.ts b/pkg/interface/src/logic/api/contacts.ts index 34db3c773c..fab1ed90a2 100644 --- a/pkg/interface/src/logic/api/contacts.ts +++ b/pkg/interface/src/logic/api/contacts.ts @@ -22,6 +22,8 @@ export default class ContactsApi extends BaseApi { {color: 'fff'} // with no 0x prefix {avatar: null} {avatar: ''} + {add-group: {ship, name}} + {remove-group: {ship, name}} */ console.log(ship, editField); return this.storeAction({ diff --git a/pkg/interface/src/logic/lib/omnibox.js b/pkg/interface/src/logic/lib/omnibox.js index 6473d86ce6..0c2e27c3e0 100644 --- a/pkg/interface/src/logic/lib/omnibox.js +++ b/pkg/interface/src/logic/lib/omnibox.js @@ -116,7 +116,7 @@ export default function index(contacts, associations, apps, currentGroup, groups title, `/~landscape${group}/join/${app}${each.resource}`, app.charAt(0).toUpperCase() + app.slice(1), - (associations?.contacts?.[each.group]?.metadata?.title || null) + (associations?.groups?.[each.group]?.metadata?.title || null) ); subscriptions.push(obj); } diff --git a/pkg/interface/src/logic/lib/workspace.ts b/pkg/interface/src/logic/lib/workspace.ts index 2ab8ce65b6..7532bac6e4 100644 --- a/pkg/interface/src/logic/lib/workspace.ts +++ b/pkg/interface/src/logic/lib/workspace.ts @@ -8,7 +8,7 @@ export function getTitleFromWorkspace( case "home": return "DMs + Drafts"; case "group": - const association = associations.contacts[workspace.group]; + const association = associations.groups[workspace.group]; return association?.metadata?.title || ""; } } diff --git a/pkg/interface/src/types/noun.ts b/pkg/interface/src/types/noun.ts index 95dedea823..f566f02403 100644 --- a/pkg/interface/src/types/noun.ts +++ b/pkg/interface/src/types/noun.ts @@ -18,7 +18,7 @@ export type Serial = string; export type Jug = Map>; // name of app -export type AppName = 'chat' | 'link' | 'contacts' | 'publish' | 'graph'; +export type AppName = 'contacts' | 'groups' | 'graph'; export function getTagFromFrond(frond: O): keyof O { const tags = Object.keys(frond) as Array; diff --git a/pkg/interface/src/views/apps/launch/components/Groups.tsx b/pkg/interface/src/views/apps/launch/components/Groups.tsx index 42eab90c61..8a23536d2c 100644 --- a/pkg/interface/src/views/apps/launch/components/Groups.tsx +++ b/pkg/interface/src/views/apps/launch/components/Groups.tsx @@ -16,7 +16,7 @@ const sortGroupsAlph = (a: Association, b: Association) => alphabeticalOrder(a.metadata.title, b.metadata.title); -const getGraphUnreads = (associations: Associations, unreads: Unreads) => (path: string) => +const getGraphUnreads = (associations: Associations, unreads: Unreads) => (path: string) => f.flow( f.pickBy((a: Association) => a.group === path), f.map('resource'), @@ -24,7 +24,7 @@ const getGraphUnreads = (associations: Associations, unreads: Unreads) => (path: f.reduce(f.add, 0) )(associations.graph); -const getGraphNotifications = (associations: Associations, unreads: Unreads) => (path: string) => +const getGraphNotifications = (associations: Associations, unreads: Unreads) => (path: string) => f.flow( f.pickBy((a: Association) => a.group === path), f.map('resource'), @@ -36,7 +36,7 @@ const getGraphNotifications = (associations: Associations, unreads: Unreads) => export default function Groups(props: GroupsProps & Parameters[0]) { const { associations, unreads, inbox, ...boxProps } = props; - const groups = Object.values(associations?.contacts || {}) + const groups = Object.values(associations?.groups || {}) .filter((e) => e?.group in props.groups) .sort(sortGroupsAlph); const graphUnreads = getGraphUnreads(associations || {}, unreads); @@ -78,10 +78,10 @@ function Group(props: GroupProps) { {title} - {unreads > 0 && + {unreads > 0 && ({unreads} unread{unreads !== 1 && 's'} ) } - {updates > 0 && + {updates > 0 && ({updates} update{updates !== 1 && 's'} ) } diff --git a/pkg/interface/src/views/apps/notifications/header.tsx b/pkg/interface/src/views/apps/notifications/header.tsx index b987a06936..e27a4ef317 100644 --- a/pkg/interface/src/views/apps/notifications/header.tsx +++ b/pkg/interface/src/views/apps/notifications/header.tsx @@ -63,7 +63,7 @@ export function Header(props: { const time = moment(props.time).format("HH:mm"); const groupTitle = - props.associations.contacts?.[props.group]?.metadata?.title; + props.associations.groups?.[props.group]?.metadata?.title; const app = props.chat ? 'chat' : 'graph'; const channelTitle = diff --git a/pkg/interface/src/views/apps/notifications/invites.tsx b/pkg/interface/src/views/apps/notifications/invites.tsx index 5b77fa212e..b01f6ff047 100644 --- a/pkg/interface/src/views/apps/notifications/invites.tsx +++ b/pkg/interface/src/views/apps/notifications/invites.tsx @@ -31,7 +31,7 @@ export function Invites(props: InvitesProps) { const resourcePath = resourceAsPath(invite.resource); if (app === "contacts") { await api.contacts.join(resource); - await waiter((p) => resourcePath in p.associations?.contacts); + await waiter((p) => resourcePath in p.associations?.groups); await api.invite.accept(app, uid); history.push(`/~landscape${resourcePath}`); } else if (app === "graph") { diff --git a/pkg/interface/src/views/apps/notifications/notifications.tsx b/pkg/interface/src/views/apps/notifications/notifications.tsx index c4567cc097..16f2658b27 100644 --- a/pkg/interface/src/views/apps/notifications/notifications.tsx +++ b/pkg/interface/src/views/apps/notifications/notifications.tsx @@ -44,7 +44,7 @@ export default function NotificationsScreen(props: any) { filter.groups.length === 0 ? "All" : filter.groups - .map((g) => props.associations?.contacts?.[g]?.metadata?.title) + .map((g) => props.associations?.groups?.[g]?.metadata?.title) .join(", "); return ( diff --git a/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx b/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx index 1a78f02999..e98448f907 100644 --- a/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx +++ b/pkg/interface/src/views/landscape/components/GroupSwitcher.tsx @@ -42,9 +42,9 @@ function RecentGroups(props: { recent: string[]; associations: Associations }) { Recent Groups {props.recent.filter((e) => { - return (e in associations?.contacts); + return (e in associations?.groups); }).slice(1, 5).map((g) => { - const assoc = associations.contacts[g]; + const assoc = associations.groups[g]; const color = uxToHex(assoc?.metadata?.color || '0x0'); return ( @@ -78,7 +78,7 @@ export function GroupSwitcher(props: { }) { const { associations, workspace, isAdmin } = props; const title = getTitleFromWorkspace(associations, workspace); - const metadata = workspace.type === 'home' ? undefined : associations.contacts[workspace.group].metadata; + const metadata = workspace.type === 'home' ? undefined : associations.groups[workspace.group].metadata; const navTo = (to: string) => `${props.baseUrl}${to}`; return ( diff --git a/pkg/interface/src/views/landscape/components/GroupsPane.tsx b/pkg/interface/src/views/landscape/components/GroupsPane.tsx index be8344c178..b1e5b08eb9 100644 --- a/pkg/interface/src/views/landscape/components/GroupsPane.tsx +++ b/pkg/interface/src/views/landscape/components/GroupsPane.tsx @@ -43,7 +43,7 @@ export function GroupsPane(props: GroupsPaneProps) { const groupContacts = (groupPath && contacts[groupPath]) || undefined; const rootIdentity = contacts?.["/~/default"]?.[window.ship]; const groupAssociation = - (groupPath && associations.contacts[groupPath]) || undefined; + (groupPath && associations.groups[groupPath]) || undefined; const group = (groupPath && groups[groupPath]) || undefined; const [recentGroups, setRecentGroups] = useLocalStorageState( "recent-groups", @@ -196,7 +196,7 @@ export function GroupsPane(props: GroupsPaneProps) { let summary: ReactNode; if(groupAssociation?.group) { const memberCount = props.groups[groupAssociation.group].members.size; - summary = { - return path in contacts && path in groups && path in associations.contacts; + return path in contacts && path in groups && path in associations.groups; }); actions.setStatus({ success: null }); diff --git a/pkg/interface/src/views/landscape/components/Resource.tsx b/pkg/interface/src/views/landscape/components/Resource.tsx index ddfd316c5c..8c3cd7d7c6 100644 --- a/pkg/interface/src/views/landscape/components/Resource.tsx +++ b/pkg/interface/src/views/landscape/components/Resource.tsx @@ -37,8 +37,8 @@ export function Resource(props: ResourceProps) { const skelProps = { api, association }; let title = props.association.metadata.title; if ('workspace' in props) { - if ('group' in props.workspace && props.workspace.group in props.associations.contacts) { - title = `${props.associations.contacts[props.workspace.group].metadata.title} - ${props.association.metadata.title}`; + if ('group' in props.workspace && props.workspace.group in props.associations.groups) { + title = `${props.associations.groups[props.workspace.group].metadata.title} - ${props.association.metadata.title}`; } } return ( diff --git a/pkg/interface/src/views/landscape/components/Sidebar/SidebarList.tsx b/pkg/interface/src/views/landscape/components/Sidebar/SidebarList.tsx index ee58939962..a3d155fd84 100644 --- a/pkg/interface/src/views/landscape/components/Sidebar/SidebarList.tsx +++ b/pkg/interface/src/views/landscape/components/Sidebar/SidebarList.tsx @@ -57,7 +57,7 @@ export function SidebarList(props: { const assoc = associations[a]; return group ? assoc.group === group - : !(assoc.group in props.associations.contacts); + : !(assoc.group in props.associations.groups); }) .sort(sidebarSort(associations, props.apps)[config.sortBy]); From 81bd17a1f291bf3f47e6385d25d1f3d5cc67bb84 Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 29 Jan 2021 17:29:18 -0500 Subject: [PATCH 38/42] groupSearch: stub in multi-group search --- .../src/views/components/GroupSearch.tsx | 95 +++++++++++-------- .../src/views/components/ShipSearch.tsx | 2 +- .../landscape/components/GroupifyForm.tsx | 5 +- 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/pkg/interface/src/views/components/GroupSearch.tsx b/pkg/interface/src/views/components/GroupSearch.tsx index 0c963a00ca..012d382622 100644 --- a/pkg/interface/src/views/components/GroupSearch.tsx +++ b/pkg/interface/src/views/components/GroupSearch.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useCallback } from "react"; +import React, { useMemo, useCallback, useState, useEffect } from 'react'; import { Box, Text, @@ -6,17 +6,17 @@ import { Row, Col, Icon, - ErrorLabel, -} from "@tlon/indigo-react"; -import _ from "lodash"; -import { useField } from "formik"; -import styled from "styled-components"; + ErrorLabel +} from '@tlon/indigo-react'; +import _ from 'lodash'; +import { useField } from 'formik'; +import styled from 'styled-components'; -import { roleForShip } from "~/logic/lib/group"; +import { roleForShip } from '~/logic/lib/group'; -import { DropdownSearch } from "./DropdownSearch"; -import { Groups } from "~/types"; -import { Associations, Association } from "~/types/metadata-update"; +import { DropdownSearch } from './DropdownSearch'; +import { Groups } from '~/types'; +import { Associations, Association } from '~/types/metadata-update'; interface InviteSearchProps { disabled?: boolean; @@ -26,11 +26,12 @@ interface InviteSearchProps { label: string; caption?: string; id: string; + maxLength?: number; } const CandidateBox = styled(Box)<{ selected: boolean }>` &:hover { - background-color: ${(p) => p.theme.colors.washedGray}; + background-color: ${p => p.theme.colors.washedGray}; } `; @@ -64,38 +65,45 @@ function renderCandidate( export function GroupSearch(props: InviteSearchProps) { const { id, caption, label } = props; + const [selected, setSelected] = useState([] as string[]); const groups: Association[] = useMemo(() => { return props.adminOnly ? Object.values( - Object.keys(props.associations?.contacts) + Object.keys(props.associations?.groups) .filter( - (e) => roleForShip(props.groups[e], window.ship) === "admin" + e => roleForShip(props.groups[e], window.ship) === 'admin' ) .reduce((obj, key) => { - obj[key] = props.associations?.contacts[key]; + obj[key] = props.associations?.groups[key]; return obj; }, {}) || {} ) - : Object.values(props.associations?.contacts || {}); - }, [props.associations?.contacts]); + : Object.values(props.associations?.groups || {}); + }, [props.associations?.groups]); const [{ value }, meta, { setValue, setTouched }] = useField(props.id); + useEffect(() => { + setValue(selected); + }, [selected]) + const { title: groupTitle } = - props.associations.contacts?.[value]?.metadata || {}; + props.associations.groups?.[value]?.metadata || {}; const onSelect = useCallback( - (a: Association) => { - setValue(a.group); + (s: string) => { setTouched(true); + setSelected(v => _.uniq([...v, s])); }, - [setValue] + [setTouched, setSelected] ); - const onUnselect = useCallback(() => { - setValue(undefined); - setTouched(true); - }, [setValue]); + const onRemove = useCallback( + (s: string) => { + setSelected(groups => groups.filter(group => group !== s)) + }, + [setSelected] + ); return ( @@ -105,25 +113,11 @@ export function GroupSearch(props: InviteSearchProps) { {caption} )} - {value && ( - - {groupTitle || value} - - - )} - {!value && ( mt="2" candidates={groups} + placeholder="Search for groups..." + disabled={props.maxLength ? selected.length >= props.maxLength : false} renderCandidate={renderCandidate} search={(s: string, a: Association) => a.metadata.title.toLowerCase().startsWith(s.toLowerCase()) @@ -131,8 +125,27 @@ export function GroupSearch(props: InviteSearchProps) { getKey={(a: Association) => a.group} onSelect={onSelect} /> + {value?.length > 0 && ( + value.map((e) => { + return ( + + {groupTitle || e} + + + ); + }) )} - + {meta.error} diff --git a/pkg/interface/src/views/components/ShipSearch.tsx b/pkg/interface/src/views/components/ShipSearch.tsx index f26ed4253f..6c9db17604 100644 --- a/pkg/interface/src/views/components/ShipSearch.tsx +++ b/pkg/interface/src/views/components/ShipSearch.tsx @@ -173,7 +173,7 @@ export function ShipSearch(props: InviteSearchProps) { const result = ob.isValidPatp(ship); return result ? deSig(s) ?? undefined : undefined; }} - placeholder="Search for ships" + placeholder="Search for ships..." candidates={peers} renderCandidate={renderCandidate} disabled={props.maxLength ? selected.length >= props.maxLength : false} diff --git a/pkg/interface/src/views/landscape/components/GroupifyForm.tsx b/pkg/interface/src/views/landscape/components/GroupifyForm.tsx index e8ba57af2d..6e8424a2b4 100644 --- a/pkg/interface/src/views/landscape/components/GroupifyForm.tsx +++ b/pkg/interface/src/views/landscape/components/GroupifyForm.tsx @@ -14,7 +14,7 @@ const formSchema = Yup.object({ }); interface FormSchema { - group: string | null; + group: string[] | null; } interface GroupifyFormProps { @@ -37,7 +37,7 @@ export function GroupifyForm(props: GroupifyFormProps) { await props.api.graph.groupifyGraph( ship, name, - values.group || undefined + values.group?.toString() || undefined ); const mod = association.metadata.module || association['app-name']; const newGroup = values.group || association.group; @@ -79,6 +79,7 @@ export function GroupifyForm(props: GroupifyFormProps) { groups={props.groups} associations={props.associations} adminOnly + maxLength={1} /> Groupify From 9e3d5b7a33c67690972879e28ac6fb56238c316d Mon Sep 17 00:00:00 2001 From: Matilde Park Date: Fri, 29 Jan 2021 17:29:41 -0500 Subject: [PATCH 39/42] landscape: add group search to profile --- .../apps/profile/components/EditProfile.tsx | 16 +++++++++++++--- .../views/apps/profile/components/Profile.tsx | 2 ++ pkg/interface/src/views/apps/profile/profile.tsx | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx index 70b94096d8..35e3a135f0 100644 --- a/pkg/interface/src/views/apps/profile/components/EditProfile.tsx +++ b/pkg/interface/src/views/apps/profile/components/EditProfile.tsx @@ -21,6 +21,8 @@ import { AsyncButton } from "~/views/components/AsyncButton"; import { ColorInput } from "~/views/components/ColorInput"; import { ImageInput } from "~/views/components/ImageInput"; import { MarkdownField } from "~/views/apps/publish/components/MarkdownField"; +import { resourceFromPath } from "~/logic/lib/group"; +import GroupSearch from "~/views/components/GroupSearch"; const formSchema = Yup.object({ @@ -62,8 +64,15 @@ export function EditProfile(props: any) { return acc.then(() => api.contacts.setPublic(newValue) ); + } else if (key === 'groups') { + newValue.map((e) => { + if (!contact['groups']?.[e]) { + return acc.then(() => { + api.contacts.edit(ship, { 'add-group': resourceFromPath(e) }); + }); + } + }) } else if ( - key !== "groups" && key !== "last-updated" && key !== "isPublic" ) { @@ -93,7 +102,7 @@ export function EditProfile(props: any) { Description - + @@ -105,7 +114,8 @@ export function EditProfile(props: any) { - + + Submit diff --git a/pkg/interface/src/views/apps/profile/components/Profile.tsx b/pkg/interface/src/views/apps/profile/components/Profile.tsx index 5c3cf67d76..64e1ec0e8f 100644 --- a/pkg/interface/src/views/apps/profile/components/Profile.tsx +++ b/pkg/interface/src/views/apps/profile/components/Profile.tsx @@ -66,6 +66,8 @@ export function Profile(props: any) { contact={contact} s3={props.s3} api={props.api} + groups={props.groups} + associations={props.associations} isPublic={isPublic}/> ) : ( diff --git a/pkg/interface/src/views/apps/profile/profile.tsx b/pkg/interface/src/views/apps/profile/profile.tsx index e2fdfed4d0..eb7853f26e 100644 --- a/pkg/interface/src/views/apps/profile/profile.tsx +++ b/pkg/interface/src/views/apps/profile/profile.tsx @@ -45,6 +45,8 @@ export default function ProfileScreen(props: any) { Date: Fri, 29 Jan 2021 16:38:32 -0600 Subject: [PATCH 40/42] metadata-store: compiles --- pkg/arvo/app/metadata-store.hoon | 2 +- pkg/arvo/ted/group/on-leave.hoon | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/arvo/app/metadata-store.hoon b/pkg/arvo/app/metadata-store.hoon index ce832247fe..fe8e8aa895 100644 --- a/pkg/arvo/app/metadata-store.hoon +++ b/pkg/arvo/app/metadata-store.hoon @@ -24,7 +24,7 @@ :: /group/%path associations for group :: /- store=metadata-store -/+ *metadata-json, default-agent, verb, dbug, resource, *migrate +/+ default-agent, verb, dbug, resource, *migrate |% +$ card card:agent:gall +$ base-state-0 diff --git a/pkg/arvo/ted/group/on-leave.hoon b/pkg/arvo/ted/group/on-leave.hoon index 3e9d00ec31..e308b7d6d9 100644 --- a/pkg/arvo/ted/group/on-leave.hoon +++ b/pkg/arvo/ted/group/on-leave.hoon @@ -50,7 +50,7 @@ (en-path:res resource.update) /noun == -=/ entries=(list [m=md-resource:met g=resource:res =metadata:met]) +=/ entries=(list [m=md-resource:met g=resource:res *]) ~(tap by associations) |- ^- form:m =* loop $ @@ -62,7 +62,7 @@ %+ raw-poke [our.bowl %metadata-store] :- %metadata-action - !> ^- metadata-action:met + !> ^- action:met [%remove g.i.entries m.i.entries] :: archive graph associated with group :: From 13bad6b16cd86cab0ef30d6edb36f91cc388c763 Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Fri, 29 Jan 2021 16:52:04 -0600 Subject: [PATCH 41/42] graph-create thread: fix metadata to metadatum rename --- pkg/arvo/ted/graph/create.hoon | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/arvo/ted/graph/create.hoon b/pkg/arvo/ted/graph/create.hoon index a883d822c9..745020ef79 100644 --- a/pkg/arvo/ted/graph/create.hoon +++ b/pkg/arvo/ted/graph/create.hoon @@ -1,6 +1,6 @@ /- spider, graph=graph-store, - *metadata-store, + met=metadata-store, *group, group-store, inv=invite-store @@ -57,18 +57,18 @@ :: :: Setup metadata :: -=/ =metadata - %* . *metadata +=/ =metadatum:met + %* . *metadatum:met title title.action description description.action date-created now.bowl creator our.bowl module module.action == -=/ =metadata-action - [%add group graph+rid.action metadata] +=/ met-action=action:met + [%add group graph+rid.action metadatum] ;< ~ bind:m - (poke-our %metadata-store %metadata-action !>(metadata-action)) + (poke-our %metadata-store %metadata-action !>(met-action)) ;< ~ bind:m (poke-our %metadata-push-hook %push-hook-action !>([%add group])) :: From 7b82c55b597192f6a1d13437ad4b802ef894d6fd Mon Sep 17 00:00:00 2001 From: Logan Allen Date: Fri, 29 Jan 2021 16:58:44 -0600 Subject: [PATCH 42/42] graph-delete thread: fix %metadata-store scry and %hook reference to %push-hook --- pkg/arvo/ted/graph/delete.hoon | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/pkg/arvo/ted/graph/delete.hoon b/pkg/arvo/ted/graph/delete.hoon index f98ddf075a..d921e4723a 100644 --- a/pkg/arvo/ted/graph/delete.hoon +++ b/pkg/arvo/ted/graph/delete.hoon @@ -1,4 +1,4 @@ -/- spider, graph-view, graph=graph-store, *metadata-store, *group +/- spider, graph-view, graph=graph-store, met=metadata-store, *group /+ strandio, resource => |% @@ -9,16 +9,14 @@ ++ scry-metadata |= rid=resource =/ m (strand ,(unit resource)) - ;< paxs=(unit (set path)) bind:m - %+ scry:strandio ,(unit (set path)) + ;< group=(unit resource) bind:m + %+ scry:strandio ,(unit resource) ;: weld /gx/metadata-store/resource/graph (en-path:resource rid) /noun == - ?~ paxs (pure:m ~) - ?~ u.paxs (pure:m ~) - (pure:m `(de-path:resource n.u.paxs)) + (pure:m group) :: ++ scry-group |= rid=resource @@ -42,11 +40,7 @@ ;< ~ bind:m (poke-our %graph-push-hook %push-hook-action !>([%remove rid])) ;< ~ bind:m - %+ poke-our %metadata-hook - :- %metadata-action - !> :+ %remove - (en-path:resource group-rid) - [%graph (en-path:resource rid)] + (poke-our %metadata-push-hook %push-hook-action !>([%remove rid])) (pure:m ~) -- :: @@ -74,6 +68,5 @@ (poke-our %group-push-hook %push-hook-action !>([%remove rid.action])) ;< ~ bind:m (delete-graph u.ugroup-rid rid.action) ;< ~ bind:m - %+ poke-our %metadata-hook - metadata-hook-action+!>([%remove (en-path:resource u.ugroup-rid)]) + (poke-our %metadata-push-hook %push-hook-action !>([%remove rid.action])) (pure:m !>(~))