import * as React from "react" import intl = require("react-intl-universal") 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, SourceOpenTarget } from "../scripts/models/source" import { store } from "../scripts/settings" import { clipboard } from "electron" 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 locale: string dismiss: () => void toggleHasRead: (item: RSSItem) => void toggleStarred: (item: RSSItem) => void toggleHidden: (item: RSSItem) => void textMenu: (text: string, position: [number, number]) => void } type ArticleState = { fontSize: number loadWebpage: boolean } class Article extends React.Component { webview: HTMLWebViewElement constructor(props) { super(props) this.state = { fontSize: this.getFontSize(), loadWebpage: this.props.source.openTarget === SourceOpenTarget.Webpage } } getFontSize = () => { return store.get(FONT_SIZE_STORE_KEY, 16) } setFontSize = (size: number) => { store.set(FONT_SIZE_STORE_KEY, 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) })) }) moreMenuProps = (): IContextualMenuProps => ({ items: [ { key: "openInBrowser", text: intl.get("openExternal"), iconProps: { iconName: "NavigateExternalInline" }, onClick: this.openInBrowser }, { key: "copyURL", text: intl.get("context.copyURL"), iconProps: { iconName: "Link" }, onClick: () => { clipboard.writeText(this.props.item.link) } }, { key: "toggleHidden", text: this.props.item.hidden ? intl.get("article.unhide") : intl.get("article.hide"), iconProps: { iconName: this.props.item.hidden ? "View" : "Hide3" }, onClick: () => { this.props.toggleHidden(this.props.item) } } ] }) ipcHandler = event => { switch (event.channel) { case "request-navigation": { openExternal(event.args[0]) break } case "context-menu": { let articlePos = document.getElementById("article").getBoundingClientRect() let [x, y] = event.args[0] this.props.textMenu(event.args[1], [x + articlePos.x, y + articlePos.y]) break } } } popUpHandler = event => { openExternal(event.url) } navigationHandler = event => { openExternal(event.url) this.props.dismiss() } componentDidMount = () => { let webview = document.getElementById("article") if (webview != this.webview) { if (this.webview) this.componentWillUnmount() webview.addEventListener("ipc-message", this.ipcHandler) webview.addEventListener("new-window", this.popUpHandler) webview.addEventListener("will-navigate", this.navigationHandler) this.webview = webview } } componentDidUpdate = (prevProps: ArticleProps) => { if (prevProps.item._id != this.props.item._id) { this.setState({loadWebpage: this.props.source.openTarget === SourceOpenTarget.Webpage}) } this.componentDidMount() } componentWillUnmount = () => { this.webview.removeEventListener("ipc-message", this.ipcHandler) this.webview.removeEventListener("new-window", this.popUpHandler) this.webview.removeEventListener("will-navigate", this.navigationHandler) } openInBrowser = () => { openExternal(this.props.item.link) } toggleWebpage = () => { if (this.state.loadWebpage) { this.setState({loadWebpage: false}) } else if (this.props.item.link.startsWith("https://") || this.props.item.link.startsWith("http://")) { this.setState({loadWebpage: true}) } } articleView = () => "article/article.html?h=" + window.btoa(encodeURIComponent(renderToString(<>

{this.props.item.title}

{this.props.item.date.toLocaleString(this.props.locale, {hour12: !this.props.locale.startsWith("zh")})}

))) + `&s=${this.state.fontSize}&u=${this.props.item.link}` render = () => (
{this.props.source.iconurl && } {this.props.source.name} this.props.toggleHasRead(this.props.item)} /> this.props.toggleStarred(this.props.item)} />
) } export default Article