Skip to content

Commit 331a07f

Browse files
authoredJun 18, 2020
alpha release build 0.3.1
Version 0.3.1
2 parents bb8520f + c26564f commit 331a07f

File tree

10 files changed

+95
-53
lines changed

10 files changed

+95
-53
lines changed
 

‎dist/styles.css

+1
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ img.favicon {
560560
align-items: center;
561561
color: var(--neutralSecondary);
562562
font-size: 14px;
563+
user-select: none;
563564
}
564565

565566
.info {

‎package.json

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fluent-reader",
3-
"version": "0.3.0",
3+
"version": "0.3.1",
44
"description": "A simplistic, modern desktop RSS reader",
55
"main": "./dist/electron.js",
66
"scripts": {
@@ -22,7 +22,10 @@
2222
"output": "./bin/${platform}/${arch}/"
2323
},
2424
"win": {
25-
"target": [ "nsis", "appx" ]
25+
"target": [
26+
"nsis",
27+
"appx"
28+
]
2629
},
2730
"appx": {
2831
"applicationId": "FluentReader",
@@ -43,7 +46,9 @@
4346
"deleteAppDataOnUninstall": true
4447
},
4548
"mac": {
46-
"target": [ "dmg" ]
49+
"target": [
50+
"dmg"
51+
]
4752
}
4853
},
4954
"devDependencies": {
@@ -55,17 +60,14 @@
5560
"@types/redux": "^3.6.0",
5661
"@types/redux-thunk": "^2.1.0",
5762
"@types/reselect": "^2.2.0",
58-
"@yang991178/electron-proxy-agent": "^1.2.1",
5963
"@yang991178/rss-parser": "^3.8.1",
6064
"electron": "^9.0.4",
6165
"electron-builder": "^22.7.0",
6266
"electron-react-devtools": "^0.5.3",
6367
"electron-store": "^5.2.0",
6468
"electron-window-state": "^5.0.3",
65-
"favicon": "0.0.2",
6669
"html-webpack-plugin": "^4.3.0",
6770
"nedb": "^1.8.0",
68-
"pac-proxy-agent": "^4.1.0",
6971
"react": "^16.13.1",
7072
"react-dom": "^16.13.1",
7173
"react-intl-universal": "^2.2.5",
@@ -74,7 +76,6 @@
7476
"redux-devtools": "^3.5.0",
7577
"redux-thunk": "^2.3.0",
7678
"reselect": "^4.0.0",
77-
"simplebar-react": "^2.2.0",
7879
"ts-loader": "^7.0.4",
7980
"typescript": "^3.9.2",
8081
"webpack": "^4.43.0",

‎src/components/settings/app.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
164164
text={intl.get("app.setPac")} />
165165
</Stack.Item>
166166
</Stack>
167+
<span className="settings-hint up">
168+
{intl.get("app.pacHint")}
169+
</span>
167170
</form>}
168171

169172
<Label>{intl.get("app.cleanup")}</Label>

‎src/electron.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,26 @@ function createWindow() {
5555
mainWindow.loadFile((app.isPackaged ? "dist/" : "") + "index.html")
5656
}
5757

58-
Menu.setApplicationMenu(null)
58+
if (process.platform === 'darwin') {
59+
const template = [
60+
{
61+
label: "Application",
62+
submenu: [
63+
{ label: "Quit", accelerator: "Command+Q", click: () => { app.quit() } }
64+
]
65+
},
66+
{
67+
label: "Edit",
68+
submenu: [
69+
{ label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
70+
{ label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
71+
]
72+
}
73+
]
74+
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
75+
} else {
76+
Menu.setApplicationMenu(null)
77+
}
5978

6079
app.on("ready", createWindow)
6180

@@ -66,6 +85,9 @@ app.on("second-instance", () => {
6685
})
6786

6887
app.on("window-all-closed", function () {
88+
if (mainWindow) {
89+
mainWindow.webContents.session.clearStorageData({ storages: ["cookies"] })
90+
}
6991
mainWindow = null
7092
if (restarting) {
7193
init()

‎src/scripts/i18n/en-US.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@
141141
"enableProxy": "Enable Proxy",
142142
"badUrl": "Invalid URL",
143143
"pac": "PAC Address",
144-
"setPac": "Set PAC"
144+
"setPac": "Set PAC",
145+
"pacHint": "For Socks proxies, it is recommended for PAC to return \"SOCKS5\" for proxy-side DNS. Turning off proxy requires restart."
145146
}
146147
}

‎src/scripts/i18n/zh-CN.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@
141141
"enableProxy": "启用代理",
142142
"badUrl": "请正确输入URL",
143143
"pac": "PAC地址",
144-
"setPac": "设置PAC"
144+
"setPac": "设置PAC",
145+
"pacHint": "对于Socks代理建议PAC返回“SOCKS5”以启用代理端解析。关闭代理需重启应用后生效。"
145146
}
146147
}

‎src/scripts/models/item.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ export function fetchItems(): AppThunk<Promise<void>> {
163163
if (r.status === "fulfilled") items.push(...r.value)
164164
else {
165165
console.log(r.reason)
166-
dispatch(fetchItemsFailure(getState().sources[i], r.reason))
166+
dispatch(fetchItemsFailure(sources[i], r.reason))
167167
}
168168
})
169169
insertItems(items)

‎src/scripts/models/source.ts

+5-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Parser = require("@yang991178/rss-parser")
22
import intl = require("react-intl-universal")
33
import * as db from "../db"
4-
import { rssParser, faviconPromise, ActionStatus, AppThunk } from "../utils"
4+
import { rssParser, fetchFavicon, ActionStatus, AppThunk, parseRSS } from "../utils"
55
import { RSSItem, insertItems, ItemActionTypes, FETCH_ITEMS, MARK_READ, MARK_UNREAD, MARK_ALL_READ } from "./item"
66
import { SourceGroup } from "./group"
77
import { saveSettings } from "./app"
@@ -29,21 +29,15 @@ export class RSSSource {
2929
}
3030

3131
async fetchMetaData(parser: Parser) {
32-
let feed = await parser.parseURL(this.url)
32+
let feed = await parseRSS(this.url)
3333
if (!this.name) {
3434
if (feed.title) this.name = feed.title.trim()
3535
this.name = this.name || intl.get("sources.untitled")
3636
}
3737
let domain = this.url.split("/").slice(0, 3).join("/")
38-
let f: string = null
3938
try {
40-
f = await faviconPromise(domain)
41-
if (f === null) f = domain + "/favicon.ico"
42-
let result = await fetch(f)
43-
if (result.status == 200 && result.headers.has("Content-Type")
44-
&& result.headers.get("Content-Type").startsWith("image")) {
45-
this.iconurl = f
46-
}
39+
let f = await fetchFavicon(domain)
40+
if (f !== null) this.iconurl = f
4741
} finally {
4842
return feed
4943
}
@@ -82,7 +76,7 @@ export class RSSSource {
8276
}
8377

8478
static async fetchItems(source: RSSSource, parser: Parser) {
85-
let feed = await parser.parseURL(source.url)
79+
let feed = await parseRSS(source.url)
8680
db.sdb.update({ sid: source.sid }, { $set: { lastFetched: new Date() } })
8781
return await this.checkItems(source, feed.items)
8882
}

‎src/scripts/settings.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,11 @@ export function setProxy(address = null) {
3535
} else {
3636
store.set(PAC_STORE_KEY, address)
3737
}
38-
remote.getCurrentWebContents().session.setProxy({
39-
pacScript: getProxyStatus() ? address : ""
40-
})
41-
remote.session.fromPartition("sandbox").setProxy({
42-
pacScript: getProxyStatus() ? address : ""
43-
})
38+
if (getProxyStatus()) {
39+
let rules = { pacScript: address }
40+
remote.getCurrentWebContents().session.setProxy(rules)
41+
remote.session.fromPartition("sandbox").setProxy(rules)
42+
}
4443
}
4544

4645
const VIEW_STORE_KEY = "view"

‎src/scripts/utils.ts

+45-25
Original file line numberDiff line numberDiff line change
@@ -17,41 +17,61 @@ export type AppThunk<ReturnType = void> = ThunkAction<
1717
export type AppDispatch = ThunkDispatch<RootState, undefined, AnyAction>
1818

1919
import Parser = require("@yang991178/rss-parser")
20-
const customFields = {
21-
item: ["thumb", "image", ["content:encoded", "fullContent"]] as Parser.CustomFieldItem[]
22-
}
23-
24-
import ElectronProxyAgent = require("@yang991178/electron-proxy-agent")
25-
import { ViewType } from "./models/page"
26-
import { IPartialTheme } from "@fluentui/react"
27-
import { SourceGroup } from "./models/group"
28-
let agent = new ElectronProxyAgent(remote.getCurrentWebContents().session)
2920
export const rssParser = new Parser({
30-
customFields: customFields,
31-
requestOptions: {
32-
agent: agent
21+
customFields: {
22+
item: ["thumb", "image", ["content:encoded", "fullContent"]] as Parser.CustomFieldItem[]
3323
}
3424
})
3525

26+
export async function parseRSS(url: string) {
27+
try {
28+
let result = await fetch(url, { credentials: "omit" })
29+
if (result.ok) {
30+
return await rssParser.parseString(await result.text())
31+
} else {
32+
throw new Error(result.statusText)
33+
}
34+
} catch {
35+
throw new Error("A network error has occured.")
36+
}
37+
}
38+
3639
export const domParser = new DOMParser()
3740

38-
const favicon = require("favicon")
39-
export function faviconPromise(url: string): Promise<string> {
40-
return new Promise<string>((resolve, reject) => {
41-
favicon(url, (err, icon: string) => {
42-
if (err) reject(err)
43-
else if (!icon) resolve(icon)
44-
else {
45-
let parts = icon.split("//")
46-
resolve(parts[0] + "//" + parts[parts.length - 1])
41+
import Url = require("url")
42+
export async function fetchFavicon(url: string) {
43+
try {
44+
let result = await fetch(url, { credentials: "omit" })
45+
if (result.ok) {
46+
let html = await result.text()
47+
let dom = domParser.parseFromString(html, "text/html")
48+
let links = dom.getElementsByTagName("link")
49+
for (let link of links) {
50+
let rel = link.getAttribute("rel")
51+
if ((rel === "icon" || rel === "shortcut icon") && link.hasAttribute("href")) {
52+
let href = link.getAttribute("href")
53+
let parsedUrl = Url.parse(url)
54+
if (href.startsWith("//")) return parsedUrl.protocol + href
55+
else if (href.startsWith("/")) return url + href
56+
else return href
57+
}
4758
}
48-
})
49-
})
59+
}
60+
url = url + "/favicon.ico"
61+
result = await fetch(url, { credentials: "omit" })
62+
if (result.status == 200 && result.headers.has("Content-Type")
63+
&& result.headers.get("Content-Type").startsWith("image")) {
64+
return url
65+
}
66+
return null
67+
} catch {
68+
return null
69+
}
5070
}
5171

5272
export function htmlDecode(input: string) {
53-
var doc = domParser.parseFromString(input, "text/html");
54-
return doc.documentElement.textContent;
73+
var doc = domParser.parseFromString(input, "text/html")
74+
return doc.documentElement.textContent
5575
}
5676

5777
export function openExternal(url: string) {

0 commit comments

Comments
 (0)