forked from cwtch.im/cwtch-ui
Quoted Messages Initial Pass
This commit is contained in:
parent
370a7fd1da
commit
b9984a3598
|
@ -122,6 +122,12 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
||||||
val indexI = a.getInt("index")
|
val indexI = a.getInt("index")
|
||||||
return Result.success(Data.Builder().putString("result", Cwtch.getMessage(profile, handle, indexI.toLong())).build())
|
return Result.success(Data.Builder().putString("result", Cwtch.getMessage(profile, handle, indexI.toLong())).build())
|
||||||
}
|
}
|
||||||
|
"GetMessageByContentHash" -> {
|
||||||
|
val profile = (a.get("profile") as? String) ?: ""
|
||||||
|
val handle = (a.get("contact") as? String) ?: ""
|
||||||
|
val contentHash = (a.get("contentHash") as? String) ?: ""
|
||||||
|
return Result.success(Data.Builder().putString("result", Cwtch.getMessageByContentHash(profile, handle, contentHash)).build())
|
||||||
|
}
|
||||||
"UpdateMessageFlags" -> {
|
"UpdateMessageFlags" -> {
|
||||||
val profile = (a.get("profile") as? String) ?: ""
|
val profile = (a.get("profile") as? String) ?: ""
|
||||||
val handle = (a.get("contact") as? String) ?: ""
|
val handle = (a.get("contact") as? String) ?: ""
|
||||||
|
|
|
@ -32,6 +32,8 @@ abstract class Cwtch {
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
Future<dynamic> GetMessage(String profile, String handle, int index);
|
Future<dynamic> GetMessage(String profile, String handle, int index);
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
|
Future<dynamic> GetMessageByContentHash(String profile, String handle, String contentHash);
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
void UpdateMessageFlags(String profile, String handle, int index, int flags);
|
void UpdateMessageFlags(String profile, String handle, int index, int flags);
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
void SendMessage(String profile, String handle, String message);
|
void SendMessage(String profile, String handle, String message);
|
||||||
|
|
|
@ -57,9 +57,9 @@ typedef GetIntFromStrStrFn = int Function(Pointer<Utf8>, int, Pointer<Utf8>, int
|
||||||
typedef get_json_blob_from_str_str_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32);
|
typedef get_json_blob_from_str_str_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32);
|
||||||
typedef GetJsonBlobFromStrStrIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int);
|
typedef GetJsonBlobFromStrStrIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int);
|
||||||
|
|
||||||
//func GetMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, start C.int, end C.int) *C.char {
|
// func c_GetMessagesByContentHash(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, contenthash_ptr *C.char, contenthash_len C.int) *C.char
|
||||||
typedef get_json_blob_from_str_str_int_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32, Int32);
|
typedef get_json_blob_from_str_str_str_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Pointer<Utf8>, Int32);
|
||||||
typedef GetJsonBlobFromStrStrIntIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int, int);
|
typedef GetJsonBlobFromStrStrStrFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, Pointer<Utf8>, int);
|
||||||
|
|
||||||
typedef appbus_events_function = Pointer<Utf8> Function();
|
typedef appbus_events_function = Pointer<Utf8> Function();
|
||||||
typedef AppbusEventsFn = Pointer<Utf8> Function();
|
typedef AppbusEventsFn = Pointer<Utf8> Function();
|
||||||
|
@ -397,4 +397,17 @@ class CwtchFfi implements Cwtch {
|
||||||
_receivePort.close();
|
_receivePort.close();
|
||||||
print("Receive Port Closed");
|
print("Receive Port Closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future GetMessageByContentHash(String profile, String handle, String contentHash) async {
|
||||||
|
var getMessagesByContentHashC = library.lookup<NativeFunction<get_json_blob_from_str_str_str_function>>("c_GetMessagesByContentHash");
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
|
final GetMessagesByContentHash = getMessagesByContentHashC.asFunction<GetJsonBlobFromStrStrStrFn>();
|
||||||
|
final utf8profile = profile.toNativeUtf8();
|
||||||
|
final utf8handle = handle.toNativeUtf8();
|
||||||
|
final utf8contentHash = contentHash.toNativeUtf8();
|
||||||
|
Pointer<Utf8> jsonMessageBytes = GetMessagesByContentHash(utf8profile, utf8profile.length, utf8handle, utf8handle.length, utf8contentHash, utf8contentHash.length);
|
||||||
|
String jsonMessage = jsonMessageBytes.toDartString();
|
||||||
|
return jsonMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,4 +183,9 @@ class CwtchGomobile implements Cwtch {
|
||||||
print("gomobile.dart Shutdown");
|
print("gomobile.dart Shutdown");
|
||||||
cwtchPlatform.invokeMethod("Shutdown", {});
|
cwtchPlatform.invokeMethod("Shutdown", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future GetMessageByContentHash(String profile, String handle, String contentHash) {
|
||||||
|
return cwtchPlatform.invokeMethod("GetMessageByContentHash", {"profile": profile, "contact": handle, "contentHash": contentHash});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ class AppState extends ChangeNotifier {
|
||||||
String appError = "";
|
String appError = "";
|
||||||
String? _selectedProfile;
|
String? _selectedProfile;
|
||||||
String? _selectedConversation;
|
String? _selectedConversation;
|
||||||
|
int? _selectedIndex;
|
||||||
|
|
||||||
void SetCwtchInit() {
|
void SetCwtchInit() {
|
||||||
cwtchInit = true;
|
cwtchInit = true;
|
||||||
|
@ -90,6 +91,12 @@ class AppState extends ChangeNotifier {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int? get selectedIndex => _selectedIndex;
|
||||||
|
set selectedIndex(int? newVal) {
|
||||||
|
this._selectedIndex = newVal;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
bool isLandscape(BuildContext c) => MediaQuery.of(c).size.width > MediaQuery.of(c).size.height;
|
bool isLandscape(BuildContext c) => MediaQuery.of(c).size.width > MediaQuery.of(c).size.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,7 +464,7 @@ class MessageState extends ChangeNotifier {
|
||||||
final String profileOnion;
|
final String profileOnion;
|
||||||
final String contactHandle;
|
final String contactHandle;
|
||||||
final int messageIndex;
|
final int messageIndex;
|
||||||
late String _message;
|
late dynamic _message;
|
||||||
late int _overlay;
|
late int _overlay;
|
||||||
late String _inviteTarget;
|
late String _inviteTarget;
|
||||||
late String _inviteNick;
|
late String _inviteNick;
|
||||||
|
@ -535,7 +542,7 @@ class MessageState extends ChangeNotifier {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dynamic message = jsonDecode(messageWrapper['Message']);
|
dynamic message = jsonDecode(messageWrapper['Message']);
|
||||||
this._message = message['d'];
|
this._message = message['d'] as dynamic;
|
||||||
this._overlay = int.parse(message['o'].toString());
|
this._overlay = int.parse(message['o'].toString());
|
||||||
this._timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
|
this._timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
|
||||||
this._senderOnion = messageWrapper['PeerID'];
|
this._senderOnion = messageWrapper['PeerID'];
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:cwtch/cwtch_icons_icons.dart';
|
import 'package:cwtch/cwtch_icons_icons.dart';
|
||||||
|
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||||
import 'package:cwtch/widgets/profileimage.dart';
|
import 'package:cwtch/widgets/profileimage.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -104,6 +105,27 @@ class _MessageViewState extends State<MessageView> {
|
||||||
|
|
||||||
void _sendMessage([String? ignoredParam]) {
|
void _sendMessage([String? ignoredParam]) {
|
||||||
if (ctrlrCompose.value.text.isNotEmpty) {
|
if (ctrlrCompose.value.text.isNotEmpty) {
|
||||||
|
if (Provider.of<AppState>(context).selectedConversation != null && Provider.of<AppState>(context).selectedIndex != null) {
|
||||||
|
Provider.of<FlwtchState>(context)
|
||||||
|
.cwtch
|
||||||
|
.GetMessage(Provider.of<AppState>(context).selectedProfile!, Provider.of<AppState>(context).selectedConversation!, Provider.of<AppState>(context).selectedIndex!).then((data) {
|
||||||
|
try {
|
||||||
|
var messageWrapper = jsonDecode(data! as String);
|
||||||
|
var bytes1 = utf8.encode(messageWrapper["PeerID"]+messageWrapper['Message']);
|
||||||
|
var digest1 = sha256.convert(bytes1);
|
||||||
|
var contentHash = base64Encode(digest1.bytes);
|
||||||
|
var quotedMessage = "{\"quotedHash\":\""+contentHash+"\",\"body\":\""+ctrlrCompose.value.text+"\"}";
|
||||||
|
ChatMessage cm = new ChatMessage(o: 10, d: quotedMessage);
|
||||||
|
Provider.of<FlwtchState>(context, listen: false)
|
||||||
|
.cwtch
|
||||||
|
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).onion, jsonEncode(cm));
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
Provider.of<AppState>(context, listen: false).selectedIndex = null;
|
||||||
|
_sendMessageHelper();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
ChatMessage cm = new ChatMessage(o: 1, d: ctrlrCompose.value.text);
|
ChatMessage cm = new ChatMessage(o: 1, d: ctrlrCompose.value.text);
|
||||||
Provider.of<FlwtchState>(context, listen: false)
|
Provider.of<FlwtchState>(context, listen: false)
|
||||||
.cwtch
|
.cwtch
|
||||||
|
@ -111,6 +133,7 @@ class _MessageViewState extends State<MessageView> {
|
||||||
_sendMessageHelper();
|
_sendMessageHelper();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _sendInvitation([String? ignoredParam]) {
|
void _sendInvitation([String? ignoredParam]) {
|
||||||
Provider.of<FlwtchState>(context, listen: false)
|
Provider.of<FlwtchState>(context, listen: false)
|
||||||
|
@ -130,7 +153,7 @@ class _MessageViewState extends State<MessageView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildComposeBox() {
|
Widget _buildComposeBox() {
|
||||||
return Container(
|
var composeBox = Container(
|
||||||
color: Provider.of<Settings>(context).theme.backgroundMainColor(),
|
color: Provider.of<Settings>(context).theme.backgroundMainColor(),
|
||||||
padding: EdgeInsets.all(2),
|
padding: EdgeInsets.all(2),
|
||||||
margin: EdgeInsets.all(2),
|
margin: EdgeInsets.all(2),
|
||||||
|
@ -174,6 +197,40 @@ class _MessageViewState extends State<MessageView> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var children;
|
||||||
|
if (Provider.of<AppState>(context).selectedConversation != null && Provider.of<AppState>(context).selectedIndex != null) {
|
||||||
|
var quoted = FutureBuilder(
|
||||||
|
future: Provider.of<FlwtchState>(context)
|
||||||
|
.cwtch
|
||||||
|
.GetMessage(Provider.of<AppState>(context).selectedProfile!, Provider.of<AppState>(context).selectedConversation!, Provider.of<AppState>(context).selectedIndex!),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
try {
|
||||||
|
var messageWrapper = jsonDecode(snapshot.data! as String);
|
||||||
|
dynamic message = jsonDecode(messageWrapper['Message']);
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.all(5),
|
||||||
|
padding: EdgeInsets.all(5),
|
||||||
|
color: messageWrapper['PeerID'] != Provider.of<AppState>(context).selectedProfile
|
||||||
|
? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor()
|
||||||
|
: Provider.of<Settings>(context).theme.messageFromMeBackgroundColor(),
|
||||||
|
child: Text(message["d"]));
|
||||||
|
} catch (e) {
|
||||||
|
return MalformedBubble();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Text("");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
children = [quoted, composeBox];
|
||||||
|
} else {
|
||||||
|
children = [composeBox];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(mainAxisSize: MainAxisSize.min, children: children);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the message if enter is pressed without the shift key...
|
// Send the message if enter is pressed without the shift key...
|
||||||
|
@ -181,11 +238,8 @@ class _MessageViewState extends State<MessageView> {
|
||||||
var data = event.data as RawKeyEventData;
|
var data = event.data as RawKeyEventData;
|
||||||
if (data.logicalKey == LogicalKeyboardKey.enter && !event.isShiftPressed) {
|
if (data.logicalKey == LogicalKeyboardKey.enter && !event.isShiftPressed) {
|
||||||
final messageWithoutNewLine = ctrlrCompose.value.text.trimRight();
|
final messageWithoutNewLine = ctrlrCompose.value.text.trimRight();
|
||||||
ctrlrCompose.value = TextEditingValue(
|
ctrlrCompose.value = TextEditingValue(text: messageWithoutNewLine);
|
||||||
text: messageWithoutNewLine
|
|
||||||
);
|
|
||||||
_sendMessage();
|
_sendMessage();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,7 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(contact.onion)!.unreadMessages = 0;
|
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(contact.onion)!.unreadMessages = 0;
|
||||||
// triggers update in Double/TripleColumnView
|
// triggers update in Double/TripleColumnView
|
||||||
Provider.of<AppState>(context, listen: false).selectedConversation = contact.onion;
|
Provider.of<AppState>(context, listen: false).selectedConversation = contact.onion;
|
||||||
|
Provider.of<AppState>(context, listen: false).selectedIndex = null;
|
||||||
// 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(contact.onion);
|
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(contact.onion);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:cwtch/widgets/quotedmessage.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cwtch/widgets/profileimage.dart';
|
import 'package:cwtch/widgets/profileimage.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
@ -33,14 +34,18 @@ class _MessageRowState extends State<MessageRow> {
|
||||||
|
|
||||||
Widget wdgBubble =
|
Widget wdgBubble =
|
||||||
Flexible(flex: 3, fit: FlexFit.loose, child: Provider.of<MessageState>(context).loaded == true ? widgetForOverlay(Provider.of<MessageState>(context).overlay) : MessageLoadingBubble());
|
Flexible(flex: 3, fit: FlexFit.loose, child: Provider.of<MessageState>(context).loaded == true ? widgetForOverlay(Provider.of<MessageState>(context).overlay) : MessageLoadingBubble());
|
||||||
Widget wdgIcons = Icon(Icons.delete_forever_outlined, color: Provider.of<Settings>(context).theme.dropShadowColor());
|
Widget wdgIcons = IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Provider.of<AppState>(context, listen: false).selectedIndex = Provider.of<MessageState>(context).messageIndex;
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.reply, color: Provider.of<Settings>(context).theme.dropShadowColor()));
|
||||||
Widget wdgSpacer = Expanded(child: SizedBox(width: 60, height: 10));
|
Widget wdgSpacer = Expanded(child: SizedBox(width: 60, height: 10));
|
||||||
var widgetRow = <Widget>[];
|
var widgetRow = <Widget>[];
|
||||||
|
|
||||||
if (fromMe) {
|
if (fromMe) {
|
||||||
widgetRow = <Widget>[
|
widgetRow = <Widget>[
|
||||||
wdgSpacer,
|
wdgSpacer,
|
||||||
//wdgIcons,
|
wdgIcons,
|
||||||
wdgBubble,
|
wdgBubble,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,7 +65,7 @@ class _MessageRowState extends State<MessageRow> {
|
||||||
widgetRow = <Widget>[
|
widgetRow = <Widget>[
|
||||||
wdgPortrait,
|
wdgPortrait,
|
||||||
wdgBubble,
|
wdgBubble,
|
||||||
//wdgIcons,
|
wdgIcons,
|
||||||
wdgSpacer,
|
wdgSpacer,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -75,6 +80,8 @@ class _MessageRowState extends State<MessageRow> {
|
||||||
case 100:
|
case 100:
|
||||||
case 101:
|
case 101:
|
||||||
return InvitationBubble();
|
return InvitationBubble();
|
||||||
|
case 10:
|
||||||
|
return QuotedMessageBubble();
|
||||||
}
|
}
|
||||||
return MalformedBubble();
|
return MalformedBubble();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:cwtch/main.dart';
|
||||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
@ -7,6 +10,22 @@ import 'package:intl/intl.dart';
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
import 'messagebubbledecorations.dart';
|
import 'messagebubbledecorations.dart';
|
||||||
|
|
||||||
|
class LocallyIndexedMessage {
|
||||||
|
final dynamic message;
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
LocallyIndexedMessage(this.message, this.index);
|
||||||
|
|
||||||
|
LocallyIndexedMessage.fromJson(Map<String, dynamic> json)
|
||||||
|
: message = json['Message'],
|
||||||
|
index = json['LocalIndex'];
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'Message': message,
|
||||||
|
'LocalIndex': index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class QuotedMessageBubble extends StatefulWidget {
|
class QuotedMessageBubble extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
QuotedMessageBubbleState createState() => QuotedMessageBubbleState();
|
QuotedMessageBubbleState createState() => QuotedMessageBubbleState();
|
||||||
|
@ -22,6 +41,27 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
||||||
var borderRadiousEh = 15.0;
|
var borderRadiousEh = 15.0;
|
||||||
var myKey = Provider.of<MessageState>(context).profileOnion + "::" + Provider.of<MessageState>(context).contactHandle + "::" + Provider.of<MessageState>(context).messageIndex.toString();
|
var myKey = Provider.of<MessageState>(context).profileOnion + "::" + Provider.of<MessageState>(context).contactHandle + "::" + Provider.of<MessageState>(context).messageIndex.toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
dynamic message = jsonDecode(Provider.of<MessageState>(context).message);
|
||||||
|
|
||||||
|
var quotedMessagePotentials =
|
||||||
|
Provider.of<FlwtchState>(context).cwtch.GetMessageByContentHash(Provider.of<MessageState>(context).profileOnion, Provider.of<MessageState>(context).contactHandle, message["quotedHash"]);
|
||||||
|
int messageIndex = Provider.of<MessageState>(context).messageIndex;
|
||||||
|
var quotedMessage = quotedMessagePotentials.then((matchingMessages) {
|
||||||
|
// reverse order the messages from newest to oldest and return the
|
||||||
|
// first matching message where it's index is less than the index of this
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
return candidate;
|
||||||
|
} catch (e) {
|
||||||
|
// Malformed Message will be returned...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (Provider.of<MessageState>(context).timestamp != null) {
|
if (Provider.of<MessageState>(context).timestamp != null) {
|
||||||
// user-configurable timestamps prolly ideal? #todo
|
// user-configurable timestamps prolly ideal? #todo
|
||||||
DateTime messageDate = Provider.of<MessageState>(context).timestamp;
|
DateTime messageDate = Provider.of<MessageState>(context).timestamp;
|
||||||
|
@ -42,7 +82,7 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
||||||
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor()));
|
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor()));
|
||||||
|
|
||||||
var wdgMessage = SelectableText(
|
var wdgMessage = SelectableText(
|
||||||
(Provider.of<MessageState>(context).message ?? "") + '\u202F',
|
(message["body"] ?? "") + '\u202F',
|
||||||
key: Key(myKey),
|
key: Key(myKey),
|
||||||
focusNode: _focus,
|
focusNode: _focus,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -52,12 +92,30 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
||||||
textWidthBasis: TextWidthBasis.longestLine,
|
textWidthBasis: TextWidthBasis.longestLine,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var wdgQuote = FutureBuilder(
|
||||||
|
future: quotedMessage,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
var lim = (snapshot.data! as LocallyIndexedMessage);
|
||||||
|
var limmessage = lim.message;
|
||||||
|
// Swap the background color for quoted tweets..
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.all(5),
|
||||||
|
padding: EdgeInsets.all(5),
|
||||||
|
color: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor() : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor(),
|
||||||
|
child: Text(jsonDecode(limmessage)["d"]));
|
||||||
|
} else {
|
||||||
|
// This should be almost instantly resolved, any failure likely means an issue in decoding...
|
||||||
|
return MalformedBubble();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
var wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageState>(context).ackd, errored: Provider.of<MessageState>(context).error, fromMe: fromMe, prettyDate: prettyDate);
|
var wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageState>(context).ackd, errored: Provider.of<MessageState>(context).error, fromMe: fromMe, prettyDate: prettyDate);
|
||||||
|
|
||||||
var error = Provider.of<MessageState>(context).error;
|
var error = Provider.of<MessageState>(context).error;
|
||||||
|
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(builder: (context, constraints) {
|
||||||
//print(constraints.toString()+", "+constraints.maxWidth.toString());
|
|
||||||
return RepaintBoundary(
|
return RepaintBoundary(
|
||||||
child: Container(
|
child: Container(
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@ -83,7 +141,10 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
||||||
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
|
mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))));
|
children: fromMe ? [wdgQuote, wdgMessage, wdgDecorations] : [wdgSender, wdgQuote, wdgMessage, wdgDecorations])))));
|
||||||
});
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return MalformedBubble();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.15.0"
|
||||||
|
crypto:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -35,6 +35,7 @@ dependencies:
|
||||||
ffi: ^1.0.0
|
ffi: ^1.0.0
|
||||||
path_provider: ^2.0.0
|
path_provider: ^2.0.0
|
||||||
desktop_notifications: 0.5.0
|
desktop_notifications: 0.5.0
|
||||||
|
crypto: 3.0.1
|
||||||
|
|
||||||
glob: any
|
glob: any
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in New Issue