Commit a41e049ca3755ddfd97e7f53c9da830711e636f1
implement asyn router, make beautiful scrollTo (fixes stupid reply button)
mix irving committed on 2/8/2018, 4:53:54 AMParent: d5d67784390b67e685e8675d95cabae952961b35
Files changed
app/async/catch-link-click.js | changed |
app/html/tabs.js | changed |
app/page/thread.js | changed |
app/sync/catch-keyboard-shortcut.js | changed |
app/sync/goTo.js | changed |
app/sync/locationId.js | added |
router/async/normalise.js | changed |
router/async/router.js | changed |
app/async/catch-link-click.js | ||
---|---|---|
@@ -5,9 +5,9 @@ | ||
5 | 5 … | |
6 | 6 … | exports.needs = nest({ |
7 | 7 … | 'app.html.externalConfirm': 'first', |
8 | 8 … | 'app.sync.goTo': 'first', |
9 | - 'router.sync.normalise': 'first' | |
9 … | + 'router.async.normalise': 'first' | |
10 | 10 … | }) |
11 | 11 … | |
12 | 12 … | exports.create = function (api) { |
13 | 13 … | return nest('app.async.catchLinkClick', catchLinkClick) |
@@ -46,9 +46,10 @@ | ||
46 | 46 … | |
47 | 47 … | function defaultCallback (link, { ctrlKey, isExternal }) { |
48 | 48 … | if (isExternal) return api.app.html.externalConfirm(link) |
49 | 49 … | |
50 | - const location = api.router.sync.normalise(link) | |
51 | 50 … | const openBackground = ctrlKey |
52 | - api.app.sync.goTo(location, openBackground) | |
51 … | + api.router.async.normalise(link, (err, location) => { | |
52 … | + if (location) api.app.sync.goTo(location, { openBackground }) | |
53 … | + }) | |
53 | 54 … | } |
54 | 55 … | } |
app/html/tabs.js | ||
---|---|---|
@@ -29,8 +29,9 @@ | ||
29 | 29 … | |
30 | 30 … | try { |
31 | 31 … | var location = JSON.parse(id) |
32 | 32 … | } catch (e) { |
33 … | + debugger | |
33 | 34 … | throw new Error('app.html.tabs expects all page ids to be stringified location objects') |
34 | 35 … | } |
35 | 36 … | |
36 | 37 … | api.history.sync.push(location) |
app/page/thread.js | |||
---|---|---|---|
@@ -1,13 +1,16 @@ | |||
1 | 1 … | const { h, Struct, Value, when, computed, map, resolve, onceTrue } = require('mutant') | |
2 | 2 … | const nest = require('depnest') | |
3 … | +const get = require('lodash/get') | ||
3 | 4 … | const { isFeed } = require('ssb-ref') | |
4 | 5 … | ||
5 | 6 … | exports.gives = nest('app.page.thread') | |
6 | 7 … | ||
7 | 8 … | exports.needs = nest({ | |
8 | 9 … | 'about.html.avatar': 'first', | |
9 | 10 … | 'app.html.scroller': 'first', | |
11 … | + 'app.html.tabs': 'first', | ||
12 … | + 'app.sync.locationId': 'first', | ||
10 | 13 … | 'contact.obs.following': 'first', | |
11 | 14 … | 'feed.obs.thread': 'first', | |
12 | 15 … | 'keys.sync.id': 'first', | |
13 | 16 … | 'message.html.compose': 'first', | |
@@ -21,13 +24,15 @@ | |||
21 | 24 … | exports.create = function (api) { | |
22 | 25 … | return nest('app.page.thread', threadPage) | |
23 | 26 … | ||
24 | 27 … | 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) | ||
26 | 31 … | ||
27 | 32 … | const myId = api.keys.sync.id() | |
28 | 33 … | 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) | ||
30 | 35 … | const meta = Struct({ | |
31 | 36 … | type: 'post', | |
32 | 37 … | root: rootId, | |
33 | 38 … | branch: lastId, | |
@@ -64,15 +69,15 @@ | |||
64 | 69 … | placeholder: 'Write a reply', | |
65 | 70 … | shrink: false | |
66 | 71 … | }) | |
67 | 72 … | 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}) | ||
69 | 74 … | })) | |
70 | 75 … | const { container } = api.app.html.scroller({ prepend: header, content, append: composer }) | |
71 | 76 … | ||
72 | 77 … | 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) => { | ||
75 | 80 … | if (err) throw err | |
76 | 81 … | container.title = name | |
77 | 82 … | }) | |
78 | 83 … | ||
@@ -82,6 +87,26 @@ | |||
82 | 87 … | channelInput.disabled = true | |
83 | 88 … | }) | |
84 | 89 … | ||
85 | 90 … | return container | |
91 … | + | ||
92 … | + | ||
93 … | + function scrollDownToMessage (id) { | ||
94 … | + const locationId = api.app.sync.locationId(location) | ||
95 … | + const tabs = api.app.html.tabs() | ||
96 … | + locateKey() | ||
97 … | + | ||
98 … | + function locateKey () { | ||
99 … | + // wait till we're on the right page | ||
100 … | + if (tabs.currentPage().id !== locationId) return setTimeout(locateKey, 200) | ||
101 … | + | ||
102 … | + tabs.currentPage().scroll('first') | ||
103 … | + const msg = tabs.currentPage().querySelector(`[data-id='${id}']`) | ||
104 … | + if (msg === null) return setTimeout(locateKey, 200) | ||
105 … | + | ||
106 … | + ;(msg.scrollIntoViewIfNeeded || msg.scrollIntoView).call(msg) | ||
107 … | + msg.focus() | ||
108 … | + } | ||
109 … | + } | ||
86 | 110 … | } | |
87 | 111 … | } | |
112 … | + |
app/sync/catch-keyboard-shortcut.js | ||
---|---|---|
@@ -63,11 +63,11 @@ | ||
63 | 63 … | return tabs.currentPage().scroll(1) |
64 | 64 … | case 75: // k = newer |
65 | 65 … | return tabs.currentPage().scroll(-1) |
66 | 66 … | case 13: // enter = open |
67 | - return goToMessage(ev, { tabs, goTo }) | |
67 … | + return goToMessage(ev, { goTo }) | |
68 | 68 … | case 79: // o = open |
69 | - return goToMessage(ev, { tabs, goTo }) | |
69 … | + return goToMessage(ev, { goTo }) | |
70 | 70 … | case 192: // ` = toggle raw message view |
71 | 71 … | return toggleRawMessage(ev) |
72 | 72 … | |
73 | 73 … | // Tabs |
@@ -99,32 +99,17 @@ | ||
99 | 99 … | if (ev.shiftKey) search.activate('%', ev) |
100 | 100 … | } |
101 | 101 … | } |
102 | 102 … | |
103 | -function goToMessage (ev, { tabs, goTo }) { | |
103 … | +function goToMessage (ev, { goTo }) { | |
104 | 104 … | const msg = ev.target |
105 | 105 … | if (!msg.classList.contains('Message')) return |
106 | 106 … | |
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 | |
112 | 109 … | } |
113 | 110 … | |
114 | -function scrollDownToMessage (id, tabs) { | |
115 | - tabs.currentPage().scroll('first') | |
116 | - locateKey() | |
117 | 111 … | |
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 | - | |
127 | 112 … | function toggleRawMessage (ev) { |
128 | 113 … | const msg = ev.target |
129 | 114 … | if (!msg.classList.contains('Message')) return |
130 | 115 … |
app/sync/goTo.js | ||
---|---|---|
@@ -3,8 +3,9 @@ | ||
3 | 3 … | exports.gives = nest({ 'app.sync.goTo': true }) |
4 | 4 … | |
5 | 5 … | exports.needs = nest({ |
6 | 6 … | 'app.html.tabs': 'first', |
7 … | + 'app.sync.locationId': 'first', | |
7 | 8 … | 'history.obs.store': 'first', |
8 | 9 … | 'history.sync.push': 'first', |
9 | 10 … | 'router.async.normalise': 'first', |
10 | 11 … | 'router.async.router': 'first' |
@@ -20,23 +21,28 @@ | ||
20 | 21 … | // allows a refactor of catch-keyboard-shortcut + patch-inbox |
21 | 22 … | // - extracts scrollToMessage into app.page.thread |
22 | 23 … | // - router.sync.router would take (location, { position }) ? |
23 | 24 … | |
24 | - function goTo (location, openBackground = false, split = false) { | |
25 | - api.router.async.normalise(location, (err, location) => { | |
26 | - if (err) throw err | |
25 … | + function goTo (location, options = {}) { | |
26 … | + const { | |
27 … | + openBackground = false, | |
28 … | + split = false | |
29 … | + } = options | |
27 | 30 … | |
28 | - const locationId = JSON.stringify(location) | |
31 … | + const tabs = api.app.html.tabs() | |
29 | 32 … | |
30 | - const tabs = api.app.html.tabs() | |
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) | |
36 … | + | |
31 | 37 … | if (tabs.has(locationId)) { |
32 | 38 … | tabs.select(locationId) |
33 | 39 … | api.history.sync.push(location) |
40 … | + | |
34 | 41 … | return true |
35 | 42 … | } |
36 | 43 … | |
37 | 44 … | api.router.async.router(location, (err, page) => { |
38 | - debugger | |
39 | 45 … | if (err) throw err |
40 | 46 … | |
41 | 47 … | if (!page) return |
42 | 48 … | |
@@ -51,10 +57,9 @@ | ||
51 | 57 … | history.set([ ..._history, location, current ]) |
52 | 58 … | } else { |
53 | 59 … | api.history.sync.push(location) |
54 | 60 … | } |
61 … | + | |
55 | 62 … | }) |
56 | - | |
57 | - // return openBackground // might not be needed? | |
58 | 63 … | }) |
59 | 64 … | } |
60 | 65 … | } |
app/sync/locationId.js | ||
---|---|---|
@@ -1,0 +1,20 @@ | ||
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)) location = { | |
14 … | + key: get(location, 'value.content.root', location.key) | |
15 … | + } | |
16 … | + | |
17 … | + return JSON.stringify(location) | |
18 … | + } | |
19 … | +} | |
20 … | + |
router/async/normalise.js | ||
---|---|---|
@@ -4,19 +4,24 @@ | ||
4 | 4 … | exports.gives = nest('router.async.normalise') |
5 | 5 … | |
6 | 6 … | exports.needs = nest({'sbot.async.get': 'first'}) |
7 | 7 … | |
8 | -exports.create = (api) => nest('router.async.normalise', normalise) | |
8 … | +exports.create = (api) => { | |
9 … | + return nest('router.async.normalise', normalise) | |
9 | 10 … | |
10 | -function normalise (location, cb) { | |
11 | - if (typeof location === 'object') return location | |
11 … | + function normalise (location, cb) { | |
12 … | + if (typeof location === 'object') cb(null, location) | |
13 … | + else if (isMsg(location)) api.sbot.async.get(location, (err, value) => { | |
14 … | + if (err) cb(err) | |
15 … | + else cb(null, {key: location, value}) | |
16 … | + }) | |
17 … | + else if (isBlob(location)) cb(null, { blob: location }) | |
18 … | + else if (isChannel(location)) cb(null, { channel: location }) | |
19 … | + else if (isFeed(location)) cb(null, { feed: location }) | |
20 … | + else if (isPage(location)) cb(null, { page: location.substring(1) }) | |
12 | 21 … | |
13 | - if (isMsg(location)) api.sbot.async.get(location, cb) | |
14 | - | |
15 | - if (isBlob(location)) cb(null, { blob: location }) | |
16 | - if (isChannel(location)) cb(null, { channel: location }) | |
17 | - if (isFeed(location)) cb(null, { feed: location }) | |
18 | - if (isPage(location)) cb(null, { page: location.substring(1) }) | |
22 … | + return true | |
23 … | + } | |
19 | 24 … | } |
20 | 25 … | |
21 | 26 … | function isChannel (str) { |
22 | 27 … | return typeof str === 'string' && str[0] === '#' && str.length > 1 |
router/async/router.js | ||
---|---|---|
@@ -17,20 +17,18 @@ | ||
17 | 17 … | |
18 | 18 … | api.router.async.normalise(location, (err, normLocation) => { |
19 | 19 … | if (err) return cb(err) |
20 | 20 … | |
21 | - debugger | |
22 | - | |
23 | 21 … | router(normLocation, cb) |
24 | 22 … | }) |
25 | 23 … | |
26 | - // return true | |
24 … | + // stop depject 'first' after this method | |
25 … | + return true | |
27 | 26 … | }) |
28 | 27 … | } |
29 | 28 … | |
30 | 29 … | function Router (routes) { |
31 | 30 … | return (location, cb) => { |
32 | - debugger | |
33 | 31 … | const route = routes.find(([validator]) => validator(location)) |
34 | 32 … | // signature of a route is [ routeValidator, routeFunction ] |
35 | 33 … | |
36 | 34 … | if (route) cb(null, route[1](location)) |
Built with git-ssb-web