use redux for NoteCreator state management (#494)
* use redux for NoteCreator state management This fixes a bug where the modal closes while replying to a note. This happens if the thread re-renders while you are replying. Drafts of notes are also now automatically saved unless the user clicks the cancel button. * fix modal closing bug * really fix modal closing bug * fix rebase
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
import "./NoteCreator.css";
|
||||
import { useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { RawEvent, TaggedRawEvent } from "@snort/nostr";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { TaggedRawEvent } from "@snort/nostr";
|
||||
|
||||
import Icon from "Icons/Icon";
|
||||
import useEventPublisher from "Feed/EventPublisher";
|
||||
@ -11,6 +11,18 @@ import Modal from "Element/Modal";
|
||||
import ProfileImage from "Element/ProfileImage";
|
||||
import useFileUpload from "Upload";
|
||||
import Note from "Element/Note";
|
||||
import {
|
||||
setShow,
|
||||
setNote,
|
||||
setError,
|
||||
setActive,
|
||||
setPreview,
|
||||
setShowAdvanced,
|
||||
setZapForward,
|
||||
setSensitive,
|
||||
reset,
|
||||
} from "State/NoteCreator";
|
||||
import type { RootState } from "State/Store";
|
||||
import { LNURL } from "LNURL";
|
||||
|
||||
import messages from "./messages";
|
||||
@ -31,26 +43,20 @@ function NotePreview({ note }: NotePreviewProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export interface NoteCreatorProps {
|
||||
show: boolean;
|
||||
setShow: (s: boolean) => void;
|
||||
replyTo?: TaggedRawEvent;
|
||||
onSend?: () => void;
|
||||
autoFocus: boolean;
|
||||
}
|
||||
|
||||
export function NoteCreator(props: NoteCreatorProps) {
|
||||
const { show, setShow, replyTo, onSend, autoFocus } = props;
|
||||
export function NoteCreator() {
|
||||
const { formatMessage } = useIntl();
|
||||
const publisher = useEventPublisher();
|
||||
const [note, setNote] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const [active, setActive] = useState(false);
|
||||
const [preview, setPreview] = useState<RawEvent>();
|
||||
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||
const [zapForward, setZapForward] = useState("");
|
||||
const [sensitive, setSensitiveContent] = useState<string>();
|
||||
const uploader = useFileUpload();
|
||||
const note = useSelector((s: RootState) => s.noteCreator.note);
|
||||
const show = useSelector((s: RootState) => s.noteCreator.show);
|
||||
const error = useSelector((s: RootState) => s.noteCreator.error);
|
||||
const active = useSelector((s: RootState) => s.noteCreator.active);
|
||||
const preview = useSelector((s: RootState) => s.noteCreator.preview);
|
||||
const replyTo = useSelector((s: RootState) => s.noteCreator.replyTo);
|
||||
const showAdvanced = useSelector((s: RootState) => s.noteCreator.showAdvanced);
|
||||
const zapForward = useSelector((s: RootState) => s.noteCreator.zapForward);
|
||||
const sensitive = useSelector((s: RootState) => s.noteCreator.sensitive);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
async function sendNote() {
|
||||
if (note) {
|
||||
@ -61,10 +67,12 @@ export function NoteCreator(props: NoteCreatorProps) {
|
||||
await svc.load();
|
||||
extraTags = [svc.getZapTag()];
|
||||
} catch {
|
||||
setError(
|
||||
formatMessage({
|
||||
defaultMessage: "Invalid LNURL",
|
||||
})
|
||||
dispatch(
|
||||
setError(
|
||||
formatMessage({
|
||||
defaultMessage: "Invalid LNURL",
|
||||
})
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -76,12 +84,7 @@ export function NoteCreator(props: NoteCreatorProps) {
|
||||
const ev = replyTo ? await publisher.reply(replyTo, note, extraTags) : await publisher.note(note, extraTags);
|
||||
console.debug("Sending note: ", ev);
|
||||
publisher.broadcast(ev);
|
||||
setNote("");
|
||||
setShow(false);
|
||||
if (typeof onSend === "function") {
|
||||
onSend();
|
||||
}
|
||||
setActive(false);
|
||||
dispatch(reset());
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,34 +94,30 @@ export function NoteCreator(props: NoteCreatorProps) {
|
||||
if (file) {
|
||||
const rx = await uploader.upload(file, file.name);
|
||||
if (rx.url) {
|
||||
setNote(n => `${n ? `${n}\n` : ""}${rx.url}`);
|
||||
dispatch(setNote(`${note ? `${note}\n` : ""}${rx.url}`));
|
||||
} else if (rx?.error) {
|
||||
setError(rx.error);
|
||||
dispatch(setError(rx.error));
|
||||
}
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
setError(error?.message);
|
||||
dispatch(setError(error?.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onChange(ev: React.ChangeEvent<HTMLTextAreaElement>) {
|
||||
const { value } = ev.target;
|
||||
setNote(value);
|
||||
dispatch(setNote(value));
|
||||
if (value) {
|
||||
setActive(true);
|
||||
dispatch(setActive(true));
|
||||
} else {
|
||||
setActive(false);
|
||||
dispatch(setActive(false));
|
||||
}
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
setShow(false);
|
||||
setNote("");
|
||||
setShowAdvanced(false);
|
||||
setPreview(undefined);
|
||||
setZapForward("");
|
||||
dispatch(reset());
|
||||
}
|
||||
|
||||
function onSubmit(ev: React.MouseEvent<HTMLButtonElement>) {
|
||||
@ -128,11 +127,11 @@ export function NoteCreator(props: NoteCreatorProps) {
|
||||
|
||||
async function loadPreview() {
|
||||
if (preview) {
|
||||
setPreview(undefined);
|
||||
dispatch(setPreview(null));
|
||||
} else {
|
||||
const tmpNote = await publisher.note(note);
|
||||
if (tmpNote) {
|
||||
setPreview(tmpNote);
|
||||
dispatch(setPreview(tmpNote));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,18 +154,18 @@ export function NoteCreator(props: NoteCreatorProps) {
|
||||
return (
|
||||
<>
|
||||
{show && (
|
||||
<Modal className="note-creator-modal" onClose={() => setShow(false)}>
|
||||
<Modal className="note-creator-modal" onClose={() => dispatch(setShow(false))}>
|
||||
{replyTo && <NotePreview note={replyTo} />}
|
||||
{preview && getPreviewNote()}
|
||||
{!preview && (
|
||||
<div className={`flex note-creator ${replyTo ? "note-reply" : ""}`}>
|
||||
<div className="flex f-col mr10 f-grow">
|
||||
<Textarea
|
||||
autoFocus={autoFocus}
|
||||
autoFocus
|
||||
className={`textarea ${active ? "textarea--focused" : ""}`}
|
||||
onChange={onChange}
|
||||
value={note}
|
||||
onFocus={() => setActive(true)}
|
||||
onFocus={() => dispatch(setActive(true))}
|
||||
/>
|
||||
<button type="button" className="attachment" onClick={attachFile}>
|
||||
<Icon name="attachment" />
|
||||
@ -176,7 +175,7 @@ export function NoteCreator(props: NoteCreatorProps) {
|
||||
</div>
|
||||
)}
|
||||
<div className="note-creator-actions">
|
||||
<button className="secondary" type="button" onClick={() => setShowAdvanced(s => !s)}>
|
||||
<button className="secondary" type="button" onClick={() => dispatch(setShowAdvanced(!showAdvanced))}>
|
||||
<FormattedMessage defaultMessage="Advanced" />
|
||||
</button>
|
||||
<button className="secondary" type="button" onClick={cancel}>
|
||||
@ -207,7 +206,7 @@ export function NoteCreator(props: NoteCreatorProps) {
|
||||
defaultMessage: "LNURL to forward zaps to",
|
||||
})}
|
||||
value={zapForward}
|
||||
onChange={e => setZapForward(e.target.value)}
|
||||
onChange={e => dispatch(setZapForward(e.target.value))}
|
||||
/>
|
||||
<h4>
|
||||
<FormattedMessage defaultMessage="Sensitive Content" />
|
||||
@ -223,7 +222,7 @@ export function NoteCreator(props: NoteCreatorProps) {
|
||||
className="w-max"
|
||||
type="text"
|
||||
value={sensitive}
|
||||
onChange={e => setSensitiveContent(e.target.value)}
|
||||
onChange={e => dispatch(setSensitive(e.target.value))}
|
||||
maxLength={50}
|
||||
minLength={1}
|
||||
placeholder={formatMessage({
|
||||
|
Reference in New Issue
Block a user