diff --git a/.github/workflows/supabase_flutter.yml b/.github/workflows/supabase_flutter.yml index 1bd63b1c2..b46614ef4 100644 --- a/.github/workflows/supabase_flutter.yml +++ b/.github/workflows/supabase_flutter.yml @@ -34,7 +34,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - flutter-version: ['3.10.x', '3.x'] + flutter-version: ['3.19.x', '3.x'] defaults: run: diff --git a/packages/gotrue/lib/src/broadcast_web.dart b/packages/gotrue/lib/src/broadcast_web.dart index 0c2c2a538..72ba708f0 100644 --- a/packages/gotrue/lib/src/broadcast_web.dart +++ b/packages/gotrue/lib/src/broadcast_web.dart @@ -1,28 +1,37 @@ +import 'dart:async'; import 'dart:convert'; -import 'dart:html' as html; -import 'dart:js_util' as js_util; +import 'dart:js_interop'; import 'package:gotrue/src/types/types.dart'; import 'package:logging/logging.dart'; +import 'package:web/web.dart' as web; final _log = Logger('supabase.auth'); BroadcastChannel getBroadcastChannel(String broadcastKey) { - final broadcast = html.BroadcastChannel(broadcastKey); - return ( - onMessage: broadcast.onMessage.map((event) { - final dataMap = js_util.dartify(event.data); + final broadcast = web.BroadcastChannel(broadcastKey); + final controller = StreamController>(); + + broadcast.addEventListener( + 'message', + (web.Event event) { + if (event is web.MessageEvent) { + final dataMap = event.data.dartify(); + controller.add(json.decode(json.encode(dataMap))); + } + } as web.EventListener, + ); - // some parts have the wrong map type. This is an easy workaround and - // should be efficient enough for the small session and user data - return json.decode(json.encode(dataMap)); - }), + return ( + onMessage: controller.stream, postMessage: (message) { _log.finest('Broadcasting message: $message'); _log.fine('Broadcasting event: ${message['event']}'); - final jsMessage = js_util.jsify(message); - broadcast.postMessage(jsMessage); + broadcast.postMessage(message.jsify() as JSAny); + }, + close: () { + broadcast.close(); + controller.close(); }, - close: broadcast.close, ); } diff --git a/packages/gotrue/lib/src/gotrue_client.dart b/packages/gotrue/lib/src/gotrue_client.dart index fcf4182d2..96e020b3e 100644 --- a/packages/gotrue/lib/src/gotrue_client.dart +++ b/packages/gotrue/lib/src/gotrue_client.dart @@ -11,12 +11,12 @@ import 'package:gotrue/src/types/auth_response.dart'; import 'package:gotrue/src/types/fetch_options.dart'; import 'package:http/http.dart'; import 'package:jwt_decode/jwt_decode.dart'; +import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; import 'package:retry/retry.dart'; import 'package:rxdart/subjects.dart'; -import 'package:logging/logging.dart'; -import 'broadcast_stub.dart' if (dart.library.html) './broadcast_web.dart' +import 'broadcast_stub.dart' if (dart.library.js_interop) './broadcast_web.dart' as web; import 'version.dart'; @@ -363,8 +363,8 @@ class GoTrueClient { ), ); - await _asyncStorage! - .removeItem(key: '${Constants.defaultStorageKey}-code-verifier'); + await _asyncStorage.removeItem( + key: '${Constants.defaultStorageKey}-code-verifier'); final authSessionUrlResponse = AuthSessionUrlResponse( session: Session.fromJson(response)!, redirectType: redirectType?.name); @@ -1163,7 +1163,7 @@ class GoTrueClient { } void _mayStartBroadcastChannel() { - if (const bool.fromEnvironment('dart.library.html')) { + if (const bool.fromEnvironment('dart.library.js_interop')) { // Used by the js library as well final broadcastKey = "sb-${Uri.parse(_url).host.split(".").first}-auth-token"; diff --git a/packages/gotrue/lib/src/types/auth_state.dart b/packages/gotrue/lib/src/types/auth_state.dart index c610790a9..ec703011b 100644 --- a/packages/gotrue/lib/src/types/auth_state.dart +++ b/packages/gotrue/lib/src/types/auth_state.dart @@ -5,7 +5,7 @@ class AuthState { final AuthChangeEvent event; final Session? session; - /// Whether this state was broadcasted via `html.ChannelBroadcast` on web from + /// Whether this state was broadcasted via `web.BroadcastChannel` on web from /// another tab or window. final bool fromBroadcast; diff --git a/packages/gotrue/pubspec.yaml b/packages/gotrue/pubspec.yaml index f17c86d90..e240341d1 100644 --- a/packages/gotrue/pubspec.yaml +++ b/packages/gotrue/pubspec.yaml @@ -6,7 +6,7 @@ repository: 'https://github.com/supabase/supabase-flutter/tree/main/packages/got documentation: 'https://supabase.com/docs/reference/dart/auth-signup' environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: collection: ^1.15.0 @@ -17,6 +17,7 @@ dependencies: rxdart: '>=0.27.7 <0.29.0' meta: ^1.7.0 logging: ^1.2.0 + web: '>=0.5.0 <2.0.0' dev_dependencies: dart_jsonwebtoken: ^2.4.1 diff --git a/packages/gotrue/test/src/broadcast_web_test.dart b/packages/gotrue/test/src/broadcast_web_test.dart new file mode 100644 index 000000000..2808723a2 --- /dev/null +++ b/packages/gotrue/test/src/broadcast_web_test.dart @@ -0,0 +1,57 @@ +@TestOn('browser') +import 'dart:async'; + +import 'package:gotrue/src/broadcast_web.dart'; +import 'package:gotrue/src/types/types.dart'; +import 'package:test/test.dart'; + +void main() { + group('getBroadcastChannel', () { + late BroadcastChannel channel1; + late BroadcastChannel channel2; + + setUp(() { + channel1 = getBroadcastChannel('test-channel'); + channel2 = getBroadcastChannel('test-channel'); + }); + + tearDown(() { + channel1.close(); + channel2.close(); + }); + + test('can send and receive messages between channels', () async { + final completer = Completer>(); + + // Listen for messages on channel2 + final subscription = channel2.onMessage.listen((message) { + completer.complete(message); + }); + + // Send message from channel1 + final testMessage = { + 'event': 'test-event', + 'data': {'foo': 'bar'} + }; + channel1.postMessage(testMessage); + + // Wait for the message to be received + final receivedMessage = await completer.future; + + expect(receivedMessage['event'], equals('test-event')); + expect(receivedMessage['data']['foo'], equals('bar')); + + await subscription.cancel(); + }); + + test('can close channels', () async { + channel1.close(); + + // Verify that sending messages after closing throws + expect( + () => channel1.postMessage({'event': 'test'}), + throwsA(anything), + ); + }); + }); +} diff --git a/packages/realtime_client/lib/src/websocket/websocket.dart b/packages/realtime_client/lib/src/websocket/websocket.dart index 9c6c59667..67bc281c4 100644 --- a/packages/realtime_client/lib/src/websocket/websocket.dart +++ b/packages/realtime_client/lib/src/websocket/websocket.dart @@ -1,3 +1,3 @@ export 'websocket_stub.dart' if (dart.library.io) 'websocket_io.dart' - if (dart.library.html) 'websocket_web.dart'; + if (dart.library.js_interop) 'websocket_web.dart'; diff --git a/packages/supabase/pubspec.yaml b/packages/supabase/pubspec.yaml index 993caabfc..eac131369 100644 --- a/packages/supabase/pubspec.yaml +++ b/packages/supabase/pubspec.yaml @@ -6,7 +6,7 @@ repository: 'https://github.com/supabase/supabase-flutter/tree/main/packages/sup documentation: 'https://supabase.com/docs/reference/dart/introduction' environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.3.0 <4.0.0' dependencies: functions_client: 2.4.1 diff --git a/packages/supabase_flutter/lib/src/local_storage.dart b/packages/supabase_flutter/lib/src/local_storage.dart index 7b2015849..d3622ea28 100644 --- a/packages/supabase_flutter/lib/src/local_storage.dart +++ b/packages/supabase_flutter/lib/src/local_storage.dart @@ -6,7 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import './local_storage_stub.dart' - if (dart.library.html) './local_storage_web.dart' as web; + if (dart.library.js_interop) './local_storage_web.dart' as web; /// Only used for migration from Hive to SharedPreferences. Not actually in use. const supabasePersistSessionKey = 'SUPABASE_PERSIST_SESSION_KEY'; @@ -70,7 +70,7 @@ class SharedPreferencesLocalStorage extends LocalStorage { final String persistSessionKey; static const _useWebLocalStorage = - kIsWeb && bool.fromEnvironment("dart.library.html"); + kIsWeb && bool.fromEnvironment("dart.library.js_interop"); @override Future initialize() async { diff --git a/packages/supabase_flutter/pubspec.yaml b/packages/supabase_flutter/pubspec.yaml index 6919637e5..d0cfde90d 100644 --- a/packages/supabase_flutter/pubspec.yaml +++ b/packages/supabase_flutter/pubspec.yaml @@ -6,8 +6,8 @@ repository: 'https://github.com/supabase/supabase-flutter/tree/main/packages/sup documentation: 'https://supabase.com/docs/reference/dart/introduction' environment: - sdk: '>=3.0.0 <4.0.0' - flutter: '>=3.0.0' + sdk: '>=3.3.0 <4.0.0' + flutter: '>=3.19.0' dependencies: app_links: '>=3.5.0 <7.0.0'