diff --git a/lib/helpers/posts_helper.dart b/lib/helpers/posts_helper.dart index eb54a85..9b4bfd4 100644 --- a/lib/helpers/posts_helper.dart +++ b/lib/helpers/posts_helper.dart @@ -167,6 +167,10 @@ class PostsHelper { request.addString("answers", post.survey.answers.join("<>")); break; + case PostKind.YOUTUBE: + request.addString("youtube_id", post.youtubeId); + break; + default: throw Exception("Unsupported post type :" + post.kind.toString()); break; diff --git a/lib/models/new_post.dart b/lib/models/new_post.dart index 3657a77..0799b29 100644 --- a/lib/models/new_post.dart +++ b/lib/models/new_post.dart @@ -31,6 +31,7 @@ class NewPost { final PostKind kind; final DateTime timeEnd; final NewSurvey survey; + final String youtubeId; const NewPost({ @required this.target, @@ -43,6 +44,7 @@ class NewPost { @required this.pdf, @required this.timeEnd, @required this.survey, + @required this.youtubeId, }) : assert(target != null), assert(targetID != null), assert(visibility != null), @@ -52,5 +54,6 @@ class NewPost { assert(kind != PostKind.WEB_LINK || url != null), assert(kind != PostKind.PDF || pdf != null), assert(kind != PostKind.COUNTDOWN || timeEnd != null), - assert(kind != PostKind.SURVEY || survey != null); + assert(kind != PostKind.SURVEY || survey != null), + assert(kind != PostKind.YOUTUBE || youtubeId != null); } diff --git a/lib/ui/dialogs/input_url_dialog.dart b/lib/ui/dialogs/input_url_dialog.dart new file mode 100644 index 0000000..caed43a --- /dev/null +++ b/lib/ui/dialogs/input_url_dialog.dart @@ -0,0 +1,28 @@ +import 'package:comunic/ui/dialogs/single_input_dialog.dart'; +import 'package:comunic/utils/input_utils.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +/// Ask the user to enter an URL +/// +/// @author Pierre Hubert + +/// Ask the user to enter an URL +Future showInputURLDialog({ + @required BuildContext context, + @required String title, + String initialURL, +}) async { + return await showDialog( + context: context, + builder: (c) => SingleInputDialog( + title: title, + icon: Icons.link, + initialValue: initialURL, + label: "http://...", + checkInput: (s) => validateUrl(s), + errorMessage: tr("Invalid URL!"), + ), + ); +} diff --git a/lib/ui/dialogs/input_youtube_link_dialog.dart b/lib/ui/dialogs/input_youtube_link_dialog.dart new file mode 100644 index 0000000..63dcf0c --- /dev/null +++ b/lib/ui/dialogs/input_youtube_link_dialog.dart @@ -0,0 +1,29 @@ +import 'package:comunic/ui/dialogs/single_input_dialog.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +/// Add YouTube link dialog +/// +/// @author Pierre Hubert + +/// Ask the user to input a YouTube ID +Future showInputYouTubeIDDialog( + BuildContext context, String initialID) async { + final value = await showDialog( + context: context, + builder: (b) => SingleInputDialog( + title: tr("Input YouTube URL"), + icon: Icons.ondemand_video, + initialValue: initialID == null + ? null + : "https://www.youtube.com/watch/?v=" + initialID, + label: tr("https://www.youtube.com/watch/?v="), + checkInput: (s) => RegExp(r'watch\/\?v=[\w\-\_]+').hasMatch(s), + errorMessage: tr("Invalid YouTube link!"), + )); + + if (value == null) return null; + + return value.split("v=")[1].split("&")[0].split("#")[0]; +} diff --git a/lib/ui/dialogs/url_dialog.dart b/lib/ui/dialogs/single_input_dialog.dart similarity index 64% rename from lib/ui/dialogs/url_dialog.dart rename to lib/ui/dialogs/single_input_dialog.dart index 9059f0d..63fd5c5 100644 --- a/lib/ui/dialogs/url_dialog.dart +++ b/lib/ui/dialogs/single_input_dialog.dart @@ -1,52 +1,45 @@ -import 'package:comunic/utils/input_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; /// Ask the user to enter an URL -/// -/// @author Pierre Hubert - -/// Ask the user to enter an URL -Future showInputURLDialog({ - @required BuildContext context, - @required String title, - String initialURL, -}) async { - return await showDialog( - context: context, - builder: (c) => _InputURLDialog( - title: title, - initialURL: initialURL, - ), - ); -} - -class _InputURLDialog extends StatefulWidget { +class SingleInputDialog extends StatefulWidget { final String title; - final String initialURL; + final IconData icon; + final String initialValue; + final String label; + final bool Function(String) checkInput; + final String errorMessage; - const _InputURLDialog({ + const SingleInputDialog({ Key key, @required this.title, - @required this.initialURL, + @required this.icon, + @required this.initialValue, + @required this.label, + @required this.checkInput, + @required this.errorMessage, }) : assert(title != null), + assert(icon != null), + assert(label != null), + assert(checkInput != null), + assert(errorMessage != null), super(key: key); @override __InputURLDialogState createState() => __InputURLDialogState(); } -class __InputURLDialogState extends State<_InputURLDialog> { +class __InputURLDialogState extends State { TextEditingController _controller; bool get _isValid => - _controller.text.isNotEmpty && validateUrl(_controller.text); + _controller.text.isNotEmpty && widget.checkInput(_controller.text); @override void initState() { super.initState(); - _controller = new TextEditingController(text: widget.initialURL); + _controller = new TextEditingController(text: widget.initialValue); } @override @@ -57,11 +50,11 @@ class __InputURLDialogState extends State<_InputURLDialog> { controller: _controller, onChanged: (s) => setState(() {}), decoration: InputDecoration( - icon: Icon(Icons.link), + icon: Icon(widget.icon), alignLabelWithHint: true, - labelText: "http://...", + labelText: widget.label, errorText: _controller.text.isNotEmpty && !_isValid - ? tr("Invalid URL!") + ? widget.errorMessage : null, ), ), diff --git a/lib/ui/widgets/post_create_form_widget.dart b/lib/ui/widgets/post_create_form_widget.dart index fbaca35..5b91e57 100644 --- a/lib/ui/widgets/post_create_form_widget.dart +++ b/lib/ui/widgets/post_create_form_widget.dart @@ -5,8 +5,9 @@ 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/url_dialog.dart'; import 'package:comunic/utils/files_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/post_utils.dart'; @@ -54,6 +55,7 @@ class _PostCreateFormWidgetState extends State { List _postPDF; DateTime _timeEnd; NewSurvey _postSurvey; + String _youtubeID; bool get hasImage => _postImage != null; @@ -65,6 +67,8 @@ class _PostCreateFormWidgetState extends State { bool get hasSurvey => _postSurvey != null; + bool get hasYoutTubeID => _youtubeID != null; + bool get canSubmitForm => !_isCreating && _postTextController.text.length > 5 || postKind != PostKind.TEXT; @@ -80,6 +84,8 @@ class _PostCreateFormWidgetState extends State { return PostKind.COUNTDOWN; else if (hasSurvey) return PostKind.SURVEY; + else if (hasYoutTubeID) + return PostKind.YOUTUBE; else return PostKind.TEXT; } @@ -158,6 +164,13 @@ class _PostCreateFormWidgetState extends State { selected: postKind == PostKind.SURVEY, onTap: _pickSurvey, ), + + // Specify YouTube video ID + _PostOptionWidget( + icon: Icons.ondemand_video, + selected: postKind == PostKind.YOUTUBE, + onTap: _pickYouTubeVideo, + ), ], ), ), @@ -222,6 +235,7 @@ class _PostCreateFormWidgetState extends State { _postPDF = null; _timeEnd = null; _postSurvey = null; + _youtubeID = null; }); } @@ -322,6 +336,20 @@ class _PostCreateFormWidgetState extends State { }); } + /// Pick a new YouTube video + Future _pickYouTubeVideo() async { + + final youtubeID = await showInputYouTubeIDDialog(context, _youtubeID); + + if (youtubeID == null) return; + + _resetPostSelection(); + setState(() { + _youtubeID = youtubeID; + }); + + } + /// Submit new post Future _submitForm() async { if (!canSubmitForm) @@ -331,16 +359,18 @@ class _PostCreateFormWidgetState extends State { 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)); + 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!"));