2022-03-23 23:08:19 +00:00
import ' dart:async ' ;
2022-03-04 19:30:19 +00:00
import ' package:flutter/foundation.dart ' ;
2022-01-18 21:26:52 +00:00
import ' message.dart ' ;
2022-01-18 23:31:10 +00:00
class MessageInfo {
2022-03-23 23:08:19 +00:00
late MessageMetadata metadata ;
late String wrapper ;
2022-01-18 23:31:10 +00:00
MessageInfo ( this . metadata , this . wrapper ) ;
2022-01-19 21:58:52 +00:00
}
2022-01-18 23:31:10 +00:00
2022-03-23 23:08:19 +00:00
class LocalIndexMessage {
late bool cacheOnly ;
late bool isLoading ;
late Future < void > loaded ;
late Completer < void > loader ;
late int ? messageId ;
LocalIndexMessage ( int ? messageId , { cacheOnly = false , isLoading = false } ) {
this . messageId = messageId ;
this . cacheOnly = cacheOnly ;
this . isLoading = isLoading ;
if ( isLoading ) {
loader = Completer < void > ( ) ;
loaded = loader . future ;
}
}
void finishLoad ( int messageId ) {
this . messageId = messageId ;
isLoading = false ;
loader . complete ( true ) ;
}
void failLoad ( ) {
this . messageId = null ;
isLoading = false ;
loader . complete ( true ) ;
}
Future < void > waitForLoad ( ) {
return loaded ;
}
Future < int ? > get ( ) async {
if ( isLoading ) {
await waitForLoad ( ) ;
}
return messageId ;
}
}
// Message cache stores messages for use by the UI and uses MessageHandler and associated ByX loaders
// the cache stores messages in a cache indexed by their storage Id, and has two secondary indexes into it, content hash, and local index
// Index is the primary way to access the cache as it is a sequential ordered access and is used by the message pane
// contentHash is used for fetching replies
// by Id is used when composing a reply
// cacheByIndex supports additional features than just a direct index into the cache (byID)
// it allows locking of ranges in order to support bulk sequential loading (see ByIndex in message.dart)
// cacheByIndex allows allows inserting temporarily non storage backed messages so that Send Message can be respected instantly and then updated upon insertion into backend
// the message cache needs storageMessageCount maintained by the system so it can inform bulk loading when it's reaching the end of fetchable messages
2022-03-04 19:30:19 +00:00
class MessageCache extends ChangeNotifier {
2022-03-23 23:08:19 +00:00
// cache of MessageId to Message
2022-01-18 23:31:10 +00:00
late Map < int , MessageInfo > cache ;
2022-03-23 23:08:19 +00:00
// local index to MessageId
late List < LocalIndexMessage > cacheByIndex ;
// map of content hash to MessageId
2022-01-20 14:13:54 +00:00
late Map < String , int > cacheByHash ;
2022-01-18 23:31:10 +00:00
2022-03-23 23:08:19 +00:00
late int _storageMessageCount ;
MessageCache ( int storageMessageCount ) {
2022-01-20 14:13:54 +00:00
cache = { } ;
cacheByIndex = List . empty ( growable: true ) ;
cacheByHash = { } ;
2022-03-23 23:08:19 +00:00
this . _storageMessageCount = storageMessageCount ;
2022-01-18 23:31:10 +00:00
}
2022-01-20 14:13:54 +00:00
int get indexedLength = > cacheByIndex . length ;
2022-01-18 23:31:10 +00:00
2022-03-23 23:08:19 +00:00
int get storageMessageCount = > _storageMessageCount ;
set storageMessageCount ( int newval ) {
this . _storageMessageCount = newval ;
}
2022-01-20 14:13:54 +00:00
MessageInfo ? getById ( int id ) = > cache [ id ] ;
2022-03-23 23:08:19 +00:00
Future < MessageInfo ? > getByIndex ( int index ) async {
2022-01-20 14:13:54 +00:00
if ( index > = cacheByIndex . length ) {
return null ;
}
2022-03-23 23:08:19 +00:00
var id = await cacheByIndex [ index ] . get ( ) ;
if ( id = = null ) {
return Future < MessageInfo ? > . value ( null ) ;
}
return cache [ id ] ;
2022-01-20 14:13:54 +00:00
}
2022-01-20 18:37:09 +00:00
2022-01-20 14:13:54 +00:00
MessageInfo ? getByContentHash ( String contenthash ) = > cache [ cacheByHash [ contenthash ] ] ;
2022-03-23 23:08:19 +00:00
void addNew ( String profileOnion , int conversation , int messageID , DateTime timestamp , String senderHandle , String senderImage , bool isAuto , String data , String contenthash ) {
this . cache [ messageID ] = MessageInfo ( MessageMetadata ( profileOnion , conversation , messageID , timestamp , senderHandle , senderImage , " " , { } , false , false , isAuto , contenthash ) , data ) ;
this . cacheByIndex . insert ( 0 , LocalIndexMessage ( messageID ) ) ;
2022-01-20 14:13:54 +00:00
if ( contenthash ! = null & & contenthash ! = " " ) {
this . cacheByHash [ contenthash ] = messageID ;
}
2022-03-04 19:30:19 +00:00
notifyListeners ( ) ;
2022-01-20 14:13:54 +00:00
}
2022-03-24 00:56:59 +00:00
// inserts place holder values into the index cache that will block on .get() until .finishLoad() is called on them with message contents
// or .failLoad() is called on them to mark them malformed
// this prevents successive ui message build requests from triggering multiple GetMesssage requests to the backend, as the first one locks a block of messages and the rest wait on that
2022-03-24 19:04:09 +00:00
void lockIndexes ( int start , int end ) {
2022-04-06 21:35:10 +00:00
for ( var i = start ; i < end ; i + + ) {
2022-03-23 23:08:19 +00:00
this . cacheByIndex . insert ( i , LocalIndexMessage ( null , isLoading: true ) ) ;
}
}
void malformIndexes ( int start , int end ) {
2022-04-06 21:35:10 +00:00
for ( var i = start ; i < end ; i + + ) {
2022-03-23 23:08:19 +00:00
this . cacheByIndex [ i ] . failLoad ( ) ;
}
}
void addIndexed ( MessageInfo messageInfo , int index ) {
2022-01-20 14:13:54 +00:00
this . cache [ messageInfo . metadata . messageID ] = messageInfo ;
2022-04-06 21:35:10 +00:00
if ( index < this . cacheByIndex . length ) {
2022-03-23 23:08:19 +00:00
this . cacheByIndex [ index ] . finishLoad ( messageInfo . metadata . messageID ) ;
} else {
this . cacheByIndex . insert ( index , LocalIndexMessage ( messageInfo . metadata . messageID ) ) ;
}
2022-03-24 00:56:59 +00:00
this . cacheByHash [ messageInfo . metadata . contenthash ] = messageInfo . metadata . messageID ;
2022-03-04 19:30:19 +00:00
notifyListeners ( ) ;
2022-01-20 14:13:54 +00:00
}
2022-03-23 23:08:19 +00:00
void addUnindexed ( MessageInfo messageInfo ) {
2022-01-20 14:13:54 +00:00
this . cache [ messageInfo . metadata . messageID ] = messageInfo ;
2022-03-23 23:08:19 +00:00
if ( messageInfo . metadata . contenthash ! = " " ) {
this . cacheByHash [ messageInfo . metadata . contenthash ] = messageInfo . metadata . messageID ;
2022-01-20 14:13:54 +00:00
}
2022-03-04 19:30:19 +00:00
notifyListeners ( ) ;
2022-01-18 23:31:10 +00:00
}
void ackCache ( int messageID ) {
cache [ messageID ] ? . metadata . ackd = true ;
2022-03-04 19:30:19 +00:00
notifyListeners ( ) ;
}
void errCache ( int messageID ) {
cache [ messageID ] ? . metadata . error = true ;
notifyListeners ( ) ;
2022-01-18 23:31:10 +00:00
}
2022-01-20 18:37:09 +00:00
}