diff --git a/lib/core/dependency_injection/get_it/services_get_it.dart b/lib/core/dependency_injection/get_it/services_get_it.dart index 3b093dd..62016b1 100644 --- a/lib/core/dependency_injection/get_it/services_get_it.dart +++ b/lib/core/dependency_injection/get_it/services_get_it.dart @@ -9,7 +9,7 @@ class ServicesGetIt extends GetItFeature { void featureInit() { getIt.registerLazySingleton(() => ApiService()); getIt.registerLazySingleton( - () => UserLocalService(secureStorage: getIt.call())); + () => UserLocalService(secureStorage: getIt.call(), getStorage: getIt.call())); getIt.registerLazySingleton( () => LocalService(getStorage: getIt.call())); } diff --git a/lib/core/routes/app_router.dart b/lib/core/routes/app_router.dart index a03ccca..34af00f 100644 --- a/lib/core/routes/app_router.dart +++ b/lib/core/routes/app_router.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:provider/provider.dart'; import 'package:waves/core/routes/route_keys.dart'; import 'package:waves/core/routes/routes.dart'; +import 'package:waves/core/services/user_local_service.dart'; import 'package:waves/core/utilities/enum.dart'; import 'package:waves/features/auth/presentation/view/auth_view.dart'; import 'package:waves/features/auth/presentation/view/hive_auth_transaction_view.dart'; @@ -9,6 +11,7 @@ import 'package:waves/features/auth/presentation/view/hive_key_chain_auth_view.d import 'package:waves/features/auth/presentation/view/hive_signer_auth_view.dart'; import 'package:waves/features/auth/presentation/view/posting_key_auth_view.dart'; import 'package:waves/features/bookmarks/views/thread_bookmark/bookmark_view.dart'; +import 'package:waves/features/settings/presentation/setting/controller/settings_controller.dart'; import 'package:waves/features/settings/presentation/setting/view/setting_view.dart'; import 'package:waves/features/threads/models/comment/comment_navigation_model.dart'; import 'package:waves/features/threads/models/thread_feeds/thread_feed_model.dart'; @@ -17,15 +20,32 @@ import 'package:waves/features/threads/presentation/comments/add_comment/view/hi import 'package:waves/features/threads/presentation/comments/comment_detail/view/comment_detail_view.dart'; import 'package:waves/features/threads/presentation/thread_feed/view/thread_feed_view.dart'; import 'package:waves/features/user/presentation/user_profile/view/user_profile_view.dart'; +import 'package:waves/features/user/repository/user_local_repository.dart'; +import 'package:waves/features/user/view/user_controller.dart'; +import 'package:waves/features/welcome/view/welcome_view.dart'; class AppRouter { static final _rootNavigatorKey = GlobalKey(); - static GoRouter router = GoRouter( - navigatorKey: _rootNavigatorKey, initialLocation: '/', routes: routes()); + static GoRouter router(BuildContext context) { + bool isTermsAccepted = context.select( + (settingsController) => settingsController.getTermsAcceptedFlag()); + + String initialPath = isTermsAccepted ? '/' : '/${Routes.welcomeView}'; + + return GoRouter( + navigatorKey: _rootNavigatorKey, + initialLocation: initialPath, + routes: routes()); + } static List routes() { return [ + GoRoute( + path: '/${Routes.welcomeView}', + name: Routes.welcomeView, + builder: (context, state) => const WelcomeView(), + ), GoRoute( path: '/', name: Routes.initialView, @@ -85,18 +105,20 @@ class AppRouter { ); }, ), - GoRoute( + GoRoute( path: '/${Routes.postingKeyAuthView}', name: Routes.postingKeyAuthView, builder: (context, state) { return const PostingKeyAuthView(); }, ), - GoRoute( + GoRoute( path: '/${Routes.hiveKeyChainAuthView}', name: Routes.hiveKeyChainAuthView, builder: (context, state) { - return HiveKeyChainAuthView(authType:state.extra as AuthType ,); + return HiveKeyChainAuthView( + authType: state.extra as AuthType, + ); }, ), GoRoute( @@ -131,16 +153,20 @@ class AppRouter { return value.toLowerCase() == "true"; } - static String currentRoute() { - return AppRouter.router.routerDelegate.currentConfiguration.uri.path + static String currentRoute(BuildContext context) { + return AppRouter.router(context).routerDelegate.currentConfiguration.uri.path .toString(); } static void popTillFirstScreen( BuildContext context, ) { - while (router - .routerDelegate.currentConfiguration.matches.last.matchedLocation != + while ((router(context)) + .routerDelegate + .currentConfiguration + .matches + .last + .matchedLocation != '/') { if (!context.canPop()) { return; diff --git a/lib/core/routes/routes.dart b/lib/core/routes/routes.dart index 1f8d7d9..180eaf4 100644 --- a/lib/core/routes/routes.dart +++ b/lib/core/routes/routes.dart @@ -1,4 +1,5 @@ class Routes { + static const String welcomeView = 'welcome'; static const String initialView = 'initial'; static const String homeView = 'home'; static const String bookmarksView = 'bookmarks'; diff --git a/lib/core/services/user_local_service.dart b/lib/core/services/user_local_service.dart index 93d854f..9c3761d 100644 --- a/lib/core/services/user_local_service.dart +++ b/lib/core/services/user_local_service.dart @@ -1,4 +1,5 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:get_storage/get_storage.dart'; import 'package:waves/core/utilities/enum.dart'; import 'package:waves/features/auth/models/hive_auth_model.dart'; import 'package:waves/features/auth/models/hive_signer_auth_model.dart'; @@ -8,10 +9,13 @@ import 'package:waves/features/auth/models/user_auth_model.dart'; class UserLocalService { static const String _currentUserAccountStorageKey = 'currentUserAccount'; static const String _allUserAccountsStorageKey = 'allUserAccounts'; + static const String _termsAcceptedFlagKey = 'termsAcceptedFlag'; + final FlutterSecureStorage _secureStorage; + final GetStorage _getStorage; - UserLocalService({required FlutterSecureStorage secureStorage}) - : _secureStorage = secureStorage; + UserLocalService({required FlutterSecureStorage secureStorage, required final GetStorage getStorage}) + : _secureStorage = secureStorage, _getStorage = getStorage; Future cleanup() async { await _secureStorage.delete(key: _currentUserAccountStorageKey); @@ -79,4 +83,16 @@ class UserLocalService { Future logOut() async { await _secureStorage.delete(key: _currentUserAccountStorageKey); } + + + Future writeTermsAcceptedFlag(bool status) async { + await _getStorage.write(_termsAcceptedFlagKey, status); + } + + bool readTermsAcceptedFlag() { + + bool? data = _getStorage.read(_termsAcceptedFlagKey); + return data ?? false; + } + } diff --git a/lib/features/user/repository/user_local_repository.dart b/lib/features/user/repository/user_local_repository.dart index 3c0eebd..2adf6fb 100644 --- a/lib/features/user/repository/user_local_repository.dart +++ b/lib/features/user/repository/user_local_repository.dart @@ -27,4 +27,14 @@ class UserLocalRepository { Future writeAllUserAccounts(List accounts) async { return await _localService.writeAllUserAccounts(accounts); } + + bool readTermsAcceptedFlag() { + return _localService.readTermsAcceptedFlag(); + } + + Future writeTermsAcceptedFlag(bool status) async { + return await _localService.writeTermsAcceptedFlag(status); + } + + } diff --git a/lib/features/user/view/user_controller.dart b/lib/features/user/view/user_controller.dart index b57edc3..a7db2d8 100644 --- a/lib/features/user/view/user_controller.dart +++ b/lib/features/user/view/user_controller.dart @@ -52,6 +52,14 @@ class UserController extends ChangeNotifier { _userSteamController.add(null); } + bool getTermsAcceptedFlag() { + return _localRepository.readTermsAcceptedFlag(); + } + + void setTermsAcceptedFlag(bool status) async { + await _localRepository.writeTermsAcceptedFlag(status); + } + @override void dispose() { _userAuthSubscription.cancel(); diff --git a/lib/features/welcome/view/welcome_view.dart b/lib/features/welcome/view/welcome_view.dart new file mode 100644 index 0000000..010107f --- /dev/null +++ b/lib/features/welcome/view/welcome_view.dart @@ -0,0 +1,193 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:go_router/go_router.dart'; +import 'package:provider/provider.dart'; +import 'package:waves/core/routes/routes.dart'; +import 'package:waves/core/utilities/act.dart'; +import 'package:waves/features/user/view/user_controller.dart'; + +class WelcomeView extends StatefulWidget { + const WelcomeView({super.key}); + + @override + _WelcomeViewState createState() => _WelcomeViewState(); +} + +class _WelcomeViewState extends State { + bool showAnimation = true; + bool isConsentChecked = false; + String appVersion = + "1.0.0"; // Flutter has no direct equivalent for getting version, use package_info for this + + @override + void initState() { + super.initState(); + } + + void _handleButtonPress() { + context.read().setTermsAcceptedFlag(true); + + // Navigate to Main Screen (Use your route navigation logic here) + context.pushReplacementNamed(Routes.initialView); + } + + void _onCheckPress(bool? value) { + setState(() { + isConsentChecked = value ?? false; + }); + } + + void _onTermsPress() { + // Navigate to a webview or open URL in browser + Act.launchThisUrl("https://ecency.com/terms-of-service"); + } + + Widget _renderInfo(IconData iconName, String heading, String body) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, // Aligns items at the top + children: [ + Icon(iconName, size: 30, color: Colors.blue), + const SizedBox(width: 10), + Expanded( + // Ensures the column takes the available space and allows wrapping + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + heading, + style: const TextStyle( + fontSize: 17, fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 5), // Add some spacing between heading and body + Text( + body, + style: const TextStyle(fontSize: 15), + softWrap: true, // Allows wrapping onto multiple lines + overflow: TextOverflow + .visible, // Ensure text remains visible if it exceeds space + ), + ], + ), + ), + ], + ), + ); + } + + Widget _renderConsent() { + return Row( + children: [ + Checkbox.adaptive( + value: isConsentChecked, + onChanged: _onCheckPress, + ), + Expanded( + child: GestureDetector( + onTap: _onTermsPress, + child: Text.rich( + TextSpan( + text: "I accept the ", + style: const TextStyle(fontSize: 14), + children: [ + TextSpan( + text: "Terms and Conditions", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryColor, + ), + ), + ], + ), + ), + ), + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Stack( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.all(40.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Waves', // Replace with localized text + style: TextStyle(fontSize: 34), + textAlign: TextAlign.start, + ), + const Text( + 'In Ocean of Thoughts', // Replace with localized text + style: TextStyle( + fontSize: 20, + ), + ), + Text( + 'Short content sharing', // Replace with localized text + style: TextStyle( + fontSize: 34, + color: Theme.of(context).primaryColor), + ), + ], + ), + ), + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Column( + children: [ + _renderInfo(Icons.lock, 'Own Your Content', + 'Post short blogs on a decentralized platform.'), + _renderInfo( + Icons.sentiment_satisfied_alt, + 'Engage with Diverse Communities', + 'Connect with a global community of content creators.'), + _renderInfo(Icons.speed, 'Post in Seconds', + 'Share quickly using our simple, fast interface.'), + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + children: [ + _renderConsent(), + const SizedBox(height: 20), + ElevatedButton( + onPressed: isConsentChecked ? _handleButtonPress : null, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).primaryColor, + padding: const EdgeInsets.symmetric( + horizontal: 30, vertical: 15), + ), + child: const Text( + 'Get Started', // Replace with localized text + style: TextStyle(fontSize: 16), + ), + ), + ], + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index cf4f270..87ab175 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,7 +21,7 @@ void main() async { if (packageInfo.version == "1.0.0" && packageInfo.buildNumber == "9") { var isCleanUpDone = GetStorage().read('did_we_clean_up') as String? ?? 'no'; if (isCleanUpDone == "no") { - await UserLocalService(secureStorage: const FlutterSecureStorage()).cleanup(); + await UserLocalService(secureStorage: const FlutterSecureStorage(), getStorage: GetStorage()).cleanup(); await GetStorage().write('did_we_clean_up', 'yes'); } } @@ -43,7 +43,7 @@ class MyApp extends StatelessWidget { child: Consumer( builder: (context, themeController, child) { return MaterialApp.router( - routerConfig: AppRouter.router, + routerConfig: AppRouter.router(context), localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, locale: context.locale,