mirror of
https://github.com/smolgrrr/TAO.git
synced 2024-09-20 01:11:25 +00:00
add reposts
This commit is contained in:
parent
a525e4a3e0
commit
d3d81578c2
85
client/src/components/Forms/RepostNote.tsx
Normal file
85
client/src/components/Forms/RepostNote.tsx
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import {
|
||||||
|
CpuChipIcon
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { UnsignedEvent, Event as NostrEvent, nip19 } from "nostr-tools";
|
||||||
|
import { useSubmitForm } from "./handleSubmit";
|
||||||
|
import "./Form.css";
|
||||||
|
|
||||||
|
interface FormProps {
|
||||||
|
refEvent: NostrEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RepostNote = ({
|
||||||
|
refEvent
|
||||||
|
}: FormProps) => {
|
||||||
|
const [difficulty, setDifficulty] = useState(
|
||||||
|
localStorage.getItem("difficulty") || "21"
|
||||||
|
);
|
||||||
|
const [unsigned] = useState<UnsignedEvent>({
|
||||||
|
kind: 6,
|
||||||
|
tags: [
|
||||||
|
['e', refEvent.id, 'wss://relay.damus.io'],
|
||||||
|
['p', refEvent.pubkey]
|
||||||
|
],
|
||||||
|
content: JSON.stringify(refEvent),
|
||||||
|
created_at: Math.floor(Date.now() / 1000),
|
||||||
|
pubkey: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleDifficultyChange = (event: Event) => {
|
||||||
|
const customEvent = event as CustomEvent;
|
||||||
|
const { difficulty } = customEvent.detail;
|
||||||
|
setDifficulty(difficulty);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("difficultyChanged", handleDifficultyChange);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("difficultyChanged", handleDifficultyChange);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const { handleSubmit, doingWorkProp, doingWorkProgress } = useSubmitForm(unsigned, difficulty);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
name="post"
|
||||||
|
method="post"
|
||||||
|
encType="multipart/form-data"
|
||||||
|
className=""
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
<div className="px-4 flex flex-col rounded-lg">
|
||||||
|
<div className="h-14 flex items-center justify-between">
|
||||||
|
<div className="inline-flex items-center gap-2 bg-neutral-800 px-1.5 py-1 rounded-lg">
|
||||||
|
<div className="inline-flex items-center gap-1.5 text-neutral-300">
|
||||||
|
<CpuChipIcon className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<p className="text-xs font-medium text-neutral-400">
|
||||||
|
{difficulty} PoW
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className={`bg-black border h-9 inline-flex items-center justify-center px-4 rounded-lg text-white font-medium text-sm ${doingWorkProp ? 'cursor-not-allowed' : ''}`}
|
||||||
|
disabled={doingWorkProp}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{doingWorkProp ? (
|
||||||
|
<div className="flex animate-pulse text-sm text-gray-300">
|
||||||
|
<CpuChipIcon className="h-4 w-4 ml-auto" />
|
||||||
|
<span>Generating Proof-of-Work.</span>
|
||||||
|
{doingWorkProgress && <span>Current iteration {doingWorkProgress}</span>}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<div id="postFormError" className="text-red-500" />
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RepostNote;
|
@ -45,8 +45,6 @@ export const useSubmitForm = (unsigned: UnsignedEvent, difficulty: string) => {
|
|||||||
|
|
||||||
const { startWork, messageFromWorker, doingWorkProgress } = useWorkers(numCores, unsignedWithPubkey, difficulty, [unsignedWithPubkey]);
|
const { startWork, messageFromWorker, doingWorkProgress } = useWorkers(numCores, unsignedWithPubkey, difficulty, [unsignedWithPubkey]);
|
||||||
|
|
||||||
console.log(powServer[0])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (unsignedPoWEvent) {
|
if (unsignedPoWEvent) {
|
||||||
setDoingWorkProp(false);
|
setDoingWorkProp(false);
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import PostCard from "./Modals/Card";
|
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";
|
||||||
import { verifyPow } from "../utils/mine";
|
import { verifyPow } from "../utils/mine";
|
||||||
import { Event } from "nostr-tools";
|
import { Event } from "nostr-tools";
|
||||||
import NewNoteCard from "./Forms/PostFormCard";
|
import NewNoteCard from "./Forms/PostFormCard";
|
||||||
|
import RepostCard from "./Modals/RepostCard";
|
||||||
|
|
||||||
const DEFAULT_DIFFICULTY = 20;
|
const DEFAULT_DIFFICULTY = 20;
|
||||||
|
|
||||||
@ -29,8 +30,8 @@ const Home = () => {
|
|||||||
const postEvents = uniqEvents
|
const postEvents = uniqEvents
|
||||||
.filter((event) =>
|
.filter((event) =>
|
||||||
verifyPow(event) >= Number(filterDifficulty) &&
|
verifyPow(event) >= Number(filterDifficulty) &&
|
||||||
event.kind === 1 &&
|
event.kind !== 0 &&
|
||||||
!event.tags.some((tag) => tag[0] === "e")
|
(event.kind !== 1 || !event.tags.some((tag) => tag[0] === "e"))
|
||||||
)
|
)
|
||||||
|
|
||||||
const sortedEvents = [...postEvents].sort((a, b) =>
|
const sortedEvents = [...postEvents].sort((a, b) =>
|
||||||
@ -49,6 +50,11 @@ const Home = () => {
|
|||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const kind6Events = sortedEvents.filter(event => event.kind === 6);
|
||||||
|
console.log('Kind 6 events:', kind6Events);
|
||||||
|
}, [uniqEvents]);
|
||||||
|
|
||||||
// Render the component
|
// Render the component
|
||||||
return (
|
return (
|
||||||
<main className="text-white mb-20">
|
<main className="text-white mb-20">
|
||||||
@ -66,7 +72,7 @@ const Home = () => {
|
|||||||
onChange={toggleSort}
|
onChange={toggleSort}
|
||||||
/>
|
/>
|
||||||
<div className="block bg-gray-600 w-8 h-4 rounded-full"></div>
|
<div className="block bg-gray-600 w-8 h-4 rounded-full"></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 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 ? 'Time' : 'PoW'}
|
||||||
@ -75,12 +81,16 @@ const Home = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4 p-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4 p-4">
|
||||||
{sortedEvents.map((event) => (
|
{sortedEvents.map((event) => (
|
||||||
<PostCard
|
event.kind === 1 ?
|
||||||
key={event.id}
|
<PostCard
|
||||||
|
event={event}
|
||||||
|
metadata={getMetadataEvent(event)}
|
||||||
|
replyCount={countReplies(event)}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<RepostCard
|
||||||
event={event}
|
event={event}
|
||||||
metadata={getMetadataEvent(event)}
|
/>
|
||||||
replyCount={countReplies(event)}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
67
client/src/components/Modals/RepostCard.tsx
Normal file
67
client/src/components/Modals/RepostCard.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import CardContainer from "./CardContainer";
|
||||||
|
import { CpuChipIcon, ArrowPathRoundedSquareIcon } from "@heroicons/react/24/outline";
|
||||||
|
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 { getIconFromHash, timeAgo } from "../../utils/cardUtils";
|
||||||
|
import { verifyPow } from "../../utils/mine";
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
interface RepostProps {
|
||||||
|
key?: string | number;
|
||||||
|
event: Event;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RepostCard = ({
|
||||||
|
key,
|
||||||
|
event
|
||||||
|
}: RepostProps) => {
|
||||||
|
const repostedEvent = JSON.parse(event.content);
|
||||||
|
const { files } = parseContent(event);
|
||||||
|
const icon = getIconFromHash(event.pubkey);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
navigate(`/thread/${nip19.noteEncode(event.id)}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<div className="ml-1 flex text-sm text-neutral-600 gap-2.5">
|
||||||
|
Repost
|
||||||
|
@
|
||||||
|
<span className="inline-flex"><CpuChipIcon className="h-5 w-5" /> {verifyPow(event)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg border border-neutral-700">
|
||||||
|
<div className="card break-inside-avoid h-min">
|
||||||
|
<div className="card-body">
|
||||||
|
|
||||||
|
<div className={`flex flex-col gap-2`}>
|
||||||
|
<div className={`flex flex-col break-words hover:cursor-pointer`} onClick={handleClick}>
|
||||||
|
<ContentPreview key={repostedEvent.id} eventdata={repostedEvent} />
|
||||||
|
</div>
|
||||||
|
{renderMedia(files)}
|
||||||
|
<div className={`flex justify-between items-center hover:cursor-pointer`} onClick={handleClick}>
|
||||||
|
<div className={`h-4 w-4 ${icon} rounded-full`} />
|
||||||
|
<div className="flex items-center ml-auto gap-2.5">
|
||||||
|
<div className="inline-flex text-xs text-neutral-600 gap-0.5">
|
||||||
|
<CpuChipIcon className="h-4 w-4" /> {verifyPow(repostedEvent)}
|
||||||
|
</div>
|
||||||
|
<span className="text-neutral-700">·</span>
|
||||||
|
<div className="text-xs font-semibold text-neutral-600">
|
||||||
|
{timeAgo(repostedEvent.created_at)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RepostCard;
|
@ -4,11 +4,12 @@ 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 } from '@heroicons/react/24/outline';
|
import { DocumentTextIcon, FolderPlusIcon, DocumentDuplicateIcon } from '@heroicons/react/24/outline';
|
||||||
import { getPow } from '../utils/mine';
|
import { getPow } from '../utils/mine';
|
||||||
import PostCard from './Modals/Card';
|
import PostCard from './Modals/NoteCard';
|
||||||
import Placeholder from './Modals/Placeholder';
|
import Placeholder from './Modals/Placeholder';
|
||||||
import NewNoteCard from './Forms/PostFormCard';
|
import NewNoteCard from './Forms/PostFormCard';
|
||||||
|
import RepostNote from './Forms/RepostNote';
|
||||||
|
|
||||||
type PostType = "" | "Reply" | "Quote" | undefined;
|
type PostType = "" | "Reply" | "Quote" | undefined;
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ const Thread = () => {
|
|||||||
const [events, setEvents] = useState<Event[]>([]); // Initialize state
|
const [events, setEvents] = useState<Event[]>([]); // Initialize state
|
||||||
const [OPEvent, setOPEvent] = useState<Event>()
|
const [OPEvent, setOPEvent] = useState<Event>()
|
||||||
const [showForm, setShowForm] = useState(false);
|
const [showForm, setShowForm] = useState(false);
|
||||||
|
const [showRepost, setShowRepost] = useState(false);
|
||||||
const [postType, setPostType] = useState<PostType>("");
|
const [postType, setPostType] = useState<PostType>("");
|
||||||
const [hasRun, setHasRun] = useState(false);
|
const [hasRun, setHasRun] = useState(false);
|
||||||
const [preOPEvents, setPreOPEvents] = useState(['']);
|
const [preOPEvents, setPreOPEvents] = useState(['']);
|
||||||
@ -130,20 +132,34 @@ const Thread = () => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowForm(prevShowForm => !prevShowForm);
|
setShowForm(prevShowForm => !prevShowForm);
|
||||||
setPostType('Reply');
|
setPostType('Reply');
|
||||||
|
setShowRepost(false)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<DocumentDuplicateIcon
|
||||||
|
className="h-5 w-5 text-gray-200 cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
setShowRepost(prevShowRepost => !prevShowRepost);
|
||||||
|
setShowForm(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FolderPlusIcon
|
<FolderPlusIcon
|
||||||
className="h-5 w-5 text-gray-200 cursor-pointer"
|
className="h-5 w-5 text-gray-200 cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowForm(prevShowForm => !prevShowForm);
|
setShowForm(prevShowForm => !prevShowForm);
|
||||||
setPostType('Quote');
|
setPostType('Quote');
|
||||||
|
setShowRepost(false)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{(showForm && postType) && <div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
{(showForm && postType) &&
|
||||||
|
<div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
||||||
|
<span className='text-center'>{postType}-post</span>
|
||||||
<NewNoteCard refEvent={uniqEvents[0]} tagType={postType}/>
|
<NewNoteCard refEvent={uniqEvents[0]} tagType={postType}/>
|
||||||
</div>}
|
</div>}
|
||||||
|
{showRepost && <div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
||||||
|
<span className='text-center'>Repost note</span>
|
||||||
|
<RepostNote refEvent={uniqEvents[0]}/>
|
||||||
|
</div>}
|
||||||
<div className="flex items-center justify-center w-full py-4">
|
<div className="flex items-center justify-center w-full py-4">
|
||||||
<label htmlFor="toggleB" className="flex items-center cursor-pointer">
|
<label htmlFor="toggleB" className="flex items-center cursor-pointer">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
@ -22,7 +22,7 @@ export const subGlobalFeed = (onEvent: SubCallback) => {
|
|||||||
},
|
},
|
||||||
filter: {
|
filter: {
|
||||||
...(prefix && { ids: ['0'.repeat(prefix)] }),
|
...(prefix && { ids: ['0'.repeat(prefix)] }),
|
||||||
kinds: [1],
|
kinds: [1, 6],
|
||||||
since: Math.floor((Date.now() * 0.001) - (24 * 60 * 60)),
|
since: Math.floor((Date.now() * 0.001) - (24 * 60 * 60)),
|
||||||
limit: 500,
|
limit: 500,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user