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;
}
let replyId = thread.ReplyTo.Event;
let replyId = thread?.ReplyTo?.Event;
return (
<div className="reply" onClick={(e) => goToEvent(e, replyId)}>
{replyId.substring(0, 8)}
{replyId?.substring(0, 8)}
</div>
)
}
if(!ev.IsContent()) {
return <pre>Event: {ev.Id}</pre>;
if (!ev.IsContent()) {
return (
<>
<pre>{ev.Id}</pre>
<pre>Kind: {ev.Kind}</pre>
<pre>Content: {ev.Content}</pre>
</>
);
}
return (

View File

@ -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}`);
}
}
}

View File

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

View File

@ -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
*/

View File

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

View File

@ -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 => <Note key={n.id} data={n}/>)}
<Note data={note}/>
</>
)
}

View File

@ -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 (
<div className="profile">
<img src={} />
<img src={user?.picture} />
</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 { 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 };
}

View File

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

View File

@ -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({

View File

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

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;