mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-04-12 17:41:51 +02:00
english translation
This commit is contained in:
parent
34b281f59a
commit
a0d5e41bf2
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import intl = require("react-intl-universal")
|
||||||
import { renderToString } from "react-dom/server"
|
import { renderToString } from "react-dom/server"
|
||||||
import { RSSItem } from "../scripts/models/item"
|
import { RSSItem } from "../scripts/models/item"
|
||||||
import { openExternal } from "../scripts/utils"
|
import { openExternal } from "../scripts/utils"
|
||||||
@ -11,6 +12,7 @@ const FONT_SIZE_OPTIONS = [12, 13, 14, 15, 16, 17, 18, 19, 20]
|
|||||||
type ArticleProps = {
|
type ArticleProps = {
|
||||||
item: RSSItem
|
item: RSSItem
|
||||||
source: RSSSource
|
source: RSSSource
|
||||||
|
locale: string
|
||||||
dismiss: () => void
|
dismiss: () => void
|
||||||
toggleHasRead: (item: RSSItem) => void
|
toggleHasRead: (item: RSSItem) => void
|
||||||
toggleStarred: (item: RSSItem) => void
|
toggleStarred: (item: RSSItem) => void
|
||||||
@ -57,13 +59,13 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: "openInBrowser",
|
key: "openInBrowser",
|
||||||
text: "在浏览器中打开",
|
text: intl.get("openExternal"),
|
||||||
iconProps: {iconName: "NavigateExternalInline"},
|
iconProps: {iconName: "NavigateExternalInline"},
|
||||||
onClick: this.openInBrowser
|
onClick: this.openInBrowser
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "toggleHidden",
|
key: "toggleHidden",
|
||||||
text: this.props.item.hidden ? "取消隐藏" : "隐藏文章",
|
text: this.props.item.hidden ? intl.get("article.unhide") : intl.get("article.hide"),
|
||||||
onClick: () => { this.props.toggleHidden(this.props.item) }
|
onClick: () => { this.props.toggleHidden(this.props.item) }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -128,7 +130,7 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
|
|
||||||
articleView = () => "article/article.html?h=" + window.btoa(encodeURIComponent(renderToString(<>
|
articleView = () => "article/article.html?h=" + window.btoa(encodeURIComponent(renderToString(<>
|
||||||
<p className="title">{this.props.item.title}</p>
|
<p className="title">{this.props.item.title}</p>
|
||||||
<p className="date">{this.props.item.date.toLocaleString("zh-cn", {hour12: false})}</p>
|
<p className="date">{this.props.item.date.toLocaleString(this.props.locale, {hour12: !this.props.locale.startsWith("zh")})}</p>
|
||||||
<article dangerouslySetInnerHTML={{__html: this.props.item.content}}></article>
|
<article dangerouslySetInnerHTML={{__html: this.props.item.content}}></article>
|
||||||
</>))) + `&s=${this.state.fontSize}&u=${this.props.item.link}&d=${Number(document.body.classList.contains("dark"))}`
|
</>))) + `&s=${this.state.fontSize}&u=${this.props.item.link}&d=${Number(document.body.classList.contains("dark"))}`
|
||||||
|
|
||||||
@ -144,35 +146,35 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||||||
</span>
|
</span>
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
<CommandBarButton
|
<CommandBarButton
|
||||||
title={this.props.item.hasRead ? "标为未读" : "标为已读"}
|
title={this.props.item.hasRead ? intl.get("article.markUnread") : intl.get("article.markRead")}
|
||||||
iconProps={this.props.item.hasRead
|
iconProps={this.props.item.hasRead
|
||||||
? {iconName: "StatusCircleRing"}
|
? {iconName: "StatusCircleRing"}
|
||||||
: {iconName: "RadioBtnOn", style: {fontSize: 14, textAlign: "center"}}}
|
: {iconName: "RadioBtnOn", style: {fontSize: 14, textAlign: "center"}}}
|
||||||
onClick={() => this.props.toggleHasRead(this.props.item)} />
|
onClick={() => this.props.toggleHasRead(this.props.item)} />
|
||||||
<CommandBarButton
|
<CommandBarButton
|
||||||
title={this.props.item.starred ? "取消星标" : "标为星标"}
|
title={this.props.item.starred ? intl.get("article.unstar") : intl.get("article.star")}
|
||||||
iconProps={{iconName: this.props.item.starred ? "FavoriteStarFill" : "FavoriteStar"}}
|
iconProps={{iconName: this.props.item.starred ? "FavoriteStarFill" : "FavoriteStar"}}
|
||||||
onClick={() => this.props.toggleStarred(this.props.item)} />
|
onClick={() => this.props.toggleStarred(this.props.item)} />
|
||||||
<CommandBarButton
|
<CommandBarButton
|
||||||
title="字体大小"
|
title={intl.get("article.fontSize")}
|
||||||
disabled={this.state.loadWebpage}
|
disabled={this.state.loadWebpage}
|
||||||
iconProps={{iconName: "FontSize"}}
|
iconProps={{iconName: "FontSize"}}
|
||||||
menuIconProps={{style: {display: "none"}}}
|
menuIconProps={{style: {display: "none"}}}
|
||||||
menuProps={this.fontMenuProps()} />
|
menuProps={this.fontMenuProps()} />
|
||||||
<CommandBarButton
|
<CommandBarButton
|
||||||
title="加载网页"
|
title={intl.get("article.loadWebpage")}
|
||||||
className={this.state.loadWebpage ? "active" : ""}
|
className={this.state.loadWebpage ? "active" : ""}
|
||||||
iconProps={{iconName: "Globe"}}
|
iconProps={{iconName: "Globe"}}
|
||||||
onClick={this.toggleWebpage} />
|
onClick={this.toggleWebpage} />
|
||||||
<CommandBarButton
|
<CommandBarButton
|
||||||
title="更多"
|
title={intl.get("more")}
|
||||||
iconProps={{iconName: "More"}}
|
iconProps={{iconName: "More"}}
|
||||||
menuIconProps={{style: {display: "none"}}}
|
menuIconProps={{style: {display: "none"}}}
|
||||||
menuProps={this.moreMenuProps()} />
|
menuProps={this.moreMenuProps()} />
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack horizontal horizontalAlign="end" style={{width: 112}}>
|
<Stack horizontal horizontalAlign="end" style={{width: 112}}>
|
||||||
<CommandBarButton
|
<CommandBarButton
|
||||||
title="关闭"
|
title={intl.get("close")}
|
||||||
iconProps={{iconName: "BackToWindow"}}
|
iconProps={{iconName: "BackToWindow"}}
|
||||||
onClick={this.props.dismiss} />
|
onClick={this.props.dismiss} />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import intl = require("react-intl-universal")
|
||||||
import { clipboard } from "electron"
|
import { clipboard } from "electron"
|
||||||
import { openExternal, cutText, googleSearch } from "../scripts/utils"
|
import { openExternal, cutText, googleSearch } from "../scripts/utils"
|
||||||
import { ContextualMenu, IContextualMenuItem, ContextualMenuItemType, DirectionalHint } from "office-ui-fabric-react/lib/ContextualMenu"
|
import { ContextualMenu, IContextualMenuItem, ContextualMenuItemType, DirectionalHint } from "office-ui-fabric-react/lib/ContextualMenu"
|
||||||
@ -34,7 +35,7 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
case ContextMenuType.Item: return [
|
case ContextMenuType.Item: return [
|
||||||
{
|
{
|
||||||
key: "showItem",
|
key: "showItem",
|
||||||
text: "阅读",
|
text: intl.get("context.read"),
|
||||||
iconProps: { iconName: "TextDocument" },
|
iconProps: { iconName: "TextDocument" },
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.props.markRead(this.props.item)
|
this.props.markRead(this.props.item)
|
||||||
@ -43,7 +44,7 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "openInBrowser",
|
key: "openInBrowser",
|
||||||
text: "在浏览器中打开",
|
text: intl.get("openExternal"),
|
||||||
iconProps: { iconName: "NavigateExternalInline" },
|
iconProps: { iconName: "NavigateExternalInline" },
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
this.props.markRead(this.props.item)
|
this.props.markRead(this.props.item)
|
||||||
@ -53,25 +54,25 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
this.props.item.hasRead
|
this.props.item.hasRead
|
||||||
? {
|
? {
|
||||||
key: "markAsUnread",
|
key: "markAsUnread",
|
||||||
text: "标为未读",
|
text: intl.get("article.markUnread"),
|
||||||
iconProps: { iconName: "RadioBtnOn", style: { fontSize: 14, textAlign: "center" } },
|
iconProps: { iconName: "RadioBtnOn", style: { fontSize: 14, textAlign: "center" } },
|
||||||
onClick: () => { this.props.markUnread(this.props.item) }
|
onClick: () => { this.props.markUnread(this.props.item) }
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
key: "markAsRead",
|
key: "markAsRead",
|
||||||
text: "标为已读",
|
text: intl.get("article.markRead"),
|
||||||
iconProps: { iconName: "StatusCircleRing" },
|
iconProps: { iconName: "StatusCircleRing" },
|
||||||
onClick: () => { this.props.markRead(this.props.item) }
|
onClick: () => { this.props.markRead(this.props.item) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "toggleStarred",
|
key: "toggleStarred",
|
||||||
text: this.props.item.starred ? "取消星标" : "标为星标",
|
text: this.props.item.starred ? intl.get("article.unstar") : intl.get("article.star"),
|
||||||
iconProps: { iconName: this.props.item.starred ? "FavoriteStar" : "FavoriteStarFill" },
|
iconProps: { iconName: this.props.item.starred ? "FavoriteStar" : "FavoriteStarFill" },
|
||||||
onClick: () => { this.props.toggleStarred(this.props.item) }
|
onClick: () => { this.props.toggleStarred(this.props.item) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "toggleHidden",
|
key: "toggleHidden",
|
||||||
text: this.props.item.hidden ? "取消隐藏" : "隐藏文章",
|
text: this.props.item.hidden ? intl.get("article.unhide") : intl.get("article.hide"),
|
||||||
onClick: () => { this.props.toggleHidden(this.props.item) }
|
onClick: () => { this.props.toggleHidden(this.props.item) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -80,25 +81,25 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "copyTitle",
|
key: "copyTitle",
|
||||||
text: "复制标题",
|
text: intl.get("context.copyTitle"),
|
||||||
onClick: () => { clipboard.writeText(this.props.item.title) }
|
onClick: () => { clipboard.writeText(this.props.item.title) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "copyURL",
|
key: "copyURL",
|
||||||
text: "复制链接",
|
text: intl.get("context.copyURL"),
|
||||||
onClick: () => { clipboard.writeText(this.props.item.link) }
|
onClick: () => { clipboard.writeText(this.props.item.link) }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
case ContextMenuType.Text: return [
|
case ContextMenuType.Text: return [
|
||||||
{
|
{
|
||||||
key: "copyText",
|
key: "copyText",
|
||||||
text: "复制",
|
text: intl.get("context.copy"),
|
||||||
iconProps: { iconName: "Copy" },
|
iconProps: { iconName: "Copy" },
|
||||||
onClick: () => { clipboard.writeText(this.props.text) }
|
onClick: () => { clipboard.writeText(this.props.text) }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "searchText",
|
key: "searchText",
|
||||||
text: `使用Google搜索“${cutText(this.props.text, 15)}”`,
|
text: intl.get("context.search", { text: cutText(this.props.text, 15) }),
|
||||||
iconProps: { iconName: "Search" },
|
iconProps: { iconName: "Search" },
|
||||||
onClick: () => { googleSearch(this.props.text) }
|
onClick: () => { googleSearch(this.props.text) }
|
||||||
}
|
}
|
||||||
@ -108,12 +109,12 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
key: "section_1",
|
key: "section_1",
|
||||||
itemType: ContextualMenuItemType.Section,
|
itemType: ContextualMenuItemType.Section,
|
||||||
sectionProps: {
|
sectionProps: {
|
||||||
title: "视图",
|
title: intl.get("context.view"),
|
||||||
bottomDivider: true,
|
bottomDivider: true,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: "cardView",
|
key: "cardView",
|
||||||
text: "卡片视图",
|
text: intl.get("context.cardView"),
|
||||||
iconProps: { iconName: "GridViewMedium" },
|
iconProps: { iconName: "GridViewMedium" },
|
||||||
canCheck: true,
|
canCheck: true,
|
||||||
checked: this.props.viewType === ViewType.Cards,
|
checked: this.props.viewType === ViewType.Cards,
|
||||||
@ -121,7 +122,7 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "listView",
|
key: "listView",
|
||||||
text: "列表视图",
|
text: intl.get("context.listView"),
|
||||||
iconProps: { iconName: "BacklogList" },
|
iconProps: { iconName: "BacklogList" },
|
||||||
canCheck: true,
|
canCheck: true,
|
||||||
checked: this.props.viewType === ViewType.List,
|
checked: this.props.viewType === ViewType.List,
|
||||||
@ -134,12 +135,12 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
key: "section_2",
|
key: "section_2",
|
||||||
itemType: ContextualMenuItemType.Section,
|
itemType: ContextualMenuItemType.Section,
|
||||||
sectionProps: {
|
sectionProps: {
|
||||||
title: "筛选",
|
title: intl.get("context.filter"),
|
||||||
bottomDivider: true,
|
bottomDivider: true,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: "allArticles",
|
key: "allArticles",
|
||||||
text: "全部文章",
|
text: intl.get("allArticles"),
|
||||||
iconProps: { iconName: "ClearFilter" },
|
iconProps: { iconName: "ClearFilter" },
|
||||||
canCheck: true,
|
canCheck: true,
|
||||||
checked: (this.props.filter & ~FeedFilter.ShowHidden) == FeedFilter.Default,
|
checked: (this.props.filter & ~FeedFilter.ShowHidden) == FeedFilter.Default,
|
||||||
@ -147,7 +148,7 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "unreadOnly",
|
key: "unreadOnly",
|
||||||
text: "仅未读文章",
|
text: intl.get("context.unreadOnly"),
|
||||||
iconProps: { iconName: "RadioBtnOn", style: { fontSize: 14, textAlign: "center" } },
|
iconProps: { iconName: "RadioBtnOn", style: { fontSize: 14, textAlign: "center" } },
|
||||||
canCheck: true,
|
canCheck: true,
|
||||||
checked: (this.props.filter & ~FeedFilter.ShowHidden) == FeedFilter.UnreadOnly,
|
checked: (this.props.filter & ~FeedFilter.ShowHidden) == FeedFilter.UnreadOnly,
|
||||||
@ -155,7 +156,7 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "starredOnly",
|
key: "starredOnly",
|
||||||
text: "仅星标文章",
|
text: intl.get("context.starredOnly"),
|
||||||
iconProps: { iconName: "FavoriteStarFill" },
|
iconProps: { iconName: "FavoriteStarFill" },
|
||||||
canCheck: true,
|
canCheck: true,
|
||||||
checked: (this.props.filter & ~FeedFilter.ShowHidden) == FeedFilter.StarredOnly,
|
checked: (this.props.filter & ~FeedFilter.ShowHidden) == FeedFilter.StarredOnly,
|
||||||
@ -166,7 +167,7 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "showHidden",
|
key: "showHidden",
|
||||||
text: "显示隐藏文章",
|
text: intl.get("context.showHidden"),
|
||||||
canCheck: true,
|
canCheck: true,
|
||||||
checked: Boolean(this.props.filter & FeedFilter.ShowHidden),
|
checked: Boolean(this.props.filter & FeedFilter.ShowHidden),
|
||||||
onClick: () => this.props.toggleFilter(FeedFilter.ShowHidden)
|
onClick: () => this.props.toggleFilter(FeedFilter.ShowHidden)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Feed, FeedProps } from "./feed"
|
import intl = require("react-intl-universal")
|
||||||
|
import { FeedProps } from "./feed"
|
||||||
import DefaultCard from "../cards/default-card"
|
import DefaultCard from "../cards/default-card"
|
||||||
import { PrimaryButton } from 'office-ui-fabric-react';
|
import { PrimaryButton } from 'office-ui-fabric-react';
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ class CardsFeed extends React.Component<FeedProps> {
|
|||||||
{
|
{
|
||||||
(this.props.feed.loaded && !this.props.feed.allLoaded)
|
(this.props.feed.loaded && !this.props.feed.allLoaded)
|
||||||
? <div className="load-more-wrapper"><PrimaryButton
|
? <div className="load-more-wrapper"><PrimaryButton
|
||||||
text="加载更多"
|
text={intl.get("loadMore")}
|
||||||
disabled={this.props.feed.loading}
|
disabled={this.props.feed.loading}
|
||||||
onClick={() => this.props.loadMore(this.props.feed)} /></div>
|
onClick={() => this.props.loadMore(this.props.feed)} /></div>
|
||||||
: null
|
: null
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import intl = require("react-intl-universal")
|
||||||
import { FeedProps } from "./feed"
|
import { FeedProps } from "./feed"
|
||||||
import { DefaultButton } from 'office-ui-fabric-react';
|
import { DefaultButton } from 'office-ui-fabric-react';
|
||||||
import ListCard from "../cards/list-card";
|
import ListCard from "../cards/list-card";
|
||||||
@ -22,7 +23,7 @@ class ListFeed extends React.Component<FeedProps> {
|
|||||||
{
|
{
|
||||||
(this.props.feed.loaded && !this.props.feed.allLoaded)
|
(this.props.feed.loaded && !this.props.feed.allLoaded)
|
||||||
? <div className="load-more-wrapper"><DefaultButton
|
? <div className="load-more-wrapper"><DefaultButton
|
||||||
text="加载更多"
|
text={intl.get("loadMore")}
|
||||||
disabled={this.props.feed.loading}
|
disabled={this.props.feed.loading}
|
||||||
onClick={() => this.props.loadMore(this.props.feed)} /></div>
|
onClick={() => this.props.loadMore(this.props.feed)} /></div>
|
||||||
: null
|
: null
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import intl = require("react-intl-universal")
|
||||||
import { Callout, ActivityItem, Icon, DirectionalHint } from "@fluentui/react"
|
import { Callout, ActivityItem, Icon, DirectionalHint } from "@fluentui/react"
|
||||||
import { AppLog, AppLogType } from "../scripts/models/app"
|
import { AppLog, AppLogType } from "../scripts/models/app"
|
||||||
import Time from "./utils/time"
|
import Time from "./utils/time"
|
||||||
@ -29,7 +30,7 @@ class LogMenu extends React.Component<LogMenuProps> {
|
|||||||
onDismiss={() => this.props.close()}
|
onDismiss={() => this.props.close()}
|
||||||
>
|
>
|
||||||
{ this.props.logs.length == 0
|
{ this.props.logs.length == 0
|
||||||
? <p style={{ textAlign: "center" }}>无消息</p>
|
? <p style={{ textAlign: "center" }}>{intl.get("log.empty")}</p>
|
||||||
: this.activityItems().map((item => (
|
: this.activityItems().map((item => (
|
||||||
<ActivityItem {...item} key={item.key} style={{ margin: 12 }} />
|
<ActivityItem {...item} key={item.key} style={{ margin: 12 }} />
|
||||||
))) }
|
))) }
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import intl = require("react-intl-universal")
|
||||||
import { Icon } from "@fluentui/react/lib/Icon"
|
import { Icon } from "@fluentui/react/lib/Icon"
|
||||||
import { Nav, INavLink, INavLinkGroup } from "office-ui-fabric-react/lib/Nav"
|
import { Nav, INavLink, INavLinkGroup } from "office-ui-fabric-react/lib/Nav"
|
||||||
import { SourceGroup } from "../scripts/models/group"
|
import { SourceGroup } from "../scripts/models/group"
|
||||||
@ -23,13 +24,13 @@ export class Menu extends React.Component<MenuProps> {
|
|||||||
{
|
{
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
name: "搜索",
|
name: intl.get("search"),
|
||||||
key: "search",
|
key: "search",
|
||||||
icon: "Search",
|
icon: "Search",
|
||||||
url: null
|
url: null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "全部文章",
|
name: intl.get("allArticles"),
|
||||||
key: ALL,
|
key: ALL,
|
||||||
icon: "TextDocument",
|
icon: "TextDocument",
|
||||||
onClick: this.props.allArticles,
|
onClick: this.props.allArticles,
|
||||||
@ -78,14 +79,14 @@ export class Menu extends React.Component<MenuProps> {
|
|||||||
<div className="menu-container" onClick={this.props.toggleMenu} style={{display: this.props.display ? "block" : "none"}}>
|
<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={intl.get("menu.close")} onClick={this.props.toggleMenu}><Icon iconName="Back" /></a>
|
||||||
<a className="btn inline-block-wide" title="关闭菜单" onClick={this.props.toggleMenu}><Icon iconName="GlobalNavButton" /></a>
|
<a className="btn inline-block-wide" title={intl.get("menu.close")} onClick={this.props.toggleMenu}><Icon iconName="GlobalNavButton" /></a>
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-wrapper">
|
<div className="nav-wrapper">
|
||||||
<Nav
|
<Nav
|
||||||
groups={this.getItems()}
|
groups={this.getItems()}
|
||||||
selectedKey={this.props.selected} />
|
selectedKey={this.props.selected} />
|
||||||
<p className={"subs-header " + AnimationClassNames.slideDownIn10}>订阅源</p>
|
<p className={"subs-header " + AnimationClassNames.slideDownIn10}>{intl.get("menu.subscriptions")}</p>
|
||||||
<Nav
|
<Nav
|
||||||
selectedKey={this.props.selected}
|
selectedKey={this.props.selected}
|
||||||
groups={this.getGroups()} />
|
groups={this.getGroups()} />
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import intl = require("react-intl-universal")
|
||||||
import { remote } from "electron"
|
import { remote } from "electron"
|
||||||
import { Icon } from "@fluentui/react/lib/Icon"
|
import { Icon } from "@fluentui/react/lib/Icon"
|
||||||
import { AppState } from "../scripts/models/app"
|
import { AppState } from "../scripts/models/app"
|
||||||
@ -76,25 +77,59 @@ class Nav extends React.Component<NavProps, NavState> {
|
|||||||
return (
|
return (
|
||||||
<nav className={this.hideButtons() + this.menuOn() + this.itemOn()}>
|
<nav className={this.hideButtons() + this.menuOn() + this.itemOn()}>
|
||||||
<div className="btn-group">
|
<div className="btn-group">
|
||||||
<a className="btn hide-wide" title="菜单" onClick={this.props.menu}><Icon iconName="GlobalNavButton" /></a>
|
<a className="btn hide-wide"
|
||||||
|
title={intl.get("nav.menu")}
|
||||||
|
onClick={this.props.menu}>
|
||||||
|
<Icon iconName="GlobalNavButton" />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<span className="title">{this.props.state.title}</span>
|
<span className="title">{this.props.state.title}</span>
|
||||||
<div className="btn-group" style={{float:"right"}}>
|
<div className="btn-group" style={{float:"right"}}>
|
||||||
<a className={"btn"+this.fetching()} onClick={this.fetch} title="刷新"><Icon iconName="Refresh" /></a>
|
<a className={"btn"+this.fetching()}
|
||||||
<a className="btn" title="全部标为已读"><Icon iconName="InboxCheck" /></a>
|
onClick={this.fetch}
|
||||||
<a className="btn" id="log-toggle" title="消息" onClick={this.props.logs}>
|
title={intl.get("nav.refresh")}>
|
||||||
|
<Icon iconName="Refresh" />
|
||||||
|
</a>
|
||||||
|
<a className="btn" title={intl.get("nav.markAllRead")}>
|
||||||
|
<Icon iconName="InboxCheck" />
|
||||||
|
</a>
|
||||||
|
<a className="btn"
|
||||||
|
id="log-toggle"
|
||||||
|
title={intl.get("nav.notifications")}
|
||||||
|
onClick={this.props.logs}>
|
||||||
{this.props.state.logMenu.notify ? <Icon iconName="RingerSolid" /> : <Icon iconName="Ringer" />}
|
{this.props.state.logMenu.notify ? <Icon iconName="RingerSolid" /> : <Icon iconName="Ringer" />}
|
||||||
</a>
|
</a>
|
||||||
<a className="btn" id="view-toggle" title="视图" onClick={this.props.views}
|
<a className="btn"
|
||||||
onMouseDown={e => {if (this.props.state.contextMenu.event === "#view-toggle") e.stopPropagation()}}>
|
id="view-toggle"
|
||||||
|
title={intl.get("nav.view")}
|
||||||
|
onClick={this.props.views}
|
||||||
|
onMouseDown={e => {
|
||||||
|
if (this.props.state.contextMenu.event === "#view-toggle") e.stopPropagation()}}>
|
||||||
<Icon iconName="View" /></a>
|
<Icon iconName="View" /></a>
|
||||||
<a className="btn" title="选项" onClick={this.props.settings}><Icon iconName="Settings" /></a>
|
<a className="btn"
|
||||||
<span className="seperator"></span>
|
title={intl.get("nav.settings")}
|
||||||
<a className="btn system" title="最小化" onClick={this.minimize} style={{fontSize: 12}}><Icon iconName="Remove" /></a>
|
onClick={this.props.settings}>
|
||||||
<a className="btn system" title="最大化" onClick={this.maximize}>
|
<Icon iconName="Settings" />
|
||||||
{this.state.maximized ? <Icon iconName="ChromeRestore" style={{fontSize: 11}} /> :<Icon iconName="Checkbox" style={{fontSize: 10}} />}
|
</a>
|
||||||
|
<span className="seperator"></span>
|
||||||
|
<a className="btn system"
|
||||||
|
title={intl.get("nav.minimize")}
|
||||||
|
onClick={this.minimize}
|
||||||
|
style={{fontSize: 12}}>
|
||||||
|
<Icon iconName="Remove" />
|
||||||
|
</a>
|
||||||
|
<a className="btn system"
|
||||||
|
title={intl.get("nav.maximize")}
|
||||||
|
onClick={this.maximize}>
|
||||||
|
{this.state.maximized
|
||||||
|
? <Icon iconName="ChromeRestore" style={{fontSize: 11}} />
|
||||||
|
: <Icon iconName="Checkbox" style={{fontSize: 10}} />}
|
||||||
|
</a>
|
||||||
|
<a className="btn system close"
|
||||||
|
title={intl.get("close")}
|
||||||
|
onClick={this.close}>
|
||||||
|
<Icon iconName="Cancel" />
|
||||||
</a>
|
</a>
|
||||||
<a className="btn system close" title="关闭" onClick={this.close}><Icon iconName="Cancel" /></a>
|
|
||||||
</div>
|
</div>
|
||||||
{!this.canFetch() &&
|
{!this.canFetch() &&
|
||||||
<ProgressIndicator
|
<ProgressIndicator
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import intl = require("react-intl-universal")
|
||||||
import { Icon } from "@fluentui/react/lib/Icon"
|
import { Icon } from "@fluentui/react/lib/Icon"
|
||||||
import { AnimationClassNames } from "@fluentui/react/lib/Styling"
|
import { AnimationClassNames } from "@fluentui/react/lib/Styling"
|
||||||
import AboutTab from "./settings/about"
|
import AboutTab from "./settings/about"
|
||||||
@ -23,24 +24,24 @@ class Settings extends React.Component<SettingsProps> {
|
|||||||
<div className="settings-container">
|
<div className="settings-container">
|
||||||
<div className={"settings " + AnimationClassNames.slideUpIn20}>
|
<div className={"settings " + AnimationClassNames.slideUpIn20}>
|
||||||
{this.props.blocked && <div className="loading">
|
{this.props.blocked && <div className="loading">
|
||||||
<Spinner label="正在更新订阅源,请稍候…" />
|
<Spinner label={intl.get("settings.fetching")} />
|
||||||
</div>}
|
</div>}
|
||||||
<div className="btn-group" style={{position: "absolute", top: 6, left: -64}}>
|
<div className="btn-group" style={{position: "absolute", top: 6, left: -64}}>
|
||||||
<a className={"btn" + (this.props.exitting ? " disabled" : "")} title="退出设置" onClick={this.props.close}>
|
<a className={"btn" + (this.props.exitting ? " disabled" : "")} title={intl.get("settings.exit")} onClick={this.props.close}>
|
||||||
<Icon iconName="Back" />
|
<Icon iconName="Back" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<Pivot>
|
<Pivot>
|
||||||
<PivotItem headerText="订阅源" itemIcon="Source">
|
<PivotItem headerText={intl.get("settings.sources")} itemIcon="Source">
|
||||||
<SourcesTabContainer />
|
<SourcesTabContainer />
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem headerText="分组与排序" itemIcon="GroupList">
|
<PivotItem headerText={intl.get("settings.grouping")} itemIcon="GroupList">
|
||||||
<GroupsTabContainer />
|
<GroupsTabContainer />
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem headerText="应用选项" itemIcon="Settings">
|
<PivotItem headerText={intl.get("settings.app")} itemIcon="Settings">
|
||||||
<AppTabContainer />
|
<AppTabContainer />
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
<PivotItem headerText="关于" itemIcon="Info">
|
<PivotItem headerText={intl.get("settings.about")} itemIcon="Info">
|
||||||
<AboutTab />
|
<AboutTab />
|
||||||
</PivotItem>
|
</PivotItem>
|
||||||
</Pivot>
|
</Pivot>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import intl = require("react-intl-universal")
|
||||||
import { Stack, Link } from "@fluentui/react"
|
import { Stack, Link } from "@fluentui/react"
|
||||||
import { openExternal } from "../../scripts/utils"
|
import { openExternal } from "../../scripts/utils"
|
||||||
|
|
||||||
@ -8,11 +9,11 @@ class AboutTab extends React.Component {
|
|||||||
<Stack className="settings-about" horizontalAlign="center">
|
<Stack className="settings-about" horizontalAlign="center">
|
||||||
<img src="logo.svg" style={{width: 120, height: 120}} />
|
<img src="logo.svg" style={{width: 120, height: 120}} />
|
||||||
<h3>Fluent Reader</h3>
|
<h3>Fluent Reader</h3>
|
||||||
<small>版本 0.1.0</small>
|
<small>{intl.get("settings.version")} 0.1.0</small>
|
||||||
<p className="settings-hint">Copyright © 2020 Haoyuan Liu. All rights reserved.</p>
|
<p className="settings-hint">Copyright © 2020 Haoyuan Liu. All rights reserved.</p>
|
||||||
<Stack horizontal horizontalAlign="center" tokens={{childrenGap: 8}}>
|
<Stack horizontal horizontalAlign="center" tokens={{childrenGap: 12}}>
|
||||||
<small><Link onClick={() => openExternal("https://github.com/yang991178/fluent-reader")}>开源项目</Link></small>
|
<small><Link onClick={() => openExternal("https://github.com/yang991178/fluent-reader")}>{intl.get("settings.openSource")}</Link></small>
|
||||||
<small><Link onClick={() => openExternal("https://github.com/yang991178/fluent-reader/issues")}>反馈</Link></small>
|
<small><Link onClick={() => openExternal("https://github.com/yang991178/fluent-reader/issues")}>{intl.get("settings.feedback")}</Link></small>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import intl = require("react-intl-universal")
|
||||||
import { SourceGroup } from "../../scripts/models/group"
|
import { SourceGroup } from "../../scripts/models/group"
|
||||||
import { SourceState, RSSSource } from "../../scripts/models/source"
|
import { SourceState, RSSSource } from "../../scripts/models/source"
|
||||||
import { IColumn, Selection, SelectionMode, DetailsList, Label, Stack,
|
import { IColumn, Selection, SelectionMode, DetailsList, Label, Stack,
|
||||||
@ -69,22 +70,22 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
groupColumns: IColumn[] = [
|
groupColumns = (): IColumn[] => [
|
||||||
{
|
{
|
||||||
key: "type",
|
key: "type",
|
||||||
name: "类型",
|
name: intl.get("groups.type"),
|
||||||
minWidth: 40,
|
minWidth: 40,
|
||||||
maxWidth: 40,
|
maxWidth: 40,
|
||||||
data: "string",
|
data: "string",
|
||||||
onRender: (g: SourceGroup) => <>
|
onRender: (g: SourceGroup) => <>
|
||||||
{g.isMultiple ? "分组" : "订阅源"}
|
{g.isMultiple ? intl.get("groups.group") : intl.get("groups.source")}
|
||||||
</>
|
</>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "capacity",
|
key: "capacity",
|
||||||
name: "容量",
|
name: intl.get("groups.capacity"),
|
||||||
minWidth: 40,
|
minWidth: 40,
|
||||||
maxWidth: 40,
|
maxWidth: 60,
|
||||||
data: "string",
|
data: "string",
|
||||||
onRender: (g: SourceGroup) => <>
|
onRender: (g: SourceGroup) => <>
|
||||||
{g.isMultiple ? g.sids.length : ""}
|
{g.isMultiple ? g.sids.length : ""}
|
||||||
@ -92,7 +93,7 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "name",
|
key: "name",
|
||||||
name: "名称",
|
name: intl.get("name"),
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
data: "string",
|
data: "string",
|
||||||
isRowHeader: true,
|
isRowHeader: true,
|
||||||
@ -105,7 +106,7 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
sourceColumns: IColumn[] = [
|
sourceColumns: IColumn[] = [
|
||||||
{
|
{
|
||||||
key: "favicon",
|
key: "favicon",
|
||||||
name: "图标",
|
name: intl.get("icon"),
|
||||||
fieldName: "name",
|
fieldName: "name",
|
||||||
isIconOnly: true,
|
isIconOnly: true,
|
||||||
iconName: "ImagePixel",
|
iconName: "ImagePixel",
|
||||||
@ -117,7 +118,7 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "name",
|
key: "name",
|
||||||
name: "名称",
|
name: intl.get("name"),
|
||||||
fieldName: "name",
|
fieldName: "name",
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
data: 'string',
|
data: 'string',
|
||||||
@ -252,11 +253,11 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
<>
|
<>
|
||||||
<Stack horizontal horizontalAlign="space-between" style={{height: 40}}>
|
<Stack horizontal horizontalAlign="space-between" style={{height: 40}}>
|
||||||
<CommandBarButton
|
<CommandBarButton
|
||||||
text="退出分组"
|
text={intl.get("groups.exitGroup")}
|
||||||
iconProps={{iconName: "BackToWindow"}}
|
iconProps={{iconName: "BackToWindow"}}
|
||||||
onClick={() => this.setState({manageGroup: false})} />
|
onClick={() => this.setState({manageGroup: false})} />
|
||||||
{this.state.selectedSources != null && <CommandBarButton
|
{this.state.selectedSources != null && <CommandBarButton
|
||||||
text="从分组删除订阅源"
|
text={intl.get("groups.deleteSource")}
|
||||||
onClick={this.removeFromGroup}
|
onClick={this.removeFromGroup}
|
||||||
iconProps={{iconName: "RemoveFromShoppingList", style: {color: "#d13438"}}} />}
|
iconProps={{iconName: "RemoveFromShoppingList", style: {color: "#d13438"}}} />}
|
||||||
</Stack>
|
</Stack>
|
||||||
@ -272,18 +273,18 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
selectionMode={SelectionMode.multiple} />
|
selectionMode={SelectionMode.multiple} />
|
||||||
</MarqueeSelection>
|
</MarqueeSelection>
|
||||||
|
|
||||||
<span className="settings-hint">拖拽订阅源以排序</span>
|
<span className="settings-hint">{intl.get("groups.sourceHint")}</span>
|
||||||
</>}
|
</>}
|
||||||
{(!this.state.manageGroup || !this.state.selectedGroup)
|
{(!this.state.manageGroup || !this.state.selectedGroup)
|
||||||
?<>
|
?<>
|
||||||
<form onSubmit={this.createGroup}>
|
<form onSubmit={this.createGroup}>
|
||||||
<Label htmlFor="newGroupName">新建分组</Label>
|
<Label htmlFor="newGroupName">{intl.get("groups.create")}</Label>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<Stack.Item grow>
|
<Stack.Item grow>
|
||||||
<TextField
|
<TextField
|
||||||
onGetErrorMessage={v => v.trim().length == 0 ? "名称不得为空" : ""}
|
onGetErrorMessage={v => v.trim().length == 0 ? intl.get("emptyName") : ""}
|
||||||
validateOnLoad={false}
|
validateOnLoad={false}
|
||||||
placeholder="输入名称"
|
placeholder={intl.get("groups.enterName")}
|
||||||
value={this.state.newGroupName}
|
value={this.state.newGroupName}
|
||||||
id="newGroupName"
|
id="newGroupName"
|
||||||
name="newGroupName"
|
name="newGroupName"
|
||||||
@ -293,7 +294,7 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
disabled={this.state.newGroupName.length == 0}
|
disabled={this.state.newGroupName.length == 0}
|
||||||
type="sumbit"
|
type="sumbit"
|
||||||
text="新建" />
|
text={intl.get("create")} />
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
</Stack>
|
</Stack>
|
||||||
</form>
|
</form>
|
||||||
@ -301,7 +302,7 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
<DetailsList
|
<DetailsList
|
||||||
compact={true}
|
compact={true}
|
||||||
items={this.props.groups}
|
items={this.props.groups}
|
||||||
columns={this.groupColumns}
|
columns={this.groupColumns()}
|
||||||
setKey="selected"
|
setKey="selected"
|
||||||
onItemInvoked={this.manageGroup}
|
onItemInvoked={this.manageGroup}
|
||||||
dragDropEvents={this.groupDragDropEvents}
|
dragDropEvents={this.groupDragDropEvents}
|
||||||
@ -311,13 +312,13 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
{this.state.selectedGroup
|
{this.state.selectedGroup
|
||||||
? ( this.state.selectedGroup.isMultiple
|
? ( this.state.selectedGroup.isMultiple
|
||||||
?<>
|
?<>
|
||||||
<Label>选中分组</Label>
|
<Label>{intl.get("groups.selectedGroup")}</Label>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<Stack.Item grow>
|
<Stack.Item grow>
|
||||||
<TextField
|
<TextField
|
||||||
onGetErrorMessage={v => v.trim().length == 0 ? "名称不得为空" : ""}
|
onGetErrorMessage={v => v.trim().length == 0 ? intl.get("emptyName") : ""}
|
||||||
validateOnLoad={false}
|
validateOnLoad={false}
|
||||||
placeholder="分组名称"
|
placeholder={intl.get("groups.enterName")}
|
||||||
value={this.state.editGroupName}
|
value={this.state.editGroupName}
|
||||||
name="editGroupName"
|
name="editGroupName"
|
||||||
onChange={this.handleInputChange} />
|
onChange={this.handleInputChange} />
|
||||||
@ -326,22 +327,22 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
<DefaultButton
|
<DefaultButton
|
||||||
disabled={this.state.editGroupName.length == 0}
|
disabled={this.state.editGroupName.length == 0}
|
||||||
onClick={this.updateGroupName}
|
onClick={this.updateGroupName}
|
||||||
text="修改名称" />
|
text={intl.get("groups.editName")} />
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
<Stack.Item>
|
<Stack.Item>
|
||||||
<DangerButton
|
<DangerButton
|
||||||
key={this.state.selectedGroup.index}
|
key={this.state.selectedGroup.index}
|
||||||
onClick={this.deleteGroup}
|
onClick={this.deleteGroup}
|
||||||
text={`删除分组`} />
|
text={intl.get("groups.deleteGroup")} />
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
:<>
|
:<>
|
||||||
<Label>选中订阅源</Label>
|
<Label>{intl.get("groups.selectedSource")}</Label>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
<Stack.Item grow>
|
<Stack.Item grow>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
placeholder="选择分组"
|
placeholder={intl.get("groups.chooseGroup")}
|
||||||
selectedKey={this.state.dropdownIndex}
|
selectedKey={this.state.dropdownIndex}
|
||||||
options={this.dropdownOptions()}
|
options={this.dropdownOptions()}
|
||||||
onChange={this.dropdownChange} />
|
onChange={this.dropdownChange} />
|
||||||
@ -350,12 +351,12 @@ class GroupsTab extends React.Component<GroupsTabProps, GroupsTabState> {
|
|||||||
<DefaultButton
|
<DefaultButton
|
||||||
disabled={this.state.dropdownIndex === null}
|
disabled={this.state.dropdownIndex === null}
|
||||||
onClick={this.addToGroup}
|
onClick={this.addToGroup}
|
||||||
text="添加至分组" />
|
text={intl.get("groups.addToGroup")} />
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
: <span className="settings-hint">双击分组以修改订阅源,可通过拖拽排序</span>
|
: <span className="settings-hint">{intl.get("groups.groupHint")}</span>
|
||||||
}
|
}
|
||||||
</> : null}
|
</> : null}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import intl = require("react-intl-universal")
|
||||||
import { PrimaryButton } from "@fluentui/react";
|
import { PrimaryButton } from "@fluentui/react";
|
||||||
|
|
||||||
class DangerButton extends PrimaryButton {
|
class DangerButton extends PrimaryButton {
|
||||||
@ -35,7 +36,7 @@ class DangerButton extends PrimaryButton {
|
|||||||
{...this.props}
|
{...this.props}
|
||||||
className={this.props.className + " danger"}
|
className={this.props.className + " danger"}
|
||||||
onClick={this.onClick}
|
onClick={this.onClick}
|
||||||
text={this.state.confirming ? `确认${this.props.text}?` : this.props.text}
|
text={this.state.confirming ? intl.get("dangerButton", { action: this.props.text.toLowerCase() }) : this.props.text}
|
||||||
>
|
>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
|
@ -13,13 +13,15 @@ type ArticleContainerProps = {
|
|||||||
|
|
||||||
const getItem = (state: RootState, props: ArticleContainerProps) => state.items[props.itemId]
|
const getItem = (state: RootState, props: ArticleContainerProps) => state.items[props.itemId]
|
||||||
const getSource = (state: RootState, props: ArticleContainerProps) => state.sources[state.items[props.itemId].source]
|
const getSource = (state: RootState, props: ArticleContainerProps) => state.sources[state.items[props.itemId].source]
|
||||||
|
const getLocale = (state: RootState) => state.app.locale
|
||||||
|
|
||||||
const makeMapStateToProps = () => {
|
const makeMapStateToProps = () => {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
[getItem, getSource],
|
[getItem, getSource, getLocale],
|
||||||
(item, source) => ({
|
(item, source, locale) => ({
|
||||||
item: item,
|
item: item,
|
||||||
source: source
|
source: source,
|
||||||
|
locale: locale
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import intl = require("react-intl-universal")
|
||||||
import { remote } from "electron"
|
import { remote } from "electron"
|
||||||
import { connect } from "react-redux"
|
import { connect } from "react-redux"
|
||||||
import { createSelector } from "reselect"
|
import { createSelector } from "reselect"
|
||||||
@ -30,7 +31,7 @@ const mapDispatchToProps = (dispatch: AppDispatch) => {
|
|||||||
let path = remote.dialog.showOpenDialogSync(
|
let path = remote.dialog.showOpenDialogSync(
|
||||||
remote.getCurrentWindow(),
|
remote.getCurrentWindow(),
|
||||||
{
|
{
|
||||||
filters: [{ name: "OPML文件", extensions: ["xml", "opml"] }],
|
filters: [{ name: intl.get("sources.opmlFile"), extensions: ["xml", "opml"] }],
|
||||||
properties: ["openFile"]
|
properties: ["openFile"]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -5,8 +5,68 @@
|
|||||||
"icon": "Icon",
|
"icon": "Icon",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"openExternal": "Open externally",
|
"openExternal": "Open externally",
|
||||||
"emptyName": "This field cannot be empty",
|
"emptyName": "This field cannot be empty.",
|
||||||
"followSystem": "Follow system",
|
"followSystem": "Follow system",
|
||||||
|
"more": "More",
|
||||||
|
"close": "Close",
|
||||||
|
"search": "Search",
|
||||||
|
"loadMore": "Load more",
|
||||||
|
"dangerButton": "Confirm {action}?",
|
||||||
|
"log": {
|
||||||
|
"empty": "No notifications",
|
||||||
|
"fetchFailure": "Failed to load source \"{name}\".",
|
||||||
|
"fetchSuccess": "Successfully fetched {count, plural, =1 {# article} other {# articles}}."
|
||||||
|
},
|
||||||
|
"nav": {
|
||||||
|
"menu": "Menu",
|
||||||
|
"refresh": "Refresh",
|
||||||
|
"markAllRead": "Mark all as read",
|
||||||
|
"notifications": "Notifications",
|
||||||
|
"view": "View",
|
||||||
|
"settings": "Settings",
|
||||||
|
"minimize": "Minimize",
|
||||||
|
"maximize": "Maximize"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"close": "Close menu",
|
||||||
|
"subscriptions": "Subscriptions"
|
||||||
|
},
|
||||||
|
"article": {
|
||||||
|
"hide": "Hide article",
|
||||||
|
"unhide": "Unhide article",
|
||||||
|
"markRead": "Mark as read",
|
||||||
|
"markUnread": "Mark as unread",
|
||||||
|
"star": "Star",
|
||||||
|
"unstar": "Remove star",
|
||||||
|
"fontSize": "Font size",
|
||||||
|
"loadWebpage": "Load webpage"
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"read": "Read",
|
||||||
|
"copyTitle": "Copy title",
|
||||||
|
"copyURL": "Copy link",
|
||||||
|
"copy": "Copy",
|
||||||
|
"search": "Search \"{text}\" on Google",
|
||||||
|
"view": "View",
|
||||||
|
"cardView": "Card view",
|
||||||
|
"listView": "List view",
|
||||||
|
"filter": "Filtering",
|
||||||
|
"unreadOnly": "Unread only",
|
||||||
|
"starredOnly": "Starred only",
|
||||||
|
"showHidden": "Show hidden articles"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"name": "Settings",
|
||||||
|
"fetching": "Updating sources, please wait …",
|
||||||
|
"exit": "Exit settings",
|
||||||
|
"sources": "Sources",
|
||||||
|
"grouping": "Grouping",
|
||||||
|
"app": "Preferences",
|
||||||
|
"about": "About",
|
||||||
|
"version": "Version",
|
||||||
|
"openSource": "Open source",
|
||||||
|
"feedback": "Feedback"
|
||||||
|
},
|
||||||
"sources": {
|
"sources": {
|
||||||
"opmlFile": "OPML File",
|
"opmlFile": "OPML File",
|
||||||
"name": "Source name",
|
"name": "Source name",
|
||||||
@ -20,9 +80,27 @@
|
|||||||
"loadWebpage": "Load webpage",
|
"loadWebpage": "Load webpage",
|
||||||
"inputUrl": "Enter URL",
|
"inputUrl": "Enter URL",
|
||||||
"badUrl": "Invalid URL",
|
"badUrl": "Invalid URL",
|
||||||
"deleteWarning": "The source and all saved articles will be removed",
|
"deleteWarning": "The source and all saved articles will be removed.",
|
||||||
"selected": "Selected source"
|
"selected": "Selected source"
|
||||||
},
|
},
|
||||||
|
"groups": {
|
||||||
|
"type": "Type",
|
||||||
|
"group": "Group",
|
||||||
|
"source": "Source",
|
||||||
|
"capacity": "Capacity",
|
||||||
|
"exitGroup": "Back to groups",
|
||||||
|
"deleteSource": "Delete from group",
|
||||||
|
"sourceHint": "Drag and drop sources to reorder.",
|
||||||
|
"create": "Create group",
|
||||||
|
"selectedGroup": "Selected group",
|
||||||
|
"selectedSource": "Selected source",
|
||||||
|
"enterName": "Enter name",
|
||||||
|
"editName": "Edit name",
|
||||||
|
"deleteGroup": "Delete group",
|
||||||
|
"chooseGroup": "Select a group",
|
||||||
|
"addToGroup": "Add to ...",
|
||||||
|
"groupHint": "Double click on group to edit sources. Drag and drop to reorder."
|
||||||
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"language": "Display language",
|
"language": "Display language",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
|
@ -7,6 +7,66 @@
|
|||||||
"openExternal": "在浏览器中打开",
|
"openExternal": "在浏览器中打开",
|
||||||
"emptyName": "名称不得为空",
|
"emptyName": "名称不得为空",
|
||||||
"followSystem": "跟随系统",
|
"followSystem": "跟随系统",
|
||||||
|
"more": "更多",
|
||||||
|
"close": "关闭",
|
||||||
|
"search": "搜索",
|
||||||
|
"loadMore": "加载更多",
|
||||||
|
"dangerButton": "确认{action}?",
|
||||||
|
"log": {
|
||||||
|
"empty": "无消息",
|
||||||
|
"fetchFailure": "无法加载订阅源“{name}”",
|
||||||
|
"fetchSuccess": "成功加载 {count} 篇文章"
|
||||||
|
},
|
||||||
|
"nav": {
|
||||||
|
"menu": "菜单",
|
||||||
|
"refresh": "刷新",
|
||||||
|
"markAllRead": "全部标为已读",
|
||||||
|
"notifications": "消息",
|
||||||
|
"view": "视图",
|
||||||
|
"settings": "选项",
|
||||||
|
"minimize": "最小化",
|
||||||
|
"maximize": "最大化"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"close": "关闭菜单",
|
||||||
|
"subscriptions": "订阅源"
|
||||||
|
},
|
||||||
|
"article": {
|
||||||
|
"hide": "隐藏文章",
|
||||||
|
"unhide": "取消隐藏",
|
||||||
|
"markRead": "标为已读",
|
||||||
|
"markUnread": "标为未读",
|
||||||
|
"star": "标为星标",
|
||||||
|
"unstar": "取消星标",
|
||||||
|
"fontSize": "字体大小",
|
||||||
|
"loadWebpage": "加载网页"
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"read": "阅读",
|
||||||
|
"copyTitle": "复制标题",
|
||||||
|
"copyURL": "复制链接",
|
||||||
|
"copy": "复制",
|
||||||
|
"search": "使用Google搜索“{text}”",
|
||||||
|
"view": "视图",
|
||||||
|
"cardView": "卡片视图",
|
||||||
|
"listView": "列表视图",
|
||||||
|
"filter": "筛选",
|
||||||
|
"unreadOnly": "仅未读文章",
|
||||||
|
"starredOnly": "仅星标文章",
|
||||||
|
"showHidden": "显示隐藏文章"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"name": "选项",
|
||||||
|
"fetching": "正在更新订阅源,请稍候…",
|
||||||
|
"exit": "退出选项",
|
||||||
|
"sources": "订阅源",
|
||||||
|
"grouping": "分组与排序",
|
||||||
|
"app": "应用偏好",
|
||||||
|
"about": "关于",
|
||||||
|
"version": "版本",
|
||||||
|
"openSource": "开源项目",
|
||||||
|
"feedback": "反馈"
|
||||||
|
},
|
||||||
"sources": {
|
"sources": {
|
||||||
"opmlFile": "OPML文件",
|
"opmlFile": "OPML文件",
|
||||||
"name": "订阅源名称",
|
"name": "订阅源名称",
|
||||||
@ -23,6 +83,24 @@
|
|||||||
"deleteWarning": "这将移除此订阅源与所有已保存的文章",
|
"deleteWarning": "这将移除此订阅源与所有已保存的文章",
|
||||||
"selected": "选中订阅源"
|
"selected": "选中订阅源"
|
||||||
},
|
},
|
||||||
|
"groups": {
|
||||||
|
"type": "类型",
|
||||||
|
"group": "分组",
|
||||||
|
"source": "订阅源",
|
||||||
|
"capacity": "容量",
|
||||||
|
"exitGroup": "退出分组",
|
||||||
|
"deleteSource": "从分组删除订阅源",
|
||||||
|
"sourceHint": "拖拽订阅源以排序",
|
||||||
|
"create": "新建分组",
|
||||||
|
"selectedGroup": "选中分组",
|
||||||
|
"selectedSource": "选中订阅源",
|
||||||
|
"enterName": "输入名称",
|
||||||
|
"editName": "修改名称",
|
||||||
|
"deleteGroup": "删除分组",
|
||||||
|
"chooseGroup": "选择分组",
|
||||||
|
"addToGroup": "添加至分组",
|
||||||
|
"groupHint": "双击分组以修改订阅源,可通过拖拽排序"
|
||||||
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"language": "界面语言",
|
"language": "界面语言",
|
||||||
"theme": "应用主题",
|
"theme": "应用主题",
|
||||||
|
@ -39,7 +39,7 @@ export class AppState {
|
|||||||
fetchingTotal = 0
|
fetchingTotal = 0
|
||||||
menu = getWindowBreakpoint()
|
menu = getWindowBreakpoint()
|
||||||
menuKey = ALL
|
menuKey = ALL
|
||||||
title = "全部文章"
|
title = ""
|
||||||
settings = {
|
settings = {
|
||||||
display: false,
|
display: false,
|
||||||
changed: false,
|
changed: false,
|
||||||
@ -163,21 +163,27 @@ export const initIntlDone = (locale: string): InitIntlAction => ({
|
|||||||
locale: locale
|
locale: locale
|
||||||
})
|
})
|
||||||
|
|
||||||
export function initIntl(): AppThunk {
|
export function initIntl(): AppThunk<Promise<void>> {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
let locale = getCurrentLocale()
|
let locale = getCurrentLocale()
|
||||||
intl.init({
|
return intl.init({
|
||||||
currentLocale: locale,
|
currentLocale: locale,
|
||||||
locales: locales,
|
locales: locales,
|
||||||
fallbackLocale: "zh-CN"
|
fallbackLocale: "zh-CN"
|
||||||
}).then(() => dispatch(initIntlDone(locale)))
|
}).then(() => { dispatch(initIntlDone(locale)) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initApp(): AppThunk {
|
export function initApp(): AppThunk {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(initSources()).then(() => dispatch(initFeeds())).then(() => dispatch(fetchItems()))
|
dispatch(initIntl()).then(() =>
|
||||||
dispatch(initIntl())
|
dispatch(initSources())
|
||||||
|
).then(() =>
|
||||||
|
dispatch(initFeeds())
|
||||||
|
).then(() => {
|
||||||
|
dispatch(selectAllArticles())
|
||||||
|
dispatch(fetchItems())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +263,7 @@ export function appReducer(
|
|||||||
notify: !state.logMenu.display,
|
notify: !state.logMenu.display,
|
||||||
logs: [...state.logMenu.logs, new AppLog(
|
logs: [...state.logMenu.logs, new AppLog(
|
||||||
AppLogType.Failure,
|
AppLogType.Failure,
|
||||||
`无法加载订阅源“${action.errSource.name}”`,
|
intl.get("log.fetchFailure", { name: action.errSource.name }),
|
||||||
String(action.err)
|
String(action.err)
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
@ -270,7 +276,7 @@ export function appReducer(
|
|||||||
...state.logMenu,
|
...state.logMenu,
|
||||||
logs: [...state.logMenu.logs, new AppLog(
|
logs: [...state.logMenu.logs, new AppLog(
|
||||||
AppLogType.Info,
|
AppLogType.Info,
|
||||||
`成功加载 ${action.items.length} 篇文章`
|
intl.get("log.fetchSuccess", { count: action.items.length })
|
||||||
)]
|
)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,7 +292,7 @@ export function appReducer(
|
|||||||
...state,
|
...state,
|
||||||
menu: state.menu && action.keepMenu,
|
menu: state.menu && action.keepMenu,
|
||||||
menuKey: ALL,
|
menuKey: ALL,
|
||||||
title: "全部文章"
|
title: intl.get("allArticles")
|
||||||
}
|
}
|
||||||
case PageType.Sources: return {
|
case PageType.Sources: return {
|
||||||
...state,
|
...state,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user