From 438b2557089da4958f8c0c00889a7297c8e51359 Mon Sep 17 00:00:00 2001 From: Athou Date: Tue, 11 Oct 2022 14:27:54 +0200 Subject: [PATCH] give responsibility of marking as read and expanding to caller --- commafeed-client/src/app/slices/entries.ts | 77 ++++++++++------ .../src/components/content/FeedEntries.tsx | 90 ++++++++++++++++--- .../src/components/content/FeedEntry.tsx | 22 +---- 3 files changed, 131 insertions(+), 58 deletions(-) diff --git a/commafeed-client/src/app/slices/entries.ts b/commafeed-client/src/app/slices/entries.ts index a6625b82..5e9d9e9b 100644 --- a/commafeed-client/src/app/slices/entries.ts +++ b/commafeed-client/src/app/slices/entries.ts @@ -122,47 +122,68 @@ export const starEntry = createAsyncThunk("entries/entry/star", (arg: { entry: E starred: arg.starred, }) }) -export const selectEntry = createAsyncThunk("entries/entry/select", (arg, thunkApi) => { +export const selectEntry = createAsyncThunk< + void, + { + entry: Entry + expand: boolean + markAsRead: boolean + }, + { state: RootState } +>("entries/entry/select", (arg, thunkApi) => { const state = thunkApi.getState() - const entry = state.entries.entries.find(e => e.id === arg.id) + const entry = state.entries.entries.find(e => e.id === arg.entry.id) if (!entry) return - // only mark entry as read if we're expanding - if (!entry.expanded) { + // mark as read if requested + if (arg.markAsRead) { thunkApi.dispatch(markEntry({ entry, read: true })) } // set entry as selected thunkApi.dispatch(entriesSlice.actions.setSelectedEntry(entry)) - // collapse or expand entry if needed + // expand if requested const previouslySelectedEntry = state.entries.entries.find(e => e.id === state.entries.selectedEntryId) - if (entry === previouslySelectedEntry) { - // selecting an entry already selected toggles expanded status - thunkApi.dispatch(entriesSlice.actions.setEntryExpanded({ entry, expanded: !entry.expanded })) - } else { - if (previouslySelectedEntry) { - thunkApi.dispatch(entriesSlice.actions.setEntryExpanded({ entry: previouslySelectedEntry, expanded: false })) + if (previouslySelectedEntry) { + thunkApi.dispatch(entriesSlice.actions.setEntryExpanded({ entry: previouslySelectedEntry, expanded: false })) + } + thunkApi.dispatch(entriesSlice.actions.setEntryExpanded({ entry, expanded: arg.expand })) +}) +export const selectPreviousEntry = createAsyncThunk( + "entries/entry/selectPrevious", + (arg, thunkApi) => { + const state = thunkApi.getState() + const { entries } = state.entries + const previousIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) - 1 + if (previousIndex >= 0) { + thunkApi.dispatch( + selectEntry({ + entry: entries[previousIndex], + expand: arg.expand, + markAsRead: arg.markAsRead, + }) + ) } - thunkApi.dispatch(entriesSlice.actions.setEntryExpanded({ entry, expanded: true })) } -}) -export const selectPreviousEntry = createAsyncThunk("entries/entry/selectPrevious", (_, thunkApi) => { - const state = thunkApi.getState() - const { entries } = state.entries - const previousIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) - 1 - if (previousIndex >= 0) { - thunkApi.dispatch(selectEntry(entries[previousIndex])) +) +export const selectNextEntry = createAsyncThunk( + "entries/entry/selectNext", + (arg, thunkApi) => { + const state = thunkApi.getState() + const { entries } = state.entries + const nextIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) + 1 + if (nextIndex < entries.length) { + thunkApi.dispatch( + selectEntry({ + entry: entries[nextIndex], + expand: arg.expand, + markAsRead: arg.markAsRead, + }) + ) + } } -}) -export const selectNextEntry = createAsyncThunk("entries/entry/selectNext", (_, thunkApi) => { - const state = thunkApi.getState() - const { entries } = state.entries - const nextIndex = entries.findIndex(e => e.id === state.entries.selectedEntryId) + 1 - if (nextIndex < entries.length) { - thunkApi.dispatch(selectEntry(entries[nextIndex])) - } -}) +) export const entriesSlice = createSlice({ name: "entries", diff --git a/commafeed-client/src/components/content/FeedEntries.tsx b/commafeed-client/src/components/content/FeedEntries.tsx index ca39cbec..b59607bf 100644 --- a/commafeed-client/src/components/content/FeedEntries.tsx +++ b/commafeed-client/src/components/content/FeedEntries.tsx @@ -2,6 +2,7 @@ import { t } from "@lingui/macro" import { openModal } from "@mantine/modals" import { Constants } from "app/constants" import { + ExpendableEntry, loadMoreEntries, markAllEntries, markEntry, @@ -31,6 +32,25 @@ export function FeedEntries() { const selectedEntry = entries.find(e => e.id === selectedEntryId) + const headerClicked = (entry: ExpendableEntry, event: React.MouseEvent) => { + if (event.button === 1 || event.ctrlKey || event.metaKey) { + // middle click + dispatch(markEntry({ entry, read: true })) + } else if (event.button === 0) { + // main click + // don't trigger the link + event.preventDefault() + + dispatch( + selectEntry({ + entry, + expand: !entry.expanded, + markAsRead: !entry.expanded, + }) + ) + } + } + // references to entries html elements const refs = useRef<{ [id: string]: HTMLDivElement }>({}) useEffect(() => { @@ -59,17 +79,32 @@ export function FeedEntries() { dispatch(reloadEntries()) }) useMousetrap("j", () => { - dispatch(selectNextEntry()) + dispatch( + selectNextEntry({ + expand: true, + markAsRead: true, + }) + ) }) useMousetrap("k", () => { - dispatch(selectPreviousEntry()) + dispatch( + selectPreviousEntry({ + expand: true, + markAsRead: true, + }) + ) }) useMousetrap("space", () => { if (selectedEntry) { if (selectedEntry.expanded) { const ref = refs.current[selectedEntry.id] if (Constants.layout.isBottomVisible(ref)) { - dispatch(selectNextEntry()) + dispatch( + selectNextEntry({ + expand: true, + markAsRead: true, + }) + ) } else { const scrollArea = document.getElementById(Constants.dom.mainScrollAreaId) scrollArea?.scrollTo({ @@ -78,10 +113,21 @@ export function FeedEntries() { }) } } else { - dispatch(selectEntry(selectedEntry)) + dispatch( + selectEntry({ + entry: selectedEntry, + expand: true, + markAsRead: true, + }) + ) } } else { - dispatch(selectNextEntry()) + dispatch( + selectNextEntry({ + expand: true, + markAsRead: true, + }) + ) } }) useMousetrap("shift+space", () => { @@ -89,7 +135,12 @@ export function FeedEntries() { if (selectedEntry.expanded) { const ref = refs.current[selectedEntry.id] if (Constants.layout.isTopVisible(ref)) { - dispatch(selectPreviousEntry()) + dispatch( + selectPreviousEntry({ + expand: true, + markAsRead: true, + }) + ) } else { const scrollArea = document.getElementById(Constants.dom.mainScrollAreaId) scrollArea?.scrollTo({ @@ -98,14 +149,25 @@ export function FeedEntries() { }) } } else { - dispatch(selectPreviousEntry()) + dispatch( + selectPreviousEntry({ + expand: true, + markAsRead: true, + }) + ) } } }) useMousetrap(["o", "enter"], () => { // toggle expanded status if (!selectedEntry) return - dispatch(selectEntry(selectedEntry)) + dispatch( + selectEntry({ + entry: selectedEntry, + expand: !selectedEntry.expanded, + markAsRead: !selectedEntry.expanded, + }) + ) }) useMousetrap("v", () => { // open tab in foreground @@ -160,14 +222,18 @@ export function FeedEntries() { useWindow={false} getScrollParent={() => document.getElementById(Constants.dom.mainScrollAreaId)} > - {entries.map(e => ( + {entries.map(entry => (
{ - refs.current[e.id] = el! + refs.current[entry.id] = el! }} > - + headerClicked(entry, event)} + />
))} diff --git a/commafeed-client/src/components/content/FeedEntry.tsx b/commafeed-client/src/components/content/FeedEntry.tsx index 567583f5..02ce74d7 100644 --- a/commafeed-client/src/components/content/FeedEntry.tsx +++ b/commafeed-client/src/components/content/FeedEntry.tsx @@ -1,7 +1,6 @@ import { Anchor, Box, createStyles, Divider, Paper } from "@mantine/core" import { Constants } from "app/constants" -import { markEntry, selectEntry } from "app/slices/entries" -import { useAppDispatch, useAppSelector } from "app/store" +import { useAppSelector } from "app/store" import { Entry } from "app/types" import React from "react" import { FeedEntryBody } from "./FeedEntryBody" @@ -12,6 +11,7 @@ import { FeedEntryHeader } from "./FeedEntryHeader" interface FeedEntryProps { entry: Entry expanded: boolean + onHeaderClick: (e: React.MouseEvent) => void } const useStyles = createStyles((theme, props: FeedEntryProps) => { @@ -38,22 +38,8 @@ const useStyles = createStyles((theme, props: FeedEntryProps) => { export function FeedEntry(props: FeedEntryProps) { const { classes } = useStyles(props) const viewMode = useAppSelector(state => state.user.settings?.viewMode) - const dispatch = useAppDispatch() const compactHeader = viewMode === "title" && !props.expanded - const headerClicked = (e: React.MouseEvent) => { - if (e.button === 1 || e.ctrlKey || e.metaKey) { - // middle click - dispatch(markEntry({ entry: props.entry, read: true })) - } else if (e.button === 0) { - // main click - // don't trigger the link - e.preventDefault() - - dispatch(selectEntry(props.entry)) - } - } - return ( {compactHeader && }