Add logic to display notification icon based on state
This commit is contained in:
parent
63335e9274
commit
3a4c6466b0
|
@ -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 (
|
||||
<AnimatedTab
|
||||
|
@ -160,7 +162,10 @@ const Tabs = ({ items, activeItem }: ITabs) => {
|
|||
</span>
|
||||
) : null}
|
||||
|
||||
{text}
|
||||
<div className='relative flex items-center justify-center gap-1.5'>
|
||||
{text}
|
||||
{notification && <div className='absolute -right-4 size-2 animate-pulse rounded-full bg-primary-500' />}
|
||||
</div>
|
||||
</div>
|
||||
</AnimatedTab>
|
||||
);
|
||||
|
|
|
@ -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<IStatusList, 'statusIds' | 'isLoading' | 'hasMore'> {
|
||||
|
@ -30,6 +31,7 @@ const Timeline: React.FC<ITimeline> = ({
|
|||
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<boolean>(window.scrollY < 50);
|
||||
const [intervalId, setIntervalId] = useState<NodeJS.Timeout | null>(null);
|
||||
|
@ -48,11 +50,18 @@ const Timeline: React.FC<ITimeline> = ({
|
|||
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);
|
||||
|
|
|
@ -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<IHomePage> = ({ 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<IHomePage> = ({ children }) => {
|
|||
<div className='sticky top-12 z-20 bg-white/90 backdrop-blur black:bg-black/90 dark:bg-primary-900/90 lg:top-0'>
|
||||
<Tabs
|
||||
items={[
|
||||
{ name: 'home', text: <FormattedMessage id='tabs_bar.home' defaultMessage='Home' />, to: '/' },
|
||||
{ name: 'local', text: <div className='block max-w-xs truncate'>{instance.domain}</div>, to: '/timeline/local' },
|
||||
{ name: 'home', text: <FormattedMessage id='tabs_bar.home' defaultMessage='Home' />, to: '/', notification: notifications.home },
|
||||
{ name: 'local', text: <div className='block max-w-xs truncate'>{instance.domain}</div>, to: '/timeline/local', notification: notifications.instance },
|
||||
]}
|
||||
activeItem={pathname === '/timeline/local' ? 'local' : 'home'}
|
||||
/>
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue