Skip to content

Commit

Permalink
chore: Sdk initialization improvements (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
mirland authored Oct 19, 2023
1 parent b8efe54 commit c143670
Show file tree
Hide file tree
Showing 16 changed files with 110 additions and 148 deletions.
75 changes: 40 additions & 35 deletions mobile/SOLUTION_CODE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@
2. Initialize Supabase in your app

```dart
Future<SupabaseClient> initSupabase() =>
Supabase.initialize(
url: 'YOUR_SUPABASE_URL',
anonKey: 'YOUR_SUPABASE_ANON_KEY',
).then((supabase) => supabase.client);
late SupabaseClient supabaseClient;
Future<void> _initSupabase() async {
await Supabase.initialize(
url: 'YOUR_SUPABASE_URL',
anonKey: 'YOUR_SUPABASE_ANON_KEY',
);
supabaseClient = Supabase.instance.client;
}
```

## Authentication
Expand All @@ -20,85 +25,87 @@ Future<SupabaseClient> initSupabase() =>

```dart
class AuthRemoteSourceImpl implements AuthRemoteSource {
final SupabaseClient _supabaseClient;
AuthRemoteSourceImpl(this._supabaseClient);
@override
Stream<String?> getUserId() =>
_supabaseClient.auth.onAuthStateChange
.map((event) => event.session)
.startWith(_supabaseClient.auth.currentSession)
.map((event) => event?.user.id)
.distinct();
Stream<String?> getUserId() => supabaseClient.auth.onAuthStateChange
.map((event) => event.session)
.startWith(supabaseClient.auth.currentSession)
.map((event) => event?.user.id)
.distinct();
@override
Future<void> signIn({
required String email,
required String password,
}) =>
_supabaseClient.auth.signInWithPassword(email: email, password: password);
supabaseClient.auth.signInWithPassword(email: email, password: password);
@override
Future<void> signUp({
required String alias,
required String email,
required String password,
}) =>
_supabaseClient.auth.signUp(
supabaseClient.auth.signUp(
email: email,
password: password,
data: {'alias': alias},
);
@override
Future<void> signOut() => _supabaseClient.auth.signOut();
Future<void> signOut() => supabaseClient.auth.signOut();
}
```

## Send messages

```dart
class MessagesRemoteSourceImpl implements MessagesRemoteSource {
final SupabaseClient _supabaseClient;
MessagesRemoteSourceImpl(this._supabaseClient);
@override
Future<void> sendMessage({required String body}) async {
final currentUserId = _supabaseClient.auth.currentUser!.id;
return _supabaseClient
final currentUserId = supabaseClient.auth.currentUser!.id;
return supabaseClient
.from('messages')
.insert(MessageRequest(body: body, sender: currentUserId).toJson());
.insert({'body': body, 'sender': currentUserId});
}
}
```

## Read messages

```dart
@override
Future<List<UserMessage>> getMessages() async {
final response = await _supabaseClient
final currentUserId = supabaseClient.auth.currentUser!.id;
final response = await supabaseClient
.from('messages')
.select('*')
.order('created_at', ascending: true);
final messageResponse = MessageResponse.fromJsonList(response);
// Json to UserMessages
final messageResponse = SupabaseMessageResponse.fromJsonList(response);
return messageResponse.toUserMessageList(
userId: _supabaseClient.auth.currentUser!.id,
userId: currentUserId,
);
}
```

### Read messages with alias



#### Create users table and insert data

Migrate data:
```sql
-- Create User Table
CREATE TABLE
public.users (
id uuid not null,
created_at timestamp with time zone not null default now(),
alias text null,
constraint users_pkey primary key (id),
constraint users_id_fkey foreign key (id) references auth.users (id)
) tablespace pg_default;


-- Insert alias into the new table
INSERT INTO
public.users (id, alias)
SELECT
Expand All @@ -108,11 +115,9 @@ FROM
auth.users
ON CONFLICT (id) DO UPDATE
SET alias = excluded.alias;
```

Add Trigger to update table when a user is registered:

```sql
-- Create trigger to insert alias automatically when a new user is created
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER
LANGUAGE plpgsql
Expand All @@ -127,7 +132,7 @@ BEGIN
END;
$$;

CREATE TRIGGER on_auth_user_created
CREATE OR REPLACE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE PROCEDURE public.handle_new_user();
```
Expand Down
8 changes: 4 additions & 4 deletions mobile/lib/core/common/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ interface class Config {
static const String xmartLabsUrl = 'https://xmartlabs.com/';
static const debugMode = kDebugMode;

static late String supabaseUrl;
static late String supabaseAnnonKey;
static late String? supabaseUrl;
static late String? supabaseAnnonKey;

static final _environment = enumFromString(
Environments.values,
Expand All @@ -30,9 +30,9 @@ interface class Config {
}

static void _initializeEnvVariables() {
supabaseUrl = _EnvConfig.getEnvVariable(_EnvConfig.ENV_KEY_SUPABASE_URL)!;
supabaseUrl = _EnvConfig.getEnvVariable(_EnvConfig.ENV_KEY_SUPABASE_URL);
supabaseAnnonKey =
_EnvConfig.getEnvVariable(_EnvConfig.ENV_KEY_SUPABASE_ANNON_KEY)!;
_EnvConfig.getEnvVariable(_EnvConfig.ENV_KEY_SUPABASE_ANNON_KEY);
}
}

Expand Down
2 changes: 1 addition & 1 deletion mobile/lib/core/common/helper/env_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ Future<Map<String, String>> loadEnvs(
bool ignoreErrors = true,
]) async {
final dotEnv = DotEnv();
await dotEnv.load(fileName: path, isOptional: true);
await dotEnv.load(fileName: path, isOptional: ignoreErrors);
return dotEnv.env;
}
2 changes: 0 additions & 2 deletions mobile/lib/core/di/app_providers_module.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_template/core/source/common/local_shared_preferences_storage.dart';
import 'package:flutter_template/main.dart';
import 'package:get_it/get_it.dart';
import 'package:shared_preferences/shared_preferences.dart';

Expand All @@ -20,7 +19,6 @@ class AppProvidersModule {

extension _GetItDiModuleExtensions on GetIt {
void _setupModule() {
registerSingletonAsync(initSupabase);
registerLazySingleton(FlutterSecureStorage.new);
registerSingletonAsync(() => SharedPreferences.getInstance());

Expand Down
10 changes: 3 additions & 7 deletions mobile/lib/core/di/di_repository_module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,8 @@ extension _GetItDiModuleExtensions on GetIt {

void _setupSources() {
registerLazySingleton(() => AuthLocalSource(get()));
registerLazySingleton<AuthRemoteSource>(() => AuthRemoteSourceImpl(get()));
registerLazySingleton<MessagesRemoteSource>(
() => MessagesRemoteSourceImpl(get()),
);
registerLazySingleton<UsersRemoteSource>(
() => UsersRemoteSourceImpl(get()),
);
registerLazySingleton<AuthRemoteSource>(AuthRemoteSourceImpl.new);
registerLazySingleton<MessagesRemoteSource>(MessagesRemoteSourceImpl.new);
registerLazySingleton<UsersRemoteSource>(UsersRemoteSourceImpl.new);
}
}
2 changes: 1 addition & 1 deletion mobile/lib/core/repository/messages_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:flutter_template/core/source/users_remote_source.dart';
import 'package:rxdart/rxdart.dart';

class MessagesRepository {
static const _useMessageStream = false;
static const _useMessageStream = true;

final MessagesRemoteSource _messagesRemoteSource;
final UsersRemoteSource _usersRemoteSource;
Expand Down
16 changes: 6 additions & 10 deletions mobile/lib/core/source/auth_remote_source.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:flutter_template/main.dart';
import 'package:rxdart/rxdart.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

abstract interface class AuthRemoteSource {
Stream<String?> getUserId();
Expand All @@ -19,14 +19,10 @@ abstract interface class AuthRemoteSource {
}

class AuthRemoteSourceImpl implements AuthRemoteSource {
final SupabaseClient _supabaseClient;

AuthRemoteSourceImpl(this._supabaseClient);

@override
Stream<String?> getUserId() => _supabaseClient.auth.onAuthStateChange
Stream<String?> getUserId() => supabaseClient.auth.onAuthStateChange
.map((event) => event.session)
.startWith(_supabaseClient.auth.currentSession)
.startWith(supabaseClient.auth.currentSession)
.map((event) => event?.user.id)
.distinct();

Expand All @@ -35,20 +31,20 @@ class AuthRemoteSourceImpl implements AuthRemoteSource {
required String email,
required String password,
}) =>
_supabaseClient.auth.signInWithPassword(email: email, password: password);
supabaseClient.auth.signInWithPassword(email: email, password: password);

@override
Future<void> signUp({
required String alias,
required String email,
required String password,
}) =>
_supabaseClient.auth.signUp(
supabaseClient.auth.signUp(
email: email,
password: password,
data: {'alias': alias},
);

@override
Future<void> signOut() => _supabaseClient.auth.signOut();
Future<void> signOut() => supabaseClient.auth.signOut();
}
27 changes: 12 additions & 15 deletions mobile/lib/core/source/messages_remote_source.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:async';

import 'package:flutter_template/core/model/message.dart';
import 'package:flutter_template/core/model/user_message.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:flutter_template/main.dart';

abstract interface class MessagesRemoteSource {
Future<List<UserMessage>> getMessages();
Expand All @@ -15,41 +15,38 @@ abstract interface class MessagesRemoteSource {
}

class MessagesRemoteSourceImpl implements MessagesRemoteSource {
final SupabaseClient _supabaseClient;

MessagesRemoteSourceImpl(this._supabaseClient);

@override
Future<List<UserMessage>> getMessages() async {
final response = await _supabaseClient
final currentUserId = supabaseClient.auth.currentUser!.id;
final response = await supabaseClient
.from('messages')
.select('*, user:sender(id, alias)')
.order('created_at', ascending: true);
final supabaseMessageResponse =
SupabaseMessageResponse.fromJsonList(response);
return supabaseMessageResponse.toUserMessageList(
userId: _supabaseClient.auth.currentUser!.id,
// Json to UserMessages
final messageResponse = SupabaseMessageResponse.fromJsonList(response);
return messageResponse.toUserMessageList(
userId: currentUserId,
);
}

@override
Stream<List<Message>> getMessagesStream() => _supabaseClient
Stream<List<Message>> getMessagesStream() => supabaseClient
.from('messages')
.stream(primaryKey: ['id'])
.order('created_at', ascending: true)
.map(Message.fromJsonList);

@override
Future<void> sendMessage({required String body}) async {
final currentUserId = _supabaseClient.auth.currentUser!.id;
return _supabaseClient
final currentUserId = supabaseClient.auth.currentUser!.id;
return supabaseClient
.from('messages')
.insert(MessageRequest(body: body, sender: currentUserId).toJson());
.insert({'body': body, 'sender': currentUserId});
}

@override
Future<void> uppercaseMessage({required String id}) =>
_supabaseClient.functions.invoke(
supabaseClient.functions.invoke(
'uppercase_message',
body: {'id': id},
);
Expand Down
8 changes: 2 additions & 6 deletions mobile/lib/core/source/users_remote_source.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import 'dart:async';

import 'package:flutter_template/core/model/user.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:flutter_template/main.dart';

abstract interface class UsersRemoteSource {
Stream<List<SupabaseUserResponse>> getUsersStream();
}

class UsersRemoteSourceImpl implements UsersRemoteSource {
final SupabaseClient _supabaseClient;

UsersRemoteSourceImpl(this._supabaseClient);

@override
Stream<List<SupabaseUserResponse>> getUsersStream() => _supabaseClient
Stream<List<SupabaseUserResponse>> getUsersStream() => supabaseClient
.from('users')
.stream(primaryKey: ['id']).map(SupabaseUserResponse.fromJsonList);
}
16 changes: 11 additions & 5 deletions mobile/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ import 'package:flutter_template/core/di/di_provider.dart';
import 'package:flutter_template/ui/main/main_screen.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

// This should be initialized in `AppProvidersModule` but it's here just for
// This should be initialized using GetIt but it's here just for
// the example.
Future<SupabaseClient> initSupabase() => Supabase.initialize(
url: Config.supabaseUrl,
anonKey: Config.supabaseAnnonKey,
).then((supabase) => supabase.client);
late SupabaseClient supabaseClient;

Future<void> _initSupabase() async {
await Supabase.initialize(
url: Config.supabaseUrl ?? 'YOUR_SUPABASE_URL',
anonKey: Config.supabaseAnnonKey ?? 'YOUR_SUPABASE_ANON_KEY',
);
supabaseClient = Supabase.instance.client;
}

Future main() async {
await runZonedGuarded(
Expand All @@ -34,6 +39,7 @@ Future _initSdks() async {
WidgetsFlutterBinding.ensureInitialized();
await Logger.init();
await Config.initialize();
await _initSupabase();

await Future.wait([
DiProvider.init(),
Expand Down
Loading

0 comments on commit c143670

Please sign in to comment.