diff --git a/commafeed-client/src/app/client.ts b/commafeed-client/src/app/client.ts index 2f21f5ac..733afeab 100644 --- a/commafeed-client/src/app/client.ts +++ b/commafeed-client/src/app/client.ts @@ -13,6 +13,7 @@ import { LoginRequest, MarkRequest, Metrics, + MultipleMarkRequest, PasswordResetRequest, ProfileModificationRequest, RegistrationRequest, @@ -45,6 +46,7 @@ export const client = { }, entry: { mark: (req: MarkRequest) => axiosInstance.post("entry/mark", req), + markMultiple: (req: MultipleMarkRequest) => axiosInstance.post("entry/markMultiple", req), star: (req: StarRequest) => axiosInstance.post("entry/star", req), }, feed: { diff --git a/commafeed-client/src/app/slices/entries.ts b/commafeed-client/src/app/slices/entries.ts index 04d481af..a6625b82 100644 --- a/commafeed-client/src/app/slices/entries.ts +++ b/commafeed-client/src/app/slices/entries.ts @@ -79,6 +79,34 @@ export const markEntry = createAsyncThunk( 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( + "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( "entries/entry/markAll", async (arg, thunkApi) => { @@ -159,6 +187,13 @@ export const entriesSlice = createSlice({ 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) => { state.entries .filter(e => (action.meta.arg.req.olderThan ? e.date < action.meta.arg.req.olderThan : true)) diff --git a/commafeed-client/src/components/content/FeedEntryFooter.tsx b/commafeed-client/src/components/content/FeedEntryFooter.tsx index d0f29416..dfd5c970 100644 --- a/commafeed-client/src/components/content/FeedEntryFooter.tsx +++ b/commafeed-client/src/components/content/FeedEntryFooter.tsx @@ -1,10 +1,10 @@ import { t } from "@lingui/macro" 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 { Entry } from "app/types" 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" interface FeedEntryFooterProps { @@ -21,38 +21,46 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) { const readStatusCheckboxClicked = () => dispatch(markEntry({ entry: props.entry, read: !props.entry.read })) return ( - - {props.entry.markable && ( - + + {props.entry.markable && ( + + )} + : } + label={props.entry.starred ? t`Unstar` : t`Star`} + onClick={() => dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))} /> - )} + + {showSharingButtons && ( + + + } label={t`Share`} /> + + + + + + )} + + + } label={t`Open link`} /> + + + : } - label={props.entry.starred ? t`Unstar` : t`Star`} - onClick={() => dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))} + icon={} + label={t`Mark as read up to here`} + onClick={() => dispatch(markEntriesUpToEntry(props.entry))} /> - - {showSharingButtons && ( - - - } label={t`Share`} /> - - - - - - )} - - - } label={t`Open link`} /> - ) } diff --git a/commafeed-client/src/locales/en/messages.po b/commafeed-client/src/locales/en/messages.po index f8f304cc..e4f3cdf8 100644 --- a/commafeed-client/src/locales/en/messages.po +++ b/commafeed-client/src/locales/en/messages.po @@ -394,6 +394,10 @@ msgstr "Mark all as read" msgid "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 msgid "Metrics" msgstr "Metrics" diff --git a/commafeed-client/src/locales/fr/messages.po b/commafeed-client/src/locales/fr/messages.po index 97e81aa8..83a2a9b3 100644 --- a/commafeed-client/src/locales/fr/messages.po +++ b/commafeed-client/src/locales/fr/messages.po @@ -394,6 +394,10 @@ msgstr "Tout marquer comme lu" msgid "Mark all entries as read" 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 msgid "Metrics" msgstr "Métriques"