2021-06-24 23:10:45 +00:00
import ' dart:convert ' ;
2023-09-24 17:28:06 +00:00
import ' package:cwtch/cwtch/cwtch.dart ' ;
2021-12-09 00:41:01 +00:00
import ' package:cwtch/main.dart ' ;
2022-01-18 21:26:52 +00:00
import ' package:cwtch/models/appstate.dart ' ;
import ' package:cwtch/models/contact.dart ' ;
import ' package:cwtch/models/profilelist.dart ' ;
2022-01-27 01:03:48 +00:00
import ' package:cwtch/models/remoteserver.dart ' ;
2021-06-24 23:10:45 +00:00
import ' package:cwtch/models/servers.dart ' ;
import ' package:cwtch/notification_manager.dart ' ;
import ' package:cwtch/torstatus.dart ' ;
2021-06-25 05:30:46 +00:00
import ' ../config.dart ' ;
2021-06-24 23:10:45 +00:00
import ' ../errorHandler.dart ' ;
import ' ../settings.dart ' ;
2022-04-01 22:54:06 +00:00
typedef SeenMessageCallback = Function ( String , int , DateTime ) ;
2021-06-24 23:10:45 +00:00
// 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
class CwtchNotifier {
late ProfileListState profileCN ;
late Settings settings ;
late ErrorHandler error ;
late TorStatus torStatus ;
late NotificationsManager notificationManager ;
late AppState appState ;
2021-10-29 23:37:02 +00:00
late ServerListState serverListState ;
2023-04-04 20:58:42 +00:00
late FlwtchState flwtchState ;
2021-06-24 23:10:45 +00:00
2022-02-08 21:54:17 +00:00
String ? notificationSimple ;
String ? notificationConversationInfo ;
2022-04-01 22:54:06 +00:00
SeenMessageCallback ? seenMessageCallback ;
2023-04-04 20:58:42 +00:00
CwtchNotifier ( ProfileListState pcn , Settings settingsCN , ErrorHandler errorCN , TorStatus torStatusCN , NotificationsManager notificationManagerP , AppState appStateCN ,
ServerListState serverListStateCN , FlwtchState flwtchStateCN ) {
2021-06-24 23:10:45 +00:00
profileCN = pcn ;
settings = settingsCN ;
error = errorCN ;
torStatus = torStatusCN ;
notificationManager = notificationManagerP ;
appState = appStateCN ;
2021-10-29 23:37:02 +00:00
serverListState = serverListStateCN ;
2023-04-04 20:58:42 +00:00
flwtchState = flwtchStateCN ;
2021-06-24 23:10:45 +00:00
}
2022-02-08 21:54:17 +00:00
void l10nInit ( String notificationSimple , String notificationConversationInfo ) {
this . notificationSimple = notificationSimple ;
this . notificationConversationInfo = notificationConversationInfo ;
}
2022-04-01 22:54:06 +00:00
void setMessageSeenCallback ( SeenMessageCallback callback ) {
seenMessageCallback = callback ;
}
2021-06-24 23:10:45 +00:00
void handleMessage ( String type , dynamic data ) {
2023-08-02 16:49:36 +00:00
// EnvironmentConfig.debugLog("NewEvent $type $data");
2021-06-24 23:10:45 +00:00
switch ( type ) {
case " CwtchStarted " :
2024-02-27 02:26:49 +00:00
if ( data [ " Reload " ] = = " true " & & profileCN . num > 0 ) {
2024-02-27 02:19:20 +00:00
// don't reload...
// unless we have loaded no profiles...then there isnt a risk and this
// might be a first time (e.g. new apk, existing service)
} else {
flwtchState . cwtch . LoadProfiles ( DefaultPassword ) ;
}
2024-02-27 02:26:49 +00:00
2021-06-24 23:10:45 +00:00
appState . SetCwtchInit ( ) ;
break ;
case " CwtchStartError " :
appState . SetAppError ( data [ " Error " ] ) ;
break ;
case " NewPeer " :
2023-03-16 23:37:38 +00:00
// EnvironmentConfig.debugLog("NewPeer $data");
2021-06-24 23:10:45 +00:00
// if tag != v1-defaultPassword then it is either encrypted OR it is an unencrypted account created during pre-beta...
2023-09-14 01:38:08 +00:00
profileCN . add (
data [ " Identity " ] ,
data [ " name " ] ,
data [ " picture " ] ,
data [ " defaultPicture " ] ,
data [ " ContactsJson " ] ,
data [ " ServerList " ] ,
data [ " Online " ] = = " true " ,
data [ " autostart " ] = = " true " ,
data [ " tag " ] ! = " v1-defaultPassword " ,
data [ " appearOffline " ] = = " true " ,
) ;
2023-04-04 20:58:42 +00:00
// Update Profile Attributes
2023-05-18 18:15:13 +00:00
EnvironmentConfig . debugLog ( " Looking up Profile Attributes ${ data [ " Identity " ] } ${ profileCN . getProfile ( data [ " Identity " ] ) } " ) ;
flwtchState . cwtch . GetProfileAttribute ( data [ " Identity " ] , " profile.profile-attribute-1 " ) . then ( ( value ) = > profileCN . getProfile ( data [ " Identity " ] ) ? . setAttribute ( 0 , value ) ) ;
flwtchState . cwtch . GetProfileAttribute ( data [ " Identity " ] , " profile.profile-attribute-2 " ) . then ( ( value ) = > profileCN . getProfile ( data [ " Identity " ] ) ? . setAttribute ( 1 , value ) ) ;
flwtchState . cwtch . GetProfileAttribute ( data [ " Identity " ] , " profile.profile-attribute-3 " ) . then ( ( value ) = > profileCN . getProfile ( data [ " Identity " ] ) ? . setAttribute ( 2 , value ) ) ;
flwtchState . cwtch . GetProfileAttribute ( data [ " Identity " ] , " profile.profile-status " ) . then ( ( value ) = > profileCN . getProfile ( data [ " Identity " ] ) ? . setAvailabilityStatus ( value ? ? " " ) ) ;
EnvironmentConfig . debugLog ( " Looking up Profile Information for Contact... " ) ;
2023-04-04 20:58:42 +00:00
profileCN . getProfile ( data [ " Identity " ] ) ? . contactList . contacts . forEach ( ( contact ) {
2023-05-18 18:15:13 +00:00
flwtchState . cwtch . GetConversationAttribute ( data [ " Identity " ] , contact . identifier , " public.profile.profile-attribute-1 " ) . then ( ( value ) = > contact . setAttribute ( 0 , value ) ) ;
flwtchState . cwtch . GetConversationAttribute ( data [ " Identity " ] , contact . identifier , " public.profile.profile-attribute-2 " ) . then ( ( value ) = > contact . setAttribute ( 1 , value ) ) ;
flwtchState . cwtch . GetConversationAttribute ( data [ " Identity " ] , contact . identifier , " public.profile.profile-attribute-3 " ) . then ( ( value ) = > contact . setAttribute ( 2 , value ) ) ;
flwtchState . cwtch . GetConversationAttribute ( data [ " Identity " ] , contact . identifier , " public.profile.profile-status " ) . then ( ( value ) = > contact . setAvailabilityStatus ( value ? ? " " ) ) ;
2023-04-04 20:58:42 +00:00
} ) ;
2021-06-24 23:10:45 +00:00
break ;
2021-11-18 23:44:54 +00:00
case " ContactCreated " :
2022-01-06 21:54:25 +00:00
EnvironmentConfig . debugLog ( " ContactCreated $ data " ) ;
2022-02-08 02:13:49 +00:00
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . add ( ContactInfoState ( data [ " ProfileOnion " ] , int . parse ( data [ " ConversationID " ] ) , data [ " RemotePeer " ] ,
nickname: data [ " nick " ] ,
status: data [ " status " ] ,
imagePath: data [ " picture " ] ,
defaultImagePath: data [ " defaultPicture " ] ,
blocked: data [ " blocked " ] = = " true " ,
accepted: data [ " accepted " ] = = " true " ,
savePeerHistory: data [ " saveConversationHistory " ] = = null ? " DeleteHistoryConfirmed " : data [ " saveConversationHistory " ] ,
numMessages: int . parse ( data [ " numMessages " ] ) ,
numUnread: int . parse ( data [ " unread " ] ) ,
2022-02-08 23:21:06 +00:00
isGroup: false , // by definition
2022-02-08 02:13:49 +00:00
server: null ,
archived: false ,
2022-02-08 23:21:06 +00:00
lastMessageTime: DateTime . now ( ) , //show at the top of the contact list even if no messages yet
2022-02-08 02:13:49 +00:00
notificationPolicy: data [ " notificationPolicy " ] ? ? " ConversationNotificationPolicy.Default " ) ) ;
2021-06-24 23:10:45 +00:00
break ;
2021-10-29 23:37:02 +00:00
case " NewServer " :
2021-11-02 02:29:58 +00:00
EnvironmentConfig . debugLog ( " NewServer $ data " ) ;
2021-11-25 23:59:54 +00:00
serverListState . add ( data [ " Onion " ] , data [ " ServerBundle " ] , data [ " Running " ] = = " true " , data [ " Description " ] , data [ " Autostart " ] = = " true " , data [ " StorageType " ] = = " storage-password " ) ;
2021-11-02 02:29:58 +00:00
break ;
case " ServerIntentUpdate " :
EnvironmentConfig . debugLog ( " ServerIntentUpdate $ data " ) ;
var server = serverListState . getServer ( data [ " Identity " ] ) ;
if ( server ! = null ) {
server . setRunning ( data [ " Intent " ] = = " running " ) ;
}
2021-10-29 23:37:02 +00:00
break ;
2021-11-26 02:13:36 +00:00
case " ServerStatsUpdate " :
EnvironmentConfig . debugLog ( " ServerStatsUpdate $ data " ) ;
var totalMessages = int . parse ( data [ " TotalMessages " ] ) ;
var connections = int . parse ( data [ " Connections " ] ) ;
serverListState . updateServerStats ( data [ " Identity " ] , totalMessages , connections ) ;
break ;
2021-06-24 23:10:45 +00:00
case " GroupCreated " :
// Retrieve Server Status from Cache...
String status = " " ;
2021-10-29 23:37:02 +00:00
RemoteServerInfoState ? serverInfoState = profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . serverList . getServer ( data [ " GroupServer " ] ) ;
2021-06-24 23:10:45 +00:00
if ( serverInfoState ! = null ) {
status = serverInfoState . status ;
}
2021-12-16 18:36:14 +00:00
if ( profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . getContact ( int . parse ( data [ " ConversationID " ] ) ) = = null ) {
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . add ( ContactInfoState ( data [ " ProfileOnion " ] , int . parse ( data [ " ConversationID " ] ) , data [ " GroupID " ] ,
2022-02-08 23:21:06 +00:00
blocked: false , // we created
accepted: true , // we created
2022-02-05 00:57:31 +00:00
imagePath: data [ " picture " ] ,
2022-02-07 22:26:14 +00:00
defaultImagePath: data [ " picture " ] ,
2021-07-13 21:46:47 +00:00
nickname: data [ " GroupName " ] ,
status: status ,
server: data [ " GroupServer " ] ,
isGroup: true ,
2022-02-04 22:19:02 +00:00
lastMessageTime: DateTime . now ( ) ,
2022-02-08 02:13:49 +00:00
notificationPolicy: data [ " notificationPolicy " ] ? ? " ConversationNotificationPolicy.Default " ) ) ;
2023-09-26 20:26:52 +00:00
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . updateLastMessageReceivedTime ( int . parse ( data [ " ConversationID " ] ) , DateTime . now ( ) ) ;
2021-06-24 23:10:45 +00:00
}
break ;
case " PeerDeleted " :
profileCN . delete ( data [ " Identity " ] ) ;
// todo standarize
error . handleUpdate ( " deleteprofile.success " ) ;
break ;
2021-11-04 01:56:53 +00:00
case " ServerDeleted " :
error . handleUpdate ( " deletedserver. " + data [ " Status " ] ) ;
if ( data [ " Status " ] = = " success " ) {
serverListState . delete ( data [ " Identity " ] ) ;
}
break ;
2021-06-24 23:10:45 +00:00
case " DeleteContact " :
2023-06-14 17:52:31 +00:00
var identifier = int . parse ( data [ " ConversationID " ] ) ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . removeContact ( identifier ) ;
2021-06-24 23:10:45 +00:00
break ;
case " PeerStateChange " :
2021-11-18 23:44:54 +00:00
ContactInfoState ? contact = profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . findContact ( data [ " RemotePeer " ] ) ;
2021-06-24 23:10:45 +00:00
if ( contact ! = null ) {
if ( data [ " ConnectionState " ] ! = null ) {
contact . status = data [ " ConnectionState " ] ;
}
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . resort ( ) ;
}
break ;
case " NewMessageFromPeer " :
2021-11-18 23:44:54 +00:00
var identifier = int . parse ( data [ " ConversationID " ] ) ;
2021-12-06 20:25:17 +00:00
var messageID = int . parse ( data [ " Index " ] ) ;
var timestamp = DateTime . tryParse ( data [ ' TimestampReceived ' ] ) ! ;
var senderHandle = data [ ' RemotePeer ' ] ;
2022-02-05 00:57:31 +00:00
var senderImage = data [ ' picture ' ] ;
2021-12-17 01:04:29 +00:00
var isAuto = data [ ' Auto ' ] = = " true " ;
2022-03-23 23:08:19 +00:00
String contenthash = data [ ' ContentHash ' ] ;
2024-02-27 02:19:20 +00:00
2022-01-25 22:37:51 +00:00
var selectedProfile = appState . selectedProfile = = data [ " ProfileOnion " ] ;
var selectedConversation = selectedProfile & & appState . selectedConversation = = identifier ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . newMessage (
2022-01-20 18:37:09 +00:00
identifier ,
messageID ,
timestamp ,
senderHandle ,
senderImage ,
isAuto ,
data [ " Data " ] ,
contenthash ,
2022-01-25 22:37:51 +00:00
selectedProfile ,
2022-01-20 18:37:09 +00:00
selectedConversation ,
) ;
2024-02-27 02:19:20 +00:00
// Now perform the notification logic...
var notification = data [ " notification " ] ;
if ( selectedConversation & & seenMessageCallback ! = null ) {
seenMessageCallback ! ( data [ " ProfileOnion " ] ! , identifier , DateTime . now ( ) . toUtc ( ) ) ;
}
if ( notification = = " SimpleEvent " ) {
notificationManager . notify ( notificationSimple ? ? " New Message " , " " , 0 ) ;
} else if ( notification = = " ContactInfo " ) {
var contact = profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . getContact ( identifier ) ;
notificationManager . notify ( ( notificationConversationInfo ? ? " New Message from %1 " ) . replaceFirst ( " %1 " , ( contact ? . nickname ? ? senderHandle . toString ( ) ) ) , data [ " ProfileOnion " ] , identifier ) ;
}
2022-01-25 22:37:51 +00:00
appState . notifyProfileUnread ( ) ;
2021-06-24 23:10:45 +00:00
break ;
case " PeerAcknowledgement " :
// We don't use these anymore, IndexedAcknowledgement is more suited to the UI front end...
break ;
case " IndexedAcknowledgement " :
2021-12-03 19:28:10 +00:00
var conversation = int . parse ( data [ " ConversationID " ] ) ;
2021-12-06 20:25:17 +00:00
var messageID = int . parse ( data [ " Index " ] ) ;
2022-03-04 19:30:19 +00:00
// 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 ( conversation ) ! . isOnline ( ) = = false ) {
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . getContact ( conversation ) ! . status = " Authenticated " ;
2021-06-24 23:10:45 +00:00
}
2022-03-04 19:30:19 +00:00
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . getContact ( conversation ) ! . ackCache ( messageID ) ;
2021-06-24 23:10:45 +00:00
break ;
case " NewMessageFromGroup " :
2021-11-18 23:44:54 +00:00
var identifier = int . parse ( data [ " ConversationID " ] ) ;
2021-06-24 23:10:45 +00:00
if ( data [ " ProfileOnion " ] ! = data [ " RemotePeer " ] ) {
2021-11-01 22:03:05 +00:00
var idx = int . parse ( data [ " Index " ] ) ;
2021-12-06 20:25:17 +00:00
var senderHandle = data [ ' RemotePeer ' ] ;
2022-02-05 00:57:31 +00:00
var senderImage = data [ ' picture ' ] ;
2021-12-06 20:25:17 +00:00
var timestampSent = DateTime . tryParse ( data [ ' TimestampSent ' ] ) ! ;
2022-01-27 01:03:48 +00:00
var contact = profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . getContact ( identifier ) ;
var currentTotal = contact ! . totalMessages ;
2021-12-17 01:04:29 +00:00
var isAuto = data [ ' Auto ' ] = = " true " ;
2022-03-23 23:08:19 +00:00
String contenthash = data [ ' ContentHash ' ] ;
2022-01-25 22:37:51 +00:00
var selectedProfile = appState . selectedProfile = = data [ " ProfileOnion " ] ;
var selectedConversation = selectedProfile & & appState . selectedConversation = = identifier ;
2022-02-04 22:19:02 +00:00
var notification = data [ " notification " ] ;
2022-01-20 14:13:54 +00:00
2021-11-01 22:03:05 +00:00
// Only bother to do anything if we know about the group and the provided index is greater than our current total...
2024-02-14 04:02:33 +00:00
if ( idx > = currentTotal ) {
2021-11-02 20:47:35 +00:00
// TODO: There are 2 timestamps associated with a new group message - time sent and time received.
// Sent refers to the time a profile alleges they sent a message
// Received refers to the time we actually saw the message from the server
// These can obviously be very different for legitimate reasons.
// We also maintain a relative hash-link through PreviousMessageSignature which is the ground truth for
// order.
// In the future we will want to combine these 3 ordering mechanisms into a cohesive view of the timeline
// For now we perform some minimal checks on the sent timestamp to use to provide a useful ordering for honest contacts
// 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`.
2022-01-25 22:37:51 +00:00
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . newMessage ( identifier , idx , timestampSent , senderHandle , senderImage , isAuto , data [ " Data " ] , contenthash , selectedProfile , selectedConversation ) ;
2022-04-01 22:54:06 +00:00
if ( selectedConversation & & seenMessageCallback ! = null ) {
seenMessageCallback ! ( data [ " ProfileOnion " ] ! , identifier , DateTime . now ( ) . toUtc ( ) ) ;
}
2022-01-20 14:13:54 +00:00
2022-02-04 22:19:02 +00:00
if ( notification = = " SimpleEvent " ) {
2022-02-18 23:50:34 +00:00
notificationManager . notify ( notificationSimple ? ? " New Message " , " " , 0 ) ;
2022-02-04 22:19:02 +00:00
} else if ( notification = = " ContactInfo " ) {
var contact = profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . getContact ( identifier ) ;
2022-02-18 23:50:34 +00:00
notificationManager . notify ( ( notificationConversationInfo ? ? " New Message from %1 " ) . replaceFirst ( " %1 " , ( contact ? . nickname ? ? senderHandle . toString ( ) ) ) , data [ " ProfileOnion " ] , identifier ) ;
2022-02-04 22:19:02 +00:00
}
2022-01-25 22:37:51 +00:00
appState . notifyProfileUnread ( ) ;
2021-06-24 23:10:45 +00:00
}
2022-02-05 00:37:25 +00:00
RemoteServerInfoState ? server = profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . serverList . getServer ( contact . server ? ? " " ) ;
2022-01-27 01:03:48 +00:00
server ? . updateSyncProgressFor ( timestampSent ) ;
2021-06-24 23:10:45 +00:00
} else {
2021-12-10 19:51:19 +00:00
// This is dealt with by IndexedAcknowledgment
EnvironmentConfig . debugLog ( " new message from group from yourself - this should not happen " ) ;
2021-11-01 22:03:05 +00:00
}
2021-06-24 23:10:45 +00:00
break ;
case " IndexedFailure " :
2021-12-19 20:44:04 +00:00
var identifier = int . parse ( data [ " ConversationID " ] ) ;
var contact = profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . getContact ( identifier ) ;
2022-03-04 19:30:19 +00:00
var messageID = int . parse ( data [ " Index " ] ) ;
contact ! . errCache ( messageID ) ;
2021-06-24 23:10:45 +00:00
break ;
case " AppError " :
2021-06-25 05:30:46 +00:00
EnvironmentConfig . debugLog ( " New App Error: $ data " ) ;
2021-06-24 23:10:45 +00:00
// special case for delete error (todo: standardize cwtch errors)
if ( data [ " Error " ] = = " Password did not match " ) {
error . handleUpdate ( " deleteprofile.error " ) ;
} else if ( data [ " Data " ] ! = null ) {
error . handleUpdate ( data [ " Data " ] ) ;
}
break ;
case " UpdateGlobalSettings " :
settings . handleUpdate ( jsonDecode ( data [ " Data " ] ) ) ;
2024-04-04 17:52:53 +00:00
appState . settingsLoaded = true ;
2021-06-24 23:10:45 +00:00
break ;
2021-11-25 23:59:54 +00:00
case " UpdatedProfileAttribute " :
if ( data [ " Key " ] = = " public.profile.name " ) {
2021-06-24 23:10:45 +00:00
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . nickname = data [ " Data " ] ;
2022-02-07 23:35:01 +00:00
} else if ( data [ " Key " ] . toString ( ) . startsWith ( " local.filesharing. " ) ) {
if ( data [ " Key " ] . toString ( ) . endsWith ( " .path " ) ) {
// local.conversation.filekey.path
List < String > keyparts = data [ " Key " ] . toString ( ) . split ( " . " ) ;
if ( keyparts . length = = 5 ) {
String filekey = keyparts [ 2 ] + " . " + keyparts [ 3 ] ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . downloadSetPathForSender ( filekey , data [ " Data " ] ) ;
}
2022-01-20 22:19:06 +00:00
}
2023-04-04 20:58:42 +00:00
} else if ( data [ " Key " ] . toString ( ) . startsWith ( " public.profile.profile-attribute " ) ) {
// ignore these events...
} else if ( data [ " Key " ] . toString ( ) . startsWith ( " public.profile.profile-status " ) ) {
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . setAvailabilityStatus ( data [ " Data " ] ) ;
2021-06-24 23:10:45 +00:00
} else {
2021-06-25 05:30:46 +00:00
EnvironmentConfig . debugLog ( " unhandled set attribute event: ${ data [ ' Key ' ] } " ) ;
2021-06-24 23:10:45 +00:00
}
break ;
case " NetworkError " :
var isOnline = data [ " Status " ] = = " Success " ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . isOnline = isOnline ;
break ;
case " ACNStatus " :
2021-06-25 05:30:46 +00:00
EnvironmentConfig . debugLog ( " acn status: $ data " ) ;
2021-06-24 23:10:45 +00:00
torStatus . handleUpdate ( int . parse ( data [ " Progress " ] ) , data [ " Status " ] ) ;
break ;
case " ACNVersion " :
2021-06-25 05:30:46 +00:00
EnvironmentConfig . debugLog ( " acn version: $ data " ) ;
2021-06-24 23:10:45 +00:00
torStatus . updateVersion ( data [ " Data " ] ) ;
break ;
case " UpdateServerInfo " :
2023-08-02 16:43:27 +00:00
EnvironmentConfig . debugLog ( " NewEvent UpdateServerInfo $ type $ data " ) ;
2021-06-24 23:10:45 +00:00
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . replaceServers ( data [ " ServerList " ] ) ;
break ;
2022-09-09 19:23:08 +00:00
case " TokenManagerInfo " :
2022-12-06 20:11:55 +00:00
try {
List < dynamic > associatedGroups = jsonDecode ( data [ " Data " ] ) ;
int count = int . parse ( data [ " ServerTokenCount " ] ) ;
associatedGroups . forEach ( ( identifier ) {
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . getContact ( int . parse ( identifier . toString ( ) ) ) ! . antispamTickets = count ;
} ) ;
2024-02-14 04:02:33 +00:00
EnvironmentConfig . debugLog ( " update server token count for $ associatedGroups , $ count " ) ;
2022-12-06 20:11:55 +00:00
} catch ( e ) {
// No tokens in data...
}
2022-09-09 19:23:08 +00:00
break ;
2021-06-24 23:10:45 +00:00
case " NewGroup " :
String invite = data [ " GroupInvite " ] . toString ( ) ;
if ( invite . startsWith ( " torv3 " ) ) {
String inviteJson = new String . fromCharCodes ( base64Decode ( invite . substring ( 5 ) ) ) ;
dynamic groupInvite = jsonDecode ( inviteJson ) ;
// Retrieve Server Status from Cache...
String status = " " ;
2021-10-29 23:37:02 +00:00
RemoteServerInfoState ? serverInfoState = profileCN . getProfile ( data [ " ProfileOnion " ] ) ! . serverList . getServer ( groupInvite [ " ServerHost " ] ) ;
2021-06-24 23:10:45 +00:00
if ( serverInfoState ! = null ) {
status = serverInfoState . status ;
}
2021-11-18 23:44:54 +00:00
if ( profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . findContact ( groupInvite [ " GroupID " ] ) = = null ) {
2021-12-01 12:17:48 +00:00
var identifier = int . parse ( data [ " ConversationID " ] ) ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . add ( ContactInfoState ( data [ " ProfileOnion " ] , identifier , groupInvite [ " GroupID " ] ,
2022-02-08 23:21:06 +00:00
blocked: false , // NewGroup only issued on accepting invite
accepted: true , // NewGroup only issued on accepting invite
2022-02-05 00:57:31 +00:00
imagePath: data [ " picture " ] ,
2021-06-24 23:10:45 +00:00
nickname: groupInvite [ " GroupName " ] ,
server: groupInvite [ " ServerHost " ] ,
status: status ,
isGroup: true ,
2022-03-04 01:00:36 +00:00
lastMessageTime: DateTime . now ( ) ) ) ;
2023-09-26 19:50:02 +00:00
2023-09-26 20:26:52 +00:00
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . updateLastMessageReceivedTime ( identifier , DateTime . fromMillisecondsSinceEpoch ( 0 ) ) ;
2021-06-24 23:10:45 +00:00
}
2023-09-26 19:50:02 +00:00
// request a new server update...
// NOTE: In the future this should also update the TokenManagerInfo
// This is not currently communicated by ServerUpdateInfo (but it probably should be)git
flwtchState . cwtch . PublishServerUpdate ( data [ " ProfileOnion " ] ) ;
2021-06-24 23:10:45 +00:00
}
break ;
case " ServerStateChange " :
// Update the Server Cache
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . updateServerStatusCache ( data [ " GroupServer " ] , data [ " ConnectionState " ] ) ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . contacts . forEach ( ( contact ) {
if ( contact . isGroup = = true & & contact . server = = data [ " GroupServer " ] ) {
contact . status = data [ " ConnectionState " ] ;
}
} ) ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . resort ( ) ;
break ;
2023-04-20 22:48:00 +00:00
case " UpdatedConversationAttribute " :
if ( data [ " Path " ] = = " profile.name " ) {
2021-12-01 12:17:48 +00:00
if ( data [ " Data " ] . toString ( ) . trim ( ) . length > 0 ) {
// Update locally on the UI...
if ( profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . findContact ( data [ " RemotePeer " ] ) ! = null ) {
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . findContact ( data [ " RemotePeer " ] ) ! . nickname = data [ " Data " ] ;
}
2021-06-24 23:10:45 +00:00
}
2022-02-08 21:59:24 +00:00
} else if ( data [ ' Path ' ] = = " profile.custom-profile-image " ) {
2023-04-20 22:48:00 +00:00
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 ) {
2023-05-18 18:15:13 +00:00
EnvironmentConfig . debugLog ( " waiting for download from $ contact " ) ;
2023-04-20 22:48:00 +00:00
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . waitForDownloadComplete ( contact . identifier , fileKey ) ;
2022-02-05 00:57:31 +00:00
}
2023-04-04 20:58:42 +00:00
} else if ( data [ ' Path ' ] = = " profile.profile-attribute-1 " | | data [ ' Path ' ] = = " profile.profile-attribute-2 " | | data [ ' Path ' ] = = " profile.profile-attribute-3 " ) {
2023-04-20 22:48:00 +00:00
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 ;
2023-04-04 20:58:42 +00:00
}
}
} else if ( data [ ' Path ' ] = = " profile.profile-status " ) {
2023-04-20 22:48:00 +00:00
var contact = profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . findContact ( data [ " RemotePeer " ] ) ;
if ( contact ! = null ) {
contact . setAvailabilityStatus ( data [ ' Data ' ] ) ;
2023-04-04 20:58:42 +00:00
}
2021-06-24 23:10:45 +00:00
} else {
2021-12-01 12:17:48 +00:00
EnvironmentConfig . debugLog ( " unhandled ret val event: ${ data [ ' Path ' ] } " ) ;
2021-06-24 23:10:45 +00:00
}
break ;
2021-12-14 21:33:30 +00:00
case " ManifestSizeReceived " :
if ( ! profileCN . getProfile ( data [ " ProfileOnion " ] ) ! . downloadActive ( data [ " FileKey " ] ) ) {
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . downloadUpdate ( data [ " FileKey " ] , 0 , 1 ) ;
}
break ;
2021-09-21 21:57:40 +00:00
case " ManifestSaved " :
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . downloadMarkManifest ( data [ " FileKey " ] ) ;
break ;
case " FileDownloadProgressUpdate " :
2021-11-04 22:31:50 +00:00
var progress = int . parse ( data [ " Progress " ] ) ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . downloadUpdate ( data [ " FileKey " ] , progress , int . parse ( data [ " FileSizeInChunks " ] ) ) ;
// progress == -1 is a "download was interrupted" message and should contain a path
if ( progress < 0 ) {
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . downloadSetPath ( data [ " FileKey " ] , data [ " FilePath " ] ) ;
}
2021-09-21 21:57:40 +00:00
break ;
case " FileDownloaded " :
2021-09-29 20:31:01 +00:00
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . downloadMarkFinished ( data [ " FileKey " ] , data [ " FilePath " ] ) ;
2021-09-21 21:57:40 +00:00
break ;
2021-12-09 00:41:01 +00:00
case " ImportingProfileEvent " :
break ;
2021-12-17 23:23:18 +00:00
case " StartingStorageMigration " :
appState . SetModalState ( ModalState . storageMigration ) ;
break ;
case " DoneStorageMigration " :
appState . SetModalState ( ModalState . none ) ;
break ;
2023-03-27 19:00:49 +00:00
case " BlodeuweddSummary " :
var identifier = int . parse ( data [ " ConversationID " ] ) ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . getContact ( identifier ) ? . updateSummaryEvent ( data [ " Summary " ] ) ;
break ;
case " BlodeuweddTranslation " :
var identifier = int . parse ( data [ " ConversationID " ] ) ;
var mid = int . parse ( data [ " Index " ] ) ;
EnvironmentConfig . debugLog ( " received translation event: $ identifier $ mid $ data " ) ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . getContact ( identifier ) ? . updateTranslationEvent ( mid , data [ " Translation " ] ) ;
break ;
2022-01-13 23:21:09 +00:00
case " ACNInfo " :
2022-01-17 22:52:15 +00:00
var key = data [ " Key " ] ;
2022-01-13 23:21:09 +00:00
var handle = data [ " Handle " ] ;
2022-01-17 22:52:15 +00:00
if ( key = = " circuit " ) {
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . contactList . findContact ( handle ) ? . acnCircuit = data [ " Data " ] ;
}
2022-01-13 23:21:09 +00:00
break ;
2023-08-02 16:43:27 +00:00
case " SearchResult " :
String searchID = data [ " SearchID " ] ;
var conversationIdentifier = int . parse ( data [ " ConversationID " ] ) ;
var messageIndex = int . parse ( data [ " RowIndex " ] ) ;
profileCN . getProfile ( data [ " ProfileOnion " ] ) ? . handleSearchResult ( searchID , conversationIdentifier , messageIndex ) ;
break ;
2021-06-24 23:10:45 +00:00
default :
2021-06-25 05:30:46 +00:00
EnvironmentConfig . debugLog ( " unhandled event: $ type " ) ;
2021-06-24 23:10:45 +00:00
}
}
}