From c78dbf0ac367ae972ce4b59964c522798564d49e Mon Sep 17 00:00:00 2001 From: erinn Date: Fri, 17 Dec 2021 16:32:22 -0800 Subject: [PATCH] fileshare confirmations and contact-add-gating --- lib/views/messageview.dart | 78 ++++++++++++++++++++++++++++++++++--- lib/widgets/filebubble.dart | 26 +++++++++---- 2 files changed, 91 insertions(+), 13 deletions(-) diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index 539bebfb..270ca4a4 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -5,6 +5,7 @@ import 'package:cwtch/config.dart'; import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/message.dart'; import 'package:cwtch/models/messages/quotedmessage.dart'; +import 'package:cwtch/widgets/malformedbubble.dart'; import 'package:cwtch/widgets/messageloadingbubble.dart'; import 'package:cwtch/widgets/profileimage.dart'; @@ -37,6 +38,7 @@ class _MessageViewState extends State { int selectedContact = -1; ItemPositionsListener scrollListener = ItemPositionsListener.create(); ItemScrollController scrollController = ItemScrollController(); + File? imagePreview; @override void initState() { @@ -85,7 +87,7 @@ class _MessageViewState extends State { appBarButtons.add(IconButton( icon: Icon(Icons.attach_file, size: 24), tooltip: AppLocalizations.of(context)!.tooltipSendFile, - onPressed: _showFilePicker, + onPressed: (){_showFilePicker(context);}, )); } appBarButtons.add(IconButton( @@ -354,7 +356,7 @@ class _MessageViewState extends State { return contact.onion != Provider.of(context).onion; }, onChanged: (newVal) { setState(() { - this.selectedContact = Provider.of(context).contactList.findContact(newVal)!.identifier; + this.selectedContact = Provider.of(context, listen: false).contactList.findContact(newVal)!.identifier; }); })), SizedBox( @@ -375,7 +377,8 @@ class _MessageViewState extends State { }); } - void _showFilePicker() async { + void _showFilePicker(BuildContext ctx) async { + imagePreview = null; FilePickerResult? result = await FilePicker.platform.pickFiles(); if (result != null) { File file = File(result.files.first.path); @@ -383,11 +386,74 @@ class _MessageViewState extends State { // a manifest (see : https://git.openprivacy.ca/cwtch.im/cwtch/src/branch/master/protocol/files/manifest.go#L25) if (file.lengthSync() <= 10737418240) { print("Sending " + file.path); - _sendFile(file.path); + _confirmFileSend(ctx, file.path); } else { - print("file size cannot exceed 10 gigabytes"); - //todo: toast error + final snackBar = SnackBar( + content: Text("File size cannot exceed 10 GB"), + duration: Duration(seconds: 4), + ); + ScaffoldMessenger.of(ctx).showSnackBar(snackBar); } } } + + void _confirmFileSend(BuildContext ctx, String path) async { + showModalBottomSheet( + context: ctx, + builder: (BuildContext bcontext) { + var lpath = path.toLowerCase(); + var showPreview = false; + if (Provider.of(context, listen: false).isExperimentEnabled(ImagePreviewsExperiment) && (lpath.endsWith("jpg") || lpath.endsWith("jpeg") || lpath.endsWith("png") || lpath.endsWith("gif") || lpath.endsWith("webp") || lpath.endsWith("bmp"))) { + showPreview = true; + if (imagePreview == null) { + imagePreview = new File(path); + } + } + return Container( + height: 300, // bespoke value courtesy of the [TextField] docs + child: Center( + child: Padding( + padding: EdgeInsets.all(10.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Text("Are you sure you want to send $path?"), + SizedBox( + height: 20, + ), + Visibility(visible: showPreview, child: showPreview ? Image.file( + imagePreview!, + cacheHeight: 150, // limit the amount of space the image can decode too, we keep this high-ish to allow quality previews... + filterQuality: FilterQuality.medium, + fit: BoxFit.fill, + alignment: Alignment.center, + height: 150, + isAntiAlias: false, + errorBuilder: (context, error, stackTrace) { + return MalformedBubble(); + }, + ) : Container()), + Visibility(visible: showPreview, child: SizedBox(height: 10,)), + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + ElevatedButton( + child: Text("Cancel", semanticsLabel: "Cancel"), + onPressed: () { + Navigator.pop(bcontext); + }, + ), + SizedBox(width: 20,), + ElevatedButton( + child: Text("Send File", semanticsLabel: "Send File"), + onPressed: () { + _sendFile(path); + Navigator.pop(bcontext); + }, + ), + ]), + ], + )), + )); + }); + } } diff --git a/lib/widgets/filebubble.dart b/lib/widgets/filebubble.dart index a3840f72..90f04423 100644 --- a/lib/widgets/filebubble.dart +++ b/lib/widgets/filebubble.dart @@ -49,6 +49,7 @@ class FileBubbleState extends State { var flagStarted = Provider.of(context).attributes["file-downloaded"] == "true"; var borderRadiousEh = 15.0; var showFileSharing = Provider.of(context).isExperimentEnabled(FileSharingExperiment); + var showImagePreviews = Provider.of(context).isExperimentEnabled(ImagePreviewsExperiment); var prettyDate = DateFormat.yMd(Platform.localeName).add_jm().format(Provider.of(context).timestamp); var downloadComplete = Provider.of(context).downloadComplete(widget.fileKey()); var downloadInterrupted = Provider.of(context).downloadInterrupted(widget.fileKey()); @@ -74,10 +75,12 @@ class FileBubbleState extends State { // If the sender is not us, then we want to give them a nickname... var senderDisplayStr = ""; + var senderIsContact = false; if (!fromMe) { ContactInfoState? contact = Provider.of(context).contactList.findContact(Provider.of(context).senderHandle); if (contact != null) { senderDisplayStr = contact.nickname; + senderIsContact = true; } else { senderDisplayStr = Provider.of(context).senderHandle; } @@ -103,7 +106,7 @@ class FileBubbleState extends State { } else if (downloadComplete) { // in this case, whatever marked download.complete would have also set the path var lpath = path!.toLowerCase(); - if (lpath.endsWith("jpg") || lpath.endsWith("jpeg") || lpath.endsWith("png") || lpath.endsWith("gif") || lpath.endsWith("webp") || lpath.endsWith("bmp")) { + if (showImagePreviews && (lpath.endsWith("jpg") || lpath.endsWith("jpeg") || lpath.endsWith("png") || lpath.endsWith("gif") || lpath.endsWith("webp") || lpath.endsWith("bmp"))) { isPreview = true; wdgDecorations = GestureDetector( child: Image.file( @@ -138,14 +141,23 @@ class FileBubbleState extends State { // 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'); + wdgDecorations = Text( + AppLocalizations.of(context)!.fileCheckingStatus + '...' + + '\u202F'); } else { - var path = Provider.of(context).downloadFinalPath(widget.fileKey()) ?? ""; - wdgDecorations = Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F'), - ElevatedButton(onPressed: _btnResume, child: Text(AppLocalizations.of(context)!.verfiyResumeButton)) - ]); + var path = Provider.of(context).downloadFinalPath( + widget.fileKey()) ?? ""; + wdgDecorations = + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + + path + '\u202F'), + ElevatedButton(onPressed: _btnResume, + child: Text( + AppLocalizations.of(context)!.verfiyResumeButton)) + ]); } + } else if (!senderIsContact) { + wdgDecorations = Text("Add this account to your contacts in order to accept this file."); } else if (!widget.isAuto) { wdgDecorations = Center( widthFactor: 1,