refactor and cleanup in prep for profile changing

This commit is contained in:
Christien Rioux 2024-06-13 14:52:34 -04:00
parent 87bb1657c7
commit 56d65442f4
49 changed files with 967 additions and 655 deletions

View file

@ -1,102 +0,0 @@
import 'package:async_tools/async_tools.dart';
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
import '../../contacts/contacts.dart';
import '../../proto/proto.dart' as proto;
import 'cubits.dart';
@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<
AsyncValue<ActiveConversationState>, AsyncValue<ConversationState>>;
typedef ActiveConversationsBlocMapState
= BlocMapState<TypedKey, AsyncValue<ActiveConversationState>>;
// Map of localConversationRecordKey to ActiveConversationCubit
// Wraps a conversation cubit to only expose completely built conversations
// Automatically follows the state of a ChatListCubit.
// 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.
class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
AsyncValue<ActiveConversationState>, ActiveConversationCubit>
with StateMapFollower<ChatListCubitState, TypedKey, proto.Chat> {
ActiveConversationsBlocMapCubit(
{required ActiveAccountInfo activeAccountInfo,
required ContactListCubit contactListCubit})
: _activeAccountInfo = activeAccountInfo,
_contactListCubit = contactListCubit;
// Add an active conversation to be tracked for changes
Future<void> _addConversation({required proto.Contact contact}) async =>
add(() => MapEntry(
contact.localConversationRecordKey.toVeilid(),
TransformerCubit(
ConversationCubit(
activeAccountInfo: _activeAccountInfo,
remoteIdentityPublicKey: contact.identityPublicKey.toVeilid(),
localConversationRecordKey:
contact.localConversationRecordKey.toVeilid(),
remoteConversationRecordKey:
contact.remoteConversationRecordKey.toVeilid(),
),
// 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))));
/// StateFollower /////////////////////////
@override
Future<void> removeFromState(TypedKey key) => remove(key);
@override
Future<void> updateState(TypedKey key, proto.Chat value) async {
final contactList = _contactListCubit.state.state.asData?.value;
if (contactList == null) {
await addState(key, const AsyncValue.loading());
return;
}
final contactIndex = contactList.indexWhere(
(c) => c.value.localConversationRecordKey.toVeilid() == key);
if (contactIndex == -1) {
await addState(key, AsyncValue.error('Contact not found'));
return;
}
final contact = contactList[contactIndex];
await _addConversation(contact: contact.value);
}
////
final ActiveAccountInfo _activeAccountInfo;
final ContactListCubit _contactListCubit;
}

View file

@ -1,101 +0,0 @@
import 'dart:async';
import 'package:async_tools/async_tools.dart';
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
import '../../chat/chat.dart';
import '../../contacts/contacts.dart';
import '../../proto/proto.dart' as proto;
import 'active_conversations_bloc_map_cubit.dart';
import 'chat_list_cubit.dart';
// Map of localConversationRecordKey to MessagesCubit
// Wraps a MessagesCubit to stream the latest messages to the state
// Automatically follows the state of a ActiveConversationsBlocMapCubit.
class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
SingleContactMessagesState, SingleContactMessagesCubit>
with
StateMapFollower<ActiveConversationsBlocMapState, TypedKey,
AsyncValue<ActiveConversationState>> {
ActiveSingleContactChatBlocMapCubit(
{required ActiveAccountInfo activeAccountInfo,
required ContactListCubit contactListCubit,
required ChatListCubit chatListCubit})
: _activeAccountInfo = activeAccountInfo,
_contactListCubit = contactListCubit,
_chatListCubit = chatListCubit;
Future<void> _addConversationMessages(
{required proto.Contact contact,
required proto.Chat chat,
required proto.Conversation localConversation,
required proto.Conversation remoteConversation}) async =>
add(() => MapEntry(
contact.localConversationRecordKey.toVeilid(),
SingleContactMessagesCubit(
activeAccountInfo: _activeAccountInfo,
remoteIdentityPublicKey: contact.identityPublicKey.toVeilid(),
localConversationRecordKey:
contact.localConversationRecordKey.toVeilid(),
remoteConversationRecordKey:
contact.remoteConversationRecordKey.toVeilid(),
localMessagesRecordKey: localConversation.messages.toVeilid(),
remoteMessagesRecordKey: remoteConversation.messages.toVeilid(),
)));
/// StateFollower /////////////////////////
@override
Future<void> removeFromState(TypedKey key) => remove(key);
@override
Future<void> updateState(
TypedKey key, AsyncValue<ActiveConversationState> value) async {
// Get the contact object for this single contact chat
final contactList = _contactListCubit.state.state.asData?.value;
if (contactList == null) {
await addState(key, const AsyncValue.loading());
return;
}
final contactIndex = contactList.indexWhere(
(c) => c.value.localConversationRecordKey.toVeilid() == key);
if (contactIndex == -1) {
await addState(
key, AsyncValue.error('Contact not found for conversation'));
return;
}
final contact = contactList[contactIndex].value;
// Get the chat object for this single contact chat
final chatList = _chatListCubit.state.state.asData?.value;
if (chatList == null) {
await addState(key, const AsyncValue.loading());
return;
}
final chatIndex = chatList.indexWhere(
(c) => c.value.localConversationRecordKey.toVeilid() == key);
if (contactIndex == -1) {
await addState(key, AsyncValue.error('Chat not found for conversation'));
return;
}
final chat = chatList[chatIndex].value;
await value.when(
data: (state) => _addConversationMessages(
contact: contact,
chat: chat,
localConversation: state.localConversation,
remoteConversation: state.remoteConversation),
loading: () => addState(key, const AsyncValue.loading()),
error: (error, stackTrace) =>
addState(key, AsyncValue.error(error, stackTrace)));
}
////
final ActiveAccountInfo _activeAccountInfo;
final ContactListCubit _contactListCubit;
final ChatListCubit _chatListCubit;
}

View file

@ -19,15 +19,15 @@ typedef ChatListCubitState = DHTShortArrayBusyState<proto.Chat>;
class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
with StateMapFollowable<ChatListCubitState, TypedKey, proto.Chat> {
ChatListCubit({
required ActiveAccountInfo activeAccountInfo,
required UnlockedAccountInfo unlockedAccountInfo,
required proto.Account account,
required this.activeChatCubit,
}) : super(
open: () => _open(activeAccountInfo, account),
open: () => _open(unlockedAccountInfo, account),
decodeElement: proto.Chat.fromBuffer);
static Future<DHTShortArray> _open(
ActiveAccountInfo activeAccountInfo, proto.Account account) async {
UnlockedAccountInfo activeAccountInfo, proto.Account account) async {
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;

View file

@ -1,3 +1 @@
export 'active_single_contact_chat_bloc_map_cubit.dart';
export 'active_conversations_bloc_map_cubit.dart';
export 'chat_list_cubit.dart';