Skip to content

Commit

Permalink
FEAT: Implement the OAuth functionality (#24)
Browse files Browse the repository at this point in the history
* initiate the routing operation

* feat(auth): launch social auth

* feat: complete the oauth log in flow

* feat: use go_router for navigation

* fix: go router doesn't work

* feat: add transition to the sign up screen

* feat: add the new routes

* refactor: use go router instead or navigator

* Update settings_section.dart
  • Loading branch information
Ahmed-Aladdiin authored Oct 30, 2024
1 parent 7aaf3ae commit 23dee9d
Show file tree
Hide file tree
Showing 40 changed files with 568 additions and 262 deletions.
24 changes: 24 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
</queries>

<application
android:usesCleartextTraffic="true"
android:label="TelWare"
Expand All @@ -33,6 +45,18 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data
android:name="flutter_deeplinking_enabled"
android:value="true"
/>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="telware" android:host="telware.online"/>
<!-- <data android:scheme="http" android:host="mytelwareauth.org"/>
<data android:scheme="https"/> -->
</intent-filter>
</activity>
<activity
android:name="com.yalantis.ucrop.UCropActivity"
Expand Down
Binary file removed assets/imgs/facebook-f-white.png
Binary file not shown.
6 changes: 6 additions & 0 deletions lib/.env_example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
API_URL=https://example.com/api/v1
RECAPTCHA_SITE_KEY={reCAPTCHA_SITE_KEY}

# Social auth URL
GOOGLE_AUTH_URL=/auth/google
GITHUB_AUTH_URL=/auth/github
9 changes: 6 additions & 3 deletions lib/core/constants/server_constants.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// ignore_for_file: non_constant_identifier_names

import 'package:dio/dio.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';

// ignore: non_constant_identifier_names
final String BASE_URL = dotenv.env['BASE_URL']!;
final String API_URL = dotenv.env['API_URL']!;
final String GOOGLE_AUTH_URL = API_URL + dotenv.env['GOOGLE_AUTH_URL']!;
final String GITHUB_AUTH_URL = API_URL + dotenv.env['GITHUB_AUTH_URL']!;
final BASE_OPTIONS =
BaseOptions(baseUrl: BASE_URL, contentType: 'application/json');
BaseOptions(baseUrl: API_URL, contentType: 'application/json');
182 changes: 182 additions & 0 deletions lib/core/routes/routes.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';

import 'package:telware_cross_platform/core/view/screen/splash_screen.dart';
import 'package:telware_cross_platform/features/auth/view/screens/change_number_form_screen.dart';
import 'package:telware_cross_platform/features/auth/view/screens/log_in_screen.dart';
import 'package:telware_cross_platform/features/auth/view/screens/sign_up_screen.dart';
import 'package:telware_cross_platform/features/auth/view/screens/social_auth_loading_screen.dart';
import 'package:telware_cross_platform/features/auth/view/screens/verification_screen.dart';
import 'package:telware_cross_platform/features/auth/view_model/auth_view_model.dart';
import 'package:telware_cross_platform/features/home/view/screens/home_screen.dart';
import 'package:telware_cross_platform/features/home/view/screens/inbox_screen.dart';
import 'package:telware_cross_platform/features/stories/view/screens/add_my_story_screen.dart';
import 'package:telware_cross_platform/features/stories/view/screens/show_taken_story_screen.dart';
import 'package:telware_cross_platform/features/stories/view/screens/story_screen.dart';
import 'package:telware_cross_platform/features/user/view/screens/block_user.dart';
import 'package:telware_cross_platform/features/user/view/screens/blocked_users.dart';
import 'package:telware_cross_platform/features/user/view/screens/change_number_screen.dart';
import 'package:telware_cross_platform/features/user/view/screens/privacy_and_security_screen.dart';
import 'package:telware_cross_platform/features/user/view/screens/profile_info_screen.dart';
import 'package:telware_cross_platform/features/user/view/screens/settings_screen.dart';
import 'package:telware_cross_platform/features/user/view/screens/user_profile_screen.dart';

class Routes {
static const String home = HomeScreen.route;
static const String splash = SplashScreen.route;
static const String logIn = LogInScreen.route;
static const String signUp = SignUpScreen.route;
static const String verification = VerificationScreen.route;
static const String socialAuthLoading = SocialAuthLoadingScreen.route;
static const String inboxScreen = InboxScreen.route;
static const String addMyStory = AddMyStoryScreen.route;
static const String showTakenStory = ShowTakenStoryScreen.route;
static const storyScreen = StoryScreen.route;
static const String settings = SettingsScreen.route;
static const String changeNumber = ChangeNumberScreen.route;
static const String changeNumberForm = ChangeNumberFormScreen.route;
static const String profileInfo = ProfileInfoScreen.route;
static const String blockUser = BlockUserScreen.route;
static const String blockedUser = BlockedUsersScreen.route;
static const String userProfile = UserProfileScreen.route;
static const String privacySettings = PrivacySettingsScreen.route;


static GoRouter appRouter(WidgetRef ref) => GoRouter(
initialLocation: Routes.splash,
redirect: (context, state) {
final isAuthenticated = ref.read(authViewModelProvider.notifier).isAuthenticated();
if (!isAuthenticated) {
if (state.fullPath != Routes.logIn &&
state.fullPath != Routes.signUp &&
state.fullPath != Routes.verification &&
state.fullPath != Routes.splash) {
return Routes.logIn;
}
}
return null;
},
routes: [
GoRoute(
path: Routes.splash,
builder: (context, state) => const SplashScreen(),
),
GoRoute(
path: Routes.logIn,
builder: (context, state) => const LogInScreen(),
),
GoRoute(
path: Routes.signUp,
pageBuilder: (context, state) => CustomTransitionPage(
key: state.pageKey,
child: const SignUpScreen(),
transitionsBuilder: _slideRightTransitionBuilder,
),
),
GoRoute(
path: Routes.verification,
pageBuilder: (context, state) => CustomTransitionPage(
key: state.pageKey,
child: const VerificationScreen(),
transitionsBuilder: _slideRightTransitionBuilder,
),
),
GoRoute(
path: '${Routes.socialAuthLoading}/:secretSessionId',
builder: (context, state) {
final secretSessionId = state.pathParameters['secretSessionId']!;
return SocialAuthLoadingScreen(secretSessionId: secretSessionId);
},
),
GoRoute(
path: home,
pageBuilder: (context, state) => CustomTransitionPage(
key: state.pageKey,
child: const HomeScreen(),
transitionsBuilder: _slideRightTransitionBuilder,
),
),
GoRoute(
path: Routes.inboxScreen,
builder: (context, state) => const InboxScreen(),
),
GoRoute(
path: Routes.addMyStory,
pageBuilder: (context, state) => CustomTransitionPage(
key: state.pageKey,
child: const AddMyStoryScreen(),
transitionsBuilder: _slideRightTransitionBuilder,
),
),
GoRoute(
path: Routes.showTakenStory,
pageBuilder: (context, state) => CustomTransitionPage(
key: state.pageKey,
child: ShowTakenStoryScreen(image: state.extra as File),
transitionsBuilder: _slideRightTransitionBuilder,
),
),
GoRoute(
path: Routes.storyScreen,
pageBuilder: (context, state) => CustomTransitionPage(
key: state.pageKey,
child: StoryScreen(
userId: (state.extra as Map<String, dynamic>)['userId'] as String,
showSeens: (state.extra as Map<String, dynamic>)['showSeens'] as bool,
),
transitionsBuilder: _slideRightTransitionBuilder,
),
),
GoRoute(
path: Routes.settings,
builder: (context, state) => const SettingsScreen(),
),
GoRoute(
path: Routes.changeNumber,
builder: (context, state) => const ChangeNumberScreen(),
),
GoRoute(
path: Routes.changeNumberForm,
builder: (context, state) => const ChangeNumberFormScreen(),
),
GoRoute(
path: Routes.profileInfo,
builder: (context, state) => const ProfileInfoScreen(),
),
GoRoute(
path: Routes.blockUser,
builder: (context, state) => const BlockUserScreen(),
),
GoRoute(
path: Routes.blockedUser,
builder: (context, state) => const BlockedUsersScreen(),
),
GoRoute(
path: Routes.userProfile,
builder: (context, state) => const UserProfileScreen(),
),
GoRoute(
path: Routes.privacySettings,
builder: (context, state) => const PrivacySettingsScreen(),
),
],
);

static Widget _slideRightTransitionBuilder(
context, animation, secondaryAnimation, child) {
const begin = Offset(1.0, 0.0);
const end = Offset.zero;
const curve = Curves.ease;

final tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
final offsetAnimation = animation.drive(tween);

return SlideTransition(
position: offsetAnimation,
child: child,
);
}
}
9 changes: 5 additions & 4 deletions lib/core/view/screen/splash_screen.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';

import 'package:telware_cross_platform/core/routes/routes.dart';
import 'package:telware_cross_platform/core/theme/dimensions.dart';
import 'package:telware_cross_platform/features/auth/view/screens/log_in_screen.dart';
import 'package:telware_cross_platform/features/auth/view_model/auth_state.dart';
import 'package:telware_cross_platform/features/auth/view_model/auth_view_model.dart';

Expand All @@ -18,10 +20,9 @@ class SplashScreen extends ConsumerWidget {
(_, state) {
// a callback function that takes the old and current state
if (state == AuthState.authorized) {
// todo(ahmed): navigate to the home screen
context.go(Routes.home);
} else if (state == AuthState.unauthorized) {
Navigator.pushNamedAndRemoveUntil(
context, LogInScreen.route, (_) => false);
context.go(Routes.logIn);
}
},
);
Expand Down
16 changes: 7 additions & 9 deletions lib/features/auth/repository/auth_remote_repository.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:io';

import 'package:fpdart/fpdart.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:dio/dio.dart';
import 'package:telware_cross_platform/core/models/user_model.dart';
import 'package:telware_cross_platform/core/providers/token_provider.dart';
import 'package:telware_cross_platform/core/constants/server_constants.dart';
import 'package:telware_cross_platform/core/models/app_error.dart';
import 'package:flutter/foundation.dart';
Expand Down Expand Up @@ -101,30 +101,28 @@ class AuthRemoteRepository {
return null;
}

Future<AppError?> getMe() async {
final token = _ref.read(tokenProvider);
Future<Either<AppError,UserModel>> getMe(String sessionId) async {
try {
final response = await _dio.get(
'/users/me',
options: Options(
headers: {HttpHeaders.authorizationHeader: 'Bearer $token'},
headers: {HttpHeaders.authorizationHeader: 'Bearer $sessionId'},
),
);

if (response.statusCode! > 200 || response.statusCode! < 200) {
final message = response.data['message'];
return AppError(message);
return Left(AppError(message));
}

final user = UserModel.fromMap(response.data['data']['user']);
_ref.read(authLocalRepositoryProvider).setUser(user);
return Right(user);
} on DioException catch (dioException) {
return handleDioException(dioException);
return Left(handleDioException(dioException));
} catch (error) {
debugPrint('Get user error:\n${error.toString()}');
return AppError('Failed to connect, check your internet connection.');
return Left(AppError('Failed to connect, check your internet connection.'));
}
return null;
}

Future<AppError?> logIn({
Expand Down
11 changes: 5 additions & 6 deletions lib/features/auth/view/screens/change_number_form_screen.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_shakemywidget/flutter_shakemywidget.dart';
import 'package:go_router/go_router.dart';
import 'package:phone_form_field/phone_form_field.dart';
import 'package:telware_cross_platform/core/routes/routes.dart';
import 'package:telware_cross_platform/core/theme/palette.dart';
import 'package:flutter/material.dart';
import 'package:telware_cross_platform/core/view/widget/responsive.dart';
import 'package:telware_cross_platform/features/auth/view/screens/verification_screen.dart';
import 'package:telware_cross_platform/features/auth/view/widget/auth_phone_number.dart';
import 'package:telware_cross_platform/features/auth/view/widget/title_element.dart';
import 'package:telware_cross_platform/core/theme/sizes.dart';
import 'package:telware_cross_platform/features/auth/view_model/auth_view_model.dart';
import 'package:telware_cross_platform/features/auth/view/widget/auth_floating_action_button.dart';
import 'package:vibration/vibration.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:telware_cross_platform/features/auth/view/widget/confirmation_dialog.dart';

class ChangeNumberFormScreen extends ConsumerStatefulWidget {
static const String route = '/change-number-form';
Expand Down Expand Up @@ -58,12 +57,12 @@ class _ChangeNumberFormScreen extends ConsumerState<ChangeNumberFormScreen> {
// ref.read(authViewModelProvider.notifier).updatePhoneNumber(
// phone: phoneController.value.international,
// );
Navigator.of(context).pop(); // to close the dialog
Navigator.pushReplacementNamed(context, VerificationScreen.route);
context.pop(); // to close the dialog
context.pushReplacement(Routes.verification);
}

void _onEdit() {
Navigator.of(context).pop();
context.pop();
}

void _handleSubmit() {
Expand Down
Loading

0 comments on commit 23dee9d

Please sign in to comment.