alpha release build 0.3.1

Version 0.3.1
This commit is contained in:
Haoyuan Liu 2020-06-18 14:22:42 +08:00 committed by GitHub
commit 331a07fb19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 95 additions and 53 deletions

1
dist/styles.css vendored
View File

@ -560,6 +560,7 @@ img.favicon {
align-items: center;
color: var(--neutralSecondary);
font-size: 14px;
user-select: none;
}
.info {

View File

@ -1,6 +1,6 @@
{
"name": "fluent-reader",
"version": "0.3.0",
"version": "0.3.1",
"description": "A simplistic, modern desktop RSS reader",
"main": "./dist/electron.js",
"scripts": {
@ -22,7 +22,10 @@
"output": "./bin/${platform}/${arch}/"
},
"win": {
"target": [ "nsis", "appx" ]
"target": [
"nsis",
"appx"
]
},
"appx": {
"applicationId": "FluentReader",
@ -43,7 +46,9 @@
"deleteAppDataOnUninstall": true
},
"mac": {
"target": [ "dmg" ]
"target": [
"dmg"
]
}
},
"devDependencies": {
@ -55,17 +60,14 @@
"@types/redux": "^3.6.0",
"@types/redux-thunk": "^2.1.0",
"@types/reselect": "^2.2.0",
"@yang991178/electron-proxy-agent": "^1.2.1",
"@yang991178/rss-parser": "^3.8.1",
"electron": "^9.0.4",
"electron-builder": "^22.7.0",
"electron-react-devtools": "^0.5.3",
"electron-store": "^5.2.0",
"electron-window-state": "^5.0.3",
"favicon": "0.0.2",
"html-webpack-plugin": "^4.3.0",
"nedb": "^1.8.0",
"pac-proxy-agent": "^4.1.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-intl-universal": "^2.2.5",
@ -74,7 +76,6 @@
"redux-devtools": "^3.5.0",
"redux-thunk": "^2.3.0",
"reselect": "^4.0.0",
"simplebar-react": "^2.2.0",
"ts-loader": "^7.0.4",
"typescript": "^3.9.2",
"webpack": "^4.43.0",

View File

@ -164,6 +164,9 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
text={intl.get("app.setPac")} />
</Stack.Item>
</Stack>
<span className="settings-hint up">
{intl.get("app.pacHint")}
</span>
</form>}
<Label>{intl.get("app.cleanup")}</Label>

View File

@ -55,7 +55,26 @@ function createWindow() {
mainWindow.loadFile((app.isPackaged ? "dist/" : "") + "index.html")
}
Menu.setApplicationMenu(null)
if (process.platform === 'darwin') {
const template = [
{
label: "Application",
submenu: [
{ label: "Quit", accelerator: "Command+Q", click: () => { app.quit() } }
]
},
{
label: "Edit",
submenu: [
{ label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
{ label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
]
}
]
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
} else {
Menu.setApplicationMenu(null)
}
app.on("ready", createWindow)
@ -66,6 +85,9 @@ app.on("second-instance", () => {
})
app.on("window-all-closed", function () {
if (mainWindow) {
mainWindow.webContents.session.clearStorageData({ storages: ["cookies"] })
}
mainWindow = null
if (restarting) {
init()

View File

@ -141,6 +141,7 @@
"enableProxy": "Enable Proxy",
"badUrl": "Invalid URL",
"pac": "PAC Address",
"setPac": "Set PAC"
"setPac": "Set PAC",
"pacHint": "For Socks proxies, it is recommended for PAC to return \"SOCKS5\" for proxy-side DNS. Turning off proxy requires restart."
}
}

View File

@ -141,6 +141,7 @@
"enableProxy": "启用代理",
"badUrl": "请正确输入URL",
"pac": "PAC地址",
"setPac": "设置PAC"
"setPac": "设置PAC",
"pacHint": "对于Socks代理建议PAC返回“SOCKS5”以启用代理端解析。关闭代理需重启应用后生效。"
}
}

View File

@ -163,7 +163,7 @@ export function fetchItems(): AppThunk<Promise<void>> {
if (r.status === "fulfilled") items.push(...r.value)
else {
console.log(r.reason)
dispatch(fetchItemsFailure(getState().sources[i], r.reason))
dispatch(fetchItemsFailure(sources[i], r.reason))
}
})
insertItems(items)

View File

@ -1,7 +1,7 @@
import Parser = require("@yang991178/rss-parser")
import intl = require("react-intl-universal")
import * as db from "../db"
import { rssParser, faviconPromise, ActionStatus, AppThunk } from "../utils"
import { rssParser, fetchFavicon, ActionStatus, AppThunk, parseRSS } from "../utils"
import { RSSItem, insertItems, ItemActionTypes, FETCH_ITEMS, MARK_READ, MARK_UNREAD, MARK_ALL_READ } from "./item"
import { SourceGroup } from "./group"
import { saveSettings } from "./app"
@ -29,21 +29,15 @@ export class RSSSource {
}
async fetchMetaData(parser: Parser) {
let feed = await parser.parseURL(this.url)
let feed = await parseRSS(this.url)
if (!this.name) {
if (feed.title) this.name = feed.title.trim()
this.name = this.name || intl.get("sources.untitled")
}
let domain = this.url.split("/").slice(0, 3).join("/")
let f: string = null
try {
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 = await fetchFavicon(domain)
if (f !== null) this.iconurl = f
} finally {
return feed
}
@ -82,7 +76,7 @@ export class RSSSource {
}
static async fetchItems(source: RSSSource, parser: Parser) {
let feed = await parser.parseURL(source.url)
let feed = await parseRSS(source.url)
db.sdb.update({ sid: source.sid }, { $set: { lastFetched: new Date() } })
return await this.checkItems(source, feed.items)
}

View File

@ -35,12 +35,11 @@ export function setProxy(address = null) {
} else {
store.set(PAC_STORE_KEY, address)
}
remote.getCurrentWebContents().session.setProxy({
pacScript: getProxyStatus() ? address : ""
})
remote.session.fromPartition("sandbox").setProxy({
pacScript: getProxyStatus() ? address : ""
})
if (getProxyStatus()) {
let rules = { pacScript: address }
remote.getCurrentWebContents().session.setProxy(rules)
remote.session.fromPartition("sandbox").setProxy(rules)
}
}
const VIEW_STORE_KEY = "view"

View File

@ -17,41 +17,61 @@ export type AppThunk<ReturnType = void> = ThunkAction<
export type AppDispatch = ThunkDispatch<RootState, undefined, AnyAction>
import Parser = require("@yang991178/rss-parser")
const customFields = {
item: ["thumb", "image", ["content:encoded", "fullContent"]] as Parser.CustomFieldItem[]
}
import ElectronProxyAgent = require("@yang991178/electron-proxy-agent")
import { ViewType } from "./models/page"
import { IPartialTheme } from "@fluentui/react"
import { SourceGroup } from "./models/group"
let agent = new ElectronProxyAgent(remote.getCurrentWebContents().session)
export const rssParser = new Parser({
customFields: customFields,
requestOptions: {
agent: agent
customFields: {
item: ["thumb", "image", ["content:encoded", "fullContent"]] as Parser.CustomFieldItem[]
}
})
export async function parseRSS(url: string) {
try {
let result = await fetch(url, { credentials: "omit" })
if (result.ok) {
return await rssParser.parseString(await result.text())
} else {
throw new Error(result.statusText)
}
} catch {
throw new Error("A network error has occured.")
}
}
export const domParser = new DOMParser()
const favicon = require("favicon")
export function faviconPromise(url: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
favicon(url, (err, icon: string) => {
if (err) reject(err)
else if (!icon) resolve(icon)
else {
let parts = icon.split("//")
resolve(parts[0] + "//" + parts[parts.length - 1])
import Url = require("url")
export async function fetchFavicon(url: string) {
try {
let result = await fetch(url, { credentials: "omit" })
if (result.ok) {
let html = await result.text()
let dom = domParser.parseFromString(html, "text/html")
let links = dom.getElementsByTagName("link")
for (let link of links) {
let rel = link.getAttribute("rel")
if ((rel === "icon" || rel === "shortcut icon") && link.hasAttribute("href")) {
let href = link.getAttribute("href")
let parsedUrl = Url.parse(url)
if (href.startsWith("//")) return parsedUrl.protocol + href
else if (href.startsWith("/")) return url + href
else return href
}
}
}
url = url + "/favicon.ico"
result = await fetch(url, { credentials: "omit" })
if (result.status == 200 && result.headers.has("Content-Type")
&& result.headers.get("Content-Type").startsWith("image")) {
return url
}
return null
} catch {
return null
}
})
})
}
export function htmlDecode(input: string) {
var doc = domParser.parseFromString(input, "text/html");
return doc.documentElement.textContent;
var doc = domParser.parseFromString(input, "text/html")
return doc.documentElement.textContent
}
export function openExternal(url: string) {