chore: move controllers from ui to core
Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
-281
@@ -1,281 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:stream_chat/stream_chat.dart' hide Success;
|
||||
import 'package:stream_chat_flutter/src/paged_value_notifier.dart';
|
||||
import 'package:stream_chat_flutter/src/v4/channel_list_view/stream_channel_list_event_handler.dart';
|
||||
|
||||
/// The default channel page limit to load.
|
||||
const defaultChannelPagedLimit = 10;
|
||||
|
||||
const _kDefaultBackendPaginationLimit = 30;
|
||||
|
||||
/// A controller for a Channel list.
|
||||
///
|
||||
/// This class lets you perform tasks such as:
|
||||
/// * Load initial data.
|
||||
/// * Use channel events handlers.
|
||||
/// * Load more data using [loadMore].
|
||||
/// * Replace the previously loaded channels.
|
||||
/// * Return/Create a new channel and start watching it.
|
||||
/// * Pause and Resume all subscriptions added to this composite.
|
||||
class StreamChannelListController extends PagedValueNotifier<int, Channel> {
|
||||
/// Creates a Stream channel list controller.
|
||||
///
|
||||
/// * `client` is the Stream chat client to use for the channels list.
|
||||
///
|
||||
/// * `channelEventHandlers` is the channel events to use for the channels
|
||||
/// list. This class can be mixed in or extended to create custom overrides.
|
||||
/// See [StreamChannelListEventHandler] for advice.
|
||||
///
|
||||
/// * `filter` is the query filters to use.
|
||||
///
|
||||
/// * `sort` is the sorting used for the channels matching the filters.
|
||||
///
|
||||
/// * `presence` sets whether you'll receive user presence updates via the
|
||||
/// websocket events.
|
||||
///
|
||||
/// * `limit` is the limit to apply to the channel list.
|
||||
///
|
||||
/// * `messageLimit` is the number of messages to fetch in each channel.
|
||||
///
|
||||
/// * `memberLimit` is the number of members to fetch in each channel.
|
||||
StreamChannelListController({
|
||||
required this.client,
|
||||
StreamChannelListEventHandler? eventHandler,
|
||||
this.filter,
|
||||
this.sort,
|
||||
this.presence = true,
|
||||
this.limit = defaultChannelPagedLimit,
|
||||
this.messageLimit,
|
||||
this.memberLimit,
|
||||
}) : _eventHandler = eventHandler ?? StreamChannelListEventHandler(),
|
||||
super(const PagedValue.loading());
|
||||
|
||||
/// Creates a [StreamChannelListController] from the passed [value].
|
||||
StreamChannelListController.fromValue(
|
||||
PagedValue<int, Channel> value, {
|
||||
required this.client,
|
||||
StreamChannelListEventHandler? eventHandler,
|
||||
this.filter,
|
||||
this.sort,
|
||||
this.presence = true,
|
||||
this.limit = defaultChannelPagedLimit,
|
||||
this.messageLimit,
|
||||
this.memberLimit,
|
||||
}) : _eventHandler = eventHandler ?? StreamChannelListEventHandler(),
|
||||
super(value);
|
||||
|
||||
/// The client to use for the channels list.
|
||||
final StreamChatClient client;
|
||||
|
||||
/// The channel event handlers to use for the channels list.
|
||||
final StreamChannelListEventHandler _eventHandler;
|
||||
|
||||
/// The query filters to use.
|
||||
///
|
||||
/// You can query on any of the custom fields you've defined on the [Channel].
|
||||
///
|
||||
/// You can also filter other built-in channel fields.
|
||||
final Filter? filter;
|
||||
|
||||
/// The sorting used for the channels matching the filters.
|
||||
///
|
||||
/// Sorting is based on field and direction, multiple sorting options
|
||||
/// can be provided.
|
||||
///
|
||||
/// You can sort based on last_updated, last_message_at, updated_at,
|
||||
/// created_at or member_count.
|
||||
///
|
||||
/// Direction can be ascending or descending.
|
||||
final List<SortOption<ChannelModel>>? sort;
|
||||
|
||||
/// If true you’ll receive user presence updates via the websocket events
|
||||
final bool presence;
|
||||
|
||||
/// The limit to apply to the channel list. The default is set to
|
||||
/// [defaultChannelPagedLimit].
|
||||
final int limit;
|
||||
|
||||
/// Number of messages to fetch in each channel.
|
||||
final int? messageLimit;
|
||||
|
||||
/// Number of members to fetch in each channel.
|
||||
final int? memberLimit;
|
||||
|
||||
@override
|
||||
Future<void> doInitialLoad() async {
|
||||
final limit = min(
|
||||
this.limit * defaultInitialPagedLimitMultiplier,
|
||||
_kDefaultBackendPaginationLimit,
|
||||
);
|
||||
try {
|
||||
await for (final channels in client.queryChannels(
|
||||
filter: filter,
|
||||
sort: sort,
|
||||
memberLimit: memberLimit,
|
||||
messageLimit: messageLimit,
|
||||
presence: presence,
|
||||
paginationParams: PaginationParams(limit: limit),
|
||||
)) {
|
||||
final nextKey = channels.length < limit ? null : channels.length;
|
||||
value = PagedValue(
|
||||
items: channels,
|
||||
nextPageKey: nextKey,
|
||||
);
|
||||
}
|
||||
// start listening to events
|
||||
_subscribeToChannelListEvents();
|
||||
} on StreamChatError catch (error) {
|
||||
value = PagedValue.error(error);
|
||||
} catch (error) {
|
||||
final chatError = StreamChatError(error.toString());
|
||||
value = PagedValue.error(chatError);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> loadMore(int nextPageKey) async {
|
||||
final previousValue = value.asSuccess;
|
||||
|
||||
try {
|
||||
await for (final channels in client.queryChannels(
|
||||
filter: filter,
|
||||
sort: sort,
|
||||
memberLimit: memberLimit,
|
||||
messageLimit: messageLimit,
|
||||
presence: presence,
|
||||
paginationParams: PaginationParams(limit: limit, offset: nextPageKey),
|
||||
)) {
|
||||
final previousItems = previousValue.items;
|
||||
final newItems = previousItems + channels;
|
||||
final nextKey = channels.length < limit ? null : newItems.length;
|
||||
value = PagedValue(
|
||||
items: newItems,
|
||||
nextPageKey: nextKey,
|
||||
);
|
||||
}
|
||||
} on StreamChatError catch (error) {
|
||||
value = previousValue.copyWith(error: error);
|
||||
} catch (error) {
|
||||
final chatError = StreamChatError(error.toString());
|
||||
value = previousValue.copyWith(error: chatError);
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the previously loaded channels with [channels] and updates
|
||||
/// the nextPageKey.
|
||||
set channels(List<Channel> channels) {
|
||||
value = PagedValue(
|
||||
items: channels,
|
||||
nextPageKey: channels.length,
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns/Creates a new Channel and starts watching it.
|
||||
Future<Channel> getChannel({
|
||||
required String id,
|
||||
required String type,
|
||||
}) async {
|
||||
final channel = client.channel(type, id: id);
|
||||
await channel.watch();
|
||||
return channel;
|
||||
}
|
||||
|
||||
/// Leaves the [channel] and updates the list.
|
||||
Future<void> leaveChannel(Channel channel) async {
|
||||
final user = client.state.currentUser;
|
||||
assert(user != null, 'You must be logged in to leave a channel.');
|
||||
await channel.removeMembers([user!.id]);
|
||||
}
|
||||
|
||||
/// Deletes the [channel] and updates the list.
|
||||
Future<void> deleteChannel(Channel channel) async {
|
||||
await channel.delete();
|
||||
}
|
||||
|
||||
/// Mutes the [channel] and updates the list.
|
||||
Future<void> muteChannel(Channel channel) async {
|
||||
await channel.mute();
|
||||
}
|
||||
|
||||
/// Un-mutes the [channel] and updates the list.
|
||||
Future<void> unmuteChannel(Channel channel) async {
|
||||
await channel.unmute();
|
||||
}
|
||||
|
||||
/// Event listener, which can be set in order to listen
|
||||
/// [client] web-socket events.
|
||||
///
|
||||
/// Return `true` if the event is handled. Return `false` to
|
||||
/// allow the event to be handled internally.
|
||||
bool Function(Event event)? eventListener;
|
||||
|
||||
StreamSubscription<Event>? _channelEventSubscription;
|
||||
|
||||
// Subscribes to the channel list events.
|
||||
void _subscribeToChannelListEvents() {
|
||||
if (_channelEventSubscription != null) {
|
||||
_unsubscribeFromChannelListEvents();
|
||||
}
|
||||
|
||||
_channelEventSubscription = client.on().listen((event) {
|
||||
// Returns early if the event is already handled by the listener.
|
||||
if (eventListener?.call(event) ?? false) return;
|
||||
|
||||
final eventType = event.type;
|
||||
if (eventType == EventType.channelDeleted) {
|
||||
_eventHandler.onChannelDeleted(event, this);
|
||||
} else if (eventType == EventType.channelHidden) {
|
||||
_eventHandler.onChannelHidden(event, this);
|
||||
} else if (eventType == EventType.channelTruncated) {
|
||||
_eventHandler.onChannelTruncated(event, this);
|
||||
} else if (eventType == EventType.channelUpdated) {
|
||||
_eventHandler.onChannelUpdated(event, this);
|
||||
} else if (eventType == EventType.channelVisible) {
|
||||
_eventHandler.onChannelVisible(event, this);
|
||||
} else if (eventType == EventType.connectionRecovered) {
|
||||
_eventHandler.onConnectionRecovered(event, this);
|
||||
} else if (eventType == EventType.connectionChanged) {
|
||||
if (event.online != null) {
|
||||
_eventHandler.onConnectionRecovered(event, this);
|
||||
}
|
||||
} else if (eventType == EventType.messageNew) {
|
||||
_eventHandler.onMessageNew(event, this);
|
||||
} else if (eventType == EventType.notificationAddedToChannel) {
|
||||
_eventHandler.onNotificationAddedToChannel(event, this);
|
||||
} else if (eventType == EventType.notificationMessageNew) {
|
||||
_eventHandler.onNotificationMessageNew(event, this);
|
||||
} else if (eventType == EventType.notificationRemovedFromChannel) {
|
||||
_eventHandler.onNotificationRemovedFromChannel(event, this);
|
||||
} else if (eventType == 'user.presence.changed' ||
|
||||
eventType == EventType.userUpdated) {
|
||||
_eventHandler.onUserPresenceChanged(event, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Unsubscribes from all channel list events.
|
||||
void _unsubscribeFromChannelListEvents() {
|
||||
if (_channelEventSubscription != null) {
|
||||
_channelEventSubscription!.cancel();
|
||||
_channelEventSubscription = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Pauses all subscriptions added to this composite.
|
||||
void pauseEventsSubscription([Future<void>? resumeSignal]) {
|
||||
_channelEventSubscription?.pause(resumeSignal);
|
||||
}
|
||||
|
||||
/// Resumes all subscriptions added to this composite.
|
||||
void resumeEventsSubscription() {
|
||||
_channelEventSubscription?.resume();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_unsubscribeFromChannelListEvents();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
-213
@@ -1,213 +0,0 @@
|
||||
import 'package:stream_chat/stream_chat.dart' show ChannelState, Event;
|
||||
import 'package:stream_chat_flutter/src/v4/channel_list_view/stream_channel_list_controller.dart';
|
||||
|
||||
/// Contains handlers that are called from [StreamChannelListController] for
|
||||
/// certain [Event]s.
|
||||
///
|
||||
/// This class can be mixed in or extended to create custom overrides.
|
||||
class StreamChannelListEventHandler {
|
||||
/// Function which gets called for the event
|
||||
/// [EventType.channelDeleted].
|
||||
///
|
||||
/// This event is fired when a channel is deleted.
|
||||
///
|
||||
/// By default, this removes the channel from the list of channels.
|
||||
void onChannelDeleted(Event event, StreamChannelListController controller) {
|
||||
final channels = [...controller.currentItems];
|
||||
|
||||
final updatedChannels = channels
|
||||
..removeWhere(
|
||||
(it) => it.cid == (event.cid ?? event.channel?.cid),
|
||||
);
|
||||
|
||||
controller.channels = updatedChannels;
|
||||
}
|
||||
|
||||
/// Function which gets called for the event
|
||||
/// [EventType.channelHidden].
|
||||
///
|
||||
/// This event is fired when a channel is hidden.
|
||||
///
|
||||
/// By default, this removes the channel from the list of channels.
|
||||
void onChannelHidden(Event event, StreamChannelListController controller) {
|
||||
onChannelDeleted(event, controller);
|
||||
}
|
||||
|
||||
/// Function which gets called for the event
|
||||
/// [EventType.channelTruncated].
|
||||
///
|
||||
/// This event is fired when a channel is truncated.
|
||||
///
|
||||
/// By default, this refreshes the whole channel list.
|
||||
void onChannelTruncated(Event event, StreamChannelListController controller) {
|
||||
controller.refresh();
|
||||
}
|
||||
|
||||
/// Function which gets called for the event
|
||||
/// [EventType.channelUpdated].
|
||||
///
|
||||
/// This event is fired when a channel is updated.
|
||||
///
|
||||
/// By default, this updates the channel received in the event.
|
||||
void onChannelUpdated(Event event, StreamChannelListController controller) {
|
||||
final eventChannel = event.channel;
|
||||
if (eventChannel == null) return;
|
||||
|
||||
final channels = [...controller.currentItems];
|
||||
final channelIndex = channels.indexWhere(
|
||||
(it) => it.cid == (event.cid ?? eventChannel.cid),
|
||||
);
|
||||
|
||||
if (channelIndex >= 0) {
|
||||
final channelState = ChannelState(channel: eventChannel);
|
||||
channels[channelIndex].state?.updateChannelState(channelState);
|
||||
}
|
||||
|
||||
controller.channels = channels;
|
||||
}
|
||||
|
||||
/// Function which gets called for the event
|
||||
/// [EventType.channelVisible].
|
||||
///
|
||||
/// This event is fired when a channel is made visible.
|
||||
///
|
||||
/// By default, this adds the channel to the list of channels.
|
||||
void onChannelVisible(
|
||||
Event event,
|
||||
StreamChannelListController controller,
|
||||
) async {
|
||||
final channelId = event.channelId;
|
||||
final channelType = event.channelType;
|
||||
|
||||
if (channelId == null || channelType == null) return;
|
||||
|
||||
final channel = await controller.getChannel(
|
||||
id: channelId,
|
||||
type: channelType,
|
||||
);
|
||||
|
||||
final currentChannels = [...controller.currentItems];
|
||||
|
||||
final updatedChannels = [
|
||||
channel,
|
||||
...currentChannels..removeWhere((it) => it.cid == channel.cid),
|
||||
];
|
||||
|
||||
controller.channels = updatedChannels;
|
||||
}
|
||||
|
||||
/// Function which gets called for the event
|
||||
/// [EventType.connectionRecovered].
|
||||
///
|
||||
/// This event is fired when the client web-socket connection recovers.
|
||||
///
|
||||
/// By default, this refreshes the whole channel list.
|
||||
void onConnectionRecovered(
|
||||
Event event,
|
||||
StreamChannelListController controller,
|
||||
) {
|
||||
controller.refresh();
|
||||
}
|
||||
|
||||
/// Function which gets called for the event [EventType.messageNew].
|
||||
///
|
||||
/// This event is fired when a new message is created in one of the channels
|
||||
/// we are currently watching.
|
||||
///
|
||||
/// By default, this moves the channel to the top of the list.
|
||||
void onMessageNew(Event event, StreamChannelListController controller) {
|
||||
final channelCid = event.cid;
|
||||
if (channelCid == null) return;
|
||||
|
||||
final channels = [...controller.currentItems];
|
||||
|
||||
final channelIndex = channels.indexWhere((it) => it.cid == channelCid);
|
||||
if (channelIndex <= 0) return;
|
||||
|
||||
final channel = channels.removeAt(channelIndex);
|
||||
channels.insert(0, channel);
|
||||
|
||||
controller.channels = [...channels];
|
||||
}
|
||||
|
||||
/// Function which gets called for the event
|
||||
/// [EventType.notificationAddedToChannel].
|
||||
///
|
||||
/// This event is fired when a channel is added which we are not watching.
|
||||
///
|
||||
/// By default, this adds the channel and moves it to the top of list.
|
||||
void onNotificationAddedToChannel(
|
||||
Event event,
|
||||
StreamChannelListController controller,
|
||||
) {
|
||||
onChannelVisible(event, controller);
|
||||
}
|
||||
|
||||
/// Function which gets called for the event
|
||||
/// [EventType.notificationMessageNew].
|
||||
///
|
||||
/// This event is fired when a new message is created in a channel
|
||||
/// which we are not currently watching.
|
||||
///
|
||||
/// By default, this adds the channel and moves it to the top of list.
|
||||
void onNotificationMessageNew(
|
||||
Event event,
|
||||
StreamChannelListController controller,
|
||||
) {
|
||||
onChannelVisible(event, controller);
|
||||
}
|
||||
|
||||
/// Function which gets called for the event
|
||||
/// [EventType.notificationRemovedFromChannel].
|
||||
///
|
||||
/// This event is fired when a user is removed from a channel which we are
|
||||
/// not currently watching.
|
||||
///
|
||||
/// By default, this removes the event channel from the list.
|
||||
void onNotificationRemovedFromChannel(
|
||||
Event event,
|
||||
StreamChannelListController controller,
|
||||
) {
|
||||
final channels = [...controller.currentItems];
|
||||
final updatedChannels =
|
||||
channels.where((it) => it.cid != event.channel?.cid);
|
||||
final listChanged = channels.length != updatedChannels.length;
|
||||
|
||||
if (!listChanged) return;
|
||||
|
||||
controller.channels = [...updatedChannels];
|
||||
}
|
||||
|
||||
/// Function which gets called for the event
|
||||
/// 'user.presence.changed' and [EventType.userUpdated].
|
||||
///
|
||||
/// This event is fired when a user's presence changes or gets updated.
|
||||
///
|
||||
/// By default, this updates the channel member with the event user.
|
||||
void onUserPresenceChanged(
|
||||
Event event,
|
||||
StreamChannelListController controller,
|
||||
) {
|
||||
final user = event.user;
|
||||
if (user == null) return;
|
||||
|
||||
final channels = [...controller.currentItems];
|
||||
|
||||
final updatedChannels = channels.map((channel) {
|
||||
final members = [...channel.state!.members];
|
||||
final memberIndex = members.indexWhere(
|
||||
(it) => user.id == (it.userId ?? it.user?.id),
|
||||
);
|
||||
|
||||
if (memberIndex < 0) return channel;
|
||||
|
||||
members[memberIndex] = members[memberIndex].copyWith(user: user);
|
||||
final updatedState = ChannelState(members: [...members]);
|
||||
channel.state!.updateChannelState(updatedState);
|
||||
|
||||
return channel;
|
||||
});
|
||||
|
||||
controller.channels = [...updatedChannels];
|
||||
}
|
||||
}
|
||||
-3
@@ -1,11 +1,8 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat/stream_chat.dart';
|
||||
import 'package:stream_chat_flutter/src/extension.dart';
|
||||
import 'package:stream_chat_flutter/src/paged_value_notifier.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
||||
import 'package:stream_chat_flutter/src/v4/channel_list_view/stream_channel_list_controller.dart';
|
||||
import 'package:stream_chat_flutter/src/v4/channel_list_view/stream_channel_list_loading_tile.dart';
|
||||
import 'package:stream_chat_flutter/src/v4/channel_list_view/stream_channel_list_tile.dart';
|
||||
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
||||
|
||||
Reference in New Issue
Block a user