mirror of
https://github.com/smolgrrr/TAO.git
synced 2024-09-20 01:11:25 +00:00
pfp cache
This commit is contained in:
parent
7346368a2b
commit
6bea0bf8f1
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback, useMemo } from "react";
|
||||||
import PostCard from "./Modals/NoteCard";
|
import PostCard from "./Modals/NoteCard";
|
||||||
import { uniqBy } from "../utils/otherUtils"; // Assume getPow is a correct import now
|
import { uniqBy } from "../utils/otherUtils"; // Assume getPow is a correct import now
|
||||||
import { subGlobalFeed } from "../utils/subscriptions";
|
import { subGlobalFeed } from "../utils/subscriptions";
|
||||||
@ -14,8 +14,36 @@ const useUniqEvents = () => {
|
|||||||
const [events, setEvents] = useState<Event[]>([]);
|
const [events, setEvents] = useState<Event[]>([]);
|
||||||
const age = Number(localStorage.getItem("age")) || 24;
|
const age = Number(localStorage.getItem("age")) || 24;
|
||||||
|
|
||||||
|
// Load cached metadataEvents from localStorage
|
||||||
|
const [cachedMetadataEvents, setCachedMetadataEvents] = useState<Event[]>(
|
||||||
|
JSON.parse(localStorage.getItem("cachedMetadataEvents") || "[]")
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onEvent = (event: Event) => setEvents((prevEvents) => [...prevEvents, event]);
|
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);
|
const unsubscribe = subGlobalFeed(onEvent, age);
|
||||||
|
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
@ -24,14 +52,18 @@ const useUniqEvents = () => {
|
|||||||
const uniqEvents = uniqBy(events, "id");
|
const uniqEvents = uniqBy(events, "id");
|
||||||
|
|
||||||
const noteEvents = uniqEvents.filter(event => event.kind === 1 || event.kind === 6);
|
const noteEvents = uniqEvents.filter(event => event.kind === 1 || event.kind === 6);
|
||||||
const metadataEvents = uniqEvents.filter(event => event.kind === 0);
|
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 };
|
return { noteEvents, metadataEvents };
|
||||||
};
|
};
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
const filterDifficulty = localStorage.getItem("filterDifficulty") || DEFAULT_DIFFICULTY;
|
const filterDifficulty = localStorage.getItem("filterDifficulty") || DEFAULT_DIFFICULTY;
|
||||||
const [sortByTime, setSortByTime] = useState<boolean>(localStorage.getItem('sortBy') !== 'false');
|
const [sortByTime, setSortByTime] = useState<boolean>(localStorage.getItem('sortBy') !== 'true');
|
||||||
const [setAnon, setSetAnon] = useState<boolean>(localStorage.getItem('anonMode') !== 'true');
|
const [setAnon, setSetAnon] = useState<boolean>(localStorage.getItem('anonMode') !== 'true');
|
||||||
const {noteEvents, metadataEvents } = useUniqEvents();
|
const {noteEvents, metadataEvents } = useUniqEvents();
|
||||||
const [delayedSort, setDelayedSort] = useState(false)
|
const [delayedSort, setDelayedSort] = useState(false)
|
||||||
@ -54,7 +86,7 @@ const Home = () => {
|
|||||||
|
|
||||||
let sortedEvents = [...postEvents]
|
let sortedEvents = [...postEvents]
|
||||||
.sort((a, b) =>
|
.sort((a, b) =>
|
||||||
sortByTime ? b.created_at - a.created_at : verifyPow(b) - verifyPow(a)
|
sortByTime ? verifyPow(b) - verifyPow(a) : b.created_at - a.created_at
|
||||||
)
|
)
|
||||||
|
|
||||||
if (delayedSort) {
|
if (delayedSort) {
|
||||||
|
@ -1,68 +1,68 @@
|
|||||||
import { getLinkPreview } from 'link-preview-js';
|
// import { getLinkPreview } from 'link-preview-js';
|
||||||
import { useState, useEffect } from 'react';
|
// import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
|
||||||
const LinkModal = ({ url }: { url: string }) => {
|
const LinkModal = ({ url }: { url: string }) => {
|
||||||
const [linkPreview, setLinkPreview] = useState<LinkPreview | null>(null);
|
// const [linkPreview, setLinkPreview] = useState<LinkPreview | null>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
// const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
const fetchWithProxy = (url: string) => {
|
// const fetchWithProxy = (url: string) => {
|
||||||
const proxyUrl = 'https://api.allorigins.win/raw?url=';
|
// const proxyUrl = 'https://api.allorigins.win/raw?url=';
|
||||||
return getLinkPreview(proxyUrl + url)
|
// return getLinkPreview(proxyUrl + url)
|
||||||
.then((preview) => setLinkPreview(preview as LinkPreview))
|
// .then((preview) => setLinkPreview(preview as LinkPreview))
|
||||||
.catch((error) => {
|
// .catch((error) => {
|
||||||
console.error("Error fetching URL with proxy:", error);
|
// console.error("Error fetching URL with proxy:", error);
|
||||||
setError('Unable to fetch URL with proxy.');
|
// setError('Unable to fetch URL with proxy.');
|
||||||
});
|
// });
|
||||||
};
|
// };
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
getLinkPreview(url)
|
// getLinkPreview(url)
|
||||||
.then((preview) => setLinkPreview(preview as LinkPreview))
|
// .then((preview) => setLinkPreview(preview as LinkPreview))
|
||||||
.catch(error => {
|
// .catch(error => {
|
||||||
console.error("Error fetching original URL, trying with proxy:", error);
|
// console.error("Error fetching original URL, trying with proxy:", error);
|
||||||
setError('Error fetching original URL. Trying with proxy...');
|
// setError('Error fetching original URL. Trying with proxy...');
|
||||||
return fetchWithProxy(url);
|
// return fetchWithProxy(url);
|
||||||
});
|
// });
|
||||||
}, [url]);
|
// }, [url]);
|
||||||
|
|
||||||
if (error) {
|
// if (error) {
|
||||||
|
// return <a className='hover:underline text-xs text-neutral-500' href={url}>{url}</a>; // or some loading state
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!linkPreview) {
|
||||||
return <a className='hover:underline text-xs text-neutral-500' href={url}>{url}</a>; // or some loading state
|
return <a className='hover:underline text-xs text-neutral-500' href={url}>{url}</a>; // or some loading state
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (!linkPreview) {
|
// return (
|
||||||
return <a className='hover:underline text-xs text-neutral-500' href={url}>{url}</a>; // or some loading state
|
// <div className="link-preview p-1 bg-neutral-800 rounded-lg border border-neutral-800">
|
||||||
}
|
// <a href={linkPreview.url} target="_blank" rel="noopener noreferrer" className="">
|
||||||
|
// <img src={linkPreview.images[0]} alt={linkPreview.title} className="rounded-lg" />
|
||||||
return (
|
// <div className="font-semibold text-xs text-gray-300">
|
||||||
<div className="link-preview p-1 bg-neutral-800 rounded-lg border border-neutral-800">
|
// {linkPreview.title}
|
||||||
<a href={linkPreview.url} target="_blank" rel="noopener noreferrer" className="">
|
// </div>
|
||||||
<img src={linkPreview.images[0]} alt={linkPreview.title} className="rounded-lg" />
|
// </a>
|
||||||
<div className="font-semibold text-xs text-gray-300">
|
// </div>
|
||||||
{linkPreview.title}
|
// );
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface LinkPreview {
|
// interface LinkPreview {
|
||||||
url: string;
|
// url: string;
|
||||||
title: string;
|
// title: string;
|
||||||
siteName?: string;
|
// siteName?: string;
|
||||||
description?: string;
|
// description?: string;
|
||||||
mediaType: string;
|
// mediaType: string;
|
||||||
contentType?: string;
|
// contentType?: string;
|
||||||
images: string[];
|
// images: string[];
|
||||||
videos: {
|
// videos: {
|
||||||
url?: string;
|
// url?: string;
|
||||||
secureUrl?: string;
|
// secureUrl?: string;
|
||||||
type?: string;
|
// type?: string;
|
||||||
width?: string;
|
// width?: string;
|
||||||
height?: string;
|
// height?: string;
|
||||||
[key: string]: any;
|
// [key: string]: any;
|
||||||
}[];
|
// }[];
|
||||||
[key: string]: any;
|
// [key: string]: any;
|
||||||
}
|
// }
|
||||||
|
|
||||||
export default LinkModal;
|
export default LinkModal;
|
@ -85,7 +85,7 @@ const ContentPreview = ({ key, eventdata }: { key: string; eventdata: Event }) =
|
|||||||
{isExpanded ? "...Read less" : "...Read more"}
|
{isExpanded ? "...Read less" : "...Read more"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{/* {url !== "" && <LinkModal key={key} url={url} />} */}
|
{url !== "" && <LinkModal key={key} url={url} />}
|
||||||
{quoteEvents[0] && quoteEvents.length > 0 && (
|
{quoteEvents[0] && quoteEvents.length > 0 && (
|
||||||
<a href={`/thread/${nip19.noteEncode(quoteEvents[0].id)}`}>
|
<a href={`/thread/${nip19.noteEncode(quoteEvents[0].id)}`}>
|
||||||
<QuoteEmbed
|
<QuoteEmbed
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import CardContainer from "./CardContainer";
|
import CardContainer from "./CardContainer";
|
||||||
import { FolderIcon, CpuChipIcon } from "@heroicons/react/24/outline";
|
import { FolderIcon, CpuChipIcon } from "@heroicons/react/24/outline";
|
||||||
import { parseContent } from "../../utils/content";
|
// import { parseContent } from "../../utils/content";
|
||||||
import { Event, nip19 } from "nostr-tools";
|
import { Event, nip19 } from "nostr-tools";
|
||||||
import { getMetadata } from "../../utils/otherUtils";
|
import { getMetadata } from "../../utils/otherUtils";
|
||||||
import ContentPreview from "./CardModals/TextModal";
|
import ContentPreview from "./CardModals/TextModal";
|
||||||
import { renderMedia } from "../../utils/FileUpload";
|
// import { renderMedia } from "../../utils/FileUpload";
|
||||||
import { getIconFromHash, timeAgo } from "../../utils/cardUtils";
|
import { getIconFromHash, timeAgo } from "../../utils/cardUtils";
|
||||||
import { verifyPow } from "../../utils/mine";
|
import { verifyPow } from "../../utils/mine";
|
||||||
import { uniqBy } from "../../utils/otherUtils";
|
import { uniqBy } from "../../utils/otherUtils";
|
||||||
@ -26,7 +26,7 @@ const PostCard = ({
|
|||||||
repliedTo,
|
repliedTo,
|
||||||
type
|
type
|
||||||
}: CardProps) => {
|
}: CardProps) => {
|
||||||
const { files } = parseContent(event);
|
// const { files } = parseContent(event);
|
||||||
const icon = getIconFromHash(event.pubkey);
|
const icon = getIconFromHash(event.pubkey);
|
||||||
const metadataParsed = metadata ? getMetadata(metadata) : null;
|
const metadataParsed = metadata ? getMetadata(metadata) : null;
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ const OptionsBar: React.FC<OptionsBarProps> = ({ sortByTime, setAnon, toggleSort
|
|||||||
<div className={`dot absolute left-1 top-0.5 bg-white w-3 h-3 rounded-full transition ${sortByTime ? 'transform translate-x-full bg-blue-400' : ''}`} ></div>
|
<div className={`dot absolute left-1 top-0.5 bg-white w-3 h-3 rounded-full transition ${sortByTime ? 'transform translate-x-full bg-blue-400' : ''}`} ></div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`ml-2 text-neutral-500 text-sm ${sortByTime ? 'text-neutral-500' : ''}`}>
|
<div className={`ml-2 text-neutral-500 text-sm ${sortByTime ? 'text-neutral-500' : ''}`}>
|
||||||
{sortByTime ? 'Time' : 'PoW'}
|
{sortByTime ? 'PoW' : 'Time'}
|
||||||
</div>
|
</div>
|
||||||
</label>}
|
</label>}
|
||||||
{toggleAnon && <label htmlFor="toggleB" className="flex items-center cursor-pointer ml-4"> {/* Add margin-left here */}
|
{toggleAnon && <label htmlFor="toggleB" className="flex items-center cursor-pointer ml-4"> {/* Add margin-left here */}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import CardContainer from "./CardContainer";
|
// import CardContainer from "./CardContainer";
|
||||||
import { CpuChipIcon, ArrowPathRoundedSquareIcon } from "@heroicons/react/24/outline";
|
import { CpuChipIcon } from "@heroicons/react/24/outline";
|
||||||
import { parseContent } from "../../utils/content";
|
// import { parseContent } from "../../utils/content";
|
||||||
import { Event as NostrEvent, nip19 } from "nostr-tools";
|
import { Event, nip19 } from "nostr-tools";
|
||||||
import { getMetadata, Metadata } from "../../utils/otherUtils";
|
import { getMetadata, Metadata } from "../../utils/otherUtils";
|
||||||
import ContentPreview from "./CardModals/TextModal";
|
import ContentPreview from "./CardModals/TextModal";
|
||||||
import { renderMedia } from "../../utils/FileUpload";
|
// import { renderMedia } from "../../utils/FileUpload";
|
||||||
import { getIconFromHash, timeAgo } from "../../utils/cardUtils";
|
import { getIconFromHash, timeAgo } from "../../utils/cardUtils";
|
||||||
import { verifyPow } from "../../utils/mine";
|
import { verifyPow } from "../../utils/mine";
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
@ -13,7 +13,7 @@ import { useEffect, useState } from "react";
|
|||||||
|
|
||||||
interface RepostProps {
|
interface RepostProps {
|
||||||
key?: string | number;
|
key?: string | number;
|
||||||
event: NostrEvent;
|
event: Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RepostCard = ({
|
const RepostCard = ({
|
||||||
@ -21,15 +21,40 @@ const RepostCard = ({
|
|||||||
event
|
event
|
||||||
}: RepostProps) => {
|
}: RepostProps) => {
|
||||||
const repostedEvent = JSON.parse(event.content);
|
const repostedEvent = JSON.parse(event.content);
|
||||||
const { files } = parseContent(repostedEvent);
|
// const { files } = parseContent(repostedEvent);
|
||||||
const icon = getIconFromHash(event.pubkey);
|
const icon = getIconFromHash(event.pubkey);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [cachedMetadataEvents, setCachedMetadataEvents] = useState<Event[]>(
|
||||||
|
JSON.parse(localStorage.getItem("cachedMetadataEvents") || "[]")
|
||||||
|
);
|
||||||
const [metadata, setMetadata] = useState<Metadata>()
|
const [metadata, setMetadata] = useState<Metadata>()
|
||||||
|
|
||||||
// Define your callback function for subGlobalFeed
|
// Define your callback function for subGlobalFeed
|
||||||
const onEvent = (event: NostrEvent, relay: string) => {
|
const onEvent = (event: Event, relay: string) => {
|
||||||
if (event.kind === 0 && event.pubkey === repostedEvent.pubkey && metadata == null) {
|
const existingEvent = cachedMetadataEvents.find((e) => e.pubkey === event.pubkey)
|
||||||
|
if (existingEvent) {
|
||||||
|
setMetadata(getMetadata(existingEvent))
|
||||||
|
}
|
||||||
|
else if (!existingEvent && event.kind === 0 && event.pubkey === repostedEvent.pubkey && metadata == null) {
|
||||||
setMetadata(getMetadata(event))
|
setMetadata(getMetadata(event))
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { Event, nip19 } from "nostr-tools"
|
|||||||
import { subNote, subNotesOnce } from '../utils/subscriptions';
|
import { subNote, subNotesOnce } from '../utils/subscriptions';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { uniqBy } from '../utils/otherUtils';
|
import { uniqBy } from '../utils/otherUtils';
|
||||||
import { DocumentTextIcon, FolderPlusIcon, DocumentDuplicateIcon } from '@heroicons/react/24/outline';
|
import { DocumentTextIcon, FolderPlusIcon, DocumentDuplicateIcon, ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline';
|
||||||
import { getPow } from '../utils/mine';
|
import { getPow } from '../utils/mine';
|
||||||
import PostCard from './Modals/NoteCard';
|
import PostCard from './Modals/NoteCard';
|
||||||
import Placeholder from './Modals/Placeholder';
|
import Placeholder from './Modals/Placeholder';
|
||||||
@ -124,7 +124,7 @@ const Thread = () => {
|
|||||||
))}
|
))}
|
||||||
{OPEvent && <PostCard event={OPEvent} metadata={getMetadataEvent(OPEvent)} replyCount={countReplies(OPEvent)} type={'OP'}/>}
|
{OPEvent && <PostCard event={OPEvent} metadata={getMetadataEvent(OPEvent)} replyCount={countReplies(OPEvent)} type={'OP'}/>}
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-full flex justify-center space-x-36 pb-4">
|
<div className="col-span-full flex justify-center space-x-16 pb-4">
|
||||||
<DocumentTextIcon
|
<DocumentTextIcon
|
||||||
className="h-5 w-5 text-gray-200 cursor-pointer"
|
className="h-5 w-5 text-gray-200 cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -148,6 +148,11 @@ const Thread = () => {
|
|||||||
setShowRepost(false)
|
setShowRepost(false)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<a href={`nostr:${id}`} target="_blank" rel="noopener noreferrer">
|
||||||
|
<ArrowTopRightOnSquareIcon
|
||||||
|
className="h-5 w-5 text-gray-200 cursor-pointer"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{(showForm && postType) &&
|
{(showForm && postType) &&
|
||||||
<div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
<div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
||||||
|
@ -36,7 +36,12 @@ export interface Metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getMetadata = (event: Event) => {
|
export const getMetadata = (event: Event) => {
|
||||||
const content = event.content.replace(/[\n\r\t]/g, '')
|
try {
|
||||||
const metadata: Metadata = JSON.parse(content)
|
const content = event.content.replace(/[\n\r\t]/g, '')
|
||||||
return metadata
|
const metadata: Metadata = JSON.parse(content)
|
||||||
|
return metadata
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error parsing metadata for event: ${event.id}`, error)
|
||||||
|
return {}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user