mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-04-25 07:28:53 +02:00
commit
73930f3e69
@ -1,10 +1,3 @@
|
|||||||
# Build the MAS app
|
|
||||||
CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder -c electron-builder-mas.yml --mac mas:universal
|
|
||||||
# Add ElectronTeamID to Info.plist
|
|
||||||
sed -i '' -e 's/<\/dict>/<key>ElectronTeamID<\/key><string>EM8VE646TZ<\/string><\/dict>/g' "bin/darwin/universal/mas-universal/Fluent Reader.app/Contents/Info.plist"
|
|
||||||
|
|
||||||
printf "......................\nresignAndPackage start\n\n"
|
|
||||||
|
|
||||||
# Name of your app.
|
# Name of your app.
|
||||||
APP="Fluent Reader"
|
APP="Fluent Reader"
|
||||||
# Your Certificate name.
|
# Your Certificate name.
|
||||||
@ -21,6 +14,17 @@ PARENT_PLIST="build/entitlements.mas.plist"
|
|||||||
CHILD_PLIST="build/entitlements.mas.inherit.plist"
|
CHILD_PLIST="build/entitlements.mas.inherit.plist"
|
||||||
LOGINHELPER_PLIST="build/entitlements.mas.loginhelper.plist"
|
LOGINHELPER_PLIST="build/entitlements.mas.loginhelper.plist"
|
||||||
FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks"
|
FRAMEWORKS_PATH="$APP_PATH/Contents/Frameworks"
|
||||||
|
|
||||||
|
# Build universal binary for font-list
|
||||||
|
# FONTLIST_PATH="node_modules/font-list/libs/darwin/fontlist.m"
|
||||||
|
# clang -arch arm64 -arch x86_64 "$FONTLIST_PATH" -fmodules -o "dist/fontlist"
|
||||||
|
# Build the MAS app
|
||||||
|
CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder -c electron-builder-mas.yml --mac mas:universal
|
||||||
|
# Add ElectronTeamID to Info.plist
|
||||||
|
sed -i '' -e 's/<\/dict>/<key>ElectronTeamID<\/key><string>EM8VE646TZ<\/string><\/dict>/g' "bin/darwin/universal/mas-universal/Fluent Reader.app/Contents/Info.plist"
|
||||||
|
|
||||||
|
printf "......................\nresignAndPackage start\n\n"
|
||||||
|
codesign --deep --force --verify --verbose=4 --timestamp --options runtime --entitlements "$CHILD_PLIST" -s "$APP_KEY" "$APP_PATH/Contents/Resources/app.asar.unpacked/dist/fontlist"
|
||||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework"
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Electron Framework"
|
||||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib"
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib"
|
||||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib"
|
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib"
|
||||||
|
34
dist/article/article.css
vendored
34
dist/article/article.css
vendored
@ -2,14 +2,25 @@
|
|||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
|
margin: 0;
|
||||||
font-family: "Segoe UI", "Source Han Sans SC Regular", "Microsoft YaHei",
|
font-family: "Segoe UI", "Source Han Sans SC Regular", "Microsoft YaHei",
|
||||||
sans-serif;
|
sans-serif;
|
||||||
}
|
}
|
||||||
html {
|
body {
|
||||||
|
padding: 12px 96px 32px;
|
||||||
overflow: hidden scroll;
|
overflow: hidden scroll;
|
||||||
}
|
}
|
||||||
|
body.rtl {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
body.vertical {
|
||||||
|
padding: 32px;
|
||||||
|
padding-right: 96px;
|
||||||
|
writing-mode: vertical-rl;
|
||||||
|
overflow: scroll hidden;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
margin: 12px 96px 32px;
|
|
||||||
--gray: #484644;
|
--gray: #484644;
|
||||||
--primary: #0078d4;
|
--primary: #0078d4;
|
||||||
--primary-alt: #004578;
|
--primary-alt: #004578;
|
||||||
@ -58,6 +69,11 @@ a:active {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
body.vertical #main {
|
||||||
|
max-width: unset;
|
||||||
|
max-height: 700px;
|
||||||
|
margin: auto 0;
|
||||||
|
}
|
||||||
#main.show {
|
#main.show {
|
||||||
display: block;
|
display: block;
|
||||||
animation-name: fadeIn;
|
animation-name: fadeIn;
|
||||||
@ -80,12 +96,21 @@ a:active {
|
|||||||
article {
|
article {
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
body.vertical article {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
body.vertical article p {
|
||||||
|
text-indent: 2rem;
|
||||||
|
}
|
||||||
article * {
|
article * {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
article img {
|
article img {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
body.vertical article img {
|
||||||
|
max-height: 75%;
|
||||||
|
}
|
||||||
article figure {
|
article figure {
|
||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -103,6 +128,11 @@ article code {
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
article pre {
|
||||||
|
word-break: normal;
|
||||||
|
overflow-wrap: normal;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
article blockquote {
|
article blockquote {
|
||||||
border-left: 2px solid var(--gray);
|
border-left: 2px solid var(--gray);
|
||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
|
4
dist/article/article.html
vendored
4
dist/article/article.html
vendored
@ -3,14 +3,14 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta http-equiv="Content-Security-Policy"
|
<meta http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'none'; script-src-elem 'sha256-sLDWrq1tUAO8IyyqmUckFqxbXYfZ2/3TEUmtxH8Unf0=' 'sha256-9YXu4Ifpt+hDzuBhE+vFtXKt1ZRbo/CkuUY4VX4dZyE='; img-src http: https: data:; style-src 'self' 'unsafe-inline'; frame-src http: https:; media-src http: https:; connect-src https: http:">
|
content="default-src 'none'; script-src-elem 'sha256-sLDWrq1tUAO8IyyqmUckFqxbXYfZ2/3TEUmtxH8Unf0=' 'sha256-iOdZeo0zvgcSuiH/7/dXCOHo7s0cn2XtsidqVOcHBjo='; img-src http: https: data:; style-src 'self' 'unsafe-inline'; frame-src http: https:; media-src http: https:; connect-src https: http:">
|
||||||
<title>Article</title>
|
<title>Article</title>
|
||||||
<link rel="stylesheet" href="article.css" />
|
<link rel="stylesheet" href="article.css" />
|
||||||
<script integrity="sha256-sLDWrq1tUAO8IyyqmUckFqxbXYfZ2/3TEUmtxH8Unf0=" src="mercury.web.js"></script>
|
<script integrity="sha256-sLDWrq1tUAO8IyyqmUckFqxbXYfZ2/3TEUmtxH8Unf0=" src="mercury.web.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="main"></div>
|
<div id="main"></div>
|
||||||
<script integrity="sha256-9YXu4Ifpt+hDzuBhE+vFtXKt1ZRbo/CkuUY4VX4dZyE=" src="article.js"></script>
|
<script integrity="sha256-iOdZeo0zvgcSuiH/7/dXCOHo7s0cn2XtsidqVOcHBjo=" src="article.js"></script>
|
||||||
<!-- Run "cat article.js | openssl dgst -sha256 -binary | openssl enc -base64 -A" for hash -->
|
<!-- Run "cat article.js | openssl dgst -sha256 -binary | openssl enc -base64 -A" for hash -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
12
dist/article/article.js
vendored
12
dist/article/article.js
vendored
@ -2,6 +2,15 @@ function get(name) {
|
|||||||
if (name = (new RegExp('[?&]' + encodeURIComponent(name) + '=([^&]*)')).exec(location.search))
|
if (name = (new RegExp('[?&]' + encodeURIComponent(name) + '=([^&]*)')).exec(location.search))
|
||||||
return decodeURIComponent(name[1]);
|
return decodeURIComponent(name[1]);
|
||||||
}
|
}
|
||||||
|
let dir = get("d")
|
||||||
|
if (dir === "1") {
|
||||||
|
document.body.classList.add("rtl")
|
||||||
|
} else if (dir === "2") {
|
||||||
|
document.body.classList.add("vertical")
|
||||||
|
document.body.addEventListener("wheel", (evt) => {
|
||||||
|
document.scrollingElement.scrollLeft -= evt.deltaY;
|
||||||
|
});
|
||||||
|
}
|
||||||
async function getArticle(url) {
|
async function getArticle(url) {
|
||||||
let article = get("a")
|
let article = get("a")
|
||||||
if (get("m") === "1") {
|
if (get("m") === "1") {
|
||||||
@ -11,6 +20,8 @@ async function getArticle(url) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.documentElement.style.fontSize = get("s") + "px"
|
document.documentElement.style.fontSize = get("s") + "px"
|
||||||
|
let font = get("f")
|
||||||
|
if (font) document.body.style.fontFamily = `"${font}"`
|
||||||
let url = get("u")
|
let url = get("u")
|
||||||
getArticle(url).then(article => {
|
getArticle(url).then(article => {
|
||||||
let domParser = new DOMParser()
|
let domParser = new DOMParser()
|
||||||
@ -32,4 +43,3 @@ getArticle(url).then(article => {
|
|||||||
main.innerHTML = dom.body.innerHTML
|
main.innerHTML = dom.body.innerHTML
|
||||||
main.classList.add("show")
|
main.classList.add("show")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
BIN
dist/fontlist
vendored
Executable file
BIN
dist/fontlist
vendored
Executable file
Binary file not shown.
29
dist/fonts.vbs
vendored
Normal file
29
dist/fonts.vbs
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Option Explicit
|
||||||
|
|
||||||
|
Dim objShell, objFSO, objFile, objFolder
|
||||||
|
Dim objFolderItem, colItems, objFont
|
||||||
|
Dim strFileName
|
||||||
|
|
||||||
|
|
||||||
|
Const FONTS = &H14& ' Fonts Folder
|
||||||
|
|
||||||
|
' Instantiate Objects
|
||||||
|
Set objShell = CreateObject("Shell.Application")
|
||||||
|
Set objFolder = objShell.Namespace(FONTS)
|
||||||
|
Set objFolderItem = objFolder.Self
|
||||||
|
Set colItems = objFolder.Items
|
||||||
|
Set objFSO = CreateObject("Scripting.FileSystemObject")
|
||||||
|
|
||||||
|
For Each objFont in colItems
|
||||||
|
WScript.StdOut.WriteLine(objFont.Path & vbtab & objFont.Name)
|
||||||
|
Next
|
||||||
|
|
||||||
|
Set objShell = nothing
|
||||||
|
Set objFile = nothing
|
||||||
|
Set objFolder = nothing
|
||||||
|
Set objFolderItem = nothing
|
||||||
|
Set colItems = nothing
|
||||||
|
Set objFont = nothing
|
||||||
|
Set objFSO = nothing
|
||||||
|
|
||||||
|
wscript.quit
|
4
dist/styles/global.css
vendored
4
dist/styles/global.css
vendored
@ -138,6 +138,10 @@ i.ms-Nav-chevron {
|
|||||||
color: var(--neutralPrimary);
|
color: var(--neutralPrimary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ms-Callout-main {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
#root > nav {
|
#root > nav {
|
||||||
height: var(--navHeight);
|
height: var(--navHeight);
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
|
@ -4,7 +4,10 @@ productName: Fluent Reader
|
|||||||
copyright: Copyright © 2020 Haoyuan Liu
|
copyright: Copyright © 2020 Haoyuan Liu
|
||||||
files:
|
files:
|
||||||
- "./dist/**/*"
|
- "./dist/**/*"
|
||||||
|
- "!./dist/fonts.vbs"
|
||||||
- "!**/*.js.map"
|
- "!**/*.js.map"
|
||||||
|
asarUnpack:
|
||||||
|
- "./dist/fontlist"
|
||||||
directories:
|
directories:
|
||||||
output: "./bin/${platform}/${arch}/"
|
output: "./bin/${platform}/${arch}/"
|
||||||
mac:
|
mac:
|
||||||
@ -25,6 +28,7 @@ mac:
|
|||||||
- uk
|
- uk
|
||||||
- it
|
- it
|
||||||
- nl
|
- nl
|
||||||
|
- ko
|
||||||
minimumSystemVersion: 10.14.0
|
minimumSystemVersion: 10.14.0
|
||||||
mas:
|
mas:
|
||||||
entitlements: build/entitlements.mas.plist
|
entitlements: build/entitlements.mas.plist
|
||||||
|
@ -3,6 +3,7 @@ productName: Fluent Reader
|
|||||||
copyright: Copyright © 2020 Haoyuan Liu
|
copyright: Copyright © 2020 Haoyuan Liu
|
||||||
files:
|
files:
|
||||||
- "./dist/**/*"
|
- "./dist/**/*"
|
||||||
|
- "!./dist/fontlist"
|
||||||
- "!**/*.js.map"
|
- "!**/*.js.map"
|
||||||
directories:
|
directories:
|
||||||
output: "./bin/${platform}/${arch}/"
|
output: "./bin/${platform}/${arch}/"
|
||||||
@ -24,6 +25,7 @@ mac:
|
|||||||
- uk
|
- uk
|
||||||
- it
|
- it
|
||||||
- nl
|
- nl
|
||||||
|
- ko
|
||||||
win:
|
win:
|
||||||
target:
|
target:
|
||||||
- nsis
|
- nsis
|
||||||
@ -46,6 +48,7 @@ appx:
|
|||||||
- uk
|
- uk
|
||||||
- it
|
- it
|
||||||
- nl
|
- nl
|
||||||
|
- ko
|
||||||
showNameOnTiles: true
|
showNameOnTiles: true
|
||||||
setBuildNumber: true
|
setBuildNumber: true
|
||||||
nsis:
|
nsis:
|
||||||
|
1094
package-lock.json
generated
1094
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.0.2",
|
"version": "1.1.0",
|
||||||
"description": "Modern desktop RSS reader",
|
"description": "Modern desktop RSS reader",
|
||||||
"main": "./dist/electron.js",
|
"main": "./dist/electron.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -26,11 +26,12 @@
|
|||||||
"@types/react-dom": "^16.9.8",
|
"@types/react-dom": "^16.9.8",
|
||||||
"@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": "^13.1.4",
|
"electron": "^16.0.2",
|
||||||
"electron-builder": "^22.11.3",
|
"electron-builder": "^22.11.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",
|
||||||
|
"font-list": "^1.4.2",
|
||||||
"hard-source-webpack-plugin": "^0.13.1",
|
"hard-source-webpack-plugin": "^0.13.1",
|
||||||
"html-webpack-plugin": "^4.3.0",
|
"html-webpack-plugin": "^4.3.0",
|
||||||
"js-md5": "^0.7.3",
|
"js-md5": "^0.7.3",
|
||||||
|
@ -75,6 +75,13 @@ const settingsBridge = {
|
|||||||
ipcRenderer.invoke("set-font-size", size)
|
ipcRenderer.invoke("set-font-size", size)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getFont: (): string => {
|
||||||
|
return ipcRenderer.sendSync("get-font")
|
||||||
|
},
|
||||||
|
setFont: (font: string) => {
|
||||||
|
ipcRenderer.invoke("set-font", font)
|
||||||
|
},
|
||||||
|
|
||||||
getFetchInterval: (): number => {
|
getFetchInterval: (): number => {
|
||||||
return ipcRenderer.sendSync("get-fetch-interval")
|
return ipcRenderer.sendSync("get-fetch-interval")
|
||||||
},
|
},
|
||||||
|
@ -170,11 +170,16 @@ const utilsBridge = {
|
|||||||
destroyTouchBar: () => {
|
destroyTouchBar: () => {
|
||||||
ipcRenderer.invoke("touchbar-destroy")
|
ipcRenderer.invoke("touchbar-destroy")
|
||||||
},
|
},
|
||||||
|
|
||||||
|
initFontList: (): Promise<Array<string>> => {
|
||||||
|
return ipcRenderer.invoke("init-font-list")
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
utils: typeof utilsBridge
|
utils: typeof utilsBridge
|
||||||
|
fontList: Array<string>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,11 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
Link,
|
Link,
|
||||||
} from "@fluentui/react"
|
} from "@fluentui/react"
|
||||||
import { RSSSource, SourceOpenTarget } from "../scripts/models/source"
|
import {
|
||||||
|
RSSSource,
|
||||||
|
SourceOpenTarget,
|
||||||
|
SourceTextDirection,
|
||||||
|
} from "../scripts/models/source"
|
||||||
import { shareSubmenu } from "./context-menu"
|
import { shareSubmenu } from "./context-menu"
|
||||||
import { platformCtrl, decodeFetchResponse } from "../scripts/utils"
|
import { platformCtrl, decodeFetchResponse } from "../scripts/utils"
|
||||||
|
|
||||||
@ -31,9 +35,14 @@ type ArticleProps = {
|
|||||||
textMenu: (position: [number, number], text: string, url: string) => void
|
textMenu: (position: [number, number], text: string, url: string) => void
|
||||||
imageMenu: (position: [number, number]) => void
|
imageMenu: (position: [number, number]) => void
|
||||||
dismissContextMenu: () => void
|
dismissContextMenu: () => void
|
||||||
|
updateSourceTextDirection: (
|
||||||
|
source: RSSSource,
|
||||||
|
direction: SourceTextDirection
|
||||||
|
) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArticleState = {
|
type ArticleState = {
|
||||||
|
fontFamily: string
|
||||||
fontSize: number
|
fontSize: number
|
||||||
loadWebpage: boolean
|
loadWebpage: boolean
|
||||||
loadFull: boolean
|
loadFull: boolean
|
||||||
@ -49,7 +58,8 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
constructor(props: ArticleProps) {
|
constructor(props: ArticleProps) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
fontSize: this.getFontSize(),
|
fontFamily: window.settings.getFont(),
|
||||||
|
fontSize: window.settings.getFontSize(),
|
||||||
loadWebpage: props.source.openTarget === SourceOpenTarget.Webpage,
|
loadWebpage: props.source.openTarget === SourceOpenTarget.Webpage,
|
||||||
loadFull: props.source.openTarget === SourceOpenTarget.FullContent,
|
loadFull: props.source.openTarget === SourceOpenTarget.FullContent,
|
||||||
fullContent: "",
|
fullContent: "",
|
||||||
@ -64,15 +74,16 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
this.loadFull()
|
this.loadFull()
|
||||||
}
|
}
|
||||||
|
|
||||||
getFontSize = () => {
|
|
||||||
return window.settings.getFontSize()
|
|
||||||
}
|
|
||||||
setFontSize = (size: number) => {
|
setFontSize = (size: number) => {
|
||||||
window.settings.setFontSize(size)
|
window.settings.setFontSize(size)
|
||||||
this.setState({ fontSize: size })
|
this.setState({ fontSize: size })
|
||||||
}
|
}
|
||||||
|
setFont = (font: string) => {
|
||||||
|
window.settings.setFont(font)
|
||||||
|
this.setState({ fontFamily: font })
|
||||||
|
}
|
||||||
|
|
||||||
fontMenuProps = (): IContextualMenuProps => ({
|
fontSizeMenuProps = (): IContextualMenuProps => ({
|
||||||
items: FONT_SIZE_OPTIONS.map(size => ({
|
items: FONT_SIZE_OPTIONS.map(size => ({
|
||||||
key: String(size),
|
key: String(size),
|
||||||
text: String(size),
|
text: String(size),
|
||||||
@ -82,6 +93,53 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fontFamilyMenuProps = (): IContextualMenuProps => ({
|
||||||
|
items: window.fontList.map((font, idx) => ({
|
||||||
|
key: String(idx),
|
||||||
|
text: font === "" ? intl.get("default") : font,
|
||||||
|
canCheck: true,
|
||||||
|
checked: this.state.fontFamily === font,
|
||||||
|
onClick: () => this.setFont(font),
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
|
||||||
|
updateTextDirection = (direction: SourceTextDirection) => {
|
||||||
|
this.props.updateSourceTextDirection(this.props.source, direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
directionMenuProps = (): IContextualMenuProps => ({
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: "LTR",
|
||||||
|
text: intl.get("article.LTR"),
|
||||||
|
iconProps: { iconName: "Forward" },
|
||||||
|
canCheck: true,
|
||||||
|
checked: this.props.source.textDir === SourceTextDirection.LTR,
|
||||||
|
onClick: () =>
|
||||||
|
this.updateTextDirection(SourceTextDirection.LTR),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "RTL",
|
||||||
|
text: intl.get("article.RTL"),
|
||||||
|
iconProps: { iconName: "Back" },
|
||||||
|
canCheck: true,
|
||||||
|
checked: this.props.source.textDir === SourceTextDirection.RTL,
|
||||||
|
onClick: () =>
|
||||||
|
this.updateTextDirection(SourceTextDirection.RTL),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Vertical",
|
||||||
|
text: intl.get("article.Vertical"),
|
||||||
|
iconProps: { iconName: "Down" },
|
||||||
|
canCheck: true,
|
||||||
|
checked:
|
||||||
|
this.props.source.textDir === SourceTextDirection.Vertical,
|
||||||
|
onClick: () =>
|
||||||
|
this.updateTextDirection(SourceTextDirection.Vertical),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
moreMenuProps = (): IContextualMenuProps => ({
|
moreMenuProps = (): IContextualMenuProps => ({
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -117,10 +175,24 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "fontMenu",
|
key: "fontMenu",
|
||||||
|
text: intl.get("article.font"),
|
||||||
|
iconProps: { iconName: "Font" },
|
||||||
|
disabled: this.state.loadWebpage,
|
||||||
|
subMenuProps: this.fontFamilyMenuProps(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "fontSizeMenu",
|
||||||
text: intl.get("article.fontSize"),
|
text: intl.get("article.fontSize"),
|
||||||
iconProps: { iconName: "FontSize" },
|
iconProps: { iconName: "FontSize" },
|
||||||
disabled: this.state.loadWebpage,
|
disabled: this.state.loadWebpage,
|
||||||
subMenuProps: this.fontMenuProps(),
|
subMenuProps: this.fontSizeMenuProps(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "directionMenu",
|
||||||
|
text: intl.get("article.textDir"),
|
||||||
|
iconProps: { iconName: "ChangeEntitlements" },
|
||||||
|
disabled: this.state.loadWebpage,
|
||||||
|
subMenuProps: this.directionMenuProps(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "divider_1",
|
key: "divider_1",
|
||||||
@ -290,7 +362,9 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return `article/article.html?a=${a}&h=${h}&s=${this.state.fontSize}&u=${
|
return `article/article.html?a=${a}&h=${h}&f=${encodeURIComponent(
|
||||||
|
this.state.fontFamily
|
||||||
|
)}&s=${this.state.fontSize}&d=${this.props.source.textDir}&u=${
|
||||||
this.props.item.link
|
this.props.item.link
|
||||||
}&m=${this.state.loadFull ? 1 : 0}`
|
}&m=${this.state.loadFull ? 1 : 0}`
|
||||||
}
|
}
|
||||||
@ -400,7 +474,7 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
? this.props.item.link
|
? this.props.item.link
|
||||||
: this.articleView()
|
: this.articleView()
|
||||||
}
|
}
|
||||||
webpreferences="contextIsolation,disableDialogs,autoplayPolicy=document-user-activation-required"
|
webpreferences="contextIsolation,disableDialogs,autoplayPolicy=document-user-activation-required,nativeWindowOpen=false"
|
||||||
partition={this.state.loadWebpage ? "sandbox" : undefined}
|
partition={this.state.loadWebpage ? "sandbox" : undefined}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -20,7 +20,6 @@ import {
|
|||||||
DefaultButton,
|
DefaultButton,
|
||||||
ChoiceGroup,
|
ChoiceGroup,
|
||||||
IChoiceGroupOption,
|
IChoiceGroupOption,
|
||||||
loadTheme,
|
|
||||||
Dropdown,
|
Dropdown,
|
||||||
IDropdownOption,
|
IDropdownOption,
|
||||||
PrimaryButton,
|
PrimaryButton,
|
||||||
@ -140,6 +139,7 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
|
|||||||
{ key: "sv", text: "Svenska" },
|
{ key: "sv", text: "Svenska" },
|
||||||
{ key: "tr", text: "Türkçe" },
|
{ key: "tr", text: "Türkçe" },
|
||||||
{ key: "uk", text: "Українська" },
|
{ key: "uk", text: "Українська" },
|
||||||
|
{ key: "ko", text: "한글" },
|
||||||
{ key: "ja", text: "日本語" },
|
{ key: "ja", text: "日本語" },
|
||||||
{ key: "zh-CN", text: "中文(简体)" },
|
{ key: "zh-CN", text: "中文(简体)" },
|
||||||
{ key: "zh-TW", text: "中文(繁體)" },
|
{ key: "zh-TW", text: "中文(繁體)" },
|
||||||
|
@ -17,6 +17,11 @@ import {
|
|||||||
closeContextMenu,
|
closeContextMenu,
|
||||||
openImageMenu,
|
openImageMenu,
|
||||||
} from "../scripts/models/app"
|
} from "../scripts/models/app"
|
||||||
|
import {
|
||||||
|
RSSSource,
|
||||||
|
SourceTextDirection,
|
||||||
|
updateSource,
|
||||||
|
} from "../scripts/models/source"
|
||||||
|
|
||||||
type ArticleContainerProps = {
|
type ArticleContainerProps = {
|
||||||
itemId: number
|
itemId: number
|
||||||
@ -58,6 +63,14 @@ const mapDispatchToProps = (dispatch: AppDispatch) => {
|
|||||||
imageMenu: (position: [number, number]) =>
|
imageMenu: (position: [number, number]) =>
|
||||||
dispatch(openImageMenu(position)),
|
dispatch(openImageMenu(position)),
|
||||||
dismissContextMenu: () => dispatch(closeContextMenu()),
|
dismissContextMenu: () => dispatch(closeContextMenu()),
|
||||||
|
updateSourceTextDirection: (
|
||||||
|
source: RSSSource,
|
||||||
|
direction: SourceTextDirection
|
||||||
|
) => {
|
||||||
|
dispatch(
|
||||||
|
updateSource({ ...source, textDir: direction } as RSSSource)
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,11 @@ window.utils.addMainContextListener((pos, text) => {
|
|||||||
store.dispatch(openTextMenu(pos, text))
|
store.dispatch(openTextMenu(pos, text))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
window.fontList = [""]
|
||||||
|
window.utils.initFontList().then(fonts => {
|
||||||
|
window.fontList.push(...fonts)
|
||||||
|
})
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Root />
|
<Root />
|
||||||
|
@ -122,6 +122,14 @@ ipcMain.handle("set-font-size", (_, size: number) => {
|
|||||||
store.set(FONT_SIZE_STORE_KEY, size)
|
store.set(FONT_SIZE_STORE_KEY, size)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const FONT_STORE_KEY = "fontFamily"
|
||||||
|
ipcMain.on("get-font", event => {
|
||||||
|
event.returnValue = store.get(FONT_STORE_KEY, "")
|
||||||
|
})
|
||||||
|
ipcMain.handle("set-font", (_, font: string) => {
|
||||||
|
store.set(FONT_STORE_KEY, font)
|
||||||
|
})
|
||||||
|
|
||||||
ipcMain.on("get-all-settings", event => {
|
ipcMain.on("get-all-settings", event => {
|
||||||
let output = {}
|
let output = {}
|
||||||
for (let [key, value] of store) {
|
for (let [key, value] of store) {
|
||||||
|
@ -1,16 +1,9 @@
|
|||||||
import {
|
import { ipcMain, shell, dialog, app, session, clipboard } from "electron"
|
||||||
ipcMain,
|
|
||||||
shell,
|
|
||||||
dialog,
|
|
||||||
app,
|
|
||||||
session,
|
|
||||||
clipboard,
|
|
||||||
TouchBar,
|
|
||||||
} from "electron"
|
|
||||||
import { WindowManager } from "./window"
|
import { WindowManager } from "./window"
|
||||||
import fs = require("fs")
|
import fs = require("fs")
|
||||||
import { ImageCallbackTypes, TouchBarTexts } from "../schema-types"
|
import { ImageCallbackTypes, TouchBarTexts } from "../schema-types"
|
||||||
import { initMainTouchBar } from "./touchbar"
|
import { initMainTouchBar } from "./touchbar"
|
||||||
|
import fontList = require("font-list")
|
||||||
|
|
||||||
export function setUtilsListeners(manager: WindowManager) {
|
export function setUtilsListeners(manager: WindowManager) {
|
||||||
async function openExternal(url: string, background = false) {
|
async function openExternal(url: string, background = false) {
|
||||||
@ -28,11 +21,15 @@ export function setUtilsListeners(manager: WindowManager) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.on("web-contents-created", (_, contents) => {
|
app.on("web-contents-created", (_, contents) => {
|
||||||
// TODO: Use contents.setWindowOpenHandler instead of new-window listener
|
contents.setWindowOpenHandler(details => {
|
||||||
contents.on("new-window", (event, url, _, disposition) => {
|
|
||||||
if (manager.hasWindow()) event.preventDefault()
|
|
||||||
if (contents.getType() === "webview")
|
if (contents.getType() === "webview")
|
||||||
openExternal(url, disposition === "background-tab")
|
openExternal(
|
||||||
|
details.url,
|
||||||
|
details.disposition === "background-tab"
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
action: manager.hasWindow() ? "deny" : "allow",
|
||||||
|
}
|
||||||
})
|
})
|
||||||
contents.on("will-navigate", (event, url) => {
|
contents.on("will-navigate", (event, url) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@ -283,4 +280,10 @@ export function setUtilsListeners(manager: WindowManager) {
|
|||||||
ipcMain.handle("touchbar-destroy", () => {
|
ipcMain.handle("touchbar-destroy", () => {
|
||||||
if (manager.hasWindow()) manager.mainWindow.setTouchBar(null)
|
if (manager.hasWindow()) manager.mainWindow.setTouchBar(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.handle("init-font-list", () => {
|
||||||
|
return fontList.getFonts({
|
||||||
|
disableQuoting: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -63,14 +63,13 @@ export class WindowManager {
|
|||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
webviewTag: true,
|
webviewTag: true,
|
||||||
enableRemoteModule: false,
|
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
worldSafeExecuteJavaScript: true,
|
|
||||||
spellcheck: false,
|
spellcheck: false,
|
||||||
preload: path.join(
|
preload: path.join(
|
||||||
app.getAppPath(),
|
app.getAppPath(),
|
||||||
(app.isPackaged ? "dist/" : "") + "preload.js"
|
(app.isPackaged ? "dist/" : "") + "preload.js"
|
||||||
),
|
),
|
||||||
|
nativeWindowOpen: false,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
this.mainWindowState.manage(this.mainWindow)
|
this.mainWindowState.manage(this.mainWindow)
|
||||||
|
@ -88,6 +88,7 @@ export type SchemaTypes = {
|
|||||||
locale: string
|
locale: string
|
||||||
sourceGroups: SourceGroup[]
|
sourceGroups: SourceGroup[]
|
||||||
fontSize: number
|
fontSize: number
|
||||||
|
fontFamily: string
|
||||||
menuOn: boolean
|
menuOn: boolean
|
||||||
fetchInterval: number
|
fetchInterval: number
|
||||||
searchEngine: SearchEngines
|
searchEngine: SearchEngines
|
||||||
|
@ -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", 1)
|
const sdbSchema = lf.schema.create("sourcesDB", 2)
|
||||||
sdbSchema
|
sdbSchema
|
||||||
.createTable("sources")
|
.createTable("sources")
|
||||||
.addColumn("sid", lf.Type.INTEGER)
|
.addColumn("sid", lf.Type.INTEGER)
|
||||||
@ -17,6 +17,7 @@ sdbSchema
|
|||||||
.addColumn("serviceRef", lf.Type.STRING)
|
.addColumn("serviceRef", lf.Type.STRING)
|
||||||
.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)
|
||||||
.addNullable(["iconurl", "serviceRef", "rules"])
|
.addNullable(["iconurl", "serviceRef", "rules"])
|
||||||
.addIndex("idxURL", ["url"], true)
|
.addIndex("idxURL", ["url"], true)
|
||||||
|
|
||||||
@ -48,8 +49,15 @@ export let sources: lf.schema.Table
|
|||||||
export let itemsDB: lf.Database
|
export let itemsDB: lf.Database
|
||||||
export let items: lf.schema.Table
|
export let items: lf.schema.Table
|
||||||
|
|
||||||
|
async function onUpgradeSourceDB(rawDb: lf.raw.BackStore) {
|
||||||
|
const version = rawDb.getVersion()
|
||||||
|
if (version < 2) {
|
||||||
|
await rawDb.addTableColumn("sources", "textDir", 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function init() {
|
export async function init() {
|
||||||
sourcesDB = await sdbSchema.connect()
|
sourcesDB = await sdbSchema.connect({ onUpgrade: onUpgradeSourceDB })
|
||||||
sources = sourcesDB.getSchema().table("sources")
|
sources = sourcesDB.getSchema().table("sources")
|
||||||
itemsDB = await idbSchema.connect()
|
itemsDB = await idbSchema.connect()
|
||||||
items = itemsDB.getSchema().table("items")
|
items = itemsDB.getSchema().table("items")
|
||||||
@ -90,6 +98,7 @@ async function migrateNeDB() {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
delete doc._id
|
delete doc._id
|
||||||
if (!doc.fetchFrequency) doc.fetchFrequency = 0
|
if (!doc.fetchFrequency) doc.fetchFrequency = 0
|
||||||
|
doc.textDir = 0
|
||||||
return sources.createRow(doc)
|
return sources.createRow(doc)
|
||||||
})
|
})
|
||||||
const iRows = itemDocs.map(doc => {
|
const iRows = itemDocs.map(doc => {
|
||||||
|
@ -18,5 +18,6 @@ Currently, Fluent Reader supports the following languages.
|
|||||||
| nl | Nederlands | [@Vistaus](https://github.com/Vistaus) |
|
| nl | Nederlands | [@Vistaus](https://github.com/Vistaus) |
|
||||||
| it | Italiano | [@andrewasd](https://github.com/andrewasd) |
|
| it | Italiano | [@andrewasd](https://github.com/andrewasd) |
|
||||||
| pt-BR | Português do Brasil | [@fabianski7](https://github.com/fabianski7) |
|
| pt-BR | Português do Brasil | [@fabianski7](https://github.com/fabianski7) |
|
||||||
|
| ko | 한글 | [@1drive](https://github.com/1drive) |
|
||||||
|
|
||||||
Refer to the repo of [react-intl-universal](https://github.com/alibaba/react-intl-universal) to get started on internationalization.
|
Refer to the repo of [react-intl-universal](https://github.com/alibaba/react-intl-universal) to get started on internationalization.
|
||||||
|
@ -12,6 +12,7 @@ import it from "./it.json"
|
|||||||
import uk from "./uk.json"
|
import uk from "./uk.json"
|
||||||
import pt_BR from "./pt-BR.json"
|
import pt_BR from "./pt-BR.json"
|
||||||
import fi_FI from "./fi-FI.json"
|
import fi_FI from "./fi-FI.json"
|
||||||
|
import ko from "./ko.json"
|
||||||
|
|
||||||
const locales = {
|
const locales = {
|
||||||
"en-US": en_US,
|
"en-US": en_US,
|
||||||
@ -28,6 +29,7 @@ const locales = {
|
|||||||
"uk": uk,
|
"uk": uk,
|
||||||
"pt-BR": pt_BR,
|
"pt-BR": pt_BR,
|
||||||
"fi-FI": fi_FI,
|
"fi-FI": fi_FI,
|
||||||
|
"ko": ko,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default locales
|
export default locales
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"confirmMarkAll": "Do you really want to mark all articles on this page as read?",
|
"confirmMarkAll": "Do you really want to mark all articles on this page as read?",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
|
"default": "Default",
|
||||||
"time": {
|
"time": {
|
||||||
"now": "now",
|
"now": "now",
|
||||||
"m": "m",
|
"m": "m",
|
||||||
@ -66,7 +67,12 @@
|
|||||||
"loadWebpage": "Load webpage",
|
"loadWebpage": "Load webpage",
|
||||||
"loadFull": "Load full content",
|
"loadFull": "Load full content",
|
||||||
"notify": "Notify if fetched in background",
|
"notify": "Notify if fetched in background",
|
||||||
"dontNotify": "Don't notify"
|
"dontNotify": "Don't notify",
|
||||||
|
"textDir": "Text direction",
|
||||||
|
"LTR": "Left-to-right",
|
||||||
|
"RTL": "Right-to-left",
|
||||||
|
"Vertical": "Vertical",
|
||||||
|
"font": "Font"
|
||||||
},
|
},
|
||||||
"context": {
|
"context": {
|
||||||
"share": "Share",
|
"share": "Share",
|
||||||
|
@ -211,7 +211,7 @@
|
|||||||
"cacheSize": "Cache-lagra {size} data",
|
"cacheSize": "Cache-lagra {size} data",
|
||||||
"deleteChoices": "Ta bort artiklar från ... dagar sedan",
|
"deleteChoices": "Ta bort artiklar från ... dagar sedan",
|
||||||
"confirmDelete": "Ta bort",
|
"confirmDelete": "Ta bort",
|
||||||
"daysAgo": "{day, plural, =1 {# dag} other {# dagar}} sedan",
|
"daysAgo": "{days, plural, =1 {# dag} other {# dagar}} sedan",
|
||||||
"deleteAll": "Ta bort alla artiklar",
|
"deleteAll": "Ta bort alla artiklar",
|
||||||
"calculatingSize": "Beräknar storlek...",
|
"calculatingSize": "Beräknar storlek...",
|
||||||
"itemSize": "Omkring {size} lokal datalagring upptas av artiklar",
|
"itemSize": "Omkring {size} lokal datalagring upptas av artiklar",
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"confirmMarkAll": "确认将本页所有文章标为已读?",
|
"confirmMarkAll": "确认将本页所有文章标为已读?",
|
||||||
"confirm": "确认",
|
"confirm": "确认",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
|
"default": "默认",
|
||||||
"time": {
|
"time": {
|
||||||
"now": "now",
|
"now": "now",
|
||||||
"m": "m",
|
"m": "m",
|
||||||
@ -66,7 +67,12 @@
|
|||||||
"loadWebpage": "加载网页",
|
"loadWebpage": "加载网页",
|
||||||
"loadFull": "抓取全文",
|
"loadFull": "抓取全文",
|
||||||
"notify": "后台抓取时发送通知",
|
"notify": "后台抓取时发送通知",
|
||||||
"dontNotify": "不发送通知"
|
"dontNotify": "不发送通知",
|
||||||
|
"textDir": "文本方向",
|
||||||
|
"LTR": "从左到右",
|
||||||
|
"RTL": "从右到左",
|
||||||
|
"Vertical": "纵书",
|
||||||
|
"font": "字体"
|
||||||
},
|
},
|
||||||
"context": {
|
"context": {
|
||||||
"share": "分享",
|
"share": "分享",
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"confirmMarkAll": "確認將本頁所有文章標為已讀?",
|
"confirmMarkAll": "確認將本頁所有文章標為已讀?",
|
||||||
"confirm": "確認",
|
"confirm": "確認",
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
|
"default": "預設",
|
||||||
"time": {
|
"time": {
|
||||||
"now": "now",
|
"now": "now",
|
||||||
"m": "m",
|
"m": "m",
|
||||||
@ -66,7 +67,12 @@
|
|||||||
"loadWebpage": "載入網頁",
|
"loadWebpage": "載入網頁",
|
||||||
"loadFull": "抓取全文",
|
"loadFull": "抓取全文",
|
||||||
"notify": "後臺抓取時傳送通知",
|
"notify": "後臺抓取時傳送通知",
|
||||||
"dontNotify": "不傳送通知"
|
"dontNotify": "不傳送通知",
|
||||||
|
"textDir": "文本方向",
|
||||||
|
"LTR": "從左到右",
|
||||||
|
"RTL": "從右到左",
|
||||||
|
"Vertical": "縱書",
|
||||||
|
"font": "字體"
|
||||||
},
|
},
|
||||||
"context": {
|
"context": {
|
||||||
"share": "分享",
|
"share": "分享",
|
||||||
|
@ -312,7 +312,8 @@ const markUnreadDone = (item: RSSItem): ItemActionTypes => ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export function markRead(item: RSSItem): AppThunk {
|
export function markRead(item: RSSItem): AppThunk {
|
||||||
return dispatch => {
|
return (dispatch, getState) => {
|
||||||
|
item = getState().items[item._id]
|
||||||
if (!item.hasRead) {
|
if (!item.hasRead) {
|
||||||
db.itemsDB
|
db.itemsDB
|
||||||
.update(db.items)
|
.update(db.items)
|
||||||
@ -377,7 +378,8 @@ export function markAllRead(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function markUnread(item: RSSItem): AppThunk {
|
export function markUnread(item: RSSItem): AppThunk {
|
||||||
return dispatch => {
|
return (dispatch, getState) => {
|
||||||
|
item = getState().items[item._id]
|
||||||
if (item.hasRead) {
|
if (item.hasRead) {
|
||||||
db.itemsDB
|
db.itemsDB
|
||||||
.update(db.items)
|
.update(db.items)
|
||||||
|
@ -23,6 +23,12 @@ export const enum SourceOpenTarget {
|
|||||||
FullContent,
|
FullContent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum SourceTextDirection {
|
||||||
|
LTR,
|
||||||
|
RTL,
|
||||||
|
Vertical,
|
||||||
|
}
|
||||||
|
|
||||||
export class RSSSource {
|
export class RSSSource {
|
||||||
sid: number
|
sid: number
|
||||||
url: string
|
url: string
|
||||||
@ -34,6 +40,7 @@ export class RSSSource {
|
|||||||
serviceRef?: string
|
serviceRef?: string
|
||||||
fetchFrequency: number // in minutes
|
fetchFrequency: number // in minutes
|
||||||
rules?: SourceRule[]
|
rules?: SourceRule[]
|
||||||
|
textDir: SourceTextDirection
|
||||||
|
|
||||||
constructor(url: string, name: string = null) {
|
constructor(url: string, name: string = null) {
|
||||||
this.url = url
|
this.url = url
|
||||||
@ -41,6 +48,7 @@ export class RSSSource {
|
|||||||
this.openTarget = SourceOpenTarget.Local
|
this.openTarget = SourceOpenTarget.Local
|
||||||
this.lastFetched = new Date()
|
this.lastFetched = new Date()
|
||||||
this.fetchFrequency = 0
|
this.fetchFrequency = 0
|
||||||
|
this.textDir = SourceTextDirection.LTR
|
||||||
}
|
}
|
||||||
|
|
||||||
static async fetchMetaData(source: RSSSource) {
|
static async fetchMetaData(source: RSSSource) {
|
||||||
@ -483,7 +491,7 @@ export function sourceReducer(
|
|||||||
}
|
}
|
||||||
case MARK_ALL_READ: {
|
case MARK_ALL_READ: {
|
||||||
let nextState = { ...state }
|
let nextState = { ...state }
|
||||||
action.sids.map((sid, i) => {
|
action.sids.forEach(sid => {
|
||||||
nextState[sid] = {
|
nextState[sid] = {
|
||||||
...state[sid],
|
...state[sid],
|
||||||
unreadCount: action.time ? state[sid].unreadCount : 0,
|
unreadCount: action.time ? state[sid].unreadCount : 0,
|
||||||
|
@ -3,6 +3,7 @@ import { IPartialTheme, loadTheme } from "@fluentui/react"
|
|||||||
import locales from "./i18n/_locales"
|
import locales from "./i18n/_locales"
|
||||||
import { ThemeSettings } from "../schema-types"
|
import { ThemeSettings } from "../schema-types"
|
||||||
import intl from "react-intl-universal"
|
import intl from "react-intl-universal"
|
||||||
|
import { SourceTextDirection } from "./models/source"
|
||||||
|
|
||||||
const lightTheme: IPartialTheme = {
|
const lightTheme: IPartialTheme = {
|
||||||
defaultFontStyle: {
|
defaultFontStyle: {
|
||||||
@ -119,6 +120,7 @@ export async function importAll() {
|
|||||||
} else {
|
} else {
|
||||||
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
|
||||||
return db.sources.createRow(s)
|
return db.sources.createRow(s)
|
||||||
})
|
})
|
||||||
const iRows = configs.lovefield.items.map(i => {
|
const iRows = configs.lovefield.items.map(i => {
|
||||||
|
@ -23,6 +23,9 @@ module.exports = [
|
|||||||
path: __dirname + "/dist",
|
path: __dirname + "/dist",
|
||||||
filename: "electron.js",
|
filename: "electron.js",
|
||||||
},
|
},
|
||||||
|
node: {
|
||||||
|
__dirname: false,
|
||||||
|
},
|
||||||
plugins: [new HardSourceWebpackPlugin()],
|
plugins: [new HardSourceWebpackPlugin()],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user