Merge pull request #626 from GetStream/cds-189
feat(ui, llc): slow mode
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
- `User` and `OwnUser` classes now have a `name` property. Setting a name will also set the 'name' key on `extraData`, so `user.name` and `user.extraData['name']` is the same.
|
||||
- `Channel` class now has extra `image` getter and setter. As well as an `updateImage` to do a partial update after a channel has been initialized.
|
||||
- `Channel` class now has extra `name` getter and setter. As well as an `updateName` to do a partial update after a channel has been initialized.
|
||||
- Added slow mode which allows a cooldown period after a user sends a message.
|
||||
## 2.1.1
|
||||
|
||||
🐞 Fixed
|
||||
|
||||
@@ -201,6 +201,21 @@ class Channel {
|
||||
return state!.channelStateStream.map((cs) => cs.channel?.frozen == true);
|
||||
}
|
||||
|
||||
/// Cooldown count
|
||||
int? get cooldown {
|
||||
_checkInitialized();
|
||||
return state?._channelState.channel?.cooldown;
|
||||
}
|
||||
|
||||
/// Cooldown count as a stream
|
||||
Stream<int?>? get cooldownStream {
|
||||
_checkInitialized();
|
||||
return state?.channelStateStream.map((cs) => cs.channel?.cooldown);
|
||||
}
|
||||
|
||||
/// Stores time at which cooldown was started
|
||||
DateTime? cooldownStartedAt;
|
||||
|
||||
/// Channel creation date.
|
||||
DateTime? get createdAt {
|
||||
_checkInitialized();
|
||||
@@ -525,6 +540,9 @@ class Channel {
|
||||
skipPush: skipPush,
|
||||
);
|
||||
state!.addMessage(response.message);
|
||||
if (cooldown! > 0) {
|
||||
cooldownStartedAt = DateTime.now();
|
||||
}
|
||||
return response;
|
||||
} catch (e) {
|
||||
if (e is StreamChatNetworkError && e.isRetriable) {
|
||||
@@ -980,6 +998,20 @@ class Channel {
|
||||
return _client.updateChannelPartial(id!, type, set: set, unset: unset);
|
||||
}
|
||||
|
||||
/// Enable slow mode
|
||||
Future<PartialUpdateChannelResponse> enableSlowMode({
|
||||
required int cooldownInterval,
|
||||
}) async {
|
||||
_checkInitialized();
|
||||
return _client.enableSlowdown(id!, type, cooldownInterval);
|
||||
}
|
||||
|
||||
/// Disable slow mode
|
||||
Future<PartialUpdateChannelResponse> disableSlowMode() async {
|
||||
_checkInitialized();
|
||||
return _client.disableSlowdown(id!, type);
|
||||
}
|
||||
|
||||
/// Delete this channel. Messages are permanently removed.
|
||||
Future<EmptyResponse> delete() async {
|
||||
_checkInitialized();
|
||||
|
||||
@@ -1252,6 +1252,28 @@ class StreamChatClient {
|
||||
language,
|
||||
);
|
||||
|
||||
/// Enables slow mode
|
||||
Future<PartialUpdateChannelResponse> enableSlowdown(
|
||||
String channelId,
|
||||
String channelType,
|
||||
int cooldown,
|
||||
) async =>
|
||||
_chatApi.channel.enableSlowdown(
|
||||
channelId,
|
||||
channelType,
|
||||
cooldown,
|
||||
);
|
||||
|
||||
/// Disables slow mode
|
||||
Future<PartialUpdateChannelResponse> disableSlowdown(
|
||||
String channelId,
|
||||
String channelType,
|
||||
) async =>
|
||||
_chatApi.channel.disableSlowdown(
|
||||
channelId,
|
||||
channelType,
|
||||
);
|
||||
|
||||
/// Pins provided message
|
||||
/// [timeoutOrExpirationDate] can either be a [DateTime] or a value in seconds
|
||||
/// to be added to [DateTime.now]
|
||||
|
||||
@@ -123,6 +123,35 @@ class ChannelApi {
|
||||
return PartialUpdateChannelResponse.fromJson(response.data);
|
||||
}
|
||||
|
||||
/// Enable slowdown
|
||||
Future<PartialUpdateChannelResponse> enableSlowdown(
|
||||
String channelId,
|
||||
String channelType,
|
||||
int cooldown,
|
||||
) async {
|
||||
final response = await updateChannelPartial(
|
||||
channelId,
|
||||
channelType,
|
||||
set: {
|
||||
'cooldown': cooldown,
|
||||
},
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
/// Disable slowdown
|
||||
Future<PartialUpdateChannelResponse> disableSlowdown(
|
||||
String channelId,
|
||||
String channelType,
|
||||
) async {
|
||||
final response = await updateChannelPartial(
|
||||
channelId,
|
||||
channelType,
|
||||
unset: ['cooldown'],
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
/// Accept invitation to the channel
|
||||
Future<AcceptInviteResponse> acceptChannelInvite(
|
||||
String channelId,
|
||||
|
||||
@@ -23,6 +23,7 @@ class ChannelModel {
|
||||
this.memberCount = 0,
|
||||
this.extraData = const {},
|
||||
this.team,
|
||||
this.cooldown = 0,
|
||||
}) : assert(
|
||||
(cid != null && cid.contains(':')) || (id != null && type != null),
|
||||
'provide either a cid or an id and type',
|
||||
@@ -81,6 +82,10 @@ class ChannelModel {
|
||||
@JsonKey(includeIfNull: false, toJson: Serializer.readOnly, defaultValue: 0)
|
||||
final int memberCount;
|
||||
|
||||
/// The number of seconds in a cooldown
|
||||
@JsonKey(includeIfNull: false, defaultValue: 0)
|
||||
final int cooldown;
|
||||
|
||||
/// Map of custom channel extraData
|
||||
@JsonKey(
|
||||
includeIfNull: false,
|
||||
@@ -107,6 +112,7 @@ class ChannelModel {
|
||||
'deleted_at',
|
||||
'member_count',
|
||||
'team',
|
||||
'cooldown',
|
||||
];
|
||||
|
||||
/// Shortcut for channel name
|
||||
@@ -133,6 +139,7 @@ class ChannelModel {
|
||||
int? memberCount,
|
||||
Map<String, Object?>? extraData,
|
||||
String? team,
|
||||
int? cooldown,
|
||||
}) =>
|
||||
ChannelModel(
|
||||
id: id ?? this.id,
|
||||
@@ -148,6 +155,7 @@ class ChannelModel {
|
||||
memberCount: memberCount ?? this.memberCount,
|
||||
extraData: extraData ?? this.extraData,
|
||||
team: team ?? this.team,
|
||||
cooldown: cooldown ?? this.cooldown,
|
||||
);
|
||||
|
||||
/// Returns a new [ChannelModel] that is a combination of this channelModel
|
||||
@@ -168,6 +176,7 @@ class ChannelModel {
|
||||
memberCount: other.memberCount,
|
||||
extraData: other.extraData,
|
||||
team: other.team,
|
||||
cooldown: other.cooldown,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ ChannelModel _$ChannelModelFromJson(Map<String, dynamic> json) {
|
||||
memberCount: json['member_count'] as int? ?? 0,
|
||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
||||
team: json['team'] as String?,
|
||||
cooldown: json['cooldown'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,6 +58,7 @@ Map<String, dynamic> _$ChannelModelToJson(ChannelModel instance) {
|
||||
writeNotNull('updated_at', readonly(instance.updatedAt));
|
||||
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
||||
writeNotNull('member_count', readonly(instance.memberCount));
|
||||
val['cooldown'] = instance.cooldown;
|
||||
val['extra_data'] = instance.extraData;
|
||||
writeNotNull('team', readonly(instance.team));
|
||||
return val;
|
||||
|
||||
@@ -184,6 +184,8 @@ class EventChannel extends ChannelModel {
|
||||
DateTime? deletedAt,
|
||||
required int memberCount,
|
||||
Map<String, Object?>? extraData,
|
||||
required int cooldown,
|
||||
String? team,
|
||||
}) : super(
|
||||
id: id,
|
||||
type: type,
|
||||
@@ -197,6 +199,8 @@ class EventChannel extends ChannelModel {
|
||||
deletedAt: deletedAt,
|
||||
memberCount: memberCount,
|
||||
extraData: extraData ?? {},
|
||||
cooldown: cooldown,
|
||||
team: team,
|
||||
);
|
||||
|
||||
/// Create a new instance from a json
|
||||
|
||||
@@ -87,5 +87,7 @@ EventChannel _$EventChannelFromJson(Map<String, dynamic> json) {
|
||||
: DateTime.parse(json['deleted_at'] as String),
|
||||
memberCount: json['member_count'] as int? ?? 0,
|
||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
||||
cooldown: json['cooldown'] as int? ?? 0,
|
||||
team: json['team'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"id": "dev",
|
||||
"type": "team",
|
||||
"frozen": true,
|
||||
"cooldown": 0,
|
||||
"name": "#dev",
|
||||
"image": "https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png",
|
||||
"example": 1
|
||||
|
||||
+44
@@ -1859,6 +1859,50 @@ void main() {
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('`.enableSlowMode`', () async {
|
||||
const cooldown = 10;
|
||||
|
||||
final channelModel = ChannelModel(
|
||||
cid: channelCid,
|
||||
cooldown: cooldown,
|
||||
);
|
||||
|
||||
when(() => client.enableSlowdown(
|
||||
channelId,
|
||||
channelType,
|
||||
cooldown,
|
||||
)).thenAnswer((_) async => PartialUpdateChannelResponse()
|
||||
..channel = channelModel);
|
||||
|
||||
final res = await channel.enableSlowMode(cooldownInterval: 10);
|
||||
|
||||
expect(res, isNotNull);
|
||||
|
||||
verify(() => client.enableSlowdown(
|
||||
channelId,
|
||||
channelType,
|
||||
cooldown,
|
||||
)).called(1);
|
||||
});
|
||||
|
||||
test('`.disableSlowMode`', () async {
|
||||
final channelModel = ChannelModel(
|
||||
cid: channelCid,
|
||||
);
|
||||
|
||||
when(() => client.disableSlowdown(
|
||||
channelId,
|
||||
channelType,
|
||||
)).thenAnswer((_) async => PartialUpdateChannelResponse()
|
||||
..channel = channelModel);
|
||||
|
||||
final res = await channel.disableSlowMode();
|
||||
|
||||
expect(res, isNotNull);
|
||||
|
||||
verify(() => client.disableSlowdown(channelId, channelType)).called(1);
|
||||
});
|
||||
|
||||
test('`.banUser`', () async {
|
||||
const userId = 'test-user-id';
|
||||
const options = {'key': 'value'};
|
||||
@@ -605,4 +605,65 @@ void main() {
|
||||
verify(() => client.post(path, data: {})).called(1);
|
||||
verifyNoMoreInteractions(client);
|
||||
});
|
||||
|
||||
test('enableSlowdown', () async {
|
||||
const channelId = 'test-channel-id';
|
||||
const channelType = 'test-channel-type';
|
||||
const cooldown = 10;
|
||||
const set = {
|
||||
'cooldown': 10,
|
||||
};
|
||||
|
||||
final path = _getChannelUrl(channelId, channelType);
|
||||
|
||||
final channelModel = ChannelModel(
|
||||
id: channelId,
|
||||
type: channelType,
|
||||
extraData: set,
|
||||
);
|
||||
|
||||
when(() => client.patch(path, data: {
|
||||
'set': set,
|
||||
})).thenAnswer((_) async => successResponse(path, data: {
|
||||
'channel': channelModel.toJson(),
|
||||
}));
|
||||
|
||||
final res =
|
||||
await channelApi.enableSlowdown(channelId, channelType, cooldown);
|
||||
|
||||
expect(res, isNotNull);
|
||||
|
||||
verify(() => client.patch(path, data: {
|
||||
'set': set,
|
||||
})).called(1);
|
||||
verifyNoMoreInteractions(client);
|
||||
});
|
||||
|
||||
test('disableSlowdown', () async {
|
||||
const channelId = 'test-channel-id';
|
||||
const channelType = 'test-channel-type';
|
||||
const unset = ['cooldown'];
|
||||
|
||||
final path = _getChannelUrl(channelId, channelType);
|
||||
|
||||
final channelModel = ChannelModel(
|
||||
id: channelId,
|
||||
type: channelType,
|
||||
);
|
||||
|
||||
when(() => client.patch(path, data: {
|
||||
'unset': unset,
|
||||
})).thenAnswer((_) async => successResponse(path, data: {
|
||||
'channel': channelModel.toJson(),
|
||||
}));
|
||||
|
||||
final res = await channelApi.disableSlowdown(channelId, channelType);
|
||||
|
||||
expect(res, isNotNull);
|
||||
|
||||
verify(() => client.patch(path, data: {
|
||||
'unset': unset,
|
||||
})).called(1);
|
||||
verifyNoMoreInteractions(client);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ void main() {
|
||||
expect(channel.cid, equals('livestream:test'));
|
||||
expect(channel.extraData['cats'], equals(true));
|
||||
expect(channel.extraData['fruit'], equals(['bananas', 'apples']));
|
||||
expect(channel.cooldown, equals(0));
|
||||
});
|
||||
|
||||
test('should serialize to json correctly', () {
|
||||
@@ -24,7 +25,13 @@ void main() {
|
||||
|
||||
expect(
|
||||
channel.toJson(),
|
||||
{'id': 'id', 'type': 'type', 'frozen': false, 'name': 'cool'},
|
||||
{
|
||||
'id': 'id',
|
||||
'type': 'type',
|
||||
'frozen': false,
|
||||
'cooldown': 0,
|
||||
'name': 'cool',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -38,7 +45,13 @@ void main() {
|
||||
|
||||
expect(
|
||||
channel.toJson(),
|
||||
{'id': 'id', 'type': 'type', 'name': 'cool', 'frozen': false},
|
||||
{
|
||||
'id': 'id',
|
||||
'type': 'type',
|
||||
'frozen': false,
|
||||
'cooldown': 0,
|
||||
'name': 'cool',
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,6 +25,8 @@ typedef ActionButtonBuilder = Widget Function(
|
||||
> **_NOTE:_** The last parameter is the default `ActionButton`
|
||||
You can call `.copyWith` to customize just a subset of properties.
|
||||
|
||||
- Added slow mode which allows a cooldown period after a user sends a message.
|
||||
|
||||
🔄 Changed
|
||||
|
||||
Theming has been upgraded! Most theme classes now have `InheritedTheme` classes associated with
|
||||
|
||||
@@ -100,6 +100,9 @@ abstract class Translations {
|
||||
/// The label for write a message in [MessageInput]
|
||||
String get writeAMessageLabel;
|
||||
|
||||
/// The label for slow mode enabled in [MessageInput]
|
||||
String get slowModeOnLabel;
|
||||
|
||||
/// The label for instant commands in [MessageInput]
|
||||
String get instantCommandsLabel;
|
||||
|
||||
@@ -667,4 +670,7 @@ class DefaultTranslations implements Translations {
|
||||
|
||||
@override
|
||||
String get replyToMessageLabel => 'Reply to Message';
|
||||
|
||||
@override
|
||||
String get slowModeOnLabel => 'Slow mode ON';
|
||||
}
|
||||
|
||||
@@ -315,9 +315,15 @@ class MessageInputState extends State<MessageInput> {
|
||||
|
||||
bool get _hasQuotedMessage => widget.quotedMessage != null;
|
||||
|
||||
late DateTime? _cooldownStartedAt;
|
||||
int? _timeOut;
|
||||
|
||||
Timer? _slowModeTimer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_startSlowMode();
|
||||
_focusNode = widget.focusNode ?? FocusNode();
|
||||
_emojiNames =
|
||||
Emoji.all().where((it) => it.name != null).map((e) => e.name!);
|
||||
@@ -348,6 +354,25 @@ class MessageInputState extends State<MessageInput> {
|
||||
});
|
||||
}
|
||||
|
||||
void _startSlowMode() {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
if (channel.cooldownStartedAt != null) {
|
||||
_cooldownStartedAt = channel.cooldownStartedAt;
|
||||
if (DateTime.now().difference(_cooldownStartedAt!).inSeconds <
|
||||
channel.cooldown!) {
|
||||
_timeOut = channel.cooldown! -
|
||||
DateTime.now().difference(_cooldownStartedAt!).inSeconds;
|
||||
_slowModeTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (_timeOut == 0) {
|
||||
timer.cancel();
|
||||
} else {
|
||||
setState(() => _timeOut = _timeOut! - 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget child = DecoratedBox(
|
||||
@@ -496,20 +521,25 @@ class MessageInputState extends State<MessageInput> {
|
||||
);
|
||||
|
||||
Widget _animateSendButton(BuildContext context) {
|
||||
final sendButton = widget.activeSendButton != null
|
||||
? InkWell(
|
||||
onTap: sendMessage,
|
||||
child: widget.activeSendButton,
|
||||
)
|
||||
: _buildSendButton(context);
|
||||
return AnimatedCrossFade(
|
||||
crossFadeState: (_messageIsPresent || _attachments.isNotEmpty)
|
||||
? CrossFadeState.showFirst
|
||||
: CrossFadeState.showSecond,
|
||||
firstChild: sendButton,
|
||||
secondChild: widget.idleSendButton ?? _buildIdleSendButton(context),
|
||||
duration: _messageInputTheme.sendAnimationDuration!,
|
||||
alignment: Alignment.center,
|
||||
late Widget sendButton;
|
||||
if (_timeOut != null && _timeOut! > 0) {
|
||||
sendButton = _CountdownButton(
|
||||
count: _timeOut!,
|
||||
);
|
||||
} else if (!_messageIsPresent && _attachments.isEmpty) {
|
||||
sendButton = widget.idleSendButton ?? _buildIdleSendButton(context);
|
||||
} else {
|
||||
sendButton = widget.activeSendButton != null
|
||||
? InkWell(
|
||||
onTap: sendMessage,
|
||||
child: widget.activeSendButton,
|
||||
)
|
||||
: _buildSendButton(context);
|
||||
}
|
||||
|
||||
return AnimatedSwitcher(
|
||||
duration: _streamChatTheme.messageInputTheme.sendAnimationDuration!,
|
||||
child: sendButton,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -781,6 +811,10 @@ class MessageInputState extends State<MessageInput> {
|
||||
if (_attachments.isNotEmpty) {
|
||||
return context.translations.addACommentOrSendLabel;
|
||||
}
|
||||
if (_timeOut != 0 && _timeOut != null) {
|
||||
return context.translations.slowModeOnLabel;
|
||||
}
|
||||
|
||||
return context.translations.writeAMessageLabel;
|
||||
}
|
||||
|
||||
@@ -2115,6 +2149,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
if (resp.message?.type == 'error') {
|
||||
_parseExistingMessage(message);
|
||||
}
|
||||
_startSlowMode();
|
||||
widget.onMessageSent?.call(resp.message);
|
||||
} catch (e, stk) {
|
||||
if (widget.onError != null) {
|
||||
@@ -2207,6 +2242,7 @@ class MessageInputState extends State<MessageInput> {
|
||||
_emojiOverlay?.remove();
|
||||
_mentionsOverlay?.remove();
|
||||
_keyboardListener?.cancel();
|
||||
_slowModeTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -2380,3 +2416,30 @@ class _PickerWidgetState extends State<_PickerWidget> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _CountdownButton extends StatelessWidget {
|
||||
const _CountdownButton({
|
||||
Key? key,
|
||||
required this.count,
|
||||
}) : super(key: key);
|
||||
|
||||
final int count;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: StreamChatTheme.of(context).colorTheme.disabled,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 24,
|
||||
width: 24,
|
||||
child: Center(
|
||||
child: Text('$count'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,4 +69,69 @@ void main() {
|
||||
expect(find.byKey(const Key('messageInputText')), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'checks message input slow mode',
|
||||
(WidgetTester tester) async {
|
||||
final client = MockClient();
|
||||
final clientState = MockClientState();
|
||||
final channel = MockChannel();
|
||||
final channelState = MockChannelState();
|
||||
final lastMessageAt = DateTime.parse('2020-06-22 12:00:00');
|
||||
|
||||
when(() => client.state).thenReturn(clientState);
|
||||
when(() => clientState.currentUser).thenReturn(OwnUser(id: 'user-id'));
|
||||
when(() => channel.lastMessageAt).thenReturn(lastMessageAt);
|
||||
when(() => channel.state).thenReturn(channelState);
|
||||
when(() => channel.cooldown).thenReturn(10);
|
||||
when(() => channel.cooldownStartedAt).thenReturn(DateTime.now());
|
||||
when(() => channel.client).thenReturn(client);
|
||||
when(() => channel.isMuted).thenReturn(false);
|
||||
when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false));
|
||||
when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({
|
||||
'name': 'test',
|
||||
}));
|
||||
when(() => channel.extraData).thenReturn({
|
||||
'name': 'test',
|
||||
});
|
||||
when(() => channelState.membersStream).thenAnswer((i) => Stream.value([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
)
|
||||
]));
|
||||
when(() => channelState.members).thenReturn([
|
||||
Member(
|
||||
userId: 'user-id',
|
||||
user: User(id: 'user-id'),
|
||||
),
|
||||
]);
|
||||
when(() => channelState.messages).thenReturn([
|
||||
Message(
|
||||
text: 'hello',
|
||||
user: User(id: 'other-user'),
|
||||
)
|
||||
]);
|
||||
when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([
|
||||
Message(
|
||||
text: 'hello',
|
||||
user: User(id: 'other-user'),
|
||||
)
|
||||
]));
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StreamChat(
|
||||
client: client,
|
||||
child: StreamChannel(
|
||||
channel: channel,
|
||||
child: const Scaffold(
|
||||
body: MessageInput(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
expect(find.text('Slow mode ON'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -558,6 +558,7 @@ void main() {
|
||||
config: ChannelConfig(),
|
||||
createdAt: DateTime.now(),
|
||||
memberCount: 1,
|
||||
cooldown: 0,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Added support for [Spanish](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_es.dart) locale.
|
||||
* Added support for [Korean](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ko.dart) locale.
|
||||
* Added support for [Japanese](https://github.com/GetStream/stream-chat-flutter/blob/master/packages/stream_chat_localizations/lib/src/stream_chat_localizations_ja.dart) locale.
|
||||
* Added strings for cooldown mode.
|
||||
|
||||
🔄 Changed
|
||||
|
||||
|
||||
@@ -383,6 +383,9 @@ class NnStreamChatLocalizations extends GlobalStreamChatLocalizations {
|
||||
|
||||
@override
|
||||
String get replyToMessageLabel => 'Reply to Message';
|
||||
|
||||
@override
|
||||
String get slowModeOnLabel => 'Slow mode ON';
|
||||
}
|
||||
|
||||
void main() async {
|
||||
|
||||
@@ -359,4 +359,7 @@ class StreamChatLocalizationsEn extends GlobalStreamChatLocalizations {
|
||||
|
||||
@override
|
||||
String get replyToMessageLabel => 'Reply to Message';
|
||||
|
||||
@override
|
||||
String get slowModeOnLabel => 'Slow mode ON';
|
||||
}
|
||||
|
||||
@@ -364,4 +364,7 @@ class StreamChatLocalizationsEs extends GlobalStreamChatLocalizations {
|
||||
|
||||
@override
|
||||
String get replyToMessageLabel => 'Responder al Mensaje';
|
||||
|
||||
@override
|
||||
String get slowModeOnLabel => 'Modo lento activado';
|
||||
}
|
||||
|
||||
@@ -363,4 +363,7 @@ class StreamChatLocalizationsFr extends GlobalStreamChatLocalizations {
|
||||
|
||||
@override
|
||||
String get replyToMessageLabel => 'Répondre au Message';
|
||||
|
||||
@override
|
||||
String get slowModeOnLabel => 'Mode lent activé';
|
||||
}
|
||||
|
||||
@@ -358,4 +358,7 @@ class StreamChatLocalizationsHi extends GlobalStreamChatLocalizations {
|
||||
|
||||
@override
|
||||
String get replyToMessageLabel => 'संदेश का जवाब';
|
||||
|
||||
@override
|
||||
String get slowModeOnLabel => 'स्लो मोड चालू';
|
||||
}
|
||||
|
||||
@@ -360,4 +360,7 @@ Il file è troppo grande per essere caricato. Il limite è di $limitInMB MB.''';
|
||||
|
||||
@override
|
||||
String get replyToMessageLabel => 'Rispondi al messaggio';
|
||||
|
||||
@override
|
||||
String get slowModeOnLabel => 'Slowmode attiva';
|
||||
}
|
||||
|
||||
@@ -346,4 +346,7 @@ class StreamChatLocalizationsJa extends GlobalStreamChatLocalizations {
|
||||
|
||||
@override
|
||||
String get replyToMessageLabel => 'メッセージに返信';
|
||||
|
||||
@override
|
||||
String get slowModeOnLabel => 'スローモードオン';
|
||||
}
|
||||
|
||||
@@ -344,4 +344,7 @@ class StreamChatLocalizationsKo extends GlobalStreamChatLocalizations {
|
||||
|
||||
@override
|
||||
String get replyToMessageLabel => '메시지에 회신합니다.';
|
||||
|
||||
@override
|
||||
String get slowModeOnLabel => '슬로모드 켜짐';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user