mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-02-09 08:18:38 +01:00
add sources through UI
This commit is contained in:
parent
b308df349c
commit
324aaee5f2
@ -5,7 +5,8 @@ import { SourceState, RSSSource } from "../../scripts/models/source"
|
||||
import { urlTest } from "../../scripts/utils"
|
||||
|
||||
type SourcesTabProps = SourcesTabReduxProps & {
|
||||
sources: SourceState
|
||||
sources: SourceState,
|
||||
addSource: (url: string) => void
|
||||
}
|
||||
|
||||
type SourcesTabState = {
|
||||
@ -79,7 +80,10 @@ class SourcesTab extends React.Component<SourcesTabProps, SourcesTabState> {
|
||||
onChange={this.handleInputChange} />
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<PrimaryButton disabled={!urlTest(this.state.newUrl)} text="添加" />
|
||||
<PrimaryButton
|
||||
disabled={!urlTest(this.state.newUrl)}
|
||||
onClick={() => this.props.addSource(this.state.newUrl)}
|
||||
text="添加" />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { connect } from "react-redux"
|
||||
import { createSelector } from "reselect"
|
||||
import { RootState } from "../../scripts/reducer"
|
||||
import SourcesTab from "../../components/settings/sources"
|
||||
import { addSource } from "../../scripts/models/source"
|
||||
|
||||
const getSources = (state: RootState) => state.sources
|
||||
|
||||
@ -14,8 +15,8 @@ const mapStateToProps = createSelector(
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
|
||||
}
|
||||
addSource: (url: string) => dispatch(addSource(url))
|
||||
}
|
||||
}
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { RSSSource, INIT_SOURCES, SourceActionTypes } from "./source"
|
||||
import { RSSSource, INIT_SOURCES, SourceActionTypes, ADD_SOURCE } from "./source"
|
||||
import { RSSItem, ItemActionTypes, FETCH_ITEMS, fetchItems } from "./item"
|
||||
import { ActionStatus, AppThunk } from "../utils"
|
||||
import { INIT_FEEDS, FeedActionTypes, ALL, initFeeds } from "./feed"
|
||||
@ -117,11 +117,9 @@ export function exitSettings(): AppThunk {
|
||||
if (getState().app.settings.changed) {
|
||||
dispatch(saveSettings())
|
||||
dispatch(selectAllArticles(true))
|
||||
dispatch(fetchItems())
|
||||
.then(() =>{
|
||||
dispatch(initFeeds(true)).then(() =>
|
||||
dispatch(toggleSettings())
|
||||
dispatch(initFeeds(true))
|
||||
})
|
||||
)
|
||||
} else {
|
||||
dispatch(toggleSettings())
|
||||
}
|
||||
@ -143,6 +141,26 @@ export function appReducer(
|
||||
}
|
||||
default: return state
|
||||
}
|
||||
case ADD_SOURCE:
|
||||
switch (action.status) {
|
||||
case ActionStatus.Request: return {
|
||||
...state,
|
||||
fetchingItems: true,
|
||||
settings: {
|
||||
...state.settings,
|
||||
changed: true,
|
||||
saving: true
|
||||
}
|
||||
}
|
||||
default: return {
|
||||
...state,
|
||||
fetchingItems: false,
|
||||
settings: {
|
||||
...state.settings,
|
||||
saving: false
|
||||
}
|
||||
}
|
||||
}
|
||||
case INIT_FEEDS:
|
||||
switch (action.status) {
|
||||
case ActionStatus.Request: return {
|
||||
|
@ -96,6 +96,25 @@ export function fetchItemsFailure(source: RSSSource, err): ItemActionTypes {
|
||||
}
|
||||
}
|
||||
|
||||
export function insertItems(items: RSSItem[]): Promise<RSSItem[]> {
|
||||
return new Promise<RSSItem[]>((resolve, reject) => {
|
||||
db.idb.count({}, (err, count) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
items.sort((a, b) => a.date.getTime() - b.date.getTime())
|
||||
for (let i of items) i.id = count++
|
||||
db.idb.insert(items, (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(items)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchItems(): AppThunk<Promise<void>> {
|
||||
return (dispatch, getState) => {
|
||||
let p = new Array<Promise<RSSItem[]>>()
|
||||
@ -105,30 +124,22 @@ export function fetchItems(): AppThunk<Promise<void>> {
|
||||
}
|
||||
dispatch(fetchItemsRequest())
|
||||
return Promise.allSettled(p).then(results => new Promise<void>((resolve, reject) => {
|
||||
db.idb.count({}, (err, count) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
let items = new Array<RSSItem>()
|
||||
results.map((r, i) => {
|
||||
if (r.status === "fulfilled") items.push(...r.value)
|
||||
else {
|
||||
console.log(r.reason)
|
||||
dispatch(fetchItemsFailure(getState().sources[i], r.reason))
|
||||
}
|
||||
let items = new Array<RSSItem>()
|
||||
results.map((r, i) => {
|
||||
if (r.status === "fulfilled") items.push(...r.value)
|
||||
else {
|
||||
console.log(r.reason)
|
||||
dispatch(fetchItemsFailure(getState().sources[i], r.reason))
|
||||
}
|
||||
})
|
||||
items.sort((a, b) => a.date.getTime() - b.date.getTime())
|
||||
for (let i of items) i.id = count++
|
||||
db.idb.insert(items, (err) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
} else {
|
||||
dispatch(fetchItemsSuccess(items.reverse()))
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
insertItems(items)
|
||||
.then(() => {
|
||||
dispatch(fetchItemsSuccess(items.reverse()))
|
||||
resolve()
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ export function selectSources(sids: number[], menuKey: string, title: string) {
|
||||
|
||||
export class PageState {
|
||||
feedId = ALL
|
||||
sourceGroups = new Array<SourceGroup>()
|
||||
sourceGroups = SourceGroup.load()
|
||||
}
|
||||
|
||||
|
||||
@ -75,14 +75,6 @@ export function pageReducer(
|
||||
action: PageActionTypes | SourceActionTypes
|
||||
): PageState {
|
||||
switch(action.type) {
|
||||
case INIT_SOURCES:
|
||||
switch (action.status) {
|
||||
case ActionStatus.Success: return {
|
||||
...state,
|
||||
sourceGroups: [new SourceGroup(action.sources, "中文")]
|
||||
}
|
||||
default: return state
|
||||
}
|
||||
case ADD_SOURCE:
|
||||
switch (action.status) {
|
||||
case ActionStatus.Success: return {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import Parser = require("rss-parser")
|
||||
import * as db from "../db"
|
||||
import { rssParser, faviconPromise, ActionStatus, AppThunk } from "../utils"
|
||||
import { RSSItem } from "./item"
|
||||
import { RSSItem, fetchItemsSuccess, insertItems } from "./item"
|
||||
import { SourceGroup } from "./page"
|
||||
|
||||
export class RSSSource {
|
||||
sid: number
|
||||
@ -25,11 +26,15 @@ export class RSSSource {
|
||||
if (this.iconurl === null) {
|
||||
let f = domain + "/favicon.ico"
|
||||
let result = await fetch(f)
|
||||
if (result.status == 200) this.iconurl = f
|
||||
if (result.status == 200 && result.headers.has("Content-Type")
|
||||
&& result.headers.get("Content-Type") === "image/x-icon") {
|
||||
this.iconurl = f
|
||||
}
|
||||
}
|
||||
return feed
|
||||
}
|
||||
|
||||
private static checkItem(source:RSSSource, item: Parser.Item, db: Nedb<RSSItem>): Promise<RSSItem> {
|
||||
private static checkItem(source: RSSSource, item: Parser.Item, db: Nedb<RSSItem>): Promise<RSSItem> {
|
||||
return new Promise<RSSItem>((resolve, reject) => {
|
||||
let i = new RSSItem(item, source)
|
||||
db.findOne({
|
||||
@ -49,30 +54,32 @@ export class RSSSource {
|
||||
})
|
||||
}
|
||||
|
||||
static fetchItems(source:RSSSource, parser: Parser, db: Nedb<RSSItem>): Promise<RSSItem[]> {
|
||||
static checkItems(source: RSSSource, items: Parser.Item[], db: Nedb<RSSItem>): Promise<RSSItem[]> {
|
||||
return new Promise<RSSItem[]>((resolve, reject) => {
|
||||
parser.parseURL(source.url)
|
||||
.then(feed => {
|
||||
let p = new Array<Promise<RSSItem>>()
|
||||
for (let item of feed.items) {
|
||||
p.push(this.checkItem(source, item, db))
|
||||
}
|
||||
Promise.all(p).then(values => {
|
||||
resolve(values.filter(v => v != null))
|
||||
}).catch(e => { reject(e) })
|
||||
})
|
||||
.catch(e => { reject(e) })
|
||||
let p = new Array<Promise<RSSItem>>()
|
||||
for (let item of items) {
|
||||
p.push(this.checkItem(source, item, db))
|
||||
}
|
||||
Promise.all(p).then(values => {
|
||||
resolve(values.filter(v => v != null))
|
||||
}).catch(e => { reject(e) })
|
||||
})
|
||||
}
|
||||
|
||||
static async fetchItems(source: RSSSource, parser: Parser, db: Nedb<RSSItem>) {
|
||||
let feed = await parser.parseURL(source.url)
|
||||
return await this.checkItems(source, feed.items, db)
|
||||
}
|
||||
}
|
||||
|
||||
export type SourceState = {
|
||||
[sid: number]: RSSSource
|
||||
}
|
||||
|
||||
export const INIT_SOURCES = 'INIT_SOURCES'
|
||||
export const ADD_SOURCE = 'ADD_SOURCE'
|
||||
export const UPDATE_SOURCE = 'UPDATE_SOURCE'
|
||||
export const INIT_SOURCES = "INIT_SOURCES"
|
||||
export const ADD_SOURCE = "ADD_SOURCE"
|
||||
export const UPDATE_SOURCE = "UPDATE_SOURCE"
|
||||
export const DELETE_SOURCE = "DELETE_SOURCE"
|
||||
|
||||
interface InitSourcesAction {
|
||||
type: typeof INIT_SOURCES
|
||||
@ -162,11 +169,12 @@ export function addSourceFailure(err): SourceActionTypes {
|
||||
|
||||
export function addSource(url: string): AppThunk<Promise<void>> {
|
||||
return (dispatch, getState) => {
|
||||
if (getState().app.sourceInit) {
|
||||
let app = getState().app
|
||||
if (app.sourceInit && !app.fetchingItems) {
|
||||
dispatch(addSourceRequest())
|
||||
let source = new RSSSource(url)
|
||||
return source.fetchMetaData(rssParser)
|
||||
.then(() => {
|
||||
.then(feed => {
|
||||
let sids = Object.values(getState().sources).map(s=>s.sid)
|
||||
source.sid = Math.max(...sids, -1) + 1
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
@ -177,10 +185,17 @@ export function addSource(url: string): AppThunk<Promise<void>> {
|
||||
reject(err)
|
||||
} else {
|
||||
dispatch(addSourceSuccess(source))
|
||||
/* dispatch(fetchItems()).then(() => {
|
||||
dispatch(initFeeds())
|
||||
}) */
|
||||
resolve()
|
||||
RSSSource.checkItems(source, feed.items, db.idb)
|
||||
.then(items => insertItems(items))
|
||||
.then(items => {
|
||||
dispatch(fetchItemsSuccess(items))
|
||||
SourceGroup.save(getState().page.sourceGroups)
|
||||
resolve()
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
reject(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -190,7 +205,7 @@ export function addSource(url: string): AppThunk<Promise<void>> {
|
||||
dispatch(addSourceFailure(e))
|
||||
})
|
||||
}
|
||||
return new Promise((_, reject) => { reject("Need to init sources before adding.") })
|
||||
return new Promise((_, reject) => { reject("Sources not initialized or fetching items.") })
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user