ev.stopPropagation()}>
{!isDefaultUser && (
{name}
diff --git a/src/Element/Nip5Service.tsx b/src/Element/Nip5Service.tsx
index d1b32a1f..82765c92 100644
--- a/src/Element/Nip5Service.tsx
+++ b/src/Element/Nip5Service.tsx
@@ -15,7 +15,7 @@ import LNURLTip from "Element/LNURLTip";
import Copy from "Element/Copy";
import useProfile from "Feed/ProfileFeed";
import useEventPublisher from "Feed/EventPublisher";
-import { debounce, hexToBech32 } from "Util";
+import { hexToBech32 } from "Util";
import { UserMetadata } from "Nostr";
type Nip05ServiceProps = {
@@ -33,7 +33,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
const pubkey = useSelector
(s => s.login.publicKey);
const user = useProfile(pubkey);
const publisher = useEventPublisher();
- const svc = useMemo(() => new ServiceProvider(props.service), [props.service]);
+ const svc = new ServiceProvider(props.service);
const [serviceConfig, setServiceConfig] = useState();
const [error, setError] = useState();
const [handle, setHandle] = useState("");
@@ -43,7 +43,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
const [showInvoice, setShowInvoice] = useState(false);
const [registerStatus, setRegisterStatus] = useState();
- 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);
@@ -77,7 +77,7 @@ export default function Nip5Service(props: Nip05ServiceProps) {
setAvailabilityResponse({ available: false, why: "REGEX" });
return;
}
- return debounce(500, () => {
+ let t = setTimeout(() => {
svc.CheckAvailable(handle, domain)
.then(a => {
if ('error' in a) {
@@ -87,9 +87,10 @@ export default function Nip5Service(props: Nip05ServiceProps) {
}
})
.catch(console.error);
- });
+ }, 500);
+ return () => clearTimeout(t);
}
- }, [handle, domain, domainConfig, svc]);
+ }, [handle, domain]);
useEffect(() => {
if (registerResponse && showInvoice) {
@@ -111,7 +112,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 +160,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)} />
@
+ {showExtra ?
+
+ {state?.events.send}
+
+
+ {state?.events.received}
+
+
+
+ Delete
+ dispatch(removeRelay(props.addr))}>
+
+
+
+
: null}
>
)
}
diff --git a/src/Element/Timeline.tsx b/src/Element/Timeline.tsx
index c837c399..075d8a87 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";
@@ -19,21 +19,21 @@ export interface TimelineProps {
* A list of notes by pubkeys
*/
export default function Timeline({ subject, postsOnly = false, method }: TimelineProps) {
- const { main, related, latest, parent, loadMore, showLatest } = useTimelineFeed(subject, {
+ const { main, related, latest, loadMore, showLatest } = useTimelineFeed(subject, {
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) {
@@ -42,8 +42,7 @@ export default function Timeline({ subject, postsOnly = false, method }: Timelin
}
case EventKind.Reaction:
case EventKind.Repost: {
- let eRef = e.tags.find(a => a[0] === "e")?.at(1);
- return
a.id === eRef)}/>
+ return
}
}
}
diff --git a/src/Element/ZapButton.css b/src/Element/ZapButton.css
index 863b095c..8f8641de 100644
--- a/src/Element/ZapButton.css
+++ b/src/Element/ZapButton.css
@@ -3,5 +3,4 @@
background-color: var(--highlight);
padding: 4px 8px;
border-radius: 16px;
- cursor: pointer;
}
diff --git a/src/Element/ZapButton.tsx b/src/Element/ZapButton.tsx
index 147b1871..82416d00 100644
--- a/src/Element/ZapButton.tsx
+++ b/src/Element/ZapButton.tsx
@@ -6,19 +6,19 @@ import useProfile from "Feed/ProfileFeed";
import { HexKey } from "Nostr";
import LNURLTip from "Element/LNURLTip";
-const ZapButton = ({ pubkey, svc }: { pubkey?: HexKey, svc?: string }) => {
- const profile = useProfile(pubkey)?.get(pubkey ?? "");
+const ZapButton = ({ pubkey }: { pubkey: HexKey }) => {
+ const profile = useProfile(pubkey)?.get(pubkey);
const [zap, setZap] = useState(false);
- const service = svc ?? (profile?.lud16 || profile?.lud06);
+ const svc = profile?.lud16 || profile?.lud06;
- if (!service) return null;
+ if (!svc) return null;
return (
<>
setZap(true)}>
- setZap(false)} />
+ setZap(false)} />
>
)
}
diff --git a/src/Feed/ProfileFeed.ts b/src/Feed/ProfileFeed.ts
index b439f814..435a5928 100644
--- a/src/Feed/ProfileFeed.ts
+++ b/src/Feed/ProfileFeed.ts
@@ -1,11 +1,11 @@
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";
import { System } from "Nostr/System";
-export default function useProfile(pubKey?: HexKey | Array | undefined): Map | undefined {
+export default function useProfile(pubKey: HexKey | Array | undefined): Map | undefined {
const user = useLiveQuery(async () => {
let userList = new Map();
if (pubKey) {
diff --git a/src/Feed/Subscription.ts b/src/Feed/Subscription.ts
index 08966653..76370252 100644
--- a/src/Feed/Subscription.ts
+++ b/src/Feed/Subscription.ts
@@ -2,7 +2,6 @@ import { useEffect, useMemo, useReducer, useState } from "react";
import { System } from "Nostr/System";
import { TaggedRawEvent } from "Nostr";
import { Subscriptions } from "Nostr/Subscriptions";
-import { debounce } from "Util";
export type NoteStore = {
notes: Array,
@@ -38,8 +37,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;
}
@@ -62,11 +60,6 @@ export interface UseSubscriptionState {
append: (notes: TaggedRawEvent[]) => void
}
-/**
- * Wait time before returning changed state
- */
-const DebounceMs = 200;
-
/**
*
* @param {Subscriptions} sub
@@ -75,16 +68,7 @@ const DebounceMs = 200;
*/
export default function useSubscription(sub: Subscriptions | null, options?: UseSubscriptionOptions): UseSubscriptionState {
const [state, dispatch] = useReducer(notesReducer, initStore);
- const [debounceOutput, setDebounceOutput] = useState(0);
- const [subDebounce, setSubDebounced] = useState();
-
- useEffect(() => {
- if (sub) {
- return debounce(DebounceMs, () => {
- setSubDebounced(sub);
- });
- }
- }, [sub, options]);
+ const [debounce, setDebounce] = useState(0);
useEffect(() => {
if (sub) {
@@ -115,15 +99,16 @@ export default function useSubscription(sub: Subscriptions | null, options?: Use
System.RemoveSubscription(sub.Id);
};
}
- }, [subDebounce]);
+ }, [sub]);
useEffect(() => {
- return debounce(DebounceMs, () => {
- setDebounceOutput(s => s += 1);
- });
+ let t = setTimeout(() => {
+ setDebounce(s => s += 1);
+ }, 100);
+ return () => clearTimeout(t);
}, [state]);
- const stateDebounced = useMemo(() => state, [debounceOutput]);
+ const stateDebounced = useMemo(() => state, [debounce]);
return {
store: stateDebounced,
clear: () => {
diff --git a/src/Feed/ThreadFeed.ts b/src/Feed/ThreadFeed.ts
index aef2c024..e6563adf 100644
--- a/src/Feed/ThreadFeed.ts
+++ b/src/Feed/ThreadFeed.ts
@@ -6,7 +6,6 @@ import useSubscription from "Feed/Subscription";
import { useSelector } from "react-redux";
import { RootState } from "State/Store";
import { UserPreferences } from "State/Login";
-import { debounce } from "Util";
export default function useThreadFeed(id: u256) {
const [trackingEvents, setTrackingEvent] = useState([id]);
@@ -15,8 +14,9 @@ export default function useThreadFeed(id: u256) {
function addId(id: u256[]) {
setTrackingEvent((s) => {
let orig = new Set(s);
- if (id.some(a => !orig.has(a))) {
- let tmp = new Set([...s, ...id]);
+ let idsMissing = id.filter(a => !orig.has(a));
+ if (idsMissing.length > 0) {
+ let tmp = new Set([...s, ...idsMissing]);
return Array.from(tmp);
} else {
return s;
@@ -36,23 +36,19 @@ export default function useThreadFeed(id: u256) {
thisSub.AddSubscription(subRelated);
return thisSub;
- }, [trackingEvents, pref, id]);
+ }, [trackingEvents]);
const main = useSubscription(sub, { leaveOpen: true });
useEffect(() => {
- if (main.store) {
- return debounce(200, () => {
- let mainNotes = main.store.notes.filter(a => a.kind === EventKind.TextNote);
-
- let eTags = mainNotes
- .filter(a => a.kind === EventKind.TextNote)
- .map(a => a.tags.filter(b => b[0] === "e").map(b => b[1])).flat();
- let ids = mainNotes.map(a => a.id);
- let allEvents = new Set([...eTags, ...ids]);
- addId(Array.from(allEvents));
- })
- }
+ // debounce
+ let t = setTimeout(() => {
+ let eTags = main.store.notes.map(a => a.tags.filter(b => b[0] === "e").map(b => b[1])).flat();
+ let ids = main.store.notes.map(a => a.id);
+ let allEvents = new Set([...eTags, ...ids]);
+ addId(Array.from(allEvents));
+ }, 200);
+ return () => clearTimeout(t);
}, [main.store]);
return main.store;
diff --git a/src/Feed/TimelineFeed.ts b/src/Feed/TimelineFeed.ts
index fe259421..d71a72b2 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,14 @@ 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 +48,7 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
}
}
return sub;
- }, [subject.type, subject.items]);
+ }
const sub = useMemo(() => {
let sub = createSub();
@@ -78,7 +77,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,58 +89,30 @@ 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 });
const subNext = useMemo(() => {
- let sub: Subscriptions | undefined;
if (trackingEvents.length > 0 && pref.enableReactions) {
- sub = new Subscriptions();
+ let sub = new Subscriptions();
sub.Id = `timeline-related:${subject.type}`;
- sub.Kinds = new Set([EventKind.Reaction, EventKind.Deletion]);
+ sub.Kinds = new Set([EventKind.Reaction, EventKind.Deletion, EventKind.Repost]);
sub.ETags = new Set(trackingEvents);
- }
- return sub ?? null;
- }, [trackingEvents, pref, subject.type]);
-
- const others = useSubscription(subNext, { leaveOpen: true });
-
- const subParents = useMemo(() => {
- if (trackingParentEvents.length > 0) {
- let parents = new Subscriptions();
- parents.Id = `timeline-parent:${subject.type}`;
- parents.Ids = new Set(trackingParentEvents);
- return parents;
+ return sub;
}
return null;
- }, [trackingParentEvents, subject.type]);
+ }, [trackingEvents]);
- const parent = useSubscription(subParents);
+ const others = useSubscription(subNext, { leaveOpen: true });
useEffect(() => {
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 === "")
- .map(a => a.tags.find(b => b[0] === "e"))
- .filter(a => a)
- .map(a => a![1]);
- if (reposts.length > 0) {
- setTrackingParentEvents(s => {
- if (reposts.some(a => !s.includes(a))) {
- let temp = new Set([...s, ...reposts]);
- return Array.from(temp);
- }
- return s;
- })
- }
}
}, [main.store]);
@@ -149,7 +120,6 @@ export default function useTimelineFeed(subject: TimelineSubject, options: Timel
main: main.store,
related: others.store,
latest: latest.store,
- parent: parent.store,
loadMore: () => {
console.debug("Timeline load more!")
if (options.method === "LIMIT_UNTIL") {
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..9b859c51 100644
--- a/src/Pages/DonatePage.tsx
+++ b/src/Pages/DonatePage.tsx
@@ -1,6 +1,4 @@
-import { ApiHost } from "Const";
import ProfilePreview from "Element/ProfilePreview";
-import ZapButton from "Element/ZapButton";
import { HexKey } from "Nostr";
import { useEffect, useState } from "react";
import { bech32ToHex } from "Util";
@@ -25,7 +23,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());
}
@@ -58,10 +56,6 @@ const DonatePage = () => {
Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below
-
-
Lightning Donation:
-
-
Primary Developers
{Developers.map(a => )}
Contributors
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