From 86a5753d10ce63cd021bce86bc5ac7a3b0849e4a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sun, 2 Apr 2023 19:54:08 -0500 Subject: [PATCH] Sign nostr event from websocket --- app/soapbox/actions/streaming.ts | 11 +++++- app/soapbox/stream.ts | 16 ++++++--- app/soapbox/types/nostr.ts | 8 +++++ app/soapbox/types/window.d.ts | 7 ++++ package.json | 1 + yarn.lock | 61 ++++++++++++++++++++++++++++++++ 6 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 app/soapbox/types/nostr.ts create mode 100644 app/soapbox/types/window.d.ts diff --git a/app/soapbox/actions/streaming.ts b/app/soapbox/actions/streaming.ts index f3e393f27..c96de40e3 100644 --- a/app/soapbox/actions/streaming.ts +++ b/app/soapbox/actions/streaming.ts @@ -95,7 +95,7 @@ const connectTimelineStream = ( dispatch(disconnectTimeline(timelineId)); }, - onReceive(data: any) { + onReceive(websocket, data: any) { switch (data.event) { case 'update': dispatch(processTimelineUpdate(timelineId, JSON.parse(data.payload), accept)); @@ -181,6 +181,15 @@ const connectTimelineStream = ( case 'marker': dispatch({ type: MARKER_FETCH_SUCCESS, marker: JSON.parse(data.payload) }); break; + case 'nostr:signEvent': + (async () => { + const event = await window.nostr?.signEvent(JSON.parse(data.payload)); + + if (event) { + websocket.send(JSON.stringify({ event: 'nostr:event', payload: event })); + } + })(); + break; } }, }; diff --git a/app/soapbox/stream.ts b/app/soapbox/stream.ts index 1a09a80e4..9370a20ee 100644 --- a/app/soapbox/stream.ts +++ b/app/soapbox/stream.ts @@ -8,10 +8,18 @@ import type { AppDispatch, RootState } from 'soapbox/store'; const randomIntUpTo = (max: number) => Math.floor(Math.random() * Math.floor(max)); +interface ConnectStreamCallbacks { + onConnect(): void + onDisconnect(): void + onReceive(websocket: WebSocket, data: unknown): void +} + +type PollingRefreshFn = (dispatch: AppDispatch, done?: () => void) => void + export function connectStream( path: string, - pollingRefresh: ((dispatch: AppDispatch, done?: () => void) => void) | null = null, - callbacks: (dispatch: AppDispatch, getState: () => RootState) => Record = () => ({ onConnect() {}, onDisconnect() {}, onReceive() {} }), + pollingRefresh: PollingRefreshFn | null = null, + callbacks: (dispatch: AppDispatch, getState: () => RootState) => ConnectStreamCallbacks, ) { return (dispatch: AppDispatch, getState: () => RootState) => { const streamingAPIBaseURL = getState().instance.urls.get('streaming_api'); @@ -35,7 +43,7 @@ export function connectStream( } }; - let subscription: WebSocketClient; + let subscription: WebSocket; // If the WebSocket fails to be created, don't crash the whole page, // just proceed without a subscription. @@ -58,7 +66,7 @@ export function connectStream( }, received(data) { - onReceive(data); + onReceive(subscription, data); }, reconnected() { diff --git a/app/soapbox/types/nostr.ts b/app/soapbox/types/nostr.ts new file mode 100644 index 000000000..b395268ef --- /dev/null +++ b/app/soapbox/types/nostr.ts @@ -0,0 +1,8 @@ +import type { Event, EventTemplate } from 'nostr-tools'; + +interface Nostr { + getPublicKey(): Promise + signEvent(event: EventTemplate): Promise +} + +export default Nostr; \ No newline at end of file diff --git a/app/soapbox/types/window.d.ts b/app/soapbox/types/window.d.ts new file mode 100644 index 000000000..fc86542be --- /dev/null +++ b/app/soapbox/types/window.d.ts @@ -0,0 +1,7 @@ +import type Nostr from './nostr'; + +declare global { + interface Window { + nostr?: Nostr + } +} \ No newline at end of file diff --git a/package.json b/package.json index 50d1409c3..dab0ef4a1 100644 --- a/package.json +++ b/package.json @@ -135,6 +135,7 @@ "localforage": "^1.10.0", "lodash": "^4.7.11", "mini-css-extract-plugin": "^2.6.0", + "nostr-tools": "^1.8.1", "path-browserify": "^1.0.1", "postcss": "^8.4.14", "postcss-loader": "^7.0.0", diff --git a/yarn.lock b/yarn.lock index 646742846..9b7feeeb0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2483,6 +2483,28 @@ dependencies: eslint-scope "5.1.1" +"@noble/curves@~0.8.3": + version "0.8.3" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-0.8.3.tgz#ad6d48baf2599cf1d58dcb734c14d5225c8996e0" + integrity sha512-OqaOf4RWDaCRuBKJLDURrgVxjLmneGsiCXGuzYB5y95YithZMA6w4uk34DHSm0rKMrrYiaeZj48/81EvaAScLQ== + dependencies: + "@noble/hashes" "1.3.0" + +"@noble/hashes@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.0.0.tgz#d5e38bfbdaba174805a4e649f13be9a9ed3351ae" + integrity sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg== + +"@noble/hashes@1.3.0", "@noble/hashes@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" + integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + +"@noble/secp256k1@^1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -2788,6 +2810,28 @@ resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.2.1.tgz#812edd4104a15a493dda1ccac0b352270d7a188c" integrity sha512-XiY0IsyHR+DXYS5vBxpoBe/8veTeoRpMHP+vDosLZxL5bnpetzI0igkxkLZS235ldLzyfkxF+2divEwWHP3vMQ== +"@scure/base@^1.1.1", "@scure/base@~1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + +"@scure/bip32@^1.1.5": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.2.0.tgz#35692d8f8cc3207200239fc119f9e038e5f465df" + integrity sha512-O+vT/hBVk+ag2i6j2CDemwd1E1MtGt+7O1KzrPNsaNvSsiEK55MyPIxJIMI2PS8Ijj464B2VbQlpRoQXxw1uHg== + dependencies: + "@noble/curves" "~0.8.3" + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b" + integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + "@sentry/browser@7.37.2", "@sentry/browser@^7.37.2": version "7.37.2" resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.37.2.tgz#355dd28ad12677d63e0b12c5209d12b3f98ac3a4" @@ -12934,6 +12978,18 @@ normalize-url@^6.0.1: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +nostr-tools@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.8.1.tgz#4e54a354cc88ea0200634da3ee5a1c3466e1794c" + integrity sha512-/2IUe5xINUYT5hYBoEz51dfRaodbRHnyF8n+ZbKWCoh0ZRX6AL88OoDNrWaWWo7tP5j5OyzSL9g/z4TP7bshEA== + dependencies: + "@noble/hashes" "1.0.0" + "@noble/secp256k1" "^1.7.1" + "@scure/base" "^1.1.1" + "@scure/bip32" "^1.1.5" + "@scure/bip39" "^1.1.1" + prettier "^2.8.4" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -14112,6 +14168,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== +prettier@^2.8.4: + version "2.8.7" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" + integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== + pretty-error@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6"