fix file upload and UI work

This commit is contained in:
smolgrrr 2023-11-25 15:28:21 +11:00
parent 2526540403
commit 933009d2c4
11 changed files with 78 additions and 116 deletions

View File

@ -6,3 +6,5 @@ README.md
.next
.git
build
.env
bun.lockb

View File

@ -12,14 +12,6 @@ interface EmojiPickerProps {
ref: RefObject<HTMLDivElement>;
}
const customCategoryIcons = {
categoryIcons: {
poast: {
src: "https://poa.st/emoji/custom/poast_hat.png",
},
},
};
export function EmojiPicker({
topOffset,
leftOffset,

View File

@ -6,8 +6,7 @@ import {
} from "@heroicons/react/24/outline";
import { XCircleIcon } from "@heroicons/react/24/solid";
import { useState, useEffect, useRef } from "react";
import { generatePrivateKey, getPublicKey, finishEvent, UnsignedEvent, Event as NostrEvent, nip19 } from "nostr-tools";
import { publish } from "../../utils/relays";
import { UnsignedEvent, Event as NostrEvent, nip19 } from "nostr-tools";
import { renderMedia, attachFile } from "../../utils/FileUpload";
import { EmojiPicker } from "./Emojis/emoji-picker";
import customEmojis from './custom_emojis.json';

View File

@ -52,6 +52,7 @@ export const useSubmitForm = (unsigned: UnsignedEvent, difficulty: string) => {
setDoingWorkProp(false);
const signedEvent = finishEvent(unsignedPoWEvent, sk);
publish(signedEvent);
setSk(generatePrivateKey())
}
}, [unsignedPoWEvent]);

View File

@ -1,6 +1,5 @@
import {
Cog6ToothIcon,
QuestionMarkCircleIcon,
Cog6ToothIcon
} from "@heroicons/react/24/outline";
export default function Header() {

View File

@ -5,31 +5,11 @@ import { Event, nip19 } from "nostr-tools";
import { getMetadata } from "../../utils/otherUtils";
import ContentPreview from "./CardModals/TextModal";
import { renderMedia } from "../../utils/FileUpload";
import { getIconFromHash } from "../../utils/deterministicProfileIcon";
import { getIconFromHash, timeAgo } from "../../utils/cardUtils";
import { verifyPow } from "../../utils/mine";
import { uniqBy } from "../../utils/otherUtils";
import { useNavigate } from 'react-router-dom';
const timeUnits = [
{ unit: 'w', value: 60 * 60 * 24 * 7 },
{ unit: 'd', value: 60 * 60 * 24 },
{ unit: 'h', value: 60 * 60 },
{ unit: 'm', value: 60 },
];
const timeAgo = (unixTime: number) => {
let seconds = Math.floor(new Date().getTime() / 1000 - unixTime);
if (seconds < 60) return `now`;
for (let unit of timeUnits) {
if (seconds >= unit.value) {
return `${Math.floor(seconds / unit.value)}${unit.unit}`;
}
seconds %= unit.value;
}
};
interface CardProps {
key?: string | number;
event: Event;
@ -47,7 +27,7 @@ const PostCard = ({
repliedTo,
type
}: CardProps) => {
const { comment, file } = parseContent(event);
const { file } = parseContent(event);
const icon = getIconFromHash(event.pubkey);
const metadataParsed = metadata ? getMetadata(metadata) : null;
const navigate = useNavigate();
@ -78,7 +58,6 @@ const PostCard = ({
))}
</div>}
<div className={`flex justify-between items-center ${type !== "OP" ? 'hover:cursor-pointer' : ''}`} onClick={handleClick}>
<div className="flex items-center gap-2.5">
{metadataParsed ?
<img
key = {key}
@ -90,7 +69,6 @@ const PostCard = ({
:
<div className={`h-4 w-4 ${icon} rounded-full`} />
}
</div>
<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(event)}

View File

@ -1,66 +1,23 @@
import { parseContent } from "../../../utils/content";
import { Event } from "nostr-tools";
import { getMetadata, uniqBy } from "../../../utils/otherUtils";
import { getMetadata } from "../../../utils/otherUtils";
import ContentPreview from "./TextModal";
import { renderMedia } from "../../../utils/FileUpload";
const colorCombos = [
"from-red-400 to-yellow-500",
"from-green-400 to-blue-500",
"from-purple-400 to-pink-500",
"from-yellow-400 to-orange-500",
"from-indigo-400 to-purple-500",
"from-pink-400 to-red-500",
"from-blue-400 to-indigo-500",
"from-orange-400 to-red-500",
"from-teal-400 to-green-500",
"from-cyan-400 to-teal-500",
"from-lime-400 to-green-500",
"from-amber-400 to-orange-500",
"from-rose-400 to-pink-500",
"from-violet-400 to-purple-500",
"from-sky-400 to-cyan-500",
];
const getColorFromHash = (id: string, colors: string[]): string => {
// Create a simple hash from the event.id
let hash = 0;
for (let i = 0; i < id.length; i++) {
hash = (hash << 5) - hash + id.charCodeAt(i);
}
// Use the hash to pick a color from the colors array
const index = Math.abs(hash) % colors.length;
return colors[index];
};
const timeAgo = (unixTime: number) => {
const seconds = Math.floor(new Date().getTime() / 1000 - unixTime);
if (seconds < 60) return `now`;
const minutes = Math.floor(seconds / 60);
if (minutes < 60) return `${minutes}m`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `${hours}h`;
const days = Math.floor(hours / 24);
if (days < 7) return `${days}d`;
const weeks = Math.floor(days / 7);
return `${weeks}w`;
};
import { getIconFromHash, timeAgo } from "../../../utils/cardUtils";
import { CpuChipIcon } from "@heroicons/react/24/outline";
import { verifyPow } from "../../../utils/mine";
const QuoteEmbed = ({
key,
event,
metadata,
}: {
key?: string | number;
event: Event;
metadata: Event | null;
}) => {
const { comment, file } = parseContent(event);
const colorCombo = getColorFromHash(event.pubkey, colorCombos);
const { file } = parseContent(event);
const icon = getIconFromHash(event.pubkey);
let metadataParsed = null;
if (metadata !== null) {
@ -68,31 +25,33 @@ const QuoteEmbed = ({
}
return (
<div className="p-3 rounded-lg border border-neutral-700 bg-neutral-800">
<div className="flex flex-col">
<div className="p-3 rounded-lg border border-neutral-700">
<div className="flex flex-col gap-2">
<div className="flex flex-col break-words">
<ContentPreview key={event.id} eventdata={event} />
</div>
{renderMedia(file)}
<div className="flex justify-between items-center">
<div className="flex items-center gap-1.5">
{metadataParsed ? (
<>
{metadataParsed ?
<img
key={key}
className={`h-5 w-5 rounded-full`}
src={metadataParsed.picture}
src={metadataParsed?.picture ?? icon}
alt=""
/>
<div className="text-sm font-medium">{metadataParsed.name}</div>
</>
) : (
<>
<div
className={`h-4 w-4 bg-gradient-to-r ${colorCombo} rounded-full`}
/>
<div className="text-sm font-medium">Anonymous</div>
</>
)}
loading="lazy"
decoding="async" />
:
<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(event)}
</div>
<span className="text-neutral-700">·</span>
<div className="text-xs font-semibold text-neutral-600">
{timeAgo(event.created_at)}
</div>
<span className="text-neutral-700">·</span>
</div>
</div>
</div>

View File

@ -26,7 +26,7 @@ const RichText = ({ text, isExpanded, emojiMap }: { text: string; isExpanded: bo
};
const ContentPreview = ({ key, eventdata }: { key: string; eventdata: Event }) => {
const { comment, file } = parseContent(eventdata);
const { comment } = parseContent(eventdata);
const [finalComment, setFinalComment] = useState(comment);
const [quoteEvents, setQuoteEvents] = useState<Event[]>([]); // Initialize state
const [isExpanded, setIsExpanded] = useState(false);

View File

@ -1,3 +1,6 @@
import { generatePrivateKey, getPublicKey, finishEvent } from "nostr-tools";
import { base64 } from "@scure/base";
export interface UploadResult {
url?: string;
error?: string;
@ -10,6 +13,20 @@ export interface UploadResult {
export default async function FileUpload(file: File): Promise<UploadResult> {
const buf = await file.arrayBuffer();
const sk = generatePrivateKey();
const auth = async () => {
const authEvent = {
kind: 27235,
tags: [
["u", "https://void.cat/upload"],
["method", "POST"]
],
content: "",
created_at: Math.floor(Date.now() / 1000),
pubkey: getPublicKey(sk),
}
return `Nostr ${base64.encode(new TextEncoder().encode(JSON.stringify(finishEvent(authEvent, sk))))}`;
};
const req = await fetch("https://void.cat/upload", {
body: buf,
@ -20,6 +37,7 @@ export default async function FileUpload(file: File): Promise<UploadResult> {
"V-Filename": file.name, // Extracting the filename
"V-Description": "Upload from https://tao-green.vercel.app/",
"V-Strip-Metadata": "true", // Here's the new header
"authorization": await auth()
},
});
if (req.ok) {

View File

@ -38,3 +38,23 @@ export const getIconFromHash = (id: string): string => {
return `${gradientDirections[directionIndex]} ${colorCombos[colorIndex]}`;
};
const timeUnits = [
{ unit: 'w', value: 60 * 60 * 24 * 7 },
{ unit: 'd', value: 60 * 60 * 24 },
{ unit: 'h', value: 60 * 60 },
{ unit: 'm', value: 60 },
];
export const timeAgo = (unixTime: number) => {
let seconds = Math.floor(new Date().getTime() / 1000 - unixTime);
if (seconds < 60) return `now`;
for (let unit of timeUnits) {
if (seconds >= unit.value) {
return `${Math.floor(seconds / unit.value)}${unit.unit}`;
}
seconds %= unit.value;
}
};

View File

@ -80,12 +80,6 @@ export const unsubAll = () => {
currentSubList.length = 0;
};
type PublishCallback = (
relay: string,
errorMessage?: string,
) => void;
export const publish = (event: Event) => {
relayMap.forEach(async (relay, url) => {
try {