aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-03-07 08:54:47 -0800
committerAdam Soltys <[email protected]>2026-03-07 08:54:47 -0800
commitad0b4009b2b2ae9173fffb41f0cfa7b35edcb303 (patch)
tree49c2eceb273b73ce5b9c0aba74dafbf6fddce442 /src/lib
parent13b2a17e6f6ed1697c51a6650874313a6da4aa8f (diff)
downloadcoinos-ui-ad0b4009b2b2ae9173fffb41f0cfa7b35edcb303.tar.gz
coinos-ui-ad0b4009b2b2ae9173fffb41f0cfa7b35edcb303.zip
image upload
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/messaging.ts1
-rw-r--r--src/lib/mls-relay.ts60
2 files changed, 61 insertions, 0 deletions
diff --git a/src/lib/messaging.ts b/src/lib/messaging.ts
index ba2d21ad..4cebd2b0 100644
--- a/src/lib/messaging.ts
+++ b/src/lib/messaging.ts
@@ -12,6 +12,7 @@ export {
getGroups,
getGroupRumours,
sendGroupMessage,
+ sendGroupMediaMessage,
findOrCreateDmGroup,
subscribeToMessages,
fetchGroupHistory,
diff --git a/src/lib/mls-relay.ts b/src/lib/mls-relay.ts
index 5eb8dbe3..d29cd535 100644
--- a/src/lib/mls-relay.ts
+++ b/src/lib/mls-relay.ts
@@ -10,8 +10,12 @@ import { SimplePool } from "nostr-tools/pool";
import { Relay } from "nostr-tools/relay";
import { getMarmotClient, getInviteReader, DM_RELAYS } from "$lib/marmot";
import { parseMediaImetaTag } from "@internet-privacy/marmot-ts";
+import { BlossomClient } from "nostr-tools/nipb7";
+import { ensureSigner } from "$lib/nip07";
import { post } from "$lib/utils";
+const BLOSSOM_SERVER = "https://blossom.primal.net";
+
const pool = new SimplePool();
/** Extract member pubkeys from an MLS group's ratchet tree (pass group.state). */
@@ -412,6 +416,62 @@ export async function sendGroupMessage(
cacheGroupRumour(groupId, rumour);
}
+/** Send a media message (image/video/audio) to a group. */
+export async function sendGroupMediaMessage(
+ user: any,
+ groupId: string,
+ file: File | Blob,
+): Promise<void> {
+ const client = getMarmotClient(user.pubkey);
+ const group = await client.getGroup(groupId);
+
+ const mime = file.type || "application/octet-stream";
+ const filename = file instanceof File ? file.name : "file";
+
+ // Encrypt with MLS group key
+ const { encrypted, attachment } = await group.encryptMedia(file, {
+ type: mime,
+ filename,
+ });
+
+ // Upload encrypted blob to blossom
+ const sk = await ensureSigner(user.pubkey);
+ const signer = sk
+ ? {
+ getPublicKey: () => user.pubkey,
+ signEvent: async (draft: any) => {
+ const { finalizeEvent } = await import("nostr-tools/pure");
+ return finalizeEvent(draft, sk);
+ },
+ }
+ : (window as any).nostr;
+
+ const blossom = new BlossomClient(BLOSSOM_SERVER, signer);
+ const encryptedBlob = new Blob([encrypted as BlobPart], { type: "application/octet-stream" });
+ const desc = await blossom.uploadBlob(encryptedBlob);
+
+ // Build imeta tag with the upload URL and MIP-04 fields
+ const imetaTag = [
+ "imeta",
+ `url ${desc.url}`,
+ `m ${attachment.type}`,
+ `x ${attachment.sha256}`,
+ `filename ${attachment.filename}`,
+ `n ${attachment.nonce}`,
+ `v ${attachment.version}`,
+ ];
+ if (attachment.size) imetaTag.push(`size ${attachment.size}`);
+
+ // Pre-cache plaintext so the image renders instantly for us
+ await putCachedMedia(attachment.sha256, file, mime);
+
+ // Send as chat message with the imeta tag
+ await group.sendChatMessage("", [imetaTag]);
+
+ const rumour = makeRumour("", user.pubkey, groupId, [imetaTag]);
+ cacheGroupRumour(groupId, rumour);
+}
+
// ---------------------------------------------------------------------------
// Dynamic group subscription
// ---------------------------------------------------------------------------