Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/supabase_flutter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
35 changes: 22 additions & 13 deletions packages/gotrue/lib/src/broadcast_web.dart
Original file line number Diff line number Diff line change
@@ -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<Map<String, dynamic>>();

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,
);
}
10 changes: 5 additions & 5 deletions packages/gotrue/lib/src/gotrue_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion packages/gotrue/lib/src/types/auth_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
3 changes: 2 additions & 1 deletion packages/gotrue/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
57 changes: 57 additions & 0 deletions packages/gotrue/test/src/broadcast_web_test.dart
Original file line number Diff line number Diff line change
@@ -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<Map<String, dynamic>>();

// 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),
);
});
});
}
2 changes: 1 addition & 1 deletion packages/realtime_client/lib/src/websocket/websocket.dart
Original file line number Diff line number Diff line change
@@ -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';
2 changes: 1 addition & 1 deletion packages/supabase/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions packages/supabase_flutter/lib/src/local_storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<void> initialize() async {
Expand Down
4 changes: 2 additions & 2 deletions packages/supabase_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down