mark articles x days ago as read and #227 #232

This commit is contained in:
Bruce Liu 2021-05-15 16:05:08 -07:00
parent 7d2860fda1
commit a1d6a2aff5
15 changed files with 85 additions and 24 deletions

Binary file not shown.

View File

@ -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", {

View File

@ -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 []
}
}

View File

@ -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"

View File

@ -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 })

View File

@ -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)),

View File

@ -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)

View File

@ -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() } }
]
},

View File

@ -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")

View File

@ -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",

View File

@ -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",

View File

@ -209,7 +209,7 @@
"cacheSize": "已缓存{size}数据",
"deleteChoices": "删除 … 天前的文章",
"confirmDelete": "删除文章",
"daysAgo": "{days}天前",
"daysAgo": "{days} 天前",
"deleteAll": "删除全部文章",
"calculatingSize": "正在计算占用空间…",
"itemSize": "本地文章约占用{size}空间",

View File

@ -209,7 +209,7 @@
"cacheSize": "已快取{size}資料",
"deleteChoices": "刪除 … 天前的文章",
"confirmDelete": "刪除文章",
"daysAgo": "{days}天前",
"daysAgo": "{days} 天前",
"deleteAll": "刪除全部文章",
"calculatingSize": "正在計算佔用空間…",
"itemSize": "本地文章約佔用{size}空間",

View File

@ -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

View File

@ -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))