move @snort/nostr code into System
This commit is contained in:
parent
baec8d6904
commit
f86053c14f
@ -12,7 +12,6 @@
|
|||||||
"@reduxjs/toolkit": "^1.9.1",
|
"@reduxjs/toolkit": "^1.9.1",
|
||||||
"@scure/bip32": "^1.3.0",
|
"@scure/bip32": "^1.3.0",
|
||||||
"@scure/bip39": "^1.1.1",
|
"@scure/bip39": "^1.1.1",
|
||||||
"@snort/nostr": "^1.0.0",
|
|
||||||
"@szhsin/react-menu": "^3.3.1",
|
"@szhsin/react-menu": "^3.3.1",
|
||||||
"@void-cat/api": "^1.0.4",
|
"@void-cat/api": "^1.0.4",
|
||||||
"base32-decode": "^1.0.0",
|
"base32-decode": "^1.0.0",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { RawEvent } from "@snort/nostr";
|
import { RawEvent } from "System";
|
||||||
import { db } from "Db";
|
import { db } from "Db";
|
||||||
import FeedCache from "./FeedCache";
|
import FeedCache from "./FeedCache";
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HexKey, RawEvent, UserMetadata } from "@snort/nostr";
|
import { HexKey, RawEvent, UserMetadata } from "System";
|
||||||
import { hexToBech32, unixNowMs } from "SnortUtils";
|
import { hexToBech32, unixNowMs } from "SnortUtils";
|
||||||
import { DmCache } from "./DMCache";
|
import { DmCache } from "./DMCache";
|
||||||
import { InteractionCache } from "./EventInteractionCache";
|
import { InteractionCache } from "./EventInteractionCache";
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { RelaySettings } from "@snort/nostr";
|
import { UserRelays } from "Cache/UserRelayCache";
|
||||||
|
import { NostrSystem, RelaySettings } from "System";
|
||||||
|
import { ProfileLoaderService } from "System/ProfileCache";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add-on api for snort features
|
* Add-on api for snort features
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Dexie, { Table } from "dexie";
|
import Dexie, { Table } from "dexie";
|
||||||
import { FullRelaySettings, HexKey, RawEvent, u256 } from "@snort/nostr";
|
import { FullRelaySettings, HexKey, RawEvent, u256 } from "System";
|
||||||
import { MetadataCache } from "Cache";
|
import { MetadataCache } from "Cache";
|
||||||
|
|
||||||
export const NAME = "snortDB";
|
export const NAME = "snortDB";
|
||||||
|
@ -2,7 +2,7 @@ import "./Avatar.css";
|
|||||||
import Nostrich from "nostrich.webp";
|
import Nostrich from "nostrich.webp";
|
||||||
|
|
||||||
import { CSSProperties, useEffect, useState } from "react";
|
import { CSSProperties, useEffect, useState } from "react";
|
||||||
import type { UserMetadata } from "@snort/nostr";
|
import type { UserMetadata } from "System";
|
||||||
|
|
||||||
import useImgProxy from "Hooks/useImgProxy";
|
import useImgProxy from "Hooks/useImgProxy";
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import "./BadgeList.css";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import { TaggedRawEvent } from "@snort/nostr";
|
import { TaggedRawEvent } from "System";
|
||||||
|
|
||||||
import { ProxyImg } from "Element/ProxyImg";
|
import { ProxyImg } from "Element/ProxyImg";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
|
|
||||||
import messages from "./messages";
|
import messages from "./messages";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useState, useMemo, ChangeEvent } from "react";
|
import { useState, useMemo, ChangeEvent } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey, TaggedRawEvent } from "@snort/nostr";
|
import { HexKey, TaggedRawEvent } from "System";
|
||||||
|
|
||||||
import Note from "Element/Note";
|
import Note from "Element/Note";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
|
@ -2,7 +2,7 @@ import "./DM.css";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
import { TaggedRawEvent } from "@snort/nostr";
|
import { TaggedRawEvent } from "System";
|
||||||
|
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import NoteTime from "Element/NoteTime";
|
import NoteTime from "Element/NoteTime";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "./DmWindow.css";
|
import "./DmWindow.css";
|
||||||
import { useEffect, useMemo, useRef } from "react";
|
import { useEffect, useMemo, useRef } from "react";
|
||||||
import { TaggedRawEvent } from "@snort/nostr";
|
import { TaggedRawEvent } from "System";
|
||||||
|
|
||||||
import ProfileImage from "Element/ProfileImage";
|
import ProfileImage from "Element/ProfileImage";
|
||||||
import DM from "Element/DM";
|
import DM from "Element/DM";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "./FollowButton.css";
|
import "./FollowButton.css";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
|
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import { parseId } from "SnortUtils";
|
import { parseId } from "SnortUtils";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
|
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import ProfilePreview from "Element/ProfilePreview";
|
import ProfilePreview from "Element/ProfilePreview";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
|
|
||||||
import { useUserProfile } from "Hooks/useUserProfile";
|
import { useUserProfile } from "Hooks/useUserProfile";
|
||||||
import { profileLink } from "SnortUtils";
|
import { profileLink } from "SnortUtils";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
|
|
||||||
import messages from "./messages";
|
import messages from "./messages";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
import MuteButton from "Element/MuteButton";
|
import MuteButton from "Element/MuteButton";
|
||||||
import ProfilePreview from "Element/ProfilePreview";
|
import ProfilePreview from "Element/ProfilePreview";
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import "./Nip05.css";
|
import "./Nip05.css";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
|
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import { useUserProfile } from "Hooks/useUserProfile";
|
import { useUserProfile } from "Hooks/useUserProfile";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useMemo, useState, ChangeEvent } from "react";
|
import { useEffect, useMemo, useState, ChangeEvent } from "react";
|
||||||
import { useIntl, FormattedMessage } from "react-intl";
|
import { useIntl, FormattedMessage } from "react-intl";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { UserMetadata } from "@snort/nostr";
|
import { UserMetadata } from "System";
|
||||||
|
|
||||||
import { unwrap } from "SnortUtils";
|
import { unwrap } from "SnortUtils";
|
||||||
import { formatShort } from "Number";
|
import { formatShort } from "Number";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { RawEvent } from "@snort/nostr";
|
import { RawEvent } from "System";
|
||||||
|
|
||||||
import { findTag, NostrLink } from "SnortUtils";
|
import { findTag, NostrLink } from "SnortUtils";
|
||||||
import useEventFeed from "Feed/EventFeed";
|
import useEventFeed from "Feed/EventFeed";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { NostrPrefix } from "@snort/nostr";
|
import { NostrPrefix } from "System";
|
||||||
|
|
||||||
import Mention from "Element/Mention";
|
import Mention from "Element/Mention";
|
||||||
import { parseNostrLink } from "SnortUtils";
|
import { parseNostrLink } from "SnortUtils";
|
||||||
|
@ -3,7 +3,7 @@ import React, { useMemo, useState, useLayoutEffect, ReactNode } from "react";
|
|||||||
import { useNavigate, Link } from "react-router-dom";
|
import { useNavigate, Link } from "react-router-dom";
|
||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
import { useIntl, FormattedMessage } from "react-intl";
|
import { useIntl, FormattedMessage } from "react-intl";
|
||||||
import { TaggedRawEvent, HexKey, EventKind, NostrPrefix, Lists } from "@snort/nostr";
|
import { TaggedRawEvent, HexKey, EventKind, NostrPrefix, Lists } from "System";
|
||||||
|
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./NoteCreator.css";
|
import "./NoteCreator.css";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { encodeTLV, EventKind, NostrPrefix, TaggedRawEvent } from "@snort/nostr";
|
import { encodeTLV, EventKind, NostrPrefix, TaggedRawEvent } from "System";
|
||||||
|
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
|
@ -3,7 +3,7 @@ import { useSelector, useDispatch } from "react-redux";
|
|||||||
import { useIntl, FormattedMessage } from "react-intl";
|
import { useIntl, FormattedMessage } from "react-intl";
|
||||||
import { Menu, MenuItem } from "@szhsin/react-menu";
|
import { Menu, MenuItem } from "@szhsin/react-menu";
|
||||||
import { useLongPress } from "use-long-press";
|
import { useLongPress } from "use-long-press";
|
||||||
import { TaggedRawEvent, HexKey, u256, encodeTLV, NostrPrefix, Lists } from "@snort/nostr";
|
import { TaggedRawEvent, HexKey, u256, encodeTLV, NostrPrefix, Lists } from "System";
|
||||||
|
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import Spinner from "Icons/Spinner";
|
import Spinner from "Icons/Spinner";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./NoteReaction.css";
|
import "./NoteReaction.css";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { EventKind, RawEvent, TaggedRawEvent, NostrPrefix } from "@snort/nostr";
|
import { EventKind, RawEvent, TaggedRawEvent, NostrPrefix } from "System";
|
||||||
|
|
||||||
import Note from "Element/Note";
|
import Note from "Element/Note";
|
||||||
import ProfileImage from "Element/ProfileImage";
|
import ProfileImage from "Element/ProfileImage";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { TaggedRawEvent } from "@snort/nostr";
|
import { TaggedRawEvent } from "System";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { FormattedMessage, FormattedNumber, useIntl } from "react-intl";
|
import { FormattedMessage, FormattedNumber, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./ProfileImage.css";
|
import "./ProfileImage.css";
|
||||||
|
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { HexKey, NostrPrefix } from "@snort/nostr";
|
import { HexKey, NostrPrefix } from "System";
|
||||||
|
|
||||||
import { useUserProfile } from "Hooks/useUserProfile";
|
import { useUserProfile } from "Hooks/useUserProfile";
|
||||||
import { hexToBech32, profileLink } from "SnortUtils";
|
import { hexToBech32, profileLink } from "SnortUtils";
|
||||||
|
@ -4,7 +4,7 @@ import { ReactNode } from "react";
|
|||||||
import ProfileImage from "Element/ProfileImage";
|
import ProfileImage from "Element/ProfileImage";
|
||||||
import FollowButton from "Element/FollowButton";
|
import FollowButton from "Element/FollowButton";
|
||||||
import { useUserProfile } from "Hooks/useUserProfile";
|
import { useUserProfile } from "Hooks/useUserProfile";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
|
|
||||||
export interface ProfilePreviewProps {
|
export interface ProfilePreviewProps {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { RawEvent } from "@snort/nostr";
|
import { RawEvent } from "System";
|
||||||
import { dedupe } from "SnortUtils";
|
import { dedupe } from "SnortUtils";
|
||||||
import FollowListBase from "./FollowListBase";
|
import FollowListBase from "./FollowListBase";
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import "./Reactions.css";
|
|||||||
|
|
||||||
import { useState, useMemo, useEffect } from "react";
|
import { useState, useMemo, useEffect } from "react";
|
||||||
import { useIntl, FormattedMessage } from "react-intl";
|
import { useIntl, FormattedMessage } from "react-intl";
|
||||||
import { TaggedRawEvent } from "@snort/nostr";
|
import { TaggedRawEvent } from "System";
|
||||||
|
|
||||||
import { formatShort } from "Number";
|
import { formatShort } from "Number";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
|
@ -2,10 +2,10 @@ import "./Relay.css";
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RelaySettings } from "@snort/nostr";
|
import { RelaySettings } from "System";
|
||||||
|
|
||||||
import useRelayState from "Feed/RelayState";
|
import useRelayState from "Feed/RelayState";
|
||||||
import { System } from "System";
|
import { System } from "index";
|
||||||
import { getRelayName, unixNowMs, unwrap } from "SnortUtils";
|
import { getRelayName, unixNowMs, unwrap } from "SnortUtils";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import { setRelays } from "Login";
|
import { setRelays } from "Login";
|
||||||
|
@ -2,7 +2,7 @@ import "./RelaysMetadata.css";
|
|||||||
import Nostrich from "nostrich.webp";
|
import Nostrich from "nostrich.webp";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import { FullRelaySettings } from "@snort/nostr";
|
import { FullRelaySettings } from "System";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
|
|
||||||
const RelayFavicon = ({ url }: { url: string }) => {
|
const RelayFavicon = ({ url }: { url: string }) => {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import "./SendSats.css";
|
import "./SendSats.css";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import { useIntl, FormattedMessage } from "react-intl";
|
import { useIntl, FormattedMessage } from "react-intl";
|
||||||
import { HexKey, RawEvent } from "@snort/nostr";
|
|
||||||
|
|
||||||
|
import { HexKey, RawEvent } from "System";
|
||||||
|
import { System } from "index";
|
||||||
import { formatShort } from "Number";
|
import { formatShort } from "Number";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
@ -133,7 +134,7 @@ export default function SendSats(props: SendSatsProps) {
|
|||||||
const randomKey = generateRandomKey();
|
const randomKey = generateRandomKey();
|
||||||
console.debug("Generated new key for zap: ", randomKey);
|
console.debug("Generated new key for zap: ", randomKey);
|
||||||
|
|
||||||
const publisher = new EventPublisher(randomKey.publicKey, randomKey.privateKey);
|
const publisher = new EventPublisher(System, randomKey.publicKey, randomKey.privateKey);
|
||||||
zap = await publisher.zap(amount * 1000, author, relays, note, comment, eb => eb.tag(["anon", ""]));
|
zap = await publisher.zap(amount * 1000, author, relays, note, comment, eb => eb.tag(["anon", ""]));
|
||||||
} else {
|
} else {
|
||||||
zap = await publisher.zap(amount * 1000, author, relays, note, comment);
|
zap = await publisher.zap(amount * 1000, author, relays, note, comment);
|
||||||
|
@ -3,11 +3,11 @@ import { useState } from "react";
|
|||||||
|
|
||||||
import useRelayState from "Feed/RelayState";
|
import useRelayState from "Feed/RelayState";
|
||||||
import Tabs, { Tab } from "Element/Tabs";
|
import Tabs, { Tab } from "Element/Tabs";
|
||||||
import { System } from "System";
|
|
||||||
import { unwrap } from "SnortUtils";
|
import { unwrap } from "SnortUtils";
|
||||||
import useSystemState from "Hooks/useSystemState";
|
import useSystemState from "Hooks/useSystemState";
|
||||||
import { RawReqFilter } from "@snort/nostr";
|
import { RawReqFilter } from "System";
|
||||||
import { useCopy } from "useCopy";
|
import { useCopy } from "useCopy";
|
||||||
|
import { System } from "index";
|
||||||
|
|
||||||
function RelayInfo({ id }: { id: string }) {
|
function RelayInfo({ id }: { id: string }) {
|
||||||
const state = useRelayState(id);
|
const state = useRelayState(id);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { HexKey, NostrPrefix } from "@snort/nostr";
|
import { HexKey, NostrPrefix } from "System";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import FollowListBase from "Element/FollowListBase";
|
import FollowListBase from "Element/FollowListBase";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./Text.css";
|
import "./Text.css";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link, useLocation } from "react-router-dom";
|
||||||
import { HexKey, NostrPrefix } from "@snort/nostr";
|
import { HexKey, NostrPrefix } from "System";
|
||||||
|
|
||||||
import { MentionRegex, InvoiceRegex, HashtagRegex, CashuRegex } from "Const";
|
import { MentionRegex, InvoiceRegex, HashtagRegex, CashuRegex } from "Const";
|
||||||
import { eventLink, hexToBech32, splitByUrl, validateNostrLink } from "SnortUtils";
|
import { eventLink, hexToBech32, splitByUrl, validateNostrLink } from "SnortUtils";
|
||||||
|
@ -4,7 +4,7 @@ import "./Textarea.css";
|
|||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete";
|
import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete";
|
||||||
import TextareaAutosize from "react-textarea-autosize";
|
import TextareaAutosize from "react-textarea-autosize";
|
||||||
import { NostrPrefix } from "@snort/nostr";
|
import { NostrPrefix } from "System";
|
||||||
|
|
||||||
import Avatar from "Element/Avatar";
|
import Avatar from "Element/Avatar";
|
||||||
import Nip05 from "Element/Nip05";
|
import Nip05 from "Element/Nip05";
|
||||||
|
@ -2,7 +2,7 @@ import "./Thread.css";
|
|||||||
import { useMemo, useState, ReactNode } from "react";
|
import { useMemo, useState, ReactNode } from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import { useNavigate, useLocation, Link, useParams } from "react-router-dom";
|
import { useNavigate, useLocation, Link, useParams } from "react-router-dom";
|
||||||
import { TaggedRawEvent, u256, EventKind, NostrPrefix } from "@snort/nostr";
|
import { TaggedRawEvent, u256, EventKind, NostrPrefix } from "System";
|
||||||
import { EventExt, Thread as ThreadInfo } from "System/EventExt";
|
import { EventExt, Thread as ThreadInfo } from "System/EventExt";
|
||||||
|
|
||||||
import { eventLink, unwrap, getReactions, parseNostrLink, getAllReactions, findTag } from "SnortUtils";
|
import { eventLink, unwrap, getReactions, parseNostrLink, getAllReactions, findTag } from "SnortUtils";
|
||||||
|
@ -2,7 +2,7 @@ import "./Timeline.css";
|
|||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useInView } from "react-intersection-observer";
|
import { useInView } from "react-intersection-observer";
|
||||||
import { TaggedRawEvent, EventKind, u256 } from "@snort/nostr";
|
import { TaggedRawEvent, EventKind, u256 } from "System";
|
||||||
|
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import { dedupeByPubkey, findTag, tagFilterOfTextRepost } from "SnortUtils";
|
import { dedupeByPubkey, findTag, tagFilterOfTextRepost } from "SnortUtils";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { RawEvent, TaggedRawEvent } from "@snort/nostr";
|
import { RawEvent, TaggedRawEvent } from "System";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import PageSpinner from "Element/PageSpinner";
|
import PageSpinner from "Element/PageSpinner";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
import FollowListBase from "Element/FollowListBase";
|
import FollowListBase from "Element/FollowListBase";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { MouseEvent } from "react";
|
import { MouseEvent } from "react";
|
||||||
import { useNavigate, Link } from "react-router-dom";
|
import { useNavigate, Link } from "react-router-dom";
|
||||||
|
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
|
|
||||||
import { useUserProfile } from "Hooks/useUserProfile";
|
import { useUserProfile } from "Hooks/useUserProfile";
|
||||||
import { profileLink } from "SnortUtils";
|
import { profileLink } from "SnortUtils";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { encodeTLV, NostrPrefix, RawEvent } from "@snort/nostr";
|
import { encodeTLV, NostrPrefix, RawEvent } from "System";
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import Spinner from "Icons/Spinner";
|
import Spinner from "Icons/Spinner";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./Zap.css";
|
import "./Zap.css";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { HexKey, TaggedRawEvent } from "@snort/nostr";
|
import { HexKey, TaggedRawEvent } from "System";
|
||||||
|
|
||||||
import { decodeInvoice, InvoiceDetails, sha256, unwrap } from "SnortUtils";
|
import { decodeInvoice, InvoiceDetails, sha256, unwrap } from "SnortUtils";
|
||||||
import { formatShort } from "Number";
|
import { formatShort } from "Number";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "./ZapButton.css";
|
import "./ZapButton.css";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
|
|
||||||
import { useUserProfile } from "Hooks/useUserProfile";
|
import { useUserProfile } from "Hooks/useUserProfile";
|
||||||
import SendSats from "Element/SendSats";
|
import SendSats from "Element/SendSats";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "./ZapstrEmbed.css";
|
import "./ZapstrEmbed.css";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { encodeTLV, NostrPrefix, RawEvent } from "@snort/nostr";
|
import { encodeTLV, NostrPrefix, RawEvent } from "System";
|
||||||
|
|
||||||
import { ProxyImg } from "Element/ProxyImg";
|
import { ProxyImg } from "Element/ProxyImg";
|
||||||
import ProfileImage from "Element/ProfileImage";
|
import ProfileImage from "Element/ProfileImage";
|
||||||
|
2
packages/app/src/External/NostrBand.ts
vendored
2
packages/app/src/External/NostrBand.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
import { RawEvent } from "@snort/nostr";
|
import { RawEvent } from "System";
|
||||||
|
|
||||||
export interface TrendingUser {
|
export interface TrendingUser {
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { EventKind, HexKey, Lists } from "@snort/nostr";
|
import { EventKind, HexKey, Lists } from "System";
|
||||||
|
|
||||||
import { unwrap, findTag, chunks } from "SnortUtils";
|
import { unwrap, findTag, chunks } from "SnortUtils";
|
||||||
import { RequestBuilder } from "System";
|
import { RequestBuilder } from "System";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HexKey, Lists } from "@snort/nostr";
|
import { HexKey, Lists } from "System";
|
||||||
|
|
||||||
import useNotelistSubscription from "Hooks/useNotelistSubscription";
|
import useNotelistSubscription from "Hooks/useNotelistSubscription";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { NostrPrefix } from "@snort/nostr";
|
import { NostrPrefix } from "System";
|
||||||
|
|
||||||
import useRequestBuilder from "Hooks/useRequestBuilder";
|
import useRequestBuilder from "Hooks/useRequestBuilder";
|
||||||
import { RequestBuilder, ReplaceableNoteStore } from "System";
|
import { RequestBuilder, ReplaceableNoteStore } from "System";
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import { EventPublisher } from "System/EventPublisher";
|
import { EventPublisher } from "System/EventPublisher";
|
||||||
|
import { System } from "index";
|
||||||
|
|
||||||
export default function useEventPublisher() {
|
export default function useEventPublisher() {
|
||||||
const { publicKey, privateKey } = useLogin();
|
const { publicKey, privateKey } = useLogin();
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
if (publicKey) {
|
if (publicKey) {
|
||||||
return new EventPublisher(publicKey, privateKey);
|
return new EventPublisher(System, publicKey, privateKey);
|
||||||
}
|
}
|
||||||
}, [publicKey, privateKey]);
|
}, [publicKey, privateKey]);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { HexKey, EventKind } from "@snort/nostr";
|
import { HexKey, EventKind } from "System";
|
||||||
|
|
||||||
import { PubkeyReplaceableNoteStore, RequestBuilder } from "System";
|
import { PubkeyReplaceableNoteStore, RequestBuilder } from "System";
|
||||||
import useRequestBuilder from "Hooks/useRequestBuilder";
|
import useRequestBuilder from "Hooks/useRequestBuilder";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { HexKey, TaggedRawEvent, EventKind } from "@snort/nostr";
|
import { HexKey, TaggedRawEvent, EventKind } from "System";
|
||||||
|
|
||||||
import { PubkeyReplaceableNoteStore, RequestBuilder } from "System";
|
import { PubkeyReplaceableNoteStore, RequestBuilder } from "System";
|
||||||
import useRequestBuilder from "Hooks/useRequestBuilder";
|
import useRequestBuilder from "Hooks/useRequestBuilder";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import { TaggedRawEvent, Lists, EventKind } from "@snort/nostr";
|
import { TaggedRawEvent, Lists, EventKind } from "System";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
|
|
||||||
import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "SnortUtils";
|
import { bech32ToHex, getNewest, getNewestEventTagsByKey, unwrap } from "SnortUtils";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { HexKey, TaggedRawEvent, Lists, EventKind } from "@snort/nostr";
|
import { HexKey, TaggedRawEvent, Lists, EventKind } from "System";
|
||||||
|
|
||||||
import { getNewest } from "SnortUtils";
|
import { getNewest } from "SnortUtils";
|
||||||
import { ParameterizedReplaceableNoteStore, RequestBuilder } from "System";
|
import { ParameterizedReplaceableNoteStore, RequestBuilder } from "System";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HexKey, Lists } from "@snort/nostr";
|
import { HexKey, Lists } from "System";
|
||||||
import useNotelistSubscription from "Hooks/useNotelistSubscription";
|
import useNotelistSubscription from "Hooks/useNotelistSubscription";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useSyncExternalStore } from "react";
|
import { useSyncExternalStore } from "react";
|
||||||
import { StateSnapshot } from "@snort/nostr";
|
import { StateSnapshot } from "System";
|
||||||
import { System } from "System";
|
import { System } from "index";
|
||||||
|
|
||||||
const noop = () => {
|
const noop = () => {
|
||||||
return () => undefined;
|
return () => undefined;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { HexKey, FullRelaySettings, EventKind } from "@snort/nostr";
|
import { HexKey, FullRelaySettings, EventKind } from "System";
|
||||||
|
|
||||||
import { RequestBuilder } from "System";
|
import { RequestBuilder } from "System";
|
||||||
import { ReplaceableNoteStore } from "System/NoteCollection";
|
import { ReplaceableNoteStore } from "System/NoteCollection";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { HexKey, FullRelaySettings, TaggedRawEvent, RelaySettings, EventKind } from "@snort/nostr";
|
import { HexKey, FullRelaySettings, TaggedRawEvent, RelaySettings, EventKind } from "System";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
|
|
||||||
import { sanitizeRelayUrl } from "SnortUtils";
|
import { sanitizeRelayUrl } from "SnortUtils";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { u256, EventKind } from "@snort/nostr";
|
import { u256, EventKind } from "System";
|
||||||
|
|
||||||
import { appendDedupe, NostrLink } from "SnortUtils";
|
import { appendDedupe, NostrLink } from "SnortUtils";
|
||||||
import { FlatNoteStore, RequestBuilder } from "System";
|
import { FlatNoteStore, RequestBuilder } from "System";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { EventKind, u256 } from "@snort/nostr";
|
import { EventKind, u256 } from "System";
|
||||||
|
|
||||||
import { unixNow, unwrap, tagFilterOfTextRepost } from "SnortUtils";
|
import { unixNow, unwrap, tagFilterOfTextRepost } from "SnortUtils";
|
||||||
import { FlatNoteStore, RequestBuilder } from "System";
|
import { FlatNoteStore, RequestBuilder } from "System";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { HexKey, EventKind } from "@snort/nostr";
|
import { HexKey, EventKind } from "System";
|
||||||
|
|
||||||
import { parseZap } from "Element/Zap";
|
import { parseZap } from "Element/Zap";
|
||||||
import { FlatNoteStore, RequestBuilder } from "System";
|
import { FlatNoteStore, RequestBuilder } from "System";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useSyncExternalStore } from "react";
|
import { useSyncExternalStore } from "react";
|
||||||
import { HexKey, u256 } from "@snort/nostr";
|
import { HexKey, u256 } from "System";
|
||||||
|
|
||||||
import { InteractionCache } from "Cache/EventInteractionCache";
|
import { InteractionCache } from "Cache/EventInteractionCache";
|
||||||
import { EventInteraction } from "Db";
|
import { EventInteraction } from "Db";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import { setBlocked, setMuted } from "Login";
|
import { setBlocked, setMuted } from "Login";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { HexKey, Lists, EventKind } from "@snort/nostr";
|
import { HexKey, Lists, EventKind } from "System";
|
||||||
|
|
||||||
import { FlatNoteStore, ParameterizedReplaceableNoteStore, RequestBuilder } from "System";
|
import { FlatNoteStore, ParameterizedReplaceableNoteStore, RequestBuilder } from "System";
|
||||||
import useRequestBuilder from "Hooks/useRequestBuilder";
|
import useRequestBuilder from "Hooks/useRequestBuilder";
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { useSyncExternalStore } from "react";
|
import { useSyncExternalStore } from "react";
|
||||||
import { RequestBuilder, System } from "System";
|
import { RequestBuilder } from "System";
|
||||||
import { EmptySnapshot, NoteStore, StoreSnapshot } from "System/NoteCollection";
|
import { EmptySnapshot, NoteStore, StoreSnapshot } from "System/NoteCollection";
|
||||||
import { unwrap } from "SnortUtils";
|
import { unwrap } from "SnortUtils";
|
||||||
|
import { System } from "index";
|
||||||
|
|
||||||
const useRequestBuilder = <TStore extends NoteStore, TSnapshot = ReturnType<TStore["getSnapshotData"]>>(
|
const useRequestBuilder = <TStore extends NoteStore, TSnapshot = ReturnType<TStore["getSnapshotData"]>>(
|
||||||
type: { new (): TStore },
|
type: { new (): TStore },
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useSyncExternalStore } from "react";
|
import { useSyncExternalStore } from "react";
|
||||||
import { System, SystemSnapshot } from "System";
|
import { SystemSnapshot } from "System";
|
||||||
|
import { System } from "index";
|
||||||
|
|
||||||
export default function useSystemState() {
|
export default function useSystemState() {
|
||||||
return useSyncExternalStore<SystemSnapshot>(
|
return useSyncExternalStore<SystemSnapshot>(
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { useEffect, useSyncExternalStore } from "react";
|
import { useEffect, useSyncExternalStore } from "react";
|
||||||
import { HexKey } from "@snort/nostr";
|
|
||||||
|
|
||||||
|
import { HexKey } from "System";
|
||||||
import { MetadataCache } from "Cache";
|
import { MetadataCache } from "Cache";
|
||||||
import { UserCache } from "Cache/UserCache";
|
import { UserCache } from "Cache/UserCache";
|
||||||
import { ProfileLoader } from "System/ProfileCache";
|
import { ProfileLoader } from "index";
|
||||||
|
|
||||||
export function useUserProfile(pubKey?: HexKey): MetadataCache | undefined {
|
export function useUserProfile(pubKey?: HexKey): MetadataCache | undefined {
|
||||||
const user = useSyncExternalStore<MetadataCache | undefined>(
|
const user = useSyncExternalStore<MetadataCache | undefined>(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HexKey, RawEvent } from "@snort/nostr";
|
import { HexKey, RawEvent } from "System";
|
||||||
import { EmailRegex } from "Const";
|
import { EmailRegex } from "Const";
|
||||||
import { bech32ToText, unwrap } from "SnortUtils";
|
import { bech32ToText, unwrap } from "SnortUtils";
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HexKey, RelaySettings } from "@snort/nostr";
|
import { HexKey, RelaySettings } from "System";
|
||||||
import * as secp from "@noble/curves/secp256k1";
|
import * as secp from "@noble/curves/secp256k1";
|
||||||
import * as utils from "@noble/curves/abstract/utils";
|
import * as utils from "@noble/curves/abstract/utils";
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ import { generateBip39Entropy, entropyToPrivateKey } from "nip6";
|
|||||||
import { bech32ToHex, dedupeById, randomSample, sanitizeRelayUrl, unixNowMs, unwrap } from "SnortUtils";
|
import { bech32ToHex, dedupeById, randomSample, sanitizeRelayUrl, unixNowMs, unwrap } from "SnortUtils";
|
||||||
import { SubscriptionEvent } from "Subscription";
|
import { SubscriptionEvent } from "Subscription";
|
||||||
import { EventPublisher } from "System/EventPublisher";
|
import { EventPublisher } from "System/EventPublisher";
|
||||||
|
import { System } from "index";
|
||||||
|
|
||||||
export function setRelays(state: LoginSession, relays: Record<string, RelaySettings>, createdAt: number) {
|
export function setRelays(state: LoginSession, relays: Record<string, RelaySettings>, createdAt: number) {
|
||||||
if (state.relays.timestamp >= createdAt) {
|
if (state.relays.timestamp >= createdAt) {
|
||||||
@ -78,7 +79,7 @@ export async function generateNewLogin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const publicKey = utils.bytesToHex(secp.schnorr.getPublicKey(privateKey));
|
const publicKey = utils.bytesToHex(secp.schnorr.getPublicKey(privateKey));
|
||||||
const publisher = new EventPublisher(publicKey, privateKey);
|
const publisher = new EventPublisher(System, publicKey, privateKey);
|
||||||
const ev = await publisher.contactList([bech32ToHex(SnortPubKey), publicKey], newRelays);
|
const ev = await publisher.contactList([bech32ToHex(SnortPubKey), publicKey], newRelays);
|
||||||
publisher.broadcast(ev);
|
publisher.broadcast(ev);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HexKey, RelaySettings, u256 } from "@snort/nostr";
|
import { HexKey, RelaySettings, u256 } from "System";
|
||||||
import { UserPreferences } from "Login";
|
import { UserPreferences } from "Login";
|
||||||
import { SubscriptionEvent } from "Subscription";
|
import { SubscriptionEvent } from "Subscription";
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import * as secp from "@noble/curves/secp256k1";
|
import * as secp from "@noble/curves/secp256k1";
|
||||||
import * as utils from "@noble/curves/abstract/utils";
|
import * as utils from "@noble/curves/abstract/utils";
|
||||||
|
|
||||||
import { HexKey, RelaySettings } from "@snort/nostr";
|
import { HexKey, RelaySettings } from "System";
|
||||||
|
|
||||||
import { DefaultRelays } from "Const";
|
import { DefaultRelays } from "Const";
|
||||||
import ExternalStore from "ExternalStore";
|
import ExternalStore from "ExternalStore";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { EventKind } from "@snort/nostr";
|
import { EventKind } from "System";
|
||||||
import { EventPublisher } from "System/EventPublisher";
|
import { EventPublisher } from "System/EventPublisher";
|
||||||
import { ServiceError, ServiceProvider } from "./ServiceProvider";
|
import { ServiceError, ServiceProvider } from "./ServiceProvider";
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import Nostrich from "nostrich.webp";
|
import Nostrich from "nostrich.webp";
|
||||||
|
|
||||||
import { TaggedRawEvent } from "@snort/nostr";
|
import { TaggedRawEvent, EventKind } from "System";
|
||||||
import { EventKind } from "@snort/nostr";
|
|
||||||
import { MetadataCache } from "Cache";
|
import { MetadataCache } from "Cache";
|
||||||
import { getDisplayName } from "Element/ProfileImage";
|
import { getDisplayName } from "Element/ProfileImage";
|
||||||
import { MentionRegex } from "Const";
|
import { MentionRegex } from "Const";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
|
|
||||||
import { ApiHost, KieranPubKey, SnortPubKey } from "Const";
|
import { ApiHost, KieranPubKey, SnortPubKey } from "Const";
|
||||||
import ProfilePreview from "Element/ProfilePreview";
|
import ProfilePreview from "Element/ProfilePreview";
|
||||||
|
@ -9,7 +9,7 @@ import messages from "./messages";
|
|||||||
import Icon from "Icons/Icon";
|
import Icon from "Icons/Icon";
|
||||||
import { RootState } from "State/Store";
|
import { RootState } from "State/Store";
|
||||||
import { setShow, reset } from "State/NoteCreator";
|
import { setShow, reset } from "State/NoteCreator";
|
||||||
import { System } from "System";
|
import { System } from "index";
|
||||||
import useLoginFeed from "Feed/LoginFeed";
|
import useLoginFeed from "Feed/LoginFeed";
|
||||||
import { totalUnread } from "Pages/MessagesPage";
|
import { totalUnread } from "Pages/MessagesPage";
|
||||||
import useModeration from "Hooks/useModeration";
|
import useModeration from "Hooks/useModeration";
|
||||||
|
@ -3,7 +3,7 @@ import "./LoginPage.css";
|
|||||||
import { CSSProperties, useEffect, useState } from "react";
|
import { CSSProperties, useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useIntl, FormattedMessage } from "react-intl";
|
import { useIntl, FormattedMessage } from "react-intl";
|
||||||
import { HexKey } from "@snort/nostr";
|
import { HexKey } from "System";
|
||||||
|
|
||||||
import { bech32ToHex, unwrap } from "SnortUtils";
|
import { bech32ToHex, unwrap } from "SnortUtils";
|
||||||
import ZapButton from "Element/ZapButton";
|
import ZapButton from "Element/ZapButton";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { HexKey, RawEvent, NostrPrefix } from "@snort/nostr";
|
import { HexKey, RawEvent, NostrPrefix } from "System";
|
||||||
|
|
||||||
import UnreadCount from "Element/UnreadCount";
|
import UnreadCount from "Element/UnreadCount";
|
||||||
import ProfileImage, { getDisplayName } from "Element/ProfileImage";
|
import ProfileImage, { getDisplayName } from "Element/ProfileImage";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NostrPrefix } from "@snort/nostr";
|
import { NostrPrefix } from "System";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
@ -6,7 +6,7 @@ import { useNavigate, useParams } from "react-router-dom";
|
|||||||
import Spinner from "Icons/Spinner";
|
import Spinner from "Icons/Spinner";
|
||||||
import { parseNostrLink, profileLink } from "SnortUtils";
|
import { parseNostrLink, profileLink } from "SnortUtils";
|
||||||
import { getNip05PubKey } from "Pages/LoginPage";
|
import { getNip05PubKey } from "Pages/LoginPage";
|
||||||
import { System } from "System";
|
import { System } from "index";
|
||||||
|
|
||||||
export default function NostrLinkHandler() {
|
export default function NostrLinkHandler() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
@ -2,7 +2,7 @@ import "./ProfilePage.css";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useIntl, FormattedMessage } from "react-intl";
|
import { useIntl, FormattedMessage } from "react-intl";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { encodeTLV, EventKind, HexKey, NostrPrefix } from "@snort/nostr";
|
import { encodeTLV, EventKind, HexKey, NostrPrefix } from "System";
|
||||||
|
|
||||||
import { parseNostrLink, getReactions, unwrap } from "SnortUtils";
|
import { parseNostrLink, getReactions, unwrap } from "SnortUtils";
|
||||||
import { formatShort } from "Number";
|
import { formatShort } from "Number";
|
||||||
|
@ -5,7 +5,7 @@ import { useIntl, FormattedMessage } from "react-intl";
|
|||||||
|
|
||||||
import Tabs, { Tab } from "Element/Tabs";
|
import Tabs, { Tab } from "Element/Tabs";
|
||||||
import Timeline from "Element/Timeline";
|
import Timeline from "Element/Timeline";
|
||||||
import { System } from "System";
|
import { System } from "index";
|
||||||
import { TimelineSubject } from "Feed/TimelineFeed";
|
import { TimelineSubject } from "Feed/TimelineFeed";
|
||||||
import { debounce, getRelayName, sha256, unixNow, unwrap } from "SnortUtils";
|
import { debounce, getRelayName, sha256, unixNow, unwrap } from "SnortUtils";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
|
@ -4,9 +4,8 @@ import Timeline from "Element/Timeline";
|
|||||||
import { Tab, TabElement } from "Element/Tabs";
|
import { Tab, TabElement } from "Element/Tabs";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { debounce } from "SnortUtils";
|
import { debounce } from "SnortUtils";
|
||||||
import { router } from "index";
|
import { System, router } from "index";
|
||||||
import { SearchRelays } from "Const";
|
import { SearchRelays } from "Const";
|
||||||
import { System } from "System";
|
|
||||||
import TrendingUsers from "Element/TrendingUsers";
|
import TrendingUsers from "Element/TrendingUsers";
|
||||||
|
|
||||||
import TrendingNotes from "Element/TrendingPosts";
|
import TrendingNotes from "Element/TrendingPosts";
|
||||||
|
@ -6,13 +6,13 @@ import { FormattedMessage, FormattedNumber } from "react-intl";
|
|||||||
import { SnortPubKey } from "Const";
|
import { SnortPubKey } from "Const";
|
||||||
import ProfilePreview from "Element/ProfilePreview";
|
import ProfilePreview from "Element/ProfilePreview";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import { System } from "System";
|
|
||||||
import { UploaderServices } from "Upload";
|
import { UploaderServices } from "Upload";
|
||||||
import { bech32ToHex, getRelayName, unwrap } from "SnortUtils";
|
import { bech32ToHex, getRelayName, unwrap } from "SnortUtils";
|
||||||
import { ZapPoolController, ZapPoolRecipient, ZapPoolRecipientType } from "ZapPoolController";
|
import { ZapPoolController, ZapPoolRecipient, ZapPoolRecipientType } from "ZapPoolController";
|
||||||
import { useUserProfile } from "Hooks/useUserProfile";
|
import { useUserProfile } from "Hooks/useUserProfile";
|
||||||
import AsyncButton from "Element/AsyncButton";
|
import AsyncButton from "Element/AsyncButton";
|
||||||
import { useWallet } from "Wallet";
|
import { useWallet } from "Wallet";
|
||||||
|
import { System } from "index";
|
||||||
|
|
||||||
const DataProviders = [
|
const DataProviders = [
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "./Keys.css";
|
import "./Keys.css";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
import { encodeTLV, NostrPrefix } from "@snort/nostr";
|
import { encodeTLV, NostrPrefix } from "System";
|
||||||
|
|
||||||
import Copy from "Element/Copy";
|
import Copy from "Element/Copy";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
|
@ -3,7 +3,7 @@ import ProfilePreview from "Element/ProfilePreview";
|
|||||||
import useRelayState from "Feed/RelayState";
|
import useRelayState from "Feed/RelayState";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { parseId, unwrap } from "SnortUtils";
|
import { parseId, unwrap } from "SnortUtils";
|
||||||
import { System } from "System";
|
import { System } from "index";
|
||||||
import { removeRelay } from "Login";
|
import { removeRelay } from "Login";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { FormattedMessage } from "react-intl";
|
|||||||
import { randomSample, unixNowMs } from "SnortUtils";
|
import { randomSample, unixNowMs } from "SnortUtils";
|
||||||
import Relay from "Element/Relay";
|
import Relay from "Element/Relay";
|
||||||
import useEventPublisher from "Feed/EventPublisher";
|
import useEventPublisher from "Feed/EventPublisher";
|
||||||
import { System } from "System";
|
import { System } from "index";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import { setRelays } from "Login";
|
import { setRelays } from "Login";
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { EventKind } from "@snort/nostr";
|
import { EventKind } from "System";
|
||||||
import { ApiHost } from "Const";
|
import { ApiHost } from "Const";
|
||||||
import { SubscriptionType } from "Subscription";
|
import { SubscriptionType } from "Subscription";
|
||||||
import { EventPublisher } from "System/EventPublisher";
|
import { EventPublisher } from "System/EventPublisher";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NostrPrefix } from "@snort/nostr";
|
import { NostrPrefix } from "System";
|
||||||
import { parseNostrLink, tryParseNostrLink } from ".";
|
import { parseNostrLink, tryParseNostrLink } from ".";
|
||||||
import { splitByUrl, magnetURIDecode, getRelayName } from ".";
|
import { splitByUrl, magnetURIDecode, getRelayName } from ".";
|
||||||
import { describe, expect } from "@jest/globals";
|
import { describe, expect } from "@jest/globals";
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
decodeTLV,
|
decodeTLV,
|
||||||
TLVEntryType,
|
TLVEntryType,
|
||||||
RawEvent,
|
RawEvent,
|
||||||
} from "@snort/nostr";
|
} from "System";
|
||||||
import { MetadataCache } from "Cache";
|
import { MetadataCache } from "Cache";
|
||||||
import NostrLink from "Element/NostrLink";
|
import NostrLink from "Element/NostrLink";
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import { RawEvent, TaggedRawEvent } from "@snort/nostr";
|
import { RawEvent, TaggedRawEvent } from "System";
|
||||||
|
|
||||||
interface NoteCreatorStore {
|
interface NoteCreatorStore {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import { RawEvent } from "@snort/nostr";
|
import { RawEvent } from "System";
|
||||||
|
|
||||||
interface ReBroadcastStore {
|
interface ReBroadcastStore {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
|
448
packages/app/src/System/Connection.ts
Normal file
448
packages/app/src/System/Connection.ts
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
import { v4 as uuid } from "uuid";
|
||||||
|
|
||||||
|
import { DefaultConnectTimeout } from "./Const";
|
||||||
|
import { ConnectionStats } from "./ConnectionStats";
|
||||||
|
import { RawEvent, ReqCommand, TaggedRawEvent, u256 } from "./Nostr";
|
||||||
|
import { RelayInfo } from "./RelayInfo";
|
||||||
|
import { unwrap } from "./Util";
|
||||||
|
|
||||||
|
export type CustomHook = (state: Readonly<StateSnapshot>) => void;
|
||||||
|
export type AuthHandler = (challenge: string, relay: string) => Promise<RawEvent | undefined>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relay settings
|
||||||
|
*/
|
||||||
|
export interface RelaySettings {
|
||||||
|
read: boolean;
|
||||||
|
write: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snapshot of connection stats
|
||||||
|
*/
|
||||||
|
export interface StateSnapshot {
|
||||||
|
connected: boolean;
|
||||||
|
disconnects: number;
|
||||||
|
avgLatency: number;
|
||||||
|
events: {
|
||||||
|
received: number;
|
||||||
|
send: number;
|
||||||
|
};
|
||||||
|
info?: RelayInfo;
|
||||||
|
pendingRequests: Array<string>;
|
||||||
|
activeRequests: Array<string>;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Connection {
|
||||||
|
Id: string;
|
||||||
|
Address: string;
|
||||||
|
Socket: WebSocket | null = null;
|
||||||
|
|
||||||
|
PendingRaw: Array<object> = [];
|
||||||
|
PendingRequests: Array<{
|
||||||
|
cmd: ReqCommand;
|
||||||
|
cb: () => void;
|
||||||
|
}> = [];
|
||||||
|
ActiveRequests = new Set<string>();
|
||||||
|
|
||||||
|
Settings: RelaySettings;
|
||||||
|
Info?: RelayInfo;
|
||||||
|
ConnectTimeout: number = DefaultConnectTimeout;
|
||||||
|
Stats: ConnectionStats = new ConnectionStats();
|
||||||
|
StateHooks: Map<string, CustomHook> = new Map();
|
||||||
|
HasStateChange: boolean = true;
|
||||||
|
CurrentState: StateSnapshot;
|
||||||
|
LastState: Readonly<StateSnapshot>;
|
||||||
|
IsClosed: boolean;
|
||||||
|
ReconnectTimer: ReturnType<typeof setTimeout> | null;
|
||||||
|
EventsCallback: Map<u256, (msg: boolean[]) => void>;
|
||||||
|
OnConnected?: () => void;
|
||||||
|
OnEvent?: (sub: string, e: TaggedRawEvent) => void;
|
||||||
|
OnEose?: (sub: string) => void;
|
||||||
|
OnDisconnect?: (id: string) => void;
|
||||||
|
Auth?: AuthHandler;
|
||||||
|
AwaitingAuth: Map<string, boolean>;
|
||||||
|
Authed = false;
|
||||||
|
Ephemeral: boolean;
|
||||||
|
EphemeralTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||||
|
Down = true;
|
||||||
|
|
||||||
|
constructor(addr: string, options: RelaySettings, auth?: AuthHandler, ephemeral: boolean = false) {
|
||||||
|
this.Id = uuid();
|
||||||
|
this.Address = addr;
|
||||||
|
this.Settings = options;
|
||||||
|
this.CurrentState = {
|
||||||
|
connected: false,
|
||||||
|
disconnects: 0,
|
||||||
|
avgLatency: 0,
|
||||||
|
events: {
|
||||||
|
received: 0,
|
||||||
|
send: 0,
|
||||||
|
},
|
||||||
|
} as StateSnapshot;
|
||||||
|
this.LastState = Object.freeze({ ...this.CurrentState });
|
||||||
|
this.IsClosed = false;
|
||||||
|
this.ReconnectTimer = null;
|
||||||
|
this.EventsCallback = new Map();
|
||||||
|
this.AwaitingAuth = new Map();
|
||||||
|
this.Auth = auth;
|
||||||
|
this.Ephemeral = ephemeral;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetEphemeralTimeout() {
|
||||||
|
if (this.EphemeralTimeout) {
|
||||||
|
clearTimeout(this.EphemeralTimeout);
|
||||||
|
}
|
||||||
|
if (this.Ephemeral) {
|
||||||
|
this.EphemeralTimeout = setTimeout(() => {
|
||||||
|
this.Close();
|
||||||
|
}, 30_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Connect() {
|
||||||
|
try {
|
||||||
|
if (this.Info === undefined) {
|
||||||
|
const u = new URL(this.Address);
|
||||||
|
const rsp = await fetch(`${u.protocol === "wss:" ? "https:" : "http:"}//${u.host}`, {
|
||||||
|
headers: {
|
||||||
|
accept: "application/nostr+json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (rsp.ok) {
|
||||||
|
const data = await rsp.json();
|
||||||
|
for (const [k, v] of Object.entries(data)) {
|
||||||
|
if (v === "unset" || v === "" || v === "~") {
|
||||||
|
data[k] = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.Info = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Could not load relay information", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.Socket) {
|
||||||
|
this.Id = uuid();
|
||||||
|
this.Socket.onopen = null;
|
||||||
|
this.Socket.onmessage = null;
|
||||||
|
this.Socket.onerror = null;
|
||||||
|
this.Socket.onclose = null;
|
||||||
|
}
|
||||||
|
this.IsClosed = false;
|
||||||
|
this.Socket = new WebSocket(this.Address);
|
||||||
|
this.Socket.onopen = () => this.OnOpen();
|
||||||
|
this.Socket.onmessage = e => this.OnMessage(e);
|
||||||
|
this.Socket.onerror = e => this.OnError(e);
|
||||||
|
this.Socket.onclose = e => this.OnClose(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Close() {
|
||||||
|
this.IsClosed = true;
|
||||||
|
if (this.ReconnectTimer !== null) {
|
||||||
|
clearTimeout(this.ReconnectTimer);
|
||||||
|
this.ReconnectTimer = null;
|
||||||
|
}
|
||||||
|
this.Socket?.close();
|
||||||
|
this.#UpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
OnOpen() {
|
||||||
|
this.ConnectTimeout = DefaultConnectTimeout;
|
||||||
|
console.log(`[${this.Address}] Open!`);
|
||||||
|
this.Down = false;
|
||||||
|
if (this.Ephemeral) {
|
||||||
|
this.ResetEphemeralTimeout();
|
||||||
|
}
|
||||||
|
this.OnConnected?.();
|
||||||
|
this.#sendPendingRaw();
|
||||||
|
}
|
||||||
|
|
||||||
|
OnClose(e: CloseEvent) {
|
||||||
|
if (!this.IsClosed) {
|
||||||
|
this.ConnectTimeout = this.ConnectTimeout * 2;
|
||||||
|
console.log(
|
||||||
|
`[${this.Address}] Closed (${e.reason}), trying again in ${(this.ConnectTimeout / 1000)
|
||||||
|
.toFixed(0)
|
||||||
|
.toLocaleString()} sec`
|
||||||
|
);
|
||||||
|
this.ReconnectTimer = setTimeout(() => {
|
||||||
|
this.Connect();
|
||||||
|
}, this.ConnectTimeout);
|
||||||
|
this.Stats.Disconnects++;
|
||||||
|
} else {
|
||||||
|
console.log(`[${this.Address}] Closed!`);
|
||||||
|
this.ReconnectTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.OnDisconnect?.(this.Id);
|
||||||
|
this.#ResetQueues();
|
||||||
|
// reset connection Id on disconnect, for query-tracking
|
||||||
|
this.Id = uuid();
|
||||||
|
this.#UpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
OnMessage(e: MessageEvent) {
|
||||||
|
if (e.data.length > 0) {
|
||||||
|
const msg = JSON.parse(e.data);
|
||||||
|
const tag = msg[0];
|
||||||
|
switch (tag) {
|
||||||
|
case "AUTH": {
|
||||||
|
this._OnAuthAsync(msg[1])
|
||||||
|
.then(() => this.#sendPendingRaw())
|
||||||
|
.catch(console.error);
|
||||||
|
this.Stats.EventsReceived++;
|
||||||
|
this.#UpdateState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "EVENT": {
|
||||||
|
this.OnEvent?.(msg[1], {
|
||||||
|
...msg[2],
|
||||||
|
relays: [this.Address],
|
||||||
|
});
|
||||||
|
this.Stats.EventsReceived++;
|
||||||
|
this.#UpdateState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "EOSE": {
|
||||||
|
this.OnEose?.(msg[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "OK": {
|
||||||
|
// feedback to broadcast call
|
||||||
|
console.debug(`${this.Address} OK: `, msg);
|
||||||
|
const id = msg[1];
|
||||||
|
if (this.EventsCallback.has(id)) {
|
||||||
|
const cb = unwrap(this.EventsCallback.get(id));
|
||||||
|
this.EventsCallback.delete(id);
|
||||||
|
cb(msg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "NOTICE": {
|
||||||
|
console.warn(`[${this.Address}] NOTICE: ${msg[1]}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
console.warn(`Unknown tag: ${tag}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OnError(e: Event) {
|
||||||
|
console.error(e);
|
||||||
|
this.#UpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send event on this connection
|
||||||
|
*/
|
||||||
|
SendEvent(e: RawEvent) {
|
||||||
|
if (!this.Settings.write) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const req = ["EVENT", e];
|
||||||
|
this.#SendJson(req);
|
||||||
|
this.Stats.EventsSent++;
|
||||||
|
this.#UpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send event on this connection and wait for OK response
|
||||||
|
*/
|
||||||
|
async SendAsync(e: RawEvent, timeout = 5000) {
|
||||||
|
return new Promise<void>(resolve => {
|
||||||
|
if (!this.Settings.write) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const t = setTimeout(() => {
|
||||||
|
resolve();
|
||||||
|
}, timeout);
|
||||||
|
this.EventsCallback.set(e.id, () => {
|
||||||
|
clearTimeout(t);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = ["EVENT", e];
|
||||||
|
this.#SendJson(req);
|
||||||
|
this.Stats.EventsSent++;
|
||||||
|
this.#UpdateState();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook status for connection
|
||||||
|
*/
|
||||||
|
StatusHook(fnHook: CustomHook) {
|
||||||
|
const id = uuid();
|
||||||
|
this.StateHooks.set(id, fnHook);
|
||||||
|
return () => {
|
||||||
|
this.StateHooks.delete(id);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current state of this connection
|
||||||
|
*/
|
||||||
|
GetState() {
|
||||||
|
if (this.HasStateChange) {
|
||||||
|
this.LastState = Object.freeze({ ...this.CurrentState });
|
||||||
|
this.HasStateChange = false;
|
||||||
|
}
|
||||||
|
return this.LastState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using relay document to determine if this relay supports a feature
|
||||||
|
*/
|
||||||
|
SupportsNip(n: number) {
|
||||||
|
return this.Info?.supported_nips?.some(a => a === n) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue or send command to the relay
|
||||||
|
* @param cmd The REQ to send to the server
|
||||||
|
*/
|
||||||
|
QueueReq(cmd: ReqCommand, cbSent: () => void) {
|
||||||
|
if (this.ActiveRequests.size >= this.#maxSubscriptions) {
|
||||||
|
this.PendingRequests.push({
|
||||||
|
cmd,
|
||||||
|
cb: cbSent,
|
||||||
|
});
|
||||||
|
console.debug("Queuing:", this.Address, cmd);
|
||||||
|
} else {
|
||||||
|
this.ActiveRequests.add(cmd[1]);
|
||||||
|
this.#SendJson(cmd);
|
||||||
|
cbSent();
|
||||||
|
}
|
||||||
|
this.#UpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseReq(id: string) {
|
||||||
|
if (this.ActiveRequests.delete(id)) {
|
||||||
|
this.#SendJson(["CLOSE", id]);
|
||||||
|
this.OnEose?.(id);
|
||||||
|
this.#SendQueuedRequests();
|
||||||
|
}
|
||||||
|
this.#UpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
#SendQueuedRequests() {
|
||||||
|
const canSend = this.#maxSubscriptions - this.ActiveRequests.size;
|
||||||
|
if (canSend > 0) {
|
||||||
|
for (let x = 0; x < canSend; x++) {
|
||||||
|
const p = this.PendingRequests.shift();
|
||||||
|
if (p) {
|
||||||
|
this.ActiveRequests.add(p.cmd[1]);
|
||||||
|
this.#SendJson(p.cmd);
|
||||||
|
p.cb();
|
||||||
|
console.debug("Sent pending REQ", this.Address, p.cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ResetQueues() {
|
||||||
|
this.ActiveRequests.clear();
|
||||||
|
this.PendingRequests = [];
|
||||||
|
this.PendingRaw = [];
|
||||||
|
this.#UpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
#UpdateState() {
|
||||||
|
this.CurrentState.connected = this.Socket?.readyState === WebSocket.OPEN;
|
||||||
|
this.CurrentState.events.received = this.Stats.EventsReceived;
|
||||||
|
this.CurrentState.events.send = this.Stats.EventsSent;
|
||||||
|
this.CurrentState.avgLatency =
|
||||||
|
this.Stats.Latency.length > 0 ? this.Stats.Latency.reduce((acc, v) => acc + v, 0) / this.Stats.Latency.length : 0;
|
||||||
|
this.CurrentState.disconnects = this.Stats.Disconnects;
|
||||||
|
this.CurrentState.info = this.Info;
|
||||||
|
this.CurrentState.id = this.Id;
|
||||||
|
this.CurrentState.pendingRequests = [...this.PendingRequests.map(a => a.cmd[1])];
|
||||||
|
this.CurrentState.activeRequests = [...this.ActiveRequests];
|
||||||
|
this.Stats.Latency = this.Stats.Latency.slice(-20); // trim
|
||||||
|
this.HasStateChange = true;
|
||||||
|
this.#NotifyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
#NotifyState() {
|
||||||
|
const state = this.GetState();
|
||||||
|
for (const [, h] of this.StateHooks) {
|
||||||
|
h(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#SendJson(obj: object) {
|
||||||
|
const authPending = !this.Authed && (this.AwaitingAuth.size > 0 || this.Info?.limitation?.auth_required === true);
|
||||||
|
if (this.Socket?.readyState !== WebSocket.OPEN || authPending) {
|
||||||
|
this.PendingRaw.push(obj);
|
||||||
|
if (this.Socket?.readyState === WebSocket.CLOSED && this.Ephemeral && this.IsClosed) {
|
||||||
|
this.Connect();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#sendPendingRaw();
|
||||||
|
this.#sendOnWire(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sendPendingRaw() {
|
||||||
|
while (this.PendingRaw.length > 0) {
|
||||||
|
const next = this.PendingRaw.shift();
|
||||||
|
if (next) {
|
||||||
|
this.#sendOnWire(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#sendOnWire(obj: unknown) {
|
||||||
|
if (this.Socket?.readyState !== WebSocket.OPEN) {
|
||||||
|
throw new Error(`Socket is not open, state is ${this.Socket?.readyState}`);
|
||||||
|
}
|
||||||
|
const json = JSON.stringify(obj);
|
||||||
|
this.Socket.send(json);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _OnAuthAsync(challenge: string): Promise<void> {
|
||||||
|
const authCleanup = () => {
|
||||||
|
this.AwaitingAuth.delete(challenge);
|
||||||
|
};
|
||||||
|
if (!this.Auth) {
|
||||||
|
throw new Error("Auth hook not registered");
|
||||||
|
}
|
||||||
|
this.AwaitingAuth.set(challenge, true);
|
||||||
|
const authEvent = await this.Auth(challenge, this.Address);
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if (!authEvent) {
|
||||||
|
authCleanup();
|
||||||
|
return Promise.reject("no event");
|
||||||
|
}
|
||||||
|
|
||||||
|
const t = setTimeout(() => {
|
||||||
|
authCleanup();
|
||||||
|
resolve();
|
||||||
|
}, 10_000);
|
||||||
|
|
||||||
|
this.EventsCallback.set(authEvent.id, (msg: boolean[]) => {
|
||||||
|
clearTimeout(t);
|
||||||
|
authCleanup();
|
||||||
|
if (msg.length > 3 && msg[2] === true) {
|
||||||
|
this.Authed = true;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.#sendOnWire(["AUTH", authEvent]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get #maxSubscriptions() {
|
||||||
|
return this.Info?.limitation?.max_subscriptions ?? 25;
|
||||||
|
}
|
||||||
|
}
|
34
packages/app/src/System/ConnectionStats.ts
Normal file
34
packages/app/src/System/ConnectionStats.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Stats class for tracking metrics per connection
|
||||||
|
*/
|
||||||
|
export class ConnectionStats {
|
||||||
|
/**
|
||||||
|
* Last n records of how long between REQ->EOSE
|
||||||
|
*/
|
||||||
|
Latency: number[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total number of REQ's sent on this connection
|
||||||
|
*/
|
||||||
|
Subs: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count of REQ which took too long and where abandoned
|
||||||
|
*/
|
||||||
|
SubsTimeout: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total number of EVENT messages received
|
||||||
|
*/
|
||||||
|
EventsReceived: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total number of EVENT messages sent
|
||||||
|
*/
|
||||||
|
EventsSent: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total number of times this connection was lost
|
||||||
|
*/
|
||||||
|
Disconnects: number = 0;
|
||||||
|
}
|
4
packages/app/src/System/Const.ts
Normal file
4
packages/app/src/System/Const.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Websocket re-connect timeout
|
||||||
|
*/
|
||||||
|
export const DefaultConnectTimeout = 2000;
|
@ -1,4 +1,4 @@
|
|||||||
import { EventKind, HexKey, NostrPrefix, RawEvent } from "@snort/nostr";
|
import { EventKind, HexKey, NostrPrefix, RawEvent } from "System";
|
||||||
import { HashtagRegex } from "Const";
|
import { HashtagRegex } from "Const";
|
||||||
import { getPublicKey, parseNostrLink, unixNow } from "SnortUtils";
|
import { getPublicKey, parseNostrLink, unixNow } from "SnortUtils";
|
||||||
import { EventExt } from "./EventExt";
|
import { EventExt } from "./EventExt";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as secp from "@noble/curves/secp256k1";
|
import * as secp from "@noble/curves/secp256k1";
|
||||||
import * as utils from "@noble/curves/abstract/utils";
|
import * as utils from "@noble/curves/abstract/utils";
|
||||||
import { EventKind, HexKey, RawEvent, Tag } from "@snort/nostr";
|
import { EventKind, HexKey, RawEvent, Tag } from "System";
|
||||||
import base64 from "@protobufjs/base64";
|
import base64 from "@protobufjs/base64";
|
||||||
import { sha256, unixNow } from "SnortUtils";
|
import { sha256, unixNow } from "SnortUtils";
|
||||||
|
|
||||||
|
29
packages/app/src/System/EventKind.ts
Normal file
29
packages/app/src/System/EventKind.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
enum EventKind {
|
||||||
|
Unknown = -1,
|
||||||
|
SetMetadata = 0,
|
||||||
|
TextNote = 1,
|
||||||
|
RecommendServer = 2,
|
||||||
|
ContactList = 3, // NIP-02
|
||||||
|
DirectMessage = 4, // NIP-04
|
||||||
|
Deletion = 5, // NIP-09
|
||||||
|
Repost = 6, // NIP-18
|
||||||
|
Reaction = 7, // NIP-25
|
||||||
|
BadgeAward = 8, // NIP-58
|
||||||
|
SnortSubscriptions = 1000, // NIP-XX
|
||||||
|
Polls = 6969, // NIP-69
|
||||||
|
FileHeader = 1063, // NIP-94
|
||||||
|
Relays = 10002, // NIP-65
|
||||||
|
Ephemeral = 20_000,
|
||||||
|
Auth = 22242, // NIP-42
|
||||||
|
PubkeyLists = 30000, // NIP-51a
|
||||||
|
NoteLists = 30001, // NIP-51b
|
||||||
|
TagLists = 30002, // NIP-51c
|
||||||
|
Badge = 30009, // NIP-58
|
||||||
|
ProfileBadges = 30008, // NIP-58
|
||||||
|
ZapstrTrack = 31337,
|
||||||
|
ZapRequest = 9734, // NIP 57
|
||||||
|
ZapReceipt = 9735, // NIP 57
|
||||||
|
HttpAuthentication = 27235, // NIP XX - HTTP Authentication
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EventKind;
|
@ -10,10 +10,9 @@ import {
|
|||||||
TaggedRawEvent,
|
TaggedRawEvent,
|
||||||
u256,
|
u256,
|
||||||
UserMetadata,
|
UserMetadata,
|
||||||
} from "@snort/nostr";
|
} from "System";
|
||||||
|
|
||||||
import { DefaultRelays } from "Const";
|
import { DefaultRelays } from "Const";
|
||||||
import { System } from "System";
|
|
||||||
import { unwrap } from "SnortUtils";
|
import { unwrap } from "SnortUtils";
|
||||||
import { EventBuilder } from "./EventBuilder";
|
import { EventBuilder } from "./EventBuilder";
|
||||||
import { EventExt } from "./EventExt";
|
import { EventExt } from "./EventExt";
|
||||||
@ -23,11 +22,34 @@ const Nip7Queue: Array<WorkQueueItem> = [];
|
|||||||
processWorkQueue(Nip7Queue);
|
processWorkQueue(Nip7Queue);
|
||||||
export type EventBuilderHook = (ev: EventBuilder) => EventBuilder;
|
export type EventBuilderHook = (ev: EventBuilder) => EventBuilder;
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
nostr?: {
|
||||||
|
getPublicKey: () => Promise<HexKey>;
|
||||||
|
signEvent: <T extends RawEvent>(event: T) => Promise<T>;
|
||||||
|
|
||||||
|
getRelays?: () => Promise<Record<string, { read: boolean; write: boolean }>>;
|
||||||
|
|
||||||
|
nip04?: {
|
||||||
|
encrypt?: (pubkey: HexKey, plaintext: string) => Promise<string>;
|
||||||
|
decrypt?: (pubkey: HexKey, ciphertext: string) => Promise<string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SystemInterface {
|
||||||
|
BroadcastEvent(ev: RawEvent): void;
|
||||||
|
WriteOnceToRelay(relay: string, ev: RawEvent): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
export class EventPublisher {
|
export class EventPublisher {
|
||||||
|
#system: SystemInterface;
|
||||||
#pubKey: string;
|
#pubKey: string;
|
||||||
#privateKey?: string;
|
#privateKey?: string;
|
||||||
|
|
||||||
constructor(pubKey: string, privKey?: string) {
|
constructor(system: SystemInterface, pubKey: string, privKey?: string) {
|
||||||
|
this.#system = system;
|
||||||
if (privKey) {
|
if (privKey) {
|
||||||
this.#privateKey = privKey;
|
this.#privateKey = privKey;
|
||||||
this.#pubKey = utils.bytesToHex(secp.schnorr.getPublicKey(privKey));
|
this.#pubKey = utils.bytesToHex(secp.schnorr.getPublicKey(privKey));
|
||||||
@ -97,7 +119,7 @@ export class EventPublisher {
|
|||||||
|
|
||||||
broadcast(ev: RawEvent) {
|
broadcast(ev: RawEvent) {
|
||||||
console.debug(ev);
|
console.debug(ev);
|
||||||
System.BroadcastEvent(ev);
|
this.#system.BroadcastEvent(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,7 +129,7 @@ export class EventPublisher {
|
|||||||
*/
|
*/
|
||||||
broadcastForBootstrap(ev: RawEvent) {
|
broadcastForBootstrap(ev: RawEvent) {
|
||||||
for (const [k] of DefaultRelays) {
|
for (const [k] of DefaultRelays) {
|
||||||
System.WriteOnceToRelay(k, ev);
|
this.#system.WriteOnceToRelay(k, ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +138,7 @@ export class EventPublisher {
|
|||||||
*/
|
*/
|
||||||
broadcastAll(ev: RawEvent, relays: string[]) {
|
broadcastAll(ev: RawEvent, relays: string[]) {
|
||||||
for (const k of relays) {
|
for (const k of relays) {
|
||||||
System.WriteOnceToRelay(k, ev);
|
this.#system.WriteOnceToRelay(k, ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { FullRelaySettings, RawReqFilter } from "@snort/nostr";
|
import { FullRelaySettings, RawReqFilter } from "System";
|
||||||
import { unwrap } from "SnortUtils";
|
import { unwrap } from "SnortUtils";
|
||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
|
|
||||||
|
88
packages/app/src/System/Links.ts
Normal file
88
packages/app/src/System/Links.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import * as utils from "@noble/curves/abstract/utils";
|
||||||
|
import { bech32 } from "bech32";
|
||||||
|
import { HexKey } from "./Nostr";
|
||||||
|
|
||||||
|
export enum NostrPrefix {
|
||||||
|
PublicKey = "npub",
|
||||||
|
PrivateKey = "nsec",
|
||||||
|
Note = "note",
|
||||||
|
|
||||||
|
// TLV prefixes
|
||||||
|
Profile = "nprofile",
|
||||||
|
Event = "nevent",
|
||||||
|
Relay = "nrelay",
|
||||||
|
Address = "naddr",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TLVEntryType {
|
||||||
|
Special = 0,
|
||||||
|
Relay = 1,
|
||||||
|
Author = 2,
|
||||||
|
Kind = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TLVEntry {
|
||||||
|
type: TLVEntryType;
|
||||||
|
length: number;
|
||||||
|
value: string | HexKey | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeTLV(prefix: NostrPrefix, id: string, relays?: string[], kind?: number, author?: string) {
|
||||||
|
const enc = new TextEncoder();
|
||||||
|
const buf = prefix === NostrPrefix.Address ? enc.encode(id) : utils.hexToBytes(id);
|
||||||
|
|
||||||
|
const tl0 = [0, buf.length, ...buf];
|
||||||
|
const tl1 =
|
||||||
|
relays
|
||||||
|
?.map(a => {
|
||||||
|
const data = enc.encode(a);
|
||||||
|
return [1, data.length, ...data];
|
||||||
|
})
|
||||||
|
.flat() ?? [];
|
||||||
|
|
||||||
|
const tl2 = author ? [2, 32, ...utils.hexToBytes(author)] : [];
|
||||||
|
const tl3 = kind ? [3, 4, ...new Uint8Array(new Uint32Array([kind]).buffer).reverse()] : [];
|
||||||
|
|
||||||
|
return bech32.encode(prefix, bech32.toWords([...tl0, ...tl1, ...tl2, ...tl3]), 1_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeTLV(str: string) {
|
||||||
|
const decoded = bech32.decode(str, 1_000);
|
||||||
|
const data = bech32.fromWords(decoded.words);
|
||||||
|
|
||||||
|
const entries: TLVEntry[] = [];
|
||||||
|
let x = 0;
|
||||||
|
while (x < data.length) {
|
||||||
|
const t = data[x];
|
||||||
|
const l = data[x + 1];
|
||||||
|
const v = data.slice(x + 2, x + 2 + l);
|
||||||
|
entries.push({
|
||||||
|
type: t,
|
||||||
|
length: l,
|
||||||
|
value: decodeTLVEntry(t, decoded.prefix, new Uint8Array(v)),
|
||||||
|
});
|
||||||
|
x += 2 + l;
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeTLVEntry(type: TLVEntryType, prefix: string, data: Uint8Array) {
|
||||||
|
switch (type) {
|
||||||
|
case TLVEntryType.Special: {
|
||||||
|
if (prefix === NostrPrefix.Address) {
|
||||||
|
return new TextDecoder("ASCII").decode(data);
|
||||||
|
} else {
|
||||||
|
return utils.bytesToHex(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case TLVEntryType.Author: {
|
||||||
|
return utils.bytesToHex(data);
|
||||||
|
}
|
||||||
|
case TLVEntryType.Kind: {
|
||||||
|
return new Uint32Array(new Uint8Array(data.reverse()).buffer)[0];
|
||||||
|
}
|
||||||
|
case TLVEntryType.Relay: {
|
||||||
|
return new TextDecoder("ASCII").decode(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
packages/app/src/System/Nips.ts
Normal file
3
packages/app/src/System/Nips.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export enum Nips {
|
||||||
|
Search = 50,
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user