parent
7d2860fda1
commit
a1d6a2aff5
BIN
build/icon.icns
BIN
build/icon.icns
Binary file not shown.
|
@ -133,7 +133,7 @@ class Article extends React.Component<ArticleProps, ArticleState> {
|
|||
this.toggleFull()
|
||||
break
|
||||
case "H": case "h":
|
||||
this.props.toggleHidden(this.props.item)
|
||||
if (!input.meta) this.props.toggleHidden(this.props.item)
|
||||
break
|
||||
default:
|
||||
const keyboardEvent = new KeyboardEvent("keydown", {
|
||||
|
|
|
@ -30,7 +30,7 @@ export type ContextMenuProps = ContextReduxProps & {
|
|||
setViewConfigs: (configs: ViewConfigs) => void
|
||||
switchFilter: (filter: FilterType) => void
|
||||
toggleFilter: (filter: FilterType) => void
|
||||
markAllRead: (sids: number[], date?: Date, before?: boolean) => void
|
||||
markAllRead: (sids?: number[], date?: Date, before?: boolean) => void
|
||||
fetchItems: (sids: number[]) => void
|
||||
settings: (sids: number[]) => void
|
||||
close: () => void
|
||||
|
@ -380,6 +380,50 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||
onClick: () => this.props.settings(this.props.sids)
|
||||
}
|
||||
]
|
||||
case ContextMenuType.MarkRead: return [
|
||||
{
|
||||
key: "section_1",
|
||||
itemType: ContextualMenuItemType.Section,
|
||||
sectionProps: {
|
||||
title: intl.get("nav.markAllRead"),
|
||||
items: [
|
||||
{
|
||||
key: "all",
|
||||
text: intl.get("allArticles"),
|
||||
iconProps: { iconName: "ReceiptCheck" },
|
||||
onClick: () => this.props.markAllRead()
|
||||
},
|
||||
{
|
||||
key: "1d",
|
||||
text: intl.get("app.daysAgo", { days: 1 }),
|
||||
onClick: () => {
|
||||
let date = new Date()
|
||||
date.setTime(date.getTime() - 86400000)
|
||||
this.props.markAllRead(null, date)
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "3d",
|
||||
text: intl.get("app.daysAgo", { days: 3 }),
|
||||
onClick: () => {
|
||||
let date = new Date()
|
||||
date.setTime(date.getTime() - 3 * 86400000)
|
||||
this.props.markAllRead(null, date)
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "7d",
|
||||
text: intl.get("app.daysAgo", { days: 7 }),
|
||||
onClick: () => {
|
||||
let date = new Date()
|
||||
date.setTime(date.getTime() - 7 * 86400000)
|
||||
this.props.markAllRead(null, date)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
default: return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,9 +139,12 @@ class Nav extends React.Component<NavProps, NavState> {
|
|||
title={intl.get("nav.refresh")}>
|
||||
<Icon iconName="Refresh" />
|
||||
</a>
|
||||
<a className="btn"
|
||||
<a className="btn"
|
||||
id="mark-all-toggle"
|
||||
onClick={this.props.markAllRead}
|
||||
title={intl.get("nav.markAllRead")}>
|
||||
title={intl.get("nav.markAllRead")}
|
||||
onMouseDown={e => {
|
||||
if (this.props.state.contextMenu.event === "#mark-all-toggle") e.stopPropagation()}}>
|
||||
<Icon iconName="InboxCheck" />
|
||||
</a>
|
||||
<a className="btn"
|
||||
|
|
|
@ -48,6 +48,7 @@ class InoreaderConfigsTab extends React.Component<ServiceConfigsTabProps, GReade
|
|||
{ key: 500, text: intl.get("service.fetchLimitNum", { count: 500 }) },
|
||||
{ key: 750, text: intl.get("service.fetchLimitNum", { count: 750 }) },
|
||||
{ key: 1000, text: intl.get("service.fetchLimitNum", { count: 1000 }) },
|
||||
{ key: Number.MAX_SAFE_INTEGER, text: intl.get("service.fetchUnlimited") },
|
||||
]
|
||||
onFetchLimitOptionChange = (_, option: IDropdownOption) => {
|
||||
this.setState({ fetchLimit: option.key as number })
|
||||
|
|
|
@ -45,6 +45,10 @@ const mapStateToProps = createSelector(
|
|||
type: context.type,
|
||||
position: context.position
|
||||
}
|
||||
case ContextMenuType.MarkRead: return {
|
||||
type: context.type,
|
||||
event: context.event
|
||||
}
|
||||
default: return { type: ContextMenuType.Hidden }
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +74,7 @@ const mapDispatchToProps = dispatch => {
|
|||
setViewConfigs: (configs: ViewConfigs) => dispatch(setViewConfigs(configs)),
|
||||
switchFilter: (filter: FilterType) => dispatch(switchFilter(filter)),
|
||||
toggleFilter: (filter: FilterType) => dispatch(toggleFilter(filter)),
|
||||
markAllRead: (sids: number[], date?: Date, before?: boolean) => {
|
||||
markAllRead: (sids?: number[], date?: Date, before?: boolean) => {
|
||||
dispatch(markAllRead(sids, date, before))
|
||||
},
|
||||
fetchItems: (sids: number[]) => dispatch(fetchItems(false, sids)),
|
||||
|
|
|
@ -3,7 +3,7 @@ import { connect } from "react-redux"
|
|||
import { createSelector } from "reselect"
|
||||
import { RootState } from "../scripts/reducer"
|
||||
import { fetchItems, markAllRead } from "../scripts/models/item"
|
||||
import { toggleMenu, toggleLogMenu, toggleSettings, openViewMenu } from "../scripts/models/app"
|
||||
import { toggleMenu, toggleLogMenu, toggleSettings, openViewMenu, openMarkAllMenu } from "../scripts/models/app"
|
||||
import { toggleSearch } from "../scripts/models/page"
|
||||
import { ViewType } from "../schema-types"
|
||||
import Nav from "../components/nav"
|
||||
|
@ -26,17 +26,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
views: () => dispatch(openViewMenu()),
|
||||
settings: () => dispatch(toggleSettings()),
|
||||
search: () => dispatch(toggleSearch()),
|
||||
markAllRead: () => {
|
||||
window.utils.showMessageBox(
|
||||
intl.get("nav.markAllRead"),
|
||||
intl.get("confirmMarkAll"),
|
||||
intl.get("confirm"), intl.get("cancel")
|
||||
).then(response => {
|
||||
if (response) {
|
||||
dispatch(markAllRead())
|
||||
}
|
||||
})
|
||||
}
|
||||
markAllRead: () => dispatch(openMarkAllMenu())
|
||||
})
|
||||
|
||||
const NavContainer = connect(mapStateToProps, mapDispatchToProps)(Nav)
|
||||
|
|
|
@ -28,6 +28,7 @@ if (process.platform === "darwin") {
|
|||
{
|
||||
label: "Application",
|
||||
submenu: [
|
||||
{ label: "Hide", accelerator:"Command+H", click: () => { app.hide() } },
|
||||
{ label: "Quit", accelerator: "Command+Q", click: () => { if (winManager.hasWindow) winManager.mainWindow.close() } }
|
||||
]
|
||||
},
|
||||
|
|
|
@ -20,6 +20,7 @@ export function setUtilsListeners(manager: WindowManager) {
|
|||
}
|
||||
|
||||
app.on("web-contents-created", (_, contents) => {
|
||||
// TODO: Use contents.setWindowOpenHandler instead of new-window listener
|
||||
contents.on("new-window", (event, url, _, disposition) => {
|
||||
if (manager.hasWindow()) event.preventDefault()
|
||||
if (contents.getType() === "webview") openExternal(url, disposition === "background-tab")
|
||||
|
|
|
@ -211,7 +211,7 @@
|
|||
"cacheSize": "Cached {size} of data",
|
||||
"deleteChoices": "Delete articles from ... days ago",
|
||||
"confirmDelete": "Delete",
|
||||
"daysAgo": "{days} days ago",
|
||||
"daysAgo": "{days, plural, =1 {# day} other {# days}} ago",
|
||||
"deleteAll": "Delete all articles",
|
||||
"calculatingSize": "Calculating size...",
|
||||
"itemSize": "Around {size} of local storage is occupied by articles",
|
||||
|
|
|
@ -208,7 +208,7 @@
|
|||
"cacheSize": "{size} de données mises en cache",
|
||||
"deleteChoices": "Supprimer les articles antérieurs à ... jours",
|
||||
"confirmDelete": "Supprimer",
|
||||
"daysAgo": "{days} jours",
|
||||
"daysAgo": "{days, plural, =1 {# jour} other {# jours}}",
|
||||
"deleteAll": "Supprimer tous les articles",
|
||||
"calculatingSize": "Calcul de la taille...",
|
||||
"itemSize": "Environ {size} du stockage local est occupé par des articles",
|
||||
|
|
|
@ -209,7 +209,7 @@
|
|||
"cacheSize": "已缓存{size}数据",
|
||||
"deleteChoices": "删除 … 天前的文章",
|
||||
"confirmDelete": "删除文章",
|
||||
"daysAgo": "{days}天前",
|
||||
"daysAgo": "{days} 天前",
|
||||
"deleteAll": "删除全部文章",
|
||||
"calculatingSize": "正在计算占用空间…",
|
||||
"itemSize": "本地文章约占用{size}空间",
|
||||
|
|
|
@ -209,7 +209,7 @@
|
|||
"cacheSize": "已快取{size}資料",
|
||||
"deleteChoices": "刪除 … 天前的文章",
|
||||
"confirmDelete": "刪除文章",
|
||||
"daysAgo": "{days}天前",
|
||||
"daysAgo": "{days} 天前",
|
||||
"deleteAll": "刪除全部文章",
|
||||
"calculatingSize": "正在計算佔用空間…",
|
||||
"itemSize": "本地文章約佔用{size}空間",
|
||||
|
|
|
@ -7,11 +7,10 @@ import { SourceGroupActionTypes, UPDATE_SOURCE_GROUP, ADD_SOURCE_TO_GROUP, DELET
|
|||
import { PageActionTypes, SELECT_PAGE, PageType, selectAllArticles, showItemFromId } from "./page"
|
||||
import { getCurrentLocale } from "../settings"
|
||||
import locales from "../i18n/_locales"
|
||||
import * as db from "../db"
|
||||
import { SYNC_SERVICE, ServiceActionTypes } from "./service"
|
||||
|
||||
export const enum ContextMenuType {
|
||||
Hidden, Item, Text, View, Group, Image
|
||||
Hidden, Item, Text, View, Group, Image, MarkRead
|
||||
}
|
||||
|
||||
export const enum AppLogType {
|
||||
|
@ -78,6 +77,7 @@ export const OPEN_TEXT_MENU = "OPEN_TEXT_MENU"
|
|||
export const OPEN_VIEW_MENU = "OPEN_VIEW_MENU"
|
||||
export const OPEN_GROUP_MENU = "OPEN_GROUP_MENU"
|
||||
export const OPEN_IMAGE_MENU = "OPEN_IMAGE_MENU"
|
||||
export const OPEN_MARK_ALL_MENU = "OPEN_MARK_ALL_MENU"
|
||||
|
||||
interface CloseContextMenuAction {
|
||||
type: typeof CLOSE_CONTEXT_MENU
|
||||
|
@ -100,6 +100,10 @@ interface OpenViewMenuAction {
|
|||
type: typeof OPEN_VIEW_MENU
|
||||
}
|
||||
|
||||
interface OpenMarkAllMenuAction {
|
||||
type: typeof OPEN_MARK_ALL_MENU
|
||||
}
|
||||
|
||||
interface OpenGroupMenuAction {
|
||||
type: typeof OPEN_GROUP_MENU
|
||||
event: MouseEvent
|
||||
|
@ -113,6 +117,7 @@ interface OpenImageMenuAction {
|
|||
|
||||
export type ContextMenuActionTypes = CloseContextMenuAction | OpenItemMenuAction
|
||||
| OpenTextMenuAction | OpenViewMenuAction | OpenGroupMenuAction | OpenImageMenuAction
|
||||
| OpenMarkAllMenuAction
|
||||
|
||||
export const TOGGLE_LOGS = "TOGGLE_LOGS"
|
||||
export const PUSH_NOTIFICATION = "PUSH_NOTIFICATION"
|
||||
|
@ -194,6 +199,8 @@ export function openImageMenu(position: [number, number]): ContextMenuActionType
|
|||
}
|
||||
}
|
||||
|
||||
export const openMarkAllMenu = (): ContextMenuActionTypes => ({ type: OPEN_MARK_ALL_MENU })
|
||||
|
||||
export function toggleMenu(): AppThunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: TOGGLE_MENU })
|
||||
|
@ -486,7 +493,8 @@ export function appReducer(
|
|||
case OPEN_VIEW_MENU: return {
|
||||
...state,
|
||||
contextMenu: {
|
||||
type: ContextMenuType.View,
|
||||
type: state.contextMenu.type === ContextMenuType.View
|
||||
? ContextMenuType.Hidden : ContextMenuType.View,
|
||||
event: "#view-toggle"
|
||||
}
|
||||
}
|
||||
|
@ -505,6 +513,14 @@ export function appReducer(
|
|||
position: action.position
|
||||
}
|
||||
}
|
||||
case OPEN_MARK_ALL_MENU: return {
|
||||
...state,
|
||||
contextMenu: {
|
||||
type: state.contextMenu.type === ContextMenuType.MarkRead
|
||||
? ContextMenuType.Hidden : ContextMenuType.MarkRead,
|
||||
event: "#mark-all-toggle"
|
||||
}
|
||||
}
|
||||
case TOGGLE_MENU: return {
|
||||
...state,
|
||||
menu: !state.menu
|
||||
|
|
|
@ -329,6 +329,7 @@ export function toggleHidden(item: RSSItem): AppThunk {
|
|||
|
||||
export function itemShortcuts(item: RSSItem, e: KeyboardEvent): AppThunk {
|
||||
return (dispatch) => {
|
||||
if (e.metaKey) return
|
||||
switch (e.key) {
|
||||
case "m": case "M":
|
||||
if (item.hasRead) dispatch(markUnread(item))
|
||||
|
|
Loading…
Reference in New Issue