Merge pull request 'store last seen time in lcg and handle unread counts' (#411) from unreadSync into trunk
continuous-integration/drone/push Build is pending Details

Reviewed-on: #411
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
This commit is contained in:
Sarah Jamie Lewis 2022-04-04 22:17:59 +00:00
commit e7c5b2cfa5
8 changed files with 27 additions and 2 deletions

View File

@ -1 +1 @@
2022-03-23-19-06-v1.6.0-13-g1acae32 2022-04-04-17-46-v1.6.0-15-g97defdf

View File

@ -1 +1 @@
2022-03-23-23-06-v1.6.0-13-g1acae32 2022-04-04-21-46-v1.6.0-15-g97defdf

View File

@ -2,6 +2,8 @@
// Details: https://docs.openprivacy.ca/cwtch-security-handbook/profile_encryption_and_storage.html // Details: https://docs.openprivacy.ca/cwtch-security-handbook/profile_encryption_and_storage.html
const DefaultPassword = "be gay do crime"; const DefaultPassword = "be gay do crime";
const LastMessageSeenTimeKey = "profile.lastMessageSeenTime";
abstract class Cwtch { abstract class Cwtch {
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names
Future<void> Start(); Future<void> Start();

View File

@ -18,6 +18,8 @@ import '../config.dart';
import '../errorHandler.dart'; import '../errorHandler.dart';
import '../settings.dart'; import '../settings.dart';
typedef SeenMessageCallback = Function(String, int, DateTime);
// Class that handles libcwtch-go events (received either via ffi with an isolate or gomobile over a method channel from kotlin) // Class that handles libcwtch-go events (received either via ffi with an isolate or gomobile over a method channel from kotlin)
// Takes Notifiers and triggers them on appropriate events // Takes Notifiers and triggers them on appropriate events
class CwtchNotifier { class CwtchNotifier {
@ -32,6 +34,8 @@ class CwtchNotifier {
String? notificationSimple; String? notificationSimple;
String? notificationConversationInfo; String? notificationConversationInfo;
SeenMessageCallback? seenMessageCallback;
CwtchNotifier( CwtchNotifier(
ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN, ServerListState serverListStateCN) { ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN, ServerListState serverListStateCN) {
profileCN = pcn; profileCN = pcn;
@ -48,6 +52,10 @@ class CwtchNotifier {
this.notificationConversationInfo = notificationConversationInfo; this.notificationConversationInfo = notificationConversationInfo;
} }
void setMessageSeenCallback(SeenMessageCallback callback) {
seenMessageCallback = callback;
}
void handleMessage(String type, dynamic data) { void handleMessage(String type, dynamic data) {
//EnvironmentConfig.debugLog("NewEvent $type $data"); //EnvironmentConfig.debugLog("NewEvent $type $data");
switch (type) { switch (type) {
@ -164,6 +172,10 @@ class CwtchNotifier {
var selectedConversation = selectedProfile && appState.selectedConversation == identifier; var selectedConversation = selectedProfile && appState.selectedConversation == identifier;
var notification = data["notification"]; var notification = data["notification"];
if (selectedConversation && seenMessageCallback != null) {
seenMessageCallback!(data["ProfileOnion"]!, identifier, DateTime.now().toUtc());
}
if (notification == "SimpleEvent") { if (notification == "SimpleEvent") {
notificationManager.notify(notificationSimple ?? "New Message", "", 0); notificationManager.notify(notificationSimple ?? "New Message", "", 0);
} else if (notification == "ContactInfo") { } else if (notification == "ContactInfo") {
@ -229,6 +241,9 @@ class CwtchNotifier {
// and ensure that malicious contacts in groups can only set this timestamp to a value within the range of `last seen message time` // and ensure that malicious contacts in groups can only set this timestamp to a value within the range of `last seen message time`
// and `local now`. // and `local now`.
profileCN.getProfile(data["ProfileOnion"])?.newMessage(identifier, idx, timestampSent, senderHandle, senderImage, isAuto, data["Data"], contenthash, selectedProfile, selectedConversation); profileCN.getProfile(data["ProfileOnion"])?.newMessage(identifier, idx, timestampSent, senderHandle, senderImage, isAuto, data["Data"], contenthash, selectedProfile, selectedConversation);
if (selectedConversation && seenMessageCallback != null) {
seenMessageCallback!(data["ProfileOnion"]!, identifier, DateTime.now().toUtc());
}
if (notification == "SimpleEvent") { if (notification == "SimpleEvent") {
notificationManager.notify(notificationSimple ?? "New Message", "", 0); notificationManager.notify(notificationSimple ?? "New Message", "", 0);

View File

@ -133,6 +133,7 @@ class CwtchFfi implements Cwtch {
} }
library = DynamicLibrary.open(libraryPath); library = DynamicLibrary.open(libraryPath);
cwtchNotifier = _cwtchNotifier; cwtchNotifier = _cwtchNotifier;
cwtchNotifier.setMessageSeenCallback((String profile, int conversation, DateTime time) => {this.SetConversationAttribute(profile, conversation, LastMessageSeenTimeKey, time.toIso8601String())});
} }
// ignore: non_constant_identifier_names // ignore: non_constant_identifier_names

View File

@ -35,6 +35,7 @@ class CwtchGomobile implements Cwtch {
CwtchGomobile(CwtchNotifier _cwtchNotifier) { CwtchGomobile(CwtchNotifier _cwtchNotifier) {
print("gomobile.dart: CwtchGomobile()"); print("gomobile.dart: CwtchGomobile()");
cwtchNotifier = _cwtchNotifier; cwtchNotifier = _cwtchNotifier;
cwtchNotifier.setMessageSeenCallback((String profile, int conversation, DateTime time) => {this.SetConversationAttribute(profile, conversation, LastMessageSeenTimeKey, time.toIso8601String())});
androidHomeDirectory = getApplicationDocumentsDirectory(); androidHomeDirectory = getApplicationDocumentsDirectory();
androidLibraryDir = appInfoPlatform.invokeMethod('getNativeLibDir'); androidLibraryDir = appInfoPlatform.invokeMethod('getNativeLibDir');

View File

@ -50,6 +50,7 @@ class ProfileInfoState extends ChangeNotifier {
List<dynamic> contacts = jsonDecode(contactsJson); List<dynamic> contacts = jsonDecode(contactsJson);
this._contacts.addAll(contacts.map((contact) { this._contacts.addAll(contacts.map((contact) {
this._unreadMessages += contact["numUnread"] as int;
return ContactInfoState(this.onion, contact["identifier"], contact["onion"], return ContactInfoState(this.onion, contact["identifier"], contact["onion"],
nickname: contact["name"], nickname: contact["name"],
status: contact["status"], status: contact["status"],
@ -164,12 +165,14 @@ class ProfileInfoState extends ChangeNotifier {
this._nickname = name; this._nickname = name;
this._imagePath = picture; this._imagePath = picture;
this._online = online; this._online = online;
this._unreadMessages = 0;
this.replaceServers(serverJson); this.replaceServers(serverJson);
if (contactsJson != null && contactsJson != "" && contactsJson != "null") { if (contactsJson != null && contactsJson != "" && contactsJson != "null") {
List<dynamic> contacts = jsonDecode(contactsJson); List<dynamic> contacts = jsonDecode(contactsJson);
contacts.forEach((contact) { contacts.forEach((contact) {
var profileContact = this._contacts.getContact(contact["identifier"]); var profileContact = this._contacts.getContact(contact["identifier"]);
this._unreadMessages += contact["numUnread"] as int;
if (profileContact != null) { if (profileContact != null) {
profileContact.status = contact["status"]; profileContact.status = contact["status"];
profileContact.totalMessages = contact["numMessages"]; // Todo: trigger cache update (bulk upload) profileContact.totalMessages = contact["numMessages"]; // Todo: trigger cache update (bulk upload)

View File

@ -1,3 +1,4 @@
import 'package:cwtch/cwtch/cwtch.dart';
import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/appstate.dart';
import 'package:cwtch/models/contact.dart'; import 'package:cwtch/models/contact.dart';
@ -38,6 +39,8 @@ void selectConversation(BuildContext context, int handle) {
// if in singlepane mode, push to the stack // if in singlepane mode, push to the stack
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context); var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle); if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle);
// Set last message seen time in backend
Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(Provider.of<ProfileInfoState>(context, listen: false).onion, handle, LastMessageSeenTimeKey, DateTime.now().toUtc().toIso8601String());
} }
void _pushMessageView(BuildContext context, int handle) { void _pushMessageView(BuildContext context, int handle) {