当前位置: 首页 > news >正文

从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(十三) 优化聊天页面

1、 ChatBox组件

目前我们收到新的消息 不会滚动到视野当中

修改chatbox组件

// 最后一条消息的ref

  const lastMessageRef = useRef(null)

// 滚动到最新消息

  useEffect(()=>{

    if(lastMessageRef.current && messages) {

      lastMessageRef.current.scrollIntoView({behavior: 'smooth'})

    }

  })


import {useEffect,useRef} from "react"
import { useChatStore } from "../store/useChatStore"
import { useAuthStore } from "../store/useAuthStore"
import {formatMessageTime} from "@/lib/util"
import ChatHeader from "./ChatHeader"
import MessageInput from "./MessageInput"

const ChatBox = () => {
  const {messages, getMessages, isMessagesLoading, selectedUser, subscribeToMessages, unsubscribeFromMessages} = useChatStore()
  const {authUser} = useAuthStore()
  // 最后一条消息的ref
  const lastMessageRef = useRef(null)
  useEffect(()=>{
    getMessages(selectedUser._id)

    // 开始订阅消息
    subscribeToMessages()

    return () => unsubscribeFromMessages()
  },[selectedUser._id, getMessages,subscribeToMessages, unsubscribeFromMessages])

  // 滚动到最新消息
  useEffect(()=>{
    if(lastMessageRef.current && messages) {
      lastMessageRef.current.scrollIntoView({behavior: 'smooth'})
    }
  })
  if(isMessagesLoading) return <div>Loading...</div>
  return (
    <div className="flex-1 flex flex-col overflow-auto">
      {/* 聊天框头部 */}
      <ChatHeader/>
      
      {/* 聊天消息 */}
      <div className="flex-1 overflow-auto p-4 space-y-4">
          {messages.map((message)=> (
             <div
              key={message._id}
              // 消息的发送者id和当前用户id一致,则显示在右侧,否则显示在左侧
              className={`chat ${message.senderId===authUser._id ? 'chat-end' : 'chat-start'}`}
              ref={lastMessageRef}
             >
              <div className="chat-image avatar">
                 <div className="size-10 rounded-full border">
                    <img
                      src={message.senderId === authUser._id ? authUser.profilePic || 'http://via.placeholder.com/150' : selectedUser.profilePic}
                      alt=""
                    />
                 </div>
              </div>

              <div className="chat-header mb-1">
                 <time className="text-xs opacity-50 ml-1">{formatMessageTime(message.createdAt)}</time>
              </div>
              {/* flex-col 图片和文字上下排列 */}
              <div className="chat-bubble flex flex-col"> 
                 {message.image && (
                   <img 
                    src={message.image}
                    alt=""
                    className="sm:max-w-[200px] rounded-md mb-2"
                   />
                 )}
                 {message.text && <p>{message.text}</p>}
              </div>
             </div>
          ))}

      </div>

      {/* 消息输入 */}
      <MessageInput/>
    </div>
  )
}

export default ChatBox

2、Sidebar组件

展示在线人员

import { useEffect,useState} from "react"
import { useChatStore } from "../store/useChatStore"
import { useAuthStore } from "../store/useAuthStore"
import {User} from "lucide-react"

const Sidebar = () => {
    const {getUsers,users,selectedUser, setSelectedUser,isUsersLoading} = useChatStore()
    const {onlineUsers} = useAuthStore()
    const [showOnlyOnlineUsers, setShowOnlyOnlineUsers] = useState(false)
    // const onlineUsers = [];

    // 过滤在线用户
    const filterOnlineUsers = showOnlyOnlineUsers ? users.filter((user) => onlineUsers.includes(user._id)) :users
    useEffect(() => {
        getUsers()
    },[getUsers])

    if(isUsersLoading) return <div>Loading...</div>
  return (
    <aside className="h-full w-20 lg:w-72 border-r border-base-300 flex flex-col transition-all duration-200">
       <div className="border-b border-base-300 w-full p-5">
            <div className="flex items-center gap-2">
                <User  className="size-6" />
                <span className="font-medium hidden lg:block">联系人</span>
            </div>
            {/* 在线人员过滤 */}

            <div className="mt-3 hidden lg:flex items-center gap-2">
                <label className="cursor-pointer flex items-center gap-2">
                    <input 
                        type="checkbox"
                        checked={showOnlyOnlineUsers}
                        onChange={(e) => setShowOnlyOnlineUsers(e.target.checked)}
                        className="checkbox checkbox-sm"
                    />
                    <span className="text-sm">只显示在线</span>
                </label>
                <span className="text-xs text-zinc-500">({onlineUsers.length -1}在线)</span>
            </div>
       </div>

       <div className="overflow-y-auto w-full py-3">

         {filterOnlineUsers.map((user) =>(
            <button
                key={user._id}
                onClick={() => setSelectedUser(user)}
                className={`w-full p-3 flex items-center gap-3
                    hover:bg-base-300 transition-colors
                    ${selectedUser?._id===user._id}?"bg-base-300 ring-l ring-base-300":""
                `}
            >
                <div className="relative mx-auto lg:mx-0">
                    <img 
                        src={user.profilePic || "https://picsum.photos/200" }
                        alt={user.userName}
                        className="size-12 object-cover rounded-full"
                    
                    />
                    {onlineUsers.includes(user._id) &&(
                        <span className="absolute bottom-0 right-0 bg-green-500 size-4 rounded-full ring-2 ring-zinc-900">

                        </span>
                    )}
                </div>

                {/* 用户信息 只在大屏显示 */}
                <div className="hidden lg:block text-left min-w-0">
                    <div className="font-medium truncate">{user.userName}</div>
                    <div className="text-sm text-zinc-400">
                        {onlineUsers.includes(user._id) ? "在线" : "离线"}
                    </div>
                </div>
            </button>
         ))}

         {filterOnlineUsers.length === 0 && (
            <div className="text-center text-zinc-500 py-4">无在线用户</div>
         )}
       </div>
    </aside>
  )
}

export default Sidebar

3、ChatHeader组件

import { useChatStore } from "../store/useChatStore";
import { useAuthStore } from "../store/useAuthStore";
import { X } from "lucide-react";

const ChatHeader = () => {
    const {selectedUser,setSelectedUser} = useChatStore();
    const {onlineUsers} = useAuthStore()
  return (
    <div className="p-2.5 border-b border-base-300">
       <div className="flex items-center justify-between">
          <div className="flex items-center">
            {/* 头像 */}
            <div className="avatar">
                <div className="size-10 rounded-full relative">
                    <img src={selectedUser.profilePic || "https://picsum.photos/200"} alt={selectedUser.userName} />
                </div>
            </div>

            {/* 用户信息 */}
            <div>
                <h3 className="font-medium">{selectedUser.userName}</h3>
                <p className="text-sm text-base-content/70">
                    {onlineUsers.includes(selectedUser._id) ? "在线" : "离线"}
                </p>
            </div>
          </div>

          {/* 关闭按钮 */}
          <button onClick={()=>setSelectedUser(null)}>
            <X/>
          </button>
       </div>
    </div>
  )
}

export default ChatHeader

这下页面体验就更好了! 

 

 

相关文章:

  • 《机器学习数学基础》补充资料:过渡矩阵和坐标变换推导
  • 华为OD最新机试真题-模拟目录管理-C++-OD统一考试(E卷)
  • R语言和RStudio安装
  • 自学Java-JavaSE基础加强(多线程)
  • 计算机毕设-基于springboot的物流管理系统的设计与实现(附源码+lw+ppt+开题报告)
  • 60页PDF | 四川电信数据湖及数据中台实施方案!(附下载)
  • Spring Boot 3.x 核心注解详解与最佳实践
  • 常见位运算技巧总结:从基础到高阶应用
  • thunder bird 配置邮箱
  • @Async与@EnableAsync有10个定时任务接口,只执行了8个
  • 利用PHP爬虫获取17网(17zwd)商品详情:实战指南
  • 设计模式之迭代器模式
  • 无人机接替通航应急优势及技术分析
  • 【星云 Orbit•STM32F4】13. 探索定时器:基本定时器
  • 碰一碰发视频系统之写卡功能开发了,支持OEM
  • 【Git】linux搭建Gitea配置mysql数据库
  • Redis特性总结
  • Sqlserver安全篇之_手工创建TLS用到的pfx证书文件
  • AAA协议:从零认识网络的“身份管家”
  • springboot3 WebClient
  • 怎么制作网站导航页/网站排名优化推广
  • 九江做网站哪家好/百度客服电话人工服务热线
  • 东莞做网站seo/微信引流主动被加软件
  • wordpress分类页首页调用分类描述/seo优化及推广如何运营
  • 公司网站制作教学/seo的中文是什么
  • 北海市住建局官方网站/全网推广软件