pfp cache

This commit is contained in:
smolgrrr 2024-08-12 18:36:07 +10:00
parent 7346368a2b
commit 6bea0bf8f1
8 changed files with 147 additions and 80 deletions

View File

@ -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) {

View File

@ -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]);
// 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) {
// 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
}
// }
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>
);
// 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;

View File

@ -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

View File

@ -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;

View File

@ -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 */}

View File

@ -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;
});
}
};

View File

@ -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">

View File

@ -36,7 +36,12 @@ export interface Metadata {
}
export const getMetadata = (event: Event) => {
const content = event.content.replace(/[\n\r\t]/g, '')
const metadata: Metadata = JSON.parse(content)
return metadata
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 {}
}
}