mark below or above as read

This commit is contained in:
刘浩远 2020-07-08 19:57:12 +08:00
parent debf45e4d6
commit ce026aec9c
7 changed files with 94 additions and 47 deletions

2
dist/styles.css vendored
View File

@ -52,7 +52,7 @@ html, body {
height: 100%;
}
.ms-ContextualMenu-link, .ms-Button {
.ms-ContextualMenu-link, .ms-Button, .ms-ContextualMenu-item button {
cursor: default;
font-size: 13px;
user-select: none;

View File

@ -27,7 +27,7 @@ export type ContextMenuProps = ContextReduxProps & {
switchView: (viewType: ViewType) => void
switchFilter: (filter: FilterType) => void
toggleFilter: (filter: FilterType) => void
markAllRead: (sids: number[]) => void
markAllRead: (sids: number[], date?: Date, before?: boolean) => void
settings: () => void
close: () => void
}
@ -67,18 +67,33 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
window.utils.openExternal(this.props.item.link)
}
},
this.props.item.hasRead
? {
key: "markAsUnread",
text: intl.get("article.markUnread"),
iconProps: { iconName: "RadioBtnOn", style: { fontSize: 14, textAlign: "center" } },
onClick: () => { this.props.markUnread(this.props.item) }
}
: {
{
key: "markAsRead",
text: intl.get("article.markRead"),
iconProps: { iconName: "StatusCircleRing" },
onClick: () => { this.props.markRead(this.props.item) }
text: this.props.item.hasRead ? intl.get("article.markUnread") : intl.get("article.markRead"),
iconProps: this.props.item.hasRead
? { iconName: "RadioBtnOn", style: { fontSize: 14, textAlign: "center" } }
: { iconName: "StatusCircleRing" },
onClick: () => {
if (this.props.item.hasRead) this.props.markUnread(this.props.item)
else this.props.markRead(this.props.item)
},
split: true,
subMenuProps: {
items: [
{
key: "markBelow",
text: intl.get("article.markBelow"),
iconProps: { iconName: "Down", style: { fontSize: 14 } },
onClick: () => this.props.markAllRead(null, this.props.item.date)
},
{
key: "markAbove",
text: intl.get("article.markAbove"),
iconProps: { iconName: "Up", style: { fontSize: 14 } },
onClick: () => this.props.markAllRead(null, this.props.item.date, false)
}
]
}
},
{
key: "toggleStarred",

View File

@ -62,7 +62,9 @@ const mapDispatchToProps = dispatch => {
},
switchFilter: (filter: FilterType) => dispatch(switchFilter(filter)),
toggleFilter: (filter: FilterType) => dispatch(toggleFilter(filter)),
markAllRead: (sids: number[]) => dispatch(markAllRead(sids)),
markAllRead: (sids: number[], date?: Date, before?: boolean) => {
dispatch(markAllRead(sids, date, before))
},
settings: () => dispatch(toggleSettings()),
close: () => dispatch(closeContextMenu())
}

View File

@ -55,6 +55,8 @@
"unhide": "Unhide article",
"markRead": "Mark as read",
"markUnread": "Mark as unread",
"markAbove": "Mark above as read",
"markBelow": "Mark below as read",
"star": "Star",
"unstar": "Remove star",
"fontSize": "Font size",

View File

@ -55,6 +55,8 @@
"unhide": "取消隐藏",
"markRead": "标为已读",
"markUnread": "标为未读",
"markAbove": "将以上标为已读",
"markBelow": "将以下标为已读",
"star": "标为星标",
"unstar": "取消星标",
"fontSize": "字体大小",

View File

@ -91,6 +91,9 @@ interface MarkReadAction {
interface MarkAllReadAction {
type: typeof MARK_ALL_READ,
sids: number[]
counts?: number[]
time?: number
before?: boolean
}
interface MarkUnreadAction {
@ -203,11 +206,6 @@ const markReadDone = (item: RSSItem): ItemActionTypes => ({
item: item
})
const markAllReadDone = (sids: number[]): ItemActionTypes => ({
type: MARK_ALL_READ,
sids: sids
})
const markUnreadDone = (item: RSSItem): ItemActionTypes => ({
type: MARK_UNREAD,
item: item
@ -222,23 +220,52 @@ export function markRead(item: RSSItem): AppThunk {
}
}
export function markAllRead(sids: number[] = null): AppThunk {
export function markAllRead(sids: number[] = null, date: Date = null, before = true): AppThunk {
return (dispatch, getState) => {
let state = getState()
if (sids === null) {
let feed = state.feeds[state.page.feedId]
sids = feed.sids
}
let query = { source: { $in: sids } }
db.idb.update(query, { $set: { hasRead: true } }, { multi: true }, (err) => {
if (err) {
console.log(err)
}
})
dispatch(markAllReadDone(sids))
if (!(state.page.filter.type & FilterType.ShowRead)) {
dispatch(initFeeds(true))
let query = {
source: { $in: sids },
hasRead: false,
} as any
if (date) {
query.date = before ? { $lte: date } : { $gte: date }
}
const callback = (items: RSSItem[] = null) => {
if (items) {
const counts = new Map<number, number>()
for (let sid of sids) {
counts.set(sid, 0)
}
for (let item of items) {
counts.set(item.source, counts.get(item.source) + 1)
}
dispatch({
type: MARK_ALL_READ,
sids: sids,
counts: sids.map(i => counts.get(i)),
time: date.getTime(),
before: before
})
} else {
dispatch({
type: MARK_ALL_READ,
sids: sids
})
}
if (!(state.page.filter.type & FilterType.ShowRead)) {
dispatch(initFeeds(true))
}
}
db.idb.update(query, { $set: { hasRead: true } }, { multi: true, returnUpdatedDocs: Boolean(date) },
(err, _, affectedDocuments) => {
if (err) console.log(err)
if (date) callback(affectedDocuments as unknown as RSSItem[])
})
if (!date) callback()
}
}
@ -352,16 +379,19 @@ export function itemReducer(
}
}
case MARK_ALL_READ: {
let nextState = {} as ItemState
let nextState = { ...state }
let sids = new Set(action.sids)
for (let [id, item] of Object.entries(state)) {
if (sids.has(item.source) && !item.hasRead) {
nextState[id] = {
...item,
hasRead: true
if (!action.time || (action.before
? item.date.getTime() <= action.time
: item.date.getTime() >= action.time)
) {
nextState[id] = {
...item,
hasRead: true
}
}
} else {
nextState[id] = item
}
}
return nextState

View File

@ -397,19 +397,15 @@ export function sourceReducer(
} as RSSSource
}
case MARK_ALL_READ: {
let nextState = {} as SourceState
let sids = new Set(action.sids)
for (let [s, source] of Object.entries(state)) {
let sid = parseInt(s)
if (sids.has(sid) && source.unreadCount > 0) {
nextState[sid] = {
...source,
unreadCount: 0
} as RSSSource
} else {
nextState[sid] = source
let nextState = { ...state }
action.sids.map((sid, i) => {
nextState[sid] = {
...state[sid],
unreadCount: action.counts
? state[sid].unreadCount - action.counts[i]
: 0
}
}
})
return nextState
}
default: return state