diff --git a/src/element/Note.js b/src/element/Note.js index f191f75a..a2bc6b1e 100644 --- a/src/element/Note.js +++ b/src/element/Note.js @@ -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()}
- + like()}> 👍 {(reactions?.length ?? 0)} console.debug(ev)}> diff --git a/src/element/NoteGhost.js b/src/element/NoteGhost.js index 0452cb55..2a20a0f5 100644 --- a/src/element/NoteGhost.js +++ b/src/element/NoteGhost.js @@ -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) {
-
- {moment().fromNow()} -
- Loading... + {props.text ?? "Loading..."}
diff --git a/src/element/Thread.js b/src/element/Thread.js index 4e0aaad7..5bc27830 100644 --- a/src/element/Thread.js +++ b/src/element/Thread.js @@ -4,6 +4,8 @@ import Note from "./Note"; import NoteGhost from "./NoteGhost"; export default function Thread(props) { + const thisEvent = props.this; + /** @type {Array} */ 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 ? - - : } - {repliesToRoot?.map(a => )} + + : } + {thisNote ? : null} +

Other Replies

+ {repliesToRoot?.map(a => )} ); } \ No newline at end of file diff --git a/src/nostr/Event.js b/src/nostr/Event.js index 2b32954b..a92b9b0f 100644 --- a/src/nostr/Event.js +++ b/src/nostr/Event.js @@ -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; - } } \ No newline at end of file diff --git a/src/pages/EventPage.js b/src/pages/EventPage.js index 002ae9bb..ab27f395 100644 --- a/src/pages/EventPage.js +++ b/src/pages/EventPage.js @@ -7,5 +7,5 @@ export default function EventPage() { const id = params.id; const { notes } = useThreadFeed(id); - return ; + return ; } \ No newline at end of file diff --git a/src/pages/ProfilePage.js b/src/pages/ProfilePage.js index 8deabd70..37f9a9ba 100644 --- a/src/pages/ProfilePage.js +++ b/src/pages/ProfilePage.js @@ -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)); } diff --git a/src/pages/Root.js b/src/pages/Root.js index d3caa240..6859be34 100644 --- a/src/pages/Root.js +++ b/src/pages/Root.js @@ -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(""); } diff --git a/src/pages/feed/EventPublisher.js b/src/pages/feed/EventPublisher.js new file mode 100644 index 00000000..712e5868 --- /dev/null +++ b/src/pages/feed/EventPublisher.js @@ -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; + } + } +} \ No newline at end of file