mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-19 03:51:34 +02:00
add kind of hacky image upload
This commit is contained in:
@@ -215,3 +215,15 @@ export const UnlockIcon = createIcon({
|
||||
d: "M7 10h13a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V11a1 1 0 0 1 1-1h1V9a7 7 0 0 1 13.262-3.131l-1.789.894A5 5 0 0 0 7 9v1zm-2 2v8h14v-8H5zm5 3h4v2h-4v-2z",
|
||||
defaultProps,
|
||||
});
|
||||
|
||||
export const ImageIcon = createIcon({
|
||||
displayName: "ImageIcon",
|
||||
d: "M4.828 21l-.02.02-.021-.02H2.992A.993.993 0 0 1 2 20.007V3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H4.828zM20 15V5H4v14L14 9l6 6zm0 2.828l-6-6L6.828 19H20v-1.172zM8 11a2 2 0 1 1 0-4 2 2 0 0 1 0 4z",
|
||||
defaultProps,
|
||||
});
|
||||
|
||||
export const CameraIcon = createIcon({
|
||||
displayName: "CameraIcon",
|
||||
d: "M9.828 5l-2 2H4v12h16V7h-3.828l-2-2H9.828zM9 3h6l2 2h4a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h4l2-2zm3 15a5.5 5.5 0 1 1 0-11 5.5 5.5 0 0 1 0 11zm0-2a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z",
|
||||
defaultProps,
|
||||
});
|
||||
|
@@ -67,7 +67,11 @@ export const Note = React.memo(({ event, maxHeight }: NoteProps) => {
|
||||
</Flex>
|
||||
</CardHeader>
|
||||
<CardBody px="2" py="0">
|
||||
<NoteContents event={event} trusted={following.includes(event.pubkey)} maxHeight={maxHeight} />
|
||||
<NoteContents
|
||||
event={event}
|
||||
trusted={event.pubkey === account.pubkey || following.includes(event.pubkey)}
|
||||
maxHeight={maxHeight}
|
||||
/>
|
||||
</CardBody>
|
||||
<CardFooter padding="2" display="flex" gap="2">
|
||||
<IconButton
|
||||
|
@@ -3,15 +3,17 @@ import {
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Flex,
|
||||
Button,
|
||||
Textarea,
|
||||
Text,
|
||||
useDisclosure,
|
||||
VisuallyHiddenInput,
|
||||
IconButton,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useList } from "react-use";
|
||||
import { nostrPostAction, PostResult } from "../../classes/nostr-post-action";
|
||||
import { getReferences } from "../../helpers/nostr-event";
|
||||
@@ -19,6 +21,7 @@ import { useWriteRelayUrls } from "../../hooks/use-client-relays";
|
||||
import { useIsMobile } from "../../hooks/use-is-mobile";
|
||||
import { useSigningContext } from "../../providers/signing-provider";
|
||||
import { DraftNostrEvent, NostrEvent } from "../../types/nostr-event";
|
||||
import { CameraIcon, ImageIcon } from "../icons";
|
||||
import { NoteLink } from "../note-link";
|
||||
import { NoteContents } from "../note/note-contents";
|
||||
import { PostResults } from "./post-results";
|
||||
@@ -39,6 +42,8 @@ type PostModalProps = {
|
||||
};
|
||||
|
||||
export const PostModal = ({ isOpen, onClose, initialDraft }: PostModalProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
const toast = useToast();
|
||||
const { requestSignature } = useSigningContext();
|
||||
const writeRelays = useWriteRelayUrls();
|
||||
const [waiting, setWaiting] = useState(false);
|
||||
@@ -46,6 +51,33 @@ export const PostModal = ({ isOpen, onClose, initialDraft }: PostModalProps) =>
|
||||
const [results, resultsActions] = useList<PostResult>();
|
||||
const { isOpen: showPreview, onToggle: togglePreview } = useDisclosure();
|
||||
const [draft, setDraft] = useState<DraftNostrEvent>(() => Object.assign(emptyDraft(), initialDraft));
|
||||
const imageUploadRef = useRef<HTMLInputElement | null>(null);
|
||||
const imageCaptureRef = useRef<HTMLInputElement | null>(null);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
const uploadImage = async (imageFile: File) => {
|
||||
try {
|
||||
if (!imageFile.type.includes("image")) throw new Error("Only images are supported");
|
||||
setUploading(true);
|
||||
const payload = new FormData();
|
||||
payload.append("fileToUpload", imageFile);
|
||||
const response = await fetch("https://nostr.build/upload.php", { body: payload, method: "POST" }).then((res) =>
|
||||
res.text()
|
||||
);
|
||||
const imageUrl = response.match(/https:\/\/nostr\.build\/i\/[\w.]+/)?.[0];
|
||||
if (imageUrl) {
|
||||
setDraft((d) => ({ ...d, content: (d.content += imageUrl) }));
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
toast({
|
||||
status: "error",
|
||||
description: e.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
setUploading(false);
|
||||
};
|
||||
|
||||
const handleContentChange: React.ChangeEventHandler<HTMLTextAreaElement> = (event) => {
|
||||
setDraft((d) => ({ ...d, content: event.target.value }));
|
||||
@@ -87,9 +119,56 @@ export const PostModal = ({ isOpen, onClose, initialDraft }: PostModalProps) =>
|
||||
{showPreview ? (
|
||||
<NoteContents event={draft} trusted />
|
||||
) : (
|
||||
<Textarea autoFocus mb="2" value={draft.content} onChange={handleContentChange} rows={5} />
|
||||
<Textarea
|
||||
autoFocus
|
||||
mb="2"
|
||||
value={draft.content}
|
||||
onChange={handleContentChange}
|
||||
rows={5}
|
||||
onPaste={(e) => {
|
||||
const imageFile = Array.from(e.clipboardData.files).find((f) => f.type.includes("image"));
|
||||
if (imageFile) uploadImage(imageFile);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Flex gap="2" alignItems="center" justifyContent="flex-end">
|
||||
<Flex mr="auto" gap="2">
|
||||
<VisuallyHiddenInput
|
||||
type="file"
|
||||
accept="image/*"
|
||||
ref={imageUploadRef}
|
||||
onChange={(e) => {
|
||||
const img = e.target.files?.[0];
|
||||
if (img) uploadImage(img);
|
||||
}}
|
||||
/>
|
||||
<VisuallyHiddenInput
|
||||
type="file"
|
||||
accept="image/*"
|
||||
ref={imageCaptureRef}
|
||||
capture="environment"
|
||||
onChange={(e) => {
|
||||
const img = e.target.files?.[0];
|
||||
if (img) uploadImage(img);
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
icon={<ImageIcon />}
|
||||
aria-label="Upload Image"
|
||||
title="Upload Image"
|
||||
onClick={() => imageUploadRef.current?.click()}
|
||||
isLoading={uploading}
|
||||
/>
|
||||
{isMobile && (
|
||||
<IconButton
|
||||
icon={<CameraIcon />}
|
||||
aria-label="Capture Image"
|
||||
title="Capture Image"
|
||||
onClick={() => imageUploadRef.current?.click()}
|
||||
isLoading={uploading}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
{draft.content.length > 0 && <Button onClick={togglePreview}>Preview</Button>}
|
||||
<Button onClick={onClose}>Cancel</Button>
|
||||
<Button colorScheme="blue" type="submit" isLoading={waiting} onClick={handleSubmit} isDisabled={!canSubmit}>
|
||||
@@ -104,7 +183,7 @@ export const PostModal = ({ isOpen, onClose, initialDraft }: PostModalProps) =>
|
||||
<Modal isOpen={isOpen} onClose={onClose} size="4xl">
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalBody padding="4">{renderContent()}</ModalBody>
|
||||
<ModalBody padding={isMobile ? "2" : "4"}>{renderContent()}</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
|
Reference in New Issue
Block a user