2021-07-06 19:46:39 +00:00
import ' dart:convert ' ;
2021-11-26 22:25:21 +00:00
import ' package:cwtch/config.dart ' ;
2021-07-06 19:46:39 +00:00
import ' package:flutter/widgets.dart ' ;
import ' package:provider/provider.dart ' ;
import ' ../main.dart ' ;
2021-07-09 22:16:06 +00:00
import ' ../model.dart ' ;
2021-09-21 21:57:40 +00:00
import ' messages/filemessage.dart ' ;
2021-07-06 19:46:39 +00:00
import ' messages/invitemessage.dart ' ;
import ' messages/malformedmessage.dart ' ;
import ' messages/quotedmessage.dart ' ;
import ' messages/textmessage.dart ' ;
2021-07-07 17:05:25 +00:00
// Define the overlays
const TextMessageOverlay = 1 ;
const QuotedMessageOverlay = 10 ;
const SuggestContactOverlay = 100 ;
const InviteGroupOverlay = 101 ;
2021-09-21 21:57:40 +00:00
const FileShareOverlay = 200 ;
2021-07-07 17:05:25 +00:00
// Defines the length of the tor v3 onion address. Code using this constant will
// need to updated when we allow multiple different identifiers. At which time
// it will likely be prudent to define a proper Contact wrapper.
const TorV3ContactHandleLength = 56 ;
2021-07-07 18:31:16 +00:00
// Defines the length of a Cwtch v2 Group.
const GroupConversationHandleLength = 32 ;
2021-07-06 19:46:39 +00:00
abstract class Message {
MessageMetadata getMetadata ( ) ;
2021-12-06 20:25:17 +00:00
Widget getWidget ( BuildContext context , Key key ) ;
2021-07-06 19:46:39 +00:00
Widget getPreviewWidget ( BuildContext context ) ;
}
2021-12-06 20:25:17 +00:00
Message compileOverlay ( MessageMetadata metadata , String messageData ) {
try {
dynamic message = jsonDecode ( messageData ) ;
var content = message [ ' d ' ] as dynamic ;
var overlay = int . parse ( message [ ' o ' ] . toString ( ) ) ;
switch ( overlay ) {
case TextMessageOverlay:
return TextMessage ( metadata , content ) ;
case SuggestContactOverlay:
case InviteGroupOverlay:
return InviteMessage ( overlay , metadata , content ) ;
case QuotedMessageOverlay:
return QuotedMessage ( metadata , content ) ;
case FileShareOverlay:
return FileMessage ( metadata , content ) ;
default :
// Metadata is valid, content is not..
return MalformedMessage ( metadata ) ;
}
} catch ( e ) {
return MalformedMessage ( metadata ) ;
}
}
2021-11-26 22:25:21 +00:00
Future < Message > messageHandler ( BuildContext context , String profileOnion , int conversationIdentifier , int index , { bool byID = false } ) {
2021-12-15 21:43:14 +00:00
try {
var cache = Provider . of < ProfileInfoState > ( context , listen: false ) . contactList . getContact ( conversationIdentifier ) ? . messageCache ;
if ( cache ! = null & & cache . length > index ) {
if ( cache [ index ] ! = null ) {
return Future . value ( compileOverlay ( cache [ index ] ! . metadata , cache [ index ] ! . wrapper ) ) ;
}
2021-12-06 20:25:17 +00:00
}
2021-12-15 21:43:14 +00:00
} catch ( e ) {
// provider check failed...make an expensive call...
2021-12-06 20:25:17 +00:00
}
2021-07-06 19:46:39 +00:00
try {
2021-11-26 22:25:21 +00:00
Future < dynamic > rawMessageEnvelopeFuture ;
if ( byID ) {
rawMessageEnvelopeFuture = Provider . of < FlwtchState > ( context , listen: false ) . cwtch . GetMessageByID ( profileOnion , conversationIdentifier , index ) ;
} else {
rawMessageEnvelopeFuture = Provider . of < FlwtchState > ( context , listen: false ) . cwtch . GetMessage ( profileOnion , conversationIdentifier , index ) ;
}
2021-07-06 19:46:39 +00:00
return rawMessageEnvelopeFuture . then ( ( dynamic rawMessageEnvelope ) {
2021-12-06 20:25:17 +00:00
var metadata = MessageMetadata ( profileOnion , conversationIdentifier , index , DateTime . now ( ) , " " , " " , " " , < String , String > { } , false , true ) ;
2021-09-29 20:31:01 +00:00
try {
dynamic messageWrapper = jsonDecode ( rawMessageEnvelope ) ;
// There are 2 conditions in which this error condition can be met:
// 1. The application == nil, in which case this instance of the UI is already
// broken beyond repair, and will either be replaced by a new version, or requires a complete
// restart.
// 2. This index was incremented and we happened to fetch the timeline prior to the messages inclusion.
// This should be rare as Timeline addition/fetching is mutex protected and Dart itself will pipeline the
// calls to libCwtch-go - however because we use goroutines on the backend there is always a chance that one
// will find itself delayed.
// The second case is recoverable by tail-recursing this future.
if ( messageWrapper [ ' Message ' ] = = null | | messageWrapper [ ' Message ' ] = = ' ' | | messageWrapper [ ' Message ' ] = = ' {} ' ) {
return Future . delayed ( Duration ( seconds: 2 ) , ( ) {
print ( " Tail recursive call to messageHandler called. This should be a rare event. If you see multiples of this log over a short period of time please log it as a bug. " ) ;
2021-12-06 20:25:17 +00:00
return messageHandler ( context , profileOnion , conversationIdentifier , - 1 , byID: byID ) . then ( ( value ) = > value ) ;
2021-09-29 20:31:01 +00:00
} ) ;
}
2021-07-06 19:46:39 +00:00
2021-09-29 20:31:01 +00:00
// Construct the initial metadata
2021-11-18 23:44:54 +00:00
var messageID = messageWrapper [ ' ID ' ] ;
2021-09-29 20:31:01 +00:00
var timestamp = DateTime . tryParse ( messageWrapper [ ' Timestamp ' ] ) ! ;
var senderHandle = messageWrapper [ ' PeerID ' ] ;
var senderImage = messageWrapper [ ' ContactImage ' ] ;
2021-12-01 12:17:48 +00:00
var attributes = messageWrapper [ ' Attributes ' ] ;
2021-09-29 20:31:01 +00:00
var ackd = messageWrapper [ ' Acknowledged ' ] ;
var error = messageWrapper [ ' Error ' ] ! = null ;
2021-11-18 23:44:54 +00:00
var signature = messageWrapper [ ' Signature ' ] ;
2021-12-06 20:25:17 +00:00
metadata = MessageMetadata ( profileOnion , conversationIdentifier , messageID , timestamp , senderHandle , senderImage , signature , attributes , ackd , error ) ;
return compileOverlay ( metadata , messageWrapper [ ' Message ' ] ) ;
2021-07-07 17:05:25 +00:00
} catch ( e ) {
2021-11-26 22:25:21 +00:00
EnvironmentConfig . debugLog ( " an error! " + e . toString ( ) ) ;
2021-07-07 17:05:25 +00:00
return MalformedMessage ( metadata ) ;
2021-07-06 19:46:39 +00:00
}
} ) ;
} catch ( e ) {
2021-12-06 20:25:17 +00:00
return Future . value ( MalformedMessage ( MessageMetadata ( profileOnion , conversationIdentifier , - 1 , DateTime . now ( ) , " " , " " , " " , < String , String > { } , false , true ) ) ) ;
2021-07-06 19:46:39 +00:00
}
}
class MessageMetadata extends ChangeNotifier {
// meta-metadata
final String profileOnion ;
2021-11-18 23:44:54 +00:00
final int conversationIdentifier ;
final int messageID ;
2021-07-06 19:46:39 +00:00
final DateTime timestamp ;
final String senderHandle ;
final String ? senderImage ;
2021-12-01 12:17:48 +00:00
final dynamic _attributes ;
2021-07-06 19:46:39 +00:00
bool _ackd ;
bool _error ;
final String ? signature ;
2021-12-01 12:17:48 +00:00
dynamic get attributes = > this . _attributes ;
2021-07-06 19:46:39 +00:00
bool get ackd = > this . _ackd ;
set ackd ( bool newVal ) {
this . _ackd = newVal ;
notifyListeners ( ) ;
}
bool get error = > this . _error ;
set error ( bool newVal ) {
this . _error = newVal ;
notifyListeners ( ) ;
}
2021-12-06 20:25:17 +00:00
MessageMetadata ( this . profileOnion , this . conversationIdentifier , this . messageID , this . timestamp , this . senderHandle , this . senderImage , this . signature , this . _attributes , this . _ackd , this . _error ) ;
2021-07-06 19:46:39 +00:00
}