mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
add "mark as read up to here"
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
|||||||
LoginRequest,
|
LoginRequest,
|
||||||
MarkRequest,
|
MarkRequest,
|
||||||
Metrics,
|
Metrics,
|
||||||
|
MultipleMarkRequest,
|
||||||
PasswordResetRequest,
|
PasswordResetRequest,
|
||||||
ProfileModificationRequest,
|
ProfileModificationRequest,
|
||||||
RegistrationRequest,
|
RegistrationRequest,
|
||||||
@@ -45,6 +46,7 @@ export const client = {
|
|||||||
},
|
},
|
||||||
entry: {
|
entry: {
|
||||||
mark: (req: MarkRequest) => axiosInstance.post("entry/mark", req),
|
mark: (req: MarkRequest) => axiosInstance.post("entry/mark", req),
|
||||||
|
markMultiple: (req: MultipleMarkRequest) => axiosInstance.post("entry/markMultiple", req),
|
||||||
star: (req: StarRequest) => axiosInstance.post("entry/star", req),
|
star: (req: StarRequest) => axiosInstance.post("entry/star", req),
|
||||||
},
|
},
|
||||||
feed: {
|
feed: {
|
||||||
|
|||||||
@@ -79,6 +79,34 @@ export const markEntry = createAsyncThunk(
|
|||||||
condition: arg => arg.entry.read !== arg.read,
|
condition: arg => arg.entry.read !== arg.read,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
export const markMultipleEntries = createAsyncThunk(
|
||||||
|
"entries/entry/markMultiple",
|
||||||
|
async (arg: { entries: Entry[]; read: boolean }, thunkApi) => {
|
||||||
|
const requests: MarkRequest[] = arg.entries.map(e => ({
|
||||||
|
id: e.id,
|
||||||
|
read: arg.read,
|
||||||
|
}))
|
||||||
|
await client.entry.markMultiple({ requests })
|
||||||
|
thunkApi.dispatch(reloadTree())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
export const markEntriesUpToEntry = createAsyncThunk<void, Entry, { state: RootState }>(
|
||||||
|
"entries/entry/upToEntry",
|
||||||
|
async (arg, thunkApi) => {
|
||||||
|
const state = thunkApi.getState()
|
||||||
|
const { entries } = state.entries
|
||||||
|
|
||||||
|
const index = entries.findIndex(e => e.id === arg.id)
|
||||||
|
if (index === -1) return
|
||||||
|
|
||||||
|
thunkApi.dispatch(
|
||||||
|
markMultipleEntries({
|
||||||
|
entries: entries.slice(0, index + 1),
|
||||||
|
read: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
export const markAllEntries = createAsyncThunk<void, { sourceType: EntrySourceType; req: MarkRequest }, { state: RootState }>(
|
export const markAllEntries = createAsyncThunk<void, { sourceType: EntrySourceType; req: MarkRequest }, { state: RootState }>(
|
||||||
"entries/entry/markAll",
|
"entries/entry/markAll",
|
||||||
async (arg, thunkApi) => {
|
async (arg, thunkApi) => {
|
||||||
@@ -159,6 +187,13 @@ export const entriesSlice = createSlice({
|
|||||||
e.read = action.meta.arg.read
|
e.read = action.meta.arg.read
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
builder.addCase(markMultipleEntries.pending, (state, action) => {
|
||||||
|
state.entries
|
||||||
|
.filter(e => action.meta.arg.entries.some(e2 => e2.id === e.id))
|
||||||
|
.forEach(e => {
|
||||||
|
e.read = action.meta.arg.read
|
||||||
|
})
|
||||||
|
})
|
||||||
builder.addCase(markAllEntries.pending, (state, action) => {
|
builder.addCase(markAllEntries.pending, (state, action) => {
|
||||||
state.entries
|
state.entries
|
||||||
.filter(e => (action.meta.arg.req.olderThan ? e.date < action.meta.arg.req.olderThan : true))
|
.filter(e => (action.meta.arg.req.olderThan ? e.date < action.meta.arg.req.olderThan : true))
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { t } from "@lingui/macro"
|
||||||
import { Checkbox, Group, Popover } from "@mantine/core"
|
import { Checkbox, Group, Popover } from "@mantine/core"
|
||||||
import { markEntry, starEntry } from "app/slices/entries"
|
import { markEntriesUpToEntry, markEntry, starEntry } from "app/slices/entries"
|
||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
import { Entry } from "app/types"
|
import { Entry } from "app/types"
|
||||||
import { ActionButton } from "components/ActionButtton"
|
import { ActionButton } from "components/ActionButtton"
|
||||||
import { TbExternalLink, TbShare, TbStar, TbStarOff } from "react-icons/tb"
|
import { TbArrowBarToDown, TbExternalLink, TbShare, TbStar, TbStarOff } from "react-icons/tb"
|
||||||
import { ShareButtons } from "./ShareButtons"
|
import { ShareButtons } from "./ShareButtons"
|
||||||
|
|
||||||
interface FeedEntryFooterProps {
|
interface FeedEntryFooterProps {
|
||||||
@@ -21,38 +21,46 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
|
|||||||
const readStatusCheckboxClicked = () => dispatch(markEntry({ entry: props.entry, read: !props.entry.read }))
|
const readStatusCheckboxClicked = () => dispatch(markEntry({ entry: props.entry, read: !props.entry.read }))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group>
|
<Group position="apart">
|
||||||
{props.entry.markable && (
|
<Group>
|
||||||
<Checkbox
|
{props.entry.markable && (
|
||||||
label={t`Keep unread`}
|
<Checkbox
|
||||||
checked={!props.entry.read}
|
label={t`Keep unread`}
|
||||||
onChange={readStatusCheckboxClicked}
|
checked={!props.entry.read}
|
||||||
styles={{
|
onChange={readStatusCheckboxClicked}
|
||||||
label: { cursor: "pointer" },
|
styles={{
|
||||||
input: { cursor: "pointer" },
|
label: { cursor: "pointer" },
|
||||||
}}
|
input: { cursor: "pointer" },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<ActionButton
|
||||||
|
icon={props.entry.starred ? <TbStarOff size={18} /> : <TbStar size={18} />}
|
||||||
|
label={props.entry.starred ? t`Unstar` : t`Star`}
|
||||||
|
onClick={() => dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
|
{showSharingButtons && (
|
||||||
|
<Popover withArrow withinPortal shadow="md">
|
||||||
|
<Popover.Target>
|
||||||
|
<ActionButton icon={<TbShare size={18} />} label={t`Share`} />
|
||||||
|
</Popover.Target>
|
||||||
|
<Popover.Dropdown>
|
||||||
|
<ShareButtons url={props.entry.url} description={props.entry.title} />
|
||||||
|
</Popover.Dropdown>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<a href={props.entry.url} target="_blank" rel="noreferrer">
|
||||||
|
<ActionButton icon={<TbExternalLink size={18} />} label={t`Open link`} />
|
||||||
|
</a>
|
||||||
|
</Group>
|
||||||
|
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={props.entry.starred ? <TbStarOff size={18} /> : <TbStar size={18} />}
|
icon={<TbArrowBarToDown size={18} />}
|
||||||
label={props.entry.starred ? t`Unstar` : t`Star`}
|
label={t`Mark as read up to here`}
|
||||||
onClick={() => dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))}
|
onClick={() => dispatch(markEntriesUpToEntry(props.entry))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{showSharingButtons && (
|
|
||||||
<Popover withArrow withinPortal shadow="md">
|
|
||||||
<Popover.Target>
|
|
||||||
<ActionButton icon={<TbShare size={18} />} label={t`Share`} />
|
|
||||||
</Popover.Target>
|
|
||||||
<Popover.Dropdown>
|
|
||||||
<ShareButtons url={props.entry.url} description={props.entry.title} />
|
|
||||||
</Popover.Dropdown>
|
|
||||||
</Popover>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<a href={props.entry.url} target="_blank" rel="noreferrer">
|
|
||||||
<ActionButton icon={<TbExternalLink size={18} />} label={t`Open link`} />
|
|
||||||
</a>
|
|
||||||
</Group>
|
</Group>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -394,6 +394,10 @@ msgstr "Mark all as read"
|
|||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "Mark all entries as read"
|
msgstr "Mark all entries as read"
|
||||||
|
|
||||||
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
msgid "Mark as read up to here"
|
||||||
|
msgstr "Mark as read up to here"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Metrics"
|
msgid "Metrics"
|
||||||
msgstr "Metrics"
|
msgstr "Metrics"
|
||||||
|
|||||||
@@ -394,6 +394,10 @@ msgstr "Tout marquer comme lu"
|
|||||||
msgid "Mark all entries as read"
|
msgid "Mark all entries as read"
|
||||||
msgstr "Marquer toutes les entrées comme lues"
|
msgstr "Marquer toutes les entrées comme lues"
|
||||||
|
|
||||||
|
#: src/components/content/FeedEntryFooter.tsx
|
||||||
|
msgid "Mark as read up to here"
|
||||||
|
msgstr "Marquer comme lu jusqu'ici"
|
||||||
|
|
||||||
#: src/components/header/ProfileMenu.tsx
|
#: src/components/header/ProfileMenu.tsx
|
||||||
msgid "Metrics"
|
msgid "Metrics"
|
||||||
msgstr "Métriques"
|
msgstr "Métriques"
|
||||||
|
|||||||
Reference in New Issue
Block a user