fix highlighted text in searched notes
This commit is contained in:
parent
04e7d0b54f
commit
95dc979b8d
@ -41,7 +41,7 @@ export interface NoteProps {
|
||||
ignoreModeration?: boolean;
|
||||
onClick?: (e: TaggedNostrEvent) => void;
|
||||
depth?: number;
|
||||
searchedValue?: string;
|
||||
highlightText?: string;
|
||||
threadChains?: Map<string, Array<NostrEvent>>;
|
||||
context?: ReactNode;
|
||||
options?: NotePropsOptions;
|
||||
|
@ -11,7 +11,7 @@ import NoteHeader from "@/Components/Event/Note/NoteHeader";
|
||||
import NoteQuote from "@/Components/Event/Note/NoteQuote";
|
||||
import { NoteText } from "@/Components/Event/Note/NoteText";
|
||||
import { TranslationInfo } from "@/Components/Event/Note/TranslationInfo";
|
||||
import {NoteTranslation} from "@/Components/Event/Note/types";
|
||||
import { NoteTranslation } from "@/Components/Event/Note/types";
|
||||
import Username from "@/Components/User/Username";
|
||||
import useModeration from "@/Hooks/useModeration";
|
||||
import { findTag } from "@/Utils";
|
||||
|
@ -1,19 +1,19 @@
|
||||
import {HexKey, NostrLink, NostrPrefix} from "@snort/system";
|
||||
import {Menu, MenuItem} from "@szhsin/react-menu";
|
||||
import {useEffect, useState} from "react";
|
||||
import {FormattedMessage, useIntl} from "react-intl";
|
||||
import { HexKey, NostrLink, NostrPrefix } from "@snort/system";
|
||||
import { Menu, MenuItem } from "@szhsin/react-menu";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import {NoteContextMenuProps, NoteTranslation} from "@/Components/Event/Note/types";
|
||||
import { NoteContextMenuProps, NoteTranslation } from "@/Components/Event/Note/types";
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
import messages from "@/Components/messages";
|
||||
import SnortApi from "@/External/SnortApi";
|
||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
import useModeration from "@/Hooks/useModeration";
|
||||
import {setBookmarked, setPinned} from "@/Utils/Login";
|
||||
import {getCurrentSubscription, SubscriptionType} from "@/Utils/Subscription";
|
||||
import { setBookmarked, setPinned } from "@/Utils/Login";
|
||||
import { getCurrentSubscription, SubscriptionType } from "@/Utils/Subscription";
|
||||
|
||||
import {ReBroadcaster} from "../../ReBroadcaster";
|
||||
import { ReBroadcaster } from "../../ReBroadcaster";
|
||||
|
||||
export function NoteContextMenu({ ev, ...props }: NoteContextMenuProps) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
@ -7,7 +7,7 @@ import { NoteContextMenu } from "@/Components/Event/Note/NoteContextMenu";
|
||||
import NoteTime from "@/Components/Event/Note/NoteTime";
|
||||
import ReactionsModal from "@/Components/Event/Note/ReactionsModal";
|
||||
import ReplyTag from "@/Components/Event/Note/ReplyTag";
|
||||
import {NoteTranslation} from "@/Components/Event/Note/types";
|
||||
import { NoteTranslation } from "@/Components/Event/Note/types";
|
||||
import Icon from "@/Components/Icons/Icon";
|
||||
import messages from "@/Components/messages";
|
||||
import ProfileImage from "@/Components/User/ProfileImage";
|
||||
|
@ -3,7 +3,7 @@ import { FormattedMessage } from "react-intl";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { NoteProps } from "@/Components/Event/EventComponent";
|
||||
import {NoteTranslation} from "@/Components/Event/Note/types";
|
||||
import { NoteTranslation } from "@/Components/Event/Note/types";
|
||||
import Reveal from "@/Components/Event/Reveal";
|
||||
import Text from "@/Components/Text/Text";
|
||||
import useLogin from "@/Hooks/useLogin";
|
||||
@ -40,7 +40,7 @@ export const NoteText = memo(function InnerContent(
|
||||
{shouldTruncate && showMore && <ToggleShowMore />}
|
||||
<Text
|
||||
id={id}
|
||||
highlighText={props.searchedValue}
|
||||
highlightText={props.highlightText}
|
||||
content={body}
|
||||
tags={ev.tags}
|
||||
creator={ev.pubkey}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import {NoteTranslation} from "@/Components/Event/Note/types";
|
||||
import { NoteTranslation } from "@/Components/Event/Note/types";
|
||||
import messages from "@/Components/messages";
|
||||
|
||||
interface TranslationInfoProps {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {TaggedNostrEvent} from "@snort/system";
|
||||
import { TaggedNostrEvent } from "@snort/system";
|
||||
|
||||
export interface NoteTranslation {
|
||||
text: string;
|
||||
@ -15,4 +15,4 @@ export interface NoteContextMenuProps {
|
||||
react(content: string): Promise<void>;
|
||||
|
||||
onTranslated?: (t: NoteTranslation) => void;
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ const Timeline = (props: TimelineProps) => {
|
||||
showLatest={t => onShowLatest(t)}
|
||||
displayAs={displayAs}
|
||||
loadMore={() => feed.loadMore()}
|
||||
highlightText={props.subject.type === "post_keyword" ? props.subject.items[0] : undefined}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -14,6 +14,7 @@ export interface TimelineFragProps {
|
||||
noteRenderer?: (ev: TaggedNostrEvent) => ReactNode;
|
||||
noteOnClick?: (ev: TaggedNostrEvent) => void;
|
||||
noteContext?: (ev: TaggedNostrEvent) => ReactNode;
|
||||
highlightText?: string;
|
||||
}
|
||||
|
||||
const options = {
|
||||
@ -35,6 +36,7 @@ export function TimelineFragment(props: TimelineFragProps) {
|
||||
context={props.noteContext?.(e)}
|
||||
options={options}
|
||||
waitUntilInView={index > 5}
|
||||
highlightText={props.highlightText}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
|
@ -25,6 +25,7 @@ export interface TimelineRendererProps {
|
||||
noteContext?: (ev: TaggedNostrEvent) => ReactNode;
|
||||
displayAs?: DisplayAs;
|
||||
loadMore?: () => void;
|
||||
highlightText?: string;
|
||||
}
|
||||
|
||||
// filter frags[0].events that have media
|
||||
@ -105,6 +106,7 @@ export function TimelineRenderer(props: TimelineRendererProps) {
|
||||
noteRenderer={props.noteRenderer}
|
||||
noteOnClick={props.noteOnClick}
|
||||
noteContext={props.noteContext}
|
||||
highlightText={props.highlightText}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
));
|
||||
|
@ -1,5 +0,0 @@
|
||||
const HighlightedText = ({ content }: { content: string }) => {
|
||||
return <strong className="highlighted-text">{content}</strong>;
|
||||
};
|
||||
|
||||
export default HighlightedText;
|
23
packages/app/src/Components/Text/HighlightedText.tsx
Normal file
23
packages/app/src/Components/Text/HighlightedText.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
|
||||
const HighlightedText = ({ content, textToHighlight }: { 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((part, index) => {
|
||||
if (textToHighlightArray.includes(part.toLowerCase())) {
|
||||
return (
|
||||
<strong key={index} className="highlighted-text">
|
||||
{part}
|
||||
</strong>
|
||||
);
|
||||
} else {
|
||||
return part;
|
||||
}
|
||||
});
|
||||
|
||||
return <>{fragments}</>;
|
||||
};
|
||||
|
||||
export default HighlightedText;
|
@ -8,12 +8,13 @@ import CashuNuts from "@/Components/Embed/CashuNuts";
|
||||
import Hashtag from "@/Components/Embed/Hashtag";
|
||||
import HyperText from "@/Components/Embed/HyperText";
|
||||
import Invoice from "@/Components/Embed/Invoice";
|
||||
import { baseImageWidth, GRID_GAP, gridConfigMap, ROW_HEIGHT } from "@/Components/Text/const";
|
||||
import { useTextTransformer } from "@/Hooks/useTextTransformCache";
|
||||
|
||||
import RevealMedia from "../Event/RevealMedia";
|
||||
import HighlightedText from "../HighlightedText";
|
||||
import { ProxyImg } from "../ProxyImg";
|
||||
import { SpotlightMediaModal } from "../Spotlight/SpotlightMedia";
|
||||
import HighlightedText from "./HighlightedText";
|
||||
|
||||
export interface TextProps {
|
||||
id: string;
|
||||
@ -26,64 +27,10 @@ export interface TextProps {
|
||||
depth?: number;
|
||||
truncate?: number;
|
||||
className?: string;
|
||||
highlighText?: string;
|
||||
highlightText?: string;
|
||||
onClick?: (e: React.MouseEvent) => void;
|
||||
}
|
||||
|
||||
const baseImageWidth = 910;
|
||||
|
||||
const gridConfigMap = new Map<number, number[][]>([
|
||||
[1, [[4, 3]]],
|
||||
[
|
||||
2,
|
||||
[
|
||||
[2, 2],
|
||||
[2, 2],
|
||||
],
|
||||
],
|
||||
[
|
||||
3,
|
||||
[
|
||||
[2, 2],
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
],
|
||||
],
|
||||
[
|
||||
4,
|
||||
[
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
],
|
||||
],
|
||||
[
|
||||
5,
|
||||
[
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
[1, 1],
|
||||
[1, 1],
|
||||
],
|
||||
],
|
||||
[
|
||||
6,
|
||||
[
|
||||
[2, 2],
|
||||
[1, 1],
|
||||
[1, 1],
|
||||
[2, 2],
|
||||
[1, 1],
|
||||
[1, 1],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
const ROW_HEIGHT = 140;
|
||||
const GRID_GAP = 2;
|
||||
|
||||
export default function Text({
|
||||
id,
|
||||
content,
|
||||
@ -95,7 +42,7 @@ export default function Text({
|
||||
disableLinkPreview,
|
||||
truncate,
|
||||
className,
|
||||
highlighText,
|
||||
highlightText,
|
||||
onClick,
|
||||
}: TextProps) {
|
||||
const [showSpotlight, setShowSpotlight] = useState(false);
|
||||
@ -104,35 +51,6 @@ export default function Text({
|
||||
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, index) => {
|
||||
if (typeof f === "string") {
|
||||
return f;
|
||||
}
|
||||
|
||||
return <HighlightedText key={index} content={f.content} />;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const DisableMedia = ({ content }: { content: string }) => (
|
||||
<a href={content} onClick={e => e.stopPropagation()} target="_blank" rel="noreferrer" className="ext">
|
||||
{content}
|
||||
@ -284,7 +202,11 @@ export default function Text({
|
||||
if (element.type === "text") {
|
||||
chunks.push(
|
||||
<div className="text-frag">
|
||||
{highlighText ? renderContentWithHighlightedText(element.content, highlighText) : element.content}
|
||||
{highlightText ? (
|
||||
<HighlightedText content={element.content} textToHighlight={highlightText} />
|
||||
) : (
|
||||
element.content
|
||||
)}
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
|
51
packages/app/src/Components/Text/const.ts
Normal file
51
packages/app/src/Components/Text/const.ts
Normal file
@ -0,0 +1,51 @@
|
||||
export const baseImageWidth = 910;
|
||||
export const gridConfigMap = new Map<number, number[][]>([
|
||||
[1, [[4, 3]]],
|
||||
[
|
||||
2,
|
||||
[
|
||||
[2, 2],
|
||||
[2, 2],
|
||||
],
|
||||
],
|
||||
[
|
||||
3,
|
||||
[
|
||||
[2, 2],
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
],
|
||||
],
|
||||
[
|
||||
4,
|
||||
[
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
],
|
||||
],
|
||||
[
|
||||
5,
|
||||
[
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
[2, 1],
|
||||
[1, 1],
|
||||
[1, 1],
|
||||
],
|
||||
],
|
||||
[
|
||||
6,
|
||||
[
|
||||
[2, 2],
|
||||
[1, 1],
|
||||
[1, 1],
|
||||
[2, 2],
|
||||
[1, 1],
|
||||
[1, 1],
|
||||
],
|
||||
],
|
||||
]);
|
||||
export const ROW_HEIGHT = 140;
|
||||
export const GRID_GAP = 2;
|
Loading…
x
Reference in New Issue
Block a user