chore: move controllers from ui to core

Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
Sahil Kumar
2022-02-07 15:25:07 +05:30
committed by xsahil03x
parent ce22e854a8
commit 202af02f8e
12 changed files with 44 additions and 71 deletions
@@ -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 youll 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();
}
}
@@ -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];
}
}
@@ -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';