Threads progress

This commit is contained in:
Kieran 2022-12-18 22:23:52 +00:00
parent e6ef1a5bc9
commit e617d6d528
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
13 changed files with 131 additions and 57 deletions

View File

@ -39,16 +39,22 @@ export default function Note(props) {
return null; return null;
} }
let replyId = thread.ReplyTo.Event; let replyId = thread?.ReplyTo?.Event;
return ( return (
<div className="reply" onClick={(e) => goToEvent(e, replyId)}> <div className="reply" onClick={(e) => goToEvent(e, replyId)}>
{replyId.substring(0, 8)} {replyId?.substring(0, 8)}
</div> </div>
) )
} }
if (!ev.IsContent()) { if (!ev.IsContent()) {
return <pre>Event: {ev.Id}</pre>; return (
<>
<pre>{ev.Id}</pre>
<pre>Kind: {ev.Kind}</pre>
<pre>Content: {ev.Content}</pre>
</>
);
} }
return ( return (

View File

@ -31,7 +31,7 @@ export default class Connection {
break; break;
} }
case "EOSE": { case "EOSE": {
// ignored for now this._OnEnd(msg[1]);
break; break;
} }
default: { default: {
@ -90,7 +90,15 @@ export default class Connection {
if (this.Subscriptions[subId]) { if (this.Subscriptions[subId]) {
this.Subscriptions[subId].OnEvent(ev); this.Subscriptions[subId].OnEvent(ev);
} else { } 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}`);
} }
} }
} }

View File

@ -75,7 +75,7 @@ export default class Event {
this.PubKey, this.PubKey,
this.CreatedAt, this.CreatedAt,
this.Kind, this.Kind,
this.Tags.map(a => a.ToObject()), this.Tags.map(a => a.ToObject()).filter(a => a !== null),
this.Content this.Content
]; ];
@ -130,7 +130,7 @@ export default class Event {
pubkey: this.PubKey, pubkey: this.PubKey,
created_at: this.CreatedAt, created_at: this.CreatedAt,
kind: this.Kind, kind: this.Kind,
tags: this.Tags.map(a => a.ToObject()), tags: this.Tags.map(a => a.ToObject()).filter(a => a !== null),
content: this.Content, content: this.Content,
sig: this.Signature sig: this.Signature
}; };

View File

@ -1,4 +1,5 @@
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
import Connection from "./Connection";
export class Subscriptions { export class Subscriptions {
constructor() { constructor() {
@ -52,6 +53,12 @@ export class Subscriptions {
*/ */
this.OnEvent = (e) => { console.warn(`No event handler was set on subscription: ${this.Id}`) }; 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 * Collection of OR sub scriptions linked to this
*/ */

View File

@ -5,6 +5,7 @@ export default class Tag {
this.PubKey = null; this.PubKey = null;
this.Relay = null; this.Relay = null;
this.Marker = null; this.Marker = null;
this.Other = null;
switch (this.Key) { switch (this.Key) {
case "e": { case "e": {
@ -19,6 +20,10 @@ export default class Tag {
this.PubKey = tag[1]; this.PubKey = tag[1];
break; break;
} }
default: {
this.Other = tag;
break;
}
} }
} }
@ -30,6 +35,9 @@ export default class Tag {
case "p": { case "p": {
return ["p", this.PubKey]; return ["p", this.PubKey];
} }
default: {
return this.Other;
}
} }
return null; return null;
} }

View File

@ -7,13 +7,12 @@ export default function EventPage() {
const params = useParams(); const params = useParams();
const id = params.id; const id = params.id;
const { note, notes } = useThreadFeed(id); const { notes } = useThreadFeed(id);
if(note) { if(notes) {
return ( return (
<> <>
{notes?.map(n => <Note key={n.id} data={n}/>)} {notes?.map(n => <Note key={n.id} data={n}/>)}
<Note data={note}/>
</> </>
) )
} }

View File

@ -1,15 +1,17 @@
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import useProfileFeed from "./feed/ProfileFeed";
export default function ProfilePage() { export default function ProfilePage() {
const params = useParams(); const params = useParams();
const id = params.id; const id = params.id;
useProfileFeed(id);
const user = useSelector(s => s.users.users[id]); const user = useSelector(s => s.users.users[id]);
return ( return (
<div className="profile"> <div className="profile">
<img src={} /> <img src={user?.picture} />
</div> </div>
) )
} }

View File

@ -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));
}, []);
} }

View File

@ -1,62 +1,70 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { useDispatch } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { NostrContext } from "../.."; import { NostrContext } from "../..";
import Event from "../../nostr/Event";
import { Subscriptions } from "../../nostr/Subscriptions"; import { Subscriptions } from "../../nostr/Subscriptions";
import { addNote, reset } from "../../state/Thread";
import { addPubKey } from "../../state/Users"; import { addPubKey } from "../../state/Users";
export default function useThreadFeed(id) { export default function useThreadFeed(id) {
const dispatch = useDispatch(); const dispatch = useDispatch();
const system = useContext(NostrContext); const system = useContext(NostrContext);
const [note, setNote] = useState(null); const notes = useSelector(s => s.thread.notes);
const [notes, setNotes] = useState([]);
const [relatedEvents, setRelatedEvents] = useState([]);
// track profiles
useEffect(() => { useEffect(() => {
if (note) { for (let n of notes) {
let eFetch = []; if (n.pubkey) {
dispatch(addPubKey(note.pubkey)); dispatch(addPubKey(n.pubkey));
for (let t of note.tags) { }
if (t[0] === "p") { for(let t of n.tags) {
if(t[0] === "p" && t[1]) {
dispatch(addPubKey(t[1])); dispatch(addPubKey(t[1]));
} else if (t[0] === "e") {
eFetch.push(t[1]);
} }
} }
if(eFetch.length > 0) {
setRelatedEvents(eFetch);
} }
} }, [notes]);
}, [note]);
useEffect(() => { useEffect(() => {
if (system) { if (system) {
let sub = new Subscriptions(); let sub = new Subscriptions();
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); sub.Ids.add(id);
sub.OnEvent = (e) => { // get replies to this event
if(e.id === id && !note) { let subRelated = new Subscriptions();
setNote(e); 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); system.AddSubscription(sub);
return () => system.RemoveSubscription(sub.Id);
} }
}, [system]); }, [system, notes]);
useEffect(() => { useEffect(() => {
if(system && relatedEvents.length > 0) { dispatch(reset());
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 };
return { notes };
} }

View File

@ -11,8 +11,6 @@ export default function useUsersStore() {
const system = useContext(NostrContext); const system = useContext(NostrContext);
const pKeys = useSelector(s => s.users.pubKeys); const pKeys = useSelector(s => s.users.pubKeys);
useEffect(() => { useEffect(() => {
if (pKeys.length > 0) { if (pKeys.length > 0) {
const sub = new Subscriptions(); const sub = new Subscriptions();
@ -34,5 +32,4 @@ export default function useUsersStore() {
} }
} }
}, [pKeys]); }, [pKeys]);
} }

View File

@ -8,7 +8,8 @@ const DefaultRelays = JSON.stringify([
"wss://nostr.zebedee.cloud", "wss://nostr.zebedee.cloud",
"wss://relay.damus.io", "wss://relay.damus.io",
"wss://nostr.rocks", "wss://nostr.rocks",
"wss://nostr.rocks" "wss://nostr.rocks",
"wss://nostr.fmt.wiz.biz"
]); ]);
const LoginSlice = createSlice({ const LoginSlice = createSlice({

View File

@ -2,12 +2,14 @@ import { configureStore } from "@reduxjs/toolkit";
import { reducer as TimelineReducer } from "./Timeline"; import { reducer as TimelineReducer } from "./Timeline";
import { reducer as UsersReducer } from "./Users"; import { reducer as UsersReducer } from "./Users";
import { reducer as LoginReducer } from "./Login"; import { reducer as LoginReducer } from "./Login";
import { reducer as ThreadReducer } from "./Thread";
const Store = configureStore({ const Store = configureStore({
reducer: { reducer: {
timeline: TimelineReducer, timeline: TimelineReducer,
users: UsersReducer, users: UsersReducer,
login: LoginReducer login: LoginReducer,
thread: ThreadReducer
} }
}); });

26
src/state/Thread.js Normal file
View File

@ -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;