ipanel-rnapp-ajb-base 1.0.38 → 1.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app/base.js CHANGED
@@ -51,12 +51,75 @@ global.base.homedHttp = homedHttp;
51
51
  global.base.scaleSize = scaleSize;
52
52
  global.base.px = scaleSize;
53
53
  global.base.messageManager = {
54
- messages: [],
55
- wsRef: null,
54
+ messages: [], // deprecated,等addrMsgData使用起来之后废弃
55
+ addrMsgData: {
56
+ /**
57
+ * "addressId": {
58
+ * messages: [],
59
+ * latestReadedMsgId: "" // 最新一条已读的消息id。 latestReadedMsgId到最后一条消息之间的云端消息条数即为未读消息数
60
+ * }
61
+ */
62
+ },
63
+ addrMsgDataUpdateTimer: null,
64
+ ws: null,
65
+ connectStatus: undefined,
56
66
  sessionId: "",
57
67
 
68
+ getAddrMsgList: function (addrId) {
69
+ return global.base.messageManager.addrMsgData[addrId]
70
+ ? global.base.messageManager.addrMsgData[addrId].messages
71
+ : [];
72
+ },
73
+
74
+ // 用户读取的最新一条消息
75
+ readLatestMsg: function (msg) {
76
+ if (msg.FromUserId !== "cloud") {
77
+ // 只针对云端消息设置已读
78
+ return;
79
+ }
80
+ if (global.base.messageManager.addrMsgData[msg.FromAddrId]) {
81
+ if (
82
+ global.base.messageManager.addrMsgData[msg.FromAddrId]
83
+ .latestReadedMsgId === msg.MsgId
84
+ ) {
85
+ return;
86
+ }
87
+ global.base.messageManager.addrMsgData[msg.FromAddrId].latestReadedMsgId =
88
+ msg.MsgId;
89
+
90
+ // 更新本地存储
91
+ clearTimeout(global.base.messageManager.addrMsgDataUpdateTimer);
92
+ global.base.messageManager.addrMsgDataUpdateTimer = setTimeout(() => {
93
+ storage.set("addrMsgData", global.base.messageManager.addrMsgData);
94
+ }, 300);
95
+
96
+ eventBus.emit(EVENTS.MESSAGE.UNREAD_COUNT_UPDATE);
97
+ }
98
+ },
99
+
100
+ // 获取未读消息数量
101
+ getUnreadMsgCount: function (addressId) {
102
+ const msgData = global.base.messageManager.addrMsgData[addressId];
103
+
104
+ if (!msgData || !msgData.latestReadedMsgId) {
105
+ return 0;
106
+ }
107
+ const readedMsgIndex = msgData.messages.findIndex(
108
+ (msg) => msg.MsgId === msgData.latestReadedMsgId
109
+ );
110
+ let unreadMsgs =
111
+ readedMsgIndex === -1
112
+ ? msgData.messages
113
+ : msgData.messages.slice(readedMsgIndex + 1);
114
+
115
+ // 来自云端的消息才计入未读消息
116
+ unreadMsgs = unreadMsgs.filter((msg) => msg.FromUserId === "cloud");
117
+
118
+ return unreadMsgs.length;
119
+ },
120
+
58
121
  isConnected: function () {
59
- return !!global.base.messageManager.wsRef.current;
122
+ return !!global.base.messageManager.ws;
60
123
  },
61
124
 
62
125
  setSessionId: function (sid) {
@@ -65,24 +128,46 @@ global.base.messageManager = {
65
128
 
66
129
  sendMessage: async function (message) {
67
130
  console.log("ws.send", message);
68
- global.base.messageManager.wsRef.current &&
69
- global.base.messageManager.wsRef.current.send(JSON.stringify(message));
131
+ global.base.messageManager.ws &&
132
+ global.base.messageManager.ws.send(JSON.stringify(message));
70
133
  eventBus.emit(EVENTS.MESSAGE.ADD, message);
71
134
  },
72
135
 
73
136
  updateMessageToStorage: async function (message) {
74
- const oldMsgIndex = global.base.messageManager.messages.findIndex(
137
+ const data = global.base.messageManager.addrMsgData[message.FromAddrId] || {
138
+ messages: [],
139
+ latestMsgId: "",
140
+ };
141
+
142
+ const oldMsgIndex = data.messages.findIndex(
75
143
  (msg) => msg.MsgId === message.MsgId
76
144
  );
77
145
  if (oldMsgIndex === -1) {
78
- console.error("不存在的消息,msgId=", message.MsgId);
146
+ console.log("不存在的消息,msgId=", message.MsgId);
79
147
  return;
80
148
  }
81
149
 
82
- global.base.messageManager.messages.splice(oldMsgIndex, 1, message);
150
+ data.messages.splice(oldMsgIndex, 1, message);
151
+
152
+ await storage.set("addrMsgData", global.base.messageManager.addrMsgData);
153
+ },
83
154
 
84
- // save to local
85
- await storage.set("messageList", global.base.messageManager.messages);
155
+ removeMsg(msgId) {
156
+ const addrMsgData = global.base.messageManager.addrMsgData;
157
+
158
+ for (let addrId in addrMsgData) {
159
+ const msgData = addrMsgData[addrId];
160
+ const msgIdx = msgData.messages.findIndex((msg) => msg.MsgId === msgId);
161
+ if (msgIdx !== -1) {
162
+ msgData.messages.splice(msgIdx, 1);
163
+ // 更新本地存储
164
+ clearTimeout(global.base.messageManager.addrMsgDataUpdateTimer);
165
+ global.base.messageManager.addrMsgDataUpdateTimer = setTimeout(() => {
166
+ storage.set("addrMsgData", addrMsgData);
167
+ }, 300);
168
+ break;
169
+ }
170
+ }
86
171
  },
87
172
 
88
173
  genMsg,
@@ -1,4 +1,4 @@
1
- import EventEmitter from 'events';
1
+ import EventEmitter from "events";
2
2
 
3
3
  // 全局事件总线
4
4
  // 事件名规范:
@@ -11,45 +11,46 @@ export default eventBus;
11
11
  export const EVENTS = {
12
12
  // 智能客服聊天界面
13
13
  MESSAGE: {
14
- ADD: 'message.add', // 新增消息事件
15
- RENDER: 'message.render', // 渲染消息UI
16
- CONNECT_STATUS_CHANGE: 'message.connectStatusChange', // websocket连接状态变更事件
17
- RECONNECT: 'message.reconnect', // 触发websocket重连事件
18
- CUSTOM_SERVICE_CONNECT_CHANGE: 'message.customServiceConnectChange', // 智能客服转人工的进入和退出事件
14
+ ADD: "message.add", // 新增消息事件
15
+ RENDER: "message.render", // 渲染消息UI
16
+ CONNECT_STATUS_CHANGE: "message.connectStatusChange", // websocket连接状态变更事件
17
+ RECONNECT: "message.reconnect", // 触发websocket重连事件
18
+ CUSTOM_SERVICE_CONNECT_CHANGE: "message.customServiceConnectChange", // 智能客服转人工的进入和退出事件
19
+ UNREAD_COUNT_UPDATE: "message.unreadCountUpdate",
19
20
  },
20
21
  // 底部自定义导航栏
21
22
  CUSTOM_TAB_BAR: {
22
- HIDE_MORE_AREA: 'CustomTabBar.hideMoreArea', // 隐藏更多按钮区域事件
23
+ HIDE_MORE_AREA: "CustomTabBar.hideMoreArea", // 隐藏更多按钮区域事件
23
24
  },
24
25
  // 我的家庭
25
26
  FAMILY: {
26
- CHANGE: 'family.change', // 当前家庭切换事件
27
+ CHANGE: "family.change", // 当前家庭切换事件
27
28
  },
28
29
  HOMED: {
29
- TOKEN_CHANGE: 'homed.tokenChange',
30
+ TOKEN_CHANGE: "homed.tokenChange",
30
31
  },
31
32
  TV: {
32
- SHARE_TIME_LIMIT: 'tv.shareTimeLimit',
33
- HOME_DATA_CHANGE: 'tv.homeDataChange',
33
+ SHARE_TIME_LIMIT: "tv.shareTimeLimit",
34
+ HOME_DATA_CHANGE: "tv.homeDataChange",
34
35
  },
35
36
  SMART_HOME: {
36
- BLE_MESH_ADD: 'smartHome.bleMeshAdd',
37
+ BLE_MESH_ADD: "smartHome.bleMeshAdd",
37
38
  },
38
39
  IOT: {
39
- DEVICE_STATUS_UPDATE: 'device.status.update',
40
- ITEE_NETWORK_CHANGE: 'itee.network.change',
40
+ DEVICE_STATUS_UPDATE: "device.status.update",
41
+ ITEE_NETWORK_CHANGE: "itee.network.change",
41
42
  },
42
43
  ACCOUNT: {
43
- AVATAR_CHANGE: 'avatar.change',
44
+ AVATAR_CHANGE: "avatar.change",
44
45
  },
45
46
  // 商城客服聊天界面
46
47
  SHOPMESSAGE: {
47
- ADD: 'shop.message.add', // 新增消息事件
48
- RENDER: 'shop.message.render', // 渲染消息UI
49
- CONNECT_STATUS_CHANGE: 'shop.message.connectStatusChange', // websocket连接状态变更事件
50
- RECONNECT: 'shop.message.reconnect', // 触发websocket重连事件
51
- ONLINE_LIST: 'shop.message.online', // 商城在线用户
52
- CUSTOM_SERVICE_CONNECT_CHANGE: 'shop.message.customServiceConnectChange', // 智能客服转人工的进入和退出事件
53
- HISTORY_MESSAGE_LIST: 'shop.message.history', // 获取历史消息
48
+ ADD: "shop.message.add", // 新增消息事件
49
+ RENDER: "shop.message.render", // 渲染消息UI
50
+ CONNECT_STATUS_CHANGE: "shop.message.connectStatusChange", // websocket连接状态变更事件
51
+ RECONNECT: "shop.message.reconnect", // 触发websocket重连事件
52
+ ONLINE_LIST: "shop.message.online", // 商城在线用户
53
+ CUSTOM_SERVICE_CONNECT_CHANGE: "shop.message.customServiceConnectChange", // 智能客服转人工的进入和退出事件
54
+ HISTORY_MESSAGE_LIST: "shop.message.history", // 获取历史消息
54
55
  },
55
56
  };
@@ -1,34 +1,69 @@
1
1
  import React, { useState, useEffect, useCallback, useRef } from "react";
2
- import { PermissionsAndroid, Platform } from "react-native";
3
-
4
- import { format } from "date-fns";
5
-
6
2
  import storage from "../storage";
7
3
  import eventBus, { EVENTS } from "../eventBus";
8
4
  import config from "../utils/config";
9
- import { MSG_TYPES, genMsg } from "../utils/common";
5
+ import { MSG_TYPES, genMsg, CONNECT_STATUS } from "../utils/common";
10
6
  import applyPermissions from "../utils/applyPermissions";
11
7
 
12
- let connectStatus = -1; // -1 连接中 0 连接成功 1连接失败
8
+ let wsInstance = null;
13
9
 
14
10
  let tryConnectTimes = 0;
11
+ let isReConnecting = false;
12
+ const MAX_CONNECT_TIMES = 7;
15
13
 
16
14
  /**
17
15
  * 智能客服中心JS
18
16
  */
19
17
 
18
+ // storage.set('addrMsgData', {}); // 这是测试代码,如果要清空本地消息,则临时放开这段运行一下
20
19
  // storage.set('messageList', []); // 这是测试代码,如果要清空本地消息,则临时放开这段运行一下
21
- // get local messageList
22
- storage.get("messageList").then((msgList) => {
23
- if (typeof msgList === "string") {
24
- try {
25
- msgList = JSON.parse(msgList);
26
- } catch (e) {
27
- console.log("msgList error", e, msgList);
28
- msgList = [];
20
+ // 将持久化的消息列表数据恢复到内存中
21
+ storage.get("addrMsgData").then((data) => {
22
+ if (data) {
23
+ if (typeof data === "string") {
24
+ try {
25
+ data = JSON.parse(data);
26
+ } catch (e) {
27
+ console.log("data error", e, data);
28
+ data = {};
29
+ }
29
30
  }
31
+ global.base.messageManager.addrMsgData = data || {};
32
+ eventBus.emit(EVENTS.MESSAGE.UNREAD_COUNT_UPDATE);
33
+ } else {
34
+ // deprecated,等addrMsgData使用起来之后废弃
35
+ // 将持久化的消息列表数据恢复到内存中
36
+ storage.get("messageList").then((msgList) => {
37
+ if (typeof msgList === "string") {
38
+ try {
39
+ msgList = JSON.parse(msgList);
40
+ } catch (e) {
41
+ console.log("msgList error", e, msgList);
42
+ msgList = [];
43
+ }
44
+ }
45
+ global.base.messageManager.messages = msgList || [];
46
+
47
+ // 将废弃的旧格式字段转成新格式字段,再恢复到内存中
48
+ const addrMsgData = {};
49
+ global.base.messageManager.messages.forEach((msg) => {
50
+ if (addrMsgData[msg.FromAddrId]) {
51
+ addrMsgData[msg.FromAddrId].messages.push(msg);
52
+ addrMsgData[msg.FromAddrId].latestReadedMsgId = msg.MsgId;
53
+ } else {
54
+ addrMsgData[msg.FromAddrId] = {
55
+ messages: [msg],
56
+ latestReadedMsgId: msg.MsgId,
57
+ };
58
+ }
59
+ });
60
+ global.base.messageManager.addrMsgData = addrMsgData;
61
+ eventBus.emit(EVENTS.MESSAGE.UNREAD_COUNT_UPDATE);
62
+ storage.set("addrMsgData", addrMsgData);
63
+ // 移除旧格式字段
64
+ storage.remove("messageList");
65
+ });
30
66
  }
31
- global.base.messageManager.messages = msgList || [];
32
67
  });
33
68
 
34
69
  // 当前是否处于智能客服转人工
@@ -43,32 +78,75 @@ let manualCustomServiceTimer;
43
78
  // 智能客服聊天AI模型: CUSTOMER_SERVICE=客服智能体
44
79
  let aiModel = "CUSTOMER_SERVICE";
45
80
 
81
+ let storageTimer;
82
+
83
+ // 缓存正在发送中的消息,MsgId->Msg,当消息发送成功或发送失败时,需要将其从缓存中删除
84
+ const pendingMsgMap = {};
85
+
46
86
  const UseMessageManager = () => {
47
- const ws = useRef(null);
87
+ const connectedAddressId = useRef(null);
48
88
 
49
89
  const sendMessage = useCallback((msg) => {
50
- if (ws.current) {
90
+ // 给用户发送的消息追加Status字段,此时消息是“未发送态”,如果未连接且重连失败,则将状态改为“发送失败态”
91
+ // 然后先直接把正在发送的消息显示出来,待消息发送成功或失败时再更新消息状态
92
+ msg.Status = "pending";
93
+ eventBus.emit(EVENTS.MESSAGE.ADD, msg);
94
+
95
+ if (wsInstance) {
51
96
  console.log("ws.send");
52
- ws.current.send(JSON.stringify(msg));
53
- eventBus.emit(EVENTS.MESSAGE.ADD, msg);
97
+ wsInstance.send(JSON.stringify(msg));
98
+ } else {
99
+ // 未连接,触发重连
100
+ reConnect(() => {
101
+ console.log("ws.send");
102
+ wsInstance.send(JSON.stringify(msg));
103
+ });
54
104
  }
55
105
  }, []);
56
106
 
57
107
  const connectMsgServer = async (connectSuccessCallback) => {
108
+ if (
109
+ global.base.messageManager.connectStatus === CONNECT_STATUS.CONNECTING ||
110
+ (global.base.messageManager.connectStatus === CONNECT_STATUS.CONNECTED &&
111
+ wsInstance &&
112
+ wsInstance.readyState < 2)
113
+ ) {
114
+ console.log(
115
+ `useMessageManager connectMsgServer skipped, connectStatus=${global.base.messageManager.connectStatus}, ws.readyState=${wsInstance?.readyState}`
116
+ );
117
+ return;
118
+ }
119
+ global.base.messageManager.connectStatus = CONNECT_STATUS.CONNECTING;
120
+ eventBus.emit(
121
+ EVENTS.MESSAGE.CONNECT_STATUS_CHANGE,
122
+ global.base.messageManager.connectStatus
123
+ );
124
+
58
125
  console.log("connectMsgServer start");
59
126
  const token = await storage.get("accessToken");
60
127
  const accountData = await storage.get("accountData");
61
128
  const activeAddr = await storage.get("activeAddr");
129
+ connectedAddressId.current = activeAddr?.address_id;
62
130
  let wsUrl = `${
63
131
  config.IPANEL_CLOUD_WSS_URI
64
132
  }/s/ch/${await global.base.getHomeCloudServiceInstanceName()}/common/msgcenter/websocket`;
65
133
 
134
+ // 获取最新一条消息的id
135
+ const addrMsgData = await storage.get("addrMsgData");
136
+ const messages =
137
+ addrMsgData && addrMsgData[activeAddr?.address_id]
138
+ ? addrMsgData[activeAddr?.address_id].messages
139
+ : [];
140
+
66
141
  const connectParams = [
67
142
  `user_id=${accountData.user.user_id}`, // 用户id
68
143
  `address_id=${activeAddr.address_id}`, // 用户当前选中的家庭地址id
69
144
  `token=${token}`,
70
145
  "lon=",
71
146
  "lat=",
147
+ `latest_msg_id=${
148
+ messages.length > 0 ? messages[messages.length - 1].MsgId : ""
149
+ }`,
72
150
  ];
73
151
 
74
152
  // 设置智能客服AI大模型
@@ -77,30 +155,71 @@ const UseMessageManager = () => {
77
155
  }
78
156
 
79
157
  // 缓存全局智能客服websocket连接
80
- global.base.messageManager.wsRef = ws;
81
158
  global.base.messageManager.connectMsgServer = connectMsgServer;
82
159
 
83
160
  const url = `${wsUrl}?${connectParams.join("&")}`;
84
161
  console.log("useMessageManager ws url:", url);
85
162
 
86
- ws.current = new WebSocket(url);
87
- ws.current.onopen = () => {
163
+ wsInstance = new WebSocket(url);
164
+ global.base.messageManager.ws = wsInstance;
165
+ wsInstance.onopen = () => {
88
166
  console.log("useMessageManager ws open");
89
- connectStatus = 0;
167
+ global.base.messageManager.connectStatus = CONNECT_STATUS.CONNECTED;
90
168
  tryConnectTimes = 0;
91
- eventBus.emit(EVENTS.MESSAGE.CONNECT_STATUS_CHANGE, connectStatus);
169
+ isReConnecting = false;
170
+ eventBus.emit(
171
+ EVENTS.MESSAGE.CONNECT_STATUS_CHANGE,
172
+ global.base.messageManager.connectStatus
173
+ );
174
+ eventBus.emit(EVENTS.MESSAGE.UNREAD_COUNT_UPDATE);
92
175
 
93
176
  connectSuccessCallback && connectSuccessCallback();
94
177
  };
95
- ws.current.onmessage = ({ data }) => {
178
+ wsInstance.onmessage = ({ data }) => {
179
+ // 处理来自云端的消息
96
180
  try {
97
181
  const msg = JSON.parse(data);
98
- if (msg.MsgType === "chat" && msg.Chat.type === "tool_call_begin") {
182
+
183
+ const messages = global.base.messageManager.getAddrMsgList(
184
+ activeAddr.address_id
185
+ );
186
+
187
+ // 用户每发送一条消息到云端,云端都会响应一条确认事件消息,用于更新该消息的本地MsgId为服务器生成的ID
188
+ if (
189
+ msg.MsgType === MSG_TYPES.EVENT &&
190
+ msg.Event.Type === "user_msg_received"
191
+ ) {
192
+ const targetMsg = messages.find(
193
+ (item) => item.MsgId === msg.Event.Data.MsgId // 本地产生的ID
194
+ );
195
+ if (targetMsg) {
196
+ targetMsg.MsgId = msg.MsgId; // 服务器产生的ID
197
+ targetMsg.CreateTime = msg.CreateTime;
198
+ // 将消息从“未发送态”修改为“已发送态”(即删除Status字段)
199
+ delete targetMsg.Status;
200
+ delete pendingMsgMap[msg.Event.Data.MsgId];
201
+
202
+ // 避免频繁写入本地存储,在ios可能会导致闪退
203
+ clearTimeout(storageTimer);
204
+ storageTimer = setTimeout(() => {
205
+ storage.set(
206
+ "addrMsgData",
207
+ global.base.messageManager.addrMsgData
208
+ );
209
+ }, 300);
210
+ }
211
+ return;
212
+ }
213
+ // AI大模型工具消息合并处理
214
+ if (
215
+ msg.MsgType === MSG_TYPES.CHAT &&
216
+ msg.Chat.type === "tool_call_begin"
217
+ ) {
99
218
  const toolCallId = msg.Chat.payload.tool_calls[0].id;
100
219
  if (
101
- global.base.messageManager.messages.find(
220
+ messages.find(
102
221
  (msg) =>
103
- msg.MsgType === "chat" &&
222
+ msg.MsgType === MSG_TYPES.CHAT &&
104
223
  msg.Chat.type === "end" &&
105
224
  msg.Chat.payload.message.tool_calls &&
106
225
  msg.Chat.payload.message.tool_calls[0].id === toolCallId
@@ -115,18 +234,30 @@ const UseMessageManager = () => {
115
234
  console.log("msg不合法", e);
116
235
  }
117
236
  };
118
- ws.current.onerror = (err) => {
237
+ wsInstance.onerror = (err) => {
119
238
  console.log("useMessageManager ws onerror", err);
120
- connectStatus = 1;
121
- eventBus.emit(EVENTS.MESSAGE.CONNECT_STATUS_CHANGE, connectStatus);
239
+ global.base.messageManager.connectStatus = CONNECT_STATUS.DISCONNECTED;
240
+ eventBus.emit(
241
+ EVENTS.MESSAGE.CONNECT_STATUS_CHANGE,
242
+ global.base.messageManager.connectStatus
243
+ );
122
244
  };
123
- ws.current.onclose = (event) => {
245
+ wsInstance.onclose = (event) => {
124
246
  console.log("useMessageManager ws onclose", event);
125
247
 
126
- global.base.messageManager.wsRef.current = null;
127
- connectStatus = 1;
128
- eventBus.emit(EVENTS.MESSAGE.CONNECT_STATUS_CHANGE, connectStatus);
129
- reConnect(connectSuccessCallback);
248
+ wsInstance = null;
249
+ global.base.messageManager.ws = null;
250
+ global.base.messageManager.connectStatus = CONNECT_STATUS.DISCONNECTED;
251
+ eventBus.emit(
252
+ EVENTS.MESSAGE.CONNECT_STATUS_CHANGE,
253
+ global.base.messageManager.connectStatus
254
+ );
255
+
256
+ // 触发重连
257
+ setTimeout(() => {
258
+ isReConnecting = false;
259
+ reConnect(connectSuccessCallback);
260
+ }, 1000);
130
261
  };
131
262
  };
132
263
 
@@ -135,22 +266,38 @@ const UseMessageManager = () => {
135
266
 
136
267
  // 正常主动关闭连接,重置重连次数
137
268
  tryConnectTimes = 0;
269
+ isReConnecting = false;
138
270
 
139
- global.base.messageManager.wsRef.current &&
140
- global.base.messageManager.wsRef.current.close();
271
+ if (wsInstance) {
272
+ wsInstance.close();
273
+ }
141
274
  };
142
275
 
143
276
  const reConnect = async (connectSuccessCallback) => {
277
+ if (isReConnecting) {
278
+ return;
279
+ }
280
+ isReConnecting = true;
144
281
  console.log("useMessageManager reConnect", tryConnectTimes);
145
- if (tryConnectTimes < 7) {
282
+ if (tryConnectTimes < MAX_CONNECT_TIMES) {
146
283
  // 重连ws
147
284
  tryConnectTimes++;
148
285
  connectMsgServer(connectSuccessCallback);
149
286
  } else {
150
- // 重连N次ws失败,不再重试。等用户自己进入智能客服页面时触发重连
287
+ // 重连N次ws失败,不再重试。等用户自己进入智能客服页面或者发送消息时触发重连
151
288
  tryConnectTimes = 0;
152
- connectStatus = 1;
153
- eventBus.emit(EVENTS.MESSAGE.CONNECT_STATUS_CHANGE, connectStatus);
289
+ global.base.messageManager.connectStatus = CONNECT_STATUS.DISCONNECTED;
290
+ isReConnecting = false;
291
+ eventBus.emit(
292
+ EVENTS.MESSAGE.CONNECT_STATUS_CHANGE,
293
+ global.base.messageManager.connectStatus
294
+ );
295
+
296
+ // 所有正在发送的消息全部失败,然后清空pendingMsgMap缓存
297
+ Object.keys(pendingMsgMap).forEach((MsgId) => {
298
+ pendingMsgMap[MsgId].Status = "fail";
299
+ delete pendingMsgMap[MsgId];
300
+ });
154
301
  }
155
302
  };
156
303
 
@@ -228,31 +375,62 @@ const UseMessageManager = () => {
228
375
  useEffect(() => {
229
376
  // 新增并显示一条消息
230
377
  const onMessageAdd = (msg) => {
378
+ if (msg.MsgType === MSG_TYPES.EVENT) {
379
+ // 事件消息不持久化保存,也不渲染
380
+ return;
381
+ }
382
+
383
+ if (!global.base.messageManager.addrMsgData[msg.FromAddrId]) {
384
+ global.base.messageManager.addrMsgData[msg.FromAddrId] = {
385
+ messages: [],
386
+ latestReadedMsgId: "",
387
+ };
388
+ }
389
+ const msgData = global.base.messageManager.addrMsgData[msg.FromAddrId];
390
+
391
+ const messages = msgData.messages;
231
392
  // 如果msg.MsgId已存在,则直接更新,而不是push
232
- const oldMsgIndex = global.base.messageManager.messages.findIndex(
233
- (m) => m.MsgId === msg.MsgId
234
- );
393
+ const oldMsgIndex = messages.findIndex((m) => m.MsgId === msg.MsgId);
235
394
  if (oldMsgIndex === -1) {
236
- global.base.messageManager.messages.push(msg);
395
+ messages.push(msg);
396
+ eventBus.emit(EVENTS.MESSAGE.UNREAD_COUNT_UPDATE);
237
397
  } else {
238
- global.base.messageManager.messages.splice(oldMsgIndex, 1, msg);
398
+ messages.splice(oldMsgIndex, 1, msg);
239
399
  }
240
- // save to local
241
- storage.set(
242
- "messageList",
243
- global.base.messageManager.messages.slice(-500)
244
- );
400
+ // 每个家庭只保留近100条消息
401
+ if (messages.length > 100) {
402
+ msgData.messages = messages.slice(-100);
403
+ }
404
+
405
+ // 避免频繁写入本地存储,在ios可能会导致闪退
406
+ clearTimeout(storageTimer);
407
+ storageTimer = setTimeout(() => {
408
+ storage.set("addrMsgData", global.base.messageManager.addrMsgData);
409
+ }, 300);
245
410
 
246
411
  eventBus.emit(EVENTS.MESSAGE.RENDER, msg);
247
412
  };
248
413
 
249
- connectStatus = -1;
250
- eventBus.emit(EVENTS.MESSAGE.CONNECT_STATUS_CHANGE, connectStatus);
414
+ const onFamilyChange = async () => {
415
+ const activeAddr = await storage.get("activeAddr");
416
+
417
+ if (activeAddr && activeAddr.address_id !== connectedAddressId.current) {
418
+ console.log(
419
+ `Family changed from ${connectedAddressId.current} to ${activeAddr.address_id}, reconnecting...`
420
+ );
421
+ closeMsgConnection();
422
+ } else {
423
+ console.log(
424
+ `Family not changed (${connectedAddressId.current}), skipping reconnect.`
425
+ );
426
+ }
427
+ };
428
+
251
429
  // requestCameraPermission();
252
430
  connectMsgServer();
253
431
 
254
432
  // 监听家庭切换事件,切换websocket上下文
255
- eventBus.on(EVENTS.FAMILY.CHANGE, closeMsgConnection);
433
+ eventBus.on(EVENTS.FAMILY.CHANGE, onFamilyChange);
256
434
  eventBus.on(EVENTS.MESSAGE.ADD, onMessageAdd);
257
435
  eventBus.on(EVENTS.MESSAGE.RECONNECT, reConnect);
258
436
 
@@ -287,7 +465,7 @@ const UseMessageManager = () => {
287
465
  eventBus.on(EVENTS.SHOPMESSAGE.ONLINE_LIST, handleGetOnlineUserList);
288
466
 
289
467
  return () => {
290
- eventBus.off(EVENTS.FAMILY.CHANGE, closeMsgConnection);
468
+ eventBus.off(EVENTS.FAMILY.CHANGE, onFamilyChange);
291
469
  eventBus.off(EVENTS.MESSAGE.ADD, onMessageAdd);
292
470
  eventBus.off(EVENTS.MESSAGE.RECONNECT, reConnect);
293
471
 
@@ -375,8 +553,9 @@ const UseMessageManager = () => {
375
553
  aiModel = model;
376
554
 
377
555
  // 触发重新连接
378
- global.base.messageManager.wsRef.current &&
379
- global.base.messageManager.wsRef.current.close();
556
+ if (wsInstance) {
557
+ wsInstance.close();
558
+ }
380
559
  };
381
560
 
382
561
  return {
@@ -1,44 +1,48 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { View } from 'react-native';
3
- import { WebView } from 'react-native-webview';
4
- import { bindBossAccount, getUserInfo, getAssets } from '../../../api';
1
+ import React, { useEffect, useState } from "react";
2
+ import { View } from "react-native";
3
+ import { WebView } from "react-native-webview";
4
+ import { bindBossAccount, getUserInfo, getAssets } from "../../../api";
5
5
  const { ScreenBackHeader } = global.base.commonComponents;
6
6
  const { storage } = global.base;
7
7
 
8
8
  export default ({ navigation, route }) => {
9
9
  const module = route?.params?.module;
10
- const [uri, setUri] = useState('');
11
- const [state, setState] = useState('');
10
+ const [uri, setUri] = useState("");
11
+ const [state, setState] = useState("");
12
12
  useEffect(() => {
13
13
  init();
14
- global.loading.showLoading('加载中...');
14
+ global.loading.showLoading("加载中...");
15
15
  }, []);
16
16
  const init = async () => {
17
- const accountData = await storage.get('accountData');
17
+ const accountData = await storage.get("accountData");
18
+ console.log("init", accountData, "accountData");
18
19
  const binding = 1; // 1:绑定,0:解绑.
19
20
  const redirect_uri =
20
- 'https://ipanelcloud2.ipanel.cn:50443/iapp/ajbstatic/bind_boss_account/'; // 绑定后跳转地址
21
+ "https://ipanelcloud2.ipanel.cn:50443/iapp/ajbstatic/bind_boss_account/"; // 绑定后跳转地址
21
22
  const state2 = generateRandomString(8);
22
23
  setState(state2);
24
+ console.log(
25
+ `https://boss.ipanel.cn/ipanelTask/api/cas_open/isport/Isport_cloud_mall_c/binding?agentid=133&oauth2_id=72d2453f689fc99548187dca4b922a5b&redirect_uri=${redirect_uri}&open_id=${accountData.user?.user_id}&binding=${binding}&scope=snsapi_privateinfo&response_type=code&state=${state2}`
26
+ );
23
27
  setUri(
24
- `https://boss.ipanel.cn/ipanelTask/api/cas_open/isport/Isport_cloud_mall_c/binding?agentid=133&oauth2_id=72d2453f689fc99548187dca4b922a5b&redirect_uri=${redirect_uri}&open_id=${accountData.user?.user_id}&binding=${binding}&scope=snsapi_base&response_type=code&state=${state2}`
28
+ `https://boss.ipanel.cn/ipanelTask/api/cas_open/isport/Isport_cloud_mall_c/binding?agentid=133&oauth2_id=72d2453f689fc99548187dca4b922a5b&redirect_uri=${redirect_uri}&open_id=${accountData.user?.user_id}&binding=${binding}&scope=snsapi_privateinfo&response_type=code&state=${state2}`
25
29
  );
26
30
  };
27
31
 
28
- const onMessage = async (e) => {
32
+ const onMessage = async e => {
29
33
  const data = JSON.parse(e.nativeEvent.data);
30
- console.log('onMessage', data);
34
+ console.log("onMessage", data);
31
35
  if (data.ret === 0 && data.code && data.state === state) {
32
- global.loading.showLoading('正在绑定...');
36
+ global.loading.showLoading("正在绑定...");
33
37
  const res = await bindBossAccount({
34
38
  code: data.code,
35
- oauth2: '72d2453f689fc99548187dca4b922a5b',
39
+ oauth2: "72d2453f689fc99548187dca4b922a5b",
36
40
  });
37
- console.log(res, 'bindBossAccount');
38
- global.loading.hideLoading('正在绑定...');
41
+ console.log(res, "bindBossAccount");
42
+ global.loading.hideLoading("正在绑定...");
39
43
  if (res.code === 0) {
40
- global.toast.showToast('绑定成功');
41
- get_user_info();
44
+ global.toast.showToast("绑定成功");
45
+ await get_user_info();
42
46
  // navigation.goBack();
43
47
  } else {
44
48
  navigation.goBack();
@@ -49,28 +53,33 @@ export default ({ navigation, route }) => {
49
53
  };
50
54
 
51
55
  const get_user_info = async () => {
52
- const res = await getUserInfo();
53
- console.log(res, 'getUserInfo');
54
- if (
55
- res?.code === 0 &&
56
- res.result?.user_list &&
57
- res.result?.user_list.length
58
- ) {
59
- // const accountData = await storage.get('accountData');
60
- // accountData.user.bind_list = res.result?.user_list[0].bind_list;
61
- // await storage.set('accountData', accountData);
62
- // const bossData = accountData.user.bind_list
63
- const bossData = res.result?.user_list[0].bind_list.filter((item) => {
64
- return item.third_type === 'boss';
65
- });
66
- console.log(bossData[0]);
67
- if (bossData[0]) {
68
- await storage.set('bossData', bossData[0]);
56
+ try {
57
+ const res = await getUserInfo();
58
+ console.log(res, "getUserInfo");
59
+ if (
60
+ res?.code === 0 &&
61
+ res.result?.user_list &&
62
+ res.result?.user_list.length
63
+ ) {
64
+ // const accountData = await storage.get('accountData');
65
+ // accountData.user.bind_list = res.result?.user_list[0].bind_list;
66
+ // await storage.set('accountData', accountData);
67
+ // const bossData = accountData.user.bind_list
68
+ const bossData = res.result?.user_list[0].bind_list.filter(item => {
69
+ return item.third_type === "boss";
70
+ });
71
+ console.log(bossData[0]);
72
+ if (bossData[0]) {
73
+ await storage.set("bossData", bossData[0]);
74
+ }
69
75
  }
70
- }
71
- if (module === 'shop') {
72
- get_assets();
73
- } else {
76
+ if (module === "shop") {
77
+ await get_assets();
78
+ } else {
79
+ navigation.goBack();
80
+ }
81
+ } catch (error) {
82
+ console.error("get_user_info error:", error);
74
83
  navigation.goBack();
75
84
  }
76
85
  };
@@ -80,35 +89,39 @@ export default ({ navigation, route }) => {
80
89
  // 获取iSport积分。如果我的模块需要显示积分则应该在登录之后去获取这接口
81
90
  const appID = await global.base.getAppId();
82
91
  const params = {
83
- action: 'get_assets',
92
+ action: "get_assets",
84
93
  currency: 0, // 0是获取全部,20000是获取iSport积分
85
94
  };
86
95
  if (appID) {
87
96
  params.appid = appID;
88
97
  }
89
- getAssets(params).then((res) => {
98
+ try {
99
+ const res = await getAssets(params);
90
100
  if (res?.code === 0) {
91
101
  if (
92
102
  res?.result &&
93
103
  res?.result?.asset_list &&
94
104
  res?.result?.asset_list?.length
95
105
  ) {
96
- const current = res.result?.asset_list.filter((item) => {
106
+ const current = res.result?.asset_list.filter(item => {
97
107
  return item.currency === 20000;
98
108
  });
99
109
  if (current.length) {
100
- storage.set('isportData', current[0]);
110
+ await storage.set("isportData", current[0]);
101
111
  }
102
112
  }
103
113
  }
114
+ } catch (error) {
115
+ console.error("get_assets error:", error);
116
+ } finally {
104
117
  navigation.goBack();
105
- });
118
+ }
106
119
  };
107
120
 
108
- const generateRandomString = (length) => {
121
+ const generateRandomString = length => {
109
122
  const characters =
110
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; // Add any additional special characters you want to include
111
- let result = '';
123
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; // Add any additional special characters you want to include
124
+ let result = "";
112
125
  for (let i = 0; i < length; i++) {
113
126
  const randomIndex = Math.floor(Math.random() * characters.length);
114
127
  result += characters.charAt(randomIndex);
@@ -120,20 +133,32 @@ export default ({ navigation, route }) => {
120
133
  <View style={{ flex: 1 }}>
121
134
  <ScreenBackHeader title="BOSS登录" />
122
135
  <View style={{ flex: 1 }}>
123
- <WebView
124
- bounces={false}
125
- // domStorageEnabled={false}
126
- javaScriptEnabled
127
- useWebKit
128
- source={{
129
- uri: uri,
130
- headers: { 'Cache-Control': 'no-store' },
131
- }}
132
- incognito={true}
133
- onMessage={onMessage}
134
- onLoadEnd={() => global.loading.hideLoading()}
135
- onError={() => global.loading.hideLoading()}
136
- />
136
+ {!!uri && (
137
+ <WebView
138
+ bounces={false}
139
+ javaScriptEnabled={true}
140
+ useWebKit={true}
141
+ source={{
142
+ uri: uri,
143
+ headers: { "Cache-Control": "no-store" },
144
+ }}
145
+ incognito={true}
146
+ onMessage={onMessage}
147
+ onLoadStart={() => global.loading.showLoading()}
148
+ onLoadEnd={() => global.loading.hideLoading()}
149
+ onError={e => {
150
+ console.log("加载错误:", e.nativeEvent.description);
151
+ global.loading.hideLoading();
152
+ }}
153
+ // iOS 特定修复
154
+ // onShouldStartLoadWithRequest={request => {
155
+ // if (request.url.startsWith('file://')) {
156
+ // return true; // 允许加载本地文件
157
+ // }
158
+ // return true;
159
+ // }}
160
+ />
161
+ )}
137
162
  </View>
138
163
  </View>
139
164
  );
@@ -29,11 +29,11 @@ async function updateHomedToken() {
29
29
  console.log(accountData?.user, "accountData?.user");
30
30
  const userList = res.result?.user_list || [];
31
31
  const currentUser = userList.find(
32
- item => item.user_id === accountData?.user.user_id
32
+ (item) => item.user_id === accountData?.user.user_id
33
33
  );
34
34
  const bindList = currentUser?.bind_list || [];
35
35
  console.log(bindList, "bindList");
36
- const homedConfig = bindList.find(item => item.third_type === "homed");
36
+ const homedConfig = bindList.find((item) => item.third_type === "homed");
37
37
  let domain = activeAddr?.domain || (await global.base.getHomedDomain());
38
38
  // if (!["禺乐慧家", "悦慧家"].includes(appName)) {
39
39
  // domain = 'homed.tv'
@@ -44,9 +44,10 @@ async function updateHomedToken() {
44
44
  let homedToken = "";
45
45
 
46
46
  if (
47
- homedConfig.extend.deviceno &&
48
- homedConfig.extend.deviceid &&
49
- homedConfig.extend.userid
47
+ homedConfig?.extend &&
48
+ homedConfig?.extend?.deviceno &&
49
+ homedConfig?.extend?.deviceid &&
50
+ homedConfig?.extend?.userid
50
51
  ) {
51
52
  const mobileRelationLoginParams = {
52
53
  deviceno: homedConfig.extend.deviceno,
@@ -63,7 +64,7 @@ async function updateHomedToken() {
63
64
  const homedLoginResult = await mobileRelationLogin(
64
65
  mobileRelationLoginParams,
65
66
  domain
66
- ).catch(err => {});
67
+ ).catch((err) => {});
67
68
  if (homedLoginResult && homedLoginResult.ret === 0) {
68
69
  homedToken = homedLoginResult.access_token;
69
70
  await storage.set("homedLoginResult", homedLoginResult);
@@ -79,7 +80,7 @@ async function updateHomedToken() {
79
80
  deviceType: 1,
80
81
  },
81
82
  domain
82
- ).catch(err => {});
83
+ ).catch((err) => {});
83
84
  console.log(tokenRes, "tokenRes");
84
85
  if (tokenRes && tokenRes.ret === 0) {
85
86
  homedToken = tokenRes.access_token;
@@ -94,7 +95,7 @@ async function updateHomedToken() {
94
95
  const list = userListRes.user_list || [];
95
96
  await storage.set(
96
97
  "homedHomeMembers",
97
- list.filter(item => item.user_id !== userListRes.bind_user_id)
98
+ list.filter((item) => item.user_id !== userListRes.bind_user_id)
98
99
  );
99
100
  }
100
101
  } else {
@@ -153,7 +154,7 @@ async function initOperatorHomedTouristAccount(opHomedConfig) {
153
154
  accounttype: 1,
154
155
  iconid: 1,
155
156
  pwd: "1bbd886460827015e5d605ed44252251", // md5("11111111")
156
- }).catch(e => {
157
+ }).catch((e) => {
157
158
  console.log("Error: homed游客注册请求失败", e);
158
159
  });
159
160
 
@@ -177,7 +178,7 @@ async function updateHomedTouristToken() {
177
178
  devicetype: 3,
178
179
  accounttype: 2,
179
180
  pwd: "1bbd886460827015e5d605ed44252251", // md5("11111111")
180
- }).catch(e => {
181
+ }).catch((e) => {
181
182
  console.log("Error: homed游客登录请求失败", e);
182
183
  });
183
184
 
@@ -262,6 +263,7 @@ const MSG_TYPES = {
262
263
  VIDEO: "video",
263
264
  EVENT: "event",
264
265
  TEMPLATE: "template",
266
+ CHAT: "chat",
265
267
  };
266
268
 
267
269
  const MSG_EVENT_TYPES = {
@@ -284,6 +286,7 @@ async function genMsg(type, msgType, payload) {
284
286
  [payloadKey]: payload,
285
287
  MsgId: id.replace(/-/g, ""),
286
288
  SessionId: global.base.messageManager.sessionId,
289
+ CreateTime: new Date().getTime(),
287
290
  };
288
291
  const activeAddr = await storage.get("activeAddr");
289
292
  const accountData = await storage.get("accountData");
@@ -292,11 +295,11 @@ async function genMsg(type, msgType, payload) {
292
295
  // 当前用户发出
293
296
  baseMsg.FromUserId = accountData.user.user_id;
294
297
  baseMsg.FromAddrId = activeAddr.address_id;
295
- baseMsg.ToUserId = "client";
298
+ baseMsg.ToUserId = "cloud";
296
299
  baseMsg.ToAddrId = activeAddr.address_id;
297
300
  } else if (type === "get") {
298
301
  // 当前用户收到
299
- baseMsg.FromUserId = "client";
302
+ baseMsg.FromUserId = "cloud";
300
303
  baseMsg.FromAddrId = activeAddr.address_id;
301
304
  baseMsg.ToUserId = accountData.user.user_id;
302
305
  baseMsg.ToAddrId = activeAddr.address_id;
@@ -393,7 +396,7 @@ async function appReport(tag, extraData = {}) {
393
396
  ...extraData,
394
397
  };
395
398
 
396
- const res = await reportBigData(tag, appID, data).catch(err => {
399
+ const res = await reportBigData(tag, appID, data).catch((err) => {
397
400
  console.log("reportBigData", err);
398
401
  });
399
402
  console.log("reportBigData", tag, res);
@@ -424,7 +427,7 @@ const checkToken = async () => {
424
427
  ]);
425
428
  };
426
429
 
427
- return new Promise(async resolve => {
430
+ return new Promise(async (resolve) => {
428
431
  try {
429
432
  const accountType = await storage.get("accountType");
430
433
  const res = await refreshToken(
@@ -450,6 +453,12 @@ const checkToken = async () => {
450
453
  });
451
454
  };
452
455
 
456
+ const CONNECT_STATUS = {
457
+ CONNECTING: -1,
458
+ CONNECTED: 0,
459
+ DISCONNECTED: 1,
460
+ };
461
+
453
462
  export {
454
463
  updateHomedToken,
455
464
  initOperatorHomedTouristAccount,
@@ -463,4 +472,5 @@ export {
463
472
  appReport,
464
473
  checkToken,
465
474
  updateApartmentToken,
475
+ CONNECT_STATUS,
466
476
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ipanel-rnapp-ajb-base",
3
- "version": "1.0.38",
4
- "ipanelVersion": "1.0.515",
3
+ "version": "1.0.40",
4
+ "ipanelVersion": "1.0.529",
5
5
  "description": "",
6
6
  "main": "index.js",
7
7
  "keywords": [
@@ -13,4 +13,4 @@
13
13
  "scripts": {
14
14
  "postinstall": "node bin/local-dev.js"
15
15
  }
16
- }
16
+ }