add search support

This commit is contained in:
Athou
2022-10-27 16:25:32 +02:00
parent 9252042c99
commit d187c23a77
10 changed files with 144 additions and 34 deletions

View File

@@ -1,8 +1,9 @@
import { Box, createStyles, TypographyStylesProvider } from "@mantine/core"
import { Box, createStyles, Mark, TypographyStylesProvider } from "@mantine/core"
import { Constants } from "app/constants"
import { useAppSelector } from "app/store"
import { calculatePlaceholderSize } from "app/utils"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import { Interweave, TransformCallback } from "interweave"
import { ChildrenNode, Interweave, Matcher, MatchResponse, Node, TransformCallback } from "interweave"
export interface ContentProps {
content: string
@@ -54,13 +55,39 @@ const transform: TransformCallback = node => {
return undefined
}
class HighlightMatcher extends Matcher {
private search: string
constructor(search: string) {
super("highlight")
this.search = search
}
match(string: string): MatchResponse<unknown> | null {
const pattern = this.search.split(" ").join("|")
return this.doMatch(string, new RegExp(pattern, "i"), () => ({}))
}
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars
replaceWith(children: ChildrenNode, props: unknown): Node {
return <Mark>{children}</Mark>
}
// eslint-disable-next-line class-methods-use-this
asTag(): string {
return "span"
}
}
export function Content(props: ContentProps) {
const { classes } = useStyles()
const search = useAppSelector(state => state.entries.search)
const matchers = search ? [new HighlightMatcher(search)] : []
return (
<TypographyStylesProvider>
<Box className={classes.content}>
<Interweave content={props.content} transform={transform} />
<Interweave content={props.content} transform={transform} matchers={matchers} />
</Box>
</TypographyStylesProvider>
)

View File

@@ -2,6 +2,7 @@ import { Box, createStyles, Image, Text } from "@mantine/core"
import { Entry } from "app/types"
import { RelativeDate } from "components/RelativeDate"
import { OnDesktop } from "components/responsive/OnDesktop"
import { FeedEntryTitle } from "./FeedEntryTitle"
export interface FeedEntryHeaderProps {
entry: Entry
@@ -43,7 +44,9 @@ export function FeedEntryCompactHeader(props: FeedEntryHeaderProps) {
{props.entry.feedName}
</Text>
</OnDesktop>
<Box className={classes.title}>{props.entry.title}</Box>
<Box className={classes.title}>
<FeedEntryTitle entry={props.entry} />
</Box>
<OnDesktop>
<Text color="dimmed" className={classes.date}>
<RelativeDate date={props.entry.date} />

View File

@@ -1,6 +1,7 @@
import { Box, createStyles, Image, Text } from "@mantine/core"
import { Entry } from "app/types"
import { RelativeDate } from "components/RelativeDate"
import { FeedEntryTitle } from "./FeedEntryTitle"
export interface FeedEntryHeaderProps {
entry: Entry
@@ -27,7 +28,9 @@ export function FeedEntryHeader(props: FeedEntryHeaderProps) {
const { classes } = useStyles(props)
return (
<Box>
<Box className={classes.headerText}>{props.entry.title}</Box>
<Box className={classes.headerText}>
<FeedEntryTitle entry={props.entry} />
</Box>
<Box className={classes.headerSubtext}>
<Box mr={6}>
<Image withPlaceholder src={props.entry.iconUrl} alt="feed icon" width={18} height={18} />

View File

@@ -0,0 +1,13 @@
import { Highlight } from "@mantine/core"
import { useAppSelector } from "app/store"
import { Entry } from "app/types"
export interface FeedEntryTitleProps {
entry: Entry
}
export function FeedEntryTitle(props: FeedEntryTitleProps) {
const search = useAppSelector(state => state.entries.search)
const keywords = search?.split(" ")
return <Highlight highlight={keywords ?? ""}>{props.entry.title}</Highlight>
}