diff --git a/app/javascript/mastodon/features/notifications_v2/index.tsx b/app/javascript/mastodon/features/notifications_v2/index.tsx index 7feefe6358..471649d989 100644 --- a/app/javascript/mastodon/features/notifications_v2/index.tsx +++ b/app/javascript/mastodon/features/notifications_v2/index.tsx @@ -19,6 +19,7 @@ import { Icon } from 'mastodon/components/icon'; import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator'; import { useIdentity } from 'mastodon/identity_context'; import type { NotificationGap } from 'mastodon/reducers/notifications_groups'; +import { selectUnreadNotificationsGroupsCount } from 'mastodon/selectors/notifications'; import { selectNeedsNotificationPermission, selectSettingsNotificationsExcludedTypes, @@ -35,8 +36,8 @@ import { expandNotifications, scrollTopNotifications, loadPending, - mountNotifications, - unmountNotifications, + // mountNotifications, + // unmountNotifications, markNotificationsAsRead, } from '../../actions/notifications'; import Column from '../../components/column'; @@ -81,52 +82,35 @@ const getNotifications = createSelector( ); // const mapStateToProps = (state) => ({ -// isUnread: -// state.getIn(['notifications', 'unread']) > 0 || -// state.getIn(['notifications', 'pendingItems']).size > 0, // numPending: state.getIn(['notifications', 'pendingItems'], ImmutableList()) // .size, -// canMarkAsRead: -// state.getIn(['settings', 'notifications', 'showUnread']) && -// state.getIn(['notifications', 'readMarkerId']) !== '0' && -// getNotifications(state).some( -// (item) => -// item !== null && -// compareId( -// item.get('id'), -// state.getIn(['notifications', 'readMarkerId']), -// ) > 0, -// ), // }); export const Notifications: React.FC<{ columnId?: string; - isUnread?: boolean; multiColumn?: boolean; numPending: number; -}> = ({ isUnread, columnId, multiColumn, numPending }) => { +}> = ({ columnId, multiColumn, numPending }) => { const intl = useIntl(); const notifications = useAppSelector(getNotifications); const dispatch = useAppDispatch(); const isLoading = useAppSelector((s) => s.notificationsGroups.isLoading); const hasMore = useAppSelector((s) => s.notificationsGroups.hasMore); - const readMarkerId = useAppSelector( - (s) => s.notificationsGroups.readMarkerId, - ); + const lastReadId = useAppSelector((s) => - selectSettingsNotificationsShowUnread(s) - ? s.notificationsGroups.readMarkerId - : '0', + selectSettingsNotificationsShowUnread(s) ? s.markers.notifications : '0', ); - const canMarkAsRead = useAppSelector( - (s) => - selectSettingsNotificationsShowUnread(s) && - s.notificationsGroups.readMarkerId !== '0' && - notifications.some( - (item) => - item.type !== 'gap' && compareId(item.group_key, readMarkerId) > 0, - ), + + const unreadNotificationsCount = useAppSelector( + selectUnreadNotificationsGroupsCount, ); + + const isUnread = unreadNotificationsCount > 0; + + const canMarkAsRead = + useAppSelector(selectSettingsNotificationsShowUnread) && + unreadNotificationsCount > 0; + const needsNotificationPermission = useAppSelector( selectNeedsNotificationPermission, ); @@ -157,14 +141,14 @@ export const Notifications: React.FC<{ }, []); useEffect(() => { - dispatch(mountNotifications()); + // dispatch(mountNotifications()); // FIXME: remove once this becomes the main implementation void dispatch(fetchNotifications()); return () => { - dispatch(unmountNotifications()); - dispatch(scrollTopNotifications(false)); + // dispatch(unmountNotifications()); + // dispatch(scrollTopNotifications(false)); }; }, [dispatch]); @@ -282,7 +266,9 @@ export const Notifications: React.FC<{ onMoveUp={handleMoveUp} onMoveDown={handleMoveDown} unread={ - lastReadId !== '0' && compareId(item.group_key, lastReadId) > 0 + lastReadId !== '0' && + !!item.page_max_id && + compareId(item.page_max_id, lastReadId) > 0 } /> ), diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx index ff90eef359..13f1b4f083 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx @@ -34,6 +34,7 @@ import { NavigationPortal } from 'mastodon/components/navigation_portal'; import { identityContextPropShape, withIdentity } from 'mastodon/identity_context'; import { timelinePreview, trendsEnabled } from 'mastodon/initial_state'; import { transientSingleColumn } from 'mastodon/is_mobile'; +import { selectUnreadNotificationsGroupsCount } from 'mastodon/selectors/notifications'; import ColumnLink from './column_link'; import DisabledAccountBanner from './disabled_account_banner'; @@ -62,14 +63,27 @@ const NotificationsLink = () => { const count = useSelector(state => state.getIn(['notifications', 'unread'])); const intl = useIntl(); + const newCount = useSelector(selectUnreadNotificationsGroupsCount); + return ( - } - activeIcon={} - text={intl.formatMessage(messages.notifications)} - /> + <> + } + activeIcon={} + text={intl.formatMessage(messages.notifications)} + /> + } + activeIcon={} + text={"New Notifications"} + /> + ); }; diff --git a/app/javascript/mastodon/reducers/notifications_groups.ts b/app/javascript/mastodon/reducers/notifications_groups.ts index a9b73ff046..aa869bf9b5 100644 --- a/app/javascript/mastodon/reducers/notifications_groups.ts +++ b/app/javascript/mastodon/reducers/notifications_groups.ts @@ -31,18 +31,14 @@ export interface NotificationGap { interface NotificationGroupsState { groups: (NotificationGroup | NotificationGap)[]; - unread: number; isLoading: boolean; hasMore: boolean; - readMarkerId: string; } const initialState: NotificationGroupsState = { groups: [], - unread: 0, isLoading: false, hasMore: false, - readMarkerId: '0', }; function removeNotificationsForAccounts( @@ -172,7 +168,6 @@ export const notificationsGroupsReducer = }) .addCase(clearNotifications.pending, (state) => { state.groups = []; - state.unread = 0; state.hasMore = false; }) .addCase(blockAccountSuccess, (state, action) => { diff --git a/app/javascript/mastodon/selectors/notifications.ts b/app/javascript/mastodon/selectors/notifications.ts new file mode 100644 index 0000000000..ced25fcafb --- /dev/null +++ b/app/javascript/mastodon/selectors/notifications.ts @@ -0,0 +1,19 @@ +import { createSelector } from '@reduxjs/toolkit'; + +import { compareId } from 'mastodon/compare_id'; +import type { RootState } from 'mastodon/store'; + +export const selectUnreadNotificationsGroupsCount = createSelector( + [ + (s: RootState) => s.markers.notifications, + (s: RootState) => s.notificationsGroups.groups, + ], + (notificationMarker, groups) => { + return groups.filter( + (group) => + group.type !== 'gap' && + group.page_max_id && + compareId(group.page_max_id, notificationMarker) > 0, + ).length; + }, +);