TurboMac 项目

category
Project
date
Jun 30, 2023
icon
slug
turbomac
status
Published
summary
该项目主要是一个仿 Mac 的系统,全部使用的 TypeScript 书写,使用 了一些前端的新技术,实现的功能包括顶部状态栏,App 的使用,如 Terminal、 TurboChat 等其他应用
tags
TypeScript
NodeJS
NestJS
React
ORM
Next
Docker
type
Post
git地址:

TurboMac

web 端

web 端所用技术栈

  • NextJS
  • React Server Component(RSC)
  • Zustand
  • Tailwind CSS
  • daisyui

调节 Web 亮度(Brightness)

TopBar 背景滤镜

backdrop-filter CSS 属性可以让你为一个元素后面区域添加图形效果(如模糊或颜色偏移)。因为它适用于元素背后的所有元素,为了看到效果,必须使元素或其背景至少部分透明。

App Window 窗口的逻辑

总体逻辑

应用的展现通过一个 displayApp 数组来记录所有启动的 app,再用 minimizeApps 数组记录缩小化的 app,还有两个特殊的记录值: maximizeApp 和 focusApp,分别记录当前最大化的 app 和当前聚焦的 app
当我们在 Dock 中点击一个 app 的时候,我们通过 Window 组件来展示它,主体逻辑是 Window 组件遍历 displayApp 展示所有需要渲染的 app,对于 minimizeApps,实现的逻辑很简单,如果最小化了,那就让当前 app 隐藏

操作

首先是一个 app 的注册配置文件,我展示其中一个的注册信息
然后先说明声明几个需要使用的变量,如上面逻辑展示
WindowApp 组件就是渲染需要展示的 app,难点在于 Window 组件的设计
可以分为三个部分
  • 窗口大小和位置的记录
  • 拖拽设计
  • 放大缩小所对应的逻辑
具体的就不展示了,整体来说,其实还是很简单的。

App Terminal 模块的逻辑

对于终端内容命令的展示,通过一个 JSX 数组存储并渲染的
还有两个变量用于历史记录的查找
  • commandHistory: 存储执行的命令
  • commandOffset: 跳转历史记录的偏移量(-commandHistory.length, 0)
命令交互的就不说了,说一下命令的执行 通过监听键盘事件来判断不同的情况
  • ArrowUp: 查找上一条历史记录
  • ArrowDown: 查找下一条历史记录
  • Tab: command 补全
  • Enter: 执行命令
上面三个很简单,主要说一下执行命令的逻辑
补充一个变量,用于记录已经注册的指令
都已经到执行命令的内部了,所以可以先将命令存入历史记录中,然后就找到指定的命令去执行即可

App TurboChat 模块的逻辑

Websocket 的逻辑

先说整体,当用户加入的时候,会通知更新当前在线人数,以及获取之前发送的消息(15 条);当用户离开的时候,也一样,更新当前在线人数。
  • onlineUsers(当前在线人数) 通过前端连接时,添加的query.id来唯一标识每一个 client socket,通过 server 获取到所有 socket,并进行过滤查询从而获取到所有用户的信息。
  • getMessages(获取历史消息) 我是根据偏移量(即已收到的消息个数)来再获取之前的消息历史,再查找想要的记录即可
    • createMessage(发送消息) 创建一个 newMessage,同时通知其他用户获取该消息 即通过addMessage事件派发新的一个消息

    Communication 的逻辑

    其实难点主要在于 Communication 模块对于消息的更新及其相应的处理
    • 滚动更新历史记录 通过监听onScroll事件并判断消息是否更新完判断是否接着更新
      • 消息渲染
        • 时间渲染 通过判断与上一条消息间隔是否超出了 5 分钟,从而显示时间戳 主要时间戳有今天的上午、下午,昨天的时间,及以前的日期
        • 消息渲染 有一个麻烦的地方就是要判断是自己发的消息还是对方发的消息,然后再显示消息的左右
      • 其余 (当然我目前的判定是有点问题的)
        • 发送消息后自动滚动到末尾
        • 开始滚动到末尾

      Input 的逻辑

      Upload 的逻辑

      首先重写了 storage 的存储方式
      条件过滤,文件为最大值:4MB,以及一些文件格式的过滤 上传后,获取到文件的 url 地址、类型,以及文件的大小,进行格式化(用于 web 端的显示)

      Input 的逻辑

      通过createMessage事件进行消息的发送
      • upload(文件上传并发送) 通过上传文件获取到对应的 type、url、size,再通过createMessage将消息发送出去
      • 媒体的渲染 就通过 src 来链接到对应的资源上,并通过对应的元素渲染它即可

      Server 端

      server 端所用技术栈

      • NestJS
      • Prsima
      • MySQL
      • passport
      • socket.io

      Websocket 的逻辑

      先说整体,当用户加入的时候,会通知更新当前在线人数,以及获取之前发送的消息(15 条);当用户离开的时候,也一样,更新当前在线人数。
      • onlineUsers(当前在线人数) 通过前端连接时,添加的query.id来唯一标识每一个 client socket,通过 server 获取到所有 socket,并进行过滤查询从而获取到所有用户的信息。
      • getMessages(获取历史消息) 我是根据偏移量(即已收到的消息个数)来再获取之前的消息历史,再查找想要的记录即可
      • createMessage(发送消息) 创建一个 newMessage,同时通知其他用户获取该消息 即通过addMessage事件派发新的一个消息

      Upload 的逻辑

      首先重写了 storage 的存储方式
      条件过滤,文件为最大值:4MB,以及一些文件格式的过滤 上传后,获取到文件的 url 地址、类型,以及文件的大小,进行格式化(用于 web 端的显示)

      鉴权登录 逻辑

      需要安装下面 4 个包

      local 登录(密码登录)

      封装一个 strategy,通过 validate 验证是否给予通过
      使用的方式

      JWT 认证

      首先先配置 JWT,即密钥和对应 token 过期时间
      接下来同理,配置 strategy

      总结

      从一开始的惊艳,到现在,哦~~~,原来这么简单,但是这种思路其他挺奇妙的 比如 App Window 窗口的实现,通过显示、隐藏等方式实现 Communication 中,多媒体的渲染,原理这么简单(可能是我自己没想过的原因) 到现在做完这个项目,其实挺开心的,因为本身也好久没做新东西了,而且也把之前学的新技术全用上了,非常的 nice

      © JinSo 2021 - 2026