150 lines
4.4 KiB
TypeScript
150 lines
4.4 KiB
TypeScript
import "./Text.css";
|
|
import { useState } from "react";
|
|
import { HexKey, ParsedFragment } from "@snort/system";
|
|
|
|
import Invoice from "Element/Embed/Invoice";
|
|
import Hashtag from "Element/Embed/Hashtag";
|
|
import HyperText from "Element/HyperText";
|
|
import CashuNuts from "Element/Embed/CashuNuts";
|
|
import RevealMedia from "./Event/RevealMedia";
|
|
import { ProxyImg } from "./ProxyImg";
|
|
import { SpotlightMediaModal } from "./Deck/SpotlightMedia";
|
|
import HighlightedText from "./HighlightedText";
|
|
import { useTextTransformer } from "Hooks/useTextTransformCache";
|
|
|
|
export interface TextProps {
|
|
id: string;
|
|
content: string;
|
|
creator: HexKey;
|
|
tags: Array<Array<string>>;
|
|
disableMedia?: boolean;
|
|
disableMediaSpotlight?: boolean;
|
|
disableLinkPreview?: boolean;
|
|
depth?: number;
|
|
truncate?: number;
|
|
className?: string;
|
|
highlighText?: string;
|
|
onClick?: (e: React.MouseEvent) => void;
|
|
}
|
|
|
|
export default function Text({
|
|
id,
|
|
content,
|
|
tags,
|
|
creator,
|
|
disableMedia,
|
|
depth,
|
|
disableMediaSpotlight,
|
|
disableLinkPreview,
|
|
truncate,
|
|
className,
|
|
highlighText,
|
|
onClick,
|
|
}: TextProps) {
|
|
const [showSpotlight, setShowSpotlight] = useState(false);
|
|
const [imageIdx, setImageIdx] = useState(0);
|
|
|
|
const elements = useTextTransformer(id, content, tags);
|
|
|
|
const images = elements.filter(a => a.type === "media" && a.mimeType?.startsWith("image")).map(a => a.content);
|
|
|
|
function renderContentWithHighlightedText(content: string, textToHighlight: string) {
|
|
const textToHighlightArray = textToHighlight.trim().toLowerCase().split(" ");
|
|
const re = new RegExp(`(${textToHighlightArray.join("|")})`, "gi");
|
|
const splittedContent = content.split(re);
|
|
|
|
const fragments = splittedContent.map(c => {
|
|
if (textToHighlightArray.includes(c.toLowerCase())) {
|
|
return {
|
|
type: "highlighted_text",
|
|
content: c,
|
|
} as ParsedFragment;
|
|
}
|
|
|
|
return c;
|
|
});
|
|
|
|
return (
|
|
<>
|
|
{fragments.map(f => {
|
|
if (typeof f === "string") {
|
|
return f;
|
|
}
|
|
|
|
return <HighlightedText content={f.content} />;
|
|
})}
|
|
</>
|
|
);
|
|
}
|
|
|
|
const renderContent = () => {
|
|
let lenCtr = 0;
|
|
function renderChunk(a: ParsedFragment) {
|
|
if (truncate) {
|
|
if (lenCtr > truncate) {
|
|
return null;
|
|
} else if (lenCtr + a.content.length > truncate) {
|
|
lenCtr += a.content.length;
|
|
return <div className="text-frag">{a.content.slice(0, truncate - lenCtr)}...</div>;
|
|
} else {
|
|
lenCtr += a.content.length;
|
|
}
|
|
}
|
|
|
|
if (a.type === "media" && !a.mimeType?.startsWith("unknown")) {
|
|
if (disableMedia ?? false) {
|
|
return (
|
|
<a href={a.content} onClick={e => e.stopPropagation()} target="_blank" rel="noreferrer" className="ext">
|
|
{a.content}
|
|
</a>
|
|
);
|
|
}
|
|
return (
|
|
<RevealMedia
|
|
link={a.content}
|
|
creator={creator}
|
|
onMediaClick={e => {
|
|
if (!disableMediaSpotlight) {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
setShowSpotlight(true);
|
|
const selected = images.findIndex(b => b === a.content);
|
|
setImageIdx(selected === -1 ? 0 : selected);
|
|
}
|
|
}}
|
|
/>
|
|
);
|
|
} else {
|
|
switch (a.type) {
|
|
case "invoice":
|
|
return <Invoice invoice={a.content} />;
|
|
case "hashtag":
|
|
return <Hashtag tag={a.content} />;
|
|
case "cashu":
|
|
return <CashuNuts token={a.content} />;
|
|
case "media":
|
|
case "link":
|
|
return <HyperText link={a.content} depth={depth} showLinkPreview={!(disableLinkPreview ?? false)} />;
|
|
case "custom_emoji":
|
|
return <ProxyImg src={a.content} size={15} className="custom-emoji" />;
|
|
default:
|
|
return (
|
|
<div className="text-frag">
|
|
{highlighText ? renderContentWithHighlightedText(a.content, highlighText) : a.content}
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return elements.map(a => renderChunk(a));
|
|
};
|
|
|
|
return (
|
|
<div dir="auto" className={`text${className ? ` ${className}` : ""}`} onClick={onClick}>
|
|
{renderContent()}
|
|
{showSpotlight && <SpotlightMediaModal images={images} onClose={() => setShowSpotlight(false)} idx={imageIdx} />}
|
|
</div>
|
|
);
|
|
}
|