diff --git a/commafeed-client/src/App.tsx b/commafeed-client/src/App.tsx index c8e97617..0519bf0e 100644 --- a/commafeed-client/src/App.tsx +++ b/commafeed-client/src/App.tsx @@ -71,7 +71,7 @@ function Providers(props: { children: React.ReactNode }) { ) } -// swagger-ui is very large, load only on-demand +// api documentation page is very large, load only on-demand const ApiDocumentationPage = React.lazy(async () => await import("pages/app/ApiDocumentationPage")) function AppRoutes() { @@ -142,16 +142,18 @@ function GoogleAnalyticsHandler() { return null } -function FaviconHandler() { - const root = useAppSelector(state => state.tree.rootCategory) +function UnreadCountTitleHandler({ unreadCount, enabled }: { unreadCount: number; enabled?: boolean }) { + return 0 ? `(${unreadCount}) CommaFeed` : "CommaFeed"} /> +} + +function UnreadCountFaviconHandler({ unreadCount, enabled }: { unreadCount: number; enabled?: boolean }) { useEffect(() => { - const unreadCount = categoryUnreadCount(root) - if (unreadCount === 0) { - Tinycon.reset() - } else { + if (enabled && unreadCount > 0) { Tinycon.setBubble(unreadCount) + } else { + Tinycon.reset() } - }, [root]) + }, [unreadCount, enabled]) return null } @@ -179,8 +181,13 @@ function CustomCode() { export function App() { useI18n() + const root = useAppSelector(state => state.tree.rootCategory) + const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle) + const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon) const dispatch = useAppDispatch() + const unreadCount = categoryUnreadCount(root) + useEffect(() => { dispatch(reloadServerInfos()) }, [dispatch]) @@ -188,7 +195,8 @@ export function App() { return ( <> - + + diff --git a/commafeed-client/src/app/types.ts b/commafeed-client/src/app/types.ts index d4ea0ff1..6fc41c6b 100644 --- a/commafeed-client/src/app/types.ts +++ b/commafeed-client/src/app/types.ts @@ -248,6 +248,8 @@ export interface Settings { markAllAsReadConfirmation: boolean customContextMenu: boolean mobileFooter: boolean + unreadCountTitle: boolean + unreadCountFavicon: boolean sharingSettings: SharingSettings } diff --git a/commafeed-client/src/app/user/slice.ts b/commafeed-client/src/app/user/slice.ts index f1611355..576c8e1f 100644 --- a/commafeed-client/src/app/user/slice.ts +++ b/commafeed-client/src/app/user/slice.ts @@ -16,6 +16,8 @@ import { changeSharingSetting, changeShowRead, changeStarIconDisplayMode, + changeUnreadCountFavicon, + changeUnreadCountTitle, reloadProfile, reloadSettings, reloadTags, @@ -91,6 +93,14 @@ export const userSlice = createSlice({ 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(changeSharingSetting.pending, (state, action) => { if (!state.settings) return state.settings.sharingSettings[action.meta.arg.site] = action.meta.arg.value @@ -107,6 +117,8 @@ export const userSlice = createSlice({ changeMarkAllAsReadConfirmation.fulfilled, changeCustomContextMenu.fulfilled, changeMobileFooter.fulfilled, + changeUnreadCountTitle.fulfilled, + changeUnreadCountFavicon.fulfilled, changeSharingSetting.fulfilled ), () => { diff --git a/commafeed-client/src/app/user/thunks.ts b/commafeed-client/src/app/user/thunks.ts index dd88bd0e..63a462d3 100644 --- a/commafeed-client/src/app/user/thunks.ts +++ b/commafeed-client/src/app/user/thunks.ts @@ -77,6 +77,16 @@ export const changeMobileFooter = createAppAsyncThunk("settings/mobileFooter", ( 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 changeSharingSetting = createAppAsyncThunk( "settings/sharingSetting", ( diff --git a/commafeed-client/src/components/settings/DisplaySettings.tsx b/commafeed-client/src/components/settings/DisplaySettings.tsx index bf444212..c3d9394a 100644 --- a/commafeed-client/src/components/settings/DisplaySettings.tsx +++ b/commafeed-client/src/components/settings/DisplaySettings.tsx @@ -17,6 +17,8 @@ import { changeSharingSetting, changeShowRead, changeStarIconDisplayMode, + changeUnreadCountFavicon, + changeUnreadCountTitle, } from "app/user/thunks" import { locales } from "i18n" import type { ReactNode } from "react" @@ -32,6 +34,8 @@ export function DisplaySettings() { const markAllAsReadConfirmation = useAppSelector(state => state.user.settings?.markAllAsReadConfirmation) 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 sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings) const dispatch = useAppDispatch() const { _ } = useLingui() @@ -91,6 +95,20 @@ export function DisplaySettings() { onChange={async e => await dispatch(changeMobileFooter(e.currentTarget.checked))} /> + Browser tab} labelPosition="center" /> + + Show unread count in tab title} + checked={unreadCountTitle} + onChange={async e => await dispatch(changeUnreadCountTitle(e.currentTarget.checked))} + /> + + Show unread count in tab favicon} + checked={unreadCountFavicon} + onChange={async e => await dispatch(changeUnreadCountFavicon(e.currentTarget.checked))} + /> + Entry headers} labelPosition="center" />