2024-05-19 17:01:06 +02:00
|
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
2024-06-17 12:51:30 +02:00
|
|
|
import 'package:collection/collection.dart';
|
2024-05-19 17:01:06 +02:00
|
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
|
|
import '../constants.dart';
|
|
|
|
import '../enumerations.dart';
|
2024-05-30 01:10:53 +02:00
|
|
|
import '../models/language.dart';
|
|
|
|
import '../models/location.dart';
|
|
|
|
import '../models/user_profile.dart';
|
2024-05-19 17:01:06 +02:00
|
|
|
|
|
|
|
class UserService {
|
2024-05-30 01:10:53 +02:00
|
|
|
UserService._(); // Private constructor to prevent instantiation
|
2024-05-19 17:01:06 +02:00
|
|
|
|
2024-06-02 16:32:19 +02:00
|
|
|
static Future<void> saveUserRegistrationData(UserCredential userCredential,
|
|
|
|
String email, String firstname, String lastname) async {
|
2024-05-19 17:01:06 +02:00
|
|
|
// create full name
|
|
|
|
String fullName = (firstname.isNotEmpty && lastname.isNotEmpty)
|
|
|
|
? '$firstname $lastname'
|
|
|
|
: (firstname.isNotEmpty
|
|
|
|
? firstname
|
|
|
|
: (lastname.isNotEmpty ? lastname : ''));
|
|
|
|
|
|
|
|
// save user info to users document
|
2024-05-30 01:10:53 +02:00
|
|
|
await FirebaseFirestore.instance
|
2024-05-19 17:01:06 +02:00
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.doc(userCredential.user!.uid)
|
|
|
|
.set(
|
|
|
|
{
|
2024-05-24 00:30:08 +02:00
|
|
|
Constants.dbFieldUsersID: userCredential.user!.uid,
|
|
|
|
Constants.dbFieldUsersEmail: email,
|
|
|
|
Constants.dbFieldUsersFirstName: firstname,
|
|
|
|
Constants.dbFieldUsersLastName: lastname,
|
|
|
|
Constants.dbFieldUsersName: fullName,
|
2024-05-19 17:01:06 +02:00
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-05-30 01:10:53 +02:00
|
|
|
static Future<List<SkillOption>> getSkillsFromFirebase(
|
2024-05-19 17:01:06 +02:00
|
|
|
bool skillsSought, String userId) async {
|
|
|
|
// Fetch skills from Firestore
|
2024-05-30 01:10:53 +02:00
|
|
|
DocumentSnapshot userDoc = await FirebaseFirestore.instance
|
2024-05-19 17:01:06 +02:00
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.doc(userId)
|
|
|
|
.get();
|
|
|
|
|
|
|
|
if (userDoc.exists && userDoc.data() != null) {
|
2024-05-30 16:37:34 +02:00
|
|
|
Map<String, dynamic> userData = userDoc.data()! as Map<String, dynamic>;
|
2024-05-19 17:01:06 +02:00
|
|
|
|
|
|
|
List<dynamic>? skills;
|
|
|
|
if (skillsSought) {
|
|
|
|
skills = userData[Constants.dbFieldUsersSkillsSought];
|
|
|
|
} else {
|
2024-05-30 16:37:34 +02:00
|
|
|
skills = userData[Constants.dbFieldUsersSkills];
|
2024-05-19 17:01:06 +02:00
|
|
|
}
|
|
|
|
|
2024-05-31 18:31:37 +02:00
|
|
|
return convertSkillStringToEnum(skills);
|
2024-05-19 17:01:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2024-05-31 18:31:37 +02:00
|
|
|
static List<SkillOption> convertSkillStringToEnum(List<dynamic>? skills) {
|
2024-06-23 02:04:13 +02:00
|
|
|
List<SkillOption> userSkills = [];
|
2024-05-31 18:31:37 +02:00
|
|
|
if (skills != null && skills.isNotEmpty) {
|
|
|
|
// Convert skills from strings to enum values
|
2024-06-23 02:04:13 +02:00
|
|
|
for (var skill in skills) {
|
|
|
|
try {
|
|
|
|
SkillOption skillOption = SkillOption.values.firstWhere(
|
|
|
|
(x) => x.toString() == 'SkillOption.$skill',
|
|
|
|
);
|
|
|
|
userSkills.add(skillOption);
|
|
|
|
} catch (e) {
|
|
|
|
// Ignore invalid values
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2024-05-31 18:31:37 +02:00
|
|
|
}
|
2024-06-23 02:04:13 +02:00
|
|
|
return userSkills;
|
2024-05-31 18:31:37 +02:00
|
|
|
}
|
|
|
|
|
2024-06-21 02:42:45 +02:00
|
|
|
static List<SectorOption> convertSectorStringToEnum(List<dynamic>? sectors) {
|
|
|
|
if (sectors != null && sectors.isNotEmpty) {
|
|
|
|
List<SectorOption> userSectors = sectors
|
|
|
|
.map((sector) =>
|
|
|
|
SectorOption.values.firstWhere((x) => x.toString() == sector))
|
|
|
|
.toList();
|
|
|
|
return userSectors;
|
|
|
|
}
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2024-05-31 23:16:15 +02:00
|
|
|
static List<VisionOption> convertVisionStringToEnum(List<dynamic>? visions) {
|
|
|
|
if (visions != null && visions.isNotEmpty) {
|
|
|
|
List<VisionOption> userVisions = visions
|
|
|
|
.map((vision) =>
|
|
|
|
VisionOption.values.firstWhere((x) => x.toString() == vision))
|
|
|
|
.toList();
|
|
|
|
return userVisions;
|
|
|
|
}
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
static List<WorkValueOption> convertWorkValuesStringToEnum(
|
|
|
|
List<dynamic>? values) {
|
|
|
|
if (values != null && values.isNotEmpty) {
|
|
|
|
List<WorkValueOption> userWorkValues = values
|
|
|
|
.map((workValue) => WorkValueOption.values
|
|
|
|
.firstWhere((x) => x.toString() == workValue))
|
|
|
|
.toList();
|
|
|
|
return userWorkValues;
|
|
|
|
}
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2024-05-30 01:10:53 +02:00
|
|
|
static Future<bool> saveSkillsToFirebase(List<SkillOption> selectedOptions,
|
2024-05-19 17:01:06 +02:00
|
|
|
bool skillsSought, String userId) async {
|
|
|
|
try {
|
|
|
|
// 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;
|
|
|
|
|
2024-05-30 01:10:53 +02:00
|
|
|
FirebaseFirestore.instance
|
2024-05-19 17:01:06 +02:00
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.doc(userId)
|
|
|
|
.update({keyToUpdate: skills});
|
|
|
|
|
|
|
|
return true;
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2024-05-30 01:10:53 +02:00
|
|
|
|
2024-06-17 12:51:30 +02:00
|
|
|
/// Get UserProfile from Document
|
|
|
|
static Future<UserProfile> getUserProfileFromDocument(
|
|
|
|
DocumentSnapshot userDoc) async {
|
|
|
|
Map<String, dynamic> data = userDoc.data() as Map<String, dynamic>;
|
|
|
|
|
|
|
|
List<SkillOption> skillsOffered = UserService.convertSkillStringToEnum(
|
|
|
|
data[Constants.dbFieldUsersSkills]);
|
|
|
|
List<SkillOption> skillsSought = UserService.convertSkillStringToEnum(
|
|
|
|
data[Constants.dbFieldUsersSkillsSought]);
|
2024-06-21 02:42:45 +02:00
|
|
|
List<SectorOption> sectors = UserService.convertSectorStringToEnum(
|
|
|
|
data[Constants.dbFieldUsersSectors]);
|
2024-06-17 12:51:30 +02:00
|
|
|
List<VisionOption> visions = UserService.convertVisionStringToEnum(
|
|
|
|
data[Constants.dbFieldUsersVisions]);
|
|
|
|
List<WorkValueOption> works = UserService.convertWorkValuesStringToEnum(
|
|
|
|
data[Constants.dbFieldUsersWorkValues]);
|
|
|
|
RiskTolerance risk =
|
|
|
|
RiskTolerance.fromString(data[Constants.dbFieldUsersRiskTolerance]);
|
|
|
|
AvailabilityOption availability =
|
|
|
|
AvailabilityOption.fromString(data[Constants.dbFieldUsersAvailability]);
|
|
|
|
CultureOption culture =
|
|
|
|
CultureOption.fromString(data[Constants.dbFieldUsersCorpCulture]);
|
|
|
|
CommunicationPreference communication = CommunicationPreference.fromString(
|
|
|
|
data[Constants.dbFieldUsersCommunication]);
|
|
|
|
|
|
|
|
// Retrieve sub collections
|
|
|
|
QuerySnapshot languagesSnapshot = await userDoc.reference
|
2024-05-30 01:10:53 +02:00
|
|
|
.collection(Constants.dbCollectionLanguages)
|
|
|
|
.get();
|
|
|
|
List<Language> languages = languagesSnapshot.docs
|
|
|
|
.map((doc) => Language.fromDocument(doc))
|
|
|
|
.toList();
|
|
|
|
|
2024-06-17 12:51:30 +02:00
|
|
|
QuerySnapshot locationsSnapshot = await userDoc.reference
|
2024-05-30 01:10:53 +02:00
|
|
|
.collection(Constants.dbCollectionLocations)
|
|
|
|
.get();
|
2024-06-17 12:51:30 +02:00
|
|
|
|
|
|
|
final mainDoc = locationsSnapshot.docs
|
|
|
|
.firstWhereOrNull((doc) => doc.id == Constants.dbDocMainLocation);
|
|
|
|
final secondaryDoc = locationsSnapshot.docs
|
|
|
|
.firstWhereOrNull((doc) => doc.id == Constants.dbDocSecondLocation);
|
|
|
|
final locations = {
|
|
|
|
Constants.dbDocMainLocation:
|
|
|
|
mainDoc != null ? MyLocation.fromDocument(mainDoc) : null,
|
2024-05-30 01:10:53 +02:00
|
|
|
};
|
2024-06-17 12:51:30 +02:00
|
|
|
if (secondaryDoc != null) {
|
|
|
|
locations.addAll({
|
|
|
|
Constants.dbDocSecondLocation: MyLocation.fromDocument(secondaryDoc)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// Map<String, MyLocation?> locations = {
|
|
|
|
// for (var doc in locationsSnapshot.docs)
|
|
|
|
// doc.id: MyLocation.fromDocument(doc)
|
|
|
|
// };
|
|
|
|
|
|
|
|
return UserProfile(
|
|
|
|
id: userDoc.id,
|
|
|
|
uid: data[Constants.dbFieldUsersID] ?? '',
|
|
|
|
email: data[Constants.dbFieldUsersEmail] ?? '',
|
|
|
|
name: data[Constants.dbFieldUsersName] ?? '',
|
|
|
|
firstName: data[Constants.dbFieldUsersFirstName] ?? '',
|
|
|
|
lastName: data[Constants.dbFieldUsersLastName] ?? '',
|
|
|
|
skills: skillsOffered,
|
|
|
|
skillsSought: skillsSought,
|
2024-06-21 02:42:45 +02:00
|
|
|
sectors: sectors,
|
2024-06-17 12:51:30 +02:00
|
|
|
visions: visions,
|
|
|
|
risk: risk,
|
|
|
|
availability: availability,
|
|
|
|
culture: culture,
|
|
|
|
communication: communication,
|
|
|
|
workValues: works,
|
|
|
|
profilePictureUrl: data[Constants.dbFieldUsersProfilePic],
|
2024-06-24 04:41:17 +02:00
|
|
|
urlFacebook: data[Constants.dbFieldUsersUrlFacebook],
|
|
|
|
urlLinkedIn: data[Constants.dbFieldUsersUrlLinkedIn],
|
|
|
|
urlXing: data[Constants.dbFieldUsersUrlXing],
|
2024-06-17 12:51:30 +02:00
|
|
|
bio: data[Constants.dbFieldUsersBio],
|
|
|
|
gender: Gender.values[data[Constants.dbFieldUsersGender] ?? 0],
|
|
|
|
born: data[Constants.dbFieldUsersYearBorn],
|
|
|
|
languages: languages,
|
|
|
|
locations: locations,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get UserProfile for given [userId]
|
|
|
|
static Future<UserProfile> getUserProfileById(String userId) async {
|
|
|
|
FirebaseFirestore firestore = FirebaseFirestore.instance;
|
|
|
|
|
|
|
|
DocumentSnapshot userDoc = await firestore
|
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.doc(userId)
|
|
|
|
.get();
|
2024-05-30 01:10:53 +02:00
|
|
|
|
2024-06-17 12:51:30 +02:00
|
|
|
UserProfile result = await getUserProfileFromDocument(userDoc);
|
2024-05-30 01:10:53 +02:00
|
|
|
|
2024-06-17 12:51:30 +02:00
|
|
|
return result;
|
2024-05-30 01:10:53 +02:00
|
|
|
}
|
2024-06-02 16:32:19 +02:00
|
|
|
|
2024-06-17 20:39:56 +02:00
|
|
|
/// Get users stream
|
2024-06-02 16:32:19 +02:00
|
|
|
static Stream<List<Map<String, dynamic>>> getUsersStream() {
|
|
|
|
return FirebaseFirestore.instance
|
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.snapshots()
|
|
|
|
.map((snapshot) {
|
|
|
|
return snapshot.docs.map((doc) {
|
2024-06-17 20:39:56 +02:00
|
|
|
// iterate and return each user
|
2024-06-02 16:32:19 +02:00
|
|
|
final user = doc.data();
|
|
|
|
return user;
|
|
|
|
}).toList();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-06-17 20:39:56 +02:00
|
|
|
/// Get list of matched user ids for a given [userId]
|
2024-06-02 16:32:19 +02:00
|
|
|
static Future<List<String>> getMatchedUserIds(String userId) async {
|
|
|
|
List<String> matchedUserIds = [];
|
|
|
|
|
|
|
|
final snapshot = await FirebaseFirestore.instance
|
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.doc(userId)
|
|
|
|
.collection(Constants.dbCollectionMatches)
|
|
|
|
.get();
|
|
|
|
|
|
|
|
for (var doc in snapshot.docs) {
|
|
|
|
final data = doc.data();
|
2024-06-17 20:39:56 +02:00
|
|
|
String? otherUserId = data['otherUserId'];
|
2024-06-02 16:32:19 +02:00
|
|
|
if (otherUserId != null && otherUserId.isNotEmpty) {
|
|
|
|
matchedUserIds.add(otherUserId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return matchedUserIds;
|
|
|
|
}
|
|
|
|
|
2024-06-17 20:39:56 +02:00
|
|
|
/// Get matched users data for a given [userId] as stream
|
2024-06-02 16:32:19 +02:00
|
|
|
static Stream<List<Map<String, dynamic>>> getMatchedUsersStream(
|
|
|
|
String userId) {
|
|
|
|
return FirebaseFirestore.instance
|
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.doc(userId)
|
|
|
|
.collection(Constants.dbCollectionMatches)
|
|
|
|
.snapshots()
|
|
|
|
.asyncMap((snapshot) async {
|
|
|
|
final matchedUserIds = snapshot.docs
|
|
|
|
.map((doc) => doc.data()['otherUserId'] as String?)
|
|
|
|
.where((otherUserId) => otherUserId != null && otherUserId.isNotEmpty)
|
|
|
|
.toList();
|
|
|
|
|
|
|
|
if (matchedUserIds.isEmpty) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
final matchedUsersSnapshot = await FirebaseFirestore.instance
|
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.where(FieldPath.documentId, whereIn: matchedUserIds)
|
|
|
|
.get();
|
|
|
|
|
|
|
|
return matchedUsersSnapshot.docs.map((doc) => doc.data()).toList();
|
|
|
|
});
|
|
|
|
}
|
2024-06-17 20:39:56 +02:00
|
|
|
|
|
|
|
/// Checks if a match between currentUser and [userId] exists.
|
|
|
|
/// Returns false in case of an error.
|
|
|
|
static Future<bool> hasMatch(String currentUserId, String userId) async {
|
2024-06-20 22:47:54 +02:00
|
|
|
try {
|
2024-06-17 20:39:56 +02:00
|
|
|
DocumentSnapshot matchDoc = await FirebaseFirestore.instance
|
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.doc(currentUserId)
|
|
|
|
.collection(Constants.dbCollectionMatches)
|
|
|
|
.doc(userId)
|
|
|
|
.get();
|
|
|
|
return matchDoc.exists;
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2024-06-20 22:47:54 +02:00
|
|
|
|
|
|
|
static Future<bool> saveSectorsToFirebase(
|
|
|
|
List<SectorOption> sectors, String userId) async {
|
|
|
|
try {
|
|
|
|
List<String> sectorStrings = sectors.map((x) => x.toString()).toList();
|
|
|
|
await FirebaseFirestore.instance
|
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.doc(userId)
|
|
|
|
.update({
|
|
|
|
Constants.dbFieldUsersSectors: sectorStrings,
|
|
|
|
});
|
|
|
|
return true;
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Future<List<SectorOption>> getSectorsFromFirebase(
|
|
|
|
String userId) async {
|
|
|
|
try {
|
|
|
|
DocumentSnapshot userDoc = await FirebaseFirestore.instance
|
|
|
|
.collection(Constants.dbCollectionUsers)
|
|
|
|
.doc(userId)
|
|
|
|
.get();
|
|
|
|
if (userDoc.exists && userDoc.data() != null) {
|
|
|
|
var data = userDoc.data() as Map<String, dynamic>;
|
|
|
|
List<dynamic> sectors = data[Constants.dbFieldUsersSectors] ?? [];
|
|
|
|
return sectors
|
|
|
|
.map((x) => SectorOption.values
|
|
|
|
.firstWhere((option) => option.toString() == x))
|
|
|
|
.toList();
|
|
|
|
} else {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
throw Exception('Error fetching sectors from Firebase: $e');
|
|
|
|
}
|
|
|
|
}
|
2024-05-19 17:01:06 +02:00
|
|
|
}
|