From 3a4c6466b053f032e0d6553f3ab99fb5e43cab56 Mon Sep 17 00:00:00 2001 From: danidfra Date: Mon, 3 Feb 2025 20:52:56 -0300 Subject: [PATCH] Add logic to display notification icon based on state --- src/components/ui/tabs.tsx | 9 +++++++-- src/features/ui/components/timeline.tsx | 9 +++++++++ src/pages/home-page.tsx | 7 +++++-- src/reducers/index.ts | 2 ++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index e3a1c11c1..f76baeb49 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -116,6 +116,8 @@ export type Item = { count?: number; /** Unique name for this tab. */ name: string; + /** Display a notificationicon over the tab */ + notification?: boolean; } interface ITabs { @@ -142,7 +144,7 @@ const Tabs = ({ items, activeItem }: ITabs) => { }; const renderItem = (item: Item, idx: number) => { - const { name, text, title, count } = item; + const { name, text, title, count, notification } = item; return ( { ) : null} - {text} +
+ {text} + {notification &&
} +
); diff --git a/src/features/ui/components/timeline.tsx b/src/features/ui/components/timeline.tsx index 795e40f72..f6dfe4cf1 100644 --- a/src/features/ui/components/timeline.tsx +++ b/src/features/ui/components/timeline.tsx @@ -6,6 +6,7 @@ import { dequeueTimeline, scrollTopTimeline } from 'soapbox/actions/timelines.ts import StatusList, { IStatusList } from 'soapbox/components/status-list.tsx'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts'; +import { setNotification } from 'soapbox/reducers/notificationsSlice.ts'; import { makeGetStatusIds } from 'soapbox/selectors/index.ts'; interface ITimeline extends Omit { @@ -30,6 +31,7 @@ const Timeline: React.FC = ({ const isLoading = useAppSelector(state => (state.timelines.get(timelineId) || { isLoading: true }).isLoading === true); const isPartial = useAppSelector(state => (state.timelines.get(timelineId)?.isPartial || false) === true); const hasMore = useAppSelector(state => state.timelines.get(timelineId)?.hasMore === true); + const hasQueuedItems = useAppSelector(state => state.timelines.get(timelineId)?.totalQueuedItemsCount || 0); const [isInTop, setIsInTop] = useState(window.scrollY < 50); const [intervalId, setIntervalId] = useState(null); @@ -48,11 +50,18 @@ const Timeline: React.FC = ({ dispatch(scrollTopTimeline(timelineId, false)); }, 100), [timelineId]); + useEffect(() => { + if (hasQueuedItems) { + dispatch(setNotification({ timelineId: timelineId, value: hasQueuedItems > 0 })); + } + }, [hasQueuedItems, timelineId]); + useEffect(() => { if (isInTop) { handleDequeueTimeline(); const interval = setInterval(handleDequeueTimeline, 2000); setIntervalId(interval); + dispatch(setNotification({ timelineId: timelineId, value: false })); } else if (intervalId) { clearInterval(intervalId); diff --git a/src/pages/home-page.tsx b/src/pages/home-page.tsx index 8db870b76..5913db7ad 100644 --- a/src/pages/home-page.tsx +++ b/src/pages/home-page.tsx @@ -1,6 +1,7 @@ import clsx from 'clsx'; import { useRef } from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; +import { useSelector } from 'react-redux'; import { Link, useLocation } from 'react-router-dom'; import { uploadCompose } from 'soapbox/actions/compose.ts'; @@ -30,6 +31,7 @@ import { useInstance } from 'soapbox/hooks/useInstance.ts'; import { useIsMobile } from 'soapbox/hooks/useIsMobile.ts'; import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts'; import { useSoapboxConfig } from 'soapbox/hooks/useSoapboxConfig.ts'; +import { RootState } from 'soapbox/store.ts'; import ComposeForm from '../features/compose/components/compose-form.tsx'; @@ -41,6 +43,7 @@ const HomePage: React.FC = ({ children }) => { const intl = useIntl(); const dispatch = useAppDispatch(); const { pathname } = useLocation(); + const notifications = useSelector((state: RootState) => state.notificationsTab); const me = useAppSelector(state => state.me); const { account } = useOwnAccount(); @@ -105,8 +108,8 @@ const HomePage: React.FC = ({ children }) => {
, to: '/' }, - { name: 'local', text:
{instance.domain}
, to: '/timeline/local' }, + { name: 'home', text: , to: '/', notification: notifications.home }, + { name: 'local', text:
{instance.domain}
, to: '/timeline/local', notification: notifications.instance }, ]} activeItem={pathname === '/timeline/local' ? 'local' : 'home'} /> diff --git a/src/reducers/index.ts b/src/reducers/index.ts index 21fae33b3..e57877462 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -32,6 +32,7 @@ import meta from './meta.ts'; import modals from './modals.ts'; import mutes from './mutes.ts'; import notifications from './notifications.ts'; +import notificationsTab from './notificationsSlice.ts'; import onboarding from './onboarding.ts'; import patron from './patron.ts'; import pending_statuses from './pending-statuses.ts'; @@ -87,6 +88,7 @@ export default combineReducers({ modals, mutes, notifications, + notificationsTab, onboarding, patron, pending_statuses,