@majikah/majik-message 0.1.5 → 0.1.7

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.
@@ -134,6 +134,12 @@ export declare class MajikMessageMail {
134
134
  * @returns true if successfully marked, false if already read
135
135
  */
136
136
  markAsRead(recipientPublicKey: MajikMessagePublicKey): boolean;
137
+ /**
138
+ * Marks this mail as unread by removing a recipient from the read_by list.
139
+ * @param recipientPublicKey - The public key of the recipient marking as unread
140
+ * @returns true if successfully unmarked, false if wasn't read
141
+ */
142
+ markAsUnread(recipientPublicKey: MajikMessagePublicKey): boolean;
137
143
  /**
138
144
  * Checks if a specific user has read this mail.
139
145
  */
@@ -557,6 +557,39 @@ export class MajikMessageMail {
557
557
  throw new MailOperationError(`Failed to mark as read: ${error instanceof Error ? error.message : "Unknown error"}`);
558
558
  }
559
559
  }
560
+ /**
561
+ * Marks this mail as unread by removing a recipient from the read_by list.
562
+ * @param recipientPublicKey - The public key of the recipient marking as unread
563
+ * @returns true if successfully unmarked, false if wasn't read
564
+ */
565
+ markAsUnread(recipientPublicKey) {
566
+ try {
567
+ if (!recipientPublicKey ||
568
+ typeof recipientPublicKey !== "string" ||
569
+ recipientPublicKey.trim() === "") {
570
+ throw new MailValidationError("Recipient public key must be a non-empty string");
571
+ }
572
+ const trimmedKey = recipientPublicKey.trim();
573
+ // Verify recipient is in recipients list
574
+ if (!this._recipients.includes(trimmedKey)) {
575
+ throw new MailOperationError(`User ${trimmedKey} is not a recipient of this mail`);
576
+ }
577
+ // Check if not in read_by list (idempotent)
578
+ const readIndex = this._readBy.indexOf(trimmedKey);
579
+ if (readIndex === -1) {
580
+ return false; // Already unread
581
+ }
582
+ // Remove from read_by array
583
+ this._readBy.splice(readIndex, 1);
584
+ return true; // Successfully marked as unread
585
+ }
586
+ catch (error) {
587
+ if (error instanceof MajikMailError) {
588
+ throw error;
589
+ }
590
+ throw new MailOperationError(`Failed to mark as unread: ${error instanceof Error ? error.message : "Unknown error"}`);
591
+ }
592
+ }
560
593
  /**
561
594
  * Checks if a specific user has read this mail.
562
595
  */
@@ -13,7 +13,7 @@ export interface ThreadMetadata {
13
13
  messageCount?: number;
14
14
  }
15
15
  export interface DeletionApproval {
16
- publicKey: string;
16
+ publicKey: MajikMessagePublicKey;
17
17
  approvalHash: string;
18
18
  timestamp: Date;
19
19
  }
@@ -50,6 +50,8 @@ export interface MajikMessageThreadSummary {
50
50
  has_unread: boolean;
51
51
  starred: boolean;
52
52
  subject?: string;
53
+ status: ThreadStatus;
54
+ deletion_requested: boolean;
53
55
  }
54
56
  export interface MajikMessageThreadJSON {
55
57
  id: MajikMessageThreadID;
@@ -113,9 +115,10 @@ export declare class MajikMessageThread {
113
115
  private static generateApprovalHash;
114
116
  validate(): boolean;
115
117
  close(): void;
116
- requestDeletion(publicKey: string): void;
118
+ hasDeletionApproval(publicKey: MajikMessagePublicKey): boolean;
119
+ requestDeletion(publicKey: MajikMessagePublicKey): void;
117
120
  private updateDeletionStatus;
118
- revokeDeletionRequest(publicKey: string): void;
121
+ revokeDeletionRequest(publicKey: MajikMessagePublicKey): void;
119
122
  canBeDeleted(): boolean;
120
123
  getDeletionProgress(): {
121
124
  approved: number;
@@ -141,8 +144,8 @@ export declare class MajikMessageThread {
141
144
  updateMetadata(metadata: Partial<ThreadMetadata>): this;
142
145
  toJSON(): MajikMessageThreadJSON;
143
146
  static fromJSON(json: MajikMessageThreadJSON | string): MajikMessageThread;
144
- isOwner(publicKey: string): boolean;
145
- isParticipant(publicKey: string): boolean;
147
+ isOwner(publicKey: MajikMessagePublicKey): boolean;
148
+ isParticipant(publicKey: MajikMessagePublicKey): boolean;
146
149
  toString(): string;
147
150
  /**
148
151
  * Deduplicates and sorts participants to ensure consistent ordering
@@ -266,6 +266,11 @@ export class MajikMessageThread {
266
266
  }
267
267
  }
268
268
  // ==================== Deletion Approval System ====================
269
+ hasDeletionApproval(publicKey) {
270
+ if (!this.isParticipant(publicKey))
271
+ return false;
272
+ return this._deletionApprovals.some((approval) => approval.publicKey === publicKey);
273
+ }
269
274
  requestDeletion(publicKey) {
270
275
  try {
271
276
  // Validate public key is a participant
@@ -276,9 +281,7 @@ export class MajikMessageThread {
276
281
  if (this._status === ThreadStatus.CLOSED) {
277
282
  throw new OperationNotAllowedError("Cannot request deletion of a closed thread");
278
283
  }
279
- // Check if already approved
280
- const existingApproval = this._deletionApprovals.find((approval) => approval.publicKey === publicKey);
281
- if (existingApproval) {
284
+ if (this.hasDeletionApproval(publicKey)) {
282
285
  throw new OperationNotAllowedError("This participant has already approved deletion");
283
286
  }
284
287
  // Create approval
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@majikah/majik-message",
3
3
  "type": "module",
4
4
  "description": "Encrypt and decrypt messages on any website or platform. Secure chats with keypairs and seed-based accounts. Open source.",
5
- "version": "0.1.5",
5
+ "version": "0.1.7",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Zelijah",
8
8
  "main": "./dist/index.js",