diff --git a/index.js b/index.js index e707e50..da8543d 100755 --- a/index.js +++ b/index.js @@ -45,7 +45,8 @@ if (config.debug) { const app = require('./src/app') const start = async () => { - config.readme = await fs.readFile(path.join(__dirname, 'README.md'), 'utf8') + const filePath = path.join(__dirname, 'README.md') + config.readme = await fs.readFile(filePath, 'utf8') app(config) } diff --git a/src/app.js b/src/app.js index 76ebe99..68d5f95 100644 --- a/src/app.js +++ b/src/app.js @@ -46,7 +46,8 @@ module.exports = (config) => { router .param('imageSize', (imageSize, ctx, next) => { const size = Number(imageSize) - ctx.assert(typeof size === 'number' && size % 1 === 0 && size > 2 && size < 1e10, 'Invalid image size') + const isInteger = size % 1 === 0 + ctx.assert(isInteger && size > 2 && size < 1e10, 'Invalid image size') return next() }) .param('blobId', (blobId, ctx, next) => { diff --git a/src/pages/mentions.js b/src/pages/mentions.js index 574258d..56b3c8e 100644 --- a/src/pages/mentions.js +++ b/src/pages/mentions.js @@ -3,7 +3,7 @@ const post = require('./models/post') const listView = require('./views/list') -module.exports = async function hashtag (channel) { +module.exports = async function mentionsPage () { const messages = await post.mentionsMe() return listView({ messages }) diff --git a/src/pages/models/lib/configure.js b/src/pages/models/lib/configure.js index 996822d..c92a51f 100644 --- a/src/pages/models/lib/configure.js +++ b/src/pages/models/lib/configure.js @@ -6,4 +6,5 @@ const defaultOptions = { meta: true } -module.exports = (...customOptions) => Object.assign({}, defaultOptions, ...customOptions) +module.exports = (...customOptions) => + Object.assign({}, defaultOptions, ...customOptions) diff --git a/src/pages/models/lib/cooler.js b/src/pages/models/lib/cooler.js index a8bd8ac..0767f86 100644 --- a/src/pages/models/lib/cooler.js +++ b/src/pages/models/lib/cooler.js @@ -49,7 +49,7 @@ const db = { }) }, read (method, ...args) { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { resolve(method(...args)) }) } @@ -57,7 +57,7 @@ const db = { debug.enabled = true -const handle = new Promise((resolve, reject) => { +const handle = new Promise((resolve) => { rawConnect().then((ssb) => { debug('Using pre-existing Scuttlebutt server instead of starting one') resolve(ssb) diff --git a/src/pages/models/lib/markdown.js b/src/pages/models/lib/markdown.js index b24cfd1..053524a 100644 --- a/src/pages/models/lib/markdown.js +++ b/src/pages/models/lib/markdown.js @@ -14,7 +14,7 @@ const toUrl = (mentions = []) => { } }) - return (ref, isImage) => { + return (ref) => { // @mentions if (ref in mentionNames) { return `/author/${encodeURIComponent(mentionNames[ref])}` diff --git a/src/pages/models/post.js b/src/pages/models/post.js index d95ea75..0c40381 100644 --- a/src/pages/models/post.js +++ b/src/pages/models/post.js @@ -36,110 +36,117 @@ const getMessages = async ({ myFeedId, customOptions, ssb, query }) => { }) } -const transform = (ssb, messages, myFeedId) => Promise.all(messages.map(async (msg) => { - debug('transforming %s', msg.key) +const transform = (ssb, messages, myFeedId) => + Promise.all(messages.map(async (msg) => { + debug('transforming %s', msg.key) - if (msg == null) { - return null - } - - lodash.set(msg, 'value.meta.md.block', () => markdown(msg.value.content.text, msg.value.content.mentions)) - - const filterQuery = { - $filter: { - dest: msg.key + if (msg == null) { + return null } - } - const referenceStream = await cooler.read(ssb.backlinks.read, { - query: [filterQuery], - index: 'DTA', // use asserted timestamps - private: true, - meta: true - }) - - const rawVotes = await new Promise((resolve, reject) => { - pull( - referenceStream, - pull.filter((ref) => typeof ref.value.content !== 'string' && - ref.value.content.type === 'vote' && - ref.value.content.vote && - typeof ref.value.content.vote.value === 'number' && - ref.value.content.vote.value >= 0 && - ref.value.content.vote.link === msg.key), - pull.collect((err, collectedMessages) => { - if (err) { - reject(err) - } else { - resolve(collectedMessages) - } - }) + lodash.set(msg, 'value.meta.md.block', () => + markdown(msg.value.content.text, msg.value.content.mentions) ) - }) - // { @key: 1, @key2: 0, @key3: 1 } - // - // only one vote per person! - const reducedVotes = rawVotes.reduce((acc, vote) => { - acc[vote.value.author] = vote.value.content.vote.value - return acc - }, {}) - - // gets *only* the people who voted 1 - // [ @key, @key, @key ] - const voters = Object.entries(reducedVotes).filter((e) => e[1] === 1).map((e) => e[0]) - - const pendingName = cooler.get( - ssb.about.socialValue, { - key: 'name', - dest: msg.value.author + const filterQuery = { + $filter: { + dest: msg.key + } } - ) - const pendingAvatarMsg = cooler.get( - ssb.about.socialValue, { - key: 'image', - dest: msg.value.author + const referenceStream = await cooler.read(ssb.backlinks.read, { + query: [filterQuery], + index: 'DTA', // use asserted timestamps + private: true, + meta: true + }) + + const rawVotes = await new Promise((resolve, reject) => { + pull( + referenceStream, + pull.filter((ref) => typeof ref.value.content !== 'string' && + ref.value.content.type === 'vote' && + ref.value.content.vote && + typeof ref.value.content.vote.value === 'number' && + ref.value.content.vote.value >= 0 && + ref.value.content.vote.link === msg.key), + pull.collect((err, collectedMessages) => { + if (err) { + reject(err) + } else { + resolve(collectedMessages) + } + }) + ) + }) + + // { @key: 1, @key2: 0, @key3: 1 } + // + // only one vote per person! + const reducedVotes = rawVotes.reduce((acc, vote) => { + acc[vote.value.author] = vote.value.content.vote.value + return acc + }, {}) + + // gets *only* the people who voted 1 + // [ @key, @key, @key ] + const voters = Object + .entries(reducedVotes) + .filter(([key, value]) => value === 1) + .map(([key]) => key) + + const pendingName = cooler.get( + ssb.about.socialValue, { + key: 'name', + dest: msg.value.author + } + ) + + const pendingAvatarMsg = cooler.get( + ssb.about.socialValue, { + key: 'image', + dest: msg.value.author + } + ) + + const pending = [pendingName, pendingAvatarMsg] + const [name, avatarMsg] = await Promise.all(pending) + + const nullImage = `&${'0'.repeat(43)}=.sha256` + const avatarId = avatarMsg != null && typeof avatarMsg.link === 'string' + ? avatarMsg.link || nullImage + : avatarMsg || nullImage + + const avatarUrl = `/image/32/${encodeURIComponent(avatarId)}` + + const ts = new Date(msg.value.timestamp) + let isoTs + + try { + isoTs = ts.toISOString() + } catch (e) { + // Just in case it's an invalid date. :( + debug(e) + const receivedTs = new Date(msg.timestamp) + isoTs = receivedTs.toISOString() } - ) - const pending = [pendingName, pendingAvatarMsg] - const [name, avatarMsg] = await Promise.all(pending) + lodash.set(msg, 'value.meta.timestamp.received.iso8601', isoTs) - const nullImage = `&${'0'.repeat(43)}=.sha256` - const avatarId = avatarMsg != null && typeof avatarMsg.link === 'string' - ? avatarMsg.link || nullImage - : avatarMsg || nullImage + const ago = Date.now() - Number(ts) + const prettyAgo = prettyMs(ago, { compact: true }) + lodash.set(msg, 'value.meta.timestamp.received.since', prettyAgo) + lodash.set(msg, 'value.meta.author.name', name) + lodash.set(msg, 'value.meta.author.avatar', { + id: avatarId, + url: avatarUrl + }) - const avatarUrl = `/image/32/${encodeURIComponent(avatarId)}` + lodash.set(msg, 'value.meta.votes', voters) + lodash.set(msg, 'value.meta.voted', voters.includes(myFeedId)) - const ts = new Date(msg.value.timestamp) - let isoTs - - try { - isoTs = ts.toISOString() - } catch (e) { - // Just in case it's an invalid date. :( - debug(e) - const receivedTs = new Date(msg.timestamp) - isoTs = receivedTs.toISOString() - } - - lodash.set(msg, 'value.meta.timestamp.received.iso8601', isoTs) - - const ago = Date.now() - Number(ts) - lodash.set(msg, 'value.meta.timestamp.received.since', prettyMs(ago, { compact: true })) - lodash.set(msg, 'value.meta.author.name', name) - lodash.set(msg, 'value.meta.author.avatar', { - id: avatarId, - url: avatarUrl - }) - - lodash.set(msg, 'value.meta.votes', voters) - lodash.set(msg, 'value.meta.voted', voters.includes(myFeedId)) - - return msg -})) + return msg + })) module.exports = { fromFeed: async (feedId, customOptions = {}) => { @@ -346,7 +353,11 @@ module.exports = { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat const flattenDeep = (arr1) => arr1.reduce( - (acc, val) => (Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val)), [] + (acc, val) => (Array.isArray(val) + ? acc.concat(flattenDeep(val)) + : acc.concat(val) + ), + [] ) const getDeepReplies = (key) => new Promise((resolve, reject) => { diff --git a/src/pages/views/author.js b/src/pages/views/author.js index 57316fd..3cef102 100644 --- a/src/pages/views/author.js +++ b/src/pages/views/author.js @@ -16,7 +16,8 @@ const template = require('./components/template') module.exports = ({ avatarUrl, name, description, messages, feedId }) => { - const markdownMention = highlightJs.highlight('markdown', `[@${name}](${feedId})`).value + const mention = `[@${name}](${feedId})` + const markdownMention = highlightJs.highlight('markdown', mention).value const prefix = section({ class: 'message' }, header({ class: 'profile' }, diff --git a/src/pages/views/components/post.js b/src/pages/views/components/post.js index fbbd019..405d51a 100644 --- a/src/pages/views/components/post.js +++ b/src/pages/views/components/post.js @@ -38,7 +38,11 @@ module.exports = ({ msg }) => { } const isPrivate = Boolean(msg.value.meta.private) - const isThreadTarget = Boolean(lodash.get(msg, 'value.meta.thread.target', false)) + const isThreadTarget = Boolean(lodash.get( + msg, + 'value.meta.thread.target', + false + )) const { name } = msg.value.meta.author const timeAgo = msg.value.meta.timestamp.received.since @@ -76,7 +80,10 @@ module.exports = ({ msg }) => { const emptyContent = '
undefined
\n' const articleElement = markdownContent === emptyContent ? article({ class: 'content' }, pre({ - innerHTML: highlightJs.highlight('json', JSON.stringify(msg, null, 2)).value + innerHTML: highlightJs.highlight( + 'json', + JSON.stringify(msg, null, 2) + ).value })) : article({ class: 'content', innerHTML: markdownContent }) diff --git a/src/pages/views/components/template.js b/src/pages/views/components/template.js index a0bfaf2..2d54d0f 100644 --- a/src/pages/views/components/template.js +++ b/src/pages/views/components/template.js @@ -16,7 +16,8 @@ const { const doctypeString = '' -const toAttributes = (obj) => Object.entries(obj).map(([key, val]) => `${key}=${val}`).join(', ') +const toAttributes = (obj) => + Object.entries(obj).map(([key, val]) => `${key}=${val}`).join(', ') module.exports = (...elements) => { const nodes = @@ -25,8 +26,14 @@ module.exports = (...elements) => { title('🏝️ Oasis'), link({ rel: 'stylesheet', href: '/assets/style.css' }), link({ rel: 'stylesheet', href: '/highlight/github.css' }), - meta({ name: 'description', content: 'friendly neighborhood scuttlebutt interface' }), - meta({ name: 'viewport', content: toAttributes({ width: 'device-width', 'initial-scale': 1 }) }) + meta({ + name: 'description', + content: 'friendly neighborhood scuttlebutt interface' + }), + meta({ + name: 'viewport', + content: toAttributes({ width: 'device-width', 'initial-scale': 1 }) + }) ), body( nav( diff --git a/src/pages/views/reply.js b/src/pages/views/reply.js index 3235d04..686f447 100644 --- a/src/pages/views/reply.js +++ b/src/pages/views/reply.js @@ -19,7 +19,11 @@ module.exports = ({ message }) => { return template( post({ msg: message }), form({ action: likeForm, method: 'post' }, - textarea({ autofocus: true, required: true, name: 'text' }, markdownMention), + textarea({ + autofocus: true, + required: true, + name: 'text' + }, markdownMention), button({ type: 'submit' }, 'reply'))