Update npm dependencies

Problem: Our dependencies are falling behind and it'd be nice to have
them up-to-date. The only big update it that Common-Good upgraded to the
latest version of Prettier.

Solution: Update them!
This commit is contained in:
Christian Bundy 2020-03-23 15:54:28 -07:00
parent 8c71d93ab3
commit 32465e5983
12 changed files with 1029 additions and 844 deletions

View File

@ -2,15 +2,15 @@ module.exports = {
env: { env: {
commonjs: true, commonjs: true,
es6: true, es6: true,
node: true node: true,
}, },
extends: "eslint:recommended", extends: "eslint:recommended",
globals: { globals: {
Atomics: "readonly", Atomics: "readonly",
SharedArrayBuffer: "readonly" SharedArrayBuffer: "readonly",
}, },
parserOptions: { parserOptions: {
ecmaVersion: 2018 ecmaVersion: 2018,
}, },
rules: {} rules: {},
}; };

View File

@ -17,5 +17,5 @@ os:
# Don't build arbitrary branches, just pull requests + master + semver tags. # Don't build arbitrary branches, just pull requests + master + semver tags.
branches: branches:
only: only:
- master - master
- "/^v\\d+\\.\\d+\\.\\d+$/" - "/^v\\d+\\.\\d+\\.\\d+$/"

1108
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,8 @@
}, },
"dependencies": { "dependencies": {
"@fraction/base16-css": "^1.1.0", "@fraction/base16-css": "^1.1.0",
"@fraction/flotilla": "^4.0.1", "@fraction/flotilla": "^4.0.0",
"@koa/router": "^8.0.0",
"debug": "^4.1.1", "debug": "^4.1.1",
"env-paths": "^2.2.0", "env-paths": "^2.2.0",
"highlight.js": "^9.18.1", "highlight.js": "^9.18.1",
@ -31,12 +32,11 @@
"koa": "^2.7.0", "koa": "^2.7.0",
"koa-body": "^4.1.0", "koa-body": "^4.1.0",
"koa-mount": "^4.0.0", "koa-mount": "^4.0.0",
"koa-router": "^7.4.0",
"koa-static": "^5.0.0", "koa-static": "^5.0.0",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"markdown-it": "^8.4.2", "markdown-it": "^8.4.2",
"open": "7.0.0", "open": "^7.0.3",
"pretty-ms": "^5.0.0", "pretty-ms": "^6.0.0",
"pull-paramap": "^1.2.2", "pull-paramap": "^1.2.2",
"pull-sort": "^1.0.2", "pull-sort": "^1.0.2",
"pull-stream": "^3.6.12", "pull-stream": "^3.6.12",
@ -44,19 +44,19 @@
"sharp": "^0.23.0", "sharp": "^0.23.0",
"ssb-client": "^4.9.0", "ssb-client": "^4.9.0",
"ssb-config": "^3.4.4", "ssb-config": "^3.4.4",
"ssb-markdown": "^6.0.4", "ssb-markdown": "^6.0.5",
"ssb-mentions": "^0.5.2", "ssb-mentions": "^0.5.2",
"ssb-msgs": "^5.2.0", "ssb-msgs": "^5.2.0",
"ssb-ref": "^2.13.9", "ssb-ref": "^2.13.9",
"ssb-tangle": "^1.0.1", "ssb-tangle": "^1.0.1",
"ssb-thread-schema": "^1.1.1", "ssb-thread-schema": "^1.1.1",
"yargs": "^15.3.0" "yargs": "^15.3.1"
}, },
"devDependencies": { "devDependencies": {
"changelog-version": "^1.0.1", "changelog-version": "^1.0.1",
"common-good": "^1.1.20", "common-good": "^2.0.2",
"husky": "^3.0.5", "husky": "^4.0.0",
"mkdirp": "^0.5.1", "mkdirp": "^1.0.0",
"nodemon": "^2.0.2", "nodemon": "^2.0.2",
"stylelint-config-recommended": "^3.0.0" "stylelint-config-recommended": "^3.0.0"
}, },

View File

@ -14,33 +14,33 @@ module.exports = (presets, defaultConfigFile) =>
describe: describe:
"Automatically open app in web browser. Use --no-open to disable.", "Automatically open app in web browser. Use --no-open to disable.",
default: _.get(presets, "open", true), default: _.get(presets, "open", true),
type: "boolean" type: "boolean",
}) })
.options("offline", { .options("offline", {
describe: describe:
"Don't try to connect to scuttlebutt peers or pubs. This can be changed on the 'settings' page while Oasis is running.", "Don't try to connect to scuttlebutt peers or pubs. This can be changed on the 'settings' page while Oasis is running.",
default: _.get(presets, "offline", false), default: _.get(presets, "offline", false),
type: "boolean" type: "boolean",
}) })
.options("host", { .options("host", {
describe: "Hostname for web app to listen on", describe: "Hostname for web app to listen on",
default: _.get(presets, "host", "localhost"), default: _.get(presets, "host", "localhost"),
type: "string" type: "string",
}) })
.options("port", { .options("port", {
describe: "Port for web app to listen on", describe: "Port for web app to listen on",
default: _.get(presets, "port", 3000), default: _.get(presets, "port", 3000),
type: "number" type: "number",
}) })
.options("public", { .options("public", {
describe: describe:
"Assume Oasis is being hosted publicly, disable HTTP POST and redact messages from people who haven't given consent for public web hosting.", "Assume Oasis is being hosted publicly, disable HTTP POST and redact messages from people who haven't given consent for public web hosting.",
default: _.get(presets, "public", false), default: _.get(presets, "public", false),
type: "boolean" type: "boolean",
}) })
.options("debug", { .options("debug", {
describe: "Use verbose output for debugging", describe: "Use verbose output for debugging",
default: _.get(presets, "debug", false), default: _.get(presets, "debug", false),
type: "boolean" type: "boolean",
}) })
.epilog(`The defaults can be configured in ${defaultConfigFile}.`).argv; .epilog(`The defaults can be configured in ${defaultConfigFile}.`).argv;

View File

@ -13,7 +13,7 @@ module.exports = ({ host, port, middleware }) => {
const app = new Koa(); const app = new Koa();
module.exports = app; module.exports = app;
app.on("error", err => { app.on("error", (err) => {
// Output full error objects // Output full error objects
err.message = err.stack; err.message = err.stack;
console.error(err); console.error(err);
@ -32,7 +32,7 @@ module.exports = ({ host, port, middleware }) => {
"img-src 'self'", "img-src 'self'",
"form-action 'self'", "form-action 'self'",
"media-src 'self'", "media-src 'self'",
"style-src 'self' 'unsafe-inline'" "style-src 'self' 'unsafe-inline'",
].join("; "); ].join("; ");
// Disallow scripts. // Disallow scripts.
@ -69,6 +69,6 @@ module.exports = ({ host, port, middleware }) => {
} }
}); });
middleware.forEach(m => app.use(m)); middleware.forEach((m) => app.use(m));
app.listen({ host, port }); app.listen({ host, port });
}; };

View File

@ -66,12 +66,12 @@ these settings the default. See the readme for details.`);
const oasisCheckPath = "/.well-known/oasis"; const oasisCheckPath = "/.well-known/oasis";
process.on("uncaughtException", function(err) { process.on("uncaughtException", function (err) {
// This isn't `err.code` because TypeScript doesn't like that. // This isn't `err.code` because TypeScript doesn't like that.
if (err["code"] === "EADDRINUSE") { if (err["code"] === "EADDRINUSE") {
nodeHttp.get(url + oasisCheckPath, res => { nodeHttp.get(url + oasisCheckPath, (res) => {
let rawData = ""; let rawData = "";
res.on("data", chunk => { res.on("data", (chunk) => {
rawData += chunk; rawData += chunk;
}); });
res.on("end", () => { res.on("end", () => {
@ -123,7 +123,7 @@ const { nav, ul, li, a } = require("hyperaxe");
const open = require("open"); const open = require("open");
const pull = require("pull-stream"); const pull = require("pull-stream");
const requireStyle = require("require-style"); const requireStyle = require("require-style");
const router = require("koa-router")(); const router = require("@koa/router")();
const ssbMentions = require("ssb-mentions"); const ssbMentions = require("ssb-mentions");
const ssbRef = require("ssb-ref"); const ssbRef = require("ssb-ref");
const isSvg = require("is-svg"); const isSvg = require("is-svg");
@ -138,7 +138,7 @@ const cooler = ssb({ offline: config.offline });
const { about, blob, friend, meta, post, vote } = require("./models")({ const { about, blob, friend, meta, post, vote } = require("./models")({
cooler, cooler,
isPublic: config.public isPublic: config.public,
}); });
const { const {
@ -162,7 +162,7 @@ const {
setLanguage, setLanguage,
settingsView, settingsView,
topicsView, topicsView,
summaryView summaryView,
} = require("./views"); } = require("./views");
let sharp; let sharp;
@ -178,11 +178,11 @@ const defaultTheme = "atelier-sulphurPool-light".toLowerCase();
const readmePath = path.join(__dirname, "..", "README.md"); const readmePath = path.join(__dirname, "..", "README.md");
const packagePath = path.join(__dirname, "..", "package.json"); const packagePath = path.join(__dirname, "..", "package.json");
fs.promises.readFile(readmePath, "utf8").then(text => { fs.promises.readFile(readmePath, "utf8").then((text) => {
config.readme = text; config.readme = text;
}); });
fs.promises.readFile(packagePath, "utf8").then(text => { fs.promises.readFile(packagePath, "utf8").then((text) => {
config.version = JSON.parse(text).version; config.version = JSON.parse(text).version;
}); });
@ -207,21 +207,21 @@ router
ctx.assert(ssbRef.isFeedId(message), 400, "Invalid feed link"); ctx.assert(ssbRef.isFeedId(message), 400, "Invalid feed link");
return next(); return next();
}) })
.get("/", async ctx => { .get("/", async (ctx) => {
ctx.redirect("/mentions"); ctx.redirect("/mentions");
}) })
.get("/robots.txt", ctx => { .get("/robots.txt", (ctx) => {
ctx.body = "User-agent: *\nDisallow: /"; ctx.body = "User-agent: *\nDisallow: /";
}) })
.get(oasisCheckPath, ctx => { .get(oasisCheckPath, (ctx) => {
ctx.body = "oasis"; ctx.body = "oasis";
}) })
.get("/public/popular/:period", async ctx => { .get("/public/popular/:period", async (ctx) => {
const { period } = ctx.params; const { period } = ctx.params;
const publicPopular = async ({ period }) => { const publicPopular = async ({ period }) => {
const messages = await post.popular({ period }); const messages = await post.popular({ period });
const option = somePeriod => { const option = (somePeriod) => {
const lowerPeriod = somePeriod.toLowerCase(); const lowerPeriod = somePeriod.toLowerCase();
return li( return li(
period === lowerPeriod period === lowerPeriod
@ -236,30 +236,30 @@ router
return popularView({ return popularView({
messages, messages,
prefix prefix,
}); });
}; };
ctx.body = await publicPopular({ period }); ctx.body = await publicPopular({ period });
}) })
.get("/public/latest", async ctx => { .get("/public/latest", async (ctx) => {
const messages = await post.latest(); const messages = await post.latest();
ctx.body = await latestView({ messages }); ctx.body = await latestView({ messages });
}) })
.get("/public/latest/extended", async ctx => { .get("/public/latest/extended", async (ctx) => {
const messages = await post.latestExtended(); const messages = await post.latestExtended();
ctx.body = await extendedView({ messages }); ctx.body = await extendedView({ messages });
}) })
.get("/public/latest/topics", async ctx => { .get("/public/latest/topics", async (ctx) => {
const messages = await post.latestTopics(); const messages = await post.latestTopics();
ctx.body = await topicsView({ messages }); ctx.body = await topicsView({ messages });
}) })
.get("/public/latest/summaries", async ctx => { .get("/public/latest/summaries", async (ctx) => {
const messages = await post.latestSummaries(); const messages = await post.latestSummaries();
ctx.body = await summaryView({ messages }); ctx.body = await summaryView({ messages });
}) })
.get("/author/:feed", async ctx => { .get("/author/:feed", async (ctx) => {
const { feed } = ctx.params; const { feed } = ctx.params;
const author = async feedId => { const author = async (feedId) => {
const description = await about.description(feedId); const description = await about.description(feedId);
const name = await about.name(feedId); const name = await about.name(feedId);
const image = await about.image(feedId); const image = await about.image(feedId);
@ -274,12 +274,12 @@ router
name, name,
description, description,
avatarUrl, avatarUrl,
relationship relationship,
}); });
}; };
ctx.body = await author(feed); ctx.body = await author(feed);
}) })
.get("/search/", async ctx => { .get("/search/", async (ctx) => {
let { query } = ctx.query; let { query } = ctx.query;
if (isMsg(query)) { if (isMsg(query)) {
@ -305,7 +305,7 @@ router
ctx.body = await searchView({ messages, query }); ctx.body = await searchView({ messages, query });
}) })
.get("/inbox", async ctx => { .get("/inbox", async (ctx) => {
const inbox = async () => { const inbox = async () => {
const messages = await post.inbox(); const messages = await post.inbox();
@ -313,13 +313,13 @@ router
}; };
ctx.body = await inbox(); ctx.body = await inbox();
}) })
.get("/hashtag/:hashtag", async ctx => { .get("/hashtag/:hashtag", async (ctx) => {
const { hashtag } = ctx.params; const { hashtag } = ctx.params;
const messages = await post.fromHashtag(hashtag); const messages = await post.fromHashtag(hashtag);
ctx.body = await hashtagView({ hashtag, messages }); ctx.body = await hashtagView({ hashtag, messages });
}) })
.get("/theme.css", ctx => { .get("/theme.css", (ctx) => {
const theme = ctx.cookies.get("theme") || defaultTheme; const theme = ctx.cookies.get("theme") || defaultTheme;
const packageName = "@fraction/base16-css"; const packageName = "@fraction/base16-css";
@ -327,7 +327,7 @@ router
ctx.type = "text/css"; ctx.type = "text/css";
ctx.body = requireStyle(filePath); ctx.body = requireStyle(filePath);
}) })
.get("/profile/", async ctx => { .get("/profile/", async (ctx) => {
const myFeedId = await meta.myFeedId(); const myFeedId = await meta.myFeedId();
const description = await about.description(myFeedId); const description = await about.description(myFeedId);
@ -344,20 +344,20 @@ router
name, name,
description, description,
avatarUrl, avatarUrl,
relationship: null relationship: null,
}); });
}) })
.get("/profile/edit", async ctx => { .get("/profile/edit", async (ctx) => {
const myFeedId = await meta.myFeedId(); const myFeedId = await meta.myFeedId();
const description = await about.description(myFeedId); const description = await about.description(myFeedId);
const name = await about.name(myFeedId); const name = await about.name(myFeedId);
ctx.body = await editProfileView({ ctx.body = await editProfileView({
name, name,
description description,
}); });
}) })
.post("/profile/edit", koaBody({ multipart: true }), async ctx => { .post("/profile/edit", koaBody({ multipart: true }), async (ctx) => {
const name = String(ctx.request.body.name); const name = String(ctx.request.body.name);
const description = String(ctx.request.body.description); const description = String(ctx.request.body.description);
@ -365,14 +365,14 @@ router
ctx.body = await post.publishProfileEdit({ ctx.body = await post.publishProfileEdit({
name, name,
description, description,
image image,
}); });
ctx.redirect("/profile"); ctx.redirect("/profile");
}) })
.get("/publish/custom/", async ctx => { .get("/publish/custom/", async (ctx) => {
ctx.body = await publishCustomView(); ctx.body = await publishCustomView();
}) })
.get("/json/:message", async ctx => { .get("/json/:message", async (ctx) => {
if (config.public) { if (config.public) {
throw new Error( throw new Error(
"Sorry, many actions are unavailable when Oasis is running in public mode. Please run Oasis in the default mode and try again." "Sorry, many actions are unavailable when Oasis is running in public mode. Please run Oasis in the default mode and try again."
@ -380,19 +380,19 @@ router
} }
const { message } = ctx.params; const { message } = ctx.params;
ctx.type = "application/json"; ctx.type = "application/json";
const json = async message => { const json = async (message) => {
const json = await meta.get(message); const json = await meta.get(message);
return JSON.stringify(json, null, 2); return JSON.stringify(json, null, 2);
}; };
ctx.body = await json(message); ctx.body = await json(message);
}) })
.get("/blob/:blobId", async ctx => { .get("/blob/:blobId", async (ctx) => {
const { blobId } = ctx.params; const { blobId } = ctx.params;
const getBlob = async ({ blobId }) => { const getBlob = async ({ blobId }) => {
const bufferSource = await blob.get({ blobId }); const bufferSource = await blob.get({ blobId });
debug("got buffer source"); debug("got buffer source");
return new Promise(resolve => { return new Promise((resolve) => {
pull( pull(
bufferSource, bufferSource,
pull.collect(async (err, bufferArray) => { pull.collect(async (err, bufferArray) => {
@ -428,7 +428,7 @@ router
ctx.type = "image/svg+xml"; ctx.type = "image/svg+xml";
} }
}) })
.get("/image/:imageSize/:blobId", async ctx => { .get("/image/:imageSize/:blobId", async (ctx) => {
const { blobId, imageSize } = ctx.params; const { blobId, imageSize } = ctx.params;
if (sharp) { if (sharp) {
ctx.type = "image/png"; ctx.type = "image/png";
@ -439,7 +439,7 @@ router
"base64" "base64"
); );
const fakeImage = imageSize => const fakeImage = (imageSize) =>
sharp sharp
? sharp({ ? sharp({
create: { create: {
@ -450,23 +450,23 @@ router
r: 0, r: 0,
g: 0, g: 0,
b: 0, b: 0,
alpha: 0.5 alpha: 0.5,
} },
} },
}) })
.png() .png()
.toBuffer() .toBuffer()
: new Promise(resolve => resolve(fakePixel)); : new Promise((resolve) => resolve(fakePixel));
const image = async ({ blobId, imageSize }) => { const image = async ({ blobId, imageSize }) => {
const bufferSource = await blob.get({ blobId }); const bufferSource = await blob.get({ blobId });
const fakeId = "&0000000000000000000000000000000000000000000=.sha256"; const fakeId = "&0000000000000000000000000000000000000000000=.sha256";
debug("got buffer source"); debug("got buffer source");
return new Promise(resolve => { return new Promise((resolve) => {
if (blobId === fakeId) { if (blobId === fakeId) {
debug("fake image"); debug("fake image");
fakeImage(imageSize).then(result => resolve(result)); fakeImage(imageSize).then((result) => resolve(result));
} else { } else {
debug("not fake image"); debug("not fake image");
pull( pull(
@ -485,7 +485,7 @@ router
.resize(imageSize, imageSize) .resize(imageSize, imageSize)
.png() .png()
.toBuffer() .toBuffer()
.then(data => { .then((data) => {
resolve(data); resolve(data);
}); });
} else { } else {
@ -499,7 +499,7 @@ router
}; };
ctx.body = await image({ blobId, imageSize: Number(imageSize) }); ctx.body = await image({ blobId, imageSize: Number(imageSize) });
}) })
.get("/settings/", async ctx => { .get("/settings/", async (ctx) => {
const theme = ctx.cookies.get("theme") || defaultTheme; const theme = ctx.cookies.get("theme") || defaultTheme;
const getMeta = async ({ theme }) => { const getMeta = async ({ theme }) => {
const status = await meta.status(); const status = await meta.status();
@ -516,12 +516,12 @@ router
peers: peersWithNames, peers: peersWithNames,
theme, theme,
themeNames, themeNames,
version: config.version version: config.version,
}); });
}; };
ctx.body = await getMeta({ theme }); ctx.body = await getMeta({ theme });
}) })
.get("/likes/:feed", async ctx => { .get("/likes/:feed", async (ctx) => {
const { feed } = ctx.params; const { feed } = ctx.params;
const likes = async ({ feed }) => { const likes = async ({ feed }) => {
const pendingMessages = post.likes({ feed }); const pendingMessages = post.likes({ feed });
@ -529,18 +529,18 @@ router
return likesView({ return likesView({
messages: await pendingMessages, messages: await pendingMessages,
feed, feed,
name: await pendingName name: await pendingName,
}); });
}; };
ctx.body = await likes({ feed }); ctx.body = await likes({ feed });
}) })
.get("/settings/readme/", async ctx => { .get("/settings/readme/", async (ctx) => {
const status = async text => { const status = async (text) => {
return markdownView({ text }); return markdownView({ text });
}; };
ctx.body = await status(config.readme); ctx.body = await status(config.readme);
}) })
.get("/mentions/", async ctx => { .get("/mentions/", async (ctx) => {
const mentions = async () => { const mentions = async () => {
const messages = await post.mentionsMe(); const messages = await post.mentionsMe();
@ -548,9 +548,9 @@ router
}; };
ctx.body = await mentions(); ctx.body = await mentions();
}) })
.get("/thread/:message", async ctx => { .get("/thread/:message", async (ctx) => {
const { message } = ctx.params; const { message } = ctx.params;
const thread = async message => { const thread = async (message) => {
const messages = await post.fromThread(message); const messages = await post.fromThread(message);
debug("got %i messages", messages.length); debug("got %i messages", messages.length);
@ -559,9 +559,9 @@ router
ctx.body = await thread(message); ctx.body = await thread(message);
}) })
.get("/reply/:message", async ctx => { .get("/reply/:message", async (ctx) => {
const { message } = ctx.params; const { message } = ctx.params;
const reply = async parentId => { const reply = async (parentId) => {
const rootMessage = await post.get(parentId); const rootMessage = await post.get(parentId);
const myFeedId = await meta.myFeedId(); const myFeedId = await meta.myFeedId();
@ -572,12 +572,12 @@ router
}; };
ctx.body = await reply(message); ctx.body = await reply(message);
}) })
.get("/publish", async ctx => { .get("/publish", async (ctx) => {
ctx.body = await publishView(); ctx.body = await publishView();
}) })
.get("/comment/:message", async ctx => { .get("/comment/:message", async (ctx) => {
const { message } = ctx.params; const { message } = ctx.params;
const comment = async parentId => { const comment = async (parentId) => {
const parentMessage = await post.get(parentId); const parentMessage = await post.get(parentId);
const myFeedId = await meta.myFeedId(); const myFeedId = await meta.myFeedId();
@ -602,7 +602,7 @@ router
}; };
ctx.body = await comment(message); ctx.body = await comment(message);
}) })
.post("/reply/:message", koaBody(), async ctx => { .post("/reply/:message", koaBody(), async (ctx) => {
const { message } = ctx.params; const { message } = ctx.params;
const text = String(ctx.request.body.text); const text = String(ctx.request.body.text);
const publishReply = async ({ message, text }) => { const publishReply = async ({ message, text }) => {
@ -612,13 +612,13 @@ router
const parent = await post.get(message); const parent = await post.get(message);
return post.reply({ return post.reply({
parent, parent,
message: { text, mentions } message: { text, mentions },
}); });
}; };
ctx.body = await publishReply({ message, text }); ctx.body = await publishReply({ message, text });
ctx.redirect(`/thread/${encodeURIComponent(message)}`); ctx.redirect(`/thread/${encodeURIComponent(message)}`);
}) })
.post("/comment/:message", koaBody(), async ctx => { .post("/comment/:message", koaBody(), async (ctx) => {
const { message } = ctx.params; const { message } = ctx.params;
const text = String(ctx.request.body.text); const text = String(ctx.request.body.text);
const publishComment = async ({ message, text }) => { const publishComment = async ({ message, text }) => {
@ -628,13 +628,13 @@ router
return post.comment({ return post.comment({
parent, parent,
message: { text, mentions } message: { text, mentions },
}); });
}; };
ctx.body = await publishComment({ message, text }); ctx.body = await publishComment({ message, text });
ctx.redirect(`/thread/${encodeURIComponent(message)}`); ctx.redirect(`/thread/${encodeURIComponent(message)}`);
}) })
.post("/publish/", koaBody(), async ctx => { .post("/publish/", koaBody(), async (ctx) => {
const text = String(ctx.request.body.text); const text = String(ctx.request.body.text);
const rawContentWarning = String(ctx.request.body.contentWarning); const rawContentWarning = String(ctx.request.body.contentWarning);
@ -648,31 +648,31 @@ router
return post.root({ return post.root({
text, text,
mentions, mentions,
contentWarning contentWarning,
}); });
}; };
ctx.body = await publish({ text, contentWarning }); ctx.body = await publish({ text, contentWarning });
ctx.redirect("/public/latest"); ctx.redirect("/public/latest");
}) })
.post("/publish/custom", koaBody(), async ctx => { .post("/publish/custom", koaBody(), async (ctx) => {
const text = String(ctx.request.body.text); const text = String(ctx.request.body.text);
const obj = JSON.parse(text); const obj = JSON.parse(text);
ctx.body = await post.publishCustom(obj); ctx.body = await post.publishCustom(obj);
ctx.redirect(`/public/latest`); ctx.redirect(`/public/latest`);
}) })
.post("/follow/:feed", koaBody(), async ctx => { .post("/follow/:feed", koaBody(), async (ctx) => {
const { feed } = ctx.params; const { feed } = ctx.params;
const referer = new URL(ctx.request.header.referer); const referer = new URL(ctx.request.header.referer);
ctx.body = await friend.follow(feed); ctx.body = await friend.follow(feed);
ctx.redirect(referer); ctx.redirect(referer);
}) })
.post("/unfollow/:feed", koaBody(), async ctx => { .post("/unfollow/:feed", koaBody(), async (ctx) => {
const { feed } = ctx.params; const { feed } = ctx.params;
const referer = new URL(ctx.request.header.referer); const referer = new URL(ctx.request.header.referer);
ctx.body = await friend.unfollow(feed); ctx.body = await friend.unfollow(feed);
ctx.redirect(referer); ctx.redirect(referer);
}) })
.post("/like/:message", koaBody(), async ctx => { .post("/like/:message", koaBody(), async (ctx) => {
const { message } = ctx.params; const { message } = ctx.params;
// TODO: convert all so `message` is full message and `messageKey` is key // TODO: convert all so `message` is full message and `messageKey` is key
const messageKey = message; const messageKey = message;
@ -680,7 +680,7 @@ router
const voteValue = Number(ctx.request.body.voteValue); const voteValue = Number(ctx.request.body.voteValue);
const encoded = { const encoded = {
message: encodeURIComponent(message) message: encodeURIComponent(message),
}; };
const referer = new URL(ctx.request.header.referer); const referer = new URL(ctx.request.header.referer);
@ -693,7 +693,7 @@ router
const isPrivate = message.value.meta.private === true; const isPrivate = message.value.meta.private === true;
const messageRecipients = isPrivate ? message.value.content.recps : []; const messageRecipients = isPrivate ? message.value.content.recps : [];
const normalized = messageRecipients.map(recipient => { const normalized = messageRecipients.map((recipient) => {
if (typeof recipient === "string") { if (typeof recipient === "string") {
return recipient; return recipient;
} }
@ -712,31 +712,31 @@ router
ctx.body = await like({ messageKey, voteValue }); ctx.body = await like({ messageKey, voteValue });
ctx.redirect(referer); ctx.redirect(referer);
}) })
.post("/theme.css", koaBody(), async ctx => { .post("/theme.css", koaBody(), async (ctx) => {
const theme = String(ctx.request.body.theme); const theme = String(ctx.request.body.theme);
ctx.cookies.set("theme", theme); ctx.cookies.set("theme", theme);
const referer = new URL(ctx.request.header.referer); const referer = new URL(ctx.request.header.referer);
ctx.redirect(referer); ctx.redirect(referer);
}) })
.post("/language", koaBody(), async ctx => { .post("/language", koaBody(), async (ctx) => {
const language = String(ctx.request.body.language); const language = String(ctx.request.body.language);
ctx.cookies.set("language", language); ctx.cookies.set("language", language);
const referer = new URL(ctx.request.header.referer); const referer = new URL(ctx.request.header.referer);
ctx.redirect(referer); ctx.redirect(referer);
}) })
.post("/settings/conn/start", koaBody(), async ctx => { .post("/settings/conn/start", koaBody(), async (ctx) => {
await meta.connStart(); await meta.connStart();
ctx.redirect("/settings"); ctx.redirect("/settings");
}) })
.post("/settings/conn/stop", koaBody(), async ctx => { .post("/settings/conn/stop", koaBody(), async (ctx) => {
await meta.connStop(); await meta.connStop();
ctx.redirect("/settings"); ctx.redirect("/settings");
}) })
.post("/settings/conn/restart", koaBody(), async ctx => { .post("/settings/conn/restart", koaBody(), async (ctx) => {
await meta.connRestart(); await meta.connRestart();
ctx.redirect("/settings"); ctx.redirect("/settings");
}) })
.post("/settings/invite/accept", koaBody(), async ctx => { .post("/settings/invite/accept", koaBody(), async (ctx) => {
const invite = String(ctx.request.body.invite); const invite = String(ctx.request.body.invite);
await meta.acceptInvite(invite); await meta.acceptInvite(invite);
ctx.redirect("/settings"); ctx.redirect("/settings");
@ -781,7 +781,7 @@ const middleware = [
await next(); await next();
} }
}, },
routes routes,
]; ];
http({ host, port, middleware }); http({ host, port, middleware });

View File

@ -18,11 +18,11 @@ const nullImage = `&${"0".repeat(43)}=.sha256`;
const defaultOptions = { const defaultOptions = {
private: true, private: true,
reverse: true, reverse: true,
meta: true meta: true,
}; };
const publicOnlyFilter = pull.filter( const publicOnlyFilter = pull.filter(
message => lodash.get(message, "value.meta.private", false) === false (message) => lodash.get(message, "value.meta.private", false) === false
); );
/** @param {object[]} customOptions */ /** @param {object[]} customOptions */
@ -55,16 +55,19 @@ module.exports = ({ cooler, isPublic }) => {
{ {
$filter: { $filter: {
dest: feedId, dest: feedId,
value: { author: feedId, content: { type: "about", about: feedId } } value: {
} author: feedId,
} content: { type: "about", about: feedId },
] },
},
},
],
}); });
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
pull( pull(
source, source,
pull.find( pull.find(
message => message.value.content[key] !== undefined, (message) => message.value.content[key] !== undefined,
(err, message) => { (err, message) => {
if (err) { if (err) {
reject(err); reject(err);
@ -82,14 +85,14 @@ module.exports = ({ cooler, isPublic }) => {
}; };
models.about = { models.about = {
publicWebHosting: async feedId => { publicWebHosting: async (feedId) => {
const result = await getAbout({ const result = await getAbout({
key: "publicWebHosting", key: "publicWebHosting",
feedId feedId,
}); });
return result === true; return result === true;
}, },
name: async feedId => { name: async (feedId) => {
if (isPublic && (await models.about.publicWebHosting(feedId)) === false) { if (isPublic && (await models.about.publicWebHosting(feedId)) === false) {
return "Redacted"; return "Redacted";
} }
@ -97,18 +100,18 @@ module.exports = ({ cooler, isPublic }) => {
return ( return (
(await getAbout({ (await getAbout({
key: "name", key: "name",
feedId feedId,
})) || feedId.slice(1, 1 + 8) })) || feedId.slice(1, 1 + 8)
); // First 8 chars of public key ); // First 8 chars of public key
}, },
image: async feedId => { image: async (feedId) => {
if (isPublic && (await models.about.publicWebHosting(feedId)) === false) { if (isPublic && (await models.about.publicWebHosting(feedId)) === false) {
return nullImage; return nullImage;
} }
const raw = await getAbout({ const raw = await getAbout({
key: "image", key: "image",
feedId feedId,
}); });
if (raw == null || raw.link == null) { if (raw == null || raw.link == null) {
@ -120,7 +123,7 @@ module.exports = ({ cooler, isPublic }) => {
} }
return raw; return raw;
}, },
description: async feedId => { description: async (feedId) => {
if (isPublic && (await models.about.publicWebHosting(feedId)) === false) { if (isPublic && (await models.about.publicWebHosting(feedId)) === false) {
return "Redacted"; return "Redacted";
} }
@ -128,10 +131,10 @@ module.exports = ({ cooler, isPublic }) => {
const raw = const raw =
(await getAbout({ (await getAbout({
key: "description", key: "description",
feedId feedId,
})) || ""; })) || "";
return raw; return raw;
} },
}; };
models.blob = { models.blob = {
@ -146,17 +149,17 @@ module.exports = ({ cooler, isPublic }) => {
// This does not wait for the blob. // This does not wait for the blob.
ssb.blobs.want(blobId); ssb.blobs.want(blobId);
} },
}; };
models.friend = { models.friend = {
isFollowing: async feedId => { isFollowing: async (feedId) => {
const ssb = await cooler.open(); const ssb = await cooler.open();
const { id } = ssb; const { id } = ssb;
const isFollowing = await ssb.friends.isFollowing({ const isFollowing = await ssb.friends.isFollowing({
source: id, source: id,
dest: feedId dest: feedId,
}); });
return isFollowing; return isFollowing;
}, },
@ -166,24 +169,24 @@ module.exports = ({ cooler, isPublic }) => {
const content = { const content = {
type: "contact", type: "contact",
contact: feedId, contact: feedId,
following following,
}; };
return ssb.publish(content); return ssb.publish(content);
}, },
follow: async feedId => { follow: async (feedId) => {
const isFollowing = await models.friend.isFollowing(feedId); const isFollowing = await models.friend.isFollowing(feedId);
if (!isFollowing) { if (!isFollowing) {
await models.friend.setFollowing({ feedId, following: true }); await models.friend.setFollowing({ feedId, following: true });
} }
}, },
unfollow: async feedId => { unfollow: async (feedId) => {
const isFollowing = await models.friend.isFollowing(feedId); const isFollowing = await models.friend.isFollowing(feedId);
if (isFollowing) { if (isFollowing) {
await models.friend.setFollowing({ feedId, following: false }); await models.friend.setFollowing({ feedId, following: false });
} }
}, },
getRelationship: async feedId => { getRelationship: async (feedId) => {
const ssb = await cooler.open(); const ssb = await cooler.open();
const { id } = ssb; const { id } = ssb;
@ -193,19 +196,19 @@ module.exports = ({ cooler, isPublic }) => {
const isFollowing = await ssb.friends.isFollowing({ const isFollowing = await ssb.friends.isFollowing({
source: id, source: id,
dest: feedId dest: feedId,
}); });
const isBlocking = await ssb.friends.isBlocking({ const isBlocking = await ssb.friends.isBlocking({
source: id, source: id,
dest: feedId dest: feedId,
}); });
return { return {
following: isFollowing, following: isFollowing,
blocking: isBlocking blocking: isBlocking,
}; };
} },
}; };
models.meta = { models.meta = {
@ -214,12 +217,12 @@ module.exports = ({ cooler, isPublic }) => {
const { id } = ssb; const { id } = ssb;
return id; return id;
}, },
get: async msgId => { get: async (msgId) => {
const ssb = await cooler.open(); const ssb = await cooler.open();
return ssb.get({ return ssb.get({
id: msgId, id: msgId,
meta: true, meta: true,
private: true private: true,
}); });
}, },
status: async () => { status: async () => {
@ -269,44 +272,44 @@ module.exports = ({ cooler, isPublic }) => {
await models.meta.connStop(); await models.meta.connStop();
await models.meta.connStart(); await models.meta.connStart();
}, },
acceptInvite: async invite => { acceptInvite: async (invite) => {
const ssb = await cooler.open(); const ssb = await cooler.open();
return await ssb.invite.accept(invite); return await ssb.invite.accept(invite);
} },
}; };
const isPost = message => const isPost = (message) =>
lodash.get(message, "value.content.type") === "post" && lodash.get(message, "value.content.type") === "post" &&
lodash.get(message, "value.content.text") != null; lodash.get(message, "value.content.text") != null;
const isLooseRoot = message => { const isLooseRoot = (message) => {
const conditions = [ const conditions = [
isPost(message), isPost(message),
lodash.get(message, "value.content.root") == null, lodash.get(message, "value.content.root") == null,
lodash.get(message, "value.content.fork") == null lodash.get(message, "value.content.fork") == null,
]; ];
return conditions.every(x => x); return conditions.every((x) => x);
}; };
const isLooseReply = message => { const isLooseReply = (message) => {
const conditions = [ const conditions = [
isPost(message), isPost(message),
lodash.get(message, "value.content.root") != null, lodash.get(message, "value.content.root") != null,
lodash.get(message, "value.content.fork") != null lodash.get(message, "value.content.fork") != null,
]; ];
return conditions.every(x => x); return conditions.every((x) => x);
}; };
const isLooseComment = message => { const isLooseComment = (message) => {
const conditions = [ const conditions = [
isPost(message), isPost(message),
lodash.get(message, "value.content.root") != null, lodash.get(message, "value.content.root") != null,
lodash.get(message, "value.content.fork") == null lodash.get(message, "value.content.fork") == null,
]; ];
return conditions.every(x => x === true); return conditions.every((x) => x === true);
}; };
const maxMessages = 64; const maxMessages = 64;
@ -316,7 +319,7 @@ module.exports = ({ cooler, isPublic }) => {
customOptions, customOptions,
ssb, ssb,
query, query,
filter = null filter = null,
}) => { }) => {
const options = configure({ query, index: "DTA" }, customOptions); const options = configure({ query, index: "DTA" }, customOptions);
@ -328,7 +331,7 @@ module.exports = ({ cooler, isPublic }) => {
source, source,
basicSocialFilter, basicSocialFilter,
pull.filter( pull.filter(
msg => (msg) =>
typeof msg.value.content !== "string" && typeof msg.value.content !== "string" &&
msg.value.content.type === "post" && msg.value.content.type === "post" &&
(filter == null || filter(msg) === true) (filter == null || filter(msg) === true)
@ -356,12 +359,12 @@ module.exports = ({ cooler, isPublic }) => {
const socialFilter = async ({ const socialFilter = async ({
following = null, following = null,
blocking = false, blocking = false,
me = null me = null,
} = {}) => { } = {}) => {
const ssb = await cooler.open(); const ssb = await cooler.open();
const { id } = ssb; const { id } = ssb;
const relationshipObject = await ssb.friends.get({ const relationshipObject = await ssb.friends.get({
source: id source: id,
}); });
const followingList = Object.entries(relationshipObject) const followingList = Object.entries(relationshipObject)
@ -372,7 +375,7 @@ module.exports = ({ cooler, isPublic }) => {
.filter(([, val]) => val === false) .filter(([, val]) => val === false)
.map(([key]) => key); .map(([key]) => key);
return pull.filter(message => { return pull.filter((message) => {
if (message.value.author === id) { if (message.value.author === id) {
return me !== false; return me !== false;
} else { } else {
@ -387,7 +390,7 @@ module.exports = ({ cooler, isPublic }) => {
}; };
const transform = (ssb, messages, myFeedId) => const transform = (ssb, messages, myFeedId) =>
Promise.all( Promise.all(
messages.map(async msg => { messages.map(async (msg) => {
debug("transforming %s", msg.key); debug("transforming %s", msg.key);
if (msg == null) { if (msg == null) {
@ -396,22 +399,22 @@ module.exports = ({ cooler, isPublic }) => {
const filterQuery = { const filterQuery = {
$filter: { $filter: {
dest: msg.key dest: msg.key,
} },
}; };
const referenceStream = ssb.backlinks.read({ const referenceStream = ssb.backlinks.read({
query: [filterQuery], query: [filterQuery],
index: "DTA", // use asserted timestamps index: "DTA", // use asserted timestamps
private: true, private: true,
meta: true meta: true,
}); });
const rawVotes = await new Promise((resolve, reject) => { const rawVotes = await new Promise((resolve, reject) => {
pull( pull(
referenceStream, referenceStream,
pull.filter( pull.filter(
ref => (ref) =>
typeof ref.value.content !== "string" && typeof ref.value.content !== "string" &&
ref.value.content.type === "vote" && ref.value.content.type === "vote" &&
ref.value.content.vote && ref.value.content.vote &&
@ -500,7 +503,7 @@ module.exports = ({ cooler, isPublic }) => {
lodash.set(msg, "value.meta.author.name", name); lodash.set(msg, "value.meta.author.name", name);
lodash.set(msg, "value.meta.author.avatar", { lodash.set(msg, "value.meta.author.avatar", {
id: avatarId, id: avatarId,
url: avatarUrl url: avatarUrl,
}); });
const isPost = const isPost =
@ -539,7 +542,7 @@ module.exports = ({ cooler, isPublic }) => {
pull( pull(
source, source,
pull.filter( pull.filter(
msg => (msg) =>
lodash.get(msg, "value.meta.private", false) === false && lodash.get(msg, "value.meta.private", false) === false &&
msg.value.content.type === "post" msg.value.content.type === "post"
), ),
@ -564,9 +567,9 @@ module.exports = ({ cooler, isPublic }) => {
const query = [ const query = [
{ {
$filter: { $filter: {
dest: myFeedId dest: myFeedId,
} },
} },
]; ];
const messages = await getMessages({ const messages = await getMessages({
@ -574,9 +577,9 @@ module.exports = ({ cooler, isPublic }) => {
customOptions, customOptions,
ssb, ssb,
query, query,
filter: msg => filter: (msg) =>
msg.value.author !== myFeedId && msg.value.author !== myFeedId &&
lodash.get(msg, "value.meta.private") !== true lodash.get(msg, "value.meta.private") !== true,
}); });
return messages; return messages;
@ -589,16 +592,16 @@ module.exports = ({ cooler, isPublic }) => {
const query = [ const query = [
{ {
$filter: { $filter: {
dest: `#${hashtag}` dest: `#${hashtag}`,
} },
} },
]; ];
const messages = await getMessages({ const messages = await getMessages({
myFeedId, myFeedId,
customOptions, customOptions,
ssb, ssb,
query query,
}); });
return messages; return messages;
@ -611,9 +614,9 @@ module.exports = ({ cooler, isPublic }) => {
const query = [ const query = [
{ {
$filter: { $filter: {
dest: rootId dest: rootId,
} },
} },
]; ];
const messages = await getMessages({ const messages = await getMessages({
@ -621,8 +624,8 @@ module.exports = ({ cooler, isPublic }) => {
customOptions, customOptions,
ssb, ssb,
query, query,
filter: msg => filter: (msg) =>
msg.value.content.root === rootId && msg.value.content.fork == null msg.value.content.root === rootId && msg.value.content.fork == null,
}); });
return messages; return messages;
@ -637,17 +640,17 @@ module.exports = ({ cooler, isPublic }) => {
author: feed, author: feed,
timestamp: { $lte: Date.now() }, timestamp: { $lte: Date.now() },
content: { content: {
type: "vote" type: "vote",
} },
} },
} },
} },
]; ];
const options = configure( const options = configure(
{ {
query, query,
reverse: true reverse: true,
}, },
customOptions customOptions
); );
@ -657,7 +660,7 @@ module.exports = ({ cooler, isPublic }) => {
const messages = await new Promise((resolve, reject) => { const messages = await new Promise((resolve, reject) => {
pull( pull(
source, source,
pull.filter(msg => { pull.filter((msg) => {
return ( return (
typeof msg.value.content === "object" && typeof msg.value.content === "object" &&
msg.value.author === feed && msg.value.author === feed &&
@ -688,7 +691,7 @@ module.exports = ({ cooler, isPublic }) => {
const myFeedId = ssb.id; const myFeedId = ssb.id;
const options = configure({ const options = configure({
query query,
}); });
const source = await ssb.search.query(options); const source = await ssb.search.query(options);
@ -729,12 +732,12 @@ module.exports = ({ cooler, isPublic }) => {
value: { value: {
timestamp: { $lte: Date.now() }, timestamp: { $lte: Date.now() },
content: { content: {
type: "post" type: "post",
} },
} },
} },
} },
] ],
}) })
); );
const followingFilter = await socialFilter({ following: true }); const followingFilter = await socialFilter({ following: true });
@ -770,18 +773,18 @@ module.exports = ({ cooler, isPublic }) => {
value: { value: {
timestamp: { $lte: Date.now() }, timestamp: { $lte: Date.now() },
content: { content: {
type: "post" type: "post",
} },
} },
} },
} },
] ],
}) })
); );
const extendedFilter = await socialFilter({ const extendedFilter = await socialFilter({
following: false, following: false,
me: false me: false,
}); });
const messages = await new Promise((resolve, reject) => { const messages = await new Promise((resolve, reject) => {
@ -815,24 +818,24 @@ module.exports = ({ cooler, isPublic }) => {
value: { value: {
timestamp: { $lte: Date.now() }, timestamp: { $lte: Date.now() },
content: { content: {
type: "post" type: "post",
} },
} },
} },
} },
] ],
}) })
); );
const extendedFilter = await socialFilter({ const extendedFilter = await socialFilter({
following: true following: true,
}); });
const messages = await new Promise((resolve, reject) => { const messages = await new Promise((resolve, reject) => {
pull( pull(
source, source,
publicOnlyFilter, publicOnlyFilter,
pull.filter(message => message.value.content.root == null), pull.filter((message) => message.value.content.root == null),
extendedFilter, extendedFilter,
pull.take(maxMessages), pull.take(maxMessages),
pull.collect((err, collectedMessages) => { pull.collect((err, collectedMessages) => {
@ -854,20 +857,20 @@ module.exports = ({ cooler, isPublic }) => {
const options = configure({ const options = configure({
type: "post", type: "post",
private: false private: false,
}); });
const source = ssb.messagesByType(options); const source = ssb.messagesByType(options);
const extendedFilter = await socialFilter({ const extendedFilter = await socialFilter({
following: true following: true,
}); });
const messages = await new Promise((resolve, reject) => { const messages = await new Promise((resolve, reject) => {
pull( pull(
source, source,
pull.filter( pull.filter(
message => (message) =>
typeof message.value.content !== "string" && typeof message.value.content !== "string" &&
message.value.content.root == null message.value.content.root == null
), ),
@ -903,7 +906,7 @@ module.exports = ({ cooler, isPublic }) => {
day: 1, day: 1,
week: 7, week: 7,
month: 30.42, month: 30.42,
year: 365 year: 365,
}; };
if (period in periodDict === false) { if (period in periodDict === false) {
@ -923,12 +926,12 @@ module.exports = ({ cooler, isPublic }) => {
value: { value: {
timestamp: { $gte: earliest }, timestamp: { $gte: earliest },
content: { content: {
type: "vote" type: "vote",
} },
} },
} },
} },
] ],
}) })
); );
const basicSocialFilter = await socialFilter(); const basicSocialFilter = await socialFilter();
@ -937,7 +940,7 @@ module.exports = ({ cooler, isPublic }) => {
pull( pull(
source, source,
publicOnlyFilter, publicOnlyFilter,
pull.filter(msg => { pull.filter((msg) => {
return ( return (
typeof msg.value.content === "object" && typeof msg.value.content === "object" &&
typeof msg.value.content.vote === "object" && typeof msg.value.content.vote === "object" &&
@ -1008,7 +1011,7 @@ module.exports = ({ cooler, isPublic }) => {
}), }),
// avoid private messages (!) and non-posts // avoid private messages (!) and non-posts
pull.filter( pull.filter(
message => (message) =>
message && message &&
typeof message.value.content !== "string" && typeof message.value.content !== "string" &&
message.value.content.type === "post" message.value.content.type === "post"
@ -1038,12 +1041,12 @@ module.exports = ({ cooler, isPublic }) => {
const options = configure({ id: msgId }, customOptions); const options = configure({ id: msgId }, customOptions);
return ssb return ssb
.get(options) .get(options)
.then(async rawMsg => { .then(async (rawMsg) => {
debug("got raw message"); debug("got raw message");
const parents = []; const parents = [];
const getRootAncestor = msg => const getRootAncestor = (msg) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
if (msg.key == null) { if (msg.key == null) {
debug("something is very wrong, we used `{ meta: true }`"); debug("something is very wrong, we used `{ meta: true }`");
@ -1070,9 +1073,9 @@ module.exports = ({ cooler, isPublic }) => {
.get({ .get({
id: msg.value.content.fork, id: msg.value.content.fork,
meta: true, meta: true,
private: true private: true,
}) })
.then(fork => { .then((fork) => {
resolve(getRootAncestor(fork)); resolve(getRootAncestor(fork));
}) })
.catch(reject); .catch(reject);
@ -1091,9 +1094,9 @@ module.exports = ({ cooler, isPublic }) => {
.get({ .get({
id: msg.value.content.root, id: msg.value.content.root,
meta: true, meta: true,
private: true private: true,
}) })
.then(root => { .then((root) => {
resolve(getRootAncestor(root)); resolve(getRootAncestor(root));
}) })
.catch(reject); .catch(reject);
@ -1116,21 +1119,21 @@ module.exports = ({ cooler, isPublic }) => {
} }
}); });
const getReplies = key => const getReplies = (key) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
const filterQuery = { const filterQuery = {
$filter: { $filter: {
dest: key dest: key,
} },
}; };
const referenceStream = ssb.backlinks.read({ const referenceStream = ssb.backlinks.read({
query: [filterQuery], query: [filterQuery],
index: "DTA" // use asserted timestamps index: "DTA", // use asserted timestamps
}); });
pull( pull(
referenceStream, referenceStream,
pull.filter(msg => { pull.filter((msg) => {
const isPost = const isPost =
lodash.get(msg, "value.content.type") === "post"; lodash.get(msg, "value.content.type") === "post";
if (isPost === false) { if (isPost === false) {
@ -1164,7 +1167,7 @@ module.exports = ({ cooler, isPublic }) => {
}); });
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
const flattenDeep = arr1 => const flattenDeep = (arr1) =>
arr1.reduce( arr1.reduce(
(acc, val) => (acc, val) =>
Array.isArray(val) Array.isArray(val)
@ -1173,13 +1176,13 @@ module.exports = ({ cooler, isPublic }) => {
[] []
); );
const getDeepReplies = key => const getDeepReplies = (key) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
const oneDeeper = async (replyKey, depth) => { const oneDeeper = async (replyKey, depth) => {
const replies = await getReplies(replyKey); const replies = await getReplies(replyKey);
debug( debug(
"replies", "replies",
replies.map(m => m.key) replies.map((m) => m.key)
); );
debug("found %s replies for %s", replies.length, replyKey); debug("found %s replies for %s", replies.length, replyKey);
@ -1188,7 +1191,7 @@ module.exports = ({ cooler, isPublic }) => {
return replies; return replies;
} }
return Promise.all( return Promise.all(
replies.map(async reply => { replies.map(async (reply) => {
const deeperReplies = await oneDeeper(reply.key, depth + 1); const deeperReplies = await oneDeeper(reply.key, depth + 1);
lodash.set(reply, "value.meta.thread.depth", depth); lodash.set(reply, "value.meta.thread.depth", depth);
lodash.set(reply, "value.meta.thread.reply", true); lodash.set(reply, "value.meta.thread.reply", true);
@ -1197,7 +1200,7 @@ module.exports = ({ cooler, isPublic }) => {
); );
}; };
oneDeeper(key, 0) oneDeeper(key, 0)
.then(nested => { .then((nested) => {
const nestedReplies = [...nested]; const nestedReplies = [...nested];
const deepReplies = flattenDeep(nestedReplies); const deepReplies = flattenDeep(nestedReplies);
resolve(deepReplies); resolve(deepReplies);
@ -1211,7 +1214,7 @@ module.exports = ({ cooler, isPublic }) => {
const deepReplies = await getDeepReplies(rootAncestor.key); const deepReplies = await getDeepReplies(rootAncestor.key);
debug("got deep replies"); debug("got deep replies");
const allMessages = [rootAncestor, ...deepReplies].map(message => { const allMessages = [rootAncestor, ...deepReplies].map((message) => {
const isThreadTarget = message.key === msgId; const isThreadTarget = message.key === msgId;
lodash.set(message, "value.meta.thread.target", isThreadTarget); lodash.set(message, "value.meta.thread.target", isThreadTarget);
return message; return message;
@ -1219,7 +1222,7 @@ module.exports = ({ cooler, isPublic }) => {
return await transform(ssb, allMessages, myFeedId); return await transform(ssb, allMessages, myFeedId);
}) })
.catch(err => { .catch((err) => {
if (err.name === "NotFoundError") { if (err.name === "NotFoundError") {
throw new Error( throw new Error(
"Message not found in the database. You've done nothing wrong. Maybe try again later?" "Message not found in the database. You've done nothing wrong. Maybe try again later?"
@ -1243,7 +1246,7 @@ module.exports = ({ cooler, isPublic }) => {
debug("transformed: %O", transformed); debug("transformed: %O", transformed);
return transformed[0]; return transformed[0];
}, },
publish: async options => { publish: async (options) => {
const ssb = await cooler.open(); const ssb = await cooler.open();
const body = { type: "post", ...options }; const body = { type: "post", ...options };
@ -1269,7 +1272,7 @@ module.exports = ({ cooler, isPublic }) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
pull( pull(
pull.values([image]), pull.values([image]),
ssb.blobs.add(blobId, err => { ssb.blobs.add(blobId, (err) => {
if (err) { if (err) {
reject(err); reject(err);
} else { } else {
@ -1278,7 +1281,7 @@ module.exports = ({ cooler, isPublic }) => {
about: ssb.id, about: ssb.id,
name, name,
description, description,
image: blobId image: blobId,
}; };
debug("Published: %O", body); debug("Published: %O", body);
resolve(ssb.publish(body)); resolve(ssb.publish(body));
@ -1292,7 +1295,7 @@ module.exports = ({ cooler, isPublic }) => {
return ssb.publish(body); return ssb.publish(body);
} }
}, },
publishCustom: async options => { publishCustom: async (options) => {
const ssb = await cooler.open(); const ssb = await cooler.open();
debug("Published: %O", options); debug("Published: %O", options);
return ssb.publish(options); return ssb.publish(options);
@ -1310,7 +1313,7 @@ module.exports = ({ cooler, isPublic }) => {
return post.publish(message); return post.publish(message);
}, },
root: async options => { root: async (options) => {
const message = { type: "post", ...options }; const message = { type: "post", ...options };
if (isRoot(message) !== true) { if (isRoot(message) !== true) {
@ -1333,7 +1336,7 @@ module.exports = ({ cooler, isPublic }) => {
if (isPrivate) { if (isPrivate) {
message.recps = lodash message.recps = lodash
.get(parent, "value.content.recps", []) .get(parent, "value.content.recps", [])
.map(recipient => { .map((recipient) => {
if ( if (
typeof recipient === "object" && typeof recipient === "object" &&
typeof recipient.link === "string" && typeof recipient.link === "string" &&
@ -1379,7 +1382,7 @@ module.exports = ({ cooler, isPublic }) => {
const options = configure( const options = configure(
{ {
query: [{ $filter: { dest: ssb.id } }] query: [{ $filter: { dest: ssb.id } }],
}, },
customOptions customOptions
); );
@ -1391,12 +1394,12 @@ module.exports = ({ cooler, isPublic }) => {
source, source,
// Make sure we're only getting private messages that are posts. // Make sure we're only getting private messages that are posts.
pull.filter( pull.filter(
message => (message) =>
typeof message.value.content !== "string" && typeof message.value.content !== "string" &&
lodash.get(message, "value.meta.private") && lodash.get(message, "value.meta.private") &&
lodash.get(message, "value.content.type") === "post" lodash.get(message, "value.content.type") === "post"
), ),
pull.unique(message => { pull.unique((message) => {
const { root } = message.value.content; const { root } = message.value.content;
if (root == null) { if (root == null) {
return message.key; return message.key;
@ -1416,7 +1419,7 @@ module.exports = ({ cooler, isPublic }) => {
}); });
return messages; return messages;
} },
}; };
models.post = post; models.post = post;
@ -1430,12 +1433,12 @@ module.exports = ({ cooler, isPublic }) => {
type: "vote", type: "vote",
vote: { vote: {
link: messageKey, link: messageKey,
value: Number(value) value: Number(value),
}, },
branch, branch,
recps recps,
}); });
} },
}; };
return models; return models;

View File

@ -18,7 +18,7 @@ const remote = `unix:${socketPath}~noauth:${publicInteger}`;
// This is unnecessary when https://github.com/ssbc/ssb-config/pull/72 is merged // This is unnecessary when https://github.com/ssbc/ssb-config/pull/72 is merged
ssbConfig.connections.incoming.unix = [ ssbConfig.connections.incoming.unix = [
{ scope: "device", transform: "noauth" } { scope: "device", transform: "noauth" },
]; ];
const server = flotilla(ssbConfig); const server = flotilla(ssbConfig);
@ -33,7 +33,7 @@ const log = (...args) => {
const rawConnect = () => const rawConnect = () =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
ssbClient(null, { remote }) ssbClient(null, { remote })
.then(api => { .then((api) => {
if (api.tangle === undefined) { if (api.tangle === undefined) {
// HACK: SSB-Tangle isn't available in Patchwork, but we want that // HACK: SSB-Tangle isn't available in Patchwork, but we want that
// compatibility. This code automatically injects SSB-Tangle into our // compatibility. This code automatically injects SSB-Tangle into our
@ -53,14 +53,14 @@ const rawConnect = () =>
let handle; let handle;
const createConnection = config => { const createConnection = (config) => {
handle = new Promise(resolve => { handle = new Promise((resolve) => {
rawConnect() rawConnect()
.then(ssb => { .then((ssb) => {
log("Using pre-existing Scuttlebutt server instead of starting one"); log("Using pre-existing Scuttlebutt server instead of starting one");
resolve(ssb); resolve(ssb);
}) })
.catch(e => { .catch((e) => {
if (e.message !== "could not connect to sbot") { if (e.message !== "could not connect to sbot") {
throw e; throw e;
} }
@ -69,11 +69,11 @@ const createConnection = config => {
server(config); server(config);
const connectOrRetry = () => { const connectOrRetry = () => {
rawConnect() rawConnect()
.then(ssb => { .then((ssb) => {
log("Retrying connection to own server"); log("Retrying connection to own server");
resolve(ssb); resolve(ssb);
}) })
.catch(e => { .catch((e) => {
if (e.message !== "could not connect to sbot") { if (e.message !== "could not connect to sbot") {
log(e); log(e);
} }
@ -98,8 +98,8 @@ module.exports = ({ offline }) => {
const config = { const config = {
conn: { conn: {
autostart: !offline autostart: !offline,
} },
}; };
createConnection(config); createConnection(config);
@ -117,14 +117,14 @@ module.exports = ({ offline }) => {
// If the connection is closed, we need to restart it. It's important to // If the connection is closed, we need to restart it. It's important to
// note that if we're depending on an external service (like Patchwork) and // note that if we're depending on an external service (like Patchwork) and
// that app is closed, then Oasis will seamlessly start its own SSB service. // that app is closed, then Oasis will seamlessly start its own SSB service.
return new Promise(resolve => { return new Promise((resolve) => {
handle.then(ssb => { handle.then((ssb) => {
if (ssb.closed) { if (ssb.closed) {
createConnection(); createConnection();
} }
resolve(handle); resolve(handle);
}); });
}); });
} },
}; };
}; };

View File

@ -7,7 +7,7 @@ module.exports = {
extendedDescription: [ extendedDescription: [
"Posts from ", "Posts from ",
strong("people you don't follow"), strong("people you don't follow"),
", sorted by recency. When you follow someone you may download messages from the people they follow, and those messages show up here." ", sorted by recency. When you follow someone you may download messages from the people they follow, and those messages show up here.",
], ],
popular: "Popular", popular: "Popular",
popularDescription: [ popularDescription: [
@ -15,7 +15,7 @@ module.exports = {
strong("hearts"), strong("hearts"),
" in a given period. Hearts are counted from ", " in a given period. Hearts are counted from ",
em("everyone"), em("everyone"),
", including people you don't follow, so this shows posts from your friends that are popular in your extended network." ", including people you don't follow, so this shows posts from your friends that are popular in your extended network.",
], ],
latest: "Latest", latest: "Latest",
latestDescription: latestDescription:
@ -23,12 +23,12 @@ module.exports = {
topics: "Topics", topics: "Topics",
topicsDescription: [ topicsDescription: [
strong("Topics"), strong("Topics"),
" from yourself and people you follow, sorted by recency. Select the timestamp of any post to see the rest of the thread." " from yourself and people you follow, sorted by recency. Select the timestamp of any post to see the rest of the thread.",
], ],
summaries: "Summaries", summaries: "Summaries",
summariesDescription: [ summariesDescription: [
strong("Topics and some comments"), strong("Topics and some comments"),
" from yourself and people you follow, sorted by recency. Select the timestamp of any post to see the rest of the thread." " from yourself and people you follow, sorted by recency. Select the timestamp of any post to see the rest of the thread.",
], ],
profile: "Profile", profile: "Profile",
manualMode: "Manual Mode", manualMode: "Manual Mode",
@ -37,13 +37,13 @@ module.exports = {
strong("Posts that mention you"), strong("Posts that mention you"),
" from ", " from ",
strong("anyone"), strong("anyone"),
" sorted by recency. Sometimes people may forget to @mention you, and those posts won't show up here." " sorted by recency. Sometimes people may forget to @mention you, and those posts won't show up here.",
], ],
private: "Private", private: "Private",
privateDescription: [ privateDescription: [
"The latest comment from ", "The latest comment from ",
strong("private threads that include you"), strong("private threads that include you"),
", sorted by recency. Private posts are encrypted for your public key, and have a maximum of 7 recipients. Recipients cannot be added after the thread has started. Select the timestamp to view the full thread." ", sorted by recency. Private posts are encrypted for your public key, and have a maximum of 7 recipients. Recipients cannot be added after the thread has started. Select the timestamp to view the full thread.",
], ],
search: "Search", search: "Search",
settings: "Settings", settings: "Settings",
@ -69,12 +69,12 @@ module.exports = {
publishCustomDescription: [ publishCustomDescription: [
"Publish a custom message by entering ", "Publish a custom message by entering ",
a({ href: "https://en.wikipedia.org/wiki/JSON" }, "JSON"), a({ href: "https://en.wikipedia.org/wiki/JSON" }, "JSON"),
" below. This may be useful for prototyping or publishing messages that Oasis doesn't support. This message cannot be edited or deleted." " below. This may be useful for prototyping or publishing messages that Oasis doesn't support. This message cannot be edited or deleted.",
], ],
commentWarning: [ commentWarning: [
" Comments cannot be edited or deleted. To respond to an individual message, select ", " Comments cannot be edited or deleted. To respond to an individual message, select ",
strong("reply"), strong("reply"),
" instead." " instead.",
], ],
commentPublic: "public", commentPublic: "public",
commentPrivate: "private", commentPrivate: "private",
@ -83,28 +83,28 @@ module.exports = {
strong(`${publicOrPrivate} comment`), strong(`${publicOrPrivate} comment`),
" on this thread with ", " on this thread with ",
a({ href: markdownUrl }, "Markdown"), a({ href: markdownUrl }, "Markdown"),
"." ".",
], ],
publishLabel: ({ markdownUrl, linkTarget }) => [ publishLabel: ({ markdownUrl, linkTarget }) => [
"Write a new public post in ", "Write a new public post in ",
a( a(
{ {
href: markdownUrl, href: markdownUrl,
target: linkTarget target: linkTarget,
}, },
"Markdown" "Markdown"
), ),
". Posts cannot be edited or deleted." ". Posts cannot be edited or deleted.",
], ],
publishCustomInfo: ({ href }) => [ publishCustomInfo: ({ href }) => [
"If you're an advanced user, you can also ", "If you're an advanced user, you can also ",
a({ href }, "publish a custom message"), a({ href }, "publish a custom message"),
"." ".",
], ],
publishBasicInfo: ({ href }) => [ publishBasicInfo: ({ href }) => [
"If you're not an advanced user, you should ", "If you're not an advanced user, you should ",
a({ href }, "publish a post"), a({ href }, "publish a post"),
"." ".",
], ],
publishCustom: "Publish custom", publishCustom: "Publish custom",
@ -115,13 +115,13 @@ module.exports = {
a({ href: markdownUrl }, "Markdown"), a({ href: markdownUrl }, "Markdown"),
". Messages cannot be edited or deleted. To respond to an entire thread, select ", ". Messages cannot be edited or deleted. To respond to an entire thread, select ",
strong("comment"), strong("comment"),
" instead." " instead.",
], ],
// settings // settings
settingsIntro: ({ readmeUrl, version }) => [ settingsIntro: ({ readmeUrl, version }) => [
`You're using Oasis ${version}. Check out `, `You're using Oasis ${version}. Check out `,
a({ href: readmeUrl }, "the readme"), a({ href: readmeUrl }, "the readme"),
", configure your theme, or view debugging information below." ", configure your theme, or view debugging information below.",
], ],
theme: "Theme", theme: "Theme",
themeIntro: themeIntro:
@ -152,11 +152,11 @@ module.exports = {
// posts and comments // posts and comments
commentDescription: ({ parentUrl }) => [ commentDescription: ({ parentUrl }) => [
"commented on ", "commented on ",
a({ href: parentUrl }, " thread") a({ href: parentUrl }, " thread"),
], ],
replyDescription: ({ parentUrl }) => [ replyDescription: ({ parentUrl }) => [
"replied to ", "replied to ",
a({ href: parentUrl }, " message") a({ href: parentUrl }, " message"),
], ],
mysteryDescription: "posted a mysterious message", mysteryDescription: "posted a mysterious message",
// misc // misc
@ -169,7 +169,7 @@ module.exports = {
profileImage: "Profile image", profileImage: "Profile image",
profileDescription: "Profile description (Markdown)", profileDescription: "Profile description (Markdown)",
hashtagDescription: hashtagDescription:
"Posts from people in your network that reference this hashtag, sorted by recency." "Posts from people in your network that reference this hashtag, sorted by recency.",
}, },
/* spell-checker: disable */ /* spell-checker: disable */
es: { es: {
@ -182,7 +182,7 @@ module.exports = {
extendedDescription: [ extendedDescription: [
"Publicaciones de ", "Publicaciones de ",
strong("personas que no seguís"), strong("personas que no seguís"),
", ordenadas por las más recientes. Quando seguís una persona poderás descargar publicaciones de otras personas que esta siga y esos mensajes aparecen aquí." ", ordenadas por las más recientes. Quando seguís una persona poderás descargar publicaciones de otras personas que esta siga y esos mensajes aparecen aquí.",
], ],
popular: "Populares", popular: "Populares",
popularDescription: [ popularDescription: [
@ -190,32 +190,32 @@ module.exports = {
strong("Me Gusta"), strong("Me Gusta"),
" en determinados periodos. Se cuentan los Me Gusta de ", " en determinados periodos. Se cuentan los Me Gusta de ",
em("todos"), em("todos"),
", incluindo aquellos que no seguís. Esta es una lista de publicaciones más populares de tu red de contacto." ", incluindo aquellos que no seguís. Esta es una lista de publicaciones más populares de tu red de contacto.",
], ],
latestDescription: latestDescription:
"Publicaciones que aquellos que seguís, ordenadas por las más recientes.", "Publicaciones que aquellos que seguís, ordenadas por las más recientes.",
topics: "Topicos", topics: "Topicos",
topicsDescription: [ topicsDescription: [
strong("Topicos"), strong("Topicos"),
" de las personas que seguís, ordenadas por las más recientes. Seleccioná la hora de una publicación para leer el hilo completo." " de las personas que seguís, ordenadas por las más recientes. Seleccioná la hora de una publicación para leer el hilo completo.",
], ],
summaries: "Resumen", summaries: "Resumen",
summariesDescription: [ summariesDescription: [
strong("Topicos y algunos comentarios"), strong("Topicos y algunos comentarios"),
" de las personas que seguís, ordenadas por las más recientes. Seleccioná la hora de una publicación para leer el hilo completo." " de las personas que seguís, ordenadas por las más recientes. Seleccioná la hora de una publicación para leer el hilo completo.",
], ],
manualMode: "Modo manual", manualMode: "Modo manual",
mentions: "Menciones", mentions: "Menciones",
mentionsDescription: [ mentionsDescription: [
strong("Publicaciones de "), strong("Publicaciones de "),
strong("cualquier persona"), strong("cualquier persona"),
" que te mencionan, ordenadas por las más recientes. Solo figuran menciones en el formato @mención." " que te mencionan, ordenadas por las más recientes. Solo figuran menciones en el formato @mención.",
], ],
private: "Privado", private: "Privado",
privateDescription: [ privateDescription: [
"Los comentarios más recientes de ", "Los comentarios más recientes de ",
strong("hilos privados que te incluyen"), strong("hilos privados que te incluyen"),
". Las publicaciones privadas están encriptadas para tu llave privada, y contienen el máximo de 7 destinatarios. No se podrán adicionar nuevos destinarios después que empieze el hilo. Seleccioná la hora de una publicación para leer el hilo completo." ". Las publicaciones privadas están encriptadas para tu llave privada, y contienen el máximo de 7 destinatarios. No se podrán adicionar nuevos destinarios después que empieze el hilo. Seleccioná la hora de una publicación para leer el hilo completo.",
], ],
// post actions // post actions
comment: "Comentar", comment: "Comentar",
@ -239,12 +239,12 @@ module.exports = {
publishCustomDescription: [ publishCustomDescription: [
"Compone un mensaje custom usando ", "Compone un mensaje custom usando ",
a({ href: "https://en.wikipedia.org/wiki/JSON" }, "JSON"), a({ href: "https://en.wikipedia.org/wiki/JSON" }, "JSON"),
". Esto puede ser util para prototipar o componer tipos de mensaje que Oasis aún no soporta. Este mensaje no podrá ser editado o borrado." ". Esto puede ser util para prototipar o componer tipos de mensaje que Oasis aún no soporta. Este mensaje no podrá ser editado o borrado.",
], ],
commentWarning: [ commentWarning: [
" Los mensajes no podrán ser editados o borrados. Para responde a mensajes, seleccione ", " Los mensajes no podrán ser editados o borrados. Para responde a mensajes, seleccione ",
strong("Responder"), strong("Responder"),
"." ".",
], ],
commentPublic: "publico", commentPublic: "publico",
commentPrivate: "privado", commentPrivate: "privado",
@ -253,28 +253,28 @@ module.exports = {
strong(`${publicOrPrivate} comentário`), strong(`${publicOrPrivate} comentário`),
" con ", " con ",
a({ href: markdownUrl }, "Markdown"), a({ href: markdownUrl }, "Markdown"),
" en este hilo." " en este hilo.",
], ],
publishLabel: ({ markdownUrl, linkTarget }) => [ publishLabel: ({ markdownUrl, linkTarget }) => [
"Escribí mensaje publico con ", "Escribí mensaje publico con ",
a( a(
{ {
href: markdownUrl, href: markdownUrl,
target: linkTarget target: linkTarget,
}, },
"Markdown" "Markdown"
), ),
". Los mensajes no podrán ser editados o borrados." ". Los mensajes no podrán ser editados o borrados.",
], ],
publishCustomInfo: ({ href }) => [ publishCustomInfo: ({ href }) => [
"Si sos un usário avanzado, podrás ", "Si sos un usário avanzado, podrás ",
a({ href }, "publicar un mensaje custom"), a({ href }, "publicar un mensaje custom"),
"." ".",
], ],
publishBasicInfo: ({ href }) => [ publishBasicInfo: ({ href }) => [
"Si no sos un usuário avanzado, podés ", "Si no sos un usuário avanzado, podés ",
a({ href }, "publicar un mensaje basico."), a({ href }, "publicar un mensaje basico."),
"." ".",
], ],
publishCustom: "Publicar custom", publishCustom: "Publicar custom",
@ -285,13 +285,13 @@ module.exports = {
a({ href: markdownUrl }, "Markdown"), a({ href: markdownUrl }, "Markdown"),
". Los mensajes no podrán ser editados o borrados. Para responder a todo un hilo, seleccioná ", ". Los mensajes no podrán ser editados o borrados. Para responder a todo un hilo, seleccioná ",
strong("comentário"), strong("comentário"),
"." ".",
], ],
// settings // settings
settingsIntro: ({ readmeUrl, version }) => [ settingsIntro: ({ readmeUrl, version }) => [
`Estás usando Oasis ${version}. Leé `, `Estás usando Oasis ${version}. Leé `,
a({ href: readmeUrl }, "el Readme"), a({ href: readmeUrl }, "el Readme"),
", configura un tema, o consultá información de debug abajo." ", configura un tema, o consultá información de debug abajo.",
], ],
theme: "Tema", theme: "Tema",
themeIntro: themeIntro:
@ -323,11 +323,11 @@ module.exports = {
// posts and comments // posts and comments
commentDescription: ({ parentUrl }) => [ commentDescription: ({ parentUrl }) => [
"comentado en el hilo ", "comentado en el hilo ",
a({ href: parentUrl }, "") a({ href: parentUrl }, ""),
], ],
replyDescription: ({ parentUrl }) => [ replyDescription: ({ parentUrl }) => [
"respondido al mensaje ", "respondido al mensaje ",
a({ href: parentUrl }, "") a({ href: parentUrl }, ""),
], ],
mysteryDescription: "publicó un mensaje misterioso", mysteryDescription: "publicó un mensaje misterioso",
// misc // misc
@ -340,14 +340,14 @@ module.exports = {
profileImage: "Imagen de perfil", profileImage: "Imagen de perfil",
profileDescription: "Descripción de perfil (Markdown)", profileDescription: "Descripción de perfil (Markdown)",
hashtagDescription: hashtagDescription:
"Publicaciones de personas en tu red que mencionan este hashtag, ordenadas por las más recientes." "Publicaciones de personas en tu red que mencionan este hashtag, ordenadas por las más recientes.",
}, },
de: { de: {
extended: "Erweitert", extended: "Erweitert",
extendedDescription: [ extendedDescription: [
"Beiträge von ", "Beiträge von ",
strong("Leuten denen du nicht folgst"), strong("Leuten denen du nicht folgst"),
", sortiert nach Aktualität. Wenn du jemandem folgst lädst du eventuell auch Beiträge von Leuten herunter denen diese Person folgt, hier erscheinen diese Beiträge." ", sortiert nach Aktualität. Wenn du jemandem folgst lädst du eventuell auch Beiträge von Leuten herunter denen diese Person folgt, hier erscheinen diese Beiträge.",
], ],
popular: "Beliebt", popular: "Beliebt",
popularDescription: [ popularDescription: [
@ -355,7 +355,7 @@ module.exports = {
strong("Herzen"), strong("Herzen"),
" in der angegebenen Periode. Herzen werden von ", " in der angegebenen Periode. Herzen werden von ",
em("jedem"), em("jedem"),
" gezählt, auch von Personen denen du nicht folgst. D.h. hier werden Beiträge von deinen Freund*innen angezeigt die in deinem erweiterten Netzwerk populär sind." " gezählt, auch von Personen denen du nicht folgst. D.h. hier werden Beiträge von deinen Freund*innen angezeigt die in deinem erweiterten Netzwerk populär sind.",
], ],
latest: "Aktuell", latest: "Aktuell",
latestDescription: latestDescription:
@ -363,12 +363,12 @@ module.exports = {
topics: "Themen", topics: "Themen",
topicsDescription: [ topicsDescription: [
strong("Themen"), strong("Themen"),
" von Leuten denen du folgst, sortiert nach Aktualität. Klicke auf den Zeitstempel eines Beitrages um den Rest des Threads zu sehen." " von Leuten denen du folgst, sortiert nach Aktualität. Klicke auf den Zeitstempel eines Beitrages um den Rest des Threads zu sehen.",
], ],
summaries: "Übersicht", summaries: "Übersicht",
summariesDescription: [ summariesDescription: [
strong("Themen und einige Kommentare"), strong("Themen und einige Kommentare"),
" von Leuten denen du folgst, sortiert nach Aktualität. Klicke auf den Zeitstempel eines Beitrages um den Rest des Threads zu sehen." " von Leuten denen du folgst, sortiert nach Aktualität. Klicke auf den Zeitstempel eines Beitrages um den Rest des Threads zu sehen.",
], ],
profile: "Profil", profile: "Profil",
manualMode: "Manueller Modus", manualMode: "Manueller Modus",
@ -377,13 +377,13 @@ module.exports = {
strong("Beiträge in denen du erwähnt wirst"), strong("Beiträge in denen du erwähnt wirst"),
" von ", " von ",
strong("allen"), strong("allen"),
", sortiert nach Aktualität. Manchmal vergessen Leute dich zu @erwähnen, diese Beiträge werden hier nicht erscheinen." ", sortiert nach Aktualität. Manchmal vergessen Leute dich zu @erwähnen, diese Beiträge werden hier nicht erscheinen.",
], ],
private: "Privat", private: "Privat",
privateDescription: [ privateDescription: [
"Die letzten Kommentare aus ", "Die letzten Kommentare aus ",
strong("privaten Threads die dich beinhalten"), strong("privaten Threads die dich beinhalten"),
", sortiert nach Aktualität. Private Beiträge werden mit deinem öffentlichen Schlüssel verschlüsselt und haben maximal 7 Empfänger*innen. Empfänger*innen können nicht hinzugefügt werden nachdem ein Thread gestartet wurde. Klicke auf den Zeitstämpel um einen komplette Thread anzuzeigen." ", sortiert nach Aktualität. Private Beiträge werden mit deinem öffentlichen Schlüssel verschlüsselt und haben maximal 7 Empfänger*innen. Empfänger*innen können nicht hinzugefügt werden nachdem ein Thread gestartet wurde. Klicke auf den Zeitstämpel um einen komplette Thread anzuzeigen.",
], ],
search: "Suche", search: "Suche",
settings: "Einstellungen", settings: "Einstellungen",
@ -409,12 +409,12 @@ module.exports = {
publishCustomDescription: [ publishCustomDescription: [
"Veröffentliche eine benutzerdefinierte Nachricht durch das Eingeben von ", "Veröffentliche eine benutzerdefinierte Nachricht durch das Eingeben von ",
a({ href: "https://en.wikipedia.org/wiki/JSON" }, "JSON"), a({ href: "https://en.wikipedia.org/wiki/JSON" }, "JSON"),
" unten. Dies kann zum Prototyping oder dem veröffentlichen von Nachrichten die Oasis nicht unterstützt nützlich sein. Diese Nachricht kann nicht bearbeitet oder gelöscht werden." " unten. Dies kann zum Prototyping oder dem veröffentlichen von Nachrichten die Oasis nicht unterstützt nützlich sein. Diese Nachricht kann nicht bearbeitet oder gelöscht werden.",
], ],
commentWarning: [ commentWarning: [
" Nachrichten können nicht bearbeitet oder gelöscht werden. Um auf eine einzelne Nachricht zu antworten, wähle ", " Nachrichten können nicht bearbeitet oder gelöscht werden. Um auf eine einzelne Nachricht zu antworten, wähle ",
strong("antworten"), strong("antworten"),
" stattdessen." " stattdessen.",
], ],
commentPublic: "öffentlichen", commentPublic: "öffentlichen",
commentPrivate: "privaten", commentPrivate: "privaten",
@ -423,28 +423,28 @@ module.exports = {
strong(`${publicOrPrivate} Kommentar`), strong(`${publicOrPrivate} Kommentar`),
" in diesem Thread mit ", " in diesem Thread mit ",
a({ href: markdownUrl }, "Markdown"), a({ href: markdownUrl }, "Markdown"),
"." ".",
], ],
publishLabel: ({ markdownUrl, linkTarget }) => [ publishLabel: ({ markdownUrl, linkTarget }) => [
"Verfasse einen neuen öffentlichen Beitrag in ", "Verfasse einen neuen öffentlichen Beitrag in ",
a( a(
{ {
href: markdownUrl, href: markdownUrl,
target: linkTarget target: linkTarget,
}, },
"Markdown" "Markdown"
), ),
". Beiträge können nicht bearbeitet oder gelöscht werden." ". Beiträge können nicht bearbeitet oder gelöscht werden.",
], ],
publishCustomInfo: ({ href }) => [ publishCustomInfo: ({ href }) => [
"Wenn du ein erfahrener Benutzer bist kannst du auch ", "Wenn du ein erfahrener Benutzer bist kannst du auch ",
a({ href }, "eine benutzerdefinierte Nachricht veröffentlichen"), a({ href }, "eine benutzerdefinierte Nachricht veröffentlichen"),
"." ".",
], ],
publishBasicInfo: ({ href }) => [ publishBasicInfo: ({ href }) => [
"Wenn du kein erfahrener Benutzer bist, solltest du ", "Wenn du kein erfahrener Benutzer bist, solltest du ",
a({ href }, "einen einfachen Beitrag veröffentlichen"), a({ href }, "einen einfachen Beitrag veröffentlichen"),
"." ".",
], ],
publishCustom: "Benutzerdefinierte Veröffentlichung", publishCustom: "Benutzerdefinierte Veröffentlichung",
replyLabel: ({ markdownUrl }) => [ replyLabel: ({ markdownUrl }) => [
@ -454,13 +454,13 @@ module.exports = {
a({ href: markdownUrl }, "Markdown"), a({ href: markdownUrl }, "Markdown"),
". Nachrichten können nicht bearbeitet oder gelöscht werden. Um auf einen kompletten Thread zu antworten, klicke auf ", ". Nachrichten können nicht bearbeitet oder gelöscht werden. Um auf einen kompletten Thread zu antworten, klicke auf ",
strong("kommentieren"), strong("kommentieren"),
" stattdessen." " stattdessen.",
], ],
// settings // settings
settingsIntro: ({ readmeUrl, version }) => [ settingsIntro: ({ readmeUrl, version }) => [
`Du verwendest Oasis ${version}. Lese `, `Du verwendest Oasis ${version}. Lese `,
a({ href: readmeUrl }, "die Readme"), a({ href: readmeUrl }, "die Readme"),
", konfiguriere dein Theme oder schaue dir Debugging-Informationen weiter unten an." ", konfiguriere dein Theme oder schaue dir Debugging-Informationen weiter unten an.",
], ],
theme: "Theme", theme: "Theme",
themeIntro: themeIntro:
@ -491,11 +491,11 @@ module.exports = {
// posts and comments // posts and comments
commentDescription: ({ parentUrl }) => [ commentDescription: ({ parentUrl }) => [
"kommentierte auf ", "kommentierte auf ",
a({ href: parentUrl }, " Thread") a({ href: parentUrl }, " Thread"),
], ],
replyDescription: ({ parentUrl }) => [ replyDescription: ({ parentUrl }) => [
"antwortete auf ", "antwortete auf ",
a({ href: parentUrl }, " Nachricht") a({ href: parentUrl }, " Nachricht"),
], ],
mysteryDescription: "veröffentlichte eine mysteriöse Nachricht", mysteryDescription: "veröffentlichte eine mysteriöse Nachricht",
// misc // misc
@ -507,6 +507,6 @@ module.exports = {
profileName: "Profilname (Text)", profileName: "Profilname (Text)",
profileDescription: "Profilbeschreibung (Markdown)", profileDescription: "Profilbeschreibung (Markdown)",
hashtagDescription: hashtagDescription:
"Beiträge von Leuten in deinem Netzwerk die dieses Hashtag referenzieren, sortiert nach Aktualität." "Beiträge von Leuten in deinem Netzwerk die dieses Hashtag referenzieren, sortiert nach Aktualität.",
} },
}; };

View File

@ -38,7 +38,7 @@ const {
summary, summary,
textarea, textarea,
title, title,
ul ul,
} = require("hyperaxe"); } = require("hyperaxe");
const lodash = require("lodash"); const lodash = require("lodash");
@ -50,7 +50,7 @@ const i18nBase = require("./i18n");
let i18n = null; let i18n = null;
let selectedLanguage = null; let selectedLanguage = null;
exports.setLanguage = language => { exports.setLanguage = (language) => {
selectedLanguage = language; selectedLanguage = language;
i18n = Object.assign({}, i18nBase.en, i18nBase[language]); i18n = Object.assign({}, i18nBase.en, i18nBase[language]);
}; };
@ -60,7 +60,7 @@ const doctypeString = "<!DOCTYPE html>";
const THREAD_PREVIEW_LENGTH = 3; const THREAD_PREVIEW_LENGTH = 3;
const toAttributes = obj => const toAttributes = (obj) =>
Object.entries(obj) Object.entries(obj)
.map(([key, val]) => `${key}=${val}`) .map(([key, val]) => `${key}=${val}`)
.join(", "); .join(", ");
@ -83,11 +83,11 @@ const template = (...elements) => {
meta({ charset: "utf-8" }), meta({ charset: "utf-8" }),
meta({ meta({
name: "description", name: "description",
content: i18n.oasisDescription content: i18n.oasisDescription,
}), }),
meta({ meta({
name: "viewport", name: "viewport",
content: toAttributes({ width: "device-width", "initial-scale": 1 }) content: toAttributes({ width: "device-width", "initial-scale": 1 }),
}) })
), ),
body( body(
@ -96,28 +96,28 @@ const template = (...elements) => {
navLink({ navLink({
href: "/publish", href: "/publish",
emoji: "📝", emoji: "📝",
text: i18n.publish text: i18n.publish,
}), }),
navLink({ navLink({
href: "/public/latest/extended", href: "/public/latest/extended",
emoji: "🗺️", emoji: "🗺️",
text: i18n.extended text: i18n.extended,
}), }),
navLink({ navLink({
href: "/public/popular/day", href: "/public/popular/day",
emoji: "📣", emoji: "📣",
text: i18n.popular text: i18n.popular,
}), }),
navLink({ href: "/public/latest", emoji: "🐇", text: i18n.latest }), navLink({ href: "/public/latest", emoji: "🐇", text: i18n.latest }),
navLink({ navLink({
href: "/public/latest/topics", href: "/public/latest/topics",
emoji: "📖", emoji: "📖",
text: i18n.topics text: i18n.topics,
}), }),
navLink({ navLink({
href: "/public/latest/summaries", href: "/public/latest/summaries",
emoji: "🗒️", emoji: "🗒️",
text: i18n.summaries text: i18n.summaries,
}), }),
navLink({ href: "/profile", emoji: "🐱", text: i18n.profile }), navLink({ href: "/profile", emoji: "🐱", text: i18n.profile }),
navLink({ href: "/mentions", emoji: "💬", text: i18n.mentions }), navLink({ href: "/mentions", emoji: "💬", text: i18n.mentions }),
@ -135,11 +135,11 @@ const template = (...elements) => {
return result; return result;
}; };
const postInAside = msg => { const postInAside = (msg) => {
const encoded = { const encoded = {
key: encodeURIComponent(msg.key), key: encodeURIComponent(msg.key),
author: encodeURIComponent(msg.value.author), author: encodeURIComponent(msg.value.author),
parent: encodeURIComponent(msg.value.content.root) parent: encodeURIComponent(msg.value.content.root),
}; };
const url = { const url = {
@ -150,7 +150,7 @@ const postInAside = msg => {
avatar: msg.value.meta.author.avatar.url, avatar: msg.value.meta.author.avatar.url,
json: `/json/${encoded.key}`, json: `/json/${encoded.key}`,
reply: `/reply/${encoded.key}`, reply: `/reply/${encoded.key}`,
comment: `/comment/${encoded.key}` comment: `/comment/${encoded.key}`,
}; };
const isPrivate = Boolean(msg.value.meta.private); const isPrivate = Boolean(msg.value.meta.private);
@ -197,16 +197,16 @@ const postInAside = msg => {
post: null, post: null,
comment: i18n.commentDescription({ parentUrl: url.parent }), comment: i18n.commentDescription({ parentUrl: url.parent }),
reply: i18n.replyDescription({ parentUrl: url.parent }), reply: i18n.replyDescription({ parentUrl: url.parent }),
mystery: i18n.mysteryDescription mystery: i18n.mysteryDescription,
}; };
const isMarkdownEmpty = md => md === "<p>undefined</p>\n"; const isMarkdownEmpty = (md) => md === "<p>undefined</p>\n";
const articleElement = isMarkdownEmpty(markdownContent) const articleElement = isMarkdownEmpty(markdownContent)
? article( ? article(
{ class: "content" }, { class: "content" },
pre({ pre({
innerHTML: highlightJs.highlight("json", JSON.stringify(msg, null, 2)) innerHTML: highlightJs.highlight("json", JSON.stringify(msg, null, 2))
.value .value,
}) })
) )
: article({ class: "content", innerHTML: markdownContent }); : article({ class: "content", innerHTML: markdownContent });
@ -217,7 +217,7 @@ const postInAside = msg => {
return section( return section(
{ {
class: messageClasses.join(" ") class: messageClasses.join(" "),
}, },
header( header(
span( span(
@ -244,7 +244,7 @@ const postInAside = msg => {
name: "voteValue", name: "voteValue",
type: "submit", type: "submit",
value: likeButton.value, value: likeButton.value,
class: likeButton.class class: likeButton.class,
}, },
`${likeCount}` `${likeCount}`
) )
@ -267,7 +267,7 @@ const postInAside = msg => {
const continueThreadComponent = (thread, isComment) => { const continueThreadComponent = (thread, isComment) => {
const encoded = { const encoded = {
next: encodeURIComponent(thread[THREAD_PREVIEW_LENGTH + 1].key), next: encodeURIComponent(thread[THREAD_PREVIEW_LENGTH + 1].key),
parent: encodeURIComponent(thread[0].key) parent: encodeURIComponent(thread[0].key),
}; };
const left = thread.length - (THREAD_PREVIEW_LENGTH + 1); const left = thread.length - (THREAD_PREVIEW_LENGTH + 1);
let continueLink; let continueLink;
@ -301,7 +301,7 @@ const postAside = ({ key, value }) => {
let postsToShow; let postsToShow;
if (isComment) { if (isComment) {
const commentPosition = thread.findIndex(msg => msg.key === key); const commentPosition = thread.findIndex((msg) => msg.key === key);
postsToShow = thread.slice( postsToShow = thread.slice(
commentPosition + 1, commentPosition + 1,
Math.min(commentPosition + (THREAD_PREVIEW_LENGTH + 1), thread.length) Math.min(commentPosition + (THREAD_PREVIEW_LENGTH + 1), thread.length)
@ -326,7 +326,7 @@ const post = ({ msg, aside = false }) => {
const encoded = { const encoded = {
key: encodeURIComponent(msg.key), key: encodeURIComponent(msg.key),
author: encodeURIComponent(msg.value.author), author: encodeURIComponent(msg.value.author),
parent: encodeURIComponent(msg.value.content.root) parent: encodeURIComponent(msg.value.content.root),
}; };
const url = { const url = {
@ -337,7 +337,7 @@ const post = ({ msg, aside = false }) => {
avatar: msg.value.meta.author.avatar.url, avatar: msg.value.meta.author.avatar.url,
json: `/json/${encoded.key}`, json: `/json/${encoded.key}`,
reply: `/reply/${encoded.key}`, reply: `/reply/${encoded.key}`,
comment: `/comment/${encoded.key}` comment: `/comment/${encoded.key}`,
}; };
const isPrivate = Boolean(msg.value.meta.private); const isPrivate = Boolean(msg.value.meta.private);
@ -390,7 +390,7 @@ const post = ({ msg, aside = false }) => {
post: null, post: null,
comment: i18n.commentDescription({ parentUrl: url.parent }), comment: i18n.commentDescription({ parentUrl: url.parent }),
reply: i18n.replyDescription({ parentUrl: url.parent }), reply: i18n.replyDescription({ parentUrl: url.parent }),
mystery: i18n.mysteryDescription mystery: i18n.mysteryDescription,
}; };
const emptyContent = "<p>undefined</p>\n"; const emptyContent = "<p>undefined</p>\n";
@ -402,7 +402,7 @@ const post = ({ msg, aside = false }) => {
innerHTML: highlightJs.highlight( innerHTML: highlightJs.highlight(
"json", "json",
JSON.stringify(msg, null, 2) JSON.stringify(msg, null, 2)
).value ).value,
}) })
) )
: article({ class: "content", innerHTML: markdownContent }); : article({ class: "content", innerHTML: markdownContent });
@ -415,7 +415,7 @@ const post = ({ msg, aside = false }) => {
{ {
id: msg.key, id: msg.key,
class: messageClasses.join(" "), class: messageClasses.join(" "),
style: `margin-left: ${depth}rem;` style: `margin-left: ${depth}rem;`,
}, },
header( header(
span( span(
@ -454,7 +454,7 @@ const post = ({ msg, aside = false }) => {
name: "voteValue", name: "voteValue",
type: "submit", type: "submit",
value: likeButton.value, value: likeButton.value,
class: likeButton.class class: likeButton.class,
}, },
`${likeCount}` `${likeCount}`
) )
@ -481,7 +481,7 @@ exports.editProfileView = ({ name, description }) =>
{ {
action: "/profile/edit", action: "/profile/edit",
method: "POST", method: "POST",
enctype: "multipart/form-data" enctype: "multipart/form-data",
}, },
label( label(
i18n.profileImage, i18n.profileImage,
@ -493,14 +493,14 @@ exports.editProfileView = ({ name, description }) =>
textarea( textarea(
{ {
autofocus: true, autofocus: true,
name: "description" name: "description",
}, },
description description
) )
), ),
button( button(
{ {
type: "submit" type: "submit",
}, },
i18n.submit i18n.submit
) )
@ -514,7 +514,7 @@ exports.authorView = ({
feedId, feedId,
messages, messages,
name, name,
relationship relationship,
}) => { }) => {
const mention = `[@${name}](${feedId})`; const mention = `[@${name}](${feedId})`;
const markdownMention = highlightJs.highlight("markdown", mention).value; const markdownMention = highlightJs.highlight("markdown", mention).value;
@ -532,11 +532,11 @@ exports.authorView = ({
: form( : form(
{ {
action: `/${contactFormType}/${encodeURIComponent(feedId)}`, action: `/${contactFormType}/${encodeURIComponent(feedId)}`,
method: "post" method: "post",
}, },
button( button(
{ {
type: "submit" type: "submit",
}, },
contactFormType contactFormType
) )
@ -579,7 +579,7 @@ exports.authorView = ({
), ),
pre({ pre({
class: "md-mention", class: "md-mention",
innerHTML: markdownMention innerHTML: markdownMention,
}), }),
description !== "" ? article({ innerHTML: markdown(description) }) : null, description !== "" ? article({ innerHTML: markdown(description) }) : null,
footer( footer(
@ -594,7 +594,7 @@ exports.authorView = ({
return template( return template(
prefix, prefix,
messages.map(msg => post({ msg })) messages.map((msg) => post({ msg }))
); );
}; };
@ -602,7 +602,7 @@ exports.commentView = async ({ messages, myFeedId, parentMessage }) => {
let markdownMention; let markdownMention;
const messageElements = await Promise.all( const messageElements = await Promise.all(
messages.reverse().map(message => { messages.reverse().map((message) => {
debug("%O", message); debug("%O", message);
const authorName = message.value.meta.author.name; const authorName = message.value.meta.author.name;
const authorFeedId = message.value.author; const authorFeedId = message.value.author;
@ -636,13 +636,13 @@ exports.commentView = async ({ messages, myFeedId, parentMessage }) => {
{ {
autofocus: true, autofocus: true,
required: true, required: true,
name: "text" name: "text",
}, },
isPrivate ? null : markdownMention isPrivate ? null : markdownMention
), ),
button( button(
{ {
type: "submit" type: "submit",
}, },
i18n.comment i18n.comment
) )
@ -654,7 +654,7 @@ exports.mentionsView = ({ messages }) => {
return messageListView({ return messageListView({
messages, messages,
viewTitle: i18n.mentions, viewTitle: i18n.mentions,
viewDescription: i18n.mentionsDescription viewDescription: i18n.mentionsDescription,
}); });
}; };
@ -662,7 +662,7 @@ exports.privateView = ({ messages }) => {
return messageListView({ return messageListView({
messages, messages,
viewTitle: i18n.private, viewTitle: i18n.private,
viewDescription: i18n.privateDescription viewDescription: i18n.privateDescription,
}); });
}; };
@ -680,7 +680,7 @@ exports.publishCustomView = async () => {
{ {
autofocus: true, autofocus: true,
required: true, required: true,
name: "text" name: "text",
}, },
"{\n", "{\n",
' "type": "test",\n', ' "type": "test",\n',
@ -689,7 +689,7 @@ exports.publishCustomView = async () => {
), ),
button( button(
{ {
type: "submit" type: "submit",
}, },
i18n.submit i18n.submit
) )
@ -700,7 +700,7 @@ exports.publishCustomView = async () => {
}; };
exports.threadView = ({ messages }) => exports.threadView = ({ messages }) =>
template(messages.map(msg => post({ msg }))); template(messages.map((msg) => post({ msg })));
exports.markdownView = ({ text }) => { exports.markdownView = ({ text }) => {
const rawHtml = md.render(text); const rawHtml = md.render(text);
@ -726,7 +726,7 @@ exports.publishView = () => {
name: "contentWarning", name: "contentWarning",
type: "text", type: "text",
class: "contentWarning", class: "contentWarning",
placeholder: i18n.contentWarningPlaceholder placeholder: i18n.contentWarningPlaceholder,
}) })
), ),
button({ type: "submit" }, i18n.submit) button({ type: "submit" }, i18n.submit)
@ -739,7 +739,7 @@ exports.publishView = () => {
exports.settingsView = ({ status, peers, theme, themeNames, version }) => { exports.settingsView = ({ status, peers, theme, themeNames, version }) => {
const max = status.sync.since; const max = status.sync.since;
const progressElements = Object.entries(status.sync.plugins).map(e => { const progressElements = Object.entries(status.sync.plugins).map((e) => {
const [key, val] = e; const [key, val] = e;
const id = `progress-${key}`; const id = `progress-${key}`;
return div(label(key, progress({ id, value: val, max }, val))); return div(label(key, progress({ id, value: val, max }, val)));
@ -763,7 +763,7 @@ exports.settingsView = ({ status, peers, theme, themeNames, version }) => {
const connButtons = div({ class: "form-button-group" }, [ const connButtons = div({ class: "form-button-group" }, [
startButton, startButton,
restartButton, restartButton,
stopButton stopButton,
]); ]);
const peerList = (peers || []).map(([, data]) => { const peerList = (peers || []).map(([, data]) => {
@ -775,7 +775,7 @@ exports.settingsView = ({ status, peers, theme, themeNames, version }) => {
); );
}); });
const themeElements = themeNames.map(cur => { const themeElements = themeNames.map((cur) => {
const isCurrentTheme = cur === theme; const isCurrentTheme = cur === theme;
if (isCurrentTheme) { if (isCurrentTheme) {
return option({ value: cur, selected: true }, cur); return option({ value: cur, selected: true }, cur);
@ -800,18 +800,18 @@ exports.settingsView = ({ status, peers, theme, themeNames, version }) => {
"0C", "0C",
"0D", "0D",
"0E", "0E",
"0F" "0F",
]; ];
const base16Elements = base16.map(base => const base16Elements = base16.map((base) =>
div({ div({
style: { style: {
"background-color": `var(--base${base})`, "background-color": `var(--base${base})`,
width: `${(1 / base16.length) * 100}%`, width: `${(1 / base16.length) * 100}%`,
height: "1em", height: "1em",
"margin-top": "1em", "margin-top": "1em",
display: "inline-block" display: "inline-block",
} },
}) })
); );
@ -853,7 +853,7 @@ exports.settingsView = ({ status, peers, theme, themeNames, version }) => {
languageOption("en", "English"), languageOption("en", "English"),
languageOption("es", "Español"), languageOption("es", "Español"),
/* cspell:disable-next-line */ /* cspell:disable-next-line */
languageOption("de", "Deutsch") languageOption("de", "Deutsch"),
]), ]),
button({ type: "submit" }, i18n.setLanguage) button({ type: "submit" }, i18n.setLanguage)
), ),
@ -882,9 +882,9 @@ exports.likesView = async ({ messages, feed, name }) => {
return template( return template(
viewInfoBox({ viewInfoBox({
viewTitle: span(authorLink, i18n.likedBy) viewTitle: span(authorLink, i18n.likedBy),
}), }),
messages.map(msg => post({ msg })) messages.map((msg) => post({ msg }))
); );
}; };
@ -894,11 +894,11 @@ const messageListView = ({
viewDescription = null, viewDescription = null,
viewElements = null, viewElements = null,
// If `aside = true`, it will show a few comments in the thread. // If `aside = true`, it will show a few comments in the thread.
aside = null aside = null,
}) => { }) => {
return template( return template(
section(h1(viewTitle), p(viewDescription), viewElements), section(h1(viewTitle), p(viewDescription), viewElements),
messages.map(msg => post({ msg, aside })) messages.map((msg) => post({ msg, aside }))
); );
}; };
@ -907,7 +907,7 @@ exports.popularView = ({ messages, prefix }) => {
messages, messages,
viewElements: prefix, viewElements: prefix,
viewTitle: i18n.popular, viewTitle: i18n.popular,
viewDescription: i18n.popularDescription viewDescription: i18n.popularDescription,
}); });
}; };
@ -915,7 +915,7 @@ exports.extendedView = ({ messages }) => {
return messageListView({ return messageListView({
messages, messages,
viewTitle: i18n.extended, viewTitle: i18n.extended,
viewDescription: i18n.extendedDescription viewDescription: i18n.extendedDescription,
}); });
}; };
@ -923,7 +923,7 @@ exports.latestView = ({ messages }) => {
return messageListView({ return messageListView({
messages, messages,
viewTitle: i18n.latest, viewTitle: i18n.latest,
viewDescription: i18n.latestDescription viewDescription: i18n.latestDescription,
}); });
}; };
@ -931,7 +931,7 @@ exports.topicsView = ({ messages }) => {
return messageListView({ return messageListView({
messages, messages,
viewTitle: i18n.topics, viewTitle: i18n.topics,
viewDescription: i18n.topicsDescription viewDescription: i18n.topicsDescription,
}); });
}; };
@ -940,7 +940,7 @@ exports.summaryView = ({ messages }) => {
messages, messages,
viewTitle: i18n.summaries, viewTitle: i18n.summaries,
viewDescription: i18n.summariesDescription, viewDescription: i18n.summariesDescription,
aside: true aside: true,
}); });
}; };
@ -952,7 +952,7 @@ exports.replyView = async ({ messages, myFeedId }) => {
let markdownMention; let markdownMention;
const messageElements = await Promise.all( const messageElements = await Promise.all(
messages.reverse().map(message => { messages.reverse().map((message) => {
debug("%O", message); debug("%O", message);
const authorName = message.value.meta.author.name; const authorName = message.value.meta.author.name;
const authorFeedId = message.value.author; const authorFeedId = message.value.author;
@ -975,13 +975,13 @@ exports.replyView = async ({ messages, myFeedId }) => {
{ {
autofocus: true, autofocus: true,
required: true, required: true,
name: "text" name: "text",
}, },
markdownMention markdownMention
), ),
button( button(
{ {
type: "submit" type: "submit",
}, },
i18n.reply i18n.reply
) )
@ -994,7 +994,7 @@ exports.searchView = ({ messages, query }) => {
name: "query", name: "query",
required: false, required: false,
type: "search", type: "search",
value: query value: query,
}); });
// - Minimum length of 3 because otherwise SSB-Search hangs forever. :) // - Minimum length of 3 because otherwise SSB-Search hangs forever. :)
@ -1012,20 +1012,20 @@ exports.searchView = ({ messages, query }) => {
label(i18n.searchLabel, searchInput), label(i18n.searchLabel, searchInput),
button( button(
{ {
type: "submit" type: "submit",
}, },
i18n.submit i18n.submit
) )
) )
), ),
messages.map(msg => post({ msg })) messages.map((msg) => post({ msg }))
); );
}; };
exports.hashtagView = ({ messages, hashtag }) => { exports.hashtagView = ({ messages, hashtag }) => {
return template( return template(
section(h1(`#${hashtag}`), p(i18n.hashtagDescription)), section(h1(`#${hashtag}`), p(i18n.hashtagDescription)),
messages.map(msg => post({ msg })) messages.map((msg) => post({ msg }))
); );
}; };
@ -1041,11 +1041,11 @@ exports.indexingView = ({ percent }) => {
meta({ charset: "utf-8" }), meta({ charset: "utf-8" }),
meta({ meta({
name: "description", name: "description",
content: i18n.oasisDescription content: i18n.oasisDescription,
}), }),
meta({ meta({
name: "viewport", name: "viewport",
content: toAttributes({ width: "device-width", "initial-scale": 1 }) content: toAttributes({ width: "device-width", "initial-scale": 1 }),
}), }),
meta({ "http-equiv": "refresh", content: 10 }) meta({ "http-equiv": "refresh", content: 10 })
), ),

View File

@ -6,7 +6,7 @@ const ssbRef = require("ssb-ref");
const { span } = require("hyperaxe"); const { span } = require("hyperaxe");
/** @param {{ link: string}[]} mentions */ /** @param {{ link: string}[]} mentions */
const toUrl = mentions => { const toUrl = (mentions) => {
/** @type {{name: string, link: string}[]} */ /** @type {{name: string, link: string}[]} */
const mentionNames = []; const mentionNames = [];
@ -21,7 +21,7 @@ const toUrl = mentions => {
ssbMessages.links(mentions, "feed").forEach(handleLink); ssbMessages.links(mentions, "feed").forEach(handleLink);
/** @param {string} ref */ /** @param {string} ref */
const urlHandler = ref => { const urlHandler = (ref) => {
// @mentions // @mentions
const found = mentionNames.find(({ name }) => name === ref); const found = mentionNames.find(({ name }) => name === ref);
if (found !== undefined) { if (found !== undefined) {
@ -53,5 +53,5 @@ const toUrl = mentions => {
module.exports = (input, mentions = []) => module.exports = (input, mentions = []) =>
md.block(input, { md.block(input, {
toUrl: toUrl(mentions), toUrl: toUrl(mentions),
emoji: character => span({ class: "emoji" }, character).outerHTML emoji: (character) => span({ class: "emoji" }, character).outerHTML,
}); });