mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-11-22 21:09:21 +00:00
Add posts with surveys support
This commit is contained in:
parent
66b4d19004
commit
fdf8cd08ef
@ -2,6 +2,7 @@ import 'package:comunic/enums/post_kind.dart';
|
|||||||
import 'package:comunic/enums/post_visibility_level.dart';
|
import 'package:comunic/enums/post_visibility_level.dart';
|
||||||
import 'package:comunic/enums/user_access_levels.dart';
|
import 'package:comunic/enums/user_access_levels.dart';
|
||||||
import 'package:comunic/helpers/comments_helper.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/comments_list.dart';
|
||||||
import 'package:comunic/lists/posts_list.dart';
|
import 'package:comunic/lists/posts_list.dart';
|
||||||
import 'package:comunic/models/api_request.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
|
/// Get the list of latest posts. Return the list of posts or null in case of
|
||||||
/// failure
|
/// failure
|
||||||
Future<PostsList> getLatest({int from = 0}) async {
|
Future<PostsList> getLatest({int from = 0}) async {
|
||||||
final response = await APIRequest(
|
final response =
|
||||||
uri: "posts/get_latest",
|
await APIRequest(uri: "posts/get_latest", needLogin: true, args: {
|
||||||
needLogin: true,
|
|
||||||
args: {
|
|
||||||
"include_groups": true.toString(),
|
"include_groups": true.toString(),
|
||||||
"startFrom": from.toString(),
|
"startFrom": from.toString(),
|
||||||
}
|
}).exec();
|
||||||
).exec();
|
|
||||||
|
|
||||||
if (response.code != 200) return null;
|
if (response.code != 200) return null;
|
||||||
|
|
||||||
@ -65,11 +63,8 @@ class PostsHelper {
|
|||||||
final response = await APIRequest(
|
final response = await APIRequest(
|
||||||
uri: "posts/get_user",
|
uri: "posts/get_user",
|
||||||
needLogin: true,
|
needLogin: true,
|
||||||
args: {
|
args: {"userID": userID.toString(), "startFrom": from.toString()})
|
||||||
"userID": userID.toString(),
|
.exec();
|
||||||
"startFrom": from.toString()
|
|
||||||
}
|
|
||||||
).exec();
|
|
||||||
|
|
||||||
if (response.code != 200) return null;
|
if (response.code != 200) return null;
|
||||||
|
|
||||||
@ -102,7 +97,8 @@ class PostsHelper {
|
|||||||
needLogin: true,
|
needLogin: true,
|
||||||
args: {
|
args: {
|
||||||
"postID": id.toString(),
|
"postID": id.toString(),
|
||||||
"new_level": _APIPostsVisibilityLevelMap.map((k, v) => MapEntry(v, k))[level]
|
"new_level":
|
||||||
|
_APIPostsVisibilityLevelMap.map((k, v) => MapEntry(v, k))[level]
|
||||||
},
|
},
|
||||||
).exec())
|
).exec())
|
||||||
.isOK;
|
.isOK;
|
||||||
@ -120,6 +116,8 @@ class PostsHelper {
|
|||||||
|
|
||||||
/// Turn an API entry into a [Post] object
|
/// Turn an API entry into a [Post] object
|
||||||
Post _apiToPost(Map<String, dynamic> map) {
|
Post _apiToPost(Map<String, dynamic> map) {
|
||||||
|
final postKind = _APIPostsKindsMap[map["kind"]];
|
||||||
|
|
||||||
// Parse comments
|
// Parse comments
|
||||||
CommentsList comments;
|
CommentsList comments;
|
||||||
if (map["comments"] != null) {
|
if (map["comments"] != null) {
|
||||||
@ -128,6 +126,10 @@ class PostsHelper {
|
|||||||
.forEach((v) => comments.add(CommentsHelper.apiToComment(v)));
|
.forEach((v) => comments.add(CommentsHelper.apiToComment(v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final survey = postKind == PostKind.SURVEY
|
||||||
|
? SurveyHelper.apiToSurvey(map["data_survey"])
|
||||||
|
: null;
|
||||||
|
|
||||||
return Post(
|
return Post(
|
||||||
id: map["ID"],
|
id: map["ID"],
|
||||||
userID: map["userID"],
|
userID: map["userID"],
|
||||||
@ -136,7 +138,7 @@ class PostsHelper {
|
|||||||
timeSent: map["post_time"],
|
timeSent: map["post_time"],
|
||||||
content: map["content"],
|
content: map["content"],
|
||||||
visibilityLevel: _APIPostsVisibilityLevelMap[map["visibility_level"]],
|
visibilityLevel: _APIPostsVisibilityLevelMap[map["visibility_level"]],
|
||||||
kind: _APIPostsKindsMap[map["kind"]],
|
kind: postKind,
|
||||||
fileSize: map["file_size"],
|
fileSize: map["file_size"],
|
||||||
fileType: map["file_type"],
|
fileType: map["file_type"],
|
||||||
filePath: map["file_path"],
|
filePath: map["file_path"],
|
||||||
@ -150,6 +152,6 @@ class PostsHelper {
|
|||||||
userLike: map["userlike"],
|
userLike: map["userlike"],
|
||||||
access: _APIUserAccessMap[map["user_access"]],
|
access: _APIUserAccessMap[map["user_access"]],
|
||||||
comments: comments,
|
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/enums/user_access_levels.dart';
|
||||||
import 'package:comunic/lists/comments_list.dart';
|
import 'package:comunic/lists/comments_list.dart';
|
||||||
import 'package:comunic/models/like_element.dart';
|
import 'package:comunic/models/like_element.dart';
|
||||||
|
import 'package:comunic/models/survey.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
/// Single post information
|
/// Single post information
|
||||||
@ -31,6 +32,7 @@ class Post implements LikeElement {
|
|||||||
bool userLike;
|
bool userLike;
|
||||||
final UserAccessLevels access;
|
final UserAccessLevels access;
|
||||||
final CommentsList comments;
|
final CommentsList comments;
|
||||||
|
final Survey survey;
|
||||||
|
|
||||||
Post(
|
Post(
|
||||||
{@required this.id,
|
{@required this.id,
|
||||||
@ -53,7 +55,8 @@ class Post implements LikeElement {
|
|||||||
@required this.likes,
|
@required this.likes,
|
||||||
@required this.userLike,
|
@required this.userLike,
|
||||||
@required this.access,
|
@required this.access,
|
||||||
@required this.comments})
|
@required this.comments,
|
||||||
|
@required this.survey})
|
||||||
: assert(id != null),
|
: assert(id != null),
|
||||||
assert(userID != null),
|
assert(userID != null),
|
||||||
assert(userPageID != 0 || groupID != 0),
|
assert(userPageID != 0 || groupID != 0),
|
||||||
@ -62,6 +65,7 @@ class Post implements LikeElement {
|
|||||||
assert(visibilityLevel != null),
|
assert(visibilityLevel != null),
|
||||||
assert(kind != null),
|
assert(kind != null),
|
||||||
assert(kind != PostKind.COUNTDOWN || timeEnd != null),
|
assert(kind != PostKind.COUNTDOWN || timeEnd != null),
|
||||||
|
assert(kind != PostKind.SURVEY || survey != null),
|
||||||
assert(likes != null),
|
assert(likes != null),
|
||||||
assert(userLike != null),
|
assert(userLike != null),
|
||||||
assert(access != 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/tiles/comment_tile.dart';
|
||||||
import 'package:comunic/ui/widgets/account_image_widget.dart';
|
import 'package:comunic/ui/widgets/account_image_widget.dart';
|
||||||
import 'package:comunic/ui/widgets/network_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/date_utils.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';
|
||||||
@ -171,6 +172,10 @@ class _PostTileState extends State<PostTile> {
|
|||||||
postContent = _buildPostWebLink();
|
postContent = _buildPostWebLink();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PostKind.SURVEY:
|
||||||
|
postContent = _buildPostSurvey();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
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
|
/// Build the list of comments
|
||||||
Widget _buildComments() {
|
Widget _buildComments() {
|
||||||
assert(widget.post.hasComments);
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -158,6 +158,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
version: "1.5.0"
|
||||||
|
pie_chart:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pie_chart
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
quiver:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -45,6 +45,9 @@ dependencies:
|
|||||||
# Allows to parse HTML special characters
|
# Allows to parse HTML special characters
|
||||||
html: ^0.14.0+2
|
html: ^0.14.0+2
|
||||||
|
|
||||||
|
# Module that display the charts for the surveys
|
||||||
|
pie_chart: ^1.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
Loading…
Reference in New Issue
Block a user