fix highlighted text in searched notes
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Martti Malmi 2024-02-29 11:14:17 +02:00
parent 04e7d0b54f
commit 95dc979b8d
14 changed files with 104 additions and 108 deletions

View File

@ -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;

View File

@ -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";

View File

@ -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();

View File

@ -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";

View File

@ -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}

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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}
/>
</>
);

View File

@ -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}
/>
),
)}

View File

@ -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>
));

View File

@ -1,5 +0,0 @@
const HighlightedText = ({ content }: { content: string }) => {
return <strong className="highlighted-text">{content}</strong>;
};
export default HighlightedText;

View 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;

View File

@ -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>,
);
}

View 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;