first registration process

master
Rafael 2024-05-20 01:16:54 +02:00
parent b22b47581c
commit 0c0bf431e2
6 changed files with 205 additions and 136 deletions

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../pages/home_page.dart';
import '../pages/user_data_page.dart'; import '../pages/user_data_page.dart';
import '../pages/settings_page.dart'; import '../pages/settings_page.dart';
import '../services/auth/auth_service.dart'; import '../services/auth/auth_service.dart';
@ -7,12 +8,6 @@ import 'feedback_dialog.dart';
class MyDrawer extends StatelessWidget { class MyDrawer extends StatelessWidget {
const MyDrawer({super.key}); const MyDrawer({super.key});
void logout() {
// get auth service
final auth = AuthService();
auth.signOut();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Drawer( return Drawer(
@ -45,7 +40,13 @@ class MyDrawer extends StatelessWidget {
onTap: () { onTap: () {
// pop the drawer // pop the drawer
Navigator.pop(context); Navigator.pop(context);
// TODO navigate to Homepage? // Navigate to HomePage?
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => HomePage(),
),
);
}, },
), ),
), ),
@ -77,15 +78,14 @@ class MyDrawer extends StatelessWidget {
title: const Text("My Profile"), title: const Text("My Profile"),
leading: const Icon(Icons.settings), leading: const Icon(Icons.settings),
onTap: () { onTap: () {
// pop the drawer // pop the drawer and navigate to settings page
Navigator.pop(context); Navigator.pop(context);
//navigate to settings page
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const SettingsPage(), builder: (context) => const SettingsPage(),
)); ),
);
}, },
), ),
), ),
@ -97,15 +97,16 @@ class MyDrawer extends StatelessWidget {
title: const Text("User Data"), title: const Text("User Data"),
leading: const Icon(Icons.supervised_user_circle), leading: const Icon(Icons.supervised_user_circle),
onTap: () { onTap: () {
// pop the drawer // pop the drawer first, then navigate to destination
Navigator.pop(context); Navigator.pop(context);
//navigate to settings page
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const UserDataPage(isRegProcess: false,), builder: (context) => const UserDataPage(
)); isRegProcess: false,
),
),
);
}, },
), ),
), ),
@ -150,7 +151,10 @@ class MyDrawer extends StatelessWidget {
child: ListTile( child: ListTile(
title: const Text("L O G O U T"), title: const Text("L O G O U T"),
leading: const Icon(Icons.logout), leading: const Icon(Icons.logout),
onTap: logout, onTap: () {
Navigator.of(context).pop();
AuthService().signOut();
},
), ),
), ),
], ],

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../helper.dart';
class ProfileCategoryForm<T> extends StatefulWidget { class ProfileCategoryForm<T> extends StatefulWidget {
final String title; final String title;
@ -91,8 +90,7 @@ class ProfileCategoryFormState<T> extends State<ProfileCategoryForm<T>> {
if (!widget.hideSaveButton) if (!widget.hideSaveButton)
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
if (!equalContent( //if (!equalContent(_selectedOptions, widget.preSelectedOptions)) {
_selectedOptions, widget.preSelectedOptions)) {
// Only save if options have changed // Only save if options have changed
if (_selectedOptions.length >= widget.minSelections) { if (_selectedOptions.length >= widget.minSelections) {
widget.onSave(_selectedOptions); widget.onSave(_selectedOptions);
@ -102,9 +100,7 @@ class ProfileCategoryFormState<T> extends State<ProfileCategoryForm<T>> {
// Provide feedback that minimum selections not met // Provide feedback that minimum selections not met
_showSnackBar('No selection made for ${widget.title}'); _showSnackBar('No selection made for ${widget.title}');
} }
} else { // } else { print('No changes to save for ${widget.title}.'); }
print('No changes to save for ${widget.title}.');
}
}, },
child: Text(widget.saveButtonText ?? 'Save'), child: Text(widget.saveButtonText ?? 'Save'),
), ),

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '../../constants.dart';
import '../../enumerations.dart'; import '../../enumerations.dart';
import '../../services/auth/auth_service.dart'; import '../../services/auth/auth_service.dart';
import '../../services/user_service.dart';
import '../pages/registration_complete_page.dart';
import 'profile_category_form.dart'; import 'profile_category_form.dart';
class SkillsForm extends StatelessWidget { class SkillsForm extends StatelessWidget {
@ -12,8 +12,7 @@ class SkillsForm extends StatelessWidget {
required this.skillsSought, required this.skillsSought,
}); });
// get instance of firestore and auth // get instance of auth
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final AuthService _authService = AuthService(); final AuthService _authService = AuthService();
final bool isRegProcess; final bool isRegProcess;
@ -22,7 +21,10 @@ class SkillsForm extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder<List<SkillOption>>( return FutureBuilder<List<SkillOption>>(
future: getSkillsFromFirebase(), // Fetch skills from Firebase future: UserService().getSkillsFromFirebase(
skillsSought,
_authService.getCurrentUser()!.uid,
), // Fetch skills from Firebase
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
// Show loading indicator while fetching data // Show loading indicator while fetching data
@ -33,8 +35,9 @@ class SkillsForm extends StatelessWidget {
List<SkillOption>? userSkills = snapshot.data; List<SkillOption>? userSkills = snapshot.data;
return ProfileCategoryForm( return ProfileCategoryForm(
title: 'Skills', title: 'Skills',
header: header: skillsSought
skillsSought ? 'Skills you are looking for' : 'Your own skills', ? 'Skills you are searching for'
: 'Your own skills',
description: skillsSought description: skillsSought
? 'Choose up to 3 areas you are looking for in a co-founder' ? 'Choose up to 3 areas you are looking for in a co-founder'
: 'Select up to 3 areas in which you are skilled', : 'Select up to 3 areas in which you are skilled',
@ -46,21 +49,37 @@ class SkillsForm extends StatelessWidget {
userSkills ?? [], // Pass pre-selected skills to the form userSkills ?? [], // Pass pre-selected skills to the form
onSave: (selectedOptions) async { onSave: (selectedOptions) async {
// Handle saving selected options // Handle saving selected options
bool success = await saveSkillsToFirebase( bool success = await UserService().saveSkillsToFirebase(
selectedOptions.cast<SkillOption>()); selectedOptions.cast<SkillOption>(),
skillsSought,
_authService.getCurrentUser()!.uid,
);
// Then navigate to another screen or perform any other action??? // Then navigate to another screen or perform any other action???
if (context.mounted) { if (context.mounted) {
if (success) { if (success) {
Navigator.push( if (isRegProcess && skillsSought) {
context, Navigator.push(
MaterialPageRoute( context,
builder: (context) => SkillsForm( MaterialPageRoute(
isRegProcess: isRegProcess, // TODO set following registration page HERE
skillsSought: true, builder: (context) => const RegistrationCompletePage(),
), ),
), );
); } else if (isRegProcess) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SkillsForm(
isRegProcess: isRegProcess,
skillsSought: true,
),
),
);
} else {
// Navigate back after saving
Navigator.pop(context);
}
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
@ -75,68 +94,4 @@ class SkillsForm extends StatelessWidget {
}, },
); );
} }
Future<List<SkillOption>> getSkillsFromFirebase() async {
// Fetch skills from Firestore
String currentUserId = _authService.getCurrentUser()!.uid;
DocumentSnapshot userDoc = await _firestore
.collection(Constants.dbCollectionUsers)
.doc(currentUserId)
.get();
if (userDoc.exists && userDoc.data() != null) {
Map<String, dynamic> userData =
userDoc.data()! as Map<String, dynamic>; // Explicit cast
List<dynamic>? skills;
if (skillsSought) {
skills = userData[Constants.dbFieldUsersSkillsSought];
} else {
skills = userData[
Constants.dbFieldUsersSkills]; //as List<dynamic>?; // Explicit cast
}
if (skills != null && skills.isNotEmpty) {
// Convert skills from strings to enum values
List<SkillOption> userSkills = skills
.map((skill) => SkillOption.values
.firstWhere((x) => x.toString() == 'SkillOption.$skill'))
.toList();
return userSkills;
}
}
return [];
}
Future<bool> saveSkillsToFirebase(List<SkillOption> selectedOptions) async {
try {
String currentUserId = _authService.getCurrentUser()!.uid;
// Convert enum values to strings, removing leading EnumType with split
List<String> skills = selectedOptions
.map((option) => option.toString().split('.').last)
.toList();
// Update the corresponding 'skills' field in the user's document
String keyToUpdate = skillsSought
? Constants.dbFieldUsersSkillsSought
: Constants.dbFieldUsersSkills;
_firestore
.collection(Constants.dbCollectionUsers)
.doc(currentUserId)
.update({
keyToUpdate: skills,
}).then((_) {
print('$keyToUpdate saved to Firebase: $skills');
}).catchError((error) {
print('Failed to save $keyToUpdate: $error');
});
return true;
} catch (e) {
return false;
}
}
} }

View File

@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'home_page.dart';
class RegistrationCompletePage extends StatelessWidget {
const RegistrationCompletePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false, // remove back button
title: const Text('Registration Complete'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(
Icons.check_circle,
color: Colors.green,
size: 100,
),
const SizedBox(height: 20),
const Text(
'Registration completed!',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 80),
const Text(
"You can now use the app.",
style: TextStyle(fontSize: 18),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
},
child: const Text("S T A R T"),
),
],
),
),
);
}
}

View File

@ -482,15 +482,19 @@ class _UserDataPageState extends State<UserDataPage> {
bool success = await saveUserData(context); bool success = await saveUserData(context);
if (context.mounted) { if (context.mounted) {
if (success) { if (success) {
Navigator.push( if (widget.isRegProcess) {
context, Navigator.push(
MaterialPageRoute( context,
builder: (context) => SkillsForm( MaterialPageRoute(
isRegProcess: widget.isRegProcess, builder: (context) => SkillsForm(
skillsSought: false, isRegProcess: widget.isRegProcess,
skillsSought: false,
),
), ),
), );
); } else {
Navigator.pop(context);
}
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(

View File

@ -19,18 +19,33 @@ class AuthGate extends StatelessWidget {
// check if user is logged in or not // check if user is logged in or not
if (snapshot.hasData) { if (snapshot.hasData) {
return FutureBuilder( return FutureBuilder(
// check database entries, if data is missing // check if user document exists
// then complete registration process future: _checkUserExists(),
// else go to HomePage
future: _checkCollectionsExist(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator(); return const CircularProgressIndicator();
} else if (snapshot.hasData && snapshot.data == true) { } else if (snapshot.hasData && snapshot.data == true) {
return HomePage(); return FutureBuilder(
// check database entries, if data is missing
// then complete registration process
// else go to HomePage
future: _checkCollectionsExist(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData && snapshot.data == true) {
return HomePage();
} else {
// also in case of (snapshot.hasError)
return const UserDataPage(isRegProcess: true);
}
},
);
} else { } else {
// also in case (snapshot.hasError) // should user entry not exist, recreate it and force logout
return const UserDataPage(isRegProcess: true); _createUserEntry();
FirebaseAuth.instance.signOut();
return const LoginOrRegister();
} }
}, },
); );
@ -42,21 +57,68 @@ class AuthGate extends StatelessWidget {
); );
} }
Future<bool> _checkUserExists() async {
// return true to avoid unneeded read access to database
return true;
/* enable code to check for deleted users
try {
String currentUserId = AuthService().getCurrentUser()!.uid;
final userDoc = await FirebaseFirestore.instance
.collection(Constants.dbCollectionUsers)
.doc(currentUserId)
.get();
return userDoc.exists;
} catch (e) {
print(e.toString());
return false;
}
*/
}
Future<void> _createUserEntry() async {
try {
var currentUser = AuthService().getCurrentUser();
String currentUserId = currentUser!.uid;
String email = currentUser.email!;
await FirebaseFirestore.instance
.collection(Constants.dbCollectionUsers)
.doc(currentUserId)
.set({
'uid': currentUserId,
'email': email,
});
} catch (e) {
print("Error creating user document: $e");
}
}
Future<bool> _checkCollectionsExist() async { Future<bool> _checkCollectionsExist() async {
bool languagesExist = try {
await _checkUsersCollectionExists(Constants.dbCollectionLanguages); bool languagesExist =
bool locationsExist = await _checkUsersCollectionExists(Constants.dbCollectionLanguages);
await _checkUsersCollectionExists(Constants.dbCollectionLocations); bool locationsExist =
return languagesExist && locationsExist; await _checkUsersCollectionExists(Constants.dbCollectionLocations);
return languagesExist && locationsExist;
} catch (e) {
print(e.toString());
return false;
}
} }
Future<bool> _checkUsersCollectionExists(String collectionName) async { Future<bool> _checkUsersCollectionExists(String collectionName) async {
String currentUserId = AuthService().getCurrentUser()!.uid; try {
final collection = FirebaseFirestore.instance String currentUserId = AuthService().getCurrentUser()!.uid;
.collection(Constants.dbCollectionUsers) final collection = FirebaseFirestore.instance
.doc(currentUserId) .collection(Constants.dbCollectionUsers)
.collection(collectionName); .doc(currentUserId)
final snapshot = await collection.limit(1).get(); .collection(collectionName);
return snapshot.docs.isNotEmpty; final snapshot = await collection.limit(1).get();
return snapshot.docs.isNotEmpty;
} catch (e) {
print(e.toString());
return false;
}
} }
} }