mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-02-06 12:23:24 +01: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.
|
||||
APP="Fluent Reader"
|
||||
# Your Certificate name.
|
||||
@ -21,6 +14,17 @@ PARENT_PLIST="build/entitlements.mas.plist"
|
||||
CHILD_PLIST="build/entitlements.mas.inherit.plist"
|
||||
LOGINHELPER_PLIST="build/entitlements.mas.loginhelper.plist"
|
||||
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/Libraries/libEGL.dylib"
|
||||
codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$FRAMEWORKS_PATH/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib"
|
||||
@ -44,4 +48,4 @@ codesign -s "$APP_KEY" -f --entitlements "$CHILD_PLIST" "$APP_PATH/Contents/MacO
|
||||
codesign -s "$APP_KEY" -f --entitlements "$PARENT_PLIST" "$APP_PATH"
|
||||
productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RESULT_PATH"
|
||||
|
||||
printf "\nresignAndPackage end\n......................\n"
|
||||
printf "\nresignAndPackage end\n......................\n"
|
||||
|
34
dist/article/article.css
vendored
34
dist/article/article.css
vendored
@ -2,14 +2,25 @@
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Segoe UI", "Source Han Sans SC Regular", "Microsoft YaHei",
|
||||
sans-serif;
|
||||
}
|
||||
html {
|
||||
body {
|
||||
padding: 12px 96px 32px;
|
||||
overflow: hidden scroll;
|
||||
}
|
||||
body.rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
body.vertical {
|
||||
padding: 32px;
|
||||
padding-right: 96px;
|
||||
writing-mode: vertical-rl;
|
||||
overflow: scroll hidden;
|
||||
}
|
||||
|
||||
:root {
|
||||
margin: 12px 96px 32px;
|
||||
--gray: #484644;
|
||||
--primary: #0078d4;
|
||||
--primary-alt: #004578;
|
||||
@ -58,6 +69,11 @@ a:active {
|
||||
margin: 0 auto;
|
||||
display: none;
|
||||
}
|
||||
body.vertical #main {
|
||||
max-width: unset;
|
||||
max-height: 700px;
|
||||
margin: auto 0;
|
||||
}
|
||||
#main.show {
|
||||
display: block;
|
||||
animation-name: fadeIn;
|
||||
@ -80,12 +96,21 @@ a:active {
|
||||
article {
|
||||
line-height: 1.6;
|
||||
}
|
||||
body.vertical article {
|
||||
line-height: 1.5;
|
||||
}
|
||||
body.vertical article p {
|
||||
text-indent: 2rem;
|
||||
}
|
||||
article * {
|
||||
max-width: 100%;
|
||||
}
|
||||
article img {
|
||||
height: auto;
|
||||
}
|
||||
body.vertical article img {
|
||||
max-height: 75%;
|
||||
}
|
||||
article figure {
|
||||
margin: 16px 0;
|
||||
text-align: center;
|
||||
@ -103,6 +128,11 @@ article code {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1;
|
||||
}
|
||||
article pre {
|
||||
word-break: normal;
|
||||
overflow-wrap: normal;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
article blockquote {
|
||||
border-left: 2px solid var(--gray);
|
||||
margin: 1em 0;
|
||||
|
4
dist/article/article.html
vendored
4
dist/article/article.html
vendored
@ -3,14 +3,14 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<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>
|
||||
<link rel="stylesheet" href="article.css" />
|
||||
<script integrity="sha256-sLDWrq1tUAO8IyyqmUckFqxbXYfZ2/3TEUmtxH8Unf0=" src="mercury.web.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<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 -->
|
||||
</body>
|
||||
</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))
|
||||
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) {
|
||||
let article = get("a")
|
||||
if (get("m") === "1") {
|
||||
@ -11,6 +20,8 @@ async function getArticle(url) {
|
||||
}
|
||||
}
|
||||
document.documentElement.style.fontSize = get("s") + "px"
|
||||
let font = get("f")
|
||||
if (font) document.body.style.fontFamily = `"${font}"`
|
||||
let url = get("u")
|
||||
getArticle(url).then(article => {
|
||||
let domParser = new DOMParser()
|
||||
@ -32,4 +43,3 @@ getArticle(url).then(article => {
|
||||
main.innerHTML = dom.body.innerHTML
|
||||
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);
|
||||
}
|
||||
|
||||
.ms-Callout-main {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#root > nav {
|
||||
height: var(--navHeight);
|
||||
-webkit-app-region: drag;
|
||||
|
@ -4,7 +4,10 @@ productName: Fluent Reader
|
||||
copyright: Copyright © 2020 Haoyuan Liu
|
||||
files:
|
||||
- "./dist/**/*"
|
||||
- "!./dist/fonts.vbs"
|
||||
- "!**/*.js.map"
|
||||
asarUnpack:
|
||||
- "./dist/fontlist"
|
||||
directories:
|
||||
output: "./bin/${platform}/${arch}/"
|
||||
mac:
|
||||
@ -25,6 +28,7 @@ mac:
|
||||
- uk
|
||||
- it
|
||||
- nl
|
||||
- ko
|
||||
minimumSystemVersion: 10.14.0
|
||||
mas:
|
||||
entitlements: build/entitlements.mas.plist
|
||||
|
@ -3,6 +3,7 @@ productName: Fluent Reader
|
||||
copyright: Copyright © 2020 Haoyuan Liu
|
||||
files:
|
||||
- "./dist/**/*"
|
||||
- "!./dist/fontlist"
|
||||
- "!**/*.js.map"
|
||||
directories:
|
||||
output: "./bin/${platform}/${arch}/"
|
||||
@ -24,6 +25,7 @@ mac:
|
||||
- uk
|
||||
- it
|
||||
- nl
|
||||
- ko
|
||||
win:
|
||||
target:
|
||||
- nsis
|
||||
@ -46,6 +48,7 @@ appx:
|
||||
- uk
|
||||
- it
|
||||
- nl
|
||||
- ko
|
||||
showNameOnTiles: true
|
||||
setBuildNumber: true
|
||||
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",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"description": "Modern desktop RSS reader",
|
||||
"main": "./dist/electron.js",
|
||||
"scripts": {
|
||||
@ -26,11 +26,12 @@
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
"@yang991178/rss-parser": "^3.8.1",
|
||||
"electron": "^13.1.4",
|
||||
"electron": "^16.0.2",
|
||||
"electron-builder": "^22.11.3",
|
||||
"electron-react-devtools": "^0.5.3",
|
||||
"electron-store": "^5.2.0",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"font-list": "^1.4.2",
|
||||
"hard-source-webpack-plugin": "^0.13.1",
|
||||
"html-webpack-plugin": "^4.3.0",
|
||||
"js-md5": "^0.7.3",
|
||||
|
@ -75,6 +75,13 @@ const settingsBridge = {
|
||||
ipcRenderer.invoke("set-font-size", size)
|
||||
},
|
||||
|
||||
getFont: (): string => {
|
||||
return ipcRenderer.sendSync("get-font")
|
||||
},
|
||||
setFont: (font: string) => {
|
||||
ipcRenderer.invoke("set-font", font)
|
||||
},
|
||||
|
||||
getFetchInterval: (): number => {
|
||||
return ipcRenderer.sendSync("get-fetch-interval")
|
||||
},
|
||||
|
@ -170,11 +170,16 @@ const utilsBridge = {
|
||||
destroyTouchBar: () => {
|
||||
ipcRenderer.invoke("touchbar-destroy")
|
||||
},
|
||||
|
||||
initFontList: (): Promise<Array<string>> => {
|
||||
return ipcRenderer.invoke("init-font-list")
|
||||
},
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
utils: typeof utilsBridge
|
||||
fontList: Array<string>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,11 @@ import {
|
||||
Icon,
|
||||
Link,
|
||||
} from "@fluentui/react"
|
||||
import { RSSSource, SourceOpenTarget } from "../scripts/models/source"
|
||||
import {
|
||||
RSSSource,
|
||||
SourceOpenTarget,
|
||||
SourceTextDirection,
|
||||
} from "../scripts/models/source"
|
||||
import { shareSubmenu } from "./context-menu"
|
||||
import { platformCtrl, decodeFetchResponse } from "../scripts/utils"
|
||||
|
||||
@ -31,9 +35,14 @@ type ArticleProps = {
|
||||
textMenu: (position: [number, number], text: string, url: string) => void
|
||||
imageMenu: (position: [number, number]) => void
|
||||
dismissContextMenu: () => void
|
||||
updateSourceTextDirection: (
|
||||
source: RSSSource,
|
||||
direction: SourceTextDirection
|
||||
) => void
|
||||
}
|
||||
|
||||
type ArticleState = {
|
||||
fontFamily: string
|
||||
fontSize: number
|
||||
loadWebpage: boolean
|
||||
loadFull: boolean
|
||||
@ -49,7 +58,8 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
||||
constructor(props: ArticleProps) {
|
||||
super(props)
|
||||
this.state = {
|
||||
fontSize: this.getFontSize(),
|
||||
fontFamily: window.settings.getFont(),
|
||||
fontSize: window.settings.getFontSize(),
|
||||
loadWebpage: props.source.openTarget === SourceOpenTarget.Webpage,
|
||||
loadFull: props.source.openTarget === SourceOpenTarget.FullContent,
|
||||
fullContent: "",
|
||||
@ -64,15 +74,16 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
||||
this.loadFull()
|
||||
}
|
||||
|
||||
getFontSize = () => {
|
||||
return window.settings.getFontSize()
|
||||
}
|
||||
setFontSize = (size: number) => {
|
||||
window.settings.setFontSize(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 => ({
|
||||
key: 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 => ({
|
||||
items: [
|
||||
{
|
||||
@ -117,10 +175,24 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
||||
},
|
||||
{
|
||||
key: "fontMenu",
|
||||
text: intl.get("article.font"),
|
||||
iconProps: { iconName: "Font" },
|
||||
disabled: this.state.loadWebpage,
|
||||
subMenuProps: this.fontFamilyMenuProps(),
|
||||
},
|
||||
{
|
||||
key: "fontSizeMenu",
|
||||
text: intl.get("article.fontSize"),
|
||||
iconProps: { iconName: "FontSize" },
|
||||
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",
|
||||
@ -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
|
||||
}&m=${this.state.loadFull ? 1 : 0}`
|
||||
}
|
||||
@ -400,7 +474,7 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
||||
? this.props.item.link
|
||||
: 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}
|
||||
/>
|
||||
)}
|
||||
|
@ -20,7 +20,6 @@ import {
|
||||
DefaultButton,
|
||||
ChoiceGroup,
|
||||
IChoiceGroupOption,
|
||||
loadTheme,
|
||||
Dropdown,
|
||||
IDropdownOption,
|
||||
PrimaryButton,
|
||||
@ -140,6 +139,7 @@ class AppTab extends React.Component<AppTabProps, AppTabState> {
|
||||
{ key: "sv", text: "Svenska" },
|
||||
{ key: "tr", text: "Türkçe" },
|
||||
{ key: "uk", text: "Українська" },
|
||||
{ key: "ko", text: "한글" },
|
||||
{ key: "ja", text: "日本語" },
|
||||
{ key: "zh-CN", text: "中文(简体)" },
|
||||
{ key: "zh-TW", text: "中文(繁體)" },
|
||||
|
@ -17,6 +17,11 @@ import {
|
||||
closeContextMenu,
|
||||
openImageMenu,
|
||||
} from "../scripts/models/app"
|
||||
import {
|
||||
RSSSource,
|
||||
SourceTextDirection,
|
||||
updateSource,
|
||||
} from "../scripts/models/source"
|
||||
|
||||
type ArticleContainerProps = {
|
||||
itemId: number
|
||||
@ -58,6 +63,14 @@ const mapDispatchToProps = (dispatch: AppDispatch) => {
|
||||
imageMenu: (position: [number, number]) =>
|
||||
dispatch(openImageMenu(position)),
|
||||
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))
|
||||
})
|
||||
|
||||
window.fontList = [""]
|
||||
window.utils.initFontList().then(fonts => {
|
||||
window.fontList.push(...fonts)
|
||||
})
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<Root />
|
||||
|
@ -122,6 +122,14 @@ ipcMain.handle("set-font-size", (_, size: number) => {
|
||||
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 => {
|
||||
let output = {}
|
||||
for (let [key, value] of store) {
|
||||
|
@ -1,16 +1,9 @@
|
||||
import {
|
||||
ipcMain,
|
||||
shell,
|
||||
dialog,
|
||||
app,
|
||||
session,
|
||||
clipboard,
|
||||
TouchBar,
|
||||
} from "electron"
|
||||
import { ipcMain, shell, dialog, app, session, clipboard } from "electron"
|
||||
import { WindowManager } from "./window"
|
||||
import fs = require("fs")
|
||||
import { ImageCallbackTypes, TouchBarTexts } from "../schema-types"
|
||||
import { initMainTouchBar } from "./touchbar"
|
||||
import fontList = require("font-list")
|
||||
|
||||
export function setUtilsListeners(manager: WindowManager) {
|
||||
async function openExternal(url: string, background = false) {
|
||||
@ -28,11 +21,15 @@ export function setUtilsListeners(manager: WindowManager) {
|
||||
}
|
||||
|
||||
app.on("web-contents-created", (_, contents) => {
|
||||
// TODO: Use contents.setWindowOpenHandler instead of new-window listener
|
||||
contents.on("new-window", (event, url, _, disposition) => {
|
||||
if (manager.hasWindow()) event.preventDefault()
|
||||
contents.setWindowOpenHandler(details => {
|
||||
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) => {
|
||||
event.preventDefault()
|
||||
@ -283,4 +280,10 @@ export function setUtilsListeners(manager: WindowManager) {
|
||||
ipcMain.handle("touchbar-destroy", () => {
|
||||
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,
|
||||
webPreferences: {
|
||||
webviewTag: true,
|
||||
enableRemoteModule: false,
|
||||
contextIsolation: true,
|
||||
worldSafeExecuteJavaScript: true,
|
||||
spellcheck: false,
|
||||
preload: path.join(
|
||||
app.getAppPath(),
|
||||
(app.isPackaged ? "dist/" : "") + "preload.js"
|
||||
),
|
||||
nativeWindowOpen: false,
|
||||
},
|
||||
})
|
||||
this.mainWindowState.manage(this.mainWindow)
|
||||
|
@ -88,6 +88,7 @@ export type SchemaTypes = {
|
||||
locale: string
|
||||
sourceGroups: SourceGroup[]
|
||||
fontSize: number
|
||||
fontFamily: string
|
||||
menuOn: boolean
|
||||
fetchInterval: number
|
||||
searchEngine: SearchEngines
|
||||
|
@ -4,7 +4,7 @@ import lf from "lovefield"
|
||||
import { RSSSource } from "./models/source"
|
||||
import { RSSItem } from "./models/item"
|
||||
|
||||
const sdbSchema = lf.schema.create("sourcesDB", 1)
|
||||
const sdbSchema = lf.schema.create("sourcesDB", 2)
|
||||
sdbSchema
|
||||
.createTable("sources")
|
||||
.addColumn("sid", lf.Type.INTEGER)
|
||||
@ -17,6 +17,7 @@ sdbSchema
|
||||
.addColumn("serviceRef", lf.Type.STRING)
|
||||
.addColumn("fetchFrequency", lf.Type.NUMBER)
|
||||
.addColumn("rules", lf.Type.OBJECT)
|
||||
.addColumn("textDir", lf.Type.NUMBER)
|
||||
.addNullable(["iconurl", "serviceRef", "rules"])
|
||||
.addIndex("idxURL", ["url"], true)
|
||||
|
||||
@ -48,8 +49,15 @@ export let sources: lf.schema.Table
|
||||
export let itemsDB: lf.Database
|
||||
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() {
|
||||
sourcesDB = await sdbSchema.connect()
|
||||
sourcesDB = await sdbSchema.connect({ onUpgrade: onUpgradeSourceDB })
|
||||
sources = sourcesDB.getSchema().table("sources")
|
||||
itemsDB = await idbSchema.connect()
|
||||
items = itemsDB.getSchema().table("items")
|
||||
@ -90,6 +98,7 @@ async function migrateNeDB() {
|
||||
// @ts-ignore
|
||||
delete doc._id
|
||||
if (!doc.fetchFrequency) doc.fetchFrequency = 0
|
||||
doc.textDir = 0
|
||||
return sources.createRow(doc)
|
||||
})
|
||||
const iRows = itemDocs.map(doc => {
|
||||
|
@ -18,5 +18,6 @@ Currently, Fluent Reader supports the following languages.
|
||||
| nl | Nederlands | [@Vistaus](https://github.com/Vistaus) |
|
||||
| it | Italiano | [@andrewasd](https://github.com/andrewasd) |
|
||||
| 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.
|
||||
|
@ -12,6 +12,7 @@ import it from "./it.json"
|
||||
import uk from "./uk.json"
|
||||
import pt_BR from "./pt-BR.json"
|
||||
import fi_FI from "./fi-FI.json"
|
||||
import ko from "./ko.json"
|
||||
|
||||
const locales = {
|
||||
"en-US": en_US,
|
||||
@ -28,6 +29,7 @@ const locales = {
|
||||
"uk": uk,
|
||||
"pt-BR": pt_BR,
|
||||
"fi-FI": fi_FI,
|
||||
"ko": ko,
|
||||
}
|
||||
|
||||
export default locales
|
||||
|
@ -18,6 +18,7 @@
|
||||
"confirmMarkAll": "Do you really want to mark all articles on this page as read?",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"default": "Default",
|
||||
"time": {
|
||||
"now": "now",
|
||||
"m": "m",
|
||||
@ -66,7 +67,12 @@
|
||||
"loadWebpage": "Load webpage",
|
||||
"loadFull": "Load full content",
|
||||
"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": {
|
||||
"share": "Share",
|
||||
|
@ -211,7 +211,7 @@
|
||||
"cacheSize": "Cache-lagra {size} data",
|
||||
"deleteChoices": "Ta bort artiklar från ... dagar sedan",
|
||||
"confirmDelete": "Ta bort",
|
||||
"daysAgo": "{day, plural, =1 {# dag} other {# dagar}} sedan",
|
||||
"daysAgo": "{days, plural, =1 {# dag} other {# dagar}} sedan",
|
||||
"deleteAll": "Ta bort alla artiklar",
|
||||
"calculatingSize": "Beräknar storlek...",
|
||||
"itemSize": "Omkring {size} lokal datalagring upptas av artiklar",
|
||||
|
@ -18,6 +18,7 @@
|
||||
"confirmMarkAll": "确认将本页所有文章标为已读?",
|
||||
"confirm": "确认",
|
||||
"cancel": "取消",
|
||||
"default": "默认",
|
||||
"time": {
|
||||
"now": "now",
|
||||
"m": "m",
|
||||
@ -66,7 +67,12 @@
|
||||
"loadWebpage": "加载网页",
|
||||
"loadFull": "抓取全文",
|
||||
"notify": "后台抓取时发送通知",
|
||||
"dontNotify": "不发送通知"
|
||||
"dontNotify": "不发送通知",
|
||||
"textDir": "文本方向",
|
||||
"LTR": "从左到右",
|
||||
"RTL": "从右到左",
|
||||
"Vertical": "纵书",
|
||||
"font": "字体"
|
||||
},
|
||||
"context": {
|
||||
"share": "分享",
|
||||
|
@ -18,6 +18,7 @@
|
||||
"confirmMarkAll": "確認將本頁所有文章標為已讀?",
|
||||
"confirm": "確認",
|
||||
"cancel": "取消",
|
||||
"default": "預設",
|
||||
"time": {
|
||||
"now": "now",
|
||||
"m": "m",
|
||||
@ -66,7 +67,12 @@
|
||||
"loadWebpage": "載入網頁",
|
||||
"loadFull": "抓取全文",
|
||||
"notify": "後臺抓取時傳送通知",
|
||||
"dontNotify": "不傳送通知"
|
||||
"dontNotify": "不傳送通知",
|
||||
"textDir": "文本方向",
|
||||
"LTR": "從左到右",
|
||||
"RTL": "從右到左",
|
||||
"Vertical": "縱書",
|
||||
"font": "字體"
|
||||
},
|
||||
"context": {
|
||||
"share": "分享",
|
||||
|
@ -312,7 +312,8 @@ const markUnreadDone = (item: RSSItem): ItemActionTypes => ({
|
||||
})
|
||||
|
||||
export function markRead(item: RSSItem): AppThunk {
|
||||
return dispatch => {
|
||||
return (dispatch, getState) => {
|
||||
item = getState().items[item._id]
|
||||
if (!item.hasRead) {
|
||||
db.itemsDB
|
||||
.update(db.items)
|
||||
@ -377,7 +378,8 @@ export function markAllRead(
|
||||
}
|
||||
|
||||
export function markUnread(item: RSSItem): AppThunk {
|
||||
return dispatch => {
|
||||
return (dispatch, getState) => {
|
||||
item = getState().items[item._id]
|
||||
if (item.hasRead) {
|
||||
db.itemsDB
|
||||
.update(db.items)
|
||||
|
@ -23,6 +23,12 @@ export const enum SourceOpenTarget {
|
||||
FullContent,
|
||||
}
|
||||
|
||||
export const enum SourceTextDirection {
|
||||
LTR,
|
||||
RTL,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
export class RSSSource {
|
||||
sid: number
|
||||
url: string
|
||||
@ -34,6 +40,7 @@ export class RSSSource {
|
||||
serviceRef?: string
|
||||
fetchFrequency: number // in minutes
|
||||
rules?: SourceRule[]
|
||||
textDir: SourceTextDirection
|
||||
|
||||
constructor(url: string, name: string = null) {
|
||||
this.url = url
|
||||
@ -41,6 +48,7 @@ export class RSSSource {
|
||||
this.openTarget = SourceOpenTarget.Local
|
||||
this.lastFetched = new Date()
|
||||
this.fetchFrequency = 0
|
||||
this.textDir = SourceTextDirection.LTR
|
||||
}
|
||||
|
||||
static async fetchMetaData(source: RSSSource) {
|
||||
@ -483,7 +491,7 @@ export function sourceReducer(
|
||||
}
|
||||
case MARK_ALL_READ: {
|
||||
let nextState = { ...state }
|
||||
action.sids.map((sid, i) => {
|
||||
action.sids.forEach(sid => {
|
||||
nextState[sid] = {
|
||||
...state[sid],
|
||||
unreadCount: action.time ? state[sid].unreadCount : 0,
|
||||
|
@ -3,6 +3,7 @@ import { IPartialTheme, loadTheme } from "@fluentui/react"
|
||||
import locales from "./i18n/_locales"
|
||||
import { ThemeSettings } from "../schema-types"
|
||||
import intl from "react-intl-universal"
|
||||
import { SourceTextDirection } from "./models/source"
|
||||
|
||||
const lightTheme: IPartialTheme = {
|
||||
defaultFontStyle: {
|
||||
@ -119,6 +120,7 @@ export async function importAll() {
|
||||
} else {
|
||||
const sRows = configs.lovefield.sources.map(s => {
|
||||
s.lastFetched = new Date(s.lastFetched)
|
||||
if (!s.textDir) s.textDir = SourceTextDirection.LTR
|
||||
return db.sources.createRow(s)
|
||||
})
|
||||
const iRows = configs.lovefield.items.map(i => {
|
||||
|
@ -23,6 +23,9 @@ module.exports = [
|
||||
path: __dirname + "/dist",
|
||||
filename: "electron.js",
|
||||
},
|
||||
node: {
|
||||
__dirname: false,
|
||||
},
|
||||
plugins: [new HardSourceWebpackPlugin()],
|
||||
},
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user