feat: Add biometric permission and update activity to FlutterFragmentActivity; refactor bindings and remove unused repositories
This commit is contained in:
parent
803a28494d
commit
8d67e7bbb3
|
@ -5,6 +5,8 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<!-- Biometric permission -->
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||
<!-- ... -->
|
||||
<application android:label="sigap"
|
||||
android:name="${applicationName}"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package com.backspacex.sigap
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||
|
||||
class MainActivity : FlutterActivity()
|
||||
class MainActivity: FlutterFragmentActivity()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.AppCompat.DayNight">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
|
@ -12,7 +12,7 @@
|
|||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<style name="NormalTheme" parent="@android:style/Theme.AppCompat.DayNight">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:get/get_navigation/src/root/get_material_app.dart';
|
||||
import 'package:sigap/src/cores/bindings/general_bindings.dart';
|
||||
import 'package:sigap/src/cores/bindings/app_bindings.dart';
|
||||
import 'package:sigap/src/cores/routes/app_pages.dart';
|
||||
import 'package:sigap/src/utils/constants/colors.dart';
|
||||
import 'package:sigap/src/utils/constants/text_strings.dart';
|
||||
|
@ -18,7 +18,7 @@ class App extends StatelessWidget {
|
|||
theme: TAppTheme.lightTheme,
|
||||
darkTheme: TAppTheme.darkTheme,
|
||||
debugShowCheckedModeBanner: false,
|
||||
initialBinding: GeneralBindings(),
|
||||
initialBinding: AppBindings(),
|
||||
localizationsDelegates: GlobalMaterialLocalizations.delegates,
|
||||
supportedLocales: const [Locale('id', '')],
|
||||
getPages: AppPages.routes,
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:sigap/app.dart';
|
||||
import 'package:sigap/src/cores/repositories/panic-button/panic_button_repository.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
|
@ -32,9 +30,5 @@ Future<void> main() async {
|
|||
storageOptions: const StorageClientOptions(retryAttempts: 10),
|
||||
);
|
||||
|
||||
// Initialize repositories
|
||||
Get.put(PanicButtonRepository());
|
||||
await Get.find<PanicButtonRepository>().init();
|
||||
|
||||
runApp(const App());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import 'package:get/get.dart';
|
||||
import 'package:sigap/src/cores/bindings/controller_bindings.dart';
|
||||
import 'package:sigap/src/cores/bindings/general_bindings.dart';
|
||||
import 'package:sigap/src/cores/bindings/repository_bindings.dart';
|
||||
import 'package:sigap/src/cores/bindings/service_bindings.dart';
|
||||
|
||||
class AppBindings implements Bindings {
|
||||
@override
|
||||
Future<void> dependencies() async {
|
||||
// Register general helpers and utilities
|
||||
UtilityBindings().dependencies();
|
||||
|
||||
// Register all services
|
||||
await ServiceBindings().dependencies();
|
||||
|
||||
// Register all repositories
|
||||
RepositoryBindings().dependencies();
|
||||
|
||||
// Register all feature controllers
|
||||
ControllerBindings().dependencies();
|
||||
}
|
||||
}
|
|
@ -1,45 +1,17 @@
|
|||
import 'package:get/get.dart';
|
||||
import 'package:sigap/src/features/auth/controllers/forgot_password_controller.dart';
|
||||
import 'package:sigap/src/features/auth/controllers/signin_controller.dart';
|
||||
import 'package:sigap/src/features/auth/controllers/signup_controller.dart';
|
||||
import 'package:sigap/src/features/onboarding/controllers/onboarding_controller.dart';
|
||||
import 'package:sigap/src/features/onboarding/controllers/role_selection_controller.dart';
|
||||
import 'package:sigap/src/features/auth/bindings/auth_bindings.dart';
|
||||
import 'package:sigap/src/features/onboarding/bindings/onboarding_binding.dart';
|
||||
|
||||
// Onboarding controller bindings
|
||||
class OnboardingControllerBinding extends Bindings {
|
||||
class ControllerBindings extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<OnboardingController>(() => OnboardingController());
|
||||
// Register all controllers here
|
||||
|
||||
// Onboarding Bindings
|
||||
OnboardingBindings().dependencies();
|
||||
|
||||
// Auth Bindings
|
||||
AuthBindings().dependencies();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class RoleSelectionControllerBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<RoleSelectionController>(() => RoleSelectionController());
|
||||
}
|
||||
}
|
||||
|
||||
// Auth controller bindings
|
||||
class SignInControllerBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<SignInController>(() => SignInController());
|
||||
}
|
||||
}
|
||||
|
||||
class SignUpControllerBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<SignUpController>(() => SignUpController());
|
||||
}
|
||||
}
|
||||
|
||||
class ForgotPasswordControllerBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<ForgotPasswordController>(() => ForgotPasswordController());
|
||||
}
|
||||
}
|
||||
|
||||
// Main controller bindings
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:get/get.dart';
|
|||
import 'package:logger/logger.dart';
|
||||
import 'package:sigap/src/utils/helpers/network_manager.dart';
|
||||
|
||||
class GeneralBindings extends Bindings {
|
||||
class UtilityBindings extends Bindings {
|
||||
Logger? get logger => Logger();
|
||||
|
||||
@override
|
||||
|
|
|
@ -9,25 +9,14 @@ import 'package:sigap/src/cores/repositories/personalization/users_repository.da
|
|||
class RepositoryBindings extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
// Auth Repository
|
||||
Get.lazyPut<AuthenticationRepository>(
|
||||
() => AuthenticationRepository(),
|
||||
fenix: true,
|
||||
);
|
||||
|
||||
// User Repository
|
||||
Get.lazyPut<UserRepository>(() => UserRepository(), fenix: true);
|
||||
|
||||
// Officer Repository
|
||||
Get.lazyPut<OfficerRepository>(() => OfficerRepository(), fenix: true);
|
||||
|
||||
// Unit Repository
|
||||
Get.lazyPut<UnitRepository>(() => UnitRepository(), fenix: true);
|
||||
|
||||
// Profile Repository
|
||||
Get.lazyPut<ProfileRepository>(() => ProfileRepository(), fenix: true);
|
||||
|
||||
// Role Repository
|
||||
Get.lazyPut<RolesRepository>(() => RolesRepository(), fenix: true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,504 +0,0 @@
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:sigap/src/cores/services/biometric_service.dart';
|
||||
import 'package:sigap/src/cores/services/location_service.dart';
|
||||
import 'package:sigap/src/cores/services/supabase_service.dart';
|
||||
import 'package:sigap/src/features/auth/screens/signin/signin_screen.dart';
|
||||
import 'package:sigap/src/utils/constants/app_routes.dart';
|
||||
import 'package:sigap/src/utils/exceptions/exceptions.dart';
|
||||
import 'package:sigap/src/utils/exceptions/format_exceptions.dart';
|
||||
import 'package:sigap/src/utils/exceptions/platform_exceptions.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
|
||||
class AuthenticationRepository extends GetxController {
|
||||
static AuthenticationRepository get instance => Get.find();
|
||||
|
||||
// Variable
|
||||
final storage = GetStorage();
|
||||
final _supabase = SupabaseService.instance.client;
|
||||
final _locationService = LocationService.instance;
|
||||
final _biometricService = Get.put(BiometricService());
|
||||
|
||||
// Getters that use the Supabase service
|
||||
User? get authUser => SupabaseService.instance.currentUser;
|
||||
String? get currentUserId => SupabaseService.instance.currentUserId;
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
FlutterNativeSplash.remove();
|
||||
screenRedirect();
|
||||
}
|
||||
|
||||
// Check for biometric login on app start
|
||||
Future<bool> attemptBiometricLogin() async {
|
||||
if (!await _biometricService.isBiometricLoginEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String? refreshToken = await _biometricService.attemptBiometricLogin();
|
||||
if (refreshToken == null || refreshToken.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Use the refresh token to recover the session
|
||||
final response = await _supabase.auth.refreshSession(refreshToken);
|
||||
if (response.session != null) {
|
||||
Get.offAllNamed(AppRoutes.explore);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
// If refresh token is invalid or expired, disable biometric login
|
||||
await _biometricService.disableBiometricLogin();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the onboarding process is complete
|
||||
Future<bool> isOnboardingComplete() async {
|
||||
return storage.read('ONBOARDING_COMPLETED') ?? false;
|
||||
}
|
||||
|
||||
// Screen redirect based on auth status
|
||||
void screenRedirect() async {
|
||||
final user = _supabase.auth.currentUser;
|
||||
|
||||
if (user != null) {
|
||||
// User is authenticated
|
||||
Get.offAllNamed(AppRoutes.panicButton);
|
||||
} else {
|
||||
// Check if onboarding is completed
|
||||
final onboardingCompleted = await isOnboardingComplete();
|
||||
|
||||
if (onboardingCompleted) {
|
||||
// Check location validity
|
||||
final isLocationValid =
|
||||
await _locationService.isLocationValidForFeature();
|
||||
|
||||
if (isLocationValid) {
|
||||
// Go to role selection
|
||||
Get.offAllNamed(AppRoutes.roleSelection);
|
||||
} else {
|
||||
// Show location warning
|
||||
Get.offAllNamed(AppRoutes.locationWarning);
|
||||
}
|
||||
} else {
|
||||
// Show onboarding
|
||||
Get.offAllNamed(AppRoutes.onboarding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- Email and Password Sign In -----------------
|
||||
Future<AuthResponse> signInWithEmailPassword({
|
||||
required String email,
|
||||
required String password,
|
||||
}) async {
|
||||
try {
|
||||
// Check if the user is banned
|
||||
final bannedUser = await _supabase.rpc(
|
||||
'check_if_banned',
|
||||
params: {'user_email': email},
|
||||
);
|
||||
if (bannedUser != null && bannedUser == true) {
|
||||
throw TExceptions(
|
||||
'This account has been banned due to violation of terms.',
|
||||
);
|
||||
}
|
||||
|
||||
final response = await _supabase.auth.signInWithPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
|
||||
// Setup biometric login if available
|
||||
if (_biometricService.isBiometricAvailable.value) {
|
||||
// Ask user if they want to enable biometric login
|
||||
// This would typically be done in the UI, but setting up the flow here
|
||||
await _biometricService.enableBiometricLogin();
|
||||
}
|
||||
|
||||
return response;
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// [SESSION] - CHECK SESSION
|
||||
Future<Map<String, dynamic>?> getSession() async {
|
||||
try {
|
||||
final session = _supabase.auth.currentSession;
|
||||
if (session != null) {
|
||||
return {'user': session.user, 'session': session};
|
||||
}
|
||||
return null;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// [Email Verification] - EMAIL VERIFICATION
|
||||
Future<void> sendEmailVerification() async {
|
||||
try {
|
||||
await _supabase.auth.resend(
|
||||
type: OtpType.signup,
|
||||
email: _supabase.auth.currentUser!.email,
|
||||
);
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// [Email Reset Password ] - RESET PASSWORD
|
||||
Future<void> sendResetPasswordForEmail(String email) async {
|
||||
try {
|
||||
await _supabase.auth.resetPasswordForEmail(email);
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// Compare OTP
|
||||
Future<AuthResponse> verifyOtp(String otp) async {
|
||||
try {
|
||||
final AuthResponse res = await _supabase.auth.verifyOTP(
|
||||
type: OtpType.signup,
|
||||
token: otp,
|
||||
);
|
||||
|
||||
final Session? session = res.session;
|
||||
final User? user = res.user;
|
||||
|
||||
if (session == null && user == null) {
|
||||
throw TExceptions('Failed to verify OTP. Please try again.');
|
||||
}
|
||||
|
||||
return res;
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// Update password after reset
|
||||
Future<UserResponse> updatePasswordAfterReset(String newPassword) async {
|
||||
try {
|
||||
final response = await _supabase.auth.updateUser(
|
||||
UserAttributes(password: newPassword),
|
||||
);
|
||||
return response;
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> changePassword(
|
||||
String currentPassword,
|
||||
String newPassword,
|
||||
String email,
|
||||
) async {
|
||||
try {
|
||||
// Reauthenticate user
|
||||
await _supabase.auth.reauthenticate();
|
||||
|
||||
// Update password
|
||||
await _supabase.auth.updateUser(UserAttributes(password: newPassword));
|
||||
|
||||
// Password changed successfully
|
||||
return 'Password changed successfully.';
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// [Email Verification] - CREATE NEW VERIFICATION USER EMAIL
|
||||
Future<void> resendEmailVerification(String email) async {
|
||||
try {
|
||||
await _supabase.auth.resend(type: OtpType.signup, email: email);
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- Social Sign In -----------------
|
||||
// [GoogleAuthentication] - GOOGLE
|
||||
Future<bool> signInWithGoogle() async {
|
||||
try {
|
||||
return await _supabase.auth.signInWithOAuth(
|
||||
OAuthProvider.google,
|
||||
authScreenLaunchMode: LaunchMode.inAppWebView,
|
||||
);
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// [FacebookAuthentication] - FACEBOOK
|
||||
Future<bool> signInWithFacebook() async {
|
||||
try {
|
||||
return await _supabase.auth.signInWithOAuth(
|
||||
OAuthProvider.facebook,
|
||||
authScreenLaunchMode: LaunchMode.inAppWebView,
|
||||
);
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// [AppleAuthentication] - APPLE
|
||||
Future<bool> signInWithApple() async {
|
||||
try {
|
||||
return await _supabase.auth.signInWithOAuth(
|
||||
OAuthProvider.apple,
|
||||
authScreenLaunchMode: LaunchMode.inAppWebView,
|
||||
);
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// [GithubAuthentication] - GITHUB
|
||||
Future<bool> signInWithGithub() async {
|
||||
try {
|
||||
return await _supabase.auth.signInWithOAuth(
|
||||
OAuthProvider.github,
|
||||
authScreenLaunchMode: LaunchMode.inAppWebView,
|
||||
);
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// [TwitterAuthentication] - TWITTER
|
||||
Future<bool> signInWithTwitter() async {
|
||||
try {
|
||||
return await _supabase.auth.signInWithOAuth(
|
||||
OAuthProvider.twitter,
|
||||
authScreenLaunchMode: LaunchMode.inAppWebView,
|
||||
);
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// [Email AUTH] - SIGN UP
|
||||
Future<AuthResponse> signUpWithEmailPassword({
|
||||
required String email,
|
||||
required String password,
|
||||
Map<String, dynamic>? userMetadata,
|
||||
}) async {
|
||||
try {
|
||||
// Validate location for registration
|
||||
bool isLocationValid = await _locationService.isLocationValidForFeature();
|
||||
if (!isLocationValid) {
|
||||
throw TExceptions('Registration is only available within Jember area. Please ensure your location services are enabled and you are not using a mock location app.');
|
||||
}
|
||||
|
||||
// Create user with email and password
|
||||
final response = await _supabase.auth.signUp(
|
||||
email: email,
|
||||
password: password,
|
||||
data: userMetadata,
|
||||
);
|
||||
|
||||
return response;
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} catch (e) {
|
||||
if (e is TExceptions) rethrow;
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// Enable or disable biometric login
|
||||
Future<void> toggleBiometricLogin(bool enable) async {
|
||||
if (enable) {
|
||||
await _biometricService.enableBiometricLogin();
|
||||
} else {
|
||||
await _biometricService.disableBiometricLogin();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if biometric login is enabled
|
||||
Future<bool> isBiometricLoginEnabled() async {
|
||||
return await _biometricService.isBiometricLoginEnabled();
|
||||
}
|
||||
|
||||
// Check if biometrics are available on the device
|
||||
Future<bool> isBiometricAvailable() async {
|
||||
return _biometricService.isBiometricAvailable.value;
|
||||
}
|
||||
|
||||
// ----------------- Logout -----------------
|
||||
// [Sign Out] - SIGN OUT
|
||||
Future<void> signOut() async {
|
||||
try {
|
||||
await _supabase.auth.signOut();
|
||||
Get.offAll(() => const SignInScreen());
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- Delete Account -----------------
|
||||
Future<void> deleteAccount() async {
|
||||
try {
|
||||
final userId = _supabase.auth.currentUser!.id;
|
||||
await _supabase.rpc('delete_account', params: {'user_id': userId});
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates a user's profile with the officer status and metadata
|
||||
Future<UserResponse> updateUserRole({
|
||||
required bool isOfficer,
|
||||
Map<String, dynamic>? officerData,
|
||||
}) async {
|
||||
try {
|
||||
// Prepare metadata with the officer flag
|
||||
final userMetadata = <String, dynamic>{'is_officer': isOfficer};
|
||||
|
||||
// Add officer data if provided
|
||||
if (isOfficer && officerData != null) {
|
||||
userMetadata['officer_data'] = officerData;
|
||||
}
|
||||
|
||||
// Update the user metadata which will trigger the role change function
|
||||
final response = await _supabase.auth.updateUser(
|
||||
UserAttributes(data: userMetadata),
|
||||
);
|
||||
return response;
|
||||
} on AuthException catch (e) {
|
||||
throw TExceptions(e.message);
|
||||
} on FormatException catch (_) {
|
||||
throw const TFormatException();
|
||||
} on PlatformException catch (e) {
|
||||
throw TPlatformException(e.code).message;
|
||||
} on PostgrestException catch (error) {
|
||||
throw TExceptions.fromCode(error.code ?? 'unknown_error');
|
||||
} catch (e) {
|
||||
throw TExceptions('Something went wrong. Please try again later.');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:get/get.dart';
|
||||
import 'package:sigap/src/cores/bindings/controller_bindings.dart';
|
||||
import 'package:sigap/src/features/auth/screens/forgot-password/forgot_password.dart';
|
||||
import 'package:sigap/src/features/auth/screens/signin/signin_screen.dart';
|
||||
import 'package:sigap/src/features/auth/screens/signup/signup_screen.dart';
|
||||
|
@ -12,46 +11,21 @@ class AppPages {
|
|||
|
||||
static final routes = [
|
||||
// Onboarding
|
||||
GetPage(
|
||||
name: AppRoutes.onboarding,
|
||||
page: () => const OnboardingScreen(),
|
||||
binding: OnboardingControllerBinding(),
|
||||
),
|
||||
GetPage(name: AppRoutes.onboarding, page: () => const OnboardingScreen()),
|
||||
|
||||
GetPage(
|
||||
name: AppRoutes.roleSelection,
|
||||
page: () => const RoleSelectionScreen(),
|
||||
binding: RoleSelectionControllerBinding(),
|
||||
),
|
||||
|
||||
// Auth
|
||||
GetPage(
|
||||
name: AppRoutes.signIn,
|
||||
page: () => const SignInScreen(),
|
||||
binding: SignInControllerBinding(),
|
||||
),
|
||||
GetPage(name: AppRoutes.signIn, page: () => const SignInScreen()),
|
||||
|
||||
GetPage(
|
||||
name: AppRoutes.signUp,
|
||||
page: () => const SignUpScreen(),
|
||||
binding: SignUpControllerBinding(),
|
||||
),
|
||||
GetPage(name: AppRoutes.signUp, page: () => const SignUpScreen()),
|
||||
|
||||
GetPage(
|
||||
name: AppRoutes.forgotPassword,
|
||||
page: () => const ForgotPasswordScreen(),
|
||||
binding: ForgotPasswordControllerBinding(),
|
||||
),
|
||||
|
||||
// Main pages
|
||||
// GetPage(name: AppRoutes.explore, page: () => const ExploreScreen()),
|
||||
// GetPage(name: AppRoutes.map, page: () => const MapScreen()),
|
||||
// GetPage(name: AppRoutes.panicButton, page: () => const PanicButtonScreen()),
|
||||
// GetPage(name: AppRoutes.communityWatch, page: () => const CommunityWatchScreen()),
|
||||
// GetPage(name: AppRoutes.dailyOps, page: () => const DailyOpsScreen()),
|
||||
|
||||
// Personalization
|
||||
// GetPage(name: AppRoutes.profile, page: () => const ProfileScreen()),
|
||||
// GetPage(name: AppRoutes.settings, page: () => const SettingsScreen()),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import 'package:get/get.dart';
|
||||
import 'package:sigap/src/features/auth/controllers/email_verification_controller.dart';
|
||||
import 'package:sigap/src/features/auth/controllers/forgot_password_controller.dart';
|
||||
import 'package:sigap/src/features/auth/controllers/signin_controller.dart';
|
||||
import 'package:sigap/src/features/auth/controllers/signup_controller.dart';
|
||||
import 'package:sigap/src/features/auth/controllers/step_form_controller.dart';
|
||||
|
||||
class AuthBindings extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
// Register all feature auth controllers
|
||||
Get.lazyPut(() => SignInController());
|
||||
Get.lazyPut(() => SignUpController());
|
||||
Get.lazyPut(() => StepFormController());
|
||||
Get.lazyPut(() => EmailVerificationController());
|
||||
Get.lazyPut(() => ForgotPasswordController());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import 'package:get/get.dart';
|
||||
import 'package:sigap/src/features/onboarding/controllers/onboarding_controller.dart';
|
||||
import 'package:sigap/src/features/onboarding/controllers/role_selection_controller.dart';
|
||||
|
||||
class OnboardingBindings extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
// Register all feature onboarding controllers
|
||||
Get.lazyPut(() => OnboardingController());
|
||||
Get.lazyPut(() => RoleSelectionController());
|
||||
}
|
||||
}
|
|
@ -93,6 +93,9 @@ dependencies:
|
|||
# Fonts
|
||||
google_fonts:
|
||||
|
||||
# Localization
|
||||
flutter_localizations
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
|
Loading…
Reference in New Issue