import OPML

This commit is contained in:
Bruce Liu 2020-06-02 21:31:21 +08:00
parent c92b195cb8
commit b6164c5af3
4 changed files with 52 additions and 30 deletions

View File

@ -3,7 +3,8 @@ import { connect } from "react-redux"
import { createSelector } from "reselect"
import { RootState } from "../../scripts/reducer"
import GroupsTab from "../../components/settings/groups"
import { createSourceGroup, SourceGroup, updateSourceGroup, addSourceToGroup, deleteSourceGroup, removeSourceFromGroup, reorderSourceGroups } from "../../scripts/models/page"
import { createSourceGroup, SourceGroup, updateSourceGroup, addSourceToGroup,
deleteSourceGroup, removeSourceFromGroup, reorderSourceGroups } from "../../scripts/models/page"
const getSources = (state: RootState) => state.sources
const getGroups = (state: RootState) => state.page.sourceGroups

View File

@ -157,7 +157,7 @@ export function appReducer(
fetchingItems: false,
settings: {
...state.settings,
saving: false
saving: action.batch
}
}
}

View File

@ -1,7 +1,7 @@
import { RSSSource, SourceActionTypes, INIT_SOURCES, ADD_SOURCE, DELETE_SOURCE, addSource } from "./source"
import fs = require("fs")
import { SourceActionTypes, ADD_SOURCE, DELETE_SOURCE, addSource } from "./source"
import { ALL, SOURCE } from "./feed"
import { ActionStatus, AppThunk, domParser, AppDispatch } from "../utils"
import fs = require("fs")
import { saveSettings } from "./app"
const GROUPS_STORE_KEY = "sourceGroups"
@ -204,11 +204,14 @@ export function reorderSourceGroups(groups: SourceGroup[]): AppThunk {
}
async function outlineToSource(dispatch: AppDispatch, outline: Element): Promise<number> {
let url = outline.getAttribute("xmlUrl").trim()
let url = outline.getAttribute("xmlUrl")
let name = outline.getAttribute("text") || outline.getAttribute("name")
if (url) {
let sid = await dispatch(addSource(url, name))
return sid || null
try {
return await dispatch(addSource(url.trim(), name, true))
} catch (e) {
return null
}
} else {
return null
}
@ -221,9 +224,13 @@ export function importOPML(path: string): AppThunk {
console.log(err)
} else {
dispatch(saveSettings())
let successes: number, failures: number
let doc = domParser.parseFromString(data, "text/xml")
for (let el of doc.body.children) {
let successes: number = 0, failures: number = 0
let doc = domParser.parseFromString(data, "text/xml").getElementsByTagName("body")
if (doc.length == 0) {
dispatch(saveSettings())
return
}
for (let el of doc[0].children) {
if (el.getAttribute("type") === "rss") {
let sid = await outlineToSource(dispatch, el)
if (sid === null) failures += 1
@ -231,14 +238,19 @@ export function importOPML(path: string): AppThunk {
} else if (el.hasAttribute("text") || el.hasAttribute("title")) {
let groupName = el.getAttribute("text") || el.getAttribute("title")
let gid = dispatch(createSourceGroup(groupName))
let sid = await outlineToSource(dispatch, el)
if (sid === null) failures += 1
else {
successes += 1
dispatch(addSourceToGroup(gid, sid))
for (let child of el.children) {
let sid = await outlineToSource(dispatch, child)
if (sid === null) {
failures += 1
} else {
successes += 1
dispatch(addSourceToGroup(gid, sid))
}
}
}
}
console.log(failures, successes)
dispatch(saveSettings())
}
})
}

View File

@ -22,17 +22,21 @@ export class RSSSource {
async fetchMetaData(parser: Parser) {
let feed = await parser.parseURL(this.url)
if (!this.name) this.name = feed.title.trim()
if (!this.name && feed.title) this.name = feed.title.trim()
this.description = feed.description
let domain = this.url.split("/").slice(0, 3).join("/")
let f = await faviconPromise(domain)
if (f === null) f = domain + "/favicon.ico"
let result = await fetch(f)
if (result.status == 200 && result.headers.has("Content-Type")
&& result.headers.get("Content-Type").startsWith("image")) {
this.iconurl = f
let f: string = null
try {
f = await faviconPromise(domain)
} finally {
if (f === null) f = domain + "/favicon.ico"
let result = await fetch(f)
if (result.status == 200 && result.headers.has("Content-Type")
&& result.headers.get("Content-Type").startsWith("image")) {
this.iconurl = f
}
return feed
}
return feed
}
private static checkItem(source: RSSSource, item: Parser.Item, db: Nedb<RSSItem>): Promise<RSSItem> {
@ -92,6 +96,7 @@ interface InitSourcesAction {
interface AddSourceAction {
type: typeof ADD_SOURCE
status: ActionStatus
batch: boolean
source?: RSSSource
err?
}
@ -148,34 +153,37 @@ export function initSources(): AppThunk<Promise<void>> {
}
}
export function addSourceRequest(): SourceActionTypes {
export function addSourceRequest(batch: boolean): SourceActionTypes {
return {
type: ADD_SOURCE,
batch: batch,
status: ActionStatus.Request
}
}
export function addSourceSuccess(source: RSSSource): SourceActionTypes {
export function addSourceSuccess(source: RSSSource, batch: boolean): SourceActionTypes {
return {
type: ADD_SOURCE,
batch: batch,
status: ActionStatus.Success,
source: source
}
}
export function addSourceFailure(err): SourceActionTypes {
export function addSourceFailure(err, batch: boolean): SourceActionTypes {
return {
type: ADD_SOURCE,
batch: batch,
status: ActionStatus.Failure,
err: err
}
}
export function addSource(url: string, name: string = null): AppThunk<Promise<void|number>> {
export function addSource(url: string, name: string = null, batch = false): AppThunk<Promise<number>> {
return (dispatch, getState) => {
let app = getState().app
if (app.sourceInit && !app.fetchingItems) {
dispatch(addSourceRequest())
dispatch(addSourceRequest(batch))
let source = new RSSSource(url, name)
return source.fetchMetaData(rssParser)
.then(feed => {
@ -186,7 +194,7 @@ export function addSource(url: string, name: string = null): AppThunk<Promise<vo
if (err) {
reject(err)
} else {
dispatch(addSourceSuccess(source))
dispatch(addSourceSuccess(source, batch))
RSSSource.checkItems(source, feed.items, db.idb)
.then(items => insertItems(items))
.then(items => {
@ -200,7 +208,8 @@ export function addSource(url: string, name: string = null): AppThunk<Promise<vo
})
.catch(e => {
console.log(e)
dispatch(addSourceFailure(e))
dispatch(addSourceFailure(e, batch))
return new Promise((_, reject) => { reject(e) })
})
}
return new Promise((_, reject) => { reject("Sources not initialized or fetching items.") })