Skip to content

Commit

Permalink
Merge pull request #154 from getlantern/crdzbird/permission_handler
Browse files Browse the repository at this point in the history
Improvements on messaging UI, and fix on minor bugs on NestedScroll
  • Loading branch information
oxtoacart authored Jun 23, 2021
2 parents 2720fe1 + 15d8800 commit 1edc354
Show file tree
Hide file tree
Showing 16 changed files with 804 additions and 504 deletions.
491 changes: 199 additions & 292 deletions lib/messaging/conversation.dart

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion lib/messaging/messaging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ class _MessagesTabState extends State<MessagesTab>
break;
case '/conversation':
builder = (BuildContext context) => Conversation(
settings.arguments ?? widget._initialRouteArguments);
settings.arguments ?? widget._initialRouteArguments,
);
break;
default:
throw Exception('unknown route ${settings.name}');
Expand Down
23 changes: 23 additions & 0 deletions lib/messaging/widgets/countdown_timer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:stop_watch_timer/stop_watch_timer.dart';

class CountdownTimer extends StatelessWidget {
final StopWatchTimer stopWatchTimer;
const CountdownTimer({required this.stopWatchTimer, Key? key})
: super(key: key);

@override
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: stopWatchTimer.rawTime,
initialData: stopWatchTimer.rawTime.value,
builder: (context, snap) {
final value = snap.data;
final displayTime = StopWatchTimer.getDisplayTime(value ?? 0,
minute: true, second: true, hours: false, milliSecond: false);
return Text(displayTime,
style: const TextStyle(fontWeight: FontWeight.bold));
},
);
}
}
150 changes: 150 additions & 0 deletions lib/messaging/widgets/message_bar.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import 'package:flutter/material.dart';
import 'package:lantern/messaging/widgets/countdown_timer.dart';
import 'package:lantern/messaging/widgets/voice_recorder.dart';
import 'package:lantern/package_store.dart';
import 'package:stop_watch_timer/stop_watch_timer.dart';

class MessageBar extends StatelessWidget {
final bool displayEmojis;
final bool willCancelRecording;
final VoidCallback? onEmojiTap;
final VoidCallback onTextFieldTap;
final VoidCallback? onSend;
final VoidCallback? onFileSend;
final VoidCallback onRecording;
final VoidCallback onStopRecording;
final Function(String)? onTextFieldChanged;
final Function(String)? onFieldSubmitted;
final TextEditingController messageController;
final StopWatchTimer stopWatchTimer;
final bool hasPermission;
final FocusNode? focusNode;
final bool sendIcon;
final bool isRecording;
final double width;
final double height;
final VoidCallback onSwipeLeft;
final Function onTapUpListener;

MessageBar(
{this.onEmojiTap,
this.focusNode,
required this.onSwipeLeft,
required this.onTapUpListener,
required this.willCancelRecording,
required this.stopWatchTimer,
required this.isRecording,
required this.onSend,
required this.width,
required this.height,
required this.hasPermission,
required this.onRecording,
required this.onStopRecording,
required this.onTextFieldChanged,
required this.messageController,
required this.onFieldSubmitted,
required this.onFileSend,
required this.displayEmojis,
required this.onTextFieldTap,
this.sendIcon = false,
Key? key})
: super(key: key);

@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
margin: isRecording
? const EdgeInsets.only(right: 0, left: 8.0, bottom: 0)
: const EdgeInsets.only(right: 1.0, left: 1.0, bottom: 6.0),
child: ListTile(
contentPadding: isRecording
? const EdgeInsets.only(right: 0, left: 8.0, bottom: 0)
: const EdgeInsets.only(right: 8.0, left: 8.0, bottom: 0),
leading: isRecording
? Flex(
direction: Axis.horizontal,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Flexible(
child: CircleAvatar(
backgroundColor: Colors.red,
radius: 12,
),
),
Flexible(
child: Padding(
padding: const EdgeInsets.only(left: 16),
child: CountdownTimer(stopWatchTimer: stopWatchTimer),
),
),
],
)
: IconButton(
onPressed: onEmojiTap,
icon: Icon(Icons.sentiment_very_satisfied,
color: !displayEmojis
? Theme.of(context).primaryIconTheme.color
: Theme.of(context).primaryColorDark),
),
title: isRecording
? Padding(
padding: isRecording
? EdgeInsets.zero
: const EdgeInsets.only(bottom: 8.0, right: 2.0),
child: Text(
willCancelRecording
? 'will cancel'.i18n
: '< ' + 'swipe to cancel'.i18n,
style: const TextStyle(fontWeight: FontWeight.bold),
),
)
: TextFormField(
autofocus: false,
textInputAction: TextInputAction.send,
controller: messageController,
onTap: onTextFieldTap,
onChanged: onTextFieldChanged,
focusNode: focusNode,
onFieldSubmitted: onFieldSubmitted,
decoration: InputDecoration(
// Send icon
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
hintText: 'Message'.i18n,
border: const OutlineInputBorder(),
),
),
trailing: sendIcon && !isRecording
? IconButton(
icon: const Icon(Icons.send, color: Colors.black),
onPressed: onSend,
)
: Flex(
direction: Axis.horizontal,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
isRecording
? const SizedBox()
: IconButton(
onPressed: onFileSend,
icon: const Icon(Icons.add_circle_rounded),
),
VoiceRecorder(
willCancelRecording: willCancelRecording,
onSwipeLeft: onSwipeLeft,
isRecording: isRecording,
onStopRecording: onStopRecording,
onTapUpListener: onTapUpListener,
onRecording: onRecording,
),
],
),
),
);
}
}
34 changes: 13 additions & 21 deletions lib/messaging/widgets/message_bubble.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:lantern/messaging/conversation.dart';
import 'package:lantern/messaging/widgets/reactions.dart';
import 'package:lantern/model/model.dart';
import 'package:lantern/model/protos_flutteronly/messaging.pb.dart';
import 'package:lantern/package_store.dart';
Expand All @@ -18,9 +20,11 @@ class MessageBubble extends StatelessWidget {
required this.contact,
required this.onReply,
required this.onTapReply,
required this.onEmojiTap,
}) : super(key: key);

final PathAndValue<StoredMessage> message;
final ShowEmojis onEmojiTap;
final StoredMessage? priorMessage;
final StoredMessage? nextMessage;
final Contact contact;
Expand Down Expand Up @@ -109,26 +113,6 @@ class MessageBubble extends StatelessWidget {
BuildContext context,
MessagingModel model,
) {
final reactionOptions = reactions.keys.toList();
final reactionArray = reactionOptions
.map((e) => Container(
margin: const EdgeInsets.symmetric(vertical: 2, horizontal: 8),
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 4),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4),
child: GestureDetector(
onTap: () {
model.react(message, e);
Navigator.pop(context);
},
child: Transform.scale(
scale: 1.3,
child: Text(e, style: const TextStyle(fontSize: 16))),
),
),
))
.toList(growable: false);

if (wasDeleted) {
final humanizedSenderName =
matchIdToDisplayName(msg.remotelyDeletedBy.id, contact);
Expand All @@ -139,7 +123,15 @@ class MessageBubble extends StatelessWidget {
return FocusedMenuHolder(
menuItems: [
FocusedMenuItem(
title: Row(children: [...reactionArray]),
title: Flexible(
fit: FlexFit.tight,
child: Reactions(
onEmojiTap: onEmojiTap,
reactionOptions: reactions.keys.toList(),
message: message,
messagingModel: model,
),
),
onPressed: () {},
),
FocusedMenuItem(
Expand Down
124 changes: 67 additions & 57 deletions lib/messaging/widgets/message_types/content_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,72 +34,82 @@ class ContentContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
final reactionsList = [];
reactions.forEach((key, value) {
if (value.isNotEmpty) {
reactionsList.add(Container(
padding: const EdgeInsets.symmetric(horizontal: 8),
// Tap on emoji to bring modal with breakdown of interactions
child: GestureDetector(
reactions.forEach(
(key, value) {
if (value.isNotEmpty) {
reactionsList.add(
Container(
padding: const EdgeInsets.symmetric(horizontal: 8),
// Tap on emoji to bring modal with breakdown of interactions
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () =>
displayEmojiBreakdownPopup(context, msg, reactions),
child: displayEmojiCount(reactions, key))));
}
});
child: displayEmojiCount(reactions, key),
),
),
);
}
},
);

final attachments = msg.attachments.values
.map((attachment) => attachmentWidget(attachment));

return Container(
padding: const EdgeInsets.only(top: 4, bottom: 8, left: 8, right: 8),
constraints: const BoxConstraints(
minWidth: 70,
maxWidth: 350, // TODO: move both these to a responsive sizes file
padding: const EdgeInsets.only(top: 4, bottom: 8, left: 8, right: 8),
decoration: BoxDecoration(
color: outbound ? outboundBgColor : inboundBgColor,
borderRadius: BorderRadius.only(
topLeft:
inbound && !startOfBlock ? Radius.zero : const Radius.circular(8),
topRight: outbound && !startOfBlock
? Radius.zero
: const Radius.circular(8),
bottomRight: outbound && (!endOfBlock || newestMessage)
? Radius.zero
: const Radius.circular(8),
bottomLeft: inbound && (!endOfBlock || newestMessage)
? Radius.zero
: const Radius.circular(8),
),
decoration: BoxDecoration(
color: outbound ? outboundBgColor : inboundBgColor,
borderRadius: BorderRadius.only(
topLeft: inbound && !startOfBlock
? Radius.zero
: const Radius.circular(8),
topRight: outbound && !startOfBlock
? Radius.zero
: const Radius.circular(8),
bottomRight: outbound && (!endOfBlock || newestMessage)
? Radius.zero
: const Radius.circular(8),
bottomLeft: inbound && (!endOfBlock || newestMessage)
? Radius.zero
: const Radius.circular(8),
),
),
child: Column(
crossAxisAlignment:
outbound ? CrossAxisAlignment.end : CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(mainAxisSize: MainAxisSize.min, children: [
if (msg.replyToId.isNotEmpty)
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => onTapReply(message),
child: ReplyBubble(outbound, msg, contact),
),
]),
const Padding(padding: EdgeInsets.symmetric(vertical: 4)),
Row(mainAxisSize: MainAxisSize.min, children: [
if (msg.text.isNotEmpty)
Flexible(
child: Text(
'${msg.text}',
style: tsMessageBody(outbound),
),
child: Flex(
direction: Axis.vertical,
crossAxisAlignment:
outbound ? CrossAxisAlignment.end : CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Flex(
direction: Axis.horizontal,
mainAxisSize: MainAxisSize.min,
children: [
if (msg.replyToId.isNotEmpty)
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => onTapReply(message),
child: ReplyBubble(outbound, msg, contact),
),
]),
const Padding(padding: EdgeInsets.symmetric(vertical: 4)),
Flex(
direction: Axis.horizontal,
mainAxisSize: MainAxisSize.min,
children: [
if (msg.text.isNotEmpty)
Flexible(
child: Text(
'${msg.text}',
style: tsMessageBody(outbound),
),
),
),
]),
...attachments,
const Padding(padding: EdgeInsets.symmetric(vertical: 4)),
...reactionsList,
StatusRow(outbound, inbound, msg, message)
]));
]),
...attachments,
const Padding(padding: EdgeInsets.symmetric(vertical: 4)),
Flexible(
child: StatusRow(outbound, inbound, msg, message, reactionsList),
),
]),
);
}
}
Loading

0 comments on commit 1edc354

Please # to comment.