Threads progress
This commit is contained in:
parent
e6ef1a5bc9
commit
e617d6d528
@ -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 (
|
||||
|
@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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}/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
@ -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));
|
||||
}, []);
|
||||
}
|
@ -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 };
|
||||
}
|
@ -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]);
|
||||
|
||||
}
|
@ -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({
|
||||
|
@ -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
26
src/state/Thread.js
Normal 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;
|
Loading…
x
Reference in New Issue
Block a user