From 010607112d78bad44836a3ba3e85a6bc6f442c9e Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Sun, 26 Dec 2021 11:30:44 +0100 Subject: [PATCH] chore: Add replies and hold to show --- assets/l10n/intl_en.arb | 3 +- lib/pages/story/story_page.dart | 59 +++++++++- lib/pages/story/story_view.dart | 198 ++++++++++++++++++++------------ 3 files changed, 182 insertions(+), 78 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 4b55266b..a5e6d44f 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2668,5 +2668,6 @@ "whoCanSeeMyStories": "Who can see my stories?", "unsubscribeStories": "Unsubscribe stories", "thisUserHasNotPostedAnythingYet": "This user has not posted anything in their story yet", - "yourStory": "Your story" + "yourStory": "Your story", + "replyHasBeenSent": "Reply has been sent" } diff --git a/lib/pages/story/story_page.dart b/lib/pages/story/story_page.dart index b86701db..8918c1c7 100644 --- a/lib/pages/story/story_page.dart +++ b/lib/pages/story/story_page.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; import 'package:path_provider/path_provider.dart'; @@ -32,12 +33,60 @@ class StoryPageController extends State { Timer? _progressTimer; bool loadingMode = false; + final TextEditingController replyController = TextEditingController(); + final FocusNode replyFocus = FocusNode(); + final List events = []; Timeline? timeline; Event? get currentEvent => index < events.length ? events[index] : null; + bool replyLoading = false; + + void replyEmojiAction() async { + if (replyLoading) return; + hold(); + await showModalBottomSheet( + context: context, + builder: (context) => EmojiPicker( + onEmojiSelected: (c, e) { + Navigator.of(context).pop(); + replyAction(e.emoji); + }, + ), + ); + unhold(); + } + + void replyAction([String? message]) async { + message ??= replyController.text; + setState(() { + replyLoading = true; + }); + try { + final client = Matrix.of(context).client; + final roomId = await client.startDirectChat(currentEvent!.senderId); + await client.getRoomById(roomId)!.sendTextEvent( + message, + inReplyTo: currentEvent!, + ); + replyController.clear(); + replyFocus.unfocus(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(L10n.of(context)!.replyHasBeenSent), + ), + ); + } catch (e, s) { + Logs().w('Unable to reply to story', e, s); + } finally { + setState(() { + replyLoading = false; + }); + } + } + List get currentSeenByUsers { final timeline = this.timeline; final currentEvent = this.currentEvent; @@ -88,8 +137,6 @@ class StoryPageController extends State { ); } - final TextEditingController replyController = TextEditingController(); - static const Duration _step = Duration(milliseconds: 50); static const Duration maxProgress = Duration(seconds: 5); @@ -145,13 +192,19 @@ class StoryPageController extends State { DateTime _holdedAt = DateTime.fromMicrosecondsSinceEpoch(0); - void hold(_) { + bool isHold = false; + + void hold([_]) { _holdedAt = DateTime.now(); if (loadingMode) return; _progressTimer?.cancel(); + setState(() { + isHold = true; + }); } void unhold([_]) { + isHold = false; if (DateTime.now().millisecondsSinceEpoch - _holdedAt.millisecondsSinceEpoch < 200) { diff --git a/lib/pages/story/story_view.dart b/lib/pages/story/story_view.dart index fdabd601..a3906c71 100644 --- a/lib/pages/story/story_view.dart +++ b/lib/pages/story/story_view.dart @@ -26,39 +26,47 @@ class StoryView extends StatelessWidget { backgroundColor: Colors.blueGrey, appBar: AppBar( titleSpacing: 0, - title: ListTile( - contentPadding: EdgeInsets.zero, - title: Text( - controller.title, - style: const TextStyle( - color: Colors.white, - shadows: [ - Shadow( - color: Colors.black, - offset: Offset(0, 0), - blurRadius: 5, - ), - ], - ), - ), - subtitle: currentEvent != null - ? Text( - currentEvent.originServerTs.localizedTime(context), - style: const TextStyle( - color: Colors.white70, - shadows: [ - Shadow( - color: Colors.black, - offset: Offset(0, 0), - blurRadius: 5, - ), - ], + leading: IconButton( + icon: const Icon(Icons.close), + onPressed: Navigator.of(context).pop, + ), + title: AnimatedOpacity( + duration: const Duration(seconds: 1), + opacity: controller.isHold ? 0 : 1, + child: ListTile( + contentPadding: EdgeInsets.zero, + title: Text( + controller.title, + style: const TextStyle( + color: Colors.white, + shadows: [ + Shadow( + color: Colors.black, + offset: Offset(0, 0), + blurRadius: 5, ), - ) - : null, - leading: Avatar( - mxContent: controller.avatar, - name: controller.title, + ], + ), + ), + subtitle: currentEvent != null + ? Text( + currentEvent.originServerTs.localizedTime(context), + style: const TextStyle( + color: Colors.white70, + shadows: [ + Shadow( + color: Colors.black, + offset: Offset(0, 0), + blurRadius: 5, + ), + ], + ), + ) + : null, + leading: Avatar( + mxContent: controller.avatar, + name: controller.title, + ), ), ), systemOverlayStyle: SystemUiOverlayStyle.light, @@ -199,56 +207,98 @@ class StoryView extends StatelessWidget { top: 4, left: 4, right: 4, - child: SafeArea( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - for (var i = 0; i < events.length; i++) - Expanded( - child: i == controller.index - ? LinearProgressIndicator( - color: Colors.white, - minHeight: 2, - backgroundColor: Colors.grey.shade600, - value: controller.loadingMode - ? null - : controller.progress.inMilliseconds / - StoryPageController - .maxProgress.inMilliseconds, - ) - : Container( - margin: const EdgeInsets.all(4), - height: 2, - color: i < controller.index - ? Colors.white - : Colors.grey.shade600, - ), - ), - ], + child: AnimatedOpacity( + duration: const Duration(seconds: 1), + opacity: controller.isHold ? 0 : 1, + child: SafeArea( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + for (var i = 0; i < events.length; i++) + Expanded( + child: i == controller.index + ? LinearProgressIndicator( + color: Colors.white, + minHeight: 2, + backgroundColor: Colors.grey.shade600, + value: controller.loadingMode + ? null + : controller.progress.inMilliseconds / + StoryPageController + .maxProgress.inMilliseconds, + ) + : Container( + margin: const EdgeInsets.all(4), + height: 2, + color: i < controller.index + ? Colors.white + : Colors.grey.shade600, + ), + ), + ], + ), ), ), ), + if (!controller.isOwnStory && currentEvent != null) + Positioned( + bottom: 16, + left: 16, + right: 16, + child: AnimatedOpacity( + duration: const Duration(seconds: 1), + opacity: controller.isHold ? 0 : 1, + child: SafeArea( + child: TextField( + onTap: controller.hold, + onEditingComplete: controller.unhold, + focusNode: controller.replyFocus, + controller: controller.replyController, + minLines: 1, + maxLines: 7, + onSubmitted: controller.replyAction, + textInputAction: TextInputAction.newline, + readOnly: controller.replyLoading, + decoration: InputDecoration( + hintText: L10n.of(context)!.reply, + prefixIcon: IconButton( + onPressed: controller.replyEmojiAction, + icon: const Icon(Icons.emoji_emotions_outlined), + ), + suffixIcon: controller.replyLoading + ? const CircularProgressIndicator.adaptive( + strokeWidth: 2) + : IconButton( + onPressed: controller.replyAction, + icon: const Icon(Icons.send_outlined), + ), + ), + ), + ), + ), + ), if (controller.isOwnStory && controller.currentSeenByUsers.isNotEmpty) Positioned( - bottom: 16, - left: 16, - right: 16, - child: SafeArea( - child: Center( - child: OutlinedButton.icon( - onPressed: controller.displaySeenByUsers, - icon: const Icon( - Icons.visibility_outlined, - color: Colors.white70, - ), - label: Text( - controller.seenByUsersTitle, - style: const TextStyle(color: Colors.white70), - ), + bottom: 16, + left: 16, + right: 16, + child: SafeArea( + child: Center( + child: OutlinedButton.icon( + onPressed: controller.displaySeenByUsers, + icon: const Icon( + Icons.visibility_outlined, + color: Colors.white70, + ), + label: Text( + controller.seenByUsersTitle, + style: const TextStyle(color: Colors.white70), ), ), - )), + ), + ), + ), ], ), );