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