Merge pull request 'feat: highlight search results' (#625) from fernandoporazzi/snort:highlight-searched-word into main
Reviewed-on: #625
This commit is contained in:
commit
1d1e8889dc
@ -49,6 +49,7 @@ export interface NoteProps {
|
|||||||
ignoreModeration?: boolean;
|
ignoreModeration?: boolean;
|
||||||
onClick?: (e: TaggedNostrEvent) => void;
|
onClick?: (e: TaggedNostrEvent) => void;
|
||||||
depth?: number;
|
depth?: number;
|
||||||
|
searchedValue?: string;
|
||||||
options?: {
|
options?: {
|
||||||
showHeader?: boolean;
|
showHeader?: boolean;
|
||||||
showContextMenu?: boolean;
|
showContextMenu?: boolean;
|
||||||
@ -206,6 +207,7 @@ export function NoteInner(props: NoteProps) {
|
|||||||
<Text
|
<Text
|
||||||
id={ev.id}
|
id={ev.id}
|
||||||
content={ev.content}
|
content={ev.content}
|
||||||
|
highlighText={props.searchedValue}
|
||||||
tags={ev.tags}
|
tags={ev.tags}
|
||||||
creator={ev.pubkey}
|
creator={ev.pubkey}
|
||||||
depth={props.depth}
|
depth={props.depth}
|
||||||
@ -222,6 +224,7 @@ export function NoteInner(props: NoteProps) {
|
|||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
id={ev.id}
|
id={ev.id}
|
||||||
|
highlighText={props.searchedValue}
|
||||||
content={body}
|
content={body}
|
||||||
tags={ev.tags}
|
tags={ev.tags}
|
||||||
creator={ev.pubkey}
|
creator={ev.pubkey}
|
||||||
|
@ -107,7 +107,7 @@ const Timeline = (props: TimelineProps) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{mainFeed.map(e => (
|
{mainFeed.map(e => (
|
||||||
<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} />
|
||||||
))}
|
))}
|
||||||
{(props.loadMore === undefined || props.loadMore === true) && (
|
{(props.loadMore === undefined || props.loadMore === true) && (
|
||||||
<div className="flex f-center">
|
<div className="flex f-center">
|
||||||
|
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;
|
@ -9,6 +9,7 @@ import CashuNuts from "Element/Embed/CashuNuts";
|
|||||||
import RevealMedia from "./Event/RevealMedia";
|
import RevealMedia from "./Event/RevealMedia";
|
||||||
import { ProxyImg } from "./ProxyImg";
|
import { ProxyImg } from "./ProxyImg";
|
||||||
import { SpotlightMediaModal } from "./Deck/SpotlightMedia";
|
import { SpotlightMediaModal } from "./Deck/SpotlightMedia";
|
||||||
|
import HighlightedText from "./HighlightedText";
|
||||||
import { useTextTransformer } from "Hooks/useTextTransformCache";
|
import { useTextTransformer } from "Hooks/useTextTransformCache";
|
||||||
|
|
||||||
export interface TextProps {
|
export interface TextProps {
|
||||||
@ -22,6 +23,7 @@ export interface TextProps {
|
|||||||
depth?: number;
|
depth?: number;
|
||||||
truncate?: number;
|
truncate?: number;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
highlighText?: string;
|
||||||
onClick?: (e: React.MouseEvent) => void;
|
onClick?: (e: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +38,7 @@ export default function Text({
|
|||||||
disableLinkPreview,
|
disableLinkPreview,
|
||||||
truncate,
|
truncate,
|
||||||
className,
|
className,
|
||||||
|
highlighText,
|
||||||
onClick,
|
onClick,
|
||||||
}: TextProps) {
|
}: TextProps) {
|
||||||
const [showSpotlight, setShowSpotlight] = useState(false);
|
const [showSpotlight, setShowSpotlight] = useState(false);
|
||||||
@ -45,6 +48,35 @@ export default function Text({
|
|||||||
|
|
||||||
const images = elements.filter(a => a.type === "media" && a.mimeType?.startsWith("image")).map(a => a.content);
|
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 = () => {
|
const renderContent = () => {
|
||||||
let lenCtr = 0;
|
let lenCtr = 0;
|
||||||
function renderChunk(a: ParsedFragment) {
|
function renderChunk(a: ParsedFragment) {
|
||||||
@ -96,7 +128,9 @@ export default function Text({
|
|||||||
case "custom_emoji":
|
case "custom_emoji":
|
||||||
return <ProxyImg src={a.content} size={15} className="custom-emoji" />;
|
return <ProxyImg src={a.content} size={15} className="custom-emoji" />;
|
||||||
default:
|
default:
|
||||||
return <div className="text-frag">{a.content}</div>;
|
return <div className="text-frag">
|
||||||
|
{highlighText ? renderContentWithHighlightedText(a.content, highlighText) : a.content}
|
||||||
|
</div>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ const Contributors = [
|
|||||||
bech32ToHex("npub179rec9sw2a5ngkr2wsjpjhwp2ksygjxn6uw5py9daj2ezhw3aw5swv3s6q"), // h3y6e - JA + other stuff
|
bech32ToHex("npub179rec9sw2a5ngkr2wsjpjhwp2ksygjxn6uw5py9daj2ezhw3aw5swv3s6q"), // h3y6e - JA + other stuff
|
||||||
bech32ToHex("npub17q5n2z8naw0xl6vu9lvt560lg33pdpe29k0k09umlfxm3vc4tqrq466f2y"), // w3irdrobot
|
bech32ToHex("npub17q5n2z8naw0xl6vu9lvt560lg33pdpe29k0k09umlfxm3vc4tqrq466f2y"), // w3irdrobot
|
||||||
bech32ToHex("npub1ltx67888tz7lqnxlrg06x234vjnq349tcfyp52r0lstclp548mcqnuz40t"), // Vivek
|
bech32ToHex("npub1ltx67888tz7lqnxlrg06x234vjnq349tcfyp52r0lstclp548mcqnuz40t"), // Vivek
|
||||||
|
bech32ToHex("npub1wh30wunfpkezx5s7edqu9g0s0raeetf5dgthzm0zw7sk8wqygmjqqfljgh"), // Fernando Porazzi
|
||||||
];
|
];
|
||||||
|
|
||||||
const Translators = [
|
const Translators = [
|
||||||
|
@ -5,7 +5,7 @@ import { validateNostrLink } from "./nostr-link";
|
|||||||
import { splitByUrl } from "./utils";
|
import { splitByUrl } from "./utils";
|
||||||
|
|
||||||
export interface ParsedFragment {
|
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;
|
content: string;
|
||||||
mimeType?: string;
|
mimeType?: string;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user