mirror of
https://github.com/smolgrrr/TAO.git
synced 2024-09-20 01:11:25 +00:00
good enough
This commit is contained in:
parent
9c540eda8c
commit
940a891943
@ -5,7 +5,7 @@ import {
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { XCircleIcon } from "@heroicons/react/24/solid";
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { generatePrivateKey, getPublicKey, finishEvent, UnsignedEvent } from "nostr-tools";
|
||||
import { generatePrivateKey, getPublicKey, finishEvent, UnsignedEvent, Event as NostrEvent, nip19 } from "nostr-tools";
|
||||
import { publish } from "../../utils/relays";
|
||||
import { renderMedia, attachFile } from "../../utils/FileUpload";
|
||||
|
||||
@ -40,7 +40,20 @@ const useWorkers = (numCores: number, unsigned: UnsignedEvent, difficulty: strin
|
||||
return { startWork, messageFromWorker, doingWorkProgress };
|
||||
};
|
||||
|
||||
const NewNoteCard: React.FC = () => {
|
||||
interface FormProps {
|
||||
refEvent?: NostrEvent;
|
||||
tagType?: 'Reply' | 'Quote' | '';
|
||||
}
|
||||
|
||||
const tagMapping = {
|
||||
'Reply': ['e', 'p'],
|
||||
'Quote': ['q', 'p']
|
||||
};
|
||||
|
||||
const NewNoteCard = ({
|
||||
refEvent,
|
||||
tagType
|
||||
}: FormProps) => {
|
||||
const [comment, setComment] = useState("");
|
||||
const [file, setFile] = useState("");
|
||||
const [sk, setSk] = useState(generatePrivateKey());
|
||||
@ -65,6 +78,17 @@ const NewNoteCard: React.FC = () => {
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (refEvent && tagType && unsigned.tags.length === 0) {
|
||||
const tags = tagMapping[tagType];
|
||||
if (tags) {
|
||||
tags.forEach(tag => unsigned.tags.push([tag, refEvent[tag === 'p' ? 'pubkey' : 'id']]));
|
||||
}
|
||||
|
||||
if (tagType === 'Quote') {
|
||||
setComment(comment + ' nostr:' + nip19.noteEncode(refEvent.id));
|
||||
}
|
||||
}
|
||||
|
||||
const handleDifficultyChange = (event: Event) => {
|
||||
const customEvent = event as CustomEvent;
|
||||
const { difficulty } = customEvent.detail;
|
||||
@ -79,15 +103,12 @@ const NewNoteCard: React.FC = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setUnsigned(
|
||||
{
|
||||
kind: 1,
|
||||
tags: [],
|
||||
content: comment + " " + file,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
pubkey: getPublicKey(sk),
|
||||
}
|
||||
);
|
||||
setUnsigned(prevUnsigned => ({
|
||||
...prevUnsigned,
|
||||
content: `${comment} ${file}`,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
pubkey: getPublicKey(sk),
|
||||
}));
|
||||
}, [comment, file]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -100,16 +121,12 @@ const NewNoteCard: React.FC = () => {
|
||||
setComment("");
|
||||
setFile("");
|
||||
setSk(generatePrivateKey());
|
||||
setUnsigned(
|
||||
{
|
||||
kind: 1,
|
||||
tags: [],
|
||||
content: "",
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
pubkey: getPublicKey(sk),
|
||||
}
|
||||
);
|
||||
|
||||
setUnsigned(prevUnsigned => ({
|
||||
...prevUnsigned,
|
||||
content: '',
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
pubkey: getPublicKey(sk),
|
||||
}));
|
||||
} catch (error) {
|
||||
setComment(error + " " + comment);
|
||||
}
|
||||
|
@ -1,226 +0,0 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
import { ArrowUpTrayIcon, CpuChipIcon, ArrowPathIcon } from '@heroicons/react/24/outline';
|
||||
import { XCircleIcon } from '@heroicons/react/24/solid';
|
||||
import { generatePrivateKey, getPublicKey, finishEvent, Event as NostrEvent } from 'nostr-tools';
|
||||
import { publish } from '../../utils/relays';
|
||||
import FileUpload from '../../utils/FileUpload';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { renderMedia } from '../../utils/FileUpload';
|
||||
|
||||
|
||||
const ThreadPost = ({ OPEvent, state, type }: { OPEvent: NostrEvent, state: Boolean, type: String }) => {
|
||||
const { id } = useParams();
|
||||
const [comment, setComment] = useState("");
|
||||
const [file, setFile] = useState("");
|
||||
const [difficulty, setDifficulty] = useState(localStorage.getItem('difficulty') || '21');
|
||||
const [uploadingFile, setUploadingFile] = useState(false);
|
||||
let decodeResult = nip19.decode(id as string);
|
||||
|
||||
const [sk, setSk] = useState(generatePrivateKey());
|
||||
|
||||
const [messageFromWorker, setMessageFromWorker] = useState(null);
|
||||
const [doingWorkProp, setDoingWorkProp] = useState(false);
|
||||
const [doingWorkProgress, setDoingWorkProgress] = useState(0);
|
||||
|
||||
// Initialize the worker outside of any effects
|
||||
const numCores = navigator.hardwareConcurrency || 4;
|
||||
|
||||
const workers = useMemo(
|
||||
() => Array(numCores).fill(null).map(() => new Worker(new URL("../../powWorker", import.meta.url))),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
workers.forEach((worker) => {
|
||||
worker.onmessage = (event) => {
|
||||
if (event.data.status === 'progress') {
|
||||
console.log(`Worker progress: Checked ${event.data.currentNonce} nonces.`);
|
||||
setDoingWorkProgress(event.data.currentNonce);
|
||||
} else if (event.data.found) {
|
||||
setMessageFromWorker(event.data.event);
|
||||
// Terminate all workers once a solution is found
|
||||
workers.forEach(w => w.terminate());
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const handleDifficultyChange = (event: Event) => {
|
||||
const customEvent = event as CustomEvent;
|
||||
setDifficulty(customEvent.detail);
|
||||
};
|
||||
|
||||
window.addEventListener('difficultyChanged', handleDifficultyChange);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('difficultyChanged', handleDifficultyChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
let id = decodeResult.data as string
|
||||
|
||||
workers.forEach((worker, index) => {
|
||||
let tags = [];
|
||||
let modifiedComment = comment + " " + file;
|
||||
if (type === 'r') {
|
||||
tags.push(["e", id as string])
|
||||
tags.push(["p", OPEvent.pubkey])
|
||||
} else if (type === 'q') {
|
||||
tags.push(["q", id as string])
|
||||
tags.push(["p", OPEvent.pubkey])
|
||||
modifiedComment += ' nostr:' + nip19.noteEncode(id);
|
||||
}
|
||||
|
||||
try {
|
||||
worker.postMessage({
|
||||
unsigned: {
|
||||
kind: 1,
|
||||
tags,
|
||||
content: modifiedComment,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
pubkey: getPublicKey(sk),
|
||||
},
|
||||
difficulty,
|
||||
nonceStart: index, // Each worker starts from its index
|
||||
nonceStep: numCores // Each worker increments by the total number of workers
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
setComment(comment + " " + error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setDoingWorkProp(false)
|
||||
if (messageFromWorker) {
|
||||
try {
|
||||
const signedEvent = finishEvent(messageFromWorker, sk);
|
||||
publish(signedEvent);
|
||||
|
||||
setComment("");
|
||||
setFile("");
|
||||
setSk(generatePrivateKey())
|
||||
setMessageFromWorker(null);
|
||||
} catch (error) {
|
||||
setComment(error + ' ' + comment);
|
||||
}
|
||||
}
|
||||
}, [messageFromWorker]);
|
||||
|
||||
async function attachFile(file_input: File | null) {
|
||||
setUploadingFile(true); // start loading
|
||||
try {
|
||||
if (file_input) {
|
||||
const rx = await FileUpload(file_input);
|
||||
setUploadingFile(false); // stop loading
|
||||
if (rx.url) {
|
||||
setFile(rx.url);
|
||||
} else if (rx?.error) {
|
||||
setFile(rx.error);
|
||||
}
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
setUploadingFile(false); // stop loading
|
||||
if (error instanceof Error) {
|
||||
setFile(error?.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{state && (
|
||||
<form
|
||||
name="post"
|
||||
method="post"
|
||||
encType="multipart/form-data"
|
||||
className=""
|
||||
onSubmit={(event) => {
|
||||
handleSubmit(event);
|
||||
setDoingWorkProp(true);
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="MAX_FILE_SIZE" defaultValue={4194304} />
|
||||
<div id="togglePostFormLink" className="text-lg font-semibold">
|
||||
{type === 'r' ? <span>Reply To Post</span> : <span>Quote Post</span>}
|
||||
</div>
|
||||
<div className="px-4 pt-4 flex flex-col bg-neutral-900 border border-neutral-800 rounded-lg">
|
||||
<div>
|
||||
<textarea
|
||||
name="com"
|
||||
wrap="soft"
|
||||
className="shadow-lg w-full px-4 py-3 h-28 rounded-md outline-none focus:outline-none bg-neutral-800 border border-neutral-700 text-white placeholder:text-neutral-500"
|
||||
placeholder='Shitpost here...'
|
||||
value={comment}
|
||||
onChange={(e) => setComment(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative">
|
||||
{file !== "" && (
|
||||
<button onClick={() => setFile("")}>
|
||||
<XCircleIcon className="h-10 w-10 absolute shadow z-100 text-blue-500" />
|
||||
</button>
|
||||
)}
|
||||
{renderMedia(file)}
|
||||
</div>
|
||||
<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>
|
||||
<div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center">
|
||||
<ArrowUpTrayIcon
|
||||
className="h-4 w-4 text-neutral-400 cursor-pointer"
|
||||
onClick={() => document.getElementById("file_input")?.click()}
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
name="file_input"
|
||||
id="file_input"
|
||||
style={{ display: "none" }}
|
||||
onChange={(e) => {
|
||||
const file_input = e.target.files?.[0];
|
||||
if (file_input) {
|
||||
attachFile(file_input);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{uploadingFile ? (
|
||||
<div className="flex animate-spin text-sm text-gray-300">
|
||||
<ArrowPathIcon className="h-4 w-4 ml-auto" />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="h-9 inline-flex items-center justify-center px-4 bg-blue-500 hover:bg-blue-600 rounded-lg text-white font-medium text-sm"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<span>iteration {doingWorkProgress}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</form>)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThreadPost;
|
@ -1,6 +1,6 @@
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import PostCard from "./Modals/Card";
|
||||
import { uniqBy } from "../utils/utils"; // 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 { verifyPow } from "../utils/mine";
|
||||
import { Event } from "nostr-tools";
|
||||
|
@ -2,12 +2,12 @@ import CardContainer from "./CardContainer";
|
||||
import { FolderIcon, CpuChipIcon } from "@heroicons/react/24/outline";
|
||||
import { parseContent } from "../../utils/content";
|
||||
import { Event, nip19 } from "nostr-tools";
|
||||
import { getMetadata } from "../../utils/utils";
|
||||
import { getMetadata } from "../../utils/otherUtils";
|
||||
import ContentPreview from "./CardModals/TextModal";
|
||||
import { renderMedia } from "../../utils/FileUpload";
|
||||
import { getIconFromHash } from "../../utils/deterministicProfileIcon";
|
||||
import { verifyPow } from "../../utils/mine";
|
||||
import { uniqBy } from "../../utils/utils";
|
||||
import { uniqBy } from "../../utils/otherUtils";
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const timeUnits = [
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { parseContent } from "../../../utils/content";
|
||||
import { Event } from "nostr-tools";
|
||||
import { getMetadata, uniqBy } from "../../../utils/utils";
|
||||
import { getMetadata, uniqBy } from "../../../utils/otherUtils";
|
||||
import ContentPreview from "./TextModal";
|
||||
import { renderMedia } from "../../../utils/FileUpload";
|
||||
|
||||
|
@ -3,22 +3,21 @@ import { useState } from "react";
|
||||
import { Event, nip19 } from "nostr-tools"
|
||||
import { subNote, subNotesOnce } from '../utils/subscriptions';
|
||||
import { useEffect } from 'react';
|
||||
import { uniqBy } from '../utils/utils';
|
||||
import { uniqBy } from '../utils/otherUtils';
|
||||
import { DocumentTextIcon, FolderPlusIcon } from '@heroicons/react/24/outline';
|
||||
import { getPow } from '../utils/mine';
|
||||
import ThreadPost from './Forms/ThreadPost';
|
||||
import PostCard from './Modals/Card';
|
||||
import Placeholder from './Modals/Placeholder';
|
||||
import NewNoteCard from './Forms/PostFormCard';
|
||||
|
||||
|
||||
const difficulty = 20
|
||||
type PostType = "" | "Reply" | "Quote" | undefined;
|
||||
|
||||
const Thread = () => {
|
||||
const { id } = useParams();
|
||||
const [events, setEvents] = useState<Event[]>([]); // Initialize state
|
||||
const [OPEvent, setOPEvent] = useState<Event>()
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [postType, setPostType] = useState("");
|
||||
const [postType, setPostType] = useState<PostType>("");
|
||||
const [hasRun, setHasRun] = useState(false);
|
||||
const [preOPEvents, setPreOPEvents] = useState(['']);
|
||||
const [sortByTime, setSortByTime] = useState(true);
|
||||
@ -48,7 +47,12 @@ const Thread = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasRun && events.length > 0) {
|
||||
let OPEvent = events.find(e => e.id === hexID);
|
||||
let OPEvent = uniqEvents[0];
|
||||
setOPEvent(OPEvent);
|
||||
|
||||
if (OPEvent && OPEvent.id !== hexID) {
|
||||
OPEvent = events.find(e => e.id === hexID) as Event;
|
||||
}
|
||||
|
||||
if (OPEvent) {
|
||||
setOPEvent(OPEvent);
|
||||
@ -119,7 +123,7 @@ const Thread = () => {
|
||||
className="h-5 w-5 text-gray-200"
|
||||
onClick={() => {
|
||||
setShowForm(prevShowForm => !prevShowForm);
|
||||
setPostType('r');
|
||||
setPostType('Reply');
|
||||
}}
|
||||
/>
|
||||
|
||||
@ -127,13 +131,13 @@ const Thread = () => {
|
||||
className="h-5 w-5 text-gray-200"
|
||||
onClick={() => {
|
||||
setShowForm(prevShowForm => !prevShowForm);
|
||||
setPostType('q');
|
||||
setPostType('Quote');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
||||
<ThreadPost OPEvent={uniqEvents[0]} state={showForm} type={postType} />
|
||||
</div>
|
||||
{(showForm && postType) && <div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
||||
<NewNoteCard refEvent={uniqEvents[0]} tagType={postType}/>
|
||||
</div>}
|
||||
<div className="flex items-center justify-center w-full py-4">
|
||||
<label htmlFor="toggleB" className="flex items-center cursor-pointer">
|
||||
<div className="relative">
|
||||
|
@ -1,32 +0,0 @@
|
||||
import {
|
||||
type Event as NostrEvent,
|
||||
generatePrivateKey,
|
||||
getEventHash,
|
||||
getPublicKey,
|
||||
signEvent,
|
||||
} from "nostr-tools";
|
||||
|
||||
export const handleThreadSubmit = async (comment: string, tags: []) => {
|
||||
if (!comment) {
|
||||
alert("no message provided");
|
||||
return;
|
||||
}
|
||||
|
||||
const newEvent: NostrEvent = {
|
||||
id: 'null',
|
||||
content: comment,
|
||||
kind: 1,
|
||||
tags,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
pubkey: 'null',
|
||||
sig: 'null',
|
||||
};
|
||||
|
||||
let sk = generatePrivateKey();
|
||||
|
||||
newEvent.pubkey = getPublicKey(sk);
|
||||
newEvent.id = getEventHash(newEvent);
|
||||
newEvent.sig = signEvent(newEvent, sk);
|
||||
|
||||
return newEvent
|
||||
};
|
@ -1,42 +0,0 @@
|
||||
import { useMemo, useEffect } from 'react';
|
||||
|
||||
interface Dispatch {
|
||||
(arg: { type: string, payload?: any }): void
|
||||
}
|
||||
|
||||
const useWorkers = (dispatch: Dispatch) => {
|
||||
const numCores = navigator.hardwareConcurrency || 4;
|
||||
|
||||
const workers = useMemo(
|
||||
() => Array(numCores).fill(null).map(() => new Worker(new URL("../../powWorker", import.meta.url))),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
workers.forEach((worker) => {
|
||||
worker.onmessage = (event) => {
|
||||
if (event.data.status === 'progress') {
|
||||
dispatch({ type: 'SET_WORK_PROGRESS', payload: event.data.currentNonce });
|
||||
} else if (event.data.found) {
|
||||
dispatch({ type: 'SET_MESSAGE_FROM_WORKER', payload: event.data.event });
|
||||
workers.forEach(w => w.terminate());
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const handleDifficultyChange = (event: Event) => {
|
||||
const customEvent = event as CustomEvent;
|
||||
dispatch({ type: 'SET_DIFFICULTY', payload: customEvent.detail });
|
||||
};
|
||||
|
||||
window.addEventListener('difficultyChanged', handleDifficultyChange);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('difficultyChanged', handleDifficultyChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return workers;
|
||||
};
|
||||
|
||||
export default useWorkers;
|
Loading…
Reference in New Issue
Block a user