Bare bones groups support
continuous-integration/drone/pr Build is failing
Details
continuous-integration/drone/pr Build is failing
Details
This commit is contained in:
parent
484f8a4b77
commit
a5a8f59841
|
@ -160,6 +160,11 @@ class MainActivity: FlutterActivity() {
|
||||||
"ResetTor" -> {
|
"ResetTor" -> {
|
||||||
Cwtch.resetTor();
|
Cwtch.resetTor();
|
||||||
}
|
}
|
||||||
|
"ImportBundle" -> {
|
||||||
|
val profile = (call.argument("ProfileOnion") as? String) ?: "";
|
||||||
|
val bundle = (call.argument("bundle") as? String) ?: "";
|
||||||
|
Cwtch.blockContact(profile, bundle);
|
||||||
|
}
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,5 +42,8 @@ abstract class Cwtch {
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
void SendMessage(String profile, String handle, String message);
|
void SendMessage(String profile, String handle, String message);
|
||||||
|
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
|
void ImportBundle(String profile, String bundle);
|
||||||
|
|
||||||
void dispose();
|
void dispose();
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ class CwtchNotifier {
|
||||||
numMessages: int.parse(data["numMessages"]),
|
numMessages: int.parse(data["numMessages"]),
|
||||||
numUnread: int.parse(data["unread"]),
|
numUnread: int.parse(data["unread"]),
|
||||||
isGroup: data["isGroup"],
|
isGroup: data["isGroup"],
|
||||||
|
server: data["groupServer"],
|
||||||
lastMessageTime: DateTime.now(), //show at the top of the contact list even if no messages yet
|
lastMessageTime: DateTime.now(), //show at the top of the contact list even if no messages yet
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
|
@ -61,6 +62,12 @@ class CwtchNotifier {
|
||||||
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]).totalMessages++;
|
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]).totalMessages++;
|
||||||
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());
|
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());
|
||||||
break;
|
break;
|
||||||
|
case "NewMessageFromGroup":
|
||||||
|
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["GroupID"]).unreadMessages++;
|
||||||
|
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["GroupID"]).totalMessages++;
|
||||||
|
// TODO exception is called in the case of groups.
|
||||||
|
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());
|
||||||
|
break;
|
||||||
case "AppError":
|
case "AppError":
|
||||||
print("New App Error: $data");
|
print("New App Error: $data");
|
||||||
error.handleUpdate(data["Data"]);
|
error.handleUpdate(data["Data"]);
|
||||||
|
@ -86,6 +93,20 @@ class CwtchNotifier {
|
||||||
case "UpdateServerInfo":
|
case "UpdateServerInfo":
|
||||||
profileCN.getProfile(data["ProfileOnion"]).replaceServers(data["ServerList"]);
|
profileCN.getProfile(data["ProfileOnion"]).replaceServers(data["ServerList"]);
|
||||||
break;
|
break;
|
||||||
|
case "NewGroupInvite":
|
||||||
|
print("new group invite: $data");
|
||||||
|
// TODO Add Group Dynamically
|
||||||
|
//profileCN.getProfile(data["ProfileOnion"]).contactList.add(ContactInfoState(isGroup: true))
|
||||||
|
break;
|
||||||
|
case "ServerStateChange":
|
||||||
|
print("server state change: $data");
|
||||||
|
profileCN.getProfile(data["ProfileOnion"]).contactList.contacts.forEach((contact) {
|
||||||
|
if (contact.isGroup && contact.server == data["GroupServer"]) {
|
||||||
|
print("server state change: $data " + contact.server);
|
||||||
|
contact.status = data["ConnectionState"];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
print("unhandled event: $type");
|
print("unhandled event: $type");
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,9 +299,21 @@ class CwtchFfi implements Cwtch {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
void ResetTor() {
|
void ResetTor() {
|
||||||
var resetTor = library.lookup<NativeFunction<Void Function()>>("c_ResetTor");
|
var resetTor = library.lookup<NativeFunction<Void Function()>>("c_ResetTor");
|
||||||
final ResetTor = resetTor.asFunction<void Function()>();
|
final ResetTor = resetTor.asFunction<void Function()>();
|
||||||
ResetTor();
|
ResetTor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
|
void ImportBundle(String profileOnion, String bundle) {
|
||||||
|
var importBundle = library.lookup<NativeFunction<string_string_to_void_function>>("c_ImportBundle");
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
|
final ImportBundle = importBundle.asFunction<VoidFromStringStringFn>();
|
||||||
|
final u1 = profileOnion.toNativeUtf8();
|
||||||
|
final u2 = bundle.toNativeUtf8();
|
||||||
|
ImportBundle(u1, u1.length, u2, u2.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,4 +148,10 @@ class CwtchGomobile implements Cwtch {
|
||||||
void ResetTor() {
|
void ResetTor() {
|
||||||
cwtchPlatform.invokeMethod("ResetTor", {});
|
cwtchPlatform.invokeMethod("ResetTor", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
|
void ImportBundle(String profileOnion, String bundle) {
|
||||||
|
cwtchPlatform.invokeMethod("ImportBundle", {"ProfileOnion": profileOnion, "bundle": bundle});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,11 @@ class ErrorHandler extends ChangeNotifier {
|
||||||
bool contactAlreadyExistsError = false;
|
bool contactAlreadyExistsError = false;
|
||||||
bool explicitAddContactSuccess = false;
|
bool explicitAddContactSuccess = false;
|
||||||
|
|
||||||
|
// Import Bundle Specific Errors
|
||||||
|
static const String importBundleErrorPrefix = "importBundle";
|
||||||
|
bool importBundleError = false;
|
||||||
|
bool importBundleSuccess = false;
|
||||||
|
|
||||||
/// Called by the event bus.
|
/// Called by the event bus.
|
||||||
handleUpdate(String error) {
|
handleUpdate(String error) {
|
||||||
var parts = error.split(".");
|
var parts = error.split(".");
|
||||||
|
@ -22,6 +27,8 @@ class ErrorHandler extends ChangeNotifier {
|
||||||
case addContactErrorPrefix:
|
case addContactErrorPrefix:
|
||||||
handleAddContactError(errorType);
|
handleAddContactError(errorType);
|
||||||
break;
|
break;
|
||||||
|
case importBundleErrorPrefix:
|
||||||
|
handleImportBundleError(errorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
@ -45,4 +52,19 @@ class ErrorHandler extends ChangeNotifier {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleImportBundleError(String errorType) {
|
||||||
|
// Reset add contact errors
|
||||||
|
importBundleError = false;
|
||||||
|
importBundleSuccess = false;
|
||||||
|
|
||||||
|
switch (errorType) {
|
||||||
|
case successErrorType:
|
||||||
|
importBundleSuccess = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
importBundleError = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,6 +149,8 @@ class ProfileInfoState extends ChangeNotifier {
|
||||||
savePeerHistory: contact["saveConversationHistory"],
|
savePeerHistory: contact["saveConversationHistory"],
|
||||||
numMessages: contact["numMessages"],
|
numMessages: contact["numMessages"],
|
||||||
numUnread: contact["numUnread"],
|
numUnread: contact["numUnread"],
|
||||||
|
isGroup: contact["isGroup"],
|
||||||
|
server: contact["groupServer"],
|
||||||
lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])));
|
lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -212,7 +214,7 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
final String profileOnion;
|
final String profileOnion;
|
||||||
final String onion;
|
final String onion;
|
||||||
String _nickname;
|
String _nickname;
|
||||||
bool _isGroup;
|
|
||||||
bool _isInvitation;
|
bool _isInvitation;
|
||||||
bool _isBlocked;
|
bool _isBlocked;
|
||||||
String _status;
|
String _status;
|
||||||
|
@ -222,6 +224,10 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
int _totalMessages = 0;
|
int _totalMessages = 0;
|
||||||
DateTime _lastMessageTime;
|
DateTime _lastMessageTime;
|
||||||
|
|
||||||
|
// todo: a nicer way to mdoel contats, groups and other "entities"
|
||||||
|
bool _isGroup;
|
||||||
|
String _server;
|
||||||
|
|
||||||
ContactInfoState({
|
ContactInfoState({
|
||||||
this.profileOnion,
|
this.profileOnion,
|
||||||
this.onion,
|
this.onion,
|
||||||
|
@ -235,6 +241,7 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
numMessages = 0,
|
numMessages = 0,
|
||||||
numUnread = 0,
|
numUnread = 0,
|
||||||
lastMessageTime = null,
|
lastMessageTime = null,
|
||||||
|
server = "",
|
||||||
}) {
|
}) {
|
||||||
this._nickname = nickname;
|
this._nickname = nickname;
|
||||||
this._isGroup = isGroup;
|
this._isGroup = isGroup;
|
||||||
|
@ -246,6 +253,7 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
this._unreadMessages = numUnread;
|
this._unreadMessages = numUnread;
|
||||||
this._savePeerHistory = savePeerHistory;
|
this._savePeerHistory = savePeerHistory;
|
||||||
this._lastMessageTime = lastMessageTime;
|
this._lastMessageTime = lastMessageTime;
|
||||||
|
this._server = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
get nickname => this._nickname;
|
get nickname => this._nickname;
|
||||||
|
@ -308,6 +316,17 @@ class ContactInfoState extends ChangeNotifier {
|
||||||
this._lastMessageTime = newVal;
|
this._lastMessageTime = newVal;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we only allow callers to fetch the server
|
||||||
|
get server => this._server;
|
||||||
|
|
||||||
|
bool isOnline() {
|
||||||
|
if (this.isGroup) {
|
||||||
|
return this.status == "Synced";
|
||||||
|
} else {
|
||||||
|
return this.status == "Authenticated";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessageState extends ChangeNotifier {
|
class MessageState extends ChangeNotifier {
|
||||||
|
|
|
@ -26,6 +26,7 @@ class AddContactView extends StatefulWidget {
|
||||||
class _AddContactViewState extends State<AddContactView> {
|
class _AddContactViewState extends State<AddContactView> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final _createGroupFormKey = GlobalKey<FormState>();
|
final _createGroupFormKey = GlobalKey<FormState>();
|
||||||
|
final _joinGroupFormKey = GlobalKey<FormState>();
|
||||||
final ctrlrOnion = TextEditingController(text: "");
|
final ctrlrOnion = TextEditingController(text: "");
|
||||||
final ctrlrContact = TextEditingController(text: "");
|
final ctrlrContact = TextEditingController(text: "");
|
||||||
final ctrlrGroupName = TextEditingController(text: "");
|
final ctrlrGroupName = TextEditingController(text: "");
|
||||||
|
@ -211,7 +212,42 @@ class _AddContactViewState extends State<AddContactView> {
|
||||||
|
|
||||||
/// TODO Join Group Pane
|
/// TODO Join Group Pane
|
||||||
Widget joinGroupTab() {
|
Widget joinGroupTab() {
|
||||||
return Icon(Icons.group);
|
return Container(
|
||||||
|
margin: EdgeInsets.all(30),
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
child: Form(
|
||||||
|
autovalidateMode: AutovalidateMode.always,
|
||||||
|
key: _joinGroupFormKey,
|
||||||
|
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
CwtchLabel(label: AppLocalizations.of(context).joinGroupTab),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
CwtchTextField(
|
||||||
|
controller: ctrlrContact,
|
||||||
|
validator: (value) {
|
||||||
|
if (value == "") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (globalErrorHandler.importBundleError) {
|
||||||
|
return AppLocalizations.of(context).invalidImportString;
|
||||||
|
} else if (globalErrorHandler.explicitAddContactSuccess) {}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
onChanged: (String importBundle) async {
|
||||||
|
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
|
||||||
|
Provider.of<FlwtchState>(context, listen: false).cwtch.ImportBundle(profileOnion, importBundle);
|
||||||
|
|
||||||
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
|
if (globalErrorHandler.importBundleSuccess) {
|
||||||
|
final snackBar = SnackBar(content: Text(AppLocalizations.of(context).successfullAddedContact + importBundle));
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
])));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO Manage Servers Tab
|
/// TODO Manage Servers Tab
|
||||||
|
|
|
@ -83,7 +83,9 @@ class _MessageViewState extends State<MessageView> {
|
||||||
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).onion, jsonEncode(cm));
|
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).onion, jsonEncode(cm));
|
||||||
ctrlrCompose.clear();
|
ctrlrCompose.clear();
|
||||||
focusNode.requestFocus();
|
focusNode.requestFocus();
|
||||||
Provider.of<ContactInfoState>(context, listen: false).totalMessages++;
|
if (Provider.of<ContactInfoState>(context, listen: false).isGroup == false) {
|
||||||
|
Provider.of<ContactInfoState>(context, listen: false).totalMessages++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildComposeBox() {
|
Widget _buildComposeBox() {
|
||||||
|
|
|
@ -30,8 +30,8 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
badgeTextColor: Provider.of<Settings>(context).theme.portraitContactBadgeTextColor(),
|
badgeTextColor: Provider.of<Settings>(context).theme.portraitContactBadgeTextColor(),
|
||||||
diameter: 64.0,
|
diameter: 64.0,
|
||||||
imagePath: contact.imagePath,
|
imagePath: contact.imagePath,
|
||||||
maskOut: contact.isGroup ? false : contact.status != "Authenticated",
|
maskOut: !contact.isOnline(),
|
||||||
border: contact.status == "Authenticated" ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor()),
|
border: contact.isOnline() ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor()),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
|
@ -47,7 +47,7 @@ class _MessageListState extends State<MessageList> {
|
||||||
controller: ctrlr1,
|
controller: ctrlr1,
|
||||||
child: Container(
|
child: Container(
|
||||||
// Only show broken heart is the contact is offline...
|
// Only show broken heart is the contact is offline...
|
||||||
decoration: Provider.of<ContactInfoState>(outerContext).status == "Authenticated"
|
decoration: Provider.of<ContactInfoState>(outerContext).isOnline()
|
||||||
? null
|
? null
|
||||||
: BoxDecoration(
|
: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
|
|
Loading…
Reference in New Issue