forked from Kieran/snort
Convert remaining pages
This commit is contained in:
parent
8aed2c5550
commit
6bfecd9d07
@ -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<Event>} */
|
||||
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<u256, NEvent[]>();
|
||||
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 <Note data-ev={root} reactions={reactions(root.Id)} deletion={reactions(root.Id, EventKind.Deletion)} isThread />
|
||||
} else {
|
||||
return <NoteGhost>
|
||||
Loading thread root.. ({notes.length} notes loaded)
|
||||
Loading thread root.. ({notes?.length} notes loaded)
|
||||
</NoteGhost>
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
<>
|
||||
<Note data-ev={a} key={a.Id} reactions={reactions(a.Id)} deletion={reactions(a.Id, EventKind.Deletion)} hightlight={thisEvent === a.Id} />
|
||||
<Note data-ev={a}
|
||||
key={a.Id}
|
||||
reactions={reactions(a.Id)}
|
||||
deletion={reactions(a.Id, EventKind.Deletion)}
|
||||
highlight={thisEvent === a.Id} />
|
||||
{renderChain(a.Id)}
|
||||
</>
|
||||
)
|
@ -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(() => {
|
@ -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<RootState, boolean | undefined>(s => s.login.loggedOut);
|
||||
const key = useSelector<RootState, HexKey | undefined>(s => s.login.publicKey);
|
||||
const relays = useSelector<RootState, Record<string, RelaySettings>>(s => s.login.relays);
|
||||
const notifications = useSelector<RootState, TaggedRawEvent[]>(s => s.login.notifications);
|
||||
const readNotifications = useSelector<RootState, number>(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 && (<span className="unread-count">
|
||||
{unreadNotifications > 100 ? ">99" : unreadNotifications}
|
||||
</span>)}
|
||||
<ProfileImage pubkey={key} showUsername={false} />
|
||||
<ProfileImage pubkey={key || ""} showUsername={false} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -84,7 +87,7 @@ export default function Layout(props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Outlet/>
|
||||
<Outlet />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -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<RootState, HexKey | undefined>(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) {
|
@ -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 (
|
@ -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<RootState, TaggedRawEvent[]>(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 <Note data={a} key={a.id} reactions={reactions} />
|
||||
return <Note data={a} key={a.id} reactions={reactions} deletion={[]}/>
|
||||
} else if (a.kind === EventKind.Reaction) {
|
||||
let ev = new Event(a);
|
||||
let reactedTo = ev.Thread?.ReplyTo?.Event ?? ev.Thread?.Root?.Event;
|
@ -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<RootState, [boolean | undefined, HexKey | undefined, HexKey[]]>(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 ? <>
|
||||
<NoteCreator show={true}/>
|
||||
<NoteCreator show={true} autoFocus={false} />
|
||||
<div className="tabs root-tabs">
|
||||
<div className={`root-tab f-1 ${tab === RootTab.Follows ? "active" : ""}`} onClick={() => setTab(RootTab.Follows)}>
|
||||
Follows
|
Loading…
Reference in New Issue
Block a user