diff --git a/src/element/Note.js b/src/element/Note.js
index 25d5b032..4ce18d3e 100644
--- a/src/element/Note.js
+++ b/src/element/Note.js
@@ -39,16 +39,22 @@ export default function Note(props) {
return null;
}
- let replyId = thread.ReplyTo.Event;
+ let replyId = thread?.ReplyTo?.Event;
return (
goToEvent(e, replyId)}>
- ➡️ {replyId.substring(0, 8)}
+ ➡️ {replyId?.substring(0, 8)}
)
}
- if(!ev.IsContent()) {
- return Event: {ev.Id}
;
+ if (!ev.IsContent()) {
+ return (
+ <>
+ {ev.Id}
+ Kind: {ev.Kind}
+ Content: {ev.Content}
+ >
+ );
}
return (
diff --git a/src/nostr/Connection.js b/src/nostr/Connection.js
index 1a7b360a..ab1056af 100644
--- a/src/nostr/Connection.js
+++ b/src/nostr/Connection.js
@@ -31,7 +31,7 @@ export default class Connection {
break;
}
case "EOSE": {
- // ignored for now
+ this._OnEnd(msg[1]);
break;
}
default: {
@@ -90,7 +90,15 @@ export default class Connection {
if (this.Subscriptions[subId]) {
this.Subscriptions[subId].OnEvent(ev);
} else {
- console.warn("No subscription for event!");
+ console.warn(`No subscription for event! ${subId}`);
+ }
+ }
+
+ _OnEnd(subId) {
+ if (this.Subscriptions[subId]) {
+ this.Subscriptions[subId].OnEnd(this);
+ } else {
+ console.warn(`No subscription for end! ${subId}`);
}
}
}
\ No newline at end of file
diff --git a/src/nostr/Event.js b/src/nostr/Event.js
index 3442692c..69030dba 100644
--- a/src/nostr/Event.js
+++ b/src/nostr/Event.js
@@ -75,7 +75,7 @@ export default class Event {
this.PubKey,
this.CreatedAt,
this.Kind,
- this.Tags.map(a => a.ToObject()),
+ this.Tags.map(a => a.ToObject()).filter(a => a !== null),
this.Content
];
@@ -130,7 +130,7 @@ export default class Event {
pubkey: this.PubKey,
created_at: this.CreatedAt,
kind: this.Kind,
- tags: this.Tags.map(a => a.ToObject()),
+ tags: this.Tags.map(a => a.ToObject()).filter(a => a !== null),
content: this.Content,
sig: this.Signature
};
diff --git a/src/nostr/Subscriptions.js b/src/nostr/Subscriptions.js
index ee83a6cc..6d36f047 100644
--- a/src/nostr/Subscriptions.js
+++ b/src/nostr/Subscriptions.js
@@ -1,4 +1,5 @@
import { v4 as uuid } from "uuid";
+import Connection from "./Connection";
export class Subscriptions {
constructor() {
@@ -52,6 +53,12 @@ export class Subscriptions {
*/
this.OnEvent = (e) => { console.warn(`No event handler was set on subscription: ${this.Id}`) };
+ /**
+ * End of data event
+ * @param {Connection} c
+ */
+ this.OnEnd = (c) => {};
+
/**
* Collection of OR sub scriptions linked to this
*/
diff --git a/src/nostr/Tag.js b/src/nostr/Tag.js
index 9a026f79..85cba679 100644
--- a/src/nostr/Tag.js
+++ b/src/nostr/Tag.js
@@ -5,6 +5,7 @@ export default class Tag {
this.PubKey = null;
this.Relay = null;
this.Marker = null;
+ this.Other = null;
switch (this.Key) {
case "e": {
@@ -19,17 +20,24 @@ export default class Tag {
this.PubKey = tag[1];
break;
}
+ default: {
+ this.Other = tag;
+ break;
+ }
}
}
ToObject() {
- switch(this.Key) {
+ switch (this.Key) {
case "e": {
return ["e", this.Event, this.Relay, this.Marker].filter(a => a !== null);
- }
+ }
case "p": {
return ["p", this.PubKey];
}
+ default: {
+ return this.Other;
+ }
}
return null;
}
diff --git a/src/pages/EventPage.js b/src/pages/EventPage.js
index 6769f3b2..da200210 100644
--- a/src/pages/EventPage.js
+++ b/src/pages/EventPage.js
@@ -7,13 +7,12 @@ export default function EventPage() {
const params = useParams();
const id = params.id;
- const { note, notes } = useThreadFeed(id);
+ const { notes } = useThreadFeed(id);
- if(note) {
+ if(notes) {
return (
<>
{notes?.map(n => )}
-
>
)
}
diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js
index 655a1ba0..601bb873 100644
--- a/src/pages/ProfilePage.js
+++ b/src/pages/ProfilePage.js
@@ -1,15 +1,17 @@
import { useSelector } from "react-redux";
import { useParams } from "react-router-dom";
+import useProfileFeed from "./feed/ProfileFeed";
export default function ProfilePage() {
const params = useParams();
const id = params.id;
-
+ useProfileFeed(id);
+
const user = useSelector(s => s.users.users[id]);
return (
-
+
)
}
\ No newline at end of file
diff --git a/src/pages/feed/ProfileFeed.js b/src/pages/feed/ProfileFeed.js
index aa4b8052..acc6d925 100644
--- a/src/pages/feed/ProfileFeed.js
+++ b/src/pages/feed/ProfileFeed.js
@@ -1,3 +1,13 @@
-export default function useProfileFeed(id) {
+import { useContext, useEffect } from "react";
+import { useDispatch } from "react-redux";
+import { NostrContext } from "../..";
+import { addPubKey } from "../../state/Users";
+export default function useProfileFeed(id) {
+ const dispatch = useDispatch();
+ const system = useContext(NostrContext);
+
+ useEffect(() => {
+ dispatch(addPubKey(id));
+ }, []);
}
\ No newline at end of file
diff --git a/src/pages/feed/ThreadFeed.js b/src/pages/feed/ThreadFeed.js
index 76c73c72..ead4eda5 100644
--- a/src/pages/feed/ThreadFeed.js
+++ b/src/pages/feed/ThreadFeed.js
@@ -1,62 +1,70 @@
import { useContext, useEffect, useState } from "react";
-import { useDispatch } from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
import { NostrContext } from "../..";
+import Event from "../../nostr/Event";
import { Subscriptions } from "../../nostr/Subscriptions";
+import { addNote, reset } from "../../state/Thread";
import { addPubKey } from "../../state/Users";
export default function useThreadFeed(id) {
const dispatch = useDispatch();
const system = useContext(NostrContext);
- const [note, setNote] = useState(null);
- const [notes, setNotes] = useState([]);
- const [relatedEvents, setRelatedEvents] = useState([]);
+ const notes = useSelector(s => s.thread.notes);
+ // track profiles
useEffect(() => {
- if (note) {
- let eFetch = [];
- dispatch(addPubKey(note.pubkey));
- for (let t of note.tags) {
- if (t[0] === "p") {
+ for (let n of notes) {
+ if (n.pubkey) {
+ dispatch(addPubKey(n.pubkey));
+ }
+ for(let t of n.tags) {
+ if(t[0] === "p" && t[1]) {
dispatch(addPubKey(t[1]));
- } else if (t[0] === "e") {
- eFetch.push(t[1]);
}
}
- if(eFetch.length > 0) {
- setRelatedEvents(eFetch);
- }
}
- }, [note]);
+ }, [notes]);
useEffect(() => {
if (system) {
let sub = new Subscriptions();
- sub.Ids.add(id);
-
- sub.OnEvent = (e) => {
- if(e.id === id && !note) {
- setNote(e);
+ if (notes.length === 1) {
+ let thisNote = Event.FromObject(notes[0]);
+ let thread = thisNote.GetThread();
+ if (thread !== null) {
+ if (thread.ReplyTo) {
+ sub.Ids.add(thread.ReplyTo.Event);
+ }
+ if (thread.Root) {
+ sub.Ids.add(thread.Root.Event);
+ }
+ for (let m of thread.Mentions) {
+ sub.Ids.add(m.Event);
+ }
}
+ } else if (notes.length === 0) {
+ sub.Ids.add(id);
+
+ // get replies to this event
+ let subRelated = new Subscriptions();
+ subRelated.ETags.add(id);
+ sub.AddSubscription(subRelated);
+ } else {
+ return;
+ }
+ sub.OnEvent = (e) => {
+ dispatch(addNote(e));
+ };
+ sub.OnEnd = (c) => {
+ c.RemoveSubscription(sub.Id);
};
system.AddSubscription(sub);
- return () => system.RemoveSubscription(sub.Id);
}
- }, [system]);
+ }, [system, notes]);
useEffect(() => {
- if(system && relatedEvents.length > 0) {
- let sub = new Subscriptions();
- sub.ETags = new Set(relatedEvents);
- sub.OnEvent = (e) => {
- let temp = new Set(notes);
- temp.add(e);
- setNotes(Array.from(temp));
- };
- system.AddSubscription(sub);
- return () => system.RemoveSubscription(sub.Id);
- }
- }, [system, relatedEvents])
-
- return { note, notes };
+ dispatch(reset());
+ }, []);
+ return { notes };
}
\ No newline at end of file
diff --git a/src/pages/feed/UsersFeed.js b/src/pages/feed/UsersFeed.js
index 96a797e9..037a36e2 100644
--- a/src/pages/feed/UsersFeed.js
+++ b/src/pages/feed/UsersFeed.js
@@ -11,8 +11,6 @@ export default function useUsersStore() {
const system = useContext(NostrContext);
const pKeys = useSelector(s => s.users.pubKeys);
-
-
useEffect(() => {
if (pKeys.length > 0) {
const sub = new Subscriptions();
@@ -34,5 +32,4 @@ export default function useUsersStore() {
}
}
}, [pKeys]);
-
}
\ No newline at end of file
diff --git a/src/state/Login.js b/src/state/Login.js
index 576c51bc..30cec609 100644
--- a/src/state/Login.js
+++ b/src/state/Login.js
@@ -8,7 +8,8 @@ const DefaultRelays = JSON.stringify([
"wss://nostr.zebedee.cloud",
"wss://relay.damus.io",
"wss://nostr.rocks",
- "wss://nostr.rocks"
+ "wss://nostr.rocks",
+ "wss://nostr.fmt.wiz.biz"
]);
const LoginSlice = createSlice({
diff --git a/src/state/Store.js b/src/state/Store.js
index 421c0a41..9046a36e 100644
--- a/src/state/Store.js
+++ b/src/state/Store.js
@@ -2,12 +2,14 @@ import { configureStore } from "@reduxjs/toolkit";
import { reducer as TimelineReducer } from "./Timeline";
import { reducer as UsersReducer } from "./Users";
import { reducer as LoginReducer } from "./Login";
+import { reducer as ThreadReducer } from "./Thread";
const Store = configureStore({
reducer: {
timeline: TimelineReducer,
users: UsersReducer,
- login: LoginReducer
+ login: LoginReducer,
+ thread: ThreadReducer
}
});
diff --git a/src/state/Thread.js b/src/state/Thread.js
new file mode 100644
index 00000000..2f6504b9
--- /dev/null
+++ b/src/state/Thread.js
@@ -0,0 +1,26 @@
+import { createSlice } from '@reduxjs/toolkit'
+
+const ThreadSlice = createSlice({
+ name: "Thread",
+ initialState: {
+ notes: [],
+ },
+ reducers: {
+ setNotes: (state, action) => {
+ state.notes = action.payload;
+ },
+ addNote: (state, action) => {
+ if (!state.notes.some(n => n.id === action.payload.id)) {
+ let tmp = new Set(state.notes);
+ tmp.add(action.payload);
+ state.notes = Array.from(tmp);
+ }
+ },
+ reset: (state) => {
+ state.notes = [];
+ }
+ }
+});
+
+export const { setNotes, addNote, reset } = ThreadSlice.actions;
+export const reducer = ThreadSlice.reducer;
\ No newline at end of file