mirror of
https://github.com/urbit/shrub.git
synced 2024-11-27 18:34:48 +03:00
Merge branch 'os1-rc' into contacts-group-validate
This commit is contained in:
commit
dc673e4bb4
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:34c491ba1d70c1485a295fb04602609fb477a20fa9aa293077a8d7069e33101f
|
||||
size 12396834
|
||||
oid sha256:12ccd71ef3ac0c6cda7502fdf88d76fe227e19c5ba9e9abfb5a2ec9c4572f098
|
||||
size 20382757
|
||||
|
@ -235,7 +235,7 @@
|
||||
|= [=wire upd=chat-update]
|
||||
^- (quip card state)
|
||||
?+ -.upd [~ all-state]
|
||||
%create (notice-create +.upd)
|
||||
%create (notice-create (path-to-target path.upd))
|
||||
%delete [[(show-delete:sh-out (path-to-target path.upd)) ~] all-state]
|
||||
%message (read-envelope (path-to-target path.upd) envelope.upd)
|
||||
%messages (read-envelopes (path-to-target path.upd) envelopes.upd)
|
||||
|
@ -137,7 +137,7 @@
|
||||
?. =(u.ship our.bol)
|
||||
~
|
||||
:: scry permissions to check if write is permitted
|
||||
?. (permitted-scry [(scot %p src.bol) %chat (weld path.act /write)])
|
||||
?. (permitted-scry [(scot %p src.bol) path.act])
|
||||
~
|
||||
=: author.envelope.act src.bol
|
||||
when.envelope.act now.bol
|
||||
@ -159,19 +159,18 @@
|
||||
:_ state
|
||||
%+ weld
|
||||
[%pass chat-path %agent [our.bol %chat-store] %watch chat-path]~
|
||||
(create-permission [%chat path.act] security.act)
|
||||
(create-permission path.act security.act)
|
||||
::
|
||||
%add-synced
|
||||
?> (team:title our.bol src.bol)
|
||||
?: (~(has by synced) [(scot %p ship.act) path.act])
|
||||
[~ state]
|
||||
=. synced (~(put by synced) [(scot %p ship.act) path.act] ship.act)
|
||||
?: (~(has by synced) path.act) [~ state]
|
||||
=. synced (~(put by synced) path.act ship.act)
|
||||
?. ask-history.act
|
||||
=/ chat-path [%mailbox (scot %p ship.act) path.act]
|
||||
=/ chat-path [%mailbox path.act]
|
||||
:_ state
|
||||
[%pass chat-path %agent [ship.act %chat-hook] %watch chat-path]~
|
||||
:: TODO: only ask for backlog from previous point
|
||||
=/ chat-history [%backlog (scot %p ship.act) (weld path.act /0)]
|
||||
=/ chat-history [%backlog (weld path.act /0)]
|
||||
:_ state
|
||||
[%pass chat-history %agent [ship.act %chat-hook] %watch chat-history]~
|
||||
::
|
||||
@ -185,7 +184,7 @@
|
||||
%- zing
|
||||
:~ (pull-wire [%backlog (weld path.act /0)])
|
||||
(pull-wire [%mailbox path.act])
|
||||
(delete-permission [%chat path.act])
|
||||
~[(permission-poke [%delete [%chat path.act]])]
|
||||
[%give %kick [%mailbox path.act]~ ~]~
|
||||
==
|
||||
?. |(=(u.ship src.bol) (team:title our.bol src.bol))
|
||||
@ -202,10 +201,10 @@
|
||||
?> ?=(^ pax)
|
||||
?> (~(has by synced) pax)
|
||||
:: scry permissions to check if read is permitted
|
||||
?> (permitted-scry [(scot %p src.bol) %chat (weld pax /read)])
|
||||
?> (permitted-scry [(scot %p src.bol) pax])
|
||||
=/ box (chat-scry pax)
|
||||
?~ box !!
|
||||
[%give %fact ~ %chat-update !>([%create (slav %p i.pax) pax])]~
|
||||
[%give %fact ~ %chat-update !>([%create pax])]~
|
||||
::
|
||||
++ watch-backlog
|
||||
|= pax=path
|
||||
@ -220,16 +219,12 @@
|
||||
?> ?=([* ^] pas)
|
||||
?> (~(has by synced) pas)
|
||||
:: scry permissions to check if read is permitted
|
||||
?> (permitted-scry [(scot %p src.bol) %chat (weld pas /read)])
|
||||
=/ box (chat-scry pas)
|
||||
?~ box !!
|
||||
:- [%give %fact ~ %chat-update !>([%create (slav %p i.pas) pas])]
|
||||
?> (permitted-scry [(scot %p src.bol) pas])
|
||||
%- zing
|
||||
:~
|
||||
?: ?&(?=(^ backlog-start) (~(got by allow-history) pas))
|
||||
(paginate-messages pas u.box u.backlog-start)
|
||||
~
|
||||
[%give %kick [%backlog pax]~ `src.bol]~
|
||||
:~ [%give %fact ~ %chat-update !>([%create pas])]~
|
||||
?. ?&(?=(^ backlog-start) (~(has by allow-history) pas)) ~
|
||||
(paginate-messages pas (need (chat-scry pas)) u.backlog-start)
|
||||
[%give %kick [%backlog pax]~ `src.bol]~
|
||||
==
|
||||
::
|
||||
++ paginate-messages
|
||||
@ -267,10 +262,7 @@
|
||||
[~ state]
|
||||
::
|
||||
%accepted
|
||||
=/ ask-history
|
||||
?~ (chat-scry [(scot %p ship.invite.fact) path.invite.fact])
|
||||
%.y
|
||||
%.n
|
||||
=/ ask-history ?~((chat-scry path.invite.fact) %.y %.n)
|
||||
:_ state
|
||||
[(chat-view-poke [%join ship.invite.fact path.invite.fact ask-history])]~
|
||||
==
|
||||
@ -279,9 +271,7 @@
|
||||
|= [wir=wire fact=permission-update]
|
||||
^- (quip card _state)
|
||||
:_ state
|
||||
?- -.fact
|
||||
%create ~
|
||||
%delete ~
|
||||
?+ -.fact ~
|
||||
%add (handle-permissions [%add path.fact who.fact])
|
||||
%remove (handle-permissions [%remove path.fact who.fact])
|
||||
==
|
||||
@ -290,10 +280,6 @@
|
||||
|= [kind=?(%add %remove) pax=path who=(set ship)]
|
||||
^- (list card)
|
||||
?> ?=([* *] pax)
|
||||
?. =(%chat i.pax) ~
|
||||
:: check path to see if this is a %read permission
|
||||
?. =(%read (snag (dec (lent pax)) `(list @t)`pax))
|
||||
~
|
||||
%- zing
|
||||
%+ turn ~(tap in who)
|
||||
|= =ship
|
||||
@ -314,11 +300,7 @@
|
||||
++ handle-local
|
||||
|= fact=chat-update
|
||||
^- (quip card _state)
|
||||
?- -.fact
|
||||
%keys [~ state]
|
||||
%read [~ state]
|
||||
%config [~ state]
|
||||
%create [~ state]
|
||||
?+ -.fact [~ state]
|
||||
%delete
|
||||
?. (~(has by synced) path.fact)
|
||||
[~ state]
|
||||
@ -337,17 +319,14 @@
|
||||
++ handle-foreign
|
||||
|= fact=chat-update
|
||||
^- (quip card _state)
|
||||
?- -.fact
|
||||
%keys [~ state]
|
||||
%read [~ state]
|
||||
%config [~ state]
|
||||
?+ -.fact [~ state]
|
||||
%create
|
||||
:_ state
|
||||
?> ?=([* ^] path.fact)
|
||||
=/ shp (~(get by synced) path.fact)
|
||||
?~ shp ~
|
||||
?. =(src.bol u.shp) ~
|
||||
[(chat-poke [%create ship.fact t.path.fact])]~
|
||||
[(chat-poke [%create path.fact])]~
|
||||
::
|
||||
%delete
|
||||
?> ?=([* ^] path.fact)
|
||||
@ -394,20 +373,17 @@
|
||||
=/ mailbox=(unit mailbox) (chat-scry t.wir)
|
||||
=/ chat-history
|
||||
%+ welp backlog+t.wir
|
||||
?~ mailbox
|
||||
/0
|
||||
/(scot %ud (lent envelopes.u.mailbox))
|
||||
?~(mailbox /0 /(scot %ud (lent envelopes.u.mailbox)))
|
||||
:_ state
|
||||
[%pass chat-history %agent [ship %chat-hook] %watch chat-history]~
|
||||
::
|
||||
?: ?=([%backlog @ *] wir)
|
||||
=/ pax `path`(oust [(dec (lent t.wir)) 1] `(list @ta)`t.wir)
|
||||
?. (~(has by synced) pax) [~ state]
|
||||
=/ mailbox=(unit mailbox) (chat-scry pax)
|
||||
=. pax ?~(mailbox wir [%mailbox pax])
|
||||
:_ state
|
||||
[%pass pax %agent [(slav %p i.t.wir) %chat-hook] %watch pax]~
|
||||
!!
|
||||
?. ?=([%backlog @ *] wir) !!
|
||||
=/ pax `path`(oust [(dec (lent t.wir)) 1] `(list @ta)`t.wir)
|
||||
?. (~(has by synced) pax) [~ state]
|
||||
=/ mailbox=(unit mailbox) (chat-scry pax)
|
||||
=. pax ?~(mailbox wir [%mailbox pax])
|
||||
:_ state
|
||||
[%pass pax %agent [(slav %p i.t.wir) %chat-hook] %watch pax]~
|
||||
::
|
||||
++ watch-ack
|
||||
|= [wir=wire saw=(unit tang)]
|
||||
@ -451,37 +427,9 @@
|
||||
++ create-permission
|
||||
|= [pax=path sec=rw-security]
|
||||
^- (list card)
|
||||
=/ read-perm (weld pax /read)
|
||||
=/ write-perm (weld pax /write)
|
||||
?- sec
|
||||
%channel
|
||||
:~ (permission-poke (sec-to-perm read-perm %black))
|
||||
(permission-poke (sec-to-perm write-perm %black))
|
||||
==
|
||||
::
|
||||
%village
|
||||
:~ (permission-poke (sec-to-perm read-perm %white))
|
||||
(permission-poke (sec-to-perm write-perm %white))
|
||||
==
|
||||
::
|
||||
%journal
|
||||
:~ (permission-poke (sec-to-perm read-perm %black))
|
||||
(permission-poke (sec-to-perm write-perm %white))
|
||||
==
|
||||
::
|
||||
%mailbox
|
||||
:~ (permission-poke (sec-to-perm read-perm %white))
|
||||
(permission-poke (sec-to-perm write-perm %black))
|
||||
==
|
||||
==
|
||||
::
|
||||
++ delete-permission
|
||||
|= pax=path
|
||||
^- (list card)
|
||||
=/ read-perm (weld pax /read)
|
||||
=/ write-perm (weld pax /write)
|
||||
:~ (permission-poke [%delete read-perm])
|
||||
(permission-poke [%delete write-perm])
|
||||
?+ sec ~
|
||||
%channel ~[(permission-poke (sec-to-perm pax %black))]
|
||||
%village ~[(permission-poke (sec-to-perm pax %white))]
|
||||
==
|
||||
::
|
||||
++ sec-to-perm
|
||||
|
@ -59,8 +59,7 @@
|
||||
[%updates ~] ~
|
||||
[%mailbox @ *]
|
||||
?> (~(has by inbox) t.path)
|
||||
=/ =ship (slav %p i.t.path)
|
||||
(give %chat-update !>([%create ship t.t.path]))
|
||||
(give %chat-update !>([%create t.path]))
|
||||
==
|
||||
[cards this]
|
||||
::
|
||||
@ -159,11 +158,10 @@
|
||||
|= act=chat-action
|
||||
^- (quip card _state)
|
||||
?> ?=(%create -.act)
|
||||
=/ pax [(scot %p ship.act) path.act]
|
||||
?: (~(has by inbox) pax)
|
||||
?: (~(has by inbox) path.act)
|
||||
[~ state]
|
||||
:- (send-diff pax act)
|
||||
state(inbox (~(put by inbox) pax *mailbox))
|
||||
:- (send-diff path.act act)
|
||||
state(inbox (~(put by inbox) path.act *mailbox))
|
||||
::
|
||||
++ handle-delete
|
||||
|= act=chat-action
|
||||
|
@ -4,6 +4,7 @@
|
||||
/- *permission-store,
|
||||
*permission-hook,
|
||||
*group-store,
|
||||
*invite-store,
|
||||
*permission-group-hook,
|
||||
*chat-hook
|
||||
/+ *server, *chat-json, default-agent
|
||||
@ -186,53 +187,95 @@
|
||||
++ poke-json
|
||||
|= jon=json
|
||||
^- (list card)
|
||||
?. =(src.bol our.bol)
|
||||
~
|
||||
?> (team:title our.bol src.bol)
|
||||
(poke-chat-view-action (json-to-view-action jon))
|
||||
::
|
||||
++ poke-chat-view-action
|
||||
|= act=chat-view-action
|
||||
^- (list card)
|
||||
?. =(src.bol our.bol)
|
||||
~
|
||||
|^
|
||||
?> (team:title our.bol src.bol)
|
||||
?- -.act
|
||||
%create
|
||||
=/ pax [(scot %p our.bol) path.act]
|
||||
=/ group-read=path [%chat (weld pax /read)]
|
||||
=/ group-write=path [%chat (weld pax /write)]
|
||||
?^ (chat-scry path.act)
|
||||
~& %chat-already-exists
|
||||
~
|
||||
%- zing
|
||||
:~ :~ (group-poke [%bundle group-read])
|
||||
(group-poke [%bundle group-write])
|
||||
(group-poke [%add read.act group-read])
|
||||
(group-poke [%add write.act group-write])
|
||||
(chat-poke [%create our.bol path.act])
|
||||
(chat-hook-poke [%add-owned pax security.act allow-history.act])
|
||||
==
|
||||
(create-security [%chat pax] security.act)
|
||||
:~ (permission-hook-poke [%add-owned group-read group-read])
|
||||
(permission-hook-poke [%add-owned group-write group-read])
|
||||
==
|
||||
:~ (create-chat path.act security.act allow-history.act)
|
||||
(create-managed-group path.act security.act members.act)
|
||||
(create-security path.act security.act)
|
||||
~[(permission-hook-poke [%add-owned path.act path.act])]
|
||||
%+ turn ~(tap in members.act)
|
||||
|= =ship
|
||||
(send-invite-poke path.act ship)
|
||||
==
|
||||
::
|
||||
%delete
|
||||
=/ group-read [%chat (weld path.act /read)]
|
||||
=/ group-write [%chat (weld path.act /write)]
|
||||
:~ (chat-hook-poke [%remove path.act])
|
||||
(permission-hook-poke [%remove group-read])
|
||||
(permission-hook-poke [%remove group-write])
|
||||
(group-poke [%unbundle group-read])
|
||||
(group-poke [%unbundle group-write])
|
||||
(permission-hook-poke [%remove path.act])
|
||||
(chat-poke [%delete path.act])
|
||||
==
|
||||
::
|
||||
%join
|
||||
=/ group-read [%chat (scot %p ship.act) (weld path.act /read)]
|
||||
=/ group-write [%chat (scot %p ship.act) (weld path.act /write)]
|
||||
:~ (chat-hook-poke [%add-synced ship.act path.act ask-history.act])
|
||||
(permission-hook-poke [%add-synced ship.act group-write])
|
||||
(permission-hook-poke [%add-synced ship.act group-read])
|
||||
(permission-hook-poke [%add-synced ship.act path.act])
|
||||
==
|
||||
==
|
||||
::
|
||||
++ create-chat
|
||||
|= [=path security=rw-security history=?]
|
||||
^- (list card)
|
||||
:~ [(chat-poke [%create path])]
|
||||
[(chat-hook-poke [%add-owned path security history])]
|
||||
==
|
||||
::
|
||||
++ create-managed-group
|
||||
|= [=path security=rw-security ships=(set ship)]
|
||||
^- (list card)
|
||||
~& [path security ships]
|
||||
?. =(security %village)
|
||||
:: if blacklist, do nothing but create the group if it isn't there
|
||||
::
|
||||
?~((group-scry path) ~[(group-poke [%bundle path])] ~)
|
||||
:: if whitelist, check if group exists already. if yes, do nothing
|
||||
::
|
||||
?^ (group-scry path) ~
|
||||
:: if group does not exist, send contact-view %create
|
||||
::
|
||||
~[(contact-view-poke [%create path ships])]
|
||||
::
|
||||
++ create-security
|
||||
|= [pax=path sec=rw-security]
|
||||
^- (list card)
|
||||
?+ sec ~
|
||||
%channel
|
||||
~[(perm-group-hook-poke [%associate pax [[pax %black] ~ ~]])]
|
||||
::
|
||||
%village
|
||||
~[(perm-group-hook-poke [%associate pax [[pax %white] ~ ~]])]
|
||||
==
|
||||
::
|
||||
++ contact-view-poke
|
||||
|= act=[%create =path ships=(set ship)]
|
||||
^- card
|
||||
[%pass / %agent [our.bol %contact-view] %poke %contact-view-action !>(act)]
|
||||
::
|
||||
++ send-invite-poke
|
||||
|= [=path =ship]
|
||||
^- card
|
||||
=/ =invite
|
||||
:* our.bol %chat-hook
|
||||
path ship ''
|
||||
==
|
||||
=/ act=invite-action [%invite /chat (shaf %msg-uid eny.bol) invite]
|
||||
[%pass / %agent [our.bol %invite-hook] %poke %invite-action !>(act)]
|
||||
::
|
||||
++ chat-scry
|
||||
|= pax=path
|
||||
^- (unit mailbox)
|
||||
=. pax ;:(weld /=chat-store/(scot %da now.bol)/mailbox pax /noun)
|
||||
.^((unit mailbox) %gx pax)
|
||||
--
|
||||
::
|
||||
++ diff-chat-update
|
||||
|= upd=chat-update
|
||||
@ -284,30 +327,9 @@
|
||||
^- chat-configs
|
||||
.^(chat-configs %gx /=chat-store/(scot %da now.bol)/configs/noun)
|
||||
::
|
||||
++ create-security
|
||||
|= [pax=path sec=rw-security]
|
||||
^- (list card)
|
||||
=/ read (weld pax /read)
|
||||
=/ write (weld pax /write)
|
||||
?- sec
|
||||
%channel
|
||||
:~ (perm-group-hook-poke [%associate read [[read %black] ~ ~]])
|
||||
(perm-group-hook-poke [%associate write [[write %black] ~ ~]])
|
||||
==
|
||||
::
|
||||
%village
|
||||
:~ (perm-group-hook-poke [%associate read [[read %white] ~ ~]])
|
||||
(perm-group-hook-poke [%associate write [[write %white] ~ ~]])
|
||||
==
|
||||
::
|
||||
%journal
|
||||
:~ (perm-group-hook-poke [%associate read [[read %black] ~ ~]])
|
||||
(perm-group-hook-poke [%associate write [[write %white] ~ ~]])
|
||||
==
|
||||
::
|
||||
%mailbox
|
||||
:~ (perm-group-hook-poke [%associate read [[read %white] ~ ~]])
|
||||
(perm-group-hook-poke [%associate write [[write %black] ~ ~]])
|
||||
==
|
||||
==
|
||||
++ group-scry
|
||||
|= pax=path
|
||||
^- (unit group)
|
||||
.^((unit group) %gx ;:(weld /=group-store/(scot %da now.bol) pax /noun))
|
||||
::
|
||||
--
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -67,6 +67,9 @@
|
||||
?> (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
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -18,8 +18,7 @@
|
||||
retry-timers=(map target @dr)
|
||||
==
|
||||
::
|
||||
::TODO revert to annotations with new link-store subscription model
|
||||
+$ what-target ?(%local-pages %allotations)
|
||||
+$ what-target ?(%local-pages %annotations)
|
||||
+$ target
|
||||
$: what=what-target
|
||||
who=ship
|
||||
@ -68,9 +67,9 @@
|
||||
=^ cards state
|
||||
(take-groups-sign:do sign)
|
||||
[cards this]
|
||||
?: ?=([%links @ @ ^] wire)
|
||||
?: ?=([%links ?(%local-pages %annotations) @ ^] wire)
|
||||
=^ cards state
|
||||
(take-links-sign:do (wire-to-target t.wire) sign)
|
||||
(take-link-sign:do (wire-to-target t.wire) sign)
|
||||
[cards this]
|
||||
?: ?=([%forward ^] wire)
|
||||
=^ cards state
|
||||
@ -170,7 +169,7 @@
|
||||
$(whos t.whos)
|
||||
(end-link-subscriptions i.whos pax.upd)
|
||||
:+ (start-link-subscription %local-pages i.whos pax.upd)
|
||||
(start-link-subscription %allotations i.whos pax.upd)
|
||||
(start-link-subscription %annotations i.whos pax.upd)
|
||||
$(whos t.whos)
|
||||
==
|
||||
::
|
||||
@ -184,13 +183,16 @@
|
||||
%agent
|
||||
[who.target %link-proxy-hook]
|
||||
%watch
|
||||
[what where]:target
|
||||
?- what.target
|
||||
%local-pages [what where]:target
|
||||
%annotations [what %$ where]:target
|
||||
==
|
||||
==
|
||||
::
|
||||
++ end-link-subscriptions
|
||||
|= [who=ship where=path]
|
||||
^- (list card)
|
||||
|^ ~[(end %local-pages) (end %allotations)]
|
||||
|^ ~[(end %local-pages) (end %annotations)]
|
||||
::
|
||||
++ end
|
||||
|= what=what-target
|
||||
@ -203,7 +205,7 @@
|
||||
==
|
||||
--
|
||||
::
|
||||
++ take-links-sign
|
||||
++ take-link-sign
|
||||
|= [=target =sign:agent:gall]
|
||||
^- (quip card _state)
|
||||
?- -.sign
|
||||
@ -224,6 +226,10 @@
|
||||
=* mark p.cage.sign
|
||||
=* vase q.cage.sign
|
||||
?+ mark ~|([dap.bowl %unexpected-mark mark] !!)
|
||||
%link-initial
|
||||
%- handle-link-initial
|
||||
[who.target where.target !<(initial vase)]
|
||||
::
|
||||
%link-update
|
||||
%- handle-link-update
|
||||
[who.target where.target !<(update vase)]
|
||||
@ -268,6 +274,40 @@
|
||||
(snoc where.target %noun)
|
||||
==
|
||||
::
|
||||
++ do-link-action
|
||||
|= [=wire =action]
|
||||
^- card
|
||||
:* %pass
|
||||
wire
|
||||
%agent
|
||||
[our.bowl %link-store]
|
||||
%poke
|
||||
%link-action
|
||||
!>(action)
|
||||
==
|
||||
::
|
||||
++ handle-link-initial
|
||||
|= [who=ship where=path =initial]
|
||||
^- (quip card _state)
|
||||
?> =(src.bowl who)
|
||||
?+ -.initial ~|([dap.bowl %unexpected-initial -.initial] !!)
|
||||
%local-pages
|
||||
=/ =pages (~(got by pages.initial) where)
|
||||
(handle-link-update who where [%local-pages where pages])
|
||||
::
|
||||
%annotations
|
||||
=/ urls=(list [=url =notes])
|
||||
~(tap by (~(got by notes.initial) where))
|
||||
=| cards=(list card)
|
||||
|- ^- (quip card _state)
|
||||
?~ urls [cards state]
|
||||
=^ caz state
|
||||
^- (quip card _state)
|
||||
=, i.urls
|
||||
(handle-link-update who where [%annotations where url notes])
|
||||
$(urls t.urls, cards (weld cards caz))
|
||||
==
|
||||
::
|
||||
++ handle-link-update
|
||||
|= [who=ship where=path =update]
|
||||
^- (quip card _state)
|
||||
@ -277,28 +317,17 @@
|
||||
%local-pages
|
||||
%+ turn pages.update
|
||||
|= =page
|
||||
^- card
|
||||
:* %pass
|
||||
[%forward -.update (scot %p who) where]
|
||||
%agent
|
||||
[our.bowl %link-store]
|
||||
%poke
|
||||
%link-action
|
||||
!>([%hear where src.bowl page])
|
||||
==
|
||||
%+ do-link-action
|
||||
[%forward %local-page (scot %p who) where]
|
||||
[%hear where who page]
|
||||
::
|
||||
%annotations
|
||||
%+ turn notes.update
|
||||
|= =note
|
||||
^- card
|
||||
:* %pass
|
||||
[%forward -.update (scot %p who) where]
|
||||
%agent
|
||||
[our.bowl %link-store]
|
||||
%poke
|
||||
%link-action
|
||||
!>([%read where url.update src.bowl note])
|
||||
==
|
||||
%+ do-link-action
|
||||
[%forward %annotation (scot %p who) where]
|
||||
[%read where url.update who note]
|
||||
==
|
||||
::
|
||||
++ take-forward-sign
|
||||
|
@ -99,10 +99,17 @@
|
||||
++ permitted
|
||||
|= [who=ship =path]
|
||||
^- ?
|
||||
:: we only expose /local-pages and /annotations,
|
||||
:: and only to ships in the relevant group
|
||||
:: we only expose group-specific /local-pages and /annotations,
|
||||
:: and only to ships in the relevant group.
|
||||
:: (no url-specific annotations subscriptions, either.)
|
||||
::
|
||||
?. ?=([?(%local-pages %annotations %allotations) ^] path) |
|
||||
=/ target=(unit ^path)
|
||||
?: ?=([%local-pages ^] path)
|
||||
`t.path
|
||||
?: ?=([%annotations ~ ^] path)
|
||||
`t.t.path
|
||||
~
|
||||
?~ target |
|
||||
=; group
|
||||
?& ?=(^ group)
|
||||
(~(has in u.group) who)
|
||||
@ -112,7 +119,7 @@
|
||||
(scot %p our.bowl)
|
||||
%group-store
|
||||
(scot %da now.bowl)
|
||||
(snoc t.path %noun)
|
||||
(snoc u.target %noun)
|
||||
==
|
||||
::
|
||||
:: groups subscription
|
||||
@ -158,21 +165,22 @@
|
||||
::
|
||||
?: =(our.bowl i.whos)
|
||||
$(whos t.whos)
|
||||
%+ weld $(whos t.whos)
|
||||
:_ $(whos t.whos)
|
||||
::NOTE this depends kind of unfortunately on the fact that we only accept
|
||||
:: subscriptions to /local-pages/* paths. it'd be more correct if we
|
||||
:: subscriptions to /local-pages//* paths. it'd be more correct if we
|
||||
:: "just" looked at all paths in the map, and found the matching ones.
|
||||
::TODO what exactly did i mean by this?
|
||||
:~ (kick-proxy i.whos [%local-pages pax.upd])
|
||||
(kick-proxy i.whos [%annotations pax.upd])
|
||||
%+ kick-proxies i.whos
|
||||
:~ [%local-pages pax.upd]
|
||||
[%annotations '' pax.upd]
|
||||
==
|
||||
::
|
||||
:: proxy subscriptions
|
||||
::
|
||||
++ kick-proxy
|
||||
|= [who=ship =path]
|
||||
++ kick-proxies
|
||||
|= [who=ship paths=(list path)]
|
||||
^- card
|
||||
[%give %kick ~[path] `who]
|
||||
[%give %kick paths `who]
|
||||
::
|
||||
++ handle-proxy-sign
|
||||
|= [=wire =sign:agent:gall]
|
||||
@ -204,15 +212,14 @@
|
||||
++ initial-response
|
||||
|= =path
|
||||
^- card
|
||||
=; initial=update
|
||||
[%give %fact ~ %link-update !>(initial)]
|
||||
=; =initial
|
||||
[%give %fact ~ %link-initial !>(initial)]
|
||||
?+ path !!
|
||||
[%local-pages ^]
|
||||
[%local-pages path .^(pages %gx path)]
|
||||
[%local-pages .^((map ^path pages) %gx path)]
|
||||
::
|
||||
[%annotations @ ^]
|
||||
=+ (split-discussion-path t.path)
|
||||
[%annotations path url .^(notes %gx path)]
|
||||
[%annotations ~ ^]
|
||||
[%annotations .^((per-path-url notes) %gx '' t.t.path)]
|
||||
==
|
||||
::
|
||||
++ start-proxy
|
||||
|
@ -1,358 +0,0 @@
|
||||
:: link-server: accessing link-store via eyre
|
||||
::
|
||||
:: only accepts requests authenticated as the host ship.
|
||||
::
|
||||
:: GET requests:
|
||||
:: /~link/local-pages/[some-path].json?p=0
|
||||
:: our submissions on path, with optional pagination
|
||||
::
|
||||
:: POST requests:
|
||||
:: /~link/add/[some-path]
|
||||
:: send {title url} json, will save link at path
|
||||
::
|
||||
/+ *link, *server, default-agent, verb
|
||||
::
|
||||
|%
|
||||
+$ state-0
|
||||
$: %0
|
||||
~
|
||||
::NOTE this means we could get away with just producing cards everywhere,
|
||||
:: never producing new state outside of the agent interface core.
|
||||
:: we opt to keep ^-(quip card _state) in place for most logic arms
|
||||
:: because it doesn't cost much, results in unsurprising code, and
|
||||
:: makes adding any state in the future easier.
|
||||
==
|
||||
::
|
||||
+$ card card:agent:gall
|
||||
--
|
||||
::
|
||||
=| state-0
|
||||
=* state -
|
||||
::
|
||||
%+ verb |
|
||||
^- agent:gall
|
||||
=<
|
||||
|_ =bowl:gall
|
||||
+* this .
|
||||
do ~(. +> bowl)
|
||||
def ~(. (default-agent this %|) bowl)
|
||||
::
|
||||
++ on-init
|
||||
^- (quip card _this)
|
||||
:- [start-serving:do launch-tile:do ~]
|
||||
this
|
||||
::
|
||||
++ on-save !>(state)
|
||||
++ on-load
|
||||
|= old=vase
|
||||
^- (quip card _this)
|
||||
[~ this(state !<(state-0 old))]
|
||||
::
|
||||
++ on-watch
|
||||
|= =path
|
||||
^- (quip card _this)
|
||||
?: ?=([%http-response *] path)
|
||||
[~ this]
|
||||
?: =(/primary path)
|
||||
[~ this]
|
||||
(on-watch:def path)
|
||||
::
|
||||
++ on-poke
|
||||
|= [=mark =vase]
|
||||
^- (quip card _this)
|
||||
?. ?=(%handle-http-request mark)
|
||||
(on-poke:def mark vase)
|
||||
:_ this
|
||||
=+ !<([eyre-id=@ta =inbound-request:eyre] vase)
|
||||
(handle-http-request:do eyre-id inbound-request)
|
||||
::
|
||||
++ on-arvo
|
||||
|= [=wire =sign-arvo]
|
||||
^- (quip card _this)
|
||||
?. ?=(%bound +<.sign-arvo)
|
||||
(on-arvo:def wire sign-arvo)
|
||||
[~ this]
|
||||
::
|
||||
++ on-agent
|
||||
|= [=wire =sign:agent:gall]
|
||||
^- (quip card _this)
|
||||
?. ?=(%poke-ack -.sign)
|
||||
(on-agent:def wire sign)
|
||||
?~ p.sign [~ this]
|
||||
=/ =tank
|
||||
leaf+"{(trip dap.bowl)} failed writing to %link-store"
|
||||
%- (slog tank u.p.sign)
|
||||
[~ this]
|
||||
::
|
||||
++ on-peek on-peek:def
|
||||
++ on-leave on-leave:def
|
||||
++ on-fail on-fail:def
|
||||
--
|
||||
::
|
||||
|_ =bowl:gall
|
||||
::
|
||||
++ start-serving
|
||||
^- card
|
||||
[%pass / %arvo %e %connect [~ /'~link'] dap.bowl]
|
||||
::
|
||||
++ launch-tile
|
||||
^- card
|
||||
(launch-poke [%link-server-hook /primary '/~link/js/tile.js'])
|
||||
::
|
||||
::
|
||||
++ launch-poke
|
||||
|= act=[@tas path @t]
|
||||
^- card
|
||||
[%pass / %agent [our.bowl %launch] %poke %launch-action !>(act)]
|
||||
::
|
||||
++ do-action
|
||||
|= =action
|
||||
^- card
|
||||
[%pass / %agent [our.bowl %link-store] %poke %link-action !>(action)]
|
||||
::
|
||||
++ do-save
|
||||
|= [=path title=@t =url]
|
||||
^- card
|
||||
(do-action %save path title url)
|
||||
::
|
||||
++ do-note
|
||||
|= [=path =url udon=@t]
|
||||
^- card
|
||||
(do-action %note path url udon)
|
||||
::
|
||||
++ handle-http-request
|
||||
|= [eyre-id=@ta =inbound-request:eyre]
|
||||
^- (list card)
|
||||
::NOTE we don't use +require-authorization because it's too restrictive
|
||||
:: on the flow we want here.
|
||||
::
|
||||
?. ?& authenticated.inbound-request
|
||||
=(src.bowl our.bowl)
|
||||
==
|
||||
(give-simple-payload:app eyre-id [[403 ~] ~])
|
||||
:: request-line: parsed url + params
|
||||
::
|
||||
=/ =request-line
|
||||
%- parse-request-line
|
||||
url.request.inbound-request
|
||||
=* req-head header-list.request.inbound-request
|
||||
=; [cards=(list card) =simple-payload:http]
|
||||
%+ weld cards
|
||||
(give-simple-payload:app eyre-id simple-payload)
|
||||
?+ method.request.inbound-request [~ not-found:gen]
|
||||
%'OPTIONS'
|
||||
[~ (include-cors-headers req-head [[200 ~] ~])]
|
||||
::
|
||||
%'GET'
|
||||
[~ (handle-get req-head request-line)]
|
||||
::
|
||||
%'POST'
|
||||
(handle-post req-head request-line body.request.inbound-request)
|
||||
==
|
||||
::
|
||||
++ handle-post
|
||||
|= [request-headers=header-list:http =request-line body=(unit octs)]
|
||||
^- [(list card) simple-payload:http]
|
||||
=; [success=? cards=(list card)]
|
||||
:- cards
|
||||
%+ include-cors-headers
|
||||
request-headers
|
||||
::TODO it would be more correct to wait for the %poke-ack instead of
|
||||
:: sending this response right away... but link-store pokes can't
|
||||
:: actually fail right now, so it's fine.
|
||||
[[?:(success 200 400) ~] `*octs]
|
||||
?~ body [| ~]
|
||||
=/ jon=(unit json) (de-json:html q.u.body)
|
||||
?~ jon [| ~]
|
||||
?+ request-line [| ~]
|
||||
[[~ [%'~link' %save ~]] ~]
|
||||
^- [? (list card)]
|
||||
=/ page=(unit [=path title=@t =url])
|
||||
%. u.jon
|
||||
=, dejs-soft:format
|
||||
=+ (su ;~(pfix net (more net urs:ab)))
|
||||
(ot path+- title+so url+so ~)
|
||||
?~ page [| ~]
|
||||
[& [(do-save u.page) ~]]
|
||||
::
|
||||
[[~ [%'~link' %note ~]] ~]
|
||||
^- [? (list card)]
|
||||
=/ note=(unit [=path =url udon=@t])
|
||||
%. u.jon
|
||||
=, dejs-soft:format
|
||||
=+ (su ;~(pfix net (more net urs:ab)))
|
||||
(ot path+- url+so udon+so ~)
|
||||
?~ note [| ~]
|
||||
[& [(do-note u.note) ~]]
|
||||
==
|
||||
::
|
||||
++ handle-get
|
||||
|= [request-headers=header-list:http =request-line]
|
||||
:: if we request base path, return index.html
|
||||
::
|
||||
?: ?=([[~ [%'~link' ~]] *] request-line)
|
||||
$(request-line request-line(ext `%html, site [%'~link' /index]))
|
||||
%+ include-cors-headers
|
||||
request-headers
|
||||
^- simple-payload:http
|
||||
:: args: map of params
|
||||
:: p: pagination index
|
||||
::
|
||||
=/ args
|
||||
%- ~(gas by *(map @t @t))
|
||||
args.request-line
|
||||
=/ p=(unit @ud)
|
||||
%+ biff (~(get by args) 'p')
|
||||
(curr rush dim:ag)
|
||||
?+ request-line
|
||||
:: for the default case, try to load file from clay
|
||||
::
|
||||
?~ ext.request-line
|
||||
:: for extension-less requests, always just serve the index.html.
|
||||
:: that way the js can load and figure out how to deal with that route.
|
||||
::
|
||||
$(request-line [[`%html ~[%'~link' 'index']] args.request-line])
|
||||
=/ file=(unit octs)
|
||||
?. ?=([%'~link' *] site.request-line) ~
|
||||
(get-file-at /app/link [t.site u.ext]:request-line)
|
||||
?~ file not-found:gen
|
||||
?+ u.ext.request-line not-found:gen
|
||||
%html (html-response:gen u.file)
|
||||
%js (js-response:gen u.file)
|
||||
%css (css-response:gen u.file)
|
||||
%png (png-response:gen u.file)
|
||||
==
|
||||
:: submissions by recency as json, including comment counts
|
||||
::
|
||||
[[[~ %json] [%'~link' %submissions ^]] *]
|
||||
%- json-response:gen
|
||||
%- json-to-octs ::TODO include in +json-response:gen
|
||||
%+ page-to-json
|
||||
(get-submissions t.t.site.request-line p)
|
||||
|= [=submission comments=@ud]
|
||||
^- json
|
||||
=+ s=(submission:en-json submission)
|
||||
?> ?=([%o *] s)
|
||||
o+(~(put by p.s) 'commentCount' (numb:enjs:format comments))
|
||||
:: local links by recency as json
|
||||
::
|
||||
[[[~ %json] [%'~link' %local-pages ^]] *]
|
||||
%- json-response:gen
|
||||
%- json-to-octs ::TODO include in +json-response:gen
|
||||
%+ page-to-json
|
||||
(get-local-pages t.t.site.request-line p)
|
||||
page:en-json
|
||||
:: comments by recency as json
|
||||
::
|
||||
[[[~ %json] [%'~link' %discussions @ ^]] *]
|
||||
%- json-response:gen
|
||||
%- json-to-octs ::TODO include in +json-response:gen
|
||||
%+ page-to-json
|
||||
(get-discussions t.t.site.request-line p)
|
||||
comment:en-json
|
||||
==
|
||||
::
|
||||
++ include-cors-headers
|
||||
|= [request-headers=header-list:http =simple-payload:http]
|
||||
^+ simple-payload
|
||||
=* out-heads headers.response-header.simple-payload
|
||||
=; =header-list:http
|
||||
|-
|
||||
?~ header-list simple-payload
|
||||
=* new-head i.header-list
|
||||
=. out-heads
|
||||
(set-header:http key.new-head value.new-head out-heads)
|
||||
$(header-list t.header-list)
|
||||
=/ origin=@t
|
||||
=/ headers=(map @t @t)
|
||||
(~(gas by *(map @t @t)) request-headers)
|
||||
(~(gut by headers) 'origin' '*')
|
||||
:~ 'Access-Control-Allow-Origin'^origin
|
||||
'Access-Control-Allow-Credentials'^'true'
|
||||
'Access-Control-Request-Method'^'OPTIONS, GET, POST'
|
||||
'Access-Control-Allow-Methods'^'OPTIONS, GET, POST'
|
||||
'Access-Control-Allow-Headers'^'content-type'
|
||||
==
|
||||
::
|
||||
++ page-size 25
|
||||
++ get-paginated
|
||||
|* [l=(list) p=(unit @ud)]
|
||||
^- [total=@ud pages=@ud page=_l]
|
||||
:+ (lent l)
|
||||
+((div (lent l) page-size))
|
||||
?~ p l
|
||||
%+ scag page-size
|
||||
%+ slag (mul u.p page-size)
|
||||
l
|
||||
::
|
||||
++ page-to-json
|
||||
=, enjs:format
|
||||
|* $: [total-items=@ud total-pages=@ud page=(list)]
|
||||
item-to-json=$-(* json)
|
||||
==
|
||||
^- json
|
||||
%- pairs
|
||||
:~ 'total-items'^(numb total-items)
|
||||
'total-pages'^(numb total-pages)
|
||||
'page'^a+(turn page item-to-json)
|
||||
==
|
||||
::
|
||||
++ get-submissions
|
||||
|= [=path p=(unit @ud)]
|
||||
^- [@ud @ud (list [submission comments=@ud])]
|
||||
=- (get-paginated - p)
|
||||
%+ turn
|
||||
%+ scry-for submissions
|
||||
[%submissions path]
|
||||
|= =submission
|
||||
:- submission
|
||||
%- lent
|
||||
%+ scry-for comments
|
||||
:- %discussions
|
||||
%+ snoc path
|
||||
%- crip
|
||||
(en-base64:mimes:html url.submission)
|
||||
::
|
||||
++ get-local-pages
|
||||
|= [=path p=(unit @ud)]
|
||||
^- [@ud @ud pages]
|
||||
=- (get-paginated - p)
|
||||
%+ scry-for pages
|
||||
[%local-pages path]
|
||||
::
|
||||
++ get-discussions
|
||||
|= [=path p=(unit @ud)]
|
||||
^- [@ud @ud comments]
|
||||
=- (get-paginated - p)
|
||||
%+ scry-for comments
|
||||
[%discussions path]
|
||||
::
|
||||
++ get-file-at
|
||||
|= [base=path file=path ext=@ta]
|
||||
^- (unit octs)
|
||||
:: only expose html, css and js files for now
|
||||
::
|
||||
?. ?=(?(%html %css %js %png) ext)
|
||||
~
|
||||
=/ =path
|
||||
:* (scot %p our.bowl)
|
||||
q.byk.bowl
|
||||
(scot %da now.bowl)
|
||||
(snoc (weld base file) ext)
|
||||
==
|
||||
?. .^(? %cu path)
|
||||
~
|
||||
%- some
|
||||
%- as-octs:mimes:html
|
||||
.^(@ %cx path)
|
||||
::
|
||||
++ scry-for
|
||||
|* [=mold =path]
|
||||
.^ mold
|
||||
%gx
|
||||
(scot %p our.bowl)
|
||||
%link-store
|
||||
(scot %da now.bowl)
|
||||
(snoc `^path`path %noun)
|
||||
==
|
||||
--
|
@ -5,20 +5,33 @@
|
||||
:: links, arbitrary paths are probably fair game, but could trip up
|
||||
:: primitive ui implementations.
|
||||
::
|
||||
:: urls in paths are expected to be encoded using +wood, for @ta sanity.
|
||||
:: use /lib/link's +build-discussion-path.
|
||||
::
|
||||
:: see link-listen-hook to see what's synced in, and similarly
|
||||
:: see link-proxy-hook to see what's exposed.
|
||||
::
|
||||
:: scry and subscription paths:
|
||||
::
|
||||
:: urls
|
||||
:: /local-pages/[some-group] all pages we saved by recency
|
||||
:: /submissions/[some-group] all submissions by recency
|
||||
:: comments
|
||||
:: /allotations/[some-group] TMP all our comments in group
|
||||
:: /annotations/[some-group]/[b64(url)] all our comments on url by recency
|
||||
:: /discussions/[some-group]/[b64(url)] all known comments on url by recency
|
||||
:: (map path pages)
|
||||
:: /local-pages our saved pages
|
||||
:: /local-pages/some-path our saved pages on path
|
||||
::
|
||||
::TODO continue work from m/uplink-broad branch!
|
||||
:: (map path submissions)
|
||||
:: /submissions all submissions we've seen
|
||||
:: /submissions/some-path all submissions we've seen on path
|
||||
::
|
||||
:: (map path (map url notes))
|
||||
:: /annotations our comments
|
||||
:: /annotations/wood-url our comments on url
|
||||
:: /annotations/wood-url/some-path our comments on url on path
|
||||
:: /annotations//some-path our comments on path
|
||||
::
|
||||
:: (map path (map url comments))
|
||||
:: /discussions all comments
|
||||
:: /discussions/wood-url all comments on url
|
||||
:: /discussions/wood-url/some-path all comments on url on path
|
||||
:: /discussions//some-path all comments on path
|
||||
::
|
||||
/+ *link, default-agent, verb, dbug
|
||||
::
|
||||
@ -27,7 +40,7 @@
|
||||
$: %0
|
||||
by-group=(map path links)
|
||||
by-site=(map site (list [path submission]))
|
||||
discussions=(map path (map url discussion))
|
||||
discussions=(per-path-url discussion)
|
||||
==
|
||||
::
|
||||
+$ links
|
||||
@ -81,25 +94,48 @@
|
||||
?+ path (on-peek:def path)
|
||||
[%y ?(%local-pages %submissions) ~]
|
||||
``noun+!>(~(key by by-group))
|
||||
::
|
||||
[%x %local-pages ^]
|
||||
::
|
||||
[%x %local-pages *]
|
||||
``noun+!>((get-local-pages:do t.t.path))
|
||||
::
|
||||
[%x %submissions ^]
|
||||
::
|
||||
[%x %submissions *]
|
||||
``noun+!>((get-submissions:do t.t.path))
|
||||
::
|
||||
[%y ?(%annotations %discussions) *]
|
||||
=/ [spath=^path surl=url]
|
||||
(break-discussion-path t.t.path)
|
||||
=- ``noun+!>(-)
|
||||
::
|
||||
[%y ?(%annotations %discussions) ~]
|
||||
``noun+!>(~(key by discussions))
|
||||
?: =(~ surl)
|
||||
:: no url, provide urls that have comments
|
||||
::
|
||||
^- (set url)
|
||||
?~ spath
|
||||
:: no path, find urls accross all paths
|
||||
::
|
||||
%- ~(rep by discussions)
|
||||
|= [[* discussions=(map url discussion)] urls=(set url)]
|
||||
%- ~(uni in urls)
|
||||
~(key by discussions)
|
||||
:: specified path, find urls for that specific path
|
||||
::
|
||||
%~ key by
|
||||
(~(gut by discussions) spath *(map url *))
|
||||
:: specified url and path, nothing to list here
|
||||
::
|
||||
[%y ?(%annotations %discussions) ^]
|
||||
=/ urls (~(get by discussions) t.t.path)
|
||||
?~ urls ~
|
||||
``noun+!>(~(key by u.urls))
|
||||
?^ spath !!
|
||||
:: no path, find paths with comments for this url
|
||||
::
|
||||
[%x %annotations @ ^]
|
||||
^- (set ^path)
|
||||
%- ~(rep by discussions)
|
||||
|= [[=^path urls=(map url discussion)] paths=(set ^path)]
|
||||
?. (~(has by urls) surl) paths
|
||||
(~(put in paths) path)
|
||||
::
|
||||
[%x %annotations *]
|
||||
``noun+!>((get-annotations:do t.t.path))
|
||||
::
|
||||
[%x %discussions @ ^]
|
||||
::
|
||||
[%x %discussions *]
|
||||
``noun+!>((get-discussions:do t.t.path))
|
||||
==
|
||||
::
|
||||
@ -109,29 +145,25 @@
|
||||
?> (team:title [our src]:bowl) ::TODO /lib/store
|
||||
:_ this
|
||||
|^ ?+ path (on-watch:def path)
|
||||
[%local-pages ^]
|
||||
%+ give %link-update
|
||||
[%local-pages t.path (get-local-pages:do t.path)]
|
||||
[%local-pages *]
|
||||
%+ give %link-initial
|
||||
^- initial
|
||||
[%local-pages (get-local-pages:do t.path)]
|
||||
::
|
||||
[%submissions ^]
|
||||
%+ give %link-update
|
||||
[%submissions t.path (get-submissions:do t.path)]
|
||||
[%submissions *]
|
||||
%+ give %link-initial
|
||||
^- initial
|
||||
[%submissions (get-submissions:do t.path)]
|
||||
::
|
||||
[%allotations ^]
|
||||
%+ turn
|
||||
%~ tap by
|
||||
(~(gut by discussions) t.path *(map url discussion))
|
||||
|= [=url =discussion]
|
||||
%+ give-single %link-update
|
||||
[%annotations t.path url ours.discussion]
|
||||
[%annotations *]
|
||||
%+ give %link-initial
|
||||
^- initial
|
||||
[%annotations (get-annotations:do t.path)]
|
||||
::
|
||||
[%annotations @ ^]
|
||||
%+ give %link-update
|
||||
[%annotations t.path (get-annotations:do t.path)]
|
||||
::
|
||||
[%discussions @ ^]
|
||||
%+ give %link-update
|
||||
[%discussions t.path (get-discussions:do t.path)]
|
||||
[%discussions *]
|
||||
%+ give %link-initial
|
||||
^- initial
|
||||
[%discussions (get-discussions:do t.path)]
|
||||
==
|
||||
::
|
||||
++ give
|
||||
@ -186,7 +218,9 @@
|
||||
:_ state
|
||||
:_ cards
|
||||
:+ %give %fact
|
||||
:+ [%local-pages path]~
|
||||
:+ :~ /local-pages
|
||||
[%local-pages path]
|
||||
==
|
||||
%link-update
|
||||
!>([%local-pages path [page]~])
|
||||
:: +note-note: save a note for a url
|
||||
@ -210,13 +244,15 @@
|
||||
::
|
||||
:_ state
|
||||
^- (list card)
|
||||
=/ fact
|
||||
:- %link-update
|
||||
!>([%annotations path url [note]~])
|
||||
:* [%give %fact [%annotations (snoc path url)]~ fact]
|
||||
[%give %fact [%allotations path]~ fact]
|
||||
cards
|
||||
==
|
||||
:_ cards
|
||||
:+ %give %fact
|
||||
:+ :~ /annotations
|
||||
[%annotations %$ path]
|
||||
[%annotations (build-discussion-path url)]
|
||||
[%annotations (build-discussion-path path url)]
|
||||
==
|
||||
%link-update
|
||||
!>([%annotations path url [note]~])
|
||||
:: +hear-submission: record page someone else saved
|
||||
::
|
||||
++ hear-submission
|
||||
@ -237,7 +273,9 @@
|
||||
:_ state
|
||||
:_ ~
|
||||
:+ %give %fact
|
||||
:+ [%submissions path]~
|
||||
:+ :~ /submissions
|
||||
[%submissions path]
|
||||
==
|
||||
%link-update
|
||||
!>([%submissions path [submission]~])
|
||||
:: +read-comment: record a comment someone else made
|
||||
@ -255,42 +293,102 @@
|
||||
:: send updates to subscribers
|
||||
::
|
||||
:_ state
|
||||
^- (list card)
|
||||
=/ fact
|
||||
:- %link-update
|
||||
!>([%discussions path url [comment]~])
|
||||
:~ [%give %fact [%discussions (snoc path url)]~ fact]
|
||||
[%give %fact [%discussions path]~ fact]
|
||||
==
|
||||
:_ ~
|
||||
:+ %give %fact
|
||||
:+ :~ /discussions
|
||||
[%discussions '' path]
|
||||
[%discussions (build-discussion-path url)]
|
||||
[%discussions (build-discussion-path path url)]
|
||||
==
|
||||
%link-update
|
||||
!>([%discussions path url [comment]~])
|
||||
::
|
||||
:: reading
|
||||
::
|
||||
++ get-local-pages
|
||||
|= =path
|
||||
^- pages
|
||||
^- (map ^path pages)
|
||||
?~ path
|
||||
:: all paths
|
||||
::
|
||||
%- ~(run by by-group)
|
||||
|=(links ours)
|
||||
:: specific path
|
||||
::
|
||||
%+ ~(put by *(map ^path pages)) path
|
||||
ours:(~(gut by by-group) path *links)
|
||||
::
|
||||
++ get-submissions
|
||||
|= =path
|
||||
^- submissions
|
||||
^- (map ^path submissions)
|
||||
?~ path
|
||||
:: all paths
|
||||
::
|
||||
%- ~(run by by-group)
|
||||
|=(links submissions)
|
||||
:: specific path
|
||||
::
|
||||
%+ ~(put by *(map ^path submissions)) path
|
||||
submissions:(~(gut by by-group) path *links)
|
||||
::
|
||||
::
|
||||
++ get-annotations
|
||||
|= =path
|
||||
^- notes
|
||||
ours:(get-discussion path)
|
||||
^- (per-path-url notes)
|
||||
=/ args=[=^path =url]
|
||||
(break-discussion-path path)
|
||||
|^ ?~ path
|
||||
:: all paths
|
||||
::
|
||||
(~(run by discussions) get-ours)
|
||||
:: specific path
|
||||
::
|
||||
%+ ~(put by *(per-path-url notes)) path.args
|
||||
%- get-ours
|
||||
%+ ~(gut by discussions) path.args
|
||||
*(map url discussion)
|
||||
::
|
||||
++ get-ours
|
||||
|= m=(map url discussion)
|
||||
^- (map url notes)
|
||||
?: =(~ url.args)
|
||||
:: all urls
|
||||
::
|
||||
%- ~(run by m)
|
||||
|=(discussion ours)
|
||||
:: specific url
|
||||
::
|
||||
%+ ~(put by *(map url notes)) url.args
|
||||
ours:(~(gut by m) url.args *discussion)
|
||||
--
|
||||
::
|
||||
++ get-discussions
|
||||
|= =path
|
||||
^- comments
|
||||
comments:(get-discussion path)
|
||||
::
|
||||
++ get-discussion
|
||||
|= =path
|
||||
^- discussion
|
||||
=/ [=^path =url]
|
||||
(split-discussion-path path)
|
||||
=- (~(gut by -) url *discussion)
|
||||
%+ ~(gut by discussions) path
|
||||
*(map ^url discussion)
|
||||
^- (per-path-url comments)
|
||||
=/ args=[=^path =url]
|
||||
(break-discussion-path path)
|
||||
|^ ?~ path
|
||||
:: all paths
|
||||
::
|
||||
(~(run by discussions) get-comments)
|
||||
:: specific path
|
||||
::
|
||||
%+ ~(put by *(per-path-url comments)) path.args
|
||||
%- get-comments
|
||||
%+ ~(gut by discussions) path.args
|
||||
*(map url discussion)
|
||||
::
|
||||
++ get-comments
|
||||
|= m=(map url discussion)
|
||||
^- (map url comments)
|
||||
?: =(~ url.args)
|
||||
:: all urls
|
||||
::
|
||||
%- ~(run by m)
|
||||
|=(discussion comments)
|
||||
:: specific url
|
||||
::
|
||||
%+ ~(put by *(map url comments)) url.args
|
||||
comments:(~(gut by m) url.args *discussion)
|
||||
--
|
||||
--
|
||||
|
309
pkg/arvo/app/link-view.hoon
Normal file
309
pkg/arvo/app/link-view.hoon
Normal file
@ -0,0 +1,309 @@
|
||||
:: link-view: frontend endpoints
|
||||
::
|
||||
:: endpoints, mapping onto link-store's paths. p is for page as in pagination.
|
||||
:: updates only work for page 0.
|
||||
:: as with link-store, urls are expected to use +wood encoding.
|
||||
::
|
||||
:: /json/[p]/submissions pages for all groups
|
||||
:: /json/[p]/submissions/[some-group] page for one group
|
||||
:: /json/[p]/discussions/[wood-url]/[some-group] page for url in group
|
||||
::
|
||||
/+ *link, *server, default-agent, verb
|
||||
::
|
||||
|%
|
||||
+$ state-0
|
||||
$: %0
|
||||
~
|
||||
==
|
||||
::
|
||||
+$ card card:agent:gall
|
||||
--
|
||||
::
|
||||
=| state-0
|
||||
=* state -
|
||||
::
|
||||
%+ verb |
|
||||
^- agent:gall
|
||||
=<
|
||||
|_ =bowl:gall
|
||||
+* this .
|
||||
do ~(. +> bowl)
|
||||
def ~(. (default-agent this %|) bowl)
|
||||
::
|
||||
++ on-init
|
||||
^- (quip card _this)
|
||||
:_ this
|
||||
:~ [%pass /connect %arvo %e %connect [~ /'~link'] dap.bowl]
|
||||
[%pass /submissions %agent [our.bowl %link-store] %watch /submissions]
|
||||
[%pass /discussions %agent [our.bowl %link-store] %watch /discussions]
|
||||
::
|
||||
=+ [%link-server-hook /tile '/~link/js/tile.js']
|
||||
[%pass /launch %agent [our.bowl %launch] %poke %launch-action !>(-)]
|
||||
==
|
||||
::
|
||||
++ on-save !>(state)
|
||||
::
|
||||
++ on-load
|
||||
|= old=vase
|
||||
^- (quip card _this)
|
||||
[~ this(state !<(state-0 old))]
|
||||
::
|
||||
++ on-poke
|
||||
|= [=mark =vase]
|
||||
^- (quip card _this)
|
||||
?> (team:title our.bowl src.bowl)
|
||||
:_ this
|
||||
?+ mark (on-poke:def mark vase)
|
||||
%handle-http-request
|
||||
=+ !<([eyre-id=@ta =inbound-request:eyre] vase)
|
||||
%+ give-simple-payload:app eyre-id
|
||||
%+ require-authorization:app inbound-request
|
||||
handle-http-request:do
|
||||
::
|
||||
%link-action
|
||||
[(handle-action:do !<(action vase)) ~]
|
||||
==
|
||||
::
|
||||
++ on-watch
|
||||
|= =path
|
||||
^- (quip card _this)
|
||||
?: ?=([%http-response *] path)
|
||||
[~ this]
|
||||
?. ?=([%json @ @ *] path)
|
||||
(on-watch:def path)
|
||||
=/ p=@ud (slav %ud i.t.path)
|
||||
?+ t.t.path (on-watch:def path)
|
||||
[%tile ~]
|
||||
[~ this]
|
||||
::
|
||||
[%submissions ~]
|
||||
:_ this
|
||||
(give-initial-submissions:do p ~)
|
||||
::
|
||||
[%submissions ^]
|
||||
:_ this
|
||||
(give-initial-submissions:do p t.t.t.path)
|
||||
::
|
||||
[%discussions @ ^]
|
||||
:_ this
|
||||
(give-initial-discussions:do p (break-discussion-path t.t.t.path))
|
||||
==
|
||||
::
|
||||
++ on-agent
|
||||
|= [=wire =sign:agent:gall]
|
||||
^- (quip card _this)
|
||||
?+ -.sign (on-agent:def wire sign)
|
||||
%kick
|
||||
:_ this
|
||||
[%pass wire %agent [our.bowl %link-store] %watch wire]~
|
||||
::
|
||||
%fact
|
||||
=* mark p.cage.sign
|
||||
=* vase q.cage.sign
|
||||
?+ mark (on-agent:def wire sign)
|
||||
%link-initial [~ this]
|
||||
%link-update [~[(send-update:do !<(update vase))] this]
|
||||
==
|
||||
==
|
||||
::
|
||||
++ on-arvo
|
||||
|= [=wire =sign-arvo]
|
||||
^- (quip card _this)
|
||||
?. ?=([%e %bound *] sign-arvo)
|
||||
(on-arvo:def wire sign-arvo)
|
||||
~? !accepted.sign-arvo
|
||||
[dap.bowl "bind rejected!" binding.sign-arvo]
|
||||
[~ this]
|
||||
::
|
||||
++ on-peek on-peek:def
|
||||
++ on-leave on-leave:def
|
||||
++ on-fail on-fail:def
|
||||
--
|
||||
::
|
||||
|_ =bowl:gall
|
||||
++ page-size 25
|
||||
++ get-paginated
|
||||
|* [p=(unit @ud) l=(list)]
|
||||
^- [total=@ud pages=@ud page=_l]
|
||||
:+ (lent l)
|
||||
%+ add (div (lent l) page-size)
|
||||
(min 1 (mod (lent l) page-size))
|
||||
?~ p l
|
||||
%+ scag page-size
|
||||
%+ slag (mul u.p page-size)
|
||||
l
|
||||
::
|
||||
++ page-to-json
|
||||
=, enjs:format
|
||||
|* $: page-number=@ud
|
||||
[total-items=@ud total-pages=@ud page=(list)]
|
||||
item-to-json=$-(* json)
|
||||
==
|
||||
^- json
|
||||
%- pairs
|
||||
:~ 'totalItems'^(numb total-items)
|
||||
'totalPages'^(numb total-pages)
|
||||
'pageNumber'^(numb page-number)
|
||||
'page'^a+(turn page item-to-json)
|
||||
==
|
||||
::
|
||||
++ handle-http-request
|
||||
|= =inbound-request:eyre
|
||||
^- simple-payload:http
|
||||
?. =(src.bowl our.bowl)
|
||||
[[403 ~] ~]
|
||||
:: request-line: parsed url + params
|
||||
::
|
||||
=/ =request-line
|
||||
%- parse-request-line
|
||||
url.request.inbound-request
|
||||
=* req-head header-list.request.inbound-request
|
||||
?+ method.request.inbound-request not-found:gen
|
||||
%'GET'
|
||||
(handle-get req-head request-line)
|
||||
==
|
||||
::
|
||||
++ handle-get
|
||||
|= [request-headers=header-list:http =request-line]
|
||||
^- simple-payload:http
|
||||
:: try to load file from clay
|
||||
::
|
||||
?~ ext.request-line
|
||||
:: for extension-less requests, always just serve the index.html.
|
||||
:: that way the js can load and figure out how to deal with that route.
|
||||
::
|
||||
$(request-line [[`%html ~[%'~link' 'index']] args.request-line])
|
||||
=/ file=(unit octs)
|
||||
?. ?=([%'~link' *] site.request-line) ~
|
||||
(get-file-at /app/link [t.site u.ext]:request-line)
|
||||
?~ file not-found:gen
|
||||
?+ u.ext.request-line not-found:gen
|
||||
%html (html-response:gen u.file)
|
||||
%js (js-response:gen u.file)
|
||||
%css (css-response:gen u.file)
|
||||
%png (png-response:gen u.file)
|
||||
==
|
||||
::
|
||||
++ get-file-at
|
||||
|= [base=path file=path ext=@ta]
|
||||
^- (unit octs)
|
||||
:: only expose html, css and js files for now
|
||||
::
|
||||
?. ?=(?(%html %css %js %png) ext)
|
||||
~
|
||||
=/ =path
|
||||
:* (scot %p our.bowl)
|
||||
q.byk.bowl
|
||||
(scot %da now.bowl)
|
||||
(snoc (weld base file) ext)
|
||||
==
|
||||
?. .^(? %cu path)
|
||||
~
|
||||
%- some
|
||||
%- as-octs:mimes:html
|
||||
.^(@ %cx path)
|
||||
::
|
||||
++ handle-action
|
||||
|= =action
|
||||
^- card
|
||||
[%pass /action %agent [our.bowl %link-store] %poke %link-action !>(action)]
|
||||
:: +give-initial-submissions: page of submissions on path
|
||||
::
|
||||
:: for the / path, give page for every path
|
||||
::
|
||||
:: result is in the shape of: {
|
||||
:: "/some/path": {
|
||||
:: totalItems: 1,
|
||||
:: totalPages: 1,
|
||||
:: pageNumber: 0,
|
||||
:: page: [
|
||||
:: { commentCount: 1, ...restOfTheSubmission }
|
||||
:: ]
|
||||
:: },
|
||||
:: "/maybe/more": { etc }
|
||||
:: }
|
||||
::
|
||||
++ give-initial-submissions
|
||||
|= [p=@ud =path]
|
||||
^- (list card)
|
||||
:_ ?: =(0 p) ~
|
||||
[%give %kick ~ ~]~
|
||||
=; =json
|
||||
[%give %fact ~ %json !>(json)]
|
||||
%+ frond:enjs:format 'initial-submissions'
|
||||
%- pairs:enjs:format
|
||||
%+ turn
|
||||
%~ tap by
|
||||
%+ scry-for (map ^path submissions)
|
||||
[%submissions path]
|
||||
|= [=^path =submissions]
|
||||
^- [@t json]
|
||||
:- (spat path)
|
||||
%^ page-to-json p
|
||||
%+ get-paginated `p
|
||||
submissions
|
||||
|= =submission
|
||||
^- json
|
||||
=/ =json (submission:en-json submission)
|
||||
?> ?=([%o *] json)
|
||||
=; comment-count=@ud
|
||||
o+(~(put by p.json) 'commentCount' (numb:enjs:format comment-count))
|
||||
:: get comment count
|
||||
::
|
||||
%- lent
|
||||
~| [path url.submission]
|
||||
^- comments
|
||||
=- (~(got by (~(got by -) path)) url.submission)
|
||||
%+ scry-for (per-path-url comments)
|
||||
:- %discussions
|
||||
(build-discussion-path path url.submission)
|
||||
::
|
||||
++ give-initial-discussions
|
||||
|= [p=@ud =path =url]
|
||||
^- (list card)
|
||||
:_ ?: =(0 p) ~
|
||||
[%give %kick ~ ~]~
|
||||
=; =json
|
||||
[%give %fact ~ %json !>(json)]
|
||||
%+ frond:enjs:format 'initial-discussions'
|
||||
%^ page-to-json p
|
||||
%+ get-paginated `p
|
||||
=- (~(got by (~(got by -) path)) url)
|
||||
%+ scry-for (per-path-url comments)
|
||||
[%discussions (build-discussion-path path url)]
|
||||
comment:en-json
|
||||
::
|
||||
++ send-update
|
||||
|= =update
|
||||
^- card
|
||||
?+ -.update ~|([dap.bowl %unexpected-update -.update] !!)
|
||||
%submissions
|
||||
%+ give-json
|
||||
(update:en-json update)
|
||||
:~ /json/0/submissions
|
||||
(weld /json/0/submissions path.update)
|
||||
==
|
||||
::
|
||||
%discussions
|
||||
%+ give-json
|
||||
(update:en-json update)
|
||||
:_ ~
|
||||
%+ weld /json/0/discussions
|
||||
(build-discussion-path [path url]:update)
|
||||
==
|
||||
::
|
||||
++ give-json
|
||||
|= [=json paths=(list path)]
|
||||
^- card
|
||||
[%give %fact paths %json !>(json)]
|
||||
::
|
||||
++ scry-for
|
||||
|* [=mold =path]
|
||||
.^ mold
|
||||
%gx
|
||||
(scot %p our.bowl)
|
||||
%link-store
|
||||
(scot %da now.bowl)
|
||||
(snoc `^path`path %noun)
|
||||
==
|
||||
--
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -116,6 +116,7 @@
|
||||
(snoc (recreate-permissions perm-paths associate) (watch-group group))
|
||||
::
|
||||
=/ grp (group-scry group)
|
||||
~& associate+grp
|
||||
=. u.perms (~(uni in u.perms) perm-paths)
|
||||
:_ state(relation (~(put by relation) group u.perms))
|
||||
%+ weld
|
||||
|
@ -142,11 +142,7 @@
|
||||
?: ?=(%read -.upd)
|
||||
[%read (pairs [%path (path path.upd)]~)]
|
||||
?: ?=(%create -.upd)
|
||||
:- %create
|
||||
%- pairs
|
||||
:~ [%ship (ship ship.upd)]
|
||||
[%path (path path.upd)]
|
||||
==
|
||||
[%create (pairs [%path (path path.upd)]~)]
|
||||
?: ?=(%delete -.upd)
|
||||
[%delete (pairs [%path (path path.upd)]~)]
|
||||
?: ?=(%config -.upd)
|
||||
@ -174,10 +170,7 @@
|
||||
==
|
||||
::
|
||||
++ create
|
||||
%- ot
|
||||
:~ [%ship (su ;~(pfix sig fed:ag))]
|
||||
[%path pa]
|
||||
==
|
||||
(ot [%path pa]~)
|
||||
::
|
||||
++ delete
|
||||
(ot [%path pa]~)
|
||||
@ -233,8 +226,7 @@
|
||||
%- ot
|
||||
:~ [%path pa]
|
||||
[%security sec]
|
||||
[%read (as (su ;~(pfix sig fed:ag)))]
|
||||
[%write (as (su ;~(pfix sig fed:ag)))]
|
||||
[%members (as (su ;~(pfix sig fed:ag)))]
|
||||
[%allow-history bo]
|
||||
==
|
||||
::
|
||||
|
@ -115,7 +115,7 @@
|
||||
%link-store
|
||||
%link-proxy-hook
|
||||
%link-listen-hook
|
||||
%link-server-hook
|
||||
%link-view
|
||||
==
|
||||
::
|
||||
++ deft-fish :: default connects
|
||||
|
@ -22,18 +22,110 @@
|
||||
%| (rsh 3 1 (scot %if p.host))
|
||||
==
|
||||
::
|
||||
++ split-discussion-path
|
||||
++ build-discussion-path
|
||||
|= args=$@(url [=path =url])
|
||||
|^ ^- path
|
||||
?@ args ~[(encode-url-for-path args)]
|
||||
:_ path.args
|
||||
(encode-url-for-path url.args)
|
||||
::
|
||||
++ encode-url-for-path
|
||||
|= =url
|
||||
(scot %ta (wood url))
|
||||
--
|
||||
::
|
||||
++ break-discussion-path
|
||||
|= =path
|
||||
^- [=^path =url]
|
||||
~| [%path-too-short path]
|
||||
?> (gth (lent path) 1) ::TODO ?= would TMI
|
||||
=/ end=@ud (dec (lent path))
|
||||
:- (scag end path)
|
||||
(de-base64:mimes:html (snag end path))
|
||||
?~ path [/ '']
|
||||
:- t.path
|
||||
?: =(~ i.path) ''
|
||||
~| path
|
||||
(woad (slav %ta i.path))
|
||||
::
|
||||
:: zip sorted a into sorted b, maintaining sort order
|
||||
::TODO stdlib
|
||||
++ merge-sorted
|
||||
|* [sort=$-([* *] ?) a=(list) b=(list)]
|
||||
|- ^- ?(_a _b)
|
||||
?~ a b
|
||||
?~ b a
|
||||
?: (sort i.a i.b)
|
||||
[i.a $(a t.a)]
|
||||
[i.b $(b t.b)]
|
||||
::
|
||||
++ merge
|
||||
|%
|
||||
++ pages
|
||||
::TODO we would just use +cury here but it don't work
|
||||
|= [a=^pages b=^pages]
|
||||
^+ a
|
||||
%+ merge-sorted
|
||||
|= [a=page b=page]
|
||||
(gth time.a time.b)
|
||||
[a b]
|
||||
::
|
||||
++ submissions
|
||||
|= [a=^submissions b=^submissions]
|
||||
^+ a
|
||||
%+ merge-sorted
|
||||
|= [a=submission b=submission]
|
||||
(gth time.a time.b)
|
||||
[a b]
|
||||
::
|
||||
++ notes
|
||||
|= [a=^notes b=^notes]
|
||||
^+ a
|
||||
%+ merge-sorted
|
||||
|= [a=note b=note]
|
||||
(gth time.a time.b)
|
||||
[a b]
|
||||
::
|
||||
++ comments
|
||||
|= [a=^comments b=^comments]
|
||||
^+ a
|
||||
%+ merge-sorted
|
||||
|= [a=comment b=comment]
|
||||
(gth time.a time.b)
|
||||
[a b]
|
||||
--
|
||||
::
|
||||
++ en-json
|
||||
=, enjs:format
|
||||
|%
|
||||
++ update
|
||||
|= upd=^update
|
||||
^- json
|
||||
%- frond
|
||||
:- -.upd
|
||||
?- -.upd
|
||||
%local-pages
|
||||
%- pairs
|
||||
:~ 'path'^(path path.upd)
|
||||
'pages'^a+(turn pages.upd page)
|
||||
==
|
||||
::
|
||||
%submissions
|
||||
%- pairs
|
||||
:~ 'path'^(path path.upd)
|
||||
'pages'^a+(turn submissions.upd submission)
|
||||
==
|
||||
::
|
||||
%annotations
|
||||
%- pairs
|
||||
:~ 'path'^(path path.upd)
|
||||
'url'^s+url.upd
|
||||
'notes'^a+(turn notes.upd note)
|
||||
==
|
||||
::
|
||||
%discussions
|
||||
%- pairs
|
||||
:~ 'path'^(path path.upd)
|
||||
'url'^s+url.upd
|
||||
'comments'^a+(turn comments.upd comment)
|
||||
==
|
||||
==
|
||||
::
|
||||
++ submission
|
||||
|= sub=^submission
|
||||
^- json
|
||||
@ -47,16 +139,22 @@
|
||||
%- pairs
|
||||
:~ 'title'^s+title.page
|
||||
'url'^s+url.page
|
||||
'timestamp'^(time time.page)
|
||||
'time'^(time time.page)
|
||||
==
|
||||
::
|
||||
++ comment
|
||||
|= =^comment
|
||||
^- json
|
||||
=+ n=(note +.comment)
|
||||
?> ?=([%o *] n)
|
||||
o+(~(put by p.n) 'ship' (ship ship.comment))
|
||||
::
|
||||
++ note
|
||||
|= =^note
|
||||
^- json
|
||||
%- pairs
|
||||
:~ 'ship'^(ship ship.comment)
|
||||
'time'^(time time.comment)
|
||||
'udon'^s+udon.comment ::TODO convert?
|
||||
:~ 'time'^(time time.note)
|
||||
'udon'^s+udon.note ::TODO convert?
|
||||
==
|
||||
--
|
||||
::
|
||||
|
16
pkg/arvo/mar/link/action.hoon
Normal file
16
pkg/arvo/mar/link/action.hoon
Normal file
@ -0,0 +1,16 @@
|
||||
:: link: subscription updates
|
||||
::
|
||||
::TODO this should include json conversion once mark performance improves
|
||||
/+ *link
|
||||
|_ =action
|
||||
++ grow
|
||||
|%
|
||||
++ noun action
|
||||
--
|
||||
::
|
||||
++ grab
|
||||
|%
|
||||
++ noun ^action
|
||||
++ json action:de-json
|
||||
--
|
||||
--
|
14
pkg/arvo/mar/link/initial.hoon
Normal file
14
pkg/arvo/mar/link/initial.hoon
Normal file
@ -0,0 +1,14 @@
|
||||
:: link: initial subscription result
|
||||
::
|
||||
/- *link
|
||||
|_ =initial
|
||||
++ grow
|
||||
|%
|
||||
++ noun initial
|
||||
--
|
||||
::
|
||||
++ grab
|
||||
|%
|
||||
++ noun ^initial
|
||||
--
|
||||
--
|
@ -1,6 +1,5 @@
|
||||
:: link: subscription updates
|
||||
::
|
||||
::TODO this should include json conversion once mark performance improves
|
||||
/- *link
|
||||
|_ =update
|
||||
++ grow
|
||||
|
@ -31,7 +31,7 @@
|
||||
+$ chat-configs (map path config)
|
||||
::
|
||||
+$ chat-base
|
||||
$% [%create =ship =path] :: %create: create a mailbox at ~ship/path
|
||||
$% [%create =path] :: %create: create a mailbox at path
|
||||
[%delete =path] :: %delete: delete a mailbox at path
|
||||
[%message =path =envelope] :: %message: append a message to mailbox
|
||||
[%read =path] :: %read: set mailbox to read
|
||||
|
@ -4,8 +4,7 @@
|
||||
$% $: %create
|
||||
=path
|
||||
security=rw-security
|
||||
read=(set ship)
|
||||
write=(set ship)
|
||||
members=(set ship)
|
||||
allow-history=?
|
||||
==
|
||||
[%delete =path]
|
||||
|
@ -23,6 +23,7 @@
|
||||
page
|
||||
==
|
||||
:: +note: a comment on some url
|
||||
::
|
||||
+$ note
|
||||
$: =time
|
||||
udon=@t
|
||||
@ -40,6 +41,12 @@
|
||||
+$ notes (list note)
|
||||
+$ comments (list comment)
|
||||
::
|
||||
:: state builder
|
||||
::
|
||||
++ per-path-url
|
||||
|$ [value]
|
||||
(map path (map url value))
|
||||
::
|
||||
:: +action: local actions
|
||||
::
|
||||
+$ action
|
||||
@ -60,6 +67,15 @@
|
||||
::
|
||||
[%read =path =url comment]
|
||||
==
|
||||
::
|
||||
:: +initial: local result
|
||||
::
|
||||
+$ initial
|
||||
$% [%local-pages pages=(map path pages)]
|
||||
[%submissions submissions=(map path submissions)]
|
||||
[%annotations notes=(per-path-url notes)]
|
||||
[%discussions comments=(per-path-url comments)]
|
||||
==
|
||||
:: +update: local updates
|
||||
::
|
||||
::NOTE we include paths/urls to support the "subscribed to all" case
|
||||
|
@ -1549,7 +1549,6 @@
|
||||
(emit-event channel-id [(en-json:html json)]~)
|
||||
::
|
||||
%kick
|
||||
~& [%recieved-quit-from-gall channel-id]
|
||||
=/ =json
|
||||
=, enjs:format
|
||||
%- pairs :~
|
||||
|
@ -129,10 +129,10 @@ class UrbitApi {
|
||||
this.action("chat-view", "json", data);
|
||||
}
|
||||
|
||||
chatViewCreate(path, security, read, write, allowHistory) {
|
||||
chatViewCreate(path, security, members, allowHistory) {
|
||||
this.chatViewAction({
|
||||
create: {
|
||||
path, security, read, write,
|
||||
path, security, members,
|
||||
'allow-history': allowHistory
|
||||
}
|
||||
});
|
||||
|
@ -19,7 +19,7 @@ export class SidebarInvite extends Component {
|
||||
<div className='pa3'>
|
||||
<div className='w-100 v-mid'>
|
||||
<p className="dib f8 mono gray4-d">
|
||||
{props.invite.text}
|
||||
{props.invite.path}
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
|
@ -124,18 +124,6 @@ export class NewScreen extends Component {
|
||||
this.textarea.value = '';
|
||||
}
|
||||
|
||||
// TODO: don't do this, it's shitty
|
||||
let writeAud;
|
||||
let readAud;
|
||||
|
||||
if (state.security === 'village') {
|
||||
aud.push(`~${window.ship}`);
|
||||
readAud = aud.slice(); // white list
|
||||
writeAud = aud.slice(); // white list
|
||||
} else if (state.security === 'channel') {
|
||||
readAud = []; // black list
|
||||
writeAud = []; // black list
|
||||
}
|
||||
this.setState({
|
||||
error: false,
|
||||
success: true,
|
||||
@ -145,14 +133,12 @@ export class NewScreen extends Component {
|
||||
}
|
||||
}, () => {
|
||||
props.setSpinner(true);
|
||||
// if we want a "proper group" that can be managed from the contacts UI,
|
||||
// we make a path of the form /~zod/cool-group
|
||||
// if not, we make a path of the form /~/~zod/free-chat
|
||||
props.api.chatView.create(
|
||||
station, state.security, readAud, writeAud, state.allowHistory
|
||||
`/~${window.ship}${station}`, state.security, aud, state.allowHistory
|
||||
);
|
||||
aud.forEach((ship) => {
|
||||
if (ship !== `~${window.ship}`) {
|
||||
props.api.invite.invite(station, ship);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -258,4 +244,4 @@ export class NewScreen extends Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,6 @@ export class Sidebar extends Component {
|
||||
})
|
||||
.map((obj) => {
|
||||
let unread = props.unreads[obj.box];
|
||||
|
||||
return (
|
||||
<SidebarItem
|
||||
key={obj.box + '/' + obj.when}
|
||||
|
@ -44,7 +44,7 @@ export class ChatUpdateReducer {
|
||||
create(json, state) {
|
||||
let data = _.get(json, 'create', false);
|
||||
if (data) {
|
||||
state.inbox[`/~${data.ship}${data.path}`] = {
|
||||
state.inbox[data.path] = {
|
||||
envelopes: [],
|
||||
config: {
|
||||
read: 0,
|
||||
|
41
pkg/interface/link/package-lock.json
generated
41
pkg/interface/link/package-lock.json
generated
@ -2148,7 +2148,8 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -2172,13 +2173,15 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -2195,19 +2198,22 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -2338,7 +2344,8 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -2352,6 +2359,7 @@
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -2368,6 +2376,7 @@
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -2376,13 +2385,15 @@
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
|
||||
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -2403,6 +2414,7 @@
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -2491,7 +2503,8 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -2505,6 +2518,7 @@
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -2600,7 +2614,8 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -2642,6 +2657,7 @@
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -2663,6 +2679,7 @@
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -2711,13 +2728,15 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
|
||||
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -91,135 +91,84 @@ class UrbitApi {
|
||||
});
|
||||
}
|
||||
|
||||
async getComments(path, url, page, index) {
|
||||
let endpoint = "/~link/discussions" + path + "/" + window.btoa(url) + ".json?p=0";
|
||||
let promise = await fetch(endpoint);
|
||||
if (promise.ok) {
|
||||
let comments = {
|
||||
"link-update": {
|
||||
comments: {
|
||||
path: path,
|
||||
page: page,
|
||||
index: index
|
||||
}
|
||||
}
|
||||
};
|
||||
comments["link-update"].comments.data = await promise.json();
|
||||
store.handleEvent(comments);
|
||||
}
|
||||
getComments(path, url) {
|
||||
return this.getCommentsPage.bind(this)(path, url, 0);
|
||||
}
|
||||
|
||||
async getCommentsPage(path, url, page, index, commentPage) {
|
||||
let endpoint = "/~link/discussions" + path + "/" + window.btoa(url) + ".json?p=" + commentPage;
|
||||
let promise = await fetch(endpoint);
|
||||
if (promise.ok) {
|
||||
let responseData = await promise.json();
|
||||
let update = {
|
||||
"link-update": {
|
||||
commentPage: {
|
||||
path: path,
|
||||
linkPage: page,
|
||||
index: index,
|
||||
comPageNo: commentPage,
|
||||
data: responseData.page
|
||||
getCommentsPage(path, url, page) {
|
||||
//TODO factor out
|
||||
// encode the url into @ta-safe format, using logic from +wood
|
||||
let strictUrl = '';
|
||||
for (let i = 0; i < url.length; i++) {
|
||||
const char = url[i];
|
||||
let add = '';
|
||||
switch (char) {
|
||||
case ' ':
|
||||
add = '.';
|
||||
break;
|
||||
case '.':
|
||||
add = '~.';
|
||||
break;
|
||||
case '~':
|
||||
add = '~~';
|
||||
break;
|
||||
default:
|
||||
const charCode = url.charCodeAt(i);
|
||||
if (
|
||||
(charCode >= 97 && charCode <= 122) || // a-z
|
||||
(charCode >= 48 && charCode <= 57) || // 0-9
|
||||
char === '-'
|
||||
) {
|
||||
add = char;
|
||||
} else {
|
||||
//TODO behavior for unicode doesn't match +wood's,
|
||||
// but we can probably get away with that for now.
|
||||
add = '~' + charCode.toString(16) + '.';
|
||||
}
|
||||
}
|
||||
};
|
||||
store.handleEvent(update);
|
||||
}
|
||||
strictUrl = strictUrl + add;
|
||||
}
|
||||
}
|
||||
strictUrl = '~.' + strictUrl;
|
||||
|
||||
async getPage(path, page) {
|
||||
let endpoint = "/~link/submissions" + path + ".json?p=" + page;
|
||||
let promise = await fetch(endpoint);
|
||||
if (promise.ok) {
|
||||
let resolvedPage = await promise.json();
|
||||
let update = {
|
||||
"link-update": {
|
||||
page: {
|
||||
[path]: {
|
||||
page: page,
|
||||
links: resolvedPage.page
|
||||
}
|
||||
}
|
||||
const endpoint = '/json/' + page + '/discussions/' + strictUrl + path;
|
||||
this.bind.bind(this)(endpoint, 'PUT', this.authTokens.ship, 'link-view',
|
||||
(res) => {
|
||||
if (res.data['initial-discussions']) {
|
||||
// these aren't returned with the response,
|
||||
// so this ensures the reducers know them.
|
||||
res.data['initial-discussions'].path = path;
|
||||
res.data['initial-discussions'].url = url;
|
||||
}
|
||||
};
|
||||
store.handleEvent(update);
|
||||
}
|
||||
}
|
||||
|
||||
async postLink(path, url, title) {
|
||||
let json =
|
||||
{'path': path,
|
||||
'title': title,
|
||||
'url': url
|
||||
};
|
||||
let endpoint = "/~link/save";
|
||||
let post = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
store.handleEvent(res);
|
||||
},
|
||||
body: JSON.stringify(json)
|
||||
});
|
||||
|
||||
if (post.ok) {
|
||||
let update = {
|
||||
"link-update": {
|
||||
add: {
|
||||
[path]: {
|
||||
title: title,
|
||||
url: url,
|
||||
timestamp: moment.now(),
|
||||
ship: window.ship,
|
||||
commentCount: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
store.handleEvent(update);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
console.error,
|
||||
()=>{} // no-op on quit
|
||||
);
|
||||
}
|
||||
|
||||
async postComment(path, url, comment, page, index) {
|
||||
let json = {
|
||||
path: path,
|
||||
url: url,
|
||||
udon: comment
|
||||
}
|
||||
getPage(path, page) {
|
||||
const endpoint = '/json/' + page + '/submissions' + path;
|
||||
this.bind.bind(this)(endpoint, 'PUT', this.authTokens.ship, 'link-view',
|
||||
(dat)=>{store.handleEvent(dat)},
|
||||
console.error,
|
||||
()=>{} // no-op on quit
|
||||
);
|
||||
}
|
||||
|
||||
let endpoint = "/~link/note";
|
||||
let post = await fetch(endpoint, {
|
||||
method: "POST",
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(json)
|
||||
linkAction(data) {
|
||||
return this.action("link-store", "link-action", data);
|
||||
}
|
||||
|
||||
postLink(path, url, title) {
|
||||
return this.linkAction({
|
||||
'save': { path, url, title }
|
||||
});
|
||||
}
|
||||
|
||||
if (post.ok) {
|
||||
let update = {
|
||||
"link-update": {
|
||||
commentAdd: {
|
||||
path: path,
|
||||
url: url,
|
||||
udon: comment,
|
||||
page: page,
|
||||
index: index,
|
||||
time: moment.now()
|
||||
}
|
||||
}
|
||||
};
|
||||
store.handleEvent(update);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
postComment(path, url, comment, page, index) {
|
||||
return this.linkAction({
|
||||
'note': { path, url, udon: comment }
|
||||
});
|
||||
}
|
||||
|
||||
sidebarToggle() {
|
||||
|
@ -17,7 +17,7 @@ export class ChannelsSidebar extends Component {
|
||||
.map((path) => {
|
||||
let name = "Private"
|
||||
let selected = (props.selected === path);
|
||||
let linkCount = !!props.links[path] ? props.links[path]['total-items'] : 0;
|
||||
let linkCount = !!props.links[path] ? props.links[path].totalItems : 0;
|
||||
return (
|
||||
<ChannelsItem
|
||||
key={path}
|
||||
@ -40,8 +40,8 @@ export class ChannelsSidebar extends Component {
|
||||
name = name.substr(nameSeparator + 1);
|
||||
|
||||
let selected = (props.selected === path);
|
||||
let linkCount = !!props.links[path] ? props.links[path]['total-items'] : 0;
|
||||
|
||||
let linkCount = !!props.links[path] ? props.links[path].totalItems : 0;
|
||||
|
||||
return (
|
||||
<ChannelsItem
|
||||
key={path}
|
||||
@ -56,9 +56,9 @@ export class ChannelsSidebar extends Component {
|
||||
let activeClasses = (this.props.active === "channels") ? " " : "dn-s ";
|
||||
|
||||
let hiddenClasses = true;
|
||||
|
||||
|
||||
// probably a more concise way to write this
|
||||
|
||||
|
||||
if (this.props.popout) {
|
||||
hiddenClasses = false;
|
||||
} else {
|
||||
@ -73,10 +73,10 @@ export class ChannelsSidebar extends Component {
|
||||
: "dn")}>
|
||||
<a className="db dn-m dn-l dn-xl f8 pb3 pl3" href="/">⟵ Landscape</a>
|
||||
<div className="overflow-y-scroll h-100">
|
||||
<h2 className={`f8 f9-m f9-l f9-xl
|
||||
pt1 pt4-m pt4-l pt4-xl
|
||||
<h2 className={`f8 f9-m f9-l f9-xl
|
||||
pt1 pt4-m pt4-l pt4-xl
|
||||
pr4 pb3 pb2-m pb2-l pb2-xl
|
||||
pl3 pl4-m pl4-l pl4-xl
|
||||
pl3 pl4-m pl4-l pl4-xl
|
||||
black-s gray2 white-d c-default
|
||||
bb bn-m bn-l bn-xl b--gray4 mb2 mb0-m mb0-l mb0-xl`}>
|
||||
Your Collections
|
||||
|
@ -2,7 +2,7 @@ import React, { Component } from 'react'
|
||||
import { CommentItem } from './comment-item';
|
||||
import { CommentsPagination } from './comments-pagination';
|
||||
|
||||
import { uxToHex } from '../../lib/util';
|
||||
import { uxToHex } from '../../lib/util';
|
||||
import { api } from '../../api';
|
||||
|
||||
export class Comments extends Component {
|
||||
@ -10,12 +10,13 @@ export class Comments extends Component {
|
||||
componentDidMount() {
|
||||
let page = "page" + this.props.commentPage;
|
||||
let comments = !!this.props.comments;
|
||||
if ((!comments[page]) && (page !== "page0")) {
|
||||
if ((page !== "page0") &&
|
||||
(!comments || !this.props.comments[page]) &&
|
||||
(this.props.path && this.props.url)
|
||||
) {
|
||||
api.getCommentsPage(
|
||||
this.props.path,
|
||||
this.props.url,
|
||||
this.props.linkPage,
|
||||
this.props.linkIndex,
|
||||
this.props.path,
|
||||
this.props.url,
|
||||
this.props.commentPage);
|
||||
}
|
||||
}
|
||||
@ -24,12 +25,10 @@ export class Comments extends Component {
|
||||
let page = "page" + this.props.commentPage;
|
||||
if (prevProps !== this.props) {
|
||||
if (!!this.props.comments) {
|
||||
if ((page !== "page0") && (!this.props.comments[page])) {
|
||||
if ((page !== "page0") && !this.props.comments[page] && this.props.url) {
|
||||
api.getCommentsPage(
|
||||
this.props.path,
|
||||
this.props.url,
|
||||
this.props.linkPage,
|
||||
this.props.linkIndex,
|
||||
this.props.path,
|
||||
this.props.url,
|
||||
this.props.commentPage);
|
||||
}
|
||||
}
|
||||
@ -38,7 +37,7 @@ export class Comments extends Component {
|
||||
|
||||
render() {
|
||||
let props = this.props;
|
||||
|
||||
|
||||
let page = "page" + props.commentPage;
|
||||
|
||||
let commentsObj = !!props.comments
|
||||
@ -50,7 +49,7 @@ export class Comments extends Component {
|
||||
: {};
|
||||
|
||||
let total = !!props.comments
|
||||
? props.comments["total-pages"]
|
||||
? props.comments.totalPages
|
||||
: {};
|
||||
|
||||
let commentsList = Object.keys(commentsPage)
|
||||
@ -59,7 +58,7 @@ export class Comments extends Component {
|
||||
let commentObj = commentsPage[entry]
|
||||
let { ship, time, udon } = commentObj;
|
||||
|
||||
let members = !!props.members
|
||||
let members = !!props.members
|
||||
? props.members
|
||||
: {};
|
||||
|
||||
|
@ -46,7 +46,7 @@ export class LinkItem extends Component {
|
||||
}
|
||||
|
||||
let comments = props.comments + " comment" + ((props.comments === 1) ? "" : "s");
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-100 pv3 flex">
|
||||
<Sigil
|
||||
@ -63,8 +63,8 @@ export class LinkItem extends Component {
|
||||
</p>
|
||||
</a>
|
||||
<div className="w-100 pt1">
|
||||
<span className={"f9 pr2 v-mid " + mono}>{(props.nickname)
|
||||
? props.nickname
|
||||
<span className={"f9 pr2 v-mid " + mono}>{(props.nickname)
|
||||
? props.nickname
|
||||
: "~" + props.ship}</span>
|
||||
<span className="f9 inter gray2 pr3 v-mid">{this.state.timeSinceLinkPost}</span>
|
||||
<Link to=
|
||||
|
@ -19,11 +19,9 @@ export class LinkSubmit extends Component {
|
||||
let title = (this.state.linkTitle)
|
||||
? this.state.linkTitle
|
||||
: this.state.linkValue;
|
||||
let request = api.postLink(this.props.path, link, title);
|
||||
|
||||
if (request) {
|
||||
this.setState({linkValue: "", linkTitle: ""})
|
||||
}
|
||||
api.postLink(this.props.path, link, title).then((r) => {
|
||||
this.setState({linkValue: "", linkTitle: ""});
|
||||
});
|
||||
}
|
||||
|
||||
setLinkValid(link) {
|
||||
@ -58,7 +56,7 @@ export class LinkSubmit extends Component {
|
||||
let activeClasses = (this.state.linkValid)
|
||||
? "green2 pointer"
|
||||
: "gray2";
|
||||
|
||||
|
||||
return (
|
||||
<div className="relative ba b--gray4 b--gray2-d br1 w-100 mb6">
|
||||
<textarea
|
||||
|
@ -18,34 +18,30 @@ export class LinkDetail extends Component {
|
||||
|
||||
this.setComment = this.setComment.bind(this);
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
// if we have no preloaded data, and we aren't expecting it, get it
|
||||
if (this.props.page != 0 && (!this.props.data || !this.props.data.url)) {
|
||||
api.getPage(this.props.path, this.props.page);
|
||||
|
||||
// if we have preloaded our data,
|
||||
// but no comments, grab the comments
|
||||
if (!!this.props.data.url) {
|
||||
let props = this.props;
|
||||
let comments = !!props.data.comments;
|
||||
|
||||
if (!comments) {
|
||||
api.getComments(props.path, props.data.url, props.page, props.link);
|
||||
}
|
||||
} else if (!this.props.comments && this.props.data.url) {
|
||||
api.getCommentsPage(this.props.path, this.props.data.url, this.props.commentPage);
|
||||
}
|
||||
|
||||
this.updateTimeSinceNewestMessageInterval = setInterval( () => {
|
||||
this.setState({timeSinceLinkPost: this.getTimeSinceLinkPost()});
|
||||
}, 60000);
|
||||
}
|
||||
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
// if we came to this page *directly*,
|
||||
// load the comments -- DidMount will fail
|
||||
if (this.props.data.url !== prevProps.data.url) {
|
||||
let props = this.props;
|
||||
let comments = !!this.props.data.comments;
|
||||
|
||||
if (!comments && this.props.data.url) {
|
||||
api.getComments(props.path, props.data.url, props.page, props.link);
|
||||
}
|
||||
if ( (this.props.data.url !== prevProps.data.url) &&
|
||||
(!this.props.comments && this.props.data.url)
|
||||
) {
|
||||
api.getCommentsPage(this.props.path, this.props.data.url, this.props.commentPage);
|
||||
}
|
||||
|
||||
if (this.props.data.timestamp !== prevProps.data.timestamp) {
|
||||
@ -70,10 +66,10 @@ export class LinkDetail extends Component {
|
||||
let url = this.props.data.url || "";
|
||||
|
||||
let request = api.postComment(
|
||||
this.props.path,
|
||||
url,
|
||||
this.state.comment,
|
||||
this.props.page,
|
||||
this.props.path,
|
||||
url,
|
||||
this.state.comment,
|
||||
this.props.page,
|
||||
this.props.link
|
||||
);
|
||||
|
||||
@ -85,7 +81,7 @@ export class LinkDetail extends Component {
|
||||
setComment(event) {
|
||||
this.setState({comment: event.target.value});
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
let props = this.props;
|
||||
let popout = (props.popout) ? "/popout" : "";
|
||||
@ -106,7 +102,7 @@ export class LinkDetail extends Component {
|
||||
let commentCount = props.data.commentCount || 0;
|
||||
|
||||
let comments = commentCount + " comment" + ((commentCount === 1) ? "" : "s");
|
||||
|
||||
|
||||
let nickname = !!props.members[props.data.ship]
|
||||
? props.members[props.data.ship].nickname
|
||||
: "";
|
||||
@ -120,18 +116,18 @@ export class LinkDetail extends Component {
|
||||
let activeClasses = (this.state.comment)
|
||||
? "black b--black pointer"
|
||||
: "gray2 b--gray2";
|
||||
|
||||
|
||||
return (
|
||||
<div className="h-100 w-100 overflow-hidden flex flex-column">
|
||||
<div
|
||||
className={`pl3 pt2 flex relative overflow-x-scroll
|
||||
className={`pl3 pt2 flex relative overflow-x-scroll
|
||||
overflow-x-auto-l overflow-x-auto-xl flex-shrink-0
|
||||
bb bn-m bn-l bn-xl b--gray4`}
|
||||
style={{ height: 48 }}>
|
||||
<SidebarSwitcher
|
||||
sidebarShown={props.sidebarShown}
|
||||
popout={props.popout}/>
|
||||
<Link
|
||||
<Link
|
||||
className="dib f8 fw4 v-top pt2 gray2"
|
||||
to={"/~link" + popout + props.path + "/" + props.page}>
|
||||
{"<- Collection index"}
|
||||
@ -158,8 +154,8 @@ export class LinkDetail extends Component {
|
||||
</p>
|
||||
</a>
|
||||
<div className="w-100 pt1">
|
||||
<span className={"f9 pr2 white-d v-mid " + nameClass}>{(nickname)
|
||||
? nickname
|
||||
<span className={"f9 pr2 white-d v-mid " + nameClass}>{(nickname)
|
||||
? nickname
|
||||
: "~" + ship}
|
||||
</span>
|
||||
<span className="f9 inter gray2 pr3 v-mid">
|
||||
@ -184,7 +180,7 @@ export class LinkDetail extends Component {
|
||||
onChange={this.setComment}
|
||||
value={this.state.comment}
|
||||
/>
|
||||
<button className={"f8 bg-gray0-d white-d ml2 absolute "
|
||||
<button className={"f8 bg-gray0-d white-d ml2 absolute "
|
||||
+ activeClasses}
|
||||
disabled={!this.state.comment}
|
||||
onClick={this.onClickPost.bind(this)}
|
||||
@ -195,11 +191,11 @@ export class LinkDetail extends Component {
|
||||
Post
|
||||
</button>
|
||||
</div>
|
||||
<Comments
|
||||
path={props.path}
|
||||
<Comments
|
||||
path={props.path}
|
||||
key={props.path + props.commentPage}
|
||||
comments={props.data.comments}
|
||||
commentPage={props.commentPage}
|
||||
comments={props.comments}
|
||||
commentPage={props.commentPage}
|
||||
members={props.members}
|
||||
popout={props.popout}
|
||||
url={props.data.url}
|
||||
@ -212,6 +208,5 @@ export class LinkDetail extends Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default LinkDetail;
|
||||
|
@ -13,15 +13,12 @@ import { uxToHex } from '../lib/util';
|
||||
export class Links extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
let linkPage = "page" + this.props.page;
|
||||
if ((this.props.page !== 0) && (!this.props.links[linkPage])) {
|
||||
api.getPage(this.props.path, this.props.page);
|
||||
}
|
||||
this.componentDidUpdate();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
let linkPage = "page" + this.props.page;
|
||||
if ((this.props.page !== 0) && (!this.props.links[linkPage])) {
|
||||
if ((this.props.page != 0) && (!this.props.links[linkPage])) {
|
||||
api.getPage(this.props.path, this.props.page);
|
||||
}
|
||||
}
|
||||
@ -41,7 +38,7 @@ export class Links extends Component {
|
||||
: 0;
|
||||
|
||||
let totalPages = !!props.links
|
||||
? Number(props.links["total-pages"])
|
||||
? Number(props.links.totalPages)
|
||||
: 1;
|
||||
|
||||
let LinkList = Object.keys(links)
|
||||
@ -95,7 +92,7 @@ export class Links extends Component {
|
||||
<Link to="/~link/">{"⟵ All Channels"}</Link>
|
||||
</div>
|
||||
<div
|
||||
className={`pl3 pt2 flex relative overflow-x-scroll
|
||||
className={`pl3 pt2 flex relative overflow-x-scroll
|
||||
overflow-x-auto-l overflow-x-auto-xl flex-shrink-0
|
||||
bb bn-m bn-l bn-xl b--gray4`}
|
||||
style={{ height: 48 }}>
|
||||
@ -104,7 +101,7 @@ export class Links extends Component {
|
||||
popout={props.popout}/>
|
||||
<Link to={`/~link` + popout + props.path} className="pt2">
|
||||
<h2
|
||||
className={`dib f8 fw4 v-top ` +
|
||||
className={`dib f8 fw4 v-top ` +
|
||||
(props.path.includes("/~/")
|
||||
? ""
|
||||
: "mono")}>
|
||||
|
@ -32,14 +32,15 @@ export class Root extends Component {
|
||||
let paths = !!state.contacts ? state.contacts : {};
|
||||
|
||||
let links = !!state.links ? state.links : {};
|
||||
|
||||
let comments = !!state.comments ? state.comments : {};
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Route exact path="/~link"
|
||||
render={ (props) => {
|
||||
return (
|
||||
<Skeleton
|
||||
active="channels"
|
||||
<Skeleton
|
||||
active="channels"
|
||||
paths={paths}
|
||||
rightPanelHide={true}
|
||||
sidebarShown={true}
|
||||
@ -58,7 +59,7 @@ export class Root extends Component {
|
||||
render={ (props) => {
|
||||
// groups/contacts and link channels are the same thing in ver 1
|
||||
|
||||
let groupPath =
|
||||
let groupPath =
|
||||
`/${props.match.params.ship}/${props.match.params.channel}`;
|
||||
let groupMembers = paths[groupPath] || {};
|
||||
|
||||
@ -66,8 +67,8 @@ export class Root extends Component {
|
||||
|
||||
let popout = props.match.url.includes("/popout/");
|
||||
|
||||
let channelLinks = !!links[groupPath]
|
||||
? links[groupPath]
|
||||
let channelLinks = !!links[groupPath]
|
||||
? links[groupPath]
|
||||
: {};
|
||||
|
||||
return (
|
||||
@ -95,7 +96,7 @@ export class Root extends Component {
|
||||
/>
|
||||
<Route exact path="/~link/(popout)?/:ship/:channel/:page/:index/(comments)?/:commentpage?"
|
||||
render={ (props) => {
|
||||
let groupPath =
|
||||
let groupPath =
|
||||
`/${props.match.params.ship}/${props.match.params.channel}`;
|
||||
|
||||
let popout = props.match.url.includes("/popout/");
|
||||
@ -106,8 +107,13 @@ export class Root extends Component {
|
||||
let page = props.match.params.page || 0;
|
||||
|
||||
let data = !!links[groupPath]
|
||||
? links[groupPath]["page" + page][index]
|
||||
: {};
|
||||
? !!links[groupPath]["page" + page]
|
||||
? links[groupPath]["page" + page][index]
|
||||
: {}
|
||||
: {};
|
||||
let coms = !comments[groupPath]
|
||||
? undefined
|
||||
: comments[groupPath][data.url];
|
||||
|
||||
let commentPage = props.match.params.commentpage || 0;
|
||||
|
||||
@ -130,6 +136,7 @@ export class Root extends Component {
|
||||
popout={popout}
|
||||
sidebarShown={state.sidebarShown}
|
||||
data={data}
|
||||
comments={coms}
|
||||
commentPage={commentPage}
|
||||
/>
|
||||
</Skeleton>
|
||||
|
@ -14,7 +14,7 @@ export class InitialReducer {
|
||||
state.groups[group] = new Set(data[group]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data = _.get(json, 'link', false);
|
||||
if (data) {
|
||||
let name = Object.keys(data)[0];
|
||||
|
@ -1,92 +1,141 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
const PAGE_SIZE = 25;
|
||||
|
||||
export class LinkUpdateReducer {
|
||||
reduce(json, state) {
|
||||
let data = _.get(json, 'link-update', false);
|
||||
if (data) {
|
||||
this.add(data, state);
|
||||
this.comments(data, state);
|
||||
this.commentAdd(data, state);
|
||||
this.commentPage(data, state);
|
||||
this.page(data, state);
|
||||
}
|
||||
this.submissionsPage(json, state);
|
||||
this.submissionsUpdate(json, state);
|
||||
this.discussionsPage(json, state);
|
||||
this.discussionsUpdate(json, state);
|
||||
}
|
||||
|
||||
add(json, state) {
|
||||
// pin ok'd link POSTs to top of page0
|
||||
let data = _.get(json, 'add', false);
|
||||
submissionsPage(json, state) {
|
||||
let data = _.get(json, 'initial-submissions', false);
|
||||
if (data) {
|
||||
let path = Object.keys(data)[0];
|
||||
let tempArray = state.links[path].page0;
|
||||
tempArray.unshift(data[path]);
|
||||
state.links[path].page0 = tempArray;
|
||||
}
|
||||
}
|
||||
// { "initial-submissions": {
|
||||
// "/~ship/group": {
|
||||
// page: [{ship, timestamp, title, url}]
|
||||
// page-number: 0
|
||||
// total-items: 1
|
||||
// total-pages: 1
|
||||
// }
|
||||
// } }
|
||||
|
||||
comments(json, state) {
|
||||
let data = _.get(json, 'comments', false);
|
||||
if (data) {
|
||||
let path = data.path;
|
||||
let page = "page" + data.page;
|
||||
let index = data.index;
|
||||
let storage = state.links[path][page][index];
|
||||
for (var path of Object.keys(data)) {
|
||||
const here = data[path];
|
||||
const page = "page" + here.pageNumber;
|
||||
|
||||
storage.comments = {};
|
||||
storage.comments["page0"] = data.data.page;
|
||||
storage.comments["total-items"] = data.data["total-items"];
|
||||
storage.comments["total-pages"] = data.data["total-pages"];
|
||||
// if we didn't have any state for this path yet, initialize.
|
||||
if (!state.links[path]) {
|
||||
state.links[path] = {};
|
||||
}
|
||||
|
||||
state.links[path][page][index] = storage;
|
||||
}
|
||||
}
|
||||
|
||||
commentAdd(json, state) {
|
||||
let data = _.get(json, 'commentAdd', false);
|
||||
if (data) {
|
||||
let path = data.path;
|
||||
let page = "page" + data.page;
|
||||
let index = data.index;
|
||||
|
||||
let ship = window.ship;
|
||||
let time = data.time;
|
||||
let udon = data.udon;
|
||||
let tempObj = {
|
||||
'ship': ship,
|
||||
'time': time,
|
||||
'udon': udon
|
||||
// since data contains an up-to-date full version of the page,
|
||||
// we can safely overwrite the one in state.
|
||||
state.links[path][page] = here.page;
|
||||
state.links[path].totalPages = here.totalPages;
|
||||
state.links[path].totalItems = here.totalItems;
|
||||
}
|
||||
|
||||
let tempArray = state.links[path][page][index].comments["page0"];
|
||||
tempArray.unshift(tempObj);
|
||||
state.links[path][page][index].comments["page0"] = tempArray;
|
||||
}
|
||||
}
|
||||
|
||||
commentPage(json, state) {
|
||||
let data = _.get(json, 'commentPage', false);
|
||||
submissionsUpdate(json, state) {
|
||||
let data = _.get(json, 'submissions', false);
|
||||
if (data) {
|
||||
let path = data.path;
|
||||
let linkPage = "page" + data.linkPage;
|
||||
let linkIndex = data.index;
|
||||
let commentPage = "page" + data.comPageNo;
|
||||
// { "submissions": {
|
||||
// path: /~ship/group
|
||||
// pages: [{ship, timestamp, title, url}]
|
||||
// } }
|
||||
|
||||
if (!state.links[path]) {
|
||||
return false;
|
||||
}
|
||||
const path = data.path;
|
||||
|
||||
state.links[path][linkPage][linkIndex].comments[commentPage] = data.data;
|
||||
// stub in a comment count, which is more or less guaranteed to be 0
|
||||
data.pages = data.pages.map(submission => {
|
||||
submission.commentCount = 0;
|
||||
return submission;
|
||||
});
|
||||
|
||||
// add the new submissions to state, update totals
|
||||
state.links[path] = this._addNewItems(
|
||||
data.pages, state.links[path]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
page(json, state) {
|
||||
let data = _.get(json, 'page', false);
|
||||
discussionsPage(json, state) {
|
||||
let data = _.get(json, 'initial-discussions', false);
|
||||
if (data) {
|
||||
let path = Object.keys(data)[0];
|
||||
let page = "page" + data[path].page;
|
||||
if (!state.links[path]) {
|
||||
state.links[path] = {};
|
||||
// { "initial-discussions": {
|
||||
// path: "/~ship/group"
|
||||
// url: https://urbit.org/
|
||||
// page: [{ship, timestamp, title, url}]
|
||||
// page-number: 0
|
||||
// total-items: 1
|
||||
// total-pages: 1
|
||||
// } }
|
||||
|
||||
const path = data.path;
|
||||
const url = data.url;
|
||||
const page = "page" + data.pageNumber;
|
||||
|
||||
// if we didn't have any state for this path yet, initialize.
|
||||
if (!state.comments[path]) {
|
||||
state.comments[path] = {};
|
||||
}
|
||||
state.links[path][page] = data[path].links;
|
||||
if (!state.comments[path][url]) {
|
||||
state.comments[path][url] = {};
|
||||
}
|
||||
let here = state.comments[path][url];
|
||||
|
||||
// since data contains an up-to-date full version of the page,
|
||||
// we can safely overwrite the one in state.
|
||||
here[page] = data.page;
|
||||
here.totalPages = data.totalPages;
|
||||
here.totalItems = data.totalItems;
|
||||
}
|
||||
}
|
||||
|
||||
discussionsUpdate(json, state) {
|
||||
let data = _.get(json, 'discussions', false);
|
||||
if (data) {
|
||||
// { "discussions": {
|
||||
// path: /~ship/path
|
||||
// url: 'https://urbit.org'
|
||||
// comments: [{ship, timestamp, udon}]
|
||||
// } }
|
||||
|
||||
const path = data.path;
|
||||
const url = data.url;
|
||||
|
||||
// add new comments to state, update totals
|
||||
state.comments[path][url] = this._addNewItems(
|
||||
data.comments, state.comments[path][url]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
_addNewItems(items, pages = {}, page = 0) {
|
||||
//TODO kinda want to refactor this, have it just be number indexes
|
||||
const i = "page" + page;
|
||||
//TODO but if there's more on the page than just the things we're
|
||||
// pushing onto it, we won't load that in. should do an
|
||||
// additional check (+ maybe load) on page-nav, right?
|
||||
if (!pages[i]) {
|
||||
pages[i] = [];
|
||||
}
|
||||
pages[i] = items.concat(pages[i]);
|
||||
if (pages[i].length <= PAGE_SIZE) {
|
||||
pages.totalPages = page + 1;
|
||||
pages.totalItems = (page * PAGE_SIZE) + pages[i].length;
|
||||
return pages;
|
||||
}
|
||||
// overflow into next page
|
||||
const tail = pages[i].slice(PAGE_SIZE);
|
||||
pages[i].length = PAGE_SIZE;
|
||||
return this._addNewItems(tail, pages, page+1);
|
||||
}
|
||||
|
||||
}
|
@ -46,6 +46,6 @@ export class PermissionUpdateReducer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ class Store {
|
||||
contacts: {},
|
||||
groups: {},
|
||||
links: {},
|
||||
comments: {},
|
||||
permissions: {},
|
||||
sidebarShown: true,
|
||||
spinner: false
|
||||
@ -23,24 +24,6 @@ class Store {
|
||||
this.setState = () => {};
|
||||
}
|
||||
|
||||
async loadLinks(json) {
|
||||
// if initial contacts, queue up getting these paths from link-store
|
||||
let data = _.get(json, 'group-initial', false);
|
||||
if (data) {
|
||||
for (let each of Object.keys(data)) {
|
||||
let linkUrl = "/~link/submissions" + each + ".json?p=0";
|
||||
let promise = await fetch(linkUrl);
|
||||
if (promise.ok) {
|
||||
let resolvedData = {}
|
||||
resolvedData.link = {};
|
||||
resolvedData.link[each] = {};
|
||||
resolvedData.link[each] = await promise.json();
|
||||
this.handleEvent(resolvedData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setStateHandler(setState) {
|
||||
this.setState = setState;
|
||||
}
|
||||
@ -53,8 +36,7 @@ class Store {
|
||||
json = data;
|
||||
}
|
||||
|
||||
console.log(json);
|
||||
this.loadLinks(json);
|
||||
console.log('event', json);
|
||||
this.initialReducer.reduce(json, this.state);
|
||||
this.permissionUpdateReducer.reduce(json, this.state);
|
||||
this.localReducer.reduce(json, this.state);
|
||||
|
@ -1,9 +1,6 @@
|
||||
import { api } from '/api';
|
||||
import { store } from '/store';
|
||||
|
||||
import urbitOb from 'urbit-ob';
|
||||
|
||||
|
||||
export class Subscription {
|
||||
start() {
|
||||
if (api.authTokens) {
|
||||
@ -24,6 +21,9 @@ export class Subscription {
|
||||
this.handleEvent.bind(this),
|
||||
this.handleError.bind(this),
|
||||
this.handleQuitAndResubscribe.bind(this));
|
||||
|
||||
// open a subscription for all submissions
|
||||
api.getPage('', 0);
|
||||
}
|
||||
|
||||
handleEvent(diff) {
|
||||
|
@ -176,3 +176,38 @@ a {
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.md h1, .md h2, .md h3, .md h4, .md h5, .md p, .md a, .md ul, .md blockquote,.md code,.md pre {
|
||||
font-size: 14px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.md h2, .md h3, .md h4, .md h5, .md p, .md a, .md ul {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.md h1 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.md h2, .md h3, .md h4, .md h5 {
|
||||
color:var(--gray);
|
||||
}
|
||||
|
||||
.md p {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.md code, .md pre {
|
||||
font-family: "Source Code Pro", mono;
|
||||
}
|
||||
.md ul>li {
|
||||
line-height: 1.5;
|
||||
}
|
||||
.md a {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
md img {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ export class Comments extends Component {
|
||||
|
||||
</textarea>
|
||||
</div>
|
||||
<button onClick={this.commentSubmit} className="f9 pa2 bg-white br1 ba b--gray2 gray2">
|
||||
<button onClick={this.commentSubmit} className="f9 pa2 bg-white br1 ba b--gray2 gray2 pointer">
|
||||
Add comment
|
||||
</button>
|
||||
</div>
|
||||
|
@ -51,7 +51,7 @@ export class NoteNavigation extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex mt4">
|
||||
<div className="flex pt4">
|
||||
{prevComponent}
|
||||
{nextComponent}
|
||||
</div>
|
||||
|
@ -2,8 +2,7 @@ import React, { Component } from 'react';
|
||||
import { Comments } from './comments';
|
||||
import { NoteNavigation } from './note-navigation';
|
||||
import moment from 'moment';
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
// import test from 'test.json';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
//TODO ask for note if we don't have it
|
||||
//TODO initialise note if no state
|
||||
|
||||
@ -129,13 +128,14 @@ export class Note extends Component {
|
||||
<div className="w-100 mw6">
|
||||
<div className="flex flex-column">
|
||||
<div className="f9 mb1">{title}</div>
|
||||
<div className="flex">
|
||||
<div className="di f9 mono gray2 mr2">{author}</div>
|
||||
<div className="di f9 gray2">{date}</div>
|
||||
<div className="flex mb6">
|
||||
<div className="di f9 mono gray2 mr2">{author}</div>
|
||||
<div className="di f9 gray2">{date}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ReactMarkdown source={newfile} />
|
||||
<div className="md">
|
||||
<ReactMarkdown source={newfile} />
|
||||
</div>
|
||||
<NoteNavigation
|
||||
prev={prev}
|
||||
next={next}
|
||||
|
Loading…
Reference in New Issue
Block a user