diff --git a/client/src/App.tsx b/client/src/App.tsx index 3791b40..0c8db94 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,14 +1,13 @@ -import "./App.css"; -import Home from "./components/Home"; -import Settings from "./components/Settings"; +import "./styles/App.css"; +import Home from "./components/routes/Home"; +import Settings from "./components/routes/Settings"; import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; -import Thread from "./components/Thread"; -import Header from "./components/Header/Header"; -import AddToHomeScreenPrompt from "./components/Modals/CheckMobile/CheckMobile"; -import Notifications from "./components/Notifications"; -// import TestUI from "./components/TestUI"; -import Hashtags from "./components/Hashtags"; -import HashtagPage from "./components/HashtagPage"; +import Thread from "./components/routes/Thread"; +import Header from "./components/modals/Header"; +import AddToHomeScreenPrompt from "./components/modals/CheckMobile/CheckMobile"; +import Notifications from "./components/routes/Notifications"; +import Hashtags from "./components/routes/Hashtags"; +import HashtagPage from "./components/routes/HashtagPage"; function App() { return ( @@ -21,7 +20,6 @@ function App() { } /> } /> } /> - {/* } /> */} diff --git a/client/src/components/Forms/PostFormCard.tsx b/client/src/components/Forms/PostFormCard.tsx index 1913362..46a55c3 100644 --- a/client/src/components/Forms/PostFormCard.tsx +++ b/client/src/components/Forms/PostFormCard.tsx @@ -8,10 +8,10 @@ import { XCircleIcon } from "@heroicons/react/24/solid"; import { useState, useEffect, useRef } from "react"; import { UnsignedEvent, Event as NostrEvent, nip19 } from "nostr-tools"; import { renderMedia, attachFile } from "../../utils/FileUpload"; -import { EmojiPicker } from "./Emojis/emoji-picker"; +import EmojiPicker from "@emoji-mart/react"; import customEmojis from './custom_emojis.json'; import { useSubmitForm } from "./handleSubmit"; -import "./Form.css"; +import "../../styles/Form.css"; interface FormProps { refEvent?: NostrEvent; diff --git a/client/src/components/Forms/RepostNote.tsx b/client/src/components/Forms/RepostNote.tsx index 151917e..388ab72 100644 --- a/client/src/components/Forms/RepostNote.tsx +++ b/client/src/components/Forms/RepostNote.tsx @@ -4,7 +4,7 @@ import { import { useState, useEffect } from "react"; import { UnsignedEvent, Event as NostrEvent, nip19 } from "nostr-tools"; import { useSubmitForm } from "./handleSubmit"; -import "./Form.css"; +import "../../styles/Form.css"; interface FormProps { refEvent: NostrEvent; diff --git a/client/src/components/HashtagPage.tsx b/client/src/components/HashtagPage.tsx deleted file mode 100644 index 764c513..0000000 --- a/client/src/components/HashtagPage.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { useEffect, useState, useCallback } from "react"; -import PostCard from "./Modals/NoteCard"; -import { uniqBy } from "../utils/otherUtils"; // Assume getPow is a correct import now -import { subHashtagFeed, subProfile} from "../utils/subscriptions"; -import { verifyPow } from "../utils/mine"; -import { Event, nip19 } from "nostr-tools"; -import NewNoteCard from "./Forms/PostFormCard"; -import RepostCard from "./Modals/RepostCard"; -import OptionsBar from "./Modals/OptionsBar"; -import { useParams } from "react-router-dom"; - -const DEFAULT_DIFFICULTY = 0; - -const useUniqEvents = (hashtag: string) => { - const [events, setEvents] = useState([]); - const age = Number(localStorage.getItem("age")) || 24; - - useEffect(() => { - const onEvent = (event: Event) => setEvents((prevEvents) => [...prevEvents, event]); - console.log(events) - const unsubscribe = subHashtagFeed(hashtag, onEvent, age); - - return unsubscribe; - }, [hashtag]); - - 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 HashtagPage = () => { - const { id } = useParams(); - const filterDifficulty = localStorage.getItem("filterHashtagDifficulty") || DEFAULT_DIFFICULTY; - const [sortByTime, setSortByTime] = useState(localStorage.getItem('sortBy') !== 'true'); - const [setAnon, setSetAnon] = useState(localStorage.getItem('anonMode') !== 'false'); - - const {noteEvents, metadataEvents } = useUniqEvents(id as string); - - const [delayedSort, setDelayedSort] = useState(false) - - const postEvents: Event[] = noteEvents - .filter((event) => - verifyPow(event) >= Number(filterDifficulty) && - event.kind !== 0 && - (event.kind !== 1 || !event.tags.some((tag) => tag[0] === "e")) - ) - - let sortedEvents = [...postEvents] - .sort((a, b) => { - // Sort by PoW in descending order - const powDiff = verifyPow(b) - verifyPow(a); - if (powDiff !== 0) return powDiff; - - // If PoW is the same, sort by created_at in descending order - return b.created_at - a.created_at; - }); - - if (delayedSort) { - sortedEvents = sortedEvents.filter( - !setAnon ? (e) => !metadataEvents.some((metadataEvent) => metadataEvent.pubkey === e.pubkey) : () => true - ); - } else { - sortedEvents = sortedEvents.filter((e) => setAnon || e.tags.some((tag) => tag[0] === "client" && tag[1] === 'getwired.app')); - } - - 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 HashtagPage; \ No newline at end of file diff --git a/client/src/components/Home.tsx b/client/src/components/Home.tsx deleted file mode 100644 index af0f05e..0000000 --- a/client/src/components/Home.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { useEffect, useState, useCallback, useMemo } 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"; - -const DEFAULT_DIFFICULTY = 20; - -const useUniqEvents = () => { - const [events, setEvents] = useState([]); - const age = Number(localStorage.getItem("age")) || 24; - - // Load cached metadataEvents from localStorage - const [cachedMetadataEvents, setCachedMetadataEvents] = useState( - JSON.parse(localStorage.getItem("cachedMetadataEvents") || "[]") - ); - - useEffect(() => { - const onEvent = (event: Event) => { - setEvents((prevEvents) => [...prevEvents, event]); - - // If the new event is a metadata event, add it to the cached metadata events - if (event.kind === 0) { - setCachedMetadataEvents((prevMetadataEvents) => { - // Check if the event already exists in the cached metadata events - const existingEvent = prevMetadataEvents.find((e) => e.id === event.id || e.pubkey === event.pubkey) - if (!existingEvent) { - // If the event doesn't exist, add it to the cached metadata events - return [...prevMetadataEvents, event]; - } else if (existingEvent && existingEvent.created_at < event.created_at) { - // Remove any existing metadata event with the same pubkey and id - const updatedMetadataEvents = prevMetadataEvents.filter( - (e) => e.id !== existingEvent.id - ); - // Add the new metadata event - return [...updatedMetadataEvents, event]; - } - // If the event already exists, return the previous cached metadata events - return prevMetadataEvents; - }); - } - }; - const unsubscribe = subGlobalFeed(onEvent, age); - - return unsubscribe; - }, []); - - const uniqEvents = uniqBy(events, "id"); - - const noteEvents = uniqEvents.filter(event => event.kind === 1 || event.kind === 6); - const metadataEvents = [...cachedMetadataEvents, ...uniqEvents.filter(event => event.kind === 0)]; - - // Save the cached metadataEvents to localStorage - useEffect(() => { - localStorage.setItem("cachedMetadataEvents", JSON.stringify(cachedMetadataEvents)); - }, [cachedMetadataEvents]); - return { noteEvents, metadataEvents }; -}; - -const Home = () => { - const filterDifficulty = localStorage.getItem("filterDifficulty") || DEFAULT_DIFFICULTY; - const [sortByTime, setSortByTime] = useState(localStorage.getItem('sortBy') !== 'true'); - const [setAnon, setSetAnon] = useState(localStorage.getItem('anonMode') !== 'true'); - const {noteEvents, metadataEvents } = useUniqEvents(); - const [delayedSort, setDelayedSort] = useState(false) - - const postEvents: Event[] = noteEvents - .filter((event) => - verifyPow(event) >= Number(filterDifficulty) && - event.kind !== 0 && - (event.kind !== 1 || !event.tags.some((tag) => tag[0] === "e" || tag[0] === "a")) - ) - - // Delayed filtering - useEffect(() => { - const timer = setTimeout(() => { - setDelayedSort(true); - }, 3000); - - return () => clearTimeout(timer); - }, []); - - let sortedEvents = [...postEvents] - .sort((a, b) => - sortByTime ? verifyPow(b) - verifyPow(a) : b.created_at - a.created_at - ) - - if (delayedSort) { - sortedEvents = sortedEvents.filter( - !setAnon ? (e) => !metadataEvents.some((metadataEvent) => metadataEvent.pubkey === e.pubkey) : () => true - ); - } else { - sortedEvents = sortedEvents.filter((e) => setAnon || e.tags.some((tag) => tag[0] === "client" && tag[1] === 'getwired.app')); - } - - 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 Home; diff --git a/client/src/components/Modals/CardModals/QuoteEmbed.tsx b/client/src/components/Modals/CardModals/QuoteEmbed.tsx index 3df95cf..bf50c53 100644 --- a/client/src/components/Modals/CardModals/QuoteEmbed.tsx +++ b/client/src/components/Modals/CardModals/QuoteEmbed.tsx @@ -1,6 +1,6 @@ import { parseContent } from "../../../utils/content"; import { Event } from "nostr-tools"; -import { getMetadata } from "../../../utils/otherUtils"; +import { getMetadata } from "../../../utils/getMetadata"; import ContentPreview from "./TextModal"; import { renderMedia } from "../../../utils/FileUpload"; import { getIconFromHash, timeAgo } from "../../../utils/cardUtils"; diff --git a/client/src/components/Modals/CardModals/TextModal.tsx b/client/src/components/Modals/CardModals/TextModal.tsx index 842be81..750875d 100644 --- a/client/src/components/Modals/CardModals/TextModal.tsx +++ b/client/src/components/Modals/CardModals/TextModal.tsx @@ -1,11 +1,11 @@ import React from "react"; -import QuoteEmbed from "./QuoteEmbed"; import { Event } from "nostr-tools"; import { useEffect, useState } from "react"; import { subNoteOnce } from "../../../utils/subscriptions"; import { nip19 } from "nostr-tools"; -import LinkModal from "./LinkPreview"; import { parseContent } from "../../../utils/content"; +import QuoteEmbed from "./QuoteEmbed"; +import LinkModal from "./LinkPreview"; const RichText = ({ text, isExpanded, emojiMap }: { text: string; isExpanded: boolean; emojiMap: Record }) => { const content = isExpanded ? text.split('\n') : text.slice(0, 350).split('\n'); diff --git a/client/src/components/Header/Header.tsx b/client/src/components/Modals/Header.tsx similarity index 100% rename from client/src/components/Header/Header.tsx rename to client/src/components/Modals/Header.tsx diff --git a/client/src/components/Modals/NoteCard.tsx b/client/src/components/Modals/NoteCard.tsx index 1d72b0e..71634f0 100644 --- a/client/src/components/Modals/NoteCard.tsx +++ b/client/src/components/Modals/NoteCard.tsx @@ -2,7 +2,7 @@ import CardContainer from "./CardContainer"; import { FolderIcon, CpuChipIcon } from "@heroicons/react/24/outline"; // import { parseContent } from "../../utils/content"; import { Event, nip19 } from "nostr-tools"; -import { getMetadata } from "../../utils/otherUtils"; +import { getMetadata } from "../../utils/getMetadata"; import ContentPreview from "./CardModals/TextModal"; // import { renderMedia } from "../../utils/FileUpload"; import { getIconFromHash, timeAgo } from "../../utils/cardUtils"; @@ -13,7 +13,7 @@ interface CardProps { key?: string | number; event: Event; metadata: Event | null; - replyCount: number; + replies: Event[]; repliedTo?: Event[] type?: 'OP' | 'Reply' | 'Post'; } @@ -22,7 +22,7 @@ const PostCard = ({ key, event, metadata, - replyCount, + replies, repliedTo, type }: CardProps) => { @@ -77,7 +77,7 @@ const PostCard = ({ ยท
- {replyCount} + {replies.length}
diff --git a/client/src/components/Modals/OptionsBar.tsx b/client/src/components/Modals/OptionsBar.tsx deleted file mode 100644 index 7c6d1de..0000000 --- a/client/src/components/Modals/OptionsBar.tsx +++ /dev/null @@ -1,57 +0,0 @@ -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/Modals/RepostCard.tsx b/client/src/components/Modals/RepostCard.tsx index 8b38128..163e5a3 100644 --- a/client/src/components/Modals/RepostCard.tsx +++ b/client/src/components/Modals/RepostCard.tsx @@ -2,7 +2,7 @@ import { CpuChipIcon } from "@heroicons/react/24/outline"; // import { parseContent } from "../../utils/content"; import { Event, nip19 } from "nostr-tools"; -import { getMetadata, Metadata } from "../../utils/otherUtils"; +import { getMetadata, Metadata } from "../../utils/getMetadata"; import ContentPreview from "./CardModals/TextModal"; // import { renderMedia } from "../../utils/FileUpload"; import { getIconFromHash, timeAgo } from "../../utils/cardUtils"; diff --git a/client/src/components/TestUI.tsx b/client/src/components/TestUI.tsx deleted file mode 100644 index 50598fb..0000000 --- a/client/src/components/TestUI.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useState } from "react"; -import Placeholder from "./Modals/Placeholder"; -import OptionsBar from "./Modals/OptionsBar"; - -const TestUI = () => { - const [sortByTime, setSortByTime] = useState(true); - - const toggleSort = () => { - setSortByTime(prev => !prev); - }; - return ( - <> - -
{/* This is the white line separator */} - - - ); - -}; - -export default TestUI; \ No newline at end of file diff --git a/client/src/components/routes/HashtagPage.tsx b/client/src/components/routes/HashtagPage.tsx new file mode 100644 index 0000000..4a25339 --- /dev/null +++ b/client/src/components/routes/HashtagPage.tsx @@ -0,0 +1,57 @@ +import PostCard from "../modals/NoteCard"; +import { verifyPow } from "../../utils/mine"; +import { Event } from "nostr-tools"; +import NewNoteCard from "../forms/PostFormCard"; +import RepostCard from "../modals/RepostCard"; +import { useParams } from "react-router-dom"; +import { useUniqEvents } from "../../hooks/useUniqEvents"; + +const DEFAULT_DIFFICULTY = 0; + +const HashtagPage = () => { + const { id } = useParams(); + const filterDifficulty = localStorage.getItem("filterHashtagDifficulty") || DEFAULT_DIFFICULTY; + const { noteEvents, metadataEvents } = useUniqEvents(id as string, false); + + const postEvents: Event[] = noteEvents + .filter((event) => + verifyPow(event) >= Number(filterDifficulty) && + event.kind !== 0 && + (event.kind !== 1 || !event.tags.some((tag) => tag[0] === "e")) + ) + + let sortedEvents = [...postEvents] + .sort((a, b) => { + // Sort by PoW in descending order + const powDiff = verifyPow(b) - verifyPow(a); + if (powDiff !== 0) return powDiff; + + // If PoW is the same, sort by created_at in descending order + return b.created_at - a.created_at; + }); + + // Render the component + return ( +
+
+ +
+
+ {sortedEvents.map((event) => ( + event.kind === 1 ? + e.pubkey === event.pubkey && e.kind === 0) || null} + replies={sortedEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id))} + /> + : + + ))} +
+
+ ); +}; + +export default HashtagPage; \ No newline at end of file diff --git a/client/src/components/Hashtags.tsx b/client/src/components/routes/Hashtags.tsx similarity index 100% rename from client/src/components/Hashtags.tsx rename to client/src/components/routes/Hashtags.tsx diff --git a/client/src/components/routes/Home.tsx b/client/src/components/routes/Home.tsx new file mode 100644 index 0000000..a2062f3 --- /dev/null +++ b/client/src/components/routes/Home.tsx @@ -0,0 +1,54 @@ +import PostCard from "../modals/NoteCard"; +import { verifyPow } from "../../utils/mine"; +import { Event } from "nostr-tools"; +import NewNoteCard from "../forms/PostFormCard"; +import RepostCard from "../modals/RepostCard"; +import { DEFAULT_DIFFICULTY } from "../../config"; +import { useUniqEvents } from "../../hooks/useUniqEvents"; + +const Home = () => { + const filterDifficulty = localStorage.getItem("filterDifficulty") || DEFAULT_DIFFICULTY; + const { noteEvents, metadataEvents } = useUniqEvents(); + + const postEvents: Event[] = noteEvents + .filter((event) => + verifyPow(event) >= Number(filterDifficulty) && + event.kind !== 0 && + (event.kind !== 1 || !event.tags.some((tag) => tag[0] === "e" || tag[0] === "a")) + ) + + let sortedEvents = [...postEvents] + .sort((a, b) => { + // Sort by PoW in descending order + const powDiff = verifyPow(b) - verifyPow(a); + if (powDiff !== 0) return powDiff; + + // If PoW is the same, sort by created_at in descending order + return b.created_at - a.created_at; + }); + + // Render the component + return ( +
+
+ +
+
+ {sortedEvents.map((event) => ( + event.kind === 1 ? + e.pubkey === event.pubkey && e.kind === 0) || null} + replies={sortedEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id))} + /> + : + + ))} +
+
+ ); +}; + +export default Home; diff --git a/client/src/components/Notifications.tsx b/client/src/components/routes/Notifications.tsx similarity index 53% rename from client/src/components/Notifications.tsx rename to client/src/components/routes/Notifications.tsx index 05519d8..f4373e2 100644 --- a/client/src/components/Notifications.tsx +++ b/client/src/components/routes/Notifications.tsx @@ -1,39 +1,12 @@ -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 { useState, useCallback } from "react"; +import PostCard from "../modals/NoteCard"; 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 }; -}; +import RepostCard from "../modals/RepostCard"; +import { useUniqEvents } from "../../hooks/useUniqEvents"; const Notifications = () => { - const [sortByTime, setSortByTime] = useState(localStorage.getItem('sortBy') !== 'false'); - const [setAnon, setSetAnon] = useState(localStorage.getItem('anonMode') !== 'false'); const [notifsView, setNotifsView] = useState(false); - const { noteEvents, metadataEvents } = useUniqEvents(); + const { noteEvents, metadataEvents } = useUniqEvents(undefined,true); const storedKeys = JSON.parse(localStorage.getItem('usedKeys') || '[]'); const storedPubkeys = storedKeys.map((key: any[]) => key[1]); @@ -45,11 +18,8 @@ const Notifications = () => { const sortedEvents = [...postEvents] .sort((a, b) => - sortByTime ? b.created_at - a.created_at : verifyPow(b) - verifyPow(a) + b.created_at - a.created_at ) - .filter( - !setAnon ? (e) => !metadataEvents.some((metadataEvent) => metadataEvent.pubkey === e.pubkey) : () => true - ); const mentions = noteEvents .filter((event) => @@ -59,40 +29,20 @@ const Notifications = () => { const sortedMentions = [...mentions] .sort((a, b) => - sortByTime ? b.created_at - a.created_at : verifyPow(b) - verifyPow(a) + b.created_at - a.created_at ) - .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 toggleNotifs = useCallback(() => { setNotifsView(prev => !prev); }, []); const countReplies = (event: Event) => { - return noteEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id)).length; + return noteEvents.filter((e) => e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id)); }; // Render the component return (
-
diff --git a/client/src/config.ts b/client/src/config.ts new file mode 100644 index 0000000..984a2f7 --- /dev/null +++ b/client/src/config.ts @@ -0,0 +1 @@ +export const DEFAULT_DIFFICULTY = 20; \ No newline at end of file diff --git a/client/src/hooks/useUniqEvents.ts b/client/src/hooks/useUniqEvents.ts new file mode 100644 index 0000000..5be3a16 --- /dev/null +++ b/client/src/hooks/useUniqEvents.ts @@ -0,0 +1,72 @@ +import { useState, useEffect } from "react"; +import { subGlobalFeed, subHashtagFeed, subNotifications } from "../utils/subscriptions"; +import { uniqBy } from "../utils/otherUtils"; +import { Event } from "nostr-tools"; +import { } from "../utils/subscriptions"; + +export const useUniqEvents = (hashtag?: string, notifications?: boolean) => { + const [events, setEvents] = useState([]); + const age = Number(localStorage.getItem("age")) || 24; + + // Load cached metadataEvents from localStorage + const [cachedMetadataEvents, setCachedMetadataEvents] = useState( + JSON.parse(localStorage.getItem("cachedMetadataEvents") || "[]") + ); + + useEffect(() => { + const onEvent = (event: Event) => { + setEvents((prevEvents) => [...prevEvents, event]); + + // If the new event is a metadata event, add it to the cached metadata events + if (event.kind === 0) { + setCachedMetadataEvents((prevMetadataEvents) => { + // Check if the event already exists in the cached metadata events + const existingEvent = prevMetadataEvents.find( + (e) => e.id === event.id || e.pubkey === event.pubkey + ); + if (!existingEvent) { + // If the event doesn't exist, add it to the cached metadata events + return [...prevMetadataEvents, event]; + } else if (existingEvent && existingEvent.created_at < event.created_at) { + // Remove any existing metadata event with the same pubkey and id + const updatedMetadataEvents = prevMetadataEvents.filter( + (e) => e.id !== existingEvent.id + ); + // Add the new metadata event + return [...updatedMetadataEvents, event]; + } + // If the event already exists, return the previous cached metadata events + return prevMetadataEvents; + }); + } + }; + + let unsubscribe; + if (hashtag) { + // Code from the second function + unsubscribe = subHashtagFeed(hashtag, onEvent, age); + } else if (notifications) { + // Code from the third function + let storedKeys = JSON.parse(localStorage.getItem("usedKeys") || "[]"); + let storedPubkeys = storedKeys.map((key: any[]) => key[1]); + unsubscribe = subNotifications(storedPubkeys, onEvent); + } else { + // Code from the first function + unsubscribe = subGlobalFeed(onEvent, age); + } + + return unsubscribe; + }, [hashtag]); + + const uniqEvents = uniqBy(events, "id"); + + const noteEvents = uniqEvents.filter((event) => event.kind === 1 || event.kind === 6); + const metadataEvents = [...cachedMetadataEvents, ...uniqEvents.filter((event) => event.kind === 0)]; + + // Save the cached metadataEvents to localStorage + useEffect(() => { + localStorage.setItem("cachedMetadataEvents", JSON.stringify(cachedMetadataEvents)); + }, [cachedMetadataEvents]); + + return { noteEvents, metadataEvents }; + }; \ No newline at end of file diff --git a/client/src/index.tsx b/client/src/index.tsx index 5cb7ff0..5a1f4ed 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import './index.css'; +import './styles/index.css'; import App from './App'; import * as serviceWorkerRegistration from './serviceWorkerRegistration'; diff --git a/client/src/App.css b/client/src/styles/App.css similarity index 100% rename from client/src/App.css rename to client/src/styles/App.css diff --git a/client/src/components/Forms/Form.css b/client/src/styles/Form.css similarity index 100% rename from client/src/components/Forms/Form.css rename to client/src/styles/Form.css diff --git a/client/src/index.css b/client/src/styles/index.css similarity index 100% rename from client/src/index.css rename to client/src/styles/index.css diff --git a/client/src/utils/getMetadata.ts b/client/src/utils/getMetadata.ts new file mode 100644 index 0000000..deecfae --- /dev/null +++ b/client/src/utils/getMetadata.ts @@ -0,0 +1,25 @@ +import { Event } from "nostr-tools" + +export interface Metadata { + name?: string + username?: string + display_name?: string + picture?: string + banner?: string + about?: string + website?: string + lud06?: string + lud16?: string + nip05?: string +} + +export const getMetadata = (event: Event) => { + try { + const content = event.content.replace(/[\n\r\t]/g, '') + const metadata: Metadata = JSON.parse(content) + return metadata + } catch (error) { + console.error(`Error parsing metadata for event: ${event.id}`, error) + return {} + } +} \ No newline at end of file diff --git a/client/src/utils/otherUtils.ts b/client/src/utils/otherUtils.ts index c368142..66fe14e 100644 --- a/client/src/utils/otherUtils.ts +++ b/client/src/utils/otherUtils.ts @@ -1,5 +1,3 @@ -import { Event } from "nostr-tools" - export const uniqBy = (arr: T[], key: keyof T): T[] => { return Object.values( arr.reduce( @@ -20,28 +18,4 @@ export const dateToUnix = (_date?: Date) => { const date = _date || new Date() return Math.floor(date.getTime() / 1000) -} - -export interface Metadata { - name?: string - username?: string - display_name?: string - picture?: string - banner?: string - about?: string - website?: string - lud06?: string - lud16?: string - nip05?: string -} - -export const getMetadata = (event: Event) => { - try { - const content = event.content.replace(/[\n\r\t]/g, '') - const metadata: Metadata = JSON.parse(content) - return metadata - } catch (error) { - console.error(`Error parsing metadata for event: ${event.id}`, error) - return {} - } } \ No newline at end of file