Permalink
Browse files

scroll to reply box on reply, context message on full thread

fixes #615
  • Loading branch information...
mmckegg committed Sep 30, 2017
1 parent 0c27672 commit 84f42d561913f861b364caecc35050feadc23744
View
@@ -0,0 +1,16 @@
var watch = require('mutant/watch')
module.exports = AnchorHook
function AnchorHook (name, current, cb) {
return function (element) {
return watch(current, (current) => {
if (current === name) {
window.requestAnimationFrame(() => {
element.scrollIntoView()
if (typeof cb === 'function') cb(element)
})
}
})
}
}
View
@@ -22,7 +22,7 @@ module.exports = function (root, cb) {
if (url.host) {
cb(href, true)
} else if (href !== '#') {
cb(href, false)
cb(href, false, anchor.anchor)
}
}
View
@@ -136,5 +136,6 @@
"Close": "Close",
"New Message": "New Message",
"unsubscribed from ": "unsubscribed from ",
"en": "en"
"en": "en",
"on ": "on "
}
View
@@ -45,13 +45,13 @@ module.exports = function (config) {
}))
setupContextMenuAndSpellCheck(api.config.sync.load())
const i18n = api.intl.sync.i18n
var id = api.keys.sync.id()
var latestUpdate = LatestUpdate()
var subscribedChannels = api.channel.obs.subscribed(id)
// prompt to setup profile on first use
onceTrue(api.sbot.obs.connection, (sbot) => {
sbot.latestSequence(sbot.id, (_, key) => {
@@ -126,7 +126,7 @@ module.exports = function (config) {
views.html
])
catchLinks(container, (href, external) => {
catchLinks(container, (href, external, anchor) => {
if (external) {
electron.shell.openExternal(href)
} else if (ref.isBlob(href)) {
@@ -137,11 +137,11 @@ module.exports = function (config) {
if (handler) {
handler(href)
} else {
api.app.navigate(href)
api.app.navigate(href, anchor)
}
})
} else {
api.app.navigate(href)
api.app.navigate(href, anchor)
}
})
@@ -211,8 +211,8 @@ module.exports = function (config) {
return element
}
function setView (href) {
views.setView(href)
function setView (href, anchor) {
views.setView(href, anchor)
}
function getExternalHandler (key, cb) {
View
@@ -99,7 +99,7 @@ exports.create = function (api) {
}
}
function setView (view) {
function setView (view, anchor) {
loadView(view)
if (views.has(view)) {
@@ -111,6 +111,12 @@ exports.create = function (api) {
lastViewed[currentView()] = Date.now()
}
var viewElement = views.get(view)
if (viewElement && typeof viewElement.setAnchor === 'function') {
viewElement.setAnchor(anchor)
}
if (view !== currentView()) {
canGoForward.set(false)
canGoBack.set(true)
@@ -209,14 +209,20 @@ exports.create = function (api) {
renderedMessage,
when(replyElements.length, [
when(replies.length > replyElements.length || partial,
h('a.full', {href: item.key}, [i18n('View full thread') +' (', replies.length, ')'])
h('a.full', {href: item.key, anchor: getFirstId(replyElements)}, [i18n('View full thread') + ' (', replies.length, ')'])
),
h('div.replies', replyElements)
])
])
}
})
function getFirstId (elements) {
if (Array.isArray(elements) && elements.length) {
return elements[0].dataset.id
}
}
function names (ids) {
var items = map(Array.from(ids), api.about.obs.name)
return computed([items], (names) => names.map((n) => `- ${n}`).join('\n'))
@@ -23,7 +23,7 @@ exports.gives = nest('message.html.compose')
exports.create = function (api) {
const i18n = api.intl.sync.i18n
return nest('message.html.compose', function ({shrink = true, meta, prepublish, placeholder = 'Write a message'}, cb) {
return nest('message.html.compose', function ({shrink = true, meta, hooks, prepublish, placeholder = 'Write a message'}, cb) {
var files = []
var filesById = {}
var focused = Value(false)
@@ -106,6 +106,7 @@ exports.create = function (api) {
])
var composer = h('Compose', {
hooks,
classList: [
when(expanded, '-expanded', '-contracted')
]
@@ -115,6 +116,10 @@ exports.create = function (api) {
actions
])
composer.focus = function () {
textArea.focus()
}
addSuggest(textArea, (inputText, cb) => {
if (inputText[0] === '@') {
cb(null, getProfileSuggestions(inputText.slice(1)))
@@ -28,9 +28,9 @@ exports.create = function (api) {
renderContactBlock(ids)
])
catchLinks(content, (href, external) => {
catchLinks(content, (href, external, anchor) => {
if (!external) {
api.app.navigate(href)
api.app.navigate(href, anchor)
close()
}
})
@@ -1,6 +1,7 @@
var { h, when, map, Proxy, Struct, Value, computed } = require('mutant')
var nest = require('depnest')
var ref = require('ssb-ref')
var AnchorHook = require('../../../../lib/anchor-hook')
exports.needs = nest({
'keys.sync.id': 'first',
@@ -11,7 +12,7 @@ exports.needs = nest({
compose: 'first'
},
'sbot.async.get': 'first',
'intl.sync.i18n':'first',
'intl.sync.i18n': 'first'
})
exports.gives = nest('page.html.render')
@@ -23,6 +24,7 @@ exports.create = function (api) {
var loader = h('div', {className: 'Loading -large'})
var result = Proxy(loader)
var anchor = Value()
var meta = Struct({
type: 'post',
@@ -35,6 +37,9 @@ exports.create = function (api) {
var compose = api.message.html.compose({
meta,
shrink: false,
hooks: [
AnchorHook('reply', anchor, (el) => el.focus())
],
placeholder: when(meta.recps, i18n('Write a private reply'), i18n('Write a public reply'))
})
@@ -69,10 +74,18 @@ exports.create = function (api) {
var container = h('Thread', [
h('div.messages', [
when(thread.branchId, h('a.full', {href: thread.rootId}, [i18n('View full thread')])),
when(thread.branchId, h('a.full', {href: thread.rootId, anchor: id}, [i18n('View full thread')])),
map(thread.messages, (msg) => {
return computed([msg, thread.previousKey(msg)], (msg, previousId) => {
return api.message.html.render(msg, {pageId: id, previousId, includeReferences: true})
return h('div', {
hooks: [AnchorHook(msg.key, anchor, showContext)]
}, [
api.message.html.render(msg, {
pageId: id,
previousId,
includeReferences: true
})
])
})
}, {
maxTime: 5,
@@ -84,10 +97,34 @@ exports.create = function (api) {
result.set(when(thread.sync, container, loader))
})
return h('div', {className: 'SplitView'}, [
var view = h('div', {className: 'SplitView'}, [
h('div.main', [
result
])
])
view.setAnchor = function (value) {
anchor.set(value)
}
return view
})
}
function showContext (element) {
var scrollParent = getScrollParent(element)
if (scrollParent) {
// ensure context is visible
scrollParent.scrollTop = Math.max(0, scrollParent.scrollTop - 100)
}
}
function getScrollParent (element) {
while (element.parentNode) {
if (element.parentNode.scrollTop) {
return element.parentNode
} else {
element = element.parentNode
}
}
}
@@ -0,0 +1,10 @@
var h = require('mutant/h')
var nest = require('depnest')
exports.gives = nest('message.html.action')
exports.create = (api) => {
return nest('message.html.action', function reply (msg) {
return h('a', { href: msg.key, anchor: 'reply' }, 'Reply')
})
}

0 comments on commit 84f42d5

Please sign in to comment.