Merge branch 'develop' into ISSUE-1456/SendingMessageIndicatorFix

This commit is contained in:
Sahil Kumar
2023-04-07 03:29:01 +05:30
committed by GitHub
180 changed files with 1620 additions and 1279 deletions
@@ -162,8 +162,8 @@ class StreamChannelHeader extends StatelessWidget
showMessage: showConnectionStateTile && showStatus,
message: statusString,
child: AppBar(
toolbarTextStyle: theme.textTheme.bodyText2,
titleTextStyle: theme.textTheme.headline6,
toolbarTextStyle: theme.textTheme.bodyMedium,
titleTextStyle: theme.textTheme.titleLarge,
systemOverlayStyle: theme.brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark,
@@ -131,8 +131,8 @@ class StreamChannelListHeader extends StatelessWidget
showMessage: showConnectionStateTile && showStatus,
message: statusString,
child: AppBar(
toolbarTextStyle: theme.textTheme.bodyText2,
titleTextStyle: theme.textTheme.headline6,
toolbarTextStyle: theme.textTheme.bodyMedium,
titleTextStyle: theme.textTheme.titleLarge,
systemOverlayStyle: theme.brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark,
@@ -181,7 +181,10 @@ class StreamChannelListHeader extends StatelessWidget
package: 'stream_chat_flutter',
width: 24,
height: 24,
color: color,
colorFilter: ColorFilter.mode(
color,
BlendMode.srcIn,
),
);
},
),
@@ -80,8 +80,8 @@ class StreamGalleryHeader extends StatelessWidget
final galleryHeaderThemeData = StreamGalleryHeaderTheme.of(context);
final theme = Theme.of(context);
return AppBar(
toolbarTextStyle: theme.textTheme.bodyText2,
titleTextStyle: theme.textTheme.headline6,
toolbarTextStyle: theme.textTheme.bodyMedium,
titleTextStyle: theme.textTheme.titleLarge,
systemOverlayStyle: theme.brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark,
@@ -2,6 +2,7 @@ import 'dart:ui';
import 'package:flutter/material.dart' hide ButtonStyle;
import 'package:stream_chat_flutter/src/message_actions_modal/mam_widgets.dart';
import 'package:stream_chat_flutter/src/message_widget/reactions/reactions_align.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// {@template messageActionsModal}
@@ -97,34 +98,12 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
Widget _showMessageOptionsModal() {
final mediaQueryData = MediaQuery.of(context);
final size = mediaQueryData.size;
final user = StreamChat.of(context).currentUser;
final orientation = mediaQueryData.orientation;
final roughMaxSize = size.width * 2 / 3;
var messageTextLength = widget.message.text!.length;
if (widget.message.quotedMessage != null) {
var quotedMessageLength =
(widget.message.quotedMessage!.text?.length ?? 0) + 40;
if (widget.message.quotedMessage!.attachments.isNotEmpty) {
quotedMessageLength += 40;
}
if (quotedMessageLength > messageTextLength) {
messageTextLength = quotedMessageLength;
}
}
final roughSentenceSize = messageTextLength *
(widget.messageTheme.messageTextStyle?.fontSize ?? 1) *
1.2;
final divFactor = widget.message.attachments.isNotEmpty
? 1
: (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize));
final fontSize = widget.messageTheme.messageTextStyle?.fontSize;
final streamChatThemeData = StreamChatTheme.of(context);
final numberOfReactions =
StreamChatConfiguration.of(context).reactionIcons.length;
final shiftFactor =
numberOfReactions < 5 ? (5 - numberOfReactions) * 0.1 : 0.0;
final channel = StreamChannel.of(context).channel;
final child = Center(
@@ -142,11 +121,12 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
builder: (context, constraints) {
return Align(
alignment: Alignment(
_calculateReactionsHorizontalAlignmentValue(
calculateReactionsHorizontalAlignment(
user,
divFactor,
shiftFactor,
widget.message,
constraints,
fontSize,
orientation,
),
0,
),
@@ -156,7 +136,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
);
},
),
const SizedBox(height: 8),
const SizedBox(height: 10),
IgnorePointer(
child: widget.messageWidget,
),
@@ -281,93 +261,6 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
);
}
double _calculateReactionsHorizontalAlignmentValue(
User? user,
num divFactor,
double shiftFactor,
BoxConstraints constraints,
) {
var result = 0.0;
var cont = true;
if (user?.id == widget.message.user?.id) {
if (divFactor >= 1.0) {
// This calculation is hacky and does not cover all bases!!!
// A better option is needed!
// Landscape calculations
if (constraints.maxWidth == 1350) {
// 12.7 iPad Pro
result = shiftFactor + 0.5;
cont = false;
} else if (constraints.maxWidth == 1178) {
// 11 inch iPad Pro
result = shiftFactor + 0.42;
cont = false;
} else if (constraints.maxWidth == 1164) {
// iPad Air 4
result = shiftFactor + 0.4;
cont = false;
} else if (constraints.maxWidth == 1117) {
// iPad Mini 6
result = shiftFactor + 0.37;
cont = false;
} else if (constraints.maxWidth == 1064) {
// iPad 9th gen
result = shiftFactor + 0.33;
cont = false;
} else if (constraints.maxWidth == 1008) {
// 9.7 inch iPad Pro
result = shiftFactor + 0.3;
cont = false;
} else if (constraints.maxWidth >= 200 && constraints.maxWidth <= 400) {
// Phone (?)
result = shiftFactor - 0.2;
cont = false;
}
if (cont) {
// Portrait calculations
if (constraints.maxWidth == 1008) {
// 12.7 iPad Pro
result = shiftFactor + 0.3;
} else if (constraints.maxWidth == 818) {
// 11 inch iPad Pro
result = shiftFactor + 0.07;
} else if (constraints.maxWidth == 804) {
// iPad Air 4
result = shiftFactor + 0.04;
} else if (constraints.maxWidth == 794) {
// iPad 9th gen
result = shiftFactor + 0.02;
} else if (constraints.maxWidth >= 752) {
// 9.7 inch iPad Pro
result = shiftFactor - 0.05;
} else if (constraints.maxWidth == 728) {
// iPad Mini 6
result = shiftFactor - 0.1;
}
}
} else {
result = 1.2 - divFactor;
}
} else {
if (divFactor >= 1.0) {
result = shiftFactor + 0.2;
} else {
result = -(1.2 - divFactor);
}
}
// Ensure reactions don't get pushed past the edge of the screen.
//
// Hacky!!! Needs improvement!!!
if (result > 1) {
return 1;
} else {
return result;
}
}
InkWell _buildCustomAction(
BuildContext context,
StreamMessageAction messageAction,
@@ -80,7 +80,6 @@ class StreamMessageTextField extends StatefulWidget {
this.textAlignVertical,
this.textDirection,
this.readOnly = false,
ToolbarOptions? toolbarOptions,
this.showCursor,
this.autofocus = false,
this.obscuringCharacter = '',
@@ -155,31 +154,7 @@ class StreamMessageTextField extends StatefulWidget {
keyboardType = keyboardType ??
(maxLines == 1 ? TextInputType.text : TextInputType.multiline),
enableInteractiveSelection =
enableInteractiveSelection ?? (!readOnly || !obscureText),
toolbarOptions = toolbarOptions ??
(obscureText
? (readOnly
// No point in even offering "Select All" in a read-only obscured
// field.
? const ToolbarOptions()
// Writable, but obscured.
: const ToolbarOptions(
selectAll: true,
paste: true,
))
: (readOnly
// Read-only, not obscured.
? const ToolbarOptions(
selectAll: true,
copy: true,
)
// Writable, not obscured.
: const ToolbarOptions(
copy: true,
cut: true,
selectAll: true,
paste: true,
)));
enableInteractiveSelection ?? (!readOnly || !obscureText);
/// Controls the message being edited.
///
@@ -304,13 +279,6 @@ class StreamMessageTextField extends StatefulWidget {
/// {@macro flutter.widgets.editableText.readOnly}
final bool readOnly;
/// Configuration of toolbar options.
///
/// If not set, select all and paste will default to be enabled. Copy and cut
/// will be disabled if [obscureText] is true. If [readOnly] is true,
/// paste and cut will be disabled regardless.
final ToolbarOptions toolbarOptions;
/// {@macro flutter.widgets.editableText.showCursor}
final bool? showCursor;
@@ -720,7 +688,6 @@ class _StreamMessageTextFieldState extends State<StreamMessageTextField>
textAlignVertical: widget.textAlignVertical,
textDirection: widget.textDirection,
readOnly: widget.readOnly,
toolbarOptions: widget.toolbarOptions,
showCursor: widget.showCursor,
autofocus: widget.autofocus,
obscuringCharacter: widget.obscuringCharacter,
@@ -2,6 +2,7 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/src/message_widget/reactions/reaction_bubble.dart';
import 'package:stream_chat_flutter/src/message_widget/reactions/reactions_align.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// {@template streamMessageReactionsModal}
@@ -39,35 +40,13 @@ class StreamMessageReactionsModal extends StatelessWidget {
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final user = StreamChat.of(context).currentUser;
final _userPermissions = StreamChannel.of(context).channel.ownCapabilities;
final orientation = MediaQuery.of(context).orientation;
final hasReactionPermission =
_userPermissions.contains(PermissionType.sendReaction);
final roughMaxSize = size.width * 2 / 3;
var messageTextLength = message.text!.length;
if (message.quotedMessage != null) {
var quotedMessageLength = message.quotedMessage!.text!.length + 40;
if (message.quotedMessage!.attachments.isNotEmpty) {
quotedMessageLength += 40;
}
if (quotedMessageLength > messageTextLength) {
messageTextLength = quotedMessageLength;
}
}
final roughSentenceSize = messageTextLength *
(messageTheme.messageTextStyle?.fontSize ?? 1) *
1.2;
final divFactor = message.attachments.isNotEmpty
? 1
: (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize));
final numberOfReactions =
StreamChatConfiguration.of(context).reactionIcons.length;
final shiftFactor =
numberOfReactions < 5 ? (5 - numberOfReactions) * 0.1 : 0.0;
final fontSize = messageTheme.messageTextStyle?.fontSize;
final child = Center(
child: SingleChildScrollView(
@@ -79,22 +58,26 @@ class StreamMessageReactionsModal extends StatelessWidget {
children: <Widget>[
if ((showReactions ?? hasReactionPermission) &&
(message.status == MessageSendingStatus.sent))
Align(
alignment: Alignment(
user!.id == message.user!.id
? (divFactor >= 1.0
? -0.2 - shiftFactor
: (1.2 - divFactor))
: (divFactor >= 1.0
? shiftFactor + 0.2
: -(1.2 - divFactor)),
0,
),
child: StreamReactionPicker(
message: message,
),
LayoutBuilder(
builder: (context, constraints) {
return Align(
alignment: Alignment(
calculateReactionsHorizontalAlignment(
user,
message,
constraints,
fontSize,
orientation,
),
0,
),
child: StreamReactionPicker(
message: message,
),
);
},
),
const SizedBox(height: 8),
const SizedBox(height: 10),
IgnorePointer(
child: messageWidget,
),
@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// This method calculates the align that the modal of reactions should have.
/// This is an approximation based on the size of the message and the
/// available space in the screen.
double calculateReactionsHorizontalAlignment(
User? user,
Message message,
BoxConstraints constraints,
double? fontSize,
Orientation orientation,
) {
final maxWidth = constraints.maxWidth;
final roughSentenceSize = message.roughMessageSize(fontSize);
final hasAttachments = message.attachments.isNotEmpty;
final isReply = message.quotedMessageId != null;
final isAttachment = hasAttachments && !isReply;
// divFactor is the percentage of the available space that the message takes.
// When the divFactor is bigger than 0.5 that means that the messages is
// bigger than 50% of the available space and the modal should have an offset
// in the direction that the message grows. When the divFactor is smaller
// than 0.5 then the offset should be to he side opposite of the message
// growth.
// In resume, when divFactor > 0.5 then result > 0, when divFactor < 0.5
// then result < 0.
var divFactor = 0.5;
// When in portrait, attachments normally take 75% of the screen, when in
// landscape, attachments normally take 50% of the screen.
if (isAttachment) {
if (orientation == Orientation.portrait) {
divFactor = 0.75;
} else {
divFactor = 0.5;
}
} else {
divFactor = roughSentenceSize == 0 ? 0.5 : (roughSentenceSize / maxWidth);
}
final signal = user?.id == message.user?.id ? 1 : -1;
final result = signal * (1 - divFactor * 2.0);
return _capResult(result);
}
// Ensure reactions don't get pushed past the edge of the screen.
//
// This happens if divFactor is really big. When this happens, we can simply
// move the model all the way to the end of screen.
double _capResult(double result) {
if (result > 1.0) {
return 1;
} else if (result < -1.0) {
return -1;
} else {
return result;
}
}
@@ -1088,7 +1088,12 @@ class StreamSvgIcon extends StatelessWidget {
key: key,
width: width,
height: height,
color: color,
colorFilter: color != null
? ColorFilter.mode(
color!,
BlendMode.srcIn,
)
: null,
);
}
}
@@ -149,8 +149,8 @@ class StreamThreadHeader extends StatelessWidget
final theme = Theme.of(context);
return AppBar(
automaticallyImplyLeading: false,
toolbarTextStyle: theme.textTheme.bodyText2,
titleTextStyle: theme.textTheme.headline6,
toolbarTextStyle: theme.textTheme.bodyMedium,
titleTextStyle: theme.textTheme.titleLarge,
systemOverlayStyle: theme.brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark,
@@ -409,7 +409,7 @@ class StreamChannelListErrorWidget extends StatelessWidget {
TextSpan(text: context.translations.loadingChannelsError),
],
),
style: Theme.of(context).textTheme.headline6,
style: Theme.of(context).textTheme.titleLarge,
),
TextButton(
onPressed: onPressed,
@@ -1,3 +1,5 @@
import 'dart:math';
import 'package:diacritic/diacritic.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
@@ -12,6 +14,16 @@ extension StringExtension on String {
String capitalize() =>
isNotEmpty ? '${this[0].toUpperCase()}${substring(1).toLowerCase()}' : '';
/// Returns the biggest line of a text.
String biggestLine() {
if (contains('\n')) {
return split('\n')
.reduce((curr, next) => curr.length > next.length ? curr : next);
} else {
return this;
}
}
/// Returns whether the string contains only emoji's or not.
///
/// Emojis guidelines
@@ -353,6 +365,33 @@ extension MessageX on Message {
return copyWith(text: messageTextToRender);
}
/// Returns an approximation of message size
double roughMessageSize(double? fontSize) {
var messageTextLength = min(text!.biggestLine().length, 65);
if (quotedMessage != null) {
var quotedMessageLength =
(min(quotedMessage!.text?.biggestLine().length ?? 0, 65)) + 8;
if (quotedMessage!.attachments.isNotEmpty) {
quotedMessageLength += 8;
}
if (quotedMessageLength > messageTextLength * 1.2) {
messageTextLength = quotedMessageLength;
}
}
// Quoted message have a smaller font, so it is necessary to reduce the
// size of the multiplier to count for the smaller font.
var multiplier = 0.55;
if (quotedMessage != null) {
multiplier = 0.45;
}
return messageTextLength * (fontSize ?? 1) * multiplier;
}
/// It returns the message with the translated text if available locally
Message translate(String language) =>
copyWith(text: i18n?['${language}_text'] ?? text);
@@ -1,6 +1,6 @@
import 'package:stream_chat_flutter/src/video/vlc/vlc_stub.dart'
if (dart.library.io) 'vlc_manager_desktop.dart'
if (dar.library.html) 'vlc_manager_web.dart';
if (dart.library.html) 'vlc_manager_web.dart';
/// {@template vlcManager}
/// An abstract class for the purpose of ensuring Flutter applications that