From e652dd0123889c3133c26d1f071c6ee9a8e18397 Mon Sep 17 00:00:00 2001
From: d-reader-luka <125265091+d-reader-luka@users.noreply.github.com>
Date: Thu, 18 Apr 2024 10:24:06 +0200
Subject: [PATCH] chore: consolidate auth flow
* some ui changes
* consolidate auth flow
allow sign up to be google and regular
refactor username validation
create new endpoint for register with google
* review changes
* change unwrapped text
---
assets/icons/double_tick.svg | 3 +
assets/icons/intro/splash_2.svg | 28 ---
lib/constants/constants.dart | 2 +
.../data/datasource/auth_remote_source.dart | 54 ++++-
.../repositories/auth_repository_impl.dart | 9 +-
.../domain/repositories/auth_repository.dart | 6 +-
.../providers/auth_providers.dart | 4 +-
.../providers/sign_in/sign_in_notifier.dart | 45 +++-
.../providers/sign_in/sign_in_notifier.g.dart | 2 +-
.../sign_up/sign_up_data_notifier.dart | 6 +-
.../sign_up/sign_up_data_notifier.g.dart | 2 +-
.../providers/sign_up/sign_up_notifier.dart | 36 +++
.../providers/sign_up/state/sign_up_data.dart | 12 +-
.../sign_up/state/sign_up_data.freezed.dart | 40 +++-
.../{step_3.dart => connect_wallet_step.dart} | 5 +-
...ep_2.dart => email_and_password_step.dart} | 33 +--
.../presentation/screens/sign_up/sign_up.dart | 210 ++++++++++++------
.../{step_1.dart => username_step.dart} | 105 ++++++---
...rification.dart => verification_step.dart} | 4 +-
.../providers/comic_providers.dart | 6 +-
.../home/presentation/screens/initial.dart | 6 +-
.../home/presentation/screens/splash.dart | 29 +--
.../widgets/tabs/favorites/favorites.dart | 2 +-
.../animations/mint_animation_screen.dart | 2 +-
.../animations/open_nft_animation_screen.dart | 5 +-
.../nft/presentation/screens/nft_details.dart | 19 +-
.../providers/change_network.g.dart | 2 +-
.../providers/security_and_privacy.g.dart | 2 +-
.../presentation/screens/profile/profile.dart | 20 +-
.../settings/presentation/screens/root.dart | 3 +-
.../providers/wallet_notifier.g.dart | 2 +-
.../data/remote/dio_network_service.dart | 8 +-
lib/shared/data/remote/network_service.dart | 1 +
.../solana/solana_transaction_notifier.dart | 2 +-
.../solana/solana_transaction_notifier.g.dart | 2 +-
lib/shared/utils/validation.dart | 12 +
.../layout/bottom_navigation_item_icon.dart | 19 +-
37 files changed, 469 insertions(+), 279 deletions(-)
create mode 100644 assets/icons/double_tick.svg
delete mode 100644 assets/icons/intro/splash_2.svg
rename lib/features/authentication/presentation/screens/sign_up/{step_3.dart => connect_wallet_step.dart} (97%)
rename lib/features/authentication/presentation/screens/sign_up/{step_2.dart => email_and_password_step.dart} (86%)
rename lib/features/authentication/presentation/screens/sign_up/{step_1.dart => username_step.dart} (58%)
rename lib/features/authentication/presentation/screens/sign_up/{step_2_verification.dart => verification_step.dart} (97%)
create mode 100644 lib/shared/utils/validation.dart
diff --git a/assets/icons/double_tick.svg b/assets/icons/double_tick.svg
new file mode 100644
index 00000000..24a1226a
--- /dev/null
+++ b/assets/icons/double_tick.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/intro/splash_2.svg b/assets/icons/intro/splash_2.svg
deleted file mode 100644
index 5c29b424..00000000
--- a/assets/icons/intro/splash_2.svg
+++ /dev/null
@@ -1,28 +0,0 @@
-
diff --git a/lib/constants/constants.dart b/lib/constants/constants.dart
index 68e93969..99398082 100644
--- a/lib/constants/constants.dart
+++ b/lib/constants/constants.dart
@@ -13,3 +13,5 @@ const String walkthroughAssetsPath = 'assets/images/walkthrough';
const String missingWalletAppText = 'Missing wallet application.';
const String powerSaveModeText = 'Cannot use wallet in the power save mode.';
const String successResult = 'OK';
+const String usernameCriteriaText =
+ 'Must be 3 to 20 characters long. Numbers, dashes and underscores are allowed.';
diff --git a/lib/features/authentication/data/datasource/auth_remote_source.dart b/lib/features/authentication/data/datasource/auth_remote_source.dart
index 8471fb93..5a8795a1 100644
--- a/lib/features/authentication/data/datasource/auth_remote_source.dart
+++ b/lib/features/authentication/data/datasource/auth_remote_source.dart
@@ -29,8 +29,12 @@ abstract class AuthDataSource {
Future disconnectWallet(String address);
Future refreshToken(String token);
- Future> googleSignIn(
+ Future> googleSignIn(
{required String accessToken});
+ Future> googleSignUp({
+ required String accessToken,
+ required String username,
+ });
}
class AuthRemoteDataSource implements AuthDataSource {
@@ -219,11 +223,11 @@ class AuthRemoteDataSource implements AuthDataSource {
}
@override
- Future> googleSignIn(
+ Future> googleSignIn(
{required String accessToken}) async {
try {
final googleSignInResult = await networkService.patch(
- '/auth/user/google-login',
+ '/auth/user/login-with-google',
headers: networkService.updateHeader(
{
'authorization': 'Google $accessToken',
@@ -235,6 +239,9 @@ class AuthRemoteDataSource implements AuthDataSource {
return Left(exception);
},
(response) {
+ if (response.data is String || response.data is bool) {
+ return Right(bool.tryParse(response.data) ?? false);
+ }
final authorizationResponse =
AuthorizationResponse.fromJson(response.data);
@@ -256,4 +263,45 @@ class AuthRemoteDataSource implements AuthDataSource {
);
}
}
+
+ @override
+ Future> googleSignUp(
+ {required String accessToken, required String username}) async {
+ try {
+ final googleSignUpResult =
+ await networkService.post('/auth/user/register-with-google',
+ headers: networkService.updateHeader(
+ {
+ 'authorization': 'Google $accessToken',
+ },
+ ),
+ data: {
+ "name": username,
+ });
+ return googleSignUpResult.fold(
+ (exception) {
+ return Left(exception);
+ },
+ (response) {
+ final authorizationResponse =
+ AuthorizationResponse.fromJson(response.data);
+
+ networkService.updateHeader(
+ {'Authorization': authorizationResponse.accessToken},
+ );
+
+ return Right(authorizationResponse);
+ },
+ );
+ } catch (exception) {
+ return Left(
+ AppException(
+ message: 'Unknown exception occurred',
+ statusCode: 500,
+ identifier:
+ '${exception.toString()}AuthRemoteDataSource.googleSignUp',
+ ),
+ );
+ }
+ }
}
diff --git a/lib/features/authentication/data/repositories/auth_repository_impl.dart b/lib/features/authentication/data/repositories/auth_repository_impl.dart
index 33a48192..f184378b 100644
--- a/lib/features/authentication/data/repositories/auth_repository_impl.dart
+++ b/lib/features/authentication/data/repositories/auth_repository_impl.dart
@@ -76,8 +76,15 @@ class AuthRepositoryImpl extends AuthRepository {
}
@override
- Future> googleSignIn(
+ Future> googleSignIn(
{required String accessToken}) {
return authDataSource.googleSignIn(accessToken: accessToken);
}
+
+ @override
+ Future> googleSignUp(
+ {required String accessToken, required String username}) {
+ return authDataSource.googleSignUp(
+ accessToken: accessToken, username: username);
+ }
}
diff --git a/lib/features/authentication/domain/repositories/auth_repository.dart b/lib/features/authentication/domain/repositories/auth_repository.dart
index 54a40de4..675154ef 100644
--- a/lib/features/authentication/domain/repositories/auth_repository.dart
+++ b/lib/features/authentication/domain/repositories/auth_repository.dart
@@ -29,7 +29,11 @@ abstract class AuthRepository {
required String address,
});
Future refreshToken(String refreshToken);
- Future> googleSignIn({
+ Future> googleSignIn({
required String accessToken,
});
+ Future> googleSignUp({
+ required String accessToken,
+ required String username,
+ });
}
diff --git a/lib/features/authentication/presentation/providers/auth_providers.dart b/lib/features/authentication/presentation/providers/auth_providers.dart
index 376d1e58..d48b4310 100644
--- a/lib/features/authentication/presentation/providers/auth_providers.dart
+++ b/lib/features/authentication/presentation/providers/auth_providers.dart
@@ -1,5 +1,6 @@
import 'package:d_reader_flutter/config/config.dart';
import 'package:d_reader_flutter/constants/constants.dart';
+import 'package:d_reader_flutter/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.dart';
import 'package:d_reader_flutter/features/user/domain/providers/user_provider.dart';
import 'package:d_reader_flutter/shared/data/local/local_store.dart';
import 'package:d_reader_flutter/routing/router.dart';
@@ -15,6 +16,7 @@ final logoutProvider = FutureProvider.autoDispose((ref) async {
ref.invalidate(environmentProvider);
ref.read(environmentProvider);
ref.invalidate(tabBarProvider);
+ ref.invalidate(signUpDataNotifierProvider);
await GoogleSignIn().signOut();
await Future.wait([
LocalStore.instance.delete(
@@ -36,5 +38,3 @@ final verifyEmailProvider = FutureProvider.autoDispose
return result.fold((exception) => exception.message, (_) => successResult);
});
-
-final isTOSSelected = StateProvider.autoDispose((ref) => false);
diff --git a/lib/features/authentication/presentation/providers/sign_in/sign_in_notifier.dart b/lib/features/authentication/presentation/providers/sign_in/sign_in_notifier.dart
index a582de00..f4d9bf6e 100644
--- a/lib/features/authentication/presentation/providers/sign_in/sign_in_notifier.dart
+++ b/lib/features/authentication/presentation/providers/sign_in/sign_in_notifier.dart
@@ -1,5 +1,7 @@
+import 'package:d_reader_flutter/features/authentication/domain/models/authorization_response.dart';
import 'package:d_reader_flutter/features/authentication/domain/providers/auth_provider.dart';
import 'package:d_reader_flutter/features/authentication/domain/repositories/auth_repository.dart';
+import 'package:d_reader_flutter/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.dart';
import 'package:d_reader_flutter/features/user/presentation/providers/user_providers.dart';
import 'package:d_reader_flutter/routing/router.dart';
import 'package:d_reader_flutter/shared/domain/providers/environment/environment_notifier.dart';
@@ -16,15 +18,19 @@ class SignInController extends _$SignInController {
@override
void build() {
- // TODO Think about using AutoState if it's possible/worth.
_authRepository = ref.watch(authRepositoryProvider);
}
Future googleSignIn({
- required Function() onSuccess,
+ required Function(bool isRegistered) onSuccess,
required Function(String message) onFail,
}) async {
final GoogleSignIn googleSignIn = GoogleSignIn();
+
+ if (await googleSignIn.isSignedIn()) {
+ await googleSignIn.signOut();
+ }
+
try {
final googleSignInResult = await googleSignIn.signIn();
final accessToken =
@@ -33,12 +39,35 @@ class SignInController extends _$SignInController {
return onFail('Failed to sign in with google.');
}
- return await signIn(
- nameOrEmail: '',
- password: '',
- onSuccess: onSuccess,
- onFail: onFail,
- googleAccessToken: accessToken,
+ ref.read(globalNotifierProvider.notifier).updateLoading(true);
+ final response =
+ await _authRepository.googleSignIn(accessToken: accessToken);
+
+ return response.fold(
+ (failure) {
+ ref.read(globalNotifierProvider.notifier).updateLoading(false);
+ onFail(failure.message);
+ },
+ (result) async {
+ if (result is bool) {
+ ref
+ .read(signUpDataNotifierProvider.notifier)
+ .updateGoogleAccessToken(accessToken);
+ ref.read(globalNotifierProvider.notifier).updateLoading(false);
+ return onSuccess(result);
+ } else if (result is AuthorizationResponse) {
+ ref.read(environmentProvider.notifier).updateEnvironmentState(
+ EnvironmentStateUpdateInput(
+ jwtToken: result.accessToken,
+ refreshToken: result.refreshToken,
+ ),
+ );
+ await ref.read(myUserProvider.future);
+ ref.read(globalNotifierProvider.notifier).updateLoading(false);
+ ref.read(authRouteProvider).login();
+ onSuccess(true);
+ }
+ },
);
} catch (exception) {
ref.read(globalNotifierProvider.notifier).updateLoading(false);
diff --git a/lib/features/authentication/presentation/providers/sign_in/sign_in_notifier.g.dart b/lib/features/authentication/presentation/providers/sign_in/sign_in_notifier.g.dart
index 2d272b7a..12e89666 100644
--- a/lib/features/authentication/presentation/providers/sign_in/sign_in_notifier.g.dart
+++ b/lib/features/authentication/presentation/providers/sign_in/sign_in_notifier.g.dart
@@ -6,7 +6,7 @@ part of 'sign_in_notifier.dart';
// RiverpodGenerator
// **************************************************************************
-String _$signInControllerHash() => r'3e73f4e10ea0553a85a391b9f8562bddbf3c4327';
+String _$signInControllerHash() => r'0b4a50956bed4e7a5c7a9fce17957f439e4db742';
/// See also [SignInController].
@ProviderFor(SignInController)
diff --git a/lib/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.dart b/lib/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.dart
index 762e20ae..1d1da5d4 100644
--- a/lib/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.dart
+++ b/lib/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.dart
@@ -14,6 +14,10 @@ class SignUpDataNotifier extends _$SignUpDataNotifier {
state = state.copyWith(username: username);
}
+ void updateGoogleAccessToken(String accessToken) {
+ state = state.copyWith(googleAccessToken: accessToken);
+ }
+
void updateEmailAndPassword({
required String email,
required String password,
@@ -21,7 +25,7 @@ class SignUpDataNotifier extends _$SignUpDataNotifier {
state = state.copyWith(email: email, password: password);
}
- void updateSucces(bool isSuccess) {
+ void updateSuccess(bool isSuccess) {
state = state.copyWith(
isSuccess: isSuccess,
email: '',
diff --git a/lib/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.g.dart b/lib/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.g.dart
index 74854ac2..11c8d31e 100644
--- a/lib/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.g.dart
+++ b/lib/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.g.dart
@@ -7,7 +7,7 @@ part of 'sign_up_data_notifier.dart';
// **************************************************************************
String _$signUpDataNotifierHash() =>
- r'fe1d5e2e19addea902619b27335dd8e2623e4ed2';
+ r'fea3fbb7bf4084a57dade097d2da86b8017a0425';
/// See also [SignUpDataNotifier].
@ProviderFor(SignUpDataNotifier)
diff --git a/lib/features/authentication/presentation/providers/sign_up/sign_up_notifier.dart b/lib/features/authentication/presentation/providers/sign_up/sign_up_notifier.dart
index a5bf1f50..512cf33e 100644
--- a/lib/features/authentication/presentation/providers/sign_up/sign_up_notifier.dart
+++ b/lib/features/authentication/presentation/providers/sign_up/sign_up_notifier.dart
@@ -82,4 +82,40 @@ class SignUpNotifier extends _$SignUpNotifier {
onSuccess();
});
}
+
+ Future googleSignUp({
+ required Function() onSuccess,
+ required Function(String message) onFail,
+ }) async {
+ ref.read(globalNotifierProvider.notifier).updateLoading(true);
+ try {
+ final SignUpData(:googleAccessToken, :username) =
+ ref.read(signUpDataNotifierProvider);
+ final response = await _authRepository.googleSignUp(
+ accessToken: googleAccessToken,
+ username: username,
+ );
+ return response.fold(
+ (failure) {
+ ref.read(globalNotifierProvider.notifier).updateLoading(false);
+ onFail(failure.message);
+ },
+ (authTokens) async {
+ ref.read(environmentProvider.notifier).updateEnvironmentState(
+ EnvironmentStateUpdateInput(
+ jwtToken: authTokens.accessToken,
+ refreshToken: authTokens.refreshToken,
+ ),
+ );
+ await ref.read(myUserProvider.future);
+ ref.read(globalNotifierProvider.notifier).updateLoading(false);
+ ref.read(authRouteProvider).login();
+ onSuccess();
+ },
+ );
+ } catch (exception) {
+ ref.read(globalNotifierProvider.notifier).updateLoading(false);
+ onFail(exception.toString());
+ }
+ }
}
diff --git a/lib/features/authentication/presentation/providers/sign_up/state/sign_up_data.dart b/lib/features/authentication/presentation/providers/sign_up/state/sign_up_data.dart
index 30ac0692..10dfecdc 100644
--- a/lib/features/authentication/presentation/providers/sign_up/state/sign_up_data.dart
+++ b/lib/features/authentication/presentation/providers/sign_up/state/sign_up_data.dart
@@ -4,10 +4,10 @@ part 'sign_up_data.freezed.dart';
@freezed
sealed class SignUpData with _$SignUpData {
- const factory SignUpData({
- @Default('') String email,
- @Default('') String password,
- @Default('') String username,
- @Default(false) bool isSuccess,
- }) = _SignUpData;
+ const factory SignUpData(
+ {@Default('') String email,
+ @Default('') String password,
+ @Default('') String username,
+ @Default('') String googleAccessToken,
+ @Default(false) bool isSuccess}) = _SignUpData;
}
diff --git a/lib/features/authentication/presentation/providers/sign_up/state/sign_up_data.freezed.dart b/lib/features/authentication/presentation/providers/sign_up/state/sign_up_data.freezed.dart
index 7142431c..b1912c57 100644
--- a/lib/features/authentication/presentation/providers/sign_up/state/sign_up_data.freezed.dart
+++ b/lib/features/authentication/presentation/providers/sign_up/state/sign_up_data.freezed.dart
@@ -19,6 +19,7 @@ mixin _$SignUpData {
String get email => throw _privateConstructorUsedError;
String get password => throw _privateConstructorUsedError;
String get username => throw _privateConstructorUsedError;
+ String get googleAccessToken => throw _privateConstructorUsedError;
bool get isSuccess => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@@ -32,7 +33,12 @@ abstract class $SignUpDataCopyWith<$Res> {
SignUpData value, $Res Function(SignUpData) then) =
_$SignUpDataCopyWithImpl<$Res, SignUpData>;
@useResult
- $Res call({String email, String password, String username, bool isSuccess});
+ $Res call(
+ {String email,
+ String password,
+ String username,
+ String googleAccessToken,
+ bool isSuccess});
}
/// @nodoc
@@ -51,6 +57,7 @@ class _$SignUpDataCopyWithImpl<$Res, $Val extends SignUpData>
Object? email = null,
Object? password = null,
Object? username = null,
+ Object? googleAccessToken = null,
Object? isSuccess = null,
}) {
return _then(_value.copyWith(
@@ -66,6 +73,10 @@ class _$SignUpDataCopyWithImpl<$Res, $Val extends SignUpData>
? _value.username
: username // ignore: cast_nullable_to_non_nullable
as String,
+ googleAccessToken: null == googleAccessToken
+ ? _value.googleAccessToken
+ : googleAccessToken // ignore: cast_nullable_to_non_nullable
+ as String,
isSuccess: null == isSuccess
? _value.isSuccess
: isSuccess // ignore: cast_nullable_to_non_nullable
@@ -82,7 +93,12 @@ abstract class _$$SignUpDataImplCopyWith<$Res>
__$$SignUpDataImplCopyWithImpl<$Res>;
@override
@useResult
- $Res call({String email, String password, String username, bool isSuccess});
+ $Res call(
+ {String email,
+ String password,
+ String username,
+ String googleAccessToken,
+ bool isSuccess});
}
/// @nodoc
@@ -99,6 +115,7 @@ class __$$SignUpDataImplCopyWithImpl<$Res>
Object? email = null,
Object? password = null,
Object? username = null,
+ Object? googleAccessToken = null,
Object? isSuccess = null,
}) {
return _then(_$SignUpDataImpl(
@@ -114,6 +131,10 @@ class __$$SignUpDataImplCopyWithImpl<$Res>
? _value.username
: username // ignore: cast_nullable_to_non_nullable
as String,
+ googleAccessToken: null == googleAccessToken
+ ? _value.googleAccessToken
+ : googleAccessToken // ignore: cast_nullable_to_non_nullable
+ as String,
isSuccess: null == isSuccess
? _value.isSuccess
: isSuccess // ignore: cast_nullable_to_non_nullable
@@ -129,6 +150,7 @@ class _$SignUpDataImpl implements _SignUpData {
{this.email = '',
this.password = '',
this.username = '',
+ this.googleAccessToken = '',
this.isSuccess = false});
@override
@@ -142,11 +164,14 @@ class _$SignUpDataImpl implements _SignUpData {
final String username;
@override
@JsonKey()
+ final String googleAccessToken;
+ @override
+ @JsonKey()
final bool isSuccess;
@override
String toString() {
- return 'SignUpData(email: $email, password: $password, username: $username, isSuccess: $isSuccess)';
+ return 'SignUpData(email: $email, password: $password, username: $username, googleAccessToken: $googleAccessToken, isSuccess: $isSuccess)';
}
@override
@@ -159,13 +184,15 @@ class _$SignUpDataImpl implements _SignUpData {
other.password == password) &&
(identical(other.username, username) ||
other.username == username) &&
+ (identical(other.googleAccessToken, googleAccessToken) ||
+ other.googleAccessToken == googleAccessToken) &&
(identical(other.isSuccess, isSuccess) ||
other.isSuccess == isSuccess));
}
@override
- int get hashCode =>
- Object.hash(runtimeType, email, password, username, isSuccess);
+ int get hashCode => Object.hash(
+ runtimeType, email, password, username, googleAccessToken, isSuccess);
@JsonKey(ignore: true)
@override
@@ -179,6 +206,7 @@ abstract class _SignUpData implements SignUpData {
{final String email,
final String password,
final String username,
+ final String googleAccessToken,
final bool isSuccess}) = _$SignUpDataImpl;
@override
@@ -188,6 +216,8 @@ abstract class _SignUpData implements SignUpData {
@override
String get username;
@override
+ String get googleAccessToken;
+ @override
bool get isSuccess;
@override
@JsonKey(ignore: true)
diff --git a/lib/features/authentication/presentation/screens/sign_up/step_3.dart b/lib/features/authentication/presentation/screens/sign_up/connect_wallet_step.dart
similarity index 97%
rename from lib/features/authentication/presentation/screens/sign_up/step_3.dart
rename to lib/features/authentication/presentation/screens/sign_up/connect_wallet_step.dart
index 20033b0f..65f4eca6 100644
--- a/lib/features/authentication/presentation/screens/sign_up/step_3.dart
+++ b/lib/features/authentication/presentation/screens/sign_up/connect_wallet_step.dart
@@ -13,8 +13,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
-class SignUpStep3 extends ConsumerWidget {
- const SignUpStep3({super.key});
+class SignUpConnectWalletStep extends ConsumerWidget {
+ const SignUpConnectWalletStep({super.key});
Future _handleConnectWallet(WidgetRef ref, BuildContext context) async {
await ref.read(walletControllerProvider.notifier).connectWallet(
@@ -49,6 +49,7 @@ class SignUpStep3 extends ConsumerWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SvgPicture.asset(
'${Config.introAssetsPath}/wallet.svg',
diff --git a/lib/features/authentication/presentation/screens/sign_up/step_2.dart b/lib/features/authentication/presentation/screens/sign_up/email_and_password_step.dart
similarity index 86%
rename from lib/features/authentication/presentation/screens/sign_up/step_2.dart
rename to lib/features/authentication/presentation/screens/sign_up/email_and_password_step.dart
index b9dc8c7f..c76eaa7d 100644
--- a/lib/features/authentication/presentation/screens/sign_up/step_2.dart
+++ b/lib/features/authentication/presentation/screens/sign_up/email_and_password_step.dart
@@ -13,20 +13,22 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
-class SignUpStep2 extends ConsumerStatefulWidget {
+class SignUpEmailAndPasswordStep extends ConsumerStatefulWidget {
final Function() onSuccess;
final Function(String text) onFail;
- const SignUpStep2({
+ const SignUpEmailAndPasswordStep({
super.key,
required this.onSuccess,
required this.onFail,
});
@override
- ConsumerState createState() => _SignUpStep1State();
+ ConsumerState createState() =>
+ _SignUpEmailAndPasswordStepState();
}
-class _SignUpStep1State extends ConsumerState {
+class _SignUpEmailAndPasswordStepState
+ extends ConsumerState {
final GlobalKey _step2FormKey = GlobalKey();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
@@ -137,29 +139,6 @@ class _SignUpStep1State extends ConsumerState {
color: ColorPalette.greyscale200,
),
),
- const SizedBox(
- height: 4,
- ),
- RichText(
- text: const TextSpan(
- text: 'By clicking on confirm you accept our ',
- style: TextStyle(
- fontSize: 14,
- fontWeight: FontWeight.w500,
- color: ColorPalette.greyscale200,
- ),
- children: [
- TextSpan(
- text: 'Privacy Policy',
- style: TextStyle(
- fontSize: 14,
- fontWeight: FontWeight.w500,
- color: ColorPalette.dReaderYellow100,
- ),
- ),
- ],
- ),
- ),
const SizedBox(
height: 48,
),
diff --git a/lib/features/authentication/presentation/screens/sign_up/sign_up.dart b/lib/features/authentication/presentation/screens/sign_up/sign_up.dart
index a0ad6d2f..ea513a24 100644
--- a/lib/features/authentication/presentation/screens/sign_up/sign_up.dart
+++ b/lib/features/authentication/presentation/screens/sign_up/sign_up.dart
@@ -1,9 +1,10 @@
import 'package:d_reader_flutter/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.dart';
+import 'package:d_reader_flutter/features/authentication/presentation/providers/sign_up/sign_up_notifier.dart';
import 'package:d_reader_flutter/features/authentication/presentation/providers/sign_up/sign_up_providers.dart';
-import 'package:d_reader_flutter/features/authentication/presentation/screens/sign_up/step_1.dart';
-import 'package:d_reader_flutter/features/authentication/presentation/screens/sign_up/step_2.dart';
-import 'package:d_reader_flutter/features/authentication/presentation/screens/sign_up/step_2_verification.dart';
-import 'package:d_reader_flutter/features/authentication/presentation/screens/sign_up/step_3.dart';
+import 'package:d_reader_flutter/features/authentication/presentation/screens/sign_up/username_step.dart';
+import 'package:d_reader_flutter/features/authentication/presentation/screens/sign_up/email_and_password_step.dart';
+import 'package:d_reader_flutter/features/authentication/presentation/screens/sign_up/verification_step.dart';
+import 'package:d_reader_flutter/features/authentication/presentation/screens/sign_up/connect_wallet_step.dart';
import 'package:d_reader_flutter/shared/theme/app_colors.dart';
import 'package:d_reader_flutter/shared/utils/show_snackbar.dart';
import 'package:flutter/material.dart';
@@ -47,62 +48,119 @@ class _SignUpScreenState extends ConsumerState {
child: const Heading(),
),
),
- body: Consumer(
- builder: (context, ref, child) {
- return PageView(
- controller: _pageController,
- physics: ref.watch(signUpPageProvider) > 2
- ? const NeverScrollableScrollPhysics()
- : null,
- onPageChanged: (value) {
- ref.read(signUpPageProvider.notifier).update((state) => value);
+ body: ref.watch(signUpDataNotifierProvider).googleAccessToken.isNotEmpty
+ ? GoogleSignUpForm(pageController: _pageController)
+ : RegularSignUpForm(pageController: _pageController),
+ ),
+ );
+ }
+}
+
+class RegularSignUpForm extends ConsumerWidget {
+ final PageController pageController;
+ const RegularSignUpForm({
+ super.key,
+ required this.pageController,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ return PageView(
+ controller: pageController,
+ physics: ref.watch(signUpPageProvider) > 2
+ ? const NeverScrollableScrollPhysics()
+ : null,
+ onPageChanged: (value) {
+ ref.read(signUpPageProvider.notifier).update((state) => value);
+ },
+ children: [
+ SignUpUsernameStep(
+ onSuccess: () {
+ pageController.animateToPage(
+ 1,
+ duration: const Duration(milliseconds: 300),
+ curve: Curves.easeIn,
+ );
+ },
+ ),
+ SignUpEmailAndPasswordStep(
+ onSuccess: () {
+ ref.read(signUpDataNotifierProvider.notifier).updateSuccess(true);
+ pageController.animateToPage(
+ 2,
+ duration: const Duration(milliseconds: 300),
+ curve: Curves.easeIn,
+ );
+ },
+ onFail: (text) {
+ showSnackBar(
+ context: context,
+ text: text,
+ backgroundColor: ColorPalette.dReaderRed,
+ );
+ },
+ ),
+ if (ref.watch(signUpDataNotifierProvider).isSuccess) ...[
+ SignUpVerificationStep(
+ handleNext: () {
+ pageController.animateToPage(
+ 3,
+ duration: const Duration(milliseconds: 300),
+ curve: Curves.easeIn,
+ );
+ },
+ ),
+ const SignUpConnectWalletStep(),
+ ],
+ ],
+ );
+ }
+}
+
+class GoogleSignUpForm extends ConsumerWidget {
+ final PageController pageController;
+ const GoogleSignUpForm({
+ super.key,
+ required this.pageController,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ return PageView(
+ controller: pageController,
+ physics: const NeverScrollableScrollPhysics(),
+ onPageChanged: (value) {
+ ref.read(signUpPageProvider.notifier).update((state) => value);
+ },
+ children: [
+ SignUpUsernameStep(
+ overrideNext: () {
+ ref.read(signUpNotifierProvider.notifier).googleSignUp(
+ onSuccess: () {
+ ref
+ .read(signUpDataNotifierProvider.notifier)
+ .updateSuccess(true);
+ pageController.animateToPage(
+ 1,
+ duration: const Duration(milliseconds: 300),
+ curve: Curves.easeIn,
+ );
+ },
+ onFail: (message) {
+ showSnackBar(
+ context: context,
+ text: message,
+ backgroundColor: ColorPalette.dReaderRed,
+ );
},
- children: [
- SignUpStep1(
- onSuccess: () {
- _pageController.animateToPage(
- 1,
- duration: const Duration(milliseconds: 300),
- curve: Curves.easeIn,
- );
- },
- ),
- SignUpStep2(
- onSuccess: () {
- ref
- .read(signUpDataNotifierProvider.notifier)
- .updateSucces(true);
- _pageController.animateToPage(
- 2,
- duration: const Duration(milliseconds: 300),
- curve: Curves.easeIn,
- );
- },
- onFail: (text) {
- showSnackBar(
- context: context,
- text: text,
- backgroundColor: ColorPalette.dReaderRed,
- );
- },
- ),
- if (ref.watch(signUpDataNotifierProvider).isSuccess) ...[
- SignUpStep2Verification(
- handleNext: () {
- _pageController.animateToPage(
- 3,
- duration: const Duration(milliseconds: 300),
- curve: Curves.easeIn,
- );
- },
- ),
- const SignUpStep3(),
- ],
- ],
);
},
+ onSuccess: () {},
),
- ),
+ if (ref.watch(signUpDataNotifierProvider).isSuccess) ...[
+ const SignUpConnectWalletStep(),
+ ],
+ ],
);
}
}
@@ -129,24 +187,32 @@ class Heading extends ConsumerWidget {
BlendMode.srcIn,
),
),
- HeadingItem(
- step: 2,
- title: 'Email & pass',
- color: ref.watch(signUpPageProvider) > 0
- ? Colors.white
- : ColorPalette.greyscale200,
- ),
- SvgPicture.asset(
- 'assets/icons/arrow_right.svg',
- height: 16,
- width: 16,
- colorFilter: const ColorFilter.mode(
- ColorPalette.greyscale200,
- BlendMode.srcIn,
+ if (ref
+ .watch(signUpDataNotifierProvider)
+ .googleAccessToken
+ .isEmpty) ...[
+ HeadingItem(
+ step: 2,
+ title: 'Email & pass',
+ color: ref.watch(signUpPageProvider) > 0
+ ? Colors.white
+ : ColorPalette.greyscale200,
),
- ),
+ SvgPicture.asset(
+ 'assets/icons/arrow_right.svg',
+ height: 16,
+ width: 16,
+ colorFilter: const ColorFilter.mode(
+ ColorPalette.greyscale200,
+ BlendMode.srcIn,
+ ),
+ ),
+ ],
HeadingItem(
- step: 3,
+ step:
+ ref.watch(signUpDataNotifierProvider).googleAccessToken.isNotEmpty
+ ? 2
+ : 3,
title: 'Wallet',
color: ref.watch(signUpPageProvider) > 2
? Colors.white
diff --git a/lib/features/authentication/presentation/screens/sign_up/step_1.dart b/lib/features/authentication/presentation/screens/sign_up/username_step.dart
similarity index 58%
rename from lib/features/authentication/presentation/screens/sign_up/step_1.dart
rename to lib/features/authentication/presentation/screens/sign_up/username_step.dart
index 4d49823e..96a1fb1c 100644
--- a/lib/features/authentication/presentation/screens/sign_up/step_1.dart
+++ b/lib/features/authentication/presentation/screens/sign_up/username_step.dart
@@ -1,32 +1,35 @@
import 'package:d_reader_flutter/config/config.dart';
import 'package:d_reader_flutter/constants/constants.dart';
-import 'package:d_reader_flutter/features/authentication/presentation/providers/auth_providers.dart';
import 'package:d_reader_flutter/features/authentication/presentation/providers/sign_up/sign_up_data_notifier.dart';
import 'package:d_reader_flutter/features/authentication/presentation/providers/sign_up/sign_up_notifier.dart';
import 'package:d_reader_flutter/shared/presentations/providers/global/global_notifier.dart';
import 'package:d_reader_flutter/shared/theme/app_colors.dart';
import 'package:d_reader_flutter/shared/utils/show_snackbar.dart';
import 'package:d_reader_flutter/shared/utils/url_utils.dart';
+import 'package:d_reader_flutter/shared/utils/validation.dart';
import 'package:d_reader_flutter/shared/widgets/buttons/custom_text_button.dart';
-import 'package:d_reader_flutter/shared/widgets/checkbox/custom_labeled_checkbox.dart';
import 'package:d_reader_flutter/shared/widgets/textfields/text_field.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:url_launcher/url_launcher.dart';
-class SignUpStep1 extends ConsumerStatefulWidget {
+class SignUpUsernameStep extends ConsumerStatefulWidget {
final Function() onSuccess;
- const SignUpStep1({
+ final void Function()? overrideNext;
+ const SignUpUsernameStep({
super.key,
required this.onSuccess,
+ this.overrideNext,
});
@override
- ConsumerState createState() => _SignUpStep1State();
+ ConsumerState createState() =>
+ _SignUpUsernameStepState();
}
-class _SignUpStep1State extends ConsumerState {
+class _SignUpUsernameStepState extends ConsumerState {
final GlobalKey _usernameFormKey = GlobalKey();
final TextEditingController _usernameController = TextEditingController();
@@ -44,6 +47,12 @@ class _SignUpStep1State extends ConsumerState {
void _handleNext() {
if (_usernameFormKey.currentState!.validate()) {
+ if (widget.overrideNext != null) {
+ ref.read(signUpDataNotifierProvider.notifier).updateUsername(
+ _usernameController.text.trim(),
+ );
+ return widget.overrideNext!();
+ }
ref.read(signUpNotifierProvider.notifier).handleSignUpStep1(
username: _usernameController.text.trim(),
onSuccess: widget.onSuccess,
@@ -96,19 +105,10 @@ class _SignUpStep1State extends ConsumerState {
labelText: 'Username',
hintText: 'e.g Bun-Bun',
controller: _usernameController,
- onValidate: (value) {
- if (value == null || value.isEmpty) {
- return 'Field cannot be empty.';
- } else if (value.length < 2 || value.length > 20) {
- return 'Username must be 3 to 20 characters long.';
- } else if (!usernameRegex.hasMatch(value)) {
- return 'Letters, numbers, hyphens and dashes are allowed.';
- }
- return null;
- },
+ onValidate: usernameValidation,
),
const Text(
- 'Must be 2 to 20 characters long. Letters, numbers and dashes are allowed.',
+ usernameCriteriaText,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
@@ -118,30 +118,62 @@ class _SignUpStep1State extends ConsumerState {
const SizedBox(
height: 24,
),
- CustomLabeledCheckbox(
- isChecked: ref.watch(isTOSSelected),
- onChange: () {
- ref.read(isTOSSelected.notifier).update((state) => !state);
- },
- label: RichText(
- text: TextSpan(
- text: 'I have read and agree to the ',
- style: Theme.of(context).textTheme.bodySmall,
- children: [
- TextSpan(
- text: 'Terms of Service',
- recognizer: TapGestureRecognizer()
- ..onTap = () {
- openUrl(Config.privacyPolicyUrl);
- },
+ Row(
+ children: [
+ SvgPicture.asset(
+ 'assets/icons/double_tick.svg',
+ ),
+ const SizedBox(
+ width: 8,
+ ),
+ Expanded(
+ child: RichText(
+ text: TextSpan(
+ text:
+ 'By creating an account I confirm I read and agree to the ',
style: Theme.of(context)
.textTheme
.bodySmall
- ?.copyWith(color: ColorPalette.dReaderYellow100),
+ ?.copyWith(color: ColorPalette.greyscale200),
+ children: [
+ TextSpan(
+ text: 'Terms of Service ',
+ recognizer: TapGestureRecognizer()
+ ..onTap = () {
+ openUrl(Config.privacyPolicyUrl,
+ LaunchMode.inAppWebView);
+ },
+ style: Theme.of(context)
+ .textTheme
+ .bodySmall
+ ?.copyWith(
+ color: ColorPalette.dReaderYellow100),
+ ),
+ TextSpan(
+ text: '& ',
+ style: Theme.of(context)
+ .textTheme
+ .bodySmall
+ ?.copyWith(color: ColorPalette.greyscale200),
+ ),
+ TextSpan(
+ text: 'Privacy Policy',
+ recognizer: TapGestureRecognizer()
+ ..onTap = () {
+ openUrl(Config.privacyPolicyUrl,
+ LaunchMode.inAppWebView);
+ },
+ style: Theme.of(context)
+ .textTheme
+ .bodySmall
+ ?.copyWith(
+ color: ColorPalette.dReaderYellow100),
+ ),
+ ],
),
- ],
+ ),
),
- ),
+ ],
),
const SizedBox(
height: 48,
@@ -149,7 +181,6 @@ class _SignUpStep1State extends ConsumerState {
CustomTextButton(
isLoading: ref.watch(globalNotifierProvider).isLoading,
padding: const EdgeInsets.all(0),
- isDisabled: !ref.watch(isTOSSelected),
size: const Size(
double.infinity,
50,
diff --git a/lib/features/authentication/presentation/screens/sign_up/step_2_verification.dart b/lib/features/authentication/presentation/screens/sign_up/verification_step.dart
similarity index 97%
rename from lib/features/authentication/presentation/screens/sign_up/step_2_verification.dart
rename to lib/features/authentication/presentation/screens/sign_up/verification_step.dart
index c07d649d..7252cdc4 100644
--- a/lib/features/authentication/presentation/screens/sign_up/step_2_verification.dart
+++ b/lib/features/authentication/presentation/screens/sign_up/verification_step.dart
@@ -6,9 +6,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
-class SignUpStep2Verification extends StatelessWidget {
+class SignUpVerificationStep extends StatelessWidget {
final Function() handleNext;
- const SignUpStep2Verification({
+ const SignUpVerificationStep({
super.key,
required this.handleNext,
});
diff --git a/lib/features/comic/presentation/providers/comic_providers.dart b/lib/features/comic/presentation/providers/comic_providers.dart
index 9085a8f2..82c6b128 100644
--- a/lib/features/comic/presentation/providers/comic_providers.dart
+++ b/lib/features/comic/presentation/providers/comic_providers.dart
@@ -46,10 +46,8 @@ final paginatedComicsProvider = StateNotifierProvider.family<
final comicSlugProvider =
FutureProvider.autoDispose.family((ref, slug) async {
- return ref
- .read(comicRepositoryProvider)
- .getComic(slug)
- .then((result) => result.fold((exception) => null, (data) => data));
+ return ref.read(comicRepositoryProvider).getComic(slug).then(
+ (result) => result.fold((exception) => throw exception, (data) => data));
});
final updateComicFavouriteProvider =
diff --git a/lib/features/home/presentation/screens/initial.dart b/lib/features/home/presentation/screens/initial.dart
index 7a8a4204..bafa2ecd 100644
--- a/lib/features/home/presentation/screens/initial.dart
+++ b/lib/features/home/presentation/screens/initial.dart
@@ -127,10 +127,12 @@ class InitialIntroScreen extends StatelessWidget {
ref
.read(signInControllerProvider.notifier)
.googleSignIn(
- onSuccess: () {
+ onSuccess: (bool isRegistered) {
nextScreenCloseOthers(
context: context,
- path: RoutePath.home,
+ path: isRegistered
+ ? RoutePath.home
+ : RoutePath.signUp,
);
},
onFail: (String message) {
diff --git a/lib/features/home/presentation/screens/splash.dart b/lib/features/home/presentation/screens/splash.dart
index 95085da7..e6c0128b 100644
--- a/lib/features/home/presentation/screens/splash.dart
+++ b/lib/features/home/presentation/screens/splash.dart
@@ -56,32 +56,9 @@ class _SplashViewState extends State with TickerProviderStateMixin {
child: Center(
child: FadeTransition(
opacity: _fadeInFadeOut,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- const SizedBox(),
- SvgPicture.asset(
- Config.whiteLogoPath,
- height: 50,
- ),
- Container(
- padding: const EdgeInsets.all(8),
- decoration: BoxDecoration(
- border: Border.all(
- color: ColorPalette.dReaderYellow100,
- ),
- borderRadius: BorderRadius.circular(
- 8,
- ),
- ),
- child: Text(
- 'beta version',
- style: Theme.of(context).textTheme.bodySmall?.copyWith(
- color: ColorPalette.dReaderYellow100,
- ),
- ),
- ),
- ],
+ child: SvgPicture.asset(
+ Config.whiteLogoPath,
+ height: 50,
),
),
),
diff --git a/lib/features/library/presentation/widgets/tabs/favorites/favorites.dart b/lib/features/library/presentation/widgets/tabs/favorites/favorites.dart
index 06d9edfc..6b16a007 100644
--- a/lib/features/library/presentation/widgets/tabs/favorites/favorites.dart
+++ b/lib/features/library/presentation/widgets/tabs/favorites/favorites.dart
@@ -36,7 +36,7 @@ class FavoritesTab extends ConsumerWidget {
height: 8,
),
const Text(
- 'Favorite comic first',
+ 'Favoritize comic first',
style: TextStyle(
fontSize: 14,
letterSpacing: 0.2,
diff --git a/lib/features/nft/presentation/screens/animations/mint_animation_screen.dart b/lib/features/nft/presentation/screens/animations/mint_animation_screen.dart
index 63f8b4da..60ec1d12 100644
--- a/lib/features/nft/presentation/screens/animations/mint_animation_screen.dart
+++ b/lib/features/nft/presentation/screens/animations/mint_animation_screen.dart
@@ -358,7 +358,7 @@ class _DoneMintingAnimationState extends State
final bool isLoading =
ref.watch(globalNotifierProvider).isLoading;
return CustomTextButton(
- backgroundColor: ColorPalette.dReaderGreen,
+ backgroundColor: ColorPalette.dReaderYellow100,
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 8,
diff --git a/lib/features/nft/presentation/screens/animations/open_nft_animation_screen.dart b/lib/features/nft/presentation/screens/animations/open_nft_animation_screen.dart
index 4fe3cc0a..a7db73c1 100644
--- a/lib/features/nft/presentation/screens/animations/open_nft_animation_screen.dart
+++ b/lib/features/nft/presentation/screens/animations/open_nft_animation_screen.dart
@@ -47,7 +47,8 @@ class _OpenNftAnimationState extends ConsumerState
onSuccess: (String nftAddress) {
nextScreenReplace(
context: context,
- path: '${RoutePath.nftDetails}/$nftAddress',
+ path:
+ '${RoutePath.nftDetails}/$nftAddress', // TODO open eReader
homeSubRoute: true,
);
},
@@ -56,7 +57,7 @@ class _OpenNftAnimationState extends ConsumerState
context.pop();
showSnackBar(
context: context,
- text: 'Failed to open.',
+ text: 'Failed to unwrap.',
backgroundColor: ColorPalette.dReaderRed,
);
return;
diff --git a/lib/features/nft/presentation/screens/nft_details.dart b/lib/features/nft/presentation/screens/nft_details.dart
index a75ecfe8..7f3d0be7 100644
--- a/lib/features/nft/presentation/screens/nft_details.dart
+++ b/lib/features/nft/presentation/screens/nft_details.dart
@@ -53,12 +53,6 @@ class NftDetails extends ConsumerWidget {
text: openResponse,
);
}
- showSnackBar(
- context: context,
- text: 'NFT Unwrapped successfully',
- backgroundColor: ColorPalette.dReaderGreen,
- );
-
ref.invalidate(lastProcessedNftProvider);
ref.invalidate(nftsProvider);
ref.invalidate(nftProvider);
@@ -66,6 +60,11 @@ class NftDetails extends ConsumerWidget {
ref.invalidate(ownedIssuesAsyncProvider);
ref.invalidate(comicIssuePagesProvider);
ref.invalidate(comicIssueDetailsProvider);
+ showSnackBar(
+ context: context,
+ text: 'Comic unwrapped successfully',
+ backgroundColor: ColorPalette.dReaderGreen,
+ );
}
@override
@@ -178,10 +177,10 @@ class NftDetails extends ConsumerWidget {
),
Expanded(
child: Button(
- borderColor: ColorPalette.dReaderGreen,
+ borderColor: ColorPalette.dReaderYellow100,
isLoading:
ref.watch(globalNotifierProvider).isLoading,
- loadingColor: ColorPalette.dReaderGreen,
+ loadingColor: ColorPalette.dReaderYellow100,
onPressed: () async {
if (nft.isUsed) {
return nextScreenPush(
@@ -222,9 +221,9 @@ class NftDetails extends ConsumerWidget {
);
},
child: Text(
- nft.isUsed ? 'Read' : 'Open',
+ nft.isUsed ? 'Read' : 'Unwrap',
style: textTheme.titleMedium?.copyWith(
- color: ColorPalette.dReaderGreen,
+ color: ColorPalette.dReaderYellow100,
),
),
),
diff --git a/lib/features/settings/presentation/providers/change_network.g.dart b/lib/features/settings/presentation/providers/change_network.g.dart
index a9c0a827..a3776e79 100644
--- a/lib/features/settings/presentation/providers/change_network.g.dart
+++ b/lib/features/settings/presentation/providers/change_network.g.dart
@@ -7,7 +7,7 @@ part of 'change_network.dart';
// **************************************************************************
String _$changeNetworkControllerHash() =>
- r'6ad2b48337d9ea11a658b001f027337a89635604';
+ r'ff62d7ea95d5b8268c597a092c9c9d2aeb036e6f';
/// See also [ChangeNetworkController].
@ProviderFor(ChangeNetworkController)
diff --git a/lib/features/settings/presentation/providers/security_and_privacy.g.dart b/lib/features/settings/presentation/providers/security_and_privacy.g.dart
index 9acd1184..c46727ab 100644
--- a/lib/features/settings/presentation/providers/security_and_privacy.g.dart
+++ b/lib/features/settings/presentation/providers/security_and_privacy.g.dart
@@ -7,7 +7,7 @@ part of 'security_and_privacy.dart';
// **************************************************************************
String _$securityAndPrivacyControllerHash() =>
- r'15833a16a90c400bbdf64d839353589ee1a3f2af';
+ r'b0d6c0f111ba747d649d5e04c8598eac56f58c22';
/// See also [SecurityAndPrivacyController].
@ProviderFor(SecurityAndPrivacyController)
diff --git a/lib/features/settings/presentation/screens/profile/profile.dart b/lib/features/settings/presentation/screens/profile/profile.dart
index fbf64431..9b8f4b31 100644
--- a/lib/features/settings/presentation/screens/profile/profile.dart
+++ b/lib/features/settings/presentation/screens/profile/profile.dart
@@ -13,6 +13,7 @@ import 'package:d_reader_flutter/shared/presentations/providers/global/global_pr
import 'package:d_reader_flutter/shared/theme/app_colors.dart';
import 'package:d_reader_flutter/shared/utils/screen_navigation.dart';
import 'package:d_reader_flutter/shared/utils/show_snackbar.dart';
+import 'package:d_reader_flutter/shared/utils/validation.dart';
import 'package:d_reader_flutter/shared/widgets/buttons/custom_text_button.dart';
import 'package:d_reader_flutter/shared/widgets/textfields/text_field.dart';
import 'package:d_reader_flutter/features/settings/presentation/widgets/list_tile.dart';
@@ -25,19 +26,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
class ProfileView extends HookConsumerWidget {
const ProfileView({super.key});
- String? _validateUsername({required String? value, required WidgetRef ref}) {
- if (value == null || value.isEmpty) {
- return "Please enter username.";
- } else if (value.length > 20) {
- return "Must be less than 20 characters.";
- } else if (value.length < 2) {
- return "Must be greater than 2 characters.";
- } else if (!usernameRegex.hasMatch(value)) {
- return 'Letters, numbers, hyphens and dashes are allowed.';
- }
- return null;
- }
-
@override
Widget build(BuildContext context, WidgetRef ref) {
final provider = ref.watch(myUserProvider);
@@ -160,16 +148,14 @@ class ProfileView extends HookConsumerWidget {
labelText: 'Username',
defaultValue:
user.name.isNotEmpty ? user.name : null,
- onValidate: (value) {
- return _validateUsername(value: value, ref: ref);
- },
+ onValidate: usernameValidation,
onChange: (String value) {
ref.read(usernameTextProvider.notifier).state =
value;
},
),
const Text(
- 'Must be 2 to 20 characters long. Letters, numbers and dashes are allowed.',
+ usernameCriteriaText,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
diff --git a/lib/features/settings/presentation/screens/root.dart b/lib/features/settings/presentation/screens/root.dart
index 05c32665..eb1879bc 100644
--- a/lib/features/settings/presentation/screens/root.dart
+++ b/lib/features/settings/presentation/screens/root.dart
@@ -7,6 +7,7 @@ import 'package:d_reader_flutter/shared/utils/screen_navigation.dart';
import 'package:d_reader_flutter/features/settings/presentation/widgets/list_tile.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:url_launcher/url_launcher.dart';
class SettingsRootView extends StatelessWidget {
const SettingsRootView({
@@ -115,7 +116,7 @@ class SettingsRootView extends StatelessWidget {
title: 'FAQ',
leadingPath: '${Config.settingsAssetsPath}/light/info_square.svg',
onTap: () {
- openUrl('https://dreader.app/faq');
+ openUrl('https://dreader.app/faq', LaunchMode.inAppWebView);
},
),
],
diff --git a/lib/features/wallet/presentation/providers/wallet_notifier.g.dart b/lib/features/wallet/presentation/providers/wallet_notifier.g.dart
index e99126c9..1a0b8b1b 100644
--- a/lib/features/wallet/presentation/providers/wallet_notifier.g.dart
+++ b/lib/features/wallet/presentation/providers/wallet_notifier.g.dart
@@ -6,7 +6,7 @@ part of 'wallet_notifier.dart';
// RiverpodGenerator
// **************************************************************************
-String _$walletControllerHash() => r'db80c8d40f427bcd29bc3d4ba02c2871216148c5';
+String _$walletControllerHash() => r'56e743ffbc0bc1da3c9f6a67760a43e0baf329f5';
/// See also [WalletController].
@ProviderFor(WalletController)
diff --git a/lib/shared/data/remote/dio_network_service.dart b/lib/shared/data/remote/dio_network_service.dart
index 74be6f42..5e86de51 100644
--- a/lib/shared/data/remote/dio_network_service.dart
+++ b/lib/shared/data/remote/dio_network_service.dart
@@ -42,14 +42,18 @@ class DioNetworkService extends NetworkService with ExceptionHandlerMixin {
}
@override
- Future> post(String endpoint,
- {Map? data}) {
+ Future> post(
+ String endpoint, {
+ Map? data,
+ Map? headers,
+ }) {
return handleExceptionWrapper(
endpoint: endpoint,
handler: () {
return dio.post(
endpoint,
data: data,
+ options: Options(headers: headers),
);
},
);
diff --git a/lib/shared/data/remote/network_service.dart b/lib/shared/data/remote/network_service.dart
index a4f643d9..9a9d2967 100644
--- a/lib/shared/data/remote/network_service.dart
+++ b/lib/shared/data/remote/network_service.dart
@@ -18,6 +18,7 @@ abstract class NetworkService {
Future> post(
String endpoint, {
Map? data,
+ Map? headers,
});
Future> patch(
diff --git a/lib/shared/domain/providers/solana/solana_transaction_notifier.dart b/lib/shared/domain/providers/solana/solana_transaction_notifier.dart
index 9a9d9e3c..d82fabe0 100644
--- a/lib/shared/domain/providers/solana/solana_transaction_notifier.dart
+++ b/lib/shared/domain/providers/solana/solana_transaction_notifier.dart
@@ -524,7 +524,7 @@ class SolanaTransactionNotifier extends _$SolanaTransactionNotifier {
AppException(
identifier: 'SolanaTransactionNotifier.list',
statusCode: 500,
- message: 'Failed to open nft',
+ message: 'Failed to unwrap nft',
),
);
}
diff --git a/lib/shared/domain/providers/solana/solana_transaction_notifier.g.dart b/lib/shared/domain/providers/solana/solana_transaction_notifier.g.dart
index 26a830ec..3c6becff 100644
--- a/lib/shared/domain/providers/solana/solana_transaction_notifier.g.dart
+++ b/lib/shared/domain/providers/solana/solana_transaction_notifier.g.dart
@@ -7,7 +7,7 @@ part of 'solana_transaction_notifier.dart';
// **************************************************************************
String _$solanaTransactionNotifierHash() =>
- r'6a91d51e015b57d2efabb80d806f09fe19f8cef9';
+ r'3a9235eb7a03fc64c870318f09d5c3d6d42076ff';
/// See also [SolanaTransactionNotifier].
@ProviderFor(SolanaTransactionNotifier)
diff --git a/lib/shared/utils/validation.dart b/lib/shared/utils/validation.dart
new file mode 100644
index 00000000..ce65cc29
--- /dev/null
+++ b/lib/shared/utils/validation.dart
@@ -0,0 +1,12 @@
+import 'package:d_reader_flutter/constants/constants.dart';
+
+String? usernameValidation(String? username) {
+ if (username == null || username.isEmpty) {
+ return 'Field cannot be empty.';
+ } else if (username.length < 3 || username.length > 20) {
+ return 'Username must be 3 to 20 characters long.';
+ } else if (!usernameRegex.hasMatch(username)) {
+ return 'Letters, numbers, dashes and underscores are allowed.';
+ }
+ return null;
+}
diff --git a/lib/shared/widgets/layout/bottom_navigation_item_icon.dart b/lib/shared/widgets/layout/bottom_navigation_item_icon.dart
index c540031b..eaaa43a2 100644
--- a/lib/shared/widgets/layout/bottom_navigation_item_icon.dart
+++ b/lib/shared/widgets/layout/bottom_navigation_item_icon.dart
@@ -13,17 +13,14 @@ class BottomNavigationItemIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return Padding(
- padding: const EdgeInsets.all(8.0),
- child: SvgPicture.asset(
- imagePath,
- colorFilter: isActive
- ? const ColorFilter.mode(
- ColorPalette.dReaderYellow100,
- BlendMode.srcIn,
- )
- : null,
- ),
+ return SvgPicture.asset(
+ imagePath,
+ colorFilter: isActive
+ ? const ColorFilter.mode(
+ ColorPalette.dReaderYellow100,
+ BlendMode.srcIn,
+ )
+ : null,
);
}
}