UserDataPage: gender and languages
parent
cccfcb5a3c
commit
feb9335e29
|
@ -8,4 +8,6 @@ class Constants {
|
||||||
static const String dbCollectionLanguages = 'languages';
|
static const String dbCollectionLanguages = 'languages';
|
||||||
static const String dbCollectionChatRooms = 'chat_rooms';
|
static const String dbCollectionChatRooms = 'chat_rooms';
|
||||||
static const String dbCollectionMessages = 'messages';
|
static const String dbCollectionMessages = 'messages';
|
||||||
|
|
||||||
|
static const String pathLanguagesJson = 'lib/assets/languages.json';
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,4 +20,10 @@ class Language {
|
||||||
'iconFile': iconFile,
|
'iconFile': iconFile,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => code.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => other is Language && code == other.code;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,11 @@ class UserDataPage extends StatefulWidget {
|
||||||
State<UserDataPage> createState() => _UserDataPageState();
|
State<UserDataPage> createState() => _UserDataPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Gender { none, male, female, divers }
|
||||||
|
|
||||||
class _UserDataPageState extends State<UserDataPage> {
|
class _UserDataPageState extends State<UserDataPage> {
|
||||||
final TextEditingController _locationController = TextEditingController();
|
final TextEditingController _locationController = TextEditingController();
|
||||||
final TextEditingController _birthdayController = TextEditingController();
|
final TextEditingController _birthdayController = TextEditingController();
|
||||||
final TextEditingController _genderController = TextEditingController();
|
|
||||||
|
|
||||||
List<LanguageSetting> languagesList = [];
|
List<LanguageSetting> languagesList = [];
|
||||||
final List<Language> _selectedLanguages = [];
|
final List<Language> _selectedLanguages = [];
|
||||||
|
@ -29,37 +30,141 @@ class _UserDataPageState extends State<UserDataPage> {
|
||||||
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
||||||
final AuthService _authService = AuthService();
|
final AuthService _authService = AuthService();
|
||||||
|
|
||||||
|
Gender genderView = Gender.none;
|
||||||
|
int _genderFromDb = 0;
|
||||||
|
List<Language> _languagesFromDb = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// Load data from JSON file when the widget initializes
|
|
||||||
loadLanguagesFromJson();
|
// 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 {
|
void saveUserData(BuildContext context) async {
|
||||||
// get userID from auth service
|
// Get userID from auth service
|
||||||
String currentUserID = _authService.getCurrentUser()!.uid;
|
String currentUserID = _authService.getCurrentUser()!.uid;
|
||||||
|
|
||||||
// Get reference to the Firebase collection
|
// Get references to the current users Firebase collections
|
||||||
CollectionReference languagesRef = _firestore
|
DocumentReference userRef =
|
||||||
.collection(Constants.dbCollectionUsers)
|
_firestore.collection(Constants.dbCollectionUsers).doc(currentUserID);
|
||||||
.doc(currentUserID)
|
CollectionReference languagesRef =
|
||||||
.collection(Constants.dbCollectionLanguages);
|
userRef.collection(Constants.dbCollectionLanguages);
|
||||||
|
|
||||||
// Loop through each Language object and save it to the collection
|
// Update Gender in database - only if value has changed
|
||||||
for (int i = 0; i < _selectedLanguages.length; i++) {
|
if (_genderFromDb != genderView.index) {
|
||||||
// Convert Language object to a map and add the map to the collection,
|
await userRef.update(
|
||||||
// using .doc(myID).set() instead of .add() to avoid duplicates.
|
{'gender': genderView.index},
|
||||||
await languagesRef
|
);
|
||||||
.doc(_selectedLanguages[i].code)
|
// update local value
|
||||||
.set(_selectedLanguages[i].toMap());
|
_genderFromDb = genderView.index;
|
||||||
|
} else {
|
||||||
|
print("gender did NOT change");
|
||||||
}
|
}
|
||||||
|
|
||||||
// List to store language codes from the provided list
|
// Save Languages - only if selected values changed
|
||||||
List<String> languageCodes =
|
if (_selectedLanguages.isEmpty) {
|
||||||
_selectedLanguages.map((language) => language.code).toList();
|
print("no language selected");
|
||||||
// Clean up languages that were not part of the provided list
|
} else if (_languagesFromDb.length != _selectedLanguages.length ||
|
||||||
await deleteUnusedDocuments(languagesRef, languageCodes);
|
_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,
|
||||||
|
// using .doc(myID).set() instead of .add() to avoid duplicates.
|
||||||
|
await languagesRef
|
||||||
|
.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
|
/// 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 {
|
void updateSelectedGender(Set<Gender> newSelection) {
|
||||||
// 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
|
|
||||||
setState(() {
|
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,
|
obscureText: false,
|
||||||
controller: _birthdayController,
|
controller: _birthdayController,
|
||||||
),
|
),
|
||||||
const Text(
|
Text(
|
||||||
'Gender',
|
'Gender (${genderView.name} selected)',
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
MyTextField(
|
SegmentedButton<Gender>(
|
||||||
hintText: "Gender",
|
style: SegmentedButton.styleFrom(
|
||||||
obscureText: false,
|
selectedBackgroundColor: Colors.blue,
|
||||||
controller: _genderController,
|
),
|
||||||
|
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(),
|
const Divider(),
|
||||||
Text(
|
Text(
|
||||||
|
@ -181,7 +294,7 @@ class _UserDataPageState extends State<UserDataPage> {
|
||||||
required VoidCallback onClicked,
|
required VoidCallback onClicked,
|
||||||
}) =>
|
}) =>
|
||||||
ListTile(
|
ListTile(
|
||||||
tileColor: Colors.greenAccent,
|
tileColor: Theme.of(context).colorScheme.secondary,
|
||||||
onTap: onClicked,
|
onTap: onClicked,
|
||||||
leading: SizedBox(
|
leading: SizedBox(
|
||||||
width: 48,
|
width: 48,
|
||||||
|
|
Loading…
Reference in New Issue