progress on slow mode
This commit is contained in:
@@ -130,6 +130,9 @@ class Channel {
|
|||||||
return state?.channelStateStream.map((cs) => cs.channel?.cooldown);
|
return state?.channelStateStream.map((cs) => cs.channel?.cooldown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
DateTime? cooldownStartedAt;
|
||||||
|
|
||||||
/// Channel creation date
|
/// Channel creation date
|
||||||
DateTime? get createdAt {
|
DateTime? get createdAt {
|
||||||
_checkInitialized();
|
_checkInitialized();
|
||||||
@@ -429,6 +432,9 @@ class Channel {
|
|||||||
skipPush: skipPush,
|
skipPush: skipPush,
|
||||||
);
|
);
|
||||||
state!.addMessage(response.message);
|
state!.addMessage(response.message);
|
||||||
|
if (cooldown! > 0) {
|
||||||
|
cooldownStartedAt = DateTime.now();
|
||||||
|
}
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e is StreamChatNetworkError && e.isRetriable) {
|
if (e is StreamChatNetworkError && e.isRetriable) {
|
||||||
@@ -827,6 +833,20 @@ class Channel {
|
|||||||
return _client.updateChannelPartial(id!, type, set: set, unset: unset);
|
return _client.updateChannelPartial(id!, type, set: set, unset: unset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable slow mode
|
||||||
|
Future<UpdateChannelResponse> enableSlowMode({
|
||||||
|
required int cooldownInterval,
|
||||||
|
}) async {
|
||||||
|
_checkInitialized();
|
||||||
|
return _client.enableSlowdown(id!, type, cooldownInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable slow mode
|
||||||
|
Future<UpdateChannelResponse> disableSlowMode() async {
|
||||||
|
_checkInitialized();
|
||||||
|
return _client.disableSlowdown(id!, type);
|
||||||
|
}
|
||||||
|
|
||||||
/// Delete this channel. Messages are permanently removed.
|
/// Delete this channel. Messages are permanently removed.
|
||||||
Future<EmptyResponse> delete() async {
|
Future<EmptyResponse> delete() async {
|
||||||
_checkInitialized();
|
_checkInitialized();
|
||||||
|
|||||||
@@ -1244,6 +1244,28 @@ class StreamChatClient {
|
|||||||
language,
|
language,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Enables slow mode
|
||||||
|
Future<UpdateChannelResponse> enableSlowdown(
|
||||||
|
String channelId,
|
||||||
|
String channelType,
|
||||||
|
int cooldown,
|
||||||
|
) async =>
|
||||||
|
_chatApi.channel.enableSlowdown(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
cooldown,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Disables slow mode
|
||||||
|
Future<UpdateChannelResponse> disableSlowdown(
|
||||||
|
String channelId,
|
||||||
|
String channelType,
|
||||||
|
) async =>
|
||||||
|
_chatApi.channel.disableSlowdown(
|
||||||
|
channelId,
|
||||||
|
channelType,
|
||||||
|
);
|
||||||
|
|
||||||
/// Pins provided message
|
/// Pins provided message
|
||||||
/// [timeoutOrExpirationDate] can either be a [DateTime] or a value in seconds
|
/// [timeoutOrExpirationDate] can either be a [DateTime] or a value in seconds
|
||||||
/// to be added to [DateTime.now]
|
/// to be added to [DateTime.now]
|
||||||
|
|||||||
@@ -123,6 +123,35 @@ class ChannelApi {
|
|||||||
return PartialUpdateChannelResponse.fromJson(response.data);
|
return PartialUpdateChannelResponse.fromJson(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable slowdown
|
||||||
|
Future<UpdateChannelResponse> enableSlowdown(
|
||||||
|
String channelId,
|
||||||
|
String channelType,
|
||||||
|
int cooldown,
|
||||||
|
) async {
|
||||||
|
final response = await _client.post(
|
||||||
|
_getChannelUrl(channelId, channelType),
|
||||||
|
data: {
|
||||||
|
'cooldown': cooldown,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return UpdateChannelResponse.fromJson(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable slowdown
|
||||||
|
Future<UpdateChannelResponse> disableSlowdown(
|
||||||
|
String channelId,
|
||||||
|
String channelType,
|
||||||
|
) async {
|
||||||
|
final response = await _client.post(
|
||||||
|
_getChannelUrl(channelId, channelType),
|
||||||
|
data: {
|
||||||
|
'cooldown': 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return UpdateChannelResponse.fromJson(response.data);
|
||||||
|
}
|
||||||
|
|
||||||
/// Accept invitation to the channel
|
/// Accept invitation to the channel
|
||||||
Future<AcceptInviteResponse> acceptChannelInvite(
|
Future<AcceptInviteResponse> acceptChannelInvite(
|
||||||
String channelId,
|
String channelId,
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class ChannelModel {
|
|||||||
final int memberCount;
|
final int memberCount;
|
||||||
|
|
||||||
/// The number of seconds in a cooldown
|
/// The number of seconds in a cooldown
|
||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(includeIfNull: false, defaultValue: 0)
|
||||||
final int cooldown;
|
final int cooldown;
|
||||||
|
|
||||||
/// Map of custom channel extraData
|
/// Map of custom channel extraData
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ ChannelModel _$ChannelModelFromJson(Map<String, dynamic> json) {
|
|||||||
memberCount: json['member_count'] as int? ?? 0,
|
memberCount: json['member_count'] as int? ?? 0,
|
||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
||||||
team: json['team'] as String?,
|
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('updated_at', readonly(instance.updatedAt));
|
||||||
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
writeNotNull('deleted_at', readonly(instance.deletedAt));
|
||||||
writeNotNull('member_count', readonly(instance.memberCount));
|
writeNotNull('member_count', readonly(instance.memberCount));
|
||||||
|
val['cooldown'] = instance.cooldown;
|
||||||
val['extra_data'] = instance.extraData;
|
val['extra_data'] = instance.extraData;
|
||||||
writeNotNull('team', readonly(instance.team));
|
writeNotNull('team', readonly(instance.team));
|
||||||
return val;
|
return val;
|
||||||
|
|||||||
@@ -36,5 +36,8 @@ OwnUser _$OwnUserFromJson(Map<String, dynamic> json) {
|
|||||||
online: json['online'] as bool? ?? false,
|
online: json['online'] as bool? ?? false,
|
||||||
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
|
||||||
banned: json['banned'] as bool? ?? false,
|
banned: json['banned'] as bool? ?? false,
|
||||||
|
teams:
|
||||||
|
(json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ??
|
||||||
|
[],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ void main() async {
|
|||||||
/// Create a new instance of [StreamChatClient] passing the apikey obtained
|
/// Create a new instance of [StreamChatClient] passing the apikey obtained
|
||||||
/// from your project dashboard.
|
/// from your project dashboard.
|
||||||
final client = StreamChatClient(
|
final client = StreamChatClient(
|
||||||
's2dxdhpxd94g',
|
'kv7mcsxr24p8',
|
||||||
logLevel: Level.INFO,
|
logLevel: Level.INFO,
|
||||||
)..chatPersistenceClient = chatPersistentClient;
|
)..chatPersistenceClient = chatPersistentClient;
|
||||||
|
|
||||||
@@ -24,15 +24,19 @@ void main() async {
|
|||||||
/// Please see the following for more information:
|
/// Please see the following for more information:
|
||||||
/// https://getstream.io/chat/docs/ios_user_setup_and_tokens/
|
/// https://getstream.io/chat/docs/ios_user_setup_and_tokens/
|
||||||
await client.connectUser(
|
await client.connectUser(
|
||||||
User(id: 'super-band-9'),
|
User(id: 'salvatore'),
|
||||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.'
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoic2FsdmF0b3JlIn0.pgiJz7sIc7iP29BHKFwe3nLm5-OaR_1l2P-SlgiC9a8',
|
||||||
'0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final channel = client.channel('messaging', id: 'godevs');
|
final channel = client.channel('messaging', id: 'godevs2', extraData: {
|
||||||
|
'members': ['salvatore'],
|
||||||
|
});
|
||||||
|
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
|
|
||||||
|
await channel.enableSlowMode(cooldownInterval: 20);
|
||||||
|
print('Channel cooldown set to ${channel.cooldown}');
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MyApp(
|
MyApp(
|
||||||
client: client,
|
client: client,
|
||||||
|
|||||||
@@ -291,9 +291,13 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
|
|
||||||
bool get _hasQuotedMessage => widget.quotedMessage != null;
|
bool get _hasQuotedMessage => widget.quotedMessage != null;
|
||||||
|
|
||||||
|
late DateTime? _cooldownStartedAt;
|
||||||
|
int? _timeOut;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_startSlowMode();
|
||||||
_focusNode = widget.focusNode ?? FocusNode();
|
_focusNode = widget.focusNode ?? FocusNode();
|
||||||
_emojiNames =
|
_emojiNames =
|
||||||
Emoji.all().where((it) => it.name != null).map((e) => e.name!);
|
Emoji.all().where((it) => it.name != null).map((e) => e.name!);
|
||||||
@@ -324,6 +328,25 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _startSlowMode() {
|
||||||
|
if (StreamChannel.of(context).channel.cooldownStartedAt != null) {
|
||||||
|
_cooldownStartedAt = StreamChannel.of(context).channel.cooldownStartedAt;
|
||||||
|
if (DateTime.now().difference(_cooldownStartedAt!).inSeconds <
|
||||||
|
StreamChannel.of(context).channel.cooldown!) {
|
||||||
|
_timeOut = StreamChannel.of(context).channel.cooldown! -
|
||||||
|
DateTime.now().difference(_cooldownStartedAt!).inSeconds;
|
||||||
|
Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||||
|
if (_timeOut == 0) {
|
||||||
|
timer.cancel();
|
||||||
|
} else {
|
||||||
|
print('Time left until cooldown is over: $_timeOut');
|
||||||
|
setState(() => _timeOut = _timeOut! - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget child = DecoratedBox(
|
Widget child = DecoratedBox(
|
||||||
@@ -472,13 +495,44 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Widget _animateSendButton(BuildContext context) {
|
Widget _animateSendButton(BuildContext context) {
|
||||||
final sendButton = widget.activeSendButton != null
|
late Widget sendButton;
|
||||||
? InkWell(
|
if (_timeOut != null && _timeOut! > 0) {
|
||||||
onTap: sendMessage,
|
sendButton = _CountdownButton(
|
||||||
child: widget.activeSendButton,
|
count: _timeOut!,
|
||||||
)
|
);
|
||||||
: _buildSendButton(context);
|
} else if (!_messageIsPresent && _attachments.isEmpty) {
|
||||||
return AnimatedCrossFade(
|
sendButton = widget.idleSendButton ?? _buildIdleSendButton(context);
|
||||||
|
} else {
|
||||||
|
sendButton = widget.activeSendButton != null
|
||||||
|
? InkWell(
|
||||||
|
onTap: sendMessage,
|
||||||
|
child: widget.activeSendButton,
|
||||||
|
)
|
||||||
|
: _buildSendButton(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if (_timeOut == null || _timeOut == 0) {
|
||||||
|
sendButton = widget.activeSendButton != null
|
||||||
|
? InkWell(
|
||||||
|
onTap: sendMessage,
|
||||||
|
child: widget.activeSendButton,
|
||||||
|
)
|
||||||
|
: _buildSendButton(context);
|
||||||
|
} else {
|
||||||
|
sendButton = _CountdownButton(
|
||||||
|
count: _timeOut!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_messageIsPresent && _attachments.isEmpty) {
|
||||||
|
sendButton = widget.idleSendButton ?? _buildIdleSendButton(context);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return AnimatedSwitcher(
|
||||||
|
duration: _streamChatTheme.messageInputTheme.sendAnimationDuration!,
|
||||||
|
child: sendButton,
|
||||||
|
);
|
||||||
|
/*return AnimatedCrossFade(
|
||||||
crossFadeState: (_messageIsPresent || _attachments.isNotEmpty)
|
crossFadeState: (_messageIsPresent || _attachments.isNotEmpty)
|
||||||
? CrossFadeState.showFirst
|
? CrossFadeState.showFirst
|
||||||
: CrossFadeState.showSecond,
|
: CrossFadeState.showSecond,
|
||||||
@@ -486,7 +540,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
secondChild: widget.idleSendButton ?? _buildIdleSendButton(context),
|
secondChild: widget.idleSendButton ?? _buildIdleSendButton(context),
|
||||||
duration: _streamChatTheme.messageInputTheme.sendAnimationDuration!,
|
duration: _streamChatTheme.messageInputTheme.sendAnimationDuration!,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
);
|
);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildExpandActionsButton() {
|
Widget _buildExpandActionsButton() {
|
||||||
@@ -2082,6 +2136,7 @@ class MessageInputState extends State<MessageInput> {
|
|||||||
if (resp.message?.type == 'error') {
|
if (resp.message?.type == 'error') {
|
||||||
_parseExistingMessage(message);
|
_parseExistingMessage(message);
|
||||||
}
|
}
|
||||||
|
_startSlowMode();
|
||||||
widget.onMessageSent?.call(resp.message);
|
widget.onMessageSent?.call(resp.message);
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
if (widget.onError != null) {
|
if (widget.onError != null) {
|
||||||
@@ -2347,3 +2402,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'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -942,7 +942,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
final currentUser = StreamChat.of(context).user;
|
final currentUser = StreamChat.of(context).user;
|
||||||
final members = StreamChannel.of(context).channel.state?.members ?? [];
|
final members = StreamChannel.of(context).channel.state?.members ?? [];
|
||||||
final currentUserMember =
|
final currentUserMember =
|
||||||
members.firstWhere((e) => e.user!.id == currentUser!.id);
|
members.firstWhereOrNull((e) => e.user!.id == currentUser!.id);
|
||||||
|
|
||||||
Widget messageWidget = MessageWidget(
|
Widget messageWidget = MessageWidget(
|
||||||
key: ValueKey<String>('MESSAGE-${message.id}'),
|
key: ValueKey<String>('MESSAGE-${message.id}'),
|
||||||
@@ -1049,7 +1049,7 @@ class _MessageListViewState extends State<MessageListView> {
|
|||||||
}
|
}
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
},
|
},
|
||||||
showPinButton: widget.pinPermissions.contains(currentUserMember.role),
|
showPinButton: widget.pinPermissions.contains(currentUserMember?.role),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (widget.messageBuilder != null) {
|
if (widget.messageBuilder != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user