add "mark as read up to here"

This commit is contained in:
Athou
2022-08-24 09:08:10 +02:00
parent a1fb5871d1
commit 126a5e3bbc
5 changed files with 84 additions and 31 deletions

View File

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

View File

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

View File

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

View File

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

View File

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