add case sensitivity option

This commit is contained in:
刘浩远 2020-08-10 12:17:37 +08:00
parent 5c6cbf5be0
commit c6420d1ec8
18 changed files with 164 additions and 104 deletions

155
package-lock.json generated

@ -46,56 +46,66 @@
"sumchecker": "^3.0.1"
}
},
"@fluentui/date-time-utilities": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@fluentui/date-time-utilities/-/date-time-utilities-7.4.0.tgz",
"integrity": "sha512-8zaFJ5I1AikQmoi5aWv/mustCf8UAFYUjJrrnlXwvXOe2HlC+wLZH236qRZPi4Wat8qG151vE24nTqzGMVldRQ==",
"dev": true,
"requires": {
"@uifabric/set-version": "^7.0.19",
"tslib": "^1.10.0"
}
},
"@fluentui/keyboard-key": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@fluentui/keyboard-key/-/keyboard-key-0.2.1.tgz",
"integrity": "sha512-s2CYcspWWdqzwXNOvkNURifuRRiZun/5CQ3gcvRw9+S9/ONvPtedRkppNeTyj2wbW6Ctzf218bu2eJqu0aVK/Q==",
"version": "0.2.8",
"resolved": "https://registry.npmjs.org/@fluentui/keyboard-key/-/keyboard-key-0.2.8.tgz",
"integrity": "sha512-GJW3NjDdigTddYuxoOuBGhOs5Egweqs6iPTDSUN+oAtXI/poYHVtgjxaFQx1OeAzD8wLXofGneAe/03ZW+TESA==",
"dev": true,
"requires": {
"tslib": "^1.10.0"
}
},
"@fluentui/react": {
"version": "7.117.1",
"resolved": "https://registry.npmjs.org/@fluentui/react/-/react-7.117.1.tgz",
"integrity": "sha512-BKXFdZPjeOVdQksfLVM4YasDYk5LByTw7rGrW/z+Ae4RWRZ9JnLrfaPZSUSn4Puu5iKqipp3+WSU6BXcaPdvPA==",
"version": "7.126.2",
"resolved": "https://registry.npmjs.org/@fluentui/react/-/react-7.126.2.tgz",
"integrity": "sha512-WQ4u1oV0Cm+N2igltRIpg/B8LpxHiaiLue9++aZTtCvz4I5I5dRjm+LlVEomV5ml3n4m5apg9ml2MpNTQ7S+cA==",
"dev": true,
"requires": {
"@uifabric/set-version": "^7.0.13",
"office-ui-fabric-react": "^7.117.1",
"@uifabric/set-version": "^7.0.19",
"office-ui-fabric-react": "^7.126.2",
"tslib": "^1.10.0"
}
},
"@fluentui/react-focus": {
"version": "7.12.5",
"resolved": "https://registry.npmjs.org/@fluentui/react-focus/-/react-focus-7.12.5.tgz",
"integrity": "sha512-K2DrfMI74pkuGeiqstWgqGVnMg836CE3eJ3fydkXa19+6lUjkB4yDrSgwrQkvvEXixCiJyfvAxyGuFJ74EKQwA==",
"version": "7.12.30",
"resolved": "https://registry.npmjs.org/@fluentui/react-focus/-/react-focus-7.12.30.tgz",
"integrity": "sha512-UV9StYMID9cRB2TQRE1u134sT8AOaOs0A2ZW4oM82/lzQ8XxL3aREFY0ohhmblYhEGzkb349y51h4eSqW5EpPA==",
"dev": true,
"requires": {
"@fluentui/keyboard-key": "^0.2.1",
"@uifabric/merge-styles": "^7.14.1",
"@uifabric/set-version": "^7.0.13",
"@uifabric/styling": "^7.12.15",
"@uifabric/utilities": "^7.20.3",
"@fluentui/keyboard-key": "^0.2.8",
"@uifabric/merge-styles": "^7.16.4",
"@uifabric/set-version": "^7.0.19",
"@uifabric/styling": "^7.14.10",
"@uifabric/utilities": "^7.26.1",
"tslib": "^1.10.0"
}
},
"@fluentui/react-icons": {
"version": "0.1.24",
"resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-0.1.24.tgz",
"integrity": "sha512-PNAN1CjfBNL6p0fViftKDK8xcVnt05psEhO9wzCSOld9iw2eGu3JEObzolKGGoASvsiPJEQxbGOHZPuasVKVFg==",
"version": "0.1.45",
"resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-0.1.45.tgz",
"integrity": "sha512-xDE7dgbh3JgUt2uFUW66ut4V9T5irmxi+S5IGJKkXLUf3MHBysknx42BkX1Yc1Uczz3r5va1oWWwlXNiN/CjsA==",
"dev": true,
"requires": {
"@uifabric/set-version": "^7.0.13",
"@uifabric/styling": "^7.12.15",
"@uifabric/utilities": "^7.20.3",
"@microsoft/load-themed-styles": "^1.10.26",
"@uifabric/set-version": "^7.0.19",
"@uifabric/utilities": "^7.26.1",
"tslib": "^1.10.0"
}
},
"@microsoft/load-themed-styles": {
"version": "1.10.55",
"resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.55.tgz",
"integrity": "sha512-1gT/zQVJC6dTvMAHfxteTlBmGsSHuLkKVe5vYCQ6JOzah8Yv9hwTSZESUZfj5HKV2PQ11KnVrT3TS815gKExoA==",
"version": "1.10.66",
"resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.66.tgz",
"integrity": "sha512-w1NCJQOrr5Ko5Og1ay7NO0vFUwxksddnLmVuY2bBi7DkgbFYtiRFQGkc+HMDpy2x6Fcy/iUTitbR674aX5TJhw==",
"dev": true
},
"@sindresorhus/is": {
@ -278,80 +288,80 @@
"dev": true
},
"@uifabric/foundation": {
"version": "7.7.22",
"resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-7.7.22.tgz",
"integrity": "sha512-vTWhNT0wz0VB5DagSCPWQ+7d8Yv7AYOGsvOgPzS6FzA3pxSLX6xmN00ica1gCCZBz4xZn4Yw7YjcyF8bDARqSw==",
"version": "7.7.44",
"resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-7.7.44.tgz",
"integrity": "sha512-0YBZTGsVxQEd1+IYXawQcOd263auoXCQo2KE8CaL2aug330+9LIqloIyjnhDH+yJQ9JZZmzzlJ2clOD7NCT97g==",
"dev": true,
"requires": {
"@uifabric/merge-styles": "^7.14.1",
"@uifabric/set-version": "^7.0.13",
"@uifabric/styling": "^7.12.15",
"@uifabric/utilities": "^7.20.3",
"@uifabric/merge-styles": "^7.16.4",
"@uifabric/set-version": "^7.0.19",
"@uifabric/styling": "^7.14.10",
"@uifabric/utilities": "^7.26.1",
"tslib": "^1.10.0"
}
},
"@uifabric/icons": {
"version": "7.3.48",
"resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-7.3.48.tgz",
"integrity": "sha512-DUhRluQrYAvvyElr5F/Gzkscl+gArgeEtLVJsjqXAsop8SAvJCEVCnSxVEZVDOOlsytEVVPpxiHGkaDow+26fg==",
"version": "7.3.70",
"resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-7.3.70.tgz",
"integrity": "sha512-ibAyKU02TFH6wdqrnu7keea9MaM29EGrMTkz24DyNlWfM+H9z3JL6UiWUeldgyok8HXDWI9GRQWB7f3YhJi60Q==",
"dev": true,
"requires": {
"@uifabric/set-version": "^7.0.13",
"@uifabric/styling": "^7.12.15",
"@uifabric/set-version": "^7.0.19",
"@uifabric/styling": "^7.14.10",
"tslib": "^1.10.0"
}
},
"@uifabric/merge-styles": {
"version": "7.14.1",
"resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.14.1.tgz",
"integrity": "sha512-nKkk0o9XyVh8HL174ZSDqw3IUnN2qb+kO73vg/rwioKPEQyuPGoEfij8jrb+CGpcCGnMYi53IeB1tm8ySlNatg==",
"version": "7.16.4",
"resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-7.16.4.tgz",
"integrity": "sha512-OhOEtwYD74AARf4VZQJPan97QEvtTYcxBGVQfdE7YxFnvR1VdfMxOsV+9CAjAIFM+Xu5ibeKkEE/ZmJYnHkqsQ==",
"dev": true,
"requires": {
"@uifabric/set-version": "^7.0.13",
"@uifabric/set-version": "^7.0.19",
"tslib": "^1.10.0"
}
},
"@uifabric/react-hooks": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/@uifabric/react-hooks/-/react-hooks-7.4.5.tgz",
"integrity": "sha512-OLEBII+7x4rlTWjQ6hvMWS+CuWRJgvEfCsUcFMu5D5teXTr0bZQThyW8oVvkqKsNmF2JdwwqT6dSwhwgarlFzA==",
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/@uifabric/react-hooks/-/react-hooks-7.7.3.tgz",
"integrity": "sha512-WAhMcnQSRgSQr0wkw8paESqxHCDWL2vdgTcDNKSj550GpAzD9BgZSBp3v3yln/qA4pN/nfjeU0CX9HujidSbMA==",
"dev": true,
"requires": {
"@uifabric/set-version": "^7.0.13",
"@uifabric/utilities": "^7.20.3",
"@uifabric/set-version": "^7.0.19",
"@uifabric/utilities": "^7.26.1",
"tslib": "^1.10.0"
}
},
"@uifabric/set-version": {
"version": "7.0.13",
"resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.13.tgz",
"integrity": "sha512-SRsYaacvNykS9lRwKNJgrJuhPV4ytblthFNg0+Wi6+zvIf/w50k/nBlmXVetV5U9dAuX4njSkd+/3iOpgevkyw==",
"version": "7.0.19",
"resolved": "https://registry.npmjs.org/@uifabric/set-version/-/set-version-7.0.19.tgz",
"integrity": "sha512-p52z9Z5Kfl0kAU3DiPNPg+0vCdSAxlkRZEtEa+RwM6fh9XSo91n4C56FFdKDW7HJVuhGjMK7UEXuU6ELY1W7fg==",
"dev": true,
"requires": {
"tslib": "^1.10.0"
}
},
"@uifabric/styling": {
"version": "7.12.15",
"resolved": "https://registry.npmjs.org/@uifabric/styling/-/styling-7.12.15.tgz",
"integrity": "sha512-mp/nRyE5Ig6a8BPEkXy/F8ckeQptQCm1SOQDPB1VT1PlknHoOXqqTVn2e1e2DGpWcpFIw4dbodqym3rgpUqkjA==",
"version": "7.14.10",
"resolved": "https://registry.npmjs.org/@uifabric/styling/-/styling-7.14.10.tgz",
"integrity": "sha512-hEmXCJJUVr+ykvPVXyvTHS5f2/GMCh1PObajuXgtpZVpJRzA+Rwgg5gBxWYeRNYw+3WZV62f0zAbmYh8ZCRAhQ==",
"dev": true,
"requires": {
"@microsoft/load-themed-styles": "^1.10.26",
"@uifabric/merge-styles": "^7.14.1",
"@uifabric/set-version": "^7.0.13",
"@uifabric/utilities": "^7.20.3",
"@uifabric/merge-styles": "^7.16.4",
"@uifabric/set-version": "^7.0.19",
"@uifabric/utilities": "^7.26.1",
"tslib": "^1.10.0"
}
},
"@uifabric/utilities": {
"version": "7.20.3",
"resolved": "https://registry.npmjs.org/@uifabric/utilities/-/utilities-7.20.3.tgz",
"integrity": "sha512-Amg+qdnNKx0yxjoEFHanM2jTCYfCZAlHPDHcS+BCiSwzeQrEPhlOrtZVPJtagNVhXLFiC09uDsmE/BF6dHb+ww==",
"version": "7.26.1",
"resolved": "https://registry.npmjs.org/@uifabric/utilities/-/utilities-7.26.1.tgz",
"integrity": "sha512-FX/Gu4XY6YlvBEyTyEeXUOtPpgTy1irHpSAE/vDbDZQlksVNv4FPnVingQZI9T/rA96ivP4q1PUutrb3X3hfsw==",
"dev": true,
"requires": {
"@uifabric/merge-styles": "^7.14.1",
"@uifabric/set-version": "^7.0.13",
"@uifabric/merge-styles": "^7.16.4",
"@uifabric/set-version": "^7.0.19",
"prop-types": "^15.7.2",
"tslib": "^1.10.0"
}
@ -4753,21 +4763,22 @@
}
},
"office-ui-fabric-react": {
"version": "7.117.1",
"resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-7.117.1.tgz",
"integrity": "sha512-AHjlLgBJmrVFOtL04V3qpljXVw2Z8sgcnzl5ZYBR4ZAGMdEtvzJBq5aMbUoQgh3jKHEgpLJfoznd4XWi75FshQ==",
"version": "7.126.2",
"resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-7.126.2.tgz",
"integrity": "sha512-1ETz4x5AsBZ36PBLIbL3T++Aso+9WudIsu3Z331UDNwSJF4LjOM50+PY/i7DUb+LSDzmwMwydCpU5yzEndLRBw==",
"dev": true,
"requires": {
"@fluentui/react-focus": "^7.12.5",
"@fluentui/react-icons": "^0.1.24",
"@fluentui/date-time-utilities": "^7.4.0",
"@fluentui/react-focus": "^7.12.30",
"@fluentui/react-icons": "^0.1.45",
"@microsoft/load-themed-styles": "^1.10.26",
"@uifabric/foundation": "^7.7.22",
"@uifabric/icons": "^7.3.48",
"@uifabric/merge-styles": "^7.14.1",
"@uifabric/react-hooks": "^7.4.5",
"@uifabric/set-version": "^7.0.13",
"@uifabric/styling": "^7.12.15",
"@uifabric/utilities": "^7.20.3",
"@uifabric/foundation": "^7.7.44",
"@uifabric/icons": "^7.3.70",
"@uifabric/merge-styles": "^7.16.4",
"@uifabric/react-hooks": "^7.7.3",
"@uifabric/set-version": "^7.0.19",
"@uifabric/styling": "^7.14.10",
"@uifabric/utilities": "^7.26.1",
"prop-types": "^15.7.2",
"tslib": "^1.10.0"
}

@ -83,7 +83,7 @@
}
},
"devDependencies": {
"@fluentui/react": "^7.115.3",
"@fluentui/react": "^7.126.2",
"@types/nedb": "^1.8.9",
"@types/react": "^16.9.35",
"@types/react-dom": "^16.9.8",

@ -2,13 +2,14 @@ import * as React from "react"
import { RSSSource, SourceOpenTarget } from "../../scripts/models/source"
import { RSSItem } from "../../scripts/models/item"
import { platformCtrl } from "../../scripts/utils"
import { FeedFilter } from "../../scripts/models/feed"
export namespace Card {
export type Props = {
feedId: string
item: RSSItem
source: RSSSource
keyword: string
filter: FeedFilter
shortcuts: (item: RSSItem, e: KeyboardEvent) => void
markRead: (item: RSSItem) => void
contextMenu: (feedId: string, item: RSSItem, e) => void

@ -18,8 +18,8 @@ const CompactCard: React.FunctionComponent<Card.Props> = (props) => (
data-is-focusable>
<CardInfo source={props.source} item={props.item} hideTime />
<div className="data">
<span className="title"><Highlights text={props.item.title} keyword={props.keyword} title /></span>
<span className="snippet"><Highlights text={props.item.snippet} keyword={props.keyword} /></span>
<span className="title"><Highlights text={props.item.title} filter={props.filter} title /></span>
<span className="snippet"><Highlights text={props.item.snippet} filter={props.filter} /></span>
</div>
<Time date={props.item.date} />
</div>

@ -24,9 +24,9 @@ const DefaultCard: React.FunctionComponent<Card.Props> = (props) => (
<img className="head" src={props.item.thumb} />
) : null}
<CardInfo source={props.source} item={props.item} />
<h3 className="title"><Highlights text={props.item.title} keyword={props.keyword} title /></h3>
<h3 className="title"><Highlights text={props.item.title} filter={props.filter} title /></h3>
<p className={"snippet" + (props.item.thumb ? "" : " show")}>
<Highlights text={props.item.snippet} keyword={props.keyword} />
<Highlights text={props.item.snippet} filter={props.filter} />
</p>
</div>
)

@ -1,16 +1,18 @@
import * as React from "react"
import { validateRegex } from "../../scripts/utils"
import { FeedFilter, FilterType } from "../../scripts/models/feed"
type HighlightsProps = {
text: string
keyword: string
filter: FeedFilter
title?: boolean
}
const Highlights: React.FunctionComponent<HighlightsProps> = (props) => {
const spans: [string, boolean][] = new Array()
const flags = (props.filter.type & FilterType.CaseInsensitive) ? "ig" : "g"
let regex: RegExp
if (props.keyword === "" || !(regex = validateRegex(props.keyword, "g"))) {
if (props.filter.search === "" || !(regex = validateRegex(props.filter.search, flags))) {
if (props.title) spans.push([props.text, false])
else spans.push([props.text.substr(0, 325), false])
} else if (props.title) {

@ -20,7 +20,7 @@ const ListCard: React.FunctionComponent<Card.Props> = (props) => (
) : null}
<div className="data">
<CardInfo source={props.source} item={props.item} />
<h3 className="title"><Highlights text={props.item.title} keyword={props.keyword} title /></h3>
<h3 className="title"><Highlights text={props.item.title} filter={props.filter} title /></h3>
</div>
</div>
)

@ -21,8 +21,8 @@ const MagazineCard: React.FunctionComponent<Card.Props> = (props) => (
) : null}
<div className="data">
<div>
<h3 className="title"><Highlights text={props.item.title} keyword={props.keyword} title /></h3>
<p className="snippet"><Highlights text={props.item.snippet} keyword={props.keyword} /></p>
<h3 className="title"><Highlights text={props.item.title} filter={props.filter} title /></h3>
<p className="snippet"><Highlights text={props.item.snippet} filter={props.filter} /></p>
</div>
<CardInfo source={props.source} item={props.item} showCreator />
</div>

@ -289,11 +289,30 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
}
},
{
key: "fullSearch",
text: intl.get("context.fullSearch"),
canCheck: true,
checked: Boolean(this.props.filter & FilterType.FullSearch),
onClick: () => this.props.toggleFilter(FilterType.FullSearch)
key: "section_3",
itemType: ContextualMenuItemType.Section,
sectionProps: {
title: intl.get("search"),
bottomDivider: true,
items: [
{
key: "caseSensitive",
text: intl.get("context.caseSensitive"),
iconProps: { style: { fontSize: 12, fontStyle: "normal" }, children: "Aa" },
canCheck: true,
checked: !(this.props.filter & FilterType.CaseInsensitive),
onClick: () => this.props.toggleFilter(FilterType.CaseInsensitive)
},
{
key: "fullSearch",
text: intl.get("context.fullSearch"),
iconProps: { iconName: "Breadcrumb" },
canCheck: true,
checked: Boolean(this.props.filter & FilterType.FullSearch),
onClick: () => this.props.toggleFilter(FilterType.FullSearch)
},
]
}
},
{
key: "showHidden",

@ -47,7 +47,7 @@ class CardsFeed extends React.Component<FeedProps> {
key={item._id}
item={item}
source={this.props.sourceMap[item.source]}
keyword={this.props.keyword}
filter={this.props.filter}
shortcuts={this.props.shortcuts}
markRead={this.props.markRead}
contextMenu={this.props.contextMenu}

@ -1,7 +1,7 @@
import * as React from "react"
import { RSSItem } from "../../scripts/models/item"
import { FeedReduxProps } from "../../containers/feed-container"
import { RSSFeed } from "../../scripts/models/feed"
import { RSSFeed, FeedFilter } from "../../scripts/models/feed"
import { ViewType } from "../../schema-types"
import CardsFeed from "./cards-feed"
import ListFeed from "./list-feed"
@ -11,7 +11,7 @@ export type FeedProps = FeedReduxProps & {
viewType: ViewType
items: RSSItem[]
sourceMap: Object
keyword: string
filter: FeedFilter
shortcuts: (item: RSSItem, e: KeyboardEvent) => void
markRead: (item: RSSItem) => void
contextMenu: (feedId: string, item: RSSItem, e) => void

@ -16,7 +16,7 @@ class ListFeed extends React.Component<FeedProps> {
key: item._id,
item: item,
source: this.props.sourceMap[item.source],
keyword: this.props.keyword,
filter: this.props.filter,
shortcuts: this.props.shortcuts,
markRead: this.props.markRead,
contextMenu: this.props.contextMenu,

@ -2,7 +2,7 @@ import * as React from "react"
import intl from "react-intl-universal"
import { SourceState, RSSSource } from "../../scripts/models/source"
import { Stack, Label, Dropdown, IDropdownOption, TextField, PrimaryButton, Icon, DropdownMenuItemType,
DefaultButton, DetailsList, IColumn, CommandBar, ICommandBarItemProps, Selection, SelectionMode, MarqueeSelection, IDragDropEvents, Link } from "@fluentui/react"
DefaultButton, DetailsList, IColumn, CommandBar, ICommandBarItemProps, Selection, SelectionMode, MarqueeSelection, IDragDropEvents, Link, IIconProps } from "@fluentui/react"
import { SourceRule, RuleActions } from "../../scripts/models/rule"
import { FilterType } from "../../scripts/models/feed"
import { validateRegex } from "../../scripts/utils"
@ -31,6 +31,7 @@ type RulesTabState = {
editIndex: number
regex: string
fullSearch: boolean
caseSensitive: boolean
match: boolean
actionKeys: string[]
mockTitle: string
@ -52,6 +53,7 @@ class RulesTab extends React.Component<RulesTabProps, RulesTabState> {
editIndex: -1,
regex: "",
fullSearch: false,
caseSensitive: false,
match: true,
actionKeys: [],
mockTitle: "",
@ -104,6 +106,7 @@ class RulesTab extends React.Component<RulesTabProps, RulesTabState> {
this.setState({
regex: rule ? rule.filter.search : "",
fullSearch: rule ? Boolean(rule.filter.type & FilterType.FullSearch) : false,
caseSensitive: rule ? !(rule.filter.type & FilterType.CaseInsensitive) : false,
match: rule ? rule.match : true,
actionKeys: rule ? RuleActions.toKeys(rule.actions) : []
})
@ -204,7 +207,7 @@ class RulesTab extends React.Component<RulesTabProps, RulesTabState> {
}
saveRule = () => {
let rule = new SourceRule(this.state.regex, this.state.actionKeys, this.state.fullSearch, this.state.match)
let rule = new SourceRule(this.state.regex, this.state.actionKeys, this.state.fullSearch, this.state.caseSensitive, this.state.match)
let source = this.props.sources[parseInt(this.state.sid)]
let rules = source.rules ? [ ...source.rules ] : []
if (this.state.editIndex === -1) {
@ -269,6 +272,23 @@ class RulesTab extends React.Component<RulesTabProps, RulesTabState> {
this.setState({ mockResult: result.join(", ") })
}
toggleCaseSensitivity = () => {
this.setState({ caseSensitive: !this.state.caseSensitive })
}
regexCaseIconProps = (): IIconProps => ({
title: intl.get("context.caseSensitive"),
children: "Aa",
style: {
fontSize: 12,
fontStyle: "normal",
cursor: "pointer",
pointerEvents: "unset",
color: this.state.caseSensitive ? "var(--black)" : "var(--neutralTertiary)",
textDecoration: this.state.caseSensitive ? "underline" : "",
},
onClick: this.toggleCaseSensitivity
})
render = () => (
<div className="tab-body">
<Stack horizontal tokens={{childrenGap: 16}}>
@ -314,6 +334,7 @@ class RulesTab extends React.Component<RulesTabProps, RulesTabState> {
<TextField
name="regex"
placeholder={intl.get("rules.regex")}
iconProps={this.regexCaseIconProps()}
value={this.state.regex}
onGetErrorMessage={this.validateRegexField}
validateOnLoad={false}

@ -16,17 +16,17 @@ interface FeedContainerProps {
const getSources = (state: RootState) => state.sources
const getItems = (state: RootState) => state.items
const getFeed = (state: RootState, props: FeedContainerProps) => state.feeds[props.feedId]
const getKeyword = (state: RootState) => state.page.filter.search
const getFilter = (state: RootState) => state.page.filter
const getView = (_, props: FeedContainerProps) => props.viewType
const makeMapStateToProps = () => {
return createSelector(
[getSources, getItems, getFeed, getView, getKeyword],
(sources, items, feed, viewType, keyword) => ({
[getSources, getItems, getFeed, getView, getFilter],
(sources, items, feed, viewType, filter) => ({
feed: feed,
items: feed.iids.map(iid => items[iid]),
sourceMap: sources,
keyword: keyword,
filter: filter,
viewType: viewType
})
)

@ -88,7 +88,8 @@
"manageSources": "Manage sources",
"saveImageAs": "Save image as …",
"copyImage": "Copy image",
"copyImageURL": "Copy image link"
"copyImageURL": "Copy image link",
"caseSensitive": "Case sensitive"
},
"searchEngine": {
"name": "Search engine",

@ -88,7 +88,8 @@
"manageSources": "管理订阅源",
"saveImageAs": "将图像另存为",
"copyImage": "复制图像",
"copyImageURL": "复制图像链接"
"copyImageURL": "复制图像链接",
"caseSensitive": "区分大小写"
},
"searchEngine": {
"name": "搜索引擎",

@ -10,8 +10,9 @@ export enum FilterType {
ShowNotStarred = 1 << 1,
ShowHidden = 1 << 2,
FullSearch = 1 << 3,
CaseInsensitive = 1 << 4,
Default = ShowRead | ShowNotStarred,
Default = ShowRead | ShowNotStarred | CaseInsensitive,
UnreadOnly = ShowNotStarred,
StarredOnly = ShowRead,
Toggles = ShowHidden | FullSearch
@ -36,7 +37,8 @@ export class FeedFilter {
if (type & FilterType.ShowNotStarred) delete query.starred
if (type & FilterType.ShowHidden) delete query.hidden
if (filter.search !== "") {
let regex = RegExp(filter.search)
const flags = (type & FilterType.CaseInsensitive) ? "i" : ""
const regex = RegExp(filter.search, flags)
if (type & FilterType.FullSearch) {
query.$or = [
{ title: { $regex: regex } },
@ -56,7 +58,8 @@ export class FeedFilter {
if (!(type & FilterType.ShowNotStarred)) flag = flag && item.starred
if (!(type & FilterType.ShowHidden)) flag = flag && !item.hidden
if (filter.search !== "") {
let regex = RegExp(filter.search)
const flags = (type & FilterType.CaseInsensitive) ? "i" : ""
const regex = RegExp(filter.search, flags)
if (type & FilterType.FullSearch) {
flag = flag && (regex.test(item.title) || regex.test(item.snippet))
} else {

@ -65,9 +65,10 @@ export class SourceRule {
match: boolean
actions: RuleActions
constructor(regex: string, actions: string[], fullSearch: boolean, match: boolean) {
constructor(regex: string, actions: string[], fullSearch: boolean, caseSensitive: boolean, match: boolean) {
this.filter = new FeedFilter(FilterType.Default | FilterType.ShowHidden, regex)
if (fullSearch) this.filter.type |= FilterType.FullSearch
if (caseSensitive) this.filter.type &= ~FilterType.CaseInsensitive
this.match = match
this.actions = RuleActions.fromKeys(actions)
}