diff --git a/flutter_01.png b/flutter_01.png new file mode 100644 index 0000000..21c5e8e Binary files /dev/null and b/flutter_01.png differ diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d6209ac..3897907 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -834,13 +834,20 @@ PODS: - Firebase/Firestore (10.22.0): - Firebase/CoreOnly - FirebaseFirestore (~> 10.22.0) + - Firebase/Storage (10.22.0): + - Firebase/CoreOnly + - FirebaseStorage (~> 10.22.0) - firebase_auth (4.19.0): - Firebase/Auth (= 10.22.0) - firebase_core - Flutter - - firebase_core (2.28.0): + - firebase_core (2.29.0): - Firebase/CoreOnly (= 10.22.0) - Flutter + - firebase_storage (11.7.1): + - Firebase/Storage (= 10.22.0) + - firebase_core + - Flutter - FirebaseAppCheckInterop (10.23.0) - FirebaseAuth (10.22.0): - FirebaseAppCheckInterop (~> 10.17) @@ -849,6 +856,7 @@ PODS: - GoogleUtilities/Environment (~> 7.8) - GTMSessionFetcher/Core (< 4.0, >= 2.1) - RecaptchaInterop (~> 100.0) + - FirebaseAuthInterop (10.24.0) - FirebaseCore (10.22.0): - FirebaseCoreInternal (~> 10.0) - GoogleUtilities/Environment (~> 7.12) @@ -878,6 +886,12 @@ PODS: - leveldb-library (~> 1.22) - nanopb (< 2.30911.0, >= 2.30908.0) - FirebaseSharedSwift (10.23.0) + - FirebaseStorage (10.22.0): + - FirebaseAppCheckInterop (~> 10.0) + - FirebaseAuthInterop (~> 10.0) + - FirebaseCore (~> 10.0) + - FirebaseCoreExtension (~> 10.0) + - GTMSessionFetcher/Core (< 4.0, >= 2.1) - Flutter (1.0.0) - flutter_native_splash (0.0.1): - Flutter @@ -982,6 +996,8 @@ PODS: - gRPC-Core/Interface (1.62.1) - gRPC-Core/Privacy (1.62.1) - GTMSessionFetcher/Core (3.3.2) + - image_picker_ios (0.0.1): + - Flutter - leveldb-library (1.22.4) - nanopb (2.30909.1): - nanopb/decode (= 2.30909.1) @@ -1000,8 +1016,10 @@ DEPENDENCIES: - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) - Flutter (from `Flutter`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) @@ -1012,12 +1030,14 @@ SPEC REPOS: - Firebase - FirebaseAppCheckInterop - FirebaseAuth + - FirebaseAuthInterop - FirebaseCore - FirebaseCoreExtension - FirebaseCoreInternal - FirebaseFirestore - FirebaseFirestoreInternal - FirebaseSharedSwift + - FirebaseStorage - GoogleUtilities - "gRPC-C++" - gRPC-Core @@ -1034,10 +1054,14 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/firebase_auth/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" + firebase_storage: + :path: ".symlinks/plugins/firebase_storage/ios" Flutter: :path: Flutter flutter_native_splash: :path: ".symlinks/plugins/flutter_native_splash/ios" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" url_launcher_ios: @@ -1049,21 +1073,25 @@ SPEC CHECKSUMS: cloud_firestore: 4991f9c366cc8446171deec57a905c572c964fc1 Firebase: 797fd7297b7e1be954432743a0b3f90038e45a71 firebase_auth: bd4ebe97d1e836aa5692b75c8f13412ed4381871 - firebase_core: d955499180c3c8ef355adf46b8752c4c01d09e0a + firebase_core: aaadbddb3cb2ee3792b9804f9dbb63e5f6f7b55c + firebase_storage: 8d42f27f702499161335b5075aa67bf6f2d821fb FirebaseAppCheckInterop: a1955ce8c30f38f87e7d091630e871e91154d65d FirebaseAuth: bbe4c68f958504ba9e54aee181adbdf5b664fbc6 + FirebaseAuthInterop: 29336ab84df12fc0f340ba5fe58d3e5811a4192d FirebaseCore: 0326ec9b05fbed8f8716cddbf0e36894a13837f7 FirebaseCoreExtension: cb88851781a24e031d1b58e0bd01eb1f46b044b5 FirebaseCoreInternal: 6a292e6f0bece1243a737e81556e56e5e19282e3 FirebaseFirestore: 16cb8a85fc29da272deaed22a101e24703251da9 FirebaseFirestoreInternal: 627b23f682c1c2aad38ba1345ed3ca6574c5a89c FirebaseSharedSwift: c92645b392db3c41a83a0aa967de16f8bad25568 + FirebaseStorage: bc7bddc743548a89cfb896843a77cf4bdde2c231 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778 GoogleUtilities: d053d902a8edaa9904e1bd00c37535385b8ed152 "gRPC-C++": 12f33a422dcab88dcd0c53e52cd549a929f0f244 gRPC-Core: 6ec9002832e1e22c5bb8c54994b050b0ee4205c6 GTMSessionFetcher: 0e876eea9782ec6462e91ab872711c357322c94f + image_picker_ios: b545a5f16c0fa88e3ecbbce3ed4de45567a8ec18 leveldb-library: 06a69cc7582d64b29424a63e085e683cc188230a nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5 path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c diff --git a/lib/data/repositories/user/user_repository.dart b/lib/data/repositories/user/user_repository.dart index b575e1e..07bb14b 100644 --- a/lib/data/repositories/user/user_repository.dart +++ b/lib/data/repositories/user/user_repository.dart @@ -8,6 +8,7 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; import 'package:uni_junction/data/repositories/authentication/authentication_repository.dart'; +import 'package:uni_junction/features/event/models/event/event_model.dart'; import 'package:uni_junction/features/personalization/models/user_model.dart'; import 'package:uni_junction/utils/exceptions/firebase_exceptions.dart'; import 'package:uni_junction/utils/exceptions/format_exceptions.dart'; @@ -101,7 +102,27 @@ class UserRepository extends GetxController { throw e; } } + // Get user liked events + Future> getUserLikedEvents() async { + try { + final user = await fetchUserDetails(); + final likedEventIds = user.likedEvents; + // Print likedEventIds + print('Liked event ids: $likedEventIds'); + + final List likedEvents = []; + for (var eventId in likedEventIds) { + final doc = await _db.collection("Events").doc(eventId).get(); + if (doc.exists) { + likedEvents.add(EventModel.fromSnapshot(doc)); + } + } + return likedEvents; + } catch (e) { + throw e; + } + } //update user Future updateUser(UserModel user) async { try { diff --git a/lib/features/event/controllers/event/event_controller.dart b/lib/features/event/controllers/event/event_controller.dart index b92f910..23ab963 100644 --- a/lib/features/event/controllers/event/event_controller.dart +++ b/lib/features/event/controllers/event/event_controller.dart @@ -15,6 +15,7 @@ import 'package:uni_junction/utils/constants/image_strings.dart'; import 'package:uni_junction/utils/popup/full_screen_loader.dart'; import 'package:uni_junction/data/services/university/universities.dart'; import 'package:uni_junction/data/services/event_category/event_category_list.dart'; +import 'dart:convert'; class EventController extends GetxController { static EventController get instance => Get.find(); @@ -37,6 +38,8 @@ class EventController extends GetxController { final selectedEventCategory = 'Select a category'.obs; final eventCategoryList = eventCategoryLists.obs; + final userLikedEvents = [].obs; + // Variables final startDate = TextEditingController(); final endDate = TextEditingController(); @@ -101,6 +104,21 @@ class EventController extends GetxController { } } + Future fetchUserLikedEvents() async { + try { + final userRepository = Get.put(UserRepository()); + final events = await userRepository.getUserLikedEvents(); + userLikedEvents.assignAll(events); + + // Convert each event to JSON and print it + for (var event in events) { + print(jsonEncode(event.toJson())); + } + } catch (e) { + print(e.toString()); + } + } + Future selectDate(BuildContext context) async { DateTime? picked = await showDatePicker( context: context, @@ -273,8 +291,8 @@ class EventController extends GetxController { // Check if user is attending the event Future isUserAttendingEvent(String eventId) async { - final eventRepository = Get.put(EventRepository()); - return eventRepository.isUserAttending(eventId); + final eventRepository = Get.put(EventRepository()); + return eventRepository.isUserAttending(eventId); } Future pickImage() async { diff --git a/lib/features/event/screens/explore/widgets/category_events.dart b/lib/features/event/screens/explore/widgets/category_events.dart index a31bbcb..4d58201 100644 --- a/lib/features/event/screens/explore/widgets/category_events.dart +++ b/lib/features/event/screens/explore/widgets/category_events.dart @@ -34,7 +34,7 @@ class TCategoryEvents extends StatelessWidget { ), Text( style: TextStyle(fontSize: 17), - textAlign: TextAlign.center, + textAlign: TextAlign.center, "Sorry, there are no events currently for this category"), ], ), diff --git a/lib/features/event/screens/saved/saved.dart b/lib/features/event/screens/saved/saved.dart index a022200..7038125 100644 --- a/lib/features/event/screens/saved/saved.dart +++ b/lib/features/event/screens/saved/saved.dart @@ -1,43 +1,45 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:get/get_connect/http/src/utils/utils.dart'; -import 'package:uni_junction/common/widgets/appbar/appbar.dart'; import 'package:uni_junction/common/widgets/custom_shapes/containers/primary_header_container.dart'; -import 'package:uni_junction/features/event/screens/create_event/widgets/create_event_appbar.dart'; -import 'package:uni_junction/features/event/screens/create_event/widgets/create_event_form.dart'; -import 'package:uni_junction/features/event/screens/home/widgets/this_month_event_all.dart'; +import 'package:uni_junction/features/event/controllers/event/event_controller.dart'; import 'package:uni_junction/features/event/screens/saved/widgets/saved_appbar.dart'; -import 'package:uni_junction/features/event/screens/saved/widgets/saved_event_.dart'; +import 'package:uni_junction/features/event/screens/saved/widgets/saved_events.dart'; import 'package:uni_junction/utils/constants/colors.dart'; -import 'package:uni_junction/utils/constants/text_strings.dart'; +import 'package:get/get.dart'; class SavedScreen extends StatelessWidget { const SavedScreen({super.key}); @override Widget build(BuildContext context) { - // final Save = Get.put(SaveController()); - return const Scaffold( - body: SingleChildScrollView( - child: Column( - children: [ - TPrimaryHeaderContainer( - height: 150, - backgroundColor: TColors.primary, - child: Column( - children: [ - // Home AppBar - TSavedEventAppBar(), - SizedBox(height: 20), - ], - ), + final eventController = Get.put(EventController()); + + return Scaffold( + body: RefreshIndicator( + onRefresh: () async { + PaintingBinding.instance.imageCache.clear(); + await eventController.fetchUserLikedEvents(); + await Future.delayed(const Duration(seconds: 1)); + }, + child: SingleChildScrollView( + child: Column( + children: [ + TPrimaryHeaderContainer( + height: 150, + backgroundColor: TColors.primary, + child: Column( + children: [ + TSavedEventAppBar(), + SizedBox(height: 20), + ], + ), + ), + SizedBox(height: 20), + TSavedEvents(), + ], ), - // TCreateEventForm(controller: Save,), - // TThisMonthEventCards(), - SizedBox(height: 20), - // TSavedEventCards(), - ], + ), ), - )); + ); } } diff --git a/lib/features/event/screens/saved/widgets/.gitkeep b/lib/features/event/screens/saved/widgets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/features/event/screens/saved/widgets/saved_event_.dart b/lib/features/event/screens/saved/widgets/saved_event_.dart deleted file mode 100644 index 39aa307..0000000 --- a/lib/features/event/screens/saved/widgets/saved_event_.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:uni_junction/features/event/screens/home/widgets/this_month_event_cards.dart'; -import 'package:uni_junction/features/event/screens/saved/widgets/saved_appbar.dart'; -import 'package:uni_junction/test/test_page.dart'; -import 'package:uni_junction/utils/constants/sizes.dart'; - -class TSavedEventCards extends StatelessWidget { - final int id; - final String date; - final String month; - final String eventName; - final String location; - final String imageUrl; - - TSavedEventCards({ - Key? key, - required this.id, - required this.date, - required this.month, - required this.eventName, - required this.location, - required this.imageUrl, - }) : super(key: key); - -factory TSavedEventCards.fromJson(Map json) { - return TSavedEventCards( - id: json['id'], - eventName: json['eventName'], - location: json['location'], - date: json['date'], - month: json['month'], - imageUrl: json['imageUrl'], - ); - } - - - - - @override - Widget build(BuildContext context) { - return Column( - children: [ - // Use the parameters here to build your widget. - ], - ); - } -} \ No newline at end of file diff --git a/lib/features/event/screens/saved/widgets/saved_event_appbar.dart b/lib/features/event/screens/saved/widgets/saved_event_appbar.dart deleted file mode 100644 index 0720d46..0000000 --- a/lib/features/event/screens/saved/widgets/saved_event_appbar.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:iconsax/iconsax.dart'; -import 'package:uni_junction/common/widgets/appbar/appbar.dart'; -import 'package:uni_junction/common/widgets/loaders/shimmer_loader.dart'; -import 'package:uni_junction/features/personalization/controllers/user_controller.dart'; -import 'package:uni_junction/utils/constants/colors.dart'; -import 'package:uni_junction/utils/constants/sizes.dart'; - -class TSavedEventAppBar extends StatelessWidget { - const TSavedEventAppBar({super.key}); - - @override - Widget build(BuildContext context) { - final controller = Get.put(UserController()); - - return TAppBar( - title: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Center( - child: Text( - "Create Event", - style: Theme.of(context) - .textTheme - .headlineMedium! - .apply(color: TColors.accent), - ), - ), - const SizedBox(height: TSizes.sm), - Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon( - Iconsax.user, - color: TColors.darkGrey, - size: 18, - ), - const SizedBox(width: 5), - Obx( - () { - if (controller.profileLodaing.value) { - return const TShimmerEffect(width: 100, height: 20); - } - return Flexible( - child: Text( - controller.user.value.username, - style: Theme.of(context) - .textTheme - .labelMedium! - .apply(color: TColors.darkGrey), - overflow: TextOverflow.ellipsis, - ), - ); - }, - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/lib/features/event/screens/saved/widgets/saved_event_cards.dart b/lib/features/event/screens/saved/widgets/saved_event_cards.dart deleted file mode 100644 index 87e25e5..0000000 --- a/lib/features/event/screens/saved/widgets/saved_event_cards.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uni_junction/common/widgets/loaders/snackbar_popup.dart'; -import 'package:uni_junction/utils/constants/colors.dart'; -import 'package:uni_junction/utils/constants/sizes.dart'; - -class SavedEventMonth extends StatelessWidget { - const SavedEventMonth( - {super.key, - required this.eventName, - required this.location, - required this.date, - required this.month, - required this.imageUrl, - this.onTap}); - - final String eventName; - final String location; - final String date; - final String month; - final String imageUrl; - final void Function()? onTap; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: TSizes.defaultSpace), - child: Container( - height: 80, - color: Colors.transparent, - child: Row( - children: [ - // Small Image on the left - Container( - width: 60, - height: double.infinity, - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(10)), - image: DecorationImage( - image: NetworkImage(imageUrl), - fit: BoxFit.cover, - ), - ), - ), - - // Event details in the center - Expanded( - child: Padding( - padding: const EdgeInsets.only( - left: TSizes.md, - right: TSizes.md, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Text( - eventName, - style: const TextStyle( - fontSize: 16, fontWeight: FontWeight.bold), - ), - Row( - children: [ - const Icon( - Icons.location_on, - size: 16, - color: TColors.darkGrey, - ), - Text( - location, - style: const TextStyle( - fontSize: 14, - color: TColors.darkGrey, - ), - ), - ], - ), - const Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: SizedBox( - height: 30, - child: Stack( - children: [ - Positioned( - left: 0, - child: CircleAvatar( - radius: 15, - backgroundImage: NetworkImage( - 'https://avatar.iran.liara.run/public/21'))), - Positioned( - left: 18, - child: CircleAvatar( - radius: 15, - backgroundImage: NetworkImage( - 'https://avatar.iran.liara.run/public/22'))), - Positioned( - left: 36, - child: CircleAvatar( - radius: 15, - backgroundImage: NetworkImage( - 'https://avatar.iran.liara.run/public/20'))), - Positioned( - left: 54, - child: CircleAvatar( - radius: 15, - backgroundImage: NetworkImage( - 'https://avatar.iran.liara.run/public/19'))), - Positioned( - left: 72, - child: CircleAvatar( - radius: 15, - backgroundImage: NetworkImage( - 'https://avatar.iran.liara.run/public/25'))), - ], - ), - ), - ), - //Text('250+ joined', style: TextStyle( - //fontSize: 12, - //color: TColors.darkGrey, - //),), - ], - ) - ], - ), - ), - ), - - // Date on the right - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - padding: const EdgeInsets.all(5), - decoration: BoxDecoration( - color: TColors.primary, - borderRadius: BorderRadius.circular(5), - ), - child: Column( - children: [ - Text( - date, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: TColors.accent), - ), - Text( - month, - style: const TextStyle( - fontSize: 16, color: TColors.accent), - ), - ], - ), - ), - ], - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/features/event/screens/saved/widgets/saved_events.dart b/lib/features/event/screens/saved/widgets/saved_events.dart new file mode 100644 index 0000000..3c9ef04 --- /dev/null +++ b/lib/features/event/screens/saved/widgets/saved_events.dart @@ -0,0 +1,102 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:uni_junction/features/event/controllers/event/category_controller.dart'; +import 'package:uni_junction/features/event/controllers/event/event_controller.dart'; +import 'package:uni_junction/features/event/screens/event_details/event_details.dart'; +import 'package:uni_junction/features/event/screens/home/widgets/this_month_event_cards.dart'; +import 'package:uni_junction/test/test_page.dart'; +import 'package:uni_junction/utils/constants/sizes.dart'; +import 'package:intl/intl.dart'; + +class TSavedEvents extends StatelessWidget { + const TSavedEvents({ + super.key, + }); + + @override + Widget build(BuildContext context) { + final eventController = Get.find(); + return GetBuilder( + init: eventController, + initState: (_) { + eventController.fetchUserLikedEvents(); + }, + builder: (controller) { + return controller.userLikedEvents.isEmpty + ? Center( + child: Padding( + padding: EdgeInsets.symmetric( + vertical: 220.0, + horizontal: 50.0, + ), + child: Column( + children: [ + Image.network( + "https://img.freepik.com/free-vector/event-calendar-notification-freelancer-project-deadline-date-appointment-reminder-calendar-megaphone-isolated-design-element-time-management-concept-illustration_335657-1693.jpg", + width: 190, + height: 190, + ), + Text( + style: TextStyle(fontSize: 17), + textAlign: TextAlign.center, + "Sorry, there are no saved events"), + ], + ), + ), + ) + : Column( + children: controller.userLikedEvents.expand((event) { + final startDate = DateFormat('d MMM').format(event.startDate); + final defaultImageUrl = "https://unsplash.it/645/411"; + return [ + EventThisMonth( + date: startDate.split(' ')[0], + month: startDate.split(' ')[1], + eventName: event.title, + location: event.location ?? 'Location not provided', + imageUrl: event.imageUrl.isEmpty + ? defaultImageUrl + : event.imageUrl, + onTap: () => { + eventController.selectedEventId.value = event.id, + eventController.selectedTitle.value = event.title, + eventController.selectedDescription.value = + event.description, + eventController.selectedStartDate.value = + event.startDate.toString(), + eventController.selectedEndDate.value = + event.endDate.toString(), + eventController.selectedStartTime.value = + event.startTime.toString(), + eventController.selectedEndTime.value = + event.endTime.toString(), + eventController.selectedLocation.value = event.location, + eventController.selectedEventUrl.value = + event.eventUrl.toString(), + eventController.selectedOrgName.value = + event.orgName.toString(), + eventController.selectedTicketPrice.value = + event.ticketPrice.toString(), + eventController.selectedHeadCount.value = + event.headCount.toString(), + eventController.selectedIsOnline.value = + event.isOnline.toString() == 'true', + eventController.selectedIsOrg.value = + event.isOrg.toString() == 'true', + eventController.selectedIsPrivate.value = + event.isPrivate.toString() == 'true', + eventController.selectedIsTicketed.value = + event.isTicketed.toString() == 'true', + Get.to( + () => EventDetailsScreen(), + ), + }, + ), + SizedBox(height: TSizes.spaceBtwItems), + ]; + }).toList(), + ); + }, + ); + } +}