mirror of
https://github.com/yang991178/fluent-reader.git
synced 2025-04-23 14:47:23 +02:00
mark below or above as read
This commit is contained in:
parent
debf45e4d6
commit
ce026aec9c
2
dist/styles.css
vendored
2
dist/styles.css
vendored
@ -52,7 +52,7 @@ html, body {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ms-ContextualMenu-link, .ms-Button {
|
.ms-ContextualMenu-link, .ms-Button, .ms-ContextualMenu-item button {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
@ -27,7 +27,7 @@ export type ContextMenuProps = ContextReduxProps & {
|
|||||||
switchView: (viewType: ViewType) => void
|
switchView: (viewType: ViewType) => void
|
||||||
switchFilter: (filter: FilterType) => void
|
switchFilter: (filter: FilterType) => void
|
||||||
toggleFilter: (filter: FilterType) => void
|
toggleFilter: (filter: FilterType) => void
|
||||||
markAllRead: (sids: number[]) => void
|
markAllRead: (sids: number[], date?: Date, before?: boolean) => void
|
||||||
settings: () => void
|
settings: () => void
|
||||||
close: () => void
|
close: () => void
|
||||||
}
|
}
|
||||||
@ -67,18 +67,33 @@ export class ContextMenu extends React.Component<ContextMenuProps> {
|
|||||||
window.utils.openExternal(this.props.item.link)
|
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",
|
key: "markAsRead",
|
||||||
text: intl.get("article.markRead"),
|
text: this.props.item.hasRead ? intl.get("article.markUnread") : intl.get("article.markRead"),
|
||||||
iconProps: { iconName: "StatusCircleRing" },
|
iconProps: this.props.item.hasRead
|
||||||
onClick: () => { this.props.markRead(this.props.item) }
|
? { 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",
|
key: "toggleStarred",
|
||||||
|
@ -62,7 +62,9 @@ const mapDispatchToProps = dispatch => {
|
|||||||
},
|
},
|
||||||
switchFilter: (filter: FilterType) => dispatch(switchFilter(filter)),
|
switchFilter: (filter: FilterType) => dispatch(switchFilter(filter)),
|
||||||
toggleFilter: (filter: FilterType) => dispatch(toggleFilter(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()),
|
settings: () => dispatch(toggleSettings()),
|
||||||
close: () => dispatch(closeContextMenu())
|
close: () => dispatch(closeContextMenu())
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,8 @@
|
|||||||
"unhide": "Unhide article",
|
"unhide": "Unhide article",
|
||||||
"markRead": "Mark as read",
|
"markRead": "Mark as read",
|
||||||
"markUnread": "Mark as unread",
|
"markUnread": "Mark as unread",
|
||||||
|
"markAbove": "Mark above as read",
|
||||||
|
"markBelow": "Mark below as read",
|
||||||
"star": "Star",
|
"star": "Star",
|
||||||
"unstar": "Remove star",
|
"unstar": "Remove star",
|
||||||
"fontSize": "Font size",
|
"fontSize": "Font size",
|
||||||
|
@ -55,6 +55,8 @@
|
|||||||
"unhide": "取消隐藏",
|
"unhide": "取消隐藏",
|
||||||
"markRead": "标为已读",
|
"markRead": "标为已读",
|
||||||
"markUnread": "标为未读",
|
"markUnread": "标为未读",
|
||||||
|
"markAbove": "将以上标为已读",
|
||||||
|
"markBelow": "将以下标为已读",
|
||||||
"star": "标为星标",
|
"star": "标为星标",
|
||||||
"unstar": "取消星标",
|
"unstar": "取消星标",
|
||||||
"fontSize": "字体大小",
|
"fontSize": "字体大小",
|
||||||
|
@ -91,6 +91,9 @@ interface MarkReadAction {
|
|||||||
interface MarkAllReadAction {
|
interface MarkAllReadAction {
|
||||||
type: typeof MARK_ALL_READ,
|
type: typeof MARK_ALL_READ,
|
||||||
sids: number[]
|
sids: number[]
|
||||||
|
counts?: number[]
|
||||||
|
time?: number
|
||||||
|
before?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MarkUnreadAction {
|
interface MarkUnreadAction {
|
||||||
@ -203,11 +206,6 @@ const markReadDone = (item: RSSItem): ItemActionTypes => ({
|
|||||||
item: item
|
item: item
|
||||||
})
|
})
|
||||||
|
|
||||||
const markAllReadDone = (sids: number[]): ItemActionTypes => ({
|
|
||||||
type: MARK_ALL_READ,
|
|
||||||
sids: sids
|
|
||||||
})
|
|
||||||
|
|
||||||
const markUnreadDone = (item: RSSItem): ItemActionTypes => ({
|
const markUnreadDone = (item: RSSItem): ItemActionTypes => ({
|
||||||
type: MARK_UNREAD,
|
type: MARK_UNREAD,
|
||||||
item: item
|
item: item
|
||||||
@ -222,24 +220,53 @@ 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) => {
|
return (dispatch, getState) => {
|
||||||
let state = getState()
|
let state = getState()
|
||||||
if (sids === null) {
|
if (sids === null) {
|
||||||
let feed = state.feeds[state.page.feedId]
|
let feed = state.feeds[state.page.feedId]
|
||||||
sids = feed.sids
|
sids = feed.sids
|
||||||
}
|
}
|
||||||
let query = { source: { $in: sids } }
|
let query = {
|
||||||
db.idb.update(query, { $set: { hasRead: true } }, { multi: true }, (err) => {
|
source: { $in: sids },
|
||||||
if (err) {
|
hasRead: false,
|
||||||
console.log(err)
|
} 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
|
||||||
})
|
})
|
||||||
dispatch(markAllReadDone(sids))
|
} else {
|
||||||
|
dispatch({
|
||||||
|
type: MARK_ALL_READ,
|
||||||
|
sids: sids
|
||||||
|
})
|
||||||
|
}
|
||||||
if (!(state.page.filter.type & FilterType.ShowRead)) {
|
if (!(state.page.filter.type & FilterType.ShowRead)) {
|
||||||
dispatch(initFeeds(true))
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function markUnread(item: RSSItem): AppThunk {
|
export function markUnread(item: RSSItem): AppThunk {
|
||||||
@ -352,16 +379,19 @@ export function itemReducer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case MARK_ALL_READ: {
|
case MARK_ALL_READ: {
|
||||||
let nextState = {} as ItemState
|
let nextState = { ...state }
|
||||||
let sids = new Set(action.sids)
|
let sids = new Set(action.sids)
|
||||||
for (let [id, item] of Object.entries(state)) {
|
for (let [id, item] of Object.entries(state)) {
|
||||||
if (sids.has(item.source) && !item.hasRead) {
|
if (sids.has(item.source) && !item.hasRead) {
|
||||||
|
if (!action.time || (action.before
|
||||||
|
? item.date.getTime() <= action.time
|
||||||
|
: item.date.getTime() >= action.time)
|
||||||
|
) {
|
||||||
nextState[id] = {
|
nextState[id] = {
|
||||||
...item,
|
...item,
|
||||||
hasRead: true
|
hasRead: true
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
nextState[id] = item
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nextState
|
return nextState
|
||||||
|
@ -397,19 +397,15 @@ export function sourceReducer(
|
|||||||
} as RSSSource
|
} as RSSSource
|
||||||
}
|
}
|
||||||
case MARK_ALL_READ: {
|
case MARK_ALL_READ: {
|
||||||
let nextState = {} as SourceState
|
let nextState = { ...state }
|
||||||
let sids = new Set(action.sids)
|
action.sids.map((sid, i) => {
|
||||||
for (let [s, source] of Object.entries(state)) {
|
|
||||||
let sid = parseInt(s)
|
|
||||||
if (sids.has(sid) && source.unreadCount > 0) {
|
|
||||||
nextState[sid] = {
|
nextState[sid] = {
|
||||||
...source,
|
...state[sid],
|
||||||
unreadCount: 0
|
unreadCount: action.counts
|
||||||
} as RSSSource
|
? state[sid].unreadCount - action.counts[i]
|
||||||
} else {
|
: 0
|
||||||
nextState[sid] = source
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
return nextState
|
return nextState
|
||||||
}
|
}
|
||||||
default: return state
|
default: return state
|
||||||
|
Loading…
x
Reference in New Issue
Block a user