fixing unique "key" prop errors in react dev mode
This commit is contained in:
@ -14,81 +14,81 @@ interface MarkdownProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: MarkdownProps, ref) => {
|
const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: MarkdownProps, ref) => {
|
||||||
function renderToken(t: Token): ReactNode {
|
function renderToken(t: Token, key: number): ReactNode {
|
||||||
try {
|
try {
|
||||||
switch (t.type) {
|
switch (t.type) {
|
||||||
case "paragraph": {
|
case "paragraph": {
|
||||||
return <p>{t.tokens ? t.tokens.map(renderToken) : t.raw}</p>;
|
return <p key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</p>;
|
||||||
}
|
}
|
||||||
case "image": {
|
case "image": {
|
||||||
return <img src={t.href} />;
|
return <img key={key} src={t.href} />;
|
||||||
}
|
}
|
||||||
case "heading": {
|
case "heading": {
|
||||||
switch (t.depth) {
|
switch (t.depth) {
|
||||||
case 1:
|
case 1:
|
||||||
return <h1>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h1>;
|
return <h1 key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h1>;
|
||||||
case 2:
|
case 2:
|
||||||
return <h2>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h2>;
|
return <h2 key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h2>;
|
||||||
case 3:
|
case 3:
|
||||||
return <h3>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h3>;
|
return <h3 key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h3>;
|
||||||
case 4:
|
case 4:
|
||||||
return <h4>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h4>;
|
return <h4 key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h4>;
|
||||||
case 5:
|
case 5:
|
||||||
return <h5>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h5>;
|
return <h5 key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h5>;
|
||||||
case 6:
|
case 6:
|
||||||
return <h6>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h6>;
|
return <h6 key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h6>;
|
||||||
}
|
}
|
||||||
throw new Error("Invalid heading");
|
throw new Error("Invalid heading");
|
||||||
}
|
}
|
||||||
case "codespan": {
|
case "codespan": {
|
||||||
return <code>{t.raw}</code>;
|
return <code key={key}>{t.raw}</code>;
|
||||||
}
|
}
|
||||||
case "code": {
|
case "code": {
|
||||||
return <pre>{t.raw}</pre>;
|
return <pre key={key}>{t.raw}</pre>;
|
||||||
}
|
}
|
||||||
case "br": {
|
case "br": {
|
||||||
return <br />;
|
return <br key={key} />;
|
||||||
}
|
}
|
||||||
case "hr": {
|
case "hr": {
|
||||||
return <hr />;
|
return <hr key={key} />;
|
||||||
}
|
}
|
||||||
case "blockquote": {
|
case "blockquote": {
|
||||||
return <blockquote>{t.tokens ? t.tokens.map(renderToken) : t.raw}</blockquote>;
|
return <blockquote key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</blockquote>;
|
||||||
}
|
}
|
||||||
case "link": {
|
case "link": {
|
||||||
return <HyperText link={t.href}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</HyperText>;
|
return <HyperText link={t.href} key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</HyperText>;
|
||||||
}
|
}
|
||||||
case "list": {
|
case "list": {
|
||||||
if (t.ordered) {
|
if (t.ordered) {
|
||||||
return <ol>{t.items.map(renderToken)}</ol>;
|
return <ol key={key}>{t.items.map(renderToken)}</ol>;
|
||||||
} else {
|
} else {
|
||||||
return <ul>{t.items.map(renderToken)}</ul>;
|
return <ul key={key}>{t.items.map(renderToken)}</ul>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "list_item": {
|
case "list_item": {
|
||||||
return <li>{t.tokens ? t.tokens.map(renderToken) : t.raw}</li>;
|
return <li key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</li>;
|
||||||
}
|
}
|
||||||
case "em": {
|
case "em": {
|
||||||
return <em>{t.tokens ? t.tokens.map(renderToken) : t.raw}</em>;
|
return <em key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</em>;
|
||||||
}
|
}
|
||||||
case "del": {
|
case "del": {
|
||||||
return <s>{t.tokens ? t.tokens.map(renderToken) : t.raw}</s>;
|
return <s key={key}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</s>;
|
||||||
}
|
}
|
||||||
case "table": {
|
case "table": {
|
||||||
return (
|
return (
|
||||||
<table className="table-auto border-collapse">
|
<table className="table-auto border-collapse" key={key}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{(t.header as Tokens.TableCell[]).map(v => (
|
{(t.header as Tokens.TableCell[]).map((v, h_key) => (
|
||||||
<th className="border">{v.tokens ? v.tokens.map(renderToken) : v.text}</th>
|
<th className="border" key={h_key}>{v.tokens ? v.tokens.map(renderToken) : v.text}</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{(t.rows as Tokens.TableCell[][]).map(v => (
|
{(t.rows as Tokens.TableCell[][]).map((v, r_key) => (
|
||||||
<tr>
|
<tr key={r_key}>
|
||||||
{v.map(d => (
|
{v.map((d, d_key) => (
|
||||||
<td className="border px-2 py-1">{d.tokens ? d.tokens.map(renderToken) : d.text}</td>
|
<td className="border px-2 py-1" key={d_key}>{d.tokens ? d.tokens.map(renderToken) : d.text}</td>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
@ -103,7 +103,7 @@ const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: MarkdownProps
|
|||||||
if (props.plainText ?? false) {
|
if (props.plainText ?? false) {
|
||||||
return t.raw;
|
return t.raw;
|
||||||
}
|
}
|
||||||
return <Text content={t.raw} tags={[]} />;
|
return <Text content={t.raw} tags={[]} key={key} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import "./send-zap.css";
|
import "./send-zap.css";
|
||||||
import { type ReactNode, useEffect, useState } from "react";
|
import { Fragment, type ReactNode, useEffect, useState } from "react";
|
||||||
import { LNURL } from "@snort/shared";
|
import { LNURL } from "@snort/shared";
|
||||||
import { EventPublisher, NostrEvent, TaggedNostrEvent } from "@snort/system";
|
import { EventPublisher, NostrEvent, TaggedNostrEvent } from "@snort/system";
|
||||||
import { secp256k1 } from "@noble/curves/secp256k1";
|
import { secp256k1 } from "@noble/curves/secp256k1";
|
||||||
@ -37,6 +37,7 @@ export interface SendZapsProps {
|
|||||||
onFinish: () => void;
|
onFinish: () => void;
|
||||||
onTargetReady?: () => void;
|
onTargetReady?: () => void;
|
||||||
button?: ReactNode;
|
button?: ReactNode;
|
||||||
|
key?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish, onTargetReady }: SendZapsProps) {
|
export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish, onTargetReady }: SendZapsProps) {
|
||||||
@ -215,7 +216,7 @@ export function SendZapsDialog(props: Omit<SendZapsProps, "onFinish">) {
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [ready, setReady] = useState(false);
|
const [ready, setReady] = useState(false);
|
||||||
return (
|
return (
|
||||||
<>
|
<Fragment key={props.key}>
|
||||||
{props.button ? (
|
{props.button ? (
|
||||||
<div onClick={() => setOpen(true)}>{props.button}</div>
|
<div onClick={() => setOpen(true)}>{props.button}</div>
|
||||||
) : (
|
) : (
|
||||||
@ -229,7 +230,7 @@ export function SendZapsDialog(props: Omit<SendZapsProps, "onFinish">) {
|
|||||||
<SendZaps {...props} onFinish={() => setOpen(false)} onTargetReady={() => setReady(true)} />
|
<SendZaps {...props} onFinish={() => setOpen(false)} onTargetReady={() => setReady(true)} />
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
</>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { NostrLink, NostrPrefix, ParsedFragment, transformText, tryParseNostrLink } from "@snort/system";
|
import { NostrLink, NostrPrefix, ParsedFragment, transformText, tryParseNostrLink } from "@snort/system";
|
||||||
import { FunctionComponent, useMemo } from "react";
|
import { Fragment, FunctionComponent, useMemo } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
import { Emoji } from "./emoji";
|
import { Emoji } from "./emoji";
|
||||||
@ -22,10 +22,10 @@ export function Text({ content, tags, eventComponent, className }: TextProps) {
|
|||||||
return transformText(content, tags);
|
return transformText(content, tags);
|
||||||
}, [content, tags]);
|
}, [content, tags]);
|
||||||
|
|
||||||
function renderFrag(f: ParsedFragment) {
|
function renderFrag(f: ParsedFragment, key: number) {
|
||||||
switch (f.type) {
|
switch (f.type) {
|
||||||
case "custom_emoji":
|
case "custom_emoji":
|
||||||
return <Emoji name={f.content} url={f.content} />;
|
return <Emoji name={f.content} url={f.content} key={key} />;
|
||||||
case "media":
|
case "media":
|
||||||
case "link": {
|
case "link": {
|
||||||
if (f.content.startsWith("nostr:")) {
|
if (f.content.startsWith("nostr:")) {
|
||||||
@ -36,24 +36,25 @@ export function Text({ content, tags, eventComponent, className }: TextProps) {
|
|||||||
link.type === NostrPrefix.Address ||
|
link.type === NostrPrefix.Address ||
|
||||||
link.type === NostrPrefix.Note
|
link.type === NostrPrefix.Note
|
||||||
) {
|
) {
|
||||||
return eventComponent?.({ link }) ?? <EventEmbed link={link} />;
|
return <Fragment key={key}>
|
||||||
|
{eventComponent?.({ link })} </Fragment> ?? <EventEmbed link={link} key={key} />;
|
||||||
} else {
|
} else {
|
||||||
return <Mention pubkey={link.id} />;
|
return <Mention pubkey={link.id} key={key} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return <HyperText link={f.content}>{f.content}</HyperText>;
|
return <HyperText link={f.content} key={key}>{f.content}</HyperText>;
|
||||||
}
|
}
|
||||||
case "mention":
|
case "mention":
|
||||||
return <Mention pubkey={f.content} />;
|
return <Mention pubkey={f.content} key={key} />;
|
||||||
case "hashtag":
|
case "hashtag":
|
||||||
return <Link to={`/t/${f.content}`}>#{f.content}</Link>;
|
return <Link to={`/t/${f.content}`} key={key}>#{f.content}</Link>;
|
||||||
default: {
|
default: {
|
||||||
if (f.content.startsWith("lnurlp:")) {
|
if (f.content.startsWith("lnurlp:")) {
|
||||||
// LUD-17: https://github.com/lnurl/luds/blob/luds/17.md
|
// LUD-17: https://github.com/lnurl/luds/blob/luds/17.md
|
||||||
const url = new URL(f.content);
|
const url = new URL(f.content);
|
||||||
url.protocol = "https:";
|
url.protocol = "https:";
|
||||||
return <SendZapsDialog pubkey={undefined} lnurl={url.toString()} button={<Link to={""}>{f.content}</Link>} />;
|
return <SendZapsDialog pubkey={undefined} lnurl={url.toString()} button={<Link to={""}>{f.content}</Link>} key={key} />;
|
||||||
}
|
}
|
||||||
return f.content;
|
return f.content;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user