2023-01-16 17:48:25 +00:00
|
|
|
import "./NoteCreator.css";
|
2023-02-01 22:14:30 +00:00
|
|
|
import { useState } from "react";
|
2023-02-08 21:10:26 +00:00
|
|
|
import { FormattedMessage } from "react-intl";
|
2023-03-31 22:43:07 +00:00
|
|
|
import { RawEvent, TaggedRawEvent } from "@snort/nostr";
|
2023-02-08 21:10:26 +00:00
|
|
|
|
2023-03-02 17:47:02 +00:00
|
|
|
import Icon from "Icons/Icon";
|
2023-01-20 11:11:50 +00:00
|
|
|
import useEventPublisher from "Feed/EventPublisher";
|
|
|
|
import { openFile } from "Util";
|
|
|
|
import Textarea from "Element/Textarea";
|
2023-01-25 18:08:53 +00:00
|
|
|
import Modal from "Element/Modal";
|
2023-02-01 22:14:30 +00:00
|
|
|
import ProfileImage from "Element/ProfileImage";
|
2023-01-31 19:08:11 +00:00
|
|
|
import useFileUpload from "Upload";
|
2023-03-31 22:43:07 +00:00
|
|
|
import Note from "Element/Note";
|
2023-01-16 17:48:25 +00:00
|
|
|
|
2023-02-08 21:10:26 +00:00
|
|
|
import messages from "./messages";
|
|
|
|
|
2023-02-01 22:14:30 +00:00
|
|
|
interface NotePreviewProps {
|
2023-03-28 14:34:01 +00:00
|
|
|
note: TaggedRawEvent;
|
2023-02-01 22:14:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function NotePreview({ note }: NotePreviewProps) {
|
|
|
|
return (
|
|
|
|
<div className="note-preview">
|
2023-03-28 14:34:01 +00:00
|
|
|
<ProfileImage pubkey={note.pubkey} />
|
2023-02-01 22:14:30 +00:00
|
|
|
<div className="note-preview-body">
|
2023-03-28 14:34:01 +00:00
|
|
|
{note.content.slice(0, 136)}
|
|
|
|
{note.content.length > 140 && "..."}
|
2023-02-01 22:14:30 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
2023-02-01 22:14:30 +00:00
|
|
|
}
|
|
|
|
|
2023-01-16 17:48:25 +00:00
|
|
|
export interface NoteCreatorProps {
|
2023-02-07 20:04:50 +00:00
|
|
|
show: boolean;
|
|
|
|
setShow: (s: boolean) => void;
|
2023-03-28 14:34:01 +00:00
|
|
|
replyTo?: TaggedRawEvent;
|
2023-02-07 19:47:57 +00:00
|
|
|
onSend?: () => void;
|
2023-02-07 20:04:50 +00:00
|
|
|
autoFocus: boolean;
|
2023-01-16 17:48:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function NoteCreator(props: NoteCreatorProps) {
|
2023-02-07 20:04:50 +00:00
|
|
|
const { show, setShow, replyTo, onSend, autoFocus } = props;
|
2023-02-05 12:32:34 +00:00
|
|
|
const publisher = useEventPublisher();
|
2023-03-31 22:43:07 +00:00
|
|
|
const [note, setNote] = useState("");
|
|
|
|
const [error, setError] = useState("");
|
|
|
|
const [active, setActive] = useState(false);
|
|
|
|
const [preview, setPreview] = useState<RawEvent>();
|
2023-02-05 12:32:34 +00:00
|
|
|
const uploader = useFileUpload();
|
2023-01-16 17:48:25 +00:00
|
|
|
|
2023-02-05 12:32:34 +00:00
|
|
|
async function sendNote() {
|
|
|
|
if (note) {
|
2023-02-09 12:26:54 +00:00
|
|
|
const ev = replyTo ? await publisher.reply(replyTo, note) : await publisher.note(note);
|
2023-02-05 12:32:34 +00:00
|
|
|
console.debug("Sending note: ", ev);
|
|
|
|
publisher.broadcast(ev);
|
|
|
|
setNote("");
|
|
|
|
setShow(false);
|
|
|
|
if (typeof onSend === "function") {
|
|
|
|
onSend();
|
|
|
|
}
|
|
|
|
setActive(false);
|
2023-01-16 17:48:25 +00:00
|
|
|
}
|
2023-02-05 12:32:34 +00:00
|
|
|
}
|
2023-01-16 17:48:25 +00:00
|
|
|
|
2023-02-05 12:32:34 +00:00
|
|
|
async function attachFile() {
|
|
|
|
try {
|
2023-02-07 19:47:57 +00:00
|
|
|
const file = await openFile();
|
2023-02-05 12:32:34 +00:00
|
|
|
if (file) {
|
2023-02-07 19:47:57 +00:00
|
|
|
const rx = await uploader.upload(file, file.name);
|
2023-02-05 12:32:34 +00:00
|
|
|
if (rx.url) {
|
2023-02-09 12:26:54 +00:00
|
|
|
setNote(n => `${n ? `${n}\n` : ""}${rx.url}`);
|
2023-02-05 12:32:34 +00:00
|
|
|
} else if (rx?.error) {
|
|
|
|
setError(rx.error);
|
2023-01-16 17:48:25 +00:00
|
|
|
}
|
2023-02-05 12:32:34 +00:00
|
|
|
}
|
2023-02-07 19:47:57 +00:00
|
|
|
} catch (error: unknown) {
|
|
|
|
if (error instanceof Error) {
|
|
|
|
setError(error?.message);
|
|
|
|
}
|
2023-01-16 17:48:25 +00:00
|
|
|
}
|
2023-02-05 12:32:34 +00:00
|
|
|
}
|
2023-01-16 17:48:25 +00:00
|
|
|
|
2023-02-07 19:47:57 +00:00
|
|
|
function onChange(ev: React.ChangeEvent<HTMLTextAreaElement>) {
|
2023-02-07 20:04:50 +00:00
|
|
|
const { value } = ev.target;
|
|
|
|
setNote(value);
|
2023-02-05 12:32:34 +00:00
|
|
|
if (value) {
|
2023-02-07 20:04:50 +00:00
|
|
|
setActive(true);
|
2023-02-05 12:32:34 +00:00
|
|
|
} else {
|
2023-02-07 20:04:50 +00:00
|
|
|
setActive(false);
|
2023-01-16 17:48:25 +00:00
|
|
|
}
|
2023-02-05 12:32:34 +00:00
|
|
|
}
|
2023-01-16 17:48:25 +00:00
|
|
|
|
2023-02-07 19:47:57 +00:00
|
|
|
function cancel() {
|
2023-02-07 20:04:50 +00:00
|
|
|
setShow(false);
|
|
|
|
setNote("");
|
2023-02-05 12:32:34 +00:00
|
|
|
}
|
2023-01-25 18:08:53 +00:00
|
|
|
|
2023-02-05 12:32:34 +00:00
|
|
|
function onSubmit(ev: React.MouseEvent<HTMLButtonElement>) {
|
|
|
|
ev.stopPropagation();
|
|
|
|
sendNote().catch(console.warn);
|
|
|
|
}
|
2023-01-16 17:48:25 +00:00
|
|
|
|
2023-03-31 22:43:07 +00:00
|
|
|
async function loadPreview() {
|
|
|
|
if (preview) {
|
|
|
|
setPreview(undefined);
|
|
|
|
} else {
|
|
|
|
const tmpNote = await publisher.note(note);
|
|
|
|
if (tmpNote) {
|
|
|
|
setPreview(tmpNote);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPreviewNote() {
|
|
|
|
if (preview) {
|
|
|
|
return (
|
|
|
|
<Note
|
|
|
|
data={preview as TaggedRawEvent}
|
|
|
|
related={[]}
|
|
|
|
options={{
|
|
|
|
showFooter: false,
|
|
|
|
canClick: false,
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-05 12:32:34 +00:00
|
|
|
return (
|
|
|
|
<>
|
|
|
|
{show && (
|
2023-02-07 20:04:50 +00:00
|
|
|
<Modal className="note-creator-modal" onClose={() => setShow(false)}>
|
|
|
|
{replyTo && <NotePreview note={replyTo} />}
|
2023-03-31 22:43:07 +00:00
|
|
|
{preview && getPreviewNote()}
|
|
|
|
{!preview && (
|
|
|
|
<div className={`flex note-creator ${replyTo ? "note-reply" : ""}`}>
|
|
|
|
<div className="flex f-col mr10 f-grow">
|
|
|
|
<Textarea
|
|
|
|
autoFocus={autoFocus}
|
|
|
|
className={`textarea ${active ? "textarea--focused" : ""}`}
|
|
|
|
onChange={onChange}
|
|
|
|
value={note}
|
|
|
|
onFocus={() => setActive(true)}
|
|
|
|
/>
|
|
|
|
<button type="button" className="attachment" onClick={attachFile}>
|
|
|
|
<Icon name="attachment" />
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
{error && <span className="error">{error}</span>}
|
2023-01-16 17:48:25 +00:00
|
|
|
</div>
|
2023-03-31 22:43:07 +00:00
|
|
|
)}
|
2023-02-05 12:32:34 +00:00
|
|
|
<div className="note-creator-actions">
|
2023-03-31 22:43:07 +00:00
|
|
|
<button className="secondary" type="button" onClick={loadPreview}>
|
|
|
|
<FormattedMessage defaultMessage="Toggle Preview" />
|
|
|
|
</button>
|
2023-02-05 12:32:34 +00:00
|
|
|
<button className="secondary" type="button" onClick={cancel}>
|
2023-02-08 21:10:26 +00:00
|
|
|
<FormattedMessage {...messages.Cancel} />
|
2023-02-05 12:32:34 +00:00
|
|
|
</button>
|
|
|
|
<button type="button" onClick={onSubmit}>
|
2023-02-09 12:26:54 +00:00
|
|
|
{replyTo ? <FormattedMessage {...messages.Reply} /> : <FormattedMessage {...messages.Send} />}
|
2023-02-05 12:32:34 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</Modal>
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
);
|
2023-01-18 23:31:34 +00:00
|
|
|
}
|