Oasis 0.8.7 mobile (nodejs-project baseline)
This commit is contained in:
commit
e563b86ede
226 changed files with 141968 additions and 0 deletions
144
src/models/pixelia_model.js
Normal file
144
src/models/pixelia_model.js
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
const pull = require('../server/node_modules/pull-stream');
|
||||
const { getConfig } = require('../configs/config-manager.js');
|
||||
const { buildValidatedTombstoneSet } = require('./tombstone_validator');
|
||||
const logLimit = getConfig().ssbLogStream?.limit || 1000;
|
||||
|
||||
module.exports = ({ cooler }) => {
|
||||
let ssb;
|
||||
|
||||
const openSsb = async () => {
|
||||
if (!ssb) ssb = await cooler.open();
|
||||
return ssb;
|
||||
};
|
||||
|
||||
const getPixelByCoordinate = async (coordinateKey) => {
|
||||
const ssbClient = await openSsb();
|
||||
const messages = await new Promise((res, rej) => {
|
||||
pull(
|
||||
ssbClient.createLogStream({ limit: logLimit }),
|
||||
pull.collect((err, msgs) => err ? rej(err) : res(msgs))
|
||||
);
|
||||
});
|
||||
|
||||
const tombstoned = buildValidatedTombstoneSet(messages);
|
||||
|
||||
const replaces = new Map();
|
||||
const byId = new Map();
|
||||
|
||||
for (const m of messages) {
|
||||
const c = m.value?.content;
|
||||
const k = m.key;
|
||||
if (!c || c.type !== 'pixelia' || c.coordinateKey !== coordinateKey) continue;
|
||||
if (tombstoned.has(k)) continue;
|
||||
if (c.replaces) replaces.set(c.replaces, k);
|
||||
byId.set(k, m);
|
||||
}
|
||||
|
||||
for (const [replacedId, replacingId] of replaces.entries()) {
|
||||
const orig = byId.get(replacedId);
|
||||
if (!orig) continue;
|
||||
const rep = byId.get(replacingId);
|
||||
if (!rep || rep.value.author !== orig.value.author) {
|
||||
byId.delete(replacingId);
|
||||
continue;
|
||||
}
|
||||
byId.delete(replacedId);
|
||||
}
|
||||
|
||||
return [...byId.values()][0] || null;
|
||||
};
|
||||
|
||||
const paintPixel = async (x, y, color) => {
|
||||
if (x < 1 || x > 50 || y < 1 || y > 200) {
|
||||
throw new Error('Coordinates out of bounds. Please use x (1-50) and y (1-200)');
|
||||
}
|
||||
|
||||
const ssbClient = await openSsb();
|
||||
const userId = ssbClient.id;
|
||||
const coordinateKey = `${x}:${y}`;
|
||||
const existingPixel = await getPixelByCoordinate(coordinateKey);
|
||||
|
||||
if (existingPixel) {
|
||||
const tombstone = {
|
||||
type: 'tombstone',
|
||||
target: existingPixel.key,
|
||||
deletedAt: new Date().toISOString()
|
||||
};
|
||||
await new Promise((resolve, reject) =>
|
||||
ssbClient.publish(tombstone, err => err ? reject(err) : resolve())
|
||||
);
|
||||
}
|
||||
|
||||
const contributors = existingPixel?.value?.content?.contributors_inhabitants || [];
|
||||
const contributors_inhabitants = contributors.includes(userId)
|
||||
? contributors
|
||||
: [...contributors, userId];
|
||||
|
||||
const content = {
|
||||
type: 'pixelia',
|
||||
x: x - 1,
|
||||
y: y - 1,
|
||||
color,
|
||||
author: userId,
|
||||
contributors_inhabitants,
|
||||
timestamp: Date.now(),
|
||||
coordinateKey,
|
||||
replaces: existingPixel?.key || null
|
||||
};
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
ssbClient.publish(content, (err) => err ? reject(err) : resolve());
|
||||
});
|
||||
};
|
||||
|
||||
const listPixels = async () => {
|
||||
const ssbClient = await openSsb();
|
||||
const messages = await new Promise((res, rej) => {
|
||||
pull(
|
||||
ssbClient.createLogStream({ limit: logLimit }),
|
||||
pull.collect((err, msgs) => err ? rej(err) : res(msgs))
|
||||
);
|
||||
});
|
||||
|
||||
const tombstoned = buildValidatedTombstoneSet(messages);
|
||||
const replaces = new Map();
|
||||
const byKey = new Map();
|
||||
|
||||
for (const m of messages) {
|
||||
const c = m.value?.content;
|
||||
const k = m.key;
|
||||
if (!c) continue;
|
||||
if (c.type === 'tombstone') continue;
|
||||
if (c.type === 'pixelia') {
|
||||
if (tombstoned.has(k)) continue;
|
||||
if (c.replaces) replaces.set(c.replaces, k);
|
||||
byKey.set(k, m);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [replacedId, replacingId] of replaces.entries()) {
|
||||
const orig = byKey.get(replacedId);
|
||||
if (!orig) continue;
|
||||
const rep = byKey.get(replacingId);
|
||||
if (!rep || rep.value.author !== orig.value.author) {
|
||||
byKey.delete(replacingId);
|
||||
continue;
|
||||
}
|
||||
byKey.delete(replacedId);
|
||||
}
|
||||
|
||||
return Array.from(byKey.values()).map(m => ({
|
||||
x: m.value.content.x + 1,
|
||||
y: m.value.content.y + 1,
|
||||
color: m.value.content.color,
|
||||
author: m.value.author,
|
||||
contributors_inhabitants: m.value.content.contributors_inhabitants || [],
|
||||
timestamp: m.value.timestamp
|
||||
}));
|
||||
};
|
||||
|
||||
return {
|
||||
paintPixel,
|
||||
listPixels
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue