Merge remote-tracking branch 'origin/la/push-hook-list-resource' into lf/handoff

This commit is contained in:
Liam Fitzgerald 2021-02-03 12:50:26 +10:00
commit 7d69b23440
No known key found for this signature in database
GPG Key ID: D390E12C61D1CFFB
32 changed files with 472 additions and 311 deletions

View File

@ -9,6 +9,7 @@
update:store
%contact-update
%contact-push-hook
%.y :: necessary to enable p2p
==
--
::
@ -32,14 +33,11 @@
++ on-agent on-agent:def
++ on-watch on-watch:def
++ on-leave on-leave:def
++ resource-for-update resource-for-update:con
++ 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]
[~ this]
::
++ on-pull-kick |=(=resource `/)
--

View File

@ -1,4 +1,6 @@
/+ store=contact-store, res=resource, contact, default-agent, dbug, push-hook
/- pull-hook
/+ store=contact-store, res=resource, contact, group,
default-agent, dbug, push-hook
~% %contact-push-hook-top ..part ~
|%
+$ card card:agent:gall
@ -12,6 +14,8 @@
==
::
+$ agent (push-hook:push-hook config)
::
+$ share [%share =ship]
--
::
%- agent:dbug
@ -22,11 +26,34 @@
+* this .
def ~(. (default-agent this %|) bowl)
con ~(. contact bowl)
grp ~(. group bowl)
::
++ on-init
^- (quip card _this)
:_ this :_ ~
=- [%pass /us %agent [our.bowl %contact-push-hook] %poke %push-hook-action -]
!> ^- action:push-hook
[%add [our.bowl %'']]
::
++ on-init on-init:def
++ on-save !>(~)
++ on-load on-load:def
++ on-poke on-poke:def
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?. =(mark %contact-share) (on-poke:def mark vase)
=/ =share !<(share vase)
?> =(src.bowl ship.share)
:_ this :_ ~
:* %pass
/(scot %p src.bowl)/share
%agent
[our.bowl %contact-pull-hook]
%poke
%pull-hook-action
!> ^- action:pull-hook
[%add ship.share [ship.share %'']]
==
::
++ on-agent on-agent:def
++ on-watch on-watch:def
++ on-leave on-leave:def
@ -48,16 +75,31 @@
%set-public %.n
==
::
++ resource-for-update resource-for-update:con
::
++ initial-watch
|= [=path =resource:res]
^- vase
?> (is-allowed:con src.bowl)
|^
?> (is-allowed:con resource src.bowl)
!> ^- update:store
=/ contact=(unit contact:store) (get-contact:con our.bowl)
:+ %add
our.bowl
?^ contact u.contact
*contact:store
[%initial rolo %.n]
::
++ rolo
^- rolodex:store
=/ ugroup (scry-group:grp resource)
%- ~(gas by *rolodex:store)
?~ ugroup
=/ c=(unit contact:store) (get-contact:con our.bowl)
?~ c
[our.bowl *contact:store]~
[our.bowl u.c]~
%+ murn ~(tap in (members:grp resource))
|= s=ship
^- (unit [ship contact:store])
=/ c=(unit contact:store) (get-contact:con s)
?~(c ~ `[s u.c])
--
::
++ take-update
|= =vase

View File

@ -3,7 +3,7 @@
:: data store that holds individual contact data
::
/- store=contact-store, *resource
/+ default-agent, dbug, *migrate
/+ default-agent, dbug, *migrate, contact
|%
+$ card card:agent:gall
+$ state-4
@ -29,6 +29,7 @@
|_ =bowl:gall
+* this .
def ~(. (default-agent this %|) bowl)
con ~(. contact bowl)
::
++ on-init
=. rolodex (~(put by rolodex) our.bowl *contact:store)
@ -101,8 +102,10 @@
++ handle-initial
|= [rolo=rolodex:store is-public=?]
^- (quip card _state)
=/ our-contact (~(got by rolodex) our.bowl)
=. rolodex (~(uni by rolodex) rolo)
:_ state(rolodex rolodex, is-public is-public)
=. rolodex (~(put by rolodex) our.bowl our-contact)
:_ state(rolodex rolodex)
(send-diff [%initial rolodex is-public] %.n)
::
++ handle-add
@ -208,9 +211,22 @@
[%x %allowed-ship @ ~]
=/ =ship (slav %p i.t.t.path)
``noun+!>((~(has in allowed-ships) ship))
::
[%x %is-public ~]
``noun+!>(is-public)
::
[%x %allowed-groups ~]
``noun+!>(allowed-groups)
::
[%x %is-allowed @ @ @ @ ~]
=/ is-personal =(i.t.t.t.t.t.path 'true')
=/ =resource
?: is-personal
[our.bowl %'']
[(slav %p i.t.t.path) i.t.t.t.path]
=/ =ship (slav %p i.t.t.t.t.path)
``json+!>(`json`b+(is-allowed:con resource ship))
==
::
++ on-leave on-leave:def

View File

@ -9,6 +9,7 @@
update:store
%graph-update
%graph-push-hook
%.n
==
--
::
@ -48,4 +49,6 @@
=/ maybe-time (peek-update-log:gra resource)
?~ maybe-time `/
`/(scot %da u.maybe-time)
::
++ resource-for-update resource-for-update:gra
--

View File

@ -93,6 +93,7 @@
%tag-queries %.n
%run-updates %.n
==
++ resource-for-update resource-for-update:gra
::
++ initial-watch
|= [=path =resource:res]

View File

@ -14,6 +14,7 @@
update:store
%group-update
%group-push-hook
%.n
==
::
--
@ -28,6 +29,7 @@
+* this .
def ~(. (default-agent this %|) bowl)
dep ~(. (default:pull-hook this config) bowl)
grp ~(. grpl bowl)
::
++ on-init on-init:def
++ on-save !>(~)
@ -45,8 +47,11 @@
:_ this
=- [%pass / %agent [our.bowl %group-store] %poke -]~
group-update+!>([%remove-group resource ~])
::
++ on-pull-kick
|= =resource
^- (unit path)
`/
::
++ resource-for-update resource-for-update:grp
--

View File

@ -141,6 +141,7 @@
=(~(tap in ships.update) ~[src.bowl])
==
--
++ resource-for-update resource-for-update:grp
::
++ take-update
|= =vase

View File

@ -1,5 +1,6 @@
/- view-sur=group-view, group-store, *group, metadata=metadata-store
/+ default-agent, agentio, mdl=metadata, resource, dbug, grpl=group, verb
/+ default-agent, agentio, mdl=metadata,
resource, dbug, grpl=group, con=contact, verb
|%
++ card card:agent:gall
+$ state-zero
@ -69,9 +70,7 @@
[cards this]
::
++ on-arvo on-arvo:def
::
++ on-leave on-leave:def
::
++ on-fail on-fail:def
--
|_ =bowl:gall
@ -86,6 +85,7 @@
++ emit-many
|= crds=(list card)
jn-core(cards (weld (flop crds) cards))
::
++ emit
|= =card
jn-core(cards [card cards])
@ -152,43 +152,65 @@
%+ poke-our:(jn-pass-io /pull-groups) %group-pull-hook
pull-hook-action+!>([%add ship rid])
(tx-progress %added)
::
::
%pull-groups
?> ?=(%poke-ack -.sign)
(ack +.sign)
::
::
%groups
?+ -.sign !!
%fact (groups-fact +.sign)
%watch-ack (ack +.sign)
%kick watch-groups
==
::
::
%pull-md
?> ?=(%poke-ack -.sign)
(ack +.sign)
::
::
%pull-co
?> ?=(%poke-ack -.sign)
(ack +.sign)
::
%share-co
?> ?=(%poke-ack -.sign)
(ack +.sign)
::
%push-co
?> ?=(%poke-ack -.sign)
(ack +.sign)
::
%md
?+ -.sign !!
%fact (md-fact +.sign)
%watch-ack (ack +.sign)
%kick watch-md
==
::
::
%pull-graphs
?> ?=(%poke-ack -.sign)
%- cleanup
?^(p.sign %strange %done)
==
::
++ groups-fact
|= =cage
?. ?=(%group-update p.cage) jn-core
=+ !<(=update:group-store q.cage)
?. ?=(%initial-group -.update) jn-core
?. =(rid resource.update) jn-core
%- emit
%+ poke-our:(jn-pass-io /pull-md) %metadata-pull-hook
pull-hook-action+!>([%add [entity .]:rid])
%- emit-many
=/ cag=^cage pull-hook-action+!>([%add [entity .]:rid])
%- zing
:~ [(poke-our:(jn-pass-io /pull-md) %metadata-pull-hook cag)]~
[(poke-our:(jn-pass-io /pull-co) %contact-pull-hook cag)]~
::
?. scry-is-public:con ~
:_ ~
%+ poke:(jn-pass-io /share-co)
[entity.rid %contact-push-hook]
[%contact-share !>([%share our.bowl])]
==
::
++ md-fact
|= [=mark =vase]

View File

@ -15,6 +15,7 @@
update:metadata
%metadata-update
%metadata-push-hook
%.n
==
+$ state-zero
[%0 previews=(map resource group-preview:metadata)]
@ -191,6 +192,7 @@
++ on-arvo on-arvo:def
::
++ on-fail on-fail:def
++ resource-for-update resource-for-update:met
++ on-pull-nack
|= [=resource =tang]
^- (quip card _this)

View File

@ -92,6 +92,7 @@
?=(%member-metadata vip.metadatum)
==
::
++ resource-for-update resource-for-update:met
++ take-update
|= =vase
^- [(list card) agent]

View File

@ -173,4 +173,10 @@
==
--
--
::
++ share-dejs
=, dejs:format
|%
++ share (ot [%share (su ;~(pfix sig fed:ag))]~)
--
--

View File

@ -1,6 +1,7 @@
/- store=contact-store, *resource
/+ group
/+ group, grpl=group
|_ =bowl:gall
+* grp ~(. grpl bowl)
++ scry-for
|* [=mold =path]
.^ mold
@ -11,24 +12,88 @@
(snoc `^path`path %noun)
==
::
++ resource-for-update
|= =vase
^- (list resource)
|^
=/ =update:store !<(update:store vase)
?- -.update
%initial ~
%add (rids-for-ship ship.update)
%remove (rids-for-ship ship.update)
%edit (rids-for-ship ship.update)
%allow ~
%disallow ~
%set-public ~
==
::
++ rids-for-ship
|= s=ship
^- (list resource)
:: if the ship is in any group that I am pushing updates for, push
:: it out to that resource.
::
=/ rids
%+ skim ~(tap in scry-sharing)
|= r=resource
(is-member:grp s r)
?. =(s our.bowl)
rids
(snoc rids [our.bowl %''])
--
++ scry-sharing
.^ (set resource)
%gx
(scot %p our.bowl)
%contact-push-hook
(scot %da now.bowl)
/sharing/noun
==
::
++ get-contact
|= =ship
^- (unit contact:store)
=/ upd (scry-for (unit update:store) /contact/(scot %p ship))
?~ upd ~
?> ?=(%add -.u.upd)
`contact.u.upd
=/ =rolodex:store
(scry-for rolodex:store /all)
(~(get by rolodex) ship)
::
++ scry-is-public
.^ ?
%gx
(scot %p our.bowl)
%contact-store
(scot %da now.bowl)
/is-public/noun
==
::
++ is-allowed
|= =ship
|= [rid=resource =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)
=/ allowed-groups (scry-for (set resource) /allowed-groups)
?| :: if they are requesting our personal profile, check if we are
:: either public, or if they are on the allowed-ships list.
:: this is used for direct messages and leap searches
::
?& =(rid [our.bowl %''])
?| :: if our profile is public, allow
::
scry-is-public
:: if the requester is an allowed-ship, allow
::
(scry-for ? /allowed-ship/(scot %p ship))
:: if the requester of our profile is the host of one of
:: our allowed-groups, allow
::
%+ lien ~(tap in allowed-groups)
|= res=resource
=(entity.res ship)
== ==
:: if they are requesting our contact data within a group,
:: we make sure that we are sharing that group,
:: and that they are a member of the group
::
?& (~(has in scry-sharing) rid)
(~(has in (members:grp rid)) ship)
== ==
--

View File

@ -11,6 +11,27 @@
(snoc `^path`path %noun)
==
::
++ resource-for-update
|= =vase
^- (list resource)
=/ =update:store !<(update:store vase)
?- -.q.update
%add-graph ~[resource.q.update]
%remove-graph ~[resource.q.update]
%add-nodes ~[resource.q.update]
%remove-nodes ~[resource.q.update]
%add-signatures ~[resource.uid.q.update]
%remove-signatures ~[resource.uid.q.update]
%archive-graph ~[resource.q.update]
%unarchive-graph ~
%add-tag ~
%remove-tag ~
%keys ~
%tags ~
%tag-queries ~
%run-updates ~[resource.q.update]
==
::
++ get-graph
|= res=resource
^- update:store

View File

@ -3,6 +3,15 @@
::
|_ =bowl:gall
+$ card card:agent:gall
::
++ resource-for-update
|= =vase
^- (list resource)
=/ =update:store !<(update:store vase)
?: ?=(%initial -.update)
~
~[resource.update]
::
++ scry-for
|* [=mold =path]
=. path
@ -28,6 +37,15 @@
%+ scry-for ,(unit group)
`path`groups+(en-path:resource rid)
::
++ scry-groups
.^ ,(set resource)
%gy
(scot %p our.bowl)
%group-store
(scot %da now.bowl)
/groups/noun
==
::
++ members
|= rid=resource
=; =group

View File

@ -90,10 +90,8 @@
%chat-cli
%herm
%contact-store
%contact-hook
%contact-push-hook
%contact-pull-hook
%contact-view
%metadata-store
%s3-store
%file-server

View File

@ -4,6 +4,13 @@
/+ resource
::
|_ =bowl:gall
++ resource-for-update
|= =vase
^- (list resource)
=/ =update:store !<(update:store vase)
?. ?=(?(%add %remove %initial-group) -.update) ~
~[group.update]
::
++ app-paths-from-group
|= [=app-name:store group=resource]
^- (list resource)

View File

@ -30,12 +30,15 @@
:: .store-name: name of the store to send subscription updates to.
:: .update-mark: mark that updates will be tagged with
:: .push-hook-name: name of the corresponding push-hook
:: .no-validate: If true, don't validate that resource/wire/src match
:: up
::
+$ config
$: store-name=term
update=mold
update-mark=term
push-hook-name=term
no-validate=_|
==
::
:: $base-state-0: state for the pull hook
@ -106,6 +109,14 @@
++ on-pull-kick
|~ resource
*(unit path)
:: +resource-for-update: get resources from vase
::
:: This should be identical to the +resource-for-update arm in the
:: corresponding push-hook
::
++ resource-for-update
|~ vase
*(list resource)
::
:: from agent:gall
++ on-init
@ -470,24 +481,30 @@
/helper/pull-hook
wire
::
++ get-conversion
.^ tube:clay
%cc (scot %p our.bowl) %home (scot %da now.bowl)
/[update-mark.config]/resource
==
::
++ give-update
^- card
[%give %fact ~[/tracking] %pull-hook-update !>(tracking)]
::
++ check-src
|= resources=(set resource)
^- ?
%+ roll ~(tap in resources)
|= [rid=resource out=_|]
?: out %.y
?~ ship=(~(get by tracking) rid)
%.n
=(src.bowl u.ship)
::
++ update-store
|= [wire-rid=resource =vase]
^- card
=/ =wire
(make-wire /store)
=+ !<(rid=resource (get-conversion vase))
?> =(src.bowl (~(got by tracking) rid))
?> =(wire-rid rid)
=+ resources=(~(gas in *(set resource)) (resource-for-update:og vase))
?> ?| no-validate.config
?& (check-src resources)
(~(has in resources) wire-rid)
== ==
[%pass wire %agent [our.bowl store-name.config] %poke update-mark.config vase]
--
--

View File

@ -67,6 +67,16 @@
|* =config
$_ ^|
|_ bowl:gall
::
:: +resource-for-update: get affected resources from an update
::
:: Given a vase of the update, the mark of which is
:: update-mark.config, produce the affected resources, if any.
::
++ resource-for-update
|~ vase
*(list resource)
::
:: +take-update: handle update from store
::
:: Given an update from the store, do other things after proxying
@ -145,12 +155,12 @@
=* state -
^- agent:gall
=<
|_ =bowl:gall
+* this .
og ~(. push-hook bowl)
hc ~(. +> bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init
=^ cards push-hook
on-init:og
@ -165,11 +175,9 @@
|^
?- -.old
%1
=. cards
:_(cards (build-mark:hc %sing))
=^ og-cards push-hook
(on-load:og inner-state.old)
[(weld (flop cards) og-cards) this(state old)]
[(weld cards og-cards) this(state old)]
::
%0
%_ $
@ -261,6 +269,7 @@
(push-updates:hc q.cage.sign)
cards
==
::
++ on-leave
|= =path
=^ cards push-hook
@ -269,20 +278,16 @@
::
++ on-arvo
|= [=wire =sign-arvo]
?. ?=([%helper %push-hook @ *] wire)
=^ cards push-hook
(on-arvo:og wire sign-arvo)
[cards this]
?. ?=(%resource-conversion i.t.t.wire)
(on-arvo:def wire sign-arvo)
:_ this
~[(build-mark:hc %next)]
=^ cards push-hook
(on-arvo:og wire sign-arvo)
[cards this]
::
++ on-fail
|= [=term =tang]
=^ cards push-hook
(on-fail:og term tang)
[cards this]
::
++ on-peek
|= =path
^- (unit (unit cage))
@ -311,6 +316,7 @@
%remove (remove +.action)
%revoke (revoke +.action)
==
::
++ add
|= rid=resource
=. sharing
@ -322,7 +328,7 @@
=/ pax=path
[%resource (en-path:resource rid)]
=/ paths=(set path)
%- sy
%- silt
%+ turn
(incoming-subscriptions pax)
|=([ship pox=path] pox)
@ -344,6 +350,7 @@
~
`[%give %kick ~[path] `her]
--
::
++ incoming-subscriptions
|= prefix=path
^- (list (pair ship path))
@ -371,58 +378,50 @@
++ push-updates
|= =vase
^- (list card:agent:gall)
=/ rid=(unit resource)
(resource-for-update vase)
?~ rid ~
=/ rids=(list resource) (resource-for-update vase)
=| cards=(list card:agent:gall)
|-
?~ rids cards
=/ prefix=path
resource+(en-path:resource u.rid)
resource+(en-path:resource i.rids)
=/ paths=(list path)
%~ tap in
%- silt
%+ turn
(incoming-subscriptions prefix)
|=([ship pax=path] pax)
?~ paths ~
[%give %fact paths update-mark.config vase]~
?~ paths $(rids t.rids)
%_ $
rids t.rids
cards (snoc cards [%give %fact paths update-mark.config vase])
==
::
++ forward-update
|= update=vase
|= =vase
^- (list card:agent:gall)
=/ rid=resource
(need (resource-for-update update))
=/ rids=(list resource) (resource-for-update vase)
=| cards=(list card:agent:gall)
|-
?~ rids cards
=/ =path
resource+(en-path:resource rid)
resource+(en-path:resource i.rids)
=/ =wire
(make-wire resource+(en-path:resource rid))
(make-wire resource+(en-path:resource i.rids))
=/ dap=term
?:(=(our.bowl entity.rid) store-name.config dap.bowl)
[%pass wire %agent [entity.rid dap] %poke update-mark.config update]~
::
++ get-conversion
.^ tube:clay
%cc (scot %p our.bowl) %home (scot %da now.bowl)
/[update-mark.config]/resource
?:(=(our.bowl entity.i.rids) store-name.config dap.bowl)
%_ $
rids t.rids
::
cards
%+ snoc cards
[%pass wire %agent [entity.i.rids dap] %poke update-mark.config vase]
==
::
++ resource-for-update
|= update=vase
^- (unit resource)
=/ converted=(each vase (list tank))
(mule |.((get-conversion update)))
?: ?=(%| -.converted)
%- (slog p.converted)
~
[~ !<(resource p.converted)]
::
++ build-mark
|= rav=?(%sing %next)
^- card
=/ =wire
(make-wire /resource-conversion)
=/ =mood:clay
[%c da+now.bowl /[update-mark.config]/resource]
=/ =rave:clay
?:(?=(%next rav) [rav mood] [rav mood])
[%pass wire %arvo %c %warp our.bowl [%home `rave]]
|= =vase
^- (list resource)
%~ tap in
%- silt
(resource-for-update:og vase)
--
--

View File

@ -0,0 +1,15 @@
/+ *contact-store
::
|_ share=[%share =ship]
++ grad %noun
++ grow
|%
++ noun share
--
::
++ grab
|%
++ noun _share
++ json share:share-dejs
--
--

View File

@ -6,22 +6,6 @@
|%
++ 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

View File

@ -7,13 +7,6 @@
|%
++ noun upd
++ json (update:enjs upd)
++ resource
?+ -.q.upd !!
?(%run-updates %add-nodes %remove-nodes %add-graph) resource.q.upd
?(%remove-graph %archive-graph %unarchive-graph) resource.q.upd
?(%add-tag %remove-tag) resource.q.upd
?(%add-signatures %remove-signatures) resource.uid.q.upd
==
++ mime [/application/x-urb-graph-update (as-octs (jam upd))]
--
::

View File

@ -4,10 +4,6 @@
++ grow
|%
++ noun upd
++ resource
?< ?=(%initial -.upd)
resource.upd
::
++ json
%+ frond:enjs:format 'groupUpdate'
(update:enjs upd)

View File

@ -4,9 +4,6 @@
++ grow
|%
++ noun update
++ resource
?> ?=(?(%add %remove %initial-group) -.update)
group.update
++ json (update:enjs:store update)
--
::

View File

@ -45,6 +45,7 @@
[%add rid groups+rid metadatum]
;< ~ bind:m (poke-our %metadata-store %metadata-action !>(met-action))
;< ~ bind:m (poke-our %metadata-push-hook push-hook-act)
;< ~ bind:m (poke-our %contact-push-hook push-hook-act)
(pure:m !>(~))

View File

@ -26,5 +26,6 @@
;< ~ bind:m (cleanup-md:view rid)
;< ~ bind:m (poke-our %group-store %group-update !>([%remove-group rid ~]))
;< ~ bind:m (poke-our %metadata-push-hook push-hook-act)
;< ~ bind:m (poke-our %contact-push-hook push-hook-act)
;< ~ bind:m (poke-our %group-push-hook push-hook-act)
(pure:m !>(~))

View File

@ -22,6 +22,7 @@
:- %pull-hook-action
!> ^- action:pull-hook
[%remove rid]
;< ~ bind:m (poke-our %contact-pull-hook pull-hook-act)
;< ~ bind:m (poke-our %metadata-pull-hook pull-hook-act)
;< ~ bind:m (poke-our %group-pull-hook pull-hook-act)
;< ~ bind:m (poke-our %group-store %group-update !>([%remove-group rid ~]))

View File

@ -57,6 +57,7 @@ export default class BaseApi<S extends object = {}> {
}
scry<T>(app: string, path: Path): Promise<T> {
console.log(path);
return fetch(`/~/scry/${app}${path}.json`).then(r => r.json() as Promise<T>);
}

View File

@ -34,12 +34,35 @@ export default class ContactsApi extends BaseApi<StoreState> {
});
}
allow(ship) {
return this.storeAction({
allow: ship
});
}
setPublic(setPublic: any) {
return this.storeAction({
'set-public': setPublic
});
}
share(recipient, us) {
return this.action(
'contact-push-hook',
'contact-share',
{ share: us },
recipient
);
}
fetchIsAllowed(entity, name, ship, personal) {
const isPersonal = personal ? 'true' : 'false';
return this.scry<any>(
'contact-store',
`/is-allowed/${entity}/${name}/${ship}/${isPersonal}`
);
}
private storeAction(action: any): Promise<any> {
return this.action('contact-store', 'contact-update', action)
}

View File

@ -1,4 +1,4 @@
import React, { useRef, useCallback, useEffect } from 'react';
import React, { useRef, useCallback, useEffect, useState } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { Col } from '@tlon/indigo-react';
import _ from 'lodash';
@ -36,6 +36,7 @@ export function ChatResource(props: ChatResourceProps) {
const [,, owner, name] = station.split('/');
const ourContact = contacts?.[`~${window.ship}`];
console.log(contacts);
const chatInput = useRef<ChatInput>();
@ -89,7 +90,15 @@ export function ChatResource(props: ChatResourceProps) {
return (
<Col {...bind} height="100%" overflow="hidden" position="relative">
<ShareProfile our={ourContact} />
<ShareProfile
our={ourContact}
api={props.api}
recipient={owner}
group={group}
groupPath={groupPath}
hideBanner={() => {
setProfileAllowed(true);
}} />
{dragging && <SubmitDragger />}
<ChatWindow
mailboxSize={5}

View File

@ -1,16 +1,100 @@
import React from 'react';
import React, {
useState,
useEffect
} from 'react';
import { Box, Row, Text, BaseImage } from '@tlon/indigo-react';
import { uxToHex } from '~/logic/lib/util';
import { Sigil } from '~/logic/lib/sigil';
export const ShareProfile = (props) => {
const image = (props?.our?.avatar)
? <BaseImage src={props.our.avatar} width='24px' height='24px' borderRadius={2} style={{ objectFit: 'cover' }} />
: <Row p={1} alignItems="center" borderRadius={2} backgroundColor={`#${uxToHex(props?.our?.color)}` || "#000000"}>
<Sigil ship={window.ship} size={16} icon color={`#${uxToHex(props?.our?.color)}` || "#000000"} />
</Row>;
const pathAsResource = (path) => {
if (!path) {
return false;
}
const pathArr = path.split('/');
if (pathArr.length !== 4) {
return false;
}
return (
return {
entity: pathArr[2],
name: pathArr[3]
};
};
export const ShareProfile = (props) => {
const { api, recipient, hideBanner, group, groupPath } = props;
console.log(groupPath);
// TODO: use isContactPublic somewhere
const [showBanner, setShowBanner] = useState(false);
const res = pathAsResource(groupPath);
useEffect(() => {
if (!res) { return; }
if (!group) { return; }
console.log(group);
if (group.hidden) {
// TODO:
// take the union of the pending set and the members set,
// subtract ourselves, then if *anyone* has not already been shared with,
// show the banner
// Promise.all all the members of the set
let check =
Promise.all()
props.api.contacts.fetchIsAllowed(
`~${window.ship}`,
'personal', // not used
recipient,
true
).then((retVal) => {
console.log(retVal);
setShowBanner(!retVal);
});
} else {
// TODO:
// if the group is not in the allowed-groups set, then show the banner
props.api.contacts.fetchIsAllowed(
res.entity,
res.name,
recipient,
false
).then((retVal) => {
console.log(retVal);
setShowBanner(!retVal);
});
}
}, [recipient, res, group]);
const image = (props?.our?.avatar)
? (
<BaseImage
src={props.our.avatar}
width='24px'
height='24px'
borderRadius={2}
style={{ objectFit: 'cover' }} />
) : (
<Row
p={1}
alignItems="center"
borderRadius={2}
backgroundColor={!props.our ? `#${uxToHex(props.our.color)}` : "#000000"}>
<Sigil
ship={window.ship}
size={16}
color={`#${uxToHex(props?.our?.color)}` || "#000000"}
icon />
</Row>
);
const onClick = () => {
api.contacts.allow(recipient).then(() => {
api.contacts.share(recipient, window.ship);
});
hideBanner();
};
return showBanner ? (
<Row
height="48px"
alignItems="center"
@ -22,9 +106,9 @@ export const ShareProfile = (props) => {
{image}
<Text verticalAlign="middle" pl={2}>Share private profile?</Text>
</Row>
<Box pr={2}>
<Box pr={2} onClick={onClick}>
<Text color="blue" bold cursor="pointer">Share</Text>
</Box>
</Row>
);
) : null;
};

View File

@ -1,167 +0,0 @@
import React from "react";
import { Sigil } from "~/logic/lib/sigil";
import * as Yup from "yup";
import { uxToHex } from "~/logic/lib/util";
import {
ManagedForm as Form,
Col,
ManagedTextInputField as Input,
Box,
Text,
Row,
BaseImage
} from "@tlon/indigo-react";
import { Formik, FormikHelpers } from "formik";
import { Contact } from "~/types/contact-update";
import { AsyncButton } from "~/views/components/AsyncButton";
import { ColorInput } from "~/views/components/ColorInput";
import GlobalApi from "~/logic/api/global";
import { ImageInput } from "~/views/components/ImageInput";
import { S3State } from "~/types";
import useLocalState from "~/logic/state/local";
interface ContactCardProps {
contact: Contact;
path: string;
api: GlobalApi;
s3: S3State;
rootIdentity: Contact;
}
const formSchema = Yup.object({
color: Yup.string(),
nickname: Yup.string(),
email: Yup.string().matches(
new RegExp(
String(
/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*/.source
) +
/@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/
.source
),
"Not a valid email"
),
phone: Yup.string().matches(
new RegExp(
String(/^\s*(?:\+?(\d{1,3}))?/.source) +
/([-. (]*(\d{3})[-. )]*)?((\d{3})[-. ]*(\d{2,4})(?:[-.x ]*(\d+))?)\s*$/
.source
),
"Not a valid phone"
),
website: Yup.string().matches(
new RegExp(
String(/[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}/.source) +
/\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/.source
),
"Not a valid website"
),
});
const emptyContact = {
avatar: null,
color: '0',
nickname: '',
email: '',
phone: '',
website: '',
notes: ''
};
export function ContactCard(props: ContactCardProps) {
const { hideAvatars, hideNicknames } = useLocalState(({ hideAvatars, hideNicknames }) => ({
hideAvatars, hideNicknames
}));
const us = `~${window.ship}`;
const { contact, rootIdentity } = props;
const onSubmit = async (values: any, actions: FormikHelpers<Contact>) => {
try {
if(!contact) {
const [,,ship] = props.path.split('/');
values.color = uxToHex(values.color);
const sharedValues = Object.assign({}, values);
sharedValues.avatar = !values.avatar ? null : { url: values.avatar };
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 });
}
};
const hexColor = contact?.color ? `#${uxToHex(contact.color)}` : "#000000";
const image = (!hideAvatars && contact?.avatar)
? <BaseImage src={contact.avatar} width='100%' height='100%' style={{ objectFit: 'cover' }} />
: <Sigil ship={us} size={32} color={hexColor} />;
const nickname = (!hideNicknames && contact?.nickname) ? contact.nickname : "";
return (
<Box p={4} height="100%" overflowY="auto">
<Formik
validationSchema={formSchema}
initialValues={contact || rootIdentity || emptyContact}
onSubmit={onSubmit}
>
<Form
display="grid"
gridAutoRows="auto"
gridTemplateColumns="100%"
gridRowGap="5"
maxWidth="400px"
width="100%"
>
<Row
borderBottom={1}
borderBottomColor="washedGray"
pb={3}
alignItems="center"
>
<Box height='32px' width='32px'>
{image}
</Box>
<Box ml={2}>
<Text mono={!Boolean(nickname)}>{nickname}</Text>
</Box>
</Row>
<ImageInput id="avatar" label="Avatar" s3={props.s3} />
<ColorInput id="color" label="Sigil Color" />
<Input id="nickname" label="Nickname" />
<Input id="email" label="Email" />
<Input id="phone" label="Phone" />
<Input id="website" label="Website" />
<Input id="notes" label="Notes" />
<AsyncButton primary loadingText="Updating..." border>
{(contact) ? "Save" : "Share Contact"}
</AsyncButton>
</Form>
</Formik>
</Box>
);
}

View File

@ -59,3 +59,4 @@ export function SetStatus(props: any) {
</Row>
);
}