mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-06-06 21:19:50 +02:00
add webrtc test
This commit is contained in:
parent
82c9f93a4f
commit
cfef0cc8b5
@ -68,7 +68,7 @@
|
|||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"ngeohash": "^0.6.3",
|
"ngeohash": "^0.6.3",
|
||||||
"nostr-idb": "^2.1.4",
|
"nostr-idb": "^2.1.4",
|
||||||
"nostr-tools": "2.5.2",
|
"nostr-tools": "^2.7.1",
|
||||||
"nostr-wasm": "^0.1.0",
|
"nostr-wasm": "^0.1.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
@ -117,7 +117,8 @@
|
|||||||
"@types/zen-observable": "^0.8.7",
|
"@types/zen-observable": "^0.8.7",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"camelcase": "^8.0.0",
|
"camelcase": "^8.0.0",
|
||||||
"typescript": "^5.3.3",
|
"eventemitter3": "^5.0.1",
|
||||||
|
"typescript": "^5.5.3",
|
||||||
"vite": "^5.2.10",
|
"vite": "^5.2.10",
|
||||||
"vite-plugin-pwa": "^0.19.8",
|
"vite-plugin-pwa": "^0.19.8",
|
||||||
"workbox-build": "^7.0.0",
|
"workbox-build": "^7.0.0",
|
||||||
|
@ -76,6 +76,7 @@ import MailboxesView from "./views/relays/mailboxes";
|
|||||||
import MediaServersView from "./views/relays/media-servers";
|
import MediaServersView from "./views/relays/media-servers";
|
||||||
import NIP05RelaysView from "./views/relays/nip05";
|
import NIP05RelaysView from "./views/relays/nip05";
|
||||||
import ContactListRelaysView from "./views/relays/contact-list";
|
import ContactListRelaysView from "./views/relays/contact-list";
|
||||||
|
import WebRtcRelaysView from "./views/relays/webrtc";
|
||||||
import UserDMsTab from "./views/user/dms";
|
import UserDMsTab from "./views/user/dms";
|
||||||
import LoginNostrConnectView from "./views/signin/nostr-connect";
|
import LoginNostrConnectView from "./views/signin/nostr-connect";
|
||||||
import ThreadsNotificationsView from "./views/notifications/threads";
|
import ThreadsNotificationsView from "./views/notifications/threads";
|
||||||
@ -291,6 +292,7 @@ const router = createHashRouter([
|
|||||||
{ path: "media-servers", element: <MediaServersView /> },
|
{ path: "media-servers", element: <MediaServersView /> },
|
||||||
{ path: "nip05", element: <NIP05RelaysView /> },
|
{ path: "nip05", element: <NIP05RelaysView /> },
|
||||||
{ path: "contacts", element: <ContactListRelaysView /> },
|
{ path: "contacts", element: <ContactListRelaysView /> },
|
||||||
|
{ path: "webrtc", element: <WebRtcRelaysView /> },
|
||||||
{ path: "sets", element: <BrowseRelaySetsView /> },
|
{ path: "sets", element: <BrowseRelaySetsView /> },
|
||||||
{ path: ":id", element: <RelaySetView /> },
|
{ path: ":id", element: <RelaySetView /> },
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { NostrEvent, AbstractRelay } from "nostr-tools";
|
import { NostrEvent } from "nostr-tools";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
import debug, { Debugger } from "debug";
|
import debug, { Debugger } from "debug";
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { NostrEvent, AbstractRelay } from "nostr-tools";
|
import { NostrEvent } from "nostr-tools";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
import debug, { Debugger } from "debug";
|
import debug, { Debugger } from "debug";
|
||||||
import { getEventUID } from "nostr-idb";
|
import { getEventUID } from "nostr-idb";
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Filter, NostrEvent, AbstractRelay } from "nostr-tools";
|
import { Filter, NostrEvent } from "nostr-tools";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
import debug, { Debugger } from "debug";
|
import debug, { Debugger } from "debug";
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { NostrEvent, AbstractRelay } from "nostr-tools";
|
import { NostrEvent } from "nostr-tools";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
import debug, { Debugger } from "debug";
|
import debug, { Debugger } from "debug";
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Debugger } from "debug";
|
import { Debugger } from "debug";
|
||||||
import { AbstractRelay, Filter, NostrEvent, matchFilters } from "nostr-tools";
|
import { Filter, NostrEvent, matchFilters } from "nostr-tools";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
import { SimpleRelay } from "nostr-idb";
|
import { SimpleRelay } from "nostr-idb";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
|
import { Filter } from "nostr-tools";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import { NostrEvent } from "../types/nostr-event";
|
import { NostrEvent } from "../types/nostr-event";
|
||||||
import relayPoolService from "../services/relay-pool";
|
import relayPoolService from "../services/relay-pool";
|
||||||
import { isFilterEqual } from "../helpers/nostr/filter";
|
import { isFilterEqual } from "../helpers/nostr/filter";
|
||||||
import ControlledObservable from "./controlled-observable";
|
import ControlledObservable from "./controlled-observable";
|
||||||
import { AbstractRelay, Filter } from "nostr-tools";
|
|
||||||
import { offlineMode } from "../services/offline-mode";
|
import { offlineMode } from "../services/offline-mode";
|
||||||
import PersistentSubscription from "./persistent-subscription";
|
import PersistentSubscription from "./persistent-subscription";
|
||||||
import Process from "./process";
|
import Process from "./process";
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { NostrEvent, AbstractRelay } from "nostr-tools";
|
import { NostrEvent } from "nostr-tools";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import relayPoolService from "../services/relay-pool";
|
import relayPoolService from "../services/relay-pool";
|
||||||
import createDefer from "./deferred";
|
import createDefer from "./deferred";
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { Filter, NostrEvent, Relay, Subscription } from "nostr-tools";
|
import { Filter, NostrEvent, Relay } from "nostr-tools";
|
||||||
|
import { Subscription } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import relayPoolService from "../services/relay-pool";
|
import relayPoolService from "../services/relay-pool";
|
||||||
import ControlledObservable from "./controlled-observable";
|
import ControlledObservable from "./controlled-observable";
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { AbstractRelay, Filter, Relay, Subscription, SubscriptionParams } from "nostr-tools";
|
import { Filter, Relay } from "nostr-tools";
|
||||||
|
import { AbstractRelay, Subscription, SubscriptionParams } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import relayPoolService from "../services/relay-pool";
|
import relayPoolService from "../services/relay-pool";
|
||||||
import Process from "./process";
|
import Process from "./process";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ComponentWithAs, IconProps } from "@chakra-ui/react";
|
import { ComponentWithAs, IconProps } from "@chakra-ui/react";
|
||||||
import { SimpleRelay } from "nostr-idb";
|
import { SimpleRelay } from "nostr-idb";
|
||||||
import { AbstractRelay } from "nostr-tools";
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
let lastId = 0;
|
let lastId = 0;
|
||||||
export default class Process {
|
export default class Process {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AbstractRelay } from "nostr-tools";
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
import { logger } from "../helpers/debug";
|
import { logger } from "../helpers/debug";
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { Debugger } from "debug";
|
import { Debugger } from "debug";
|
||||||
import { AbstractRelay, Filter, NostrEvent } from "nostr-tools";
|
import { Filter, NostrEvent } from "nostr-tools";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
|
|
||||||
import MultiSubscription from "./multi-subscription";
|
import MultiSubscription from "./multi-subscription";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Suspense, lazy } from "react";
|
import { Suspense, lazy } from "react";
|
||||||
import type { DecodeResult } from "nostr-tools/lib/types/nip19";
|
import type { DecodeResult } from "nostr-tools/nip19";
|
||||||
import { CardProps, Spinner } from "@chakra-ui/react";
|
import { CardProps, Spinner } from "@chakra-ui/react";
|
||||||
import { kinds } from "nostr-tools";
|
import { kinds } from "nostr-tools";
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { EventTemplate, NostrEvent, kinds } from "nostr-tools";
|
import { EventTemplate, NostrEvent, kinds } from "nostr-tools";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import type { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import type { AddressPointer } from "nostr-tools/nip19";
|
||||||
|
|
||||||
import { ChevronDownIcon, ChevronUpIcon, ExternalLinkIcon } from "../../../icons";
|
import { ChevronDownIcon, ChevronUpIcon, ExternalLinkIcon } from "../../../icons";
|
||||||
import relayHintService from "../../../../services/event-relay-hint";
|
import relayHintService from "../../../../services/event-relay-hint";
|
||||||
|
@ -4,7 +4,7 @@ import { Select, SelectProps } from "@chakra-ui/react";
|
|||||||
import useUserCommunitiesList from "../../hooks/use-user-communities-list";
|
import useUserCommunitiesList from "../../hooks/use-user-communities-list";
|
||||||
import useCurrentAccount from "../../hooks/use-current-account";
|
import useCurrentAccount from "../../hooks/use-current-account";
|
||||||
import { getCommunityName } from "../../helpers/nostr/communities";
|
import { getCommunityName } from "../../helpers/nostr/communities";
|
||||||
import { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer } from "nostr-tools/nip19";
|
||||||
import useReplaceableEvent from "../../hooks/use-replaceable-event";
|
import useReplaceableEvent from "../../hooks/use-replaceable-event";
|
||||||
import { getEventCoordinate } from "../../helpers/nostr/event";
|
import { getEventCoordinate } from "../../helpers/nostr/event";
|
||||||
|
|
||||||
|
@ -1,14 +1,6 @@
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import {
|
import { IconButton, IconButtonProps, useForceUpdate, useInterval, useToast } from "@chakra-ui/react";
|
||||||
Button,
|
import { type AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
ButtonProps,
|
|
||||||
IconButton,
|
|
||||||
IconButtonProps,
|
|
||||||
useForceUpdate,
|
|
||||||
useInterval,
|
|
||||||
useToast,
|
|
||||||
} from "@chakra-ui/react";
|
|
||||||
import { AbstractRelay } from "nostr-tools";
|
|
||||||
|
|
||||||
import relayPoolService from "../../services/relay-pool";
|
import relayPoolService from "../../services/relay-pool";
|
||||||
import { useSigningContext } from "../../providers/global/signing-provider";
|
import { useSigningContext } from "../../providers/global/signing-provider";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ChangeEventHandler } from "react";
|
import { ChangeEventHandler } from "react";
|
||||||
import { Switch, useForceUpdate, useInterval, useToast } from "@chakra-ui/react";
|
import { Switch, useForceUpdate, useInterval, useToast } from "@chakra-ui/react";
|
||||||
import { AbstractRelay } from "nostr-tools";
|
import { type AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import relayPoolService from "../../services/relay-pool";
|
import relayPoolService from "../../services/relay-pool";
|
||||||
import useSubject from "../../hooks/use-subject";
|
import useSubject from "../../hooks/use-subject";
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Badge, useForceUpdate } from "@chakra-ui/react";
|
import { Badge, useForceUpdate } from "@chakra-ui/react";
|
||||||
import { useInterval } from "react-use";
|
import { useInterval } from "react-use";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import relayPoolService from "../../services/relay-pool";
|
import relayPoolService from "../../services/relay-pool";
|
||||||
import { AbstractRelay } from "nostr-tools";
|
|
||||||
import useSubject from "../../hooks/use-subject";
|
import useSubject from "../../hooks/use-subject";
|
||||||
|
|
||||||
const getStatusText = (relay: AbstractRelay, connecting = false) => {
|
const getStatusText = (relay: AbstractRelay, connecting = false) => {
|
||||||
|
@ -5,7 +5,7 @@ import { nanoid } from "nanoid";
|
|||||||
|
|
||||||
import { ATag, DraftNostrEvent, ETag, isATag, isDTag, isETag, isPTag, NostrEvent, Tag } from "../../types/nostr-event";
|
import { ATag, DraftNostrEvent, ETag, isATag, isDTag, isETag, isPTag, NostrEvent, Tag } from "../../types/nostr-event";
|
||||||
import { getMatchNostrLink } from "../regexp";
|
import { getMatchNostrLink } from "../regexp";
|
||||||
import { AddressPointer, DecodeResult, EventPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer, DecodeResult, EventPointer } from "nostr-tools/nip19";
|
||||||
import { safeJson } from "../parse";
|
import { safeJson } from "../parse";
|
||||||
import { safeDecode } from "../nip19";
|
import { safeDecode } from "../nip19";
|
||||||
import { safeRelayUrl, safeRelayUrls } from "../relay";
|
import { safeRelayUrl, safeRelayUrls } from "../relay";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { NostrEvent, isRTag } from "../../types/nostr-event";
|
import { NostrEvent, isRTag } from "../../types/nostr-event";
|
||||||
import { DecodeResult } from "nostr-tools/lib/types/nip19";
|
import { DecodeResult } from "nostr-tools/nip19";
|
||||||
import { getPointerFromTag } from "../nip19";
|
import { getPointerFromTag } from "../nip19";
|
||||||
|
|
||||||
export const GOAL_KIND = 9041;
|
export const GOAL_KIND = 9041;
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { AbstractRelay, Filter, SubCloser, SubscribeManyParams, Subscription } from "nostr-tools";
|
import { Filter } from "nostr-tools";
|
||||||
|
import { SubCloser, SubscribeManyParams } from "nostr-tools/abstract-pool";
|
||||||
|
import { AbstractRelay, Subscription } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
// NOTE: only use this for equality checks and querying
|
// NOTE: only use this for equality checks and querying
|
||||||
export function getRelayVariations(relay: string) {
|
export function getRelayVariations(relay: string) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import type { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import type { AddressPointer } from "nostr-tools/nip19";
|
||||||
|
|
||||||
import useReplaceableEvent from "./use-replaceable-event";
|
import useReplaceableEvent from "./use-replaceable-event";
|
||||||
import { parseDVMMetadata } from "../helpers/nostr/dvm";
|
import { parseDVMMetadata } from "../helpers/nostr/dvm";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
import type { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import type { AddressPointer } from "nostr-tools/nip19";
|
||||||
|
|
||||||
import { CustomAddressPointer, parseCoordinate } from "../helpers/nostr/event";
|
import { CustomAddressPointer, parseCoordinate } from "../helpers/nostr/event";
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
import type { EventPointer } from "nostr-tools/lib/types/nip19";
|
import type { EventPointer } from "nostr-tools/nip19";
|
||||||
|
|
||||||
import { isHexKey } from "../helpers/nip19";
|
import { isHexKey } from "../helpers/nip19";
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
import type { ProfilePointer } from "nostr-tools/lib/types/nip19";
|
import type { ProfilePointer } from "nostr-tools/nip19";
|
||||||
import { isHexKey } from "../helpers/nip19";
|
import { isHexKey } from "../helpers/nip19";
|
||||||
|
|
||||||
export default function useParamsProfilePointer(key: string = "pubkey"): ProfilePointer {
|
export default function useParamsProfilePointer(key: string = "pubkey"): ProfilePointer {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { AbstractRelay, NostrEvent } from "nostr-tools";
|
import { NostrEvent } from "nostr-tools";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import { WIKI_PAGE_KIND } from "../helpers/nostr/wiki";
|
import { WIKI_PAGE_KIND } from "../helpers/nostr/wiki";
|
||||||
import { logger } from "../helpers/debug";
|
import { logger } from "../helpers/debug";
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { AbstractRelay, kinds } from "nostr-tools";
|
import { kinds } from "nostr-tools";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import Subject from "../classes/subject";
|
import Subject from "../classes/subject";
|
||||||
import SuperMap from "../classes/super-map";
|
import SuperMap from "../classes/super-map";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
import type { AddressPointer, EventPointer } from "nostr-tools/lib/types/nip19";
|
import type { AddressPointer, EventPointer } from "nostr-tools/nip19";
|
||||||
|
|
||||||
import { NostrEvent, isDTag } from "../types/nostr-event";
|
import { NostrEvent, isDTag } from "../types/nostr-event";
|
||||||
import relayScoreboardService from "./relay-scoreboard";
|
import relayScoreboardService from "./relay-scoreboard";
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { AbstractRelay, kinds } from "nostr-tools";
|
import { kinds } from "nostr-tools";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import Subject from "../classes/subject";
|
import Subject from "../classes/subject";
|
||||||
import SuperMap from "../classes/super-map";
|
import SuperMap from "../classes/super-map";
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { CacheRelay, openDB } from "nostr-idb";
|
import { CacheRelay, openDB } from "nostr-idb";
|
||||||
import { AbstractRelay } from "nostr-tools";
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import { logger } from "../helpers/debug";
|
import { logger } from "../helpers/debug";
|
||||||
import { safeRelayUrl } from "../helpers/relay";
|
import { safeRelayUrl } from "../helpers/relay";
|
||||||
import WasmRelay from "./wasm-relay";
|
import WasmRelay from "./wasm-relay";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AbstractRelay } from "nostr-tools";
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
import Process from "../classes/process";
|
import Process from "../classes/process";
|
||||||
import relayPoolService from "./relay-pool";
|
import relayPoolService from "./relay-pool";
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { AbstractRelay, NostrEvent } from "nostr-tools";
|
import { NostrEvent } from "nostr-tools";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
|
|
||||||
import SuperMap from "../classes/super-map";
|
import SuperMap from "../classes/super-map";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { nip04, getPublicKey, finalizeEvent } from "nostr-tools";
|
import { nip04, getPublicKey, finalizeEvent, EventTemplate } from "nostr-tools";
|
||||||
import { hexToBytes } from "@noble/hashes/utils";
|
import { hexToBytes } from "@noble/hashes/utils";
|
||||||
|
|
||||||
import { DraftNostrEvent, NostrEvent } from "../types/nostr-event";
|
import { NostrEvent } from "../types/nostr-event";
|
||||||
import { Account } from "./account";
|
import { Account } from "./account";
|
||||||
import db from "./db";
|
import db from "./db";
|
||||||
import serialPortService from "./serial-port";
|
import serialPortService from "./serial-port";
|
||||||
@ -96,7 +96,7 @@ class SigningService {
|
|||||||
return await p;
|
return await p;
|
||||||
}
|
}
|
||||||
|
|
||||||
async requestSignature(draft: DraftNostrEvent, account: Account) {
|
async requestSignature(draft: EventTemplate, account: Account) {
|
||||||
const checkSig = (signed: NostrEvent) => {
|
const checkSig = (signed: NostrEvent) => {
|
||||||
if (signed.pubkey !== account.pubkey) throw new Error("Signed with the wrong pubkey");
|
if (signed.pubkey !== account.pubkey) throw new Error("Signed with the wrong pubkey");
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import SuperMap from "../classes/super-map";
|
import SuperMap from "../classes/super-map";
|
||||||
import { NostrEvent } from "../types/nostr-event";
|
import { NostrEvent } from "../types/nostr-event";
|
||||||
@ -7,7 +8,6 @@ import { logger } from "../helpers/debug";
|
|||||||
import Subject from "../classes/subject";
|
import Subject from "../classes/subject";
|
||||||
import relayPoolService from "./relay-pool";
|
import relayPoolService from "./relay-pool";
|
||||||
import Process from "../classes/process";
|
import Process from "../classes/process";
|
||||||
import { AbstractRelay } from "nostr-tools";
|
|
||||||
import processManager from "./process-manager";
|
import processManager from "./process-manager";
|
||||||
import Code02 from "../components/icons/code-02";
|
import Code02 from "../components/icons/code-02";
|
||||||
import BatchEventLoader from "../classes/batch-event-loader";
|
import BatchEventLoader from "../classes/batch-event-loader";
|
||||||
|
8
src/types/nostr-extensions.d.ts
vendored
8
src/types/nostr-extensions.d.ts
vendored
@ -1,15 +1,19 @@
|
|||||||
import { DraftNostrEvent, NostrEvent } from "./nostr-event";
|
import { EventTemplate, NostrEvent, UnsignedEvent } from "nostr-tools";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
nostr?: {
|
nostr?: {
|
||||||
getPublicKey: () => Promise<string> | string;
|
getPublicKey: () => Promise<string> | string;
|
||||||
signEvent: (event: DraftNostrEvent) => Promise<NostrEvent> | NostrEvent;
|
signEvent: (event: EventTemplate) => Promise<NostrEvent> | NostrEvent;
|
||||||
getRelays?: () => Record<string, { read: boolean; write: boolean }> | string[];
|
getRelays?: () => Record<string, { read: boolean; write: boolean }> | string[];
|
||||||
nip04?: {
|
nip04?: {
|
||||||
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
||||||
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
||||||
};
|
};
|
||||||
|
nip44?: {
|
||||||
|
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
||||||
|
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
src/types/nostr-tools-wasm.d.ts
vendored
3
src/types/nostr-tools-wasm.d.ts
vendored
@ -1,3 +0,0 @@
|
|||||||
declare module "nostr-tools/wasm" {
|
|
||||||
export * from "nostr-tools/lib/types/wasm.d.ts";
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import { AddressPointer, EventPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer, EventPointer } from "nostr-tools/nip19";
|
||||||
import { Button, ButtonGroup, Flex, Heading, SkeletonText, Spinner } from "@chakra-ui/react";
|
import { Button, ButtonGroup, Flex, Heading, SkeletonText, Spinner } from "@chakra-ui/react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
import { EventPointer } from "nostr-tools/lib/types/nip19";
|
import { EventPointer } from "nostr-tools/nip19";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Card,
|
Card,
|
||||||
|
@ -23,7 +23,7 @@ import useCountCommunityMembers from "../../../hooks/use-count-community-members
|
|||||||
import { readablizeSats } from "../../../helpers/bolt11";
|
import { readablizeSats } from "../../../helpers/bolt11";
|
||||||
import User01 from "../../../components/icons/user-01";
|
import User01 from "../../../components/icons/user-01";
|
||||||
import useReplaceableEvent from "../../../hooks/use-replaceable-event";
|
import useReplaceableEvent from "../../../hooks/use-replaceable-event";
|
||||||
import { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer } from "nostr-tools/nip19";
|
||||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||||
|
|
||||||
function CommunityCard({ community, ...props }: Omit<CardProps, "children"> & { community: NostrEvent }) {
|
function CommunityCard({ community, ...props }: Omit<CardProps, "children"> & { community: NostrEvent }) {
|
||||||
|
@ -15,7 +15,7 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import { ChevronLeftIcon } from "../../components/icons";
|
import { ChevronLeftIcon } from "../../components/icons";
|
||||||
import { parseCoordinate } from "../../helpers/nostr/event";
|
import { parseCoordinate } from "../../helpers/nostr/event";
|
||||||
import UserAvatarLink from "../../components/user/user-avatar-link";
|
import UserAvatarLink from "../../components/user/user-avatar-link";
|
||||||
import { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer } from "nostr-tools/nip19";
|
||||||
|
|
||||||
export function useUsersJoinedCommunitiesLists(pubkeys: string[], additionalRelays?: Iterable<string>) {
|
export function useUsersJoinedCommunitiesLists(pubkeys: string[], additionalRelays?: Iterable<string>) {
|
||||||
const readRelays = useReadRelays(additionalRelays);
|
const readRelays = useReadRelays(additionalRelays);
|
||||||
|
@ -5,7 +5,7 @@ import { Box, BoxProps } from "@chakra-ui/react";
|
|||||||
|
|
||||||
import useUserMetadata from "../../../hooks/use-user-metadata";
|
import useUserMetadata from "../../../hooks/use-user-metadata";
|
||||||
|
|
||||||
import { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer } from "nostr-tools/nip19";
|
||||||
import useDVMMetadata from "../../../hooks/use-dvm-metadata";
|
import useDVMMetadata from "../../../hooks/use-dvm-metadata";
|
||||||
|
|
||||||
type DVMAvatarProps = {
|
type DVMAvatarProps = {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Card, CardProps, Heading, LinkBox, LinkOverlayProps, Text } from "@chakra-ui/react";
|
import { Card, CardProps, Heading, LinkBox, LinkOverlayProps, Text } from "@chakra-ui/react";
|
||||||
import { Link as RouterLink, To } from "react-router-dom";
|
import { Link as RouterLink, To } from "react-router-dom";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer } from "nostr-tools/nip19";
|
||||||
|
|
||||||
import { NostrEvent } from "../../../types/nostr-event";
|
import { NostrEvent } from "../../../types/nostr-event";
|
||||||
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
||||||
|
@ -4,7 +4,7 @@ import { nip19 } from "nostr-tools";
|
|||||||
|
|
||||||
import useUserMetadata from "../../../hooks/use-user-metadata";
|
import useUserMetadata from "../../../hooks/use-user-metadata";
|
||||||
import { getDisplayName } from "../../../helpers/nostr/user-metadata";
|
import { getDisplayName } from "../../../helpers/nostr/user-metadata";
|
||||||
import { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer } from "nostr-tools/nip19";
|
||||||
import useDVMMetadata from "../../../hooks/use-dvm-metadata";
|
import useDVMMetadata from "../../../hooks/use-dvm-metadata";
|
||||||
|
|
||||||
export function DVMName({
|
export function DVMName({
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer } from "nostr-tools/nip19";
|
||||||
import useDVMMetadata from "../../../hooks/use-dvm-metadata";
|
import useDVMMetadata from "../../../hooks/use-dvm-metadata";
|
||||||
import { Select } from "@chakra-ui/react";
|
import { Select } from "@chakra-ui/react";
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ import { DraftNostrEvent } from "../../../types/nostr-event";
|
|||||||
import { useReadRelays } from "../../../hooks/use-client-relays";
|
import { useReadRelays } from "../../../hooks/use-client-relays";
|
||||||
import { DVMAvatarLink } from "./dvm-avatar";
|
import { DVMAvatarLink } from "./dvm-avatar";
|
||||||
import DVMLink from "./dvm-name";
|
import DVMLink from "./dvm-name";
|
||||||
import { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer } from "nostr-tools/nip19";
|
||||||
import useUserMailboxes from "../../../hooks/use-user-mailboxes";
|
import useUserMailboxes from "../../../hooks/use-user-mailboxes";
|
||||||
import { usePublishEvent } from "../../../providers/global/publish-provider";
|
import { usePublishEvent } from "../../../providers/global/publish-provider";
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ChainedDVMJob, getEventIdsFromJobs } from "../../../helpers/nostr/dvm";
|
import { ChainedDVMJob, getEventIdsFromJobs } from "../../../helpers/nostr/dvm";
|
||||||
import FeedStatus from "./feed-status";
|
import FeedStatus from "./feed-status";
|
||||||
import { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer } from "nostr-tools/nip19";
|
||||||
import useSingleEvents from "../../../hooks/use-single-events";
|
import useSingleEvents from "../../../hooks/use-single-events";
|
||||||
import TimelineItem from "../../../components/timeline-page/generic-note-timeline/timeline-item";
|
import TimelineItem from "../../../components/timeline-page/generic-note-timeline/timeline-item";
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ import RequireCurrentAccount from "../../providers/route/require-current-account
|
|||||||
import { CodeIcon } from "../../components/icons";
|
import { CodeIcon } from "../../components/icons";
|
||||||
import DebugChains from "./components/debug-chains";
|
import DebugChains from "./components/debug-chains";
|
||||||
import Feed from "./components/feed";
|
import Feed from "./components/feed";
|
||||||
import { AddressPointer } from "nostr-tools/lib/types/nip19";
|
import { AddressPointer } from "nostr-tools/nip19";
|
||||||
import useParamsAddressPointer from "../../hooks/use-params-address-pointer";
|
import useParamsAddressPointer from "../../hooks/use-params-address-pointer";
|
||||||
import DVMParams from "./components/dvm-params";
|
import DVMParams from "./components/dvm-params";
|
||||||
import useUserMailboxes from "../../hooks/use-user-mailboxes";
|
import useUserMailboxes from "../../hooks/use-user-mailboxes";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { kinds, nip19 } from "nostr-tools";
|
import { kinds, nip19 } from "nostr-tools";
|
||||||
import type { DecodeResult } from "nostr-tools/lib/types/nip19";
|
import type { DecodeResult } from "nostr-tools/nip19";
|
||||||
import { Box, Button, Flex, Heading, SimpleGrid, Spacer, Spinner, Text } from "@chakra-ui/react";
|
import { Box, Button, Flex, Heading, SimpleGrid, Spacer, Spinner, Text } from "@chakra-ui/react";
|
||||||
|
|
||||||
import UserLink from "../../../components/user/user-link";
|
import UserLink from "../../../components/user/user-link";
|
||||||
|
@ -12,6 +12,7 @@ import { useUserDNSIdentity } from "../../hooks/use-user-dns-identity";
|
|||||||
import useUserContactRelays from "../../hooks/use-user-contact-relays";
|
import useUserContactRelays from "../../hooks/use-user-contact-relays";
|
||||||
import UserSquare from "../../components/icons/user-square";
|
import UserSquare from "../../components/icons/user-square";
|
||||||
import Image01 from "../../components/icons/image-01";
|
import Image01 from "../../components/icons/image-01";
|
||||||
|
import Server05 from "../../components/icons/server-05";
|
||||||
|
|
||||||
export default function RelaysView() {
|
export default function RelaysView() {
|
||||||
const account = useCurrentAccount();
|
const account = useCurrentAccount();
|
||||||
@ -69,6 +70,15 @@ export default function RelaysView() {
|
|||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{/* <Button
|
||||||
|
variant="outline"
|
||||||
|
as={RouterLink}
|
||||||
|
to="/relays/webrtc"
|
||||||
|
leftIcon={<Server05 boxSize={6} />}
|
||||||
|
colorScheme={location.pathname.startsWith("/relays/webrtc") ? "primary" : undefined}
|
||||||
|
>
|
||||||
|
WebRTC Relays
|
||||||
|
</Button> */}
|
||||||
{nip05?.exists && (
|
{nip05?.exists && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
356
src/views/relays/webrtc/connect.tsx
Normal file
356
src/views/relays/webrtc/connect.tsx
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
import { Debugger } from "debug";
|
||||||
|
import EventEmitter from "eventemitter3";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import {
|
||||||
|
EventTemplate,
|
||||||
|
Filter,
|
||||||
|
NostrEvent,
|
||||||
|
SimplePool,
|
||||||
|
finalizeEvent,
|
||||||
|
generateSecretKey,
|
||||||
|
getPublicKey,
|
||||||
|
nip44,
|
||||||
|
} from "nostr-tools";
|
||||||
|
import { SubCloser, SubscribeManyParams } from "nostr-tools/abstract-pool";
|
||||||
|
|
||||||
|
import { logger } from "../../../helpers/debug";
|
||||||
|
|
||||||
|
const RTCDescriptionEventKind = 25050;
|
||||||
|
const RTCICEEventKind = 25051;
|
||||||
|
type Signer = {
|
||||||
|
getPublicKey: () => Promise<string> | string;
|
||||||
|
signEvent: (event: EventTemplate) => Promise<NostrEvent> | NostrEvent;
|
||||||
|
nip44: {
|
||||||
|
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
||||||
|
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type Pool = {
|
||||||
|
subscribeMany(relays: string[], filters: Filter[], params: SubscribeManyParams): SubCloser;
|
||||||
|
publish(relays: string[], event: NostrEvent): Promise<string>[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type EventMap = {
|
||||||
|
connect: [];
|
||||||
|
disconnect: [];
|
||||||
|
incomingCall: [NostrEvent];
|
||||||
|
message: [string];
|
||||||
|
};
|
||||||
|
|
||||||
|
class SimpleSigner {
|
||||||
|
key: Uint8Array;
|
||||||
|
constructor() {
|
||||||
|
this.key = generateSecretKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPublicKey() {
|
||||||
|
return getPublicKey(this.key);
|
||||||
|
}
|
||||||
|
async signEvent(event: EventTemplate) {
|
||||||
|
return finalizeEvent(event, this.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
nip44 = {
|
||||||
|
encrypt: async (pubkey: string, plaintext: string) =>
|
||||||
|
nip44.v2.encrypt(plaintext, nip44.v2.utils.getConversationKey(this.key, pubkey)),
|
||||||
|
decrypt: async (pubkey: string, ciphertext: string) =>
|
||||||
|
nip44.v2.decrypt(ciphertext, nip44.v2.utils.getConversationKey(this.key, pubkey)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultPool = new SimplePool();
|
||||||
|
|
||||||
|
class WebRTCPeer extends EventEmitter<EventMap> {
|
||||||
|
log: Debugger;
|
||||||
|
signer: Signer;
|
||||||
|
pool: Pool;
|
||||||
|
peer?: string;
|
||||||
|
relays: string[] = [];
|
||||||
|
iceServers: RTCIceServer[] = [];
|
||||||
|
|
||||||
|
connection?: RTCPeerConnection;
|
||||||
|
channel?: RTCDataChannel;
|
||||||
|
|
||||||
|
listening = false;
|
||||||
|
subscription?: SubCloser;
|
||||||
|
|
||||||
|
async isCaller() {
|
||||||
|
if (!this.offerEvent) return null;
|
||||||
|
return (await this.signer.getPublicKey()) === this.offerEvent?.pubkey;
|
||||||
|
}
|
||||||
|
get offer() {
|
||||||
|
return this.connection?.localDescription;
|
||||||
|
}
|
||||||
|
offerEvent?: NostrEvent;
|
||||||
|
get answer() {
|
||||||
|
return this.connection?.remoteDescription;
|
||||||
|
}
|
||||||
|
answerEvent?: NostrEvent;
|
||||||
|
|
||||||
|
private candidateQueue: RTCIceCandidateInit[] = [];
|
||||||
|
|
||||||
|
constructor(signer: Signer, pool: Pool = defaultPool, relays?: string[], iceServers?: RTCIceServer[]) {
|
||||||
|
super();
|
||||||
|
this.log = logger.extend(`webrtc`);
|
||||||
|
this.signer = signer;
|
||||||
|
this.pool = pool;
|
||||||
|
|
||||||
|
if (iceServers) this.iceServers = iceServers;
|
||||||
|
if (relays) this.relays = relays;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createConnection() {
|
||||||
|
if (this.connection) return this.connection;
|
||||||
|
|
||||||
|
this.connection = new RTCPeerConnection({ iceServers: this.iceServers });
|
||||||
|
this.log("Created local connection");
|
||||||
|
|
||||||
|
this.connection.onicecandidate = async ({ candidate }) => {
|
||||||
|
if (candidate) {
|
||||||
|
this.candidateQueue.push(candidate.toJSON());
|
||||||
|
} else this.flushCandidateQueue();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.connection.onicegatheringstatechange = this.flushCandidateQueue.bind(this);
|
||||||
|
|
||||||
|
this.connection.ondatachannel = ({ channel }) => {
|
||||||
|
this.log("Got data channel", channel);
|
||||||
|
|
||||||
|
if (channel.label !== "nostr") return;
|
||||||
|
|
||||||
|
this.channel = channel;
|
||||||
|
this.channel.onclose = this.onChannelStateChange.bind(this);
|
||||||
|
this.channel.onopen = this.onChannelStateChange.bind(this);
|
||||||
|
this.channel.onmessage = this.handleChannelMessage.bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async flushCandidateQueue() {
|
||||||
|
if (this.connection?.iceGatheringState !== "complete") return;
|
||||||
|
|
||||||
|
if (this.offerEvent && this.answerEvent && this.peer && this.candidateQueue.length > 0) {
|
||||||
|
const cipherText = await this.signer.nip44.encrypt(this.peer, JSON.stringify(this.candidateQueue));
|
||||||
|
const iceEvent = await this.signer.signEvent({
|
||||||
|
kind: RTCICEEventKind,
|
||||||
|
content: cipherText,
|
||||||
|
tags: [["e", this.offerEvent.id]],
|
||||||
|
created_at: dayjs().unix(),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.log(`Publishing ICE candidates`, this.candidateQueue);
|
||||||
|
await this.pool.publish(this.relays, iceEvent);
|
||||||
|
this.candidateQueue = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async makeCall(peer: string) {
|
||||||
|
if (this.peer) throw new Error("Already calling peer");
|
||||||
|
|
||||||
|
this.stopListening();
|
||||||
|
const pc = this.createConnection();
|
||||||
|
|
||||||
|
this.channel = pc.createDataChannel("nostr", { ordered: true });
|
||||||
|
this.channel.onopen = this.onChannelStateChange.bind(this);
|
||||||
|
this.channel.onclose = this.onChannelStateChange.bind(this);
|
||||||
|
this.channel.onmessage = this.handleChannelMessage.bind(this);
|
||||||
|
|
||||||
|
this.log(`Making call to ${peer} `);
|
||||||
|
|
||||||
|
const offer = await pc.createOffer();
|
||||||
|
const cipherText = await this.signer.nip44.encrypt(peer, JSON.stringify(offer));
|
||||||
|
const offerEvent = await this.signer.signEvent({
|
||||||
|
kind: RTCDescriptionEventKind,
|
||||||
|
content: cipherText,
|
||||||
|
tags: [["p", peer], ...this.relays.map((r) => ["relay", r])],
|
||||||
|
created_at: dayjs().unix(),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.log("Created offer", offer);
|
||||||
|
|
||||||
|
// listen for answers and ice events
|
||||||
|
this.subscription = this.pool.subscribeMany(
|
||||||
|
this.relays,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
kinds: [RTCDescriptionEventKind, RTCICEEventKind],
|
||||||
|
"#e": [offerEvent.id],
|
||||||
|
authors: [peer],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
onevent: async (event: NostrEvent) => {
|
||||||
|
if (!this.offerEvent) return;
|
||||||
|
if (!event.tags.some((t) => t[0] === "e" && t[1] === this.offerEvent?.id)) return;
|
||||||
|
|
||||||
|
console.log(event);
|
||||||
|
|
||||||
|
switch (event.kind) {
|
||||||
|
case RTCDescriptionEventKind:
|
||||||
|
await this.handleAnswer(event);
|
||||||
|
// got answer, send ICE candidates
|
||||||
|
await this.flushCandidateQueue();
|
||||||
|
break;
|
||||||
|
case RTCICEEventKind:
|
||||||
|
await this.handleICEEvent(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onclose: () => {
|
||||||
|
this.log("Subscription Closed");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
this.peer = peer;
|
||||||
|
|
||||||
|
this.log("Publishing event", offerEvent);
|
||||||
|
await this.pool.publish(this.relays, offerEvent);
|
||||||
|
await pc.setLocalDescription(offer);
|
||||||
|
|
||||||
|
this.offerEvent = offerEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleAnswer(event: NostrEvent) {
|
||||||
|
const pc = this.createConnection();
|
||||||
|
|
||||||
|
if (!pc.localDescription) throw new Error("Got answer without offering");
|
||||||
|
|
||||||
|
const plaintext = await this.signer.nip44.decrypt(event.pubkey, event.content);
|
||||||
|
const answer = JSON.parse(plaintext) as RTCSessionDescriptionInit;
|
||||||
|
if (answer.type !== "answer") throw new Error("Unexpected rtc description type");
|
||||||
|
|
||||||
|
this.log("Got answer", answer);
|
||||||
|
|
||||||
|
await pc.setRemoteDescription(answer);
|
||||||
|
|
||||||
|
this.answerEvent = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
async answerCall(event: NostrEvent) {
|
||||||
|
this.stopListening();
|
||||||
|
const pc = this.createConnection();
|
||||||
|
|
||||||
|
this.log(`Answering call ${event.id} from ${event.pubkey}`);
|
||||||
|
|
||||||
|
const plaintext = await this.signer.nip44.decrypt(event.pubkey, event.content);
|
||||||
|
const offer = JSON.parse(plaintext) as RTCSessionDescriptionInit;
|
||||||
|
if (offer.type !== "offer") throw new Error("Unexpected rtc description type");
|
||||||
|
|
||||||
|
this.relays = event.tags.filter((t) => t[0] === "relay" && t[1]).map((t) => t[1]);
|
||||||
|
await pc.setRemoteDescription(offer);
|
||||||
|
|
||||||
|
const answer = await pc.createAnswer();
|
||||||
|
const cipherText = await this.signer.nip44.encrypt(event.pubkey, JSON.stringify(answer));
|
||||||
|
const answerEvent = await this.signer.signEvent({
|
||||||
|
kind: RTCDescriptionEventKind,
|
||||||
|
content: cipherText,
|
||||||
|
tags: [
|
||||||
|
["p", event.pubkey],
|
||||||
|
["e", event.id],
|
||||||
|
],
|
||||||
|
created_at: dayjs().unix(),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.log("Created answer", answer);
|
||||||
|
|
||||||
|
this.peer = event.pubkey;
|
||||||
|
this.offerEvent = event;
|
||||||
|
|
||||||
|
// listen for ice events
|
||||||
|
this.subscription = this.pool.subscribeMany(
|
||||||
|
this.relays,
|
||||||
|
[{ kinds: [RTCICEEventKind], "#e": [event.id], authors: [event.pubkey] }],
|
||||||
|
{
|
||||||
|
onevent: async (event) => {
|
||||||
|
if (!this.offerEvent) return;
|
||||||
|
if (!event.tags.some((t) => t[0] === "e" && t[1] === this.offerEvent?.id)) return;
|
||||||
|
|
||||||
|
switch (event.kind) {
|
||||||
|
case RTCICEEventKind:
|
||||||
|
await this.handleICEEvent(event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onclose: () => {
|
||||||
|
this.log("Subscription Closed");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
this.log("Publishing event", answerEvent);
|
||||||
|
|
||||||
|
await this.pool.publish(this.relays, answerEvent);
|
||||||
|
await pc.setLocalDescription(answer);
|
||||||
|
this.answerEvent = answerEvent;
|
||||||
|
|
||||||
|
// answered call, send ICE candidates
|
||||||
|
await this.flushCandidateQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleICEEvent(event: NostrEvent) {
|
||||||
|
if (!this.connection) throw new Error("Got ICE event without connection");
|
||||||
|
const pc = this.createConnection();
|
||||||
|
|
||||||
|
const plaintext = await this.signer.nip44.decrypt(event.pubkey, event.content);
|
||||||
|
const candidates = JSON.parse(plaintext) as RTCIceCandidateInit[];
|
||||||
|
|
||||||
|
this.log("Got candidates", candidates);
|
||||||
|
|
||||||
|
for (let candidate of candidates) {
|
||||||
|
await pc.addIceCandidate(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async listenForCall() {
|
||||||
|
if (this.listening) throw new Error("Already listening");
|
||||||
|
|
||||||
|
this.listening = true;
|
||||||
|
this.subscription = this.pool.subscribeMany(
|
||||||
|
this.relays,
|
||||||
|
[{ kinds: [RTCDescriptionEventKind], "#p": [await this.signer.getPublicKey()], since: dayjs().unix() }],
|
||||||
|
{
|
||||||
|
onevent: (event) => {
|
||||||
|
this.emit("incomingCall", event);
|
||||||
|
},
|
||||||
|
onclose: () => {
|
||||||
|
this.listening = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopListening() {
|
||||||
|
if (!this.listening) return;
|
||||||
|
|
||||||
|
if (this.subscription) this.subscription.close();
|
||||||
|
this.subscription = undefined;
|
||||||
|
this.listening = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onChannelStateChange() {
|
||||||
|
const readyState = this.channel?.readyState;
|
||||||
|
console.log("Send channel state is: " + readyState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleChannelMessage(event: MessageEvent<any>) {
|
||||||
|
if (typeof event.data === "string") this.emit("message", event.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
send(message: string) {
|
||||||
|
this.channel?.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
this.log("Closing data channel");
|
||||||
|
if (this.channel) this.channel.close();
|
||||||
|
if (this.connection) this.connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
window.SimpleSigner = SimpleSigner;
|
||||||
|
// @ts-expect-error
|
||||||
|
window.WebRTCPeer = WebRTCPeer;
|
30
src/views/relays/webrtc/index.tsx
Normal file
30
src/views/relays/webrtc/index.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Button, ButtonGroup, Code, Flex, Heading, Link, Text } from "@chakra-ui/react";
|
||||||
|
import BackButton from "../../../components/router/back-button";
|
||||||
|
import useCurrentAccount from "../../../hooks/use-current-account";
|
||||||
|
import { useUserDNSIdentity } from "../../../hooks/use-user-dns-identity";
|
||||||
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
|
|
||||||
|
import { RelayFavicon } from "../../../components/relay-favicon";
|
||||||
|
import { QrCodeIcon } from "../../../components/icons";
|
||||||
|
|
||||||
|
import "./connect";
|
||||||
|
|
||||||
|
export default function WebRtcRelaysView() {
|
||||||
|
return (
|
||||||
|
<Flex gap="2" direction="column" overflow="auto hidden" flex={1} px={{ base: "2", lg: 0 }}>
|
||||||
|
<Flex gap="2" alignItems="center" wrap="wrap">
|
||||||
|
<BackButton hideFrom="lg" size="sm" />
|
||||||
|
<Heading size="lg">WebRTC Relays</Heading>
|
||||||
|
|
||||||
|
<ButtonGroup size="sm" ml="auto">
|
||||||
|
<Button leftIcon={<QrCodeIcon />}>Pair</Button>
|
||||||
|
<Button colorScheme="primary">Connect</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{/* <Text fontStyle="italic" mt="-2">
|
||||||
|
These relays cant be modified by noStrudel, they must be set manually on your
|
||||||
|
</Text> */}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
@ -18,8 +18,8 @@ import {
|
|||||||
useInterval,
|
useInterval,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
import { AbstractRelay } from "nostr-tools";
|
|
||||||
import { useLocalStorage } from "react-use";
|
import { useLocalStorage } from "react-use";
|
||||||
|
import { AbstractRelay } from "nostr-tools/abstract-relay";
|
||||||
|
|
||||||
import relayPoolService from "../../../services/relay-pool";
|
import relayPoolService from "../../../services/relay-pool";
|
||||||
import { RelayFavicon } from "../../../components/relay-favicon";
|
import { RelayFavicon } from "../../../components/relay-favicon";
|
||||||
|
@ -15,7 +15,8 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { AbstractRelay, NostrEvent, Subscription } from "nostr-tools";
|
import { NostrEvent } from "nostr-tools";
|
||||||
|
import { AbstractRelay, Subscription } from "nostr-tools/abstract-relay";
|
||||||
import { useLocalStorage } from "react-use";
|
import { useLocalStorage } from "react-use";
|
||||||
import { Subscription as IDBSubscription } from "nostr-idb";
|
import { Subscription as IDBSubscription } from "nostr-idb";
|
||||||
import _throttle from "lodash.throttle";
|
import _throttle from "lodash.throttle";
|
||||||
@ -93,7 +94,7 @@ export default function EventConsoleView() {
|
|||||||
if (!relay || relay.url !== url.toString()) {
|
if (!relay || relay.url !== url.toString()) {
|
||||||
r = await relayPoolService.requestRelay(url);
|
r = await relayPoolService.requestRelay(url);
|
||||||
await relayPoolService.requestConnect(r);
|
await relayPoolService.requestConnect(r);
|
||||||
setRelay(r);
|
setRelay(r as AbstractRelay);
|
||||||
} else r = relay;
|
} else r = relay;
|
||||||
} else {
|
} else {
|
||||||
if (relay) setRelay(null);
|
if (relay) setRelay(null);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "Bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
36
yarn.lock
36
yarn.lock
@ -6218,20 +6218,6 @@ nostr-idb@^2.1.4:
|
|||||||
idb "^8.0.0"
|
idb "^8.0.0"
|
||||||
nostr-tools "^2.1.3"
|
nostr-tools "^2.1.3"
|
||||||
|
|
||||||
nostr-tools@2.5.2:
|
|
||||||
version "2.5.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.5.2.tgz#54b445380ac2a7740ad90ed3b044bca93ecf23bd"
|
|
||||||
integrity sha512-Ls2FKh694eudBye6q89yJ5JhXjQle1MWp1yD2sBZ5j9M3IOBEW8ia9IED5W6daSAjlT/Z/pV77yTkdF45c1Rbg==
|
|
||||||
dependencies:
|
|
||||||
"@noble/ciphers" "^0.5.1"
|
|
||||||
"@noble/curves" "1.2.0"
|
|
||||||
"@noble/hashes" "1.3.1"
|
|
||||||
"@scure/base" "1.1.1"
|
|
||||||
"@scure/bip32" "1.3.1"
|
|
||||||
"@scure/bip39" "1.2.1"
|
|
||||||
optionalDependencies:
|
|
||||||
nostr-wasm v0.1.0
|
|
||||||
|
|
||||||
nostr-tools@^1.17.0:
|
nostr-tools@^1.17.0:
|
||||||
version "1.17.0"
|
version "1.17.0"
|
||||||
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.17.0.tgz#b6f62e32fedfd9e68ec0a7ce57f74c44fc768e8c"
|
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.17.0.tgz#b6f62e32fedfd9e68ec0a7ce57f74c44fc768e8c"
|
||||||
@ -6272,6 +6258,20 @@ nostr-tools@^2.3.2:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
nostr-wasm v0.1.0
|
nostr-wasm v0.1.0
|
||||||
|
|
||||||
|
nostr-tools@^2.7.1:
|
||||||
|
version "2.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.7.1.tgz#cfedfe6c7ebf7f127f3ac32a5b57c7e570c35f67"
|
||||||
|
integrity sha512-4qAvlHSqBAA8lQMwRWE6dalSNdQT77Xut9lPiJZgEcb9RAlR69wR2+KVBAgnZVaabVYH7FJ7gOQXLw/jQBAYBg==
|
||||||
|
dependencies:
|
||||||
|
"@noble/ciphers" "^0.5.1"
|
||||||
|
"@noble/curves" "1.2.0"
|
||||||
|
"@noble/hashes" "1.3.1"
|
||||||
|
"@scure/base" "1.1.1"
|
||||||
|
"@scure/bip32" "1.3.1"
|
||||||
|
"@scure/bip39" "1.2.1"
|
||||||
|
optionalDependencies:
|
||||||
|
nostr-wasm v0.1.0
|
||||||
|
|
||||||
nostr-wasm@^0.1.0, nostr-wasm@v0.1.0:
|
nostr-wasm@^0.1.0, nostr-wasm@v0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/nostr-wasm/-/nostr-wasm-0.1.0.tgz#17af486745feb2b7dd29503fdd81613a24058d94"
|
resolved "https://registry.yarnpkg.com/nostr-wasm/-/nostr-wasm-0.1.0.tgz#17af486745feb2b7dd29503fdd81613a24058d94"
|
||||||
@ -7773,10 +7773,10 @@ typed-array-length@^1.0.4:
|
|||||||
for-each "^0.3.3"
|
for-each "^0.3.3"
|
||||||
is-typed-array "^1.1.9"
|
is-typed-array "^1.1.9"
|
||||||
|
|
||||||
typescript@^5.3.3:
|
typescript@^5.5.3:
|
||||||
version "5.3.3"
|
version "5.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa"
|
||||||
integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
|
integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==
|
||||||
|
|
||||||
typo-js@*:
|
typo-js@*:
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user