diff --git a/lib/components/my_drawer.dart b/lib/components/my_drawer.dart index 1c499e3..047a85a 100644 --- a/lib/components/my_drawer.dart +++ b/lib/components/my_drawer.dart @@ -1,3 +1,4 @@ +import 'package:cofounderella/pages/user_data_page.dart'; import 'package:cofounderella/services/auth/auth_service.dart'; import 'package:cofounderella/pages/settings_page.dart'; import 'package:flutter/material.dart'; @@ -85,6 +86,26 @@ class MyDrawer extends StatelessWidget { ), ), + // TODO TESTING - user data tile + Padding( + padding: const EdgeInsets.only(left: 25), + child: ListTile( + title: const Text("User Data"), + leading: const Icon(Icons.supervised_user_circle), + onTap: () { + // pop the drawer + Navigator.pop(context); + + //navigate to settings page + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const UserDataPage(), + )); + }, + ), + ), + // horizontal line Padding( padding: const EdgeInsets.symmetric(horizontal: 16), diff --git a/lib/models/language.dart b/lib/models/language.dart new file mode 100644 index 0000000..f7e63f1 --- /dev/null +++ b/lib/models/language.dart @@ -0,0 +1,23 @@ +class Language { + final String code; + final String name; + final String nativeName; + final String iconFile; + + Language({ + required this.code, + required this.name, + required this.nativeName, + required this.iconFile, + }); + + // convert to a map + Map toMap() { + return { + 'code': code, + 'name': name, + 'nativeName': nativeName, + 'iconFile': iconFile, + }; + } +} diff --git a/lib/models/language_setting.dart b/lib/models/language_setting.dart new file mode 100644 index 0000000..2baf97d --- /dev/null +++ b/lib/models/language_setting.dart @@ -0,0 +1,11 @@ +import 'language.dart'; + +class LanguageSetting { + Language language; + bool isSelected; + + LanguageSetting({ + required this.language, + this.isSelected = false, + }); +} \ No newline at end of file diff --git a/lib/pages/user_data_page.dart b/lib/pages/user_data_page.dart new file mode 100644 index 0000000..28cb9b9 --- /dev/null +++ b/lib/pages/user_data_page.dart @@ -0,0 +1,202 @@ +import 'dart:convert'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:cofounderella/components/my_button.dart'; +import 'package:cofounderella/components/my_textfield.dart'; +import 'package:cofounderella/models/language.dart'; +import 'package:cofounderella/models/language_setting.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:cofounderella/services/auth/auth_service.dart'; + +class UserDataPage extends StatefulWidget { + const UserDataPage({super.key}); + + @override + State createState() => _UserDataPageState(); +} + +class _UserDataPageState extends State { + final TextEditingController _locationController = TextEditingController(); + final TextEditingController _birthdayController = TextEditingController(); + final TextEditingController _genderController = TextEditingController(); + + List languagesList = []; + final List _selectedLanguages = []; + + // get instance of firestore and auth + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + final AuthService _authService = AuthService(); + + @override + void initState() { + super.initState(); + // Load data from JSON file when the widget initializes + loadLanguagesFromJson(); + } + + void saveUserData(BuildContext context) async { + // get userID from auth service + String currentUserID = _authService.getCurrentUser()!.uid; + + // Get reference to the Firebase collection + CollectionReference languagesRef = _firestore + .collection('Users') + .doc(currentUserID) + .collection('languages'); + + // 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()); + } + + // List to store language codes from the provided list + List languageCodes = + _selectedLanguages.map((language) => language.code).toList(); + // Clean up languages that were not part of the provided list + await deleteUnusedDocuments(languagesRef, languageCodes); + } + + /// Deletes documents from collection that are not part of the provided list + Future deleteUnusedDocuments( + CollectionReference refCollection, List idsToKeep) async { + // Fetch the existing documents from the database + QuerySnapshot snapshot = await refCollection.get(); + + // Loop through each document in the collection + for (QueryDocumentSnapshot doc in snapshot.docs) { + String documentId = doc.id; + // If the language code is not in the provided list, delete the document + if (!idsToKeep.contains(documentId)) { + await doc.reference.delete(); + } + } + } + + Future loadLanguagesFromJson() async { + // Load JSON data from assets + String jsonData = await rootBundle.loadString('lib/assets/languages.json'); + // Parse JSON into a list of objects + List 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(() { + // TODO ? + }); + } + + @override + Widget build(BuildContext context) { + if (languagesList.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } else { + return Scaffold( + appBar: AppBar( + title: const Text("User Data"), + centerTitle: true, + ), + body: ListView( + children: [ + const Text( + 'Location', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + MyTextField( + hintText: "Location", + obscureText: false, + controller: _locationController, + ), + const Text( + 'Birthday', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + MyTextField( + hintText: "Birthday", + obscureText: false, + controller: _birthdayController, + ), + const Text( + 'Gender', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + MyTextField( + hintText: "Gender", + obscureText: false, + controller: _genderController, + ), + const Divider(), + Text( + 'Language: (${_selectedLanguages.length} selected)', + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + ...languagesList.map(buildSingleCheckbox), // ... spread operator + const Divider(), + Padding( + padding: const EdgeInsets.all(8.0), + child: MyButton(text: "Save", onTap: () => saveUserData(context)), + ) + ], + ), + ); + } + } + + Widget buildSingleCheckbox(LanguageSetting languageSetting) => buildCheckbox( + languageSetting: languageSetting, + onClicked: () { + setState(() { + final newValue = !languageSetting.isSelected; + languageSetting.isSelected = newValue; + + if (languageSetting.isSelected && + !_selectedLanguages.contains(languageSetting.language)) { + _selectedLanguages.add(languageSetting.language); + } else if (languageSetting.isSelected == false) { + _selectedLanguages.removeWhere( + (element) => element.code == languageSetting.language.code); + } + }); + }, + ); + + Widget buildCheckbox({ + required LanguageSetting languageSetting, + required VoidCallback onClicked, + }) => + ListTile( + tileColor: Colors.greenAccent, + onTap: onClicked, + leading: SizedBox( + width: 48, + height: 32, + child: SvgPicture.asset( + languageSetting.language.iconFile, + ), + ), + title: Text( + languageSetting.language.nativeName, + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + subtitle: Text(languageSetting.language.name), + trailing: Checkbox( + value: languageSetting.isSelected, + onChanged: (value) => onClicked(), + ), + ); +}