diff --git a/assets/jsons/generosity_challenge/chat_scripts/chat_script_day_6.json b/assets/jsons/generosity_challenge/chat_scripts/chat_script_day_6.json index 742dcfcf5..de7220c7f 100644 --- a/assets/jsons/generosity_challenge/chat_scripts/chat_script_day_6.json +++ b/assets/jsons/generosity_challenge/chat_scripts/chat_script_day_6.json @@ -2,11 +2,81 @@ "main": { "type": "textMessage", "side": "interlocutor", - "text": "Still in progress.. Stay tuned for the new updates!" + "text": "Hey {lastName} Family. Your nearly at the end of this challenge!", + "next": { + "type": "textMessage", + "side": "interlocutor", + "text": "Our sixth generosity activity is about giving with our money.", + "next": { + "type": "textMessage", + "side": "interlocutor", + "text": "To do that I'll need to register the details you've given me so far.", + "next": { + "type": "textMessage", + "side": "interlocutor", + "text": "One sec....", + "functionName": "registerUser", + "next": { + "type": "textMessage", + "side": "interlocutor", + "text": "All done!", + "next": { + "type": "textMessage", + "side": "interlocutor", + "text": "Now you can give your money to help people and causes that you care about.", + "next": { + "type": "buttonAnswer", + "side": "user", + "text": "Like who?", + "answerText": "Like who?", + "next": { + "type": "textMessage", + "side": "interlocutor", + "text": "Great question. Today's assignment will guide you.", + "next": { + "type": "imageMessage", + "side": "interlocutor", + "path": "https://givtstoragedebug.blob.core.windows.net/public/cdn/generosity-challenge/Givt_Superhero_THUMBNAILS_010.png", + "mediaSourceType": "network", + "next": { + "type": "buttonAnswer", + "side": "user", + "text": "Let's give!", + "answerText": "Let's give!" + } + } + } + } + } + } + } + } + } }, - "postChat" :{ + "postChat": { "type": "textMessage", - "side": "interlocutor", - "text": "Awesome!" + "side": "user", + "text": "We gave to {organisation}", + "next": { + "type": "textMessage", + "side": "interlocutor", + "text": "That's incredible. When we all join in, we can make a big impact.", + "next": { + "type": "textMessage", + "side": "interlocutor", + "text": "That's why giving a little can become a lot when we all give together.", + "next": { + "type": "buttonAnswer", + "side": "user", + "text": "See you soon.", + "answerText": "See you soon.", + "next": { + "type": "textMessage", + "side": "interlocutor", + "text": "😊" + } + } + } + } } } \ No newline at end of file diff --git a/lib/app/routes/app_router.dart b/lib/app/routes/app_router.dart index 86a8a3a90..8e88a2d7a 100644 --- a/lib/app/routes/app_router.dart +++ b/lib/app/routes/app_router.dart @@ -124,6 +124,7 @@ class AppRouter { create: (_) => GenerosityChallengeCubit( getIt(), getIt(), + getIt(), )..loadFromCache(), child: const GenerosityChallenge(), ); @@ -862,7 +863,7 @@ class AppRouter { final query = Uri( queryParameters: params, ).query; - + if (GenerosityChallengeHelper.isActivated || navigatingPage == Pages.generosityChallenge.path) { return Pages.generosityChallenge.path; diff --git a/lib/features/children/add_member/widgets/add_member_form.dart b/lib/features/children/add_member/widgets/add_member_form.dart index 67c87e65d..dba847ac5 100644 --- a/lib/features/children/add_member/widgets/add_member_form.dart +++ b/lib/features/children/add_member/widgets/add_member_form.dart @@ -9,6 +9,7 @@ import 'package:givt_app/core/enums/country.dart'; import 'package:givt_app/features/auth/cubit/auth_cubit.dart'; import 'package:givt_app/features/children/add_member/cubit/add_member_cubit.dart'; import 'package:givt_app/features/children/add_member/models/member.dart'; +import 'package:givt_app/features/children/add_member/widgets/allowance_counter.dart'; import 'package:givt_app/features/children/add_member/widgets/family_text_form_field.dart'; import 'package:givt_app/features/children/edit_child/widgets/giving_allowance_info_button.dart'; import 'package:givt_app/features/children/shared/profile_type.dart'; @@ -24,6 +25,7 @@ class AddMemberForm extends StatefulWidget { required this.ageFocusNode, super.key, }); + final bool firstMember; final VoidCallback onRemove; final FocusNode ageFocusNode; @@ -38,36 +40,14 @@ class _AddMemberFormState extends State { final _emailParentController = TextEditingController(); final _ageController = TextEditingController(); bool isChildSelected = true; - int _allowanceController = 15; + int _allowance = 15; final formKeyChild = GlobalKey(); final formKeyParent = GlobalKey(); late final FocusNode _childNameFocusNode; late final FocusNode _parentNameFocusNode; - Timer? _timer; - Duration _heldDuration = Duration.zero; - int tapTime = 240; - int holdDownDuration = 1000; - int holdDownDuration2 = 2000; - int maxAllowance = 999; - int minAllowance = 1; - int allowanceIncrement = 5; - int allowanceIncrement2 = 10; - - void _startTimer(void Function() callback) { - _timer = Timer.periodic(Duration(milliseconds: tapTime), (_) { - _heldDuration += Duration(milliseconds: tapTime); - callback(); - }); - } - - void _stopTimer() { - _timer?.cancel(); - _heldDuration = Duration.zero; - } @override void dispose() { - _stopTimer(); _childNameFocusNode.dispose(); _parentNameFocusNode.dispose(); super.dispose(); @@ -80,54 +60,6 @@ class _AddMemberFormState extends State { _parentNameFocusNode = FocusNode()..requestFocus(); } - void _incrementCounter() { - final isHeldDurationShortEnoughForIncrement1 = - (_heldDuration.inMilliseconds > holdDownDuration) && - _allowanceController >= maxAllowance - allowanceIncrement; - final isHeldDurationShortEnoughForIncrement2 = - (_heldDuration.inMilliseconds > holdDownDuration2) && - _allowanceController >= maxAllowance - allowanceIncrement2; - - if (_allowanceController >= maxAllowance || - isHeldDurationShortEnoughForIncrement1 || - isHeldDurationShortEnoughForIncrement2) { - return; - } - setState(() { - HapticFeedback.lightImpact(); - SystemSound.play(SystemSoundType.click); - _allowanceController += (_heldDuration.inMilliseconds < holdDownDuration) - ? 1 - : (_heldDuration.inMilliseconds < holdDownDuration2) - ? allowanceIncrement - : allowanceIncrement2; - }); - } - - void _decrementCounter() { - final isHeldDurationLongEnoughForNegative1 = - (_heldDuration.inMilliseconds > holdDownDuration) && - _allowanceController <= minAllowance + allowanceIncrement; - final isHeldDurationLongEnoughForNegative2 = - (_heldDuration.inMilliseconds > holdDownDuration2) && - _allowanceController <= minAllowance + allowanceIncrement2; - - if (_allowanceController <= minAllowance || - isHeldDurationLongEnoughForNegative1 || - isHeldDurationLongEnoughForNegative2) { - return; - } - setState(() { - HapticFeedback.lightImpact(); - SystemSound.play(SystemSoundType.click); - _allowanceController -= (_heldDuration.inMilliseconds < holdDownDuration) - ? 1 - : (_heldDuration.inMilliseconds < holdDownDuration2) - ? allowanceIncrement - : allowanceIncrement2; - }); - } - @override Widget build(BuildContext context) { final user = context.read().state.user; @@ -155,7 +87,7 @@ class _AddMemberFormState extends State { firstName: name, dateOfBirth: dateOfBirth, age: age, - allowance: _allowanceController, + allowance: _allowance, key: formKeyChild.toString(), type: ProfileType.Child, ); @@ -169,7 +101,7 @@ class _AddMemberFormState extends State { eventProperties: { 'name': name, 'age': age, - 'allowance': _allowanceController, + 'allowance': _allowance, }, ); } else { @@ -342,85 +274,6 @@ class _AddMemberFormState extends State { ); } - Widget allowanceCounter(String currency) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GestureDetector( - onTapDown: (_) { - _startTimer(_decrementCounter); - }, - onTapUp: (_) { - _stopTimer(); - }, - onTapCancel: _stopTimer, - onTap: (_allowanceController < 2) ? null : _decrementCounter, - child: Container( - width: 32, - height: 32, - child: Icon( - FontAwesomeIcons.circleMinus, - size: 32, - color: (_allowanceController < 2) - ? Colors.grey - : Theme.of(context).colorScheme.primary, - ), - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 15), - width: _allowanceController < 100 ? 75 : 95, - child: Text.rich( - TextSpan( - children: [ - TextSpan( - text: currency, - style: const TextStyle( - fontSize: 16, - fontFamily: 'Raleway', - fontWeight: FontWeight.w700, - ), - ), - TextSpan( - text: '$_allowanceController', - style: const TextStyle( - fontSize: 24, - fontFamily: 'Raleway', - fontWeight: FontWeight.w700, - fontFeatures: [FontFeature.liningFigures()], - ), - ), - ], - ), - textAlign: TextAlign.center, - ), - ), - GestureDetector( - onTapDown: (_) { - _startTimer(_incrementCounter); - }, - onTapUp: (_) { - _stopTimer(); - }, - onTapCancel: _stopTimer, - onTap: (_allowanceController > 998) ? null : _incrementCounter, - child: Container( - width: 32, - height: 32, - child: Icon( - FontAwesomeIcons.circlePlus, - size: 32, - color: (_allowanceController > 998) - ? Colors.grey - : Theme.of(context).colorScheme.primary, - ), - ), - ), - ], - ); - } - Widget createChildForm(String currency) { return Form( key: formKeyChild, @@ -472,7 +325,11 @@ class _AddMemberFormState extends State { ), const SizedBox(height: 10), const GivingAllowanceInfoButton(), - allowanceCounter(currency), + AllowanceCounter( + currency: currency, + initialAllowance: _allowance, + onAllowanceChanged: (allowance) => _allowance = allowance, + ), ], ), ); diff --git a/lib/features/children/add_member/widgets/allowance_counter.dart b/lib/features/children/add_member/widgets/allowance_counter.dart new file mode 100644 index 000000000..3ecbae3e8 --- /dev/null +++ b/lib/features/children/add_member/widgets/allowance_counter.dart @@ -0,0 +1,189 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +class AllowanceCounter extends StatefulWidget { + const AllowanceCounter({ + required this.currency, + this.initialAllowance, + this.onAllowanceChanged, + super.key, + }); + + final String currency; + final int? initialAllowance; + final void Function(int allowance)? onAllowanceChanged; + + @override + State createState() => _AllowanceCounterState(); +} + +class _AllowanceCounterState extends State { + static const int tapTime = 240; + static const int holdDownDuration = 1000; + static const int holdDownDuration2 = 2000; + static const int maxAllowance = 999; + static const int minAllowance = 1; + static const int allowanceIncrement = 5; + static const int allowanceIncrement2 = 10; + + late int _allowance; + Timer? _timer; + Duration _heldDuration = Duration.zero; + + @override + void initState() { + super.initState(); + _allowance = widget.initialAllowance ?? 15; + } + + @override + void dispose() { + _stopTimer(); + super.dispose(); + } + + void _startTimer(void Function() callback) { + _timer = Timer.periodic(const Duration(milliseconds: tapTime), (_) { + _heldDuration += const Duration(milliseconds: tapTime); + callback(); + }); + } + + void _stopTimer() { + _timer?.cancel(); + _heldDuration = Duration.zero; + } + + void _incrementCounter() { + final isHeldDurationShortEnoughForIncrement1 = + (_heldDuration.inMilliseconds > holdDownDuration) && + _allowance >= maxAllowance - allowanceIncrement; + final isHeldDurationShortEnoughForIncrement2 = + (_heldDuration.inMilliseconds > holdDownDuration2) && + _allowance >= maxAllowance - allowanceIncrement2; + + if (_allowance >= maxAllowance || + isHeldDurationShortEnoughForIncrement1 || + isHeldDurationShortEnoughForIncrement2) { + return; + } + setState(() { + HapticFeedback.lightImpact(); + SystemSound.play(SystemSoundType.click); + _allowance += (_heldDuration.inMilliseconds < holdDownDuration) + ? 1 + : (_heldDuration.inMilliseconds < holdDownDuration2) + ? allowanceIncrement + : allowanceIncrement2; + }); + widget.onAllowanceChanged?.call(_allowance); + } + + void _decrementCounter() { + final isHeldDurationLongEnoughForNegative1 = + (_heldDuration.inMilliseconds > holdDownDuration) && + _allowance <= minAllowance + allowanceIncrement; + final isHeldDurationLongEnoughForNegative2 = + (_heldDuration.inMilliseconds > holdDownDuration2) && + _allowance <= minAllowance + allowanceIncrement2; + + if (_allowance <= minAllowance || + isHeldDurationLongEnoughForNegative1 || + isHeldDurationLongEnoughForNegative2) { + return; + } + setState(() { + HapticFeedback.lightImpact(); + SystemSound.play(SystemSoundType.click); + _allowance -= (_heldDuration.inMilliseconds < holdDownDuration) + ? 1 + : (_heldDuration.inMilliseconds < holdDownDuration2) + ? allowanceIncrement + : allowanceIncrement2; + }); + widget.onAllowanceChanged?.call(_allowance); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTapDown: (_) { + _startTimer(_decrementCounter); + }, + onTapUp: (_) { + _stopTimer(); + }, + onTapCancel: _stopTimer, + onTap: (_allowance < 2) ? null : _decrementCounter, + child: SizedBox( + width: 32, + height: 32, + child: Icon( + FontAwesomeIcons.circleMinus, + size: 32, + color: (_allowance < 2) + ? Colors.grey + : Theme.of(context).colorScheme.primary, + ), + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 15), + width: _allowance < 100 ? 80 : 100, + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: widget.currency, + style: const TextStyle( + fontSize: 16, + fontFamily: 'Raleway', + fontWeight: FontWeight.w700, + ), + ), + TextSpan( + text: '$_allowance', + style: const TextStyle( + fontSize: 24, + fontFamily: 'Raleway', + fontWeight: FontWeight.w700, + fontFeatures: [FontFeature.liningFigures()], + ), + ), + ], + ), + textAlign: TextAlign.center, + ), + ), + GestureDetector( + onTapDown: (_) { + _startTimer(_incrementCounter); + }, + onTapUp: (_) { + _stopTimer(); + }, + onTapCancel: _stopTimer, + onTap: (_allowance > 998) ? null : _incrementCounter, + child: SizedBox( + width: 32, + height: 32, + child: Icon( + FontAwesomeIcons.circlePlus, + size: 32, + color: (_allowance > 998) + ? Colors.grey + : Theme.of(context).colorScheme.primary, + ), + ), + ), + ], + ); + } +} diff --git a/lib/features/children/generosity_challenge/cubit/generosity_challenge_cubit.dart b/lib/features/children/generosity_challenge/cubit/generosity_challenge_cubit.dart index 21983e28c..3964fcfad 100644 --- a/lib/features/children/generosity_challenge/cubit/generosity_challenge_cubit.dart +++ b/lib/features/children/generosity_challenge/cubit/generosity_challenge_cubit.dart @@ -8,7 +8,9 @@ import 'package:givt_app/features/children/generosity_challenge/repositories/cha import 'package:givt_app/features/children/generosity_challenge/repositories/generosity_challenge_repository.dart'; import 'package:givt_app/features/children/generosity_challenge/utils/generosity_challenge_helper.dart'; import 'package:givt_app/features/children/generosity_challenge_chat/chat_scripts/models/chat_script_item.dart'; +import 'package:givt_app/features/children/generosity_challenge_chat/chat_scripts/models/enums/chat_script_item_type.dart'; import 'package:givt_app/features/children/generosity_challenge_chat/chat_scripts/models/enums/chat_script_save_key.dart'; +import 'package:givt_app/features/children/generosity_challenge_chat/chat_scripts/repositories/chat_history_repository.dart'; part 'generosity_challenge_state.dart'; @@ -16,10 +18,12 @@ class GenerosityChallengeCubit extends Cubit { GenerosityChallengeCubit( this._generosityChallengeRepository, this._chatScriptsRepository, + this._chatHistoryRepository, ) : super(const GenerosityChallengeState.initial()); final GenerosityChallengeRepository _generosityChallengeRepository; final ChatScriptsRepository _chatScriptsRepository; + final ChatHistoryRepository _chatHistoryRepository; Future loadFromCache() async { emit(state.copyWith(status: GenerosityChallengeStatus.loading)); @@ -42,6 +46,37 @@ class GenerosityChallengeCubit extends Cubit { await loadFromCache(); } + Future undoProgress(int dayIndex) async { + if (dayIndex < 0 || + dayIndex >= GenerosityChallengeHelper.generosityChallengeDays) { + return; + } + + final newDays = [...state.days]..fillRange( + dayIndex, + GenerosityChallengeHelper.generosityChallengeDays, + const Day.empty(), + ); + await _generosityChallengeRepository.saveToCache(newDays); + _refreshState( + days: newDays, + ); + + final chatHistory = _chatHistoryRepository.loadChatHistory(); + ChatScriptItem? currentItem; + + for (currentItem = chatHistory; + currentItem != null && currentItem != const ChatScriptItem.empty(); + currentItem = currentItem.next) { + if (currentItem.type == ChatScriptItemType.delimiter && + currentItem.text == 'Day ${dayIndex + 1}') { + final newHistory = currentItem.next ?? const ChatScriptItem.empty(); + await _chatHistoryRepository.saveChatHistory(newHistory); + return; + } + } + } + void overview() => emit( state.copyWith( status: GenerosityChallengeStatus.overview, @@ -55,6 +90,7 @@ class GenerosityChallengeCubit extends Cubit { detailedDayIndex: dayIndex, ), ); + void confirmAssignment(String description) => emit( state.copyWith( status: GenerosityChallengeStatus.dailyAssigmentConfirm, @@ -129,7 +165,7 @@ class GenerosityChallengeCubit extends Cubit { Future formatChatTextWithUserData({ required String source, }) async { - final userData = await _generosityChallengeRepository.loadUserData(); + final userData = _generosityChallengeRepository.loadUserData(); return format(source, userData); } @@ -160,18 +196,21 @@ class GenerosityChallengeCubit extends Cubit { final nextActiveDayIndex = lastCompletedDayIndex + 1; if (nextActiveDayIndex < GenerosityChallengeHelper.generosityChallengeDays) { - final lastCompletedDateTime = - DateTime.parse(days[lastCompletedDayIndex].dateCompleted); - final diff = lastCompletedDateTime.difference(DateTime.now()); - final timeDifference = - state.unlockDayTimeDifference == UnlockDayTimeDifference.days - ? diff.inDays - : diff.inMinutes; - if (timeDifference != 0) { + if (state.unlockDayTimeDifference == UnlockDayTimeDifference.minutes) { + // the intention of this mode is just to unlock the next day fast + // for development/testing purposes, no need to do an actual time-check return nextActiveDayIndex; } else { - //no active day yet - return -1; + final lastCompletedDateTime = DateTime.parse( + days[lastCompletedDayIndex].dateCompleted, + ); + final nowDateTime = DateTime.now(); + if (lastCompletedDateTime.day != nowDateTime.day) { + return nextActiveDayIndex; + } else { + //no active day yet + return -1; + } } } else { //the challenge is completed, no active day diff --git a/lib/features/children/generosity_challenge/pages/generosity_challenge_overview.dart b/lib/features/children/generosity_challenge/pages/generosity_challenge_overview.dart index fa74555dc..1833cae63 100644 --- a/lib/features/children/generosity_challenge/pages/generosity_challenge_overview.dart +++ b/lib/features/children/generosity_challenge/pages/generosity_challenge_overview.dart @@ -33,6 +33,37 @@ class _GenerosityChallengeOverviewState ); } + Future _undoProgress(int dayIndax) async { + if (!isDebug) { + return; + } + + final challenge = context.read(); + final result = await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Undo Challenge Progress'), + content: Text( + 'Are you sure you want to undo generosity challenge progress including Day ${dayIndax + 1}?'), + actions: [ + TextButton( + onPressed: () => context.pop(true), + child: const Text('Yes'), + ), + TextButton( + onPressed: () => context.pop(false), + child: const Text('Cancel'), + ), + ], + ); + }, + ); + if (true == result) { + await challenge.undoProgress(dayIndax); + } + } + @override Widget build(BuildContext context) { final challenge = context.watch(); @@ -74,6 +105,8 @@ class _GenerosityChallengeOverviewState itemBuilder: (BuildContext context, int index) { final day = challenge.state.days[index]; return DayButton( + onLongPressed: + isDebug ? () => _undoProgress(index) : null, isCompleted: day.isCompleted, isActive: challenge.state.activeDayIndex == index, dayIndex: index, @@ -87,7 +120,7 @@ class _GenerosityChallengeOverviewState 'day': index + 1, }, ); - + // If the (pre)chat is available for the day, navigate to the chat screen if (challenge.state.hasAvailableChat && challenge.state.availableChatDayIndex == diff --git a/lib/features/children/generosity_challenge/repositories/generosity_challenge_repository.dart b/lib/features/children/generosity_challenge/repositories/generosity_challenge_repository.dart index cb5da1121..b6c2e0778 100644 --- a/lib/features/children/generosity_challenge/repositories/generosity_challenge_repository.dart +++ b/lib/features/children/generosity_challenge/repositories/generosity_challenge_repository.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'package:givt_app/features/children/generosity_challenge/models/day.dart'; import 'package:givt_app/features/children/generosity_challenge/utils/generosity_challenge_helper.dart'; import 'package:givt_app/features/children/generosity_challenge_chat/chat_scripts/models/enums/chat_script_save_key.dart'; @@ -6,9 +7,13 @@ import 'package:shared_preferences/shared_preferences.dart'; mixin GenerosityChallengeRepository { Future saveToCache(List days); + Future> loadFromCache(); + Future clearCache(); + Future saveUserData(ChatScriptSaveKey key, String value); + Map loadUserData(); } @@ -35,25 +40,35 @@ class GenerosityChallengeRepositoryImpl with GenerosityChallengeRepository { @override Future> loadFromCache() async { - final encodedString = - sharedPreferences.getString(_generosityChallengeDaysKey) ?? ''; - if (encodedString.isNotEmpty) { - final result = (jsonDecode(encodedString) as List) - .map( - (day) => Day.fromMap(day as Map), - ) - .toList(); - return result; - } else { - final days = List.filled( - GenerosityChallengeHelper.generosityChallengeDays, - const Day.empty(), - ); - await saveToCache(days); - return days; + try { + final encodedString = + sharedPreferences.getString(_generosityChallengeDaysKey) ?? ''; + if (encodedString.isNotEmpty) { + final result = (jsonDecode(encodedString) as List) + .map( + (day) => Day.fromMap(day as Map), + ) + .toList(); + return result; + } else { + return await _saveAndReturnEmptyDays(); + } + } catch (e, s) { + log(e.toString()); + log(s.toString()); + return _saveAndReturnEmptyDays(); } } + Future> _saveAndReturnEmptyDays() async { + final days = List.filled( + GenerosityChallengeHelper.generosityChallengeDays, + const Day.empty(), + ); + await saveToCache(days); + return days; + } + @override Future clearCache() async { await sharedPreferences.remove(_generosityChallengeDaysKey); diff --git a/lib/features/children/generosity_challenge/widgets/day_button.dart b/lib/features/children/generosity_challenge/widgets/day_button.dart index b93292ba1..48431ba22 100644 --- a/lib/features/children/generosity_challenge/widgets/day_button.dart +++ b/lib/features/children/generosity_challenge/widgets/day_button.dart @@ -10,6 +10,7 @@ class DayButton extends StatelessWidget { required this.isActive, required this.dayIndex, required this.onPressed, + this.onLongPressed, super.key, }); @@ -17,60 +18,64 @@ class DayButton extends StatelessWidget { final bool isCompleted; final int dayIndex; final VoidCallback onPressed; + final VoidCallback? onLongPressed; @override Widget build(BuildContext context) { final isLocked = !isActive && !isCompleted; - return ActionContainer( - isMuted: isLocked, - isSelected: isCompleted || isLocked, - borderColor: isActive || isCompleted - ? ColorCombo.values[dayIndex % ColorCombo.values.length].borderColor - : AppTheme.neutralVariant80, - onTap: onPressed, - child: ColoredBox( - color: isActive || isCompleted - ? ColorCombo - .values[dayIndex % ColorCombo.values.length].backgroundColor - : AppTheme.neutralVariant90, - child: Stack( - children: [ - Center( - child: Text( - 'Day ${dayIndex + 1}', - textAlign: TextAlign.center, - style: TextStyle( - color: isActive || isCompleted - ? ColorCombo - .values[dayIndex % ColorCombo.values.length].textColor - : AppTheme.neutralVariant50, - fontSize: 20, - fontFamily: 'Rouna', - fontWeight: FontWeight.w700, + return GestureDetector( + onLongPress: !isLocked ? onLongPressed : null, + child: ActionContainer( + isMuted: isLocked, + isSelected: isCompleted || isLocked, + borderColor: isActive || isCompleted + ? ColorCombo.values[dayIndex % ColorCombo.values.length].borderColor + : AppTheme.neutralVariant80, + onTap: onPressed, + child: ColoredBox( + color: isActive || isCompleted + ? ColorCombo + .values[dayIndex % ColorCombo.values.length].backgroundColor + : AppTheme.neutralVariant90, + child: Stack( + children: [ + Center( + child: Text( + 'Day ${dayIndex + 1}', + textAlign: TextAlign.center, + style: TextStyle( + color: isActive || isCompleted + ? ColorCombo.values[dayIndex % ColorCombo.values.length] + .textColor + : AppTheme.neutralVariant50, + fontSize: 20, + fontFamily: 'Rouna', + fontWeight: FontWeight.w700, + ), ), ), - ), - if (isCompleted) - Positioned( - top: 0, - right: 0, - child: Container( - padding: const EdgeInsets.all(4), - decoration: const BoxDecoration( - color: AppTheme.primary70, - borderRadius: BorderRadius.only( - topRight: Radius.circular(8), - bottomLeft: Radius.circular(4), + if (isCompleted) + Positioned( + top: 0, + right: 0, + child: Container( + padding: const EdgeInsets.all(4), + decoration: const BoxDecoration( + color: AppTheme.primary70, + borderRadius: BorderRadius.only( + topRight: Radius.circular(8), + bottomLeft: Radius.circular(4), + ), + ), + child: const Icon( + FontAwesomeIcons.check, + color: AppTheme.primary40, + size: 16, ), - ), - child: const Icon( - FontAwesomeIcons.check, - color: AppTheme.primary40, - size: 16, ), ), - ), - ], + ], + ), ), ), ); diff --git a/lib/features/children/generosity_challenge_chat/chat_scripts/models/enums/chat_script_function.dart b/lib/features/children/generosity_challenge_chat/chat_scripts/models/enums/chat_script_function.dart index bb97cdebd..05006d6a9 100644 --- a/lib/features/children/generosity_challenge_chat/chat_scripts/models/enums/chat_script_function.dart +++ b/lib/features/children/generosity_challenge_chat/chat_scripts/models/enums/chat_script_function.dart @@ -7,6 +7,9 @@ enum ChatScriptFunction { pushNotificationPermission( function: _askForPushNotificationPermission, ), + registerUser( + function: _registerUser, + ), ; const ChatScriptFunction({ @@ -38,4 +41,12 @@ enum ChatScriptFunction { } return true; } + + static Future _registerUser( + BuildContext context, + ) async { + // KIDS-941: Implement registration + throw UnimplementedError(); + return true; + } } diff --git a/pubspec.yaml b/pubspec.yaml index 28ff5a816..bb06461f0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: givt_app description: Givt App -version: 4.2.14 +version: 4.2.15 publish_to: none environment: