Line data Source code
1 : import 'package:cwtch/models/contact.dart';
2 : import 'package:cwtch/models/message.dart';
3 : import 'package:cwtch/models/profile.dart';
4 : import 'package:cwtch/widgets/malformedbubble.dart';
5 : import 'package:cwtch/widgets/messageloadingbubble.dart';
6 : import 'package:flutter/material.dart';
7 : import 'package:provider/provider.dart';
8 : import 'package:cwtch/l10n/app_localizations.dart';
9 : import '../models/redaction.dart';
10 : import '../settings.dart';
11 : import 'messageBubbleWidgetHelpers.dart';
12 : import 'messagebubbledecorations.dart';
13 :
14 : class QuotedMessageBubble extends StatefulWidget {
15 : final Future<Message> quotedMessage;
16 : final String body;
17 :
18 0 : QuotedMessageBubble(this.body, this.quotedMessage);
19 :
20 0 : @override
21 0 : QuotedMessageBubbleState createState() => QuotedMessageBubbleState();
22 : }
23 :
24 : class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
25 : FocusNode _focus = FocusNode();
26 :
27 0 : @override
28 : Widget build(BuildContext context) {
29 0 : var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
30 : var borderRadiousEh = 15.0;
31 :
32 0 : DateTime messageDate = Provider.of<MessageMetadata>(context).timestamp;
33 :
34 : // If the sender is not us, then we want to give them a nickname...
35 : var senderDisplayStr = "";
36 : if (!fromMe) {
37 0 : ContactInfoState? contact = Provider.of<ProfileInfoState>(context).contactList.findContact(Provider.of<MessageMetadata>(context).senderHandle);
38 : if (contact != null) {
39 0 : senderDisplayStr = redactedNick(context, contact.onion, contact.nickname);
40 : } else {
41 0 : senderDisplayStr = Provider.of<MessageMetadata>(context).senderHandle;
42 : }
43 : }
44 :
45 0 : var showClickableLinks = Provider.of<Settings>(context).isExperimentEnabled(ClickableLinksExperiment);
46 0 : var formatMessages = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
47 0 : Size size = MediaQuery.of(context).size;
48 0 : BoxConstraints constraints = BoxConstraints.loose(size);
49 0 : Widget wdgMessage = compileMessageContentWidget(context, constraints, fromMe, widget.body, _focus, formatMessages, showClickableLinks);
50 0 : var wdgQuote = FutureBuilder(
51 0 : future: widget.quotedMessage,
52 0 : builder: (context, snapshot) {
53 0 : if (snapshot.hasData) {
54 : try {
55 0 : var qMessage = (snapshot.data! as Message);
56 :
57 : // If the sender is not us, then we want to give them a nickname...
58 : String qMessageSender;
59 :
60 : // if we sent the quoted message then display our nickname
61 0 : if (qMessage.getMetadata().senderHandle == Provider.of<ProfileInfoState>(context).onion) {
62 0 : qMessageSender = Provider.of<ProfileInfoState>(context).nickname;
63 : } else {
64 : // default to handle
65 0 : qMessageSender = qMessage.getMetadata().senderHandle;
66 : // if we have the handle as a contact then replace with the nickname...
67 0 : ContactInfoState? contact = Provider.of<ProfileInfoState>(context).contactList.findContact(qMessageSender);
68 : if (contact != null) {
69 0 : qMessageSender = contact.nickname;
70 : }
71 : }
72 :
73 0 : var qTextColor = fromMe ? Provider.of<Settings>(context).theme.messageFromOtherTextColor : Provider.of<Settings>(context).theme.messageFromMeTextColor;
74 :
75 0 : var wdgReplyingTo = SelectableText(
76 0 : AppLocalizations.of(context)!.replyingTo.replaceAll("%1", qMessageSender),
77 0 : style: Provider.of<Settings>(context).scaleFonts(TextStyle(fontSize: 10, color: qTextColor.withOpacity(0.8))),
78 : );
79 : // Swap the background color for quoted tweets..
80 0 : return MouseRegion(
81 : cursor: SystemMouseCursors.click,
82 0 : child: GestureDetector(
83 0 : onTap: () {
84 0 : var messageInfo = Provider.of<ContactInfoState>(context, listen: false).messageCache.getByContentHash(qMessage.getMetadata().contenthash);
85 : if (messageInfo != null) {
86 0 : var index = Provider.of<ContactInfoState>(context, listen: false).messageCache.findIndex(messageInfo.metadata.messageID);
87 0 : Provider.of<ContactInfoState>(context, listen: false).messageScrollController.scrollTo(index: index, duration: Duration(milliseconds: 100));
88 : }
89 : },
90 0 : child: Container(
91 0 : margin: EdgeInsets.all(5),
92 0 : padding: EdgeInsets.all(5),
93 : clipBehavior: Clip.antiAliasWithSaveLayer,
94 0 : decoration: BoxDecoration(color: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor),
95 : height: 75,
96 0 : child: Column(
97 : crossAxisAlignment: CrossAxisAlignment.start,
98 : mainAxisSize: MainAxisSize.min,
99 0 : children: [
100 0 : Align(alignment: Alignment.centerLeft, child: wdgReplyingTo),
101 0 : Flexible(
102 0 : child: Row(
103 : mainAxisSize: MainAxisSize.max,
104 : mainAxisAlignment: MainAxisAlignment.start,
105 : crossAxisAlignment: CrossAxisAlignment.center,
106 0 : children: [
107 0 : Padding(
108 0 : padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
109 0 : child: Icon(Icons.reply, size: 32, color: qTextColor),
110 : ),
111 0 : Flexible(child: IntrinsicWidth(child: qMessage.getPreviewWidget(context))),
112 : ],
113 : ),
114 : ),
115 : ],
116 : ),
117 : ),
118 : ),
119 : );
120 : } catch (e) {
121 0 : return MalformedBubble();
122 : }
123 : } else {
124 : // This should be almost instantly resolved, any failure likely means an issue in decoding...
125 0 : return MessageLoadingBubble();
126 : }
127 : },
128 : );
129 :
130 0 : var wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, messageDate: messageDate);
131 :
132 0 : var error = Provider.of<MessageMetadata>(context).error;
133 0 : var wdgSender = compileSenderWidget(context, constraints, fromMe, senderDisplayStr);
134 0 : return Container(
135 0 : decoration: BoxDecoration(
136 0 : color: error ? malformedColor : (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor),
137 0 : border: Border.all(
138 0 : color: error ? malformedColor : (fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor),
139 : width: 1,
140 : ),
141 0 : borderRadius: BorderRadius.only(
142 0 : topLeft: Radius.circular(borderRadiousEh),
143 0 : topRight: Radius.circular(borderRadiousEh),
144 0 : bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero,
145 0 : bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh),
146 : ),
147 : ),
148 0 : child: Padding(
149 0 : padding: EdgeInsets.all(9.0),
150 0 : child: Theme(
151 0 : data: Theme.of(context).copyWith(
152 0 : textSelectionTheme: TextSelectionThemeData(
153 0 : cursorColor: Provider.of<Settings>(context).theme.messageSelectionColor,
154 0 : selectionColor: Provider.of<Settings>(context).theme.messageSelectionColor,
155 0 : selectionHandleColor: Provider.of<Settings>(context).theme.messageSelectionColor,
156 : ),
157 :
158 : // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯
159 0 : textButtonTheme: TextButtonThemeData(style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.menuBackgroundColor))),
160 : ),
161 0 : child: IntrinsicWidth(
162 0 : child: Column(
163 : crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
164 : mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
165 : mainAxisSize: MainAxisSize.min,
166 : verticalDirection: VerticalDirection.up,
167 0 : children: fromMe ? [wdgDecorations, wdgMessage, wdgQuote] : [wdgDecorations, wdgMessage, wdgQuote, wdgSender],
168 : ),
169 : ),
170 : ),
171 : ),
172 : );
173 : }
174 : }
|