@unicitylabs/sphere-sdk 0.6.0 → 0.6.2

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.
package/README.md CHANGED
@@ -409,6 +409,12 @@ const privateGroup = await gc.createGroup({
409
409
  visibility: GroupVisibility.PRIVATE,
410
410
  });
411
411
 
412
+ // Create a write-restricted group (only admins/writers can post)
413
+ const announcements = await gc.createGroup({
414
+ name: 'Announcements',
415
+ writeRestricted: true,
416
+ });
417
+
412
418
  // Discover and join
413
419
  const available = await gc.fetchAvailableGroups(); // public groups on relay
414
420
  await gc.joinGroup(group.id);
@@ -455,6 +461,7 @@ const members = gc.getMembers(group.id);
455
461
  gc.isCurrentUserAdmin(group.id); // boolean
456
462
  gc.isCurrentUserModerator(group.id); // boolean
457
463
  await gc.canModerateGroup(group.id); // includes relay admin check
464
+ gc.canWriteToGroup(group.id); // false if write-restricted and not admin/moderator
458
465
 
459
466
  // Moderate (requires admin/moderator role)
460
467
  await gc.kickUser(group.id, userPubkey, 'reason');
@@ -487,6 +494,7 @@ interface GroupData {
487
494
  name: string;
488
495
  description?: string;
489
496
  visibility: GroupVisibility; // 'PUBLIC' | 'PRIVATE'
497
+ writeRestricted?: boolean; // Only admins and moderators can post
490
498
  memberCount?: number;
491
499
  unreadCount?: number;
492
500
  lastMessageTime?: number;
@@ -8401,6 +8401,12 @@ var CommunicationsModule = class {
8401
8401
  this.unsubscribeComposing = deps.transport.onComposing?.((indicator) => {
8402
8402
  this.handleComposingIndicator(indicator);
8403
8403
  }) ?? null;
8404
+ if (deps.transport.onChatReady) {
8405
+ deps.transport.onChatReady(() => {
8406
+ const conversations = this.getConversations();
8407
+ deps.emitEvent("communications:ready", { conversationCount: conversations.size });
8408
+ });
8409
+ }
8404
8410
  }
8405
8411
  /**
8406
8412
  * Load messages from storage.
@@ -9043,6 +9049,7 @@ var GroupChatModule = class {
9043
9049
  await this.subscribeToJoinedGroups();
9044
9050
  }
9045
9051
  this.deps.emitEvent("groupchat:connection", { connected: true });
9052
+ this.deps.emitEvent("groupchat:ready", { groupCount: this.groups.size });
9046
9053
  } catch (error) {
9047
9054
  logger.error("GroupChat", "Failed to connect to relays", error);
9048
9055
  this.deps.emitEvent("groupchat:connection", { connected: false });
@@ -9161,17 +9168,23 @@ var GroupChatModule = class {
9161
9168
  const group = this.groups.get(groupId);
9162
9169
  if (!group) return;
9163
9170
  if (event.kind === NIP29_KINDS.GROUP_METADATA) {
9164
- if (!event.content || event.content.trim() === "") return;
9165
- try {
9166
- const metadata = JSON.parse(event.content);
9167
- group.name = metadata.name || group.name;
9168
- group.description = metadata.about || group.description;
9169
- group.picture = metadata.picture || group.picture;
9170
- group.updatedAt = event.created_at * 1e3;
9171
- this.groups.set(groupId, group);
9172
- this.persistGroups();
9173
- } catch {
9171
+ if (event.content && event.content.trim()) {
9172
+ try {
9173
+ const metadata = JSON.parse(event.content);
9174
+ group.name = metadata.name || group.name;
9175
+ group.description = metadata.about || group.description;
9176
+ group.picture = metadata.picture || group.picture;
9177
+ if (metadata["write-restricted"] === true) group.writeRestricted = true;
9178
+ else group.writeRestricted = void 0;
9179
+ } catch {
9180
+ }
9174
9181
  }
9182
+ for (const tag of event.tags) {
9183
+ if (tag[0] === "write-restricted") group.writeRestricted = true;
9184
+ }
9185
+ group.updatedAt = event.created_at * 1e3;
9186
+ this.groups.set(groupId, group);
9187
+ this.persistGroups();
9175
9188
  } else if (event.kind === NIP29_KINDS.GROUP_MEMBERS) {
9176
9189
  this.updateMembersFromEvent(groupId, event);
9177
9190
  } else if (event.kind === NIP29_KINDS.GROUP_ADMINS) {
@@ -9475,7 +9488,8 @@ var GroupChatModule = class {
9475
9488
  picture: options.picture,
9476
9489
  closed: true,
9477
9490
  private: isPrivate,
9478
- hidden: isPrivate
9491
+ hidden: isPrivate,
9492
+ ...options.writeRestricted ? { "write-restricted": true } : {}
9479
9493
  })
9480
9494
  });
9481
9495
  if (!eventId) return null;
@@ -9670,6 +9684,19 @@ var GroupChatModule = class {
9670
9684
  getMessages(groupId) {
9671
9685
  return (this.messages.get(groupId) || []).sort((a, b) => a.timestamp - b.timestamp);
9672
9686
  }
9687
+ getMessagesPage(groupId, options) {
9688
+ const limit = options?.limit ?? 20;
9689
+ const before = options?.before ?? Infinity;
9690
+ const groupMessages = this.messages.get(groupId) ?? [];
9691
+ const filtered = groupMessages.filter((m) => m.timestamp < before).sort((a, b) => b.timestamp - a.timestamp);
9692
+ const page = filtered.slice(0, limit);
9693
+ return {
9694
+ messages: page.reverse(),
9695
+ // chronological order
9696
+ hasMore: filtered.length > limit,
9697
+ oldestTimestamp: page.length > 0 ? page[0].timestamp : null
9698
+ };
9699
+ }
9673
9700
  getMembers(groupId) {
9674
9701
  return (this.members.get(groupId) || []).sort((a, b) => a.joinedAt - b.joinedAt);
9675
9702
  }
@@ -9776,6 +9803,17 @@ var GroupChatModule = class {
9776
9803
  const admins = await this.fetchRelayAdmins();
9777
9804
  return admins.has(myPubkey);
9778
9805
  }
9806
+ /**
9807
+ * Check if current user can write messages to a group.
9808
+ * For write-restricted groups, only admins/moderators can post.
9809
+ * For normal groups, any member can post.
9810
+ */
9811
+ canWriteToGroup(groupId) {
9812
+ const group = this.groups.get(groupId);
9813
+ if (!group) return false;
9814
+ if (!group.writeRestricted) return true;
9815
+ return this.isCurrentUserModerator(groupId);
9816
+ }
9779
9817
  getCurrentUserRole(groupId) {
9780
9818
  const myPubkey = this.getMyPublicKey();
9781
9819
  if (!myPubkey) return null;
@@ -10150,6 +10188,7 @@ var GroupChatModule = class {
10150
10188
  let description;
10151
10189
  let picture;
10152
10190
  let isPrivate = false;
10191
+ let writeRestricted = false;
10153
10192
  if (event.content && event.content.trim()) {
10154
10193
  try {
10155
10194
  const metadata = JSON.parse(event.content);
@@ -10157,6 +10196,7 @@ var GroupChatModule = class {
10157
10196
  description = metadata.about || metadata.description;
10158
10197
  picture = metadata.picture;
10159
10198
  isPrivate = metadata.private === true;
10199
+ if (metadata["write-restricted"] === true) writeRestricted = true;
10160
10200
  } catch {
10161
10201
  }
10162
10202
  }
@@ -10166,6 +10206,7 @@ var GroupChatModule = class {
10166
10206
  if (tag[0] === "picture" && tag[1]) picture = tag[1];
10167
10207
  if (tag[0] === "private") isPrivate = true;
10168
10208
  if (tag[0] === "public" && tag[1] === "false") isPrivate = true;
10209
+ if (tag[0] === "write-restricted") writeRestricted = true;
10169
10210
  }
10170
10211
  return {
10171
10212
  id: groupId,
@@ -10174,6 +10215,7 @@ var GroupChatModule = class {
10174
10215
  description,
10175
10216
  picture,
10176
10217
  visibility: isPrivate ? GroupVisibility.PRIVATE : GroupVisibility.PUBLIC,
10218
+ writeRestricted: writeRestricted || void 0,
10177
10219
  createdAt: event.created_at * 1e3
10178
10220
  };
10179
10221
  } catch {