UserDataPage: gender and languages

master
Rafael 2024-05-06 15:21:21 +02:00
parent cccfcb5a3c
commit feb9335e29
3 changed files with 170 additions and 49 deletions

View File

@ -8,4 +8,6 @@ class Constants {
static const String dbCollectionLanguages = 'languages';
static const String dbCollectionChatRooms = 'chat_rooms';
static const String dbCollectionMessages = 'messages';
static const String pathLanguagesJson = 'lib/assets/languages.json';
}

View File

@ -20,4 +20,10 @@ class Language {
'iconFile': iconFile,
};
}
@override
int get hashCode => code.hashCode;
@override
bool operator ==(Object other) => other is Language && code == other.code;
}

View File

@ -17,10 +17,11 @@ class UserDataPage extends StatefulWidget {
State<UserDataPage> createState() => _UserDataPageState();
}
enum Gender { none, male, female, divers }
class _UserDataPageState extends State<UserDataPage> {
final TextEditingController _locationController = TextEditingController();
final TextEditingController _birthdayController = TextEditingController();
final TextEditingController _genderController = TextEditingController();
List<LanguageSetting> languagesList = [];
final List<Language> _selectedLanguages = [];
@ -29,23 +30,121 @@ class _UserDataPageState extends State<UserDataPage> {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final AuthService _authService = AuthService();
Gender genderView = Gender.none;
int _genderFromDb = 0;
List<Language> _languagesFromDb = [];
@override
void initState() {
super.initState();
// load settings from database
_fetchSettings();
}
Future<void> _fetchSettings() async {
try {
// Fetch user ID
String currentUserId = _authService.getCurrentUser()!.uid;
// Fetch user document fields (email, uid, gender, ...) from database
DocumentSnapshot userSnapshot = await _firestore
.collection(Constants.dbCollectionUsers)
.doc(_authService.getCurrentUser()!.uid)
.get();
// Extract gender
setState(() {
_genderFromDb = userSnapshot['gender'];
genderView = Gender.values[_genderFromDb];
});
// Fetch languages
QuerySnapshot languagesSnapshot = await _firestore
.collection(Constants.dbCollectionUsers)
.doc(currentUserId)
.collection(Constants.dbCollectionLanguages)
.get();
List<Language> userLanguages = [];
for (var doc in languagesSnapshot.docs) {
//languages.add(Language.fromDocument(doc));
userLanguages.add(Language(
code: doc.id, // aka doc['code'],
name: doc['name'],
nativeName: doc['nativeName'],
iconFile: doc['iconFile'],
));
}
setState(() {
_languagesFromDb = userLanguages;
});
// Load data from JSON file when the widget initializes
loadLanguagesFromJson();
} catch (error) {
print("Error fetching settings: $error");
}
}
/// Loads the languages to display
Future<void> loadLanguagesFromJson() async {
// Load JSON data from assets
String jsonData = await rootBundle.loadString(Constants.pathLanguagesJson);
// Parse JSON into a list of objects
List<dynamic> jsonList = await json.decode(jsonData);
// Create LanguageSetting objects from JSON data
languagesList = jsonList.map((item) {
Language language = Language(
code: item['code'],
name: item['name'],
nativeName: item['nativeName'],
iconFile: item['iconFile'],
);
return LanguageSetting(
language: language,
isSelected: _languagesFromDb.any(
(language) => language.code == item['code'],
),
);
}).toList();
// Update the UI after loading data
setState(() {
if (_selectedLanguages.isEmpty) {
_selectedLanguages.addAll(_languagesFromDb);
}
});
}
void saveUserData(BuildContext context) async {
// get userID from auth service
// Get userID from auth service
String currentUserID = _authService.getCurrentUser()!.uid;
// Get reference to the Firebase collection
CollectionReference languagesRef = _firestore
.collection(Constants.dbCollectionUsers)
.doc(currentUserID)
.collection(Constants.dbCollectionLanguages);
// Get references to the current users Firebase collections
DocumentReference userRef =
_firestore.collection(Constants.dbCollectionUsers).doc(currentUserID);
CollectionReference languagesRef =
userRef.collection(Constants.dbCollectionLanguages);
// Update Gender in database - only if value has changed
if (_genderFromDb != genderView.index) {
await userRef.update(
{'gender': genderView.index},
);
// update local value
_genderFromDb = genderView.index;
} else {
print("gender did NOT change");
}
// Save Languages - only if selected values changed
if (_selectedLanguages.isEmpty) {
print("no language selected");
} else if (_languagesFromDb.length != _selectedLanguages.length ||
_selectedLanguages
.any((element) => !_languagesFromDb.contains(element))) {
// Loop through each Language object and save it to the collection
for (int i = 0; i < _selectedLanguages.length; i++) {
// Convert Language object to a map and add the map to the collection,
@ -54,12 +153,18 @@ class _UserDataPageState extends State<UserDataPage> {
.doc(_selectedLanguages[i].code)
.set(_selectedLanguages[i].toMap());
}
// update local variable (only if selection is not empty)
_languagesFromDb.clear();
_languagesFromDb.addAll(_selectedLanguages);
// List to store language codes from the provided list
List<String> languageCodes =
_selectedLanguages.map((language) => language.code).toList();
// Clean up languages that were not part of the provided list
await deleteUnusedDocuments(languagesRef, languageCodes);
} else {
print("languages did NOT change");
}
}
/// Deletes documents from collection that are not part of the provided list
@ -78,27 +183,12 @@ class _UserDataPageState extends State<UserDataPage> {
}
}
Future<void> loadLanguagesFromJson() async {
// Load JSON data from assets
String jsonData = await rootBundle.loadString('lib/assets/languages.json');
// Parse JSON into a list of objects
List<dynamic> jsonList = await json.decode(jsonData);
// Create LanguageSetting objects from JSON data
languagesList = jsonList.map((item) {
Language language = Language(
code: item['code'],
name: item['name'],
nativeName: item['nativeName'],
iconFile: item['iconFile'],
);
// TODO Werte aus DB laden
return LanguageSetting(language: language);
}).toList();
// Update the UI after loading data
void updateSelectedGender(Set<Gender> newSelection) {
setState(() {
// TODO ?
// By default there is only a single segment that can be
// selected at one time, so its value is always the first
// item in the selected set.
genderView = newSelection.first;
});
}
@ -132,14 +222,37 @@ class _UserDataPageState extends State<UserDataPage> {
obscureText: false,
controller: _birthdayController,
),
const Text(
'Gender',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
Text(
'Gender (${genderView.name} selected)',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
MyTextField(
hintText: "Gender",
obscureText: false,
controller: _genderController,
SegmentedButton<Gender>(
style: SegmentedButton.styleFrom(
selectedBackgroundColor: Colors.blue,
),
segments: const <ButtonSegment<Gender>>[
ButtonSegment<Gender>(
value: Gender.none,
label: Text('none'),
),
ButtonSegment<Gender>(
value: Gender.male,
label: Text('male'),
//icon: Icon(Icons.male_sharp),
),
ButtonSegment<Gender>(
value: Gender.female,
label: Text('female'),
//icon: Icon(Icons.female_sharp),
),
ButtonSegment<Gender>(
value: Gender.divers,
label: Text('divers'),
),
],
selected: <Gender>{genderView},
showSelectedIcon: false,
onSelectionChanged: updateSelectedGender,
),
const Divider(),
Text(
@ -181,7 +294,7 @@ class _UserDataPageState extends State<UserDataPage> {
required VoidCallback onClicked,
}) =>
ListTile(
tileColor: Colors.greenAccent,
tileColor: Theme.of(context).colorScheme.secondary,
onTap: onClicked,
leading: SizedBox(
width: 48,