various tings alright

This commit is contained in:
smolgrrr 2023-10-25 18:53:34 +11:00
parent 23da7698c8
commit 80a2b3fc29
9 changed files with 313 additions and 153 deletions

View File

@ -15,8 +15,10 @@
"nostr-tools": "latest",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-pull-to-refresh": "^2.0.1",
"react-router-dom": "^6.15.0",
"react-scripts": "5.0.1",
"react-swipe": "^6.0.4",
"react-swipeable-views": "^0.14.0",
"web-vitals": "^2.1.4",
"workbox-background-sync": "^6.6.0",

View File

@ -2,7 +2,7 @@ import { PropsWithChildren } from 'react';
export default function CardContainer({ children }: PropsWithChildren) {
return (
<div className="card bg-gradient-to-r from-black to-neutral-900 shadow-lg shadow-black">
<div className="card bg-gradient-to-r from-black to-neutral-950 shadow-lg shadow-black">
<div className="card-body p-4">{children}</div>
</div>
);

View File

@ -1,14 +1,16 @@
import CardContainer from './CardContainer';
import { ArrowUpTrayIcon, CpuChipIcon } from '@heroicons/react/24/outline';
import { useState } from 'react';
import { Event, generatePrivateKey, getPublicKey, finishEvent, relayInit} from 'nostr-tools';
import { Event, generatePrivateKey, getPublicKey, finishEvent, relayInit } from 'nostr-tools';
import { minePow } from '../../utils/mine';
import { publish } from '../../utils/relays';
import NostrImg from '../../utils/ImgUpload';
const difficulty = 20
const NewThreadCard: React.FC = () => {
const [comment, setComment] = useState("");
const [file, setFile] = useState("");
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
@ -18,73 +20,98 @@ const NewThreadCard: React.FC = () => {
const event = minePow({
kind: 1,
tags: [],
content: comment,
content: comment + " " + file,
created_at: Math.floor(Date.now() / 1000),
pubkey: getPublicKey(sk),
}, difficulty);
const signedEvent = finishEvent(event, sk);
await publish(signedEvent);
console.log(signedEvent.id);
setComment("")
setFile("")
} catch (error) {
setComment(comment + " " + error);
}
};
// async function attachFile(file_input: File | null) {
// try {
// if (file_input) {
// const rx = await NostrImg(file_input);
// if (rx.url) {
// setComment(comment + " " + rx.url);
// } else if (rx?.error) {
// setComment(comment + " " + rx.error);
// }
// }
// } catch (error: unknown) {
// if (error instanceof Error) {
// setComment(comment + " " + error?.message);
// }
// }
// }
async function attachFile(file_input: File | null) {
try {
if (file_input) {
const rx = await NostrImg(file_input);
if (rx.url) {
setFile(rx.url);
} else if (rx?.error) {
setFile(rx.error);
}
}
} catch (error: unknown) {
if (error instanceof Error) {
setFile(error?.message);
}
}
}
return (
<>
<CardContainer>
<form
name="post"
method="post"
encType="multipart/form-data"
className=""
onSubmit={handleSubmit}
>
<input type="hidden" name="MAX_FILE_SIZE" defaultValue={4194304} />
<div id="togglePostFormLink" className="text-lg font-semibold">
Start a New Thread
<form
name="post"
method="post"
encType="multipart/form-data"
className=""
onSubmit={handleSubmit}
>
<input type="hidden" name="MAX_FILE_SIZE" defaultValue={4194304} />
<div id="togglePostFormLink" className="text-lg font-semibold">
Start a New Thread
</div>
<div>
<textarea
name="com"
wrap="soft"
className="w-full p-2 rounded bg-gradient-to-r from-blue-900 to-cyan-500 text-white border-none placeholder-blue-300"
placeholder='Shitpost here...'
value={comment}
onChange={(e) => setComment(e.target.value)}
/>
</div>
<div>
{file !== "" && (
<div className="file m-0.5">
<img
src={file}
loading="lazy"
/>
</div>
<div>
<textarea
name="com"
wrap="soft"
className="w-full p-2 rounded bg-gradient-to-r from-blue-900 to-cyan-500 text-white border-none placeholder-blue-300"
placeholder='Shitpost here...'
value={comment}
onChange={(e) => setComment(e.target.value)}
)}
</div>
<div className="flex justify-between items-center">
<div className="flex items-center">
<ArrowUpTrayIcon
className="h-6 w-6 text-white 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);
}
}}
/>
</div>
<div className="flex justify-between items-center">
<div className="flex items-center">
<ArrowUpTrayIcon className="h-6 w-6 text-white" />
<input type="file" className="hidden" />
</div>
<span className="flex items-center"><CpuChipIcon className="h-6 w-6 text-white" />: {difficulty}</span>
<button type="submit" className="px-4 py-2 bg-gradient-to-r from-cyan-900 to-blue-500 rounded text-white font-semibold">
Submit
</button>
</div>
<div id="postFormError" className="text-red-500" />
</form>
<span className="flex items-center"><CpuChipIcon className="h-6 w-6 text-white" />: {difficulty}</span>
<button type="submit" className="px-4 py-2 bg-gradient-to-r from-cyan-900 to-blue-500 rounded text-white font-semibold">
Submit
</button>
</div>
<div id="postFormError" className="text-red-500" />
</form>
</CardContainer>
</>
);

View File

@ -94,14 +94,14 @@ const PostCard = ({ event, metadata, replyCount }: { event: Event, metadata: Eve
<div className="mr-2 flex flex-col break-words">
{comment}
</div>
{/* {file !== "" && (
{file !== "" && (
<div className="file">
<img
src={file}
loading="lazy"
/>
</div>
)} */}
)}
</div>
</a>
</CardContainer>

View File

@ -1,56 +1,55 @@
import React, { useEffect, useState } from 'react';
import React, { useState, useEffect } from 'react';
import {generatePrivateKey, getPublicKey} from 'nostr-tools';
// import {powEvent} from './system';
// import {publish} from './relays';
const Settings = () => {
// State variables to hold the settings
const [isDarkMode, setIsDarkMode] = useState(false);
const [username, setUsername] = useState('');
const [filterDifficulty, setFilterDifficulty] = useState(localStorage.getItem('filterDifficulty') || 20);
const [difficulty, setDifficulty] = useState(localStorage.getItem('difficulty') || 20);
// Mimic fetching existing settings from an API or local storage
useEffect(() => {
// Simulate fetching existing settings
const existingSettings = {
isDarkMode: false, // replace with actual value
username: '' // replace with actual value
};
setIsDarkMode(existingSettings.isDarkMode);
setUsername(existingSettings.username);
}, []);
// Function to save changes (simulate API call or local storage update)
const saveChanges = () => {
// Replace this with an actual API call or local storage update
console.log('Dark Mode:', isDarkMode);
console.log('Username:', username);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
localStorage.setItem('filterDifficulty', String(filterDifficulty));
localStorage.setItem('difficulty', String(difficulty));
};
return (
<div className="settings-page">
<h1>Settings</h1>
<div className="setting-item">
<label>
Dark Mode
<input
type="checkbox"
checked={isDarkMode}
onChange={(e) => setIsDarkMode(e.target.checked)}
/>
<div className="settings-page bg-black text-white min-h-screen p-8">
<h1 className="text-lg font-semibold mb-4">Settings</h1>
<form onSubmit={handleSubmit}>
<div className="flex flex-wrap -mx-2 mb-4">
<div className="w-full md:w-1/3 px-2 mb-4 md:mb-0">
<label className="block mb-2" htmlFor="filterDifficulty">
Filter Difficulty:
</label>
<input
id="filterDifficulty"
type="number"
value={filterDifficulty}
onChange={e => setFilterDifficulty(e.target.value)}
className="w-full px-3 py-2 border rounded-md text-black"
/>
</div>
<div className="setting-item">
<label>
Username
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<div className="w-full md:w-1/3 px-2 mb-4 md:mb-0">
<label className="block mb-2" htmlFor="difficulty">
Post Difficulty:
</label>
<input
id="difficulty"
type="number"
value={difficulty}
onChange={e => setDifficulty(e.target.value)}
className="w-full px-3 py-2 border rounded-md text-black"
/>
</div>
<button onClick={saveChanges}>Save Changes</button>
</div>
<button className="bg-gradient-to-r from-blue-900 to-cyan-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Save Settings
</button>
</form>
</div>
);
};

View File

@ -10,6 +10,7 @@ import { ArrowUpTrayIcon, CpuChipIcon } from '@heroicons/react/24/outline';
import { generatePrivateKey, getPublicKey, finishEvent, relayInit } from 'nostr-tools';
import { minePow } from '../../utils/mine';
import { publish } from '../../utils/relays';
import ThreadPost from './ThreadPost';
const difficulty = 20
@ -20,6 +21,7 @@ const Thread = () => {
let decodeResult = nip19.decode(id as string);
const [comment, setComment] = useState("");
const [showForm, setShowForm] = useState(false);
const [postType, setPostType] = useState("");
@ -80,7 +82,7 @@ const Thread = () => {
if (!uniqEvents[0]) {
return (
<main className="bg-black text-white min-h-screen">
<>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 p-4">
<div className="border border-blue-300 shadow rounded-md p-4 max-w-sm w-full mx-auto">
<div className="animate-pulse flex space-x-4">
@ -98,7 +100,7 @@ const Thread = () => {
</div>
</div>
</div>
</main>
</>
);
}
return (
@ -107,44 +109,24 @@ const Thread = () => {
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 p-4">
<PostCard event={uniqEvents[0]} metadata={getMetadataEvent(uniqEvents[0])} replyCount={countReplies(uniqEvents[0])} />
<div className="col-span-full flex justify-center space-x-36 ">
<DocumentTextIcon className="h-5 w-5 text-gray-200" onClick={() => setShowForm(prevShowForm => !prevShowForm)} />
<FolderPlusIcon className="h-5 w-5 text-gray-200" onClick={() => setShowForm(prevShowForm => !prevShowForm)} />
<DocumentTextIcon
className="h-5 w-5 text-gray-200"
onClick={() => {
setShowForm(prevShowForm => !prevShowForm);
setPostType('r');
}}
/>
<FolderPlusIcon
className="h-5 w-5 text-gray-200"
onClick={() => {
setShowForm(prevShowForm => !prevShowForm);
setPostType('q');
}}
/>
</div>
<div>
{showForm && (
<form
name="post"
method="post"
encType="multipart/form-data"
className=""
onSubmit={handleSubmit}
>
<input type="hidden" name="MAX_FILE_SIZE" defaultValue={4194304} />
<div id="togglePostFormLink" className="text-lg font-semibold">
Reply to note
</div>
<div>
<textarea
name="com"
wrap="soft"
className="w-full p-2 rounded bg-gradient-to-r from-blue-900 to-cyan-500 text-white border-none placeholder-blue-300"
placeholder='Shitpost here...'
value={comment}
onChange={(e) => setComment(e.target.value)}
/>
</div>
<div className="flex justify-between items-center">
<div className="flex items-center">
<ArrowUpTrayIcon className="h-6 w-6 text-white" />
<input type="file" className="hidden" />
</div>
<span className="flex items-center"><CpuChipIcon className="h-6 w-6 text-white" />: {difficulty}</span>
<button type="submit" className="px-4 py-2 bg-gradient-to-r from-cyan-900 to-blue-500 rounded text-white font-semibold">
Submit
</button>
</div>
<div id="postFormError" className="text-red-500" />
</form>)}
<ThreadPost state={showForm} type={postType} />
</div>
<div className="col-span-full h-0.5 bg-neutral-900"></div> {/* This is the white line separator */}
{uniqEvents

View File

@ -0,0 +1,133 @@
import { useParams } from 'react-router-dom';
import { useState } from "react";
import { ArrowUpTrayIcon, CpuChipIcon } from '@heroicons/react/24/outline';
import { generatePrivateKey, getPublicKey, finishEvent, relayInit } from 'nostr-tools';
import { minePow } from '../../utils/mine';
import { publish } from '../../utils/relays';
import NostrImg from '../../utils/ImgUpload';
import { nip19 } from 'nostr-tools';
const difficulty = 5
const ThreadPost = ({ state, type }: { state: Boolean, type: String }) => {
const { id} = useParams();
const [comment, setComment] = useState("");
const [file, setFile] = useState("");
const [tags, setTags] = useState([['']]);
let decodeResult = nip19.decode(id as string);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
let sk = generatePrivateKey();
let id = decodeResult.data as string
if (type === 'r') {
tags.push(["e", id as string])
} else if (type === 'q') {
tags.push(["q", id as string])
setComment(comment + ' nostr:' + id)
}
try {
const event = minePow({
kind: 1,
tags,
content: comment + " " + file,
created_at: Math.floor(Date.now() / 1000),
pubkey: getPublicKey(sk),
}, difficulty);
const signedEvent = finishEvent(event, sk);
await publish(signedEvent);
console.log(signedEvent.id);
} catch (error) {
setComment(comment + " " + error);
}
};
async function attachFile(file_input: File | null) {
try {
if (file_input) {
const rx = await NostrImg(file_input);
if (rx.url) {
setFile(rx.url);
} else if (rx?.error) {
setFile(rx.error);
}
}
} catch (error: unknown) {
if (error instanceof Error) {
setFile(error?.message);
}
}
}
return (
<>
{state && (
<form
name="post"
method="post"
encType="multipart/form-data"
className=""
onSubmit={handleSubmit}
>
<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>
<textarea
name="com"
wrap="soft"
className="w-full p-2 rounded bg-gradient-to-r from-blue-900 to-cyan-500 text-white border-none placeholder-blue-300"
placeholder='Shitpost here...'
value={comment}
onChange={(e) => setComment(e.target.value)}
/>
</div>
<div>
{file !== "" && (
<div className="file m-0.5">
<img
src={file}
loading="lazy"
/>
</div>
)}
</div>
<div className="flex justify-between items-center">
<div className="flex items-center">
<ArrowUpTrayIcon
className="h-6 w-6 text-white 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);
}
}}
/>
</div>
<span className="flex items-center"><CpuChipIcon className="h-6 w-6 text-white" />: {difficulty}</span>
<button type="submit" className="px-4 py-2 bg-gradient-to-r from-cyan-900 to-blue-500 rounded text-white font-semibold">
Submit
</button>
</div>
<div id="postFormError" className="text-red-500" />
</form>)}
</>
);
};
export default ThreadPost;

View File

@ -9,6 +9,7 @@ body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: black;
}
code {

View File

@ -9,27 +9,27 @@ export interface UploadResult {
*/
export default async function NostrImg(file: File ): Promise<UploadResult> {
const buf = await file.arrayBuffer();
const fd = new FormData();
fd.append("image", file);
const req = await fetch("https://void.cat/upload", {
body: buf,
method: "POST",
headers: {
"Content-Type": "application/octet-stream",
"V-Content-Type": file.type, // Extracting the mime type
"V-Filename": file.name, // Extracting the filename
"V-Description": "Upload from https://tao-green.vercel.app/",
},
});
if (req.ok) {
let rsp: VoidUploadResponse = await req.json();
const fileExtension = file.name.split('.').pop(); // Extracting the file extension
const resultUrl = `https://void.cat/d/${rsp.file?.id}.${fileExtension}`;
return {url: resultUrl};
const req = await fetch("https://nostrimg.com/api/upload", {
body: fd,
method: "POST",
headers: {
accept: "application/json",
},
});
if (req.ok) {
const data: UploadResponse = await req.json();
if (typeof data?.imageUrl === "string" && data.success) {
return {
url: new URL(data.imageUrl).toString(),
};
}
return {
error: "Upload failed",
};
}
return {
error: "Upload failed",
};
}
export interface UploadResult {
@ -43,6 +43,22 @@ export type VoidUploadResponse = {
errorMessage?: string,
}
interface UploadResponse {
fileID?: string;
fileName?: string;
imageUrl?: string;
lightningDestination?: string;
lightningPaymentLink?: string;
message?: string;
route?: string;
status: number;
success: boolean;
url?: string;
data?: {
url?: string;
};
}
export type VoidFile = {
id: string,
meta?: VoidFileMeta