New Cwtch Library Integration #258
|
@ -184,6 +184,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
|||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||
val conversation = a.getInt("conversation").toLong()
|
||||
val indexI = a.getInt("index").toLong()
|
||||
Log.i("FlwtchWorker", "Cwtch GetMessage " + profile + " " + conversation.toString() + " " + indexI.toString())
|
||||
return Result.success(Data.Builder().putString("result", Cwtch.getMessage(profile, conversation, indexI)).build())
|
||||
}
|
||||
"GetMessageByID" -> {
|
||||
|
|
|
@ -30,6 +30,7 @@ import android.os.Build
|
|||
import android.os.Environment
|
||||
import android.database.Cursor
|
||||
import android.provider.MediaStore
|
||||
import cwtch.Cwtch
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
override fun provideSplashScreen(): SplashScreen? = SplashView()
|
||||
|
|
|
@ -130,6 +130,10 @@ class CwtchNotifier {
|
|||
case "NewMessageFromPeer":
|
||||
notificationManager.notify("New Message From Peer!");
|
||||
var identifier = int.parse(data["ConversationID"]);
|
||||
var messageID = int.parse(data["Index"]);
|
||||
var timestamp = DateTime.tryParse(data['TimestampReceived'])!;
|
||||
var senderHandle = data['RemotePeer'];
|
||||
var senderImage = data['Picture'];
|
||||
|
||||
// We might not have received a contact created for this contact yet...
|
||||
// In that case the **next** event we receive will actually update these values...
|
||||
|
@ -140,6 +144,7 @@ class CwtchNotifier {
|
|||
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier)!.newMarker++;
|
||||
}
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(identifier, DateTime.now());
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier)!.updateMessageCache(identifier, messageID, timestamp, senderHandle, senderImage, data["Data"], "");
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier)!.totalMessages++;
|
||||
|
||||
// We only ever see messages from authenticated peers.
|
||||
|
@ -156,11 +161,12 @@ class CwtchNotifier {
|
|||
break;
|
||||
case "IndexedAcknowledgement":
|
||||
var conversation = int.parse(data["ConversationID"]);
|
||||
var message_index = int.parse(data["Index"]);
|
||||
var messageID = int.parse(data["Index"]);
|
||||
|
||||
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(conversation);
|
||||
// We return -1 for protocol message acks if there is no message
|
||||
if (message_index == -1) break;
|
||||
var key = contact!.getMessageKeyOrFail(conversation, message_index, contact.lastMessageTime);
|
||||
if (messageID == -1) break;
|
||||
var key = contact!.getMessageKeyOrFail(conversation, messageID, contact.lastMessageTime);
|
||||
if (key == null) break;
|
||||
try {
|
||||
var message = Provider.of<MessageMetadata>(key.currentContext!, listen: false);
|
||||
|
@ -181,11 +187,19 @@ class CwtchNotifier {
|
|||
var identifier = int.parse(data["ConversationID"]);
|
||||
if (data["ProfileOnion"] != data["RemotePeer"]) {
|
||||
var idx = int.parse(data["Index"]);
|
||||
var senderHandle = data['RemotePeer'];
|
||||
var senderImage = data['Picture'];
|
||||
var timestampSent = DateTime.tryParse(data['TimestampSent'])!;
|
||||
var currentTotal = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier)!.totalMessages;
|
||||
|
||||
// Only bother to do anything if we know about the group and the provided index is greater than our current total...
|
||||
if (currentTotal != null && idx >= currentTotal) {
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier)!.totalMessages = idx + 1;
|
||||
profileCN
|
||||
.getProfile(data["ProfileOnion"])
|
||||
?.contactList
|
||||
.getContact(identifier)!
|
||||
.updateMessageCache(identifier, idx, timestampSent, senderHandle, senderImage, data["Data"], data["Signature"]);
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier)!.totalMessages++;
|
||||
|
||||
//if not currently open
|
||||
if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != identifier) {
|
||||
|
@ -194,7 +208,6 @@ class CwtchNotifier {
|
|||
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier)!.newMarker++;
|
||||
}
|
||||
|
||||
var timestampSent = DateTime.tryParse(data['TimestampSent'])!;
|
||||
// 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
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cwtch/config.dart';
|
||||
import 'package:cwtch/models/message.dart';
|
||||
import 'package:cwtch/widgets/messagerow.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cwtch/models/profileservers.dart';
|
||||
|
@ -501,6 +502,12 @@ ContactAuthorization stringToContactAuthorization(String authStr) {
|
|||
}
|
||||
}
|
||||
|
||||
class MessageCache {
|
||||
final MessageMetadata metadata;
|
||||
final String wrapper;
|
||||
MessageCache(this.metadata, this.wrapper);
|
||||
}
|
||||
|
||||
class ContactInfoState extends ChangeNotifier {
|
||||
final String profileOnion;
|
||||
final int identifier;
|
||||
|
@ -515,6 +522,7 @@ class ContactInfoState extends ChangeNotifier {
|
|||
late int _totalMessages = 0;
|
||||
late DateTime _lastMessageTime;
|
||||
late Map<String, GlobalKey<MessageRowState>> keys;
|
||||
late List<MessageCache?> messageCache;
|
||||
int _newMarker = 0;
|
||||
DateTime _newMarkerClearAt = DateTime.now();
|
||||
|
||||
|
@ -546,6 +554,7 @@ class ContactInfoState extends ChangeNotifier {
|
|||
this._lastMessageTime = lastMessageTime == null ? DateTime.fromMillisecondsSinceEpoch(0) : lastMessageTime;
|
||||
this._server = server;
|
||||
this._archived = archived;
|
||||
this.messageCache = List.empty(growable: true);
|
||||
keys = Map<String, GlobalKey<MessageRowState>>();
|
||||
}
|
||||
|
||||
|
@ -677,4 +686,12 @@ class ContactInfoState extends ChangeNotifier {
|
|||
GlobalKey<MessageRowState> ret = keys[index]!;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void updateMessageCache(int conversation, int messageID, DateTime timestamp, String senderHandle, String senderImage, String data, String signature) {
|
||||
this.messageCache.insert(0, MessageCache(MessageMetadata(profileOnion, conversation, messageID, timestamp, senderHandle, senderImage, signature, {}, false, false), data));
|
||||
}
|
||||
|
||||
void bumpMessageCache() {
|
||||
sarah marked this conversation as resolved
|
||||
this.messageCache.insert(0, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,52 +28,13 @@ const GroupConversationHandleLength = 32;
|
|||
|
||||
abstract class Message {
|
||||
MessageMetadata getMetadata();
|
||||
Widget getWidget(BuildContext context);
|
||||
Widget getWidget(BuildContext context, Key key);
|
||||
Widget getPreviewWidget(BuildContext context);
|
||||
}
|
||||
|
||||
Future<Message> messageHandler(BuildContext context, String profileOnion, int conversationIdentifier, int index, {bool byID = false}) {
|
||||
Message compileOverlay(MessageMetadata metadata, String messageData) {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
|
||||
return rawMessageEnvelopeFuture.then((dynamic rawMessageEnvelope) {
|
||||
var metadata = MessageMetadata(profileOnion, conversationIdentifier, index, -1, DateTime.now(), "", "", "", <String, String>{}, false, true);
|
||||
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.");
|
||||
return messageHandler(context, profileOnion, conversationIdentifier, index, byID: byID).then((value) => value);
|
||||
});
|
||||
}
|
||||
|
||||
// Construct the initial metadata
|
||||
var messageID = messageWrapper['ID'];
|
||||
var timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
|
||||
var senderHandle = messageWrapper['PeerID'];
|
||||
var senderImage = messageWrapper['ContactImage'];
|
||||
var attributes = messageWrapper['Attributes'];
|
||||
var ackd = messageWrapper['Acknowledged'];
|
||||
var error = messageWrapper['Error'] != null;
|
||||
var signature = messageWrapper['Signature'];
|
||||
metadata = MessageMetadata(profileOnion, conversationIdentifier, index, messageID, timestamp, senderHandle, senderImage, signature, attributes, ackd, error);
|
||||
|
||||
dynamic message = jsonDecode(messageWrapper['Message']);
|
||||
dynamic message = jsonDecode(messageData);
|
||||
var content = message['d'] as dynamic;
|
||||
var overlay = int.parse(message['o'].toString());
|
||||
|
||||
|
@ -91,13 +52,67 @@ Future<Message> messageHandler(BuildContext context, String profileOnion, int co
|
|||
// Metadata is valid, content is not..
|
||||
return MalformedMessage(metadata);
|
||||
}
|
||||
} catch (e) {
|
||||
return MalformedMessage(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Message> messageHandler(BuildContext context, String profileOnion, int conversationIdentifier, int index, {bool byID = false}) {
|
||||
var cache = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(conversationIdentifier)!.messageCache;
|
||||
if (cache.length > index) {
|
||||
if (cache[index] != null) {
|
||||
return Future.value(compileOverlay(cache[index]!.metadata, cache[index]!.wrapper));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
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);
|
||||
}
|
||||
|
||||
return rawMessageEnvelopeFuture.then((dynamic rawMessageEnvelope) {
|
||||
var metadata = MessageMetadata(profileOnion, conversationIdentifier, index, DateTime.now(), "", "", "", <String, String>{}, false, true);
|
||||
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.");
|
||||
return messageHandler(context, profileOnion, conversationIdentifier, -1, byID: byID).then((value) => value);
|
||||
});
|
||||
}
|
||||
|
||||
// Construct the initial metadata
|
||||
var messageID = messageWrapper['ID'];
|
||||
var timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
|
||||
var senderHandle = messageWrapper['PeerID'];
|
||||
var senderImage = messageWrapper['ContactImage'];
|
||||
var attributes = messageWrapper['Attributes'];
|
||||
var ackd = messageWrapper['Acknowledged'];
|
||||
var error = messageWrapper['Error'] != null;
|
||||
var signature = messageWrapper['Signature'];
|
||||
metadata = MessageMetadata(profileOnion, conversationIdentifier, messageID, timestamp, senderHandle, senderImage, signature, attributes, ackd, error);
|
||||
|
||||
return compileOverlay(metadata, messageWrapper['Message']);
|
||||
} catch (e) {
|
||||
EnvironmentConfig.debugLog("an error! " + e.toString());
|
||||
return MalformedMessage(metadata);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
return Future.value(MalformedMessage(MessageMetadata(profileOnion, conversationIdentifier, index, -1, DateTime.now(), "", "", "", <String, String>{}, false, true)));
|
||||
return Future.value(MalformedMessage(MessageMetadata(profileOnion, conversationIdentifier, -1, DateTime.now(), "", "", "", <String, String>{}, false, true)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +120,6 @@ class MessageMetadata extends ChangeNotifier {
|
|||
// meta-metadata
|
||||
final String profileOnion;
|
||||
final int conversationIdentifier;
|
||||
final int messageIndex;
|
||||
final int messageID;
|
||||
|
||||
final DateTime timestamp;
|
||||
|
@ -131,6 +145,5 @@ class MessageMetadata extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
MessageMetadata(this.profileOnion, this.conversationIdentifier, this.messageIndex, this.messageID, this.timestamp, this.senderHandle, this.senderImage, this.signature, this._attributes, this._ackd,
|
||||
this._error);
|
||||
MessageMetadata(this.profileOnion, this.conversationIdentifier, this.messageID, this.timestamp, this.senderHandle, this.senderImage, this.signature, this._attributes, this._ackd, this._error);
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@ class FileMessage extends Message {
|
|||
FileMessage(this.metadata, this.content);
|
||||
|
||||
@override
|
||||
Widget getWidget(BuildContext context) {
|
||||
Widget getWidget(BuildContext context, Key key) {
|
||||
return ChangeNotifierProvider.value(
|
||||
key: key,
|
||||
value: this.metadata,
|
||||
builder: (bcontext, child) {
|
||||
dynamic shareObj = jsonDecode(this.content);
|
||||
|
@ -34,9 +35,7 @@ class FileMessage extends Message {
|
|||
return MessageRow(MalformedBubble());
|
||||
}
|
||||
|
||||
var lrt = Provider.of<ContactInfoState>(bcontext).lastMessageTime;
|
||||
return MessageRow(FileBubble(nameSuggestion, rootHash, nonce, fileSize),
|
||||
key: Provider.of<ContactInfoState>(bcontext).getMessageKey(this.metadata.conversationIdentifier, this.metadata.messageID, lrt));
|
||||
return MessageRow(FileBubble(nameSuggestion, rootHash, nonce, fileSize));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,9 @@ class InviteMessage extends Message {
|
|||
InviteMessage(this.overlay, this.metadata, this.content);
|
||||
|
||||
@override
|
||||
Widget getWidget(BuildContext context) {
|
||||
Widget getWidget(BuildContext context, Key key) {
|
||||
return ChangeNotifierProvider.value(
|
||||
key: key,
|
||||
value: this.metadata,
|
||||
builder: (bcontext, child) {
|
||||
String inviteTarget;
|
||||
|
@ -40,8 +41,7 @@ class InviteMessage extends Message {
|
|||
}
|
||||
}
|
||||
var lrt = Provider.of<ContactInfoState>(bcontext).lastMessageTime;
|
||||
return MessageRow(InvitationBubble(overlay, inviteTarget, inviteNick, invite),
|
||||
key: Provider.of<ContactInfoState>(bcontext).getMessageKey(this.metadata.conversationIdentifier, this.metadata.messageID, lrt));
|
||||
return MessageRow(InvitationBubble(overlay, inviteTarget, inviteNick, invite));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,9 @@ class MalformedMessage extends Message {
|
|||
MalformedMessage(this.metadata);
|
||||
|
||||
@override
|
||||
Widget getWidget(BuildContext context) {
|
||||
Widget getWidget(BuildContext context, Key key) {
|
||||
return ChangeNotifierProvider.value(
|
||||
key: key,
|
||||
value: this.metadata,
|
||||
builder: (context, child) {
|
||||
return MessageRow(MalformedBubble());
|
||||
|
|
|
@ -51,7 +51,7 @@ class QuotedMessage extends Message {
|
|||
dynamic message = jsonDecode(this.content);
|
||||
return Text(message["body"]);
|
||||
} catch (e) {
|
||||
return MalformedMessage(this.metadata).getWidget(context);
|
||||
return MalformedMessage(this.metadata).getWidget(context, Key("malformed"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -62,16 +62,15 @@ class QuotedMessage extends Message {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget getWidget(BuildContext context) {
|
||||
Widget getWidget(BuildContext context, Key key) {
|
||||
try {
|
||||
dynamic message = jsonDecode(this.content);
|
||||
|
||||
if (message["body"] == null || message["quotedHash"] == null) {
|
||||
return MalformedMessage(this.metadata).getWidget(context);
|
||||
return MalformedMessage(this.metadata).getWidget(context, key);
|
||||
}
|
||||
|
||||
var quotedMessagePotentials = Provider.of<FlwtchState>(context).cwtch.GetMessageByContentHash(metadata.profileOnion, metadata.conversationIdentifier, message["quotedHash"]);
|
||||
int messageIndex = metadata.messageIndex;
|
||||
Future<LocallyIndexedMessage?> quotedMessage = quotedMessagePotentials.then((matchingMessages) {
|
||||
if (matchingMessages == "[]") {
|
||||
return null;
|
||||
|
@ -81,9 +80,7 @@ class QuotedMessage extends Message {
|
|||
// message
|
||||
try {
|
||||
var list = (jsonDecode(matchingMessages) as List<dynamic>).map((data) => LocallyIndexedMessage.fromJson(data)).toList();
|
||||
LocallyIndexedMessage candidate = list.reversed.firstWhere((element) => messageIndex < element.index, orElse: () {
|
||||
return list.firstWhere((element) => messageIndex > element.index);
|
||||
});
|
||||
LocallyIndexedMessage candidate = list.reversed.first;
|
||||
return candidate;
|
||||
} catch (e) {
|
||||
// Malformed Message will be returned...
|
||||
|
@ -92,20 +89,18 @@ class QuotedMessage extends Message {
|
|||
});
|
||||
|
||||
return ChangeNotifierProvider.value(
|
||||
key: key,
|
||||
value: this.metadata,
|
||||
builder: (bcontext, child) {
|
||||
var lrt = Provider.of<ContactInfoState>(bcontext).lastMessageTime;
|
||||
return MessageRow(
|
||||
QuotedMessageBubble(message["body"], quotedMessage.then((LocallyIndexedMessage? localIndex) {
|
||||
return MessageRow(QuotedMessageBubble(message["body"], quotedMessage.then((LocallyIndexedMessage? localIndex) {
|
||||
if (localIndex != null) {
|
||||
return messageHandler(context, metadata.profileOnion, metadata.conversationIdentifier, localIndex.index);
|
||||
}
|
||||
return MalformedMessage(this.metadata);
|
||||
})),
|
||||
key: Provider.of<ContactInfoState>(bcontext).getMessageKey(this.metadata.conversationIdentifier, this.metadata.messageID, lrt));
|
||||
})));
|
||||
});
|
||||
} catch (e) {
|
||||
return MalformedMessage(this.metadata).getWidget(context);
|
||||
return MalformedMessage(this.metadata).getWidget(context, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,14 +31,15 @@ class TextMessage extends Message {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget getWidget(BuildContext context) {
|
||||
Widget getWidget(BuildContext context, Key key) {
|
||||
return ChangeNotifierProvider.value(
|
||||
key: key,
|
||||
value: this.metadata,
|
||||
builder: (bcontext, child) {
|
||||
var lrt = Provider.of<ContactInfoState>(bcontext).lastMessageTime;
|
||||
var key = Provider.of<ContactInfoState>(bcontext).getMessageKey(this.metadata.conversationIdentifier, this.metadata.messageID, lrt);
|
||||
// var key = Provider.of<ContactInfoState>(bcontext).getMessageKey(this.metadata.conversationIdentifier, this.metadata.messageID, lrt);
|
||||
|
||||
return MessageRow(MessageBubble(this.content), key: key);
|
||||
return MessageRow(MessageBubble(this.content));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:cwtch/config.dart';
|
||||
import 'package:cwtch/cwtch_icons_icons.dart';
|
||||
import 'package:cwtch/models/message.dart';
|
||||
import 'package:cwtch/models/messages/quotedmessage.dart';
|
||||
|
@ -213,6 +214,7 @@ class _MessageViewState extends State<MessageView> {
|
|||
ctrlrCompose.clear();
|
||||
focusNode.requestFocus();
|
||||
Future.delayed(const Duration(milliseconds: 80), () {
|
||||
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(Provider.of<ContactInfoState>(context, listen: false).identifier)?.bumpMessageCache();
|
||||
Provider.of<ContactInfoState>(context, listen: false).totalMessages++;
|
||||
Provider.of<ContactInfoState>(context, listen: false).newMarker++;
|
||||
// Resort the contact list...
|
||||
|
|
|
@ -79,13 +79,15 @@ class _MessageListState extends State<MessageList> {
|
|||
var contactHandle = Provider.of<ContactInfoState>(outerContext, listen: false).identifier;
|
||||
var messageIndex = index;
|
||||
|
||||
// var key = Provider.of<ContactInfoState>(outerContext, listen: false).getMessageKey(contactHandle, Provider.of<ContactInfoState>(outerContext).totalMessages - index, DateTime.now());
|
||||
return FutureBuilder(
|
||||
//key: Provider.of<ContactInfoState>(outerContext, listen: false).getMessageKey(contactHandle, Provider.of<ContactInfoState>(outerContext).totalMessages - index, DateTime.now()),
|
||||
future: messageHandler(outerContext, profileOnion, contactHandle, messageIndex),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
var message = snapshot.data as Message;
|
||||
// Already includes MessageRow,,
|
||||
return message.getWidget(context);
|
||||
var key = Provider.of<ContactInfoState>(outerContext, listen: false).getMessageKey(contactHandle, message.getMetadata().messageID, DateTime.now());
|
||||
return message.getWidget(context, key);
|
||||
} else {
|
||||
return MessageLoadingBubble();
|
||||
}
|
||||
|
@ -97,3 +99,23 @@ class _MessageListState extends State<MessageList> {
|
|||
])));
|
||||
}
|
||||
}
|
||||
|
||||
class CachedMessage extends Message {
|
||||
@override
|
||||
MessageMetadata getMetadata() {
|
||||
// TODO: implement getMetadata
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getPreviewWidget(BuildContext context) {
|
||||
// TODO: implement getPreviewWidget
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget getWidget(BuildContext context, Key key) {
|
||||
// TODO: implement getWidget
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
index = Provider.of<MessageMetadata>(context, listen: false).messageIndex;
|
||||
index = Provider.of<MessageMetadata>(context, listen: false).messageID;
|
||||
_controller = AnimationController(vsync: this);
|
||||
_controller.addListener(() {
|
||||
setState(() {
|
||||
|
@ -75,7 +75,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
|
|||
}
|
||||
|
||||
Widget wdgIcons = Visibility(
|
||||
visible: Provider.of<AppState>(context).hoveredIndex == Provider.of<MessageMetadata>(context).messageIndex,
|
||||
visible: Provider.of<AppState>(context).hoveredIndex == Provider.of<MessageMetadata>(context).messageID,
|
||||
maintainSize: true,
|
||||
maintainAnimation: true,
|
||||
maintainState: true,
|
||||
|
@ -169,7 +169,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
|
|||
// For desktop...
|
||||
onHover: (event) {
|
||||
setState(() {
|
||||
Provider.of<AppState>(context, listen: false).hoveredIndex = Provider.of<MessageMetadata>(context, listen: false).messageIndex;
|
||||
Provider.of<AppState>(context, listen: false).hoveredIndex = Provider.of<MessageMetadata>(context, listen: false).messageID;
|
||||
});
|
||||
},
|
||||
onExit: (event) {
|
||||
|
@ -204,7 +204,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
|
|||
children: widgetRow,
|
||||
)))));
|
||||
var mark = Provider.of<ContactInfoState>(context).newMarker;
|
||||
if (mark > 0 && mark == Provider.of<MessageMetadata>(context).messageIndex + 1) {
|
||||
if (mark > 0 && Provider.of<ContactInfoState>(context).messageCache[mark]?.metadata.messageID == Provider.of<MessageMetadata>(context).messageID) {
|
||||
return Column(crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [Align(alignment: Alignment.center, child: _bubbleNew()), mr]);
|
||||
} else {
|
||||
return mr;
|
||||
|
@ -251,12 +251,17 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
|
|||
}
|
||||
|
||||
void _btnGoto() {
|
||||
selectConversation(context, Provider.of<MessageMetadata>(context, listen: false).conversationIdentifier);
|
||||
var id = Provider.of<ProfileInfoState>(context, listen: false).contactList.findContact(Provider.of<MessageMetadata>(context, listen: false).senderHandle)?.identifier;
|
||||
if (id == null) {
|
||||
// Can't happen
|
||||
} else {
|
||||
selectConversation(context, id);
|
||||
}
|
||||
}
|
||||
|
||||
void _btnAdd() {
|
||||
var sender = Provider.of<MessageMetadata>(context, listen: false).senderHandle;
|
||||
if (sender == null || sender == "") {
|
||||
if (sender == "") {
|
||||
print("sender not yet loaded");
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
ideally internal. the public setter for totalMessages can call, and updateMessageCaches should internally inc totalMessages