✨ Poll collapse
This commit is contained in:
		| @@ -133,6 +133,25 @@ | |||||||
|     "other": "{} replies" |     "other": "{} replies" | ||||||
|   }, |   }, | ||||||
|   "forward": "Forward", |   "forward": "Forward", | ||||||
|  |   "award": "Award", | ||||||
|  |   "awardPost": "Award Post", | ||||||
|  |   "awardMessage": "Message", | ||||||
|  |   "awardMessageHint": "Enter your award message...", | ||||||
|  |   "awardAttitude": "Attitude", | ||||||
|  |   "awardAttitudePositive": "Positive", | ||||||
|  |   "awardAttitudeNegative": "Negative", | ||||||
|  |   "awardAmount": "Amount", | ||||||
|  |   "awardAmountHint": "Enter amount...", | ||||||
|  |   "awardAmountRequired": "Amount is required", | ||||||
|  |   "awardAmountInvalid": "Please enter a valid amount", | ||||||
|  |   "awardMessageTooLong": "Message is too long (max 4096 characters)", | ||||||
|  |   "awardSuccess": "Award sent successfully!", | ||||||
|  |   "awardSubmit": "Award", | ||||||
|  |   "awardPostPreview": "Post Preview", | ||||||
|  |   "awardNoContent": "No content available", | ||||||
|  |   "awardByPublisher": "By {}", | ||||||
|  |   "awardBenefits": "Award Benefits", | ||||||
|  |   "awardBenefitsDescription": "Awarding this post increases its value and visibility. Higher valued posts have a better chance of being featured and highlighted in the community.", | ||||||
|   "repliedTo": "Replied to", |   "repliedTo": "Replied to", | ||||||
|   "forwarded": "Forwarded", |   "forwarded": "Forwarded", | ||||||
|   "hasAttachments": { |   "hasAttachments": { | ||||||
| @@ -977,5 +996,7 @@ | |||||||
|   "pinned": "Pinned", |   "pinned": "Pinned", | ||||||
|   "noResultsFound": "No results found", |   "noResultsFound": "No results found", | ||||||
|   "toggleFilters": "Toggle filters", |   "toggleFilters": "Toggle filters", | ||||||
|   "notableDayNext": "{} is in" |   "notableDayNext": "{} is in", | ||||||
|  |   "expandPoll": "Expand Poll", | ||||||
|  |   "collapsePoll": "Collapse Poll" | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ class PollSubmit extends ConsumerStatefulWidget { | |||||||
|     this.onCancel, |     this.onCancel, | ||||||
|     this.showProgress = true, |     this.showProgress = true, | ||||||
|     this.isReadonly = false, |     this.isReadonly = false, | ||||||
|  |     this.isInitiallyExpanded = false, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   final SnPollWithStats poll; |   final SnPollWithStats poll; | ||||||
| @@ -36,6 +37,9 @@ class PollSubmit extends ConsumerStatefulWidget { | |||||||
|  |  | ||||||
|   final bool isReadonly; |   final bool isReadonly; | ||||||
|  |  | ||||||
|  |   /// Whether the poll should start expanded instead of collapsed. | ||||||
|  |   final bool isInitiallyExpanded; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   ConsumerState<PollSubmit> createState() => _PollSubmitState(); |   ConsumerState<PollSubmit> createState() => _PollSubmitState(); | ||||||
| } | } | ||||||
| @@ -45,6 +49,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> { | |||||||
|   int _index = 0; |   int _index = 0; | ||||||
|   bool _submitting = false; |   bool _submitting = false; | ||||||
|   bool _isModifying = false; // New state to track if user is modifying answers |   bool _isModifying = false; // New state to track if user is modifying answers | ||||||
|  |   bool _isCollapsed = true; // New state to track collapse/expand | ||||||
|  |  | ||||||
|   /// Collected answers, keyed by questionId |   /// Collected answers, keyed by questionId | ||||||
|   late Map<String, dynamic> _answers; |   late Map<String, dynamic> _answers; | ||||||
| @@ -65,6 +70,8 @@ class _PollSubmitState extends ConsumerState<PollSubmit> { | |||||||
|     _questions = [...widget.poll.questions] |     _questions = [...widget.poll.questions] | ||||||
|       ..sort((a, b) => a.order.compareTo(b.order)); |       ..sort((a, b) => a.order.compareTo(b.order)); | ||||||
|     _answers = Map<String, dynamic>.from(widget.initialAnswers ?? {}); |     _answers = Map<String, dynamic>.from(widget.initialAnswers ?? {}); | ||||||
|  |     // Set initial collapse state based on the parameter | ||||||
|  |     _isCollapsed = !widget.isInitiallyExpanded; | ||||||
|     if (!widget.isReadonly) { |     if (!widget.isReadonly) { | ||||||
|       _loadCurrentIntoLocalState(); |       _loadCurrentIntoLocalState(); | ||||||
|       // If initial answers are provided, set _isModifying to false initially |       // If initial answers are provided, set _isModifying to false initially | ||||||
| @@ -653,26 +660,160 @@ class _PollSubmitState extends ConsumerState<PollSubmit> { | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   Widget _buildCollapsedView(BuildContext context) { | ||||||
|  |     return Column( | ||||||
|  |       crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |       children: [ | ||||||
|  |         Row( | ||||||
|  |           children: [ | ||||||
|  |             Expanded( | ||||||
|  |               child: Column( | ||||||
|  |                 crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                 children: [ | ||||||
|  |                   if (widget.poll.title != null) | ||||||
|  |                     Text( | ||||||
|  |                       widget.poll.title!, | ||||||
|  |                       style: Theme.of(context).textTheme.titleMedium?.copyWith( | ||||||
|  |                         fontWeight: FontWeight.w600, | ||||||
|  |                       ), | ||||||
|  |                       maxLines: 1, | ||||||
|  |                       overflow: TextOverflow.ellipsis, | ||||||
|  |                     ), | ||||||
|  |                   if (widget.poll.description != null) | ||||||
|  |                     Padding( | ||||||
|  |                       padding: const EdgeInsets.only(top: 2), | ||||||
|  |                       child: Text( | ||||||
|  |                         widget.poll.description!, | ||||||
|  |                         style: Theme.of(context).textTheme.bodySmall?.copyWith( | ||||||
|  |                           color: Theme.of( | ||||||
|  |                             context, | ||||||
|  |                           ).textTheme.bodySmall?.color?.withOpacity(0.7), | ||||||
|  |                         ), | ||||||
|  |                         maxLines: 2, | ||||||
|  |                         overflow: TextOverflow.ellipsis, | ||||||
|  |                       ), | ||||||
|  |                     ) | ||||||
|  |                   else | ||||||
|  |                     Padding( | ||||||
|  |                       padding: const EdgeInsets.only(top: 2), | ||||||
|  |                       child: Text( | ||||||
|  |                         '${_questions.length} question${_questions.length == 1 ? '' : 's'}', | ||||||
|  |                         style: Theme.of(context).textTheme.bodySmall?.copyWith( | ||||||
|  |                           color: Theme.of( | ||||||
|  |                             context, | ||||||
|  |                           ).textTheme.bodySmall?.color?.withOpacity(0.7), | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                 ], | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             IconButton( | ||||||
|  |               icon: Icon( | ||||||
|  |                 _isCollapsed ? Icons.expand_more : Icons.expand_less, | ||||||
|  |                 size: 20, | ||||||
|  |               ), | ||||||
|  |               onPressed: () { | ||||||
|  |                 setState(() { | ||||||
|  |                   _isCollapsed = !_isCollapsed; | ||||||
|  |                 }); | ||||||
|  |               }, | ||||||
|  |               visualDensity: VisualDensity.compact, | ||||||
|  |               tooltip: _isCollapsed ? 'expandPoll'.tr() : 'collapsePoll'.tr(), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ], | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     if (_questions.isEmpty) { |     if (_questions.isEmpty) { | ||||||
|       return const SizedBox.shrink(); |       return const SizedBox.shrink(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // If collapsed, show collapsed view for all states | ||||||
|  |     if (_isCollapsed) { | ||||||
|  |       return _buildCollapsedView(context); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // If poll is already submitted and not in readonly mode, and not in modification mode, show submitted view |     // If poll is already submitted and not in readonly mode, and not in modification mode, show submitted view | ||||||
|     if (widget.initialAnswers != null && !widget.isReadonly && !_isModifying) { |     if (widget.initialAnswers != null && !widget.isReadonly && !_isModifying) { | ||||||
|       return Column( |       return Column( | ||||||
|  |         crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|  |         children: [ | ||||||
|  |           _buildCollapsedView(context), | ||||||
|  |           const SizedBox(height: 8), | ||||||
|  |           AnimatedSwitcher( | ||||||
|  |             duration: const Duration(milliseconds: 300), | ||||||
|  |             transitionBuilder: (child, anim) { | ||||||
|  |               final offset = Tween<Offset>( | ||||||
|  |                 begin: const Offset(0, -0.1), | ||||||
|  |                 end: Offset.zero, | ||||||
|  |               ).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut)); | ||||||
|  |               final fade = CurvedAnimation(parent: anim, curve: Curves.easeOut); | ||||||
|  |               return FadeTransition( | ||||||
|  |                 opacity: fade, | ||||||
|  |                 child: SlideTransition(position: offset, child: child), | ||||||
|  |               ); | ||||||
|  |             }, | ||||||
|  |             child: Column( | ||||||
|  |               key: const ValueKey('submitted_expanded'), | ||||||
|               crossAxisAlignment: CrossAxisAlignment.stretch, |               crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|               children: [_buildSubmittedView(context), _buildNavBar(context)], |               children: [_buildSubmittedView(context), _buildNavBar(context)], | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // If poll is in readonly mode, show readonly view |     // If poll is in readonly mode, show readonly view | ||||||
|     if (widget.isReadonly) { |     if (widget.isReadonly) { | ||||||
|       return _buildReadonlyView(context); |       return Column( | ||||||
|  |         crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|  |         children: [ | ||||||
|  |           _buildCollapsedView(context), | ||||||
|  |           const SizedBox(height: 8), | ||||||
|  |           AnimatedSwitcher( | ||||||
|  |             duration: const Duration(milliseconds: 300), | ||||||
|  |             transitionBuilder: (child, anim) { | ||||||
|  |               final offset = Tween<Offset>( | ||||||
|  |                 begin: const Offset(0, -0.1), | ||||||
|  |                 end: Offset.zero, | ||||||
|  |               ).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut)); | ||||||
|  |               final fade = CurvedAnimation(parent: anim, curve: Curves.easeOut); | ||||||
|  |               return FadeTransition( | ||||||
|  |                 opacity: fade, | ||||||
|  |                 child: SlideTransition(position: offset, child: child), | ||||||
|  |               ); | ||||||
|  |             }, | ||||||
|  |             child: _buildReadonlyView(context), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return Column( |     return Column( | ||||||
|  |       crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|  |       children: [ | ||||||
|  |         _buildCollapsedView(context), | ||||||
|  |         const SizedBox(height: 8), | ||||||
|  |         AnimatedSwitcher( | ||||||
|  |           duration: const Duration(milliseconds: 300), | ||||||
|  |           transitionBuilder: (child, anim) { | ||||||
|  |             final offset = Tween<Offset>( | ||||||
|  |               begin: const Offset(0, -0.1), | ||||||
|  |               end: Offset.zero, | ||||||
|  |             ).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut)); | ||||||
|  |             final fade = CurvedAnimation(parent: anim, curve: Curves.easeOut); | ||||||
|  |             return FadeTransition( | ||||||
|  |               opacity: fade, | ||||||
|  |               child: SlideTransition(position: offset, child: child), | ||||||
|  |             ); | ||||||
|  |           }, | ||||||
|  |           child: Column( | ||||||
|  |             key: const ValueKey('normal_expanded'), | ||||||
|             crossAxisAlignment: CrossAxisAlignment.stretch, |             crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|             children: [ |             children: [ | ||||||
|               _buildHeader(context), |               _buildHeader(context), | ||||||
| @@ -681,12 +822,18 @@ class _PollSubmitState extends ConsumerState<PollSubmit> { | |||||||
|                 key: ValueKey(_current.id), |                 key: ValueKey(_current.id), | ||||||
|                 child: Column( |                 child: Column( | ||||||
|                   crossAxisAlignment: CrossAxisAlignment.stretch, |                   crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|             children: [_buildBody(context), _buildStats(context, _current)], |                   children: [ | ||||||
|  |                     _buildBody(context), | ||||||
|  |                     _buildStats(context, _current), | ||||||
|  |                   ], | ||||||
|                 ), |                 ), | ||||||
|               ), |               ), | ||||||
|               const SizedBox(height: 16), |               const SizedBox(height: 16), | ||||||
|               _buildNavBar(context), |               _buildNavBar(context), | ||||||
|             ], |             ], | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ], | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user