diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index 8ac98c3e..2ea31b78 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -2023-04-05-19-46-v0.0.3-13-gb7a4bc2 \ No newline at end of file +2023-04-20-15-37-v0.0.3-20-gb2a43b9 \ No newline at end of file diff --git a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt index 3b3c4d85..7f19e000 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt @@ -442,7 +442,7 @@ class MainActivity: FlutterActivity() { val profile: String = call.argument("ProfileOnion") ?: "" val conversation: Int = call.argument("conversation") ?: 0 val fileKey: String = call.argument("fileKey") ?: "" - Cwtch.verifyOrResumeDownload(profile, conversation.toLong(), fileKey) + Cwtch.verifyOrResumeDownloadDefaultLimit(profile, conversation.toLong(), fileKey) } "UpdateSettings" -> { val json: String = call.argument("json") ?: "" diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 85324657..e2c2b4db 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -379,8 +379,8 @@ class CwtchNotifier { }); profileCN.getProfile(data["ProfileOnion"])?.contactList.resort(); break; - case "NewRetValMessageFromPeer": - if (data["Path"] == "profile.name" && data["Exists"] == "true") { + case "UpdatedConversationAttribute": + if (data["Path"] == "profile.name") { if (data["Data"].toString().trim().length > 0) { // Update locally on the UI... if (profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]) != null) { @@ -388,37 +388,31 @@ class CwtchNotifier { } } } else if (data['Path'] == "profile.custom-profile-image") { - if (data["Exists"] == "true") { - EnvironmentConfig.debugLog("received ret val of custom profile image: $data"); - String fileKey = data['Data']; - var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]); - if (contact != null) { - profileCN.getProfile(data["ProfileOnion"])?.waitForDownloadComplete(contact.identifier, fileKey); - } + EnvironmentConfig.debugLog("received ret val of custom profile image: $data"); + String fileKey = data['Data']; + var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]); + if (contact != null) { + profileCN.getProfile(data["ProfileOnion"])?.waitForDownloadComplete(contact.identifier, fileKey); } } else if (data['Path'] == "profile.profile-attribute-1" || data['Path'] == "profile.profile-attribute-2" || data['Path'] == "profile.profile-attribute-3") { - if (data["Exists"] == "true") { - var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]); - if (contact != null) { - switch (data['Path']) { - case "profile.profile-attribute-1": - contact.setAttribute(0, data["Data"]); - break; - case "profile.profile-attribute-2": - contact.setAttribute(1, data["Data"]); - break; - case "profile.profile-attribute-3": - contact.setAttribute(2, data["Data"]); - break; - } + var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]); + if (contact != null) { + switch (data['Path']) { + case "profile.profile-attribute-1": + contact.setAttribute(0, data["Data"]); + break; + case "profile.profile-attribute-2": + contact.setAttribute(1, data["Data"]); + break; + case "profile.profile-attribute-3": + contact.setAttribute(2, data["Data"]); + break; } } } else if (data['Path'] == "profile.profile-status") { - if (data["Exists"] == "true") { - var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]); - if (contact != null) { - contact.setAvailabilityStatus(data['Data']); - } + var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]); + if (contact != null) { + contact.setAvailabilityStatus(data['Data']); } } else { EnvironmentConfig.debugLog("unhandled ret val event: ${data['Path']}"); diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index b5ed651c..5c70e7bc 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -498,7 +498,7 @@ class CwtchFfi implements Cwtch { @override // ignore: non_constant_identifier_names void VerifyOrResumeDownload(String profileOnion, int contactHandle, String filekey) { - var fn = library.lookup>("c_VerifyOrResumeDownload"); + var fn = library.lookup>("c_VerifyOrResumeDownloadDefaultLimit"); // ignore: non_constant_identifier_names final VerifyOrResumeDownload = fn.asFunction(); final u1 = profileOnion.toNativeUtf8(); diff --git a/lib/main.dart b/lib/main.dart index f972a018..449b4026 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -131,7 +131,7 @@ class FlwtchState extends State with WindowListener { key: Key('app'), navigatorKey: navKey, locale: settings.locale, - showPerformanceOverlay: false, + showPerformanceOverlay: settings.profileMode, localizationsDelegates: >[ AppLocalizations.delegate, MaterialLocalizationDelegate(), diff --git a/lib/models/appstate.dart b/lib/models/appstate.dart index f543211d..a00d15f7 100644 --- a/lib/models/appstate.dart +++ b/lib/models/appstate.dart @@ -12,8 +12,6 @@ class AppState extends ChangeNotifier { String? _selectedProfile; int? _selectedConversation; int _initialScrollIndex = 0; - int _hoveredIndex = -1; - int? _selectedIndex; bool _unreadMessagesBelow = false; bool _disableFilePicker = false; bool _focus = true; @@ -59,14 +57,6 @@ 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; diff --git a/lib/models/contact.dart b/lib/models/contact.dart index 4d6c8f30..a12e19db 100644 --- a/lib/models/contact.dart +++ b/lib/models/contact.dart @@ -39,6 +39,7 @@ class ContactInfoState extends ChangeNotifier { final int identifier; final String onion; late String _nickname; + late String _localNickname; late ConversationNotificationPolicy _notificationPolicy; @@ -66,11 +67,14 @@ class ContactInfoState extends ChangeNotifier { String? _acnCircuit; MessageDraft _messageDraft = MessageDraft.empty(); + var _hoveredIndex = -1; + ContactInfoState( this.profileOnion, this.identifier, this.onion, { nickname = "", + localNickname = "", isGroup = false, accepted = false, blocked = false, @@ -87,6 +91,7 @@ class ContactInfoState extends ChangeNotifier { pinned = false, }) { this._nickname = nickname; + this._localNickname = localNickname; this._isGroup = isGroup; this._accepted = accepted; this._blocked = blocked; @@ -105,7 +110,13 @@ class ContactInfoState extends ChangeNotifier { keys = Map>(); } - String get nickname => this._nickname; + String get nickname { + if (this._localNickname != "") { + return this._localNickname; + } + return this._nickname; + } + String get savePeerHistory => this._savePeerHistory; String? get acnCircuit => this._acnCircuit; @@ -144,6 +155,11 @@ class ContactInfoState extends ChangeNotifier { notifyListeners(); } + set localNickname(String newVal) { + this._localNickname = newVal; + notifyListeners(); + } + bool get isGroup => this._isGroup; set isGroup(bool newVal) { @@ -402,7 +418,15 @@ class ContactInfoState extends ChangeNotifier { } String augmentedNickname(BuildContext context) { - return this.nickname + (this.availabilityStatus == ProfileStatusMenu.available ? "" : " (" +this.statusString(context) + ")"); + return this.nickname + (this.availabilityStatus == ProfileStatusMenu.available ? "" : " (" + this.statusString(context) + ")"); + } + + // 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(); } String statusString(BuildContext context) { diff --git a/lib/models/filedownloadprogress.dart b/lib/models/filedownloadprogress.dart index 9212f379..52542206 100644 --- a/lib/models/filedownloadprogress.dart +++ b/lib/models/filedownloadprogress.dart @@ -8,12 +8,17 @@ class FileDownloadProgress { DateTime? timeStart; DateTime? timeEnd; DateTime? requested; + DateTime lastUpdate = DateTime.now(); FileDownloadProgress(this.chunksTotal, this.timeStart); double progress() { return 1.0 * chunksDownloaded / chunksTotal; } + + void markUpdate() { + lastUpdate = DateTime.now(); + } } String prettyBytes(int bytes) { diff --git a/lib/models/messages/filemessage.dart b/lib/models/messages/filemessage.dart index c425ae0b..d8ebc6cf 100644 --- a/lib/models/messages/filemessage.dart +++ b/lib/models/messages/filemessage.dart @@ -33,11 +33,9 @@ class FileMessage extends Message { int fileSize = shareObj['s'] as int; String fileKey = rootHash + "." + nonce; - // if (metadata.attributes["file-downloaded"] == "true") { - // if (!Provider.of(context).downloadKnown(fileKey)) { - Provider.of(context, listen: false).cwtch.CheckDownloadStatus(Provider.of(context, listen: false).onion, fileKey); - // } - //} + if (!Provider.of(context, listen: false).downloadKnown(fileKey)) { + Provider.of(context, listen: false).cwtch.CheckDownloadStatus(Provider.of(context, listen: false).onion, fileKey); + } if (!validHash(rootHash, nonce)) { return MessageRow(MalformedBubble(), index); diff --git a/lib/models/profile.dart b/lib/models/profile.dart index 0c43d519..86ef3f2b 100644 --- a/lib/models/profile.dart +++ b/lib/models/profile.dart @@ -67,6 +67,7 @@ class ProfileInfoState extends ChangeNotifier { this._unreadMessages += contact["numUnread"] as int; return ContactInfoState(this.onion, contact["identifier"], contact["onion"], nickname: contact["name"], + localNickname: contact["localname"], status: contact["status"], imagePath: contact["picture"], defaultImagePath: contact["isGroup"] ? contact["picture"] : contact["defaultPicture"], @@ -268,6 +269,7 @@ class ProfileInfoState extends ChangeNotifier { } this._downloads[fileKey]!.chunksDownloaded = progress; this._downloads[fileKey]!.chunksTotal = numChunks; + this._downloads[fileKey]!.markUpdate(); } notifyListeners(); } @@ -277,6 +279,7 @@ class ProfileInfoState extends ChangeNotifier { this._downloads[fileKey] = FileDownloadProgress(1, DateTime.now()); } this._downloads[fileKey]!.gotManifest = true; + this._downloads[fileKey]!.markUpdate(); notifyListeners(); } @@ -301,6 +304,7 @@ class ProfileInfoState extends ChangeNotifier { this._downloads[fileKey]!.timeEnd = DateTime.now(); this._downloads[fileKey]!.downloadedTo = finalPath; this._downloads[fileKey]!.complete = true; + this._downloads[fileKey]!.markUpdate(); notifyListeners(); } } @@ -333,9 +337,13 @@ class ProfileInfoState extends ChangeNotifier { this._downloads[fileKey]!.interrupted = true; return true; } + if (DateTime.now().difference(this._downloads[fileKey]!.lastUpdate) > Duration(minutes: 1)) { + this._downloads[fileKey]!.requested = null; + this._downloads[fileKey]!.interrupted = true; + return true; + } } } - return false; } @@ -343,6 +351,7 @@ class ProfileInfoState extends ChangeNotifier { if (this._downloads.containsKey(fileKey)) { this._downloads[fileKey]!.interrupted = false; this._downloads[fileKey]!.requested = DateTime.now(); + this._downloads[fileKey]!.markUpdate(); notifyListeners(); } } diff --git a/lib/settings.dart b/lib/settings.dart index 956fd5b9..74eb7af8 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -69,6 +69,15 @@ class Settings extends ChangeNotifier { String get torCacheDir => _torCacheDir; + // Whether to show the profiling interface, not saved + bool _profileMode = false; + + bool get profileMode => _profileMode; + set profileMode(bool newval) { + this._profileMode = newval; + notifyListeners(); + } + set useSemanticDebugger(bool newval) { this._useSemanticDebugger = newval; notifyListeners(); diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index 8a37a76e..3bb9cc30 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -50,7 +50,7 @@ void selectConversation(BuildContext context, int handle) { // triggers update in Double/TripleColumnView Provider.of(context, listen: false).initialScrollIndex = unread; Provider.of(context, listen: false).selectedConversation = handle; - Provider.of(context, listen: false).hoveredIndex = -1; + 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); @@ -164,15 +164,21 @@ class _ContactsViewState extends State { itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: ProfileStatusMenu.available, - child: Text(AppLocalizations.of(context)!.availabilityStatusAvailable!,), + child: Text( + AppLocalizations.of(context)!.availabilityStatusAvailable!, + ), ), PopupMenuItem( value: ProfileStatusMenu.away, - child: Text(AppLocalizations.of(context)!.availabilityStatusAway!,), + child: Text( + AppLocalizations.of(context)!.availabilityStatusAway!, + ), ), PopupMenuItem( value: ProfileStatusMenu.busy, - child: Text(AppLocalizations.of(context)!.availabilityStatusBusy!,), + child: Text( + AppLocalizations.of(context)!.availabilityStatusBusy!, + ), ), ], ), diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index fda4ca89..30f1fc18 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -545,6 +545,24 @@ class _GlobalSettingsViewState extends State { child: SelectableText(AppLocalizations.of(context)!.versionBuilddate.replaceAll("%1", EnvironmentConfig.BUILD_VER).replaceAll("%2", EnvironmentConfig.BUILD_DATE)), ) ]), + SwitchListTile( + // TODO: Translate, Remove, OR Hide Prior to Release + title: Text("Show Performance Overlay", style: TextStyle(color: settings.current().mainTextColor)), + subtitle: Text("Display an overlay graph of render time."), + value: settings.profileMode, + onChanged: (bool value) { + setState(() { + if (value) { + settings.profileMode = value; + } else { + settings.profileMode = value; + } + }); + }, + activeTrackColor: settings.theme.defaultButtonActiveColor, + inactiveTrackColor: settings.theme.defaultButtonDisabledColor, + secondary: Icon(Icons.bar_chart, color: settings.current().mainTextColor), + ), Visibility( visible: EnvironmentConfig.BUILD_VER == dev_version && !Platform.isAndroid, child: SwitchListTile( diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index a66a4561..96791583 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -17,11 +17,9 @@ import 'package:cwtch/models/profile.dart'; import 'package:cwtch/third_party/linkify/flutter_linkify.dart'; import 'package:cwtch/widgets/malformedbubble.dart'; import 'package:cwtch/widgets/messageloadingbubble.dart'; -import 'package:cwtch/widgets/messagerow.dart'; import 'package:cwtch/widgets/profileimage.dart'; import 'package:cwtch/controllers/filesharing.dart' as filesharing; import 'package:cwtch/widgets/staticmessagebubble.dart'; -import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -230,11 +228,12 @@ class _MessageViewState extends State { ), Expanded( child: Container( - height: 42, - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(), - child: Align( - alignment: Alignment.centerLeft, child: Text( + height: 42, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration(), + child: Align( + alignment: Alignment.centerLeft, + child: Text( Provider.of(context).augmentedNickname(context), overflow: TextOverflow.clip, maxLines: 1, diff --git a/lib/views/peersettingsview.dart b/lib/views/peersettingsview.dart index a066a315..616609f7 100644 --- a/lib/views/peersettingsview.dart +++ b/lib/views/peersettingsview.dart @@ -113,15 +113,24 @@ class _PeerSettingsViewState extends State { child: Column(children: [ Padding( padding: EdgeInsets.all(1), - child: SelectableText(Provider.of(context, listen: false).attributes[0] ?? "", textAlign: TextAlign.center,), + child: SelectableText( + Provider.of(context, listen: false).attributes[0] ?? "", + textAlign: TextAlign.center, + ), ), Padding( padding: EdgeInsets.all(1), - child: SelectableText(Provider.of(context, listen: false).attributes[1] ?? "", textAlign: TextAlign.center,), + child: SelectableText( + Provider.of(context, listen: false).attributes[1] ?? "", + textAlign: TextAlign.center, + ), ), Padding( padding: EdgeInsets.all(1), - child: SelectableText(Provider.of(context, listen: false).attributes[2] ?? "", textAlign: TextAlign.center,), + child: SelectableText( + Provider.of(context, listen: false).attributes[2] ?? "", + textAlign: TextAlign.center, + ), ) ])) ]), @@ -137,7 +146,7 @@ class _PeerSettingsViewState extends State { onPressed: () { var profileOnion = Provider.of(context, listen: false).profileOnion; var conversation = Provider.of(context, listen: false).identifier; - Provider.of(context, listen: false).nickname = ctrlrNick.text; + Provider.of(context, listen: false).localNickname = ctrlrNick.text; Provider.of(context, listen: false).cwtch.SetConversationAttribute(profileOnion, conversation, "profile.name", ctrlrNick.text); final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.nickChangeSuccess)); ScaffoldMessenger.of(context).showSnackBar(snackBar); diff --git a/lib/widgets/filebubble.dart b/lib/widgets/filebubble.dart index 64d2bc78..7ab8282e 100644 --- a/lib/widgets/filebubble.dart +++ b/lib/widgets/filebubble.dart @@ -165,7 +165,7 @@ class FileBubbleState extends State { wdgDecorations = Text('\u202F'); } else if (downloadComplete && path != null) { // in this case, whatever marked download.complete would have also set the path - if (Provider.of(context).shouldPreview(path)) { + if (myFile != null && Provider.of(context).shouldPreview(path)) { isPreview = true; wdgDecorations = Center( widthFactor: 1.0, @@ -196,6 +196,7 @@ class FileBubbleState extends State { // so we probably have to request an info lookup if (!downloadInterrupted) { wdgDecorations = Text(AppLocalizations.of(context)!.fileCheckingStatus + '...' + '\u202F'); + // We should have already requested this... } else { var path = Provider.of(context).downloadFinalPath(widget.fileKey()) ?? ""; wdgDecorations = Visibility( diff --git a/lib/widgets/messagelist.dart b/lib/widgets/messagelist.dart index 6319ba59..99d0d313 100644 --- a/lib/widgets/messagelist.dart +++ b/lib/widgets/messagelist.dart @@ -1,3 +1,4 @@ +import 'package:cwtch/config.dart'; import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/message.dart'; diff --git a/lib/widgets/messagerow.dart b/lib/widgets/messagerow.dart index 8920bdee..2d3faa34 100644 --- a/lib/widgets/messagerow.dart +++ b/lib/widgets/messagerow.dart @@ -81,7 +81,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi Widget wdgReply = Platform.isAndroid ? SizedBox.shrink() : Visibility( - visible: EnvironmentConfig.TEST_MODE || Provider.of(context).hoveredIndex == Provider.of(context).messageID, + visible: EnvironmentConfig.TEST_MODE || Provider.of(context).hoveredIndex == Provider.of(context).messageID, maintainSize: true, maintainAnimation: true, maintainState: true, @@ -106,7 +106,7 @@ class MessageRowState extends State with SingleTickerProviderStateMi Widget wdgSeeReplies = Platform.isAndroid ? SizedBox.shrink() : Visibility( - visible: EnvironmentConfig.TEST_MODE || Provider.of(context).hoveredIndex == Provider.of(context).messageID, + visible: EnvironmentConfig.TEST_MODE || Provider.of(context).hoveredIndex == Provider.of(context).messageID, maintainSize: true, maintainAnimation: true, maintainState: true, @@ -128,8 +128,8 @@ class MessageRowState extends State with SingleTickerProviderStateMi : Visibility( visible: Provider.of(context, listen: false).cwtch.IsBlodeuweddSupported() && Provider.of(context).isExperimentEnabled(BlodeuweddExperiment) && - (EnvironmentConfig.TEST_MODE || Provider.of(context).hoveredIndex == Provider.of(context).messageID), - maintainSize: false, + (EnvironmentConfig.TEST_MODE || Provider.of(context).hoveredIndex == Provider.of(context).messageID), + maintainSize: true, maintainAnimation: true, maintainState: true, maintainInteractivity: false, @@ -245,16 +245,12 @@ class MessageRowState extends State with SingleTickerProviderStateMi var mr = MouseRegion( // For desktop... onHover: (event) { - if (Provider.of(context, listen: false).hoveredIndex != Provider.of(context, listen: false).messageID) { - setState(() { - Provider.of(context, listen: false).hoveredIndex = Provider.of(context, listen: false).messageID; - }); + if (Provider.of(context, listen: false).hoveredIndex != Provider.of(context, listen: false).messageID) { + Provider.of(context, listen: false).hoveredIndex = Provider.of(context, listen: false).messageID; } }, onExit: (event) { - // setState(() { - // Provider.of(context, listen: false).hoveredIndex = -1; - //}); + Provider.of(context, listen: false).hoveredIndex = -1; }, child: GestureDetector( onPanUpdate: (details) { @@ -270,12 +266,16 @@ class MessageRowState extends State with SingleTickerProviderStateMi }, onPanEnd: (details) { _runAnimation(details.velocity.pixelsPerSecond, size); - Provider.of(context, listen: false).messageDraft.quotedReference = Provider.of(context, listen: false).messageID; - Provider.of(context, listen: false).notifyMessageDraftUpdate(); - setState(() {}); + if (Platform.isAndroid) { + Provider.of(context, listen: false).messageDraft.quotedReference = Provider.of(context, listen: false).messageID; + Provider.of(context, listen: false).notifyMessageDraftUpdate(); + setState(() {}); + } }, onLongPress: () async { - modalShowReplies(context, AppLocalizations.of(context)!.headingReplies, AppLocalizations.of(context)!.messageNoReplies, settings, pis, cis, borderColor, cache, messageID); + if (Platform.isAndroid) { + modalShowReplies(context, AppLocalizations.of(context)!.headingReplies, AppLocalizations.of(context)!.messageNoReplies, settings, pis, cis, borderColor, cache, messageID); + } }, child: Padding( padding: EdgeInsets.all(2),