read content in webview
This commit is contained in:
parent
143a295024
commit
288907fdae
36
dist/article/article.css
vendored
Normal file
36
dist/article/article.css
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
html, body {
|
||||||
|
font-family: "Segoe UI Regular", "Source Han Sans SC Regular", "Microsoft YaHei", sans-serif;
|
||||||
|
margin: 16px 48px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #0078d4;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:hover, a:active {
|
||||||
|
color: #004578;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main > p.title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
article img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
article figure {
|
||||||
|
margin: 16px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
article figure figcaption {
|
||||||
|
font-size: .875rem;
|
||||||
|
color: #484644;
|
||||||
|
}
|
14
dist/article/article.html
vendored
Normal file
14
dist/article/article.html
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="Content-Security-Policy"
|
||||||
|
content="default-src 'self'; img-src *; style-src 'self' 'unsafe-inline'; frame-src *">
|
||||||
|
<title>Hello World!</title>
|
||||||
|
<link rel="stylesheet" href="article.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="main"></div>
|
||||||
|
<script src="article.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
10
dist/article/article.js
vendored
Normal file
10
dist/article/article.js
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
function get(name) {
|
||||||
|
if (name = (new RegExp('[?&]' + encodeURIComponent(name) + '=([^&]*)')).exec(location.search))
|
||||||
|
return decodeURIComponent(name[1]);
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
})
|
1
dist/article/preload.js
vendored
Normal file
1
dist/article/preload.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
global.ipcRenderer = require("electron").ipcRenderer
|
37
dist/styles.css
vendored
37
dist/styles.css
vendored
@ -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 {
|
nav.menu-on .btn-group .btn.system, nav.hide-btns .btn-group .btn.system {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.btn-group .btn.system.menu-on {
|
nav.menu-on .btn-group .btn.system {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
.btn-group .btn:hover {
|
.btn-group .btn:hover {
|
||||||
@ -139,17 +139,17 @@ nav.menu-on .btn-group .btn.system, nav.hide-btns .btn-group .btn.system {
|
|||||||
}
|
}
|
||||||
.btn-group .btn.close:hover {
|
.btn-group .btn.close:hover {
|
||||||
background-color: #e81123;
|
background-color: #e81123;
|
||||||
color: #fff;
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
.btn-group .btn.close:active {
|
.btn-group .btn.close:active {
|
||||||
background-color: #f1707a;
|
background-color: #f1707a;
|
||||||
color: #fff;
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
.btn-group .btn.inline-block-wide {
|
.btn-group .btn.inline-block-wide {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-container {
|
.menu-container, .article-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -159,6 +159,10 @@ nav.menu-on .btn-group .btn.system, nav.hide-btns .btn-group .btn.system {
|
|||||||
background-color: #0008;
|
background-color: #0008;
|
||||||
backdrop-filter: saturate(150%) blur(20px);
|
backdrop-filter: saturate(150%) blur(20px);
|
||||||
}
|
}
|
||||||
|
.article-container {
|
||||||
|
z-index: 6;
|
||||||
|
background-color: #fff6;
|
||||||
|
}
|
||||||
.menu-container .menu {
|
.menu-container .menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -266,7 +270,7 @@ img.favicon {
|
|||||||
nav.menu-on .btn-group .btn {
|
nav.menu-on .btn-group .btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
.btn-group .btn.system.menu-on {
|
nav.menu-on .btn-group .btn.system {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
.menu-container {
|
.menu-container {
|
||||||
@ -298,16 +302,29 @@ img.favicon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.article-wrapper {
|
||||||
|
margin: 32px auto 0;
|
||||||
|
width: 860px;
|
||||||
|
height: calc(100% - 50px);
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 6.4px 14.4px 0 rgba(0,0,0,.132), 0 1.2px 3.6px 0 rgba(0,0,0,.108);
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.article webview {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 50px);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
.cards-feed-container {
|
.cards-feed-container {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: space-around;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
.cards-feed-container > div {
|
.cards-feed-container > div.load-more-wrapper, .flex-fix {
|
||||||
flex-grow: 1;
|
text-align: center;
|
||||||
display: inline-flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
.cards-feed-container > div.load-more-wrapper {
|
.cards-feed-container > div.load-more-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
50
src/components/article.tsx
Normal file
50
src/components/article.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { renderToString } from "react-dom/server"
|
||||||
|
import { RSSItem } from "../scripts/models/item"
|
||||||
|
import { openExternal } from "../scripts/utils"
|
||||||
|
|
||||||
|
type ArticleProps = {
|
||||||
|
item: RSSItem
|
||||||
|
dismiss: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
class Article extends React.Component<ArticleProps> {
|
||||||
|
webview: HTMLWebViewElement
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcHandler = event => {
|
||||||
|
if (event.channel === "request-navigation") {
|
||||||
|
openExternal(event.args[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount = () => {
|
||||||
|
this.webview = document.getElementById("article")
|
||||||
|
this.webview.addEventListener("ipc-message", this.ipcHandler)
|
||||||
|
this.webview.addEventListener("will-navigate", this.props.dismiss)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount = () => {
|
||||||
|
this.webview.removeEventListener("ipc-message", this.ipcHandler)
|
||||||
|
this.webview.removeEventListener("will-navigate", this.props.dismiss)
|
||||||
|
}
|
||||||
|
|
||||||
|
articleView = () => "article/article.html?h=" + window.btoa(encodeURIComponent(renderToString(<>
|
||||||
|
<p className="title">{this.props.item.title}</p>
|
||||||
|
<article dangerouslySetInnerHTML={{__html: this.props.item.content}}></article>
|
||||||
|
</>)))
|
||||||
|
|
||||||
|
render = () => (
|
||||||
|
<div className="article">
|
||||||
|
<webview
|
||||||
|
id="article"
|
||||||
|
src={this.articleView()}
|
||||||
|
preload="article/preload.js" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Article
|
@ -2,12 +2,16 @@ import * as React from "react"
|
|||||||
import { openExternal } from "../../scripts/utils"
|
import { openExternal } from "../../scripts/utils"
|
||||||
import { RSSSource } from "../../scripts/models/source"
|
import { RSSSource } from "../../scripts/models/source"
|
||||||
import { RSSItem } from "../../scripts/models/item"
|
import { RSSItem } from "../../scripts/models/item"
|
||||||
|
import { FeedIdType } from "../../scripts/models/feed"
|
||||||
|
|
||||||
export interface CardProps {
|
export interface CardProps {
|
||||||
|
feedId: FeedIdType
|
||||||
|
index: number
|
||||||
item: RSSItem
|
item: RSSItem
|
||||||
source: RSSSource
|
source: RSSSource
|
||||||
markRead: Function
|
markRead: (item: RSSItem) => void
|
||||||
contextMenu: Function
|
contextMenu: (item: RSSItem, e) => void
|
||||||
|
showItem: (fid: FeedIdType, index: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Card extends React.Component<CardProps> {
|
export class Card extends React.Component<CardProps> {
|
||||||
@ -19,7 +23,8 @@ export class Card extends React.Component<CardProps> {
|
|||||||
onClick = (e: React.MouseEvent) => {
|
onClick = (e: React.MouseEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
this.openInBrowser()
|
this.props.markRead(this.props.item)
|
||||||
|
this.props.showItem(this.props.feedId, this.props.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseUp = (e: React.MouseEvent) => {
|
onMouseUp = (e: React.MouseEvent) => {
|
||||||
|
@ -32,14 +32,16 @@ class CardsFeed extends Feed {
|
|||||||
return this.props.feed.loaded && (
|
return this.props.feed.loaded && (
|
||||||
<div className="cards-feed-container">
|
<div className="cards-feed-container">
|
||||||
{
|
{
|
||||||
this.props.items.map(item => (
|
this.props.items.map((item, index) => (
|
||||||
<div key={item.id}>
|
|
||||||
<DefaultCard
|
<DefaultCard
|
||||||
|
feedId={this.props.feed.id}
|
||||||
|
index={index}
|
||||||
|
key={item.id}
|
||||||
item={item}
|
item={item}
|
||||||
source={this.props.sourceMap[item.source]}
|
source={this.props.sourceMap[item.source]}
|
||||||
markRead={this.props.markRead}
|
markRead={this.props.markRead}
|
||||||
contextMenu={this.props.contextMenu} />
|
contextMenu={this.props.contextMenu}
|
||||||
</div>
|
showItem={this.props.showItem} />
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
{ this.flexFix() }
|
{ this.flexFix() }
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { RSSItem } from "../../scripts/models/item"
|
import { RSSItem } from "../../scripts/models/item"
|
||||||
import { FeedReduxProps } from "../../containers/feed-container"
|
import { FeedReduxProps } from "../../containers/feed-container"
|
||||||
import { RSSFeed } from "../../scripts/models/feed"
|
import { RSSFeed, FeedIdType } from "../../scripts/models/feed"
|
||||||
|
|
||||||
type FeedProps = FeedReduxProps & {
|
type FeedProps = FeedReduxProps & {
|
||||||
feed: RSSFeed
|
feed: RSSFeed
|
||||||
items: RSSItem[]
|
items: RSSItem[]
|
||||||
sourceMap: Object
|
sourceMap: Object
|
||||||
markRead: Function
|
markRead: (item: RSSItem) => void
|
||||||
contextMenu: Function
|
contextMenu: (item: RSSItem, e) => void
|
||||||
loadMore: Function
|
loadMore: (feed: RSSFeed) => void
|
||||||
|
showItem: (fid: FeedIdType, index: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Feed extends React.Component<FeedProps> { }
|
export class Feed extends React.Component<FeedProps> { }
|
@ -8,6 +8,7 @@ import { AnimationClassNames } from "@fluentui/react"
|
|||||||
|
|
||||||
export type MenuProps = {
|
export type MenuProps = {
|
||||||
status: boolean,
|
status: boolean,
|
||||||
|
display: boolean,
|
||||||
selected: string,
|
selected: string,
|
||||||
sources: SourceState,
|
sources: SourceState,
|
||||||
groups: SourceGroup[],
|
groups: SourceGroup[],
|
||||||
@ -73,8 +74,8 @@ export class Menu extends React.Component<MenuProps> {
|
|||||||
})
|
})
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return this.props.status ? (
|
return this.props.status && (
|
||||||
<div className="menu-container" onClick={this.props.toggleMenu}>
|
<div className="menu-container" onClick={this.props.toggleMenu} style={{display: this.props.display ? "block" : "none"}}>
|
||||||
<div className="menu" onClick={(e) => e.stopPropagation()}>
|
<div className="menu" onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="btn-group">
|
<div className="btn-group">
|
||||||
<a className="btn hide-wide" title="关闭菜单" onClick={this.props.toggleMenu}><Icon iconName="Back" /></a>
|
<a className="btn hide-wide" title="关闭菜单" onClick={this.props.toggleMenu}><Icon iconName="Back" /></a>
|
||||||
@ -91,6 +92,6 @@ export class Menu extends React.Component<MenuProps> {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -79,11 +79,11 @@ class Nav extends React.Component<NavProps, NavState> {
|
|||||||
<a className="btn" title="视图"><Icon iconName="View" /></a>
|
<a className="btn" title="视图"><Icon iconName="View" /></a>
|
||||||
<a className="btn" title="选项" onClick={this.props.settings}><Icon iconName="Settings" /></a>
|
<a className="btn" title="选项" onClick={this.props.settings}><Icon iconName="Settings" /></a>
|
||||||
<span className="seperator"></span>
|
<span className="seperator"></span>
|
||||||
<a className={"btn system"+this.menuOn()} title="最小化" onClick={this.minimize} style={{fontSize: 12}}><Icon iconName="Remove" /></a>
|
<a className="btn system" title="最小化" onClick={this.minimize} style={{fontSize: 12}}><Icon iconName="Remove" /></a>
|
||||||
<a className={"btn system"+this.menuOn()} title="最大化" onClick={this.maximize}>
|
<a className="btn system" title="最大化" onClick={this.maximize}>
|
||||||
{this.state.maximized ? <Icon iconName="ChromeRestore" style={{fontSize: 11}} /> :<Icon iconName="Checkbox" style={{fontSize: 10}} />}
|
{this.state.maximized ? <Icon iconName="ChromeRestore" style={{fontSize: 11}} /> :<Icon iconName="Checkbox" style={{fontSize: 10}} />}
|
||||||
</a>
|
</a>
|
||||||
<a className={"btn system close"+this.menuOn()} title="关闭" onClick={this.close}><Icon iconName="Cancel" /></a>
|
<a className="btn system close" title="关闭" onClick={this.close}><Icon iconName="Cancel" /></a>
|
||||||
</div>
|
</div>
|
||||||
{!this.canFetch() &&
|
{!this.canFetch() &&
|
||||||
<ProgressIndicator
|
<ProgressIndicator
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { FeedIdType } from "../scripts/models/feed"
|
import { FeedIdType } from "../scripts/models/feed"
|
||||||
import { FeedContainer } from "../containers/feed-container"
|
import { FeedContainer } from "../containers/feed-container"
|
||||||
|
import { RSSItem } from "../scripts/models/item"
|
||||||
|
import Article from "./article"
|
||||||
|
import { dismissItem } from "../scripts/models/page"
|
||||||
|
import { AnimationClassNames } from "@fluentui/react"
|
||||||
|
|
||||||
type PageProps = {
|
type PageProps = {
|
||||||
menuOn: boolean
|
menuOn: boolean
|
||||||
settingsOn: boolean
|
settingsOn: boolean
|
||||||
feeds: FeedIdType[]
|
feeds: FeedIdType[]
|
||||||
|
item: RSSItem
|
||||||
|
dismissItem: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
class Page extends React.Component<PageProps> {
|
class Page extends React.Component<PageProps> {
|
||||||
@ -17,6 +23,13 @@ class Page extends React.Component<PageProps> {
|
|||||||
<FeedContainer feedId={fid} key={fid} />
|
<FeedContainer feedId={fid} key={fid} />
|
||||||
))}
|
))}
|
||||||
</div>}
|
</div>}
|
||||||
|
{this.props.item && (
|
||||||
|
<div className="article-container" onClick={this.props.dismissItem}>
|
||||||
|
<div className={"article-wrapper " + AnimationClassNames.slideUpIn20} onClick={e => e.stopPropagation()}>
|
||||||
|
<Article item={this.props.item} dismiss={dismissItem} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,10 @@ import { connect } from "react-redux"
|
|||||||
import { createSelector } from "reselect"
|
import { createSelector } from "reselect"
|
||||||
import { RootState } from "../scripts/reducer"
|
import { RootState } from "../scripts/reducer"
|
||||||
import CardsFeed from "../components/feeds/cards-feed"
|
import CardsFeed from "../components/feeds/cards-feed"
|
||||||
import { markRead } from "../scripts/models/item"
|
import { markRead, RSSItem } from "../scripts/models/item"
|
||||||
import { openItemMenu } from "../scripts/models/app"
|
import { openItemMenu } from "../scripts/models/app"
|
||||||
import { FeedIdType, loadMore } from "../scripts/models/feed"
|
import { FeedIdType, loadMore, RSSFeed } from "../scripts/models/feed"
|
||||||
|
import { showItem } from "../scripts/models/page"
|
||||||
|
|
||||||
interface FeedContainerProps {
|
interface FeedContainerProps {
|
||||||
feedId: FeedIdType
|
feedId: FeedIdType
|
||||||
@ -26,9 +27,10 @@ const makeMapStateToProps = () => {
|
|||||||
}
|
}
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
markRead: item => dispatch(markRead(item)),
|
markRead: (item: RSSItem) => dispatch(markRead(item)),
|
||||||
contextMenu: (item, e) => dispatch(openItemMenu(item, e)),
|
contextMenu: (item: RSSItem, e) => dispatch(openItemMenu(item, e)),
|
||||||
loadMore: feed => dispatch(loadMore(feed))
|
loadMore: (feed: RSSFeed) => dispatch(loadMore(feed)),
|
||||||
|
showItem: (fid: FeedIdType, index: number) => dispatch(showItem(fid, index))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,16 +8,16 @@ import { selectAllArticles, selectSources } from "../scripts/models/page"
|
|||||||
import { initFeeds } from "../scripts/models/feed"
|
import { initFeeds } from "../scripts/models/feed"
|
||||||
import { RSSSource } from "../scripts/models/source"
|
import { RSSSource } from "../scripts/models/source"
|
||||||
|
|
||||||
const getStatus = (state: RootState) => state.app.menu && state.app.sourceInit
|
const getApp = (state: RootState) => state.app
|
||||||
const getKey = (state: RootState) => state.app.menuKey
|
|
||||||
const getSources = (state: RootState) => state.sources
|
const getSources = (state: RootState) => state.sources
|
||||||
const getGroups = (state: RootState) => state.groups
|
const getGroups = (state: RootState) => state.groups
|
||||||
|
|
||||||
const mapStateToProps = createSelector(
|
const mapStateToProps = createSelector(
|
||||||
[getStatus, getKey, getSources, getGroups],
|
[getApp, getSources, getGroups],
|
||||||
(status, key, sources, groups) => ({
|
(app, sources, groups) => ({
|
||||||
status: status,
|
status: app.sourceInit,
|
||||||
selected: key,
|
display: app.menu,
|
||||||
|
selected: app.menuKey,
|
||||||
sources: sources,
|
sources: sources,
|
||||||
groups: groups
|
groups: groups
|
||||||
})
|
})
|
||||||
|
@ -2,19 +2,30 @@ import { connect } from "react-redux"
|
|||||||
import { createSelector } from "reselect"
|
import { createSelector } from "reselect"
|
||||||
import { RootState } from "../scripts/reducer"
|
import { RootState } from "../scripts/reducer"
|
||||||
import Page from "../components/page"
|
import Page from "../components/page"
|
||||||
|
import { AppDispatch } from "../scripts/utils"
|
||||||
|
import { dismissItem } from "../scripts/models/page"
|
||||||
|
|
||||||
const getFeeds = (state: RootState) => state.page.feedId
|
const getPage = (state: RootState) => state.page
|
||||||
const getSettings = (state: RootState) => state.app.settings.display
|
const getSettings = (state: RootState) => state.app.settings.display
|
||||||
const getMenu = (state: RootState) => state.app.menu
|
const getMenu = (state: RootState) => state.app.menu
|
||||||
|
const getItems = (state: RootState) => state.items
|
||||||
|
const getFeeds = (state: RootState) => state.feeds
|
||||||
|
|
||||||
const mapStateToProps = createSelector(
|
const mapStateToProps = createSelector(
|
||||||
[getFeeds, getSettings, getMenu],
|
[getPage, getSettings, getMenu, getItems, getFeeds],
|
||||||
(feeds, settingsOn, menuOn) => ({
|
(page, settingsOn, menuOn, items, feeds) => ({
|
||||||
feeds: [feeds],
|
feeds: [page.feedId],
|
||||||
settingsOn: settingsOn,
|
settingsOn: settingsOn,
|
||||||
menuOn: menuOn
|
menuOn: menuOn,
|
||||||
|
item: page.itemIndex >= 0 // && page.itemIndex < feeds[page.feedId].iids.length
|
||||||
|
? items[feeds[page.feedId].iids[page.itemIndex]]
|
||||||
|
: null
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const PageContainer = connect(mapStateToProps)(Page)
|
const mapDispatchToProps = (dispatch: AppDispatch) => ({
|
||||||
|
dismissItem: () => dispatch(dismissItem())
|
||||||
|
})
|
||||||
|
|
||||||
|
const PageContainer = connect(mapStateToProps, mapDispatchToProps)(Page)
|
||||||
export default PageContainer
|
export default PageContainer
|
@ -5,6 +5,7 @@ import { RootState } from "../../scripts/reducer"
|
|||||||
import SourcesTab from "../../components/settings/sources"
|
import SourcesTab from "../../components/settings/sources"
|
||||||
import { addSource, RSSSource, updateSource, deleteSource } from "../../scripts/models/source"
|
import { addSource, RSSSource, updateSource, deleteSource } from "../../scripts/models/source"
|
||||||
import { importOPML } from "../../scripts/models/group"
|
import { importOPML } from "../../scripts/models/group"
|
||||||
|
import { AppDispatch } from "../../scripts/utils"
|
||||||
|
|
||||||
const getSources = (state: RootState) => state.sources
|
const getSources = (state: RootState) => state.sources
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ const mapStateToProps = createSelector(
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = (dispatch: AppDispatch) => {
|
||||||
return {
|
return {
|
||||||
addSource: (url: string) => dispatch(addSource(url)),
|
addSource: (url: string) => dispatch(addSource(url)),
|
||||||
updateSourceName: (source: RSSSource, name: string) => {
|
updateSourceName: (source: RSSSource, name: string) => {
|
||||||
@ -30,7 +31,7 @@ const mapDispatchToProps = dispatch => {
|
|||||||
properties: ["openFile"]
|
properties: ["openFile"]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if (path.length > 0) dispatch(importOPML(path[0]))
|
if (path && path.length > 0) dispatch(importOPML(path[0]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@ function createWindow() {
|
|||||||
minHeight: 600,
|
minHeight: 600,
|
||||||
frame: false,
|
frame: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true
|
nodeIntegration: true,
|
||||||
|
webviewTag: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
mainWindowState.manage(mainWindow)
|
mainWindowState.manage(mainWindow)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src *; style-src 'self' 'unsafe-inline'; font-src 'self' https://static2.sharepointonline.com; connect-src *">
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src *; style-src 'self' 'unsafe-inline'; font-src 'self' https://static2.sharepointonline.com; connect-src *; frame-src *">
|
||||||
<title>Hello World!</title>
|
<title>Hello World!</title>
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
|
@ -2,6 +2,8 @@ import { ALL, SOURCE, FeedIdType } from "./feed"
|
|||||||
import { getWindowBreakpoint } from "../utils"
|
import { getWindowBreakpoint } from "../utils"
|
||||||
|
|
||||||
export const SELECT_PAGE = "SELECT_PAGE"
|
export const SELECT_PAGE = "SELECT_PAGE"
|
||||||
|
export const SHOW_ITEM = "SHOW_ITEM"
|
||||||
|
export const DISMISS_ITEM = "DISMISS_ITEM"
|
||||||
|
|
||||||
export enum PageType {
|
export enum PageType {
|
||||||
AllArticles, Sources, Page
|
AllArticles, Sources, Page
|
||||||
@ -17,9 +19,17 @@ interface SelectPageAction {
|
|||||||
title?: string
|
title?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PageActionTypes = SelectPageAction
|
interface ShowItemAction {
|
||||||
|
type: typeof SHOW_ITEM
|
||||||
|
feedId: FeedIdType
|
||||||
|
itemIndex: number
|
||||||
|
}
|
||||||
|
|
||||||
export function selectAllArticles(init = false): SelectPageAction {
|
interface DismissItemAction { type: typeof DISMISS_ITEM }
|
||||||
|
|
||||||
|
export type PageActionTypes = SelectPageAction | ShowItemAction | DismissItemAction
|
||||||
|
|
||||||
|
export function selectAllArticles(init = false): PageActionTypes {
|
||||||
return {
|
return {
|
||||||
type: SELECT_PAGE,
|
type: SELECT_PAGE,
|
||||||
keepMenu: getWindowBreakpoint(),
|
keepMenu: getWindowBreakpoint(),
|
||||||
@ -28,7 +38,7 @@ export function selectAllArticles(init = false): SelectPageAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectSources(sids: number[], menuKey: string, title: string) {
|
export function selectSources(sids: number[], menuKey: string, title: string): PageActionTypes {
|
||||||
return {
|
return {
|
||||||
type: SELECT_PAGE,
|
type: SELECT_PAGE,
|
||||||
pageType: PageType.Sources,
|
pageType: PageType.Sources,
|
||||||
@ -40,8 +50,19 @@ export function selectSources(sids: number[], menuKey: string, title: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function showItem(feedId: FeedIdType, itemIndex: number): PageActionTypes {
|
||||||
|
return {
|
||||||
|
type: SHOW_ITEM,
|
||||||
|
feedId: feedId,
|
||||||
|
itemIndex: itemIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const dismissItem = (): PageActionTypes => ({ type: DISMISS_ITEM })
|
||||||
|
|
||||||
export class PageState {
|
export class PageState {
|
||||||
feedId = ALL
|
feedId = ALL as FeedIdType
|
||||||
|
itemIndex = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pageReducer(
|
export function pageReducer(
|
||||||
@ -61,6 +82,14 @@ export function pageReducer(
|
|||||||
}
|
}
|
||||||
default: return state
|
default: return state
|
||||||
}
|
}
|
||||||
|
case SHOW_ITEM: return {
|
||||||
|
...state,
|
||||||
|
itemIndex: action.itemIndex
|
||||||
|
}
|
||||||
|
case DISMISS_ITEM: return {
|
||||||
|
...state,
|
||||||
|
itemIndex: -1
|
||||||
|
}
|
||||||
default: return state
|
default: return state
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user