-
-
-
Edit
+ function details() {
+ return (
+ <>
+
-
-
- {isMe ? editor() : null}
-
+
+ {website ?
+
+
Website:
+
+ {website}
+
+
: null}
+
+
NIP-05:
+
+ {nip05}
+
+
+
+
Lightning Address:
+
+ {lud16}
+
+
+ >
+ )
+ }
-
+ return (
+ <>
+
+
+
+ {isMe ?
+
+ : null
+ }
+
+
+
+ {isMe ? editor() : details()}
+
+
+
Notes
+ {notes?.sort((a, b) => b.created_at - a.created_at).map(a =>
)}
+ >
)
}
\ No newline at end of file
diff --git a/src/pages/Timeline.js b/src/pages/Timeline.js
index 9d4360b..645923c 100644
--- a/src/pages/Timeline.js
+++ b/src/pages/Timeline.js
@@ -1,16 +1,14 @@
+import { useSelector } from "react-redux";
import Note from "../element/Note";
import useTimelineFeed from "./feed/TimelineFeed";
export default function Timeline() {
- const { notes } = useTimelineFeed();
-
- const sorted = [
- ...(notes || [])
- ].sort((a, b) => b.created_at - a.created_at);
+ const follows = useSelector(a => a.login.follows)
+ const { notes } = useTimelineFeed(follows);
return (
- {sorted.map(e => )}
+ {notes?.sort((a, b) => b.created_at - a.created_at).map(e => )}
);
}
\ No newline at end of file
diff --git a/src/pages/feed/LoginFeed.js b/src/pages/feed/LoginFeed.js
new file mode 100644
index 0000000..5ac3f60
--- /dev/null
+++ b/src/pages/feed/LoginFeed.js
@@ -0,0 +1,37 @@
+import { useContext, useEffect } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { NostrContext } from "../..";
+import Event from "../../nostr/Event";
+import EventKind from "../../nostr/EventKind";
+import { Subscriptions } from "../../nostr/Subscriptions";
+import { setFollows, setRelays } from "../../state/Login";
+
+/**
+ * Managed loading data for the current logged in user
+ */
+export default function useLoginFeed() {
+ const dispatch = useDispatch();
+ const system = useContext(NostrContext);
+ const pubKey = useSelector(s => s.login.publicKey);
+
+ useEffect(() => {
+ if (system && pubKey) {
+ let sub = new Subscriptions();
+ sub.Authors.add(pubKey);
+ sub.Kinds.add(EventKind.ContactList);
+ sub.OnEvent = (e) => {
+ let ev = Event.FromObject(e);
+ if (ev.Content !== "") {
+ let relays = JSON.parse(ev.Content);
+ dispatch(setRelays(relays));
+ }
+ let pTags = ev.Tags.filter(a => a.Key === "p").map(a => a.PubKey);
+ dispatch(setFollows(pTags));
+ }
+ system.AddSubscription(sub);
+ return () => system.RemoveSubscription(sub.Id);
+ }
+ }, [system, pubKey]);
+
+ return {};
+}
\ No newline at end of file
diff --git a/src/pages/feed/TimelineFeed.js b/src/pages/feed/TimelineFeed.js
index 96c6c5a..2155689 100644
--- a/src/pages/feed/TimelineFeed.js
+++ b/src/pages/feed/TimelineFeed.js
@@ -1,55 +1,38 @@
-import { useContext, useEffect } from "react";
-import { useDispatch, useSelector } from "react-redux";
+import { useContext, useEffect, useState } from "react";
import { NostrContext } from "../../index";
import EventKind from "../../nostr/EventKind";
import { Subscriptions } from "../../nostr/Subscriptions";
-import { addNote } from "../../state/Timeline";
-import { addPubKey } from "../../state/Users";
-export default function useTimelineFeed(opt) {
+export default function useTimelineFeed(pubKeys) {
const system = useContext(NostrContext);
- const dispatch = useDispatch();
- const follows = useSelector(s => s.timeline?.follows);
- const notes = useSelector(s => s.timeline?.notes);
- const pubKeys = useSelector(s => s.users.pubKeys);
-
- const options = {
-
- ...opt
- };
-
- function trackPubKeys(keys) {
- for (let pk of keys) {
- if (!pubKeys.includes(pk)) {
- dispatch(addPubKey(pk));
- }
- }
- }
+ const [notes, setNotes] = useState([]);
useEffect(() => {
- if (follows.length > 0) {
+ if (system && pubKeys.length > 0) {
const sub = new Subscriptions();
- sub.Authors = new Set(follows);
+ sub.Authors = new Set(pubKeys);
sub.Kinds.add(EventKind.TextNote);
sub.Limit = 10;
sub.OnEvent = (e) => {
- dispatch(addNote(e));
+ setNotes(n => {
+ if (Array.isArray(n) && !n.some(a => a.id === e.id)) {
+ return [
+ ...n,
+ e
+ ]
+ } else {
+ return n;
+ }
+ });
};
- trackPubKeys(follows);
- if (system) {
- system.AddSubscription(sub);
- return () => system.RemoveSubscription(sub.Id);
- }
+ system.AddSubscription(sub);
+ return () => {
+ system.RemoveSubscription(sub.Id);
+ };
}
- }, [follows]);
+ }, [system, pubKeys]);
- useEffect(() => {
- for (let n of notes) {
-
- }
- }, [notes]);
-
- return { notes, follows };
+ return { notes };
}
\ No newline at end of file
diff --git a/src/pages/feed/UsersFeed.js b/src/pages/feed/UsersFeed.js
index 7995b17..f2d0a37 100644
--- a/src/pages/feed/UsersFeed.js
+++ b/src/pages/feed/UsersFeed.js
@@ -6,7 +6,7 @@ import EventKind from "../../nostr/EventKind";
import { Subscriptions } from "../../nostr/Subscriptions";
import { setUserData } from "../../state/Users";
-export default function useUsersStore() {
+export default function useUsersCache() {
const dispatch = useDispatch();
const system = useContext(NostrContext);
const pKeys = useSelector(s => s.users.pubKeys);
diff --git a/src/state/Login.js b/src/state/Login.js
index 4aebf75..e272589 100644
--- a/src/state/Login.js
+++ b/src/state/Login.js
@@ -2,12 +2,6 @@ import { createSlice } from '@reduxjs/toolkit'
import * as secp from '@noble/secp256k1';
const PrivateKeyItem = "secret";
-const RelayList = "relays";
-const DefaultRelays = JSON.stringify([
- "wss://nostr-pub.wellorder.net",
- "wss://relay.damus.io",
- "wss://beta.nostr.v0l.io"
-]);
const LoginSlice = createSlice({
name: "Login",
@@ -25,21 +19,35 @@ const LoginSlice = createSlice({
/**
* Configured relays for this user
*/
- relays: []
+ relays: {},
+
+ /**
+ * A list of pubkeys this user follows
+ */
+ follows: []
},
reducers: {
init: (state) => {
state.privateKey = window.localStorage.getItem(PrivateKeyItem);
- if(state.privateKey) {
+ if (state.privateKey) {
state.publicKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(state.privateKey, true));
}
- state.relays = JSON.parse(window.localStorage.getItem(RelayList) || DefaultRelays);
+ state.relays = {
+ "wss://beta.nostr.v0l.io": { read: true, write: true },
+ "wss://nostr.v0l.io": { read: true, write: true }
+ };
},
setPrivateKey: (state, action) => {
state.privateKey = action.payload;
window.localStorage.setItem(PrivateKeyItem, action.payload);
state.publicKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(action.payload, true));
},
+ setRelays: (state, action) => {
+ state.relays = action.payload;
+ },
+ setFollows: (state, action) => {
+ state.follows = action.payload;
+ },
logout: (state) => {
state.privateKey = null;
window.localStorage.removeItem(PrivateKeyItem);
@@ -47,5 +55,5 @@ const LoginSlice = createSlice({
}
});
-export const { init, setPrivateKey, logout } = LoginSlice.actions;
+export const { init, setPrivateKey, setRelays, setFollows, logout } = LoginSlice.actions;
export const reducer = LoginSlice.reducer;
\ No newline at end of file
diff --git a/src/state/Store.js b/src/state/Store.js
index 9046a36..64bab66 100644
--- a/src/state/Store.js
+++ b/src/state/Store.js
@@ -1,12 +1,10 @@
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,
thread: ThreadReducer
diff --git a/src/state/Timeline.js b/src/state/Timeline.js
deleted file mode 100644
index ed77519..0000000
--- a/src/state/Timeline.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { createSlice } from '@reduxjs/toolkit'
-
-const TimelineSlice = createSlice({
- name: "Timeline",
- initialState: {
- notes: [],
- follows: ["217e3d8b61c087b10422427e114737a4a4a4b1e15f22301fb4b07e1f33204d7c", "82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2", "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245", "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"]
- },
- 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);
- }
- },
- setFollowers: (state, action) => {
- state.follows = action.payload;
- },
- addFollower: (state, action) => {
- let tmp = new Set(state.follows);
- tmp.add(action.payload);
- state.follows = Array.from(tmp);
- }
- }
-});
-
-export const { setNotes, addNote, setFollowers, addFollower } = TimelineSlice.actions;
-export const reducer = TimelineSlice.reducer;
\ No newline at end of file