mirror of
https://github.com/ilyakooo0/urbit.git
synced 2025-01-05 05:45:46 +03:00
Merge pull request #2233 from urbit/m/uplink-os1
link: subscriptions for the frontend
This commit is contained in:
commit
9727fab259
@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:34c491ba1d70c1485a295fb04602609fb477a20fa9aa293077a8d7069e33101f
|
oid sha256:12ccd71ef3ac0c6cda7502fdf88d76fe227e19c5ba9e9abfb5a2ec9c4572f098
|
||||||
size 12396834
|
size 20382757
|
||||||
|
@ -18,8 +18,7 @@
|
|||||||
retry-timers=(map target @dr)
|
retry-timers=(map target @dr)
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
::TODO revert to annotations with new link-store subscription model
|
+$ what-target ?(%local-pages %annotations)
|
||||||
+$ what-target ?(%local-pages %allotations)
|
|
||||||
+$ target
|
+$ target
|
||||||
$: what=what-target
|
$: what=what-target
|
||||||
who=ship
|
who=ship
|
||||||
@ -68,9 +67,9 @@
|
|||||||
=^ cards state
|
=^ cards state
|
||||||
(take-groups-sign:do sign)
|
(take-groups-sign:do sign)
|
||||||
[cards this]
|
[cards this]
|
||||||
?: ?=([%links @ @ ^] wire)
|
?: ?=([%links ?(%local-pages %annotations) @ ^] wire)
|
||||||
=^ cards state
|
=^ cards state
|
||||||
(take-links-sign:do (wire-to-target t.wire) sign)
|
(take-link-sign:do (wire-to-target t.wire) sign)
|
||||||
[cards this]
|
[cards this]
|
||||||
?: ?=([%forward ^] wire)
|
?: ?=([%forward ^] wire)
|
||||||
=^ cards state
|
=^ cards state
|
||||||
@ -170,7 +169,7 @@
|
|||||||
$(whos t.whos)
|
$(whos t.whos)
|
||||||
(end-link-subscriptions i.whos pax.upd)
|
(end-link-subscriptions i.whos pax.upd)
|
||||||
:+ (start-link-subscription %local-pages 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)
|
$(whos t.whos)
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
@ -184,13 +183,16 @@
|
|||||||
%agent
|
%agent
|
||||||
[who.target %link-proxy-hook]
|
[who.target %link-proxy-hook]
|
||||||
%watch
|
%watch
|
||||||
[what where]:target
|
?- what.target
|
||||||
|
%local-pages [what where]:target
|
||||||
|
%annotations [what %$ where]:target
|
||||||
|
==
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ end-link-subscriptions
|
++ end-link-subscriptions
|
||||||
|= [who=ship where=path]
|
|= [who=ship where=path]
|
||||||
^- (list card)
|
^- (list card)
|
||||||
|^ ~[(end %local-pages) (end %allotations)]
|
|^ ~[(end %local-pages) (end %annotations)]
|
||||||
::
|
::
|
||||||
++ end
|
++ end
|
||||||
|= what=what-target
|
|= what=what-target
|
||||||
@ -203,7 +205,7 @@
|
|||||||
==
|
==
|
||||||
--
|
--
|
||||||
::
|
::
|
||||||
++ take-links-sign
|
++ take-link-sign
|
||||||
|= [=target =sign:agent:gall]
|
|= [=target =sign:agent:gall]
|
||||||
^- (quip card _state)
|
^- (quip card _state)
|
||||||
?- -.sign
|
?- -.sign
|
||||||
@ -224,6 +226,10 @@
|
|||||||
=* mark p.cage.sign
|
=* mark p.cage.sign
|
||||||
=* vase q.cage.sign
|
=* vase q.cage.sign
|
||||||
?+ mark ~|([dap.bowl %unexpected-mark mark] !!)
|
?+ mark ~|([dap.bowl %unexpected-mark mark] !!)
|
||||||
|
%link-initial
|
||||||
|
%- handle-link-initial
|
||||||
|
[who.target where.target !<(initial vase)]
|
||||||
|
::
|
||||||
%link-update
|
%link-update
|
||||||
%- handle-link-update
|
%- handle-link-update
|
||||||
[who.target where.target !<(update vase)]
|
[who.target where.target !<(update vase)]
|
||||||
@ -268,6 +274,40 @@
|
|||||||
(snoc where.target %noun)
|
(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
|
++ handle-link-update
|
||||||
|= [who=ship where=path =update]
|
|= [who=ship where=path =update]
|
||||||
^- (quip card _state)
|
^- (quip card _state)
|
||||||
@ -277,28 +317,17 @@
|
|||||||
%local-pages
|
%local-pages
|
||||||
%+ turn pages.update
|
%+ turn pages.update
|
||||||
|= =page
|
|= =page
|
||||||
^- card
|
%+ do-link-action
|
||||||
:* %pass
|
[%forward %local-page (scot %p who) where]
|
||||||
[%forward -.update (scot %p who) where]
|
[%hear where who page]
|
||||||
%agent
|
|
||||||
[our.bowl %link-store]
|
|
||||||
%poke
|
|
||||||
%link-action
|
|
||||||
!>([%hear where src.bowl page])
|
|
||||||
==
|
|
||||||
::
|
::
|
||||||
%annotations
|
%annotations
|
||||||
%+ turn notes.update
|
%+ turn notes.update
|
||||||
|= =note
|
|= =note
|
||||||
^- card
|
^- card
|
||||||
:* %pass
|
%+ do-link-action
|
||||||
[%forward -.update (scot %p who) where]
|
[%forward %annotation (scot %p who) where]
|
||||||
%agent
|
[%read where url.update who note]
|
||||||
[our.bowl %link-store]
|
|
||||||
%poke
|
|
||||||
%link-action
|
|
||||||
!>([%read where url.update src.bowl note])
|
|
||||||
==
|
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ take-forward-sign
|
++ take-forward-sign
|
||||||
|
@ -99,10 +99,17 @@
|
|||||||
++ permitted
|
++ permitted
|
||||||
|= [who=ship =path]
|
|= [who=ship =path]
|
||||||
^- ?
|
^- ?
|
||||||
:: we only expose /local-pages and /annotations,
|
:: we only expose group-specific /local-pages and /annotations,
|
||||||
:: and only to ships in the relevant group
|
:: 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
|
||||||
?& ?=(^ group)
|
?& ?=(^ group)
|
||||||
(~(has in u.group) who)
|
(~(has in u.group) who)
|
||||||
@ -112,7 +119,7 @@
|
|||||||
(scot %p our.bowl)
|
(scot %p our.bowl)
|
||||||
%group-store
|
%group-store
|
||||||
(scot %da now.bowl)
|
(scot %da now.bowl)
|
||||||
(snoc t.path %noun)
|
(snoc u.target %noun)
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
:: groups subscription
|
:: groups subscription
|
||||||
@ -158,21 +165,22 @@
|
|||||||
::
|
::
|
||||||
?: =(our.bowl i.whos)
|
?: =(our.bowl i.whos)
|
||||||
$(whos t.whos)
|
$(whos t.whos)
|
||||||
%+ weld $(whos t.whos)
|
:_ $(whos t.whos)
|
||||||
::NOTE this depends kind of unfortunately on the fact that we only accept
|
::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.
|
:: "just" looked at all paths in the map, and found the matching ones.
|
||||||
::TODO what exactly did i mean by this?
|
::TODO what exactly did i mean by this?
|
||||||
:~ (kick-proxy i.whos [%local-pages pax.upd])
|
%+ kick-proxies i.whos
|
||||||
(kick-proxy i.whos [%annotations pax.upd])
|
:~ [%local-pages pax.upd]
|
||||||
|
[%annotations '' pax.upd]
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
:: proxy subscriptions
|
:: proxy subscriptions
|
||||||
::
|
::
|
||||||
++ kick-proxy
|
++ kick-proxies
|
||||||
|= [who=ship =path]
|
|= [who=ship paths=(list path)]
|
||||||
^- card
|
^- card
|
||||||
[%give %kick ~[path] `who]
|
[%give %kick paths `who]
|
||||||
::
|
::
|
||||||
++ handle-proxy-sign
|
++ handle-proxy-sign
|
||||||
|= [=wire =sign:agent:gall]
|
|= [=wire =sign:agent:gall]
|
||||||
@ -204,15 +212,14 @@
|
|||||||
++ initial-response
|
++ initial-response
|
||||||
|= =path
|
|= =path
|
||||||
^- card
|
^- card
|
||||||
=; initial=update
|
=; =initial
|
||||||
[%give %fact ~ %link-update !>(initial)]
|
[%give %fact ~ %link-initial !>(initial)]
|
||||||
?+ path !!
|
?+ path !!
|
||||||
[%local-pages ^]
|
[%local-pages ^]
|
||||||
[%local-pages path .^(pages %gx path)]
|
[%local-pages .^((map ^path pages) %gx path)]
|
||||||
::
|
::
|
||||||
[%annotations @ ^]
|
[%annotations ~ ^]
|
||||||
=+ (split-discussion-path t.path)
|
[%annotations .^((per-path-url notes) %gx '' t.t.path)]
|
||||||
[%annotations path url .^(notes %gx path)]
|
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ start-proxy
|
++ 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
|
:: links, arbitrary paths are probably fair game, but could trip up
|
||||||
:: primitive ui implementations.
|
:: 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-listen-hook to see what's synced in, and similarly
|
||||||
:: see link-proxy-hook to see what's exposed.
|
:: see link-proxy-hook to see what's exposed.
|
||||||
::
|
::
|
||||||
:: scry and subscription paths:
|
:: scry and subscription paths:
|
||||||
::
|
::
|
||||||
:: urls
|
:: (map path pages)
|
||||||
:: /local-pages/[some-group] all pages we saved by recency
|
:: /local-pages our saved pages
|
||||||
:: /submissions/[some-group] all submissions by recency
|
:: /local-pages/some-path our saved pages on path
|
||||||
:: 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
|
|
||||||
::
|
::
|
||||||
::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
|
/+ *link, default-agent, verb, dbug
|
||||||
::
|
::
|
||||||
@ -27,7 +40,7 @@
|
|||||||
$: %0
|
$: %0
|
||||||
by-group=(map path links)
|
by-group=(map path links)
|
||||||
by-site=(map site (list [path submission]))
|
by-site=(map site (list [path submission]))
|
||||||
discussions=(map path (map url discussion))
|
discussions=(per-path-url discussion)
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
+$ links
|
+$ links
|
||||||
@ -81,25 +94,48 @@
|
|||||||
?+ path (on-peek:def path)
|
?+ path (on-peek:def path)
|
||||||
[%y ?(%local-pages %submissions) ~]
|
[%y ?(%local-pages %submissions) ~]
|
||||||
``noun+!>(~(key by by-group))
|
``noun+!>(~(key by by-group))
|
||||||
::
|
::
|
||||||
[%x %local-pages ^]
|
[%x %local-pages *]
|
||||||
``noun+!>((get-local-pages:do t.t.path))
|
``noun+!>((get-local-pages:do t.t.path))
|
||||||
::
|
::
|
||||||
[%x %submissions ^]
|
[%x %submissions *]
|
||||||
``noun+!>((get-submissions:do t.t.path))
|
``noun+!>((get-submissions:do t.t.path))
|
||||||
|
::
|
||||||
|
[%y ?(%annotations %discussions) *]
|
||||||
|
=/ [spath=^path surl=url]
|
||||||
|
(break-discussion-path t.t.path)
|
||||||
|
=- ``noun+!>(-)
|
||||||
::
|
::
|
||||||
[%y ?(%annotations %discussions) ~]
|
?: =(~ surl)
|
||||||
``noun+!>(~(key by discussions))
|
:: 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) ^]
|
?^ spath !!
|
||||||
=/ urls (~(get by discussions) t.t.path)
|
:: no path, find paths with comments for this url
|
||||||
?~ urls ~
|
|
||||||
``noun+!>(~(key by u.urls))
|
|
||||||
::
|
::
|
||||||
[%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))
|
``noun+!>((get-annotations:do t.t.path))
|
||||||
::
|
::
|
||||||
[%x %discussions @ ^]
|
[%x %discussions *]
|
||||||
``noun+!>((get-discussions:do t.t.path))
|
``noun+!>((get-discussions:do t.t.path))
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
@ -109,29 +145,25 @@
|
|||||||
?> (team:title [our src]:bowl) ::TODO /lib/store
|
?> (team:title [our src]:bowl) ::TODO /lib/store
|
||||||
:_ this
|
:_ this
|
||||||
|^ ?+ path (on-watch:def path)
|
|^ ?+ path (on-watch:def path)
|
||||||
[%local-pages ^]
|
[%local-pages *]
|
||||||
%+ give %link-update
|
%+ give %link-initial
|
||||||
[%local-pages t.path (get-local-pages:do t.path)]
|
^- initial
|
||||||
|
[%local-pages (get-local-pages:do t.path)]
|
||||||
::
|
::
|
||||||
[%submissions ^]
|
[%submissions *]
|
||||||
%+ give %link-update
|
%+ give %link-initial
|
||||||
[%submissions t.path (get-submissions:do t.path)]
|
^- initial
|
||||||
|
[%submissions (get-submissions:do t.path)]
|
||||||
::
|
::
|
||||||
[%allotations ^]
|
[%annotations *]
|
||||||
%+ turn
|
%+ give %link-initial
|
||||||
%~ tap by
|
^- initial
|
||||||
(~(gut by discussions) t.path *(map url discussion))
|
[%annotations (get-annotations:do t.path)]
|
||||||
|= [=url =discussion]
|
|
||||||
%+ give-single %link-update
|
|
||||||
[%annotations t.path url ours.discussion]
|
|
||||||
::
|
::
|
||||||
[%annotations @ ^]
|
[%discussions *]
|
||||||
%+ give %link-update
|
%+ give %link-initial
|
||||||
[%annotations t.path (get-annotations:do t.path)]
|
^- initial
|
||||||
::
|
[%discussions (get-discussions:do t.path)]
|
||||||
[%discussions @ ^]
|
|
||||||
%+ give %link-update
|
|
||||||
[%discussions t.path (get-discussions:do t.path)]
|
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ give
|
++ give
|
||||||
@ -186,7 +218,9 @@
|
|||||||
:_ state
|
:_ state
|
||||||
:_ cards
|
:_ cards
|
||||||
:+ %give %fact
|
:+ %give %fact
|
||||||
:+ [%local-pages path]~
|
:+ :~ /local-pages
|
||||||
|
[%local-pages path]
|
||||||
|
==
|
||||||
%link-update
|
%link-update
|
||||||
!>([%local-pages path [page]~])
|
!>([%local-pages path [page]~])
|
||||||
:: +note-note: save a note for a url
|
:: +note-note: save a note for a url
|
||||||
@ -210,13 +244,15 @@
|
|||||||
::
|
::
|
||||||
:_ state
|
:_ state
|
||||||
^- (list card)
|
^- (list card)
|
||||||
=/ fact
|
:_ cards
|
||||||
:- %link-update
|
:+ %give %fact
|
||||||
!>([%annotations path url [note]~])
|
:+ :~ /annotations
|
||||||
:* [%give %fact [%annotations (snoc path url)]~ fact]
|
[%annotations %$ path]
|
||||||
[%give %fact [%allotations path]~ fact]
|
[%annotations (build-discussion-path url)]
|
||||||
cards
|
[%annotations (build-discussion-path path url)]
|
||||||
==
|
==
|
||||||
|
%link-update
|
||||||
|
!>([%annotations path url [note]~])
|
||||||
:: +hear-submission: record page someone else saved
|
:: +hear-submission: record page someone else saved
|
||||||
::
|
::
|
||||||
++ hear-submission
|
++ hear-submission
|
||||||
@ -237,7 +273,9 @@
|
|||||||
:_ state
|
:_ state
|
||||||
:_ ~
|
:_ ~
|
||||||
:+ %give %fact
|
:+ %give %fact
|
||||||
:+ [%submissions path]~
|
:+ :~ /submissions
|
||||||
|
[%submissions path]
|
||||||
|
==
|
||||||
%link-update
|
%link-update
|
||||||
!>([%submissions path [submission]~])
|
!>([%submissions path [submission]~])
|
||||||
:: +read-comment: record a comment someone else made
|
:: +read-comment: record a comment someone else made
|
||||||
@ -255,42 +293,102 @@
|
|||||||
:: send updates to subscribers
|
:: send updates to subscribers
|
||||||
::
|
::
|
||||||
:_ state
|
:_ state
|
||||||
^- (list card)
|
:_ ~
|
||||||
=/ fact
|
:+ %give %fact
|
||||||
:- %link-update
|
:+ :~ /discussions
|
||||||
!>([%discussions path url [comment]~])
|
[%discussions '' path]
|
||||||
:~ [%give %fact [%discussions (snoc path url)]~ fact]
|
[%discussions (build-discussion-path url)]
|
||||||
[%give %fact [%discussions path]~ fact]
|
[%discussions (build-discussion-path path url)]
|
||||||
==
|
==
|
||||||
|
%link-update
|
||||||
|
!>([%discussions path url [comment]~])
|
||||||
::
|
::
|
||||||
:: reading
|
:: reading
|
||||||
::
|
::
|
||||||
++ get-local-pages
|
++ get-local-pages
|
||||||
|= =path
|
|= =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)
|
ours:(~(gut by by-group) path *links)
|
||||||
::
|
::
|
||||||
++ get-submissions
|
++ get-submissions
|
||||||
|= =path
|
|= =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)
|
submissions:(~(gut by by-group) path *links)
|
||||||
::
|
::
|
||||||
|
::
|
||||||
++ get-annotations
|
++ get-annotations
|
||||||
|= =path
|
|= =path
|
||||||
^- notes
|
^- (per-path-url notes)
|
||||||
ours:(get-discussion path)
|
=/ 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
|
++ get-discussions
|
||||||
|= =path
|
|= =path
|
||||||
^- comments
|
^- (per-path-url comments)
|
||||||
comments:(get-discussion path)
|
=/ args=[=^path =url]
|
||||||
::
|
(break-discussion-path path)
|
||||||
++ get-discussion
|
|^ ?~ path
|
||||||
|= =path
|
:: all paths
|
||||||
^- discussion
|
::
|
||||||
=/ [=^path =url]
|
(~(run by discussions) get-comments)
|
||||||
(split-discussion-path path)
|
:: specific path
|
||||||
=- (~(gut by -) url *discussion)
|
::
|
||||||
%+ ~(gut by discussions) path
|
%+ ~(put by *(per-path-url comments)) path.args
|
||||||
*(map ^url discussion)
|
%- 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
@ -115,7 +115,7 @@
|
|||||||
%link-store
|
%link-store
|
||||||
%link-proxy-hook
|
%link-proxy-hook
|
||||||
%link-listen-hook
|
%link-listen-hook
|
||||||
%link-server-hook
|
%link-view
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ deft-fish :: default connects
|
++ deft-fish :: default connects
|
||||||
|
@ -22,18 +22,110 @@
|
|||||||
%| (rsh 3 1 (scot %if p.host))
|
%| (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
|
||||||
^- [=^path =url]
|
^- [=^path =url]
|
||||||
~| [%path-too-short path]
|
?~ path [/ '']
|
||||||
?> (gth (lent path) 1) ::TODO ?= would TMI
|
:- t.path
|
||||||
=/ end=@ud (dec (lent path))
|
?: =(~ i.path) ''
|
||||||
:- (scag end path)
|
~| path
|
||||||
(de-base64:mimes:html (snag end 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
|
++ en-json
|
||||||
=, enjs:format
|
=, 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
|
++ submission
|
||||||
|= sub=^submission
|
|= sub=^submission
|
||||||
^- json
|
^- json
|
||||||
@ -47,16 +139,22 @@
|
|||||||
%- pairs
|
%- pairs
|
||||||
:~ 'title'^s+title.page
|
:~ 'title'^s+title.page
|
||||||
'url'^s+url.page
|
'url'^s+url.page
|
||||||
'timestamp'^(time time.page)
|
'time'^(time time.page)
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ comment
|
++ comment
|
||||||
|= =^comment
|
|= =^comment
|
||||||
^- json
|
^- json
|
||||||
|
=+ n=(note +.comment)
|
||||||
|
?> ?=([%o *] n)
|
||||||
|
o+(~(put by p.n) 'ship' (ship ship.comment))
|
||||||
|
::
|
||||||
|
++ note
|
||||||
|
|= =^note
|
||||||
|
^- json
|
||||||
%- pairs
|
%- pairs
|
||||||
:~ 'ship'^(ship ship.comment)
|
:~ 'time'^(time time.note)
|
||||||
'time'^(time time.comment)
|
'udon'^s+udon.note ::TODO convert?
|
||||||
'udon'^s+udon.comment ::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
|
:: link: subscription updates
|
||||||
::
|
::
|
||||||
::TODO this should include json conversion once mark performance improves
|
|
||||||
/- *link
|
/- *link
|
||||||
|_ =update
|
|_ =update
|
||||||
++ grow
|
++ grow
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
page
|
page
|
||||||
==
|
==
|
||||||
:: +note: a comment on some url
|
:: +note: a comment on some url
|
||||||
|
::
|
||||||
+$ note
|
+$ note
|
||||||
$: =time
|
$: =time
|
||||||
udon=@t
|
udon=@t
|
||||||
@ -40,6 +41,12 @@
|
|||||||
+$ notes (list note)
|
+$ notes (list note)
|
||||||
+$ comments (list comment)
|
+$ comments (list comment)
|
||||||
::
|
::
|
||||||
|
:: state builder
|
||||||
|
::
|
||||||
|
++ per-path-url
|
||||||
|
|$ [value]
|
||||||
|
(map path (map url value))
|
||||||
|
::
|
||||||
:: +action: local actions
|
:: +action: local actions
|
||||||
::
|
::
|
||||||
+$ action
|
+$ action
|
||||||
@ -60,6 +67,15 @@
|
|||||||
::
|
::
|
||||||
[%read =path =url comment]
|
[%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
|
:: +update: local updates
|
||||||
::
|
::
|
||||||
::NOTE we include paths/urls to support the "subscribed to all" case
|
::NOTE we include paths/urls to support the "subscribed to all" case
|
||||||
|
@ -1549,7 +1549,6 @@
|
|||||||
(emit-event channel-id [(en-json:html json)]~)
|
(emit-event channel-id [(en-json:html json)]~)
|
||||||
::
|
::
|
||||||
%kick
|
%kick
|
||||||
~& [%recieved-quit-from-gall channel-id]
|
|
||||||
=/ =json
|
=/ =json
|
||||||
=, enjs:format
|
=, enjs:format
|
||||||
%- pairs :~
|
%- pairs :~
|
||||||
|
41
pkg/interface/link/package-lock.json
generated
41
pkg/interface/link/package-lock.json
generated
@ -2148,7 +2148,8 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
@ -2172,13 +2173,15 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@ -2195,19 +2198,22 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@ -2338,7 +2344,8 @@
|
|||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"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",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
@ -2368,6 +2376,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
@ -2376,13 +2385,15 @@
|
|||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.3.5",
|
"version": "2.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
|
||||||
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
|
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
@ -2403,6 +2414,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
@ -2491,7 +2503,8 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
@ -2505,6 +2518,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@ -2600,7 +2614,8 @@
|
|||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
@ -2642,6 +2657,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^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",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
@ -2711,13 +2728,15 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
|
||||||
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
|
"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) {
|
getComments(path, url) {
|
||||||
let endpoint = "/~link/discussions" + path + "/" + window.btoa(url) + ".json?p=0";
|
return this.getCommentsPage.bind(this)(path, url, 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCommentsPage(path, url, page, index, commentPage) {
|
getCommentsPage(path, url, page) {
|
||||||
let endpoint = "/~link/discussions" + path + "/" + window.btoa(url) + ".json?p=" + commentPage;
|
//TODO factor out
|
||||||
let promise = await fetch(endpoint);
|
// encode the url into @ta-safe format, using logic from +wood
|
||||||
if (promise.ok) {
|
let strictUrl = '';
|
||||||
let responseData = await promise.json();
|
for (let i = 0; i < url.length; i++) {
|
||||||
let update = {
|
const char = url[i];
|
||||||
"link-update": {
|
let add = '';
|
||||||
commentPage: {
|
switch (char) {
|
||||||
path: path,
|
case ' ':
|
||||||
linkPage: page,
|
add = '.';
|
||||||
index: index,
|
break;
|
||||||
comPageNo: commentPage,
|
case '.':
|
||||||
data: responseData.page
|
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) + '.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
strictUrl = strictUrl + add;
|
||||||
store.handleEvent(update);
|
|
||||||
}
|
}
|
||||||
}
|
strictUrl = '~.' + strictUrl;
|
||||||
|
|
||||||
async getPage(path, page) {
|
const endpoint = '/json/' + page + '/discussions/' + strictUrl + path;
|
||||||
let endpoint = "/~link/submissions" + path + ".json?p=" + page;
|
this.bind.bind(this)(endpoint, 'PUT', this.authTokens.ship, 'link-view',
|
||||||
let promise = await fetch(endpoint);
|
(res) => {
|
||||||
if (promise.ok) {
|
if (res.data['initial-discussions']) {
|
||||||
let resolvedPage = await promise.json();
|
// these aren't returned with the response,
|
||||||
let update = {
|
// so this ensures the reducers know them.
|
||||||
"link-update": {
|
res.data['initial-discussions'].path = path;
|
||||||
page: {
|
res.data['initial-discussions'].url = url;
|
||||||
[path]: {
|
|
||||||
page: page,
|
|
||||||
links: resolvedPage.page
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
store.handleEvent(res);
|
||||||
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'
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify(json)
|
console.error,
|
||||||
});
|
()=>{} // no-op on quit
|
||||||
|
);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async postComment(path, url, comment, page, index) {
|
getPage(path, page) {
|
||||||
let json = {
|
const endpoint = '/json/' + page + '/submissions' + path;
|
||||||
path: path,
|
this.bind.bind(this)(endpoint, 'PUT', this.authTokens.ship, 'link-view',
|
||||||
url: url,
|
(dat)=>{store.handleEvent(dat)},
|
||||||
udon: comment
|
console.error,
|
||||||
}
|
()=>{} // no-op on quit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let endpoint = "/~link/note";
|
linkAction(data) {
|
||||||
let post = await fetch(endpoint, {
|
return this.action("link-store", "link-action", data);
|
||||||
method: "POST",
|
}
|
||||||
credentials: 'include',
|
|
||||||
headers: {
|
postLink(path, url, title) {
|
||||||
'Content-Type': 'application/json'
|
return this.linkAction({
|
||||||
},
|
'save': { path, url, title }
|
||||||
body: JSON.stringify(json)
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (post.ok) {
|
postComment(path, url, comment, page, index) {
|
||||||
let update = {
|
return this.linkAction({
|
||||||
"link-update": {
|
'note': { path, url, udon: comment }
|
||||||
commentAdd: {
|
});
|
||||||
path: path,
|
|
||||||
url: url,
|
|
||||||
udon: comment,
|
|
||||||
page: page,
|
|
||||||
index: index,
|
|
||||||
time: moment.now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
store.handleEvent(update);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sidebarToggle() {
|
sidebarToggle() {
|
||||||
|
@ -17,7 +17,7 @@ export class ChannelsSidebar extends Component {
|
|||||||
.map((path) => {
|
.map((path) => {
|
||||||
let name = "Private"
|
let name = "Private"
|
||||||
let selected = (props.selected === path);
|
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 (
|
return (
|
||||||
<ChannelsItem
|
<ChannelsItem
|
||||||
key={path}
|
key={path}
|
||||||
@ -40,8 +40,8 @@ export class ChannelsSidebar extends Component {
|
|||||||
name = name.substr(nameSeparator + 1);
|
name = name.substr(nameSeparator + 1);
|
||||||
|
|
||||||
let selected = (props.selected === path);
|
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 (
|
return (
|
||||||
<ChannelsItem
|
<ChannelsItem
|
||||||
key={path}
|
key={path}
|
||||||
@ -56,9 +56,9 @@ export class ChannelsSidebar extends Component {
|
|||||||
let activeClasses = (this.props.active === "channels") ? " " : "dn-s ";
|
let activeClasses = (this.props.active === "channels") ? " " : "dn-s ";
|
||||||
|
|
||||||
let hiddenClasses = true;
|
let hiddenClasses = true;
|
||||||
|
|
||||||
// probably a more concise way to write this
|
// probably a more concise way to write this
|
||||||
|
|
||||||
if (this.props.popout) {
|
if (this.props.popout) {
|
||||||
hiddenClasses = false;
|
hiddenClasses = false;
|
||||||
} else {
|
} else {
|
||||||
@ -73,10 +73,10 @@ export class ChannelsSidebar extends Component {
|
|||||||
: "dn")}>
|
: "dn")}>
|
||||||
<a className="db dn-m dn-l dn-xl f8 pb3 pl3" href="/">⟵ Landscape</a>
|
<a className="db dn-m dn-l dn-xl f8 pb3 pl3" href="/">⟵ Landscape</a>
|
||||||
<div className="overflow-y-scroll h-100">
|
<div className="overflow-y-scroll h-100">
|
||||||
<h2 className={`f8 f9-m f9-l f9-xl
|
<h2 className={`f8 f9-m f9-l f9-xl
|
||||||
pt1 pt4-m pt4-l pt4-xl
|
pt1 pt4-m pt4-l pt4-xl
|
||||||
pr4 pb3 pb2-m pb2-l pb2-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
|
black-s gray2 white-d c-default
|
||||||
bb bn-m bn-l bn-xl b--gray4 mb2 mb0-m mb0-l mb0-xl`}>
|
bb bn-m bn-l bn-xl b--gray4 mb2 mb0-m mb0-l mb0-xl`}>
|
||||||
Your Collections
|
Your Collections
|
||||||
|
@ -2,7 +2,7 @@ import React, { Component } from 'react'
|
|||||||
import { CommentItem } from './comment-item';
|
import { CommentItem } from './comment-item';
|
||||||
import { CommentsPagination } from './comments-pagination';
|
import { CommentsPagination } from './comments-pagination';
|
||||||
|
|
||||||
import { uxToHex } from '../../lib/util';
|
import { uxToHex } from '../../lib/util';
|
||||||
import { api } from '../../api';
|
import { api } from '../../api';
|
||||||
|
|
||||||
export class Comments extends Component {
|
export class Comments extends Component {
|
||||||
@ -10,12 +10,13 @@ export class Comments extends Component {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let page = "page" + this.props.commentPage;
|
let page = "page" + this.props.commentPage;
|
||||||
let comments = !!this.props.comments;
|
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(
|
api.getCommentsPage(
|
||||||
this.props.path,
|
this.props.path,
|
||||||
this.props.url,
|
this.props.url,
|
||||||
this.props.linkPage,
|
|
||||||
this.props.linkIndex,
|
|
||||||
this.props.commentPage);
|
this.props.commentPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,12 +25,10 @@ export class Comments extends Component {
|
|||||||
let page = "page" + this.props.commentPage;
|
let page = "page" + this.props.commentPage;
|
||||||
if (prevProps !== this.props) {
|
if (prevProps !== this.props) {
|
||||||
if (!!this.props.comments) {
|
if (!!this.props.comments) {
|
||||||
if ((page !== "page0") && (!this.props.comments[page])) {
|
if ((page !== "page0") && !this.props.comments[page] && this.props.url) {
|
||||||
api.getCommentsPage(
|
api.getCommentsPage(
|
||||||
this.props.path,
|
this.props.path,
|
||||||
this.props.url,
|
this.props.url,
|
||||||
this.props.linkPage,
|
|
||||||
this.props.linkIndex,
|
|
||||||
this.props.commentPage);
|
this.props.commentPage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +37,7 @@ export class Comments extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
let props = this.props;
|
let props = this.props;
|
||||||
|
|
||||||
let page = "page" + props.commentPage;
|
let page = "page" + props.commentPage;
|
||||||
|
|
||||||
let commentsObj = !!props.comments
|
let commentsObj = !!props.comments
|
||||||
@ -50,7 +49,7 @@ export class Comments extends Component {
|
|||||||
: {};
|
: {};
|
||||||
|
|
||||||
let total = !!props.comments
|
let total = !!props.comments
|
||||||
? props.comments["total-pages"]
|
? props.comments.totalPages
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
let commentsList = Object.keys(commentsPage)
|
let commentsList = Object.keys(commentsPage)
|
||||||
@ -59,7 +58,7 @@ export class Comments extends Component {
|
|||||||
let commentObj = commentsPage[entry]
|
let commentObj = commentsPage[entry]
|
||||||
let { ship, time, udon } = commentObj;
|
let { ship, time, udon } = commentObj;
|
||||||
|
|
||||||
let members = !!props.members
|
let members = !!props.members
|
||||||
? props.members
|
? props.members
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export class LinkItem extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let comments = props.comments + " comment" + ((props.comments === 1) ? "" : "s");
|
let comments = props.comments + " comment" + ((props.comments === 1) ? "" : "s");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-100 pv3 flex">
|
<div className="w-100 pv3 flex">
|
||||||
<Sigil
|
<Sigil
|
||||||
@ -63,8 +63,8 @@ export class LinkItem extends Component {
|
|||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
<div className="w-100 pt1">
|
<div className="w-100 pt1">
|
||||||
<span className={"f9 pr2 v-mid " + mono}>{(props.nickname)
|
<span className={"f9 pr2 v-mid " + mono}>{(props.nickname)
|
||||||
? props.nickname
|
? props.nickname
|
||||||
: "~" + props.ship}</span>
|
: "~" + props.ship}</span>
|
||||||
<span className="f9 inter gray2 pr3 v-mid">{this.state.timeSinceLinkPost}</span>
|
<span className="f9 inter gray2 pr3 v-mid">{this.state.timeSinceLinkPost}</span>
|
||||||
<Link to=
|
<Link to=
|
||||||
|
@ -19,11 +19,9 @@ export class LinkSubmit extends Component {
|
|||||||
let title = (this.state.linkTitle)
|
let title = (this.state.linkTitle)
|
||||||
? this.state.linkTitle
|
? this.state.linkTitle
|
||||||
: this.state.linkValue;
|
: this.state.linkValue;
|
||||||
let request = api.postLink(this.props.path, link, title);
|
api.postLink(this.props.path, link, title).then((r) => {
|
||||||
|
this.setState({linkValue: "", linkTitle: ""});
|
||||||
if (request) {
|
});
|
||||||
this.setState({linkValue: "", linkTitle: ""})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setLinkValid(link) {
|
setLinkValid(link) {
|
||||||
@ -58,7 +56,7 @@ export class LinkSubmit extends Component {
|
|||||||
let activeClasses = (this.state.linkValid)
|
let activeClasses = (this.state.linkValid)
|
||||||
? "green2 pointer"
|
? "green2 pointer"
|
||||||
: "gray2";
|
: "gray2";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative ba b--gray4 b--gray2-d br1 w-100 mb6">
|
<div className="relative ba b--gray4 b--gray2-d br1 w-100 mb6">
|
||||||
<textarea
|
<textarea
|
||||||
|
@ -18,34 +18,30 @@ export class LinkDetail extends Component {
|
|||||||
|
|
||||||
this.setComment = this.setComment.bind(this);
|
this.setComment = this.setComment.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
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,
|
// if we have preloaded our data,
|
||||||
// but no comments, grab the comments
|
// but no comments, grab the comments
|
||||||
if (!!this.props.data.url) {
|
} else if (!this.props.comments && this.props.data.url) {
|
||||||
let props = this.props;
|
api.getCommentsPage(this.props.path, this.props.data.url, this.props.commentPage);
|
||||||
let comments = !!props.data.comments;
|
|
||||||
|
|
||||||
if (!comments) {
|
|
||||||
api.getComments(props.path, props.data.url, props.page, props.link);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateTimeSinceNewestMessageInterval = setInterval( () => {
|
this.updateTimeSinceNewestMessageInterval = setInterval( () => {
|
||||||
this.setState({timeSinceLinkPost: this.getTimeSinceLinkPost()});
|
this.setState({timeSinceLinkPost: this.getTimeSinceLinkPost()});
|
||||||
}, 60000);
|
}, 60000);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
// if we came to this page *directly*,
|
// if we came to this page *directly*,
|
||||||
// load the comments -- DidMount will fail
|
// load the comments -- DidMount will fail
|
||||||
if (this.props.data.url !== prevProps.data.url) {
|
if ( (this.props.data.url !== prevProps.data.url) &&
|
||||||
let props = this.props;
|
(!this.props.comments && this.props.data.url)
|
||||||
let comments = !!this.props.data.comments;
|
) {
|
||||||
|
api.getCommentsPage(this.props.path, this.props.data.url, this.props.commentPage);
|
||||||
if (!comments && this.props.data.url) {
|
|
||||||
api.getComments(props.path, props.data.url, props.page, props.link);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.data.timestamp !== prevProps.data.timestamp) {
|
if (this.props.data.timestamp !== prevProps.data.timestamp) {
|
||||||
@ -70,10 +66,10 @@ export class LinkDetail extends Component {
|
|||||||
let url = this.props.data.url || "";
|
let url = this.props.data.url || "";
|
||||||
|
|
||||||
let request = api.postComment(
|
let request = api.postComment(
|
||||||
this.props.path,
|
this.props.path,
|
||||||
url,
|
url,
|
||||||
this.state.comment,
|
this.state.comment,
|
||||||
this.props.page,
|
this.props.page,
|
||||||
this.props.link
|
this.props.link
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -85,7 +81,7 @@ export class LinkDetail extends Component {
|
|||||||
setComment(event) {
|
setComment(event) {
|
||||||
this.setState({comment: event.target.value});
|
this.setState({comment: event.target.value});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let props = this.props;
|
let props = this.props;
|
||||||
let popout = (props.popout) ? "/popout" : "";
|
let popout = (props.popout) ? "/popout" : "";
|
||||||
@ -106,7 +102,7 @@ export class LinkDetail extends Component {
|
|||||||
let commentCount = props.data.commentCount || 0;
|
let commentCount = props.data.commentCount || 0;
|
||||||
|
|
||||||
let comments = commentCount + " comment" + ((commentCount === 1) ? "" : "s");
|
let comments = commentCount + " comment" + ((commentCount === 1) ? "" : "s");
|
||||||
|
|
||||||
let nickname = !!props.members[props.data.ship]
|
let nickname = !!props.members[props.data.ship]
|
||||||
? props.members[props.data.ship].nickname
|
? props.members[props.data.ship].nickname
|
||||||
: "";
|
: "";
|
||||||
@ -120,18 +116,18 @@ export class LinkDetail extends Component {
|
|||||||
let activeClasses = (this.state.comment)
|
let activeClasses = (this.state.comment)
|
||||||
? "black b--black pointer"
|
? "black b--black pointer"
|
||||||
: "gray2 b--gray2";
|
: "gray2 b--gray2";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-100 w-100 overflow-hidden flex flex-column">
|
<div className="h-100 w-100 overflow-hidden flex flex-column">
|
||||||
<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
|
overflow-x-auto-l overflow-x-auto-xl flex-shrink-0
|
||||||
bb bn-m bn-l bn-xl b--gray4`}
|
bb bn-m bn-l bn-xl b--gray4`}
|
||||||
style={{ height: 48 }}>
|
style={{ height: 48 }}>
|
||||||
<SidebarSwitcher
|
<SidebarSwitcher
|
||||||
sidebarShown={props.sidebarShown}
|
sidebarShown={props.sidebarShown}
|
||||||
popout={props.popout}/>
|
popout={props.popout}/>
|
||||||
<Link
|
<Link
|
||||||
className="dib f8 fw4 v-top pt2 gray2"
|
className="dib f8 fw4 v-top pt2 gray2"
|
||||||
to={"/~link" + popout + props.path + "/" + props.page}>
|
to={"/~link" + popout + props.path + "/" + props.page}>
|
||||||
{"<- Collection index"}
|
{"<- Collection index"}
|
||||||
@ -158,8 +154,8 @@ export class LinkDetail extends Component {
|
|||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
<div className="w-100 pt1">
|
<div className="w-100 pt1">
|
||||||
<span className={"f9 pr2 white-d v-mid " + nameClass}>{(nickname)
|
<span className={"f9 pr2 white-d v-mid " + nameClass}>{(nickname)
|
||||||
? nickname
|
? nickname
|
||||||
: "~" + ship}
|
: "~" + ship}
|
||||||
</span>
|
</span>
|
||||||
<span className="f9 inter gray2 pr3 v-mid">
|
<span className="f9 inter gray2 pr3 v-mid">
|
||||||
@ -184,7 +180,7 @@ export class LinkDetail extends Component {
|
|||||||
onChange={this.setComment}
|
onChange={this.setComment}
|
||||||
value={this.state.comment}
|
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}
|
+ activeClasses}
|
||||||
disabled={!this.state.comment}
|
disabled={!this.state.comment}
|
||||||
onClick={this.onClickPost.bind(this)}
|
onClick={this.onClickPost.bind(this)}
|
||||||
@ -195,11 +191,11 @@ export class LinkDetail extends Component {
|
|||||||
Post
|
Post
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<Comments
|
<Comments
|
||||||
path={props.path}
|
path={props.path}
|
||||||
key={props.path + props.commentPage}
|
key={props.path + props.commentPage}
|
||||||
comments={props.data.comments}
|
comments={props.comments}
|
||||||
commentPage={props.commentPage}
|
commentPage={props.commentPage}
|
||||||
members={props.members}
|
members={props.members}
|
||||||
popout={props.popout}
|
popout={props.popout}
|
||||||
url={props.data.url}
|
url={props.data.url}
|
||||||
@ -212,6 +208,5 @@ export class LinkDetail extends Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LinkDetail;
|
export default LinkDetail;
|
||||||
|
|
@ -13,15 +13,12 @@ import { uxToHex } from '../lib/util';
|
|||||||
export class Links extends Component {
|
export class Links extends Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
let linkPage = "page" + this.props.page;
|
this.componentDidUpdate();
|
||||||
if ((this.props.page !== 0) && (!this.props.links[linkPage])) {
|
|
||||||
api.getPage(this.props.path, this.props.page);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
let linkPage = "page" + this.props.page;
|
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);
|
api.getPage(this.props.path, this.props.page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,7 +38,7 @@ export class Links extends Component {
|
|||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
let totalPages = !!props.links
|
let totalPages = !!props.links
|
||||||
? Number(props.links["total-pages"])
|
? Number(props.links.totalPages)
|
||||||
: 1;
|
: 1;
|
||||||
|
|
||||||
let LinkList = Object.keys(links)
|
let LinkList = Object.keys(links)
|
||||||
@ -95,7 +92,7 @@ export class Links extends Component {
|
|||||||
<Link to="/~link/">{"⟵ All Channels"}</Link>
|
<Link to="/~link/">{"⟵ All Channels"}</Link>
|
||||||
</div>
|
</div>
|
||||||
<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
|
overflow-x-auto-l overflow-x-auto-xl flex-shrink-0
|
||||||
bb bn-m bn-l bn-xl b--gray4`}
|
bb bn-m bn-l bn-xl b--gray4`}
|
||||||
style={{ height: 48 }}>
|
style={{ height: 48 }}>
|
||||||
@ -104,7 +101,7 @@ export class Links extends Component {
|
|||||||
popout={props.popout}/>
|
popout={props.popout}/>
|
||||||
<Link to={`/~link` + popout + props.path} className="pt2">
|
<Link to={`/~link` + popout + props.path} className="pt2">
|
||||||
<h2
|
<h2
|
||||||
className={`dib f8 fw4 v-top ` +
|
className={`dib f8 fw4 v-top ` +
|
||||||
(props.path.includes("/~/")
|
(props.path.includes("/~/")
|
||||||
? ""
|
? ""
|
||||||
: "mono")}>
|
: "mono")}>
|
||||||
|
@ -32,14 +32,15 @@ export class Root extends Component {
|
|||||||
let paths = !!state.contacts ? state.contacts : {};
|
let paths = !!state.contacts ? state.contacts : {};
|
||||||
|
|
||||||
let links = !!state.links ? state.links : {};
|
let links = !!state.links ? state.links : {};
|
||||||
|
let comments = !!state.comments ? state.comments : {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Route exact path="/~link"
|
<Route exact path="/~link"
|
||||||
render={ (props) => {
|
render={ (props) => {
|
||||||
return (
|
return (
|
||||||
<Skeleton
|
<Skeleton
|
||||||
active="channels"
|
active="channels"
|
||||||
paths={paths}
|
paths={paths}
|
||||||
rightPanelHide={true}
|
rightPanelHide={true}
|
||||||
sidebarShown={true}
|
sidebarShown={true}
|
||||||
@ -58,7 +59,7 @@ export class Root extends Component {
|
|||||||
render={ (props) => {
|
render={ (props) => {
|
||||||
// groups/contacts and link channels are the same thing in ver 1
|
// groups/contacts and link channels are the same thing in ver 1
|
||||||
|
|
||||||
let groupPath =
|
let groupPath =
|
||||||
`/${props.match.params.ship}/${props.match.params.channel}`;
|
`/${props.match.params.ship}/${props.match.params.channel}`;
|
||||||
let groupMembers = paths[groupPath] || {};
|
let groupMembers = paths[groupPath] || {};
|
||||||
|
|
||||||
@ -66,8 +67,8 @@ export class Root extends Component {
|
|||||||
|
|
||||||
let popout = props.match.url.includes("/popout/");
|
let popout = props.match.url.includes("/popout/");
|
||||||
|
|
||||||
let channelLinks = !!links[groupPath]
|
let channelLinks = !!links[groupPath]
|
||||||
? links[groupPath]
|
? links[groupPath]
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -95,7 +96,7 @@ export class Root extends Component {
|
|||||||
/>
|
/>
|
||||||
<Route exact path="/~link/(popout)?/:ship/:channel/:page/:index/(comments)?/:commentpage?"
|
<Route exact path="/~link/(popout)?/:ship/:channel/:page/:index/(comments)?/:commentpage?"
|
||||||
render={ (props) => {
|
render={ (props) => {
|
||||||
let groupPath =
|
let groupPath =
|
||||||
`/${props.match.params.ship}/${props.match.params.channel}`;
|
`/${props.match.params.ship}/${props.match.params.channel}`;
|
||||||
|
|
||||||
let popout = props.match.url.includes("/popout/");
|
let popout = props.match.url.includes("/popout/");
|
||||||
@ -106,8 +107,13 @@ export class Root extends Component {
|
|||||||
let page = props.match.params.page || 0;
|
let page = props.match.params.page || 0;
|
||||||
|
|
||||||
let data = !!links[groupPath]
|
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;
|
let commentPage = props.match.params.commentpage || 0;
|
||||||
|
|
||||||
@ -130,6 +136,7 @@ export class Root extends Component {
|
|||||||
popout={popout}
|
popout={popout}
|
||||||
sidebarShown={state.sidebarShown}
|
sidebarShown={state.sidebarShown}
|
||||||
data={data}
|
data={data}
|
||||||
|
comments={coms}
|
||||||
commentPage={commentPage}
|
commentPage={commentPage}
|
||||||
/>
|
/>
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
|
@ -14,7 +14,7 @@ export class InitialReducer {
|
|||||||
state.groups[group] = new Set(data[group]);
|
state.groups[group] = new Set(data[group]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data = _.get(json, 'link', false);
|
data = _.get(json, 'link', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
let name = Object.keys(data)[0];
|
let name = Object.keys(data)[0];
|
||||||
|
@ -1,92 +1,141 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
const PAGE_SIZE = 25;
|
||||||
|
|
||||||
export class LinkUpdateReducer {
|
export class LinkUpdateReducer {
|
||||||
reduce(json, state) {
|
reduce(json, state) {
|
||||||
let data = _.get(json, 'link-update', false);
|
this.submissionsPage(json, state);
|
||||||
if (data) {
|
this.submissionsUpdate(json, state);
|
||||||
this.add(data, state);
|
this.discussionsPage(json, state);
|
||||||
this.comments(data, state);
|
this.discussionsUpdate(json, state);
|
||||||
this.commentAdd(data, state);
|
|
||||||
this.commentPage(data, state);
|
|
||||||
this.page(data, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
add(json, state) {
|
submissionsPage(json, state) {
|
||||||
// pin ok'd link POSTs to top of page0
|
let data = _.get(json, 'initial-submissions', false);
|
||||||
let data = _.get(json, 'add', false);
|
|
||||||
if (data) {
|
if (data) {
|
||||||
let path = Object.keys(data)[0];
|
// { "initial-submissions": {
|
||||||
let tempArray = state.links[path].page0;
|
// "/~ship/group": {
|
||||||
tempArray.unshift(data[path]);
|
// page: [{ship, timestamp, title, url}]
|
||||||
state.links[path].page0 = tempArray;
|
// page-number: 0
|
||||||
}
|
// total-items: 1
|
||||||
}
|
// total-pages: 1
|
||||||
|
// }
|
||||||
|
// } }
|
||||||
|
|
||||||
comments(json, state) {
|
for (var path of Object.keys(data)) {
|
||||||
let data = _.get(json, 'comments', false);
|
const here = data[path];
|
||||||
if (data) {
|
const page = "page" + here.pageNumber;
|
||||||
let path = data.path;
|
|
||||||
let page = "page" + data.page;
|
|
||||||
let index = data.index;
|
|
||||||
let storage = state.links[path][page][index];
|
|
||||||
|
|
||||||
storage.comments = {};
|
// if we didn't have any state for this path yet, initialize.
|
||||||
storage.comments["page0"] = data.data.page;
|
if (!state.links[path]) {
|
||||||
storage.comments["total-items"] = data.data["total-items"];
|
state.links[path] = {};
|
||||||
storage.comments["total-pages"] = data.data["total-pages"];
|
}
|
||||||
|
|
||||||
state.links[path][page][index] = storage;
|
// 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;
|
||||||
commentAdd(json, state) {
|
state.links[path].totalItems = here.totalItems;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let tempArray = state.links[path][page][index].comments["page0"];
|
|
||||||
tempArray.unshift(tempObj);
|
|
||||||
state.links[path][page][index].comments["page0"] = tempArray;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commentPage(json, state) {
|
submissionsUpdate(json, state) {
|
||||||
let data = _.get(json, 'commentPage', false);
|
let data = _.get(json, 'submissions', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
let path = data.path;
|
// { "submissions": {
|
||||||
let linkPage = "page" + data.linkPage;
|
// path: /~ship/group
|
||||||
let linkIndex = data.index;
|
// pages: [{ship, timestamp, title, url}]
|
||||||
let commentPage = "page" + data.comPageNo;
|
// } }
|
||||||
|
|
||||||
if (!state.links[path]) {
|
const path = data.path;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
discussionsPage(json, state) {
|
||||||
let data = _.get(json, 'page', false);
|
let data = _.get(json, 'initial-discussions', false);
|
||||||
if (data) {
|
if (data) {
|
||||||
let path = Object.keys(data)[0];
|
// { "initial-discussions": {
|
||||||
let page = "page" + data[path].page;
|
// path: "/~ship/group"
|
||||||
if (!state.links[path]) {
|
// url: https://urbit.org/
|
||||||
state.links[path] = {};
|
// 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: {},
|
contacts: {},
|
||||||
groups: {},
|
groups: {},
|
||||||
links: {},
|
links: {},
|
||||||
|
comments: {},
|
||||||
permissions: {},
|
permissions: {},
|
||||||
sidebarShown: true,
|
sidebarShown: true,
|
||||||
spinner: false
|
spinner: false
|
||||||
@ -23,24 +24,6 @@ class Store {
|
|||||||
this.setState = () => {};
|
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) {
|
setStateHandler(setState) {
|
||||||
this.setState = setState;
|
this.setState = setState;
|
||||||
}
|
}
|
||||||
@ -53,8 +36,7 @@ class Store {
|
|||||||
json = data;
|
json = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(json);
|
console.log('event', json);
|
||||||
this.loadLinks(json);
|
|
||||||
this.initialReducer.reduce(json, this.state);
|
this.initialReducer.reduce(json, this.state);
|
||||||
this.permissionUpdateReducer.reduce(json, this.state);
|
this.permissionUpdateReducer.reduce(json, this.state);
|
||||||
this.localReducer.reduce(json, this.state);
|
this.localReducer.reduce(json, this.state);
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import { api } from '/api';
|
import { api } from '/api';
|
||||||
import { store } from '/store';
|
import { store } from '/store';
|
||||||
|
|
||||||
import urbitOb from 'urbit-ob';
|
|
||||||
|
|
||||||
|
|
||||||
export class Subscription {
|
export class Subscription {
|
||||||
start() {
|
start() {
|
||||||
if (api.authTokens) {
|
if (api.authTokens) {
|
||||||
@ -24,6 +21,9 @@ export class Subscription {
|
|||||||
this.handleEvent.bind(this),
|
this.handleEvent.bind(this),
|
||||||
this.handleError.bind(this),
|
this.handleError.bind(this),
|
||||||
this.handleQuitAndResubscribe.bind(this));
|
this.handleQuitAndResubscribe.bind(this));
|
||||||
|
|
||||||
|
// open a subscription for all submissions
|
||||||
|
api.getPage('', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(diff) {
|
handleEvent(diff) {
|
||||||
|
Loading…
Reference in New Issue
Block a user