diff --git a/dist/article/article.css b/dist/article/article.css
index 06faa55..c333c78 100644
--- a/dist/article/article.css
+++ b/dist/article/article.css
@@ -1,7 +1,11 @@
html, body {
font-family: "Segoe UI Regular", "Source Han Sans SC Regular", "Microsoft YaHei", sans-serif;
- margin: 16px 48px;
- overflow-x: hidden;
+}
+html {
+ overflow: hidden scroll;
+}
+body {
+ margin: 12px 96px 32px;
}
a {
@@ -22,8 +26,10 @@ a:hover, a:active {
article {
line-height: 1.6;
}
-article img {
+article > * {
max-width: 100%;
+}
+article img {
height: auto;
}
article figure {
diff --git a/dist/article/article.html b/dist/article/article.html
index 8314b32..086eb46 100644
--- a/dist/article/article.html
+++ b/dist/article/article.html
@@ -3,7 +3,7 @@
+ content="default-src 'self'; img-src *; style-src 'self' 'unsafe-inline'; frame-src *; media-src *">
Hello World!
diff --git a/dist/article/article.js b/dist/article/article.js
index 86cd60f..1810a39 100644
--- a/dist/article/article.js
+++ b/dist/article/article.js
@@ -2,9 +2,10 @@ function get(name) {
if (name = (new RegExp('[?&]' + encodeURIComponent(name) + '=([^&]*)')).exec(location.search))
return decodeURIComponent(name[1]);
}
+document.documentElement.style.fontSize = get("s") + "px"
let main = document.getElementById("main")
main.innerHTML = decodeURIComponent(window.atob(get("h")))
document.addEventListener("click", event => {
event.preventDefault()
- if (event.target.href) ipcRenderer.sendToHost("request-navigation", event.target.href)
+ if (event.target.href) post("request-navigation", event.target.href)
})
\ No newline at end of file
diff --git a/dist/article/preload.js b/dist/article/preload.js
index 82734e1..c15cd6d 100644
--- a/dist/article/preload.js
+++ b/dist/article/preload.js
@@ -1 +1 @@
-global.ipcRenderer = require("electron").ipcRenderer
\ No newline at end of file
+global.post = require("electron").ipcRenderer.sendToHost
\ No newline at end of file
diff --git a/dist/icons/fabric-icons-5-f95ba260.woff b/dist/icons/fabric-icons-5-f95ba260.woff
new file mode 100644
index 0000000..f403adf
Binary files /dev/null and b/dist/icons/fabric-icons-5-f95ba260.woff differ
diff --git a/dist/styles.css b/dist/styles.css
index 6d69d31..d976e70 100644
--- a/dist/styles.css
+++ b/dist/styles.css
@@ -117,7 +117,7 @@ nav.menu-on .btn-group .btn, nav.hide-btns .btn-group .btn {
nav.menu-on .btn-group .btn.system, nav.hide-btns .btn-group .btn.system {
display: inline-block;
}
-nav.menu-on .btn-group .btn.system {
+nav.menu-on .btn-group .btn.system, nav.item-on .btn-group .btn.system {
color: #fff;
}
.btn-group .btn:hover {
@@ -161,7 +161,6 @@ nav.menu-on .btn-group .btn.system {
}
.article-container {
z-index: 6;
- background-color: #fff6;
}
.menu-container .menu {
position: absolute;
@@ -273,6 +272,9 @@ img.favicon {
nav.menu-on .btn-group .btn.system {
color: #000;
}
+ nav.item-on .btn-group .btn.system {
+ color: #fff;
+ }
.menu-container {
width: 280px;
}
@@ -313,9 +315,21 @@ img.favicon {
}
.article webview {
width: 100%;
- height: calc(100vh - 50px);
+ height: calc(100vh - 86px);
border: none;
}
+.article i.ms-Icon {
+ color: #161514;
+}
+.article .actions {
+ border-bottom: 1px solid #e1dfdd;
+}
+.article .actions .favicon {
+ margin-right: 8px;
+}
+.article .actions .source-name {
+ line-height: 35px;
+}
.cards-feed-container {
display: inline-flex;
diff --git a/src/components/article.tsx b/src/components/article.tsx
index f27a8e5..daf2dca 100644
--- a/src/components/article.tsx
+++ b/src/components/article.tsx
@@ -2,47 +2,122 @@ import * as React from "react"
import { renderToString } from "react-dom/server"
import { RSSItem } from "../scripts/models/item"
import { openExternal } from "../scripts/utils"
+import { Stack, CommandBarButton, IContextualMenuProps } from "@fluentui/react"
+import { RSSSource } from "../scripts/models/source"
+
+const FONT_SIZE_STORE_KEY = "fontSize"
+const FONT_SIZE_OPTIONS = [12, 13, 14, 15, 16, 17, 18, 19, 20]
type ArticleProps = {
item: RSSItem
+ source: RSSSource
dismiss: () => void
+ toggleHasRead: (item: RSSItem) => void
}
-class Article extends React.Component {
- webview: HTMLWebViewElement
+type ArticleState = {
+ fontSize: number
+}
+class Article extends React.Component {
+ webview: HTMLWebViewElement
+
constructor(props) {
super(props)
+ this.state = {
+ fontSize: this.getFontSize()
+ }
}
+ getFontSize = () => {
+ let size = window.localStorage.getItem(FONT_SIZE_STORE_KEY)
+ return size ? parseInt(size) : 16
+ }
+ setFontSize = (size: number) => {
+ window.localStorage.setItem(FONT_SIZE_STORE_KEY, String(size))
+ this.setState({fontSize: size})
+ }
+
+ fontMenuProps = (): IContextualMenuProps => ({
+ items: FONT_SIZE_OPTIONS.map(size => ({
+ key: String(size),
+ text: String(size),
+ canCheck: true,
+ checked: size === this.state.fontSize,
+ onClick: () => this.setFontSize(size)
+ }))
+ })
+
ipcHandler = event => {
if (event.channel === "request-navigation") {
openExternal(event.args[0])
}
}
+ popUpHandler = event => {
+ openExternal(event.url)
+ }
componentDidMount = () => {
this.webview = document.getElementById("article")
this.webview.addEventListener("ipc-message", this.ipcHandler)
+ this.webview.addEventListener("new-window", this.popUpHandler)
this.webview.addEventListener("will-navigate", this.props.dismiss)
}
componentWillUnmount = () => {
this.webview.removeEventListener("ipc-message", this.ipcHandler)
+ this.webview.removeEventListener("new-window", this.popUpHandler)
this.webview.removeEventListener("will-navigate", this.props.dismiss)
}
+ openInBrowser = () => {
+ openExternal(this.props.item.link)
+ }
+
articleView = () => "article/article.html?h=" + window.btoa(encodeURIComponent(renderToString(<>
{this.props.item.title}
- >)))
+ >))) + "&s=" + this.state.fontSize
render = () => (
+
+
+
+
+ {this.props.source.iconurl &&
}
+ {this.props.source.name}
+
+ this.props.toggleHasRead(this.props.item)} />
+
+
+
+
+
+
+
+
+ preload="article/preload.js"
+ partition="sandbox" />
)
}
diff --git a/src/components/cards/card.tsx b/src/components/cards/card.tsx
index 23dac05..61ddedb 100644
--- a/src/components/cards/card.tsx
+++ b/src/components/cards/card.tsx
@@ -6,12 +6,11 @@ import { FeedIdType } from "../../scripts/models/feed"
export interface CardProps {
feedId: FeedIdType
- index: number
item: RSSItem
source: RSSSource
markRead: (item: RSSItem) => void
contextMenu: (item: RSSItem, e) => void
- showItem: (fid: FeedIdType, index: number) => void
+ showItem: (fid: FeedIdType, item: RSSItem) => void
}
export class Card extends React.Component {
@@ -24,7 +23,7 @@ export class Card extends React.Component {
e.preventDefault()
e.stopPropagation()
this.props.markRead(this.props.item)
- this.props.showItem(this.props.feedId, this.props.index)
+ this.props.showItem(this.props.feedId, this.props.item)
}
onMouseUp = (e: React.MouseEvent) => {
diff --git a/src/components/feeds/cards-feed.tsx b/src/components/feeds/cards-feed.tsx
index f5f4cf1..143f877 100644
--- a/src/components/feeds/cards-feed.tsx
+++ b/src/components/feeds/cards-feed.tsx
@@ -32,10 +32,9 @@ class CardsFeed extends Feed {
return this.props.feed.loaded && (
{
- this.props.items.map((item, index) => (
+ this.props.items.map((item) => (
void
contextMenu: (item: RSSItem, e) => void
loadMore: (feed: RSSFeed) => void
- showItem: (fid: FeedIdType, index: number) => void
+ showItem: (fid: FeedIdType, item: RSSItem) => void
}
export class Feed extends React.Component { }
\ No newline at end of file
diff --git a/src/components/nav.tsx b/src/components/nav.tsx
index 2d18731..92223e5 100644
--- a/src/components/nav.tsx
+++ b/src/components/nav.tsx
@@ -6,6 +6,7 @@ import { ProgressIndicator } from "@fluentui/react"
type NavProps = {
state: AppState,
+ itemShown: boolean,
fetch: () => void,
menu: () => void,
logs: () => void,
@@ -51,6 +52,7 @@ class Nav extends React.Component {
canFetch = () => this.props.state.sourceInit && this.props.state.feedInit && !this.props.state.fetchingItems
fetching = () => !this.canFetch() ? " fetching" : ""
menuOn = () => this.props.state.menu ? " menu-on" : ""
+ itemOn = () => this.props.itemShown ? " item-on" : ""
hideButtons = () => this.props.state.settings.display ? "hide-btns" : ""
fetch = () => {
@@ -65,7 +67,7 @@ class Nav extends React.Component {
render() {
return (
- }
- {this.props.item && (
+ {this.props.itemId >= 0 && (
e.stopPropagation()}>
-
+
)}
diff --git a/src/containers/article-container.tsx b/src/containers/article-container.tsx
new file mode 100644
index 0000000..6f41f66
--- /dev/null
+++ b/src/containers/article-container.tsx
@@ -0,0 +1,34 @@
+import { connect } from "react-redux"
+import { createSelector } from "reselect"
+import { RootState } from "../scripts/reducer"
+import { RSSItem, markUnread, markRead } from "../scripts/models/item"
+import { AppDispatch } from "../scripts/utils"
+import { dismissItem } from "../scripts/models/page"
+import Article from "../components/article"
+
+type ArticleContainerProps = {
+ itemId: number
+}
+
+const getItem = (state: RootState, props: ArticleContainerProps) => state.items[props.itemId]
+const getSource = (state: RootState, props: ArticleContainerProps) => state.sources[state.items[props.itemId].source]
+
+const makeMapStateToProps = () => {
+ return createSelector(
+ [getItem, getSource],
+ (item, source) => ({
+ item: item,
+ source: source
+ })
+ )
+}
+
+const mapDispatchToProps = (dispatch: AppDispatch) => {
+ return {
+ dismiss: () => dispatch(dismissItem()),
+ toggleHasRead: (item: RSSItem) => dispatch(item.hasRead ? markUnread(item) : markRead(item))
+ }
+}
+
+const ArticleContainer = connect(makeMapStateToProps, mapDispatchToProps)(Article)
+export default ArticleContainer
\ No newline at end of file
diff --git a/src/containers/feed-container.tsx b/src/containers/feed-container.tsx
index 5209cad..fc7a858 100644
--- a/src/containers/feed-container.tsx
+++ b/src/containers/feed-container.tsx
@@ -30,7 +30,7 @@ const mapDispatchToProps = dispatch => {
markRead: (item: RSSItem) => dispatch(markRead(item)),
contextMenu: (item: RSSItem, e) => dispatch(openItemMenu(item, e)),
loadMore: (feed: RSSFeed) => dispatch(loadMore(feed)),
- showItem: (fid: FeedIdType, index: number) => dispatch(showItem(fid, index))
+ showItem: (fid: FeedIdType, item: RSSItem) => dispatch(showItem(fid, item))
}
}
diff --git a/src/containers/nav-container.tsx b/src/containers/nav-container.tsx
index 7adc9e4..c2b7a2f 100644
--- a/src/containers/nav-container.tsx
+++ b/src/containers/nav-container.tsx
@@ -6,10 +6,15 @@ import { toggleMenu, toggleLogMenu, toggleSettings } from "../scripts/models/app
import Nav from "../components/nav"
const getState = (state: RootState) => state.app
+const getItemShown = (state: RootState) => state.page.itemId >= 0
-const mapStateToProps = createSelector(getState, (state) => ({
- state: state
-}))
+const mapStateToProps = createSelector(
+ [getState, getItemShown],
+ (state, itemShown) => ({
+ state: state,
+ itemShown: itemShown
+ }
+))
const mapDispatchToProps = (dispatch) => ({
fetch: () => dispatch(fetchItems()),
diff --git a/src/containers/page-container.tsx b/src/containers/page-container.tsx
index 3368aa3..f770ca1 100644
--- a/src/containers/page-container.tsx
+++ b/src/containers/page-container.tsx
@@ -8,18 +8,14 @@ import { dismissItem } from "../scripts/models/page"
const getPage = (state: RootState) => state.page
const getSettings = (state: RootState) => state.app.settings.display
const getMenu = (state: RootState) => state.app.menu
-const getItems = (state: RootState) => state.items
-const getFeeds = (state: RootState) => state.feeds
const mapStateToProps = createSelector(
- [getPage, getSettings, getMenu, getItems, getFeeds],
- (page, settingsOn, menuOn, items, feeds) => ({
+ [getPage, getSettings, getMenu],
+ (page, settingsOn, menuOn) => ({
feeds: [page.feedId],
settingsOn: settingsOn,
menuOn: menuOn,
- item: page.itemIndex >= 0 // && page.itemIndex < feeds[page.feedId].iids.length
- ? items[feeds[page.feedId].iids[page.itemIndex]]
- : null
+ itemId: page.itemId
})
)
diff --git a/src/index.html b/src/index.html
index 0753f9e..0b72c16 100644
--- a/src/index.html
+++ b/src/index.html
@@ -2,7 +2,7 @@
-
+
Hello World!
diff --git a/src/scripts/models/app.ts b/src/scripts/models/app.ts
index bfc7490..e071b9b 100644
--- a/src/scripts/models/app.ts
+++ b/src/scripts/models/app.ts
@@ -193,7 +193,7 @@ export function appReducer(
...state,
logMenu: {
...state.logMenu,
- notify: true,
+ notify: !state.logMenu.display,
logs: [...state.logMenu.logs, new AppLog(
AppLogType.Failure,
`无法加载订阅源“${action.errSource.name}”`,
diff --git a/src/scripts/models/item.ts b/src/scripts/models/item.ts
index 702760a..c3090da 100644
--- a/src/scripts/models/item.ts
+++ b/src/scripts/models/item.ts
@@ -199,18 +199,12 @@ export function itemReducer(
}
default: return state
}
+ case MARK_UNREAD:
case MARK_READ: return {
...state,
[action.item.id] : {
...action.item,
- hasRead: true
- }
- }
- case MARK_UNREAD: return {
- ...state,
- [action.item.id] : {
- ...action.item,
- hasRead: false
+ hasRead: action.type === MARK_READ
}
}
case LOAD_MORE:
diff --git a/src/scripts/models/page.ts b/src/scripts/models/page.ts
index 5a7fddc..e6d2f27 100644
--- a/src/scripts/models/page.ts
+++ b/src/scripts/models/page.ts
@@ -1,5 +1,6 @@
import { ALL, SOURCE, FeedIdType } from "./feed"
import { getWindowBreakpoint } from "../utils"
+import { RSSItem, ItemActionTypes, MARK_READ, MARK_UNREAD } from "./item"
export const SELECT_PAGE = "SELECT_PAGE"
export const SHOW_ITEM = "SHOW_ITEM"
@@ -22,7 +23,7 @@ interface SelectPageAction {
interface ShowItemAction {
type: typeof SHOW_ITEM
feedId: FeedIdType
- itemIndex: number
+ item: RSSItem
}
interface DismissItemAction { type: typeof DISMISS_ITEM }
@@ -50,11 +51,11 @@ export function selectSources(sids: number[], menuKey: string, title: string): P
}
}
-export function showItem(feedId: FeedIdType, itemIndex: number): PageActionTypes {
+export function showItem(feedId: FeedIdType, item: RSSItem): PageActionTypes {
return {
type: SHOW_ITEM,
feedId: feedId,
- itemIndex: itemIndex
+ item: item
}
}
@@ -62,7 +63,7 @@ export const dismissItem = (): PageActionTypes => ({ type: DISMISS_ITEM })
export class PageState {
feedId = ALL as FeedIdType
- itemIndex = -1
+ itemId = -1
}
export function pageReducer(
@@ -84,11 +85,11 @@ export function pageReducer(
}
case SHOW_ITEM: return {
...state,
- itemIndex: action.itemIndex
+ itemId: action.item.id
}
case DISMISS_ITEM: return {
...state,
- itemIndex: -1
+ itemId: -1
}
default: return state
}