From cb079c2fd34caf1ea6a170c10122e94a02bf45c2 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Thu, 8 Feb 2024 15:43:10 -0800 Subject: [PATCH] Fix Up File Sharing Overlay --- lib/views/filesharingview.dart | 3 - lib/widgets/filebubble.dart | 331 ++++++++++------------ lib/widgets/messagebubbledecorations.dart | 55 ++-- lib/widgets/messagerow.dart | 2 +- 4 files changed, 178 insertions(+), 213 deletions(-) diff --git a/lib/views/filesharingview.dart b/lib/views/filesharingview.dart index d6399ccf..f368d3bc 100644 --- a/lib/views/filesharingview.dart +++ b/lib/views/filesharingview.dart @@ -1,9 +1,6 @@ import 'dart:convert'; -import 'package:cwtch/config.dart'; -import 'package:cwtch/cwtch/cwtch.dart'; import 'package:cwtch/main.dart'; -import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/profile.dart'; import 'package:cwtch/settings.dart'; diff --git a/lib/widgets/filebubble.dart b/lib/widgets/filebubble.dart index e0369688..fb7a102c 100644 --- a/lib/widgets/filebubble.dart +++ b/lib/widgets/filebubble.dart @@ -146,133 +146,129 @@ class FileBubbleState extends State { ); } - return LayoutBuilder(builder: (bcontext, constraints) { - var wdgSender = Visibility( - visible: widget.interactive, - child: Container( - height: 14 * Provider.of(context).fontScaling, - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(), - child: compileSenderWidget(context, constraints, fromMe, senderDisplayStr))); - var isPreview = false; - var wdgMessage = !showFileSharing - ? Text(AppLocalizations.of(context)!.messageEnableFileSharing, style: Provider.of(context).scaleFonts(defaultTextStyle)) - : fromMe - ? senderFileChrome(AppLocalizations.of(context)!.messageFileSent, widget.nameSuggestion, widget.rootHash, widget.fileSize) - : (fileChrome(AppLocalizations.of(context)!.messageFileOffered + ":", widget.nameSuggestion, widget.rootHash, widget.fileSize, - Provider.of(context).downloadSpeed(widget.fileKey()))); - Widget wdgDecorations; + var wdgSender = Visibility( + visible: widget.interactive, + child: Container( + height: 14 * Provider.of(context).fontScaling, clipBehavior: Clip.hardEdge, decoration: BoxDecoration(), child: compileSenderWidget(context, null, fromMe, senderDisplayStr))); + var isPreview = false; + var wdgMessage = !showFileSharing + ? Text(AppLocalizations.of(context)!.messageEnableFileSharing, style: Provider.of(context).scaleFonts(defaultTextStyle)) + : fromMe + ? senderFileChrome(AppLocalizations.of(context)!.messageFileSent, widget.nameSuggestion, widget.rootHash) + : (fileChrome(AppLocalizations.of(context)!.messageFileOffered + ":", widget.nameSuggestion, widget.rootHash, widget.fileSize, + Provider.of(context).downloadSpeed(widget.fileKey()))); + Widget wdgDecorations; - if (!showFileSharing) { - wdgDecorations = Text('\u202F'); - } else if (downloadComplete && path != null) { - // in this case, whatever marked download.complete would have also set the path - if (myFile != null && Provider.of(context).shouldPreview(path)) { - isPreview = true; - wdgDecorations = Center( - widthFactor: 1.0, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - child: Padding(padding: EdgeInsets.all(1.0), child: getPreview(context)), - onTap: () { - pop(bcontext, myFile!, widget.nameSuggestion); - }, - ))); - } else { - wdgDecorations = Visibility( - visible: widget.interactive, child: Text(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle))); - } - } else if (downloadActive) { - if (!downloadGotManifest) { - wdgDecorations = Visibility( - visible: widget.interactive, child: Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle))); - } else { - wdgDecorations = Visibility( - visible: widget.interactive, - child: LinearProgressIndicator( - value: Provider.of(context).downloadProgress(widget.fileKey()), - color: Provider.of(context).theme.defaultButtonActiveColor, - )); - } - } else if (flagStarted) { - // in this case, the download was done in a previous application launch, - // so we probably have to request an info lookup - if (!downloadInterrupted) { - wdgDecorations = Text(AppLocalizations.of(context)!.fileCheckingStatus + '...' + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); - // We should have already requested this... - } else { - var path = Provider.of(context).downloadFinalPath(widget.fileKey()) ?? ""; - wdgDecorations = Visibility( - visible: widget.interactive, - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)), - ElevatedButton(onPressed: _btnResume, child: Text(AppLocalizations.of(context)!.verfiyResumeButton, style: Provider.of(context).scaleFonts(defaultTextButtonStyle))) - ])); - } - } else if (!senderIsContact) { - wdgDecorations = Text(AppLocalizations.of(context)!.msgAddToAccept, style: Provider.of(context).scaleFonts(defaultTextStyle)); - } else if (!widget.isAuto || Provider.of(context).attributes["file-missing"] == "false") { - //Note: we need this second case to account for scenarios where a user deletes the downloaded file, we won't automatically - // fetch it again, so we need to offer the user the ability to restart.. + if (!showFileSharing) { + wdgDecorations = Text('\u202F'); + } else if (fromMe) { + wdgDecorations = Text('\u202F'); + } else if (downloadComplete && path != null) { + // in this case, whatever marked download.complete would have also set the path + if (myFile != null && Provider.of(context).shouldPreview(path)) { + isPreview = true; + wdgDecorations = Center( + widthFactor: 1.0, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + child: Padding(padding: EdgeInsets.all(1.0), child: getPreview(context)), + onTap: () { + pop(context, myFile!, widget.nameSuggestion); + }, + ))); + } else { + wdgDecorations = Visibility( + visible: widget.interactive, child: SelectableText(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle))); + } + } else if (downloadActive) { + if (!downloadGotManifest) { + wdgDecorations = Visibility( + visible: widget.interactive, child: SelectableText(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle))); + } else { wdgDecorations = Visibility( visible: widget.interactive, - child: Center( - widthFactor: 1, - child: Wrap(children: [ - Padding( - padding: EdgeInsets.all(5), - child: ElevatedButton( - child: Text(AppLocalizations.of(context)!.downloadFileButton + '\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnAccept)), - ]))); - } else { - wdgDecorations = Container(); + child: LinearProgressIndicator( + value: Provider.of(context).downloadProgress(widget.fileKey()), + color: Provider.of(context).theme.defaultButtonActiveColor, + )); } + } else if (flagStarted) { + // in this case, the download was done in a previous application launch, + // so we probably have to request an info lookup + if (!downloadInterrupted) { + wdgDecorations = Text(AppLocalizations.of(context)!.fileCheckingStatus + '...' + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)); + // We should have already requested this... + } else { + var path = Provider.of(context).downloadFinalPath(widget.fileKey()) ?? ""; + wdgDecorations = Visibility( + visible: widget.interactive, + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F', style: Provider.of(context).scaleFonts(defaultTextStyle)), + ElevatedButton(onPressed: _btnResume, child: Text(AppLocalizations.of(context)!.verfiyResumeButton, style: Provider.of(context).scaleFonts(defaultTextButtonStyle))) + ])); + } + } else if (!senderIsContact) { + wdgDecorations = Text(AppLocalizations.of(context)!.msgAddToAccept, style: Provider.of(context).scaleFonts(defaultTextStyle)); + } else if (!widget.isAuto || Provider.of(context).attributes["file-missing"] == "false") { + //Note: we need this second case to account for scenarios where a user deletes the downloaded file, we won't automatically + // fetch it again, so we need to offer the user the ability to restart.. + wdgDecorations = Visibility( + visible: widget.interactive, + child: Center( + widthFactor: 1, + child: Wrap(children: [ + Padding( + padding: EdgeInsets.all(5), + child: ElevatedButton( + child: Text(AppLocalizations.of(context)!.downloadFileButton + '\u202F', style: Provider.of(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnAccept)), + ]))); + } else { + wdgDecorations = Container(); + } - return Container( - constraints: constraints, - decoration: BoxDecoration( - color: fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor, - border: Border.all(color: fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor, width: 1), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(borderRadius), - topRight: Radius.circular(borderRadius), - bottomLeft: fromMe ? Radius.circular(borderRadius) : Radius.zero, - bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadius), - ), + return Container( + decoration: BoxDecoration( + color: fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor, + border: Border.all(color: fromMe ? Provider.of(context).theme.messageFromMeBackgroundColor : Provider.of(context).theme.messageFromOtherBackgroundColor, width: 1), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(borderRadius), + topRight: Radius.circular(borderRadius), + bottomLeft: fromMe ? Radius.circular(borderRadius) : Radius.zero, + bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadius), ), - child: Theme( - data: Theme.of(context).copyWith( - textSelectionTheme: TextSelectionThemeData( - cursorColor: Provider.of(context).theme.messageSelectionColor, - selectionColor: Provider.of(context).theme.messageSelectionColor, - selectionHandleColor: Provider.of(context).theme.messageSelectionColor), + ), + child: Theme( + data: Theme.of(context).copyWith( + textSelectionTheme: TextSelectionThemeData( + cursorColor: Provider.of(context).theme.messageSelectionColor, + selectionColor: Provider.of(context).theme.messageSelectionColor, + selectionHandleColor: Provider.of(context).theme.messageSelectionColor), - // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ - textButtonTheme: TextButtonThemeData( - style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.menuBackgroundColor)), - ), + // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ + textButtonTheme: TextButtonThemeData( + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.menuBackgroundColor)), ), - child: Padding( - padding: EdgeInsets.all(9.0), - child: Column( - crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, - mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - wdgSender, - isPreview - ? Container( - width: 0, - padding: EdgeInsets.zero, - margin: EdgeInsets.zero, - ) - : wdgMessage, - wdgDecorations, - messageStatusWidget - ]), - ))); - }); + ), + child: Padding( + padding: EdgeInsets.all(9.0), + child: Column( + crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, + mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + wdgSender, + isPreview + ? Container( + width: 0, + padding: EdgeInsets.zero, + margin: EdgeInsets.zero, + ) + : wdgMessage, + wdgDecorations, + messageStatusWidget + ]), + ))); } void _btnAccept() async { @@ -322,41 +318,27 @@ class FileBubbleState extends State { } // Construct an file chrome for the sender - Widget senderFileChrome(String chrome, String fileName, String rootHash, int fileSize) { + Widget senderFileChrome(String chrome, String fileName, String rootHash) { var settings = Provider.of(context); return ListTile( visualDensity: VisualDensity.compact, - title: Wrap(direction: Axis.horizontal, alignment: WrapAlignment.start, children: [ - SelectableText( - chrome + '\u202F', - style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of(context).theme.messageFromMeTextColor)), - textAlign: TextAlign.left, - maxLines: 2, - textWidthBasis: TextWidthBasis.longestLine, - ), - SelectableText( - fileName + '\u202F', - style: - settings.scaleFonts(defaultMessageTextStyle.copyWith(overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold, color: Provider.of(context).theme.messageFromMeTextColor)), - textAlign: TextAlign.left, - textWidthBasis: TextWidthBasis.parent, - maxLines: 2, - ), - SelectableText( - prettyBytes(fileSize) + '\u202F' + '\n', - style: settings.scaleFonts(defaultSmallTextStyle.copyWith(color: Provider.of(context).theme.messageFromMeTextColor)), - textAlign: TextAlign.left, - maxLines: 2, - ) - ]), + contentPadding: EdgeInsets.all(1.0), + title: SelectableText( + fileName + '\u202F', + style: + settings.scaleFonts(defaultMessageTextStyle.copyWith(overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold, color: Provider.of(context).theme.messageFromMeTextColor)), + textAlign: TextAlign.left, + textWidthBasis: TextWidthBasis.longestLine, + maxLines: 2, + ), subtitle: SelectableText( - 'sha512: ' + rootHash + '\u202F', + prettyBytes(widget.fileSize) + '\u202F' + '\n' + 'sha512: ' + rootHash + '\u202F', style: settings.scaleFonts(defaultSmallTextStyle.copyWith(fontFamily: "RobotoMono", color: Provider.of(context).theme.messageFromMeTextColor)), textAlign: TextAlign.left, maxLines: 4, - textWidthBasis: TextWidthBasis.parent, + textWidthBasis: TextWidthBasis.longestLine, ), - leading: Icon(CwtchIcons.attached_file_3, size: 32, color: Provider.of(context).theme.messageFromMeTextColor)); + leading: FittedBox(child: Icon(CwtchIcons.attached_file_3, size: 32, color: Provider.of(context).theme.messageFromOtherTextColor))); } // Construct an file chrome @@ -364,46 +346,33 @@ class FileBubbleState extends State { var settings = Provider.of(context); return ListTile( visualDensity: VisualDensity.compact, - title: Wrap(direction: Axis.horizontal, alignment: WrapAlignment.start, children: [ - SelectableText( - chrome + '\u202F', - style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of(context).theme.messageFromOtherTextColor)), - textAlign: TextAlign.left, - maxLines: 2, - textWidthBasis: TextWidthBasis.longestLine, - ), - SelectableText( - fileName + '\u202F', - style: settings - .scaleFonts(defaultMessageTextStyle.copyWith(overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold, color: Provider.of(context).theme.messageFromOtherTextColor)), - textAlign: TextAlign.left, - textWidthBasis: TextWidthBasis.parent, - maxLines: 2, - ), - SelectableText( - AppLocalizations.of(context)!.labelFilesize + ': ' + prettyBytes(fileSize) + '\u202F' + '\n', - style: settings.scaleFonts(defaultSmallTextStyle.copyWith(color: Provider.of(context).theme.messageFromOtherTextColor)), - textAlign: TextAlign.left, - maxLines: 2, - ) - ]), + contentPadding: EdgeInsets.all(1.0), + title: SelectableText( + fileName + '\u202F', + style: + settings.scaleFonts(defaultMessageTextStyle.copyWith(overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold, color: Provider.of(context).theme.messageFromOtherTextColor)), + textAlign: TextAlign.left, + textWidthBasis: TextWidthBasis.longestLine, + maxLines: 2, + ), subtitle: SelectableText( - 'sha512: ' + rootHash + '\u202F', + prettyBytes(widget.fileSize) + '\u202F' + '\n' + 'sha512: ' + rootHash + '\u202F', style: settings.scaleFonts(defaultSmallTextStyle.copyWith(fontFamily: "RobotoMono", color: Provider.of(context).theme.messageFromOtherTextColor)), textAlign: TextAlign.left, maxLines: 4, - textWidthBasis: TextWidthBasis.parent, + textWidthBasis: TextWidthBasis.longestLine, ), - leading: Icon(CwtchIcons.attached_file_3, size: 32, color: Provider.of(context).theme.messageFromOtherTextColor), - trailing: Visibility( - visible: speed != "0 B/s", - child: SelectableText( - speed + '\u202F', - style: settings.scaleFonts(defaultSmallTextStyle.copyWith(color: Provider.of(context).theme.messageFromOtherTextColor)), - textAlign: TextAlign.left, - maxLines: 1, - textWidthBasis: TextWidthBasis.longestLine, - )), + leading: FittedBox(child: Icon(CwtchIcons.attached_file_3, size: 32, color: Provider.of(context).theme.messageFromOtherTextColor)), + // Note: not using Visible here because we want to shrink this to nothing when not in use... + trailing: speed == "0 B/s" + ? null + : SelectableText( + speed + '\u202F', + style: settings.scaleFonts(defaultSmallTextStyle.copyWith(color: Provider.of(context).theme.messageFromOtherTextColor)), + textAlign: TextAlign.left, + maxLines: 1, + textWidthBasis: TextWidthBasis.longestLine, + ), ); } diff --git a/lib/widgets/messagebubbledecorations.dart b/lib/widgets/messagebubbledecorations.dart index 55bbc3eb..104a41b0 100644 --- a/lib/widgets/messagebubbledecorations.dart +++ b/lib/widgets/messagebubbledecorations.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -23,35 +24,33 @@ class _MessageBubbleDecoration extends State { Widget build(BuildContext context) { var prettyDate = prettyDateString(context, widget.messageDate.toLocal()); - return Center( - widthFactor: 1.0, + return FittedBox( child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(prettyDate, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 9.0 * Provider.of(context).fontScaling, - fontWeight: FontWeight.w200, - fontFamily: "Inter", - color: widget.fromMe ? Provider.of(context).theme.messageFromMeTextColor : Provider.of(context).theme.messageFromOtherTextColor), - textAlign: widget.fromMe ? TextAlign.right : TextAlign.left), - !widget.fromMe - ? SizedBox(width: 1, height: 1) - : Padding( - padding: EdgeInsets.all(1.0), - child: widget.ackd == true + mainAxisSize: MainAxisSize.min, + children: [ + Text(prettyDate, + overflow: TextOverflow.ellipsis, + textWidthBasis: TextWidthBasis.longestLine, + style: TextStyle( + fontSize: 9.0 * Provider.of(context).fontScaling, + fontWeight: FontWeight.w200, + fontFamily: "Inter", + color: widget.fromMe ? Provider.of(context).theme.messageFromMeTextColor : Provider.of(context).theme.messageFromOtherTextColor), + textAlign: widget.fromMe ? TextAlign.right : TextAlign.left), + !widget.fromMe + ? SizedBox(width: 1, height: 1) + : Padding( + padding: EdgeInsets.all(1.0), + child: widget.ackd == true + ? Tooltip( + message: AppLocalizations.of(context)!.acknowledgedLabel, child: Icon(Icons.check_circle_outline, color: Provider.of(context).theme.messageFromMeTextColor, size: 16)) + : (widget.errored == true ? Tooltip( - message: AppLocalizations.of(context)!.acknowledgedLabel, - child: Icon(Icons.check_circle_outline, color: Provider.of(context).theme.messageFromMeTextColor, size: 16)) - : (widget.errored == true - ? Tooltip( - message: AppLocalizations.of(context)!.couldNotSendMsgError, - child: Icon(Icons.error_outline, color: Provider.of(context).theme.messageFromMeTextColor, size: 16)) - : Tooltip( - message: AppLocalizations.of(context)!.pendingLabel, - child: Icon(Icons.hourglass_bottom_outlined, color: Provider.of(context).theme.messageFromMeTextColor, size: 16)))) - ], - )); + message: AppLocalizations.of(context)!.couldNotSendMsgError, child: Icon(Icons.error_outline, color: Provider.of(context).theme.messageFromMeTextColor, size: 16)) + : Tooltip( + message: AppLocalizations.of(context)!.pendingLabel, + child: Icon(Icons.hourglass_bottom_outlined, color: Provider.of(context).theme.messageFromMeTextColor, size: 16)))) + ], + )); } } diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index 0230a986..0385bade 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -63,7 +63,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi var isContact = Provider.of(context).contactList.findContact(Provider.of(context).senderHandle) != null; var isGroup = Provider.of(context).contactList.getContact(Provider.of(context, listen: false).conversationIdentifier)!.isGroup; var isBlocked = isContact ? Provider.of(context).contactList.findContact(Provider.of(context).senderHandle)!.isBlocked : false; - var actualMessage = Flexible(flex: 1, fit: FlexFit.loose, child: widget.child); + var actualMessage = Flexible(flex: Platform.isAndroid ? 10 : 7, fit: FlexFit.loose, child: widget.child); _dragAffinity = fromMe ? Alignment.centerRight : Alignment.centerLeft; _dragAlignment = fromMe ? Alignment.centerRight : Alignment.centerLeft;