diff --git a/client/src/components/Forms/PostFormCard.tsx b/client/src/components/Forms/PostFormCard.tsx
index e978d83..73a847a 100644
--- a/client/src/components/Forms/PostFormCard.tsx
+++ b/client/src/components/Forms/PostFormCard.tsx
@@ -45,10 +45,13 @@ const NewNoteCard = ({
useEffect(() => {
if (refEvent && tagType && unsigned.tags.length === 0) {
- const tags = tagMapping[tagType];
- if (tags) {
- tags.forEach(tag => unsigned.tags.push([tag, refEvent[tag === 'p' ? 'pubkey' : 'id']]));
- // Ref event should be the latest event they're replying to, and their event should include prev replies
+ if (tagType === 'Reply' && unsigned.tags.length === 0) {
+ unsigned.tags.push(['p', refEvent.pubkey]);
+ unsigned.tags.push(['e', refEvent.id, 'root']);
+ } else {
+ unsigned.tags = refEvent.tags
+ unsigned.tags.push(['p', refEvent.pubkey]);
+ unsigned.tags.push(['e', refEvent.id]);
}
if (tagType === 'Quote') {
setComment(comment + '\nnostr:' + nip19.noteEncode(refEvent.id));
diff --git a/client/src/components/Home.tsx b/client/src/components/Home.tsx
index 188bc04..2e19df0 100644
--- a/client/src/components/Home.tsx
+++ b/client/src/components/Home.tsx
@@ -6,6 +6,7 @@ import { verifyPow } from "../utils/mine";
import { Event } from "nostr-tools";
import NewNoteCard from "./Forms/PostFormCard";
import RepostCard from "./Modals/RepostCard";
+import OptionsBar from "./Modals/OptionsBar";
const DEFAULT_DIFFICULTY = 20;
@@ -21,7 +22,7 @@ const useUniqEvents = () => {
const uniqEvents = uniqBy(events, "id");
- const noteEvents = uniqEvents.filter(event => event.kind === 1);
+ const noteEvents = uniqEvents.filter(event => event.kind === 1 || event.kind === 6);
const metadataEvents = uniqEvents.filter(event => event.kind === 0);
return { noteEvents, metadataEvents };
@@ -74,40 +75,7 @@ const Home = () => {
-
-
-
-
+
{sortedEvents.map((event) => (
event.kind === 1 ?
diff --git a/client/src/components/Modals/OptionsBar.tsx b/client/src/components/Modals/OptionsBar.tsx
new file mode 100644
index 0000000..3c9b936
--- /dev/null
+++ b/client/src/components/Modals/OptionsBar.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import { useState } from 'react';
+
+interface OptionsBarProps {
+ sortByTime?: boolean;
+ setAnon?: boolean;
+ toggleSort?: () => void;
+ toggleAnon?: () => void;
+}
+
+const OptionsBar: React.FC
= ({ sortByTime, setAnon, toggleSort, toggleAnon }) => {
+ const [showAdvancedSettings, setShowAdvancedSettings] = useState(false);
+
+ return (
+
+
setShowAdvancedSettings(!showAdvancedSettings)} className="text-xs text-neutral-600">
+ {">"} Alter Feed
+
+
+ {toggleSort &&
}
+ {toggleAnon &&
}
+
+
+ );
+};
+
+export default OptionsBar;
\ No newline at end of file
diff --git a/client/src/components/Notifications.tsx b/client/src/components/Notifications.tsx
new file mode 100644
index 0000000..7fdc1c5
--- /dev/null
+++ b/client/src/components/Notifications.tsx
@@ -0,0 +1,93 @@
+import { useEffect, useState, useCallback } from "react";
+import PostCard from "./Modals/NoteCard";
+import { uniqBy } from "../utils/otherUtils"; // Assume getPow is a correct import now
+import { subGlobalFeed } from "../utils/subscriptions";
+import { verifyPow } from "../utils/mine";
+import { Event } from "nostr-tools";
+import NewNoteCard from "./Forms/PostFormCard";
+import RepostCard from "./Modals/RepostCard";
+import OptionsBar from "./Modals/OptionsBar";
+import { subNotifications } from "../utils/subscriptions";
+
+const useUniqEvents = () => {
+ const [events, setEvents] = useState([]);
+ let storedKeys = JSON.parse(localStorage.getItem('usedKeys') || '[]');
+ let storedPubkeys = storedKeys.map((key: any[]) => key[1]);
+
+ useEffect(() => {
+ const onEvent = (event: Event) => setEvents((prevEvents) => [...prevEvents, event]);
+ const unsubscribe = subNotifications(storedPubkeys, onEvent);
+
+ return unsubscribe;
+ }, []);
+
+ const uniqEvents = uniqBy(events, "id");
+
+ const noteEvents = uniqEvents.filter(event => event.kind === 1 || event.kind === 6);
+ const metadataEvents = uniqEvents.filter(event => event.kind === 0);
+
+ return { noteEvents, metadataEvents };
+};
+
+const Notifications = () => {
+ const [sortByTime, setSortByTime] = useState(localStorage.getItem('sortBy') !== 'false');
+ const [setAnon, setSetAnon] = useState(localStorage.getItem('anonMode') !== 'false');
+ const { noteEvents, metadataEvents } = useUniqEvents();
+
+ const postEvents = noteEvents
+ .filter((event) =>
+ event.kind !== 0 &&
+ (event.kind !== 1)
+ )
+
+ const sortedEvents = [...postEvents]
+ .sort((a, b) =>
+ sortByTime ? b.created_at - a.created_at : verifyPow(b) - verifyPow(a)
+ )
+ .filter(
+ !setAnon ? (e) => !metadataEvents.some((metadataEvent) => metadataEvent.pubkey === e.pubkey) : () => true
+ );
+
+ const toggleSort = useCallback(() => {
+ setSortByTime(prev => {
+ const newValue = !prev;
+ localStorage.setItem('sortBy', String(newValue));
+ return newValue;
+ });
+ }, []);
+
+ const toggleAnon = useCallback(() => {
+ setSetAnon(prev => {
+ const newValue = !prev;
+ localStorage.setItem('anonMode', String(newValue));
+ return newValue;
+ });
+ }, []);
+
+ const countReplies = (event: Event) => {
+ return noteEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id)).length;
+ };
+
+ // Render the component
+ return (
+
+
+
+ {sortedEvents.map((event) => (
+ event.kind === 1 ?
+
e.pubkey === event.pubkey && e.kind === 0) || null}
+ replyCount={countReplies(event)}
+ />
+ :
+
+ ))}
+
+
+ );
+};
+
+export default Notifications;
diff --git a/client/src/components/Thread.tsx b/client/src/components/Thread.tsx
index d4d17fa..7a4b3ac 100644
--- a/client/src/components/Thread.tsx
+++ b/client/src/components/Thread.tsx
@@ -10,6 +10,7 @@ import PostCard from './Modals/NoteCard';
import Placeholder from './Modals/Placeholder';
import NewNoteCard from './Forms/PostFormCard';
import RepostNote from './Forms/RepostNote';
+import OptionsBar from './Modals/OptionsBar';
type PostType = "" | "Reply" | "Quote" | undefined;
@@ -49,9 +50,10 @@ const Thread = () => {
useEffect(() => {
if (!hasRun && events.length > 0) {
- let OPEvent = uniqEvents[0];
+ let OPEvent = uniqEvents.find(event => event.id === hexID);
setOPEvent(OPEvent);
+ console.log(OPEvent)
if (OPEvent && OPEvent.id !== hexID) {
OPEvent = events.find(e => e.id === hexID) as Event;
}
@@ -126,7 +128,7 @@ const Thread = () => {
))}
{OPEvent && }
-
+
{
@@ -153,36 +155,20 @@ const Thread = () => {
{(showForm && postType) &&
-
{postType}-post
-
+
+ {postType}-post
+
+
}
- {showRepost &&
-
Repost note
-
+ {showRepost && OPEvent && }
-
-
-
+ {/* This is the white line separator */}
+
-
{/* This is the white line separator */}
{displayedEvents.map((event, index) => (
))}
diff --git a/client/src/utils/subscriptions.ts b/client/src/utils/subscriptions.ts
index a8477b4..437098b 100644
--- a/client/src/utils/subscriptions.ts
+++ b/client/src/utils/subscriptions.ts
@@ -29,15 +29,6 @@ export const subGlobalFeed = (onEvent: SubCallback) => {
unsub: true
});
- // // New Callback to only add events that pass the PoW requirement
- // const powFilteredCallback = (evt: Event, relay: string) => {
- // if (getPow(evt.id) > 2) { // Replace '5' with your actual PoW requirement
- // pubkeys.add(evt.pubkey);
- // notes.add(evt.id);
- // onEvent(evt, relay);
- // }
- // };
-
setTimeout(() => {
// get profile info
sub({
@@ -91,20 +82,6 @@ export const subGlobalFeed = (onEvent: SubCallback) => {
});
};
-/** subscribe to global feed */
-export const simpleSub24hFeed = (onEvent: SubCallback) => {
- unsubAll();
- sub({
- cb: onEvent,
- filter: {
- kinds: [1],
- //until: Math.floor(Date.now() * 0.001),
- since: Math.floor((Date.now() * 0.001) - (24 * 60 * 60)),
- limit: 1,
- }
- });
-};
-
/** subscribe to a note id (nip-19) */
export const subNote = (
eventId: string,
@@ -236,6 +213,40 @@ export const subNotesOnce = (
}, 2000);
};
+// /** quick subscribe to a note id (nip-19) */
+// export const subNotifications = (
+// pubkeys: string[],
+// onEvent: SubCallback,
+// ) => {
+// const replyPubkeys = new Set
();
+// sub({
+// cb: (evt, relay) => {
+// replyPubkeys.add(evt.pubkey);
+// onEvent(evt, relay);
+// },
+// filter: {
+// "#p": pubkeys,
+// kinds: [1],
+// limit: 50,
+// },
+// unsub: true,
+// });
+
+// setTimeout(() => {
+// // get profile info
+// sub({
+// cb: onEvent,
+// filter: {
+// authors: Array.from(replyPubkeys),
+// kinds: [0],
+// limit: replyPubkeys.size,
+// },
+// unsub: true,
+// });
+// replyPubkeys.clear();
+// }, 2000);
+// };
+
/** quick subscribe to a note id (nip-19) */
export const subNotifications = (
pubkeys: string[],
@@ -243,12 +254,9 @@ export const subNotifications = (
) => {
const replyPubkeys = new Set();
sub({
- cb: (evt, relay) => {
- replyPubkeys.add(evt.pubkey);
- onEvent(evt, relay);
- },
+ cb: onEvent,
filter: {
- "#p": pubkeys,
+ authors: Array.from(pubkeys),
kinds: [1],
limit: 50,
},
@@ -270,3 +278,68 @@ export const subNotifications = (
}, 2000);
};
+// const hasEventTag = (tag: string[]) => tag[0] === 'e';
+// const isReply = ([tag, , , marker]: string[]) => tag === 'e' && marker !== 'mention';
+
+// export const getReplyTo = (evt: Event): string | null => {
+// const eventTags = evt.tags.filter(isReply);
+// const withReplyMarker = eventTags.filter(([, , , marker]) => marker === 'reply');
+// if (withReplyMarker.length === 1) {
+// return withReplyMarker[0][1];
+// }
+// const withRootMarker = eventTags.filter(([, , , marker]) => marker === 'root');
+// if (withReplyMarker.length === 0 && withRootMarker.length === 1) {
+// return withRootMarker[0][1];
+// }
+// // fallback to deprecated positional 'e' tags (nip-10)
+// const lastTag = eventTags.at(-1);
+// return lastTag ? lastTag[1] : null;
+// };
+
+// export const subNotifications = (
+// pubkeys: string[],
+// onEvent: SubCallback,
+// ) => {
+// const authorsPrefixes = pubkeys.map(pubkey => pubkey.slice(0, 32));
+// console.info(`subscribe to homefeed ${authorsPrefixes}`);
+// unsubAll();
+
+// const repliesTo = new Set();
+// sub({
+// cb: (evt, relay) => {
+// if (
+// evt.tags.some(hasEventTag)
+// ) {
+// const note = getReplyTo(evt); // get all reply to events instead?
+// if (note && !repliesTo.has(note)) {
+// repliesTo.add(note);
+// subOnce({
+// cb: onEvent,
+// filter: {
+// ids: [note],
+// kinds: [1],
+// limit: 1,
+// },
+// relay,
+// });
+// }
+// }
+// onEvent(evt, relay);
+// },
+// filter: {
+// authors: authorsPrefixes,
+// kinds: [1],
+// limit: 20,
+// },
+// });
+// // get metadata
+// sub({
+// cb: onEvent,
+// filter: {
+// authors: pubkeys,
+// kinds: [0],
+// limit: pubkeys.length,
+// },
+// unsub: true,
+// });
+// };