From 7ed6391b818040e005ce92805b2089265aa3d4f0 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 2 Feb 2024 12:36:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=8F=98=E9=87=8F=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E5=92=8C=E6=9B=BF=E6=8D=A2;=20=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E5=92=8C=E5=88=86=E9=A1=B5;=20=E5=A4=9A?= =?UTF-8?q?=E8=A1=8C=E6=96=87=E6=9C=AC=E8=BE=93=E5=85=A5,=20=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E5=8C=BA=E5=9F=9F=E6=A0=B7=E5=BC=8F,=20=E5=9B=9E?= =?UTF-8?q?=E8=BD=A6=E5=8F=91=E9=80=81=E6=B6=88=E6=81=AF;=20=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=98=BE=E7=A4=BA=E6=94=AF=E6=8C=81=E6=8D=A2=E8=A1=8C?= =?UTF-8?q?;=20websocket=20=E8=BF=9E=E6=8E=A5=E7=8A=B6=E6=80=81;=20?= =?UTF-8?q?=E9=87=8D=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actions/ConversationActions.js | 9 +- src/lib/realTimeAPI.js | 77 ++++++++++-- src/records/ConversationState.js | 4 + src/reducers/ConversationReducer.js | 4 + src/views/Conversations/ChatWindow.jsx | 8 +- .../Components/InputComposer.jsx | 44 ++++--- .../Components/InputTemplate.jsx | 114 ++++++++++++------ .../Conversations/Components/Messages.jsx | 21 ++-- .../Components/MessagesHeader.jsx | 9 +- .../Conversations/ConversationProvider.jsx | 13 +- tailwind.config.js | 1 + 11 files changed, 219 insertions(+), 85 deletions(-) diff --git a/src/actions/ConversationActions.js b/src/actions/ConversationActions.js index b44fd84..ec0359c 100644 --- a/src/actions/ConversationActions.js +++ b/src/actions/ConversationActions.js @@ -14,7 +14,14 @@ export const closeWebsocket0 = () => ({ type: NAME_SPACE + 'CLOSE_WEBSOCKET', payload: null, }); -export const closeWebsocket = () => {}; +export const updateWebsocketState = (v) => ({ + type: NAME_SPACE + 'MODIFY_WEBSOCKET_STATE', + payload: v, +}); +export const updateWebsocketRetrytimes = (v) => ({ + type: NAME_SPACE + 'MODIFY_WEBSOCKET_RETRYTIMES', + payload: v, +}); export const receivedNewMessage = (targetId, message) => ({ type: NAME_SPACE + 'RECEIVED_NEW_MESSAGE', diff --git a/src/lib/realTimeAPI.js b/src/lib/realTimeAPI.js index 72a78fe..348ed21 100644 --- a/src/lib/realTimeAPI.js +++ b/src/lib/realTimeAPI.js @@ -1,20 +1,76 @@ import { webSocket } from 'rxjs/webSocket'; -import { of, timer, concatMap } from 'rxjs'; -import { filter, buffer, map, tap, retryWhen, retry, delay, take, } from 'rxjs/operators'; -import { v4 as uuid } from "uuid"; +import { of, timer, concatMap, EMPTY, takeWhile, concat } from 'rxjs'; +import { filter, buffer, map, tap, retryWhen, retry, delay, take, catchError } from 'rxjs/operators'; +import { v4 as uuid } from 'uuid'; export class RealTimeAPI { - constructor(param) { - this.webSocket = webSocket(param); + constructor(param, onOpenCallback, onCloseCallback, onRetryCallback) { + this.onOpenCallback = onOpenCallback; + this.onCloseCallback = onCloseCallback; + this.onErrorCallback = onRetryCallback; + this.webSocket = webSocket({ + ...param, + openObserver: { + next: () => { + this.onOpen(); + }, + }, + closeObserver: { + next: () => { + this.onClose(); + }, + }, + }); + this.retryCount = 0; + } + onOpen() { + console.log( + `%c WebSocket connection opened `, + 'background:#41b883 ; padding: 1px; border-radius: 3px; color: #fff', + ); + if (this.onOpenCallback) { + this.onOpenCallback(); + } + } + + onClose() { + console.log( + `%c WebSocket connection closed `, + 'background:#35495e ; padding: 1px; border-radius: 3px; color: #fff' + ); + if (this.onCloseCallback) { + this.onCloseCallback(this.retryCount); + } + } + + onRetry(i) { + this.retryCount = i; + if (this.onErrorCallback) { + this.onErrorCallback(i); + } } getObservable() { return this.webSocket.pipe( - retry(10) - // retry({ - // count: 10, - // delay: () => timer(3000) - // }) + // retry(10) + retry({ + count: 10, + // delay: 3000, + delay: (errors, index) => { + this.onRetry(index); + return timer(3000); + }, + resetOnSuccess: true, + }), + catchError((error) => { + this.retryCount = 0; + this.onRetry(-1); + console.log( + `%c All retries exhausted `, + 'background:#fb923c ; padding: 1px; border-radius: 3px; color: #fff' + ); + return EMPTY; + }) ); } @@ -72,7 +128,6 @@ export class RealTimeAPI { return this.getObservableFilteredByMessageType('ping').pipe(tap(() => this.sendMessage({ msg: 'pong' }))); } - callMethod(method, ...params) { let id = 'uuid()'; this.sendMessage({ diff --git a/src/records/ConversationState.js b/src/records/ConversationState.js index fc3e06a..f16ef30 100644 --- a/src/records/ConversationState.js +++ b/src/records/ConversationState.js @@ -1,5 +1,9 @@ const initialState = { websocket: null, + websocketOpened: null, + websocketRetrying: null, + websocketRetrytimes: null, + errors: [], // 错误信息 conversationsList: [], // 对话列表 diff --git a/src/reducers/ConversationReducer.js b/src/reducers/ConversationReducer.js index a5c1595..5e12877 100644 --- a/src/reducers/ConversationReducer.js +++ b/src/reducers/ConversationReducer.js @@ -6,6 +6,10 @@ const ConversationReducer = (state = initialState, action) => { switch (action.type) { case NAME_SPACE + 'INIT_WEBSOCKET': return { ...state, websocket: action.payload }; + case NAME_SPACE + 'MODIFY_WEBSOCKET_STATE': + return { ...state, websocketOpened: action.payload, }; + case NAME_SPACE + 'MODIFY_WEBSOCKET_RETRYTIMES': + return { ...state, websocketRetrytimes: action.payload, websocketRetrying: action.payload > 0 }; case NAME_SPACE + 'SET_CONVERSATION_LIST':{ const conversationsMapped = action.payload.reduce((r, v) => ({ ...r, [`${v.sn}`]: [] }), {}); diff --git a/src/views/Conversations/ChatWindow.jsx b/src/views/Conversations/ChatWindow.jsx index fd7605d..8882ba2 100644 --- a/src/views/Conversations/ChatWindow.jsx +++ b/src/views/Conversations/ChatWindow.jsx @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { Layout, Spin } from 'antd'; import MessagesHeader from './Components/MessagesHeader'; import Messages from './Components/Messages'; -import InputBox from './Components/InputComposer'; +import InputComposer from './Components/InputComposer'; import ConversationsList from './Components/ConversationsList'; import CustomerProfile from './Components/CustomerProfile'; @@ -31,7 +31,7 @@ const ChatWindow = () => { -
+
@@ -39,8 +39,8 @@ const ChatWindow = () => { -