Merge branch 'feature/new-ui' into feature/mono-repo
This commit is contained in:
@@ -612,7 +612,10 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
body: UsersBloc(
|
||||
child: Column(
|
||||
children: [
|
||||
_buildTextInputSection(modalSetState),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: _buildTextInputSection(modalSetState),
|
||||
),
|
||||
Expanded(
|
||||
child: UserListView(
|
||||
selectedUsers: {},
|
||||
@@ -690,66 +693,57 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
}
|
||||
|
||||
Widget _buildTextInputSection(modalSetState) {
|
||||
final theme = StreamChatTheme.of(context);
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 16.0,
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _searchController,
|
||||
cursorColor: StreamChatTheme.of(context).colorTheme.black,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
prefixIconConstraints: BoxConstraints.tight(Size(36.0, 44.0)),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 2.0, horizontal: 6.0),
|
||||
child: StreamSvgIcon.search(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
child: Container(
|
||||
height: 36,
|
||||
child: TextField(
|
||||
controller: _searchController,
|
||||
cursorColor: theme.colorTheme.black,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search',
|
||||
hintStyle: theme.textTheme.body.copyWith(
|
||||
color: theme.colorTheme.grey,
|
||||
),
|
||||
prefixIconConstraints: BoxConstraints.tight(Size(40, 24)),
|
||||
prefixIcon: StreamSvgIcon.search(
|
||||
color: theme.colorTheme.black,
|
||||
size: 24,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
borderSide: BorderSide(
|
||||
color: theme.colorTheme.greyWhisper,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
borderSide: BorderSide(
|
||||
color: theme.colorTheme.greyWhisper,
|
||||
)),
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
),
|
||||
hintText: 'Search',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(32.0),
|
||||
borderSide: BorderSide(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.08)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(32.0),
|
||||
borderSide: BorderSide(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.08)),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 8.0,
|
||||
),
|
||||
SizedBox(width: 16.0),
|
||||
IconButton(
|
||||
icon: StreamSvgIcon.close_small(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.5),
|
||||
color: theme.colorTheme.grey,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
constraints: BoxConstraints.tightFor(
|
||||
height: 24,
|
||||
width: 24,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
splashRadius: 24,
|
||||
onPressed: () => Navigator.pop(context),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
@@ -457,133 +457,132 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
},
|
||||
child: ChannelsBloc(
|
||||
child: MessageSearchBloc(
|
||||
child: Column(
|
||||
children: [
|
||||
SearchTextField(
|
||||
controller: _controller,
|
||||
showCloseButton: _isSearchActive,
|
||||
),
|
||||
Expanded(
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: _isSearchActive
|
||||
? MessageSearchListView(
|
||||
messageQuery: _channelQuery,
|
||||
filters: {
|
||||
'members': {
|
||||
r'$in': [user.id]
|
||||
}
|
||||
},
|
||||
sortOptions: [
|
||||
SortOption(
|
||||
'created_at',
|
||||
direction: SortOption.ASC,
|
||||
),
|
||||
],
|
||||
pullToRefresh: false,
|
||||
paginationParams: PaginationParams(limit: 20),
|
||||
emptyBuilder: (_, query) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight:
|
||||
viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'No results for \"$query\"...',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
onItemTap: (messageResponse) async {
|
||||
final client = StreamChat.of(context).client;
|
||||
final message = messageResponse.message;
|
||||
final channel = client.channel(
|
||||
messageResponse.channel.type,
|
||||
id: messageResponse.channel.id,
|
||||
);
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
}
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
initialMessage: message,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: ChannelListView(
|
||||
onStartChatPressed: () {
|
||||
Navigator.pushNamed(context, Routes.NEW_CHAT);
|
||||
},
|
||||
swipeToAction: true,
|
||||
filter: {
|
||||
'members': {
|
||||
r'$in': [user.id],
|
||||
},
|
||||
},
|
||||
options: {
|
||||
'presence': true,
|
||||
},
|
||||
pagination: PaginationParams(
|
||||
limit: 20,
|
||||
),
|
||||
channelWidget: ChannelPage(),
|
||||
onViewInfoTap: (channel) {
|
||||
if (channel.memberCount == 2 &&
|
||||
channel.isDistinct) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: ChatInfoScreen(
|
||||
user: channel.state.members.first.user,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: GroupInfoScreen(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
child: NestedScrollView(
|
||||
floatHeaderSlivers: true,
|
||||
headerSliverBuilder: (_, __) => [
|
||||
SliverToBoxAdapter(
|
||||
child: SearchTextField(
|
||||
controller: _controller,
|
||||
showCloseButton: _isSearchActive,
|
||||
),
|
||||
),
|
||||
],
|
||||
body: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPanDown: (_) => FocusScope.of(context).unfocus(),
|
||||
child: _isSearchActive
|
||||
? MessageSearchListView(
|
||||
messageQuery: _channelQuery,
|
||||
filters: {
|
||||
'members': {
|
||||
r'$in': [user.id]
|
||||
}
|
||||
},
|
||||
sortOptions: [
|
||||
SortOption(
|
||||
'created_at',
|
||||
direction: SortOption.ASC,
|
||||
),
|
||||
],
|
||||
pullToRefresh: false,
|
||||
paginationParams: PaginationParams(limit: 20),
|
||||
emptyBuilder: (_, query) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, viewportConstraints) {
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: viewportConstraints.maxHeight,
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: StreamSvgIcon.search(
|
||||
size: 96,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'No results for \"$query\"...',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
onItemTap: (messageResponse) async {
|
||||
final client = StreamChat.of(context).client;
|
||||
final message = messageResponse.message;
|
||||
final channel = client.channel(
|
||||
messageResponse.channel.type,
|
||||
id: messageResponse.channel.id,
|
||||
);
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
}
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.CHANNEL_PAGE,
|
||||
arguments: ChannelPageArgs(
|
||||
channel: channel,
|
||||
initialMessage: message,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: ChannelListView(
|
||||
onStartChatPressed: () {
|
||||
Navigator.pushNamed(context, Routes.NEW_CHAT);
|
||||
},
|
||||
swipeToAction: true,
|
||||
filter: {
|
||||
'members': {
|
||||
r'$in': [user.id],
|
||||
},
|
||||
},
|
||||
options: {
|
||||
'presence': true,
|
||||
},
|
||||
pagination: PaginationParams(
|
||||
limit: 20,
|
||||
),
|
||||
channelWidget: ChannelPage(),
|
||||
onViewInfoTap: (channel) {
|
||||
if (channel.memberCount == 2 && channel.isDistinct) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: ChatInfoScreen(
|
||||
user: channel.state.members.first.user,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => StreamChannel(
|
||||
channel: channel,
|
||||
child: GroupInfoScreen(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: example
|
||||
description: A new Flutter project.
|
||||
version: 1.1.1+3
|
||||
version: 1.1.2
|
||||
|
||||
environment:
|
||||
sdk: ">=2.2.2 <3.0.0"
|
||||
|
||||
@@ -729,17 +729,11 @@ class _ChannelListViewState extends State<ChannelListView>
|
||||
}
|
||||
|
||||
Widget _separatorBuilder(context, i) {
|
||||
var effect = StreamChatTheme.of(context).colorTheme.borderTop;
|
||||
var effect = StreamChatTheme.of(context).colorTheme.borderBottom;
|
||||
|
||||
return BackdropFilter(
|
||||
filter: ui.ImageFilter.blur(
|
||||
sigmaX: effect.sigmaX,
|
||||
sigmaY: effect.sigmaY,
|
||||
),
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: effect.color.withOpacity(0.08),
|
||||
),
|
||||
return Container(
|
||||
height: 1,
|
||||
color: effect.color.withOpacity(effect.alpha ?? 1.0),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,7 @@ extension StringExtension on String {
|
||||
final characters = this.trim().characters;
|
||||
if (characters.isEmpty) return false;
|
||||
if (characters.length > 3) return false;
|
||||
return characters.every((c) {
|
||||
return _emojis.firstWhere(
|
||||
(Emoji emoji) => emoji.char.contains(c),
|
||||
orElse: () => null,
|
||||
) !=
|
||||
null;
|
||||
});
|
||||
return characters.every((c) => _emojis.map((e) => e.char).contains(c));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -174,117 +174,138 @@ class _ImageFooterState extends State<ImageFooter> {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
barrierColor: StreamChatTheme.of(context).colorTheme.overlay,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.white,
|
||||
isScrollControlled: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: const Radius.circular(16.0),
|
||||
topRight: const Radius.circular(16.0),
|
||||
),
|
||||
),
|
||||
builder: (context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(16.0),
|
||||
topLeft: Radius.circular(16.0),
|
||||
)),
|
||||
child: Stack(
|
||||
final crossAxisCount = 3;
|
||||
final noOfRowToShowInitially =
|
||||
widget.mediaAttachments.length > crossAxisCount ? 2 : 1;
|
||||
final size = MediaQuery.of(context).size;
|
||||
final initialChildSize =
|
||||
48 + (size.width * noOfRowToShowInitially) / crossAxisCount;
|
||||
return DraggableScrollableSheet(
|
||||
expand: false,
|
||||
initialChildSize: initialChildSize / size.height,
|
||||
builder: (context, scrollController) {
|
||||
return SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Photos',
|
||||
style:
|
||||
StreamChatTheme.of(context).textTheme.headlineBold,
|
||||
Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Photos',
|
||||
style: StreamChatTheme.of(context)
|
||||
.textTheme
|
||||
.headlineBold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
icon: StreamSvgIcon.close(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
),
|
||||
onPressed: () => Navigator.maybePop(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
icon: StreamSvgIcon.close(
|
||||
color: StreamChatTheme.of(context).colorTheme.black,
|
||||
Flexible(
|
||||
child: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: widget.mediaAttachments.length,
|
||||
padding: const EdgeInsets.all(1),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: crossAxisCount,
|
||||
mainAxisSpacing: 2.0,
|
||||
crossAxisSpacing: 2.0,
|
||||
),
|
||||
onPressed: () => Navigator.maybePop(context),
|
||||
itemBuilder: (context, index) {
|
||||
Widget media;
|
||||
final attachment = widget.mediaAttachments[index];
|
||||
|
||||
if (attachment.type == 'video') {
|
||||
var controllerPackage = widget.videoPackages[
|
||||
videoAttachments.indexOf(attachment)];
|
||||
|
||||
media = InkWell(
|
||||
onTap: () => widget.mediaSelectedCallBack(index),
|
||||
child: FittedBox(
|
||||
fit: BoxFit.cover,
|
||||
child: Chewie(
|
||||
controller: controllerPackage.chewieController,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
media = InkWell(
|
||||
onTap: () => widget.mediaSelectedCallBack(index),
|
||||
child: AspectRatio(
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: attachment.imageUrl ??
|
||||
attachment.assetUrl ??
|
||||
attachment.thumbUrl,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
aspectRatio: 1.0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
media,
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.white
|
||||
.withOpacity(0.6),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 8.0,
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
.black
|
||||
.withOpacity(0.3),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.all(1),
|
||||
child: UserAvatar(
|
||||
user: widget.message.user,
|
||||
constraints:
|
||||
BoxConstraints.tight(Size(24, 24)),
|
||||
showOnlineStatus: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
child: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, position) {
|
||||
Widget media;
|
||||
|
||||
if (widget.mediaAttachments[position].type == 'video') {
|
||||
var controllerPackage = widget.videoPackages[
|
||||
videoAttachments
|
||||
.indexOf(widget.mediaAttachments[position])];
|
||||
|
||||
media = InkWell(
|
||||
onTap: () {
|
||||
widget.mediaSelectedCallBack(position);
|
||||
},
|
||||
child: FittedBox(
|
||||
fit: BoxFit.cover,
|
||||
child: Chewie(
|
||||
controller: controllerPackage.chewieController,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
media = InkWell(
|
||||
onTap: () {
|
||||
widget.mediaSelectedCallBack(position);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(1.0),
|
||||
child: AspectRatio(
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: widget
|
||||
.mediaAttachments[position].imageUrl ??
|
||||
widget.mediaAttachments[position].assetUrl ??
|
||||
widget.mediaAttachments[position].thumbUrl,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
aspectRatio: 1.0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
media,
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: UserAvatar(
|
||||
user: widget.message.user,
|
||||
constraints: BoxConstraints.tight(
|
||||
Size(
|
||||
24,
|
||||
24,
|
||||
),
|
||||
),
|
||||
showOnlineStatus: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
itemCount: widget.mediaAttachments.length,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -32,6 +32,7 @@ class MessageActionsModal extends StatefulWidget {
|
||||
final bool showFlagButton;
|
||||
final bool reverse;
|
||||
final ShapeBorder messageShape;
|
||||
final ShapeBorder attachmentShape;
|
||||
final DisplayWidget showUserAvatar;
|
||||
|
||||
const MessageActionsModal({
|
||||
@@ -51,6 +52,7 @@ class MessageActionsModal extends StatefulWidget {
|
||||
this.showUserAvatar = DisplayWidget.show,
|
||||
this.editMessageInputBuilder,
|
||||
this.messageShape,
|
||||
this.attachmentShape,
|
||||
this.reverse = false,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -173,6 +175,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
showInChannelIndicator: false,
|
||||
showSendingIndicator: false,
|
||||
shape: widget.messageShape,
|
||||
attachmentShape: widget.attachmentShape,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
@@ -461,7 +464,7 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
'Flag',
|
||||
'Flag Message',
|
||||
style: StreamChatTheme.of(context).textTheme.body,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -20,6 +20,7 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
final bool showReactions;
|
||||
final DisplayWidget showUserAvatar;
|
||||
final ShapeBorder messageShape;
|
||||
final ShapeBorder attachmentShape;
|
||||
final void Function(User) onUserAvatarTap;
|
||||
|
||||
const MessageReactionsModal({
|
||||
@@ -30,6 +31,7 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
this.onThreadTap,
|
||||
this.editMessageInputBuilder,
|
||||
this.messageShape,
|
||||
this.attachmentShape,
|
||||
this.reverse = false,
|
||||
this.showUserAvatar = DisplayWidget.show,
|
||||
this.onUserAvatarTap,
|
||||
@@ -127,6 +129,7 @@ class MessageReactionsModal extends StatelessWidget {
|
||||
translateUserAvatar: false,
|
||||
showSendingIndicator: false,
|
||||
shape: messageShape,
|
||||
attachmentShape: attachmentShape,
|
||||
padding: const EdgeInsets.all(0),
|
||||
attachmentPadding: EdgeInsets.all(
|
||||
hasFileAttachment ? 4 : 2,
|
||||
|
||||
@@ -295,10 +295,6 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
var leftPadding =
|
||||
widget.showUserAvatar != DisplayWidget.gone ? avatarWidth + 8.5 : 0.5;
|
||||
|
||||
final hasFiles =
|
||||
widget.message.attachments?.any((element) => element.type == 'file') ==
|
||||
true;
|
||||
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Portal(
|
||||
@@ -406,29 +402,19 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
BorderRadius.zero,
|
||||
),
|
||||
color: _getBackgroundColor(),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(
|
||||
hasFiles ? 2.0 : 0.0),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment
|
||||
.end,
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (hasQuotedMessage)
|
||||
_buildQuotedMessage(),
|
||||
if (hasNonUrlAttachments)
|
||||
_parseAttachments(
|
||||
context),
|
||||
if (widget.message.text
|
||||
.trim()
|
||||
.isNotEmpty &&
|
||||
!isGiphy)
|
||||
_buildTextBubble(
|
||||
context),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.end,
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (hasQuotedMessage)
|
||||
_buildQuotedMessage(),
|
||||
if (hasNonUrlAttachments)
|
||||
_parseAttachments(),
|
||||
if (!isGiphy)
|
||||
_buildTextBubble(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -536,7 +522,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
|
||||
var children = <Widget>[];
|
||||
|
||||
final threadParticipants = widget.message.threadParticipants;
|
||||
final threadParticipants = widget.message?.threadParticipants?.take(2);
|
||||
final showThreadParticipants = threadParticipants?.isNotEmpty == true;
|
||||
final replyCount = widget.message.replyCount;
|
||||
|
||||
@@ -565,7 +551,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
if (showThreadParticipants)
|
||||
SizedBox.fromSize(
|
||||
size: Size((threadParticipants.length * 8.0) + 8, 16),
|
||||
child: _buildThreadParticipantsIndicator(),
|
||||
child: _buildThreadParticipantsIndicator(threadParticipants),
|
||||
),
|
||||
InkWell(
|
||||
onTap: widget.onThreadTap != null ? onThreadTap : null,
|
||||
@@ -642,10 +628,10 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildThreadParticipantsIndicator() {
|
||||
Widget _buildThreadParticipantsIndicator(Iterable<User> threadParticipants) {
|
||||
var padding = 0.0;
|
||||
return Stack(
|
||||
children: widget.message.threadParticipants.map((user) {
|
||||
children: threadParticipants.map((user) {
|
||||
padding += 8.0;
|
||||
return Positioned(
|
||||
right: padding - 8,
|
||||
@@ -718,6 +704,8 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
: DisplayWidget.show,
|
||||
messageTheme: widget.messageTheme,
|
||||
messageShape: widget.shape ?? _getDefaultShape(context),
|
||||
attachmentShape:
|
||||
widget.attachmentShape ?? _getDefaultAttachmentShape(context),
|
||||
reverse: widget.reverse,
|
||||
showDeleteMessage: widget.showDeleteMessage || isDeleteFailed,
|
||||
message: widget.message,
|
||||
@@ -763,6 +751,8 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
onUserAvatarTap: widget.onUserAvatarTap,
|
||||
messageTheme: widget.messageTheme,
|
||||
messageShape: widget.shape ?? _getDefaultShape(context),
|
||||
attachmentShape:
|
||||
widget.attachmentShape ?? _getDefaultAttachmentShape(context),
|
||||
reverse: widget.reverse,
|
||||
message: widget.message,
|
||||
editMessageInputBuilder: widget.editMessageInputBuilder,
|
||||
@@ -773,20 +763,31 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
});
|
||||
}
|
||||
|
||||
ShapeBorder _getDefaultShape(BuildContext context) {
|
||||
ShapeBorder _getDefaultAttachmentShape(BuildContext context) {
|
||||
final hasFiles =
|
||||
widget.message.attachments?.any((it) => it.type == 'file') == true;
|
||||
return RoundedRectangleBorder(
|
||||
side: widget.attachmentBorderSide ??
|
||||
widget.borderSide ??
|
||||
BorderSide(
|
||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||
),
|
||||
borderRadius: widget.attachmentBorderRadiusGeometry ??
|
||||
widget.borderRadiusGeometry ??
|
||||
BorderRadius.zero,
|
||||
side: hasFiles
|
||||
? widget.attachmentBorderSide ??
|
||||
BorderSide(
|
||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||
)
|
||||
: BorderSide.none,
|
||||
borderRadius: widget.attachmentBorderRadiusGeometry ?? BorderRadius.zero,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _parseAttachments(BuildContext context) {
|
||||
ShapeBorder _getDefaultShape(BuildContext context) {
|
||||
return RoundedRectangleBorder(
|
||||
side: widget.borderSide ??
|
||||
BorderSide(
|
||||
color: StreamChatTheme.of(context).colorTheme.greyWhisper,
|
||||
),
|
||||
borderRadius: widget.borderRadiusGeometry ?? BorderRadius.zero,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _parseAttachments() {
|
||||
final images = widget.message.attachments
|
||||
?.where((element) =>
|
||||
element.type == 'image' && element.ogScrapeUrl == null)
|
||||
@@ -849,7 +850,7 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
Attachment attachment,
|
||||
}) {
|
||||
final attachmentShape =
|
||||
widget.attachmentShape ?? widget.shape ?? _getDefaultShape(context);
|
||||
widget.attachmentShape ?? _getDefaultAttachmentShape(context);
|
||||
return Material(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: attachmentShape,
|
||||
@@ -920,7 +921,8 @@ class _MessageWidgetState extends State<MessageWidget> {
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTextBubble(BuildContext context) {
|
||||
Widget _buildTextBubble() {
|
||||
if (widget.message.text.trim().isEmpty) return Offstage();
|
||||
return Transform(
|
||||
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
|
||||
alignment: Alignment.center,
|
||||
|
||||
@@ -492,8 +492,8 @@ class ColorTheme {
|
||||
),
|
||||
this.borderTop = const Effect(
|
||||
sigmaX: 0, sigmaY: -1, color: Color(0xff000000), blur: 0.0),
|
||||
this.borderBottom =
|
||||
const Effect(sigmaX: 0, sigmaY: 1, color: Color(0xff000000), blur: 0.0),
|
||||
this.borderBottom = const Effect(
|
||||
sigmaX: 0, sigmaY: 1, color: Color(0xff000000), blur: 0.0, alpha: 0.08),
|
||||
this.shadowIconButton = const Effect(
|
||||
sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0),
|
||||
this.modalShadow = const Effect(
|
||||
@@ -514,8 +514,8 @@ class ColorTheme {
|
||||
this.accentGreen = const Color(0xff20E070),
|
||||
this.borderTop = const Effect(
|
||||
sigmaX: 0, sigmaY: -1, color: Color(0xff141924), blur: 0.0),
|
||||
this.borderBottom =
|
||||
const Effect(sigmaX: 0, sigmaY: 1, color: Color(0xff141924), blur: 0.0),
|
||||
this.borderBottom = const Effect(
|
||||
sigmaX: 0, sigmaY: 1, color: Color(0xff141924), blur: 0.0, alpha: 1.0),
|
||||
this.shadowIconButton = const Effect(
|
||||
sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0),
|
||||
this.modalShadow = const Effect(
|
||||
|
||||
@@ -416,6 +416,10 @@ class _UserListViewState extends State<UserListView>
|
||||
height: 64,
|
||||
width: 64,
|
||||
),
|
||||
onlineIndicatorConstraints: BoxConstraints.tightFor(
|
||||
height: 12,
|
||||
width: 12,
|
||||
),
|
||||
onTap: (user) =>
|
||||
widget.onUserTap(user, widget.userWidget),
|
||||
onLongPress: widget.onUserLongPress,
|
||||
|
||||
Reference in New Issue
Block a user