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 { uniqBy } from "../utils/otherUtils"; // Assume getPow is a correct import now
|
||||
import { subGlobalFeed } from "../utils/subscriptions";
|
||||
@ -14,8 +14,36 @@ const useUniqEvents = () => {
|
||||
const [events, setEvents] = useState<Event[]>([]);
|
||||
const age = Number(localStorage.getItem("age")) || 24;
|
||||
|
||||
// Load cached metadataEvents from localStorage
|
||||
const [cachedMetadataEvents, setCachedMetadataEvents] = useState<Event[]>(
|
||||
JSON.parse(localStorage.getItem("cachedMetadataEvents") || "[]")
|
||||
);
|
||||
|
||||
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);
|
||||
|
||||
return unsubscribe;
|
||||
@ -24,14 +52,18 @@ const useUniqEvents = () => {
|
||||
const uniqEvents = uniqBy(events, "id");
|
||||
|
||||
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 };
|
||||
};
|
||||
|
||||
const Home = () => {
|
||||
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 {noteEvents, metadataEvents } = useUniqEvents();
|
||||
const [delayedSort, setDelayedSort] = useState(false)
|
||||
@ -54,7 +86,7 @@ const Home = () => {
|
||||
|
||||
let sortedEvents = [...postEvents]
|
||||
.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) {
|
||||
|
@ -1,68 +1,68 @@
|
||||
import { getLinkPreview } from 'link-preview-js';
|
||||
import { useState, useEffect } from 'react';
|
||||
// import { getLinkPreview } from 'link-preview-js';
|
||||
// import { useState, useEffect } from 'react';
|
||||
|
||||
|
||||
const LinkModal = ({ url }: { url: string }) => {
|
||||
const [linkPreview, setLinkPreview] = useState<LinkPreview | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
// const [linkPreview, setLinkPreview] = useState<LinkPreview | null>(null);
|
||||
// const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchWithProxy = (url: string) => {
|
||||
const proxyUrl = 'https://api.allorigins.win/raw?url=';
|
||||
return getLinkPreview(proxyUrl + url)
|
||||
.then((preview) => setLinkPreview(preview as LinkPreview))
|
||||
.catch((error) => {
|
||||
console.error("Error fetching URL with proxy:", error);
|
||||
setError('Unable to fetch URL with proxy.');
|
||||
});
|
||||
// const fetchWithProxy = (url: string) => {
|
||||
// const proxyUrl = 'https://api.allorigins.win/raw?url=';
|
||||
// return getLinkPreview(proxyUrl + url)
|
||||
// .then((preview) => setLinkPreview(preview as LinkPreview))
|
||||
// .catch((error) => {
|
||||
// console.error("Error fetching URL with proxy:", error);
|
||||
// setError('Unable to fetch URL with proxy.');
|
||||
// });
|
||||
// };
|
||||
|
||||
// useEffect(() => {
|
||||
// getLinkPreview(url)
|
||||
// .then((preview) => setLinkPreview(preview as LinkPreview))
|
||||
// .catch(error => {
|
||||
// console.error("Error fetching original URL, trying with proxy:", error);
|
||||
// setError('Error fetching original URL. Trying with proxy...');
|
||||
// return fetchWithProxy(url);
|
||||
// });
|
||||
// }, [url]);
|
||||
|
||||
// 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 (
|
||||
// <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" />
|
||||
// <div className="font-semibold text-xs text-gray-300">
|
||||
// {linkPreview.title}
|
||||
// </div>
|
||||
// </a>
|
||||
// </div>
|
||||
// );
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getLinkPreview(url)
|
||||
.then((preview) => setLinkPreview(preview as LinkPreview))
|
||||
.catch(error => {
|
||||
console.error("Error fetching original URL, trying with proxy:", error);
|
||||
setError('Error fetching original URL. Trying with proxy...');
|
||||
return fetchWithProxy(url);
|
||||
});
|
||||
}, [url]);
|
||||
|
||||
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 (
|
||||
<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" />
|
||||
<div className="font-semibold text-xs text-gray-300">
|
||||
{linkPreview.title}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface LinkPreview {
|
||||
url: string;
|
||||
title: string;
|
||||
siteName?: string;
|
||||
description?: string;
|
||||
mediaType: string;
|
||||
contentType?: string;
|
||||
images: string[];
|
||||
videos: {
|
||||
url?: string;
|
||||
secureUrl?: string;
|
||||
type?: string;
|
||||
width?: string;
|
||||
height?: string;
|
||||
[key: string]: any;
|
||||
}[];
|
||||
[key: string]: any;
|
||||
}
|
||||
// interface LinkPreview {
|
||||
// url: string;
|
||||
// title: string;
|
||||
// siteName?: string;
|
||||
// description?: string;
|
||||
// mediaType: string;
|
||||
// contentType?: string;
|
||||
// images: string[];
|
||||
// videos: {
|
||||
// url?: string;
|
||||
// secureUrl?: string;
|
||||
// type?: string;
|
||||
// width?: string;
|
||||
// height?: string;
|
||||
// [key: string]: any;
|
||||
// }[];
|
||||
// [key: string]: any;
|
||||
// }
|
||||
|
||||
export default LinkModal;
|
@ -85,7 +85,7 @@ const ContentPreview = ({ key, eventdata }: { key: string; eventdata: Event }) =
|
||||
{isExpanded ? "...Read less" : "...Read more"}
|
||||
</button>
|
||||
)}
|
||||
{/* {url !== "" && <LinkModal key={key} url={url} />} */}
|
||||
{url !== "" && <LinkModal key={key} url={url} />}
|
||||
{quoteEvents[0] && quoteEvents.length > 0 && (
|
||||
<a href={`/thread/${nip19.noteEncode(quoteEvents[0].id)}`}>
|
||||
<QuoteEmbed
|
||||
|
@ -1,10 +1,10 @@
|
||||
import CardContainer from "./CardContainer";
|
||||
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 { getMetadata } from "../../utils/otherUtils";
|
||||
import ContentPreview from "./CardModals/TextModal";
|
||||
import { renderMedia } from "../../utils/FileUpload";
|
||||
// import { renderMedia } from "../../utils/FileUpload";
|
||||
import { getIconFromHash, timeAgo } from "../../utils/cardUtils";
|
||||
import { verifyPow } from "../../utils/mine";
|
||||
import { uniqBy } from "../../utils/otherUtils";
|
||||
@ -26,7 +26,7 @@ const PostCard = ({
|
||||
repliedTo,
|
||||
type
|
||||
}: CardProps) => {
|
||||
const { files } = parseContent(event);
|
||||
// const { files } = parseContent(event);
|
||||
const icon = getIconFromHash(event.pubkey);
|
||||
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>
|
||||
<div className={`ml-2 text-neutral-500 text-sm ${sortByTime ? 'text-neutral-500' : ''}`}>
|
||||
{sortByTime ? 'Time' : 'PoW'}
|
||||
{sortByTime ? 'PoW' : 'Time'}
|
||||
</div>
|
||||
</label>}
|
||||
{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 { CpuChipIcon, ArrowPathRoundedSquareIcon } from "@heroicons/react/24/outline";
|
||||
import { parseContent } from "../../utils/content";
|
||||
import { Event as NostrEvent, nip19 } from "nostr-tools";
|
||||
// import CardContainer from "./CardContainer";
|
||||
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 ContentPreview from "./CardModals/TextModal";
|
||||
import { renderMedia } from "../../utils/FileUpload";
|
||||
// import { renderMedia } from "../../utils/FileUpload";
|
||||
import { getIconFromHash, timeAgo } from "../../utils/cardUtils";
|
||||
import { verifyPow } from "../../utils/mine";
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@ -13,7 +13,7 @@ import { useEffect, useState } from "react";
|
||||
|
||||
interface RepostProps {
|
||||
key?: string | number;
|
||||
event: NostrEvent;
|
||||
event: Event;
|
||||
}
|
||||
|
||||
const RepostCard = ({
|
||||
@ -21,15 +21,40 @@ const RepostCard = ({
|
||||
event
|
||||
}: RepostProps) => {
|
||||
const repostedEvent = JSON.parse(event.content);
|
||||
const { files } = parseContent(repostedEvent);
|
||||
// const { files } = parseContent(repostedEvent);
|
||||
const icon = getIconFromHash(event.pubkey);
|
||||
const navigate = useNavigate();
|
||||
const [cachedMetadataEvents, setCachedMetadataEvents] = useState<Event[]>(
|
||||
JSON.parse(localStorage.getItem("cachedMetadataEvents") || "[]")
|
||||
);
|
||||
const [metadata, setMetadata] = useState<Metadata>()
|
||||
|
||||
// Define your callback function for subGlobalFeed
|
||||
const onEvent = (event: NostrEvent, relay: string) => {
|
||||
if (event.kind === 0 && event.pubkey === repostedEvent.pubkey && metadata == null) {
|
||||
const onEvent = (event: Event, relay: string) => {
|
||||
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))
|
||||
|
||||
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 { useEffect } from 'react';
|
||||
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 PostCard from './Modals/NoteCard';
|
||||
import Placeholder from './Modals/Placeholder';
|
||||
@ -124,7 +124,7 @@ const Thread = () => {
|
||||
))}
|
||||
{OPEvent && <PostCard event={OPEvent} metadata={getMetadataEvent(OPEvent)} replyCount={countReplies(OPEvent)} type={'OP'}/>}
|
||||
</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
|
||||
className="h-5 w-5 text-gray-200 cursor-pointer"
|
||||
onClick={() => {
|
||||
@ -148,6 +148,11 @@ const Thread = () => {
|
||||
setShowRepost(false)
|
||||
}}
|
||||
/>
|
||||
<a href={`nostr:${id}`} target="_blank" rel="noopener noreferrer">
|
||||
<ArrowTopRightOnSquareIcon
|
||||
className="h-5 w-5 text-gray-200 cursor-pointer"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
{(showForm && postType) &&
|
||||
<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) => {
|
||||
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 {}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user