first registration process
parent
b22b47581c
commit
0c0bf431e2
|
@ -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();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -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'),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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,12 +49,24 @@ 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) {
|
||||||
|
if (isRegProcess && skillsSought) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
// TODO set following registration page HERE
|
||||||
|
builder: (context) => const RegistrationCompletePage(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (isRegProcess) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
|
@ -61,6 +76,10 @@ class SkillsForm extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -482,6 +482,7 @@ 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) {
|
||||||
|
if (widget.isRegProcess) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
|
@ -491,6 +492,9 @@ class _UserDataPageState extends State<UserDataPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
|
|
|
@ -18,6 +18,13 @@ class AuthGate extends StatelessWidget {
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
// check if user is logged in or not
|
// check if user is logged in or not
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
|
return FutureBuilder(
|
||||||
|
// check if user document exists
|
||||||
|
future: _checkUserExists(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return const CircularProgressIndicator();
|
||||||
|
} else if (snapshot.hasData && snapshot.data == true) {
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
// check database entries, if data is missing
|
// check database entries, if data is missing
|
||||||
// then complete registration process
|
// then complete registration process
|
||||||
|
@ -29,11 +36,19 @@ class AuthGate extends StatelessWidget {
|
||||||
} else if (snapshot.hasData && snapshot.data == true) {
|
} else if (snapshot.hasData && snapshot.data == true) {
|
||||||
return HomePage();
|
return HomePage();
|
||||||
} else {
|
} else {
|
||||||
// also in case (snapshot.hasError)
|
// also in case of (snapshot.hasError)
|
||||||
return const UserDataPage(isRegProcess: true);
|
return const UserDataPage(isRegProcess: true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
// should user entry not exist, recreate it and force logout
|
||||||
|
_createUserEntry();
|
||||||
|
FirebaseAuth.instance.signOut();
|
||||||
|
return const LoginOrRegister();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return const LoginOrRegister();
|
return const LoginOrRegister();
|
||||||
}
|
}
|
||||||
|
@ -42,15 +57,58 @@ 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 {
|
||||||
|
try {
|
||||||
bool languagesExist =
|
bool languagesExist =
|
||||||
await _checkUsersCollectionExists(Constants.dbCollectionLanguages);
|
await _checkUsersCollectionExists(Constants.dbCollectionLanguages);
|
||||||
bool locationsExist =
|
bool locationsExist =
|
||||||
await _checkUsersCollectionExists(Constants.dbCollectionLocations);
|
await _checkUsersCollectionExists(Constants.dbCollectionLocations);
|
||||||
return languagesExist && locationsExist;
|
return languagesExist && locationsExist;
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _checkUsersCollectionExists(String collectionName) async {
|
Future<bool> _checkUsersCollectionExists(String collectionName) async {
|
||||||
|
try {
|
||||||
String currentUserId = AuthService().getCurrentUser()!.uid;
|
String currentUserId = AuthService().getCurrentUser()!.uid;
|
||||||
final collection = FirebaseFirestore.instance
|
final collection = FirebaseFirestore.instance
|
||||||
.collection(Constants.dbCollectionUsers)
|
.collection(Constants.dbCollectionUsers)
|
||||||
|
@ -58,5 +116,9 @@ class AuthGate extends StatelessWidget {
|
||||||
.collection(collectionName);
|
.collection(collectionName);
|
||||||
final snapshot = await collection.limit(1).get();
|
final snapshot = await collection.limit(1).get();
|
||||||
return snapshot.docs.isNotEmpty;
|
return snapshot.docs.isNotEmpty;
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue