mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-12-25 12:28:50 +00:00
Can create posts
This commit is contained in:
parent
44d47e7f5e
commit
3a5a395f79
8
lib/enums/post_target.dart
Normal file
8
lib/enums/post_target.dart
Normal file
@ -0,0 +1,8 @@
|
||||
/// Posts targets types enum
|
||||
///
|
||||
/// @author Pierre HUBERT
|
||||
|
||||
enum PostTarget {
|
||||
USER_PAGE,
|
||||
GROUP_PAGE
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import 'package:comunic/enums/post_kind.dart';
|
||||
import 'package:comunic/enums/post_target.dart';
|
||||
import 'package:comunic/enums/post_visibility_level.dart';
|
||||
import 'package:comunic/enums/user_access_levels.dart';
|
||||
import 'package:comunic/helpers/comments_helper.dart';
|
||||
@ -6,6 +7,7 @@ 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';
|
||||
import 'package:comunic/models/new_post.dart';
|
||||
import 'package:comunic/models/post.dart';
|
||||
|
||||
/// Posts helper
|
||||
@ -37,6 +39,11 @@ const _APIUserAccessMap = {
|
||||
"full": UserAccessLevels.FULL
|
||||
};
|
||||
|
||||
const _APIPostsTargetKindsMap = {
|
||||
PostTarget.USER_PAGE: "user",
|
||||
PostTarget.GROUP_PAGE: "group"
|
||||
};
|
||||
|
||||
class PostsHelper {
|
||||
/// Get the list of latest posts. Return the list of posts or null in case of
|
||||
/// failure
|
||||
@ -77,6 +84,38 @@ class PostsHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new post
|
||||
///
|
||||
/// This function crash in case of error
|
||||
Future<void> createPost(NewPost post) async {
|
||||
APIRequest request =
|
||||
APIRequest(uri: "posts/create", needLogin: true, args: {
|
||||
"kind-page": _APIPostsTargetKindsMap[post.target],
|
||||
"kind-id": post.targetID.toString(),
|
||||
"visibility": _APIPostsVisibilityLevelMap.map(
|
||||
(s, v) => MapEntry(v, s))[post.visibility],
|
||||
"kind": _APIPostsKindsMap.map((s, k) => MapEntry(k, s))[post.kind],
|
||||
"content": post.content
|
||||
});
|
||||
|
||||
switch (post.kind) {
|
||||
case PostKind.TEXT:
|
||||
break;
|
||||
|
||||
case PostKind.IMAGE:
|
||||
request.addFile("image", post.image);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Exception("Unsupported post type :" + post.kind.toString());
|
||||
break;
|
||||
}
|
||||
|
||||
final response = await request.execWithFiles();
|
||||
|
||||
if (!response.isOK) throw Exception("Could not create the post !");
|
||||
}
|
||||
|
||||
/// Update a post content
|
||||
Future<bool> updateContent(int id, String newContent) async {
|
||||
return (await APIRequest(
|
||||
|
@ -155,6 +155,7 @@ class UsersHelper {
|
||||
data["virtualDirectory"] == "" ? null : data["virtualDirectory"],
|
||||
accountImageURL: data["accountImage"],
|
||||
publicNote: data["publicNote"],
|
||||
canPostTexts: data["can_post_texts"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
|
||||
|
||||
class AdvancedUserInfo extends User {
|
||||
final String publicNote;
|
||||
final bool canPostTexts;
|
||||
|
||||
const AdvancedUserInfo({
|
||||
@required int id,
|
||||
@ -17,7 +18,9 @@ class AdvancedUserInfo extends User {
|
||||
@required String virtualDirectory,
|
||||
@required String accountImageURL,
|
||||
@required this.publicNote,
|
||||
@required this.canPostTexts,
|
||||
}) : assert(publicNote != null),
|
||||
assert(canPostTexts != null),
|
||||
super(
|
||||
id: id,
|
||||
firstName: firstName,
|
||||
|
33
lib/models/new_post.dart
Normal file
33
lib/models/new_post.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:comunic/enums/post_kind.dart';
|
||||
import 'package:comunic/enums/post_target.dart';
|
||||
import 'package:comunic/enums/post_visibility_level.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
/// New post information
|
||||
///
|
||||
/// @author Pierre HUBERT
|
||||
|
||||
class NewPost {
|
||||
final PostTarget target;
|
||||
final int targetID;
|
||||
final PostVisibilityLevel visibility;
|
||||
final String content;
|
||||
final File image;
|
||||
final PostKind kind;
|
||||
|
||||
NewPost({
|
||||
@required this.target,
|
||||
@required this.targetID,
|
||||
@required this.visibility,
|
||||
@required this.content,
|
||||
@required this.kind,
|
||||
@required this.image,
|
||||
}) : assert(target != null),
|
||||
assert(targetID != null),
|
||||
assert(visibility != null),
|
||||
assert(content != null),
|
||||
assert(kind != PostKind.TEXT || content.length > 3),
|
||||
assert(kind != PostKind.IMAGE || image != null);
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
import 'package:comunic/enums/post_target.dart';
|
||||
import 'package:comunic/helpers/posts_helper.dart';
|
||||
import 'package:comunic/helpers/users_helper.dart';
|
||||
import 'package:comunic/models/advanced_user_info.dart';
|
||||
import 'package:comunic/ui/routes/other_friends_lists_route.dart';
|
||||
import 'package:comunic/ui/routes/user_access_denied_route.dart';
|
||||
import 'package:comunic/ui/widgets/network_image_widget.dart';
|
||||
import 'package:comunic/ui/widgets/post_create_form_widget.dart';
|
||||
import 'package:comunic/ui/widgets/posts_list_widget.dart';
|
||||
import 'package:comunic/utils/conversations_utils.dart';
|
||||
import 'package:comunic/utils/intl_utils.dart';
|
||||
@ -38,6 +40,8 @@ class _UserPageRouteState extends State<UserPageRoute> {
|
||||
final double _appBarHeight = 256.0;
|
||||
_PageStatus _status = _PageStatus.LOADING;
|
||||
AdvancedUserInfo _userInfo;
|
||||
GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
|
||||
GlobalKey<RefreshIndicatorState>();
|
||||
|
||||
_setStatus(_PageStatus s) => setState(() => _status = s);
|
||||
|
||||
@ -80,8 +84,10 @@ class _UserPageRouteState extends State<UserPageRoute> {
|
||||
|
||||
return Scaffold(
|
||||
body: RefreshIndicator(
|
||||
key: _refreshIndicatorKey,
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[_buildHeader(), _buildBody()],
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
),
|
||||
onRefresh: _getUserInfo,
|
||||
),
|
||||
@ -175,6 +181,16 @@ class _UserPageRouteState extends State<UserPageRoute> {
|
||||
return SliverList(
|
||||
delegate: SliverChildListDelegate(
|
||||
<Widget>[
|
||||
// Posts create form
|
||||
_userInfo.canPostTexts
|
||||
? PostCreateFormWidget(
|
||||
postTarget: PostTarget.USER_PAGE,
|
||||
targetID: _userInfo.id,
|
||||
onCreated: _postCreated,
|
||||
)
|
||||
: Container(),
|
||||
|
||||
// Posts list
|
||||
PostsListWidget(
|
||||
getPostsList: () => _postsHelper.getUserPosts(widget.userID),
|
||||
showPostsTarget: false,
|
||||
@ -199,4 +215,9 @@ class _UserPageRouteState extends State<UserPageRoute> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Method called once a post has been created
|
||||
void _postCreated() {
|
||||
_refreshIndicatorKey.currentState.show();
|
||||
}
|
||||
}
|
||||
|
205
lib/ui/widgets/post_create_form_widget.dart
Normal file
205
lib/ui/widgets/post_create_form_widget.dart
Normal file
@ -0,0 +1,205 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:comunic/enums/post_kind.dart';
|
||||
import 'package:comunic/enums/post_target.dart';
|
||||
import 'package:comunic/enums/post_visibility_level.dart';
|
||||
import 'package:comunic/helpers/posts_helper.dart';
|
||||
import 'package:comunic/models/new_post.dart';
|
||||
import 'package:comunic/utils/files_utils.dart';
|
||||
import 'package:comunic/utils/intl_utils.dart';
|
||||
import 'package:comunic/utils/post_utils.dart';
|
||||
import 'package:comunic/utils/ui_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Widget that allows to create posts
|
||||
///
|
||||
/// @author Pierre HUBERT
|
||||
|
||||
const _ActiveButtonsColor = Colors.blue;
|
||||
const _ActiveButtonsTextColor = Colors.white;
|
||||
const _InactiveButtonsColor = Colors.grey;
|
||||
const _InactiveButtonsTextColor = Colors.black;
|
||||
|
||||
class PostCreateFormWidget extends StatefulWidget {
|
||||
final PostTarget postTarget;
|
||||
final int targetID;
|
||||
final void Function() onCreated;
|
||||
|
||||
const PostCreateFormWidget({
|
||||
Key key,
|
||||
@required this.postTarget,
|
||||
@required this.targetID,
|
||||
@required this.onCreated,
|
||||
}) : assert(postTarget != null),
|
||||
assert(targetID != null),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_PostCreateFormWidgetState createState() => _PostCreateFormWidgetState();
|
||||
}
|
||||
|
||||
class _PostCreateFormWidgetState extends State<PostCreateFormWidget> {
|
||||
// Helpers
|
||||
final PostsHelper _postHelper = PostsHelper();
|
||||
|
||||
// Class members
|
||||
bool _isCreating = false;
|
||||
final TextEditingController _postTextController = TextEditingController();
|
||||
PostVisibilityLevel _postVisibilityLevel;
|
||||
File _postImage;
|
||||
|
||||
bool get hasImage => _postImage != null;
|
||||
|
||||
bool get canSubmitForm =>
|
||||
!_isCreating && _postTextController.text.length > 5 || hasImage;
|
||||
|
||||
PostKind get postKind {
|
||||
if (hasImage)
|
||||
return PostKind.IMAGE;
|
||||
else
|
||||
return PostKind.TEXT;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_postVisibilityLevel = widget.postTarget == PostTarget.GROUP_PAGE
|
||||
? PostVisibilityLevel.GROUP_MEMBERS
|
||||
: PostVisibilityLevel.FRIENDS;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
// Post text content
|
||||
TextField(
|
||||
controller: _postTextController,
|
||||
minLines: 3,
|
||||
maxLines: 10,
|
||||
decoration: InputDecoration(hintText: tr("Create a new post...")),
|
||||
onChanged: (s) => setState(() {}),
|
||||
),
|
||||
|
||||
// Post options
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 16.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
// Include image button
|
||||
_PostOptionWidget(
|
||||
icon: Icons.image,
|
||||
selected: hasImage,
|
||||
onTap: _pickImageForPost,
|
||||
),
|
||||
|
||||
// Post visibility level
|
||||
_PostOptionWidget(
|
||||
icon: PostVisibilityLevelsMapIcons[_postVisibilityLevel],
|
||||
selected: false,
|
||||
customColor: Colors.black,
|
||||
onTap: _changeVisibilityLevel,
|
||||
),
|
||||
|
||||
// Submit post button
|
||||
_isCreating
|
||||
? Container()
|
||||
: FlatButton(
|
||||
child: Text(tr("Send").toUpperCase()),
|
||||
onPressed: canSubmitForm ? _submitForm : null,
|
||||
color: _ActiveButtonsColor,
|
||||
textColor: _ActiveButtonsTextColor,
|
||||
disabledColor: _InactiveButtonsColor,
|
||||
disabledTextColor: _InactiveButtonsTextColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Change post visibility level
|
||||
Future<void> _changeVisibilityLevel() async {
|
||||
final newLevel = await showPostVisibilityPicker(
|
||||
context: context,
|
||||
initialLevel: _postVisibilityLevel,
|
||||
isGroup: widget.postTarget == PostTarget.GROUP_PAGE,
|
||||
);
|
||||
|
||||
setState(() => _postVisibilityLevel = newLevel);
|
||||
}
|
||||
|
||||
/// Pick an image for the new post
|
||||
Future<void> _pickImageForPost() async {
|
||||
final image = await pickImage(context);
|
||||
|
||||
if (image == null) return;
|
||||
|
||||
setState(() {
|
||||
this._postImage = image;
|
||||
});
|
||||
}
|
||||
|
||||
/// Submit new post
|
||||
Future<void> _submitForm() async {
|
||||
if (!canSubmitForm)
|
||||
showSimpleSnack(context, tr("Form can not be submitted at this point!"));
|
||||
|
||||
setState(() => _isCreating = true);
|
||||
|
||||
try {
|
||||
await _postHelper.createPost(NewPost(
|
||||
target: widget.postTarget,
|
||||
targetID: widget.targetID,
|
||||
visibility: _postVisibilityLevel,
|
||||
content: _postTextController.text,
|
||||
kind: postKind,
|
||||
image: _postImage,
|
||||
));
|
||||
setState(() => _isCreating = false);
|
||||
|
||||
showSimpleSnack(context, tr("The post has been successfully created!"));
|
||||
|
||||
widget.onCreated();
|
||||
} catch (e) {
|
||||
setState(() => _isCreating = false);
|
||||
print("Error while creating post : " + e.toString());
|
||||
showSimpleSnack(context, tr("Could not create post !"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Widget for a single post option
|
||||
class _PostOptionWidget extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final bool selected;
|
||||
final Color customColor;
|
||||
final void Function() onTap;
|
||||
|
||||
const _PostOptionWidget(
|
||||
{Key key,
|
||||
@required this.icon,
|
||||
@required this.selected,
|
||||
@required this.onTap,
|
||||
this.customColor})
|
||||
: assert(icon != null),
|
||||
assert(selected != null),
|
||||
assert(onTap != null),
|
||||
super(key: key);
|
||||
|
||||
bool get hasCustomColor => customColor != null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
icon: Icon(icon),
|
||||
onPressed: onTap,
|
||||
color: hasCustomColor
|
||||
? customColor
|
||||
: selected ? _ActiveButtonsColor : _InactiveButtonsColor,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user