mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 04:04:18 +00:00 
			
		
		
		
	Add posts with surveys support
This commit is contained in:
		@@ -2,6 +2,7 @@ import 'package:comunic/enums/post_kind.dart';
 | 
			
		||||
import 'package:comunic/enums/post_visibility_level.dart';
 | 
			
		||||
import 'package:comunic/enums/user_access_levels.dart';
 | 
			
		||||
import 'package:comunic/helpers/comments_helper.dart';
 | 
			
		||||
import 'package:comunic/helpers/survey_helper.dart';
 | 
			
		||||
import 'package:comunic/lists/comments_list.dart';
 | 
			
		||||
import 'package:comunic/lists/posts_list.dart';
 | 
			
		||||
import 'package:comunic/models/api_request.dart';
 | 
			
		||||
@@ -40,14 +41,11 @@ class PostsHelper {
 | 
			
		||||
  /// Get the list of latest posts. Return the list of posts or null in case of
 | 
			
		||||
  /// failure
 | 
			
		||||
  Future<PostsList> getLatest({int from = 0}) async {
 | 
			
		||||
    final response = await APIRequest(
 | 
			
		||||
      uri: "posts/get_latest",
 | 
			
		||||
      needLogin: true,
 | 
			
		||||
      args: {
 | 
			
		||||
        "include_groups": true.toString(),
 | 
			
		||||
        "startFrom": from.toString(),
 | 
			
		||||
      }
 | 
			
		||||
    ).exec();
 | 
			
		||||
    final response =
 | 
			
		||||
        await APIRequest(uri: "posts/get_latest", needLogin: true, args: {
 | 
			
		||||
      "include_groups": true.toString(),
 | 
			
		||||
      "startFrom": from.toString(),
 | 
			
		||||
    }).exec();
 | 
			
		||||
 | 
			
		||||
    if (response.code != 200) return null;
 | 
			
		||||
 | 
			
		||||
@@ -63,13 +61,10 @@ class PostsHelper {
 | 
			
		||||
  /// Get the list of posts of a user
 | 
			
		||||
  Future<PostsList> getUserPosts(int userID, {int from = 0}) async {
 | 
			
		||||
    final response = await APIRequest(
 | 
			
		||||
        uri: "posts/get_user",
 | 
			
		||||
        needLogin: true,
 | 
			
		||||
        args: {
 | 
			
		||||
          "userID": userID.toString(),
 | 
			
		||||
          "startFrom": from.toString()
 | 
			
		||||
        }
 | 
			
		||||
    ).exec();
 | 
			
		||||
            uri: "posts/get_user",
 | 
			
		||||
            needLogin: true,
 | 
			
		||||
            args: {"userID": userID.toString(), "startFrom": from.toString()})
 | 
			
		||||
        .exec();
 | 
			
		||||
 | 
			
		||||
    if (response.code != 200) return null;
 | 
			
		||||
 | 
			
		||||
@@ -102,7 +97,8 @@ class PostsHelper {
 | 
			
		||||
      needLogin: true,
 | 
			
		||||
      args: {
 | 
			
		||||
        "postID": id.toString(),
 | 
			
		||||
        "new_level": _APIPostsVisibilityLevelMap.map((k, v) => MapEntry(v, k))[level]
 | 
			
		||||
        "new_level":
 | 
			
		||||
            _APIPostsVisibilityLevelMap.map((k, v) => MapEntry(v, k))[level]
 | 
			
		||||
      },
 | 
			
		||||
    ).exec())
 | 
			
		||||
        .isOK;
 | 
			
		||||
@@ -120,6 +116,8 @@ class PostsHelper {
 | 
			
		||||
 | 
			
		||||
  /// Turn an API entry into a [Post] object
 | 
			
		||||
  Post _apiToPost(Map<String, dynamic> map) {
 | 
			
		||||
    final postKind = _APIPostsKindsMap[map["kind"]];
 | 
			
		||||
 | 
			
		||||
    // Parse comments
 | 
			
		||||
    CommentsList comments;
 | 
			
		||||
    if (map["comments"] != null) {
 | 
			
		||||
@@ -128,28 +126,32 @@ class PostsHelper {
 | 
			
		||||
          .forEach((v) => comments.add(CommentsHelper.apiToComment(v)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final survey = postKind == PostKind.SURVEY
 | 
			
		||||
        ? SurveyHelper.apiToSurvey(map["data_survey"])
 | 
			
		||||
        : null;
 | 
			
		||||
 | 
			
		||||
    return Post(
 | 
			
		||||
      id: map["ID"],
 | 
			
		||||
      userID: map["userID"],
 | 
			
		||||
      userPageID: map["user_page_id"],
 | 
			
		||||
      groupID: map["group_id"],
 | 
			
		||||
      timeSent: map["post_time"],
 | 
			
		||||
      content: map["content"],
 | 
			
		||||
      visibilityLevel: _APIPostsVisibilityLevelMap[map["visibility_level"]],
 | 
			
		||||
      kind: _APIPostsKindsMap[map["kind"]],
 | 
			
		||||
      fileSize: map["file_size"],
 | 
			
		||||
      fileType: map["file_type"],
 | 
			
		||||
      filePath: map["file_path"],
 | 
			
		||||
      fileURL: map["file_path_url"],
 | 
			
		||||
      timeEnd: map["time_end"],
 | 
			
		||||
      linkURL: map["link_url"],
 | 
			
		||||
      linkTitle: map["link_title"],
 | 
			
		||||
      linkDescription: map["link_description"],
 | 
			
		||||
      linkImage: map["link_image"],
 | 
			
		||||
      likes: map["likes"],
 | 
			
		||||
      userLike: map["userlike"],
 | 
			
		||||
      access: _APIUserAccessMap[map["user_access"]],
 | 
			
		||||
      comments: comments,
 | 
			
		||||
    );
 | 
			
		||||
        id: map["ID"],
 | 
			
		||||
        userID: map["userID"],
 | 
			
		||||
        userPageID: map["user_page_id"],
 | 
			
		||||
        groupID: map["group_id"],
 | 
			
		||||
        timeSent: map["post_time"],
 | 
			
		||||
        content: map["content"],
 | 
			
		||||
        visibilityLevel: _APIPostsVisibilityLevelMap[map["visibility_level"]],
 | 
			
		||||
        kind: postKind,
 | 
			
		||||
        fileSize: map["file_size"],
 | 
			
		||||
        fileType: map["file_type"],
 | 
			
		||||
        filePath: map["file_path"],
 | 
			
		||||
        fileURL: map["file_path_url"],
 | 
			
		||||
        timeEnd: map["time_end"],
 | 
			
		||||
        linkURL: map["link_url"],
 | 
			
		||||
        linkTitle: map["link_title"],
 | 
			
		||||
        linkDescription: map["link_description"],
 | 
			
		||||
        linkImage: map["link_image"],
 | 
			
		||||
        likes: map["likes"],
 | 
			
		||||
        userLike: map["userlike"],
 | 
			
		||||
        access: _APIUserAccessMap[map["user_access"]],
 | 
			
		||||
        comments: comments,
 | 
			
		||||
        survey: survey);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								lib/helpers/survey_helper.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								lib/helpers/survey_helper.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import 'package:comunic/models/api_request.dart';
 | 
			
		||||
import 'package:comunic/models/survey.dart';
 | 
			
		||||
import 'package:comunic/models/survey_choice.dart';
 | 
			
		||||
import 'package:meta/meta.dart';
 | 
			
		||||
 | 
			
		||||
/// Survey helper
 | 
			
		||||
///
 | 
			
		||||
/// @author Pierre HUBERT
 | 
			
		||||
 | 
			
		||||
class SurveyHelper {
 | 
			
		||||
  /// Cancel the response of a user to a survey
 | 
			
		||||
  Future<bool> cancelResponse(Survey survey) async {
 | 
			
		||||
    return (await APIRequest(
 | 
			
		||||
            uri: "surveys/cancel_response",
 | 
			
		||||
            needLogin: true,
 | 
			
		||||
            args: {"postID": survey.postID.toString()}).exec())
 | 
			
		||||
        .isOK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Send the response of a user to a survey
 | 
			
		||||
  Future<bool> respondToSurvey(
 | 
			
		||||
      {@required Survey survey, @required SurveyChoice choice}) async {
 | 
			
		||||
    assert(survey != null);
 | 
			
		||||
    assert(choice != null);
 | 
			
		||||
 | 
			
		||||
    return (await APIRequest(
 | 
			
		||||
      uri: "surveys/send_response",
 | 
			
		||||
      needLogin: true,
 | 
			
		||||
      args: {
 | 
			
		||||
        "postID": survey.postID.toString(),
 | 
			
		||||
        "choiceID": choice.id.toString(),
 | 
			
		||||
      },
 | 
			
		||||
    ).exec())
 | 
			
		||||
        .isOK;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Turn an API entry into a [Survey] object
 | 
			
		||||
  static Survey apiToSurvey(Map<String, dynamic> map) {
 | 
			
		||||
    // Parse survey responses
 | 
			
		||||
    Set<SurveyChoice> choices = Set();
 | 
			
		||||
 | 
			
		||||
    map["choices"].forEach((k, e) => choices.add(SurveyChoice(
 | 
			
		||||
        id: e["choiceID"], name: e["name"], responses: e["responses"])));
 | 
			
		||||
 | 
			
		||||
    return Survey(
 | 
			
		||||
      id: map["ID"],
 | 
			
		||||
      userID: map["userID"],
 | 
			
		||||
      postID: map["postID"],
 | 
			
		||||
      creationTime: map["creation_time"],
 | 
			
		||||
      question: map["question"],
 | 
			
		||||
      userChoice: map["user_choice"],
 | 
			
		||||
      choices: choices,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,7 @@ import 'package:comunic/enums/post_visibility_level.dart';
 | 
			
		||||
import 'package:comunic/enums/user_access_levels.dart';
 | 
			
		||||
import 'package:comunic/lists/comments_list.dart';
 | 
			
		||||
import 'package:comunic/models/like_element.dart';
 | 
			
		||||
import 'package:comunic/models/survey.dart';
 | 
			
		||||
import 'package:meta/meta.dart';
 | 
			
		||||
 | 
			
		||||
/// Single post information
 | 
			
		||||
@@ -31,6 +32,7 @@ class Post implements LikeElement {
 | 
			
		||||
  bool userLike;
 | 
			
		||||
  final UserAccessLevels access;
 | 
			
		||||
  final CommentsList comments;
 | 
			
		||||
  final Survey survey;
 | 
			
		||||
 | 
			
		||||
  Post(
 | 
			
		||||
      {@required this.id,
 | 
			
		||||
@@ -53,7 +55,8 @@ class Post implements LikeElement {
 | 
			
		||||
      @required this.likes,
 | 
			
		||||
      @required this.userLike,
 | 
			
		||||
      @required this.access,
 | 
			
		||||
      @required this.comments})
 | 
			
		||||
      @required this.comments,
 | 
			
		||||
      @required this.survey})
 | 
			
		||||
      : assert(id != null),
 | 
			
		||||
        assert(userID != null),
 | 
			
		||||
        assert(userPageID != 0 || groupID != 0),
 | 
			
		||||
@@ -62,6 +65,7 @@ class Post implements LikeElement {
 | 
			
		||||
        assert(visibilityLevel != null),
 | 
			
		||||
        assert(kind != null),
 | 
			
		||||
        assert(kind != PostKind.COUNTDOWN || timeEnd != null),
 | 
			
		||||
        assert(kind != PostKind.SURVEY || survey != null),
 | 
			
		||||
        assert(likes != null),
 | 
			
		||||
        assert(userLike != null),
 | 
			
		||||
        assert(access != null);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										52
									
								
								lib/models/survey.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								lib/models/survey.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
import 'package:comunic/models/survey_choice.dart';
 | 
			
		||||
import 'package:meta/meta.dart';
 | 
			
		||||
 | 
			
		||||
/// Survey information
 | 
			
		||||
///
 | 
			
		||||
/// @author Pierre HUBERT
 | 
			
		||||
 | 
			
		||||
class Survey {
 | 
			
		||||
  final int id;
 | 
			
		||||
  final int userID;
 | 
			
		||||
  final int postID;
 | 
			
		||||
  final int creationTime;
 | 
			
		||||
  final String question;
 | 
			
		||||
  int userChoice;
 | 
			
		||||
  final Set<SurveyChoice> choices;
 | 
			
		||||
 | 
			
		||||
  Survey({
 | 
			
		||||
    @required this.id,
 | 
			
		||||
    @required this.userID,
 | 
			
		||||
    @required this.postID,
 | 
			
		||||
    @required this.creationTime,
 | 
			
		||||
    @required this.question,
 | 
			
		||||
    @required this.userChoice,
 | 
			
		||||
    @required this.choices,
 | 
			
		||||
  })  : assert(id != null),
 | 
			
		||||
        assert(userID != null),
 | 
			
		||||
        assert(postID != null),
 | 
			
		||||
        assert(creationTime != null),
 | 
			
		||||
        assert(question != null),
 | 
			
		||||
        assert(userChoice != null),
 | 
			
		||||
        assert(choices != null),
 | 
			
		||||
        assert(choices.length > 0);
 | 
			
		||||
 | 
			
		||||
  bool get hasResponded => this.userChoice != null && this.userChoice > 0;
 | 
			
		||||
 | 
			
		||||
  SurveyChoice get userResponse {
 | 
			
		||||
    if (!hasResponded) return null;
 | 
			
		||||
 | 
			
		||||
    return choices.firstWhere((e) => e.id == userChoice);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void cancelUserResponse() {
 | 
			
		||||
    if (hasResponded) userResponse.responses--;
 | 
			
		||||
    userChoice = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void setUserResponse(SurveyChoice choice) {
 | 
			
		||||
    cancelUserResponse();
 | 
			
		||||
    userChoice = choice.id;
 | 
			
		||||
    choice.responses++;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								lib/models/survey_choice.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								lib/models/survey_choice.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
import 'package:meta/meta.dart';
 | 
			
		||||
 | 
			
		||||
/// Single survey choice
 | 
			
		||||
///
 | 
			
		||||
/// @author Pierre HUBERT
 | 
			
		||||
 | 
			
		||||
class SurveyChoice {
 | 
			
		||||
  final int id;
 | 
			
		||||
  final String name;
 | 
			
		||||
  int responses;
 | 
			
		||||
 | 
			
		||||
  SurveyChoice({
 | 
			
		||||
    @required this.id,
 | 
			
		||||
    @required this.name,
 | 
			
		||||
    @required this.responses,
 | 
			
		||||
  })  : assert(id != null),
 | 
			
		||||
        assert(name != null),
 | 
			
		||||
        assert(responses != null);
 | 
			
		||||
}
 | 
			
		||||
@@ -16,6 +16,7 @@ import 'package:comunic/models/user.dart';
 | 
			
		||||
import 'package:comunic/ui/tiles/comment_tile.dart';
 | 
			
		||||
import 'package:comunic/ui/widgets/account_image_widget.dart';
 | 
			
		||||
import 'package:comunic/ui/widgets/network_image_widget.dart';
 | 
			
		||||
import 'package:comunic/ui/widgets/survey_widget.dart';
 | 
			
		||||
import 'package:comunic/utils/date_utils.dart';
 | 
			
		||||
import 'package:comunic/utils/files_utils.dart';
 | 
			
		||||
import 'package:comunic/utils/intl_utils.dart';
 | 
			
		||||
@@ -171,6 +172,10 @@ class _PostTileState extends State<PostTile> {
 | 
			
		||||
        postContent = _buildPostWebLink();
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case PostKind.SURVEY:
 | 
			
		||||
        postContent = _buildPostSurvey();
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -294,6 +299,13 @@ class _PostTileState extends State<PostTile> {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Build post survey
 | 
			
		||||
  Widget _buildPostSurvey() {
 | 
			
		||||
    return SurveyWidget(
 | 
			
		||||
      survey: widget.post.survey,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Build the list of comments
 | 
			
		||||
  Widget _buildComments() {
 | 
			
		||||
    assert(widget.post.hasComments);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										121
									
								
								lib/ui/widgets/survey_widget.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								lib/ui/widgets/survey_widget.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
			
		||||
import 'package:comunic/helpers/survey_helper.dart';
 | 
			
		||||
import 'package:comunic/models/survey.dart';
 | 
			
		||||
import 'package:comunic/models/survey_choice.dart';
 | 
			
		||||
import 'package:comunic/utils/intl_utils.dart';
 | 
			
		||||
import 'package:comunic/utils/ui_utils.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:pie_chart/pie_chart.dart';
 | 
			
		||||
 | 
			
		||||
/// Survey widget
 | 
			
		||||
///
 | 
			
		||||
/// @author Pierre HUBERT
 | 
			
		||||
 | 
			
		||||
class SurveyWidget extends StatefulWidget {
 | 
			
		||||
  final Survey survey;
 | 
			
		||||
 | 
			
		||||
  const SurveyWidget({Key key, @required this.survey})
 | 
			
		||||
      : assert(survey != null),
 | 
			
		||||
        super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  _SurveyWidgetState createState() => _SurveyWidgetState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _SurveyWidgetState extends State<SurveyWidget> {
 | 
			
		||||
  final SurveyHelper _helper = SurveyHelper();
 | 
			
		||||
 | 
			
		||||
  Survey get survey => widget.survey;
 | 
			
		||||
 | 
			
		||||
  Map<String, double> _buildDataMap() {
 | 
			
		||||
    Map<String, double> data = Map();
 | 
			
		||||
    widget.survey.choices.forEach((e) => data.putIfAbsent(
 | 
			
		||||
        e.name + " (" + e.responses.toString() + ")", () => 1.0 * e.responses));
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Column(
 | 
			
		||||
      children: <Widget>[
 | 
			
		||||
        Text(
 | 
			
		||||
          widget.survey.question,
 | 
			
		||||
          style: TextStyle(fontWeight: FontWeight.bold),
 | 
			
		||||
        ),
 | 
			
		||||
        _buildUserResponse(),
 | 
			
		||||
        PieChart(
 | 
			
		||||
          dataMap: _buildDataMap(),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildUserResponse() {
 | 
			
		||||
    if (survey.hasResponded) return _buildUserRespondedWidget();
 | 
			
		||||
    return _buildUserResponseFormWidget();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildUserRespondedWidget() {
 | 
			
		||||
    return Row(
 | 
			
		||||
      mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
      children: <Widget>[
 | 
			
		||||
        Flexible(
 | 
			
		||||
          child: Text(
 | 
			
		||||
            tr("Your response: %response%",
 | 
			
		||||
                args: {"response": survey.userResponse.name}),
 | 
			
		||||
            textAlign: TextAlign.center,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        MaterialButton(
 | 
			
		||||
          child: Text(tr("Cancel").toUpperCase()),
 | 
			
		||||
          onPressed: _cancelUserResponse,
 | 
			
		||||
        )
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _cancelUserResponse() async {
 | 
			
		||||
    if (!await showConfirmDialog(
 | 
			
		||||
      context: context,
 | 
			
		||||
      title: tr("Cancel response to survey"),
 | 
			
		||||
      message:
 | 
			
		||||
          tr("Do you really want to cancel your response to this survey ?"),
 | 
			
		||||
    )) return;
 | 
			
		||||
 | 
			
		||||
    if (!await _helper.cancelResponse(survey)) {
 | 
			
		||||
      showSimpleSnack(
 | 
			
		||||
          context, tr("Could not cancel your response to the survey !"));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setState(() {
 | 
			
		||||
      survey.cancelUserResponse();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildUserResponseFormWidget() {
 | 
			
		||||
    return DropdownButton<SurveyChoice>(
 | 
			
		||||
      hint: Text(tr("Respond to survey")),
 | 
			
		||||
      items: survey.choices
 | 
			
		||||
          .map(
 | 
			
		||||
            (f) => DropdownMenuItem<SurveyChoice>(
 | 
			
		||||
                  value: f,
 | 
			
		||||
                  child: Text(f.name),
 | 
			
		||||
                ),
 | 
			
		||||
          )
 | 
			
		||||
          .toList(),
 | 
			
		||||
      onChanged: _respondToSurvey,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Respond to survey
 | 
			
		||||
  Future<void> _respondToSurvey(SurveyChoice choice) async {
 | 
			
		||||
    // Send the response to the server
 | 
			
		||||
    if (!await _helper.respondToSurvey(survey: survey, choice: choice))
 | 
			
		||||
      return showSimpleSnack(
 | 
			
		||||
          context, tr("Could not send your response to the survey!"));
 | 
			
		||||
 | 
			
		||||
    setState(() {
 | 
			
		||||
      survey.setUserResponse(choice);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user