diff --git a/src/element/Thread.js b/src/element/Thread.tsx similarity index 61% rename from src/element/Thread.js rename to src/element/Thread.tsx index af4c67b2..a83233ee 100644 --- a/src/element/Thread.js +++ b/src/element/Thread.tsx @@ -1,29 +1,32 @@ import { useMemo } from "react"; import { Link } from "react-router-dom"; -import Event from "../nostr/Event"; +import { TaggedRawEvent, u256 } from "../nostr"; +import { default as NEvent } from "../nostr/Event"; import EventKind from "../nostr/EventKind"; import { eventLink } from "../Util"; import Note from "./Note"; import NoteGhost from "./NoteGhost"; -export default function Thread(props) { +export interface ThreadProps { + this?: u256, + notes?: TaggedRawEvent[] +} +export default function Thread(props: ThreadProps) { const thisEvent = props.this; - - /** @type {Array} */ - const notes = props.notes?.map(a => new Event(a)); + const notes = props.notes?.map(a => new NEvent(a)); // root note has no thread info - const root = useMemo(() => notes.find(a => a.Thread === null), [notes]); + const root = useMemo(() => notes?.find(a => a.Thread === null), [notes]); const chains = useMemo(() => { - let chains = new Map(); - notes.filter(a => a.Kind === EventKind.TextNote).sort((a, b) => b.CreatedAt - a.CreatedAt).forEach((v) => { + let chains = new Map(); + notes?.filter(a => a.Kind === EventKind.TextNote).sort((a, b) => b.CreatedAt - a.CreatedAt).forEach((v) => { let replyTo = v.Thread?.ReplyTo?.Event ?? v.Thread?.Root?.Event; if (replyTo) { if (!chains.has(replyTo)) { chains.set(replyTo, [v]); } else { - chains.get(replyTo).push(v); + chains.get(replyTo)!.push(v); } } else if (v.Tags.length > 0) { console.log("Not replying to anything: ", v); @@ -34,15 +37,15 @@ export default function Thread(props) { }, [notes]); const brokenChains = useMemo(() => { - return Array.from(chains?.keys()).filter(a => !notes.some(b => b.Id === a)); + return Array.from(chains?.keys()).filter(a => !notes?.some(b => b.Id === a)); }, [chains]); const mentionsRoot = useMemo(() => { - return notes.filter(a => a.Kind === EventKind.TextNote && a.Thread) + return notes?.filter(a => a.Kind === EventKind.TextNote && a.Thread) }, [chains]); - function reactions(id, kind = EventKind.Reaction) { - return notes?.filter(a => a.Kind === kind && a.Tags.find(a => a.Key === "e" && a.Event === id)); + function reactions(id: u256, kind = EventKind.Reaction) { + return (notes?.filter(a => a.Kind === kind && a.Tags.find(a => a.Key === "e" && a.Event === id)) || []).map(a => a.Original!); } function renderRoot() { @@ -50,12 +53,12 @@ export default function Thread(props) { return } else { return - Loading thread root.. ({notes.length} notes loaded) + Loading thread root.. ({notes?.length} notes loaded) } } - function renderChain(from) { + function renderChain(from: u256) { if (from && chains) { let replies = chains.get(from); if (replies) { @@ -64,7 +67,11 @@ export default function Thread(props) { {replies.map(a => { return ( <> - + {renderChain(a.Id)} ) diff --git a/src/pages/EventPage.js b/src/pages/EventPage.tsx similarity index 93% rename from src/pages/EventPage.js rename to src/pages/EventPage.tsx index 6a1794a0..bbe21a81 100644 --- a/src/pages/EventPage.js +++ b/src/pages/EventPage.tsx @@ -6,8 +6,7 @@ import { parseId } from "../Util"; export default function EventPage() { const params = useParams(); - const id = parseId(params.id); - + const id = parseId(params.id!); const thread = useThreadFeed(id); const filtered = useMemo(() => { diff --git a/src/pages/Layout.js b/src/pages/Layout.tsx similarity index 77% rename from src/pages/Layout.js rename to src/pages/Layout.tsx index 08c4668c..ee27a5d3 100644 --- a/src/pages/Layout.js +++ b/src/pages/Layout.tsx @@ -9,15 +9,18 @@ import { System } from "../nostr/System" import ProfileImage from "../element/ProfileImage"; import { init } from "../state/Login"; import useLoginFeed from "../feed/LoginFeed"; +import { RootState } from "../state/Store"; +import { HexKey, TaggedRawEvent } from "../nostr"; +import { RelaySettings } from "../nostr/Connection"; -export default function Layout(props) { +export default function Layout() { const dispatch = useDispatch(); const navigate = useNavigate(); - const isInit = useSelector(s => s.login.loggedOut); - const key = useSelector(s => s.login.publicKey); - const relays = useSelector(s => s.login.relays); - const notifications = useSelector(s => s.login.notifications); - const readNotifications = useSelector(s => s.login.readNotifications); + const isInit = useSelector(s => s.login.loggedOut); + const key = useSelector(s => s.login.publicKey); + const relays = useSelector>(s => s.login.relays); + const notifications = useSelector(s => s.login.notifications); + const readNotifications = useSelector(s => s.login.readNotifications); useLoginFeed(); useEffect(() => { @@ -37,7 +40,7 @@ export default function Layout(props) { dispatch(init()); }, []); - async function goToNotifications(e) { + async function goToNotifications(e: any) { e.stopPropagation(); // request permissions to send notifications if ("Notification" in window && Notification.permission !== "granted") { @@ -64,7 +67,7 @@ export default function Layout(props) { {unreadNotifications > 0 && ( {unreadNotifications > 100 ? ">99" : unreadNotifications} )} - + ) } @@ -84,7 +87,7 @@ export default function Layout(props) { - + ) } \ No newline at end of file diff --git a/src/pages/Login.js b/src/pages/Login.tsx similarity index 93% rename from src/pages/Login.js rename to src/pages/Login.tsx index 95ec9ec1..c0653e8a 100644 --- a/src/pages/Login.js +++ b/src/pages/Login.tsx @@ -6,11 +6,13 @@ import * as secp from '@noble/secp256k1'; import { setPrivateKey, setPublicKey } from "../state/Login"; import { EmailRegex } from "../Const"; import { bech32ToHex } from "../Util"; +import { RootState } from "../state/Store"; +import { HexKey } from "../nostr"; export default function LoginPage() { const dispatch = useDispatch(); const navigate = useNavigate(); - const publicKey = useSelector(s => s.login.publicKey); + const publicKey = useSelector(s => s.login.publicKey); const [key, setKey] = useState(""); const [error, setError] = useState(""); @@ -20,7 +22,7 @@ export default function LoginPage() { } }, [publicKey]); - async function getNip05PubKey(addr) { + async function getNip05PubKey(addr: string) { let [username, domain] = addr.split("@"); let rsp = await fetch(`https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(username)}`); if (rsp.ok) { diff --git a/src/pages/NewUserPage.js b/src/pages/NewUserPage.tsx similarity index 85% rename from src/pages/NewUserPage.js rename to src/pages/NewUserPage.tsx index dbef7557..17b721a7 100644 --- a/src/pages/NewUserPage.js +++ b/src/pages/NewUserPage.tsx @@ -1,8 +1,7 @@ -import { Outlet } from "react-router-dom"; import { RecommendedFollows } from "../Const"; import ProfilePreview from "../element/ProfilePreview"; -export default function NewUserPage(props) { +export default function NewUserPage() { function followSomebody() { return ( diff --git a/src/pages/Notifications.js b/src/pages/Notifications.tsx similarity index 89% rename from src/pages/Notifications.js rename to src/pages/Notifications.tsx index 3d537ed9..b8182d4e 100644 --- a/src/pages/Notifications.js +++ b/src/pages/Notifications.tsx @@ -3,14 +3,16 @@ import { useDispatch, useSelector } from "react-redux" import Note from "../element/Note"; import NoteReaction from "../element/NoteReaction"; import useSubscription from "../feed/Subscription"; +import { TaggedRawEvent } from "../nostr"; import Event from "../nostr/Event"; import EventKind from "../nostr/EventKind"; import { Subscriptions } from "../nostr/Subscriptions"; import { markNotificationsRead } from "../state/Login"; +import { RootState } from "../state/Store"; export default function NotificationsPage() { const dispatch = useDispatch(); - const notifications = useSelector(s => s.login.notifications); + const notifications = useSelector(s => s.login.notifications); useEffect(() => { dispatch(markNotificationsRead()); @@ -21,7 +23,7 @@ export default function NotificationsPage() { .map(a => { let ev = new Event(a); return ev.Thread?.ReplyTo?.Event ?? ev.Thread?.Root?.Event; - }) + }).filter(a => a !== undefined).map(a => a!); }, [notifications]); const subEvents = useMemo(() => { @@ -50,7 +52,7 @@ export default function NotificationsPage() { {sorted?.map(a => { if (a.kind === EventKind.TextNote) { let reactions = otherNotes?.notes?.filter(c => c.tags.find(b => b[0] === "e" && b[1] === a.id)); - return + return } else if (a.kind === EventKind.Reaction) { let ev = new Event(a); let reactedTo = ev.Thread?.ReplyTo?.Event ?? ev.Thread?.Root?.Event; diff --git a/src/pages/Root.js b/src/pages/Root.tsx similarity index 83% rename from src/pages/Root.js rename to src/pages/Root.tsx index 074e4665..1c699e72 100644 --- a/src/pages/Root.js +++ b/src/pages/Root.tsx @@ -4,7 +4,8 @@ import { Link } from "react-router-dom"; import { NoteCreator } from "../element/NoteCreator"; import Timeline from "../element/Timeline"; import { useState } from "react"; -import useScroll from "../useScroll"; +import { RootState } from "../state/Store"; +import { HexKey } from "../nostr"; const RootTab = { Follows: 0, @@ -12,9 +13,8 @@ const RootTab = { }; export default function RootPage() { - const [loggedOut, pubKey, follows] = useSelector(s => [s.login.loggedOut, s.login.publicKey, s.login.follows]); + const [loggedOut, pubKey, follows] = useSelector(s => [s.login.loggedOut, s.login.publicKey, s.login.follows]); const [tab, setTab] = useState(RootTab.Follows); - const [eop] = useScroll(); function followHints() { if (follows?.length === 0 && pubKey && tab !== RootTab.Global) { @@ -27,7 +27,7 @@ export default function RootPage() { return ( <> {pubKey ? <> - +
setTab(RootTab.Follows)}> Follows