Handle unread notifications

Renaud Chaput 2024-06-18 11:03:47 +02:00
parent a4ba310adf
commit c14e537ef0
No known key found for this signature in database
GPG Key ID: BCFC859D49B46990
4 changed files with 62 additions and 48 deletions

View File

@ -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
}
/>
),

View File

@ -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 (
<ColumnLink
transparent
to='/notifications'
icon={<IconWithBadge id='bell' icon={NotificationsIcon} count={count} className='column-link__icon' />}
activeIcon={<IconWithBadge id='bell' icon={NotificationsActiveIcon} count={count} className='column-link__icon' />}
text={intl.formatMessage(messages.notifications)}
/>
<>
<ColumnLink
key='notifications'
transparent
to='/notifications'
icon={<IconWithBadge id='bell' icon={NotificationsIcon} count={count} className='column-link__icon' />}
activeIcon={<IconWithBadge id='bell' icon={NotificationsActiveIcon} count={count} className='column-link__icon' />}
text={intl.formatMessage(messages.notifications)}
/>
<ColumnLink
key='notifications-v2'
transparent
to='/notifications_v2'
icon={<IconWithBadge id='bell' icon={NotificationsIcon} count={newCount} className='column-link__icon' />}
activeIcon={<IconWithBadge id='bell' icon={NotificationsActiveIcon} count={newCount} className='column-link__icon' />}
text={"New Notifications"}
/>
</>
);
};

View File

@ -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) => {

View File

@ -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;
},
);