upstream: integrar Oasis 1.3.1
Base del merge a tres bandas: git 0.9.5 de epsylon, esta rama y el src de la
APK oficial 1.3.1 (la variante movil: OASIS_MOBILE, regex sin \p{}, audio y
video de Android). Corre en el mismo nodejs-mobile 18.20.4 que ya usabamos.
Se conserva de la rama: Karvan, identidades y multicuenta, topbar y barra
inferior, borrado de EXIF sin sharp, filtro hot del foro, RTC sin STUN y los
modulos apagados en movil (wallet, banking, torrents).
Se toma de upstream lo que ya hace de serie: respuesta citada en chats, ssb-lan
en movil, configuracion en ~/.ssb/oasis (sobrevive a reinstalar el backend).
El asistente de bienvenida deja elegir workflow tambien en movil.
This commit is contained in:
parent
8d838dd8b1
commit
c332afd6ea
265 changed files with 80970 additions and 29167 deletions
|
|
@ -3,7 +3,9 @@ const { getConfig } = require('../configs/config-manager.js');
|
|||
const logLimit = getConfig().ssbLogStream?.limit || 1000;
|
||||
const opinionCategories = require('../backend/opinion_categories');
|
||||
const { buildValidatedTombstoneSet } = require('./tombstone_validator');
|
||||
const { readTyped, CONTENT_TYPES, requestScope } = require('./typed_log');
|
||||
const { buildVoteTally } = require('../backend/vote_tally');
|
||||
const { versionGroups, versionEntry } = require('../backend/dedupe');
|
||||
|
||||
module.exports = ({ cooler }) => {
|
||||
let ssb;
|
||||
|
|
@ -20,27 +22,19 @@ module.exports = ({ cooler }) => {
|
|||
|
||||
const types = [
|
||||
'bookmark', 'votes', 'poll', 'feed',
|
||||
'image', 'audio', 'video', 'document', 'torrent', 'transfer',
|
||||
'industry', 'project', 'report', 'task', 'event', 'shopProduct', 'housing', 'market'
|
||||
'image', 'audio', 'video', 'document', 'torrent', 'file', 'transfer',
|
||||
'industry', 'project', 'report', 'task', 'event', 'shopProduct', 'housing', 'market', 'schoolCourse',
|
||||
'podcast', 'podcastEpisode', 'campaign', 'logisticsRoute'
|
||||
];
|
||||
|
||||
const categories = opinionCategories;
|
||||
|
||||
const listTrending = async (filter = 'ALL') => {
|
||||
const computeBase = async () => {
|
||||
const ssbClient = await openSsb();
|
||||
const userId = ssbClient.id;
|
||||
|
||||
const messages = await new Promise((res, rej) => {
|
||||
pull(
|
||||
ssbClient.createLogStream({ limit: logLimit }),
|
||||
pull.collect((err, xs) => err ? rej(err) : res(xs))
|
||||
);
|
||||
});
|
||||
const messages = await readTyped(ssbClient, CONTENT_TYPES, { limit: logLimit, withWindow: true });
|
||||
|
||||
const tombstoned = buildValidatedTombstoneSet(messages);
|
||||
const replaces = new Map();
|
||||
const itemsById = new Map();
|
||||
const authorOf = new Map();
|
||||
const opinionMsgs = [];
|
||||
|
||||
for (const m of messages) {
|
||||
|
|
@ -48,54 +42,26 @@ module.exports = ({ cooler }) => {
|
|||
const c = m.value?.content;
|
||||
if (!c) continue;
|
||||
|
||||
if (c.type === 'tombstone' && c.target) {
|
||||
tombstoned.add(c.target);
|
||||
itemsById.delete(c.target);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k) authorOf.set(k, m.value?.author);
|
||||
if (c.type === 'tombstone') continue;
|
||||
|
||||
if (typeof c.type === 'string' && c.type.endsWith('Opinion') && c.target) {
|
||||
opinionMsgs.push({ target: c.target, author: m.value?.author, category: c.category });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c.opinions && !tombstoned.has(k) && !['task', 'event', 'report'].includes(c.type)) {
|
||||
if (c.replaces) replaces.set(c.replaces, k);
|
||||
itemsById.set(k, m);
|
||||
}
|
||||
if (c.opinions && !['task', 'event', 'report'].includes(c.type)) itemsById.set(k, m);
|
||||
|
||||
if (c.type === 'feed' && !tombstoned.has(k) && !itemsById.has(k)) {
|
||||
if (c.replaces) replaces.set(c.replaces, k);
|
||||
itemsById.set(k, m);
|
||||
}
|
||||
if (c.type === 'feed' && !itemsById.has(k)) itemsById.set(k, m);
|
||||
}
|
||||
|
||||
for (const [replacedId, newId] of Array.from(replaces.entries())) {
|
||||
const oldAuthor = authorOf.get(replacedId);
|
||||
const newMsg = itemsById.get(newId);
|
||||
if (oldAuthor === undefined) { replaces.delete(replacedId); continue; }
|
||||
if (!newMsg || String(newMsg.value?.author) !== String(oldAuthor)) {
|
||||
replaces.delete(replacedId);
|
||||
itemsById.delete(newId);
|
||||
continue;
|
||||
}
|
||||
itemsById.delete(replacedId);
|
||||
const vg = versionGroups(Array.from(itemsById.values()).map(versionEntry));
|
||||
for (const k of Array.from(itemsById.keys())) {
|
||||
if (!vg.roots.has(vg.rootOf(k)) || vg.tipOf(k) !== k || tombstoned.has(k)) itemsById.delete(k);
|
||||
}
|
||||
|
||||
const tipToChain = new Map();
|
||||
for (const [oldId, newId] of replaces.entries()) {
|
||||
let tip = newId; let g = 0;
|
||||
while (replaces.has(tip) && g++ < 100000) tip = replaces.get(tip);
|
||||
if (!tipToChain.has(tip)) tipToChain.set(tip, new Set([tip]));
|
||||
tipToChain.get(tip).add(oldId); tipToChain.get(tip).add(newId);
|
||||
}
|
||||
const idToTip = new Map();
|
||||
for (const [tip, chain] of tipToChain.entries()) for (const id of chain) idToTip.set(id, tip);
|
||||
const opinionByTip = new Map();
|
||||
for (const op of opinionMsgs) {
|
||||
const tip = idToTip.get(op.target) || op.target;
|
||||
const tip = vg.tipOf(op.target);
|
||||
if (!opinionByTip.has(tip)) opinionByTip.set(tip, { opinions: {}, voters: new Set() });
|
||||
const agg = opinionByTip.get(tip);
|
||||
if (agg.voters.has(op.author)) continue;
|
||||
|
|
@ -114,6 +80,12 @@ module.exports = ({ cooler }) => {
|
|||
}
|
||||
|
||||
let rawItems = Array.from(itemsById.values()).filter(m => types.includes(m.value?.content?.type));
|
||||
rawItems = rawItems.filter(m => {
|
||||
const c = m.value?.content;
|
||||
if (c?.type !== 'schoolCourse') return true;
|
||||
if (c.encryptedPayload) return false;
|
||||
return String(c.visibility || 'PUBLIC').toUpperCase() !== 'INVITE';
|
||||
});
|
||||
const blobTypes = ['document', 'image', 'audio', 'video'];
|
||||
|
||||
let items = await Promise.all(
|
||||
|
|
@ -176,7 +148,21 @@ module.exports = ({ cooler }) => {
|
|||
bySig.set(sig, m);
|
||||
}
|
||||
}
|
||||
items = Array.from(bySig.values());
|
||||
return Array.from(bySig.values());
|
||||
};
|
||||
|
||||
const baseItems = () => {
|
||||
const store = requestScope.getStore();
|
||||
if (store && store.trendingBase) return store.trendingBase;
|
||||
const pending = computeBase();
|
||||
if (store) store.trendingBase = pending;
|
||||
return pending;
|
||||
};
|
||||
|
||||
const listTrending = async (filter = 'ALL') => {
|
||||
const ssbClient = await openSsb();
|
||||
const userId = ssbClient.id;
|
||||
let items = (await baseItems()).slice();
|
||||
|
||||
if (filter === 'MINE') {
|
||||
items = items.filter(m => m.value.author === userId);
|
||||
|
|
@ -232,31 +218,12 @@ module.exports = ({ cooler }) => {
|
|||
|
||||
const inhabitants = Array.isArray(msg.content.opinions_inhabitants) ? msg.content.opinions_inhabitants : [];
|
||||
if (inhabitants.includes(userId)) throw new Error('Already voted');
|
||||
const otype = categories.messageTypes[type];
|
||||
if (!otype) throw new Error('Voting not allowed on this content type');
|
||||
|
||||
const tombstone = {
|
||||
type: 'tombstone',
|
||||
target: contentId,
|
||||
deletedAt: new Date().toISOString(),
|
||||
author: userId
|
||||
};
|
||||
|
||||
const updated = {
|
||||
...msg.content,
|
||||
opinions: {
|
||||
...(msg.content.opinions || {}),
|
||||
[category]: ((msg.content.opinions && msg.content.opinions[category]) || 0) + 1
|
||||
},
|
||||
opinions_inhabitants: inhabitants.concat(userId),
|
||||
updatedAt: new Date().toISOString(),
|
||||
replaces: contentId
|
||||
};
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
ssbClient.publish(tombstone, err => err ? rej(err) : res());
|
||||
});
|
||||
|
||||
const content = { type: otype, target: contentId, category, createdAt: new Date().toISOString() };
|
||||
return new Promise((res, rej) => {
|
||||
ssbClient.publish(updated, (err, result) => err ? rej(err) : res(result));
|
||||
ssbClient.publish(content, (err, result) => err ? rej(err) : res(result));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue