forked from Kieran/snort
feat: highlight search results
This commit is contained in:
parent
3bbad28c32
commit
c223c89045
9
packages/app/src/Element/HighlightedText.tsx
Normal file
9
packages/app/src/Element/HighlightedText.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
const HighlightedText = ({ content }: { content: string }) => {
|
||||
return (
|
||||
<strong className="highlighted-text">
|
||||
{content}
|
||||
</strong>
|
||||
);
|
||||
};
|
||||
|
||||
export default HighlightedText;
|
@ -47,6 +47,7 @@ export interface NoteProps {
|
||||
ignoreModeration?: boolean;
|
||||
onClick?: (e: TaggedNostrEvent) => void;
|
||||
depth?: number;
|
||||
searchedValue?: string;
|
||||
options?: {
|
||||
showHeader?: boolean;
|
||||
showContextMenu?: boolean;
|
||||
@ -209,12 +210,13 @@ export default function Note(props: NoteProps) {
|
||||
)}
|
||||
</>
|
||||
}>
|
||||
<Text content={body} tags={ev.tags} creator={ev.pubkey} />
|
||||
<Text highlighText={props.searchedValue} content={body} tags={ev.tags} creator={ev.pubkey} />
|
||||
</Reveal>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Text
|
||||
highlighText={props.searchedValue}
|
||||
content={body}
|
||||
tags={ev.tags}
|
||||
creator={ev.pubkey}
|
||||
|
@ -9,6 +9,7 @@ import CashuNuts from "Element/CashuNuts";
|
||||
import RevealMedia from "./RevealMedia";
|
||||
import { ProxyImg } from "./ProxyImg";
|
||||
import { SpotlightMedia } from "./SpotlightMedia";
|
||||
import HighlightedText from "./HighlightedText";
|
||||
|
||||
export interface TextProps {
|
||||
content: string;
|
||||
@ -17,9 +18,10 @@ export interface TextProps {
|
||||
disableMedia?: boolean;
|
||||
disableMediaSpotlight?: boolean;
|
||||
depth?: number;
|
||||
highlighText?: string;
|
||||
}
|
||||
|
||||
export default function Text({ content, tags, creator, disableMedia, depth, disableMediaSpotlight }: TextProps) {
|
||||
export default function Text({ content, tags, creator, disableMedia, depth, disableMediaSpotlight, highlighText }: TextProps) {
|
||||
const [showSpotlight, setShowSpotlight] = useState(false);
|
||||
const [imageIdx, setImageIdx] = useState(0);
|
||||
|
||||
@ -29,6 +31,35 @@ export default function Text({ content, tags, creator, disableMedia, depth, disa
|
||||
|
||||
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} />;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function renderChunk(a: ParsedFragment) {
|
||||
if (a.type === "media" && !a.mimeType?.startsWith("unknown")) {
|
||||
if (disableMedia ?? false) {
|
||||
@ -67,7 +98,9 @@ export default function Text({ content, tags, creator, disableMedia, depth, disa
|
||||
case "custom_emoji":
|
||||
return <ProxyImg src={a.content} size={15} className="custom-emoji" />;
|
||||
default:
|
||||
return <div className="text-frag">{a.content}</div>;
|
||||
return <div className="text-frag">
|
||||
{highlighText ? renderContentWithHighlightedText(a.content, highlighText) : a.content}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ const Timeline = (props: TimelineProps) => {
|
||||
return <NoteReaction data={e} key={e.id} root={findRelated(eRef)} />;
|
||||
}
|
||||
return (
|
||||
<Note key={e.id} data={e} related={relatedFeed(e.id)} ignoreModeration={props.ignoreModeration} depth={0} />
|
||||
<Note key={e.id} searchedValue={props.subject.discriminator} data={e} related={relatedFeed(e.id)} ignoreModeration={props.ignoreModeration} depth={0} />
|
||||
);
|
||||
}
|
||||
case EventKind.ZapReceipt: {
|
||||
|
@ -22,6 +22,7 @@ const Contributors = [
|
||||
bech32ToHex("npub179rec9sw2a5ngkr2wsjpjhwp2ksygjxn6uw5py9daj2ezhw3aw5swv3s6q"), // h3y6e - JA + other stuff
|
||||
bech32ToHex("npub17q5n2z8naw0xl6vu9lvt560lg33pdpe29k0k09umlfxm3vc4tqrq466f2y"), // w3irdrobot
|
||||
bech32ToHex("npub1ltx67888tz7lqnxlrg06x234vjnq349tcfyp52r0lstclp548mcqnuz40t"), // Vivek
|
||||
bech32ToHex("npub1wh30wunfpkezx5s7edqu9g0s0raeetf5dgthzm0zw7sk8wqygmjqqfljgh"), // Fernando Porazzi
|
||||
];
|
||||
|
||||
const Translators = [
|
||||
|
@ -5,7 +5,7 @@ import { validateNostrLink } from "./nostr-link";
|
||||
import { splitByUrl } from "./utils";
|
||||
|
||||
export interface ParsedFragment {
|
||||
type: "text" | "link" | "mention" | "invoice" | "media" | "cashu" | "hashtag" | "custom_emoji";
|
||||
type: "text" | "link" | "mention" | "invoice" | "media" | "cashu" | "hashtag" | "custom_emoji" | "highlighted_text";
|
||||
content: string;
|
||||
mimeType?: string;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user