test & apply rules

This commit is contained in:
刘浩远 2020-06-24 21:46:26 +08:00
parent a922a2434f
commit eba23e606f
4 changed files with 63 additions and 6 deletions

View File

@ -6,6 +6,8 @@ import { Stack, Label, Dropdown, IDropdownOption, TextField, PrimaryButton, Icon
import { SourceRule, RuleActions } from "../../scripts/models/rule" import { SourceRule, RuleActions } from "../../scripts/models/rule"
import { FilterType } from "../../scripts/models/feed" import { FilterType } from "../../scripts/models/feed"
import { validateRegex } from "../../scripts/utils" import { validateRegex } from "../../scripts/utils"
import { RSSItem } from "../../scripts/models/item"
import Parser = require("@yang991178/rss-parser")
const actionKeyMap = { const actionKeyMap = {
"r-true": "article.markRead", "r-true": "article.markRead",
@ -29,6 +31,9 @@ type RulesTabState = {
fullSearch: boolean fullSearch: boolean
match: boolean match: boolean
actionKeys: string[] actionKeys: string[]
mockTitle: string
mockContent: string
mockResult: string
} }
class RulesTab extends React.Component<RulesTabProps, RulesTabState> { class RulesTab extends React.Component<RulesTabProps, RulesTabState> {
@ -46,7 +51,10 @@ class RulesTab extends React.Component<RulesTabProps, RulesTabState> {
regex: "", regex: "",
fullSearch: false, fullSearch: false,
match: true, match: true,
actionKeys: [] actionKeys: [],
mockTitle: "",
mockContent: "",
mockResult: ""
} }
this.rulesSelection = new Selection({ this.rulesSelection = new Selection({
getKey: (_, i) => i, getKey: (_, i) => i,
@ -141,7 +149,10 @@ class RulesTab extends React.Component<RulesTabProps, RulesTabState> {
} }
onSourceOptionChange = (_, item: IDropdownOption) => { onSourceOptionChange = (_, item: IDropdownOption) => {
this.initRuleEdit() this.initRuleEdit()
this.setState({ sid: item.key as string, selectedRules: [], editIndex: -1 }) this.setState({
sid: item.key as string, selectedRules: [], editIndex: -1,
mockTitle: "", mockContent: "", mockResult: ""
})
} }
searchOptions = (): IDropdownOption[] => [ searchOptions = (): IDropdownOption[] => [
@ -241,6 +252,19 @@ class RulesTab extends React.Component<RulesTabProps, RulesTabState> {
return items return items
} }
testMockItem = () => {
let parsed = { title: this.state.mockTitle }
let source = this.props.sources[parseInt(this.state.sid)]
let item = new RSSItem(parsed as Parser.Item, source)
item.snippet = this.state.mockContent
SourceRule.applyAll(this.getSourceRules(), item)
let result = []
result.push(intl.get(item.hasRead ? "article.markRead" : "article.markUnread"))
if (item.starred) result.push(intl.get("article.star"))
if (item.hidden) result.push(intl.get("article.hide"))
this.setState({ mockResult: result.join(", ") })
}
render = () => ( render = () => (
<div className="tab-body"> <div className="tab-body">
<Stack horizontal tokens={{childrenGap: 16}}> <Stack horizontal tokens={{childrenGap: 16}}>
@ -331,6 +355,31 @@ class RulesTab extends React.Component<RulesTabProps, RulesTabState> {
selection={this.rulesSelection} selection={this.rulesSelection}
selectionMode={SelectionMode.multiple} /> selectionMode={SelectionMode.multiple} />
</MarqueeSelection> </MarqueeSelection>
<span className="settings-hint up">{intl.get("rules.hint")}</span>
<Label>{intl.get("rules.test")}</Label>
<Stack horizontal>
<Stack.Item grow>
<TextField
name="mockTitle"
placeholder={intl.get("rules.title")}
value={this.state.mockTitle}
onChange={this.handleInputChange} />
</Stack.Item>
<Stack.Item grow>
<TextField
name="mockContent"
placeholder={intl.get("rules.content")}
value={this.state.mockContent}
onChange={this.handleInputChange} />
</Stack.Item>
<Stack.Item>
<PrimaryButton
text={intl.get("confirm")}
onClick={this.testMockItem} />
</Stack.Item>
</Stack>
<span className="settings-hint up">{this.state.mockResult}</span>
</>)} </>)}
</div> </div>
) )

View File

@ -134,13 +134,16 @@
"if": "If", "if": "If",
"then": "Then", "then": "Then",
"title": "Title", "title": "Title",
"content": "Content",
"fullSearch": "Title or content", "fullSearch": "Title or content",
"match": "matches", "match": "matches",
"notMatch": "doesn't match", "notMatch": "doesn't match",
"regex": "Regular expression", "regex": "Regular expression",
"badRegex": "Invalid regular expression.", "badRegex": "Invalid regular expression.",
"action": "Actions", "action": "Actions",
"selectAction": "Select actions" "selectAction": "Select actions",
"hint": "Rules will be applied in order. Drag and drop to reorder.",
"test": "Test rules"
}, },
"app": { "app": {
"cleanup": "Clean up", "cleanup": "Clean up",

View File

@ -134,13 +134,16 @@
"if": "若", "if": "若",
"then": "则", "then": "则",
"title": "标题", "title": "标题",
"content": "正文",
"fullSearch": "标题或正文", "fullSearch": "标题或正文",
"match": "匹配", "match": "匹配",
"notMatch": "不匹配", "notMatch": "不匹配",
"regex": "正则表达式", "regex": "正则表达式",
"badRegex": "正则表达式非法", "badRegex": "正则表达式非法",
"action": "行为", "action": "行为",
"selectAction": "选择行为" "selectAction": "选择行为",
"hint": "规则将按顺序执行,拖拽以排序",
"test": "测试规则"
}, },
"app": { "app": {
"cleanup": "清理", "cleanup": "清理",

View File

@ -58,6 +58,7 @@ export class RSSSource {
reject(err) reject(err)
} else if (doc === null) { } else if (doc === null) {
RSSItem.parseContent(i, item) RSSItem.parseContent(i, item)
if (source.rules) SourceRule.applyAll(source.rules, i)
resolve(i) resolve(i)
} else { } else {
resolve(null) resolve(null)
@ -367,9 +368,10 @@ export function sourceReducer(
case ActionStatus.Success: { case ActionStatus.Success: {
let updateMap = new Map<number, number>() let updateMap = new Map<number, number>()
for (let item of action.items) { for (let item of action.items) {
updateMap.set( if (!item.hasRead) { updateMap.set(
item.source, item.source,
updateMap.has(item.source) ? (updateMap.get(item.source) + 1) : 1) updateMap.has(item.source) ? (updateMap.get(item.source) + 1) : 1
)}
} }
let nextState = {} as SourceState let nextState = {} as SourceState
for (let [s, source] of Object.entries(state)) { for (let [s, source] of Object.entries(state)) {