@fyresmith/hive-server 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,6 +19,12 @@ const socketToFiles = new Map();
19
19
  */
20
20
  const userBySocket = new Map();
21
21
 
22
+ /**
23
+ * claimedFiles: file path → { socketId, userId, username, color }
24
+ * @type {Map<string, object>}
25
+ */
26
+ const claimedFiles = new Map();
27
+
22
28
  function respond(cb, payload) {
23
29
  if (typeof cb === 'function') cb(payload);
24
30
  }
@@ -184,6 +190,38 @@ export function attachHandlers(io, getActiveRooms, broadcastFileUpdated, forceCl
184
190
  }
185
191
  });
186
192
 
193
+ // -----------------------------------------------------------------------
194
+ // file-claim
195
+ // -----------------------------------------------------------------------
196
+ socket.on('file-claim', ({ relPath } = {}, cb) => {
197
+ if (!isAllowedPath(relPath)) return respond(cb, { ok: false, error: 'Not allowed' });
198
+ const claimUser = userBySocket.get(socket.id);
199
+ if (!claimUser) return;
200
+ claimedFiles.set(relPath, { socketId: socket.id, ...claimUser });
201
+ io.emit('file-claimed', { relPath, user: claimUser });
202
+ respond(cb, { ok: true });
203
+ });
204
+
205
+ // -----------------------------------------------------------------------
206
+ // file-unclaim
207
+ // -----------------------------------------------------------------------
208
+ socket.on('file-unclaim', ({ relPath } = {}, cb) => {
209
+ const claim = claimedFiles.get(relPath);
210
+ if (claim?.socketId !== socket.id) return respond(cb, { ok: false, error: 'Not your claim' });
211
+ claimedFiles.delete(relPath);
212
+ io.emit('file-unclaimed', { relPath, userId: userBySocket.get(socket.id)?.id });
213
+ respond(cb, { ok: true });
214
+ });
215
+
216
+ // -----------------------------------------------------------------------
217
+ // user-status-changed
218
+ // -----------------------------------------------------------------------
219
+ socket.on('user-status-changed', ({ status } = {}) => {
220
+ const statusUser = userBySocket.get(socket.id);
221
+ if (!statusUser) return;
222
+ socket.broadcast.emit('user-status-changed', { userId: statusUser.id, status });
223
+ });
224
+
187
225
  // -----------------------------------------------------------------------
188
226
  // presence-file-opened
189
227
  // -----------------------------------------------------------------------
@@ -219,6 +257,15 @@ export function attachHandlers(io, getActiveRooms, broadcastFileUpdated, forceCl
219
257
  socket.broadcast.emit('presence-file-closed', { relPath, user });
220
258
  }
221
259
  socketToFiles.delete(socket.id);
260
+
261
+ // Release all file claims held by this socket
262
+ for (const [relPath, claim] of claimedFiles) {
263
+ if (claim.socketId === socket.id) {
264
+ claimedFiles.delete(relPath);
265
+ io.emit('file-unclaimed', { relPath, userId: claim.id });
266
+ }
267
+ }
268
+
222
269
  userBySocket.delete(socket.id);
223
270
  socket.broadcast.emit('user-left', { user });
224
271
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fyresmith/hive-server",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "type": "module",
5
5
  "description": "Collaborative Obsidian vault server",
6
6
  "main": "index.js",