chatStore.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. import {
  2. MESSAGE_TYPE,
  3. MESSAGE_STATUS
  4. } from '@/common/enums.js';
  5. import userStore from './userStore';
  6. /*
  7. uniapp性能优化:
  8. 1.由于uniapp渲染消息性能非常拉胯,所以先把离线消息存储到cacheChats,等
  9. 待所有离线消息拉取完成后,再统一进行渲染
  10. 2.在vuex中对数组进行unshift,splice特别卡,所以删除会话、会话置顶、删
  11. 除消息等操作进行优化,不通过unshift,splice实现,改造方案如下:
  12. 删除会话: 通过delete标志判断是否删除
  13. 删除消息:通过delete标志判断是否删除
  14. 会话置顶:通过lastSendTime排序确定会话顺序
  15. */
  16. let cacheChats = [];
  17. export default {
  18. state: {
  19. chats: [],
  20. privateMsgMaxId: 0,
  21. groupMsgMaxId: 0,
  22. loadingPrivateMsg: false,
  23. loadingGroupMsg: false,
  24. },
  25. mutations: {
  26. initChats(state, chatsData) {
  27. cacheChats = [];
  28. state.chats = [];
  29. for (let chat of chatsData.chats) {
  30. // 已删除的会话直接丢弃
  31. if (chat.delete) {
  32. continue;
  33. }
  34. // 暂存至缓冲区
  35. cacheChats.push(JSON.parse(JSON.stringify(chat)));
  36. // 加载期间显示只前15个会话做做样子,一切都为了加快初始化时间
  37. if (state.chats.length < 15) {
  38. chat.messages = [];
  39. state.chats.push(chat);
  40. }
  41. }
  42. state.privateMsgMaxId = chatsData.privateMsgMaxId || 0;
  43. state.groupMsgMaxId = chatsData.groupMsgMaxId || 0;
  44. // 防止图片一直处在加载中状态
  45. cacheChats.forEach((chat) => {
  46. chat.messages.forEach((msg) => {
  47. if (msg.loadStatus == "loading") {
  48. msg.loadStatus = "fail"
  49. }
  50. })
  51. })
  52. },
  53. openChat(state, chatInfo) {
  54. let chats = this.getters.findChats();
  55. let chat = null;
  56. for (let idx in chats) {
  57. if (chats[idx].type == chatInfo.type &&
  58. chats[idx].targetId === chatInfo.targetId) {
  59. chat = chats[idx];
  60. chat.delete = false;
  61. // 放置头部
  62. this.commit("moveTop", idx)
  63. break;
  64. }
  65. }
  66. // 创建会话
  67. if (chat == null) {
  68. chat = {
  69. targetId: chatInfo.targetId,
  70. type: chatInfo.type,
  71. showName: chatInfo.showName,
  72. headImage: chatInfo.headImage,
  73. lastContent: "",
  74. lastSendTime: new Date().getTime(),
  75. unreadCount: 0,
  76. messages: [],
  77. atMe: false,
  78. atAll: false,
  79. delete: false
  80. };
  81. chats.push(chat);
  82. this.commit("moveTop", chats.length - 1)
  83. }
  84. },
  85. activeChat(state, idx) {
  86. let chats = this.getters.findChats();
  87. if (idx >= 0) {
  88. chats[idx].unreadCount = 0;
  89. }
  90. },
  91. resetUnreadCount(state, chatInfo) {
  92. let chats = this.getters.findChats();
  93. for (let idx in chats) {
  94. if (chats[idx].type == chatInfo.type &&
  95. chats[idx].targetId == chatInfo.targetId) {
  96. chats[idx].unreadCount = 0;
  97. chats[idx].atMe = false;
  98. chats[idx].atAll = false;
  99. }
  100. }
  101. this.commit("saveToStorage");
  102. },
  103. readedMessage(state, pos) {
  104. let chats = this.getters.findChats();
  105. for (let idx in chats) {
  106. if (chats[idx].type == 'PRIVATE' &&
  107. chats[idx].targetId == pos.friendId) {
  108. chats[idx].messages.forEach((m) => {
  109. if (m.selfSend && m.status != MESSAGE_STATUS.RECALL) {
  110. // pos.maxId为空表示整个会话已读
  111. if (!pos.maxId || m.id <= pos.maxId) {
  112. m.status = MESSAGE_STATUS.READED
  113. }
  114. }
  115. })
  116. }
  117. }
  118. this.commit("saveToStorage");
  119. },
  120. removeChat(state, idx) {
  121. let chats = this.getters.findChats();
  122. chats[idx].delete = true;
  123. this.commit("saveToStorage");
  124. },
  125. removePrivateChat(state, userId) {
  126. let chats = this.getters.findChats();
  127. for (let idx in chats) {
  128. if (chats[idx].type == 'PRIVATE' &&
  129. chats[idx].targetId == userId) {
  130. this.commit("removeChat", idx);
  131. }
  132. }
  133. },
  134. removeGroupChat(state, groupId) {
  135. let chats = this.getters.findChats();
  136. for (let idx in chats) {
  137. if (chats[idx].type == 'GROUP' &&
  138. chats[idx].targetId == groupId) {
  139. this.commit("removeChat", idx);
  140. }
  141. }
  142. },
  143. moveTop(state, idx) {
  144. let chats = this.getters.findChats();
  145. let chat = chats[idx];
  146. // 最新的时间会显示在顶部
  147. chat.lastSendTime = new Date().getTime();
  148. this.commit("saveToStorage");
  149. },
  150. insertMessage(state, msgInfo) {
  151. // 获取对方id或群id
  152. let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE';
  153. // 记录消息的最大id
  154. if (msgInfo.id && type == "PRIVATE" && msgInfo.id > state.privateMsgMaxId) {
  155. state.privateMsgMaxId = msgInfo.id;
  156. }
  157. if (msgInfo.id && type == "GROUP" && msgInfo.id > state.groupMsgMaxId) {
  158. state.groupMsgMaxId = msgInfo.id;
  159. }
  160. // 如果是已存在消息,则覆盖旧的消息数据
  161. let chat = this.getters.findChat(msgInfo);
  162. let message = this.getters.findMessage(chat, msgInfo);
  163. if (message) {
  164. Object.assign(message, msgInfo);
  165. // 撤回消息需要显示
  166. if (msgInfo.type == MESSAGE_TYPE.RECALL) {
  167. chat.lastContent = msgInfo.content;
  168. }
  169. this.commit("saveToStorage");
  170. return;
  171. }
  172. // 会话列表内容
  173. if (msgInfo.type == MESSAGE_TYPE.IMAGE) {
  174. chat.lastContent = "[图片]";
  175. } else if (msgInfo.type == MESSAGE_TYPE.FILE) {
  176. chat.lastContent = "[文件]";
  177. } else if (msgInfo.type == MESSAGE_TYPE.AUDIO) {
  178. chat.lastContent = "[语音]";
  179. } else if (msgInfo.type == MESSAGE_TYPE.TEXT || msgInfo.type == MESSAGE_TYPE.RECALL) {
  180. chat.lastContent = msgInfo.content;
  181. } else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VOICE) {
  182. chat.lastContent = "[语音通话]";
  183. } else if (msgInfo.type == MESSAGE_TYPE.ACT_RT_VIDEO) {
  184. chat.lastContent = "[视频通话]";
  185. }
  186. chat.lastSendTime = msgInfo.sendTime;
  187. chat.sendNickName = msgInfo.sendNickName;
  188. // 未读加1
  189. if (!msgInfo.selfSend && msgInfo.status != MESSAGE_STATUS.READED &&
  190. msgInfo.type != MESSAGE_TYPE.TIP_TEXT) {
  191. chat.unreadCount++;
  192. }
  193. // 是否有人@我
  194. if (!msgInfo.selfSend && chat.type == "GROUP" && msgInfo.atUserIds &&
  195. msgInfo.status != MESSAGE_STATUS.READED) {
  196. let userId = userStore.state.userInfo.id;
  197. if (msgInfo.atUserIds.indexOf(userId) >= 0) {
  198. chat.atMe = true;
  199. }
  200. if (msgInfo.atUserIds.indexOf(-1) >= 0) {
  201. chat.atAll = true;
  202. }
  203. }
  204. // 间隔大于10分钟插入时间显示
  205. if (!chat.lastTimeTip || (chat.lastTimeTip < msgInfo.sendTime - 600 * 1000)) {
  206. chat.messages.push({
  207. sendTime: msgInfo.sendTime,
  208. type: MESSAGE_TYPE.TIP_TIME,
  209. });
  210. chat.lastTimeTip = msgInfo.sendTime;
  211. }
  212. // 根据id顺序插入,防止消息乱序
  213. let insertPos = chat.messages.length;
  214. // 防止 图片、文件 在发送方 显示 在顶端 因为还没存库,id=0
  215. if (msgInfo.id && msgInfo.id > 0) {
  216. for (let idx in chat.messages) {
  217. if (chat.messages[idx].id && msgInfo.id < chat.messages[idx].id) {
  218. insertPos = idx;
  219. console.log(`消息出现乱序,位置:${chat.messages.length},修正至:${insertPos}`);
  220. break;
  221. }
  222. }
  223. }
  224. if (insertPos == chat.messages.length) {
  225. // 这种赋值效率最高
  226. chat.messages[insertPos] = msgInfo;
  227. } else {
  228. chat.messages.splice(insertPos, 0, msgInfo);
  229. }
  230. this.commit("saveToStorage");
  231. },
  232. updateMessage(state, msgInfo) {
  233. // 获取对方id或群id
  234. let chat = this.getters.findChat(msgInfo);
  235. let message = this.getters.findMessage(chat, msgInfo);
  236. if (message) {
  237. // 属性拷贝
  238. Object.assign(message, msgInfo);
  239. this.commit("saveToStorage");
  240. }
  241. },
  242. deleteMessage(state, msgInfo) {
  243. // 获取对方id或群id
  244. let chat = this.getters.findChat(msgInfo);
  245. for (let idx in chat.messages) {
  246. // 已经发送成功的,根据id删除
  247. if (chat.messages[idx].id && chat.messages[idx].id == msgInfo.id) {
  248. chat.messages[idx].delete = true;
  249. break;
  250. }
  251. // 正在发送中的消息可能没有id,根据发送时间删除
  252. if (msgInfo.selfSend && chat.messages[idx].selfSend &&
  253. chat.messages[idx].sendTime == msgInfo.sendTime) {
  254. chat.messages[idx].delete = true;
  255. break;
  256. }
  257. }
  258. this.commit("saveToStorage");
  259. },
  260. updateChatFromFriend(state, friend) {
  261. let chats = this.getters.findChats();
  262. for (let i in chats) {
  263. let chat = chats[i];
  264. if (chat.type == 'PRIVATE' && chat.targetId == friend.id) {
  265. chat.headImage = friend.headImageThumb;
  266. chat.showName = friend.nickName;
  267. break;
  268. }
  269. }
  270. this.commit("saveToStorage");
  271. },
  272. updateChatFromGroup(state, group) {
  273. let chats = this.getters.findChats();
  274. for (let i in chats) {
  275. let chat = chats[i];
  276. if (chat.type == 'GROUP' && chat.targetId == group.id) {
  277. chat.headImage = group.headImageThumb;
  278. chat.showName = group.showGroupName;
  279. break;
  280. }
  281. }
  282. this.commit("saveToStorage");
  283. },
  284. loadingPrivateMsg(state, loading) {
  285. state.loadingPrivateMsg = loading;
  286. if (!this.getters.isLoading()) {
  287. this.commit("refreshChats")
  288. }
  289. },
  290. loadingGroupMsg(state, loading) {
  291. state.loadingGroupMsg = loading;
  292. if (!this.getters.isLoading()) {
  293. this.commit("refreshChats")
  294. }
  295. },
  296. refreshChats(state) {
  297. // 排序
  298. cacheChats.sort((chat1, chat2) => {
  299. return chat2.lastSendTime - chat1.lastSendTime;
  300. });
  301. // 将消息一次性装载回来
  302. state.chats = cacheChats;
  303. // 断线重连后不能使用缓存模式,否则会导致聊天窗口的消息不刷新
  304. cacheChats = state.chats;
  305. this.commit("saveToStorage");
  306. },
  307. saveToStorage(state) {
  308. // 加载中不保存,防止卡顿
  309. if (this.getters.isLoading()) {
  310. return;
  311. }
  312. let userId = userStore.state.userInfo.id;
  313. let key = "chats-app-" + userId;
  314. let chatsData = {
  315. privateMsgMaxId: state.privateMsgMaxId,
  316. groupMsgMaxId: state.groupMsgMaxId,
  317. chats: state.chats
  318. }
  319. uni.setStorage({
  320. key: key,
  321. data: chatsData ,
  322. })
  323. },
  324. clear(state) {
  325. cacheChats = [];
  326. state.chats = [];
  327. state.privateMsgMaxId = 0;
  328. state.groupMsgMaxId = 0;
  329. state.loadingPrivateMsg = false;
  330. state.loadingGroupMsg = false;
  331. }
  332. },
  333. actions: {
  334. loadChat(context) {
  335. return new Promise((resolve, reject) => {
  336. let userId = userStore.state.userInfo.id;
  337. uni.getStorage({
  338. key: "chats-app-" + userId,
  339. success(res) {
  340. context.commit("initChats", res.data);
  341. resolve()
  342. },
  343. fail(e) {
  344. resolve()
  345. }
  346. });
  347. })
  348. }
  349. },
  350. getters: {
  351. isLoading: (state) => () => {
  352. return state.loadingPrivateMsg || state.loadingGroupMsg
  353. },
  354. findChats: (state, getters) => () => {
  355. return getters.isLoading() ? cacheChats : state.chats;
  356. },
  357. findChatIdx: (state, getters) => (chat) => {
  358. let chats = getters.findChats();
  359. for (let idx in chats) {
  360. if (chats[idx].type == chat.type &&
  361. chats[idx].targetId === chat.targetId) {
  362. chat = state.chats[idx];
  363. return idx;
  364. }
  365. }
  366. },
  367. findChat: (state, getters) => (msgInfo) => {
  368. let chats = getters.findChats();
  369. // 获取对方id或群id
  370. let type = msgInfo.groupId ? 'GROUP' : 'PRIVATE';
  371. let targetId = msgInfo.groupId ? msgInfo.groupId : msgInfo.selfSend ? msgInfo.recvId : msgInfo.sendId;
  372. let chat = null;
  373. for (let idx in chats) {
  374. if (chats[idx].type == type &&
  375. chats[idx].targetId === targetId) {
  376. chat = chats[idx];
  377. break;
  378. }
  379. }
  380. return chat;
  381. },
  382. findMessage: (state) => (chat, msgInfo) => {
  383. if (!chat) {
  384. return null;
  385. }
  386. for (let idx in chat.messages) {
  387. // 通过id判断
  388. if (msgInfo.id && chat.messages[idx].id == msgInfo.id) {
  389. return chat.messages[idx];
  390. }
  391. // 正在发送中的消息可能没有id,只有tmpId
  392. if (msgInfo.tmpId && chat.messages[idx].tmpId &&
  393. chat.messages[idx].tmpId == msgInfo.tmpId) {
  394. console.log("chat.messages[idx].tmpId == msgInfo.tmpId")
  395. return chat.messages[idx];
  396. }
  397. }
  398. }
  399. }
  400. }