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")
|
||||
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" -> {
|
||||
val profile = (a.get("profile") as? String) ?: ""
|
||||
val handle = (a.get("contact") as? String) ?: ""
|
||||
|
|
|
@ -32,6 +32,8 @@ abstract class Cwtch {
|
|||
// ignore: non_constant_identifier_names
|
||||
Future<dynamic> GetMessage(String profile, String handle, int index);
|
||||
// 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);
|
||||
// ignore: non_constant_identifier_names
|
||||
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 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 {
|
||||
typedef get_json_blob_from_str_str_int_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32, Int32);
|
||||
typedef GetJsonBlobFromStrStrIntIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int, int);
|
||||
// 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_str_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Pointer<Utf8>, Int32);
|
||||
typedef GetJsonBlobFromStrStrStrFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, Pointer<Utf8>, int);
|
||||
|
||||
typedef appbus_events_function = Pointer<Utf8> Function();
|
||||
typedef AppbusEventsFn = Pointer<Utf8> Function();
|
||||
|
@ -397,4 +397,17 @@ class CwtchFfi implements Cwtch {
|
|||
_receivePort.close();
|
||||
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");
|
||||
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? _selectedProfile;
|
||||
String? _selectedConversation;
|
||||
int? _selectedIndex;
|
||||
|
||||
void SetCwtchInit() {
|
||||
cwtchInit = true;
|
||||
|
@ -90,6 +91,12 @@ class AppState extends ChangeNotifier {
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -457,7 +464,7 @@ class MessageState extends ChangeNotifier {
|
|||
final String profileOnion;
|
||||
final String contactHandle;
|
||||
final int messageIndex;
|
||||
late String _message;
|
||||
late dynamic _message;
|
||||
late int _overlay;
|
||||
late String _inviteTarget;
|
||||
late String _inviteNick;
|
||||
|
@ -535,7 +542,7 @@ class MessageState extends ChangeNotifier {
|
|||
return;
|
||||
}
|
||||
dynamic message = jsonDecode(messageWrapper['Message']);
|
||||
this._message = message['d'];
|
||||
this._message = message['d'] as dynamic;
|
||||
this._overlay = int.parse(message['o'].toString());
|
||||
this._timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
|
||||
this._senderOnion = messageWrapper['PeerID'];
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:cwtch/cwtch_icons_icons.dart';
|
||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||
import 'package:cwtch/widgets/profileimage.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -104,11 +105,33 @@ class _MessageViewState extends State<MessageView> {
|
|||
|
||||
void _sendMessage([String? ignoredParam]) {
|
||||
if (ctrlrCompose.value.text.isNotEmpty) {
|
||||
ChatMessage cm = new ChatMessage(o: 1, d: ctrlrCompose.value.text);
|
||||
Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).onion, jsonEncode(cm));
|
||||
_sendMessageHelper();
|
||||
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);
|
||||
Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).onion, jsonEncode(cm));
|
||||
_sendMessageHelper();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +153,7 @@ class _MessageViewState extends State<MessageView> {
|
|||
}
|
||||
|
||||
Widget _buildComposeBox() {
|
||||
return Container(
|
||||
var composeBox = Container(
|
||||
color: Provider.of<Settings>(context).theme.backgroundMainColor(),
|
||||
padding: EdgeInsets.all(2),
|
||||
margin: EdgeInsets.all(2),
|
||||
|
@ -141,52 +164,83 @@ class _MessageViewState extends State<MessageView> {
|
|||
child: Container(
|
||||
decoration: BoxDecoration(border: Border(top: BorderSide(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor()))),
|
||||
child: RawKeyboardListener(
|
||||
focusNode: FocusNode(),
|
||||
onKey: handleKeyPress,
|
||||
child: TextFormField(
|
||||
key: Key('txtCompose'),
|
||||
controller: ctrlrCompose,
|
||||
focusNode: focusNode,
|
||||
autofocus: !Platform.isAndroid,
|
||||
textInputAction: TextInputAction.newline,
|
||||
keyboardType: TextInputType.multiline,
|
||||
minLines: 1,
|
||||
maxLines: null,
|
||||
onFieldSubmitted: _sendMessage,
|
||||
decoration: InputDecoration(
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabled: true,
|
||||
prefixIcon: IconButton(
|
||||
icon: Icon(CwtchIcons.send_invite, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
|
||||
tooltip: AppLocalizations.of(context)!.sendInvite,
|
||||
enableFeedback: true,
|
||||
splashColor: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||
hoverColor: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||
onPressed: () => _modalSendInvitation(context)),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
|
||||
tooltip: AppLocalizations.of(context)!.sendMessage,
|
||||
onPressed: _sendMessage,
|
||||
),
|
||||
)))),
|
||||
focusNode: FocusNode(),
|
||||
onKey: handleKeyPress,
|
||||
child: TextFormField(
|
||||
key: Key('txtCompose'),
|
||||
controller: ctrlrCompose,
|
||||
focusNode: focusNode,
|
||||
autofocus: !Platform.isAndroid,
|
||||
textInputAction: TextInputAction.newline,
|
||||
keyboardType: TextInputType.multiline,
|
||||
minLines: 1,
|
||||
maxLines: null,
|
||||
onFieldSubmitted: _sendMessage,
|
||||
decoration: InputDecoration(
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabled: true,
|
||||
prefixIcon: IconButton(
|
||||
icon: Icon(CwtchIcons.send_invite, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
|
||||
tooltip: AppLocalizations.of(context)!.sendInvite,
|
||||
enableFeedback: true,
|
||||
splashColor: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||
hoverColor: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||
onPressed: () => _modalSendInvitation(context)),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
|
||||
tooltip: AppLocalizations.of(context)!.sendMessage,
|
||||
onPressed: _sendMessage,
|
||||
),
|
||||
)))),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
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...
|
||||
void handleKeyPress(event) {
|
||||
var data = event.data as RawKeyEventData;
|
||||
if (data.logicalKey == LogicalKeyboardKey.enter && !event.isShiftPressed) {
|
||||
final messageWithoutNewLine = ctrlrCompose.value.text.trimRight();
|
||||
ctrlrCompose.value = TextEditingValue(
|
||||
text: messageWithoutNewLine
|
||||
);
|
||||
_sendMessage();
|
||||
|
||||
}
|
||||
var data = event.data as RawKeyEventData;
|
||||
if (data.logicalKey == LogicalKeyboardKey.enter && !event.isShiftPressed) {
|
||||
final messageWithoutNewLine = ctrlrCompose.value.text.trimRight();
|
||||
ctrlrCompose.value = TextEditingValue(text: messageWithoutNewLine);
|
||||
_sendMessage();
|
||||
}
|
||||
}
|
||||
|
||||
void placeHolder() => {};
|
||||
|
|
|
@ -98,6 +98,7 @@ class _ContactRowState extends State<ContactRow> {
|
|||
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(contact.onion)!.unreadMessages = 0;
|
||||
// triggers update in Double/TripleColumnView
|
||||
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
|
||||
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
|
||||
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(contact.onion);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cwtch/widgets/quotedmessage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cwtch/widgets/profileimage.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
@ -33,14 +34,18 @@ class _MessageRowState extends State<MessageRow> {
|
|||
|
||||
Widget wdgBubble =
|
||||
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));
|
||||
var widgetRow = <Widget>[];
|
||||
|
||||
if (fromMe) {
|
||||
widgetRow = <Widget>[
|
||||
wdgSpacer,
|
||||
//wdgIcons,
|
||||
wdgIcons,
|
||||
wdgBubble,
|
||||
];
|
||||
} else {
|
||||
|
@ -60,7 +65,7 @@ class _MessageRowState extends State<MessageRow> {
|
|||
widgetRow = <Widget>[
|
||||
wdgPortrait,
|
||||
wdgBubble,
|
||||
//wdgIcons,
|
||||
wdgIcons,
|
||||
wdgSpacer,
|
||||
];
|
||||
}
|
||||
|
@ -75,6 +80,8 @@ class _MessageRowState extends State<MessageRow> {
|
|||
case 100:
|
||||
case 101:
|
||||
return InvitationBubble();
|
||||
case 10:
|
||||
return QuotedMessageBubble();
|
||||
}
|
||||
return MalformedBubble();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cwtch/main.dart';
|
||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
@ -7,6 +10,22 @@ import 'package:intl/intl.dart';
|
|||
import '../settings.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 {
|
||||
@override
|
||||
QuotedMessageBubbleState createState() => QuotedMessageBubbleState();
|
||||
|
@ -22,68 +41,110 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
|||
var borderRadiousEh = 15.0;
|
||||
var myKey = Provider.of<MessageState>(context).profileOnion + "::" + Provider.of<MessageState>(context).contactHandle + "::" + Provider.of<MessageState>(context).messageIndex.toString();
|
||||
|
||||
if (Provider.of<MessageState>(context).timestamp != null) {
|
||||
// user-configurable timestamps prolly ideal? #todo
|
||||
DateTime messageDate = Provider.of<MessageState>(context).timestamp;
|
||||
prettyDate = DateFormat.yMd().add_jm().format(messageDate.toLocal());
|
||||
}
|
||||
try {
|
||||
dynamic message = jsonDecode(Provider.of<MessageState>(context).message);
|
||||
|
||||
// If the sender is not us, then we want to give them a nickname...
|
||||
var senderDisplayStr = "";
|
||||
if (!fromMe && Provider.of<MessageState>(context).senderOnion != null) {
|
||||
ContactInfoState? contact = Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageState>(context).senderOnion);
|
||||
if (contact != null) {
|
||||
senderDisplayStr = contact.nickname;
|
||||
} else {
|
||||
senderDisplayStr = Provider.of<MessageState>(context).senderOnion;
|
||||
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) {
|
||||
// user-configurable timestamps prolly ideal? #todo
|
||||
DateTime messageDate = Provider.of<MessageState>(context).timestamp;
|
||||
prettyDate = DateFormat.yMd().add_jm().format(messageDate.toLocal());
|
||||
}
|
||||
}
|
||||
var wdgSender = SelectableText(senderDisplayStr,
|
||||
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor()));
|
||||
|
||||
var wdgMessage = SelectableText(
|
||||
(Provider.of<MessageState>(context).message ?? "") + '\u202F',
|
||||
key: Key(myKey),
|
||||
focusNode: _focus,
|
||||
style: TextStyle(
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
);
|
||||
// If the sender is not us, then we want to give them a nickname...
|
||||
var senderDisplayStr = "";
|
||||
if (!fromMe && Provider.of<MessageState>(context).senderOnion != null) {
|
||||
ContactInfoState? contact = Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageState>(context).senderOnion);
|
||||
if (contact != null) {
|
||||
senderDisplayStr = contact.nickname;
|
||||
} else {
|
||||
senderDisplayStr = Provider.of<MessageState>(context).senderOnion;
|
||||
}
|
||||
}
|
||||
var wdgSender = SelectableText(senderDisplayStr,
|
||||
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor()));
|
||||
|
||||
var wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageState>(context).ackd, errored: Provider.of<MessageState>(context).error, fromMe: fromMe, prettyDate: prettyDate);
|
||||
var wdgMessage = SelectableText(
|
||||
(message["body"] ?? "") + '\u202F',
|
||||
key: Key(myKey),
|
||||
focusNode: _focus,
|
||||
style: TextStyle(
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
);
|
||||
|
||||
var error = Provider.of<MessageState>(context).error;
|
||||
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();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
//print(constraints.toString()+", "+constraints.maxWidth.toString());
|
||||
return RepaintBoundary(
|
||||
child: Container(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: error
|
||||
? malformedColor
|
||||
: (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor()),
|
||||
border: Border.all(
|
||||
color: error
|
||||
? malformedColor
|
||||
: (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor()),
|
||||
width: 1),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(borderRadiousEh),
|
||||
topRight: Radius.circular(borderRadiousEh),
|
||||
bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero,
|
||||
bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh),
|
||||
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;
|
||||
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
return RepaintBoundary(
|
||||
child: Container(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: error
|
||||
? malformedColor
|
||||
: (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor()),
|
||||
border: Border.all(
|
||||
color: error
|
||||
? malformedColor
|
||||
: (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor()),
|
||||
width: 1),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(borderRadiousEh),
|
||||
topRight: Radius.circular(borderRadiousEh),
|
||||
bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero,
|
||||
bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(9.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
||||
mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))));
|
||||
});
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(9.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
|
||||
mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: fromMe ? [wdgQuote, wdgMessage, wdgDecorations] : [wdgSender, wdgQuote, wdgMessage, wdgDecorations])))));
|
||||
});
|
||||
} catch (e) {
|
||||
return MalformedBubble();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.15.0"
|
||||
crypto:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -35,6 +35,7 @@ dependencies:
|
|||
ffi: ^1.0.0
|
||||
path_provider: ^2.0.0
|
||||
desktop_notifications: 0.5.0
|
||||
crypto: 3.0.1
|
||||
|
||||
glob: any
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in New Issue