diff --git a/lib/appwrite.dart b/lib/appwrite.dart deleted file mode 100644 index 3ec2340..0000000 --- a/lib/appwrite.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:appwrite/appwrite.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; - -Client client = Client() - .setEndpoint(dotenv.env['APP_ENDPOINT']!) - .setProject(dotenv.env['APP_PROJECT']!); diff --git a/lib/components/at_sign_svg.dart b/lib/assets/svg/at_sign_svg.dart similarity index 100% rename from lib/components/at_sign_svg.dart rename to lib/assets/svg/at_sign_svg.dart diff --git a/lib/components/battery_svg.dart b/lib/assets/svg/battery_svg.dart similarity index 100% rename from lib/components/battery_svg.dart rename to lib/assets/svg/battery_svg.dart diff --git a/lib/components/calendar_svg.dart b/lib/assets/svg/calendar_svg.dart similarity index 100% rename from lib/components/calendar_svg.dart rename to lib/assets/svg/calendar_svg.dart diff --git a/lib/components/clock_svg.dart b/lib/assets/svg/clock_svg.dart similarity index 100% rename from lib/components/clock_svg.dart rename to lib/assets/svg/clock_svg.dart diff --git a/lib/components/country_svg.dart b/lib/assets/svg/country_svg.dart similarity index 100% rename from lib/components/country_svg.dart rename to lib/assets/svg/country_svg.dart diff --git a/lib/components/eye_svg.dart b/lib/assets/svg/eye_svg.dart similarity index 100% rename from lib/components/eye_svg.dart rename to lib/assets/svg/eye_svg.dart diff --git a/lib/components/gender_svg.dart b/lib/assets/svg/gender_svg.dart similarity index 100% rename from lib/components/gender_svg.dart rename to lib/assets/svg/gender_svg.dart diff --git a/lib/components/info_svg.dart b/lib/assets/svg/info_svg.dart similarity index 100% rename from lib/components/info_svg.dart rename to lib/assets/svg/info_svg.dart diff --git a/lib/components/people_svg.dart b/lib/assets/svg/people_svg.dart similarity index 100% rename from lib/components/people_svg.dart rename to lib/assets/svg/people_svg.dart diff --git a/lib/components/shield_svg.dart b/lib/assets/svg/shield_svg.dart similarity index 100% rename from lib/components/shield_svg.dart rename to lib/assets/svg/shield_svg.dart diff --git a/lib/components/text_svg.dart b/lib/assets/svg/text_svg.dart similarity index 100% rename from lib/components/text_svg.dart rename to lib/assets/svg/text_svg.dart diff --git a/lib/components/trophy_svg.dart b/lib/assets/svg/trophy_svg.dart similarity index 100% rename from lib/components/trophy_svg.dart rename to lib/assets/svg/trophy_svg.dart diff --git a/lib/components/word_svg.dart b/lib/assets/svg/word_svg.dart similarity index 100% rename from lib/components/word_svg.dart rename to lib/assets/svg/word_svg.dart diff --git a/lib/pages/home/bottom_navigation_bar.dart b/lib/components/bottom_navigation_bar.dart similarity index 100% rename from lib/pages/home/bottom_navigation_bar.dart rename to lib/components/bottom_navigation_bar.dart diff --git a/lib/components/usercard.dart b/lib/components/community/usercard.dart similarity index 100% rename from lib/components/usercard.dart rename to lib/components/community/usercard.dart diff --git a/lib/dfault_framework/dfault_material/dfault_buttons.dart b/lib/components/dfault_framework/dfault_material/dfault_buttons.dart similarity index 100% rename from lib/dfault_framework/dfault_material/dfault_buttons.dart rename to lib/components/dfault_framework/dfault_material/dfault_buttons.dart diff --git a/lib/dfault_framework/dfault_material/dfault_input.dart b/lib/components/dfault_framework/dfault_material/dfault_input.dart similarity index 100% rename from lib/dfault_framework/dfault_material/dfault_input.dart rename to lib/components/dfault_framework/dfault_material/dfault_input.dart diff --git a/lib/components/headappbar.dart b/lib/components/headappbar.dart deleted file mode 100644 index 0381ce2..0000000 --- a/lib/components/headappbar.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; - -class HeadAppBar extends StatelessWidget implements PreferredSizeWidget { - final String title; - final bool centerTile; - - const HeadAppBar({ - super.key, - required this.title, - this.centerTile = true, - }); - - @override - Widget build(BuildContext context) { - return AppBar( - title: Text(title, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - )), - centerTitle: centerTile, - backgroundColor: Colors.yellow[700], - ); - } - - @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); -} diff --git a/lib/components/profile_about_me_card.dart b/lib/components/profile/about_me_card.dart similarity index 93% rename from lib/components/profile_about_me_card.dart rename to lib/components/profile/about_me_card.dart index 9ca6ec6..da84602 100644 --- a/lib/components/profile_about_me_card.dart +++ b/lib/components/profile/about_me_card.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:langx_flutter/components/info_svg.dart'; -import 'package:langx_flutter/components/country_svg.dart'; -import 'package:langx_flutter/components/gender_svg.dart'; -import 'package:langx_flutter/components/calendar_svg.dart'; -import 'package:langx_flutter/components/clock_svg.dart'; -import 'package:langx_flutter/components/at_sign_svg.dart'; -import 'package:langx_flutter/components/shield_svg.dart'; + +// Components Import +import 'package:langx_flutter/assets/svg/info_svg.dart'; +import 'package:langx_flutter/assets/svg/country_svg.dart'; +import 'package:langx_flutter/assets/svg/gender_svg.dart'; +import 'package:langx_flutter/assets/svg/calendar_svg.dart'; +import 'package:langx_flutter/assets/svg/clock_svg.dart'; +import 'package:langx_flutter/assets/svg/at_sign_svg.dart'; +import 'package:langx_flutter/assets/svg/shield_svg.dart'; class ProfileAboutMeCard extends StatelessWidget { final String country; diff --git a/lib/components/profile_badges_card.dart b/lib/components/profile/badges_card.dart similarity index 100% rename from lib/components/profile_badges_card.dart rename to lib/components/profile/badges_card.dart diff --git a/lib/components/profile_day_streaks_card.dart b/lib/components/profile/day_streaks_card.dart similarity index 97% rename from lib/components/profile_day_streaks_card.dart rename to lib/components/profile/day_streaks_card.dart index 9ddbb10..8831999 100644 --- a/lib/components/profile_day_streaks_card.dart +++ b/lib/components/profile/day_streaks_card.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:langx_flutter/components/info_svg.dart'; + +// Components Imports +import 'package:langx_flutter/assets/svg/info_svg.dart'; class ProfileDayStreaksCard extends StatelessWidget { final String imgUrl; diff --git a/lib/components/profile_mother_tongues_card.dart b/lib/components/profile/mother_tongues_card.dart similarity index 97% rename from lib/components/profile_mother_tongues_card.dart rename to lib/components/profile/mother_tongues_card.dart index 2cfec1e..5e0510a 100644 --- a/lib/components/profile_mother_tongues_card.dart +++ b/lib/components/profile/mother_tongues_card.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:langx_flutter/components/word_svg.dart'; + +// Components Imports +import 'package:langx_flutter/assets/svg/word_svg.dart'; class MotherTonguesCard extends StatelessWidget { final List languages; diff --git a/lib/components/profile_others_card.dart b/lib/components/profile/others_card.dart similarity index 95% rename from lib/components/profile_others_card.dart rename to lib/components/profile/others_card.dart index 6554a57..8d11dd7 100644 --- a/lib/components/profile_others_card.dart +++ b/lib/components/profile/others_card.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:langx_flutter/components/people_svg.dart'; -import 'package:langx_flutter/components/eye_svg.dart'; + +// Components Imports +import 'package:langx_flutter/assets/svg/people_svg.dart'; +import 'package:langx_flutter/assets/svg/eye_svg.dart'; class OthersCard extends StatelessWidget { const OthersCard({ diff --git a/lib/components/profile_study_languages_card.dart b/lib/components/profile/study_languages_card.dart similarity index 97% rename from lib/components/profile_study_languages_card.dart rename to lib/components/profile/study_languages_card.dart index 0ef1bcf..d1e75bb 100644 --- a/lib/components/profile_study_languages_card.dart +++ b/lib/components/profile/study_languages_card.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:langx_flutter/components/battery_svg.dart'; + +// Components Imports +import 'package:langx_flutter/assets/svg/battery_svg.dart'; class StudyLanguagesCard extends StatelessWidget { final List languages; diff --git a/lib/components/profile_token_card.dart b/lib/components/profile/token_card.dart similarity index 96% rename from lib/components/profile_token_card.dart rename to lib/components/profile/token_card.dart index 6201014..f8d9647 100644 --- a/lib/components/profile_token_card.dart +++ b/lib/components/profile/token_card.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:langx_flutter/components/trophy_svg.dart'; -import 'package:langx_flutter/components/info_svg.dart'; + +// Components Imports +import 'package:langx_flutter/assets/svg/trophy_svg.dart'; +import 'package:langx_flutter/assets/svg/info_svg.dart'; class ProfileTokenCard extends StatelessWidget { final String imgUrl; diff --git a/lib/components/profile_user_card.dart b/lib/components/profile/user_card.dart similarity index 100% rename from lib/components/profile_user_card.dart rename to lib/components/profile/user_card.dart diff --git a/lib/extras/.gitkeep b/lib/extras/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/main.dart b/lib/main.dart index b6557b0..0397ffa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,27 +1,73 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; -//import 'package:langx_flutter/pages/login/login.dart'; + +// Themes Import import 'package:langx_flutter/theme.dart'; +// Pages Import +import 'package:langx_flutter/pages/login/login.dart'; import 'package:langx_flutter/pages/home/home.dart'; +// Providers Import +import 'package:langx_flutter/providers/auth_provider.dart'; + void main() async { WidgetsFlutterBinding.ensureInitialized(); await dotenv.load(fileName: ".env"); - runApp(const Main()); + runApp( + const ProviderScope( + child: Main(), + ), + ); } -class Main extends StatelessWidget { +class Main extends ConsumerWidget { const Main({super.key}); + @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { + final authStatus = ref.watch(authProvider); + final authNotifier = ref.read(authProvider.notifier); + return MaterialApp( theme: whiteTheme(), darkTheme: darkTheme(), -// home: const LoginScreen(), - home: const Home(), - // home: const Home(), + home: Scaffold( + body: Builder( + builder: (context) { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (authNotifier.errorMessage != null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(authNotifier.errorMessage!)), + ); + authNotifier + .clearErrorMessage(); // Clear the error message after displaying it + } + }); + + return authStatus == AuthStatus.loading + ? const SplashScreen() + : authStatus == AuthStatus.authenticated + ? const Home() + : const LoginScreen(); + }, + ), + ), + ); + } +} + +class SplashScreen extends StatelessWidget { + const SplashScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + body: Center( + child: CircularProgressIndicator(), + ), ); } } diff --git a/lib/models/.gitkeep b/lib/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/pages/home/community.dart b/lib/pages/home/community.dart index 4bdf53c..5ccaad1 100644 --- a/lib/pages/home/community.dart +++ b/lib/pages/home/community.dart @@ -1,14 +1,24 @@ import 'package:flutter/material.dart'; -import 'package:langx_flutter/components/usercard.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -class Community extends StatefulWidget { +// Components Import +import 'package:langx_flutter/components/community/usercard.dart'; +import 'package:langx_flutter/providers/user_provider.dart'; + +class Community extends ConsumerStatefulWidget { const Community({super.key}); @override - State createState() => _CommunityState(); + ConsumerState createState() => _CommunityState(); } -class _CommunityState extends State { +class _CommunityState extends ConsumerState { + @override + void initState() { + super.initState(); + ref.read(userProvider.notifier).fetchUsers(); + } + Widget buildCategoryButton(IconData icon, String label, Color iconColor) { return GestureDetector( onTap: () {}, @@ -49,6 +59,8 @@ class _CommunityState extends State { double width = MediaQuery.of(context).size.width; int crossAxisCount = _calculateCrossAxisCount(width); + final users = ref.watch(userProvider); + return Scaffold( backgroundColor: const Color.fromARGB(31, 163, 163, 163), body: CustomScrollView( @@ -140,30 +152,36 @@ class _CommunityState extends State { ), ), ), - SliverPadding( - padding: const EdgeInsets.all(8.0), - sliver: SliverGrid( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return UserCard( - name: 'User ${index + 1}', - age: 18 + index, - studies: 'Studies info', - speaks: 'Speaks info', - imageUrl: 'assets/images/preview.png', - status: 'Active', - ); - }, - childCount: 12, - ), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: crossAxisCount, - mainAxisSpacing: 10.0, - crossAxisSpacing: 10.0, - childAspectRatio: 0.75, - ), - ), - ), + users.isEmpty + ? const SliverToBoxAdapter( + child: Center(child: CircularProgressIndicator()), + ) + : SliverPadding( + padding: const EdgeInsets.all(8.0), + sliver: SliverGrid( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + final user = users[index].data; + return UserCard( + name: user['name'].toString(), + age: user['age'] ?? 0, + studies: user['studyLanguages'].toString(), + speaks: user['motherLanguages'].toString(), + imageUrl: + user['imageUrl'] ?? 'assets/images/preview.png', + status: user['status'] ?? 'Active', + ); + }, + childCount: users.length, + ), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: crossAxisCount, + mainAxisSpacing: 10.0, + crossAxisSpacing: 10.0, + childAspectRatio: 0.75, + ), + ), + ), ], ), ); diff --git a/lib/pages/home/home.dart b/lib/pages/home/home.dart index 39d8faf..95c16f4 100644 --- a/lib/pages/home/home.dart +++ b/lib/pages/home/home.dart @@ -1,6 +1,6 @@ -// import 'package:flutter/cupertino.dart'; -// import 'package:flutter/widgets.dart'; import 'package:flutter/material.dart'; + +// Pages Import import 'package:langx_flutter/pages/home/community.dart'; import 'package:langx_flutter/pages/home/profile.dart'; diff --git a/lib/pages/home/profile.dart b/lib/pages/home/profile.dart index 9b2a934..c09dc64 100644 --- a/lib/pages/home/profile.dart +++ b/lib/pages/home/profile.dart @@ -1,30 +1,48 @@ import 'package:flutter/material.dart'; -import 'package:langx_flutter/components/profile_user_card.dart'; -import 'package:langx_flutter/components/profile_token_card.dart'; -import 'package:langx_flutter/components/profile_day_streaks_card.dart'; -import 'package:langx_flutter/components/profile_about_me_card.dart'; -import 'package:langx_flutter/components/profile_study_languages_card.dart'; -import 'package:langx_flutter/components/profile_mother_tongues_card.dart'; -import 'package:langx_flutter/components/profile_badges_card.dart'; -import 'package:langx_flutter/components/profile_others_card.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -class Profile extends StatefulWidget { +// Providers Import +import 'package:langx_flutter/providers/auth_provider.dart'; + +// Components Import +import 'package:langx_flutter/components/profile/user_card.dart'; +import 'package:langx_flutter/components/profile/token_card.dart'; +import 'package:langx_flutter/components/profile/day_streaks_card.dart'; +import 'package:langx_flutter/components/profile/about_me_card.dart'; +import 'package:langx_flutter/components/profile/study_languages_card.dart'; +import 'package:langx_flutter/components/profile/mother_tongues_card.dart'; +import 'package:langx_flutter/components/profile/badges_card.dart'; +import 'package:langx_flutter/components/profile/others_card.dart'; + +class Profile extends ConsumerStatefulWidget { const Profile({super.key}); @override - State createState() => _ProfileState(); + ConsumerState createState() => _ProfileState(); } -class _ProfileState extends State { +class _ProfileState extends ConsumerState { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text("Profile", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - )), + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text("Profile"), + Row( + children: [ + GestureDetector( + onTap: () async { + ref.read(authProvider.notifier).logout(context); + }, + child: const Icon(Icons.exit_to_app_outlined, size: 30), + ), + const SizedBox(width: 10.0), + ], + ), + ], + ), centerTitle: false, backgroundColor: Colors.yellow[700], ), @@ -33,8 +51,8 @@ class _ProfileState extends State { padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0), child: ListView( children: const [ - //Afterwards, it should be modified to take an ID as input and retrieve data based on that ID. - //Alternatively, the outer layer retrieves all the data for the cards and inserts them individually. + // Afterwards, it should be modified to take an ID as input and retrieve data based on that ID. + // Alternatively, the outer layer retrieves all the data for the cards and inserts them individually. ProfileUserCard( name: 'Addison', age: 18, diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart index 21c1405..597b7df 100644 --- a/lib/pages/login/login.dart +++ b/lib/pages/login/login.dart @@ -1,19 +1,23 @@ import 'package:appwrite/enums.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'login_service.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:langx_flutter/dfault_framework/dfault_material/dfault_buttons.dart'; -import 'package:langx_flutter/dfault_framework/dfault_material/dfault_input.dart'; +// Providers Import +import 'package:langx_flutter/providers/auth_provider.dart'; -class LoginScreen extends StatefulWidget { +// Component Imports +import 'package:langx_flutter/components/dfault_framework/dfault_material/dfault_buttons.dart'; +import 'package:langx_flutter/components/dfault_framework/dfault_material/dfault_input.dart'; + +class LoginScreen extends ConsumerStatefulWidget { const LoginScreen({super.key}); @override LoginScreenState createState() => LoginScreenState(); } -class LoginScreenState extends State { +class LoginScreenState extends ConsumerState { final TextEditingController emailController = TextEditingController(); final TextEditingController passwordController = TextEditingController(); @@ -54,25 +58,27 @@ class LoginScreenState extends State { ), const SizedBox(height: 35), dButton( - text: "Login", - onPress: () async { - logUserIn( + text: "Login", + onPress: () async { + await ref.read(authProvider.notifier).login( email: emailController.text, password: passwordController.text, - context: context); - }), + context: context, + ); + }, + ), const SizedBox(height: 16), TextButton( - // onPressed: () { - // register(emailController.text, passwordController.text, - // nameController.text); - // }, - onPressed: () {}, + onPressed: () { + // Navigate to registration page + }, child: const Text('Not a member yet? SIGN UP'), ), const SizedBox(height: 8), TextButton( - onPressed: () {}, + onPressed: () { + // Navigate to forgot password page + }, child: const Text('Forgot password?'), ), const SizedBox(height: 16), @@ -85,10 +91,11 @@ class LoginScreenState extends State { width: 40, height: 40, ), - onPressed: () { - oAuthLogin( - provider: OAuthProvider.google, - context: context); + onPressed: () async { + await ref.read(authProvider.notifier).oAuthLogin( + provider: OAuthProvider.google, + context: context, + ); }, ), const SizedBox(width: 30), @@ -98,30 +105,31 @@ class LoginScreenState extends State { width: 40, height: 40, ), - onPressed: () { - oAuthLogin( - provider: OAuthProvider.facebook, - context: context); + onPressed: () async { + await ref.read(authProvider.notifier).oAuthLogin( + provider: OAuthProvider.facebook, + context: context, + ); }, ), const SizedBox(width: 30), IconButton( - icon: SvgPicture.asset( - 'assets/images/apple_icon.svg', - width: 40, - height: 40, - ), - onPressed: () { - oAuthLogin( - provider: OAuthProvider.apple, - context: context); + icon: SvgPicture.asset('assets/images/apple_icon.svg', + width: 40, height: 40), + onPressed: () async { + await ref.read(authProvider.notifier).oAuthLogin( + provider: OAuthProvider.apple, + context: context, + ); }, ), ], ), const SizedBox(height: 30), TextButton( - onPressed: () {}, + onPressed: () { + // Show introduction + }, child: const Text('SHOW INTRO'), ), ], diff --git a/lib/pages/login/login_service.dart b/lib/pages/login/login_service.dart deleted file mode 100644 index 2c5030d..0000000 --- a/lib/pages/login/login_service.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:appwrite/appwrite.dart'; -import 'package:flutter/material.dart'; -import 'package:langx_flutter/appwrite.dart'; -import 'package:langx_flutter/pages/home/home.dart'; - -Account account = Account(client); - -Future checkLoggedInUser() async { - try { - await account.get(); - return true; - } catch (e) { - debugPrint(e.toString()); - return false; - } -} - -logUserIn({required email, required password, required context}) async { - try { - await account.createEmailPasswordSession(email: email, password: password); - Navigator.pushReplacement( - context, MaterialPageRoute(builder: (context) => const Home())); - } catch (e) { - debugPrint(e.toString()); - } -} - -oAuthLogin({required provider, required context}) async { - await account.createOAuth2Session(provider: provider); - Navigator.pushReplacement( - context, MaterialPageRoute(builder: (context) => const Home())); -} diff --git a/lib/providers/auth_provider.dart b/lib/providers/auth_provider.dart new file mode 100644 index 0000000..ecceeac --- /dev/null +++ b/lib/providers/auth_provider.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +// Service Imports +import 'package:langx_flutter/services/auth_service.dart'; + +// Page Imports +import 'package:langx_flutter/pages/home/home.dart'; +import 'package:langx_flutter/pages/login/login.dart'; + +// Define an enum for authentication state +enum AuthStatus { authenticated, unauthenticated, loading, error } + +// Define a state notifier for authentication +class AuthNotifier extends StateNotifier { + AuthNotifier() : super(AuthStatus.loading) { + checkAuthStatus(); + } + + String? _errorMessage; + + String? get errorMessage => _errorMessage; + + void clearErrorMessage() { + _errorMessage = null; + } + + @override + set state(AuthStatus value) { + super.state = value; + debugPrint('Auth status changed to: $value'); + } + + Future checkAuthStatus() async { + final isAuthenticated = await isLoggedIn(); + if (isAuthenticated) { + state = AuthStatus.authenticated; + } else { + state = AuthStatus.unauthenticated; + } + } + + Future login({ + required String email, + required String password, + required BuildContext context, + }) async { + state = AuthStatus.loading; + _errorMessage = null; // Clear any previous error message + try { + await loginService(email: email, password: password); + state = AuthStatus.authenticated; + if (context.mounted) { + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const Home()), + ); + } + } catch (e) { + state = AuthStatus.unauthenticated; // Revert to unauthenticated state + _errorMessage = 'Login failed: ${e.toString()}'; + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(_errorMessage!)), + ); + } + } + } + + Future oAuthLogin({ + required provider, + required BuildContext context, + }) async { + state = AuthStatus.loading; + _errorMessage = null; // Clear any previous error message + try { + await oAuthLoginService(provider: provider); + state = AuthStatus.authenticated; + if (context.mounted) { + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const Home()), + ); + } + } catch (e) { + state = AuthStatus.unauthenticated; // Revert to unauthenticated state + _errorMessage = 'OAuth login failed: ${e.toString()}'; + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(_errorMessage!)), + ); + } + } + } + + Future logout(BuildContext context) async { + state = AuthStatus.loading; + _errorMessage = null; // Clear any previous error message + try { + await logoutService(); + state = AuthStatus.unauthenticated; + if (context.mounted) { + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const LoginScreen()), + ); + } + } catch (e) { + state = AuthStatus.error; + _errorMessage = 'Logout failed: ${e.toString()}'; + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(_errorMessage!)), + ); + } + } + } +} + +// Define a provider for the AuthNotifier +final authProvider = StateNotifierProvider((ref) { + return AuthNotifier(); +}); diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart new file mode 100644 index 0000000..408de38 --- /dev/null +++ b/lib/providers/user_provider.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +// Service Imports +import 'package:langx_flutter/services/user_service.dart'; + +class UserNotifier extends StateNotifier> { + UserNotifier() : super([]); + + Future fetchUsers() async { + try { + final users = await listUsers(); + state = users; + } catch (e) { + state = []; + debugPrint('Failed to fetch users: $e'); + } + } +} + +final userProvider = StateNotifierProvider>((ref) { + return UserNotifier(); +}); diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart new file mode 100644 index 0000000..1cdba83 --- /dev/null +++ b/lib/services/api_service.dart @@ -0,0 +1,28 @@ +import 'package:flutter/foundation.dart'; +import 'package:appwrite/appwrite.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +Client client = Client() + .setEndpoint(dotenv.env['APP_ENDPOINT']!) + .setProject(dotenv.env['APP_PROJECT']!); + +Account account = Account(client); +Databases databases = Databases(client); +// Functions functions = Functions(client); +// Storage storage = Storage(client); +// Locale locale = Locale(client); + +Future listDocuments(String collectionId, + {List? queries}) async { + try { + final response = await databases.listDocuments( + databaseId: dotenv.env['APP_DATABASE']!, + collectionId: collectionId, + queries: queries ?? [], + ); + return response; + } catch (e) { + debugPrint('Error listing documents: $e'); + return null; + } +} diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart new file mode 100644 index 0000000..8e15070 --- /dev/null +++ b/lib/services/auth_service.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +// Service Imports +import 'package:langx_flutter/services/api_service.dart'; + +Future isLoggedIn() async { + try { + await account.get(); + return true; + } catch (e) { + debugPrint('Error checking login status: $e'); + return false; + } +} + +Future loginService({ + required String email, + required String password, +}) async { + await account.createEmailPasswordSession(email: email, password: password); +} + +Future oAuthLoginService({ + required provider, +}) async { + await account.createOAuth2Session(provider: provider); +} + +Future logoutService() async { + await account.deleteSession(sessionId: 'current'); +} diff --git a/lib/services/user_service.dart b/lib/services/user_service.dart new file mode 100644 index 0000000..ce94361 --- /dev/null +++ b/lib/services/user_service.dart @@ -0,0 +1,17 @@ +import 'package:appwrite/appwrite.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +// Service Imports +import 'package:langx_flutter/services/api_service.dart'; + +Future> listUsers() async { + try { + final response = await listDocuments(dotenv.env['USERS_COLLECTION']!, + queries: [Query.orderDesc('\$updatedAt')]); + return response.documents; + } catch (e) { + debugPrint('Error listing users: $e'); + rethrow; + } +} diff --git a/pubspec.lock b/pubspec.lock index 6e4ec2a..73d3d3d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -158,6 +158,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + sha256: "0f1974eff5bbe774bf1d870e406fc6f29e3d6f1c46bd9c58e7172ff68a785d7d" + url: "https://pub.dev" + source: hosted + version: "2.5.1" flutter_svg: dependency: "direct main" description: @@ -256,14 +264,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" - logger: - dependency: "direct main" - description: - name: logger - sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4 - url: "https://pub.dev" - source: hosted - version: "2.3.0" logging: dependency: transitive description: @@ -400,6 +400,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: f21b32ffd26a36555e501b04f4a5dca43ed59e16343f1a30c13632b2351dfa4d + url: "https://pub.dev" + source: hosted + version: "2.5.1" sky_engine: dependency: transitive description: flutter @@ -421,6 +429,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.1" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + url: "https://pub.dev" + source: hosted + version: "1.0.0" stream_channel: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ca0a0f9..b49acb6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,8 +37,8 @@ dependencies: flutter_svg: ^2.0.0 go_router: ^14.1.4 appwrite: ^12.0.4 - logger: ^2.3.0 flutter_dotenv: ^5.1.0 + flutter_riverpod: ^2.5.1 dev_dependencies: flutter_test: @@ -83,7 +83,7 @@ flutter: fonts: - family: Comfortaa fonts: - - asset: assets/fonts/Comfortaa.ttf + - asset: assets/fonts/Comfortaa.ttf # # For details regarding fonts from package dependencies,