diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index e34915df..4bbee290 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -98,7 +98,7 @@ class CwtchFfi implements Cwtch { Map envVars = Platform.environment; String cwtchDir = ""; if (Platform.isLinux) { - cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, ".cwtch"); + cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, ".cwtch"); if (await File("linux/tor").exists()) { bundledTor = "linux/tor"; } else if (await File("lib/tor").exists()) { @@ -126,7 +126,7 @@ class CwtchFfi implements Cwtch { print("We couldn't find Tor in the Cwtch app directory, however we can fall back to the Tor Browser binary"); } else { var splitPath = path.split(dirname(Platform.script.path)); - if (splitPath[0] == "/" && splitPath[1] == "Applications"){ + if (splitPath[0] == "/" && splitPath[1] == "Applications") { var appName = splitPath[2]; print("We're running in /Applications in a non standard app name: $appName"); if (await File("/Applications/$appName/Contents/MacOS/Tor/tor.real").exists()) { @@ -138,8 +138,8 @@ class CwtchFfi implements Cwtch { // the first Cwtch MacOS release (1.2) accidently was a dev build // we need to temporarily remedy this for a release or two then delete - // if macOs and release build and no profile and is dev profile - // copy dev profile to release profile + // if macOs and release build and no profile and is dev profile + // copy dev profile to release profile if (Platform.isMacOS && EnvironmentConfig.BUILD_VER != dev_version) { var devProfileExists = await Directory(path.join(cwtchDir, "dev", "profiles")).exists(); var releaseProfileExists = await Directory(path.join(cwtchDir, "profiles")).exists(); @@ -448,7 +448,6 @@ class CwtchFfi implements Cwtch { malloc.free(u2); } - @override // ignore: non_constant_identifier_names void UpdateMessageFlags(String profile, String handle, int index, int flags) { diff --git a/lib/model.dart b/lib/model.dart index 603fd0bb..d63fe3c3 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -31,6 +31,7 @@ class AppState extends ChangeNotifier { String? _selectedProfile; String? _selectedConversation; int _initialScrollIndex = 0; + int _hoveredIndex = -1; int? _selectedIndex; bool _unreadMessagesBelow = false; @@ -62,6 +63,14 @@ class AppState extends ChangeNotifier { notifyListeners(); } + // Never use this for message lookup - can be a non-indexed value + // e.g. -1 + int get hoveredIndex => _hoveredIndex; + set hoveredIndex(int newVal) { + this._hoveredIndex = newVal; + notifyListeners(); + } + bool get unreadMessagesBelow => _unreadMessagesBelow; set unreadMessagesBelow(bool newVal) { this._unreadMessagesBelow = newVal; @@ -381,21 +390,18 @@ class ContactInfoState extends ChangeNotifier { String? _server; late bool _archived; - ContactInfoState( - this.profileOnion, - this.onion, { - nickname = "", - isGroup = false, - authorization = ContactAuthorization.unknown, - status = "", - imagePath = "", - savePeerHistory = "DeleteHistoryConfirmed", - numMessages = 0, - numUnread = 0, - lastMessageTime, - server, - archived = false - }) { + ContactInfoState(this.profileOnion, this.onion, + {nickname = "", + isGroup = false, + authorization = ContactAuthorization.unknown, + status = "", + imagePath = "", + savePeerHistory = "DeleteHistoryConfirmed", + numMessages = 0, + numUnread = 0, + lastMessageTime, + server, + archived = false}) { this._nickname = nickname; this._isGroup = isGroup; this._authorization = authorization; @@ -421,8 +427,8 @@ class ContactInfoState extends ChangeNotifier { this._archived = archived; notifyListeners(); } - bool get isArchived => this._archived; + bool get isArchived => this._archived; set savePeerHistory(String newVal) { this._savePeerHistory = newVal; diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index 297fc0d5..f1caeb6d 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -30,6 +30,7 @@ void selectConversation(BuildContext context, String handle) { Provider.of(context, listen: false).initialScrollIndex = initialIndex; Provider.of(context, listen: false).selectedConversation = handle; Provider.of(context, listen: false).selectedIndex = null; + Provider.of(context, listen: false).hoveredIndex = -1; // if in singlepane mode, push to the stack var isLandscape = Provider.of(context, listen: false).isLandscape(context); if (Provider.of(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle); diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index fa7b1619..7c330ea9 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -142,7 +142,7 @@ class _GlobalSettingsViewState extends State { subtitle: Text(AppLocalizations.of(context)!.descriptionStreamerMode), value: settings.streamerMode, onChanged: (bool value) { - settings.setStreamerMode(value); + settings.setStreamerMode(value); // Save Settings... saveSettings(context); }, diff --git a/lib/views/groupsettingsview.dart b/lib/views/groupsettingsview.dart index 6d5acfe9..72afb5d2 100644 --- a/lib/views/groupsettingsview.dart +++ b/lib/views/groupsettingsview.dart @@ -162,11 +162,12 @@ class _GroupSettingsViewState extends State { onPressed: () { showAlertDialog(context); }, - style: ButtonStyle ( - backgroundColor: MaterialStateProperty.all(Colors.transparent) - ), + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.transparent)), icon: Icon(CwtchIcons.leave_group), - label: Text(AppLocalizations.of(context)!.leaveGroup, style: TextStyle(decoration: TextDecoration.underline),), + label: Text( + AppLocalizations.of(context)!.leaveGroup, + style: TextStyle(decoration: TextDecoration.underline), + ), )) ]) ]) diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index 625608bc..034c7d68 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -257,18 +257,27 @@ class _MessageViewState extends State { color: message.getMetadata().senderHandle != Provider.of(context).selectedProfile ? Provider.of(context).theme.messageFromOtherBackgroundColor() : Provider.of(context).theme.messageFromMeBackgroundColor(), - child: Wrap(runAlignment: WrapAlignment.spaceEvenly, alignment: WrapAlignment.spaceEvenly, runSpacing: 1.0, crossAxisAlignment: WrapCrossAlignment.center, children: [ - Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(Icons.reply, size: 32))), - Center(widthFactor: 1.0, child: message.getPreviewWidget(context)), - Center( - widthFactor: 1.0, - child: IconButton( - icon: Icon(Icons.highlight_remove), - tooltip: AppLocalizations.of(context)!.tooltipRemoveThisQuotedMessage, - onPressed: () { - Provider.of(context, listen: false).selectedIndex = null; - }, - )) + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Stack(children: [ + Align( + alignment: Alignment.topRight, + child: IconButton( + icon: Icon(Icons.highlight_remove), + tooltip: AppLocalizations.of(context)!.tooltipRemoveThisQuotedMessage, + onPressed: () { + Provider.of(context, listen: false).selectedIndex = null; + }, + )), + Align( + alignment: Alignment.topLeft, + child: Padding(padding: EdgeInsets.all(2.0), child: Icon(Icons.reply)), + ) + ]), + Wrap( + runAlignment: WrapAlignment.spaceEvenly, + alignment: WrapAlignment.center, + runSpacing: 1.0, + children: [Center(widthFactor: 1.0, child: Padding(padding: EdgeInsets.all(10.0), child: message.getPreviewWidget(context)))]), ])); } else { return MessageLoadingBubble(); diff --git a/lib/widgets/contactrow.dart b/lib/widgets/contactrow.dart index 9514e0ec..6a50008a 100644 --- a/lib/widgets/contactrow.dart +++ b/lib/widgets/contactrow.dart @@ -65,11 +65,12 @@ class _ContactRowState extends State { child: LinearProgressIndicator( color: Provider.of(context).theme.defaultButtonActiveColor(), )), - Visibility( - visible: !Provider.of(context).streamerMode, - child: Text(contact.onion, - style: TextStyle(color: contact.isBlocked ? Provider.of(context).theme.portraitBlockedTextColor() : Provider.of(context).theme.mainTextColor())), - )], + Visibility( + visible: !Provider.of(context).streamerMode, + child: Text(contact.onion, + style: TextStyle(color: contact.isBlocked ? Provider.of(context).theme.portraitBlockedTextColor() : Provider.of(context).theme.mainTextColor())), + ) + ], ))), Padding( padding: const EdgeInsets.all(5.0), diff --git a/lib/widgets/messagelist.dart b/lib/widgets/messagelist.dart index 0605352b..a6a8353d 100644 --- a/lib/widgets/messagelist.dart +++ b/lib/widgets/messagelist.dart @@ -56,44 +56,44 @@ class _MessageListState extends State { Text("")), ))), Expanded( - child: Container( - // Only show broken heart is the contact is offline... - decoration: BoxDecoration( - image: Provider.of(outerContext).isOnline() - ? null - : DecorationImage( - fit: BoxFit.scaleDown, - alignment: Alignment.center, - image: AssetImage("assets/core/negative_heart_512px.png"), - colorFilter: ColorFilter.mode(Provider.of(context).theme.hilightElementTextColor(), BlendMode.srcIn))), - // Don't load messages for syncing server... - child: loadMessages - ? ScrollablePositionedList.builder( - itemPositionsListener: widget.scrollListener, - itemScrollController: widget.scrollController, - initialScrollIndex: Provider.of(outerContext, listen: false).initialScrollIndex, - itemCount: Provider.of(outerContext).totalMessages, - reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction... - itemBuilder: (itemBuilderContext, index) { - var profileOnion = Provider.of(outerContext, listen: false).onion; - var contactHandle = Provider.of(outerContext, listen: false).onion; - var messageIndex = Provider.of(outerContext).totalMessages - index - 1; + child: Container( + // Only show broken heart is the contact is offline... + decoration: BoxDecoration( + image: Provider.of(outerContext).isOnline() + ? null + : DecorationImage( + fit: BoxFit.scaleDown, + alignment: Alignment.center, + image: AssetImage("assets/core/negative_heart_512px.png"), + colorFilter: ColorFilter.mode(Provider.of(context).theme.hilightElementTextColor(), BlendMode.srcIn))), + // Don't load messages for syncing server... + child: loadMessages + ? ScrollablePositionedList.builder( + itemPositionsListener: widget.scrollListener, + itemScrollController: widget.scrollController, + initialScrollIndex: Provider.of(outerContext, listen: false).initialScrollIndex, + itemCount: Provider.of(outerContext).totalMessages, + reverse: true, // NOTE: There seems to be a bug in flutter that corrects the mouse wheel scroll, but not the drag direction... + itemBuilder: (itemBuilderContext, index) { + var profileOnion = Provider.of(outerContext, listen: false).onion; + var contactHandle = Provider.of(outerContext, listen: false).onion; + var messageIndex = Provider.of(outerContext).totalMessages - index - 1; - return FutureBuilder( - future: messageHandler(outerContext, profileOnion, contactHandle, messageIndex), - builder: (context, snapshot) { - if (snapshot.hasData) { - var message = snapshot.data as Message; - // Already includes MessageRow,, - return message.getWidget(context); - } else { - return MessageLoadingBubble(); - } - }, - ); + return FutureBuilder( + future: messageHandler(outerContext, profileOnion, contactHandle, messageIndex), + builder: (context, snapshot) { + if (snapshot.hasData) { + var message = snapshot.data as Message; + // Already includes MessageRow,, + return message.getWidget(context); + } else { + return MessageLoadingBubble(); + } }, - ) - : null)) + ); + }, + ) + : null)) ]))); } } diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index 2084cca5..69e97a00 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -22,7 +22,6 @@ class MessageRow extends StatefulWidget { } class MessageRowState extends State with SingleTickerProviderStateMixin { - bool showMenu = false; bool showBlockedMessage = false; late AnimationController _controller; late Animation _animation; @@ -70,7 +69,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi } Widget wdgIcons = Visibility( - visible: this.showMenu, + visible: Provider.of(context).hoveredIndex == Provider.of(context).messageIndex, maintainSize: true, maintainAnimation: true, maintainState: true, @@ -164,12 +163,12 @@ class MessageRowState extends State with SingleTickerProviderStateMi // For desktop... onHover: (event) { setState(() { - this.showMenu = true; + Provider.of(context, listen: false).hoveredIndex = Provider.of(context).messageIndex; }); }, onExit: (event) { setState(() { - this.showMenu = false; + Provider.of(context, listen: false).hoveredIndex = -1; }); }, child: GestureDetector( diff --git a/lib/widgets/profilerow.dart b/lib/widgets/profilerow.dart index 3782195e..9d43997f 100644 --- a/lib/widgets/profilerow.dart +++ b/lib/widgets/profilerow.dart @@ -47,13 +47,13 @@ class _ProfileRowState extends State { overflow: TextOverflow.ellipsis, ), Visibility( - visible: !Provider.of(context).streamerMode, + visible: !Provider.of(context).streamerMode, child: ExcludeSemantics( - child: Text( - profile.onion, - softWrap: true, - overflow: TextOverflow.ellipsis, - ))) + child: Text( + profile.onion, + softWrap: true, + overflow: TextOverflow.ellipsis, + ))) ], )), IconButton(