fix(core): fix notification list (#11423)

This commit is contained in:
EYHN
2025-04-03 10:44:32 +00:00
parent 01429f8f66
commit 093bffdf5c
4 changed files with 46 additions and 19 deletions
@@ -138,3 +138,11 @@ export const itemNameLabelIcon = style({
marginRight: '4px', marginRight: '4px',
color: cssVarV2('icon/primary'), color: cssVarV2('icon/primary'),
}); });
export const loadMoreIndicator = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '24px',
color: cssVarV2('text/secondary'),
});
@@ -3,6 +3,7 @@ import {
Button, Button,
IconButton, IconButton,
notify, notify,
observeIntersection,
Scrollable, Scrollable,
Skeleton, Skeleton,
} from '@affine/component'; } from '@affine/component';
@@ -35,47 +36,53 @@ import {
} from '@blocksuite/icons/rc'; } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import clsx from 'clsx'; import clsx from 'clsx';
import { useCallback, useEffect, useMemo, useState } from 'react'; import {
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
} from 'react';
import { useNavigateHelper } from '../hooks/use-navigate-helper'; import { useNavigateHelper } from '../hooks/use-navigate-helper';
import * as styles from './list.style.css'; import * as styles from './list.style.css';
export const NotificationList = () => { export const NotificationList = () => {
const t = useI18n();
const notificationListService = useService(NotificationListService); const notificationListService = useService(NotificationListService);
const notifications = useLiveData(notificationListService.notifications$); const notifications = useLiveData(notificationListService.notifications$);
const isLoading = useLiveData(notificationListService.isLoading$); const isLoading = useLiveData(notificationListService.isLoading$);
const error = useLiveData(notificationListService.error$); const error = useLiveData(notificationListService.error$);
const hasMore = useLiveData(notificationListService.hasMore$);
const loadMoreIndicatorRef = useRef<HTMLDivElement>(null);
const userFriendlyError = useMemo(() => { const userFriendlyError = useMemo(() => {
return error && UserFriendlyError.fromAny(error); return error && UserFriendlyError.fromAny(error);
}, [error]); }, [error]);
useEffect(() => { useLayoutEffect(() => {
// reset the notification list when the component is mounted // reset the notification list when the component is mounted
notificationListService.reset(); notificationListService.reset();
notificationListService.loadMore(); notificationListService.loadMore();
}, [notificationListService]); }, [notificationListService]);
const handleScrollEnd = useCallback(() => { useEffect(() => {
notificationListService.loadMore(); if (loadMoreIndicatorRef.current) {
}, [notificationListService]); let previousIsIntersecting = false;
return observeIntersection(loadMoreIndicatorRef.current, entity => {
const handleScroll = useCallback( if (entity.isIntersecting && !previousIsIntersecting && hasMore) {
(e: React.UIEvent<HTMLDivElement>) => { notificationListService.loadMore();
const target = e.currentTarget; }
if (target.scrollHeight - target.scrollTop <= target.clientHeight + 1) { previousIsIntersecting = entity.isIntersecting;
handleScrollEnd(); });
} }
}, return;
[handleScrollEnd] }, [hasMore, notificationListService]);
);
return ( return (
<Scrollable.Root> <Scrollable.Root>
<Scrollable.Viewport <Scrollable.Viewport className={styles.containerScrollViewport}>
className={styles.containerScrollViewport}
onScroll={handleScroll}
>
{notifications.length > 0 ? ( {notifications.length > 0 ? (
<ul className={styles.itemList}> <ul className={styles.itemList}>
{notifications.map(notification => ( {notifications.map(notification => (
@@ -94,6 +101,13 @@ export const NotificationList = () => {
) : ( ) : (
<NotificationListEmpty /> <NotificationListEmpty />
)} )}
<div
ref={loadMoreIndicatorRef}
className={hasMore ? styles.loadMoreIndicator : ''}
>
{hasMore ? t['com.affine.notification.loading-more']() : null}
</div>
</Scrollable.Viewport> </Scrollable.Viewport>
<Scrollable.Scrollbar /> <Scrollable.Scrollbar />
</Scrollable.Root> </Scrollable.Root>
+4
View File
@@ -7229,6 +7229,10 @@ export function useAFFiNEI18N(): {
* `No new notifications` * `No new notifications`
*/ */
["com.affine.notification.empty"](): string; ["com.affine.notification.empty"](): string;
/**
* `Loading more...`
*/
["com.affine.notification.loading-more"](): string;
/** /**
* `You'll be notified here for @mentions and workspace invites.` * `You'll be notified here for @mentions and workspace invites.`
*/ */
@@ -1797,6 +1797,7 @@
"com.affine.notification.unsupported": "Unsupported message", "com.affine.notification.unsupported": "Unsupported message",
"com.affine.notification.mention": "<1>{{username}}</1> mentioned you in <2>{{docTitle}}</2>", "com.affine.notification.mention": "<1>{{username}}</1> mentioned you in <2>{{docTitle}}</2>",
"com.affine.notification.empty": "No new notifications", "com.affine.notification.empty": "No new notifications",
"com.affine.notification.loading-more": "Loading more...",
"com.affine.notification.empty.description": "You'll be notified here for @mentions and workspace invites.", "com.affine.notification.empty.description": "You'll be notified here for @mentions and workspace invites.",
"com.affine.notification.invitation-accepted": "<1>{{username}}</1> has accept your invitation", "com.affine.notification.invitation-accepted": "<1>{{username}}</1> has accept your invitation",
"com.affine.notification.invitation-review-request": "<1>{{username}}</1> has requested to join <2>{{workspaceName}}</2>", "com.affine.notification.invitation-review-request": "<1>{{username}}</1> has requested to join <2>{{workspaceName}}</2>",