mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-12-26 04:48:51 +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/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);
|
||||
});
|
||||
}
|
||||
}
|
@ -158,6 +158,13 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -45,6 +45,9 @@ dependencies:
|
||||
# Allows to parse HTML special characters
|
||||
html: ^0.14.0+2
|
||||
|
||||
# Module that display the charts for the surveys
|
||||
pie_chart: ^1.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
Loading…
Reference in New Issue
Block a user