diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index 080fef9f..b70bed05 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -v1.0.0-25-g801a805-2021-07-07-16-10 \ No newline at end of file +v1.0.0-29-g41ae09d-2021-07-08-20-22 diff --git a/README.md b/README.md index 25c80b52..f95318aa 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,12 @@ This README covers build instructions, for information on Cwtch itself please go - Linux: Available from [https://cwtch.im/download/](https://cwtch.im/download/) as a .tar.gz - `install.home.sh` installs the app into your home directory - `install.sys.sh` as root to install system wide + - or run out of the unziped directory ## Running -Cwtch logging is controlable with the following environment variables: +Cwtch processes the following environment variables: +- `CWTCH_HOME=` overrides the default storage path of `~/.cwtch` with what ever you choose - `LOG_FILE=` will reroute all of libcwtch-go's logging to the specified file instead of the console - `LOG_LEVEL=debug` will set the log level to debug instead of info diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index cdfbb15a..e1a96c50 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -103,6 +103,14 @@ class CwtchNotifier { } profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.totalMessages++; profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now()); + + // We only ever see messages from authenticated peers. + // If the contact is marked as offline then override this - can happen when the contact is removed from the front + // end during syncing. + if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.isOnline() == false) { + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.status = "Authenticated"; + } + break; case "PeerAcknowledgement": // We don't use these anymore, IndexedAcknowledgement is more suited to the UI front end... @@ -116,6 +124,12 @@ class CwtchNotifier { try { var message = Provider.of(key.currentContext!, listen: false); if (message == null) break; + // We only ever see acks from authenticated peers. + // If the contact is marked as offline then override this - can happen when the contact is removed from the front + // end during syncing. + if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.isOnline() == false) { + profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.status = "Authenticated"; + } message.ackd = true; } catch (e) { // ignore, we received an ack for a message that hasn't loaded onto the screen yet... @@ -134,7 +148,7 @@ class CwtchNotifier { } else { // from me (already displayed - do not update counter) var idx = data["Signature"]; - var key = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.getMessageKey(idx); + var key = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])?.getMessageKey(idx); if (key == null) break; try { var message = Provider.of(key.currentContext!, listen: false); @@ -153,7 +167,7 @@ class CwtchNotifier { case "IndexedFailure": EnvironmentConfig.debugLog("IndexedFailure"); var idx = data["Index"]; - var key = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.getMessageKey(idx); + var key = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])?.getMessageKey(idx); try { var message = Provider.of(key!.currentContext!, listen: false); message.error = true; diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index a1b4bda7..f0957f08 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -89,7 +89,7 @@ class CwtchFfi implements Cwtch { String bundledTor = ""; Map envVars = Platform.environment; if (Platform.isLinux) { - home = (envVars['HOME'])!; + home = envVars['HOME']!; if (await File("linux/tor").exists()) { bundledTor = "linux/tor"; } else if (await File("lib/tor").exists()) { @@ -102,10 +102,11 @@ class CwtchFfi implements Cwtch { bundledTor = "tor"; } } else if (Platform.isWindows) { - home = (envVars['UserProfile'])!; + home = envVars['UserProfile']!; bundledTor = "Tor\\Tor\\tor.exe"; } - var cwtchDir = path.join(home, ".cwtch"); + + var cwtchDir = envVars['CWTCH_HOME'] ?? path.join(home, ".cwtch"); if (EnvironmentConfig.BUILD_VER == dev_version) { cwtchDir = path.join(cwtchDir, "dev"); } diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart index 6d5bfc1f..fb1af6c3 100644 --- a/lib/views/contactsview.dart +++ b/lib/views/contactsview.dart @@ -83,7 +83,7 @@ class _ContactsViewState extends State { ), Expanded( child: Text("%1 ยป %2".replaceAll("%1", Provider.of(context).nickname).replaceAll("%2", AppLocalizations.of(context)!.titleManageContacts), - overflow: TextOverflow.ellipsis, style: TextStyle(color: Provider.of(context).current().mainTextColor()))), //todo + overflow: TextOverflow.ellipsis, style: TextStyle(color: Provider.of(context).current().mainTextColor()))), ])), actions: [ IconButton(icon: TorIcon(), onPressed: _pushTorStatus), diff --git a/lib/views/doublecolview.dart b/lib/views/doublecolview.dart index e7d3a260..ebd7ffff 100644 --- a/lib/views/doublecolview.dart +++ b/lib/views/doublecolview.dart @@ -33,7 +33,8 @@ class _DoubleColumnViewState extends State { : //dev MultiProvider(providers: [ ChangeNotifierProvider.value(value: Provider.of(context)), - ChangeNotifierProvider.value(value: Provider.of(context).contactList.getContact(flwtch.selectedConversation!)!), + ChangeNotifierProvider.value( + value: flwtch.selectedConversation != null ? Provider.of(context).contactList.getContact(flwtch.selectedConversation!)! : ContactInfoState("", "")), ], child: Container(child: MessageView())), ), ], diff --git a/lib/views/globalsettingsview.dart b/lib/views/globalsettingsview.dart index 01f37db2..aa782034 100644 --- a/lib/views/globalsettingsview.dart +++ b/lib/views/globalsettingsview.dart @@ -93,20 +93,31 @@ class _GlobalSettingsViewState extends State { ); }).toList())), ListTile( - title: Text(AppLocalizations.of(context)!.settingUIColumnLandscape, style: TextStyle(color: settings.current().mainTextColor())), + title: Text( + AppLocalizations.of(context)!.settingUIColumnLandscape, + textWidthBasis: TextWidthBasis.longestLine, + softWrap: true, + style: TextStyle(color: settings.current().mainTextColor()), + ), leading: Icon(Icons.table_chart, color: settings.current().mainTextColor()), - trailing: DropdownButton( - value: settings.uiColumnModeLandscape.toString(), - onChanged: (String? newValue) { - settings.uiColumnModeLandscape = Settings.uiColumnModeFromString(newValue!); - saveSettings(context); - }, - items: Settings.uiColumnModeOptions(true).map>((DualpaneMode value) { - return DropdownMenuItem( - value: value.toString(), - child: Text(Settings.uiColumnModeToString(value, context)), - ); - }).toList())), + trailing: Container( + width: 200.0, + child: DropdownButton( + isExpanded: true, + value: settings.uiColumnModeLandscape.toString(), + onChanged: (String? newValue) { + settings.uiColumnModeLandscape = Settings.uiColumnModeFromString(newValue!); + saveSettings(context); + }, + items: Settings.uiColumnModeOptions(true).map>((DualpaneMode value) { + return DropdownMenuItem( + value: value.toString(), + child: Text( + Settings.uiColumnModeToString(value, context), + overflow: TextOverflow.ellipsis, + ), + ); + }).toList()))), SwitchListTile( title: Text(AppLocalizations.of(context)!.blockUnknownLabel, style: TextStyle(color: settings.current().mainTextColor())), subtitle: Text(AppLocalizations.of(context)!.descriptionBlockUnknownConnections), diff --git a/lib/views/groupsettingsview.dart b/lib/views/groupsettingsview.dart index f1e50b95..a0149122 100644 --- a/lib/views/groupsettingsview.dart +++ b/lib/views/groupsettingsview.dart @@ -173,6 +173,7 @@ class _GroupSettingsViewState extends State { var handle = Provider.of(context, listen: false).onion; Provider.of(context, listen: false).cwtch.LeaveGroup(profileOnion, handle); Future.delayed(Duration(milliseconds: 500), () { + Provider.of(context, listen: false).selectedConversation = null; Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog }); }, diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index d295e379..b327fc09 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -47,6 +47,11 @@ class _MessageViewState extends State { @override Widget build(BuildContext context) { + // After leaving a conversation the selected conversation is set to null... + if (Provider.of(context).profileOnion == "") { + return Card(child: Center(child: Text(AppLocalizations.of(context)!.addContactFirst))); + } + var appState = Provider.of(context); return WillPopScope( onWillPop: _onWillPop, @@ -65,7 +70,11 @@ class _MessageViewState extends State { SizedBox( width: 10, ), - Text(Provider.of(context).nickname) + Expanded( + child: Text( + Provider.of(context).nickname, + overflow: TextOverflow.ellipsis, + )) ]), actions: [ //IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings), diff --git a/lib/views/peersettingsview.dart b/lib/views/peersettingsview.dart index 0f71eb2e..b33abed9 100644 --- a/lib/views/peersettingsview.dart +++ b/lib/views/peersettingsview.dart @@ -230,6 +230,7 @@ class _PeerSettingsViewState extends State { var handle = Provider.of(context, listen: false).onion; Provider.of(context, listen: false).cwtch.LeaveConversation(profileOnion, handle); Future.delayed(Duration(milliseconds: 500), () { + Provider.of(context, listen: false).selectedConversation = null; Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog }); },