mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2024-10-01 06:55:46 -04:00
checkpoint
This commit is contained in:
parent
3edf2ebb46
commit
c40f835ec5
@ -8,7 +8,8 @@ import '../repository/account_repository.dart';
|
||||
|
||||
class AccountInfoCubit extends Cubit<AccountInfo> {
|
||||
AccountInfoCubit(
|
||||
AccountRepository accountRepository, TypedKey superIdentityRecordKey)
|
||||
{required AccountRepository accountRepository,
|
||||
required TypedKey superIdentityRecordKey})
|
||||
: _accountRepository = accountRepository,
|
||||
super(accountRepository.getAccountInfo(superIdentityRecordKey)!) {
|
||||
// Subscribe to streams
|
||||
|
@ -1,6 +1,7 @@
|
||||
export 'account_info_cubit.dart';
|
||||
export 'account_record_cubit.dart';
|
||||
export 'account_records_bloc_map_cubit.dart';
|
||||
export 'active_local_account_cubit.dart';
|
||||
export 'local_accounts_cubit.dart';
|
||||
export 'per_account_collection_bloc_map_cubit.dart';
|
||||
export 'per_account_collection_cubit.dart';
|
||||
export 'user_logins_cubit.dart';
|
||||
|
@ -8,26 +8,30 @@ import '../../account_manager/account_manager.dart';
|
||||
typedef AccountRecordsBlocMapState
|
||||
= BlocMapState<TypedKey, AsyncValue<AccountRecordState>>;
|
||||
|
||||
/// Map of the logged in user accounts to their AccountRecordCubit
|
||||
/// Map of the logged in user accounts to their PerAccountCollectionCubit
|
||||
/// Ensures there is an single account record cubit for each logged in account
|
||||
class AccountRecordsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
AsyncValue<AccountRecordState>, AccountRecordCubit>
|
||||
class PerAccountCollectionBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
PerAccountCollectionState, PerAccountCollectionCubit>
|
||||
with StateMapFollower<LocalAccountsState, TypedKey, LocalAccount> {
|
||||
AccountRecordsBlocMapCubit(
|
||||
AccountRepository accountRepository, Locator locator)
|
||||
: _accountRepository = accountRepository {
|
||||
PerAccountCollectionBlocMapCubit({
|
||||
required Locator locator,
|
||||
required AccountRepository accountRepository,
|
||||
}) : _locator = locator,
|
||||
_accountRepository = accountRepository {
|
||||
// Follow the local accounts cubit
|
||||
follow(locator<LocalAccountsCubit>());
|
||||
}
|
||||
|
||||
// Add account record cubit
|
||||
Future<void> _addAccountRecordCubit(
|
||||
Future<void> _addPerAccountCollectionCubit(
|
||||
{required TypedKey superIdentityRecordKey}) async =>
|
||||
add(() => MapEntry(
|
||||
superIdentityRecordKey,
|
||||
AccountRecordCubit(
|
||||
accountRepository: _accountRepository,
|
||||
superIdentityRecordKey: superIdentityRecordKey)));
|
||||
PerAccountCollectionCubit(
|
||||
locator: _locator,
|
||||
accountInfoCubit: AccountInfoCubit(
|
||||
accountRepository: _accountRepository,
|
||||
superIdentityRecordKey: superIdentityRecordKey))));
|
||||
|
||||
/// StateFollower /////////////////////////
|
||||
|
||||
@ -36,10 +40,11 @@ class AccountRecordsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
|
||||
@override
|
||||
Future<void> updateState(TypedKey key, LocalAccount value) async {
|
||||
await _addAccountRecordCubit(
|
||||
await _addPerAccountCollectionCubit(
|
||||
superIdentityRecordKey: value.superIdentity.recordKey);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
final AccountRepository _accountRepository;
|
||||
final Locator _locator;
|
||||
}
|
@ -10,6 +10,7 @@ import '../../chat/chat.dart';
|
||||
import '../../chat_list/chat_list.dart';
|
||||
import '../../contact_invitation/contact_invitation.dart';
|
||||
import '../../contacts/contacts.dart';
|
||||
import '../../conversation/conversation.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../account_manager.dart';
|
||||
|
||||
@ -30,6 +31,14 @@ class PerAccountCollectionCubit extends Cubit<PerAccountCollectionState> {
|
||||
await _accountRecordSubscription?.cancel();
|
||||
await accountRecordCubit?.close();
|
||||
|
||||
await activeSingleContactChatBlocMapCubitUpdater.close();
|
||||
await activeConversationsBlocMapCubitUpdater.close();
|
||||
await activeChatCubitUpdater.close();
|
||||
await waitingInvitationsBlocMapCubitUpdater.close();
|
||||
await chatListCubitUpdater.close();
|
||||
await contactListCubitUpdater.close();
|
||||
await contactInvitationListCubitUpdater.close();
|
||||
|
||||
await super.close();
|
||||
}
|
||||
|
||||
@ -95,48 +104,72 @@ class PerAccountCollectionCubit extends Cubit<PerAccountCollectionState> {
|
||||
state.copyWith(avAccountRecordState: accountRecordCubit!.state);
|
||||
|
||||
// Get bloc parameters
|
||||
final accountRecordKey = nextState.accountInfo.accountRecordKey;
|
||||
final accountInfo = nextState.accountInfo;
|
||||
|
||||
// ContactInvitationListCubit
|
||||
final contactInvitationListRecordPointer = nextState
|
||||
.avAccountRecordState.asData?.value.contactInvitationRecords
|
||||
.toVeilid();
|
||||
|
||||
contactInvitationListCubitUpdater.update(
|
||||
final contactInvitationListCubit = contactInvitationListCubitUpdater.update(
|
||||
contactInvitationListRecordPointer == null
|
||||
? null
|
||||
: (
|
||||
collectionLocator,
|
||||
accountRecordKey,
|
||||
contactInvitationListRecordPointer
|
||||
));
|
||||
: (accountInfo, contactInvitationListRecordPointer));
|
||||
|
||||
// ContactListCubit
|
||||
final contactListRecordPointer =
|
||||
nextState.avAccountRecordState.asData?.value.contactList.toVeilid();
|
||||
|
||||
contactListCubitUpdater.update(contactListRecordPointer == null
|
||||
? null
|
||||
: (collectionLocator, accountRecordKey, contactListRecordPointer));
|
||||
final contactListCubit = contactListCubitUpdater.update(
|
||||
contactListRecordPointer == null
|
||||
? null
|
||||
: (accountInfo, contactListRecordPointer));
|
||||
|
||||
// WaitingInvitationsBlocMapCubit
|
||||
waitingInvitationsBlocMapCubitUpdater.update(
|
||||
nextState.avAccountRecordState.isData ? collectionLocator : null);
|
||||
contactInvitationListCubit == null
|
||||
? null
|
||||
: (accountInfo, accountRecordCubit!, contactInvitationListCubit));
|
||||
|
||||
// ActiveChatCubit
|
||||
activeChatCubitUpdater
|
||||
final activeChatCubit = activeChatCubitUpdater
|
||||
.update(nextState.avAccountRecordState.isData ? true : null);
|
||||
|
||||
// ChatListCubit
|
||||
final chatListRecordPointer =
|
||||
nextState.avAccountRecordState.asData?.value.chatList.toVeilid();
|
||||
|
||||
chatListCubitUpdater.update(chatListRecordPointer == null
|
||||
? null
|
||||
: (collectionLocator, accountRecordKey, chatListRecordPointer));
|
||||
final chatListCubit = chatListCubitUpdater.update(
|
||||
chatListRecordPointer == null || activeChatCubit == null
|
||||
? null
|
||||
: (accountInfo, chatListRecordPointer, activeChatCubit));
|
||||
|
||||
// ActiveConversationsBlocMapCubit
|
||||
// xxx
|
||||
final activeConversationsBlocMapCubit =
|
||||
activeConversationsBlocMapCubitUpdater.update(
|
||||
accountRecordCubit == null ||
|
||||
chatListCubit == null ||
|
||||
contactListCubit == null
|
||||
? null
|
||||
: (
|
||||
accountInfo,
|
||||
accountRecordCubit!,
|
||||
chatListCubit,
|
||||
contactListCubit
|
||||
));
|
||||
|
||||
// ActiveSingleContactChatBlocMapCubit
|
||||
activeSingleContactChatBlocMapCubitUpdater.update(
|
||||
activeConversationsBlocMapCubit == null ||
|
||||
chatListCubit == null ||
|
||||
contactListCubit == null
|
||||
? null
|
||||
: (
|
||||
accountInfo,
|
||||
activeConversationsBlocMapCubit,
|
||||
chatListCubit,
|
||||
contactListCubit
|
||||
));
|
||||
|
||||
return nextState;
|
||||
}
|
||||
@ -163,6 +196,12 @@ class PerAccountCollectionCubit extends Cubit<PerAccountCollectionState> {
|
||||
if (T is ChatListCubit) {
|
||||
return chatListCubitUpdater.bloc! as T;
|
||||
}
|
||||
if (T is ActiveConversationsBlocMapCubit) {
|
||||
return activeConversationsBlocMapCubitUpdater.bloc! as T;
|
||||
}
|
||||
if (T is ActiveSingleContactChatBlocMapCubit) {
|
||||
return activeSingleContactChatBlocMapCubitUpdater.bloc! as T;
|
||||
}
|
||||
return _locator<T>();
|
||||
}
|
||||
|
||||
@ -178,32 +217,52 @@ class PerAccountCollectionCubit extends Cubit<PerAccountCollectionState> {
|
||||
StreamSubscription<AsyncValue<AccountRecordState>>?
|
||||
_accountRecordSubscription;
|
||||
final contactInvitationListCubitUpdater = BlocUpdater<
|
||||
ContactInvitationListCubit,
|
||||
(Locator, TypedKey, OwnedDHTRecordPointer)>(
|
||||
ContactInvitationListCubit, (AccountInfo, OwnedDHTRecordPointer)>(
|
||||
create: (params) => ContactInvitationListCubit(
|
||||
locator: params.$1,
|
||||
accountRecordKey: params.$2,
|
||||
contactInvitationListRecordPointer: params.$3,
|
||||
accountInfo: params.$1,
|
||||
contactInvitationListRecordPointer: params.$2,
|
||||
));
|
||||
final contactListCubitUpdater =
|
||||
BlocUpdater<ContactListCubit, (Locator, TypedKey, OwnedDHTRecordPointer)>(
|
||||
BlocUpdater<ContactListCubit, (AccountInfo, OwnedDHTRecordPointer)>(
|
||||
create: (params) => ContactListCubit(
|
||||
locator: params.$1,
|
||||
accountRecordKey: params.$2,
|
||||
contactListRecordPointer: params.$3,
|
||||
));
|
||||
final waitingInvitationsBlocMapCubitUpdater =
|
||||
BlocUpdater<WaitingInvitationsBlocMapCubit, Locator>(
|
||||
create: (params) => WaitingInvitationsBlocMapCubit(
|
||||
locator: params,
|
||||
accountInfo: params.$1,
|
||||
contactListRecordPointer: params.$2,
|
||||
));
|
||||
final waitingInvitationsBlocMapCubitUpdater = BlocUpdater<
|
||||
WaitingInvitationsBlocMapCubit,
|
||||
(AccountInfo, AccountRecordCubit, ContactInvitationListCubit)>(
|
||||
create: (params) => WaitingInvitationsBlocMapCubit(
|
||||
accountInfo: params.$1,
|
||||
accountRecordCubit: params.$2,
|
||||
contactInvitationListCubit: params.$3));
|
||||
final activeChatCubitUpdater =
|
||||
BlocUpdater<ActiveChatCubit, bool>(create: (_) => ActiveChatCubit(null));
|
||||
final chatListCubitUpdater =
|
||||
BlocUpdater<ChatListCubit, (Locator, TypedKey, OwnedDHTRecordPointer)>(
|
||||
create: (params) => ChatListCubit(
|
||||
locator: params.$1,
|
||||
accountRecordKey: params.$2,
|
||||
chatListRecordPointer: params.$3,
|
||||
));
|
||||
final chatListCubitUpdater = BlocUpdater<ChatListCubit,
|
||||
(AccountInfo, OwnedDHTRecordPointer, ActiveChatCubit)>(
|
||||
create: (params) => ChatListCubit(
|
||||
accountInfo: params.$1,
|
||||
chatListRecordPointer: params.$2,
|
||||
activeChatCubit: params.$3));
|
||||
final activeConversationsBlocMapCubitUpdater = BlocUpdater<
|
||||
ActiveConversationsBlocMapCubit,
|
||||
(AccountInfo, AccountRecordCubit, ChatListCubit, ContactListCubit)>(
|
||||
create: (params) => ActiveConversationsBlocMapCubit(
|
||||
accountInfo: params.$1,
|
||||
accountRecordCubit: params.$2,
|
||||
chatListCubit: params.$3,
|
||||
contactListCubit: params.$4));
|
||||
final activeSingleContactChatBlocMapCubitUpdater = BlocUpdater<
|
||||
ActiveSingleContactChatBlocMapCubit,
|
||||
(
|
||||
AccountInfo,
|
||||
ActiveConversationsBlocMapCubit,
|
||||
ChatListCubit,
|
||||
ContactListCubit
|
||||
)>(
|
||||
create: (params) => ActiveSingleContactChatBlocMapCubit(
|
||||
accountInfo: params.$1,
|
||||
activeConversationsBlocMapCubit: params.$2,
|
||||
chatListCubit: params.$3,
|
||||
contactListCubit: params.$4,
|
||||
));
|
||||
}
|
||||
|
@ -16,20 +16,17 @@ enum AccountInfoStatus {
|
||||
class AccountInfo extends Equatable {
|
||||
const AccountInfo({
|
||||
required this.status,
|
||||
required this.active,
|
||||
required this.localAccount,
|
||||
required this.userLogin,
|
||||
});
|
||||
|
||||
final AccountInfoStatus status;
|
||||
final bool active;
|
||||
final LocalAccount localAccount;
|
||||
final UserLogin? userLogin;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
status,
|
||||
active,
|
||||
localAccount,
|
||||
userLogin,
|
||||
];
|
||||
|
@ -93,10 +93,6 @@ class AccountRepository {
|
||||
}
|
||||
|
||||
AccountInfo? getAccountInfo(TypedKey superIdentityRecordKey) {
|
||||
// Get active account if we have one
|
||||
final activeLocalAccount = getActiveLocalAccount();
|
||||
final active = superIdentityRecordKey == activeLocalAccount;
|
||||
|
||||
// Get which local account we want to fetch the profile for
|
||||
final localAccount = fetchLocalAccount(superIdentityRecordKey);
|
||||
if (localAccount == null) {
|
||||
@ -109,7 +105,6 @@ class AccountRepository {
|
||||
// Account was locked
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountLocked,
|
||||
active: active,
|
||||
localAccount: localAccount,
|
||||
userLogin: null,
|
||||
);
|
||||
@ -118,7 +113,6 @@ class AccountRepository {
|
||||
// Got account, decrypted and decoded
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountUnlocked,
|
||||
active: active,
|
||||
localAccount: localAccount,
|
||||
userLogin: userLogin,
|
||||
);
|
||||
|
@ -116,13 +116,13 @@ class _EditAccountPageState extends State<EditAccountPage> {
|
||||
});
|
||||
try {
|
||||
// Look up account cubit for this specific account
|
||||
final accountRecordsCubit =
|
||||
context.read<AccountRecordsBlocMapCubit>();
|
||||
await accountRecordsCubit.operateAsync(
|
||||
final perAccountCollectionCubit =
|
||||
context.read<PerAccountCollectionBlocMapCubit>();
|
||||
await perAccountCollectionCubit.operateAsync(
|
||||
widget.superIdentityRecordKey, closure: (c) async {
|
||||
// Update account profile DHT record
|
||||
// This triggers ConversationCubits to update
|
||||
await c.updateProfile(newProfile);
|
||||
await c.accountRecordCubit!.updateProfile(newProfile);
|
||||
});
|
||||
|
||||
// Update local account profile
|
||||
|
@ -131,9 +131,10 @@ class VeilidChatApp extends StatelessWidget {
|
||||
create: (context) =>
|
||||
PreferencesCubit(PreferencesRepository.instance),
|
||||
),
|
||||
BlocProvider<AccountRecordsBlocMapCubit>(
|
||||
create: (context) => AccountRecordsBlocMapCubit(
|
||||
AccountRepository.instance, context.read)),
|
||||
BlocProvider<PerAccountCollectionBlocMapCubit>(
|
||||
create: (context) => PerAccountCollectionBlocMapCubit(
|
||||
accountRepository: AccountRepository.instance,
|
||||
locator: context.read)),
|
||||
],
|
||||
child: BackgroundTicker(
|
||||
child: _buildShortcuts(
|
||||
|
@ -8,7 +8,6 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
|
||||
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
@ -27,10 +26,12 @@ const metadataKeyAttachments = 'attachments';
|
||||
|
||||
class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
ChatComponentCubit._({
|
||||
required Locator locator,
|
||||
required AccountInfo accountInfo,
|
||||
required AccountRecordCubit accountRecordCubit,
|
||||
required List<ActiveConversationCubit> conversationCubits,
|
||||
required SingleContactMessagesCubit messagesCubit,
|
||||
}) : _locator = locator,
|
||||
}) : _accountInfo = accountInfo,
|
||||
_accountRecordCubit = accountRecordCubit,
|
||||
_conversationCubits = conversationCubits,
|
||||
_messagesCubit = messagesCubit,
|
||||
super(ChatComponentState(
|
||||
@ -48,26 +49,25 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
}
|
||||
|
||||
factory ChatComponentCubit.singleContact(
|
||||
{required Locator locator,
|
||||
{required AccountInfo accountInfo,
|
||||
required AccountRecordCubit accountRecordCubit,
|
||||
required ActiveConversationCubit activeConversationCubit,
|
||||
required SingleContactMessagesCubit messagesCubit}) =>
|
||||
ChatComponentCubit._(
|
||||
locator: locator,
|
||||
accountInfo: accountInfo,
|
||||
accountRecordCubit: accountRecordCubit,
|
||||
conversationCubits: [activeConversationCubit],
|
||||
messagesCubit: messagesCubit,
|
||||
);
|
||||
|
||||
Future<void> _init() async {
|
||||
// Get local user info and account record cubit
|
||||
final unlockedAccountInfo =
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_localUserIdentityKey = unlockedAccountInfo.identityTypedPublicKey;
|
||||
_localUserAccountRecordCubit = _locator<AccountRecordCubit>();
|
||||
_localUserIdentityKey = _accountInfo.identityTypedPublicKey;
|
||||
|
||||
// Subscribe to local user info
|
||||
_localUserAccountRecordSubscription = _localUserAccountRecordCubit.stream
|
||||
.listen(_onChangedLocalUserAccountRecord);
|
||||
_onChangedLocalUserAccountRecord(_localUserAccountRecordCubit.state);
|
||||
_accountRecordSubscription =
|
||||
_accountRecordCubit.stream.listen(_onChangedAccountRecord);
|
||||
_onChangedAccountRecord(_accountRecordCubit.state);
|
||||
|
||||
// Subscribe to remote user info
|
||||
await _updateConversationSubscriptions();
|
||||
@ -80,7 +80,7 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _initWait();
|
||||
await _localUserAccountRecordSubscription.cancel();
|
||||
await _accountRecordSubscription.cancel();
|
||||
await _messagesSubscription.cancel();
|
||||
await super.close();
|
||||
}
|
||||
@ -145,7 +145,7 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Private Implementation
|
||||
|
||||
void _onChangedLocalUserAccountRecord(AsyncValue<proto.Account> avAccount) {
|
||||
void _onChangedAccountRecord(AsyncValue<proto.Account> avAccount) {
|
||||
final account = avAccount.asData?.value;
|
||||
if (account == null) {
|
||||
emit(state.copyWith(localUser: null));
|
||||
@ -374,15 +374,14 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
final _initWait = WaitSet<void>();
|
||||
|
||||
final Locator _locator;
|
||||
final AccountInfo _accountInfo;
|
||||
final AccountRecordCubit _accountRecordCubit;
|
||||
final List<ActiveConversationCubit> _conversationCubits;
|
||||
final SingleContactMessagesCubit _messagesCubit;
|
||||
|
||||
late final TypedKey _localUserIdentityKey;
|
||||
late final AccountRecordCubit _localUserAccountRecordCubit;
|
||||
late final StreamSubscription<AsyncValue<proto.Account>>
|
||||
_localUserAccountRecordSubscription;
|
||||
_accountRecordSubscription;
|
||||
final Map<TypedKey, StreamSubscription<AsyncValue<ActiveConversationState>>>
|
||||
_conversationSubscriptions = {};
|
||||
late StreamSubscription<SingleContactMessagesState> _messagesSubscription;
|
||||
|
@ -4,7 +4,6 @@ import 'package:async_tools/async_tools.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
@ -51,13 +50,13 @@ typedef SingleContactMessagesState = AsyncValue<WindowState<MessageState>>;
|
||||
// Builds the reconciled chat record from the local and remote conversation keys
|
||||
class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
SingleContactMessagesCubit({
|
||||
required Locator locator,
|
||||
required AccountInfo accountInfo,
|
||||
required TypedKey remoteIdentityPublicKey,
|
||||
required TypedKey localConversationRecordKey,
|
||||
required TypedKey localMessagesRecordKey,
|
||||
required TypedKey remoteConversationRecordKey,
|
||||
required TypedKey remoteMessagesRecordKey,
|
||||
}) : _locator = locator,
|
||||
}) : _accountInfo = accountInfo,
|
||||
_remoteIdentityPublicKey = remoteIdentityPublicKey,
|
||||
_localConversationRecordKey = localConversationRecordKey,
|
||||
_localMessagesRecordKey = localMessagesRecordKey,
|
||||
@ -87,9 +86,6 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
|
||||
// Initialize everything
|
||||
Future<void> _init() async {
|
||||
_unlockedAccountInfo =
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
|
||||
_unsentMessagesQueue = PersistentQueue<proto.Message>(
|
||||
table: 'SingleContactUnsentMessages',
|
||||
key: _remoteConversationRecordKey.toString(),
|
||||
@ -115,15 +111,15 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
|
||||
// Make crypto
|
||||
Future<void> _initCrypto() async {
|
||||
_conversationCrypto = await _unlockedAccountInfo
|
||||
.makeConversationCrypto(_remoteIdentityPublicKey);
|
||||
_conversationCrypto =
|
||||
await _accountInfo.makeConversationCrypto(_remoteIdentityPublicKey);
|
||||
_senderMessageIntegrity = await MessageIntegrity.create(
|
||||
author: _unlockedAccountInfo.identityTypedPublicKey);
|
||||
author: _accountInfo.identityTypedPublicKey);
|
||||
}
|
||||
|
||||
// Open local messages key
|
||||
Future<void> _initSentMessagesCubit() async {
|
||||
final writer = _unlockedAccountInfo.identityWriter;
|
||||
final writer = _accountInfo.identityWriter;
|
||||
|
||||
_sentMessagesCubit = DHTLogCubit(
|
||||
open: () async => DHTLog.openWrite(_localMessagesRecordKey, writer,
|
||||
@ -153,7 +149,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
|
||||
Future<VeilidCrypto> _makeLocalMessagesCrypto() async =>
|
||||
VeilidCryptoPrivate.fromTypedKey(
|
||||
_unlockedAccountInfo.userLogin.identitySecret, 'tabledb');
|
||||
_accountInfo.userLogin!.identitySecret, 'tabledb');
|
||||
|
||||
// Open reconciled chat record key
|
||||
Future<void> _initReconciledMessagesCubit() async {
|
||||
@ -245,9 +241,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
}
|
||||
|
||||
_reconciliation.reconcileMessages(
|
||||
_unlockedAccountInfo.identityTypedPublicKey,
|
||||
sentMessages,
|
||||
_sentMessagesCubit!);
|
||||
_accountInfo.identityTypedPublicKey, sentMessages, _sentMessagesCubit!);
|
||||
|
||||
// Update the view
|
||||
_renderState();
|
||||
@ -284,7 +278,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
|
||||
// Now sign it
|
||||
await _senderMessageIntegrity.signMessage(
|
||||
message, _unlockedAccountInfo.identitySecretKey);
|
||||
message, _accountInfo.identitySecretKey);
|
||||
}
|
||||
|
||||
// Async process to send messages in the background
|
||||
@ -336,8 +330,8 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
final renderedElements = <RenderStateElement>[];
|
||||
|
||||
for (final m in reconciledMessages.windowElements) {
|
||||
final isLocal = m.content.author.toVeilid() ==
|
||||
_unlockedAccountInfo.identityTypedPublicKey;
|
||||
final isLocal =
|
||||
m.content.author.toVeilid() == _accountInfo.identityTypedPublicKey;
|
||||
final reconciledTimestamp = Timestamp.fromInt64(m.reconciledTime);
|
||||
final sm =
|
||||
isLocal ? sentMessagesMap[m.content.authorUniqueIdString] : null;
|
||||
@ -375,7 +369,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
// Add common fields
|
||||
// id and signature will get set by _processMessageToSend
|
||||
message
|
||||
..author = _unlockedAccountInfo.identityTypedPublicKey.toProto()
|
||||
..author = _accountInfo.identityTypedPublicKey.toProto()
|
||||
..timestamp = Veilid.instance.now().toInt64();
|
||||
|
||||
// Put in the queue
|
||||
@ -408,8 +402,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
final WaitSet<void> _initWait = WaitSet();
|
||||
final Locator _locator;
|
||||
late final UnlockedAccountInfo _unlockedAccountInfo;
|
||||
late final AccountInfo _accountInfo;
|
||||
final TypedKey _remoteIdentityPublicKey;
|
||||
final TypedKey _localConversationRecordKey;
|
||||
final TypedKey _localMessagesRecordKey;
|
||||
|
@ -8,6 +8,7 @@ import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
|
||||
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../conversation/conversation.dart';
|
||||
import '../../theme/theme.dart';
|
||||
import '../chat.dart';
|
||||
@ -21,6 +22,12 @@ class ChatComponentWidget extends StatelessWidget {
|
||||
static Widget builder(
|
||||
{required TypedKey localConversationRecordKey, Key? key}) =>
|
||||
Builder(builder: (context) {
|
||||
// Get the account info
|
||||
final accountInfo = context.watch<AccountInfoCubit>().state;
|
||||
|
||||
// Get the account record cubit
|
||||
final accountRecordCubit = context.read<AccountRecordCubit>();
|
||||
|
||||
// Get the active conversation cubit
|
||||
final activeConversationCubit = context
|
||||
.select<ActiveConversationsBlocMapCubit, ActiveConversationCubit?>(
|
||||
@ -43,7 +50,8 @@ class ChatComponentWidget extends StatelessWidget {
|
||||
// Make chat component state
|
||||
return BlocProvider(
|
||||
create: (context) => ChatComponentCubit.singleContact(
|
||||
locator: context.read,
|
||||
accountInfo: accountInfo,
|
||||
accountRecordCubit: accountRecordCubit,
|
||||
activeConversationCubit: activeConversationCubit,
|
||||
messagesCubit: messagesCubit,
|
||||
),
|
||||
|
@ -3,9 +3,9 @@ import 'dart:async';
|
||||
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../chat/chat.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../tools/tools.dart';
|
||||
@ -19,18 +19,19 @@ typedef ChatListCubitState = DHTShortArrayBusyState<proto.Chat>;
|
||||
class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||
with StateMapFollowable<ChatListCubitState, TypedKey, proto.Chat> {
|
||||
ChatListCubit({
|
||||
required Locator locator,
|
||||
required TypedKey accountRecordKey,
|
||||
required AccountInfo accountInfo,
|
||||
required OwnedDHTRecordPointer chatListRecordPointer,
|
||||
}) : _locator = locator,
|
||||
required ActiveChatCubit activeChatCubit,
|
||||
}) : _activeChatCubit = activeChatCubit,
|
||||
super(
|
||||
open: () => _open(locator, accountRecordKey, chatListRecordPointer),
|
||||
open: () => _open(accountInfo, chatListRecordPointer),
|
||||
decodeElement: proto.Chat.fromBuffer);
|
||||
|
||||
static Future<DHTShortArray> _open(Locator locator, TypedKey accountRecordKey,
|
||||
static Future<DHTShortArray> _open(AccountInfo accountInfo,
|
||||
OwnedDHTRecordPointer chatListRecordPointer) async {
|
||||
final dhtRecord = await DHTShortArray.openOwned(chatListRecordPointer,
|
||||
debugName: 'ChatListCubit::_open::ChatList', parent: accountRecordKey);
|
||||
debugName: 'ChatListCubit::_open::ChatList',
|
||||
parent: accountInfo.accountRecordKey);
|
||||
|
||||
return dhtRecord;
|
||||
}
|
||||
@ -95,9 +96,8 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||
final deletedItem =
|
||||
// Ensure followers get their changes before we return
|
||||
await syncFollowers(() => operateWrite((writer) async {
|
||||
final activeChatCubit = _locator<ActiveChatCubit>();
|
||||
if (activeChatCubit.state == localConversationRecordKey) {
|
||||
activeChatCubit.setActiveChat(null);
|
||||
if (_activeChatCubit.state == localConversationRecordKey) {
|
||||
_activeChatCubit.setActiveChat(null);
|
||||
}
|
||||
for (var i = 0; i < writer.length; i++) {
|
||||
final c = await writer.getProtobuf(proto.Chat.fromBuffer, i);
|
||||
@ -139,5 +139,5 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
final Locator _locator;
|
||||
final ActiveChatCubit _activeChatCubit;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
@ -37,14 +36,12 @@ class ContactInvitationListCubit
|
||||
StateMapFollowable<ContactInvitiationListState, TypedKey,
|
||||
proto.ContactInvitationRecord> {
|
||||
ContactInvitationListCubit({
|
||||
required Locator locator,
|
||||
required TypedKey accountRecordKey,
|
||||
required AccountInfo accountInfo,
|
||||
required OwnedDHTRecordPointer contactInvitationListRecordPointer,
|
||||
}) : _locator = locator,
|
||||
_accountRecordKey = accountRecordKey,
|
||||
}) : _accountInfo = accountInfo,
|
||||
super(
|
||||
open: () =>
|
||||
_open(accountRecordKey, contactInvitationListRecordPointer),
|
||||
open: () => _open(accountInfo.accountRecordKey,
|
||||
contactInvitationListRecordPointer),
|
||||
decodeElement: proto.ContactInvitationRecord.fromBuffer);
|
||||
|
||||
static Future<DHTShortArray> _open(TypedKey accountRecordKey,
|
||||
@ -58,7 +55,8 @@ class ContactInvitationListCubit
|
||||
}
|
||||
|
||||
Future<Uint8List> createInvitation(
|
||||
{required EncryptionKeyType encryptionKeyType,
|
||||
{required proto.Profile profile,
|
||||
required EncryptionKeyType encryptionKeyType,
|
||||
required String encryptionKey,
|
||||
required String message,
|
||||
required Timestamp? expiration}) async {
|
||||
@ -68,12 +66,8 @@ class ContactInvitationListCubit
|
||||
final crcs = await pool.veilid.bestCryptoSystem();
|
||||
final contactRequestWriter = await crcs.generateKeyPair();
|
||||
|
||||
final activeAccountInfo =
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final profile = _locator<AccountRecordCubit>().state.asData!.value.profile;
|
||||
|
||||
final idcs = await activeAccountInfo.identityCryptoSystem;
|
||||
final identityWriter = activeAccountInfo.identityWriter;
|
||||
final idcs = await _accountInfo.identityCryptoSystem;
|
||||
final identityWriter = _accountInfo.identityWriter;
|
||||
|
||||
// Encrypt the writer secret with the encryption key
|
||||
final encryptedSecret = await encryptionKeyType.encryptSecretToBytes(
|
||||
@ -91,7 +85,7 @@ class ContactInvitationListCubit
|
||||
await (await pool.createRecord(
|
||||
debugName: 'ContactInvitationListCubit::createInvitation::'
|
||||
'LocalConversation',
|
||||
parent: _accountRecordKey,
|
||||
parent: _accountInfo.accountRecordKey,
|
||||
schema: DHTSchema.smpl(
|
||||
oCnt: 0,
|
||||
members: [DHTSchemaMember(mKey: identityWriter.key, mCnt: 1)])))
|
||||
@ -101,8 +95,7 @@ class ContactInvitationListCubit
|
||||
final crpriv = proto.ContactRequestPrivate()
|
||||
..writerKey = contactRequestWriter.key.toProto()
|
||||
..profile = profile
|
||||
..superIdentityRecordKey =
|
||||
activeAccountInfo.userLogin.superIdentityRecordKey.toProto()
|
||||
..superIdentityRecordKey = _accountInfo.superIdentityRecordKey.toProto()
|
||||
..chatRecordKey = localConversation.key.toProto()
|
||||
..expiration = expiration?.toInt64() ?? Int64.ZERO;
|
||||
final crprivbytes = crpriv.writeToBuffer();
|
||||
@ -120,7 +113,7 @@ class ContactInvitationListCubit
|
||||
await (await pool.createRecord(
|
||||
debugName: 'ContactInvitationListCubit::createInvitation::'
|
||||
'ContactRequestInbox',
|
||||
parent: _accountRecordKey,
|
||||
parent: _accountInfo.accountRecordKey,
|
||||
schema: DHTSchema.smpl(oCnt: 1, members: [
|
||||
DHTSchemaMember(mCnt: 1, mKey: contactRequestWriter.key)
|
||||
]),
|
||||
@ -197,7 +190,7 @@ class ContactInvitationListCubit
|
||||
await (await pool.openRecordOwned(contactRequestInbox,
|
||||
debugName: 'ContactInvitationListCubit::deleteInvitation::'
|
||||
'ContactRequestInbox',
|
||||
parent: _accountRecordKey))
|
||||
parent: _accountInfo.accountRecordKey))
|
||||
.scope((contactRequestInbox) async {
|
||||
// Wipe out old invitation so it shows up as invalid
|
||||
await contactRequestInbox.tryWriteBytes(Uint8List(0));
|
||||
@ -248,7 +241,7 @@ class ContactInvitationListCubit
|
||||
debugName: 'ContactInvitationListCubit::validateInvitation::'
|
||||
'ContactRequestInbox',
|
||||
parent: pool.getParentRecordKey(contactRequestInboxKey) ??
|
||||
_accountRecordKey))
|
||||
_accountInfo.accountRecordKey))
|
||||
.maybeDeleteScope(!isSelf, (contactRequestInbox) async {
|
||||
//
|
||||
final contactRequest = await contactRequestInbox
|
||||
@ -293,7 +286,7 @@ class ContactInvitationListCubit
|
||||
secret: writerSecret);
|
||||
|
||||
out = ValidContactInvitation(
|
||||
locator: _locator,
|
||||
accountInfo: _accountInfo,
|
||||
contactRequestInboxKey: contactRequestInboxKey,
|
||||
contactRequestPrivate: contactRequestPrivate,
|
||||
contactSuperIdentity: contactSuperIdentity,
|
||||
@ -317,6 +310,5 @@ class ContactInvitationListCubit
|
||||
}
|
||||
|
||||
//
|
||||
final Locator _locator;
|
||||
final TypedKey _accountRecordKey;
|
||||
final AccountInfo _accountInfo;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
@ -8,23 +7,21 @@ import '../../proto/proto.dart' as proto;
|
||||
class ContactRequestInboxCubit
|
||||
extends DefaultDHTRecordCubit<proto.SignedContactResponse?> {
|
||||
ContactRequestInboxCubit(
|
||||
{required Locator locator, required this.contactInvitationRecord})
|
||||
{required AccountInfo accountInfo, required this.contactInvitationRecord})
|
||||
: super(
|
||||
open: () => _open(
|
||||
locator: locator,
|
||||
accountInfo: accountInfo,
|
||||
contactInvitationRecord: contactInvitationRecord),
|
||||
decodeState: (buf) => buf.isEmpty
|
||||
? null
|
||||
: proto.SignedContactResponse.fromBuffer(buf));
|
||||
|
||||
static Future<DHTRecord> _open(
|
||||
{required Locator locator,
|
||||
{required AccountInfo accountInfo,
|
||||
required proto.ContactInvitationRecord contactInvitationRecord}) async {
|
||||
final pool = DHTRecordPool.instance;
|
||||
|
||||
final unlockedAccountInfo =
|
||||
locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final accountRecordKey = unlockedAccountInfo.accountRecordKey;
|
||||
final accountRecordKey = accountInfo.accountRecordKey;
|
||||
|
||||
final writerSecret = contactInvitationRecord.writerSecret.toVeilid();
|
||||
final recordKey =
|
||||
|
@ -4,9 +4,9 @@ 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:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../conversation/conversation.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../tools/tools.dart';
|
||||
@ -24,18 +24,22 @@ class InvitationStatus extends Equatable {
|
||||
|
||||
class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
|
||||
proto.SignedContactResponse?> {
|
||||
WaitingInvitationCubit(ContactRequestInboxCubit super.input,
|
||||
{required Locator locator,
|
||||
required proto.ContactInvitationRecord contactInvitationRecord})
|
||||
: super(
|
||||
WaitingInvitationCubit(
|
||||
ContactRequestInboxCubit super.input, {
|
||||
required AccountInfo accountInfo,
|
||||
required AccountRecordCubit accountRecordCubit,
|
||||
required proto.ContactInvitationRecord contactInvitationRecord,
|
||||
}) : super(
|
||||
transform: (signedContactResponse) => _transform(
|
||||
signedContactResponse,
|
||||
locator: locator,
|
||||
accountInfo: accountInfo,
|
||||
accountRecordCubit: accountRecordCubit,
|
||||
contactInvitationRecord: contactInvitationRecord));
|
||||
|
||||
static Future<AsyncValue<InvitationStatus>> _transform(
|
||||
proto.SignedContactResponse? signedContactResponse,
|
||||
{required Locator locator,
|
||||
{required AccountInfo accountInfo,
|
||||
required AccountRecordCubit accountRecordCubit,
|
||||
required proto.ContactInvitationRecord contactInvitationRecord}) async {
|
||||
if (signedContactResponse == null) {
|
||||
return const AsyncValue.loading();
|
||||
@ -69,7 +73,7 @@ class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
|
||||
contactResponse.remoteConversationRecordKey.toVeilid();
|
||||
|
||||
final conversation = ConversationCubit(
|
||||
locator: locator,
|
||||
accountInfo: accountInfo,
|
||||
remoteIdentityPublicKey:
|
||||
contactSuperIdentity.currentInstance.typedPublicKey,
|
||||
remoteConversationRecordKey: remoteConversationRecordKey);
|
||||
@ -96,6 +100,7 @@ class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
|
||||
final localConversationRecordKey =
|
||||
contactInvitationRecord.localConversationRecordKey.toVeilid();
|
||||
return conversation.initLocalConversation(
|
||||
profile: accountRecordCubit.state.asData!.value.profile,
|
||||
existingConversationRecordKey: localConversationRecordKey,
|
||||
callback: (localConversation) async => AsyncValue.data(InvitationStatus(
|
||||
acceptedContact: AcceptedContact(
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import 'cubits.dart';
|
||||
|
||||
@ -17,11 +17,14 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
with
|
||||
StateMapFollower<DHTShortArrayBusyState<proto.ContactInvitationRecord>,
|
||||
TypedKey, proto.ContactInvitationRecord> {
|
||||
WaitingInvitationsBlocMapCubit({
|
||||
required Locator locator,
|
||||
}) : _locator = locator {
|
||||
WaitingInvitationsBlocMapCubit(
|
||||
{required AccountInfo accountInfo,
|
||||
required AccountRecordCubit accountRecordCubit,
|
||||
required ContactInvitationListCubit contactInvitationListCubit})
|
||||
: _accountInfo = accountInfo,
|
||||
_accountRecordCubit = accountRecordCubit {
|
||||
// Follow the contact invitation list cubit
|
||||
follow(locator<ContactInvitationListCubit>());
|
||||
follow(contactInvitationListCubit);
|
||||
}
|
||||
|
||||
Future<void> _addWaitingInvitation(
|
||||
@ -31,9 +34,10 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
contactInvitationRecord.contactRequestInbox.recordKey.toVeilid(),
|
||||
WaitingInvitationCubit(
|
||||
ContactRequestInboxCubit(
|
||||
locator: _locator,
|
||||
accountInfo: _accountInfo,
|
||||
contactInvitationRecord: contactInvitationRecord),
|
||||
locator: _locator,
|
||||
accountInfo: _accountInfo,
|
||||
accountRecordCubit: _accountRecordCubit,
|
||||
contactInvitationRecord: contactInvitationRecord)));
|
||||
|
||||
/// StateFollower /////////////////////////
|
||||
@ -46,5 +50,6 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
_addWaitingInvitation(contactInvitationRecord: value);
|
||||
|
||||
////
|
||||
final Locator _locator;
|
||||
final AccountInfo _accountInfo;
|
||||
final AccountRecordCubit _accountRecordCubit;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
@ -14,12 +13,12 @@ import 'models.dart';
|
||||
class ValidContactInvitation {
|
||||
@internal
|
||||
ValidContactInvitation(
|
||||
{required Locator locator,
|
||||
{required AccountInfo accountInfo,
|
||||
required TypedKey contactRequestInboxKey,
|
||||
required proto.ContactRequestPrivate contactRequestPrivate,
|
||||
required SuperIdentity contactSuperIdentity,
|
||||
required KeyPair writer})
|
||||
: _locator = locator,
|
||||
: _accountInfo = accountInfo,
|
||||
_contactRequestInboxKey = contactRequestInboxKey,
|
||||
_contactRequestPrivate = contactRequestPrivate,
|
||||
_contactSuperIdentity = contactSuperIdentity,
|
||||
@ -27,65 +26,57 @@ class ValidContactInvitation {
|
||||
|
||||
proto.Profile get remoteProfile => _contactRequestPrivate.profile;
|
||||
|
||||
Future<AcceptedContact?> accept() async {
|
||||
Future<AcceptedContact?> accept(proto.Profile profile) async {
|
||||
final pool = DHTRecordPool.instance;
|
||||
try {
|
||||
final unlockedAccountInfo =
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final accountRecordKey = unlockedAccountInfo.accountRecordKey;
|
||||
final identityPublicKey = unlockedAccountInfo.identityPublicKey;
|
||||
|
||||
// Ensure we don't delete this if we're trying to chat to self
|
||||
// The initiating side will delete the records in deleteInvitation()
|
||||
final isSelf =
|
||||
_contactSuperIdentity.currentInstance.publicKey == identityPublicKey;
|
||||
final isSelf = _contactSuperIdentity.currentInstance.publicKey ==
|
||||
_accountInfo.identityPublicKey;
|
||||
|
||||
return (await pool.openRecordWrite(_contactRequestInboxKey, _writer,
|
||||
debugName: 'ValidContactInvitation::accept::'
|
||||
'ContactRequestInbox',
|
||||
parent: pool.getParentRecordKey(_contactRequestInboxKey) ??
|
||||
accountRecordKey))
|
||||
_accountInfo.accountRecordKey))
|
||||
// ignore: prefer_expression_function_bodies
|
||||
.maybeDeleteScope(!isSelf, (contactRequestInbox) async {
|
||||
// Create local conversation key for this
|
||||
// contact and send via contact response
|
||||
final conversation = ConversationCubit(
|
||||
locator: _locator,
|
||||
accountInfo: _accountInfo,
|
||||
remoteIdentityPublicKey:
|
||||
_contactSuperIdentity.currentInstance.typedPublicKey);
|
||||
return conversation.initLocalConversation(
|
||||
profile: profile,
|
||||
callback: (localConversation) async {
|
||||
final contactResponse = proto.ContactResponse()
|
||||
..accept = true
|
||||
..remoteConversationRecordKey = localConversation.key.toProto()
|
||||
..superIdentityRecordKey =
|
||||
unlockedAccountInfo.superIdentityRecordKey.toProto();
|
||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||
final contactResponse = proto.ContactResponse()
|
||||
..accept = true
|
||||
..remoteConversationRecordKey = localConversation.key.toProto()
|
||||
..superIdentityRecordKey =
|
||||
_accountInfo.superIdentityRecordKey.toProto();
|
||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||
|
||||
final cs =
|
||||
await pool.veilid.getCryptoSystem(_contactRequestInboxKey.kind);
|
||||
final cs = await _accountInfo.identityCryptoSystem;
|
||||
final identitySignature = await cs.signWithKeyPair(
|
||||
_accountInfo.identityWriter, contactResponseBytes);
|
||||
|
||||
final identitySignature = await cs.sign(
|
||||
unlockedAccountInfo.identityWriter.key,
|
||||
unlockedAccountInfo.identityWriter.secret,
|
||||
contactResponseBytes);
|
||||
final signedContactResponse = proto.SignedContactResponse()
|
||||
..contactResponse = contactResponseBytes
|
||||
..identitySignature = identitySignature.toProto();
|
||||
|
||||
final signedContactResponse = proto.SignedContactResponse()
|
||||
..contactResponse = contactResponseBytes
|
||||
..identitySignature = identitySignature.toProto();
|
||||
// Write the acceptance to the inbox
|
||||
await contactRequestInbox
|
||||
.eventualWriteProtobuf(signedContactResponse, subkey: 1);
|
||||
|
||||
// Write the acceptance to the inbox
|
||||
await contactRequestInbox.eventualWriteProtobuf(signedContactResponse,
|
||||
subkey: 1);
|
||||
|
||||
return AcceptedContact(
|
||||
remoteProfile: _contactRequestPrivate.profile,
|
||||
remoteIdentity: _contactSuperIdentity,
|
||||
remoteConversationRecordKey:
|
||||
_contactRequestPrivate.chatRecordKey.toVeilid(),
|
||||
localConversationRecordKey: localConversation.key,
|
||||
);
|
||||
});
|
||||
return AcceptedContact(
|
||||
remoteProfile: _contactRequestPrivate.profile,
|
||||
remoteIdentity: _contactSuperIdentity,
|
||||
remoteConversationRecordKey:
|
||||
_contactRequestPrivate.chatRecordKey.toVeilid(),
|
||||
localConversationRecordKey: localConversation.key,
|
||||
);
|
||||
});
|
||||
});
|
||||
} on Exception catch (e) {
|
||||
log.debug('exception: $e', e);
|
||||
@ -96,33 +87,24 @@ class ValidContactInvitation {
|
||||
Future<bool> reject() async {
|
||||
final pool = DHTRecordPool.instance;
|
||||
|
||||
final unlockedAccountInfo =
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final accountRecordKey = unlockedAccountInfo.accountRecordKey;
|
||||
final identityPublicKey = unlockedAccountInfo.identityPublicKey;
|
||||
|
||||
// Ensure we don't delete this if we're trying to chat to self
|
||||
final isSelf =
|
||||
_contactSuperIdentity.currentInstance.publicKey == identityPublicKey;
|
||||
final isSelf = _contactSuperIdentity.currentInstance.publicKey ==
|
||||
_accountInfo.identityPublicKey;
|
||||
|
||||
return (await pool.openRecordWrite(_contactRequestInboxKey, _writer,
|
||||
debugName: 'ValidContactInvitation::reject::'
|
||||
'ContactRequestInbox',
|
||||
parent: accountRecordKey))
|
||||
parent: _accountInfo.accountRecordKey))
|
||||
.maybeDeleteScope(!isSelf, (contactRequestInbox) async {
|
||||
final cs =
|
||||
await pool.veilid.getCryptoSystem(_contactRequestInboxKey.kind);
|
||||
|
||||
final contactResponse = proto.ContactResponse()
|
||||
..accept = false
|
||||
..superIdentityRecordKey =
|
||||
unlockedAccountInfo.superIdentityRecordKey.toProto();
|
||||
_accountInfo.superIdentityRecordKey.toProto();
|
||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||
|
||||
final identitySignature = await cs.sign(
|
||||
unlockedAccountInfo.identityWriter.key,
|
||||
unlockedAccountInfo.identityWriter.secret,
|
||||
contactResponseBytes);
|
||||
final cs = await _accountInfo.identityCryptoSystem;
|
||||
final identitySignature = await cs.signWithKeyPair(
|
||||
_accountInfo.identityWriter, contactResponseBytes);
|
||||
|
||||
final signedContactResponse = proto.SignedContactResponse()
|
||||
..contactResponse = contactResponseBytes
|
||||
@ -136,7 +118,7 @@ class ValidContactInvitation {
|
||||
}
|
||||
|
||||
//
|
||||
final Locator _locator;
|
||||
final AccountInfo _accountInfo;
|
||||
final TypedKey _contactRequestInboxKey;
|
||||
final SuperIdentity _contactSuperIdentity;
|
||||
final KeyPair _writer;
|
||||
|
@ -140,8 +140,18 @@ class CreateInvitationDialogState extends State<CreateInvitationDialog> {
|
||||
// Start generation
|
||||
final contactInvitationListCubit =
|
||||
widget.modalContext.read<ContactInvitationListCubit>();
|
||||
final profile = widget.modalContext
|
||||
.read<AccountRecordCubit>()
|
||||
.state
|
||||
.asData
|
||||
?.value
|
||||
.profile;
|
||||
if (profile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final generator = contactInvitationListCubit.createInvitation(
|
||||
profile: profile,
|
||||
encryptionKeyType: _encryptionKeyType,
|
||||
encryptionKey: _encryptionKey,
|
||||
message: _messageTextController.text,
|
||||
|
@ -76,17 +76,19 @@ class InvitationDialogState extends State<InvitationDialog> {
|
||||
final navigator = Navigator.of(context);
|
||||
final accountInfo = widget._locator<AccountInfoCubit>().state;
|
||||
final contactList = widget._locator<ContactListCubit>();
|
||||
final profile =
|
||||
widget._locator<AccountRecordCubit>().state.asData!.value.profile;
|
||||
|
||||
setState(() {
|
||||
_isAccepting = true;
|
||||
});
|
||||
final validInvitation = _validInvitation;
|
||||
if (validInvitation != null) {
|
||||
final acceptedContact = await validInvitation.accept();
|
||||
final acceptedContact = await validInvitation.accept(profile);
|
||||
if (acceptedContact != null) {
|
||||
// initiator when accept is received will create
|
||||
// contact in the case of a 'note to self'
|
||||
final isSelf = accountInfo.unlockedAccountInfo!.identityPublicKey ==
|
||||
final isSelf = accountInfo.identityPublicKey ==
|
||||
acceptedContact.remoteIdentity.currentInstance.publicKey;
|
||||
if (!isSelf) {
|
||||
await contactList.createContact(
|
||||
|
@ -3,9 +3,9 @@ import 'dart:convert';
|
||||
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../conversation/conversation.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../tools/tools.dart';
|
||||
@ -15,12 +15,12 @@ import '../../tools/tools.dart';
|
||||
|
||||
class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
|
||||
ContactListCubit({
|
||||
required Locator locator,
|
||||
required TypedKey accountRecordKey,
|
||||
required AccountInfo accountInfo,
|
||||
required OwnedDHTRecordPointer contactListRecordPointer,
|
||||
}) : _locator = locator,
|
||||
}) : _accountInfo = accountInfo,
|
||||
super(
|
||||
open: () => _open(accountRecordKey, contactListRecordPointer),
|
||||
open: () =>
|
||||
_open(accountInfo.accountRecordKey, contactListRecordPointer),
|
||||
decodeElement: proto.Contact.fromBuffer);
|
||||
|
||||
static Future<DHTShortArray> _open(TypedKey accountRecordKey,
|
||||
@ -126,7 +126,7 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
|
||||
try {
|
||||
// Make a conversation cubit to manipulate the conversation
|
||||
final conversationCubit = ConversationCubit(
|
||||
locator: _locator,
|
||||
accountInfo: _accountInfo,
|
||||
remoteIdentityPublicKey: deletedItem.identityPublicKey.toVeilid(),
|
||||
localConversationRecordKey:
|
||||
deletedItem.localConversationRecordKey.toVeilid(),
|
||||
@ -144,5 +144,5 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
|
||||
|
||||
final _contactProfileUpdateMap =
|
||||
SingleStateProcessorMap<TypedKey, proto.Profile?>();
|
||||
final Locator _locator;
|
||||
final AccountInfo _accountInfo;
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ 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:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
@ -45,10 +44,15 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
AsyncValue<ActiveConversationState>, ActiveConversationCubit>
|
||||
with StateMapFollower<ChatListCubitState, TypedKey, proto.Chat> {
|
||||
ActiveConversationsBlocMapCubit({
|
||||
required Locator locator,
|
||||
}) : _locator = locator {
|
||||
required AccountInfo accountInfo,
|
||||
required AccountRecordCubit accountRecordCubit,
|
||||
required ChatListCubit chatListCubit,
|
||||
required ContactListCubit contactListCubit,
|
||||
}) : _accountInfo = accountInfo,
|
||||
_accountRecordCubit = accountRecordCubit,
|
||||
_contactListCubit = contactListCubit {
|
||||
// Follow the chat list cubit
|
||||
follow(locator<ChatListCubit>());
|
||||
follow(chatListCubit);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@ -69,20 +73,15 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
// Conversation cubit the tracks the state between the local
|
||||
// and remote halves of a contact's relationship with this account
|
||||
final conversationCubit = ConversationCubit(
|
||||
locator: _locator,
|
||||
accountInfo: _accountInfo,
|
||||
remoteIdentityPublicKey: remoteIdentityPublicKey,
|
||||
localConversationRecordKey: localConversationRecordKey,
|
||||
remoteConversationRecordKey: remoteConversationRecordKey,
|
||||
);
|
||||
|
||||
// When our local account profile changes, send it to the conversation
|
||||
final accountRecordCubit = _locator<AccountRecordCubit>();
|
||||
conversationCubit.watchAccountChanges(
|
||||
accountRecordCubit.stream, accountRecordCubit.state);
|
||||
|
||||
// When remote conversation changes its profile,
|
||||
// update our local contact
|
||||
_locator<ContactListCubit>().followContactProfileChanges(
|
||||
_contactListCubit.followContactProfileChanges(
|
||||
localConversationRecordKey,
|
||||
conversationCubit.stream.map((x) => x.map(
|
||||
data: (d) => d.value.remoteConversation?.profile,
|
||||
@ -90,6 +89,10 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
error: (_) => null)),
|
||||
conversationCubit.state.asData?.value.remoteConversation?.profile);
|
||||
|
||||
// When our local account profile changes, send it to the conversation
|
||||
conversationCubit.watchAccountChanges(
|
||||
_accountRecordCubit.stream, _accountRecordCubit.state);
|
||||
|
||||
// Transformer that only passes through completed/active conversations
|
||||
// along with the contact that corresponds to the completed
|
||||
// conversation
|
||||
@ -119,7 +122,7 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
|
||||
@override
|
||||
Future<void> updateState(TypedKey key, proto.Chat value) async {
|
||||
final contactList = _locator<ContactListCubit>().state.state.asData?.value;
|
||||
final contactList = _contactListCubit.state.state.asData?.value;
|
||||
if (contactList == null) {
|
||||
await addState(key, const AsyncValue.loading());
|
||||
return;
|
||||
@ -136,5 +139,7 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
|
||||
////
|
||||
|
||||
final Locator _locator;
|
||||
final AccountInfo _accountInfo;
|
||||
final AccountRecordCubit _accountRecordCubit;
|
||||
final ContactListCubit _contactListCubit;
|
||||
}
|
||||
|
@ -2,13 +2,14 @@ import 'dart:async';
|
||||
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../chat/chat.dart';
|
||||
import '../../chat_list/cubits/chat_list_cubit.dart';
|
||||
import '../../contacts/contacts.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../conversation.dart';
|
||||
import 'active_conversations_bloc_map_cubit.dart';
|
||||
|
||||
// Map of localConversationRecordKey to MessagesCubit
|
||||
@ -19,10 +20,16 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
with
|
||||
StateMapFollower<ActiveConversationsBlocMapState, TypedKey,
|
||||
AsyncValue<ActiveConversationState>> {
|
||||
ActiveSingleContactChatBlocMapCubit({required Locator locator})
|
||||
: _locator = locator {
|
||||
ActiveSingleContactChatBlocMapCubit(
|
||||
{required AccountInfo accountInfo,
|
||||
required ActiveConversationsBlocMapCubit activeConversationsBlocMapCubit,
|
||||
required ContactListCubit contactListCubit,
|
||||
required ChatListCubit chatListCubit})
|
||||
: _accountInfo = accountInfo,
|
||||
_contactListCubit = contactListCubit,
|
||||
_chatListCubit = chatListCubit {
|
||||
// Follow the active conversations bloc map cubit
|
||||
follow(locator<ActiveConversationsBlocMapCubit>());
|
||||
follow(activeConversationsBlocMapCubit);
|
||||
}
|
||||
|
||||
Future<void> _addConversationMessages(
|
||||
@ -33,7 +40,7 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
add(() => MapEntry(
|
||||
contact.localConversationRecordKey.toVeilid(),
|
||||
SingleContactMessagesCubit(
|
||||
locator: _locator,
|
||||
accountInfo: _accountInfo,
|
||||
remoteIdentityPublicKey: contact.identityPublicKey.toVeilid(),
|
||||
localConversationRecordKey:
|
||||
contact.localConversationRecordKey.toVeilid(),
|
||||
@ -52,7 +59,7 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
Future<void> updateState(
|
||||
TypedKey key, AsyncValue<ActiveConversationState> value) async {
|
||||
// Get the contact object for this single contact chat
|
||||
final contactList = _locator<ContactListCubit>().state.state.asData?.value;
|
||||
final contactList = _contactListCubit.state.state.asData?.value;
|
||||
if (contactList == null) {
|
||||
await addState(key, const AsyncValue.loading());
|
||||
return;
|
||||
@ -67,7 +74,7 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
final contact = contactList[contactIndex].value;
|
||||
|
||||
// Get the chat object for this single contact chat
|
||||
final chatList = _locator<ChatListCubit>().state.state.asData?.value;
|
||||
final chatList = _chatListCubit.state.state.asData?.value;
|
||||
if (chatList == null) {
|
||||
await addState(key, const AsyncValue.loading());
|
||||
return;
|
||||
@ -92,6 +99,7 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
final Locator _locator;
|
||||
final AccountInfo _accountInfo;
|
||||
final ContactListCubit _contactListCubit;
|
||||
final ChatListCubit _chatListCubit;
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
@ -36,19 +35,16 @@ class ConversationState extends Equatable {
|
||||
/// 1-1 chats
|
||||
class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
ConversationCubit(
|
||||
{required Locator locator,
|
||||
{required AccountInfo accountInfo,
|
||||
required TypedKey remoteIdentityPublicKey,
|
||||
TypedKey? localConversationRecordKey,
|
||||
TypedKey? remoteConversationRecordKey})
|
||||
: _locator = locator,
|
||||
: _accountInfo = accountInfo,
|
||||
_localConversationRecordKey = localConversationRecordKey,
|
||||
_remoteIdentityPublicKey = remoteIdentityPublicKey,
|
||||
_remoteConversationRecordKey = remoteConversationRecordKey,
|
||||
super(const AsyncValue.loading()) {
|
||||
final unlockedAccountInfo =
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
_accountRecordKey = unlockedAccountInfo.accountRecordKey;
|
||||
_identityWriter = unlockedAccountInfo.identityWriter;
|
||||
_identityWriter = _accountInfo.identityWriter;
|
||||
|
||||
if (_localConversationRecordKey != null) {
|
||||
_initWait.add(() async {
|
||||
@ -60,7 +56,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
final record = await pool.openRecordWrite(
|
||||
_localConversationRecordKey!, writer,
|
||||
debugName: 'ConversationCubit::LocalConversation',
|
||||
parent: _accountRecordKey,
|
||||
parent: accountInfo.accountRecordKey,
|
||||
crypto: crypto);
|
||||
|
||||
return record;
|
||||
@ -77,7 +73,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
final record = await pool.openRecordRead(_remoteConversationRecordKey,
|
||||
debugName: 'ConversationCubit::RemoteConversation',
|
||||
parent: pool.getParentRecordKey(_remoteConversationRecordKey) ??
|
||||
_accountRecordKey,
|
||||
accountInfo.accountRecordKey,
|
||||
crypto: crypto);
|
||||
return record;
|
||||
});
|
||||
@ -108,7 +104,8 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
/// The callback allows for more initialization to occur and for
|
||||
/// cleanup to delete records upon failure of the callback
|
||||
Future<T> initLocalConversation<T>(
|
||||
{required FutureOr<T> Function(DHTRecord) callback,
|
||||
{required proto.Profile profile,
|
||||
required FutureOr<T> Function(DHTRecord) callback,
|
||||
TypedKey? existingConversationRecordKey}) async {
|
||||
assert(_localConversationRecordKey == null,
|
||||
'must not have a local conversation yet');
|
||||
@ -116,11 +113,8 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
final pool = DHTRecordPool.instance;
|
||||
|
||||
final crypto = await _cachedConversationCrypto();
|
||||
final account = _locator<AccountRecordCubit>().state.asData!.value;
|
||||
final unlockedAccountInfo =
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
final accountRecordKey = unlockedAccountInfo.accountRecordKey;
|
||||
final writer = unlockedAccountInfo.identityWriter;
|
||||
final accountRecordKey = _accountInfo.accountRecordKey;
|
||||
final writer = _accountInfo.identityWriter;
|
||||
|
||||
// Open with SMPL scheme for identity writer
|
||||
late final DHTRecord localConversationRecord;
|
||||
@ -150,9 +144,9 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
callback: (messages) async {
|
||||
// Create initial local conversation key contents
|
||||
final conversation = proto.Conversation()
|
||||
..profile = account.profile
|
||||
..superIdentityJson = jsonEncode(
|
||||
unlockedAccountInfo.localAccount.superIdentity.toJson())
|
||||
..profile = profile
|
||||
..superIdentityJson =
|
||||
jsonEncode(_accountInfo.localAccount.superIdentity.toJson())
|
||||
..messages = messages.recordKey.toProto();
|
||||
|
||||
// Write initial conversation to record
|
||||
@ -359,10 +353,8 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
if (conversationCrypto != null) {
|
||||
return conversationCrypto;
|
||||
}
|
||||
final unlockedAccountInfo =
|
||||
_locator<AccountInfoCubit>().state.unlockedAccountInfo!;
|
||||
conversationCrypto = await unlockedAccountInfo
|
||||
.makeConversationCrypto(_remoteIdentityPublicKey);
|
||||
conversationCrypto =
|
||||
await _accountInfo.makeConversationCrypto(_remoteIdentityPublicKey);
|
||||
_conversationCrypto = conversationCrypto;
|
||||
return conversationCrypto;
|
||||
}
|
||||
@ -371,8 +363,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
||||
// Fields
|
||||
TypedKey get remoteIdentityPublicKey => _remoteIdentityPublicKey;
|
||||
|
||||
final Locator _locator;
|
||||
late final TypedKey _accountRecordKey;
|
||||
final AccountInfo _accountInfo;
|
||||
late final KeyPair _identityWriter;
|
||||
final TypedKey _remoteIdentityPublicKey;
|
||||
TypedKey? _localConversationRecordKey;
|
||||
|
@ -233,7 +233,8 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
//final textTheme = theme.textTheme;
|
||||
final localAccounts = context.watch<LocalAccountsCubit>().state;
|
||||
final accountRecords = context.watch<AccountRecordsBlocMapCubit>().state;
|
||||
final accountRecords =
|
||||
context.watch<PerAccountCollectionBlocMapCubit>().state;
|
||||
final activeLocalAccount = context.watch<ActiveLocalAccountCubit>().state;
|
||||
final gradient = LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
|
@ -164,45 +164,44 @@ class HomeScreenState extends State<HomeScreen> {
|
||||
], child: Builder(builder: _buildAccountReadyDeviceSpecific)));
|
||||
}
|
||||
|
||||
Widget _buildAccount(BuildContext context, TypedKey superIdentityRecordKey) =>
|
||||
BlocProvider<AccountInfoCubit>(
|
||||
Widget _buildAccount(BuildContext context, TypedKey superIdentityRecordKey,
|
||||
PerAccountCollectionCubit perAccountCollectionCubit) =>
|
||||
BlocBuilder<PerAccountCollectionCubit, PerAccountCollectionState>(
|
||||
key: ValueKey(superIdentityRecordKey),
|
||||
create: (context) => AccountInfoCubit(
|
||||
AccountRepository.instance, superIdentityRecordKey),
|
||||
child: Builder(builder: (context) {
|
||||
// Get active account info status
|
||||
final accountInfoStatus =
|
||||
context.select<AccountInfoCubit, AccountInfoStatus>(
|
||||
(c) => c.state.status);
|
||||
bloc: perAccountCollectionCubit,
|
||||
builder: (context, state) {
|
||||
|
||||
switch (accountInfoStatus) {
|
||||
case AccountInfoStatus.noAccount:
|
||||
return const HomeAccountMissing();
|
||||
case AccountInfoStatus.accountInvalid:
|
||||
return const HomeAccountInvalid();
|
||||
case AccountInfoStatus.accountLocked:
|
||||
return const HomeAccountLocked();
|
||||
case AccountInfoStatus.accountUnlocked:
|
||||
|
||||
// Get the current active account record cubit
|
||||
final activeAccountRecordCubit = context
|
||||
.select<AccountRecordsBlocMapCubit, AccountRecordCubit?>(
|
||||
(c) => c.tryOperate(superIdentityRecordKey,
|
||||
closure: (x) => x));
|
||||
if (activeAccountRecordCubit == null) {
|
||||
return waitingPage();
|
||||
}
|
||||
switch (state.accountInfo.status) {
|
||||
case AccountInfoStatus.accountInvalid:
|
||||
return const HomeAccountInvalid();
|
||||
case AccountInfoStatus.accountLocked:
|
||||
return const HomeAccountLocked();
|
||||
case AccountInfoStatus.accountUnlocked:
|
||||
|
||||
return MultiBlocProvider(providers: [
|
||||
BlocProvider<AccountRecordCubit>.value(
|
||||
value: activeAccountRecordCubit),
|
||||
], child: Builder(builder: _buildUnlockedAccount));
|
||||
}
|
||||
}));
|
||||
// Get the current active account record cubit
|
||||
final activeAccountRecordCubit = context.select<
|
||||
PerAccountCollectionBlocMapCubit,
|
||||
AccountRecordCubit?>(
|
||||
(c) => c.tryOperate(superIdentityRecordKey,
|
||||
closure: (x) => x));
|
||||
if (activeAccountRecordCubit == null) {
|
||||
return waitingPage();
|
||||
}
|
||||
|
||||
return MultiBlocProvider(providers: [
|
||||
BlocProvider<AccountRecordCubit>.value(
|
||||
value: activeAccountRecordCubit),
|
||||
], child: Builder(builder: _buildUnlockedAccount));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Widget _buildAccountPageView(BuildContext context) {
|
||||
final localAccounts = context.watch<LocalAccountsCubit>().state;
|
||||
final activeLocalAccountCubit = context.read<ActiveLocalAccountCubit>();
|
||||
final activeLocalAccountCubit = context.watch<ActiveLocalAccountCubit>();
|
||||
final perAccountCollectionBlocMapCubit =
|
||||
context.watch<PerAccountCollectionBlocMapCubit>();
|
||||
|
||||
final activeIndex = localAccounts.indexWhere(
|
||||
(x) => x.superIdentity.recordKey == activeLocalAccountCubit.state);
|
||||
@ -218,7 +217,7 @@ class HomeScreenState extends State<HomeScreen> {
|
||||
value.dispose();
|
||||
},
|
||||
child: Builder(
|
||||
builder: (context) => PageView.custom(
|
||||
builder: (context) => PageView.builder(
|
||||
onPageChanged: (idx) {
|
||||
singleFuture(this, () async {
|
||||
await AccountRepository.instance.switchToAccount(
|
||||
@ -228,10 +227,21 @@ class HomeScreenState extends State<HomeScreen> {
|
||||
controller: context
|
||||
.read<ActiveAccountPageControllerWrapper>()
|
||||
.pageController,
|
||||
childrenDelegate: SliverChildListDelegate(localAccounts
|
||||
.map((la) =>
|
||||
_buildAccount(context, la.superIdentity.recordKey))
|
||||
.toList()))));
|
||||
itemCount: localAccounts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final superIdentityRecordKey =
|
||||
localAccounts[index].superIdentity.recordKey;
|
||||
final perAccountCollectionCubit =
|
||||
perAccountCollectionBlocMapCubit.tryOperate(
|
||||
superIdentityRecordKey,
|
||||
closure: (c) => c);
|
||||
if (perAccountCollectionCubit == null) {
|
||||
return HomeAccountMissing(
|
||||
key: ValueKey(superIdentityRecordKey));
|
||||
}
|
||||
return _buildAccount(context, superIdentityRecordKey,
|
||||
perAccountCollectionCubit);
|
||||
})));
|
||||
}
|
||||
|
||||
@override
|
||||
|
Loading…
Reference in New Issue
Block a user