Threading improvments

This commit is contained in:
Kieran 2022-12-28 22:09:39 +00:00
parent b26f3a9b95
commit 99410dd8c2
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
8 changed files with 89 additions and 59 deletions

View File

@ -1,10 +1,11 @@
import "./Note.css";
import Event from "../nostr/Event";
import { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import moment from "moment";
import { Link, useNavigate } from "react-router-dom";
import Event from "../nostr/Event";
import ProfileImage from "./ProfileImage";
import useEventPublisher from "../pages/feed/EventPublisher";
const UrlRegex = /((?:http|ftp|https):\/\/(?:[\w+?\.\w+])+(?:[a-zA-Z0-9\~\!\@\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?)/;
const FileExtensionRegex = /\.([\w]+)$/;
@ -13,11 +14,12 @@ const MentionRegex = /(#\[\d+\])/g;
export default function Note(props) {
const navigate = useNavigate();
const data = props.data;
const dataEvent = props["data-ev"];
const reactions = props.reactions;
const publisher = useEventPublisher();
const [sig, setSig] = useState(false);
const users = useSelector(s => s.users?.users);
const user = users[data?.pubkey];
const ev = Event.FromObject(data);
const ev = dataEvent ?? Event.FromObject(data);
useEffect(() => {
if (sig === false) {
@ -109,6 +111,11 @@ export default function Note(props) {
});
}
async function like() {
let evLike = await publisher.like(ev);
publisher.broadcast(evLike);
}
if (!ev.IsContent()) {
return (
<>
@ -131,7 +138,7 @@ export default function Note(props) {
{transformBody()}
</div>
<div className="footer">
<span className="pill">
<span className="pill" onClick={() => like()}>
👍 {(reactions?.length ?? 0)}
</span>
<span className="pill" onClick={() => console.debug(ev)}>

View File

@ -1,5 +1,4 @@
import "./Note.css";
import moment from "moment";
import ProfileImage from "./ProfileImage";
export default function NoteGhost(props) {
@ -7,12 +6,9 @@ export default function NoteGhost(props) {
<div className="note">
<div className="header">
<ProfileImage pubKey="" />
<div className="info">
{moment().fromNow()}
</div>
</div>
<div className="body">
Loading...
{props.text ?? "Loading..."}
</div>
<div className="footer">
</div>

View File

@ -4,6 +4,8 @@ import Note from "./Note";
import NoteGhost from "./NoteGhost";
export default function Thread(props) {
const thisEvent = props.this;
/** @type {Array<Event>} */
const notes = props.notes?.map(a => Event.FromObject(a));
@ -11,18 +13,21 @@ export default function Thread(props) {
const root = notes.find(a => a.GetThread() === null);
function reactions(id) {
return notes?.filter(a => a.Kind === EventKind.Reaction && a.GetThread()?.Root?.Event === id);
return notes?.filter(a => a.Kind === EventKind.Reaction && a.Tags.find(a => a.Key === "e").Event === id);
}
const repliesToRoot = notes?.
filter(a => a.GetThread()?.Root?.Event === root?.Id && a.Kind === EventKind.TextNote)
.sort((a, b) => b.CreatedAt - a.CreatedAt);
filter(a => a.GetThread()?.Root !== null && a.Kind === EventKind.TextNote && a.Id !== thisEvent)
.sort((a, b) => a.CreatedAt - b.CreatedAt);
const thisNote = notes?.find(a => a.Id === thisEvent);
return (
<>
{root === undefined ?
<NoteGhost />
: <Note data={root?.ToObject()} reactions={reactions(root?.Id)} />}
{repliesToRoot?.map(a => <Note key={a.Id} data={a.ToObject()} reactions={reactions(a.Id)} />)}
<NoteGhost text={`Loading... (${notes.length} events loaded)`}/>
: <Note data-ev={root} reactions={reactions(root?.Id)} />}
{thisNote ? <Note data-ev={thisNote} reactions={reactions(thisNote.Id)}/> : null}
<h4>Other Replies</h4>
{repliesToRoot?.map(a => <Note key={a.Id} data-ev={a} reactions={reactions(a.Id)} />)}
</>
);
}

View File

@ -149,30 +149,4 @@ export default class Event {
ev.PubKey = pubKey;
return ev;
}
/**
* Create new SetMetadata event
* @param {String} pubKey Pubkey of the creator of this event
* @param {any} obj Metadata content
* @returns {Event}
*/
static SetMetadata(pubKey, obj) {
let ev = Event.ForPubKey(pubKey);
ev.Kind = EventKind.SetMetadata;
ev.Content = JSON.stringify(obj);
return ev;
}
/**
* Create a new TextNote event
* @param {String} pubKey
* @param {String} message
* @returns
*/
static NewNote(pubKey, message) {
let ev = Event.ForPubKey(pubKey);
ev.Kind = EventKind.TextNote;
ev.Content = message;
return ev;
}
}

View File

@ -7,5 +7,5 @@ export default function EventPage() {
const id = params.id;
const { notes } = useThreadFeed(id);
return <Thread notes={notes}/>;
return <Thread notes={notes} this={id}/>;
}

View File

@ -3,19 +3,17 @@ import { useDispatch, useSelector } from "react-redux";
import { useParams } from "react-router-dom";
import useProfile from "./feed/ProfileFeed";
import { useContext, useEffect, useState } from "react";
import Event from "../nostr/Event";
import { NostrContext } from "..";
import { resetProfile } from "../state/Users";
import Nostrich from "../nostrich.jpg";
import useEventPublisher from "./feed/EventPublisher";
export default function ProfilePage() {
const system = useContext(NostrContext);
const dispatch = useDispatch();
const params = useParams();
const id = params.id;
const user = useProfile(id);
const publisher = useEventPublisher();
const loginPubKey = useSelector(s => s.login.publicKey);
const privKey = useSelector(s => s.login.privateKey);
const isMe = loginPubKey === id;
let [name, setName] = useState("");
@ -37,7 +35,7 @@ export default function ProfilePage() {
}, [user]);
async function saveProfile() {
let ev = Event.SetMetadata(id, {
let ev = await publisher.metadata({
name,
about,
picture,
@ -45,10 +43,8 @@ export default function ProfilePage() {
nip05,
lud16
});
await ev.Sign(privKey);
console.debug(ev);
system.BroadcastEvent(ev);
publisher.broadcast(ev);
dispatch(resetProfile(id));
}

View File

@ -1,22 +1,20 @@
import "./Root.css";
import Timeline from "./Timeline";
import { useSelector } from "react-redux";
import { useContext, useState } from "react";
import Event from "../nostr/Event";
import { NostrContext } from "..";
import { useState } from "react";
import Timeline from "./Timeline";
import useEventPublisher from "./feed/EventPublisher";
export default function RootPage() {
const system = useContext(NostrContext);
const publisher = useEventPublisher();
const pubKey = useSelector(s => s.login.publicKey);
const privKey = useSelector(s => s.login.privateKey);
const [note, setNote] = useState("");
async function sendNote() {
let ev = Event.NewNote(pubKey, note);
await ev.Sign(privKey);
let ev = await publisher.note(note);
console.debug("Sending note: ", ev);
system.BroadcastEvent(ev);
publisher.broadcast(ev);
setNote("");
}

View File

@ -0,0 +1,54 @@
import { useContext } from "react";
import { useSelector } from "react-redux";
import { NostrContext } from "../..";
import Event from "../../nostr/Event";
import EventKind from "../../nostr/EventKind";
import Tag from "../../nostr/Tag";
export default function useEventPublisher() {
const system = useContext(NostrContext);
const pubKey = useSelector(s => s.login.publicKey);
const privKey = useSelector(s => s.login.privateKey);
return {
broadcast: (ev) => {
console.debug("Sending event: ", ev);
system.BroadcastEvent(ev);
},
metadata: async (obj) => {
let ev = Event.ForPubKey(pubKey);
ev.Kind = EventKind.SetMetadata;
ev.Content = JSON.stringify(obj);
await ev.Sign(privKey);
return ev;
},
note: async (msg) => {
if(typeof msg !== "string") {
throw "Must be text!";
}
let ev = Event.ForPubKey(pubKey);
ev.Kind = EventKind.TextNote;
ev.Content = msg;
await ev.Sign(privKey);
return ev;
},
like: async (evRef) => {
let ev = Event.ForPubKey(pubKey);
ev.Kind = EventKind.Reaction;
ev.Content = "+";
ev.Tags.push(new Tag(["e", evRef.Id], 0));
ev.Tags.push(new Tag(["p", evRef.PubKey], 1));
await ev.Sign(privKey);
return ev;
},
dislike: async (evRef) => {
let ev = Event.ForPubKey(pubKey);
ev.Kind = EventKind.Reaction;
ev.Content = "-";
ev.Tags.push(new Tag(["e", evRef.Id], 0));
ev.Tags.push(new Tag(["p", evRef.PubKey], 1));
await ev.Sign(privKey);
return ev;
}
}
}