123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- const pull = require('../server/node_modules/pull-stream');
- 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(),
- pull.collect((err, msgs) => err ? rej(err) : res(msgs))
- );
- });
- const tombstoned = new Set(
- messages
- .filter(m => m.value?.content?.type === 'tombstone' && m.value?.content?.target)
- .map(m => m.value.content.target)
- );
- 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 r of replaces.keys()) {
- byId.delete(r);
- }
- 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(),
- pull.collect((err, msgs) => err ? rej(err) : res(msgs))
- );
- });
- const tombstoned = new Set();
- 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' && c.target) {
- tombstoned.add(c.target);
- continue;
- }
- if (c.type === 'pixelia') {
- if (tombstoned.has(k)) continue;
- if (c.replaces) replaces.set(c.replaces, k);
- byKey.set(k, m);
- }
- }
- for (const replaced of replaces.keys()) {
- byKey.delete(replaced);
- }
- 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.content.author,
- contributors_inhabitants: m.value.content.contributors_inhabitants || [],
- timestamp: m.value.timestamp
- }));
- };
- return {
- paintPixel,
- listPixels
- };
- };
|