From ac88f84321c99d59e4e02ca6ee869ae6608497c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Elias=20M=C3=BCller?= <elias@elias-mueller.com>
Date: Sat, 6 May 2023 20:48:22 +0200
Subject: [PATCH] Refactoring chatBubble.dart to stateful widget

---
 lib/screen/pages/files/fileElement.dart |   4 +-
 lib/screen/pages/talk/chatBubble.dart   | 159 +++++++++++++++---------
 lib/screen/pages/talk/chatView.dart     |   4 +-
 3 files changed, 106 insertions(+), 61 deletions(-)

diff --git a/lib/screen/pages/files/fileElement.dart b/lib/screen/pages/files/fileElement.dart
index 8cd50ad..681b411 100644
--- a/lib/screen/pages/files/fileElement.dart
+++ b/lib/screen/pages/files/fileElement.dart
@@ -18,7 +18,7 @@ class FileElement extends StatefulWidget {
   final List<String> path;
   const FileElement(this.file, this.path, {Key? key}) : super(key: key);
 
-  static void download(String remotePath, String name, Function(double) onProgress, Function(OpenResult) onDone) async {
+  static Future<DownloaderCore> download(String remotePath, String name, Function(double) onProgress, Function(OpenResult) onDone) async {
     Directory paths = await getApplicationDocumentsDirectory();
 
     String local = paths.path + Platform.pathSeparator + name;
@@ -40,7 +40,7 @@ class FileElement extends StatefulWidget {
       },
     );
 
-    await Flowder.download(
+    return await Flowder.download(
       "${await WebdavApi.webdavConnectString}$remotePath",
       options,
     );
diff --git a/lib/screen/pages/talk/chatBubble.dart b/lib/screen/pages/talk/chatBubble.dart
index e170b11..35a7736 100644
--- a/lib/screen/pages/talk/chatBubble.dart
+++ b/lib/screen/pages/talk/chatBubble.dart
@@ -1,16 +1,35 @@
 import 'package:better_open_file/better_open_file.dart';
 import 'package:bubble/bubble.dart';
+import 'package:flowder/flowder.dart';
+import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:jiffy/jiffy.dart';
-import 'package:marianum_mobile/api/marianumcloud/talk/chat/getChatResponse.dart';
-import 'package:marianum_mobile/screen/pages/files/fileElement.dart';
-import 'package:marianum_mobile/screen/pages/talk/chatMessage.dart';
 
+import '../../../api/marianumcloud/talk/chat/getChatResponse.dart';
 import '../../../api/marianumcloud/talk/room/getRoomResponse.dart';
 import '../../settings/debug/jsonViewer.dart';
+import '../files/fileElement.dart';
+import 'chatMessage.dart';
 
-class ChatBubble {
+class ChatBubble extends StatefulWidget {
+  BuildContext context;
+  bool isSender;
+  GetChatResponseObject bubbleData;
+  GetRoomResponseObject chatData;
+
+  ChatBubble({
+    required this.context,
+    required this.isSender,
+    required this.bubbleData,
+    required this.chatData,
+    Key? key}) : super(key: key);
+
+  @override
+  State<ChatBubble> createState() => _ChatBubbleState();
+}
+
+class _ChatBubbleState extends State<ChatBubble> {
   static const styleSystem = BubbleStyle(
     color: Color(0xffd4eaf4),
     borderWidth: 1,
@@ -41,25 +60,22 @@ class ChatBubble {
     );
   }
 
-  BuildContext context;
-  bool isSender;
-  GetChatResponseObject bubbleData;
-  GetRoomResponseObject chatData;
-  double downloadProgress = 0;
   late ChatMessage message;
+  double downloadProgress = 0;
+  Future<DownloaderCore>? downloadCore;
 
-  ChatBubble({
-    required this.context,
-    required this.isSender, 
-    required this.bubbleData,
-    required this.chatData,
-  }) {
-    message = ChatMessage(originalMessage: bubbleData.message, originalData: bubbleData.messageParameters);
+  Size _textSize(String text, TextStyle style) {
+    final TextPainter textPainter = TextPainter(
+        text: TextSpan(text: text, style: style),
+        maxLines: 1,
+        textDirection: TextDirection.ltr)
+      ..layout(minWidth: 0, maxWidth: double.infinity);
+    return textPainter.size;
   }
 
   BubbleStyle getStyle() {
-    if(bubbleData.messageType == GetRoomResponseObjectMessageType.comment) {
-      if(isSender) {
+    if(widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment) {
+      if(widget.isSender) {
         return getStyleSelf(message.containsFile);
       } else {
         return getStyleOther(message.containsFile);
@@ -69,9 +85,12 @@ class ChatBubble {
     }
   }
 
-  Widget generateBubble() {
-    bool showActorDisplayName = bubbleData.messageType == GetRoomResponseObjectMessageType.comment && chatData.type != GetRoomResponseObjectConversationType.oneToOne;
-    bool showBubbleTime = bubbleData.messageType != GetRoomResponseObjectMessageType.system;
+
+  @override
+  Widget build(BuildContext context) {
+    message = ChatMessage(originalMessage: widget.bubbleData.message, originalData: widget.bubbleData.messageParameters);
+    bool showActorDisplayName = widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment && widget.chatData.type != GetRoomResponseObjectConversationType.oneToOne;
+    bool showBubbleTime = widget.bubbleData.messageType != GetRoomResponseObjectMessageType.system;
     var actorTextStyle = TextStyle(color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold);
 
     return GestureDetector(
@@ -81,7 +100,7 @@ class ChatBubble {
         child: Container(
           constraints: BoxConstraints(
             maxWidth: MediaQuery.of(context).size.width * 0.9,
-            minWidth: showActorDisplayName ? _textSize(bubbleData.actorDisplayName, actorTextStyle).width : 30,
+            minWidth: showActorDisplayName ? _textSize(widget.bubbleData.actorDisplayName, actorTextStyle).width : 30,
           ),
           child: Stack(
             children: [
@@ -101,7 +120,7 @@ class ChatBubble {
                   top: 0,
                   left: 0,
                   child: Text(
-                    bubbleData.actorDisplayName,
+                    widget.bubbleData.actorDisplayName,
                     textAlign: TextAlign.start,
                     style: actorTextStyle,
                   ),
@@ -112,19 +131,21 @@ class ChatBubble {
                 child: Positioned(
                   bottom: 0,
                   right: 0,
-                  child: Row(
-                    children: [
-                      Visibility(
-                        visible: downloadProgress > 0,
-                        child: LinearProgressIndicator(value: downloadProgress),
-                      ),
-                      Text(
-                        Jiffy.unixFromSecondsSinceEpoch(bubbleData.timestamp).format("HH:mm"),
-                        textAlign: TextAlign.end,
-                        style: const TextStyle(color: Colors.grey, fontSize: 12),
-                      ),
-                    ],
-                  )
+                  child: Text(
+                    Jiffy.unixFromSecondsSinceEpoch(widget.bubbleData.timestamp).format("HH:mm"),
+                    textAlign: TextAlign.end,
+                    style: const TextStyle(color: Colors.grey, fontSize: 12),
+                  ),
+                ),
+              ),
+              Visibility(
+                visible: downloadProgress > 0,
+                child: Positioned(
+                  top: 0,
+                  left: 0,
+                  right: 0,
+                  bottom: 0,
+                  child: Center(child: CircularProgressIndicator(value: downloadProgress/100)),
                 ),
               ),
             ],
@@ -136,28 +157,28 @@ class ChatBubble {
           return SimpleDialog(
             children: [
               Visibility(
-                visible: !message.containsFile && bubbleData.messageType == GetRoomResponseObjectMessageType.comment,
+                visible: !message.containsFile && widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment,
                 child: ListTile(
                   leading: const Icon(Icons.copy),
                   title: const Text("Nachricht kopieren"),
                   onTap: () => {
-                    Clipboard.setData(ClipboardData(text: bubbleData.message)),
+                    Clipboard.setData(ClipboardData(text: widget.bubbleData.message)),
                     Navigator.of(context).pop(),
                   },
                 ),
               ),
               Visibility(
-                visible: !isSender && chatData.type != GetRoomResponseObjectConversationType.oneToOne,
+                visible: !widget.isSender && widget.chatData.type != GetRoomResponseObjectConversationType.oneToOne,
                 child: ListTile(
                   leading: const Icon(Icons.person),
-                  title: Text("Private Nachricht an '${bubbleData.actorDisplayName}'"),
+                  title: Text("Private Nachricht an '${widget.bubbleData.actorDisplayName}'"),
                   onTap: () => {},
                 ),
               ),
               ListTile(
                 leading: const Icon(Icons.bug_report_outlined),
                 title: const Text("Debugdaten anzeigen"),
-                onTap: () => JsonViewer.asDialog(context, bubbleData.toJson()),
+                onTap: () => JsonViewer.asDialog(context, widget.bubbleData.toJson()),
               )
             ],
           );
@@ -166,29 +187,53 @@ class ChatBubble {
       onTap: () {
         if(message.file == null) return;
 
-        FileElement.download(message.file!.path!, message.file!.name, (progress) => {
-          downloadProgress = progress,
-        }, (result) => {
-          downloadProgress = 0,
+        if(downloadProgress > 0) {
+          showDialog(context: context, builder: (context) {
+            return AlertDialog(
+              title: const Text("Download abbrechen?"),
+              content: const Text("Möchtest du den Download abbrechen?"),
+              actions: [
+                TextButton(onPressed: () {
+                  Navigator.of(context).pop();
+                }, child: const Text("Nein")),
+                TextButton(onPressed: () {
+                  downloadCore?.then((value) {
+                    if(!value.isCancelled) value.cancel();
+                    setState(() {
+                      downloadProgress = 0;
+                      downloadCore = null;
+                    });
+                    Navigator.of(context).pop();
+                  });
+                }, child: const Text("Ja, Abbrechen"))
+              ],
+            );
+          });
+
+          return;
+        }
+
+        downloadProgress = 1;
+        downloadCore = FileElement.download(message.file!.path!, message.file!.name, (progress) {
+          if(progress > 1) {
+            setState(() {
+              downloadProgress = progress;
+            });
+          }
+        }, (result) {
+          setState(() {
+            downloadProgress = 0;
+          });
+
           if(result.type != ResultType.done) {
             showDialog(context: context, builder: (context) {
               return AlertDialog(
                 content: Text(result.message),
               );
-            })
+            });
           }
         });
-
       },
     );
   }
-
-  Size _textSize(String text, TextStyle style) {
-    final TextPainter textPainter = TextPainter(
-        text: TextSpan(text: text, style: style),
-        maxLines: 1,
-        textDirection: TextDirection.ltr)
-      ..layout(minWidth: 0, maxWidth: double.infinity);
-    return textPainter.size;
-  }
-}
\ No newline at end of file
+}
diff --git a/lib/screen/pages/talk/chatView.dart b/lib/screen/pages/talk/chatView.dart
index b4203fc..89066b7 100644
--- a/lib/screen/pages/talk/chatView.dart
+++ b/lib/screen/pages/talk/chatView.dart
@@ -64,9 +64,9 @@ class _ChatViewState extends State<ChatView> {
                     null
                   ),
                   chatData: widget.room
-              ).generateBubble());
+              ));
             }
-            messages.add(ChatBubble(context: context, isSender: element.actorId == widget.selfId, bubbleData: element, chatData: widget.room).generateBubble());
+            messages.add(ChatBubble(context: context, isSender: element.actorId == widget.selfId, bubbleData: element, chatData: widget.room));
           });
         }