mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-02-12 01:30:52 +01:00
commit
b827f268ac
4
.github/workflows/release-linux.yml
vendored
4
.github/workflows/release-linux.yml
vendored
@ -14,9 +14,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and package the app
|
- name: Build and package the app
|
||||||
run: |
|
run: |
|
||||||
sudo npm install --unsafe-perm=true --allow-root
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
sudo npm run package-linux
|
npm run package-linux
|
||||||
|
|
||||||
- name: Get app version
|
- name: Get app version
|
||||||
id: package-version
|
id: package-version
|
||||||
|
4
dist/styles/cards.css
vendored
4
dist/styles/cards.css
vendored
@ -91,6 +91,10 @@
|
|||||||
.card span.h {
|
.card span.h {
|
||||||
background: #fce10080;
|
background: #fce10080;
|
||||||
}
|
}
|
||||||
|
.card.rtl .snippet,
|
||||||
|
.card.rtl .title {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
.default-card {
|
.default-card {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
2
dist/styles/main.css
vendored
2
dist/styles/main.css
vendored
@ -209,7 +209,7 @@ img.favicon.dropdown {
|
|||||||
.main::before {
|
.main::before {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
position: sticky;
|
position: relative;
|
||||||
top: var(--navHeight);
|
top: var(--navHeight);
|
||||||
left: 0;
|
left: 0;
|
||||||
width: calc(100% - 16px);
|
width: calc(100% - 16px);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
appId: DevHYLiu.FluentReader
|
appId: DevHYLiu.FluentReader
|
||||||
buildVersion: 26
|
buildVersion: 27
|
||||||
productName: Fluent Reader
|
productName: Fluent Reader
|
||||||
copyright: Copyright © 2020 Haoyuan Liu
|
copyright: Copyright © 2020 Haoyuan Liu
|
||||||
files:
|
files:
|
||||||
|
999
package-lock.json
generated
999
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "fluent-reader",
|
"name": "fluent-reader",
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"description": "Modern desktop RSS reader",
|
"description": "Modern desktop RSS reader",
|
||||||
"main": "./dist/electron.js",
|
"main": "./dist/electron.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"@types/react-redux": "^7.1.9",
|
"@types/react-redux": "^7.1.9",
|
||||||
"@yang991178/rss-parser": "^3.8.1",
|
"@yang991178/rss-parser": "^3.8.1",
|
||||||
"electron": "^19.0.0",
|
"electron": "^19.0.0",
|
||||||
"electron-builder": "^22.11.3",
|
"electron-builder": "^23.0.3",
|
||||||
"electron-react-devtools": "^0.5.3",
|
"electron-react-devtools": "^0.5.3",
|
||||||
"electron-store": "^5.2.0",
|
"electron-store": "^5.2.0",
|
||||||
"electron-window-state": "^5.0.3",
|
"electron-window-state": "^5.0.3",
|
||||||
|
@ -3,10 +3,12 @@ import { Card } from "./card"
|
|||||||
import CardInfo from "./info"
|
import CardInfo from "./info"
|
||||||
import Time from "../utils/time"
|
import Time from "../utils/time"
|
||||||
import Highlights from "./highlights"
|
import Highlights from "./highlights"
|
||||||
|
import { SourceTextDirection } from "../../scripts/models/source"
|
||||||
|
|
||||||
const className = (props: Card.Props) => {
|
const className = (props: Card.Props) => {
|
||||||
let cn = ["card", "compact-card"]
|
let cn = ["card", "compact-card"]
|
||||||
if (props.item.hidden) cn.push("hidden")
|
if (props.item.hidden) cn.push("hidden")
|
||||||
|
if (props.source.textDir === SourceTextDirection.RTL) cn.push("rtl")
|
||||||
return cn.join(" ")
|
return cn.join(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,15 +25,10 @@ const CompactCard: React.FunctionComponent<Card.Props> = props => (
|
|||||||
text={props.item.title}
|
text={props.item.title}
|
||||||
filter={props.filter}
|
filter={props.filter}
|
||||||
title
|
title
|
||||||
dir={props.source.textDir}
|
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span className="snippet">
|
<span className="snippet">
|
||||||
<Highlights
|
<Highlights text={props.item.snippet} filter={props.filter} />
|
||||||
text={props.item.snippet}
|
|
||||||
filter={props.filter}
|
|
||||||
dir={props.source.textDir}
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Time date={props.item.date} />
|
<Time date={props.item.date} />
|
||||||
|
@ -2,11 +2,13 @@ import * as React from "react"
|
|||||||
import { Card } from "./card"
|
import { Card } from "./card"
|
||||||
import CardInfo from "./info"
|
import CardInfo from "./info"
|
||||||
import Highlights from "./highlights"
|
import Highlights from "./highlights"
|
||||||
|
import { SourceTextDirection } from "../../scripts/models/source"
|
||||||
|
|
||||||
const className = (props: Card.Props) => {
|
const className = (props: Card.Props) => {
|
||||||
let cn = ["card", "default-card"]
|
let cn = ["card", "default-card"]
|
||||||
if (props.item.snippet && props.item.thumb) cn.push("transform")
|
if (props.item.snippet && props.item.thumb) cn.push("transform")
|
||||||
if (props.item.hidden) cn.push("hidden")
|
if (props.item.hidden) cn.push("hidden")
|
||||||
|
if (props.source.textDir === SourceTextDirection.RTL) cn.push("rtl")
|
||||||
return cn.join(" ")
|
return cn.join(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,19 +27,10 @@ const DefaultCard: React.FunctionComponent<Card.Props> = props => (
|
|||||||
) : null}
|
) : null}
|
||||||
<CardInfo source={props.source} item={props.item} />
|
<CardInfo source={props.source} item={props.item} />
|
||||||
<h3 className="title">
|
<h3 className="title">
|
||||||
<Highlights
|
<Highlights text={props.item.title} filter={props.filter} title />
|
||||||
text={props.item.title}
|
|
||||||
filter={props.filter}
|
|
||||||
title
|
|
||||||
dir={props.source.textDir}
|
|
||||||
/>
|
|
||||||
</h3>
|
</h3>
|
||||||
<p className={"snippet" + (props.item.thumb ? "" : " show")}>
|
<p className={"snippet" + (props.item.thumb ? "" : " show")}>
|
||||||
<Highlights
|
<Highlights text={props.item.snippet} filter={props.filter} />
|
||||||
text={props.item.snippet}
|
|
||||||
filter={props.filter}
|
|
||||||
dir={props.source.textDir}
|
|
||||||
/>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -7,7 +7,6 @@ type HighlightsProps = {
|
|||||||
text: string
|
text: string
|
||||||
filter: FeedFilter
|
filter: FeedFilter
|
||||||
title?: boolean
|
title?: boolean
|
||||||
dir?: SourceTextDirection
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Highlights: React.FunctionComponent<HighlightsProps> = props => {
|
const Highlights: React.FunctionComponent<HighlightsProps> = props => {
|
||||||
@ -59,22 +58,10 @@ const Highlights: React.FunctionComponent<HighlightsProps> = props => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const testStyle = {
|
|
||||||
direction: "inherit",
|
|
||||||
} as React.CSSProperties
|
|
||||||
if (props.dir === SourceTextDirection.RTL) {
|
|
||||||
testStyle.direction = "rtl"
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{spans.map(([text, flag]) =>
|
{spans.map(([text, flag]) =>
|
||||||
flag ? (
|
flag ? <span className="h">{text}</span> : text
|
||||||
<div className="h" style={testStyle}>
|
|
||||||
{text}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div style={testStyle}>{text}</div>
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ import { Card } from "./card"
|
|||||||
import CardInfo from "./info"
|
import CardInfo from "./info"
|
||||||
import Highlights from "./highlights"
|
import Highlights from "./highlights"
|
||||||
import { ViewConfigs } from "../../schema-types"
|
import { ViewConfigs } from "../../schema-types"
|
||||||
|
import { SourceTextDirection } from "../../scripts/models/source"
|
||||||
|
|
||||||
const className = (props: Card.Props) => {
|
const className = (props: Card.Props) => {
|
||||||
let cn = ["card", "list-card"]
|
let cn = ["card", "list-card"]
|
||||||
@ -10,6 +11,7 @@ const className = (props: Card.Props) => {
|
|||||||
if (props.selected) cn.push("selected")
|
if (props.selected) cn.push("selected")
|
||||||
if (props.viewConfigs & ViewConfigs.FadeRead && props.item.hasRead)
|
if (props.viewConfigs & ViewConfigs.FadeRead && props.item.hasRead)
|
||||||
cn.push("read")
|
cn.push("read")
|
||||||
|
if (props.source.textDir === SourceTextDirection.RTL) cn.push("rtl")
|
||||||
return cn.join(" ")
|
return cn.join(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +33,6 @@ const ListCard: React.FunctionComponent<Card.Props> = props => (
|
|||||||
text={props.item.title}
|
text={props.item.title}
|
||||||
filter={props.filter}
|
filter={props.filter}
|
||||||
title
|
title
|
||||||
dir={props.source.textDir}
|
|
||||||
/>
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
{Boolean(props.viewConfigs & ViewConfigs.ShowSnippet) && (
|
{Boolean(props.viewConfigs & ViewConfigs.ShowSnippet) && (
|
||||||
@ -39,7 +40,6 @@ const ListCard: React.FunctionComponent<Card.Props> = props => (
|
|||||||
<Highlights
|
<Highlights
|
||||||
text={props.item.snippet}
|
text={props.item.snippet}
|
||||||
filter={props.filter}
|
filter={props.filter}
|
||||||
dir={props.source.textDir}
|
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
@ -2,11 +2,13 @@ import * as React from "react"
|
|||||||
import { Card } from "./card"
|
import { Card } from "./card"
|
||||||
import CardInfo from "./info"
|
import CardInfo from "./info"
|
||||||
import Highlights from "./highlights"
|
import Highlights from "./highlights"
|
||||||
|
import { SourceTextDirection } from "../../scripts/models/source"
|
||||||
|
|
||||||
const className = (props: Card.Props) => {
|
const className = (props: Card.Props) => {
|
||||||
let cn = ["card", "magazine-card"]
|
let cn = ["card", "magazine-card"]
|
||||||
if (props.item.hasRead) cn.push("read")
|
if (props.item.hasRead) cn.push("read")
|
||||||
if (props.item.hidden) cn.push("hidden")
|
if (props.item.hidden) cn.push("hidden")
|
||||||
|
if (props.source.textDir === SourceTextDirection.RTL) cn.push("rtl")
|
||||||
return cn.join(" ")
|
return cn.join(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,14 +30,12 @@ const MagazineCard: React.FunctionComponent<Card.Props> = props => (
|
|||||||
text={props.item.title}
|
text={props.item.title}
|
||||||
filter={props.filter}
|
filter={props.filter}
|
||||||
title
|
title
|
||||||
dir={props.source.textDir}
|
|
||||||
/>
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
<p className="snippet">
|
<p className="snippet">
|
||||||
<Highlights
|
<Highlights
|
||||||
text={props.item.snippet}
|
text={props.item.snippet}
|
||||||
filter={props.filter}
|
filter={props.filter}
|
||||||
dir={props.source.textDir}
|
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,6 +49,7 @@ export class Menu extends React.Component<MenuProps> {
|
|||||||
intl.get("allArticles") +
|
intl.get("allArticles") +
|
||||||
this.countOverflow(
|
this.countOverflow(
|
||||||
Object.values(this.props.sources)
|
Object.values(this.props.sources)
|
||||||
|
.filter(s => !s.hidden)
|
||||||
.map(s => s.unreadCount)
|
.map(s => s.unreadCount)
|
||||||
.reduce((a, b) => a + b, 0)
|
.reduce((a, b) => a + b, 0)
|
||||||
),
|
),
|
||||||
|
@ -16,6 +16,7 @@ import {
|
|||||||
Dropdown,
|
Dropdown,
|
||||||
MessageBar,
|
MessageBar,
|
||||||
MessageBarType,
|
MessageBarType,
|
||||||
|
Toggle,
|
||||||
} from "@fluentui/react"
|
} from "@fluentui/react"
|
||||||
import {
|
import {
|
||||||
SourceState,
|
SourceState,
|
||||||
@ -42,6 +43,7 @@ type SourcesTabProps = {
|
|||||||
deleteSources: (sources: RSSSource[]) => void
|
deleteSources: (sources: RSSSource[]) => void
|
||||||
importOPML: () => void
|
importOPML: () => void
|
||||||
exportOPML: () => void
|
exportOPML: () => void
|
||||||
|
toggleSourceHidden: (source: RSSSource) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
type SourcesTabState = {
|
type SourcesTabState = {
|
||||||
@ -217,6 +219,16 @@ class SourcesTab extends React.Component<SourcesTabProps, SourcesTabState> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onToggleHidden = () => {
|
||||||
|
this.props.toggleSourceHidden(this.state.selectedSource)
|
||||||
|
this.setState({
|
||||||
|
selectedSource: {
|
||||||
|
...this.state.selectedSource,
|
||||||
|
hidden: !this.state.selectedSource.hidden,
|
||||||
|
} as RSSSource,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render = () => (
|
render = () => (
|
||||||
<div className="tab-body">
|
<div className="tab-body">
|
||||||
{this.props.serviceOn && (
|
{this.props.serviceOn && (
|
||||||
@ -409,6 +421,17 @@ class SourcesTab extends React.Component<SourcesTabProps, SourcesTabState> {
|
|||||||
)}
|
)}
|
||||||
onChange={this.onOpenTargetChange}
|
onChange={this.onOpenTargetChange}
|
||||||
/>
|
/>
|
||||||
|
<Stack horizontal verticalAlign="baseline">
|
||||||
|
<Stack.Item grow>
|
||||||
|
<Label>{intl.get("sources.hidden")}</Label>
|
||||||
|
</Stack.Item>
|
||||||
|
<Stack.Item>
|
||||||
|
<Toggle
|
||||||
|
checked={this.state.selectedSource.hidden}
|
||||||
|
onChange={this.onToggleHidden}
|
||||||
|
/>
|
||||||
|
</Stack.Item>
|
||||||
|
</Stack>
|
||||||
{!this.state.selectedSource.serviceRef && (
|
{!this.state.selectedSource.serviceRef && (
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<Stack.Item>
|
<Stack.Item>
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
deleteSource,
|
deleteSource,
|
||||||
SourceOpenTarget,
|
SourceOpenTarget,
|
||||||
deleteSources,
|
deleteSources,
|
||||||
|
toggleSourceHidden,
|
||||||
} from "../../scripts/models/source"
|
} from "../../scripts/models/source"
|
||||||
import { importOPML, exportOPML } from "../../scripts/models/group"
|
import { importOPML, exportOPML } from "../../scripts/models/group"
|
||||||
import { AppDispatch, validateFavicon } from "../../scripts/utils"
|
import { AppDispatch, validateFavicon } from "../../scripts/utils"
|
||||||
@ -67,6 +68,8 @@ const mapDispatchToProps = (dispatch: AppDispatch) => {
|
|||||||
dispatch(deleteSources(sources)),
|
dispatch(deleteSources(sources)),
|
||||||
importOPML: () => dispatch(importOPML()),
|
importOPML: () => dispatch(importOPML()),
|
||||||
exportOPML: () => dispatch(exportOPML()),
|
exportOPML: () => dispatch(exportOPML()),
|
||||||
|
toggleSourceHidden: (source: RSSSource) =>
|
||||||
|
dispatch(toggleSourceHidden(source)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import lf from "lovefield"
|
|||||||
import { RSSSource } from "./models/source"
|
import { RSSSource } from "./models/source"
|
||||||
import { RSSItem } from "./models/item"
|
import { RSSItem } from "./models/item"
|
||||||
|
|
||||||
const sdbSchema = lf.schema.create("sourcesDB", 2)
|
const sdbSchema = lf.schema.create("sourcesDB", 3)
|
||||||
sdbSchema
|
sdbSchema
|
||||||
.createTable("sources")
|
.createTable("sources")
|
||||||
.addColumn("sid", lf.Type.INTEGER)
|
.addColumn("sid", lf.Type.INTEGER)
|
||||||
@ -18,6 +18,7 @@ sdbSchema
|
|||||||
.addColumn("fetchFrequency", lf.Type.NUMBER)
|
.addColumn("fetchFrequency", lf.Type.NUMBER)
|
||||||
.addColumn("rules", lf.Type.OBJECT)
|
.addColumn("rules", lf.Type.OBJECT)
|
||||||
.addColumn("textDir", lf.Type.NUMBER)
|
.addColumn("textDir", lf.Type.NUMBER)
|
||||||
|
.addColumn("hidden", lf.Type.BOOLEAN)
|
||||||
.addNullable(["iconurl", "serviceRef", "rules"])
|
.addNullable(["iconurl", "serviceRef", "rules"])
|
||||||
.addIndex("idxURL", ["url"], true)
|
.addIndex("idxURL", ["url"], true)
|
||||||
|
|
||||||
@ -54,6 +55,9 @@ async function onUpgradeSourceDB(rawDb: lf.raw.BackStore) {
|
|||||||
if (version < 2) {
|
if (version < 2) {
|
||||||
await rawDb.addTableColumn("sources", "textDir", 0)
|
await rawDb.addTableColumn("sources", "textDir", 0)
|
||||||
}
|
}
|
||||||
|
if (version < 3) {
|
||||||
|
await rawDb.addTableColumn("sources", "hidden", false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function init() {
|
export async function init() {
|
||||||
@ -99,6 +103,7 @@ async function migrateNeDB() {
|
|||||||
delete doc._id
|
delete doc._id
|
||||||
if (!doc.fetchFrequency) doc.fetchFrequency = 0
|
if (!doc.fetchFrequency) doc.fetchFrequency = 0
|
||||||
doc.textDir = 0
|
doc.textDir = 0
|
||||||
|
doc.hidden = false
|
||||||
return sources.createRow(doc)
|
return sources.createRow(doc)
|
||||||
})
|
})
|
||||||
const iRows = itemDocs.map(doc => {
|
const iRows = itemDocs.map(doc => {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"openExternal": "Extern öffnen",
|
"openExternal": "Extern öffnen",
|
||||||
"emptyName": "Dieses Feld darf nicht leer sein.",
|
"emptyName": "Dieses Feld darf nicht leer sein.",
|
||||||
"emptyField": "Dieses Feld darf nicht leer sein.",
|
"emptyField": "Dieses Feld darf nicht leer sein.",
|
||||||
"edit": "Berarbeiten",
|
"edit": "Bearbeiten",
|
||||||
"delete": "Entfernen",
|
"delete": "Entfernen",
|
||||||
"followSystem": "Systemstandard verwenden",
|
"followSystem": "Systemstandard verwenden",
|
||||||
"more": "Mehr",
|
"more": "Mehr",
|
||||||
@ -62,7 +62,7 @@
|
|||||||
"markBelow": "Darunter als gelesen markieren",
|
"markBelow": "Darunter als gelesen markieren",
|
||||||
"star": "Favorisieren",
|
"star": "Favorisieren",
|
||||||
"unstar": "Favorit entfernen",
|
"unstar": "Favorit entfernen",
|
||||||
"fontSize": "Schritgröße",
|
"fontSize": "Schriftgröße",
|
||||||
"loadWebpage": "Internetseite laden",
|
"loadWebpage": "Internetseite laden",
|
||||||
"loadFull": "Kompletten Inhalt laden",
|
"loadFull": "Kompletten Inhalt laden",
|
||||||
"notify": "Benachrichtigen, wenn im Hintergrund geladen",
|
"notify": "Benachrichtigen, wenn im Hintergrund geladen",
|
||||||
@ -201,7 +201,7 @@
|
|||||||
"fetchLimitNum": "{count} Artikel",
|
"fetchLimitNum": "{count} Artikel",
|
||||||
"importGroups": "Gruppen importieren",
|
"importGroups": "Gruppen importieren",
|
||||||
"failure": "Fehler beim Verbinden zum Server",
|
"failure": "Fehler beim Verbinden zum Server",
|
||||||
"failureHint": "Bite überprüfe die Server-Konfiguration oder deinen Netzwerk-Status.",
|
"failureHint": "Bitte überprüfe die Server-Konfiguration oder deinen Netzwerk-Status.",
|
||||||
"fetchUnlimited": "Unbegrenzt (nicht empfohlen)",
|
"fetchUnlimited": "Unbegrenzt (nicht empfohlen)",
|
||||||
"exportToLite": "Für Fluent Reader Lite exportieren"
|
"exportToLite": "Für Fluent Reader Lite exportieren"
|
||||||
},
|
},
|
||||||
|
@ -149,7 +149,8 @@
|
|||||||
"badUrl": "Invalid URL",
|
"badUrl": "Invalid URL",
|
||||||
"deleteWarning": "The source and all saved articles will be removed.",
|
"deleteWarning": "The source and all saved articles will be removed.",
|
||||||
"selected": "Selected source",
|
"selected": "Selected source",
|
||||||
"selectedMulti": "Selected multiple sources"
|
"selectedMulti": "Selected multiple sources",
|
||||||
|
"hidden": "Hide in \"all articles\""
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
"exist": "This group already exists.",
|
"exist": "This group already exists.",
|
||||||
|
@ -147,7 +147,8 @@
|
|||||||
"badUrl": "请正确输入URL",
|
"badUrl": "请正确输入URL",
|
||||||
"deleteWarning": "这将移除订阅源与所有已保存的文章",
|
"deleteWarning": "这将移除订阅源与所有已保存的文章",
|
||||||
"selected": "选中订阅源",
|
"selected": "选中订阅源",
|
||||||
"selectedMulti": "选中多个订阅源"
|
"selectedMulti": "选中多个订阅源",
|
||||||
|
"hidden": "从“全部文章”中隐藏"
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
"exist": "该分组已存在",
|
"exist": "该分组已存在",
|
||||||
|
@ -147,7 +147,8 @@
|
|||||||
"badUrl": "請正確輸入URL",
|
"badUrl": "請正確輸入URL",
|
||||||
"deleteWarning": "這將移除訂閱源與所有已儲存的文章",
|
"deleteWarning": "這將移除訂閱源與所有已儲存的文章",
|
||||||
"selected": "選中訂閱源",
|
"selected": "選中訂閱源",
|
||||||
"selectedMulti": "選中多個訂閱源"
|
"selectedMulti": "選中多個訂閱源",
|
||||||
|
"hidden": "從“全部文章”中隱藏"
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
"exist": "該分組已存在",
|
"exist": "該分組已存在",
|
||||||
|
@ -5,6 +5,8 @@ import {
|
|||||||
INIT_SOURCES,
|
INIT_SOURCES,
|
||||||
ADD_SOURCE,
|
ADD_SOURCE,
|
||||||
DELETE_SOURCE,
|
DELETE_SOURCE,
|
||||||
|
UNHIDE_SOURCE,
|
||||||
|
HIDE_SOURCE,
|
||||||
} from "./source"
|
} from "./source"
|
||||||
import {
|
import {
|
||||||
ItemActionTypes,
|
ItemActionTypes,
|
||||||
@ -316,13 +318,16 @@ export function feedReducer(
|
|||||||
...state,
|
...state,
|
||||||
[ALL]: new RSSFeed(
|
[ALL]: new RSSFeed(
|
||||||
ALL,
|
ALL,
|
||||||
Object.values(action.sources).map(s => s.sid)
|
Object.values(action.sources)
|
||||||
|
.filter(s => !s.hidden)
|
||||||
|
.map(s => s.sid)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
case ADD_SOURCE:
|
case ADD_SOURCE:
|
||||||
|
case UNHIDE_SOURCE:
|
||||||
switch (action.status) {
|
switch (action.status) {
|
||||||
case ActionStatus.Success:
|
case ActionStatus.Success:
|
||||||
return {
|
return {
|
||||||
@ -336,7 +341,8 @@ export function feedReducer(
|
|||||||
default:
|
default:
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
case DELETE_SOURCE: {
|
case DELETE_SOURCE:
|
||||||
|
case HIDE_SOURCE: {
|
||||||
let nextState = {}
|
let nextState = {}
|
||||||
for (let [id, feed] of Object.entries(state)) {
|
for (let [id, feed] of Object.entries(state)) {
|
||||||
nextState[id] = new RSSFeed(
|
nextState[id] = new RSSFeed(
|
||||||
|
@ -41,6 +41,7 @@ export class RSSSource {
|
|||||||
fetchFrequency: number // in minutes
|
fetchFrequency: number // in minutes
|
||||||
rules?: SourceRule[]
|
rules?: SourceRule[]
|
||||||
textDir: SourceTextDirection
|
textDir: SourceTextDirection
|
||||||
|
hidden: boolean
|
||||||
|
|
||||||
constructor(url: string, name: string = null) {
|
constructor(url: string, name: string = null) {
|
||||||
this.url = url
|
this.url = url
|
||||||
@ -49,6 +50,7 @@ export class RSSSource {
|
|||||||
this.lastFetched = new Date()
|
this.lastFetched = new Date()
|
||||||
this.fetchFrequency = 0
|
this.fetchFrequency = 0
|
||||||
this.textDir = SourceTextDirection.LTR
|
this.textDir = SourceTextDirection.LTR
|
||||||
|
this.hidden = false
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fetchMetaData(source: RSSSource) {
|
static async fetchMetaData(source: RSSSource) {
|
||||||
@ -120,6 +122,8 @@ export const ADD_SOURCE = "ADD_SOURCE"
|
|||||||
export const UPDATE_SOURCE = "UPDATE_SOURCE"
|
export const UPDATE_SOURCE = "UPDATE_SOURCE"
|
||||||
export const UPDATE_UNREAD_COUNTS = "UPDATE_UNREAD_COUNTS"
|
export const UPDATE_UNREAD_COUNTS = "UPDATE_UNREAD_COUNTS"
|
||||||
export const DELETE_SOURCE = "DELETE_SOURCE"
|
export const DELETE_SOURCE = "DELETE_SOURCE"
|
||||||
|
export const HIDE_SOURCE = "HIDE_SOURCE"
|
||||||
|
export const UNHIDE_SOURCE = "UNHIDE_SOURCE"
|
||||||
|
|
||||||
interface InitSourcesAction {
|
interface InitSourcesAction {
|
||||||
type: typeof INIT_SOURCES
|
type: typeof INIT_SOURCES
|
||||||
@ -151,12 +155,19 @@ interface DeleteSourceAction {
|
|||||||
source: RSSSource
|
source: RSSSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ToggleSourceHiddenAction {
|
||||||
|
type: typeof HIDE_SOURCE | typeof UNHIDE_SOURCE
|
||||||
|
status: ActionStatus
|
||||||
|
source: RSSSource
|
||||||
|
}
|
||||||
|
|
||||||
export type SourceActionTypes =
|
export type SourceActionTypes =
|
||||||
| InitSourcesAction
|
| InitSourcesAction
|
||||||
| AddSourceAction
|
| AddSourceAction
|
||||||
| UpdateSourceAction
|
| UpdateSourceAction
|
||||||
| UpdateUnreadCountsAction
|
| UpdateUnreadCountsAction
|
||||||
| DeleteSourceAction
|
| DeleteSourceAction
|
||||||
|
| ToggleSourceHiddenAction
|
||||||
|
|
||||||
export function initSourcesRequest(): SourceActionTypes {
|
export function initSourcesRequest(): SourceActionTypes {
|
||||||
return {
|
return {
|
||||||
@ -382,6 +393,19 @@ export function deleteSources(sources: RSSSource[]): AppThunk<Promise<void>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toggleSourceHidden(source: RSSSource): AppThunk<Promise<void>> {
|
||||||
|
return async (dispatch, getState) => {
|
||||||
|
const sourceCopy: RSSSource = { ...getState().sources[source.sid] }
|
||||||
|
sourceCopy.hidden = !sourceCopy.hidden
|
||||||
|
dispatch({
|
||||||
|
type: sourceCopy.hidden ? HIDE_SOURCE : UNHIDE_SOURCE,
|
||||||
|
status: ActionStatus.Success,
|
||||||
|
source: sourceCopy,
|
||||||
|
})
|
||||||
|
await dispatch(updateSource(sourceCopy))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function updateFavicon(
|
export function updateFavicon(
|
||||||
sids?: number[],
|
sids?: number[],
|
||||||
force = false
|
force = false
|
||||||
|
@ -146,6 +146,7 @@ export async function importAll() {
|
|||||||
const sRows = configs.lovefield.sources.map(s => {
|
const sRows = configs.lovefield.sources.map(s => {
|
||||||
s.lastFetched = new Date(s.lastFetched)
|
s.lastFetched = new Date(s.lastFetched)
|
||||||
if (!s.textDir) s.textDir = SourceTextDirection.LTR
|
if (!s.textDir) s.textDir = SourceTextDirection.LTR
|
||||||
|
if (!s.hidden) s.hidden = false
|
||||||
return db.sources.createRow(s)
|
return db.sources.createRow(s)
|
||||||
})
|
})
|
||||||
const iRows = configs.lovefield.items.map(i => {
|
const iRows = configs.lovefield.items.map(i => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user