mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 12:14:11 +00:00 
			
		
		
		
	Can create surveys
This commit is contained in:
		@@ -158,6 +158,11 @@ class PostsHelper {
 | 
				
			|||||||
            "time-end", (post.timeEnd.millisecondsSinceEpoch / 1000).floor());
 | 
					            "time-end", (post.timeEnd.millisecondsSinceEpoch / 1000).floor());
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      case PostKind.SURVEY:
 | 
				
			||||||
 | 
					        request.addString("question", post.survey.question);
 | 
				
			||||||
 | 
					        request.addString("answers", post.survey.answers.join("<>"));
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        throw Exception("Unsupported post type :" + post.kind.toString());
 | 
					        throw Exception("Unsupported post type :" + post.kind.toString());
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,17 @@ import 'package:meta/meta.dart';
 | 
				
			|||||||
///
 | 
					///
 | 
				
			||||||
/// @author Pierre HUBERT
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NewSurvey {
 | 
				
			||||||
 | 
					  final String question;
 | 
				
			||||||
 | 
					  final Set<String> answers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const NewSurvey({
 | 
				
			||||||
 | 
					    @required this.question,
 | 
				
			||||||
 | 
					    @required this.answers,
 | 
				
			||||||
 | 
					  })  : assert(question != null),
 | 
				
			||||||
 | 
					        assert(answers.length > 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NewPost {
 | 
					class NewPost {
 | 
				
			||||||
  final PostTarget target;
 | 
					  final PostTarget target;
 | 
				
			||||||
  final int targetID;
 | 
					  final int targetID;
 | 
				
			||||||
@@ -18,6 +29,7 @@ class NewPost {
 | 
				
			|||||||
  final List<int> pdf;
 | 
					  final List<int> pdf;
 | 
				
			||||||
  final PostKind kind;
 | 
					  final PostKind kind;
 | 
				
			||||||
  final DateTime timeEnd;
 | 
					  final DateTime timeEnd;
 | 
				
			||||||
 | 
					  final NewSurvey survey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const NewPost({
 | 
					  const NewPost({
 | 
				
			||||||
    @required this.target,
 | 
					    @required this.target,
 | 
				
			||||||
@@ -28,6 +40,7 @@ class NewPost {
 | 
				
			|||||||
    @required this.image,
 | 
					    @required this.image,
 | 
				
			||||||
    @required this.pdf,
 | 
					    @required this.pdf,
 | 
				
			||||||
    @required this.timeEnd,
 | 
					    @required this.timeEnd,
 | 
				
			||||||
 | 
					    @required this.survey,
 | 
				
			||||||
  })  : assert(target != null),
 | 
					  })  : assert(target != null),
 | 
				
			||||||
        assert(targetID != null),
 | 
					        assert(targetID != null),
 | 
				
			||||||
        assert(visibility != null),
 | 
					        assert(visibility != null),
 | 
				
			||||||
@@ -35,5 +48,6 @@ class NewPost {
 | 
				
			|||||||
        assert(kind != PostKind.TEXT || content.length > 3),
 | 
					        assert(kind != PostKind.TEXT || content.length > 3),
 | 
				
			||||||
        assert(kind != PostKind.IMAGE || image != null),
 | 
					        assert(kind != PostKind.IMAGE || image != null),
 | 
				
			||||||
        assert(kind != PostKind.PDF || pdf != null),
 | 
					        assert(kind != PostKind.PDF || pdf != null),
 | 
				
			||||||
        assert(kind != PostKind.COUNTDOWN || timeEnd != null);
 | 
					        assert(kind != PostKind.COUNTDOWN || timeEnd != null),
 | 
				
			||||||
 | 
					        assert(kind != PostKind.SURVEY || survey != null);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										137
									
								
								lib/ui/dialogs/new_survey_dialog.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								lib/ui/dialogs/new_survey_dialog.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
				
			|||||||
 | 
					import 'package:comunic/models/new_post.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Create a new survey dialog
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre Hubert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Show a dialog to prompt information about a new survey to create
 | 
				
			||||||
 | 
					Future<NewSurvey> showNewSurveyDialog(
 | 
				
			||||||
 | 
					    {@required BuildContext context, NewSurvey initialSurvey}) async {
 | 
				
			||||||
 | 
					  return await showDialog<NewSurvey>(
 | 
				
			||||||
 | 
					      context: context,
 | 
				
			||||||
 | 
					      builder: (c) => _SurveyDialog(
 | 
				
			||||||
 | 
					            initialSurvey: initialSurvey,
 | 
				
			||||||
 | 
					          ));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _SurveyDialog extends StatefulWidget {
 | 
				
			||||||
 | 
					  final NewSurvey initialSurvey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const _SurveyDialog({Key key, this.initialSurvey}) : super(key: key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  __SurveyDialogState createState() => __SurveyDialogState();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class __SurveyDialogState extends State<_SurveyDialog> {
 | 
				
			||||||
 | 
					  TextEditingController _questionController;
 | 
				
			||||||
 | 
					  final _choiceController = TextEditingController();
 | 
				
			||||||
 | 
					  var _choices = Set<String>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool get canConfirm =>
 | 
				
			||||||
 | 
					      _questionController.text.length > 2 && _choices.length > 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  NewSurvey get newSurvey =>
 | 
				
			||||||
 | 
					      NewSurvey(question: _questionController.text, answers: _choices);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState() {
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					    _questionController = TextEditingController(
 | 
				
			||||||
 | 
					      text: widget.initialSurvey == null ? "" : widget.initialSurvey.question,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    if (widget.initialSurvey != null) _choices = widget.initialSurvey.answers;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    return AlertDialog(
 | 
				
			||||||
 | 
					      title: Text(tr("New survey")),
 | 
				
			||||||
 | 
					      content: _buildBody(),
 | 
				
			||||||
 | 
					      actions: <Widget>[
 | 
				
			||||||
 | 
					        // Cancel
 | 
				
			||||||
 | 
					        MaterialButton(
 | 
				
			||||||
 | 
					          onPressed: () => Navigator.of(context).pop(),
 | 
				
			||||||
 | 
					          child: Text(tr("Cancel").toUpperCase()),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Confirm
 | 
				
			||||||
 | 
					        MaterialButton(
 | 
				
			||||||
 | 
					          onPressed:
 | 
				
			||||||
 | 
					              canConfirm ? () => Navigator.of(context).pop(newSurvey) : null,
 | 
				
			||||||
 | 
					          child: Text(tr("Confirm").toUpperCase()),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Widget _buildBody() {
 | 
				
			||||||
 | 
					    return ConstrainedBox(
 | 
				
			||||||
 | 
					      constraints:
 | 
				
			||||||
 | 
					          BoxConstraints(maxHeight: MediaQuery.of(context).size.height - 50),
 | 
				
			||||||
 | 
					      child: SingleChildScrollView(
 | 
				
			||||||
 | 
					        child: Column(
 | 
				
			||||||
 | 
					            crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
				
			||||||
 | 
					            children: <Widget>[
 | 
				
			||||||
 | 
					              // Survey question
 | 
				
			||||||
 | 
					              TextField(
 | 
				
			||||||
 | 
					                controller: _questionController,
 | 
				
			||||||
 | 
					                onChanged: (s) => setState(() {}),
 | 
				
			||||||
 | 
					                decoration: InputDecoration(
 | 
				
			||||||
 | 
					                    alignLabelWithHint: true, labelText: tr("Question")),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              Container(height: 30),
 | 
				
			||||||
 | 
					              Text(tr("Current choices:")),
 | 
				
			||||||
 | 
					              _buildCurrentChoices(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              Container(
 | 
				
			||||||
 | 
					                height: 10,
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // Add choice form
 | 
				
			||||||
 | 
					              TextField(
 | 
				
			||||||
 | 
					                controller: _choiceController,
 | 
				
			||||||
 | 
					                onChanged: (s) => setState(() {}),
 | 
				
			||||||
 | 
					                onSubmitted: (s) => _addChoice(),
 | 
				
			||||||
 | 
					                decoration: InputDecoration(
 | 
				
			||||||
 | 
					                    hintText: tr("New choice..."),
 | 
				
			||||||
 | 
					                    suffixIcon: IconButton(
 | 
				
			||||||
 | 
					                      icon: Icon(Icons.add),
 | 
				
			||||||
 | 
					                      onPressed: () => _choiceController.text.length > 0
 | 
				
			||||||
 | 
					                          ? _addChoice()
 | 
				
			||||||
 | 
					                          : null,
 | 
				
			||||||
 | 
					                    )),
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
 | 
					            ]),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Widget _buildCurrentChoices() {
 | 
				
			||||||
 | 
					    if (_choices.length == 0) return Text(tr("No choice yet."));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Column(
 | 
				
			||||||
 | 
					      children: _choices
 | 
				
			||||||
 | 
					          .map((f) => ListTile(
 | 
				
			||||||
 | 
					              title: Text(f),
 | 
				
			||||||
 | 
					              dense: true,
 | 
				
			||||||
 | 
					              trailing: IconButton(
 | 
				
			||||||
 | 
					                icon: Icon(Icons.delete),
 | 
				
			||||||
 | 
					                iconSize: 15,
 | 
				
			||||||
 | 
					                onPressed: () => setState(() => _choices.remove(f)),
 | 
				
			||||||
 | 
					              )))
 | 
				
			||||||
 | 
					          .toList(),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _addChoice() {
 | 
				
			||||||
 | 
					    if (_choiceController.text.length == 0) return;
 | 
				
			||||||
 | 
					    setState(() {
 | 
				
			||||||
 | 
					      _choices.add(_choiceController.text);
 | 
				
			||||||
 | 
					      _choiceController.text = "";
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,6 +5,7 @@ import 'package:comunic/enums/post_target.dart';
 | 
				
			|||||||
import 'package:comunic/enums/post_visibility_level.dart';
 | 
					import 'package:comunic/enums/post_visibility_level.dart';
 | 
				
			||||||
import 'package:comunic/helpers/posts_helper.dart';
 | 
					import 'package:comunic/helpers/posts_helper.dart';
 | 
				
			||||||
import 'package:comunic/models/new_post.dart';
 | 
					import 'package:comunic/models/new_post.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/ui/dialogs/new_survey_dialog.dart';
 | 
				
			||||||
import 'package:comunic/utils/files_utils.dart';
 | 
					import 'package:comunic/utils/files_utils.dart';
 | 
				
			||||||
import 'package:comunic/utils/intl_utils.dart';
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
import 'package:comunic/utils/post_utils.dart';
 | 
					import 'package:comunic/utils/post_utils.dart';
 | 
				
			||||||
@@ -50,6 +51,7 @@ class _PostCreateFormWidgetState extends State<PostCreateFormWidget> {
 | 
				
			|||||||
  File _postImage;
 | 
					  File _postImage;
 | 
				
			||||||
  List<int> _postPDF;
 | 
					  List<int> _postPDF;
 | 
				
			||||||
  DateTime _timeEnd;
 | 
					  DateTime _timeEnd;
 | 
				
			||||||
 | 
					  NewSurvey _postSurvey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool get hasImage => _postImage != null;
 | 
					  bool get hasImage => _postImage != null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -57,6 +59,8 @@ class _PostCreateFormWidgetState extends State<PostCreateFormWidget> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  bool get hasTimeEnd => _timeEnd != null;
 | 
					  bool get hasTimeEnd => _timeEnd != null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool get hasSurvey => _postSurvey != null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool get canSubmitForm =>
 | 
					  bool get canSubmitForm =>
 | 
				
			||||||
      !_isCreating && _postTextController.text.length > 5 ||
 | 
					      !_isCreating && _postTextController.text.length > 5 ||
 | 
				
			||||||
      postKind != PostKind.TEXT;
 | 
					      postKind != PostKind.TEXT;
 | 
				
			||||||
@@ -68,6 +72,8 @@ class _PostCreateFormWidgetState extends State<PostCreateFormWidget> {
 | 
				
			|||||||
      return PostKind.PDF;
 | 
					      return PostKind.PDF;
 | 
				
			||||||
    else if (hasTimeEnd)
 | 
					    else if (hasTimeEnd)
 | 
				
			||||||
      return PostKind.COUNTDOWN;
 | 
					      return PostKind.COUNTDOWN;
 | 
				
			||||||
 | 
					    else if (hasSurvey)
 | 
				
			||||||
 | 
					      return PostKind.SURVEY;
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
      return PostKind.TEXT;
 | 
					      return PostKind.TEXT;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -132,6 +138,13 @@ class _PostCreateFormWidgetState extends State<PostCreateFormWidget> {
 | 
				
			|||||||
                        selected: postKind == PostKind.COUNTDOWN,
 | 
					                        selected: postKind == PostKind.COUNTDOWN,
 | 
				
			||||||
                        onTap: _pickCountdownTime,
 | 
					                        onTap: _pickCountdownTime,
 | 
				
			||||||
                      ),
 | 
					                      ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                      // Add survey
 | 
				
			||||||
 | 
					                      _PostOptionWidget(
 | 
				
			||||||
 | 
					                        icon: Icons.insert_chart,
 | 
				
			||||||
 | 
					                        selected: postKind == PostKind.SURVEY,
 | 
				
			||||||
 | 
					                        onTap: _pickSurvey,
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
                    ],
 | 
					                    ],
 | 
				
			||||||
                  ),
 | 
					                  ),
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
@@ -192,6 +205,7 @@ class _PostCreateFormWidgetState extends State<PostCreateFormWidget> {
 | 
				
			|||||||
      _postImage = null;
 | 
					      _postImage = null;
 | 
				
			||||||
      _postPDF = null;
 | 
					      _postPDF = null;
 | 
				
			||||||
      _timeEnd = null;
 | 
					      _timeEnd = null;
 | 
				
			||||||
 | 
					      _postSurvey = null;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -262,6 +276,19 @@ class _PostCreateFormWidgetState extends State<PostCreateFormWidget> {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Pick a new survey for this post
 | 
				
			||||||
 | 
					  Future<void> _pickSurvey() async {
 | 
				
			||||||
 | 
					    final newSurvey =
 | 
				
			||||||
 | 
					        await showNewSurveyDialog(context: context, initialSurvey: _postSurvey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (newSurvey == null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _resetPostSelection();
 | 
				
			||||||
 | 
					    setState(() {
 | 
				
			||||||
 | 
					      _postSurvey = newSurvey;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Submit new post
 | 
					  /// Submit new post
 | 
				
			||||||
  Future<void> _submitForm() async {
 | 
					  Future<void> _submitForm() async {
 | 
				
			||||||
    if (!canSubmitForm)
 | 
					    if (!canSubmitForm)
 | 
				
			||||||
@@ -279,7 +306,7 @@ class _PostCreateFormWidgetState extends State<PostCreateFormWidget> {
 | 
				
			|||||||
          image: _postImage,
 | 
					          image: _postImage,
 | 
				
			||||||
          pdf: _postPDF,
 | 
					          pdf: _postPDF,
 | 
				
			||||||
          timeEnd: _timeEnd,
 | 
					          timeEnd: _timeEnd,
 | 
				
			||||||
      ));
 | 
					          survey: _postSurvey));
 | 
				
			||||||
      setState(() => _isCreating = false);
 | 
					      setState(() => _isCreating = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      showSimpleSnack(context, tr("The post has been successfully created!"));
 | 
					      showSimpleSnack(context, tr("The post has been successfully created!"));
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user