feat: 频道未读提示

pull/13/head
moonrailgun 4 years ago
parent 8a993203bb
commit b31ffdf6e3

@ -106,6 +106,8 @@ export {
useGroupInfo,
useGroupPanel,
useIsGroupOwner,
useGroupUnread,
useGroupTextPanelUnread,
} from './redux/hooks/useGroup';
export { useUserInfo, useUserId } from './redux/hooks/useUserInfo';
export { userActions, groupActions } from './redux/slices';

@ -1,7 +1,8 @@
import { useMemo } from 'react';
import type { GroupInfo, GroupPanel } from '../../model/group';
import { GroupInfo, GroupPanel, GroupPanelType } from '../../model/group';
import { isValidStr } from '../../utils/string-helper';
import { useAppSelector } from './useAppSelector';
import { useUnread } from './useUnread';
import { useUserId } from './useUserInfo';
/**
@ -41,3 +42,28 @@ export function useIsGroupOwner(groupId: string, userId?: string): boolean {
return typeof selfUserId === 'string' && groupInfo?.owner === selfUserId;
}
}
/**
*
* @param groupId id
*/
export function useGroupUnread(groupId: string): boolean {
const group = useGroupInfo(groupId);
const groupTextPanelIds = (group?.panels ?? [])
.filter((panel) => panel.type === GroupPanelType.TEXT)
.map((p) => p.id);
const unread = useUnread(groupTextPanelIds);
return unread.some((u) => u === true);
}
/**
*
* @param textPanelId id
*/
export function useGroupTextPanelUnread(textPanelId: string): boolean {
const unread = useUnread([textPanelId]);
return unread[0];
}

@ -0,0 +1,23 @@
import { useAppSelector } from './useAppSelector';
/**
*
*/
export function useUnread(converseIds: string[]) {
const ack = useAppSelector((state) => state.chat.ack);
const lastMessageMap = useAppSelector((state) => state.chat.lastMessageMap);
return converseIds.map((converseId) => {
if (
ack[converseId] === undefined &&
lastMessageMap[converseId] !== undefined
) {
// 没有已读记录且远程有数据
return true;
}
// 当远端最后一条消息的id > 本地已读状态的最后一条消息id,
// 则返回true(有未读消息)
return lastMessageMap[converseId] > ack[converseId];
});
}

@ -43,6 +43,10 @@ export const ChatMessageList = React.forwardRef<
const onUpdateReadedMessageRef = useUpdateRef(props.onUpdateReadedMessage);
useEffect(() => {
if (props.messages.length === 0) {
return;
}
if (containerRef.current?.scrollTop === 0) {
// 当前列表在最低
onUpdateReadedMessageRef.current(
@ -52,6 +56,10 @@ export const ChatMessageList = React.forwardRef<
}, [props.messages.length]);
const handleScroll = useCallback(() => {
if (props.messages.length === 0) {
return;
}
if (containerRef.current?.scrollTop === 0) {
onUpdateReadedMessageRef.current(
props.messages[props.messages.length - 1]._id

@ -31,7 +31,7 @@ export function useMessageAck(converseId: string, messages: ChatMessage[]) {
lastMessageIdRef.current = lastMessageId;
},
1000,
{ leading: false, trailing: true }
{ leading: true, trailing: true }
),
[]
);

@ -1,9 +1,15 @@
import React from 'react';
import { GroupPanelType, isValidStr, useGroupInfo } from 'tailchat-shared';
import {
GroupPanel,
GroupPanelType,
isValidStr,
useGroupInfo,
} from 'tailchat-shared';
import { useParams } from 'react-router';
import { GroupHeader } from './GroupHeader';
import { GroupSection } from '@/components/GroupSection';
import { GroupPanelItem } from '@/components/GroupPanelItem';
import { GroupTextPanelItem } from './TextPanelItem';
interface GroupParams {
groupId: string;
@ -17,6 +23,17 @@ export const Sidebar: React.FC = React.memo(() => {
const groupInfo = useGroupInfo(groupId);
const groupPanels = groupInfo?.panels ?? [];
const renderItem = (panel: GroupPanel) =>
panel.type === GroupPanelType.TEXT ? (
<GroupTextPanelItem groupId={groupId} panel={panel} />
) : (
<GroupPanelItem
name={panel.name}
icon={<div>#</div>}
to={`/main/group/${groupId}/${panel.id}`}
/>
);
return (
<div>
<GroupHeader groupId={groupId} />
@ -30,22 +47,11 @@ export const Sidebar: React.FC = React.memo(() => {
{groupPanels
.filter((sub) => sub.parentId === panel.id)
.map((sub) => (
<div key={sub.id}>
<GroupPanelItem
name={sub.name}
icon={<div>#</div>}
to={`/main/group/${groupId}/${sub.id}`}
/>
</div>
<div key={sub.id}>{renderItem(sub)}</div>
))}
</GroupSection>
) : (
<GroupPanelItem
key={panel.id}
name={panel.name}
icon={<div>#</div>}
to={`/main/group/${groupId}/${panel.id}`}
/>
<div key={panel.id}>{renderItem(panel)}</div>
)
)}
</div>

@ -0,0 +1,25 @@
import { GroupPanelItem } from '@/components/GroupPanelItem';
import React from 'react';
import { GroupPanel, useGroupTextPanelUnread } from 'tailchat-shared';
interface GroupTextPanelItemProps {
groupId: string;
panel: GroupPanel;
}
export const GroupTextPanelItem: React.FC<GroupTextPanelItemProps> = React.memo(
(props) => {
const { groupId, panel } = props;
const panelId = panel.id;
const hasUnread = useGroupTextPanelUnread(panelId);
return (
<GroupPanelItem
name={panel.name}
icon={<div>#</div>}
to={`/main/group/${groupId}/${panel.id}`}
badge={hasUnread}
/>
);
}
);
GroupTextPanelItem.displayName = 'GroupTextPanelItem';
Loading…
Cancel
Save