mirror of
https://github.com/smolgrrr/TAO.git
synced 2024-09-20 09:21:25 +00:00
idk if this actually works but ah well
This commit is contained in:
parent
4fc156b744
commit
4f3bbbf054
@ -5,78 +5,101 @@ import { getPow } from "../utils/mine";
|
||||
import { Event } from "nostr-tools";
|
||||
import { subGlobalFeed } from "../utils/subscriptions";
|
||||
import { uniqBy } from "../utils/utils";
|
||||
import PWAInstallPopup from "./Modals/PWACheckModal";
|
||||
// import PWAInstallPopup from "./Modals/PWACheckModal"; // Removed as it's not being used
|
||||
|
||||
const Home = () => {
|
||||
// State declarations
|
||||
const [events, setEvents] = useState<Event[]>([]);
|
||||
const [filterDifficulty, setFilterDifficulty] = useState(
|
||||
localStorage.getItem("filterDifficulty") || "20"
|
||||
);
|
||||
const [inBrowser, setInBrowser] = useState(false);
|
||||
const [filterDifficulty, setFilterDifficulty] = useState(localStorage.getItem("filterDifficulty") || "20");
|
||||
const [sortByPoW, setSortByPoW] = useState(true);
|
||||
// const [inBrowser, setInBrowser] = useState(false); // Removed as it's not being used
|
||||
|
||||
// Function to handle new events
|
||||
const onEvent = (event: Event) => {
|
||||
setEvents((prevEvents) => [...prevEvents, event]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Subscribe to the global feed
|
||||
subGlobalFeed(onEvent);
|
||||
// If you eventually need a cleanup function, put it here
|
||||
|
||||
// Event listener to handle difficulty changes
|
||||
const handleDifficultyChange = (event: any) => {
|
||||
const customEvent = event as CustomEvent;
|
||||
const { difficulty, filterDifficulty } = customEvent.detail;
|
||||
const { filterDifficulty } = event.detail;
|
||||
setFilterDifficulty(filterDifficulty);
|
||||
};
|
||||
|
||||
// if ((window.navigator as any).standalone || window.matchMedia('(display-mode: standalone)').matches) {
|
||||
// console.log('App is running in standalone mode.');
|
||||
// } else {
|
||||
// console.log('App is running in a browser.');
|
||||
// setInBrowser(true)
|
||||
// }
|
||||
|
||||
// Attach event listener
|
||||
window.addEventListener("difficultyChanged", handleDifficultyChange);
|
||||
|
||||
// Cleanup listener on component unmount
|
||||
return () => {
|
||||
window.removeEventListener("difficultyChanged", handleDifficultyChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Get unique events based on id
|
||||
const uniqEvents = events.length > 0 ? uniqBy(events, "id") : [];
|
||||
|
||||
const filteredAndSortedEvents = uniqEvents
|
||||
.filter(
|
||||
(event) =>
|
||||
// Filter and sort events
|
||||
const filteredEvents = uniqEvents
|
||||
.filter((event) =>
|
||||
getPow(event.id) > Number(filterDifficulty) &&
|
||||
event.kind === 1 &&
|
||||
!event.tags.some((tag) => tag[0] === "e")
|
||||
)
|
||||
.sort((a, b) => (b.created_at as any) - (a.created_at as any));
|
||||
|
||||
const toggleSort = () => {
|
||||
setSortByPoW(prev => !prev);
|
||||
};
|
||||
|
||||
// Events sorted by time
|
||||
const eventsSortedByTime = [...filteredEvents].sort((a, b) => b.created_at - a.created_at);
|
||||
|
||||
// Events sorted by PoW (assuming `getPow` returns a numerical representation of the PoW)
|
||||
const eventsSortedByPow = [...filteredEvents].sort((a, b) => getPow(b.id) - getPow(a.id));
|
||||
|
||||
const displayedEvents = sortByPoW ? eventsSortedByPow : eventsSortedByTime;
|
||||
|
||||
// Get metadata for an event
|
||||
const getMetadataEvent = (event: Event) => {
|
||||
const metadataEvent = uniqEvents.find(
|
||||
(e) => e.pubkey === event.pubkey && e.kind === 0
|
||||
);
|
||||
if (metadataEvent) {
|
||||
return metadataEvent;
|
||||
}
|
||||
return null;
|
||||
return uniqEvents.find((e) => e.pubkey === event.pubkey && e.kind === 0) || null;
|
||||
};
|
||||
|
||||
// Count replies for an event
|
||||
const countReplies = (event: Event) => {
|
||||
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;
|
||||
};
|
||||
|
||||
// Render the component
|
||||
return (
|
||||
<main className="text-white mb-20">
|
||||
{/* {inBrowser && <PWAInstallPopup onClose={() => setInBrowser(false)} />} */}
|
||||
<div className="w-full px-4 sm:px-0 sm:max-w-xl mx-auto my-2">
|
||||
<NewThreadCard />
|
||||
</div>
|
||||
<div className="flex items-center justify-center w-full py-4">
|
||||
<label htmlFor="toggleB" className="flex items-center cursor-pointer">
|
||||
<div className="relative">
|
||||
<input
|
||||
id="toggleB"
|
||||
type="checkbox"
|
||||
className="sr-only"
|
||||
checked={sortByPoW}
|
||||
onChange={toggleSort}
|
||||
/>
|
||||
<div className="block bg-gray-600 w-10 h-6 rounded-full"></div>
|
||||
<div
|
||||
className={`dot absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition ${sortByPoW ? 'transform translate-x-full bg-blue-400' : ''
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
<div className={`ml-3 text-neutral-500 font-medium ${sortByPoW ? 'text-neutral-500' : ''}`}>
|
||||
{sortByPoW ? 'Sort by PoW' : 'Sort by recent'}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 p-4">
|
||||
{filteredAndSortedEvents.map((event) => (
|
||||
{displayedEvents.map((event) => (
|
||||
<PostCard
|
||||
key={event.id}
|
||||
event={event}
|
||||
|
@ -44,8 +44,8 @@ const ContentPreview = ({ key, comment }: { key: string; comment: string }) => {
|
||||
|
||||
return (
|
||||
<div className="gap-2 flex flex-col break-words text-sm">
|
||||
{isExpanded ? finalComment : finalComment.slice(0, 240)}
|
||||
{finalComment.length > 240 && (
|
||||
{isExpanded ? finalComment : finalComment.slice(0, 350)}
|
||||
{finalComment.length > 350 && (
|
||||
<button
|
||||
className="text-sm text-neutral-500"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
|
@ -21,16 +21,30 @@ const NewThreadCard: React.FC = () => {
|
||||
const [uploadingFile, setUploadingFile] = useState(false);
|
||||
const [messageFromWorker, setMessageFromWorker] = useState(null);
|
||||
const [doingWorkProp, setDoingWorkProp] = useState(false);
|
||||
const [doingWorkProgress, setDoingWorkProgress] = useState(0);
|
||||
|
||||
// Initialize the worker outside of any effects
|
||||
const worker = useMemo(
|
||||
() => new Worker(new URL("../../powWorker", import.meta.url)),
|
||||
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) => {
|
||||
setMessageFromWorker(event.data);
|
||||
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;
|
||||
@ -47,6 +61,8 @@ const NewThreadCard: React.FC = () => {
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
workers.forEach((worker, index) => {
|
||||
worker.postMessage({
|
||||
unsigned: {
|
||||
kind: 1,
|
||||
@ -56,9 +72,13 @@ const NewThreadCard: React.FC = () => {
|
||||
pubkey: getPublicKey(sk),
|
||||
},
|
||||
difficulty,
|
||||
nonceStart: index, // Each worker starts from its index
|
||||
nonceStep: numCores // Each worker increments by the total number of workers
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setDoingWorkProp(false);
|
||||
if (messageFromWorker) {
|
||||
@ -71,9 +91,6 @@ const NewThreadCard: React.FC = () => {
|
||||
setSk(generatePrivateKey());
|
||||
setMessageFromWorker(null);
|
||||
|
||||
return () => {
|
||||
worker.terminate();
|
||||
};
|
||||
} catch (error) {
|
||||
setComment(error + " " + comment);
|
||||
}
|
||||
@ -114,7 +131,7 @@ const NewThreadCard: React.FC = () => {
|
||||
<input type="hidden" name="MAX_FILE_SIZE" defaultValue={4194304} />
|
||||
<div
|
||||
id="togglePostFormLink"
|
||||
className="text-lg text-neutral-700 text-center mb-2 font-semibold"
|
||||
className="text-lg text-neutral-500 text-center mb-2 font-semibold"
|
||||
>
|
||||
Start a New Thread
|
||||
</div>
|
||||
@ -176,15 +193,16 @@ const NewThreadCard: React.FC = () => {
|
||||
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>Generating Proof-of-Work:</span>
|
||||
<span>iteration {doingWorkProgress}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="postFormError" className="text-red-500" />
|
||||
</form>
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ import { useEffect } from 'react';
|
||||
import { uniqBy } from '../../utils/utils';
|
||||
import { DocumentTextIcon, FolderPlusIcon } from '@heroicons/react/24/outline';
|
||||
import { generatePrivateKey, getPublicKey, finishEvent } from 'nostr-tools';
|
||||
import { minePow } from '../../utils/mine';
|
||||
import { getPow } from '../../utils/mine';
|
||||
import { publish } from '../../utils/relays';
|
||||
import ThreadPost from './ThreadPost';
|
||||
import ReplyCard from './ReplyCard';
|
||||
@ -23,6 +23,7 @@ const Thread = () => {
|
||||
const [postType, setPostType] = useState("");
|
||||
const [hasRun, setHasRun] = useState(false);
|
||||
const [preOPEvents, setPreOPEvents] = useState(['']);
|
||||
const [sortByPoW, setSortByPoW] = useState(false);
|
||||
|
||||
// Define your callback function for subGlobalFeed
|
||||
const onEvent = (event: Event, relay: string) => {
|
||||
@ -78,6 +79,17 @@ const Thread = () => {
|
||||
)
|
||||
.sort((a, b) => (b.created_at as any) - (a.created_at as any));
|
||||
|
||||
const toggleSort = () => {
|
||||
setSortByPoW(prev => !prev);
|
||||
};
|
||||
|
||||
const eventsSortedByTime = [...uniqEvents].slice(1).sort((a, b) => a.created_at - b.created_at);
|
||||
|
||||
// Events sorted by PoW (assuming `getPow` returns a numerical representation of the PoW)
|
||||
const eventsSortedByPow = [...uniqEvents].slice(1).sort((a, b) => getPow(b.id) - getPow(a.id));
|
||||
|
||||
const displayedEvents = sortByPoW ? eventsSortedByPow : eventsSortedByTime;
|
||||
|
||||
if (!uniqEvents[0]) {
|
||||
return (
|
||||
<>
|
||||
@ -132,12 +144,30 @@ const Thread = () => {
|
||||
<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>
|
||||
<div className="flex items-center justify-center w-full py-4">
|
||||
<label htmlFor="toggleB" className="flex items-center cursor-pointer">
|
||||
<div className="relative">
|
||||
<input
|
||||
id="toggleB"
|
||||
type="checkbox"
|
||||
className="sr-only"
|
||||
checked={sortByPoW}
|
||||
onChange={toggleSort}
|
||||
/>
|
||||
<div className="block bg-gray-600 w-10 h-6 rounded-full"></div>
|
||||
<div
|
||||
className={`dot absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition ${sortByPoW ? 'transform translate-x-full bg-blue-400' : ''
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
<div className={`ml-3 text-neutral-500 font-medium ${sortByPoW ? 'text-neutral-500' : ''}`}>
|
||||
{sortByPoW ? 'Sort by PoW' : 'Sort by age'}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 p-4">
|
||||
<div className="col-span-full h-0.5 bg-neutral-900"></div> {/* This is the white line separator */}
|
||||
{uniqEvents
|
||||
.slice(1)
|
||||
.filter(event => event.kind === 1)
|
||||
.sort((a, b) => a.created_at - b.created_at).map((event, index) => (
|
||||
{displayedEvents.map((event, index) => (
|
||||
<ReplyCard key={index} event={event} metadata={getMetadataEvent(event)} replyCount={countReplies(event)} repliedTo={repliedList(event)} />
|
||||
))}
|
||||
</div>
|
||||
|
@ -21,13 +21,29 @@ const ThreadPost = ({ OPEvent, state, type }: { OPEvent: NostrEvent, state: Bool
|
||||
|
||||
const [messageFromWorker, setMessageFromWorker] = useState(null);
|
||||
const [doingWorkProp, setDoingWorkProp] = useState(false);
|
||||
const [doingWorkProgress, setDoingWorkProgress] = useState(0);
|
||||
|
||||
// Initialize the worker outside of any effects
|
||||
const worker = useMemo(() => new Worker(new URL('../../powWorker', import.meta.url)), []);
|
||||
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) => {
|
||||
setMessageFromWorker(event.data);
|
||||
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;
|
||||
@ -45,6 +61,7 @@ const ThreadPost = ({ OPEvent, state, type }: { OPEvent: NostrEvent, state: Bool
|
||||
event.preventDefault();
|
||||
let id = decodeResult.data as string
|
||||
|
||||
workers.forEach((worker, index) => {
|
||||
let tags = [];
|
||||
let modifiedComment = comment + " " + file;
|
||||
if (type === 'r') {
|
||||
@ -64,12 +81,16 @@ const ThreadPost = ({ OPEvent, state, type }: { OPEvent: NostrEvent, state: Bool
|
||||
content: modifiedComment,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
pubkey: getPublicKey(sk),
|
||||
}, difficulty
|
||||
},
|
||||
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(() => {
|
||||
@ -83,10 +104,6 @@ const ThreadPost = ({ OPEvent, state, type }: { OPEvent: NostrEvent, state: Bool
|
||||
setFile("");
|
||||
setSk(generatePrivateKey())
|
||||
setMessageFromWorker(null);
|
||||
|
||||
return () => {
|
||||
worker.terminate();
|
||||
};
|
||||
} catch (error) {
|
||||
setComment(error + ' ' + comment);
|
||||
}
|
||||
@ -191,15 +208,16 @@ const ThreadPost = ({ OPEvent, state, type }: { OPEvent: NostrEvent, state: Bool
|
||||
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>Generating Proof-of-Work:</span>
|
||||
<span>iteration {doingWorkProgress}</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>)}
|
||||
</>
|
||||
);
|
||||
|
@ -7,9 +7,9 @@ const ctx: Worker = self as any;
|
||||
ctx.addEventListener('message', (event) => {
|
||||
console.log("Received message in worker:", event.data);
|
||||
|
||||
const { unsigned, difficulty } = event.data;
|
||||
const { unsigned, difficulty, nonceStart, nonceStep } = event.data;
|
||||
|
||||
const result = minePow(unsigned, difficulty);
|
||||
const result = minePow(unsigned, difficulty, nonceStart, nonceStep);
|
||||
console.log("Mining result:", result);
|
||||
|
||||
// Post the mined event back to the main thread
|
||||
@ -43,32 +43,32 @@ export function getPow(hex: string): number {
|
||||
*
|
||||
* Adapted from Snort: https://git.v0l.io/Kieran/snort/src/commit/4df6c19248184218c4c03728d61e94dae5f2d90c/packages/system/src/pow-util.ts#L14-L36
|
||||
*/
|
||||
export function minePow<K extends number>(unsigned: UnsignedEvent<K>, difficulty: number): Omit<Event<K>, 'sig'> {
|
||||
let count = 0
|
||||
export function minePow<K extends number>(unsigned: UnsignedEvent<K>, difficulty: number, nonceStart: number, nonceStep: number): { found: boolean, event?: Omit<Event<K>, 'sig'> } {
|
||||
let nonce = nonceStart;
|
||||
|
||||
const event = unsigned as Omit<Event<K>, 'sig'>
|
||||
const tag = ['nonce', count.toString(), difficulty.toString()]
|
||||
const tag = ['nonce', nonce.toString(), difficulty.toString()]
|
||||
|
||||
event.tags.push(tag)
|
||||
event.tags.push(tag);
|
||||
|
||||
// We use a while loop that might run indefinitely until a solution is found.
|
||||
// Consider adding a breaking condition if you want to limit the number of nonces each worker checks.
|
||||
while (true) {
|
||||
const now = Math.floor(new Date().getTime() / 1000)
|
||||
tag[1] = (nonce).toString();
|
||||
|
||||
if (now !== event.created_at) {
|
||||
count = 0
|
||||
event.created_at = now
|
||||
}
|
||||
|
||||
tag[1] = (++count).toString()
|
||||
|
||||
event.id = getEventHash(event)
|
||||
event.id = getEventHash(event);
|
||||
|
||||
if (getPow(event.id) >= difficulty) {
|
||||
break
|
||||
}
|
||||
return { found: true, event: event };
|
||||
}
|
||||
|
||||
return event
|
||||
nonce += nonceStep;
|
||||
|
||||
if (nonce % (nonceStep * 10000) === 0) {
|
||||
ctx.postMessage({ status: 'progress', currentNonce: nonce });
|
||||
}
|
||||
}
|
||||
return { found: false };
|
||||
}
|
||||
|
||||
export default ctx;
|
||||
|
Loading…
Reference in New Issue
Block a user