1- import 'package:stream_core/src/user/user.dart' ;
1+ import 'dart:async' ;
2+
3+ import 'package:stream_core/stream_core.dart' ;
24import 'package:uuid/uuid.dart' ;
35
46import '../stream_feeds.dart' ;
57import 'generated/api/api.g.dart' as api;
68import 'repositories.dart' ;
9+ import 'utils/endpoint_config.dart' ;
10+ import 'ws/feeds_ws_event.dart' ;
711
812class FeedsClient {
913 FeedsClient ({
@@ -12,17 +16,33 @@ class FeedsClient {
1216 required this .userToken,
1317 this .config = const FeedsConfig (),
1418 this .userTokenProvider,
19+ this .networkMonitor,
1520 }) {
1621 apiClient = api.DefaultApi (
1722 api.ApiClient (
23+ basePath: endpointConfig.baseFeedsUrl,
1824 authentication: _Authentication (
1925 apiKey: apiKey,
2026 user: user,
2127 getToken: () async => userToken,
22- getConnectionId: () => null ,
28+ getConnectionId: () => webSocketClient ? .connectionId ,
2329 ),
2430 ),
2531 );
32+ final websocketUri = Uri .parse (endpointConfig.wsEndpoint).replace (
33+ queryParameters: < String , String > {
34+ 'api_key' : apiKey,
35+ 'stream-auth-type' : 'jwt' ,
36+ 'X-Stream-Client' : 'stream-feeds-dart' ,
37+ },
38+ );
39+
40+ webSocketClient = WebSocketClient (
41+ url: websocketUri.toString (),
42+ eventDecoder: FeedsWsEvent .fromEventObject,
43+ onConnectionEstablished: _authenticate,
44+ );
45+
2646 feedsRepository = FeedsRepository (apiClient: apiClient);
2747 }
2848
@@ -31,10 +51,74 @@ class FeedsClient {
3151 final String userToken;
3252 final FeedsConfig config;
3353 final UserTokenProvider ? userTokenProvider;
54+ final NetworkMonitor ? networkMonitor;
3455
3556 late final api.DefaultApi apiClient;
3657 late final FeedsRepository feedsRepository;
3758
59+ static final endpointConfig = EndpointConfig .production;
60+ late final WebSocketClient webSocketClient;
61+ ConnectionRecoveryHandler ? connectionRecoveryHandler;
62+
63+ Completer <void >? _connectionCompleter;
64+ StreamSubscription <WebSocketConnectionState >? _connectionSubscription;
65+
66+ /// Connects to the feeds websocket.
67+ /// Future will complete when the connection is established and the user is authenticated.
68+ /// If the authentication fails, the future will complete with an error.
69+ Future <void > connect () async {
70+ webSocketClient.connect ();
71+
72+ _connectionSubscription = webSocketClient! .connectionStateStream
73+ .listen (_onConnectionStateChanged);
74+
75+ connectionRecoveryHandler = DefaultConnectionRecoveryHandler (
76+ client: webSocketClient,
77+ networkMonitor: networkMonitor,
78+ );
79+
80+ _connectionCompleter = Completer <void >();
81+ return _connectionCompleter! .future;
82+ }
83+
84+ /// Disconnects from the feeds websocket.
85+ /// The FeedsClient should no longer be used after calling this method.
86+ void disconnect () {
87+ connectionRecoveryHandler? .dispose ();
88+ webSocketClient.disconnect ();
89+ _connectionSubscription? .cancel ();
90+ _connectionCompleter? .complete ();
91+ _connectionCompleter = null ;
92+ }
93+
94+ void _onConnectionStateChanged (WebSocketConnectionState state) {
95+ if (_connectionCompleter != null ) {
96+ if (state is Connected ) {
97+ _connectionCompleter! .complete ();
98+ _connectionCompleter = null ;
99+ }
100+ if (state is Disconnected ) {
101+ _connectionCompleter! .completeError (Exception ('Connection failed' ));
102+ _connectionCompleter = null ;
103+ }
104+ }
105+ }
106+
107+ void _authenticate () {
108+ final connectUserRequest = WsAuthMessageRequest (
109+ products: ['feeds' ],
110+ token: userToken,
111+ userDetails: ConnectUserDetailsRequest (
112+ id: user.id,
113+ name: user.originalName,
114+ image: user.imageUrl,
115+ customData: user.customData,
116+ ),
117+ );
118+
119+ webSocketClient.send (connectUserRequest);
120+ }
121+
38122 /// Creates a feed instance based on the provided query.
39123 ///
40124 /// This method creates a [Feed] object using a [FeedQuery] that can include additional
0 commit comments