Skip to content

Commit 8afd1f9

Browse files
committed
Split public api from feeds client
1 parent 96fcae0 commit 8afd1f9

File tree

7 files changed

+221
-161
lines changed

7 files changed

+221
-161
lines changed
Lines changed: 21 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,36 @@
1-
import 'dart:async';
2-
3-
import 'package:meta/meta.dart';
4-
import 'package:rxdart/rxdart.dart';
51
import 'package:stream_core/stream_core.dart';
62

73
import '../stream_feeds.dart';
8-
import 'generated/api/api.dart' as api;
9-
import 'repositories.dart';
10-
import 'utils/endpoint_config.dart';
11-
import 'ws/feeds_ws_event.dart';
4+
import 'internal_feeds_client.dart';
125

13-
class FeedsClient {
14-
FeedsClient({
15-
required this.apiKey,
16-
required this.user,
6+
abstract interface class FeedsClient {
7+
factory FeedsClient({
8+
required String apiKey,
9+
required User user,
1710
String? userToken,
1811
TokenProvider? userTokenProvider,
19-
this.config = const FeedsConfig(),
20-
FeedsClientEnvironment environment = const FeedsClientEnvironment(),
21-
}) : assert(
22-
userToken != null || userTokenProvider != null,
23-
'Provide either a user token or a user token provider, or both',
24-
) {
25-
tokenManager = userTokenProvider != null
26-
? TokenManager.provider(
27-
user: user,
28-
provider: userTokenProvider,
29-
token: userToken,
30-
)
31-
: TokenManager.static(user: user, token: userToken ?? '');
32-
33-
// TODO: fill with correct values
34-
final systemEnvironmentManager = SystemEnvironmentManager(
35-
environment: const SystemEnvironment(
36-
sdkName: 'stream-feeds-dart',
37-
sdkIdentifier: 'dart',
38-
sdkVersion: '0.1.0',
39-
),
40-
);
41-
42-
apiClient = api.DefaultApi(
43-
CoreHttpClient(
44-
apiKey,
45-
systemEnvironmentManager: systemEnvironmentManager,
46-
options: HttpClientOptions(baseUrl: endpointConfig.baseFeedsUrl),
47-
connectionIdProvider: () => webSocketClient.connectionId,
48-
tokenManager: tokenManager,
49-
),
50-
);
51-
final websocketUri = Uri.parse(endpointConfig.wsEndpoint).replace(
52-
queryParameters: <String, String>{
53-
'api_key': apiKey,
54-
'stream-auth-type': 'jwt',
55-
'X-Stream-Client': 'stream-feeds-dart',
56-
},
57-
);
58-
59-
webSocketClient = environment.createWebSocketClient(
60-
url: websocketUri.toString(),
61-
eventDecoder: FeedsWsEvent.fromEventObject,
62-
onConnectionEstablished: _authenticate,
63-
);
64-
65-
feedsRepository = FeedsRepository(apiClient: apiClient);
66-
}
67-
68-
final String apiKey;
69-
final User user;
70-
late final TokenManager tokenManager;
71-
final FeedsConfig config;
72-
73-
late final api.DefaultApi apiClient;
74-
75-
@internal
76-
late final FeedsRepository feedsRepository;
77-
78-
static const endpointConfig = EndpointConfig.production;
79-
late final WebSocketClient webSocketClient;
80-
ConnectionRecoveryHandler? connectionRecoveryHandler;
81-
Stream<FeedsWsEvent> get feedsEvents =>
82-
webSocketClient.events.asStream().whereType<FeedsWsEvent>();
83-
84-
Completer<void>? _connectionCompleter;
85-
StreamSubscription<WebSocketConnectionState>? _connectionSubscription;
12+
FeedsConfig config = const FeedsConfig(),
13+
}) =>
14+
InternalFeedsClient(
15+
apiKey: apiKey,
16+
user: user,
17+
userToken: userToken,
18+
userTokenProvider: userTokenProvider,
19+
config: config,
20+
);
8621

8722
/// Connects to the feeds websocket.
8823
/// Future will complete when the connection is established and the user is authenticated.
8924
/// If the authentication fails, the future will complete with an error.
90-
Future<void> connect() async {
91-
webSocketClient.connect();
92-
93-
_connectionSubscription =
94-
webSocketClient.connectionStateStream.listen(_onConnectionStateChanged);
95-
96-
connectionRecoveryHandler = DefaultConnectionRecoveryHandler(
97-
client: webSocketClient,
98-
networkMonitor: config.networkMonitor,
99-
);
100-
101-
_connectionCompleter = Completer<void>();
102-
return _connectionCompleter!.future;
103-
}
25+
Future<void> connect();
10426

10527
/// Disconnects from the feeds websocket.
106-
/// The FeedsClient should no longer be used after calling this method.
107-
void disconnect() {
108-
connectionRecoveryHandler?.dispose();
109-
webSocketClient.disconnect();
110-
_connectionSubscription?.cancel();
111-
_connectionCompleter?.complete();
112-
_connectionCompleter = null;
113-
}
114-
115-
void dispose() {
116-
if (webSocketClient.connectionState is Connected) {
117-
disconnect();
118-
}
119-
webSocketClient.dispose();
120-
}
121-
122-
void _onConnectionStateChanged(WebSocketConnectionState state) {
123-
if (_connectionCompleter != null) {
124-
if (state is Connected) {
125-
_connectionCompleter!.complete();
126-
_connectionCompleter = null;
127-
}
128-
if (state is Disconnected) {
129-
_connectionCompleter!.completeError(Exception('Connection failed'));
130-
_connectionCompleter = null;
131-
}
132-
}
133-
}
134-
135-
Future<void> _authenticate() async {
136-
final connectUserRequest = WsAuthMessageRequest(
137-
products: ['feeds'],
138-
token: await tokenManager.loadToken(),
139-
userDetails: ConnectUserDetailsRequest(
140-
id: user.id,
141-
name: user.originalName,
142-
image: user.imageUrl,
143-
customData: user.customData,
144-
),
145-
);
28+
void disconnect();
14629

147-
webSocketClient.send(connectUserRequest);
148-
}
30+
/// Disposes the FeedsClient.
31+
/// This should be called when the FeedsClient is no longer needed.
32+
/// It will disconnect from the websocket and dispose of the client.
33+
void dispose();
14934

15035
/// Creates a feed instance based on the provided query.
15136
///
@@ -154,9 +39,7 @@ class FeedsClient {
15439
///
15540
/// - Parameter query: The feed query containing the feed identifier and optional configuration
15641
/// - Returns: A [Feed] instance that can be used to interact with the specified feed
157-
Feed feed({required FeedQuery query}) {
158-
return Feed(query: query, client: this);
159-
}
42+
Feed feed({required FeedQuery query});
16043
}
16144

16245
class FeedsConfig {
@@ -166,22 +49,3 @@ class FeedsConfig {
16649

16750
final NetworkMonitor? networkMonitor;
16851
}
169-
170-
class FeedsClientEnvironment {
171-
const FeedsClientEnvironment();
172-
173-
WebSocketClient createWebSocketClient({
174-
required String url,
175-
required EventDecoder eventDecoder,
176-
PingReguestBuilder? pingReguestBuilder,
177-
VoidCallback? onConnectionEstablished,
178-
VoidCallback? onConnected,
179-
}) =>
180-
WebSocketClient(
181-
url: url,
182-
eventDecoder: FeedsWsEvent.fromEventObject,
183-
pingReguestBuilder: pingReguestBuilder,
184-
onConnectionEstablished: onConnectionEstablished,
185-
onConnected: onConnected,
186-
);
187-
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import 'dart:async';
2+
3+
import 'package:meta/meta.dart';
4+
import 'package:rxdart/rxdart.dart';
5+
import 'package:stream_core/stream_core.dart';
6+
7+
import '../stream_feeds.dart';
8+
import 'generated/api/api.dart' as api;
9+
import 'repositories.dart';
10+
import 'utils/endpoint_config.dart';
11+
import 'ws/feeds_ws_event.dart';
12+
13+
class InternalFeedsClient implements FeedsClient, RepositoryProvider {
14+
InternalFeedsClient({
15+
required this.apiKey,
16+
required this.user,
17+
String? userToken,
18+
TokenProvider? userTokenProvider,
19+
this.config = const FeedsConfig(),
20+
FeedsClientEnvironment environment = const FeedsClientEnvironment(),
21+
}) : assert(
22+
userToken != null || userTokenProvider != null,
23+
'Provide either a user token or a user token provider, or both',
24+
) {
25+
tokenManager = userTokenProvider != null
26+
? TokenManager.provider(
27+
user: user,
28+
provider: userTokenProvider,
29+
token: userToken,
30+
)
31+
: TokenManager.static(user: user, token: userToken ?? '');
32+
33+
// TODO: fill with correct values
34+
final systemEnvironmentManager = SystemEnvironmentManager(
35+
environment: const SystemEnvironment(
36+
sdkName: 'stream-feeds-dart',
37+
sdkIdentifier: 'dart',
38+
sdkVersion: '0.1.0',
39+
),
40+
);
41+
42+
apiClient = api.DefaultApi(
43+
CoreHttpClient(
44+
apiKey,
45+
systemEnvironmentManager: systemEnvironmentManager,
46+
options: HttpClientOptions(baseUrl: endpointConfig.baseFeedsUrl),
47+
connectionIdProvider: () => webSocketClient.connectionId,
48+
tokenManager: tokenManager,
49+
),
50+
);
51+
final websocketUri = Uri.parse(endpointConfig.wsEndpoint).replace(
52+
queryParameters: <String, String>{
53+
'api_key': apiKey,
54+
'stream-auth-type': 'jwt',
55+
'X-Stream-Client': 'stream-feeds-dart',
56+
},
57+
);
58+
59+
webSocketClient = environment.createWebSocketClient(
60+
url: websocketUri.toString(),
61+
eventDecoder: FeedsWsEvent.fromEventObject,
62+
onConnectionEstablished: _authenticate,
63+
);
64+
65+
feedsRepository = FeedsRepository(apiClient: apiClient);
66+
}
67+
68+
final String apiKey;
69+
final User user;
70+
late final TokenManager tokenManager;
71+
final FeedsConfig config;
72+
73+
late final api.DefaultApi apiClient;
74+
75+
@override
76+
@internal
77+
late final FeedsRepository feedsRepository;
78+
79+
static const endpointConfig = EndpointConfig.production;
80+
late final WebSocketClient webSocketClient;
81+
ConnectionRecoveryHandler? connectionRecoveryHandler;
82+
Stream<FeedsWsEvent> get feedsEvents =>
83+
webSocketClient.events.asStream().whereType<FeedsWsEvent>();
84+
85+
Completer<void>? _connectionCompleter;
86+
StreamSubscription<WebSocketConnectionState>? _connectionSubscription;
87+
88+
/// Connects to the feeds websocket.
89+
/// Future will complete when the connection is established and the user is authenticated.
90+
/// If the authentication fails, the future will complete with an error.
91+
@override
92+
Future<void> connect() async {
93+
webSocketClient.connect();
94+
95+
_connectionSubscription =
96+
webSocketClient.connectionStateStream.listen(_onConnectionStateChanged);
97+
98+
connectionRecoveryHandler = DefaultConnectionRecoveryHandler(
99+
client: webSocketClient,
100+
networkMonitor: config.networkMonitor,
101+
);
102+
103+
_connectionCompleter = Completer<void>();
104+
return _connectionCompleter!.future;
105+
}
106+
107+
/// Disconnects from the feeds websocket.
108+
@override
109+
void disconnect() {
110+
connectionRecoveryHandler?.dispose();
111+
webSocketClient.disconnect();
112+
_connectionSubscription?.cancel();
113+
_connectionCompleter?.complete();
114+
_connectionCompleter = null;
115+
}
116+
117+
@override
118+
void dispose() {
119+
if (webSocketClient.connectionState is Connected) {
120+
disconnect();
121+
}
122+
webSocketClient.dispose();
123+
}
124+
125+
void _onConnectionStateChanged(WebSocketConnectionState state) {
126+
if (_connectionCompleter != null) {
127+
if (state is Connected) {
128+
_connectionCompleter!.complete();
129+
_connectionCompleter = null;
130+
}
131+
if (state is Disconnected) {
132+
_connectionCompleter!.completeError(Exception('Connection failed'));
133+
_connectionCompleter = null;
134+
}
135+
}
136+
}
137+
138+
Future<void> _authenticate() async {
139+
final connectUserRequest = WsAuthMessageRequest(
140+
products: ['feeds'],
141+
token: await tokenManager.loadToken(),
142+
userDetails: ConnectUserDetailsRequest(
143+
id: user.id,
144+
name: user.originalName,
145+
image: user.imageUrl,
146+
customData: user.customData,
147+
),
148+
);
149+
150+
webSocketClient.send(connectUserRequest);
151+
}
152+
153+
/// Creates a feed instance based on the provided query.
154+
///
155+
/// This method creates a [Feed] object using a [FeedQuery] that can include additional
156+
/// configuration such as activity filters, limits, and feed data for creation.
157+
///
158+
/// - Parameter query: The feed query containing the feed identifier and optional configuration
159+
/// - Returns: A [Feed] instance that can be used to interact with the specified feed
160+
@override
161+
Feed feed({required FeedQuery query}) {
162+
return Feed(query: query, client: this);
163+
}
164+
}
165+
166+
class FeedsClientEnvironment {
167+
const FeedsClientEnvironment();
168+
169+
WebSocketClient createWebSocketClient({
170+
required String url,
171+
required EventDecoder eventDecoder,
172+
PingReguestBuilder? pingReguestBuilder,
173+
VoidCallback? onConnectionEstablished,
174+
VoidCallback? onConnected,
175+
}) =>
176+
WebSocketClient(
177+
url: url,
178+
eventDecoder: FeedsWsEvent.fromEventObject,
179+
pingReguestBuilder: pingReguestBuilder,
180+
onConnectionEstablished: onConnectionEstablished,
181+
onConnected: onConnected,
182+
);
183+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export 'repositories/feeds_repository.dart';
2+
export 'repositories/repository_provider.dart';

0 commit comments

Comments
 (0)