From a8db632c4a3b5ed518da46cf133b8e43125df716 Mon Sep 17 00:00:00 2001 From: Athou Date: Mon, 15 Aug 2022 20:47:46 +0200 Subject: [PATCH] support for marking entries older than a threshold --- commafeed-client/src/app/slices/entries.ts | 24 ++++-- commafeed-client/src/app/slices/tree.ts | 23 +---- .../src/components/header/Header.tsx | 41 ++------- .../components/header/MarkAllAsReadButton.tsx | 83 +++++++++++++++++++ commafeed-client/src/locales/en/messages.po | 14 ++-- commafeed-client/src/locales/fr/messages.po | 14 ++-- 6 files changed, 125 insertions(+), 74 deletions(-) create mode 100644 commafeed-client/src/components/header/MarkAllAsReadButton.tsx diff --git a/commafeed-client/src/app/slices/entries.ts b/commafeed-client/src/app/slices/entries.ts index 499507f9..8d3a9d9a 100644 --- a/commafeed-client/src/app/slices/entries.ts +++ b/commafeed-client/src/app/slices/entries.ts @@ -3,6 +3,8 @@ import { client } from "app/client" import { Constants } from "app/constants" import { RootState } from "app/store" import { Entries, Entry, MarkRequest } from "app/types" +// eslint-disable-next-line import/no-cycle +import { reloadTree } from "./tree" export type EntrySourceType = "category" | "feed" export type EntrySource = { type: EntrySourceType; id: string } @@ -77,10 +79,14 @@ export const markEntry = createAsyncThunk( condition: arg => arg.entry.read !== arg.read, } ) -export const markAllEntries = createAsyncThunk("entries/entry/markAll", (arg: { sourceType: EntrySourceType; req: MarkRequest }) => { - const endpoint = arg.sourceType === "category" ? client.category.markEntries : client.feed.markEntries - endpoint(arg.req) -}) +export const markAllEntries = createAsyncThunk( + "entries/entry/markAll", + async (arg, thunkApi) => { + const endpoint = arg.sourceType === "category" ? client.category.markEntries : client.feed.markEntries + await endpoint(arg.req) + thunkApi.dispatch(reloadTree()) + } +) export const selectEntry = createAsyncThunk("entries/entry/select", (arg, thunkApi) => { const state = thunkApi.getState() const entry = state.entries.entries.find(e => e.id === arg.id) @@ -146,10 +152,12 @@ export const entriesSlice = createSlice({ e.read = action.meta.arg.read }) }) - builder.addCase(markAllEntries.pending, state => { - state.entries.forEach(e => { - e.read = true - }) + builder.addCase(markAllEntries.pending, (state, action) => { + state.entries + .filter(e => (action.meta.arg.req.olderThan ? e.date < action.meta.arg.req.olderThan : true)) + .forEach(e => { + e.read = true + }) }) builder.addCase(loadEntries.pending, (state, action) => { state.source = action.meta.arg diff --git a/commafeed-client/src/app/slices/tree.ts b/commafeed-client/src/app/slices/tree.ts index 75a2b9f6..5b32bfa2 100644 --- a/commafeed-client/src/app/slices/tree.ts +++ b/commafeed-client/src/app/slices/tree.ts @@ -2,7 +2,8 @@ import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit" import { client } from "app/client" import { Category, CollapseRequest } from "app/types" import { visitCategoryTree } from "app/utils" -import { markAllEntries, markEntry } from "./entries" +// eslint-disable-next-line import/no-cycle +import { markEntry } from "./entries" import { redirectTo } from "./redirect" interface TreeState { @@ -47,26 +48,6 @@ export const treeSlice = createSlice({ }) ) }) - builder.addCase(markAllEntries.pending, (state, action) => { - if (!state.rootCategory) return - const { sourceType } = action.meta.arg - const sourceId = action.meta.arg.req.id - visitCategoryTree(state.rootCategory, c => { - if (sourceType === "category" && c.id === sourceId) { - visitCategoryTree(c, c2 => - c2.feeds.forEach(f => { - f.unread = 0 - }) - ) - } else if (sourceType === "feed") { - c.feeds - .filter(f => f.id === +sourceId) - .forEach(f => { - f.unread = 0 - }) - } - }) - }) builder.addCase(redirectTo, state => { state.mobileMenuOpen = false }) diff --git a/commafeed-client/src/components/header/Header.tsx b/commafeed-client/src/components/header/Header.tsx index 923807d6..285bab57 100644 --- a/commafeed-client/src/components/header/Header.tsx +++ b/commafeed-client/src/components/header/Header.tsx @@ -1,12 +1,12 @@ -import { t, Trans } from "@lingui/macro" -import { Center, Code, Divider, Group, Text } from "@mantine/core" -import { openConfirmModal } from "@mantine/modals" -import { markAllEntries, reloadEntries } from "app/slices/entries" +import { t } from "@lingui/macro" +import { Center, Divider, Group } from "@mantine/core" +import { reloadEntries } from "app/slices/entries" import { changeReadingMode, changeReadingOrder } from "app/slices/user" import { useAppDispatch, useAppSelector } from "app/store" import { ActionButton } from "components/ActionButtton" import { Loader } from "components/Loader" -import { TbArrowDown, TbArrowUp, TbChecks, TbEye, TbEyeOff, TbRefresh, TbUser } from "react-icons/tb" +import { TbArrowDown, TbArrowUp, TbEye, TbEyeOff, TbRefresh, TbUser } from "react-icons/tb" +import { MarkAllAsReadButton } from "./MarkAllAsReadButton" import { ProfileMenu } from "./ProfileMenu" function HeaderDivider() { @@ -14,46 +14,17 @@ function HeaderDivider() { } const iconSize = 18 - export function Header() { - const source = useAppSelector(state => state.entries.source) - const sourceLabel = useAppSelector(state => state.entries.sourceLabel) - const entriesTimestamp = useAppSelector(state => state.entries.timestamp) const settings = useAppSelector(state => state.user.settings) const profile = useAppSelector(state => state.user.profile) const dispatch = useAppDispatch() - const openMarkAllEntriesModal = () => - openConfirmModal({ - title: t`Mark all entries as read`, - children: ( - - - Are you sure you want to mark all entries of {sourceLabel} as read? - - - ), - labels: { confirm: t`Confirm`, cancel: t`Cancel` }, - confirmProps: { color: "red" }, - onConfirm: () => - dispatch( - markAllEntries({ - sourceType: source.type, - req: { - id: source.id, - read: true, - olderThan: entriesTimestamp, - }, - }) - ), - }) - if (!settings) return return (
} label={t`Refresh`} onClick={() => dispatch(reloadEntries())} /> - } label={t`Mark all as read`} onClick={openMarkAllEntriesModal} /> + diff --git a/commafeed-client/src/components/header/MarkAllAsReadButton.tsx b/commafeed-client/src/components/header/MarkAllAsReadButton.tsx new file mode 100644 index 00000000..89aa4f01 --- /dev/null +++ b/commafeed-client/src/components/header/MarkAllAsReadButton.tsx @@ -0,0 +1,83 @@ +import { t, Trans } from "@lingui/macro" + +import { Button, Code, Group, Modal, Slider, Stack, Text } from "@mantine/core" +import { markAllEntries } from "app/slices/entries" +import { useAppDispatch, useAppSelector } from "app/store" +import { ActionButton } from "components/ActionButtton" +import { useState } from "react" +import { TbChecks } from "react-icons/tb" + +export function MarkAllAsReadButton(props: { iconSize: number }) { + const [opened, setOpened] = useState(false) + const [threshold, setThreshold] = useState(0) + const source = useAppSelector(state => state.entries.source) + const sourceLabel = useAppSelector(state => state.entries.sourceLabel) + const entriesTimestamp = useAppSelector(state => state.entries.timestamp) ?? Date.now() + const dispatch = useAppDispatch() + + return ( + <> + setOpened(false)} title={t`Mark all entries as read`}> + + + {threshold === 0 && ( + + Are you sure you want to mark all entries of {sourceLabel} as read? + + )} + {threshold > 0 && ( + + Are you sure you want to mark entries older than {threshold} days of {sourceLabel} as read? + + )} + + + + + + + + + } + label={t`Mark all as read`} + onClick={() => { + setThreshold(0) + setOpened(true) + }} + /> + + ) +} diff --git a/commafeed-client/src/locales/en/messages.po b/commafeed-client/src/locales/en/messages.po index f03e62af..1db276ad 100644 --- a/commafeed-client/src/locales/en/messages.po +++ b/commafeed-client/src/locales/en/messages.po @@ -86,10 +86,14 @@ msgstr "Are you sure you want to delete user <0>{userName} ?" msgid "Are you sure you want to delete your account? There's no turning back!" msgstr "Are you sure you want to delete your account? There's no turning back!" -#: src/components/header/Header.tsx +#: src/components/header/MarkAllAsReadButton.tsx msgid "Are you sure you want to mark all entries of <0>{sourceLabel} as read?" msgstr "Are you sure you want to mark all entries of <0>{sourceLabel} as read?" +#: src/components/header/MarkAllAsReadButton.tsx +msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel} as read?" +msgstr "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel} as read?" + #: src/pages/app/FeedDetailsPage.tsx msgid "Are you sure you want to unsubscribe from <0>{feedName}?" msgstr "Are you sure you want to unsubscribe from <0>{feedName}?" @@ -117,7 +121,7 @@ msgstr "Browser extentions" #: src/components/admin/UserEdit.tsx #: src/components/content/add/AddCategory.tsx #: src/components/content/add/ImportOpml.tsx -#: src/components/header/Header.tsx +#: src/components/header/MarkAllAsReadButton.tsx #: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx #: src/pages/admin/AdminUsersPage.tsx @@ -156,7 +160,7 @@ msgstr "CommaFeed next unread item" msgid "CommaFeed version {version} ({revision})" msgstr "CommaFeed version {version} ({revision})" -#: src/components/header/Header.tsx +#: src/components/header/MarkAllAsReadButton.tsx #: src/components/settings/ProfileSettings.tsx #: src/pages/admin/AdminUsersPage.tsx #: src/pages/app/CategoryDetailsPage.tsx @@ -371,11 +375,11 @@ msgstr "Logout" msgid "Manage users" msgstr "Manage users" -#: src/components/header/Header.tsx +#: src/components/header/MarkAllAsReadButton.tsx msgid "Mark all as read" msgstr "Mark all as read" -#: src/components/header/Header.tsx +#: src/components/header/MarkAllAsReadButton.tsx #: src/components/KeyboardShortcutsHelp.tsx msgid "Mark all entries as read" msgstr "Mark all entries as read" diff --git a/commafeed-client/src/locales/fr/messages.po b/commafeed-client/src/locales/fr/messages.po index 6f2c87ba..b8d79e13 100644 --- a/commafeed-client/src/locales/fr/messages.po +++ b/commafeed-client/src/locales/fr/messages.po @@ -86,10 +86,14 @@ msgstr "Etes-vous sûr de vouloir supprimer l'utilisateur <0>{userName} ?" msgid "Are you sure you want to delete your account? There's no turning back!" msgstr "Êtes-vous sûr de vouloir supprimer définitivement votre compte ?" -#: src/components/header/Header.tsx +#: src/components/header/MarkAllAsReadButton.tsx msgid "Are you sure you want to mark all entries of <0>{sourceLabel} as read?" msgstr "Etes-vous sûr de vouloir marquer toutes les entrées de <0>{sourceLabel} comme lues?" +#: src/components/header/MarkAllAsReadButton.tsx +msgid "Are you sure you want to mark entries older than {threshold} days of <0>{sourceLabel} as read?" +msgstr "Etes-vous sûr de vouloir marquer les entrées de <0>{sourceLabel} plus anciennes que {threshold} jours comme lues?" + #: src/pages/app/FeedDetailsPage.tsx msgid "Are you sure you want to unsubscribe from <0>{feedName}?" msgstr "Etes-vous sûr de vouloir vous désabonner de <0>{feedName}?" @@ -117,7 +121,7 @@ msgstr "Extensions pour navigateurs" #: src/components/admin/UserEdit.tsx #: src/components/content/add/AddCategory.tsx #: src/components/content/add/ImportOpml.tsx -#: src/components/header/Header.tsx +#: src/components/header/MarkAllAsReadButton.tsx #: src/components/settings/ProfileSettings.tsx #: src/components/settings/ProfileSettings.tsx #: src/pages/admin/AdminUsersPage.tsx @@ -156,7 +160,7 @@ msgstr "CommaFeed prochain article non lu" msgid "CommaFeed version {version} ({revision})" msgstr "CommaFeed version {version} ({revision})" -#: src/components/header/Header.tsx +#: src/components/header/MarkAllAsReadButton.tsx #: src/components/settings/ProfileSettings.tsx #: src/pages/admin/AdminUsersPage.tsx #: src/pages/app/CategoryDetailsPage.tsx @@ -371,11 +375,11 @@ msgstr "Déconnexion" msgid "Manage users" msgstr "Gestion des utilisateurs" -#: src/components/header/Header.tsx +#: src/components/header/MarkAllAsReadButton.tsx msgid "Mark all as read" msgstr "Tout marquer comme lu" -#: src/components/header/Header.tsx +#: src/components/header/MarkAllAsReadButton.tsx #: src/components/KeyboardShortcutsHelp.tsx msgid "Mark all entries as read" msgstr "Marquer toutes les entrées comme lues"