diff --git a/LIBCWTCH-GO.version b/LIBCWTCH-GO.version index 8e40226..d12319f 100644 --- a/LIBCWTCH-GO.version +++ b/LIBCWTCH-GO.version @@ -1 +1 @@ -v0.0.2-49-g6a0e839-2021-06-02-19-40 \ No newline at end of file +v0.0.2-58-gfddfd41-2021-06-10-18-36 \ 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 5066757..dc34259 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt @@ -129,6 +129,13 @@ class MainActivity: FlutterActivity() { val end = (call.argument("end") as? Long) ?: 0; result.success(Cwtch.getMessages(profile, handle, start, end)) } + "UpdateMessageFlags" -> { + val profile = (call.argument("profile") as? String) ?: ""; + val handle = (call.argument("contact") as? String) ?: ""; + val midx = (call.argument("midx") as? Long) ?: 0; + val flags = (call.argument("flags") as? Long) ?: 0; + Cwtch.updateMessageFlags(profile, handle, midx, flags); + } "AcceptContact" -> { val profile = (call.argument("ProfileOnion") as? String) ?: ""; val handle = (call.argument("handle") as? String) ?: ""; diff --git a/assets/fonts/CwtchIcons.ttf b/assets/fonts/CwtchIcons.ttf index ef83f2e..9a6e5cb 100644 Binary files a/assets/fonts/CwtchIcons.ttf and b/assets/fonts/CwtchIcons.ttf differ diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index cb2b0cd..816fa65 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -38,6 +38,8 @@ abstract class Cwtch { // ignore: non_constant_identifier_names Future GetMessage(String profile, String handle, int index); // ignore: non_constant_identifier_names + void UpdateMessageFlags(String profile, String handle, int index, int flags); + // ignore: non_constant_identifier_names Future GetMessages(String profile, String handle, int start, int end); // ignore: non_constant_identifier_names void SendMessage(String profile, String handle, String message); diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 021f94c..950e21c 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -178,23 +178,24 @@ class CwtchNotifier { profileCN.getProfile(data["ProfileOnion"])?.replaceServers(data["ServerList"]); break; case "NewGroup": - print("new group invite: $data"); + print("new group: $data"); String invite = data["GroupInvite"].toString(); if (invite.startsWith("torv3")) { String inviteJson = new String.fromCharCodes(base64Decode(invite.substring(5))); dynamic groupInvite = jsonDecode(inviteJson); - print("new group invite: $groupInvite"); + print("group invite: $groupInvite"); // Retrieve Server Status from Cache... String status = ""; - ServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])?.serverList.getServer(groupInvite["ServerHost"]); + ServerInfoState? serverInfoState = profileCN.getProfile(data["ProfileOnion"])!.serverList.getServer(groupInvite["ServerHost"]); if (serverInfoState != null) { + print("Got server status: " + serverInfoState.status); status = serverInfoState.status; } if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(groupInvite["GroupID"]) == null) { profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], groupInvite["GroupID"], - isInvitation: true, + isInvitation: false, imagePath: data["PicturePath"], nickname: groupInvite["GroupName"], server: groupInvite["ServerHost"], diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index 410ffba..df0e163 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -27,6 +27,9 @@ typedef VoidFromStringStringStringFn = void Function(Pointer, int, Pointer typedef void_from_string_string_string_string_function = Void Function(Pointer, Int32, Pointer, Int32, Pointer, Int32, Pointer, Int32); typedef VoidFromStringStringStringStringFn = void Function(Pointer, int, Pointer, int, Pointer, int, Pointer, int); +typedef void_from_string_string_int_int_function = Void Function(Pointer, Int32, Pointer, Int32, Int64, Int64); +typedef VoidFromStringStringIntIntFn = void Function(Pointer, int, Pointer, int, int, int); + typedef access_cwtch_eventbus_function = Void Function(); typedef NextEventFn = void Function(); @@ -384,4 +387,14 @@ class CwtchFfi implements Cwtch { final u2 = groupHandle.toNativeUtf8(); RejectInvite(u1, u1.length, u2, u2.length); } + + @override + void UpdateMessageFlags(String profile, String handle, int index, int flags) { + var updateMessageFlagsC = library.lookup>("c_UpdateMessageFlags"); + // ignore: non_constant_identifier_names + final updateMessageFlags = updateMessageFlagsC.asFunction(); + final utf8profile = profile.toNativeUtf8(); + final utf8handle = handle.toNativeUtf8(); + updateMessageFlags(utf8profile, utf8profile.length, utf8handle, utf8handle.length, index, flags); + } } diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index 210eb42..d89f38e 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:io'; import 'package:cwtch/config.dart'; import 'package:flutter/services.dart'; @@ -187,4 +186,10 @@ class CwtchGomobile implements Cwtch { void LeaveGroup(String profileOnion, String groupHandle) { cwtchPlatform.invokeMethod("LeaveGroup", {"ProfileOnion": profileOnion, "handle": groupHandle}); } + + @override + void UpdateMessageFlags(String profile, String handle, int index, int flags) { + print("gomobile.dart UpdateMessageFlags " + index.toString()); + cwtchPlatform.invokeMethod("UpdateMessageFlags", {"profile": profile, "contact": handle, "index": index, "flags": flags}); + } } diff --git a/lib/cwtch_icons_icons.dart b/lib/cwtch_icons_icons.dart index 8552f4d..8722310 100644 --- a/lib/cwtch_icons_icons.dart +++ b/lib/cwtch_icons_icons.dart @@ -1,5 +1,5 @@ -/// Flutter icons MyFlutterApp -/// Copyright (C) 2021 by original authors @ fluttericon.com, fontello.com +/// Flutter icons CwtchIcons +/// Copyright (C) 2021 by Open Privacy Research Society via fluttericon.com, fontello.com /// This font was generated by FlutterIcon.com, which is derived from Fontello. /// /// To use this font, place it in your fonts/ directory and include the @@ -102,8 +102,9 @@ class CwtchIcons { static const IconData add_group = IconData(0xe84e, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData add_peer = IconData(0xe84f, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData add_24px = IconData(0xe850, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData address_copy_2 = IconData(0xe852, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData address = IconData(0xe856, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData send_invite = IconData(0xe888, fontFamily: _kFontFam, fontPackage: _kFontPkg); - static const IconData copy_address = IconData(0xe889, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData leave_group = IconData(0xe88a, fontFamily: _kFontFam, fontPackage: _kFontPkg); static const IconData leave_chat = IconData(0xe88b, fontFamily: _kFontFam, fontPackage: _kFontPkg); } diff --git a/lib/model.dart b/lib/model.dart index a07fb07..7dec94b 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -183,7 +183,7 @@ class ProfileInfoState extends ChangeNotifier { // Parse out the server list json into our server info state struct... void replaceServers(String serversJson) { - if (serversJson != null && serversJson != "" && serversJson != "null") { + if (serversJson != "" && serversJson != "null") { print("got servers $serversJson"); List servers = jsonDecode(serversJson); this._servers.replace(servers.map((server) { @@ -382,6 +382,7 @@ class MessageState extends ChangeNotifier { late String _inviteNick; late DateTime _timestamp; late String _senderOnion; + late int _flags; String? _senderImage; late String _signature = ""; late bool _ackd = false; @@ -402,6 +403,12 @@ class MessageState extends ChangeNotifier { get message => this._message; get overlay => this._overlay; get timestamp => this._timestamp; + int get flags => this._flags; + set flags(int newVal) { + this._flags = newVal; + notifyListeners(); + } + bool get ackd => this._ackd; bool get error => this._error; bool get malformed => this._malformed; @@ -450,6 +457,7 @@ class MessageState extends ChangeNotifier { this._timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!; this._senderOnion = messageWrapper['PeerID']; this._senderImage = messageWrapper['ContactImage']; + this._flags = int.parse(messageWrapper['Flags'].toString(), radix: 2); // If this is a group, store the signature if (contactHandle.length == 32) { diff --git a/lib/views/addcontactview.dart b/lib/views/addcontactview.dart index 3d9bc61..3c27c64 100644 --- a/lib/views/addcontactview.dart +++ b/lib/views/addcontactview.dart @@ -119,7 +119,11 @@ class _AddContactViewState extends State { CwtchButtonTextField( controller: ctrlrOnion, onPressed: _copyOnion, - icon: Icon(CwtchIcons.copy_address), + readonly: true, + icon: Icon( + CwtchIcons.address_copy_2, + size: 32, + ), tooltip: AppLocalizations.of(context)!.copyBtn, ), SizedBox( diff --git a/lib/views/addeditprofileview.dart b/lib/views/addeditprofileview.dart index c27308c..7fcc3b4 100644 --- a/lib/views/addeditprofileview.dart +++ b/lib/views/addeditprofileview.dart @@ -11,6 +11,7 @@ import 'package:cwtch/widgets/textfield.dart'; import 'package:provider/provider.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import '../cwtch_icons_icons.dart'; import '../main.dart'; import '../opaque.dart'; import '../settings.dart'; @@ -122,7 +123,11 @@ class _AddEditProfileViewState extends State { CwtchButtonTextField( controller: ctrlrOnion, onPressed: _copyOnion, - icon: Icon(Icons.copy), + readonly: true, + icon: Icon( + CwtchIcons.address_copy_2, + size: 32, + ), tooltip: AppLocalizations.of(context)!.copyBtn, ) ])), diff --git a/lib/widgets/buttontextfield.dart b/lib/widgets/buttontextfield.dart index 83083fb..65f8fa5 100644 --- a/lib/widgets/buttontextfield.dart +++ b/lib/widgets/buttontextfield.dart @@ -28,6 +28,7 @@ class _CwtchButtonTextFieldState extends State { suffixIcon: IconButton( onPressed: widget.onPressed, icon: widget.icon, + padding: EdgeInsets.fromLTRB(0.0, 4.0, 2.0, 2.0), tooltip: widget.tooltip, enableFeedback: true, color: theme.current().mainTextColor(), diff --git a/lib/widgets/invitationbubble.dart b/lib/widgets/invitationbubble.dart index 232fde3..7913f38 100644 --- a/lib/widgets/invitationbubble.dart +++ b/lib/widgets/invitationbubble.dart @@ -22,15 +22,17 @@ class InvitationBubble extends StatefulWidget { class InvitationBubbleState extends State { bool rejected = false; + bool isAccepted = false; FocusNode _focus = FocusNode(); @override Widget build(BuildContext context) { var fromMe = Provider.of(context).senderOnion == Provider.of(context).onion; var isGroup = Provider.of(context).overlay == 101; - var isAccepted = Provider.of(context).contactList.getContact(Provider.of(context).inviteTarget) != null; + isAccepted = Provider.of(context).contactList.getContact(Provider.of(context).inviteTarget) != null; var prettyDate = ""; var borderRadiousEh = 15.0; + rejected = Provider.of(context).flags & 0x01 == 0x01; var myKey = Provider.of(context).profileOnion + "::" + Provider.of(context).contactHandle + "::" + Provider.of(context).messageIndex.toString(); if (Provider.of(context).timestamp != null) { @@ -54,8 +56,6 @@ class InvitationBubbleState extends State { child: SelectableText(senderDisplayStr + '\u202F', style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of(context).theme.messageFromMeTextColor() : Provider.of(context).theme.messageFromOtherTextColor()))); - // todo: translations - // If we receive an invite for ourselves, treat it as a bug. The UI no longer allows this so it could have only come from // some kind of malfeasance. var selfInvite = Provider.of(context).inviteNick == Provider.of(context).onion; @@ -120,22 +120,21 @@ class InvitationBubbleState extends State { } void _btnReject() { - //todo: how should we track inline invite rejections? - setState(() => this.rejected = true); + setState(() { + var profileOnion = Provider.of(context, listen: false).onion; + var contact = Provider.of(context, listen: false).onion; + var idx = Provider.of(context, listen: false).messageIndex; + Provider.of(context, listen: false).cwtch.UpdateMessageFlags(profileOnion, contact, idx, Provider.of(context, listen: false).flags | 0x01); + Provider.of(context).flags |= 0x01; + }); } void _btnAccept() { - var profileOnion = Provider.of(context, listen: false).onion; - if (Provider.of(context, listen: false).overlay == 100) { - final setPeerAttribute = { - "EventType": "AddContact", - "Data": {"ImportString": Provider.of(context, listen: false).message}, - }; - final setPeerAttributeJson = jsonEncode(setPeerAttribute); - Provider.of(context, listen: false).cwtch.SendProfileEvent(profileOnion, setPeerAttributeJson); - } else { + setState(() { + var profileOnion = Provider.of(context, listen: false).onion; Provider.of(context, listen: false).cwtch.ImportBundle(profileOnion, Provider.of(context, listen: false).message); - } + isAccepted = true; + }); } // Construct an invite chrome for the sender diff --git a/pubspec.yaml b/pubspec.yaml index b232973..45e488b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+10 +version: 1.0.0+11 environment: sdk: ">=2.12.0 <3.0.0"