2024-02-11 00:29:58 -05:00
|
|
|
import 'package:async_tools/async_tools.dart';
|
2024-05-01 20:58:25 -04:00
|
|
|
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
2024-02-11 00:29:58 -05:00
|
|
|
import 'package:equatable/equatable.dart';
|
|
|
|
import 'package:meta/meta.dart';
|
2024-02-09 21:17:28 -05:00
|
|
|
import 'package:veilid_support/veilid_support.dart';
|
|
|
|
|
|
|
|
import '../../account_manager/account_manager.dart';
|
|
|
|
import '../../contacts/contacts.dart';
|
2024-06-13 14:52:34 -04:00
|
|
|
import '../../conversation/conversation.dart';
|
2024-02-09 21:17:28 -05:00
|
|
|
import '../../proto/proto.dart' as proto;
|
2024-04-03 21:55:49 -04:00
|
|
|
import 'cubits.dart';
|
2024-02-09 21:17:28 -05:00
|
|
|
|
2024-02-11 00:29:58 -05:00
|
|
|
@immutable
|
|
|
|
class ActiveConversationState extends Equatable {
|
|
|
|
const ActiveConversationState({
|
|
|
|
required this.contact,
|
|
|
|
required this.localConversation,
|
|
|
|
required this.remoteConversation,
|
|
|
|
});
|
|
|
|
|
|
|
|
final proto.Contact contact;
|
|
|
|
final proto.Conversation localConversation;
|
|
|
|
final proto.Conversation remoteConversation;
|
|
|
|
|
|
|
|
@override
|
|
|
|
List<Object?> get props => [contact, localConversation, remoteConversation];
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef ActiveConversationCubit = TransformerCubit<
|
2024-06-13 14:52:34 -04:00
|
|
|
AsyncValue<ActiveConversationState>,
|
|
|
|
AsyncValue<ConversationState>,
|
|
|
|
ConversationCubit>;
|
2024-02-11 00:29:58 -05:00
|
|
|
|
|
|
|
typedef ActiveConversationsBlocMapState
|
|
|
|
= BlocMapState<TypedKey, AsyncValue<ActiveConversationState>>;
|
|
|
|
|
2024-05-27 18:04:00 -04:00
|
|
|
// Map of localConversationRecordKey to ActiveConversationCubit
|
2024-02-11 00:29:58 -05:00
|
|
|
// Wraps a conversation cubit to only expose completely built conversations
|
2024-02-20 20:07:35 -05:00
|
|
|
// Automatically follows the state of a ChatListCubit.
|
2024-03-24 12:13:27 -04:00
|
|
|
// Even though 'conversations' are per-contact and not per-chat
|
|
|
|
// We currently only build the cubits for the chats that are active, not
|
|
|
|
// archived chats or contacts that are not actively in a chat.
|
2024-02-20 17:57:05 -05:00
|
|
|
class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
2024-02-20 20:07:35 -05:00
|
|
|
AsyncValue<ActiveConversationState>, ActiveConversationCubit>
|
2024-04-03 21:55:49 -04:00
|
|
|
with StateMapFollower<ChatListCubitState, TypedKey, proto.Chat> {
|
2024-02-20 17:57:05 -05:00
|
|
|
ActiveConversationsBlocMapCubit(
|
2024-06-13 14:52:34 -04:00
|
|
|
{required UnlockedAccountInfo unlockedAccountInfo,
|
2024-02-20 20:07:35 -05:00
|
|
|
required ContactListCubit contactListCubit})
|
2024-06-13 14:52:34 -04:00
|
|
|
: _activeAccountInfo = unlockedAccountInfo,
|
2024-02-20 20:07:35 -05:00
|
|
|
_contactListCubit = contactListCubit;
|
2024-02-09 21:17:28 -05:00
|
|
|
|
2024-06-13 14:52:34 -04:00
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Public Interface
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Private Implementation
|
|
|
|
|
2024-02-11 00:29:58 -05:00
|
|
|
// Add an active conversation to be tracked for changes
|
2024-02-28 11:58:46 -05:00
|
|
|
Future<void> _addConversation({required proto.Contact contact}) async =>
|
2024-02-09 21:17:28 -05:00
|
|
|
add(() => MapEntry(
|
2024-05-27 18:04:00 -04:00
|
|
|
contact.localConversationRecordKey.toVeilid(),
|
2024-02-11 00:29:58 -05:00
|
|
|
TransformerCubit(
|
|
|
|
ConversationCubit(
|
|
|
|
activeAccountInfo: _activeAccountInfo,
|
2024-02-11 23:18:20 -05:00
|
|
|
remoteIdentityPublicKey: contact.identityPublicKey.toVeilid(),
|
|
|
|
localConversationRecordKey:
|
|
|
|
contact.localConversationRecordKey.toVeilid(),
|
2024-02-11 00:29:58 -05:00
|
|
|
remoteConversationRecordKey:
|
2024-02-11 23:18:20 -05:00
|
|
|
contact.remoteConversationRecordKey.toVeilid(),
|
2024-02-11 00:29:58 -05:00
|
|
|
),
|
|
|
|
// Transformer that only passes through completed conversations
|
|
|
|
// along with the contact that corresponds to the completed
|
|
|
|
// conversation
|
|
|
|
transform: (avstate) => avstate.when(
|
|
|
|
data: (data) => (data.localConversation == null ||
|
|
|
|
data.remoteConversation == null)
|
|
|
|
? const AsyncValue.loading()
|
|
|
|
: AsyncValue.data(ActiveConversationState(
|
|
|
|
contact: contact,
|
|
|
|
localConversation: data.localConversation!,
|
|
|
|
remoteConversation: data.remoteConversation!)),
|
|
|
|
loading: AsyncValue.loading,
|
|
|
|
error: AsyncValue.error))));
|
2024-02-09 21:17:28 -05:00
|
|
|
|
2024-02-20 20:07:35 -05:00
|
|
|
/// StateFollower /////////////////////////
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> removeFromState(TypedKey key) => remove(key);
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> updateState(TypedKey key, proto.Chat value) async {
|
2024-04-05 22:03:04 -04:00
|
|
|
final contactList = _contactListCubit.state.state.asData?.value;
|
2024-02-20 20:07:35 -05:00
|
|
|
if (contactList == null) {
|
|
|
|
await addState(key, const AsyncValue.loading());
|
|
|
|
return;
|
|
|
|
}
|
2024-04-17 21:31:26 -04:00
|
|
|
final contactIndex = contactList.indexWhere(
|
2024-05-27 18:04:00 -04:00
|
|
|
(c) => c.value.localConversationRecordKey.toVeilid() == key);
|
2024-02-20 20:07:35 -05:00
|
|
|
if (contactIndex == -1) {
|
2024-03-24 12:13:27 -04:00
|
|
|
await addState(key, AsyncValue.error('Contact not found'));
|
2024-02-20 20:07:35 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
final contact = contactList[contactIndex];
|
2024-04-17 21:31:26 -04:00
|
|
|
await _addConversation(contact: contact.value);
|
2024-02-20 20:07:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
////
|
|
|
|
|
2024-06-13 14:52:34 -04:00
|
|
|
final UnlockedAccountInfo _activeAccountInfo;
|
2024-02-20 20:07:35 -05:00
|
|
|
final ContactListCubit _contactListCubit;
|
2024-02-09 21:17:28 -05:00
|
|
|
}
|