Skip to content

Commit c85ff3b

Browse files
Merge pull request #138 from sendbird/v4.3.1
Add 4.3.1.
2 parents e4e762b + 1b186a6 commit c85ff3b

18 files changed

+235
-11
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## v4.3.1 (Apr 4, 2025)
2+
3+
### Improvements
4+
- Fixed duplicate userId bug related to reaction
5+
- Improved websocket statistics
6+
17
## v4.3.0 (Mar 11, 2025)
28

39
### Features

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Before installing Sendbird Chat SDK, you need to create a Sendbird application o
5050

5151
```yaml
5252
dependencies:
53-
sendbird_chat_sdk: ^4.3.0
53+
sendbird_chat_sdk: ^4.3.1
5454
```
5555
5656
- Run `flutter pub get` command in your project directory.

lib/src/internal/main/chat/chat.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ part 'chat_notifications.dart';
6464
part 'chat_push.dart';
6565
part 'chat_user.dart';
6666

67-
const sdkVersion = '4.3.0';
67+
const sdkVersion = '4.3.1';
6868

6969
// Internal implementation for main class. Do not directly access this class.
7070
class Chat with WidgetsBindingObserver {
@@ -257,6 +257,12 @@ class Chat with WidgetsBindingObserver {
257257
} else if (results.contains(ConnectivityResult.none)) {
258258
channelCache.markAsDirtyAll(); // Check
259259

260+
statManager.appendWsDisconnectStat(
261+
success: true,
262+
errorCode: SendbirdError.networkError,
263+
errorDescription: "cause=network_closed",
264+
);
265+
260266
sbLog.d(StackTrace.current, 'disconnect()');
261267
await connectionManager.disconnect(logout: false);
262268
}

lib/src/internal/main/chat/chat_connection.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ extension ChatConnection on Chat {
3535

3636
Future<bool> reconnect({bool reset = false}) async {
3737
sbLog.i(StackTrace.current, 'userId: ${chatContext.currentUserId}');
38+
39+
if (reset) {
40+
statManager.appendWsDisconnectStat(
41+
success: true,
42+
errorCode: 0,
43+
errorDescription: "cause=explicit_reconnect",
44+
);
45+
}
46+
3847
return await connectionManager.reconnect(reset: reset);
3948
}
4049

lib/src/internal/main/chat_manager/command_manager.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class CommandManager {
4949
final Map<String, int> _readMap = {};
5050
final Map<String, Completer<int?>> messageOffsetTsCompleterMap = {};
5151

52+
int? logiTs;
53+
5254
final Chat _chat;
5355

5456
CommandManager({required Chat chat}) : _chat = chat;
@@ -275,6 +277,8 @@ class CommandManager {
275277
return;
276278
}
277279

280+
logiTs = DateTime.now().millisecondsSinceEpoch;
281+
278282
await processLoginPayload(fromWebSocket: true, loginPayload: cmd.payload);
279283
}
280284

@@ -390,6 +394,12 @@ class CommandManager {
390394
await _chat.connectionManager.disconnect(logout: true);
391395
_chat.eventManager.notifySessionClosed();
392396
} else {
397+
_chat.statManager.appendWsDisconnectStat(
398+
success: true,
399+
errorCode: SendbirdError.sessionKeyExpired,
400+
errorDescription: "cause=session_expired",
401+
);
402+
393403
await _chat.sessionManager.updateSessionKey();
394404
}
395405
}

lib/src/internal/main/chat_manager/connection_manager.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'package:sendbird_chat_sdk/src/internal/network/websocket/command/command
1818
import 'package:sendbird_chat_sdk/src/internal/network/websocket/websocket_client.dart';
1919
import 'package:sendbird_chat_sdk/src/public/core/user/user.dart';
2020
import 'package:sendbird_chat_sdk/src/public/main/define/exceptions.dart';
21+
import 'package:sendbird_chat_sdk/src/public/main/define/sendbird_error.dart';
2122
import 'package:universal_io/io.dart';
2223

2324
class ConnectionManager {
@@ -29,6 +30,7 @@ class ConnectionManager {
2930

3031
ConnectionManager({required this.chat}) {
3132
webSocketClient = WebSocketClient(
33+
chat: chat,
3234
chatContext: chat.chatContext,
3335
onWebSocketConnected: _onWebSocketConnected,
3436
onWebSocketClosed: _onWebSocketClosed,
@@ -175,6 +177,7 @@ class ConnectionManager {
175177
success: false,
176178
errorCode: e.code,
177179
errorDescription: e.message,
180+
accumTrial: 1,
178181
);
179182

180183
//+ [DBManager]
@@ -205,6 +208,7 @@ class ConnectionManager {
205208
success: false,
206209
errorCode: exception.code,
207210
errorDescription: exception.message,
211+
accumTrial: 1,
208212
);
209213

210214
if (chat.chatContext.loginCompleter != null &&
@@ -226,6 +230,7 @@ class ConnectionManager {
226230
success: false,
227231
errorCode: e.code,
228232
errorDescription: e.name,
233+
accumTrial: 1,
229234
);
230235

231236
await doDisconnect(clear: true);
@@ -236,6 +241,9 @@ class ConnectionManager {
236241
chat.statManager.endWsConnectStat(
237242
hostUrl: url,
238243
success: true,
244+
connectedTs: webSocketClient.connectedTs,
245+
logiTs: chat.commandManager.logiTs,
246+
accumTrial: 1,
239247
);
240248
return user;
241249
}
@@ -405,6 +413,15 @@ class ConnectionManager {
405413

406414
void _onWebSocketClosed() {
407415
chat.commandManager.clearCompleterMap();
416+
417+
final closeCode = webSocketClient.getCloseCode();
418+
if (closeCode != null) {
419+
chat.statManager.appendWsDisconnectStat(
420+
success: true,
421+
errorCode: SendbirdError.webSocketConnectionClosed,
422+
errorDescription: "cause=$closeCode",
423+
);
424+
}
408425
}
409426

410427
Future<void> _onWebSocketData(dynamic data) async {

lib/src/internal/main/connection_state/connected_state.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:sendbird_chat_sdk/src/internal/main/chat_context/chat_context.da
55
import 'package:sendbird_chat_sdk/src/internal/main/connection_state/base_connection_state.dart';
66
import 'package:sendbird_chat_sdk/src/internal/main/logger/sendbird_logger.dart';
77
import 'package:sendbird_chat_sdk/src/public/core/user/user.dart';
8+
import 'package:sendbird_chat_sdk/src/public/main/define/sendbird_error.dart';
89

910
class ConnectedState extends BaseConnectionState {
1011
ConnectedState({
@@ -51,6 +52,13 @@ class ConnectedState extends BaseConnectionState {
5152
@override
5253
Future<void> enterBackground() async {
5354
sbLog.i(StackTrace.current);
55+
56+
chat.statManager.appendWsDisconnectStat(
57+
success: true,
58+
errorCode: SendbirdError.webSocketConnectionClosed,
59+
errorDescription: "cause=background",
60+
);
61+
5462
await chat.connectionManager
5563
.doDisconnect(clear: false, fromEnterBackground: true);
5664
}

lib/src/internal/main/stats/daily_record_stat_prefs.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class DailyRecordStatPrefs {
9595
return null;
9696
case StatType.wsConnect:
9797
return null;
98+
case StatType.wsDisconnect:
99+
return null;
98100
case StatType.featureLocalCache:
99101
return LocalCacheStat.fromJson(ts: ts, data: data);
100102
case StatType.featureLocalCacheEvent:

lib/src/internal/main/stats/default_stat_prefs.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:sendbird_chat_sdk/src/internal/main/stats/model/default/api_resu
88
import 'package:sendbird_chat_sdk/src/internal/main/stats/model/default/default_stat.dart';
99
import 'package:sendbird_chat_sdk/src/internal/main/stats/model/default/local_cache_event_stat.dart';
1010
import 'package:sendbird_chat_sdk/src/internal/main/stats/model/default/notification_stat.dart';
11+
import 'package:sendbird_chat_sdk/src/internal/main/stats/model/default/ws_disconnect_stat.dart';
1112
import 'package:sendbird_chat_sdk/src/internal/main/stats/stat_type.dart';
1213
import 'package:sendbird_chat_sdk/src/internal/main/stats/stat_utils.dart';
1314
import 'package:sendbird_chat_sdk/src/internal/main/stats/model/default/ws_connect_stat.dart';
@@ -85,6 +86,8 @@ class DefaultStatPrefs {
8586
return ApiResultStat.fromJson(ts: ts, data: data);
8687
case StatType.wsConnect:
8788
return WsConnectStat.fromJson(ts: ts, data: data);
89+
case StatType.wsDisconnect:
90+
return WsDisconnectStat.fromJson(ts: ts, data: data);
8891
case StatType.featureLocalCache:
8992
return null;
9093
case StatType.featureLocalCacheEvent:

lib/src/internal/main/stats/model/default/ws_connect_stat.dart

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import 'package:sendbird_chat_sdk/src/internal/main/stats/stat_type.dart';
77
class WsConnectStat extends DefaultStat {
88
final String hostUrl; // ws host url
99
final bool success; // success or not
10-
final int latency; // roundtrip latency
10+
final int latency; // until connected or failed
11+
final int accumTrial;
12+
final String connectionId;
13+
final int? logiLatency; // until received LOGI
1114
final int? errorCode; // error code if exist
1215
final String? errorDescription; // detailed error message
1316

@@ -16,6 +19,9 @@ class WsConnectStat extends DefaultStat {
1619
required this.hostUrl,
1720
required this.success,
1821
required this.latency,
22+
required this.accumTrial,
23+
required this.connectionId,
24+
this.logiLatency,
1925
this.errorCode,
2026
this.errorDescription,
2127
}) : super(StatType.wsConnect, ts);
@@ -26,6 +32,9 @@ class WsConnectStat extends DefaultStat {
2632
'host_url': hostUrl,
2733
'success': success,
2834
'latency': latency,
35+
'accum_trial': accumTrial,
36+
'connection_id': connectionId,
37+
'logi_latency': logiLatency,
2938
'error_code': errorCode,
3039
'error_description': errorDescription,
3140
};
@@ -41,6 +50,9 @@ class WsConnectStat extends DefaultStat {
4150
// 'host_url': String,
4251
// 'success': bool,
4352
// 'latency': int,
53+
// 'accum_trial': int,
54+
// 'connection_id': String,
55+
// 'logi_latency': int?,
4456
// 'error_code': int?,
4557
// 'error_description': String?,
4658
// },
@@ -53,10 +65,17 @@ class WsConnectStat extends DefaultStat {
5365
final String? hostUrl = data['host_url'] as String?;
5466
final bool? success = data['success'] as bool?;
5567
final int? latency = data['latency'] as int?;
68+
final int? accumTrial = data['accum_trial'] as int?;
69+
final String? connectionId = data['connection_id'] as String?;
70+
final int? logiLatency = data['logi_latency'] as int?;
5671
final int? errorCode = data['error_code'] as int?;
5772
final String? errorDescription = data['error_description'] as String?;
5873

59-
if (hostUrl == null || success == null || latency == null) {
74+
if (hostUrl == null ||
75+
success == null ||
76+
latency == null ||
77+
accumTrial == null ||
78+
connectionId == null) {
6079
return null;
6180
}
6281

@@ -65,6 +84,9 @@ class WsConnectStat extends DefaultStat {
6584
hostUrl: hostUrl,
6685
success: success,
6786
latency: latency,
87+
accumTrial: accumTrial,
88+
connectionId: connectionId,
89+
logiLatency: logiLatency,
6890
errorCode: errorCode,
6991
errorDescription: errorDescription,
7092
);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) 2025 Sendbird, Inc. All rights reserved.
2+
3+
import 'package:sendbird_chat_sdk/src/internal/main/logger/sendbird_logger.dart';
4+
import 'package:sendbird_chat_sdk/src/internal/main/stats/model/default/default_stat.dart';
5+
import 'package:sendbird_chat_sdk/src/internal/main/stats/stat_type.dart';
6+
7+
class WsDisconnectStat extends DefaultStat {
8+
final bool success; // success or not
9+
final int errorCode; // error code
10+
final String errorDescription; // detailed error message
11+
12+
WsDisconnectStat({
13+
required int ts,
14+
required this.success,
15+
required this.errorCode,
16+
required this.errorDescription,
17+
}) : super(StatType.wsDisconnect, ts);
18+
19+
@override
20+
Map<String, dynamic> toJson() {
21+
final json = <String, dynamic>{
22+
'success': success,
23+
'error_code': errorCode,
24+
'error_description': errorDescription,
25+
};
26+
final result = super.toJson();
27+
result['data'] = json;
28+
return result;
29+
}
30+
31+
// {
32+
// 'stat_type' : 'ws:disconnect',
33+
// 'ts': int, // timestamp for log creation,
34+
// 'data' : {
35+
// 'success': bool,
36+
// 'error_code': int,
37+
// 'error_description': String,
38+
// },
39+
// }
40+
static WsDisconnectStat? fromJson({
41+
required int ts,
42+
required Map<String, dynamic> data,
43+
}) {
44+
try {
45+
final bool? success = data['success'] as bool?;
46+
final int? errorCode = data['error_code'] as int?;
47+
final String? errorDescription = data['error_description'] as String?;
48+
49+
if (success == null || errorCode == null || errorDescription == null) {
50+
return null;
51+
}
52+
53+
return WsDisconnectStat(
54+
ts: ts,
55+
success: success,
56+
errorCode: errorCode,
57+
errorDescription: errorDescription,
58+
);
59+
} catch (e) {
60+
sbLog.d(StackTrace.current, 'e: ${e.toString()}');
61+
}
62+
return null;
63+
}
64+
}

lib/src/internal/main/stats/sendbird_statistics.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:sendbird_chat_sdk/src/public/main/chat/sendbird_chat.dart';
88
class SendbirdStatistics {
99
static const String apiResultType = 'api:result';
1010
static const String wsConnectType = 'ws:connect';
11+
static const String wsDisconnectType = 'ws:disconnect';
1112
static const String featureLocalCacheType = 'feature:local_cache';
1213
static const String featureLocalCacheEventType = 'feature:local_cache_event';
1314
static const String notiStatsType = 'noti:stats';

0 commit comments

Comments
 (0)