Skip to content

Commit 84255c8

Browse files
committed
fix changelog
1 parent 7186e87 commit 84255c8

File tree

10 files changed

+52
-72
lines changed

10 files changed

+52
-72
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 12.0.2
2+
3+
* Fixed realtime multiple subscription issues
4+
15
## 12.0.1
26

37
* Fixed parameters using enum types

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Add this to your package's `pubspec.yaml` file:
2121

2222
```yml
2323
dependencies:
24-
appwrite: ^12.0.2
24+
appwrite: ^12.0.1
2525
```
2626
2727
You can install packages from the command line:

lib/services/account.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ class Account extends Service {
188188
///
189189
/// Add an authenticator app to be used as an MFA factor. Verify the
190190
/// authenticator using the [verify
191-
/// authenticator](/docs/references/cloud/client-web/account#verifyAuthenticator)
191+
/// authenticator](/docs/references/cloud/client-web/account#updateMfaAuthenticator)
192192
/// method.
193193
Future<models.MfaType> createMfaAuthenticator({required enums.AuthenticatorType type}) async {
194194
final String apiPath = '/account/mfa/authenticators/{type}'.replaceAll('{type}', type.value);
@@ -209,8 +209,8 @@ class Account extends Service {
209209
/// Verify Authenticator
210210
///
211211
/// Verify an authenticator app after adding it using the [add
212-
/// authenticator](/docs/references/cloud/client-web/account#addAuthenticator)
213-
/// method.
212+
/// authenticator](/docs/references/cloud/client-web/account#createMfaAuthenticator)
213+
/// method. add
214214
Future<models.User> updateMfaAuthenticator({required enums.AuthenticatorType type, required String otp}) async {
215215
final String apiPath = '/account/mfa/authenticators/{type}'.replaceAll('{type}', type.value);
216216

lib/src/client_browser.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class ClientBrowser extends ClientBase with ClientMixin {
4343
'x-sdk-name': 'Flutter',
4444
'x-sdk-platform': 'client',
4545
'x-sdk-language': 'flutter',
46-
'x-sdk-version': '12.0.2',
46+
'x-sdk-version': '12.0.1',
4747
'X-Appwrite-Response-Format': '1.5.0',
4848
};
4949

lib/src/client_io.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class ClientIO extends ClientBase with ClientMixin {
6464
'x-sdk-name': 'Flutter',
6565
'x-sdk-platform': 'client',
6666
'x-sdk-language': 'flutter',
67-
'x-sdk-version': '12.0.2',
67+
'x-sdk-version': '12.0.1',
6868
'X-Appwrite-Response-Format' : '1.5.0',
6969
};
7070

lib/src/enums/flag.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ enum Flag {
142142
palau(value: 'pw'),
143143
papuaNewGuinea(value: 'pg'),
144144
poland(value: 'pl'),
145+
frenchPolynesia(value: 'pf'),
145146
northKorea(value: 'kp'),
146147
portugal(value: 'pt'),
147148
paraguay(value: 'py'),

lib/src/realtime_mixin.dart

Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'dart:async';
22
import 'dart:convert';
33
import 'package:flutter/foundation.dart';
44
import 'package:web_socket_channel/web_socket_channel.dart';
5-
import 'package:web_socket_channel/status.dart' as status;
5+
import 'package:web_socket_channel/status.dart';
66
import 'exception.dart';
77
import 'realtime_subscription.dart';
88
import 'client.dart';
@@ -15,46 +15,35 @@ typedef GetFallbackCookie = String? Function();
1515

1616
mixin RealtimeMixin {
1717
late Client client;
18-
final Set<String> _channels = {};
18+
final Map<String, List<StreamController<RealtimeMessage>>> _channels = {};
1919
WebSocketChannel? _websok;
2020
String? _lastUrl;
2121
late WebSocketFactory getWebSocket;
2222
GetFallbackCookie? getFallbackCookie;
2323
int? get closeCode => _websok?.closeCode;
24-
int _subscriptionsCounter = 0;
25-
Map<int, RealtimeSubscription> _subscriptions = {};
26-
bool _notifyDone = true;
27-
StreamSubscription? _websocketSubscription;
28-
bool _creatingSocket = false;
2924

3025
Future<dynamic> _closeConnection() async {
31-
await _websocketSubscription?.cancel();
32-
await _websok?.sink.close(status.normalClosure, 'Ending session');
26+
await _websok?.sink.close(normalClosure);
3327
_lastUrl = null;
3428
}
3529

3630
_createSocket() async {
37-
if(_creatingSocket || _channels.isEmpty) return;
38-
_creatingSocket = true;
3931
final uri = _prepareUri();
4032
if (_websok == null) {
4133
_websok = await getWebSocket(uri);
4234
_lastUrl = uri.toString();
4335
} else {
4436
if (_lastUrl == uri.toString() && _websok?.closeCode == null) {
45-
_creatingSocket = false;
4637
return;
4738
}
48-
_notifyDone = false;
4939
await _closeConnection();
5040
_lastUrl = uri.toString();
5141
_websok = await getWebSocket(uri);
52-
_notifyDone = true;
5342
}
5443
debugPrint('subscription: $_lastUrl');
5544

5645
try {
57-
_websocketSubscription = _websok?.stream.listen((response) {
46+
_websok?.stream.listen((response) {
5847
final data = RealtimeResponse.fromJson(response);
5948
switch (data.type) {
6049
case 'error':
@@ -78,25 +67,28 @@ mixin RealtimeMixin {
7867
break;
7968
case 'event':
8069
final message = RealtimeMessage.fromMap(data.data);
81-
for (var subscription in _subscriptions.values) {
82-
for (var channel in message.channels) {
83-
if (subscription.channels.contains(channel)) {
84-
subscription.controller.add(message);
70+
for(var channel in message.channels) {
71+
if (_channels[channel] != null) {
72+
for( var stream in _channels[channel]!) {
73+
stream.sink.add(message);
8574
}
8675
}
8776
}
8877
break;
8978
}
9079
}, onDone: () {
91-
if (!_notifyDone || _creatingSocket) return;
92-
for (var subscription in _subscriptions.values) {
93-
subscription.close();
80+
for (var list in _channels.values) {
81+
for (var stream in list) {
82+
stream.close();
83+
}
9484
}
9585
_channels.clear();
9686
_closeConnection();
9787
}, onError: (err, stack) {
98-
for (var subscription in _subscriptions.values) {
99-
subscription.controller.addError(err, stack);
88+
for (var list in _channels.values) {
89+
for (var stream in list) {
90+
stream.sink.addError(err, stack);
91+
}
10092
}
10193
if (_websok?.closeCode != null && _websok?.closeCode != 1008) {
10294
debugPrint("Reconnecting in one second.");
@@ -111,8 +103,6 @@ mixin RealtimeMixin {
111103
throw AppwriteException(e.message);
112104
}
113105
throw AppwriteException(e.toString());
114-
} finally {
115-
_creatingSocket = false;
116106
}
117107
}
118108

@@ -128,46 +118,40 @@ mixin RealtimeMixin {
128118
port: uri.port,
129119
queryParameters: {
130120
"project": client.config['project'],
131-
"channels[]": _channels.toList(),
121+
"channels[]": _channels.keys.toList(),
132122
},
133123
path: uri.path + "/realtime",
134124
);
135125
}
136126

137127
RealtimeSubscription subscribeTo(List<String> channels) {
138128
StreamController<RealtimeMessage> controller = StreamController.broadcast();
139-
_channels.addAll(channels);
129+
for(var channel in channels) {
130+
if (!_channels.containsKey(channel)) {
131+
_channels[channel] = [];
132+
}
133+
_channels[channel]!.add(controller);
134+
}
140135
Future.delayed(Duration.zero, () => _createSocket());
141-
int id = DateTime.now().microsecondsSinceEpoch;
142136
RealtimeSubscription subscription = RealtimeSubscription(
143-
controller: controller,
144-
channels: channels,
137+
stream: controller.stream,
145138
close: () async {
146-
_subscriptions.remove(id);
147-
_subscriptionsCounter--;
148139
controller.close();
149-
_cleanup(channels);
150-
151-
if (_channels.isNotEmpty) {
140+
for(var channel in channels) {
141+
_channels[channel]!.remove(controller);
142+
if (_channels[channel]!.isEmpty) {
143+
_channels.remove(channel);
144+
}
145+
}
146+
if(_channels.isNotEmpty) {
152147
await Future.delayed(Duration.zero, () => _createSocket());
153148
} else {
154149
await _closeConnection();
155150
}
156151
});
157-
_subscriptions[id] = subscription;
158152
return subscription;
159153
}
160154

161-
void _cleanup(List<String> channels) {
162-
for (var channel in channels) {
163-
bool found = _subscriptions.values
164-
.any((subscription) => subscription.channels.contains(channel));
165-
if (!found) {
166-
_channels.remove(channel);
167-
}
168-
}
169-
}
170-
171155
void handleError(RealtimeResponse response) {
172156
if (response.data['code'] == 1008) {
173157
throw AppwriteException(response.data["message"], response.data["code"]);

lib/src/realtime_subscription.dart

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,13 @@
1-
import 'dart:async';
2-
31
import 'realtime_message.dart';
42

53
/// Realtime Subscription
64
class RealtimeSubscription {
75
/// Stream of [RealtimeMessage]s
86
final Stream<RealtimeMessage> stream;
97

10-
final StreamController<RealtimeMessage> controller;
11-
12-
/// List of channels
13-
List<String> channels;
14-
158
/// Closes the subscription
169
final Future<void> Function() close;
1710

1811
/// Initializes a [RealtimeSubscription]
19-
RealtimeSubscription(
20-
{required this.close, required this.channels, required this.controller})
21-
: stream = controller.stream;
12+
RealtimeSubscription({required this.stream, required this.close});
2213
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: appwrite
2-
version: 12.0.2
2+
version: 12.0.1
33
description: Appwrite is an open-source self-hosted backend server that abstract and simplify complex and repetitive development tasks behind a very simple REST API
44
homepage: https://appwrite.io
55
repository: https://github.com/appwrite/sdk-for-flutter

test/src/realtime_subscription_test.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1+
import 'package:mockito/mockito.dart';
12
import 'package:appwrite/src/realtime_message.dart';
23
import 'package:appwrite/src/realtime_subscription.dart';
34
import 'package:flutter_test/flutter_test.dart';
4-
import 'dart:async';
5+
6+
class MockStream<T> extends Mock implements Stream<T> {}
7+
8+
59

610
void main() {
711
group('RealtimeSubscription', () {
8-
final mockStream = StreamController<RealtimeMessage>.broadcast();
12+
final mockStream = MockStream<RealtimeMessage>();
913
final mockCloseFunction = () async {};
10-
final subscription = RealtimeSubscription(
11-
controller: mockStream,
12-
close: mockCloseFunction,
13-
channels: ['documents']);
14+
final subscription = RealtimeSubscription(stream: mockStream, close: mockCloseFunction);
1415

1516
test('should have the correct stream and close function', () {
16-
expect(subscription.controller, equals(mockStream));
17-
expect(subscription.stream, equals(mockStream.stream));
17+
expect(subscription.stream, equals(mockStream));
1818
expect(subscription.close, equals(mockCloseFunction));
1919
});
2020
});

0 commit comments

Comments
 (0)