mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-11-22 12:59:21 +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_kind.dart';
|
||||||
|
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/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';
|
||||||
@ -6,6 +7,7 @@ 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';
|
||||||
|
import 'package:comunic/models/new_post.dart';
|
||||||
import 'package:comunic/models/post.dart';
|
import 'package:comunic/models/post.dart';
|
||||||
|
|
||||||
/// Posts helper
|
/// Posts helper
|
||||||
@ -37,6 +39,11 @@ const _APIUserAccessMap = {
|
|||||||
"full": UserAccessLevels.FULL
|
"full": UserAccessLevels.FULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _APIPostsTargetKindsMap = {
|
||||||
|
PostTarget.USER_PAGE: "user",
|
||||||
|
PostTarget.GROUP_PAGE: "group"
|
||||||
|
};
|
||||||
|
|
||||||
class PostsHelper {
|
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
|
||||||
@ -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
|
/// Update a post content
|
||||||
Future<bool> updateContent(int id, String newContent) async {
|
Future<bool> updateContent(int id, String newContent) async {
|
||||||
return (await APIRequest(
|
return (await APIRequest(
|
||||||
|
@ -155,6 +155,7 @@ class UsersHelper {
|
|||||||
data["virtualDirectory"] == "" ? null : data["virtualDirectory"],
|
data["virtualDirectory"] == "" ? null : data["virtualDirectory"],
|
||||||
accountImageURL: data["accountImage"],
|
accountImageURL: data["accountImage"],
|
||||||
publicNote: data["publicNote"],
|
publicNote: data["publicNote"],
|
||||||
|
canPostTexts: data["can_post_texts"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
|
|||||||
|
|
||||||
class AdvancedUserInfo extends User {
|
class AdvancedUserInfo extends User {
|
||||||
final String publicNote;
|
final String publicNote;
|
||||||
|
final bool canPostTexts;
|
||||||
|
|
||||||
const AdvancedUserInfo({
|
const AdvancedUserInfo({
|
||||||
@required int id,
|
@required int id,
|
||||||
@ -17,7 +18,9 @@ class AdvancedUserInfo extends User {
|
|||||||
@required String virtualDirectory,
|
@required String virtualDirectory,
|
||||||
@required String accountImageURL,
|
@required String accountImageURL,
|
||||||
@required this.publicNote,
|
@required this.publicNote,
|
||||||
|
@required this.canPostTexts,
|
||||||
}) : assert(publicNote != null),
|
}) : assert(publicNote != null),
|
||||||
|
assert(canPostTexts != null),
|
||||||
super(
|
super(
|
||||||
id: id,
|
id: id,
|
||||||
firstName: firstName,
|
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/posts_helper.dart';
|
||||||
import 'package:comunic/helpers/users_helper.dart';
|
import 'package:comunic/helpers/users_helper.dart';
|
||||||
import 'package:comunic/models/advanced_user_info.dart';
|
import 'package:comunic/models/advanced_user_info.dart';
|
||||||
import 'package:comunic/ui/routes/other_friends_lists_route.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/routes/user_access_denied_route.dart';
|
||||||
import 'package:comunic/ui/widgets/network_image_widget.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/ui/widgets/posts_list_widget.dart';
|
||||||
import 'package:comunic/utils/conversations_utils.dart';
|
import 'package:comunic/utils/conversations_utils.dart';
|
||||||
import 'package:comunic/utils/intl_utils.dart';
|
import 'package:comunic/utils/intl_utils.dart';
|
||||||
@ -38,6 +40,8 @@ class _UserPageRouteState extends State<UserPageRoute> {
|
|||||||
final double _appBarHeight = 256.0;
|
final double _appBarHeight = 256.0;
|
||||||
_PageStatus _status = _PageStatus.LOADING;
|
_PageStatus _status = _PageStatus.LOADING;
|
||||||
AdvancedUserInfo _userInfo;
|
AdvancedUserInfo _userInfo;
|
||||||
|
GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
|
||||||
|
GlobalKey<RefreshIndicatorState>();
|
||||||
|
|
||||||
_setStatus(_PageStatus s) => setState(() => _status = s);
|
_setStatus(_PageStatus s) => setState(() => _status = s);
|
||||||
|
|
||||||
@ -80,8 +84,10 @@ class _UserPageRouteState extends State<UserPageRoute> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
|
key: _refreshIndicatorKey,
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: <Widget>[_buildHeader(), _buildBody()],
|
slivers: <Widget>[_buildHeader(), _buildBody()],
|
||||||
|
physics: AlwaysScrollableScrollPhysics(),
|
||||||
),
|
),
|
||||||
onRefresh: _getUserInfo,
|
onRefresh: _getUserInfo,
|
||||||
),
|
),
|
||||||
@ -175,6 +181,16 @@ class _UserPageRouteState extends State<UserPageRoute> {
|
|||||||
return SliverList(
|
return SliverList(
|
||||||
delegate: SliverChildListDelegate(
|
delegate: SliverChildListDelegate(
|
||||||
<Widget>[
|
<Widget>[
|
||||||
|
// Posts create form
|
||||||
|
_userInfo.canPostTexts
|
||||||
|
? PostCreateFormWidget(
|
||||||
|
postTarget: PostTarget.USER_PAGE,
|
||||||
|
targetID: _userInfo.id,
|
||||||
|
onCreated: _postCreated,
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
|
||||||
|
// Posts list
|
||||||
PostsListWidget(
|
PostsListWidget(
|
||||||
getPostsList: () => _postsHelper.getUserPosts(widget.userID),
|
getPostsList: () => _postsHelper.getUserPosts(widget.userID),
|
||||||
showPostsTarget: false,
|
showPostsTarget: false,
|
||||||
@ -199,4 +215,9 @@ class _UserPageRouteState extends State<UserPageRoute> {
|
|||||||
break;
|
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