From e970e3ae9a5e51a82ef96eb8b32b78ebb2c10034 Mon Sep 17 00:00:00 2001 From: mahendra-918 Date: Sat, 15 Mar 2025 08:24:25 +0530 Subject: [PATCH 1/5] you --- lib/app/data/models/alarm_model.dart | 9 +- lib/app/data/models/sort_mode.dart | 39 +++ lib/app/data/models/word_challenge.dart | 97 +++++++ .../providers/secure_storage_provider.dart | 16 ++ .../home/controllers/home_controller.dart | 129 +++++++++ .../controllers/settings_controller.dart | 42 +++ .../modules/settings/views/settings_view.dart | 11 + .../settings/views/sort_options_view.dart | 148 +++++++++++ .../bindings/word_builder_binding.dart | 11 + .../controllers/word_builder_controller.dart | 40 +++ .../word_builder/views/word_builder_view.dart | 249 ++++++++++++++++++ 11 files changed, 789 insertions(+), 2 deletions(-) create mode 100644 lib/app/data/models/sort_mode.dart create mode 100644 lib/app/data/models/word_challenge.dart create mode 100644 lib/app/modules/settings/views/sort_options_view.dart create mode 100644 lib/app/modules/word_builder/bindings/word_builder_binding.dart create mode 100644 lib/app/modules/word_builder/controllers/word_builder_controller.dart create mode 100644 lib/app/modules/word_builder/views/word_builder_view.dart diff --git a/lib/app/data/models/alarm_model.dart b/lib/app/data/models/alarm_model.dart index 4bd43aad..17e087b2 100644 --- a/lib/app/data/models/alarm_model.dart +++ b/lib/app/data/models/alarm_model.dart @@ -62,6 +62,9 @@ class AlarmModel { @ignore Map? offsetDetails; + @Index() + late DateTime lastModifiedDate; + AlarmModel( {required this.alarmTime, required this.alarmID, @@ -109,7 +112,10 @@ class AlarmModel { required this.isGuardian, required this.guardianTimer, required this.guardian, - required this.isCall}); + required this.isCall, + DateTime? lastModifiedDate}) { + this.lastModifiedDate = lastModifiedDate ?? DateTime.now(); + } AlarmModel.fromDocumentSnapshot({ required firestore.DocumentSnapshot documentSnapshot, @@ -337,7 +343,6 @@ class AlarmModel { guardianTimer = alarmData['guardianTimer']; guardian = alarmData['guardian']; isCall = alarmData['isCall']; - ringOn = alarmData['ringOn']; } AlarmModel.fromJson(String alarmData, UserModel? user) { diff --git a/lib/app/data/models/sort_mode.dart b/lib/app/data/models/sort_mode.dart new file mode 100644 index 00000000..db031169 --- /dev/null +++ b/lib/app/data/models/sort_mode.dart @@ -0,0 +1,39 @@ +enum AlarmSortMode { + defaultSort, // Current implementation (enabled + next occurrence) + timeOfDay, // Sort by time regardless of enabled status + label, // Alphabetical sorting by label + creationDate, // Most recently created first + lastModified, // Most recently modified first + customOrder; // Manual drag-and-drop ordering + + String get displayName { + switch (this) { + case AlarmSortMode.defaultSort: + return 'Smart Sort (Enabled + Next Occurrence)'; + case AlarmSortMode.timeOfDay: + return 'Time of Day'; + case AlarmSortMode.label: + return 'Alarm Label'; + case AlarmSortMode.creationDate: + return 'Creation Date'; + case AlarmSortMode.lastModified: + return 'Last Modified'; + case AlarmSortMode.customOrder: + return 'Custom Order'; + } + } +} + +enum SortDirection { + ascending, + descending; + + String get displayName { + switch (this) { + case SortDirection.ascending: + return 'Ascending'; + case SortDirection.descending: + return 'Descending'; + } + } +} \ No newline at end of file diff --git a/lib/app/data/models/word_challenge.dart b/lib/app/data/models/word_challenge.dart new file mode 100644 index 00000000..ec0b0e19 --- /dev/null +++ b/lib/app/data/models/word_challenge.dart @@ -0,0 +1,97 @@ +import 'dart:math'; + +class WordChallenge { + final String targetWord; + final List scrambledLetters; + final String hint; + final int difficulty; // 1: Easy, 2: Medium, 3: Hard + + WordChallenge({ + required this.targetWord, + required this.scrambledLetters, + required this.hint, + required this.difficulty, + }); + + factory WordChallenge.generate(int difficulty) { + // Word lists by difficulty + const easyWords = [ + {'word': 'WAKE', 'hint': 'Stop sleeping'}, + {'word': 'RISE', 'hint': 'Get up'}, + {'word': 'TIME', 'hint': 'What an alarm tracks'}, + {'word': 'DAWN', 'hint': 'Early morning'}, + {'word': 'DAY', 'hint': 'Opposite of night'}, + ]; + + const mediumWords = [ + {'word': 'MORNING', 'hint': 'Start of the day'}, + {'word': 'SUNRISE', 'hint': 'When the day begins'}, + {'word': 'AWAKEN', 'hint': 'Become conscious'}, + {'word': 'ACTIVE', 'hint': 'Not idle'}, + {'word': 'ENERGY', 'hint': 'Power to move'}, + ]; + + const hardWords = [ + {'word': 'CONSCIOUS', 'hint': 'Aware and alert'}, + {'word': 'VIGILANT', 'hint': 'Staying watchful'}, + {'word': 'DAYBREAK', 'hint': 'First light of day'}, + {'word': 'SCHEDULE', 'hint': 'Daily plan'}, + {'word': 'PUNCTUAL', 'hint': 'Always on time'}, + ]; + + // Select word list based on difficulty + final wordList = difficulty == 1 + ? easyWords + : difficulty == 2 + ? mediumWords + : hardWords; + + // Randomly select a word-hint pair + final random = Random(); + final wordData = wordList[random.nextInt(wordList.length)]; + final word = wordData['word']!; + final hint = wordData['hint']!; + + // Create scrambled letters + List letters = word.split(''); + letters.shuffle(random); + + // Add some random extra letters for increased difficulty + if (difficulty > 1) { + final extraLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + .split('') + .where((l) => !word.contains(l)) + .toList(); + extraLetters.shuffle(random); + letters.addAll(extraLetters.take(difficulty == 2 ? 2 : 4)); + letters.shuffle(random); + } + + return WordChallenge( + targetWord: word, + scrambledLetters: letters, + hint: hint, + difficulty: difficulty, + ); + } + + bool checkWord(String attempt) { + return attempt.toUpperCase() == targetWord; + } + + bool canFormWord(String attempt) { + // Check if the attempted word can be formed from available letters + Map letterCount = {}; + for (String letter in scrambledLetters) { + letterCount[letter] = (letterCount[letter] ?? 0) + 1; + } + + for (String letter in attempt.toUpperCase().split('')) { + if (!letterCount.containsKey(letter) || letterCount[letter]! <= 0) { + return false; + } + letterCount[letter] = letterCount[letter]! - 1; + } + return true; + } +} \ No newline at end of file diff --git a/lib/app/data/providers/secure_storage_provider.dart b/lib/app/data/providers/secure_storage_provider.dart index 24592e8d..8d860847 100644 --- a/lib/app/data/providers/secure_storage_provider.dart +++ b/lib/app/data/providers/secure_storage_provider.dart @@ -212,4 +212,20 @@ class SecureStorageProvider { value: timerId.toString(), ); } + + Future readSortMode(String key) async { + return await _secureStorage.read(key: key); + } + + Future readSortDirection(String key) async { + return await _secureStorage.read(key: key); + } + + Future writeSortMode(String key, String value) async { + await _secureStorage.write(key: key, value: value); + } + + Future writeSortDirection(String key, String value) async { + await _secureStorage.write(key: key, value: value); + } } diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index edcabf86..61a0ecb1 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -19,6 +19,8 @@ import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider. import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart'; import 'package:ultimate_alarm_clock/app/utils/constants.dart'; import 'package:ultimate_alarm_clock/app/utils/utils.dart'; +import 'package:ultimate_alarm_clock/app/data/models/sort_mode.dart'; +import 'package:ultimate_alarm_clock/app/modules/settings/controllers/settings_controller.dart'; import '../../../data/models/profile_model.dart'; import '../../../data/providers/google_cloud_api_provider.dart'; @@ -735,4 +737,131 @@ class HomeController extends GetxController { isCall: profileModel.value.isCall, ringOn: false); } + + void sortAlarms(List alarms) { + final settingsController = Get.find(); + + if (!settingsController.isSortedAlarmListEnabled.value) { + return; + } + + switch (settingsController.currentSortMode.value) { + case AlarmSortMode.defaultSort: + _defaultSort(alarms); + break; + case AlarmSortMode.timeOfDay: + _timeOfDaySort(alarms); + break; + case AlarmSortMode.label: + _labelSort(alarms); + break; + case AlarmSortMode.creationDate: + _creationDateSort(alarms); + break; + case AlarmSortMode.lastModified: + _lastModifiedSort(alarms); + break; + case AlarmSortMode.customOrder: + // Custom order is handled by drag and drop + break; + } + + // Apply reverse sort if direction is descending + if (settingsController.currentSortDirection.value == SortDirection.descending) { + alarms = alarms.reversed.toList(); + } + } + + void _defaultSort(List alarms) { + alarms.sort((a, b) { + // First sort by isEnabled + if (a.isEnabled != b.isEnabled) { + return a.isEnabled ? -1 : 1; + } + + // Then sort by upcoming time + return _compareUpcomingTime(a, b); + }); + } + + void _timeOfDaySort(List alarms) { + alarms.sort((a, b) { + return a.minutesSinceMidnight.compareTo(b.minutesSinceMidnight); + }); + } + + void _labelSort(List alarms) { + alarms.sort((a, b) { + return (a.label ?? '').compareTo(b.label ?? ''); + }); + } + + void _creationDateSort(List alarms) { + alarms.sort((a, b) { + return b.alarmID.compareTo(a.alarmID); // Assuming alarmID is sequential + }); + } + + void _lastModifiedSort(List alarms) { + // Assuming we add lastModifiedDate to AlarmModel + alarms.sort((a, b) { + return b.lastModifiedDate.compareTo(a.lastModifiedDate); + }); + } + + int _compareUpcomingTime(AlarmModel a, AlarmModel b) { + int aUpcomingTime = a.minutesSinceMidnight; + int bUpcomingTime = b.minutesSinceMidnight; + + // Check if alarm repeats on any day + bool aRepeats = a.days.any((day) => day); + bool bRepeats = b.days.any((day) => day); + + // Calculate next occurrence for repeating alarms + if (aRepeats) { + aUpcomingTime = _calculateNextOccurrence(a); + } else if (aUpcomingTime <= DateTime.now().hour * 60 + DateTime.now().minute) { + aUpcomingTime += Duration.minutesPerDay; + } + + if (bRepeats) { + bUpcomingTime = _calculateNextOccurrence(b); + } else if (bUpcomingTime <= DateTime.now().hour * 60 + DateTime.now().minute) { + bUpcomingTime += Duration.minutesPerDay; + } + + return aUpcomingTime.compareTo(bUpcomingTime); + } + + int _calculateNextOccurrence(AlarmModel alarm) { + int currentDay = DateTime.now().weekday - 1; + int minutesSinceMidnight = alarm.minutesSinceMidnight; + int currentTimeInMinutes = DateTime.now().hour * 60 + DateTime.now().minute; + + // Find the next day when the alarm is scheduled + for (int i = 0; i < alarm.days.length; i++) { + int dayIndex = (currentDay + i) % alarm.days.length; + if (alarm.days[dayIndex]) { + if (i == 0 && minutesSinceMidnight <= currentTimeInMinutes) { + continue; // Skip today if alarm time has passed + } + return minutesSinceMidnight + i * Duration.minutesPerDay; + } + } + + // If we get here, the next occurrence is on the first enabled day next week + for (int i = 0; i < alarm.days.length; i++) { + if (alarm.days[i]) { + return minutesSinceMidnight + (7 - currentDay + i) * Duration.minutesPerDay; + } + } + + return minutesSinceMidnight; // Fallback + } + + // Add this method to refresh the alarm list when sort settings change + void refreshAlarmList() { + // Trigger a rebuild of the alarm list + update(); + } } diff --git a/lib/app/modules/settings/controllers/settings_controller.dart b/lib/app/modules/settings/controllers/settings_controller.dart index 5db0c333..1bbe33ca 100644 --- a/lib/app/modules/settings/controllers/settings_controller.dart +++ b/lib/app/modules/settings/controllers/settings_controller.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:ultimate_alarm_clock/app/data/models/user_model.dart'; +import 'package:ultimate_alarm_clock/app/data/models/sort_mode.dart'; import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart'; import 'package:ultimate_alarm_clock/app/modules/home/controllers/home_controller.dart'; @@ -65,6 +66,12 @@ class SettingsController extends GetxController { }, }; + final _sortModeKey = 'sortMode'; + final _sortDirectionKey = 'sortDirection'; + + final Rx currentSortMode = AlarmSortMode.defaultSort.obs; + final Rx currentSortDirection = SortDirection.ascending.obs; + @override void onInit() async { super.onInit(); @@ -74,6 +81,18 @@ class SettingsController extends GetxController { userModel.value = await _secureStorageProvider.retrieveUserModel(); } _loadPreference(); + + // Load sort preferences + String? savedSortMode = await _secureStorageProvider.readSortMode(_sortModeKey); + String? savedSortDirection = await _secureStorageProvider.readSortDirection(_sortDirectionKey); + + currentSortMode.value = savedSortMode != null + ? AlarmSortMode.values.firstWhere((e) => e.name == savedSortMode) + : AlarmSortMode.defaultSort; + + currentSortDirection.value = savedSortDirection != null + ? SortDirection.values.firstWhere((e) => e.name == savedSortDirection) + : SortDirection.ascending; } // Logins user using GoogleSignIn @@ -242,4 +261,27 @@ class SettingsController extends GetxController { storage.writeCurrentLanguage(local.value); storage.writeLocale(languageCode, countryCode); } + + void _saveSortPreferences() async { + await _secureStorageProvider.writeSortMode( + _sortModeKey, + currentSortMode.value.name, + ); + await _secureStorageProvider.writeSortDirection( + _sortDirectionKey, + currentSortDirection.value.name, + ); + } + + void updateSortMode(AlarmSortMode mode) { + currentSortMode.value = mode; + _saveSortPreferences(); + homeController.refreshAlarmList(); + } + + void updateSortDirection(SortDirection direction) { + currentSortDirection.value = direction; + _saveSortPreferences(); + homeController.refreshAlarmList(); + } } diff --git a/lib/app/modules/settings/views/settings_view.dart b/lib/app/modules/settings/views/settings_view.dart index 680430cd..e09ecc8e 100644 --- a/lib/app/modules/settings/views/settings_view.dart +++ b/lib/app/modules/settings/views/settings_view.dart @@ -12,6 +12,8 @@ import 'package:ultimate_alarm_clock/app/modules/settings/views/theme_value_tile import 'package:ultimate_alarm_clock/app/utils/utils.dart'; import '../controllers/settings_controller.dart'; import 'google_sign_in.dart'; +import 'package:ultimate_alarm_clock/app/modules/settings/views/sort_options_view.dart'; +import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart'; class SettingsView extends GetView { const SettingsView({super.key}); @@ -114,6 +116,15 @@ class SettingsView extends GetView { const SizedBox( height: 20, ), + Padding( + padding: EdgeInsets.symmetric(vertical: 10), + child: SortOptionsView( + controller: controller, + themeController: Get.find(), + height: height, + width: width, + ), + ), ], ), ), diff --git a/lib/app/modules/settings/views/sort_options_view.dart b/lib/app/modules/settings/views/sort_options_view.dart new file mode 100644 index 00000000..5b035cdd --- /dev/null +++ b/lib/app/modules/settings/views/sort_options_view.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:ultimate_alarm_clock/app/data/models/sort_mode.dart'; +import 'package:ultimate_alarm_clock/app/modules/settings/controllers/settings_controller.dart'; +import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart'; +import 'package:ultimate_alarm_clock/app/utils/constants.dart'; + +class SortOptionsView extends StatelessWidget { + final SettingsController controller; + final ThemeController themeController; + final double height; + final double width; + + const SortOptionsView({ + super.key, + required this.controller, + required this.themeController, + required this.height, + required this.width, + }); + + @override + Widget build(BuildContext context) { + return Obx( + () => Container( + width: width * 0.91, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(18)), + color: themeController.secondaryBackgroundColor.value, + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Sort Options'.tr, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + color: themeController.primaryTextColor.value, + ), + ), + const SizedBox(height: 15), + // Enable/Disable sorting switch + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Enable Sorting'.tr, + style: Theme.of(context).textTheme.bodyLarge!.copyWith( + color: themeController.primaryTextColor.value, + ), + ), + Switch.adaptive( + value: controller.isSortedAlarmListEnabled.value, + activeColor: ksecondaryColor, + onChanged: (bool value) { + controller.toggleSortedAlarmList(value); + }, + ), + ], + ), + const SizedBox(height: 10), + // Sort Mode Dropdown + if (controller.isSortedAlarmListEnabled.value) ...[ + Text( + 'Sort By'.tr, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: themeController.secondaryTextColor.value, + ), + ), + const SizedBox(height: 5), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: themeController.primaryBackgroundColor.value, + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + value: controller.currentSortMode.value, + isExpanded: true, + dropdownColor: themeController.primaryBackgroundColor.value, + padding: const EdgeInsets.symmetric(horizontal: 15), + items: AlarmSortMode.values.map((mode) { + return DropdownMenuItem( + value: mode, + child: Text( + mode.displayName, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: themeController.primaryTextColor.value, + ), + ), + ); + }).toList(), + onChanged: (mode) { + if (mode != null) controller.updateSortMode(mode); + }, + ), + ), + ), + const SizedBox(height: 15), + // Sort Direction + if (controller.currentSortMode.value != AlarmSortMode.customOrder) ...[ + Text( + 'Direction'.tr, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: themeController.secondaryTextColor.value, + ), + ), + const SizedBox(height: 5), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: themeController.primaryBackgroundColor.value, + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + value: controller.currentSortDirection.value, + isExpanded: true, + dropdownColor: themeController.primaryBackgroundColor.value, + padding: const EdgeInsets.symmetric(horizontal: 15), + items: SortDirection.values.map((direction) { + return DropdownMenuItem( + value: direction, + child: Text( + direction.displayName, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: themeController.primaryTextColor.value, + ), + ), + ); + }).toList(), + onChanged: (direction) { + if (direction != null) { + controller.updateSortDirection(direction); + } + }, + ), + ), + ), + ], + ], + ], + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/app/modules/word_builder/bindings/word_builder_binding.dart b/lib/app/modules/word_builder/bindings/word_builder_binding.dart new file mode 100644 index 00000000..91a05d65 --- /dev/null +++ b/lib/app/modules/word_builder/bindings/word_builder_binding.dart @@ -0,0 +1,11 @@ +import 'package:get/get.dart'; +import 'package:ultimate_alarm_clock/app/modules/word_builder/controllers/word_builder_controller.dart'; + +class WordBuilderBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut( + () => WordBuilderController(), + ); + } +} \ No newline at end of file diff --git a/lib/app/modules/word_builder/controllers/word_builder_controller.dart b/lib/app/modules/word_builder/controllers/word_builder_controller.dart new file mode 100644 index 00000000..39c8b2c3 --- /dev/null +++ b/lib/app/modules/word_builder/controllers/word_builder_controller.dart @@ -0,0 +1,40 @@ +import 'package:get/get.dart'; +import 'package:ultimate_alarm_clock/app/data/models/word_challenge.dart'; + +class WordBuilderController extends GetxController { + final difficulty = 1.obs; + final Rx currentChallenge = Rx(null); + final RxBool isCompleted = false.obs; + final RxInt attemptsCount = 0.obs; + + @override + void onInit() { + super.onInit(); + generateNewChallenge(); + } + + void generateNewChallenge() { + currentChallenge.value = WordChallenge.generate(difficulty.value); + isCompleted.value = false; + attemptsCount.value = 0; + } + + void setDifficulty(int level) { + difficulty.value = level; + generateNewChallenge(); + } + + bool checkWord(String word) { + if (currentChallenge.value == null) return false; + attemptsCount.value++; + final isCorrect = currentChallenge.value!.checkWord(word); + if (isCorrect) { + isCompleted.value = true; + } + return isCorrect; + } + + bool canFormWord(String word) { + return currentChallenge.value?.canFormWord(word) ?? false; + } +} \ No newline at end of file diff --git a/lib/app/modules/word_builder/views/word_builder_view.dart b/lib/app/modules/word_builder/views/word_builder_view.dart new file mode 100644 index 00000000..0c435e44 --- /dev/null +++ b/lib/app/modules/word_builder/views/word_builder_view.dart @@ -0,0 +1,249 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:ultimate_alarm_clock/app/data/models/word_challenge.dart'; +import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart'; +import 'package:ultimate_alarm_clock/app/utils/constants.dart'; + +class WordBuilderView extends StatefulWidget { + final Function onComplete; + final int difficulty; + final ThemeController themeController; + + const WordBuilderView({ + Key? key, + required this.onComplete, + required this.difficulty, + required this.themeController, + }) : super(key: key); + + @override + State createState() => _WordBuilderViewState(); +} + +class _WordBuilderViewState extends State { + late WordChallenge challenge; + String currentWord = ''; + List selectedLetters = []; + List letterUsed = []; + bool showError = false; + bool showSuccess = false; + + @override + void initState() { + super.initState(); + challenge = WordChallenge.generate(widget.difficulty); + letterUsed = List.generate(challenge.scrambledLetters.length, (index) => false); + } + + void _selectLetter(int index) { + if (!letterUsed[index]) { + setState(() { + selectedLetters.add(challenge.scrambledLetters[index]); + letterUsed[index] = true; + currentWord = selectedLetters.join(); + showError = false; + }); + } + } + + void _removeLetter(int index) { + setState(() { + // Find the last occurrence of this letter in the scrambled letters + final scrambledIndex = challenge.scrambledLetters.lastIndexWhere( + (letter) => letter == selectedLetters[index] && letterUsed[challenge.scrambledLetters.indexOf(letter)], + ); + if (scrambledIndex != -1) { + letterUsed[scrambledIndex] = false; + } + selectedLetters.removeAt(index); + currentWord = selectedLetters.join(); + showError = false; + }); + } + + void _checkWord() { + if (challenge.checkWord(currentWord)) { + setState(() { + showSuccess = true; + showError = false; + }); + Future.delayed(const Duration(milliseconds: 500), () { + widget.onComplete(); + }); + } else { + setState(() { + showError = true; + }); + } + } + + void _clearWord() { + setState(() { + selectedLetters.clear(); + letterUsed = List.generate(challenge.scrambledLetters.length, (index) => false); + currentWord = ''; + showError = false; + }); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Build the Word'.tr, + style: Theme.of(context).textTheme.headlineSmall!.copyWith( + color: widget.themeController.primaryTextColor.value, + ), + ), + const SizedBox(height: 20), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: widget.themeController.secondaryBackgroundColor.value, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + 'Hint: ${challenge.hint}', + style: Theme.of(context).textTheme.titleMedium!.copyWith( + color: widget.themeController.secondaryTextColor.value, + ), + ), + ), + const SizedBox(height: 30), + // Selected letters display + Container( + height: 60, + padding: const EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + border: Border.all( + color: showError + ? Colors.red + : showSuccess + ? Colors.green + : widget.themeController.primaryTextColor.value, + ), + borderRadius: BorderRadius.circular(10), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (selectedLetters.isEmpty) + Text( + 'Select letters below'.tr, + style: TextStyle( + color: widget.themeController.secondaryTextColor.value, + ), + ) + else + Wrap( + spacing: 5, + children: List.generate( + selectedLetters.length, + (index) => GestureDetector( + onTap: () => _removeLetter(index), + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + color: widget.themeController.primaryBackgroundColor.value, + borderRadius: BorderRadius.circular(5), + ), + child: Text( + selectedLetters[index], + style: TextStyle( + fontSize: 24, + color: widget.themeController.primaryTextColor.value, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ], + ), + ), + const SizedBox(height: 20), + // Available letters + Wrap( + spacing: 10, + runSpacing: 10, + alignment: WrapAlignment.center, + children: List.generate( + challenge.scrambledLetters.length, + (index) => GestureDetector( + onTap: () => _selectLetter(index), + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: letterUsed[index] + ? widget.themeController.secondaryBackgroundColor.value + : ksecondaryColor, + borderRadius: BorderRadius.circular(5), + ), + child: Center( + child: Text( + challenge.scrambledLetters[index], + style: TextStyle( + fontSize: 24, + color: letterUsed[index] + ? widget.themeController.secondaryTextColor.value + : Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ), + ), + const SizedBox(height: 20), + // Action buttons + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: _clearWord, + style: ElevatedButton.styleFrom( + backgroundColor: widget.themeController.secondaryBackgroundColor.value, + ), + child: Text( + 'Clear'.tr, + style: TextStyle( + color: widget.themeController.primaryTextColor.value, + ), + ), + ), + ElevatedButton( + onPressed: selectedLetters.isNotEmpty ? _checkWord : null, + style: ElevatedButton.styleFrom( + backgroundColor: ksecondaryColor, + ), + child: Text( + 'Check'.tr, + style: const TextStyle( + color: Colors.white, + ), + ), + ), + ], + ), + if (showError) ...[ + const SizedBox(height: 10), + Text( + 'Try again!'.tr, + style: const TextStyle( + color: Colors.red, + fontSize: 16, + ), + ), + ], + ], + ), + ); + } +} \ No newline at end of file From 2cafc4a03770ab723a3bb9c016101bf0785b5dc1 Mon Sep 17 00:00:00 2001 From: mahendra-918 Date: Tue, 18 Mar 2025 15:15:39 +0530 Subject: [PATCH 2/5] changes --- lib/app/data/models/alarm_model.dart | 8 +- .../add_or_update_alarm_controller.dart | 3 +- .../views/guardian_angel.dart | 148 +++++++++--------- .../home/controllers/home_controller.dart | 88 ++--------- lib/app/utils/utils.dart | 1 + 5 files changed, 101 insertions(+), 147 deletions(-) diff --git a/lib/app/data/models/alarm_model.dart b/lib/app/data/models/alarm_model.dart index 17e087b2..3bfe01bd 100644 --- a/lib/app/data/models/alarm_model.dart +++ b/lib/app/data/models/alarm_model.dart @@ -185,6 +185,11 @@ class AlarmModel { guardianTimer = documentSnapshot['guardianTimer']; guardian = documentSnapshot['guardian']; isCall = documentSnapshot['isCall']; + + // Handle lastModifiedDate field + lastModifiedDate = documentSnapshot['lastModifiedDate'] != null + ? (documentSnapshot['lastModifiedDate'] as firestore.Timestamp).toDate() + : DateTime.now(); } AlarmModel fromMapSQFlite(Map map) { @@ -400,7 +405,8 @@ class AlarmModel { 'guardianTimer': alarmRecord.guardianTimer, 'guardian': alarmRecord.guardian, 'isCall': alarmRecord.isCall, - 'ringOn': alarmRecord.ringOn + 'ringOn': alarmRecord.ringOn, + 'lastModifiedDate': firestore.Timestamp.fromDate(alarmRecord.lastModifiedDate), }; if (alarmRecord.isSharedAlarmEnabled) { diff --git a/lib/app/modules/addOrUpdateAlarm/controllers/add_or_update_alarm_controller.dart b/lib/app/modules/addOrUpdateAlarm/controllers/add_or_update_alarm_controller.dart index cdc9a591..1a40b3c7 100644 --- a/lib/app/modules/addOrUpdateAlarm/controllers/add_or_update_alarm_controller.dart +++ b/lib/app/modules/addOrUpdateAlarm/controllers/add_or_update_alarm_controller.dart @@ -1075,6 +1075,7 @@ class AddOrUpdateAlarmController extends GetxController { guardian: guardian.value, isCall: isCall.value, ringOn: isFutureDate.value, + lastModifiedDate: DateTime.now(), ); } @@ -1351,7 +1352,6 @@ class AddOrUpdateAlarmController extends GetxController { storage.writeProfile(profileModel.profileName); homeController.writeProfileName(profileModel.profileName); } -} int orderedCountryCode(Country countryA, Country countryB) { // `??` for null safety of 'dialCode' @@ -1360,3 +1360,4 @@ class AddOrUpdateAlarmController extends GetxController { return int.parse(dialCodeA).compareTo(int.parse(dialCodeB)); } +} diff --git a/lib/app/modules/addOrUpdateAlarm/views/guardian_angel.dart b/lib/app/modules/addOrUpdateAlarm/views/guardian_angel.dart index b0315a11..25c9f47f 100644 --- a/lib/app/modules/addOrUpdateAlarm/views/guardian_angel.dart +++ b/lib/app/modules/addOrUpdateAlarm/views/guardian_angel.dart @@ -7,7 +7,7 @@ import 'package:ultimate_alarm_clock/app/utils/constants.dart'; import 'package:ultimate_alarm_clock/app/utils/utils.dart'; import 'package:permission_handler/permission_handler.dart'; -class GuardianAngel extends StatelessWidget { +class GuardianAngel extends GetView { const GuardianAngel({ super.key, required this.controller, @@ -19,93 +19,97 @@ class GuardianAngel extends StatelessWidget { @override Widget build(BuildContext context) { - // Check if using Firestore and the current user is the owner - // and if not using, just show the tile - return Column( children: [ ListTile( onTap: () async { - var phonePerm = - await Permission.phone.request().isGranted; - var smsPerm = await Permission.sms.request().isGranted; + var phonePerm = await Permission.phone.request().isGranted; + var smsPerm = await Permission.sms.request().isGranted; - if (phonePerm && smsPerm) { - Get.dialog( - Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(18), - ), - backgroundColor: themeController - .secondaryBackgroundColor.value, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: InternationalPhoneNumberInput( - textFieldController: controller - .contactTextEditingController, - onInputChanged: (value) {}, - onInputValidated: (value) {}, - spaceBetweenSelectorAndTextField: 0, - selectorConfig: const SelectorConfig( - showFlags: true, - setSelectorButtonAsPrefixIcon: true, - leadingPadding: 0, - trailingSpace: false, - countryComparator: orderedCountryCode, - ), - ), + if (phonePerm && smsPerm) { + Get.dialog( + Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18), ), - Padding( - padding: EdgeInsets.symmetric( - vertical: controller - .homeController.scalingFactor * - 8, - horizontal: controller - .homeController.scalingFactor * - 4, - ), - child: Row( + backgroundColor: themeController.secondaryBackgroundColor.value, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, children: [ - Option(0, Icons.sms, 'Text'), - Option(1, Icons.call, 'Call'), - const Spacer(), - Submit(), + Padding( + padding: const EdgeInsets.all(8.0), + child: InternationalPhoneNumberInput( + textFieldController: controller.contactTextEditingController, + onInputChanged: (PhoneNumber number) { + controller.guardian.value = number.phoneNumber ?? ''; + }, + selectorConfig: const SelectorConfig( + selectorType: PhoneInputSelectorType.DIALOG, + showFlags: true, + setSelectorButtonAsPrefixIcon: true, + leadingPadding: 0, + trailingSpace: false, + ), + ignoreBlank: false, + autoValidateMode: AutovalidateMode.disabled, + selectorTextStyle: TextStyle(color: themeController.primaryTextColor.value), + formatInput: true, + keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true), + inputDecoration: InputDecoration( + contentPadding: EdgeInsets.only(bottom: 15, left: 0), + border: OutlineInputBorder( + borderSide: BorderSide(color: themeController.primaryTextColor.value), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: themeController.primaryTextColor.value), + ), + ), + ), + ), + Padding( + padding: EdgeInsets.symmetric( + vertical: controller.homeController.scalingFactor * 8, + horizontal: controller.homeController.scalingFactor * 4, + ), + child: Row( + children: [ + Option(0, Icons.sms, 'Text'), + Option(1, Icons.call, 'Call'), + const Spacer(), + Submit(), + ], + ), + ), ], ), ), - ], - ), - ), - ), - ); - } - }, - title: Row( - children: [ - FittedBox( - fit: BoxFit.scaleDown, - alignment: Alignment.centerLeft, - child: Obx( - () => Text( - 'Guardian Angel'.tr, - style: TextStyle( - color: themeController.primaryTextColor.value, + ), + ); + } + }, + title: Row( + children: [ + FittedBox( + fit: BoxFit.scaleDown, + alignment: Alignment.centerLeft, + child: Obx( + () => Text( + 'Guardian Angel'.tr, + style: TextStyle( + color: themeController.primaryTextColor.value, + ), ), ), ), - ), Obx( () => IconButton( icon: Icon( - Icons.info_sharp, - size: 21, - color: themeController.primaryTextColor.value.withOpacity(0.3), - ), + Icons.info_sharp, + size: 21, + color: themeController.primaryTextColor.value.withOpacity(0.3), + ), onPressed: () { Utils.showModal( context: context, diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index 61a0ecb1..b085d1e9 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -155,73 +155,8 @@ class HomeController extends GetxController { ...latestIsarAlarms, ]; - if (isSortedAlarmListEnabled.value) { - alarms.sort((a, b) { - final String timeA = a.alarmTime; - final String timeB = b.alarmTime; - - // Convert the alarm time strings to DateTime objects for comparison - DateTime dateTimeA = DateFormat('HH:mm').parse(timeA); - DateTime dateTimeB = DateFormat('HH:mm').parse(timeB); - - // Compare the DateTime objects to sort in ascending order - return dateTimeA.compareTo(dateTimeB); - }); - } else { - alarms.sort((a, b) { - // First sort by isEnabled - if (a.isEnabled != b.isEnabled) { - return a.isEnabled ? -1 : 1; - } - - // Then sort by upcoming time - int aUpcomingTime = a.minutesSinceMidnight; - int bUpcomingTime = b.minutesSinceMidnight; - - // Check if alarm repeats on any day - bool aRepeats = a.days.any((day) => day); - bool bRepeats = b.days.any((day) => day); - - // If alarm repeats on any day, find the next up+coming day - if (aRepeats) { - int currentDay = DateTime.now().weekday - 1; - for (int i = 0; i < a.days.length; i++) { - int dayIndex = (currentDay + i) % a.days.length; - if (a.days[dayIndex]) { - aUpcomingTime += i * Duration.minutesPerDay; - break; - } - } - } else { - // If alarm is one-time and has already passed, set upcoming time - // to next day - if (aUpcomingTime <= - DateTime.now().hour * 60 + DateTime.now().minute) { - aUpcomingTime += Duration.minutesPerDay; - } - } - - if (bRepeats) { - int currentDay = DateTime.now().weekday - 1; - for (int i = 0; i < b.days.length; i++) { - int dayIndex = (currentDay + i) % b.days.length; - if (b.days[dayIndex]) { - bUpcomingTime += i * Duration.minutesPerDay; - break; - } - } - } else { - // If alarm is one-time and has already passed, set upcoming time - // to next day - if (bUpcomingTime <= - DateTime.now().hour * 60 + DateTime.now().minute) { - bUpcomingTime += Duration.minutesPerDay; - } - } - - return aUpcomingTime.compareTo(bUpcomingTime); - }); - } + // Use the proper sorting functionality + sortAlarms(alarms); return alarms; }, @@ -745,21 +680,24 @@ class HomeController extends GetxController { return; } + // Create a copy of the list to sort + List sortedAlarms = List.from(alarms); + switch (settingsController.currentSortMode.value) { case AlarmSortMode.defaultSort: - _defaultSort(alarms); + _defaultSort(sortedAlarms); break; case AlarmSortMode.timeOfDay: - _timeOfDaySort(alarms); + _timeOfDaySort(sortedAlarms); break; case AlarmSortMode.label: - _labelSort(alarms); + _labelSort(sortedAlarms); break; case AlarmSortMode.creationDate: - _creationDateSort(alarms); + _creationDateSort(sortedAlarms); break; case AlarmSortMode.lastModified: - _lastModifiedSort(alarms); + _lastModifiedSort(sortedAlarms); break; case AlarmSortMode.customOrder: // Custom order is handled by drag and drop @@ -768,8 +706,12 @@ class HomeController extends GetxController { // Apply reverse sort if direction is descending if (settingsController.currentSortDirection.value == SortDirection.descending) { - alarms = alarms.reversed.toList(); + sortedAlarms = sortedAlarms.reversed.toList(); } + + // Clear and update the original list + alarms.clear(); + alarms.addAll(sortedAlarms); } void _defaultSort(List alarms) { diff --git a/lib/app/utils/utils.dart b/lib/app/utils/utils.dart index 53c16127..b58586ad 100644 --- a/lib/app/utils/utils.dart +++ b/lib/app/utils/utils.dart @@ -76,6 +76,7 @@ class Utils { guardian: '', isCall: false, ringOn: false, + lastModifiedDate: DateTime.now(), ); static String formatDateTimeToHHMMSS(DateTime dateTime) { From 64ac127f590cc359171e6e5d91a7a514a1a0acf8 Mon Sep 17 00:00:00 2001 From: mahendra-918 Date: Tue, 18 Mar 2025 15:24:51 +0530 Subject: [PATCH 3/5] removed word challenge --- lib/app/data/models/word_challenge.dart | 97 ------- .../bindings/word_builder_binding.dart | 11 - .../controllers/word_builder_controller.dart | 40 --- .../word_builder/views/word_builder_view.dart | 249 ------------------ 4 files changed, 397 deletions(-) delete mode 100644 lib/app/data/models/word_challenge.dart delete mode 100644 lib/app/modules/word_builder/bindings/word_builder_binding.dart delete mode 100644 lib/app/modules/word_builder/controllers/word_builder_controller.dart delete mode 100644 lib/app/modules/word_builder/views/word_builder_view.dart diff --git a/lib/app/data/models/word_challenge.dart b/lib/app/data/models/word_challenge.dart deleted file mode 100644 index ec0b0e19..00000000 --- a/lib/app/data/models/word_challenge.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'dart:math'; - -class WordChallenge { - final String targetWord; - final List scrambledLetters; - final String hint; - final int difficulty; // 1: Easy, 2: Medium, 3: Hard - - WordChallenge({ - required this.targetWord, - required this.scrambledLetters, - required this.hint, - required this.difficulty, - }); - - factory WordChallenge.generate(int difficulty) { - // Word lists by difficulty - const easyWords = [ - {'word': 'WAKE', 'hint': 'Stop sleeping'}, - {'word': 'RISE', 'hint': 'Get up'}, - {'word': 'TIME', 'hint': 'What an alarm tracks'}, - {'word': 'DAWN', 'hint': 'Early morning'}, - {'word': 'DAY', 'hint': 'Opposite of night'}, - ]; - - const mediumWords = [ - {'word': 'MORNING', 'hint': 'Start of the day'}, - {'word': 'SUNRISE', 'hint': 'When the day begins'}, - {'word': 'AWAKEN', 'hint': 'Become conscious'}, - {'word': 'ACTIVE', 'hint': 'Not idle'}, - {'word': 'ENERGY', 'hint': 'Power to move'}, - ]; - - const hardWords = [ - {'word': 'CONSCIOUS', 'hint': 'Aware and alert'}, - {'word': 'VIGILANT', 'hint': 'Staying watchful'}, - {'word': 'DAYBREAK', 'hint': 'First light of day'}, - {'word': 'SCHEDULE', 'hint': 'Daily plan'}, - {'word': 'PUNCTUAL', 'hint': 'Always on time'}, - ]; - - // Select word list based on difficulty - final wordList = difficulty == 1 - ? easyWords - : difficulty == 2 - ? mediumWords - : hardWords; - - // Randomly select a word-hint pair - final random = Random(); - final wordData = wordList[random.nextInt(wordList.length)]; - final word = wordData['word']!; - final hint = wordData['hint']!; - - // Create scrambled letters - List letters = word.split(''); - letters.shuffle(random); - - // Add some random extra letters for increased difficulty - if (difficulty > 1) { - final extraLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - .split('') - .where((l) => !word.contains(l)) - .toList(); - extraLetters.shuffle(random); - letters.addAll(extraLetters.take(difficulty == 2 ? 2 : 4)); - letters.shuffle(random); - } - - return WordChallenge( - targetWord: word, - scrambledLetters: letters, - hint: hint, - difficulty: difficulty, - ); - } - - bool checkWord(String attempt) { - return attempt.toUpperCase() == targetWord; - } - - bool canFormWord(String attempt) { - // Check if the attempted word can be formed from available letters - Map letterCount = {}; - for (String letter in scrambledLetters) { - letterCount[letter] = (letterCount[letter] ?? 0) + 1; - } - - for (String letter in attempt.toUpperCase().split('')) { - if (!letterCount.containsKey(letter) || letterCount[letter]! <= 0) { - return false; - } - letterCount[letter] = letterCount[letter]! - 1; - } - return true; - } -} \ No newline at end of file diff --git a/lib/app/modules/word_builder/bindings/word_builder_binding.dart b/lib/app/modules/word_builder/bindings/word_builder_binding.dart deleted file mode 100644 index 91a05d65..00000000 --- a/lib/app/modules/word_builder/bindings/word_builder_binding.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:get/get.dart'; -import 'package:ultimate_alarm_clock/app/modules/word_builder/controllers/word_builder_controller.dart'; - -class WordBuilderBinding extends Bindings { - @override - void dependencies() { - Get.lazyPut( - () => WordBuilderController(), - ); - } -} \ No newline at end of file diff --git a/lib/app/modules/word_builder/controllers/word_builder_controller.dart b/lib/app/modules/word_builder/controllers/word_builder_controller.dart deleted file mode 100644 index 39c8b2c3..00000000 --- a/lib/app/modules/word_builder/controllers/word_builder_controller.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:get/get.dart'; -import 'package:ultimate_alarm_clock/app/data/models/word_challenge.dart'; - -class WordBuilderController extends GetxController { - final difficulty = 1.obs; - final Rx currentChallenge = Rx(null); - final RxBool isCompleted = false.obs; - final RxInt attemptsCount = 0.obs; - - @override - void onInit() { - super.onInit(); - generateNewChallenge(); - } - - void generateNewChallenge() { - currentChallenge.value = WordChallenge.generate(difficulty.value); - isCompleted.value = false; - attemptsCount.value = 0; - } - - void setDifficulty(int level) { - difficulty.value = level; - generateNewChallenge(); - } - - bool checkWord(String word) { - if (currentChallenge.value == null) return false; - attemptsCount.value++; - final isCorrect = currentChallenge.value!.checkWord(word); - if (isCorrect) { - isCompleted.value = true; - } - return isCorrect; - } - - bool canFormWord(String word) { - return currentChallenge.value?.canFormWord(word) ?? false; - } -} \ No newline at end of file diff --git a/lib/app/modules/word_builder/views/word_builder_view.dart b/lib/app/modules/word_builder/views/word_builder_view.dart deleted file mode 100644 index 0c435e44..00000000 --- a/lib/app/modules/word_builder/views/word_builder_view.dart +++ /dev/null @@ -1,249 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:ultimate_alarm_clock/app/data/models/word_challenge.dart'; -import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart'; -import 'package:ultimate_alarm_clock/app/utils/constants.dart'; - -class WordBuilderView extends StatefulWidget { - final Function onComplete; - final int difficulty; - final ThemeController themeController; - - const WordBuilderView({ - Key? key, - required this.onComplete, - required this.difficulty, - required this.themeController, - }) : super(key: key); - - @override - State createState() => _WordBuilderViewState(); -} - -class _WordBuilderViewState extends State { - late WordChallenge challenge; - String currentWord = ''; - List selectedLetters = []; - List letterUsed = []; - bool showError = false; - bool showSuccess = false; - - @override - void initState() { - super.initState(); - challenge = WordChallenge.generate(widget.difficulty); - letterUsed = List.generate(challenge.scrambledLetters.length, (index) => false); - } - - void _selectLetter(int index) { - if (!letterUsed[index]) { - setState(() { - selectedLetters.add(challenge.scrambledLetters[index]); - letterUsed[index] = true; - currentWord = selectedLetters.join(); - showError = false; - }); - } - } - - void _removeLetter(int index) { - setState(() { - // Find the last occurrence of this letter in the scrambled letters - final scrambledIndex = challenge.scrambledLetters.lastIndexWhere( - (letter) => letter == selectedLetters[index] && letterUsed[challenge.scrambledLetters.indexOf(letter)], - ); - if (scrambledIndex != -1) { - letterUsed[scrambledIndex] = false; - } - selectedLetters.removeAt(index); - currentWord = selectedLetters.join(); - showError = false; - }); - } - - void _checkWord() { - if (challenge.checkWord(currentWord)) { - setState(() { - showSuccess = true; - showError = false; - }); - Future.delayed(const Duration(milliseconds: 500), () { - widget.onComplete(); - }); - } else { - setState(() { - showError = true; - }); - } - } - - void _clearWord() { - setState(() { - selectedLetters.clear(); - letterUsed = List.generate(challenge.scrambledLetters.length, (index) => false); - currentWord = ''; - showError = false; - }); - } - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Build the Word'.tr, - style: Theme.of(context).textTheme.headlineSmall!.copyWith( - color: widget.themeController.primaryTextColor.value, - ), - ), - const SizedBox(height: 20), - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: widget.themeController.secondaryBackgroundColor.value, - borderRadius: BorderRadius.circular(10), - ), - child: Text( - 'Hint: ${challenge.hint}', - style: Theme.of(context).textTheme.titleMedium!.copyWith( - color: widget.themeController.secondaryTextColor.value, - ), - ), - ), - const SizedBox(height: 30), - // Selected letters display - Container( - height: 60, - padding: const EdgeInsets.symmetric(horizontal: 10), - decoration: BoxDecoration( - border: Border.all( - color: showError - ? Colors.red - : showSuccess - ? Colors.green - : widget.themeController.primaryTextColor.value, - ), - borderRadius: BorderRadius.circular(10), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (selectedLetters.isEmpty) - Text( - 'Select letters below'.tr, - style: TextStyle( - color: widget.themeController.secondaryTextColor.value, - ), - ) - else - Wrap( - spacing: 5, - children: List.generate( - selectedLetters.length, - (index) => GestureDetector( - onTap: () => _removeLetter(index), - child: Container( - padding: const EdgeInsets.all(5), - decoration: BoxDecoration( - color: widget.themeController.primaryBackgroundColor.value, - borderRadius: BorderRadius.circular(5), - ), - child: Text( - selectedLetters[index], - style: TextStyle( - fontSize: 24, - color: widget.themeController.primaryTextColor.value, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ), - ), - ], - ), - ), - const SizedBox(height: 20), - // Available letters - Wrap( - spacing: 10, - runSpacing: 10, - alignment: WrapAlignment.center, - children: List.generate( - challenge.scrambledLetters.length, - (index) => GestureDetector( - onTap: () => _selectLetter(index), - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: letterUsed[index] - ? widget.themeController.secondaryBackgroundColor.value - : ksecondaryColor, - borderRadius: BorderRadius.circular(5), - ), - child: Center( - child: Text( - challenge.scrambledLetters[index], - style: TextStyle( - fontSize: 24, - color: letterUsed[index] - ? widget.themeController.secondaryTextColor.value - : Colors.white, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ), - ), - ), - const SizedBox(height: 20), - // Action buttons - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ElevatedButton( - onPressed: _clearWord, - style: ElevatedButton.styleFrom( - backgroundColor: widget.themeController.secondaryBackgroundColor.value, - ), - child: Text( - 'Clear'.tr, - style: TextStyle( - color: widget.themeController.primaryTextColor.value, - ), - ), - ), - ElevatedButton( - onPressed: selectedLetters.isNotEmpty ? _checkWord : null, - style: ElevatedButton.styleFrom( - backgroundColor: ksecondaryColor, - ), - child: Text( - 'Check'.tr, - style: const TextStyle( - color: Colors.white, - ), - ), - ), - ], - ), - if (showError) ...[ - const SizedBox(height: 10), - Text( - 'Try again!'.tr, - style: const TextStyle( - color: Colors.red, - fontSize: 16, - ), - ), - ], - ], - ), - ); - } -} \ No newline at end of file From 0d7c11f587caff8648f2ede6ef4ae87fd382ed73 Mon Sep 17 00:00:00 2001 From: mahendra-918 Date: Tue, 18 Mar 2025 15:39:56 +0530 Subject: [PATCH 4/5] final changes --- lib/app/data/models/alarm_model.dart | 2 -- .../{sort_mode.dart => sort_model.dart} | 12 +++---- .../views/guardian_angel.dart | 31 +++++++----------- .../home/controllers/home_controller.dart | 32 ++++++------------- .../controllers/settings_controller.dart | 3 +- .../settings/views/sort_options_view.dart | 8 ++--- 6 files changed, 33 insertions(+), 55 deletions(-) rename lib/app/data/models/{sort_mode.dart => sort_model.dart} (70%) diff --git a/lib/app/data/models/alarm_model.dart b/lib/app/data/models/alarm_model.dart index 3bfe01bd..0e2069e7 100644 --- a/lib/app/data/models/alarm_model.dart +++ b/lib/app/data/models/alarm_model.dart @@ -185,8 +185,6 @@ class AlarmModel { guardianTimer = documentSnapshot['guardianTimer']; guardian = documentSnapshot['guardian']; isCall = documentSnapshot['isCall']; - - // Handle lastModifiedDate field lastModifiedDate = documentSnapshot['lastModifiedDate'] != null ? (documentSnapshot['lastModifiedDate'] as firestore.Timestamp).toDate() : DateTime.now(); diff --git a/lib/app/data/models/sort_mode.dart b/lib/app/data/models/sort_model.dart similarity index 70% rename from lib/app/data/models/sort_mode.dart rename to lib/app/data/models/sort_model.dart index db031169..3075e9f7 100644 --- a/lib/app/data/models/sort_mode.dart +++ b/lib/app/data/models/sort_model.dart @@ -1,10 +1,10 @@ enum AlarmSortMode { - defaultSort, // Current implementation (enabled + next occurrence) - timeOfDay, // Sort by time regardless of enabled status - label, // Alphabetical sorting by label - creationDate, // Most recently created first - lastModified, // Most recently modified first - customOrder; // Manual drag-and-drop ordering + defaultSort, + timeOfDay, + label, + creationDate, + lastModified, + customOrder; String get displayName { switch (this) { diff --git a/lib/app/modules/addOrUpdateAlarm/views/guardian_angel.dart b/lib/app/modules/addOrUpdateAlarm/views/guardian_angel.dart index 25c9f47f..804058c6 100644 --- a/lib/app/modules/addOrUpdateAlarm/views/guardian_angel.dart +++ b/lib/app/modules/addOrUpdateAlarm/views/guardian_angel.dart @@ -1,13 +1,14 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl_phone_number_input/intl_phone_number_input.dart'; +import 'package:intl_phone_number_input/src/models/country_model.dart'; import 'package:ultimate_alarm_clock/app/modules/addOrUpdateAlarm/controllers/add_or_update_alarm_controller.dart'; import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart'; import 'package:ultimate_alarm_clock/app/utils/constants.dart'; import 'package:ultimate_alarm_clock/app/utils/utils.dart'; import 'package:permission_handler/permission_handler.dart'; -class GuardianAngel extends GetView { +class GuardianAngel extends StatelessWidget { const GuardianAngel({ super.key, required this.controller, @@ -17,6 +18,12 @@ class GuardianAngel extends GetView { final AddOrUpdateAlarmController controller; final ThemeController themeController; + static int orderedCountryCode(Country countryA, Country countryB) { + String dialCodeA = countryA.dialCode?.replaceAll('+', '') ?? '0'; + String dialCodeB = countryB.dialCode?.replaceAll('+', '') ?? '0'; + return int.parse(dialCodeA).compareTo(int.parse(dialCodeB)); + } + @override Widget build(BuildContext context) { return Column( @@ -42,29 +49,15 @@ class GuardianAngel extends GetView { padding: const EdgeInsets.all(8.0), child: InternationalPhoneNumberInput( textFieldController: controller.contactTextEditingController, - onInputChanged: (PhoneNumber number) { - controller.guardian.value = number.phoneNumber ?? ''; - }, + onInputChanged: (value) {}, + onInputValidated: (value) {}, + spaceBetweenSelectorAndTextField: 0, selectorConfig: const SelectorConfig( - selectorType: PhoneInputSelectorType.DIALOG, showFlags: true, setSelectorButtonAsPrefixIcon: true, leadingPadding: 0, trailingSpace: false, - ), - ignoreBlank: false, - autoValidateMode: AutovalidateMode.disabled, - selectorTextStyle: TextStyle(color: themeController.primaryTextColor.value), - formatInput: true, - keyboardType: TextInputType.numberWithOptions(signed: true, decimal: true), - inputDecoration: InputDecoration( - contentPadding: EdgeInsets.only(bottom: 15, left: 0), - border: OutlineInputBorder( - borderSide: BorderSide(color: themeController.primaryTextColor.value), - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: themeController.primaryTextColor.value), - ), + countryComparator: orderedCountryCode, ), ), ), diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart index b085d1e9..4e3c8404 100644 --- a/lib/app/modules/home/controllers/home_controller.dart +++ b/lib/app/modules/home/controllers/home_controller.dart @@ -19,7 +19,7 @@ import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider. import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart'; import 'package:ultimate_alarm_clock/app/utils/constants.dart'; import 'package:ultimate_alarm_clock/app/utils/utils.dart'; -import 'package:ultimate_alarm_clock/app/data/models/sort_mode.dart'; +import 'package:ultimate_alarm_clock/app/data/models/sort_model.dart'; import 'package:ultimate_alarm_clock/app/modules/settings/controllers/settings_controller.dart'; import '../../../data/models/profile_model.dart'; @@ -154,8 +154,6 @@ class HomeController extends GetxController { ...latestFirestoreAlarms, ...latestIsarAlarms, ]; - - // Use the proper sorting functionality sortAlarms(alarms); return alarms; @@ -679,8 +677,6 @@ class HomeController extends GetxController { if (!settingsController.isSortedAlarmListEnabled.value) { return; } - - // Create a copy of the list to sort List sortedAlarms = List.from(alarms); switch (settingsController.currentSortMode.value) { @@ -700,28 +696,23 @@ class HomeController extends GetxController { _lastModifiedSort(sortedAlarms); break; case AlarmSortMode.customOrder: - // Custom order is handled by drag and drop break; } - - // Apply reverse sort if direction is descending if (settingsController.currentSortDirection.value == SortDirection.descending) { sortedAlarms = sortedAlarms.reversed.toList(); } - - // Clear and update the original list alarms.clear(); alarms.addAll(sortedAlarms); } void _defaultSort(List alarms) { alarms.sort((a, b) { - // First sort by isEnabled + if (a.isEnabled != b.isEnabled) { return a.isEnabled ? -1 : 1; } - // Then sort by upcoming time + return _compareUpcomingTime(a, b); }); } @@ -740,12 +731,11 @@ class HomeController extends GetxController { void _creationDateSort(List alarms) { alarms.sort((a, b) { - return b.alarmID.compareTo(a.alarmID); // Assuming alarmID is sequential + return b.alarmID.compareTo(a.alarmID); }); } void _lastModifiedSort(List alarms) { - // Assuming we add lastModifiedDate to AlarmModel alarms.sort((a, b) { return b.lastModifiedDate.compareTo(a.lastModifiedDate); }); @@ -755,11 +745,11 @@ class HomeController extends GetxController { int aUpcomingTime = a.minutesSinceMidnight; int bUpcomingTime = b.minutesSinceMidnight; - // Check if alarm repeats on any day + bool aRepeats = a.days.any((day) => day); bool bRepeats = b.days.any((day) => day); - // Calculate next occurrence for repeating alarms + if (aRepeats) { aUpcomingTime = _calculateNextOccurrence(a); } else if (aUpcomingTime <= DateTime.now().hour * 60 + DateTime.now().minute) { @@ -780,30 +770,28 @@ class HomeController extends GetxController { int minutesSinceMidnight = alarm.minutesSinceMidnight; int currentTimeInMinutes = DateTime.now().hour * 60 + DateTime.now().minute; - // Find the next day when the alarm is scheduled + for (int i = 0; i < alarm.days.length; i++) { int dayIndex = (currentDay + i) % alarm.days.length; if (alarm.days[dayIndex]) { if (i == 0 && minutesSinceMidnight <= currentTimeInMinutes) { - continue; // Skip today if alarm time has passed + continue; } return minutesSinceMidnight + i * Duration.minutesPerDay; } } - // If we get here, the next occurrence is on the first enabled day next week for (int i = 0; i < alarm.days.length; i++) { if (alarm.days[i]) { return minutesSinceMidnight + (7 - currentDay + i) * Duration.minutesPerDay; } } - return minutesSinceMidnight; // Fallback + return minutesSinceMidnight; } - // Add this method to refresh the alarm list when sort settings change void refreshAlarmList() { - // Trigger a rebuild of the alarm list + update(); } } diff --git a/lib/app/modules/settings/controllers/settings_controller.dart b/lib/app/modules/settings/controllers/settings_controller.dart index 1bbe33ca..c8b23668 100644 --- a/lib/app/modules/settings/controllers/settings_controller.dart +++ b/lib/app/modules/settings/controllers/settings_controller.dart @@ -3,7 +3,7 @@ import 'package:get/get.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:ultimate_alarm_clock/app/data/models/user_model.dart'; -import 'package:ultimate_alarm_clock/app/data/models/sort_mode.dart'; +import 'package:ultimate_alarm_clock/app/data/models/sort_model.dart'; import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart'; import 'package:ultimate_alarm_clock/app/modules/home/controllers/home_controller.dart'; @@ -82,7 +82,6 @@ class SettingsController extends GetxController { } _loadPreference(); - // Load sort preferences String? savedSortMode = await _secureStorageProvider.readSortMode(_sortModeKey); String? savedSortDirection = await _secureStorageProvider.readSortDirection(_sortDirectionKey); diff --git a/lib/app/modules/settings/views/sort_options_view.dart b/lib/app/modules/settings/views/sort_options_view.dart index 5b035cdd..c78103db 100644 --- a/lib/app/modules/settings/views/sort_options_view.dart +++ b/lib/app/modules/settings/views/sort_options_view.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:ultimate_alarm_clock/app/data/models/sort_mode.dart'; +import 'package:ultimate_alarm_clock/app/data/models/sort_model.dart'; import 'package:ultimate_alarm_clock/app/modules/settings/controllers/settings_controller.dart'; import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart'; import 'package:ultimate_alarm_clock/app/utils/constants.dart'; @@ -40,7 +40,7 @@ class SortOptionsView extends StatelessWidget { ), ), const SizedBox(height: 15), - // Enable/Disable sorting switch + Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -60,7 +60,7 @@ class SortOptionsView extends StatelessWidget { ], ), const SizedBox(height: 10), - // Sort Mode Dropdown + if (controller.isSortedAlarmListEnabled.value) ...[ Text( 'Sort By'.tr, @@ -98,7 +98,7 @@ class SortOptionsView extends StatelessWidget { ), ), const SizedBox(height: 15), - // Sort Direction + if (controller.currentSortMode.value != AlarmSortMode.customOrder) ...[ Text( 'Direction'.tr, From 2f8a02e731b61df9e07f4e44330ef233179ef773 Mon Sep 17 00:00:00 2001 From: mahendra-918 Date: Tue, 18 Mar 2025 16:00:25 +0530 Subject: [PATCH 5/5] fixed some UI --- lib/app/modules/settings/views/sort_options_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/app/modules/settings/views/sort_options_view.dart b/lib/app/modules/settings/views/sort_options_view.dart index c78103db..7c6082ee 100644 --- a/lib/app/modules/settings/views/sort_options_view.dart +++ b/lib/app/modules/settings/views/sort_options_view.dart @@ -65,7 +65,7 @@ class SortOptionsView extends StatelessWidget { Text( 'Sort By'.tr, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: themeController.secondaryTextColor.value, + color: themeController.primaryTextColor.value, ), ), const SizedBox(height: 5), @@ -103,7 +103,7 @@ class SortOptionsView extends StatelessWidget { Text( 'Direction'.tr, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: themeController.secondaryTextColor.value, + color: themeController.primaryTextColor.value, ), ), const SizedBox(height: 5),