mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-03-17 11:50:06 +01:00
article view optimization
This commit is contained in:
parent
7fde620d00
commit
9f85be51dc
26
dist/styles/feeds.css
vendored
26
dist/styles/feeds.css
vendored
@ -22,11 +22,16 @@
|
||||
}
|
||||
.article {
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
.article webview {
|
||||
.article webview, .article .error-prompt {
|
||||
width: 100%;
|
||||
height: calc(100% - 36px);
|
||||
border: none;
|
||||
color: var(--black);
|
||||
}
|
||||
.article webview.error {
|
||||
display: none;
|
||||
}
|
||||
.article i.ms-Icon {
|
||||
color: var(--neutralDarker);
|
||||
@ -35,18 +40,31 @@
|
||||
color: var(--black);
|
||||
border-bottom: 1px solid var(--neutralQuaternaryAlt);
|
||||
}
|
||||
.article .actions .favicon {
|
||||
margin-right: 8px;
|
||||
.article .actions .favicon, .article .actions .ms-Spinner {
|
||||
margin: 8px 8px 11px 0;
|
||||
}
|
||||
.article .actions .ms-Spinner {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.article .actions .source-name {
|
||||
line-height: 35px;
|
||||
user-select: none;
|
||||
max-width: 280px;
|
||||
max-width: 320px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
}
|
||||
.article .actions .creator {
|
||||
color: var(--neutralSecondaryAlt);
|
||||
user-select: text;
|
||||
}
|
||||
.article .actions .creator::before {
|
||||
display: inline-block;
|
||||
content: "/";
|
||||
margin: 0 6px;
|
||||
}
|
||||
.side-article-wrapper, .side-logo-wrapper {
|
||||
flex-grow: 1;
|
||||
padding-top: var(--navHeight);
|
||||
|
3
dist/styles/global.css
vendored
3
dist/styles/global.css
vendored
@ -52,6 +52,9 @@ html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ms-Link {
|
||||
user-select: none;
|
||||
}
|
||||
.ms-ContextualMenu-link, .ms-Button, .ms-ContextualMenu-item button {
|
||||
cursor: default;
|
||||
font-size: 13px;
|
||||
|
@ -42,6 +42,12 @@ const utilsBridge = {
|
||||
await ipcRenderer.invoke("clear-cache")
|
||||
},
|
||||
|
||||
addMainContextListener: (callback: (pos: [number, number], text: string) => any) => {
|
||||
ipcRenderer.removeAllListeners("window-context-menu")
|
||||
ipcRenderer.on("window-context-menu", (_, pos, text) => {
|
||||
callback(pos, text)
|
||||
})
|
||||
},
|
||||
addWebviewContextListener: (callback: (pos: [number, number], text: string) => any) => {
|
||||
ipcRenderer.removeAllListeners("webview-context-menu")
|
||||
ipcRenderer.on("webview-context-menu", (_, pos, text) => {
|
||||
|
@ -2,7 +2,7 @@ import * as React from "react"
|
||||
import intl from "react-intl-universal"
|
||||
import { renderToString } from "react-dom/server"
|
||||
import { RSSItem } from "../scripts/models/item"
|
||||
import { Stack, CommandBarButton, IContextualMenuProps, FocusZone, ContextualMenuItemType } from "@fluentui/react"
|
||||
import { Stack, CommandBarButton, IContextualMenuProps, FocusZone, ContextualMenuItemType, Spinner, Icon, Link } from "@fluentui/react"
|
||||
import { RSSSource, SourceOpenTarget } from "../scripts/models/source"
|
||||
import { shareSubmenu } from "./context-menu"
|
||||
|
||||
@ -25,6 +25,8 @@ type ArticleProps = {
|
||||
type ArticleState = {
|
||||
fontSize: number
|
||||
loadWebpage: boolean
|
||||
loaded: boolean
|
||||
error: boolean
|
||||
}
|
||||
|
||||
class Article extends React.Component<ArticleProps, ArticleState> {
|
||||
@ -34,7 +36,9 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
||||
super(props)
|
||||
this.state = {
|
||||
fontSize: this.getFontSize(),
|
||||
loadWebpage: this.props.source.openTarget === SourceOpenTarget.Webpage
|
||||
loadWebpage: this.props.source.openTarget === SourceOpenTarget.Webpage,
|
||||
loaded: false,
|
||||
error: false,
|
||||
}
|
||||
window.utils.addWebviewContextListener(this.contextMenuHandler)
|
||||
window.utils.addWebviewKeydownListener(this.keyDownHandler)
|
||||
@ -125,11 +129,27 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
||||
}
|
||||
}
|
||||
|
||||
webviewLoaded = () => {
|
||||
this.setState({loaded: true})
|
||||
}
|
||||
webviewError = () => {
|
||||
this.setState({error: true})
|
||||
}
|
||||
webviewReload = () => {
|
||||
if (this.webview) {
|
||||
this.setState({loaded: false, error: false})
|
||||
this.webview.reload()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
let webview = document.getElementById("article") as Electron.WebviewTag
|
||||
if (webview != this.webview) {
|
||||
this.webview = webview
|
||||
webview.focus()
|
||||
this.setState({loaded: false, error: false})
|
||||
webview.addEventListener("did-stop-loading", this.webviewLoaded)
|
||||
webview.addEventListener("did-fail-load", this.webviewError)
|
||||
let card = document.querySelector(`#refocus div[data-iid="${this.props.item._id}"]`) as HTMLElement
|
||||
// @ts-ignore
|
||||
if (card) card.scrollIntoViewIfNeeded()
|
||||
@ -172,8 +192,11 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
||||
<Stack className="actions" grow horizontal tokens={{childrenGap: 12}}>
|
||||
<Stack.Item grow>
|
||||
<span className="source-name">
|
||||
{this.props.source.iconurl && <img className="favicon" src={this.props.source.iconurl} />}
|
||||
{this.state.loaded
|
||||
? (this.props.source.iconurl && <img className="favicon" src={this.props.source.iconurl} />)
|
||||
: <Spinner size={1} />}
|
||||
{this.props.source.name}
|
||||
{this.props.item.creator && <span className="creator">{this.props.item.creator}</span>}
|
||||
</span>
|
||||
</Stack.Item>
|
||||
<CommandBarButton
|
||||
@ -212,10 +235,20 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
||||
</Stack>
|
||||
<webview
|
||||
id="article"
|
||||
className={this.state.error ? "error" : ""}
|
||||
key={this.props.item._id + (this.state.loadWebpage ? "_" : "")}
|
||||
src={this.state.loadWebpage ? this.props.item.link : this.articleView()}
|
||||
webpreferences="contextIsolation,disableDialogs,autoplayPolicy=document-user-activation-required"
|
||||
partition={this.state.loadWebpage ? "sandbox" : undefined} />
|
||||
{this.state.error && (
|
||||
<Stack className="error-prompt" verticalAlign="center" horizontalAlign="center" tokens={{childrenGap: 12}}>
|
||||
<Icon iconName="HeartBroken" style={{fontSize: 32}} />
|
||||
<Stack horizontal horizontalAlign="center" tokens={{childrenGap: 7}}>
|
||||
<small>{intl.get("article.error")}</small>
|
||||
<small><Link onClick={this.webviewReload}>{intl.get("article.reload")}</Link></small>
|
||||
</Stack>
|
||||
</Stack>
|
||||
)}
|
||||
</FocusZone>
|
||||
)
|
||||
}
|
||||
|
@ -12,11 +12,7 @@ import { RootState } from "../scripts/reducer"
|
||||
const Root = ({ locale, dispatch }) => locale && (
|
||||
<div id="root"
|
||||
key={locale}
|
||||
onMouseDown={() => dispatch(closeContextMenu())}
|
||||
onContextMenu={event => {
|
||||
let text = document.getSelection().toString()
|
||||
if (text) dispatch(openTextMenu(text, [event.clientX, event.clientY]))
|
||||
}}>
|
||||
onMouseDown={() => dispatch(closeContextMenu())}>
|
||||
<NavContainer />
|
||||
<PageContainer />
|
||||
<LogMenuContainer />
|
||||
|
@ -8,7 +8,7 @@ import { rootReducer, RootState } from "./scripts/reducer"
|
||||
import Root from "./components/root"
|
||||
import { AppDispatch } from "./scripts/utils"
|
||||
import { applyThemeSettings } from "./scripts/settings"
|
||||
import { initApp } from "./scripts/models/app"
|
||||
import { initApp, openTextMenu } from "./scripts/models/app"
|
||||
|
||||
window.settings.setProxy()
|
||||
|
||||
@ -22,6 +22,10 @@ const store = createStore(
|
||||
|
||||
store.dispatch(initApp())
|
||||
|
||||
window.utils.addMainContextListener((pos, text) => {
|
||||
store.dispatch(openTextMenu(text, pos))
|
||||
})
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<Root />
|
||||
|
@ -78,6 +78,11 @@ export class WindowManager {
|
||||
this.mainWindow.on("unmaximize", () => {
|
||||
this.mainWindow.webContents.send("unmaximized")
|
||||
})
|
||||
this.mainWindow.webContents.on("context-menu", (_, params) => {
|
||||
if (params.selectionText) {
|
||||
this.mainWindow.webContents.send("window-context-menu", [params.x, params.y], params.selectionText)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,8 @@
|
||||
"subscriptions": "Subscriptions"
|
||||
},
|
||||
"article": {
|
||||
"error": "Failed to load article.",
|
||||
"reload": "Reload?",
|
||||
"empty": "No articles",
|
||||
"untitled": "(Untitled)",
|
||||
"hide": "Hide article",
|
||||
|
@ -49,6 +49,8 @@
|
||||
"subscriptions": "订阅源"
|
||||
},
|
||||
"article": {
|
||||
"error": "文章加载失败",
|
||||
"reload": "重新加载",
|
||||
"empty": "无文章",
|
||||
"untitled": "(无标题)",
|
||||
"hide": "隐藏文章",
|
||||
|
Loading…
x
Reference in New Issue
Block a user