();
- const domainConfig = useMemo(() => serviceConfig?.domains.find(a => a.name === domain), [domain, serviceConfig]);
+ const domainConfig = useMemo(() => serviceConfig?.domains.find(a => a.name === domain), [domain]);
useEffect(() => {
svc.GetConfig()
@@ -58,7 +58,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
}
})
.catch(console.error)
- }, [props, svc]);
+ }, [props]);
useEffect(() => {
setError(undefined);
@@ -89,7 +89,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
.catch(console.error);
});
}
- }, [handle, domain, domainConfig, svc]);
+ }, [handle, domain]);
useEffect(() => {
if (registerResponse && showInvoice) {
@@ -111,7 +111,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
}, 2_000);
return () => clearInterval(t);
}
- }, [registerResponse, showInvoice, svc])
+ }, [registerResponse, showInvoice])
function mapError(e: ServiceErrorCode, t: string | null): string | undefined {
let whyMap = new Map([
@@ -159,7 +159,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
Find out more info about {props.name} at {props.link}
{error && {error.error}}
{!registerStatus &&
- setHandle(e.target.value.toLowerCase())} />
+ setHandle(e.target.value)} />
@
- navigate(name)}>
-
+ setShowExtra(s => !s)}>
+
+ {showExtra ?
+
+ {state?.events.send}
+
+
+ {state?.events.received}
+
+
+
+ Delete
+ dispatch(removeRelay(props.addr))}>
+
+
+
+
: null}
>
)
}
diff --git a/src/Element/Text.tsx b/src/Element/Text.tsx
index e74a5cef..74f7551c 100644
--- a/src/Element/Text.tsx
+++ b/src/Element/Text.tsx
@@ -4,7 +4,7 @@ import { Link } from "react-router-dom";
import ReactMarkdown from "react-markdown";
import { TwitterTweetEmbed } from "react-twitter-embed";
-import { UrlRegex, FileExtensionRegex, MentionRegex, InvoiceRegex, YoutubeUrlRegex, TweetUrlRegex, HashtagRegex, TidalRegex, SoundCloudRegex } from "Const";
+import { UrlRegex, FileExtensionRegex, MentionRegex, InvoiceRegex, YoutubeUrlRegex, TweetUrlRegex, HashtagRegex, TidalRegex, SoundCloudRegex, MixCloudRegex } from "Const";
import { eventLink, hexToBech32 } from "Util";
import Invoice from "Element/Invoice";
import Hashtag from "Element/Hashtag";
@@ -17,6 +17,7 @@ import { useSelector } from 'react-redux';
import { RootState } from 'State/Store';
import { UserPreferences } from 'State/Login';
import SoundCloudEmbed from 'Element/SoundCloudEmded'
+import MixCloudEmbed from './MixCloudEmbed';
function transformHttpLink(a: string, pref: UserPreferences) {
try {
@@ -28,6 +29,7 @@ function transformHttpLink(a: string, pref: UserPreferences) {
const tweetId = TweetUrlRegex.test(a) && RegExp.$2;
const tidalId = TidalRegex.test(a) && RegExp.$1;
const soundcloundId = SoundCloudRegex.test(a) && RegExp.$1;
+ const mixcloudId = MixCloudRegex.test(a) && RegExp.$1;
const extension = FileExtensionRegex.test(url.pathname.toLowerCase()) && RegExp.$1;
if (extension) {
switch (extension) {
@@ -75,6 +77,8 @@ function transformHttpLink(a: string, pref: UserPreferences) {
return
} else if (soundcloundId){
return
+ } else if (mixcloudId){
+ return
} else {
return e.stopPropagation()} target="_blank" rel="noreferrer" className="ext">{a}
}
diff --git a/src/Element/Timeline.tsx b/src/Element/Timeline.tsx
index c837c399..a907ca23 100644
--- a/src/Element/Timeline.tsx
+++ b/src/Element/Timeline.tsx
@@ -1,5 +1,5 @@
import "./Timeline.css";
-import { useCallback, useMemo } from "react";
+import { useMemo } from "react";
import useTimelineFeed, { TimelineSubject } from "Feed/TimelineFeed";
import { TaggedRawEvent } from "Nostr";
import EventKind from "Nostr/EventKind";
@@ -23,17 +23,17 @@ export default function Timeline({ subject, postsOnly = false, method }: Timelin
method
});
- const filterPosts = useCallback((nts: TaggedRawEvent[]) => {
- return [...nts].sort((a, b) => b.created_at - a.created_at)?.filter(a => postsOnly ? !a.tags.some(b => b[0] === "e") : true);
- }, [postsOnly]);
+ const filterPosts = (notes: TaggedRawEvent[]) => {
+ return [...notes].sort((a, b) => b.created_at - a.created_at)?.filter(a => postsOnly ? !a.tags.some(b => b[0] === "e") : true);
+ }
const mainFeed = useMemo(() => {
return filterPosts(main.notes);
- }, [main, filterPosts]);
+ }, [main]);
const latestFeed = useMemo(() => {
return filterPosts(latest.notes).filter(a => !mainFeed.some(b => b.id === a.id));
- }, [latest, mainFeed, filterPosts]);
+ }, [latest]);
function eventElement(e: TaggedRawEvent) {
switch (e.kind) {
diff --git a/src/Feed/ProfileFeed.ts b/src/Feed/ProfileFeed.ts
index b439f814..a4c4028d 100644
--- a/src/Feed/ProfileFeed.ts
+++ b/src/Feed/ProfileFeed.ts
@@ -1,5 +1,5 @@
import { useLiveQuery } from "dexie-react-hooks";
-import { useEffect } from "react";
+import { useEffect, useMemo } from "react";
import { db } from "Db";
import { MetadataCache } from "Db/User";
import { HexKey } from "Nostr";
diff --git a/src/Feed/Subscription.ts b/src/Feed/Subscription.ts
index 08966653..052201e2 100644
--- a/src/Feed/Subscription.ts
+++ b/src/Feed/Subscription.ts
@@ -38,8 +38,7 @@ function notesReducer(state: NoteStore, arg: ReducerArg) {
if (!Array.isArray(evs)) {
evs = [evs];
}
- let existingIds = new Set(state.notes.map(a => a.id));
- evs = evs.filter(a => !existingIds.has(a.id));
+ evs = evs.filter(a => !state.notes.some(b => b.id === a.id));
if (evs.length === 0) {
return state;
}
@@ -84,7 +83,7 @@ export default function useSubscription(sub: Subscriptions | null, options?: Use
setSubDebounced(sub);
});
}
- }, [sub, options]);
+ }, [sub]);
useEffect(() => {
if (sub) {
@@ -116,7 +115,6 @@ export default function useSubscription(sub: Subscriptions | null, options?: Use
};
}
}, [subDebounce]);
-
useEffect(() => {
return debounce(DebounceMs, () => {
setDebounceOutput(s => s += 1);
diff --git a/src/Feed/ThreadFeed.ts b/src/Feed/ThreadFeed.ts
index aef2c024..81921197 100644
--- a/src/Feed/ThreadFeed.ts
+++ b/src/Feed/ThreadFeed.ts
@@ -36,7 +36,7 @@ export default function useThreadFeed(id: u256) {
thisSub.AddSubscription(subRelated);
return thisSub;
- }, [trackingEvents, pref, id]);
+ }, [trackingEvents]);
const main = useSubscription(sub, { leaveOpen: true });
diff --git a/src/Feed/TimelineFeed.ts b/src/Feed/TimelineFeed.ts
index fe259421..abfd05d1 100644
--- a/src/Feed/TimelineFeed.ts
+++ b/src/Feed/TimelineFeed.ts
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
import { u256 } from "Nostr";
import EventKind from "Nostr/EventKind";
import { Subscriptions } from "Nostr/Subscriptions";
@@ -19,15 +19,15 @@ export interface TimelineSubject {
export default function useTimelineFeed(subject: TimelineSubject, options: TimelineFeedOptions) {
const now = unixNow();
- const [window] = useState(60 * 60);
+ const [window, setWindow] = useState(60 * 60);
const [until, setUntil] = useState(now);
const [since, setSince] = useState(now - window);
const [trackingEvents, setTrackingEvent] = useState([]);
const [trackingParentEvents, setTrackingParentEvents] = useState([]);
const pref = useSelector(s => s.login.preferences);
- const createSub = useCallback(() => {
- if (subject.type !== "global" && subject.items.length === 0) {
+ function createSub() {
+ if (subject.type !== "global" && subject.items.length == 0) {
return null;
}
@@ -49,7 +49,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
}
}
return sub;
- }, [subject.type, subject.items]);
+ }
const sub = useMemo(() => {
let sub = createSub();
@@ -78,7 +78,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
}
}
return sub;
- }, [until, since, options.method, pref, createSub]);
+ }, [subject.type, subject.items, until, since, window]);
const main = useSubscription(sub, { leaveOpen: true });
@@ -90,7 +90,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
subLatest.Since = Math.floor(new Date().getTime() / 1000);
}
return subLatest;
- }, [pref, createSub]);
+ }, [subject.type, subject.items]);
const latest = useSubscription(subRealtime, { leaveOpen: true });
@@ -103,7 +103,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
sub.ETags = new Set(trackingEvents);
}
return sub ?? null;
- }, [trackingEvents, pref, subject.type]);
+ }, [trackingEvents]);
const others = useSubscription(subNext, { leaveOpen: true });
@@ -115,7 +115,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
return parents;
}
return null;
- }, [trackingParentEvents, subject.type]);
+ }, [trackingParentEvents]);
const parent = useSubscription(subParents);
@@ -123,10 +123,8 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
if (main.store.notes.length > 0) {
setTrackingEvent(s => {
let ids = main.store.notes.map(a => a.id);
- if(ids.some(a => !s.includes(a))) {
- return Array.from(new Set([...s, ...ids]));
- }
- return s;
+ let temp = new Set([...s, ...ids]);
+ return Array.from(temp);
});
let reposts = main.store.notes
.filter(a => a.kind === EventKind.Repost && a.content === "")
diff --git a/src/Nostr/Connection.ts b/src/Nostr/Connection.ts
index 092b49f0..1ddd14f8 100644
--- a/src/Nostr/Connection.ts
+++ b/src/Nostr/Connection.ts
@@ -6,7 +6,6 @@ import { default as NEvent } from "Nostr/Event";
import { DefaultConnectTimeout } from "Const";
import { ConnectionStats } from "Nostr/ConnectionStats";
import { RawEvent, TaggedRawEvent, u256 } from "Nostr";
-import { RelayInfo } from "./RelayInfo";
export type CustomHook = (state: Readonly) => void;
@@ -28,8 +27,7 @@ export type StateSnapshot = {
events: {
received: number,
send: number
- },
- info?: RelayInfo
+ }
};
export default class Connection {
@@ -38,7 +36,6 @@ export default class Connection {
Pending: Subscriptions[];
Subscriptions: Map;
Settings: RelaySettings;
- Info?: RelayInfo;
ConnectTimeout: number;
Stats: ConnectionStats;
StateHooks: Map;
@@ -59,7 +56,7 @@ export default class Connection {
this.Stats = new ConnectionStats();
this.StateHooks = new Map();
this.HasStateChange = true;
- this.CurrentState = {
+ this.CurrentState = {
connected: false,
disconnects: 0,
avgLatency: 0,
@@ -67,7 +64,7 @@ export default class Connection {
received: 0,
send: 0
}
- } as StateSnapshot;
+ };
this.LastState = Object.freeze({ ...this.CurrentState });
this.IsClosed = false;
this.ReconnectTimer = null;
@@ -75,29 +72,7 @@ export default class Connection {
this.Connect();
}
- async Connect() {
- try {
- if (this.Info === undefined) {
- let u = new URL(this.Address);
- let rsp = await fetch(`https://${u.host}`, {
- headers: {
- "accept": "application/nostr+json"
- }
- });
- if (rsp.ok) {
- let data = await rsp.json();
- for (let [k, v] of Object.entries(data)) {
- if (v === "unset" || v === "") {
- data[k] = undefined;
- }
- }
- this.Info = data;
- }
- }
- } catch (e) {
- console.warn("Could not load relay information", e);
- }
-
+ Connect() {
this.IsClosed = false;
this.Socket = new WebSocket(this.Address);
this.Socket.onopen = (e) => this.OnOpen(e);
@@ -284,7 +259,6 @@ export default class Connection {
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.Stats.Latency = this.Stats.Latency.slice(-20); // trim
this.HasStateChange = true;
this._NotifyState();
diff --git a/src/Nostr/ConnectionStats.ts b/src/Nostr/ConnectionStats.ts
index 844e9dd0..0de56615 100644
--- a/src/Nostr/ConnectionStats.ts
+++ b/src/Nostr/ConnectionStats.ts
@@ -1,3 +1,4 @@
+
/**
* Stats class for tracking metrics per connection
*/
diff --git a/src/Nostr/RelayInfo.ts b/src/Nostr/RelayInfo.ts
deleted file mode 100644
index 21d01cef..00000000
--- a/src/Nostr/RelayInfo.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export interface RelayInfo {
- name?: string,
- description?: string,
- pubkey?: string,
- contact?: string,
- supported_nips?: number[],
- software?: string,
- version?: string
-}
\ No newline at end of file
diff --git a/src/Pages/DonatePage.tsx b/src/Pages/DonatePage.tsx
index b5be933c..f422ede8 100644
--- a/src/Pages/DonatePage.tsx
+++ b/src/Pages/DonatePage.tsx
@@ -1,4 +1,3 @@
-import { ApiHost } from "Const";
import ProfilePreview from "Element/ProfilePreview";
import ZapButton from "Element/ZapButton";
import { HexKey } from "Nostr";
@@ -25,7 +24,7 @@ const DonatePage = () => {
const [splits, setSplits] = useState([]);
async function loadSplits() {
- let rsp = await fetch(`${ApiHost}/api/v1/revenue/splits`);
+ let rsp = await fetch("https://api.snort.social/api/v1/revenue/splits");
if(rsp.ok) {
setSplits(await rsp.json());
}
diff --git a/src/Pages/MessagesPage.tsx b/src/Pages/MessagesPage.tsx
index 5142f016..8a24f670 100644
--- a/src/Pages/MessagesPage.tsx
+++ b/src/Pages/MessagesPage.tsx
@@ -57,9 +57,8 @@ export default function MessagesPage() {
markAllRead()}>Mark All Read
{chats.sort((a, b) => {
- return a.pubkey === myPubKey ? -1 :
- b.pubkey === myPubKey ? 1 :
- b.newestMessage - a.newestMessage
+ if(b.pubkey === myPubKey) return 1
+ return b.newestMessage - a.newestMessage
}).map(person)}
>
)
diff --git a/src/Pages/NewUserPage.tsx b/src/Pages/NewUserPage.tsx
index f268856f..369fc78b 100644
--- a/src/Pages/NewUserPage.tsx
+++ b/src/Pages/NewUserPage.tsx
@@ -1,4 +1,4 @@
-import { ApiHost, RecommendedFollows } from "Const";
+import { RecommendedFollows } from "Const";
import AsyncButton from "Element/AsyncButton";
import FollowListBase from "Element/FollowListBase";
import ProfilePreview from "Element/ProfilePreview";
@@ -8,7 +8,7 @@ import { useSelector } from "react-redux";
import { RootState } from "State/Store";
import { bech32ToHex } from "Util";
-const TwitterFollowsApi = `${ApiHost}/api/v1/twitter/follows-for-nostr`;
+const TwitterFollowsApi = "https://api.snort.social/api/v1/twitter/follows-for-nostr";
export default function NewUserPage() {
const [twitterUsername, setTwitterUsername] = useState("");
@@ -24,7 +24,7 @@ export default function NewUserPage() {
const sortedTwitterFollows = useMemo(() => {
return follows.map(a => bech32ToHex(a))
.sort((a, b) => currentFollows.includes(a) ? 1 : -1);
- }, [follows, currentFollows]);
+ }, [follows]);
async function loadFollows() {
setFollows([]);
diff --git a/src/Pages/SettingsPage.tsx b/src/Pages/SettingsPage.tsx
index aa60cd87..aa9c89f5 100644
--- a/src/Pages/SettingsPage.tsx
+++ b/src/Pages/SettingsPage.tsx
@@ -3,7 +3,6 @@ import SettingsIndex from "Pages/settings/Index";
import Profile from "Pages/settings/Profile";
import Relay from "Pages/settings/Relays";
import Preferences from "Pages/settings/Preferences";
-import RelayInfo from "Pages/settings/RelayInfo";
export default function SettingsPage() {
const navigate = useNavigate();
@@ -27,11 +26,7 @@ export const SettingsRoutes: RouteObject[] = [
},
{
path: "relays",
- element: ,
- },
- {
- path: "relays/:addr",
- element:
+ element:
},
{
path: "preferences",
diff --git a/src/Pages/Verification.tsx b/src/Pages/Verification.tsx
index 683c9025..56204541 100644
--- a/src/Pages/Verification.tsx
+++ b/src/Pages/Verification.tsx
@@ -1,4 +1,3 @@
-import { ApiHost } from "Const";
import Nip5Service from "Element/Nip5Service";
import './Verification.css'
@@ -7,7 +6,7 @@ export default function VerificationPage() {
const services = [
{
name: "Snort",
- service: `${ApiHost}/api/v1/n5sp`,
+ service: "https://api.snort.social/api/v1/n5sp",
link: "https://snort.social/",
supportLink: "https://snort.social/help",
about: <>Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!>
diff --git a/src/Pages/settings/Profile.tsx b/src/Pages/settings/Profile.tsx
index 482340da..f8ab59e0 100644
--- a/src/Pages/settings/Profile.tsx
+++ b/src/Pages/settings/Profile.tsx
@@ -31,6 +31,7 @@ export default function ProfileSettings() {
const [about, setAbout] = useState();
const [website, setWebsite] = useState();
const [nip05, setNip05] = useState();
+ const [lud06, setLud06] = useState();
const [lud16, setLud16] = useState();
const avatarPicture = (picture?.length ?? 0) === 0 ? Nostrich : picture
@@ -44,6 +45,7 @@ export default function ProfileSettings() {
setAbout(user.about);
setWebsite(user.website);
setNip05(user.nip05);
+ setLud06(user.lud06);
setLud16(user.lud16);
}
}, [user]);
diff --git a/src/Pages/settings/RelayInfo.tsx b/src/Pages/settings/RelayInfo.tsx
deleted file mode 100644
index 2dd6aa7b..00000000
--- a/src/Pages/settings/RelayInfo.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import ProfilePreview from "Element/ProfilePreview";
-import useRelayState from "Feed/RelayState";
-import { System } from "Nostr/System";
-import { useDispatch } from "react-redux";
-import { useNavigate, useParams } from "react-router-dom";
-import { removeRelay } from "State/Login";
-import { parseId } from "Util";
-
-const RelayInfo = () => {
- const params = useParams();
- const navigate = useNavigate();
- const dispatch = useDispatch();
- const addr: string = `wss://${params.addr}`;
-
- const con = System.Sockets.get(addr) ?? System.Sockets.get(`${addr}/`);
- const stats = useRelayState(con?.Address ?? addr);
-
- return (
- <>
- navigate("/settings/relays")}>Relays
-
-
{stats?.info?.name ?? addr}
-
{stats?.info?.description}
-
- {stats?.info?.pubkey && (<>
-
Owner
-
- >)}
- {stats?.info?.software && (
-
Software
-
- {stats.info.software.startsWith("http") ?
{stats.info.software} : <>{stats.info.software}>}
-
{!stats.info.version?.startsWith("v") && "v"}{stats.info.version}
-
-
)}
- {stats?.info?.contact && (
)}
- {stats?.info?.supported_nips && (<>
-
Supports
-
- {stats.info.supported_nips.map(a => navigate(`https://github.com/nostr-protocol/nips/blob/master/${a.toString().padStart(2, "0")}.md`)}>NIP-{a.toString().padStart(2, "0")})}
-
- >)}
-
-
{
- dispatch(removeRelay(con!.Address));
- navigate("/settings/relays")
- }}>Remove
-
-
- >
- )
-}
-
-export default RelayInfo;
\ No newline at end of file
diff --git a/src/Pages/settings/Relays.tsx b/src/Pages/settings/Relays.tsx
index b5e2d241..3ca0fca4 100644
--- a/src/Pages/settings/Relays.tsx
+++ b/src/Pages/settings/Relays.tsx
@@ -52,7 +52,7 @@ const RelaySettingsPage = () => {
{Object.keys(relays || {}).map(a => )}
-
+
diff --git a/src/State/Login.ts b/src/State/Login.ts
index ca009db4..23c0a4b6 100644
--- a/src/State/Login.ts
+++ b/src/State/Login.ts
@@ -1,8 +1,9 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import * as secp from '@noble/secp256k1';
import { DefaultRelays } from 'Const';
-import { HexKey, TaggedRawEvent } from 'Nostr';
+import { HexKey, RawEvent, TaggedRawEvent } from 'Nostr';
import { RelaySettings } from 'Nostr/Connection';
+import { useDispatch } from 'react-redux';
const PrivateKeyItem = "secret";
const PublicKeyItem = "pubkey";
@@ -181,7 +182,7 @@ const LoginSlice = createSlice({
let filtered = new Map
();
for (let [k, v] of Object.entries(relays)) {
if (k.startsWith("wss://") || k.startsWith("ws://")) {
- filtered.set(k, v as RelaySettings);
+ filtered.set(k, v);
}
}
diff --git a/src/Util.ts b/src/Util.ts
index abd73dde..2e68e50b 100644
--- a/src/Util.ts
+++ b/src/Util.ts
@@ -1,6 +1,6 @@
import * as secp from "@noble/secp256k1";
import { bech32 } from "bech32";
-import { HexKey, TaggedRawEvent, u256 } from "Nostr";
+import { HexKey, RawEvent, TaggedRawEvent, u256 } from "Nostr";
import EventKind from "Nostr/EventKind";
export async function openFile(): Promise {
@@ -65,7 +65,7 @@ export function eventLink(hex: u256) {
* @param {string} hex
*/
export function hexToBech32(hrp: string, hex: string) {
- if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 !== 0) {
+ if (typeof hex !== "string" || hex.length === 0 || hex.length % 2 != 0) {
return "";
}
diff --git a/src/index.css b/src/index.css
index 8556f0db..38e9ae7a 100644
--- a/src/index.css
+++ b/src/index.css
@@ -222,18 +222,18 @@ textarea:placeholder {
align-items: flex-start !important;
}
-.f-end {
- justify-content: flex-end;
-}
-
.w-max {
width: 100%;
- width: stretch;
+ width: -moz-available;
+ width: -webkit-fill-available;
+ width: fill-available;
}
.w-max-w {
max-width: 100%;
- max-width: stretch;
+ max-width: -moz-available;
+ max-width: -webkit-fill-available;
+ max-width: fill-available;
}
a {
@@ -263,7 +263,7 @@ div.form-group>div:nth-child(1) {
div.form-group>div:nth-child(2) {
display: flex;
flex-grow: 1;
- justify-content: flex-end;
+ justify-content: end;
}
div.form-group>div:nth-child(2) input {