1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-30 00:36:28 +00:00
comunicmobile/lib/ui/widgets/post_create_form_widget.dart

426 lines
12 KiB
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/helpers/posts_helper.dart';
import 'package:comunic/models/new_post.dart';
import 'package:comunic/ui/dialogs/input_url_dialog.dart';
import 'package:comunic/ui/dialogs/input_youtube_link_dialog.dart';
import 'package:comunic/ui/dialogs/new_survey_dialog.dart';
import 'package:comunic/ui/dialogs/post_visibility_picker_dialog.dart';
import 'package:comunic/ui/widgets/post_container_widget.dart';
import 'package:comunic/utils/files_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import '../../models/api_request.dart';
import '../../utils/log_utils.dart';
import '../../utils/ui_utils.dart';
/// Widget that allows to create posts
///
/// @author Pierre HUBERT
const _ActiveButtonsColor = Colors.blue;
const _InactiveButtonsColor = Colors.grey;
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;
BytesFile _postImage;
String _postURL;
List<int> _postPDF;
DateTime _timeEnd;
NewSurvey _postSurvey;
String _youtubeID;
bool get hasImage => _postImage != null;
bool get hasURL => _postURL != null;
bool get hasPDF => _postPDF != null;
bool get hasTimeEnd => _timeEnd != null;
bool get hasSurvey => _postSurvey != null;
bool get hasYouTubeID => _youtubeID != null;
bool get canSubmitForm =>
!_isCreating && _postTextController.text.length > 5 ||
postKind != PostKind.TEXT;
PostKind get postKind {
if (hasImage)
return PostKind.IMAGE;
else if (hasPDF)
return PostKind.PDF;
else if (hasURL)
return PostKind.WEB_LINK;
else if (hasTimeEnd)
return PostKind.COUNTDOWN;
else if (hasSurvey)
return PostKind.SURVEY;
else if (hasYouTubeID)
return PostKind.YOUTUBE;
else
return PostKind.TEXT;
}
@override
void initState() {
super.initState();
_resetForm();
}
@override
Widget build(BuildContext context) {
return PostContainer(
child: Card(
child: Column(
children: <Widget>[
// Post text content
Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0),
child: 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>[
// Text post button
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: <Widget>[
_PostOptionWidget(
icon: Icons.text_format,
selected: postKind == PostKind.TEXT,
onTap: _resetPostSelection),
// Include image button
_PostOptionWidget(
icon: Icons.image,
selected: postKind == PostKind.IMAGE,
onTap: _pickImageForPost,
),
// Web link
_PostOptionWidget(
icon: Icons.link,
selected: postKind == PostKind.WEB_LINK,
onTap: _pickURLForPost,
),
// Include PDF button
_PostOptionWidget(
icon: Icons.picture_as_pdf,
selected: postKind == PostKind.PDF,
onTap: _pickPDFForPost,
),
// Add countdown timer
_PostOptionWidget(
icon: Icons.timer,
selected: postKind == PostKind.COUNTDOWN,
onTap: _pickCountdownTime,
),
// Add survey
_PostOptionWidget(
icon: Icons.insert_chart,
selected: postKind == PostKind.SURVEY,
onTap: _pickSurvey,
),
// Specify YouTube video ID
_PostOptionWidget(
icon: Icons.ondemand_video,
selected: postKind == PostKind.YOUTUBE,
onTap: _pickYouTubeVideo,
),
],
),
),
),
Container(width: 20),
// Post visibility level
_PostOptionWidget(
icon: PostVisibilityLevelsMapIcons[_postVisibilityLevel],
selected: false,
customColor: Colors.black,
onTap: _changeVisibilityLevel,
),
// Submit post button
_isCreating
? Container()
: ElevatedButton(
child: Text(tr("Send").toUpperCase()),
onPressed: canSubmitForm ? _submitForm : null),
],
),
)
],
),
),
);
}
/// Reset the form
void _resetForm() {
setState(() {
_postVisibilityLevel = widget.postTarget == PostTarget.GROUP_PAGE
? PostVisibilityLevel.GROUP_MEMBERS
: PostVisibilityLevel.FRIENDS;
_postTextController.text = "";
_resetPostSelection();
});
}
/// Change post visibility level
Future<void> _changeVisibilityLevel() async {
final newLevel = await showPostVisibilityPickerDialog(
context: context,
initialLevel: _postVisibilityLevel,
isGroup: widget.postTarget == PostTarget.GROUP_PAGE,
);
setState(() => _postVisibilityLevel = newLevel);
}
/// Remove all data attached to the post (image, etc...)
void _resetPostSelection() {
setState(() {
_postImage = null;
_postURL = null;
_postPDF = null;
_timeEnd = null;
_postSurvey = null;
_youtubeID = null;
});
}
/// Pick an image for the new post
Future<void> _pickImageForPost() async {
try {
final image = await pickImage(context);
if (image == null) return;
_resetPostSelection();
setState(() {
this._postImage = image;
});
} catch (e, s) {
logError(e, s);
snack(context, tr("Failed to pick an image for the post!"));
}
}
/// Choose a new URL for the post
Future<void> _pickURLForPost() async {
final url = await showInputURLDialog(
context: context,
title: tr("Specify URL"),
initialURL: _postURL,
);
if (url == null) return;
_resetPostSelection();
setState(() {
_postURL = url;
});
}
/// Pick a PDF for the new post
Future<void> _pickPDFForPost() async {
try {
final file = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ["pdf"],
withData: true,
);
if (file == null || file.files.isEmpty) return;
_resetPostSelection();
setState(() {
this._postPDF = file.files.first.bytes;
});
} catch (e, stack) {
print("Pick PDF error: $e\n$stack");
showSimpleSnack(context, tr("Could not pick a PDF!"));
}
}
/// Pick countdown time
Future<void> _pickCountdownTime() async {
final yesterday = DateTime.now().subtract(Duration(days: 1));
final initialDate = _timeEnd == null ? DateTime.now() : _timeEnd;
// Pick date
final newDate = await showDatePicker(
context: context,
initialDate: initialDate,
firstDate: yesterday.isBefore(initialDate) ? yesterday : initialDate,
lastDate: DateTime.now().add(Duration(days: 5000)));
if (newDate == null) return;
// Pick time
final newTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(initialDate),
);
if (newTime == null) return;
// Apply new selection
_resetPostSelection();
setState(() {
_timeEnd = newDate.add(Duration(
hours: newTime.hour - newDate.hour,
minutes: newTime.minute - newDate.minute,
seconds: -newDate.second));
});
}
/// Pick a new survey for this post
Future<void> _pickSurvey() async {
final newSurvey =
await showNewSurveyDialog(context: context, initialSurvey: _postSurvey);
if (newSurvey == null) return;
_resetPostSelection();
setState(() {
_postSurvey = newSurvey;
});
}
/// Pick a new YouTube video
Future<void> _pickYouTubeVideo() async {
final youtubeID = await showInputYouTubeIDDialog(context, _youtubeID);
if (youtubeID == null) return;
_resetPostSelection();
setState(() {
_youtubeID = youtubeID;
});
}
/// 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,
url: _postURL,
pdf: _postPDF,
timeEnd: _timeEnd,
survey: _postSurvey,
youtubeId: _youtubeID,
));
setState(() => _isCreating = false);
showSimpleSnack(context, tr("The post has been successfully created!"));
this._resetForm();
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,
);
}
}