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