mirror of https://github.com/msgbyte/tailchat
				
				
				
			feat: 收件箱侧边栏展示
							parent
							
								
									f1238badbd
								
							
						
					
					
						commit
						db917d26b9
					
				@ -0,0 +1,17 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 收件箱记录项类型
 | 
			
		||||
 */
 | 
			
		||||
export interface InboxItem {
 | 
			
		||||
  _id: string;
 | 
			
		||||
  userId: string;
 | 
			
		||||
  readed: boolean;
 | 
			
		||||
  type: 'message';
 | 
			
		||||
  message?: {
 | 
			
		||||
    groupId?: string;
 | 
			
		||||
    converseId: string;
 | 
			
		||||
    messageId: string;
 | 
			
		||||
    messageSnippet: string;
 | 
			
		||||
  };
 | 
			
		||||
  createdAt: string;
 | 
			
		||||
  updatedAt: string;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
import type { InboxItem } from '../../model/inbox';
 | 
			
		||||
import { useAppSelector } from './useAppSelector';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 返回收件箱列表
 | 
			
		||||
 */
 | 
			
		||||
export function useInboxList(): InboxItem[] {
 | 
			
		||||
  return useAppSelector((state) => state.chat.inbox ?? []);
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,21 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { useAppSelector, useDMConverseName } from 'tailchat-shared';
 | 
			
		||||
 | 
			
		||||
interface ConverseNameProps {
 | 
			
		||||
  converseId: string;
 | 
			
		||||
  className?: string;
 | 
			
		||||
  style?: React.CSSProperties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ConverseName: React.FC<ConverseNameProps> = React.memo((props) => {
 | 
			
		||||
  const { converseId, className, style } = props;
 | 
			
		||||
  const converse = useAppSelector((state) => state.chat.converses[converseId]);
 | 
			
		||||
  const converseName = useDMConverseName(converse);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <span className={className} style={style}>
 | 
			
		||||
      {converseName}
 | 
			
		||||
    </span>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
ConverseName.displayName = 'ConverseName';
 | 
			
		||||
@ -0,0 +1,20 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import { useGroupInfo } from 'tailchat-shared';
 | 
			
		||||
 | 
			
		||||
interface GroupNameProps {
 | 
			
		||||
  groupId: string;
 | 
			
		||||
  className?: string;
 | 
			
		||||
  style?: React.CSSProperties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const GroupName: React.FC<GroupNameProps> = React.memo((props) => {
 | 
			
		||||
  const { groupId, className, style } = props;
 | 
			
		||||
  const groupInfo = useGroupInfo(groupId);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <span className={className} style={style}>
 | 
			
		||||
      {groupInfo?.name}
 | 
			
		||||
    </span>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
GroupName.displayName = 'GroupName';
 | 
			
		||||
@ -1,32 +1,86 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { useMemo } from 'react';
 | 
			
		||||
import { CommonSidebarWrapper } from '@/components/CommonSidebarWrapper';
 | 
			
		||||
import { t } from 'tailchat-shared';
 | 
			
		||||
import { isValidStr, model, t, useInboxList } from 'tailchat-shared';
 | 
			
		||||
import clsx from 'clsx';
 | 
			
		||||
import _orderBy from 'lodash/orderBy';
 | 
			
		||||
import { GroupName } from '@/components/GroupName';
 | 
			
		||||
import { ConverseName } from '@/components/ConverseName';
 | 
			
		||||
import { getMessageRender } from '@/plugin/common';
 | 
			
		||||
 | 
			
		||||
interface InboxSidebarProps {
 | 
			
		||||
  selectedItem: string;
 | 
			
		||||
  onSelect: (itemId: string) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 收件箱侧边栏组件
 | 
			
		||||
 */
 | 
			
		||||
export const InboxSidebar: React.FC = React.memo(() => {
 | 
			
		||||
export const InboxSidebar: React.FC<InboxSidebarProps> = React.memo((props) => {
 | 
			
		||||
  const inbox = useInboxList();
 | 
			
		||||
  const list = useMemo(() => _orderBy(inbox, 'createdAt', 'desc'), [inbox]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <CommonSidebarWrapper data-tc-role="sidebar-inbox">
 | 
			
		||||
      <div className="overflow-auto">
 | 
			
		||||
        {Array.from({ length: 20 }).map((_, i) => {
 | 
			
		||||
          return (
 | 
			
		||||
            <div
 | 
			
		||||
              key={i}
 | 
			
		||||
              className="p-2 overflow-auto cursor-pointer hover:bg-black hover:bg-opacity-10 dark:hover:bg-white dark:hover:bg-opacity-10"
 | 
			
		||||
            >
 | 
			
		||||
              <div className="text-lg">Title {i}</div>
 | 
			
		||||
              <div className="break-all text-opacity-80 text-black dark:text-opacity-80 dark:text-white text-sm p-1 border-l-2 border-gray-500 border-opacity-50">
 | 
			
		||||
                DescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDescDesc
 | 
			
		||||
              </div>
 | 
			
		||||
              <div className="text-xs text-opacity-50 text-black dark:text-opacity-50 dark:text-white">
 | 
			
		||||
                {t('来自')}: Tailchat
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          );
 | 
			
		||||
        {list.map((item) => {
 | 
			
		||||
          const { type } = item;
 | 
			
		||||
 | 
			
		||||
          if (type === 'message') {
 | 
			
		||||
            const message: Partial<model.inbox.InboxItem['message']> =
 | 
			
		||||
              item.message ?? {};
 | 
			
		||||
            let title: React.ReactNode = '';
 | 
			
		||||
            if (isValidStr(message.groupId)) {
 | 
			
		||||
              title = <GroupName groupId={message.groupId} />;
 | 
			
		||||
            } else if (isValidStr(message.converseId)) {
 | 
			
		||||
              title = <ConverseName converseId={message.converseId} />;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return (
 | 
			
		||||
              <InboxSidebarItem
 | 
			
		||||
                key={item._id}
 | 
			
		||||
                title={title}
 | 
			
		||||
                desc={getMessageRender(message.messageSnippet ?? '')}
 | 
			
		||||
                source={'Tailchat'}
 | 
			
		||||
                selected={props.selectedItem === item._id}
 | 
			
		||||
                onSelect={() => props.onSelect(item._id)}
 | 
			
		||||
              />
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        })}
 | 
			
		||||
      </div>
 | 
			
		||||
    </CommonSidebarWrapper>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
InboxSidebar.displayName = 'InboxSidebar';
 | 
			
		||||
 | 
			
		||||
const InboxSidebarItem: React.FC<{
 | 
			
		||||
  title: React.ReactNode;
 | 
			
		||||
  desc: React.ReactNode;
 | 
			
		||||
  source: string;
 | 
			
		||||
  selected: boolean;
 | 
			
		||||
  onSelect: () => void;
 | 
			
		||||
}> = React.memo((props) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className={clsx(
 | 
			
		||||
        'p-2 overflow-auto cursor-pointer hover:bg-black hover:bg-opacity-10 dark:hover:bg-white dark:hover:bg-opacity-10',
 | 
			
		||||
        {
 | 
			
		||||
          'bg-black bg-opacity-10 dark:bg-white dark:bg-opacity-10':
 | 
			
		||||
            props.selected,
 | 
			
		||||
        }
 | 
			
		||||
      )}
 | 
			
		||||
      onClick={props.onSelect}
 | 
			
		||||
    >
 | 
			
		||||
      <div className="text-lg overflow-ellipsis overflow-hidden">
 | 
			
		||||
        {props.title || <span> </span>}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="break-all text-opacity-80 text-black dark:text-opacity-80 dark:text-white text-sm p-1 border-l-2 border-gray-500 border-opacity-50">
 | 
			
		||||
        {props.desc}
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="text-xs text-opacity-50 text-black dark:text-opacity-50 dark:text-white">
 | 
			
		||||
        {t('来自')}: {props.source}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
InboxSidebarItem.displayName = 'InboxSidebarItem';
 | 
			
		||||
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue