Merge branch 'feature/new-ui' into hotfix/commands

This commit is contained in:
Salvatore Giordano
2021-01-07 17:06:16 +01:00
committed by GitHub
13 changed files with 326 additions and 131 deletions
+24
View File
@@ -0,0 +1,24 @@
name: Flutter Analyze
on:
push:
branches:
- master
pull_request:
branches:
- '*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: subosito/flutter-action@v1
with:
channel: 'beta'
- run: flutter pub get
- name: 'Flutter Format Check'
run: flutter format --set-exit-if-changed --dry-run .
- run: flutter analyze
+51
View File
@@ -1,3 +1,54 @@
## 0.2.20+2
- Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the list when a new message arrives
## 0.2.20+1
- Fixed bug that caused video attachment to show the same preview
## 0.2.20
- Implement shadowban
## 0.2.19
- Updated llc dependency
- Added loading builder in channellistview
- Added sendButtonLocation and animationduration to messageinput
## 0.2.18
- Updated llc dependency
## 0.2.17+2
- Expose ChannelsBloc.channelsComparator to sort channels on message.new event
## 0.2.17+1
- Fix mention tap bug
## 0.2.17
- Expose messageInputDecoration as part of the theme
## 0.2.16
- Do not wrap channel preview builder. Users will have to implement they're custom onTap/onLongPress implementation
- Make public autofocus field of the TextField of message_input
## 0.2.15
- Add onLongPress on channel when using custom channel builder
## 0.2.14
- Add onMessageTap callbacks
## 0.2.13+2
- Add debounce to on change messageinput listener
## 0.2.13+1 ## 0.2.13+1
- Use TextEditingController.addListener instead of TextField.onChanged - Use TextEditingController.addListener instead of TextField.onChanged
+2 -4
View File
@@ -11,8 +11,6 @@
![](https://img.shields.io/badge/platform-flutter%20%7C%20flutter%20web-ff69b4.svg?style=flat-square) ![](https://img.shields.io/badge/platform-flutter%20%7C%20flutter%20web-ff69b4.svg?style=flat-square)
[![Gitter](https://badges.gitter.im/GetStream/stream-chat-flutter.svg)](https://gitter.im/GetStream/stream-chat-flutter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Gitter](https://badges.gitter.im/GetStream/stream-chat-flutter.svg)](https://gitter.im/GetStream/stream-chat-flutter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
![CI](https://github.com/GetStream/stream-chat-flutter/workflows/CI/badge.svg?branch=master) ![CI](https://github.com/GetStream/stream-chat-flutter/workflows/CI/badge.svg?branch=master)
[![codecov](https://codecov.io/gh/GetStream/stream-chat-flutter/branch/master/graph/badge.svg)](https://codecov.io/gh/GetStream/stream-chat-flutter)
<img align="right" src="https://getstream.imgix.net/images/ios-chat-tutorial/iphone_chat_art@1x.png?auto=format,enhance" width="50%" /> <img align="right" src="https://getstream.imgix.net/images/ios-chat-tutorial/iphone_chat_art@1x.png?auto=format,enhance" width="50%" />
**Quick Links** **Quick Links**
@@ -32,10 +30,10 @@ This repo includes a fully functional example app with setup instructions.
The example is available under the [example](https://github.com/GetStream/stream-chat-flutter/tree/master/example) folder. The example is available under the [example](https://github.com/GetStream/stream-chat-flutter/tree/master/example) folder.
## Add dependency ## Add dependency
Add this to your package's pubspec.yaml file, use the latest version [![Pub](https://img.shields.io/pub/v/stream_chat_flutter.svg)](https://pub.dartlang.org/packages/stream_chat_flutter)
```yaml ```yaml
dependencies: dependencies:
stream_chat_flutter: ^0.2.1 stream_chat_flutter: ^latest_version
``` ```
You should then run `flutter packages get` You should then run `flutter packages get`
+14 -1
View File
@@ -7,6 +7,7 @@ import 'package:example/group_info_screen.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
@@ -55,13 +56,25 @@ class MyApp extends StatelessWidget {
builder: (context, child) { builder: (context, child) {
return StreamChat( return StreamChat(
client: client, client: client,
child: Builder(
builder: (context) => AnnotatedRegion<SystemUiOverlayStyle>(
child: child, child: child,
value: SystemUiOverlayStyle(
systemNavigationBarColor:
StreamChatTheme.of(context).colorTheme.white,
systemNavigationBarIconBrightness:
Theme.of(context).brightness == Brightness.dark
? Brightness.light
: Brightness.dark,
),
),
),
); );
}, },
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: ThemeData.light(), theme: ThemeData.light(),
darkTheme: ThemeData.dark(), darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system, themeMode: ThemeMode.dark,
onGenerateRoute: AppRoutes.generateRoute, onGenerateRoute: AppRoutes.generateRoute,
initialRoute: initialRoute:
client.state.user == null ? Routes.CHOOSE_USER : Routes.HOME, client.state.user == null ? Routes.CHOOSE_USER : Routes.HOME,
+1 -1
View File
@@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -9,7 +10,6 @@ import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/channels_bloc.dart'; import 'package:stream_chat_flutter/src/channels_bloc.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/src/utils.dart'; import 'package:stream_chat_flutter/src/utils.dart';
import 'dart:ui' as ui;
import '../stream_chat_flutter.dart'; import '../stream_chat_flutter.dart';
import 'channel_bottom_sheet.dart'; import 'channel_bottom_sheet.dart';
+2 -1
View File
@@ -201,7 +201,8 @@ class ChannelPreview extends StatelessWidget {
stream: channel.state.messagesStream, stream: channel.state.messagesStream,
initialData: channel.state.messages, initialData: channel.state.messages,
builder: (context, snapshot) { builder: (context, snapshot) {
final lastMessage = channel.state.lastMessage; final lastMessage = snapshot.data
?.lastWhere((m) => m.shadowed != true, orElse: () => null);
if (lastMessage == null) { if (lastMessage == null) {
return SizedBox(); return SizedBox();
} }
+19 -8
View File
@@ -14,11 +14,19 @@ class ChannelsBloc extends StatefulWidget {
/// Set this to true to prevent channels to be brought to the top of the list when a new message arrives /// Set this to true to prevent channels to be brought to the top of the list when a new message arrives
final bool lockChannelsOrder; final bool lockChannelsOrder;
/// Comparator used to sort the channels when a message.new event is received
final Comparator<Channel> channelsComparator;
/// Function used to evaluate if a channel should be added to the list when a message.new event is received
final bool Function(Event) shouldAddChannel;
/// Instantiate a new ChannelsBloc /// Instantiate a new ChannelsBloc
const ChannelsBloc({ const ChannelsBloc({
Key key, Key key,
this.child, this.child,
this.lockChannelsOrder = false, this.lockChannelsOrder = false,
this.channelsComparator,
this.shouldAddChannel,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -85,23 +93,21 @@ class ChannelsBlocState extends State<ChannelsBloc>
paginationParams.offset == null || paginationParams.offset == null ||
paginationParams.offset == 0; paginationParams.offset == 0;
final oldChannels = List<Channel>.from(channels ?? []); final oldChannels = List<Channel>.from(channels ?? []);
await client final _channels = await client.queryChannels(
.queryChannels(
filter: filter, filter: filter,
sort: sortOptions, sort: sortOptions,
options: options, options: options,
paginationParams: paginationParams, paginationParams: paginationParams,
onlyOffline: onlyOffline, onlyOffline: onlyOffline,
) );
.then((channels) {
if (clear) { if (clear) {
_channelsController.add(channels); _channelsController.add(_channels);
} else { } else {
final l = oldChannels + channels; final l = oldChannels + _channels;
_channelsController.add(l); _channelsController.add(l);
} }
_queryChannelsLoadingController.sink.add(false); _queryChannelsLoadingController.sink.add(false);
});
} catch (err, stackTrace) { } catch (err, stackTrace) {
print(err); print(err);
print(stackTrace); print(stackTrace);
@@ -126,7 +132,8 @@ class ChannelsBlocState extends State<ChannelsBloc>
final channel = newChannels.removeAt(index); final channel = newChannels.removeAt(index);
newChannels.insert(0, channel); newChannels.insert(0, channel);
} }
} else { } else if (widget.shouldAddChannel != null &&
widget.shouldAddChannel(e)) {
final hiddenIndex = _hiddenChannels.indexWhere((c) => c.cid == e.cid); final hiddenIndex = _hiddenChannels.indexWhere((c) => c.cid == e.cid);
if (hiddenIndex > -1) { if (hiddenIndex > -1) {
newChannels.insert(0, _hiddenChannels[hiddenIndex]); newChannels.insert(0, _hiddenChannels[hiddenIndex]);
@@ -138,6 +145,10 @@ class ChannelsBlocState extends State<ChannelsBloc>
} }
} }
} }
if (widget.channelsComparator != null) {
newChannels.sort(widget.channelsComparator);
}
_channelsController.add(newChannels); _channelsController.add(newChannels);
})); }));
} }
+101 -29
View File
@@ -1208,7 +1208,8 @@ class MessageInputState extends State<MessageInput> {
RenderBox renderBox = context.findRenderObject(); RenderBox renderBox = context.findRenderObject();
final size = renderBox.size; final size = renderBox.size;
return OverlayEntry(builder: (context) { return OverlayEntry(
builder: (context) {
return Positioned( return Positioned(
bottom: size.height + MediaQuery.of(context).viewInsets.bottom, bottom: size.height + MediaQuery.of(context).viewInsets.bottom,
left: 0, left: 0,
@@ -1229,7 +1230,7 @@ class MessageInputState extends State<MessageInput> {
), ),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: Container( child: Container(
constraints: BoxConstraints.loose(Size.fromHeight(400)), constraints: BoxConstraints.loose(Size.fromHeight(240)),
decoration: BoxDecoration( decoration: BoxDecoration(
color: StreamChatTheme.of(context).colorTheme.white, color: StreamChatTheme.of(context).colorTheme.white,
), ),
@@ -1240,28 +1241,17 @@ class MessageInputState extends State<MessageInput> {
return ListView( return ListView(
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
shrinkWrap: true, shrinkWrap: true,
children: snapshot.data children: [
.map((m) => ListTile( SizedBox(
leading: UserAvatar( height: 8.0,
constraints: BoxConstraints.tight(
Size(
40,
40,
), ),
), ...snapshot.data.map(
user: m.user, (m) {
), return Material(
title: Text(
'${m.user.name}',
style: TextStyle(
fontWeight: FontWeight.bold),
),
subtitle: Text('@${m.userId}'),
trailing: StreamSvgIcon.mentions(
color: StreamChatTheme.of(context) color: StreamChatTheme.of(context)
.colorTheme .colorTheme
.accentBlue, .white,
), child: InkWell(
onTap: () { onTap: () {
_mentionedUsers.add(m.user); _mentionedUsers.add(m.user);
@@ -1272,8 +1262,7 @@ class MessageInputState extends State<MessageInput> {
TextEditingValue( TextEditingValue(
text: rejoin + text: rejoin +
textEditingController.text textEditingController.text
.substring( .substring(textEditingController
textEditingController
.selection.start), .selection.start),
selection: TextSelection.collapsed( selection: TextSelection.collapsed(
offset: rejoin.length, offset: rejoin.length,
@@ -1283,16 +1272,99 @@ class MessageInputState extends State<MessageInput> {
_mentionsOverlay?.remove(); _mentionsOverlay?.remove();
_mentionsOverlay = null; _mentionsOverlay = null;
}, },
)) child: Container(
.toList(), height: 56.0,
); child: Row(
}), crossAxisAlignment:
CrossAxisAlignment.center,
children: [
SizedBox(
width: 16.0,
),
UserAvatar(
constraints: BoxConstraints.tight(
Size(
40,
40,
),
),
user: m.user,
),
SizedBox(
width: 8.0,
),
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'${m.user.name}',
maxLines: 1,
overflow:
TextOverflow.ellipsis,
style: StreamChatTheme.of(
context)
.textTheme
.bodyBold,
),
SizedBox(
height: 2.0,
),
Text(
'@${m.userId}',
maxLines: 1,
overflow:
TextOverflow.ellipsis,
style: StreamChatTheme.of(
context)
.textTheme
.footnoteBold
.copyWith(
color: StreamChatTheme
.of(context)
.colorTheme
.grey),
),
],
),
),
),
Padding(
padding: const EdgeInsets.only(
right: 18.0, left: 8.0),
child: StreamSvgIcon.mentions(
color: StreamChatTheme.of(context)
.colorTheme
.accentBlue,
),
),
],
),
), ),
), ),
); );
}), },
).toList(),
SizedBox(
height: 8.0,
),
],
);
},
),
),
),
);
},
),
);
},
); );
});
} }
OverlayEntry _buildEmojiOverlay() { OverlayEntry _buildEmojiOverlay() {
+1 -1
View File
@@ -243,7 +243,7 @@ class _MessageListViewState extends State<MessageListView> {
return StreamBuilder<List<Message>>( return StreamBuilder<List<Message>>(
stream: messagesStream?.map((messages) => messages stream: messagesStream?.map((messages) => messages
?.where((e) => ?.where((e) =>
!e.isDeleted || (!e.isDeleted && e.shadowed != true) ||
(e.isDeleted && (e.isDeleted &&
e.user.id == streamChannel.channel.client.state.user.id)) e.user.id == streamChannel.channel.client.state.user.id))
?.toList()), ?.toList()),
+11 -3
View File
@@ -572,13 +572,16 @@ class _MessageWidgetState extends State<MessageWidget> {
return Padding( return Padding(
padding: EdgeInsets.only(left: leftPadding), padding: EdgeInsets.only(left: leftPadding),
child: Row( child: Flex(
direction: Axis.horizontal,
clipBehavior: Clip.none,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
if (!deleted && (showThreadReplyIndicator || showInChannel)) if (!deleted && (showThreadReplyIndicator || showInChannel))
Container( Container(
margin: EdgeInsets.only( margin: EdgeInsets.only(
bottom: widget.messageTheme.replies.fontSize / 2), bottom: widget.messageTheme.replies.fontSize / 2,
),
child: CustomPaint( child: CustomPaint(
size: const Size(16, 32), size: const Size(16, 32),
painter: _ThreadReplyPainter( painter: _ThreadReplyPainter(
@@ -591,7 +594,12 @@ class _MessageWidgetState extends State<MessageWidget> {
(child) => Transform( (child) => Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0), transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center, alignment: Alignment.center,
child: Container(height: 16, child: Center(child: child)), child: Container(
height: 16,
child: Center(
child: child,
),
),
), ),
), ),
].insertBetween(const SizedBox(width: 8.0)), ].insertBetween(const SizedBox(width: 8.0)),
+1
View File
@@ -125,6 +125,7 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
.on(EventType.messageNew) .on(EventType.messageNew)
.where((e) => e.user?.id != user.id) .where((e) => e.user?.id != user.id)
.where((e) => e.message.silent != true) .where((e) => e.message.silent != true)
.where((e) => e.message.shadowed != true)
.listen((event) async { .listen((event) async {
final channel = client.channel( final channel = client.channel(
event.channelType, event.channelType,
+17 -1
View File
@@ -4,11 +4,16 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// It shows a date divider depending on the date difference /// It shows a date divider depending on the date difference
class SystemMessage extends StatelessWidget { class SystemMessage extends StatelessWidget {
/// This message
final Message message; final Message message;
/// The function called when tapping on the message when the message is not failed
final void Function(Message) onMessageTap;
const SystemMessage({ const SystemMessage({
Key key, Key key,
@required this.message, @required this.message,
this.onMessageTap,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -44,7 +49,16 @@ class SystemMessage extends StatelessWidget {
dayInfo = createdAt.format('dd/MM/yyyy').toUpperCase(); dayInfo = createdAt.format('dd/MM/yyyy').toUpperCase();
} }
return Row( return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if (onMessageTap != null) {
onMessageTap(message);
}
},
child: Container(
width: double.infinity,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
divider, divider,
@@ -95,6 +109,8 @@ class SystemMessage extends StatelessWidget {
), ),
divider, divider,
], ],
),
),
); );
} }
} }
+1 -1
View File
@@ -1,7 +1,7 @@
name: stream_chat_flutter name: stream_chat_flutter
homepage: https://github.com/GetStream/stream-chat-flutter homepage: https://github.com/GetStream/stream-chat-flutter
description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter.
version: 0.2.13+1 version: 0.2.20+2
repository: https://github.com/GetStream/stream-chat-flutter repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues