git ssb

16+

Dominic / patchbay



Commit dc2e96d4d0a86072d9393a2d15d786a80cb341ff

Merge pull request #176 from ssbc/async_router

Async router normalisation
mix irving authored on 2/21/2018, 6:54:47 AM
GitHub committed on 2/21/2018, 6:54:47 AM
Parent: 084946f795412a5d314bb4c3f4848d887219c041
Parent: db575953239b738e0d908ed0e7005bc4aa45d14c

Files changed

app/async/catch-link-click.jschanged
app/html/app.jschanged
app/html/scroller.jschanged
app/html/tabs.jschanged
app/page/thread.jschanged
app/sync/catch-keyboard-shortcut.jschanged
app/sync/goTo.jschanged
app/sync/locationId.jsadded
router/async/normalise.jsadded
router/async/router.jsadded
app/async/catch-link-click.jsView
@@ -5,9 +5,9 @@
@@ -46,9 +46,10 @@
app/html/app.jsView
@@ -10,9 +10,8 @@
1010 'app.sync.initialise': 'first',
1111 'app.sync.window': 'reduce',
1212 'history.obs.location': 'first',
1313 'history.sync.push': 'first',
14- 'router.sync.router': 'first',
1514 'settings.sync.get': 'first',
1615 'settings.sync.set': 'first'
1716 })
1817
app/html/scroller.jsView
@@ -42,21 +42,23 @@
4242 }
4343 }
4444 }
4545
46 + return function scroll (d) {
47 + selectChild((!curMsgEl || d === 'first') ? container.firstChild
48 + : d < 0 ? curMsgEl.previousElementSibling || container.firstChild
49 + : d > 0 ? curMsgEl.nextElementSibling || container.lastChild
50 + : curMsgEl)
51 +
52 + return curMsgEl
53 + }
54 +
4655 function selectChild (el) {
4756 if (!el) { return }
4857
58 + if (!el.scrollIntoViewIfNeeded && !el.scrollIntoView) return
4959 ;(el.scrollIntoViewIfNeeded || el.scrollIntoView).call(el)
5060 el.focus()
5161 curMsgEl = el
5262 }
5363
54- return function scroll (d) {
55- selectChild((!curMsgEl || d === 'first') ? container.firstChild
56- : d < 0 ? curMsgEl.previousElementSibling || container.firstChild
57- : d > 0 ? curMsgEl.nextElementSibling || container.lastChild
58- : curMsgEl)
59-
60- return curMsgEl
61- }
6264 }
app/html/tabs.jsView
@@ -9,8 +9,9 @@
99 exports.needs = nest({
1010 'app.html.menu': 'first',
1111 'app.html.searchBar': 'first',
1212 'app.sync.goTo': 'first',
13 + 'app.sync.locationId': 'first',
1314 'history.obs.store': 'first',
1415 'history.sync.push': 'first'
1516 })
1617
@@ -38,9 +39,9 @@
3839 }
3940 const onClose = (page) => {
4041 var history = api.history.obs.store()
4142 const prunedHistory = history().filter(loc => {
42- return JSON.stringify(loc) != page.id
43 + return api.app.sync.locationId(loc) != page.id
4344 })
4445 history.set(prunedHistory)
4546 }
4647
app/page/thread.jsView
@@ -1,13 +1,16 @@
11 const { h, Struct, Value, when, computed, map, resolve, onceTrue } = require('mutant')
22 const nest = require('depnest')
3 +const get = require('lodash/get')
34 const { isFeed } = require('ssb-ref')
45
56 exports.gives = nest('app.page.thread')
67
78 exports.needs = nest({
89 'about.html.avatar': 'first',
910 'app.html.scroller': 'first',
11 + 'app.html.tabs': 'first',
12 + 'app.sync.locationId': 'first',
1013 'contact.obs.following': 'first',
1114 'feed.obs.thread': 'first',
1215 'keys.sync.id': 'first',
1316 'message.html.compose': 'first',
@@ -21,13 +24,15 @@
2124 exports.create = function (api) {
2225 return nest('app.page.thread', threadPage)
2326
2427 function threadPage (location) {
25- const { key } = location
28 + const root = get(location, 'value.content.root', location.key)
29 + const msg = location.key
30 + if (msg !== root) scrollDownToMessage(msg)
2631
2732 const myId = api.keys.sync.id()
2833 const ImFollowing = api.contact.obs.following(myId)
29- const { messages, isPrivate, rootId, lastId, channel, recps } = api.feed.obs.thread(key)
34 + const { messages, isPrivate, rootId, lastId, channel, recps } = api.feed.obs.thread(root)
3035 const meta = Struct({
3136 type: 'post',
3237 root: rootId,
3338 branch: lastId,
@@ -64,15 +69,15 @@
6469 placeholder: 'Write a reply',
6570 shrink: false
6671 })
6772 const content = h('section.content', map(messages, m => {
68- return api.message.html.render(resolve(m), {pageId: key})
73 + return api.message.html.render(resolve(m), {pageId: root})
6974 }))
7075 const { container } = api.app.html.scroller({ prepend: header, content, append: composer })
7176
7277 container.classList.add('Thread')
73- container.title = key
74- api.message.async.name(key, (err, name) => {
78 + container.title = msg
79 + api.message.async.name(msg, (err, name) => {
7580 if (err) throw err
7681 container.title = name
7782 })
7883
@@ -82,6 +87,26 @@
8287 channelInput.disabled = true
8388 })
8489
8590 return container
91 +
92 + function scrollDownToMessage (id) {
93 + const locationId = api.app.sync.locationId(location)
94 + const tabs = api.app.html.tabs()
95 + locateKey()
96 +
97 + function locateKey () {
98 + // wait till we're on the right page
99 + if (tabs.currentPage().id !== locationId) return setTimeout(locateKey, 200)
100 +
101 + if (!tabs.currentPage().scroll) return setTimeout(locateKey, 200)
102 +
103 + tabs.currentPage().scroll('first')
104 + const msg = tabs.currentPage().querySelector(`[data-id='${id}']`)
105 + if (!msg) return setTimeout(locateKey, 200)
106 +
107 + ;(msg.scrollIntoViewIfNeeded || msg.scrollIntoView).call(msg)
108 + msg.focus()
109 + }
110 + }
86111 }
87112 }
app/sync/catch-keyboard-shortcut.jsView
@@ -4,9 +4,9 @@
44
55 exports.needs = nest({
66 'app.html.searchBar': 'first',
77 'app.html.tabs': 'first',
8- 'app.sync.goTo': 'first'
8 + 'app.sync.goTo': 'first',
99 })
1010
1111 var gPressed = false
1212
@@ -42,9 +42,9 @@
4242 return ev.target.blur()
4343 }
4444 }
4545
46-function genericShortcuts (ev, { tabs, search, goTo }) {
46 +function genericShortcuts (ev, { tabs, search, goTo, back }) {
4747 // Messages
4848 if (ev.keyCode === 71) { // gg = scroll to top
4949 if (!gPressed) {
5050 gPressed = true
@@ -63,11 +63,11 @@
6363 return tabs.currentPage().scroll(1)
6464 case 75: // k = newer
6565 return tabs.currentPage().scroll(-1)
6666 case 13: // enter = open
67- return goToMessage(ev, { tabs, goTo })
67 + return goToMessage(ev, { goTo })
6868 case 79: // o = open
69- return goToMessage(ev, { tabs, goTo })
69 + return goToMessage(ev, { goTo })
7070 case 192: // ` = toggle raw message view
7171 return toggleRawMessage(ev)
7272
7373 // Tabs
@@ -99,32 +99,16 @@
9999 if (ev.shiftKey) search.activate('%', ev)
100100 }
101101 }
102102
103-function goToMessage (ev, { tabs, goTo }) {
103 +function goToMessage (ev, { goTo }) {
104104 const msg = ev.target
105105 if (!msg.classList.contains('Message')) return
106106
107- const { root, id } = msg.dataset
108- if (!root) return goTo(id)
109-
110- goTo(root)
111- setTimeout(() => scrollDownToMessage(id, tabs), 250)
107 + goTo(msg.dataset.id)
108 + // TODO - rm the dataset.root decorator
112109 }
113110
114-function scrollDownToMessage (id, tabs) {
115- tabs.currentPage().scroll('first')
116- locateKey()
117-
118- function locateKey () {
119- const msg = tabs.currentPage().querySelector(`[data-id='${id}']`)
120- if (msg === null) return setTimeout(locateKey, 100)
121-
122- ;(msg.scrollIntoViewIfNeeded || msg.scrollIntoView).call(msg)
123- msg.focus()
124- }
125-}
126-
127111 function toggleRawMessage (ev) {
128112 const msg = ev.target
129113 if (!msg.classList.contains('Message')) return
130114
app/sync/goTo.jsView
@@ -3,12 +3,13 @@
33 exports.gives = nest({ 'app.sync.goTo': true })
44
55 exports.needs = nest({
66 'app.html.tabs': 'first',
7 + 'app.sync.locationId': 'first',
78 'history.obs.store': 'first',
89 'history.sync.push': 'first',
9- 'router.sync.normalise': 'first',
10- 'router.sync.router': 'first'
10 + 'router.async.normalise': 'first',
11 + 'router.async.router': 'first'
1112 })
1213
1314 exports.create = function (api) {
1415 return nest('app.sync.goTo', goTo)
@@ -20,34 +21,44 @@
2021 // allows a refactor of catch-keyboard-shortcut + patch-inbox
2122 // - extracts scrollToMessage into app.page.thread
2223 // - router.sync.router would take (location, { position }) ?
2324
24- function goTo (location, openBackground = false, split = false) {
25- location = api.router.sync.normalise(location)
26- const locationId = JSON.stringify(location)
25 + function goTo (location, options = {}) {
26 + const {
27 + openBackground = false,
28 + split = false
29 + } = options
2730
2831 const tabs = api.app.html.tabs()
29- if (tabs.has(locationId)) {
30- tabs.select(locationId)
31- api.history.sync.push(location)
32- return true
33- }
3432
35- const page = api.router.sync.router(location)
36- if (!page) return
33 + // currently do normalisation here only to generate normalised locationId
34 + api.router.async.normalise(location, (err, location) => {
35 + const locationId = api.app.sync.locationId(location)
3736
38- page.id = page.id || locationId
39- tabs.add(page, !openBackground, split)
37 + if (tabs.has(locationId)) {
38 + tabs.select(locationId)
39 + api.history.sync.push(location)
4040
41- if (openBackground) {
42- const history = api.history.obs.store()
43- var _history = history()
44- var current = _history.pop()
41 + return true
42 + }
4543
46- history.set([ ..._history, location, current ])
47- } else {
48- api.history.sync.push(location)
49- }
44 + api.router.async.router(location, (err, page) => {
45 + if (err) throw err
5046
51- return openBackground
47 + if (!page) return
48 +
49 + page.id = page.id || locationId
50 + tabs.add(page, !openBackground, split)
51 +
52 + if (openBackground) {
53 + const history = api.history.obs.store()
54 + var _history = history()
55 + var current = _history.pop()
56 +
57 + history.set([ ..._history, location, current ])
58 + } else {
59 + api.history.sync.push(location)
60 + }
61 + })
62 + })
5263 }
5364 }
app/sync/locationId.jsView
@@ -1,0 +1,21 @@
1 +const nest = require('depnest')
2 +const get = require('lodash/get')
3 +const { isMsg } = require('ssb-ref')
4 +
5 +exports.gives = nest({ 'app.sync.locationId': true })
6 +
7 +exports.create = function (api) {
8 + return nest('app.sync.locationId', locationId)
9 +
10 + function locationId (location) {
11 + if (typeof location === 'string') return string
12 +
13 + if (isMsg(location.key)) {
14 + location = {
15 + key: get(location, 'value.content.root', location.key)
16 + }
17 + }
18 +
19 + return JSON.stringify(location)
20 + }
21 +}
router/async/normalise.jsView
@@ -1,0 +1,39 @@
1 +const nest = require('depnest')
2 +const { isBlob, isFeed, isMsg } = require('ssb-ref')
3 +
4 +exports.gives = nest('router.async.normalise')
5 +
6 +exports.needs = nest({
7 + 'message.sync.unbox': 'first',
8 + 'sbot.async.get': 'first'
9 +})
10 +
11 +exports.create = (api) => {
12 + return nest('router.async.normalise', normalise)
13 +
14 + function normalise (location, cb) {
15 + if (typeof location === 'object') cb(null, location)
16 + else if (isMsg(location)) {
17 + api.sbot.async.get(location, (err, value) => {
18 + if (err) cb(err)
19 + else {
20 + if (typeof value.content === 'string') value = api.message.sync.unbox(value)
21 + cb(null, {key: location, value})
22 + }
23 + })
24 + } else if (isBlob(location)) cb(null, { blob: location })
25 + else if (isChannel(location)) cb(null, { channel: location })
26 + else if (isFeed(location)) cb(null, { feed: location })
27 + else if (isPage(location)) cb(null, { page: location.substring(1) })
28 +
29 + return true
30 + }
31 +}
32 +
33 +function isChannel (str) {
34 + return typeof str === 'string' && str[0] === '#' && str.length > 1
35 +}
36 +
37 +function isPage (str) {
38 + return typeof str === 'string' && str[0] === '/' && str.length > 1
39 +}
router/async/router.jsView
@@ -1,0 +1,36 @@
1 +const nest = require('depnest')
2 +
3 +exports.gives = nest('router.async.router')
4 +
5 +exports.needs = nest({
6 + 'router.async.normalise': 'first',
7 + 'router.sync.routes': 'reduce'
8 +})
9 +
10 +exports.create = (api) => {
11 + var router = null
12 +
13 + return nest('router.async.router', (location, cb) => {
14 + if (!router) {
15 + router = Router(api.router.sync.routes())
16 + }
17 +
18 + api.router.async.normalise(location, (err, normLocation) => {
19 + if (err) return cb(err)
20 +
21 + router(normLocation, cb)
22 + })
23 +
24 + // stop depject 'first' after this method
25 + return true
26 + })
27 +}
28 +
29 +function Router (routes) {
30 + return (location, cb) => {
31 + const route = routes.find(([validator]) => validator(location))
32 + // signature of a route is [ routeValidator, routeFunction ]
33 +
34 + if (route) cb(null, route[1](location))
35 + }
36 +}

Built with git-ssb-web