simplify settings thunks

This commit is contained in:
Athou
2026-05-03 19:14:50 +02:00
parent b170e0bc17
commit 8645c57d13
5 changed files with 80 additions and 370 deletions

View File

@@ -1,32 +1,8 @@
import { t } from "@lingui/core/macro" import { t } from "@lingui/core/macro"
import { showNotification } from "@mantine/notifications" import { showNotification } from "@mantine/notifications"
import { createSlice, isAnyOf, type PayloadAction } from "@reduxjs/toolkit" import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
import type { LocalSettings, Settings, UserModel, ViewMode } from "@/app/types" import type { LocalSettings, Settings, UserModel, ViewMode } from "@/app/types"
import { import { changeSettings, reloadProfile, reloadSettings, reloadTags } from "./thunks"
changeCustomContextMenu,
changeDisablePullToRefresh,
changeEntriesToKeepOnTopWhenScrolling,
changeExternalLinkIconDisplayMode,
changeLanguage,
changeMarkAllAsReadConfirmation,
changeMarkAllAsReadNavigateToUnread,
changeMobileFooter,
changePrimaryColor,
changePushNotificationSettings,
changeReadingMode,
changeReadingOrder,
changeScrollMarks,
changeScrollMode,
changeScrollSpeed,
changeSharingSetting,
changeShowRead,
changeStarIconDisplayMode,
changeUnreadCountFavicon,
changeUnreadCountTitle,
reloadProfile,
reloadSettings,
reloadTags,
} from "./thunks"
interface UserState { interface UserState {
settings?: Settings settings?: Settings
@@ -73,115 +49,16 @@ export const userSlice = createSlice({
builder.addCase(reloadTags.fulfilled, (state, action) => { builder.addCase(reloadTags.fulfilled, (state, action) => {
state.tags = action.payload state.tags = action.payload
}) })
builder.addCase(changeReadingMode.pending, (state, action) => { builder.addCase(changeSettings.pending, (state, action) => {
if (!state.settings) return if (!state.settings) return
state.settings.readingMode = action.meta.arg state.settings = { ...state.settings, ...action.meta.arg }
}) })
builder.addCase(changeReadingOrder.pending, (state, action) => { builder.addCase(changeSettings.fulfilled, () => {
if (!state.settings) return showNotification({
state.settings.readingOrder = action.meta.arg message: t`Settings saved.`,
color: "green",
})
}) })
builder.addCase(changeLanguage.pending, (state, action) => {
if (!state.settings) return
state.settings.language = action.meta.arg
})
builder.addCase(changeScrollSpeed.pending, (state, action) => {
if (!state.settings) return
state.settings.scrollSpeed = action.meta.arg ? 400 : 0
})
builder.addCase(changeShowRead.pending, (state, action) => {
if (!state.settings) return
state.settings.showRead = action.meta.arg
})
builder.addCase(changeScrollMarks.pending, (state, action) => {
if (!state.settings) return
state.settings.scrollMarks = action.meta.arg
})
builder.addCase(changeScrollMode.pending, (state, action) => {
if (!state.settings) return
state.settings.scrollMode = action.meta.arg
})
builder.addCase(changeEntriesToKeepOnTopWhenScrolling.pending, (state, action) => {
if (!state.settings) return
state.settings.entriesToKeepOnTopWhenScrolling = action.meta.arg
})
builder.addCase(changeStarIconDisplayMode.pending, (state, action) => {
if (!state.settings) return
state.settings.starIconDisplayMode = action.meta.arg
})
builder.addCase(changeExternalLinkIconDisplayMode.pending, (state, action) => {
if (!state.settings) return
state.settings.externalLinkIconDisplayMode = action.meta.arg
})
builder.addCase(changeMarkAllAsReadConfirmation.pending, (state, action) => {
if (!state.settings) return
state.settings.markAllAsReadConfirmation = action.meta.arg
})
builder.addCase(changeMarkAllAsReadNavigateToUnread.pending, (state, action) => {
if (!state.settings) return
state.settings.markAllAsReadNavigateToNextUnread = action.meta.arg
})
builder.addCase(changeCustomContextMenu.pending, (state, action) => {
if (!state.settings) return
state.settings.customContextMenu = action.meta.arg
})
builder.addCase(changeMobileFooter.pending, (state, action) => {
if (!state.settings) return
state.settings.mobileFooter = action.meta.arg
})
builder.addCase(changeUnreadCountTitle.pending, (state, action) => {
if (!state.settings) return
state.settings.unreadCountTitle = action.meta.arg
})
builder.addCase(changeUnreadCountFavicon.pending, (state, action) => {
if (!state.settings) return
state.settings.unreadCountFavicon = action.meta.arg
})
builder.addCase(changeDisablePullToRefresh.pending, (state, action) => {
if (!state.settings) return
state.settings.disablePullToRefresh = action.meta.arg
})
builder.addCase(changePrimaryColor.pending, (state, action) => {
if (!state.settings) return
state.settings.primaryColor = action.meta.arg
})
builder.addCase(changeSharingSetting.pending, (state, action) => {
if (!state.settings) return
state.settings.sharingSettings[action.meta.arg.site] = action.meta.arg.value
})
builder.addCase(changePushNotificationSettings.pending, (state, action) => {
if (!state.settings) return
state.settings.pushNotificationSettings = action.meta.arg
})
builder.addMatcher(
isAnyOf(
changeLanguage.fulfilled,
changeScrollSpeed.fulfilled,
changeShowRead.fulfilled,
changeScrollMarks.fulfilled,
changeScrollMode.fulfilled,
changeEntriesToKeepOnTopWhenScrolling.fulfilled,
changeStarIconDisplayMode.fulfilled,
changeExternalLinkIconDisplayMode.fulfilled,
changeMarkAllAsReadConfirmation.fulfilled,
changeMarkAllAsReadNavigateToUnread.fulfilled,
changeCustomContextMenu.fulfilled,
changeMobileFooter.fulfilled,
changeUnreadCountTitle.fulfilled,
changeUnreadCountFavicon.fulfilled,
changeDisablePullToRefresh.fulfilled,
changePrimaryColor.fulfilled,
changeSharingSetting.fulfilled,
changePushNotificationSettings.fulfilled
),
() => {
showNotification({
message: t`Settings saved.`,
color: "green",
})
}
)
}, },
}) })

View File

@@ -1,7 +1,6 @@
import { createAppAsyncThunk } from "@/app/async-thunk" import { createAppAsyncThunk } from "@/app/async-thunk"
import { client } from "@/app/client" import { client } from "@/app/client"
import { reloadEntries } from "@/app/entries/thunks" import type { Settings } from "@/app/types"
import type { IconDisplayMode, PushNotificationSettings, ReadingMode, ReadingOrder, ScrollMode, SharingSettings } from "@/app/types"
export const reloadSettings = createAppAsyncThunk("settings/reload", async () => await client.user.getSettings().then(r => r.data)) export const reloadSettings = createAppAsyncThunk("settings/reload", async () => await client.user.getSettings().then(r => r.data))
@@ -9,163 +8,8 @@ export const reloadProfile = createAppAsyncThunk("profile/reload", async () => a
export const reloadTags = createAppAsyncThunk("entries/tags", async () => await client.entry.getTags().then(r => r.data)) export const reloadTags = createAppAsyncThunk("entries/tags", async () => await client.entry.getTags().then(r => r.data))
export const changeReadingMode = createAppAsyncThunk("settings/readingMode", (readingMode: ReadingMode, thunkApi) => { export const changeSettings = createAppAsyncThunk("settings/change", async (newSettings: Partial<Settings>, thunkApi) => {
const { settings } = thunkApi.getState().user const { settings } = thunkApi.getState().user
if (!settings) return if (!settings) return
client.user.saveSettings({ ...settings, readingMode }) await client.user.saveSettings({ ...settings, ...newSettings })
thunkApi.dispatch(reloadEntries())
}) })
export const changeReadingOrder = createAppAsyncThunk("settings/readingOrder", (readingOrder: ReadingOrder, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, readingOrder })
thunkApi.dispatch(reloadEntries())
})
export const changeLanguage = createAppAsyncThunk("settings/language", (language: string, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, language })
})
export const changeScrollSpeed = createAppAsyncThunk("settings/scrollSpeed", (speed: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, scrollSpeed: speed ? 400 : 0 })
})
export const changeShowRead = createAppAsyncThunk("settings/showRead", (showRead: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, showRead })
})
export const changeScrollMarks = createAppAsyncThunk("settings/scrollMarks", (scrollMarks: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, scrollMarks })
})
export const changeScrollMode = createAppAsyncThunk("settings/scrollMode", (scrollMode: ScrollMode, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, scrollMode })
})
export const changeEntriesToKeepOnTopWhenScrolling = createAppAsyncThunk(
"settings/entriesToKeepOnTopWhenScrolling",
(entriesToKeepOnTopWhenScrolling: number, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, entriesToKeepOnTopWhenScrolling })
}
)
export const changeStarIconDisplayMode = createAppAsyncThunk(
"settings/starIconDisplayMode",
(starIconDisplayMode: IconDisplayMode, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, starIconDisplayMode })
}
)
export const changeExternalLinkIconDisplayMode = createAppAsyncThunk(
"settings/externalLinkIconDisplayMode",
(externalLinkIconDisplayMode: IconDisplayMode, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, externalLinkIconDisplayMode })
}
)
export const changeMarkAllAsReadConfirmation = createAppAsyncThunk(
"settings/markAllAsReadConfirmation",
(markAllAsReadConfirmation: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, markAllAsReadConfirmation })
}
)
export const changeMarkAllAsReadNavigateToUnread = createAppAsyncThunk(
"settings/markAllAsReadNavigateToUnread",
(markAllAsReadNavigateToNextUnread: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, markAllAsReadNavigateToNextUnread })
}
)
export const changeCustomContextMenu = createAppAsyncThunk("settings/customContextMenu", (customContextMenu: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, customContextMenu })
})
export const changeMobileFooter = createAppAsyncThunk("settings/mobileFooter", (mobileFooter: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, mobileFooter })
})
export const changeUnreadCountTitle = createAppAsyncThunk("settings/unreadCountTitle", (unreadCountTitle: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, unreadCountTitle })
})
export const changeUnreadCountFavicon = createAppAsyncThunk("settings/unreadCountFavicon", (unreadCountFavicon: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, unreadCountFavicon })
})
export const changeDisablePullToRefresh = createAppAsyncThunk(
"settings/disablePullToRefresh",
(disablePullToRefresh: boolean, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, disablePullToRefresh })
}
)
export const changePrimaryColor = createAppAsyncThunk("settings/primaryColor", (primaryColor: string, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({ ...settings, primaryColor })
})
export const changeSharingSetting = createAppAsyncThunk(
"settings/sharingSetting",
(
sharingSetting: {
site: keyof SharingSettings
value: boolean
},
thunkApi
) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({
...settings,
sharingSettings: {
...settings.sharingSettings,
[sharingSetting.site]: sharingSetting.value,
},
})
}
)
export const changePushNotificationSettings = createAppAsyncThunk(
"settings/pushNotificationSettings",
(pushNotificationSettings: PushNotificationSettings, thunkApi) => {
const { settings } = thunkApi.getState().user
if (!settings) return
client.user.saveSettings({
...settings,
pushNotificationSettings,
})
}
)

View File

@@ -19,7 +19,7 @@ import {
} from "react-icons/tb" } from "react-icons/tb"
import { markAllAsReadWithConfirmationIfRequired, reloadEntries, search, selectNextEntry, selectPreviousEntry } from "@/app/entries/thunks" import { markAllAsReadWithConfirmationIfRequired, reloadEntries, search, selectNextEntry, selectPreviousEntry } from "@/app/entries/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store" import { useAppDispatch, useAppSelector } from "@/app/store"
import { changeReadingMode, changeReadingOrder } from "@/app/user/thunks" import { changeSettings } from "@/app/user/thunks"
import { ActionButton } from "@/components/ActionButton" import { ActionButton } from "@/components/ActionButton"
import { Loader } from "@/components/Loader" import { Loader } from "@/components/Loader"
import { useActionButton } from "@/hooks/useActionButton" import { useActionButton } from "@/hooks/useActionButton"
@@ -73,6 +73,25 @@ export function Header() {
}, [setValues, searchFromStore]) }, [setValues, searchFromStore])
if (!settings) return <Loader /> if (!settings) return <Loader />
const toggleReadingMode = async () => {
await dispatch(
changeSettings({
readingMode: settings.readingMode === "all" ? "unread" : "all",
})
)
dispatch(reloadEntries())
}
const toggleReadingOrder = async () => {
await dispatch(
changeSettings({
readingOrder: settings.readingOrder === "asc" ? "desc" : "asc",
})
)
dispatch(reloadEntries())
}
return ( return (
<Center className="cf-toolbar-wrapper"> <Center className="cf-toolbar-wrapper">
<HeaderToolbar> <HeaderToolbar>
@@ -121,12 +140,12 @@ export function Header() {
<ActionButton <ActionButton
icon={settings.readingMode === "all" ? <TbEye size={iconSize} /> : <TbEyeOff size={iconSize} />} icon={settings.readingMode === "all" ? <TbEye size={iconSize} /> : <TbEyeOff size={iconSize} />}
label={settings.readingMode === "all" ? msg`All` : msg`Unread`} label={settings.readingMode === "all" ? msg`All` : msg`Unread`}
onClick={async () => await dispatch(changeReadingMode(settings.readingMode === "all" ? "unread" : "all"))} onClick={toggleReadingMode}
/> />
<ActionButton <ActionButton
icon={settings.readingOrder === "asc" ? <TbSortAscending size={iconSize} /> : <TbSortDescending size={iconSize} />} icon={settings.readingOrder === "asc" ? <TbSortAscending size={iconSize} /> : <TbSortDescending size={iconSize} />}
label={settings.readingOrder === "asc" ? msg`Asc` : msg`Desc`} label={settings.readingOrder === "asc" ? msg`Asc` : msg`Desc`}
onClick={async () => await dispatch(changeReadingOrder(settings.readingOrder === "asc" ? "desc" : "asc"))} onClick={toggleReadingOrder}
/> />
<Popover> <Popover>

View File

@@ -6,6 +6,7 @@ import {
type ComboboxData, type ComboboxData,
Divider, Divider,
Group, Group,
Loader,
NumberInput, NumberInput,
Radio, Radio,
Select, Select,
@@ -18,45 +19,11 @@ import type { ReactNode } from "react"
import { Constants } from "@/app/constants" import { Constants } from "@/app/constants"
import { useAppDispatch, useAppSelector } from "@/app/store" import { useAppDispatch, useAppSelector } from "@/app/store"
import type { IconDisplayMode, ScrollMode, SharingSettings } from "@/app/types" import type { IconDisplayMode, ScrollMode, SharingSettings } from "@/app/types"
import { import { changeSettings } from "@/app/user/thunks"
changeCustomContextMenu,
changeDisablePullToRefresh,
changeEntriesToKeepOnTopWhenScrolling,
changeExternalLinkIconDisplayMode,
changeLanguage,
changeMarkAllAsReadConfirmation,
changeMarkAllAsReadNavigateToUnread,
changeMobileFooter,
changePrimaryColor,
changeScrollMarks,
changeScrollMode,
changeScrollSpeed,
changeSharingSetting,
changeShowRead,
changeStarIconDisplayMode,
changeUnreadCountFavicon,
changeUnreadCountTitle,
} from "@/app/user/thunks"
import { locales } from "@/i18n" import { locales } from "@/i18n"
export function DisplaySettings() { export function DisplaySettings() {
const language = useAppSelector(state => state.user.settings?.language) const settings = useAppSelector(state => state.user.settings)
const scrollSpeed = useAppSelector(state => state.user.settings?.scrollSpeed)
const showRead = useAppSelector(state => state.user.settings?.showRead)
const scrollMarks = useAppSelector(state => state.user.settings?.scrollMarks)
const scrollMode = useAppSelector(state => state.user.settings?.scrollMode)
const entriesToKeepOnTop = useAppSelector(state => state.user.settings?.entriesToKeepOnTopWhenScrolling)
const starIconDisplayMode = useAppSelector(state => state.user.settings?.starIconDisplayMode)
const externalLinkIconDisplayMode = useAppSelector(state => state.user.settings?.externalLinkIconDisplayMode)
const markAllAsReadConfirmation = useAppSelector(state => state.user.settings?.markAllAsReadConfirmation)
const markAllAsReadNavigateToNextUnread = useAppSelector(state => state.user.settings?.markAllAsReadNavigateToNextUnread)
const customContextMenu = useAppSelector(state => state.user.settings?.customContextMenu)
const mobileFooter = useAppSelector(state => state.user.settings?.mobileFooter)
const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
const disablePullToRefresh = useAppSelector(state => state.user.settings?.disablePullToRefresh)
const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings)
const primaryColor = useAppSelector(state => state.user.settings?.primaryColor) || Constants.theme.defaultPrimaryColor
const { _ } = useLingui() const { _ } = useLingui()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
@@ -108,50 +75,51 @@ export function DisplaySettings() {
</Group> </Group>
) )
if (!settings) return <Loader />
return ( return (
<Stack> <Stack>
<Divider label={<Trans>Display</Trans>} labelPosition="center" /> <Divider label={<Trans>Display</Trans>} labelPosition="center" />
<Select <Select
label={<Trans>Language</Trans>} label={<Trans>Language</Trans>}
value={language} value={settings.language}
data={locales.map(l => ({ data={locales.map(l => ({
value: l.key, value: l.key,
label: l.label, label: l.label,
}))} }))}
onChange={async s => await (s && dispatch(changeLanguage(s)))} onChange={language => language && dispatch(changeSettings({ language }))}
/> />
<Select <Select
label={<Trans>Primary color</Trans>} label={<Trans>Primary color</Trans>}
data={colorData} data={colorData}
value={primaryColor} value={settings.primaryColor}
onChange={async value => value && (await dispatch(changePrimaryColor(value)))} onChange={primaryColor => primaryColor && dispatch(changeSettings({ primaryColor }))}
renderOption={colorRenderer} renderOption={colorRenderer}
/> />
<Switch <Switch
label={<Trans>Show feeds and categories with no unread entries</Trans>} label={<Trans>Show feeds and categories with no unread entries</Trans>}
checked={showRead} checked={settings.showRead}
onChange={async e => await dispatch(changeShowRead(e.currentTarget.checked))} onChange={e => dispatch(changeSettings({ showRead: e.currentTarget.checked }))}
/> />
<Switch <Switch
label={<Trans>Show confirmation when marking all entries as read</Trans>} label={<Trans>Show confirmation when marking all entries as read</Trans>}
checked={markAllAsReadConfirmation} checked={settings.markAllAsReadConfirmation}
onChange={async e => await dispatch(changeMarkAllAsReadConfirmation(e.currentTarget.checked))} onChange={e => dispatch(changeSettings({ markAllAsReadConfirmation: e.currentTarget.checked }))}
/> />
<Switch <Switch
label={<Trans>Navigate to the next category/feed with unread entries when marking all entries as read</Trans>} label={<Trans>Navigate to the next category/feed with unread entries when marking all entries as read</Trans>}
checked={markAllAsReadNavigateToNextUnread} checked={settings.markAllAsReadNavigateToNextUnread}
onChange={async e => await dispatch(changeMarkAllAsReadNavigateToUnread(e.currentTarget.checked))} onChange={e => dispatch(changeSettings({ markAllAsReadNavigateToNextUnread: e.currentTarget.checked }))}
/> />
<Switch <Switch
label={<Trans>On mobile, show action buttons at the bottom of the screen</Trans>} label={<Trans>On mobile, show action buttons at the bottom of the screen</Trans>}
checked={mobileFooter} checked={settings.mobileFooter}
onChange={async e => await dispatch(changeMobileFooter(e.currentTarget.checked))} onChange={e => dispatch(changeSettings({ mobileFooter: e.currentTarget.checked }))}
/> />
<Divider label={<Trans>Scrolling</Trans>} labelPosition="center" /> <Divider label={<Trans>Scrolling</Trans>} labelPosition="center" />
@@ -159,14 +127,14 @@ export function DisplaySettings() {
<Switch <Switch
label={<Trans>Disable "Pull to refresh" browser behavior</Trans>} label={<Trans>Disable "Pull to refresh" browser behavior</Trans>}
description={<Trans>This setting can cause scrolling issues on some browsers (e.g. Safari)</Trans>} description={<Trans>This setting can cause scrolling issues on some browsers (e.g. Safari)</Trans>}
checked={disablePullToRefresh} checked={settings.disablePullToRefresh}
onChange={async e => await dispatch(changeDisablePullToRefresh(e.currentTarget.checked))} onChange={e => dispatch(changeSettings({ disablePullToRefresh: e.currentTarget.checked }))}
/> />
<Radio.Group <Radio.Group
label={<Trans>Scroll selected entry to the top of the page</Trans>} label={<Trans>Scroll selected entry to the top of the page</Trans>}
value={scrollMode} value={settings.scrollMode}
onChange={async value => await dispatch(changeScrollMode(value as ScrollMode))} onChange={value => dispatch(changeSettings({ scrollMode: value }))}
> >
<Group mt="xs"> <Group mt="xs">
{Object.entries(scrollModeOptions).map(e => ( {Object.entries(scrollModeOptions).map(e => (
@@ -179,56 +147,56 @@ export function DisplaySettings() {
label={<Trans>Entries to keep above the selected entry when scrolling</Trans>} label={<Trans>Entries to keep above the selected entry when scrolling</Trans>}
description={<Trans>Only applies to compact, cozy and detailed modes</Trans>} description={<Trans>Only applies to compact, cozy and detailed modes</Trans>}
min={0} min={0}
value={entriesToKeepOnTop} value={settings.entriesToKeepOnTopWhenScrolling}
onChange={async value => await dispatch(changeEntriesToKeepOnTopWhenScrolling(+value))} onChange={value => dispatch(changeSettings({ entriesToKeepOnTopWhenScrolling: +value }))}
/> />
<Switch <Switch
label={<Trans>Scroll smoothly when navigating between entries</Trans>} label={<Trans>Scroll smoothly when navigating between entries</Trans>}
checked={scrollSpeed ? scrollSpeed > 0 : false} checked={settings.scrollSpeed ? settings.scrollSpeed > 0 : false}
onChange={async e => await dispatch(changeScrollSpeed(e.currentTarget.checked))} onChange={e => dispatch(changeSettings({ scrollSpeed: e.currentTarget.checked ? 400 : 0 }))}
/> />
<Switch <Switch
label={<Trans>In expanded view, scrolling through entries mark them as read</Trans>} label={<Trans>In expanded view, scrolling through entries mark them as read</Trans>}
checked={scrollMarks} checked={settings.scrollMarks}
onChange={async e => await dispatch(changeScrollMarks(e.currentTarget.checked))} onChange={e => dispatch(changeSettings({ scrollMarks: e.currentTarget.checked }))}
/> />
<Divider label={<Trans>Browser tab</Trans>} labelPosition="center" /> <Divider label={<Trans>Browser tab</Trans>} labelPosition="center" />
<Switch <Switch
label={<Trans>Show unread count in tab title</Trans>} label={<Trans>Show unread count in tab title</Trans>}
checked={unreadCountTitle} checked={settings.unreadCountTitle}
onChange={async e => await dispatch(changeUnreadCountTitle(e.currentTarget.checked))} onChange={e => dispatch(changeSettings({ unreadCountTitle: e.currentTarget.checked }))}
/> />
<Switch <Switch
label={<Trans>Show unread count in tab favicon</Trans>} label={<Trans>Show unread count in tab favicon</Trans>}
checked={unreadCountFavicon} checked={settings.unreadCountFavicon}
onChange={async e => await dispatch(changeUnreadCountFavicon(e.currentTarget.checked))} onChange={e => dispatch(changeSettings({ unreadCountFavicon: e.currentTarget.checked }))}
/> />
<Divider label={<Trans>Entry headers</Trans>} labelPosition="center" /> <Divider label={<Trans>Entry headers</Trans>} labelPosition="center" />
<Select <Select
label={<Trans>Show star icon</Trans>} label={<Trans>Show star icon</Trans>}
value={starIconDisplayMode} value={settings.starIconDisplayMode}
data={displayModeData} data={displayModeData}
onChange={async s => await dispatch(changeStarIconDisplayMode(s as IconDisplayMode))} onChange={value => dispatch(changeSettings({ starIconDisplayMode: value as IconDisplayMode }))}
/> />
<Select <Select
label={<Trans>Show external link icon</Trans>} label={<Trans>Show external link icon</Trans>}
value={externalLinkIconDisplayMode} value={settings.externalLinkIconDisplayMode}
data={displayModeData} data={displayModeData}
onChange={async s => await dispatch(changeExternalLinkIconDisplayMode(s as IconDisplayMode))} onChange={value => dispatch(changeSettings({ externalLinkIconDisplayMode: value as IconDisplayMode }))}
/> />
<Switch <Switch
label={<Trans>Show CommaFeed's own context menu on right click</Trans>} label={<Trans>Show CommaFeed's own context menu on right click</Trans>}
checked={customContextMenu} checked={settings.customContextMenu}
onChange={async e => await dispatch(changeCustomContextMenu(e.currentTarget.checked))} onChange={e => dispatch(changeSettings({ customContextMenu: e.currentTarget.checked }))}
/> />
<Divider label={<Trans>Sharing sites</Trans>} labelPosition="center" /> <Divider label={<Trans>Sharing sites</Trans>} labelPosition="center" />
@@ -238,12 +206,14 @@ export function DisplaySettings() {
<Switch <Switch
key={site} key={site}
label={Constants.sharing[site].label} label={Constants.sharing[site].label}
checked={sharingSettings?.[site]} checked={settings.sharingSettings[site]}
onChange={async e => onChange={e =>
await dispatch( dispatch(
changeSharingSetting({ changeSettings({
site, sharingSettings: {
value: e.currentTarget.checked, ...settings.sharingSettings,
[site]: e.currentTarget.checked,
},
}) })
) )
} }

View File

@@ -10,7 +10,7 @@ import { client, errorToStrings } from "@/app/client"
import { redirectToSelectedSource } from "@/app/redirect/thunks" import { redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store" import { useAppDispatch, useAppSelector } from "@/app/store"
import type { PushNotificationSettings as PushNotificationSettingsModel } from "@/app/types" import type { PushNotificationSettings as PushNotificationSettingsModel } from "@/app/types"
import { changePushNotificationSettings } from "@/app/user/thunks" import { changeSettings } from "@/app/user/thunks"
import { Alert } from "@/components/Alert" import { Alert } from "@/components/Alert"
export function PushNotificationSettings() { export function PushNotificationSettings() {
@@ -25,7 +25,7 @@ export function PushNotificationSettings() {
}, [form.initialize, notificationSettings]) }, [form.initialize, notificationSettings])
const handleSubmit = (values: PushNotificationSettingsModel) => { const handleSubmit = (values: PushNotificationSettingsModel) => {
dispatch(changePushNotificationSettings(values)) dispatch(changeSettings({ pushNotificationSettings: values }))
} }
const sendTestPushNotification = useAsyncCallback(client.user.sendTestPushNotification) const sendTestPushNotification = useAsyncCallback(client.user.sendTestPushNotification)