diff --git a/client/src/App.tsx b/client/src/App.tsx index 43063eb..5b29dc6 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,17 +1,17 @@ -import React from 'react'; -import './App.css'; -import Home from './components/Home'; -import Settings from './components/Settings'; -import SwipeableViews from 'react-swipeable-views'; -import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; -import Thread from './components/Thread/Thread'; -import Header from './components/Header/Header'; +import React from "react"; +import "./App.css"; +import Home from "./components/Home"; +import Settings from "./components/Settings"; +import SwipeableViews from "react-swipeable-views"; +import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; +import Thread from "./components/Thread/Thread"; +import Header from "./components/Header/Header"; function App() { const [index, setIndex] = React.useState(1); const handleChangeIndex = (index: number) => { - console.log("Changed index to:", index); // Add a log to see if this function is called + console.log("Changed index to:", index); // Add a log to see if this function is called setIndex(index); }; @@ -21,17 +21,23 @@ function App() { } /> } /> - } /> - - - - - } /> + } /> + + + + + } + /> ); } export default App; - diff --git a/client/src/components/Header/Header.tsx b/client/src/components/Header/Header.tsx index 53539a7..e63c4f8 100644 --- a/client/src/components/Header/Header.tsx +++ b/client/src/components/Header/Header.tsx @@ -1,25 +1,32 @@ -import { Cog6ToothIcon, QuestionMarkCircleIcon } from '@heroicons/react/24/outline'; +import { + Cog6ToothIcon, + QuestionMarkCircleIcon, +} from "@heroicons/react/24/outline"; export default function Header() { - return ( -
-
- -
- - The Anon Operation -
-
- - - - - -
-
- ); -} \ No newline at end of file + return ( +
+
+ +
+ logo + + The Anon Operation + +
+
+ + + + +
+
+ ); +} diff --git a/client/src/components/Home.tsx b/client/src/components/Home.tsx index 83c25ba..fb1672d 100644 --- a/client/src/components/Home.tsx +++ b/client/src/components/Home.tsx @@ -1,16 +1,18 @@ -import { useEffect, useState } from 'react'; -import PostCard from './PostCard/PostCard'; -import NewThreadCard from './PostCard/NewThreadCard'; -import { getPow } from '../utils/mine'; -import { Event } from 'nostr-tools'; -import { subGlobalFeed } from '../utils/subscriptions'; -import { uniqBy } from '../utils/utils'; -import PWAInstallPopup from './Modals/PWACheckModal'; +import { useEffect, useState } from "react"; +import PostCard from "./PostCard/PostCard"; +import NewThreadCard from "./PostCard/NewThreadCard"; +import { getPow } from "../utils/mine"; +import { Event } from "nostr-tools"; +import { subGlobalFeed } from "../utils/subscriptions"; +import { uniqBy } from "../utils/utils"; +import PWAInstallPopup from "./Modals/PWACheckModal"; const Home = () => { const [events, setEvents] = useState([]); - const [filterDifficulty, setFilterDifficulty] = useState(localStorage.getItem('filterDifficulty') || '20'); - const [inBrowser, setInBrowser] = useState(false) + const [filterDifficulty, setFilterDifficulty] = useState( + localStorage.getItem("filterDifficulty") || "20" + ); + const [inBrowser, setInBrowser] = useState(false); const onEvent = (event: Event) => { setEvents((prevEvents) => [...prevEvents, event]); @@ -30,46 +32,57 @@ const Home = () => { // console.log('App is running in standalone mode.'); // } else { // console.log('App is running in a browser.'); - // setInBrowser(true) + // setInBrowser(true) // } - - window.addEventListener('difficultyChanged', handleDifficultyChange); - + + window.addEventListener("difficultyChanged", handleDifficultyChange); + return () => { - window.removeEventListener('difficultyChanged', handleDifficultyChange); + window.removeEventListener("difficultyChanged", handleDifficultyChange); }; }, []); const uniqEvents = events.length > 0 ? uniqBy(events, "id") : []; - + const filteredAndSortedEvents = uniqEvents - .filter(event => - getPow(event.id) > Number(filterDifficulty) && - event.kind === 1 && - !event.tags.some(tag => tag[0] === 'e') + .filter( + (event) => + getPow(event.id) > Number(filterDifficulty) && + event.kind === 1 && + !event.tags.some((tag) => tag[0] === "e") ) .sort((a, b) => (b.created_at as any) - (a.created_at as any)); - const getMetadataEvent = (event: Event) => { - const metadataEvent = uniqEvents.find(e => e.pubkey === event.pubkey && e.kind === 0); + const metadataEvent = uniqEvents.find( + (e) => e.pubkey === event.pubkey && e.kind === 0 + ); if (metadataEvent) { return metadataEvent; } return null; - } + }; const countReplies = (event: Event) => { - return uniqEvents.filter(e => e.tags.some(tag => tag[0] === 'e' && tag[1] === event.id)).length; - } + return uniqEvents.filter((e) => + e.tags.some((tag) => tag[0] === "e" && tag[1] === event.id) + ).length; + }; return ( -
+
{/* {inBrowser && setInBrowser(false)} />} */} -
+
- {filteredAndSortedEvents.map((event, index) => ( - +
+
+ {filteredAndSortedEvents.map((event) => ( + ))}
diff --git a/client/src/components/Modals/QuoteEmbed.tsx b/client/src/components/Modals/QuoteEmbed.tsx index 327718d..0c31361 100644 --- a/client/src/components/Modals/QuoteEmbed.tsx +++ b/client/src/components/Modals/QuoteEmbed.tsx @@ -1,25 +1,25 @@ -import { parseContent } from '../../utils/content'; -import { Event } from 'nostr-tools'; -import { getMetadata, uniqBy } from '../../utils/utils'; -import ContentPreview from './TextModal'; -import { renderMedia } from '../../utils/FileUpload'; +import { parseContent } from "../../utils/content"; +import { Event } from "nostr-tools"; +import { getMetadata, uniqBy } from "../../utils/utils"; +import ContentPreview from "./TextModal"; +import { renderMedia } from "../../utils/FileUpload"; const colorCombos = [ - 'from-red-400 to-yellow-500', - 'from-green-400 to-blue-500', - 'from-purple-400 to-pink-500', - 'from-yellow-400 to-orange-500', - 'from-indigo-400 to-purple-500', - 'from-pink-400 to-red-500', - 'from-blue-400 to-indigo-500', - 'from-orange-400 to-red-500', - 'from-teal-400 to-green-500', - 'from-cyan-400 to-teal-500', - 'from-lime-400 to-green-500', - 'from-amber-400 to-orange-500', - 'from-rose-400 to-pink-500', - 'from-violet-400 to-purple-500', - 'from-sky-400 to-cyan-500' + "from-red-400 to-yellow-500", + "from-green-400 to-blue-500", + "from-purple-400 to-pink-500", + "from-yellow-400 to-orange-500", + "from-indigo-400 to-purple-500", + "from-pink-400 to-red-500", + "from-blue-400 to-indigo-500", + "from-orange-400 to-red-500", + "from-teal-400 to-green-500", + "from-cyan-400 to-teal-500", + "from-lime-400 to-green-500", + "from-amber-400 to-orange-500", + "from-rose-400 to-pink-500", + "from-violet-400 to-purple-500", + "from-sky-400 to-cyan-500", ]; const getColorFromHash = (id: string, colors: string[]): string => { @@ -35,10 +35,10 @@ const getColorFromHash = (id: string, colors: string[]): string => { }; const timeAgo = (unixTime: number) => { - const seconds = Math.floor((new Date().getTime() / 1000) - unixTime); - + const seconds = Math.floor(new Date().getTime() / 1000 - unixTime); + if (seconds < 60) return `now`; - + const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${minutes}m`; @@ -52,40 +52,52 @@ const timeAgo = (unixTime: number) => { return `${weeks}w`; }; -const QuoteEmbed = ({ event, metadata }: { event: Event, metadata: Event | null}) => { - const { comment, file } = parseContent(event); - const colorCombo = getColorFromHash(event.pubkey, colorCombos); +const QuoteEmbed = ({ + event, + metadata, +}: { + event: Event; + metadata: Event | null; +}) => { + const { comment, file } = parseContent(event); + const colorCombo = getColorFromHash(event.pubkey, colorCombos); - let metadataParsed = null; - if (metadata !== null) { - metadataParsed = getMetadata(metadata); - } + let metadataParsed = null; + if (metadata !== null) { + metadataParsed = getMetadata(metadata); + } return ( -
-
-
-
- {metadataParsed ? +
+
+
+
+ {metadataParsed ? ( <> - -
{metadataParsed.name}
+ +
{metadataParsed.name}
- : + ) : ( <> -
-
Anonymous
+
+
Anonymous
- } -
+ )}
-
- -
- {renderMedia(file)}
+
+ +
+ {renderMedia(file)} +
); }; -export default QuoteEmbed; \ No newline at end of file +export default QuoteEmbed; diff --git a/client/src/components/Modals/TextModal.tsx b/client/src/components/Modals/TextModal.tsx index 52444fe..9e21aa7 100644 --- a/client/src/components/Modals/TextModal.tsx +++ b/client/src/components/Modals/TextModal.tsx @@ -1,15 +1,15 @@ import QuoteEmbed from "./QuoteEmbed"; -import { Event } from 'nostr-tools'; +import { Event } from "nostr-tools"; import { useEffect, useState } from "react"; -import { subNoteOnce } from '../../utils/subscriptions'; +import { subNoteOnce } from "../../utils/subscriptions"; import { nip19 } from "nostr-tools"; import LinkModal from "./LinkPreview"; -const ContentPreview = ({ key, comment }: { key: string, comment: string }) => { - const [finalComment, setFinalComment] = useState(comment) +const ContentPreview = ({ key, comment }: { key: string; comment: string }) => { + const [finalComment, setFinalComment] = useState(comment); const [quoteEvents, setQuoteEvents] = useState([]); // Initialize state const [isExpanded, setIsExpanded] = useState(false); - const [url, setUrl] = useState('') + const [url, setUrl] = useState(""); // Define your callback function for subGlobalFeed const onEvent = (event: Event, relay: string) => { @@ -19,8 +19,8 @@ const ContentPreview = ({ key, comment }: { key: string, comment: string }) => { useEffect(() => { const findUrl = comment.match(/\bhttps?:\/\/\S+/gi); if (findUrl && findUrl.length > 0) { - setUrl(findUrl[0]) - setFinalComment(finalComment.replace(findUrl[0], '').trim()) + setUrl(findUrl[0]); + setFinalComment(finalComment.replace(findUrl[0], "").trim()); } const match = comment.match(/\bnostr:([a-z0-9]+)/i); @@ -28,39 +28,43 @@ const ContentPreview = ({ key, comment }: { key: string, comment: string }) => { if (nostrQuoteID && nostrQuoteID.length > 0) { let id_to_hex = String(nip19.decode(nostrQuoteID as string).data); subNoteOnce(id_to_hex, onEvent); - setFinalComment(finalComment.replace('nostr:' + nostrQuoteID, '').trim()) + setFinalComment(finalComment.replace("nostr:" + nostrQuoteID, "").trim()); } }, [comment, finalComment]); const getMetadataEvent = (event: Event) => { - const metadataEvent = quoteEvents.find(e => e.pubkey === event.pubkey && e.kind === 0); + const metadataEvent = quoteEvents.find( + (e) => e.pubkey === event.pubkey && e.kind === 0 + ); if (metadataEvent) { return metadataEvent; } return null; - } + }; return ( -
+
{isExpanded ? finalComment : finalComment.slice(0, 240)} {finalComment.length > 240 && ( )} - {url !== '' && ( - - )} + {url !== "" && } {quoteEvents[0] && quoteEvents.length > 0 && ( - + )}
); -} +}; export default ContentPreview; diff --git a/client/src/components/PostCard/CardContainer.tsx b/client/src/components/PostCard/CardContainer.tsx index cc0467b..c9829cb 100644 --- a/client/src/components/PostCard/CardContainer.tsx +++ b/client/src/components/PostCard/CardContainer.tsx @@ -1,9 +1,9 @@ -import { PropsWithChildren } from 'react'; +import { PropsWithChildren } from "react"; export default function CardContainer({ children }: PropsWithChildren) { return ( -
-
{children}
+
+
{children}
); -} \ No newline at end of file +} diff --git a/client/src/components/PostCard/NewThreadCard.tsx b/client/src/components/PostCard/NewThreadCard.tsx index 2781461..f89b85d 100644 --- a/client/src/components/PostCard/NewThreadCard.tsx +++ b/client/src/components/PostCard/NewThreadCard.tsx @@ -1,22 +1,31 @@ -import CardContainer from './CardContainer'; -import { ArrowUpTrayIcon, CpuChipIcon, ArrowPathIcon } from '@heroicons/react/24/outline'; -import { XCircleIcon } from '@heroicons/react/24/solid'; -import { useState, useEffect, useMemo } from 'react'; -import { generatePrivateKey, getPublicKey, finishEvent } from 'nostr-tools'; -import { publish } from '../../utils/relays'; -import FileUpload from '../../utils/FileUpload'; -import { renderMedia } from '../../utils/FileUpload'; +import CardContainer from "./CardContainer"; +import { + ArrowUpTrayIcon, + CpuChipIcon, + ArrowPathIcon, +} from "@heroicons/react/24/outline"; +import { XCircleIcon } from "@heroicons/react/24/solid"; +import { useState, useEffect, useMemo } from "react"; +import { generatePrivateKey, getPublicKey, finishEvent } from "nostr-tools"; +import { publish } from "../../utils/relays"; +import FileUpload from "../../utils/FileUpload"; +import { renderMedia } from "../../utils/FileUpload"; const NewThreadCard: React.FC = () => { const [comment, setComment] = useState(""); const [file, setFile] = useState(""); const [sk, setSk] = useState(generatePrivateKey()); - const [difficulty, setDifficulty] = useState(localStorage.getItem('difficulty') || '21'); + const [difficulty, setDifficulty] = useState( + localStorage.getItem("difficulty") || "21" + ); const [uploadingFile, setUploadingFile] = useState(false); const [messageFromWorker, setMessageFromWorker] = useState(null); const [doingWorkProp, setDoingWorkProp] = useState(false); // Initialize the worker outside of any effects - const worker = useMemo(() => new Worker(new URL('../../powWorker', import.meta.url)), []); + const worker = useMemo( + () => new Worker(new URL("../../powWorker", import.meta.url)), + [] + ); useEffect(() => { worker.onmessage = (event) => { @@ -29,10 +38,10 @@ const NewThreadCard: React.FC = () => { setDifficulty(difficulty); }; - window.addEventListener('difficultyChanged', handleDifficultyChange); + window.addEventListener("difficultyChanged", handleDifficultyChange); return () => { - window.removeEventListener('difficultyChanged', handleDifficultyChange); + window.removeEventListener("difficultyChanged", handleDifficultyChange); }; }, []); @@ -46,12 +55,12 @@ const NewThreadCard: React.FC = () => { created_at: Math.floor(Date.now() / 1000), pubkey: getPublicKey(sk), }, - difficulty + difficulty, }); }; useEffect(() => { - setDoingWorkProp(false) + setDoingWorkProp(false); if (messageFromWorker) { try { const signedEvent = finishEvent(messageFromWorker, sk); @@ -66,17 +75,17 @@ const NewThreadCard: React.FC = () => { worker.terminate(); }; } catch (error) { - setComment(error + ' ' + comment); + setComment(error + " " + comment); } } }, [messageFromWorker]); async function attachFile(file_input: File | null) { - setUploadingFile(true); // start loading + setUploadingFile(true); // start loading try { if (file_input) { const rx = await FileUpload(file_input); - setUploadingFile(false); // stop loading + setUploadingFile(false); // stop loading if (rx.url) { setFile(rx.url); } else if (rx?.error) { @@ -84,87 +93,101 @@ const NewThreadCard: React.FC = () => { } } } catch (error: unknown) { - setUploadingFile(false); // stop loading + setUploadingFile(false); // stop loading if (error instanceof Error) { setFile(error?.message); } } } - return ( - <> - -
{ - handleSubmit(event); - setDoingWorkProp(true); - }} - > - -