mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-01-23 05:51:06 -05:00
refactor cubits to keep them alive, wip
This commit is contained in:
parent
360ba436f8
commit
3edf2ebb46
@ -10,14 +10,18 @@ class AccountInfoCubit extends Cubit<AccountInfo> {
|
||||
AccountInfoCubit(
|
||||
AccountRepository accountRepository, TypedKey superIdentityRecordKey)
|
||||
: _accountRepository = accountRepository,
|
||||
super(accountRepository.getAccountInfo(superIdentityRecordKey)) {
|
||||
super(accountRepository.getAccountInfo(superIdentityRecordKey)!) {
|
||||
// Subscribe to streams
|
||||
_accountRepositorySubscription = _accountRepository.stream.listen((change) {
|
||||
switch (change) {
|
||||
case AccountRepositoryChange.activeLocalAccount:
|
||||
case AccountRepositoryChange.localAccounts:
|
||||
case AccountRepositoryChange.userLogins:
|
||||
emit(accountRepository.getAccountInfo(superIdentityRecordKey));
|
||||
final acctInfo =
|
||||
accountRepository.getAccountInfo(superIdentityRecordKey);
|
||||
if (acctInfo != null) {
|
||||
emit(acctInfo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
@ -15,18 +15,13 @@ typedef AccountRecordState = proto.Account;
|
||||
/// tabledb-local storage, encrypted by the unlock code for the account.
|
||||
class AccountRecordCubit extends DefaultDHTRecordCubit<AccountRecordState> {
|
||||
AccountRecordCubit(
|
||||
{required AccountRepository accountRepository,
|
||||
required TypedKey superIdentityRecordKey})
|
||||
{required LocalAccount localAccount, required UserLogin userLogin})
|
||||
: super(
|
||||
decodeState: proto.Account.fromBuffer,
|
||||
open: () => _open(accountRepository, superIdentityRecordKey));
|
||||
|
||||
static Future<DHTRecord> _open(AccountRepository accountRepository,
|
||||
TypedKey superIdentityRecordKey) async {
|
||||
final localAccount =
|
||||
accountRepository.fetchLocalAccount(superIdentityRecordKey)!;
|
||||
final userLogin = accountRepository.fetchUserLogin(superIdentityRecordKey)!;
|
||||
open: () => _open(localAccount, userLogin));
|
||||
|
||||
static Future<DHTRecord> _open(
|
||||
LocalAccount localAccount, UserLogin userLogin) async {
|
||||
// Record not yet open, do it
|
||||
final pool = DHTRecordPool.instance;
|
||||
final record = await pool.openRecordOwned(
|
||||
|
@ -12,12 +12,12 @@ typedef AccountRecordsBlocMapState
|
||||
/// Ensures there is an single account record cubit for each logged in account
|
||||
class AccountRecordsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
AsyncValue<AccountRecordState>, AccountRecordCubit>
|
||||
with StateMapFollower<UserLoginsState, TypedKey, UserLogin> {
|
||||
with StateMapFollower<LocalAccountsState, TypedKey, LocalAccount> {
|
||||
AccountRecordsBlocMapCubit(
|
||||
AccountRepository accountRepository, Locator locator)
|
||||
: _accountRepository = accountRepository {
|
||||
// Follow the user logins cubit
|
||||
follow(locator<UserLoginsCubit>());
|
||||
// Follow the local accounts cubit
|
||||
follow(locator<LocalAccountsCubit>());
|
||||
}
|
||||
|
||||
// Add account record cubit
|
||||
@ -35,9 +35,9 @@ class AccountRecordsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||
Future<void> removeFromState(TypedKey key) => remove(key);
|
||||
|
||||
@override
|
||||
Future<void> updateState(TypedKey key, UserLogin value) async {
|
||||
Future<void> updateState(TypedKey key, LocalAccount value) async {
|
||||
await _addAccountRecordCubit(
|
||||
superIdentityRecordKey: value.superIdentityRecordKey);
|
||||
superIdentityRecordKey: value.superIdentity.recordKey);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,12 +1,17 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../models/models.dart';
|
||||
import '../repository/account_repository.dart';
|
||||
|
||||
class LocalAccountsCubit extends Cubit<IList<LocalAccount>> {
|
||||
typedef LocalAccountsState = IList<LocalAccount>;
|
||||
|
||||
class LocalAccountsCubit extends Cubit<LocalAccountsState>
|
||||
with StateMapFollowable<LocalAccountsState, TypedKey, LocalAccount> {
|
||||
LocalAccountsCubit(AccountRepository accountRepository)
|
||||
: _accountRepository = accountRepository,
|
||||
super(accountRepository.getLocalAccounts()) {
|
||||
@ -30,6 +35,14 @@ class LocalAccountsCubit extends Cubit<IList<LocalAccount>> {
|
||||
await _accountRepositorySubscription.cancel();
|
||||
}
|
||||
|
||||
/// StateMapFollowable /////////////////////////
|
||||
@override
|
||||
IMap<TypedKey, LocalAccount> getStateMap(LocalAccountsState state) {
|
||||
final stateValue = state;
|
||||
return IMap.fromIterable(stateValue,
|
||||
keyMapper: (e) => e.superIdentity.recordKey, valueMapper: (e) => e);
|
||||
}
|
||||
|
||||
final AccountRepository _accountRepository;
|
||||
late final StreamSubscription<AccountRepositoryChange>
|
||||
_accountRepositorySubscription;
|
||||
|
209
lib/account_manager/cubits/per_account_collection_cubit.dart
Normal file
209
lib/account_manager/cubits/per_account_collection_cubit.dart
Normal file
@ -0,0 +1,209 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../chat/chat.dart';
|
||||
import '../../chat_list/chat_list.dart';
|
||||
import '../../contact_invitation/contact_invitation.dart';
|
||||
import '../../contacts/contacts.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../account_manager.dart';
|
||||
|
||||
class PerAccountCollectionCubit extends Cubit<PerAccountCollectionState> {
|
||||
PerAccountCollectionCubit({
|
||||
required Locator locator,
|
||||
required this.accountInfoCubit,
|
||||
}) : _locator = locator,
|
||||
super(_initialState(accountInfoCubit)) {
|
||||
// Async Init
|
||||
_initWait.add(_init);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _processor.close();
|
||||
await accountInfoCubit.close();
|
||||
await _accountRecordSubscription?.cancel();
|
||||
await accountRecordCubit?.close();
|
||||
|
||||
await super.close();
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
await _initWait();
|
||||
|
||||
// subscribe to accountInfo changes
|
||||
_processor.follow(accountInfoCubit.stream, accountInfoCubit.state,
|
||||
_followAccountInfoState);
|
||||
}
|
||||
|
||||
static PerAccountCollectionState _initialState(
|
||||
AccountInfoCubit accountInfoCubit) =>
|
||||
PerAccountCollectionState(
|
||||
accountInfo: accountInfoCubit.state,
|
||||
avAccountRecordState: const AsyncValue.loading(),
|
||||
contactInvitationListCubit: null);
|
||||
|
||||
Future<void> _followAccountInfoState(AccountInfo accountInfo) async {
|
||||
var nextState = state.copyWith(accountInfo: accountInfo);
|
||||
|
||||
if (accountInfo.userLogin == null) {
|
||||
/////////////// Not logged in /////////////////
|
||||
|
||||
// Unsubscribe AccountRecordCubit
|
||||
await _accountRecordSubscription?.cancel();
|
||||
_accountRecordSubscription = null;
|
||||
|
||||
// Update state
|
||||
nextState =
|
||||
_updateAccountRecordState(nextState, const AsyncValue.loading());
|
||||
emit(nextState);
|
||||
|
||||
// Close AccountRecordCubit
|
||||
await accountRecordCubit?.close();
|
||||
accountRecordCubit = null;
|
||||
} else {
|
||||
///////////////// Logged in ///////////////////
|
||||
|
||||
// AccountRecordCubit
|
||||
accountRecordCubit ??= AccountRecordCubit(
|
||||
localAccount: accountInfo.localAccount,
|
||||
userLogin: accountInfo.userLogin!);
|
||||
|
||||
// Update State
|
||||
nextState =
|
||||
_updateAccountRecordState(nextState, accountRecordCubit!.state);
|
||||
emit(nextState);
|
||||
|
||||
// Subscribe AccountRecordCubit
|
||||
_accountRecordSubscription ??=
|
||||
accountRecordCubit!.stream.listen((avAccountRecordState) {
|
||||
emit(_updateAccountRecordState(state, avAccountRecordState));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
PerAccountCollectionState _updateAccountRecordState(
|
||||
PerAccountCollectionState prevState,
|
||||
AsyncValue<AccountRecordState> avAccountRecordState) {
|
||||
// Get next state
|
||||
final nextState =
|
||||
state.copyWith(avAccountRecordState: accountRecordCubit!.state);
|
||||
|
||||
// Get bloc parameters
|
||||
final accountRecordKey = nextState.accountInfo.accountRecordKey;
|
||||
|
||||
// ContactInvitationListCubit
|
||||
final contactInvitationListRecordPointer = nextState
|
||||
.avAccountRecordState.asData?.value.contactInvitationRecords
|
||||
.toVeilid();
|
||||
|
||||
contactInvitationListCubitUpdater.update(
|
||||
contactInvitationListRecordPointer == null
|
||||
? null
|
||||
: (
|
||||
collectionLocator,
|
||||
accountRecordKey,
|
||||
contactInvitationListRecordPointer
|
||||
));
|
||||
|
||||
// ContactListCubit
|
||||
final contactListRecordPointer =
|
||||
nextState.avAccountRecordState.asData?.value.contactList.toVeilid();
|
||||
|
||||
contactListCubitUpdater.update(contactListRecordPointer == null
|
||||
? null
|
||||
: (collectionLocator, accountRecordKey, contactListRecordPointer));
|
||||
|
||||
// WaitingInvitationsBlocMapCubit
|
||||
waitingInvitationsBlocMapCubitUpdater.update(
|
||||
nextState.avAccountRecordState.isData ? collectionLocator : null);
|
||||
|
||||
// ActiveChatCubit
|
||||
activeChatCubitUpdater
|
||||
.update(nextState.avAccountRecordState.isData ? true : null);
|
||||
|
||||
// ChatListCubit
|
||||
final chatListRecordPointer =
|
||||
nextState.avAccountRecordState.asData?.value.chatList.toVeilid();
|
||||
|
||||
chatListCubitUpdater.update(chatListRecordPointer == null
|
||||
? null
|
||||
: (collectionLocator, accountRecordKey, chatListRecordPointer));
|
||||
|
||||
// ActiveConversationsBlocMapCubit
|
||||
// xxx
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
T collectionLocator<T>() {
|
||||
if (T is AccountInfoCubit) {
|
||||
return accountInfoCubit as T;
|
||||
}
|
||||
if (T is AccountRecordCubit) {
|
||||
return accountRecordCubit! as T;
|
||||
}
|
||||
if (T is ContactInvitationListCubit) {
|
||||
return contactInvitationListCubitUpdater.bloc! as T;
|
||||
}
|
||||
if (T is ContactListCubit) {
|
||||
return contactListCubitUpdater.bloc! as T;
|
||||
}
|
||||
if (T is WaitingInvitationsBlocMapCubit) {
|
||||
return waitingInvitationsBlocMapCubitUpdater.bloc! as T;
|
||||
}
|
||||
if (T is ActiveChatCubit) {
|
||||
return activeChatCubitUpdater.bloc! as T;
|
||||
}
|
||||
if (T is ChatListCubit) {
|
||||
return chatListCubitUpdater.bloc! as T;
|
||||
}
|
||||
return _locator<T>();
|
||||
}
|
||||
|
||||
final Locator _locator;
|
||||
final _processor = SingleStateProcessor<AccountInfo>();
|
||||
final _initWait = WaitSet<void>();
|
||||
|
||||
// Per-account cubits regardless of login state
|
||||
final AccountInfoCubit accountInfoCubit;
|
||||
|
||||
// Per logged-in account cubits
|
||||
AccountRecordCubit? accountRecordCubit;
|
||||
StreamSubscription<AsyncValue<AccountRecordState>>?
|
||||
_accountRecordSubscription;
|
||||
final contactInvitationListCubitUpdater = BlocUpdater<
|
||||
ContactInvitationListCubit,
|
||||
(Locator, TypedKey, OwnedDHTRecordPointer)>(
|
||||
create: (params) => ContactInvitationListCubit(
|
||||
locator: params.$1,
|
||||
accountRecordKey: params.$2,
|
||||
contactInvitationListRecordPointer: params.$3,
|
||||
));
|
||||
final contactListCubitUpdater =
|
||||
BlocUpdater<ContactListCubit, (Locator, TypedKey, OwnedDHTRecordPointer)>(
|
||||
create: (params) => ContactListCubit(
|
||||
locator: params.$1,
|
||||
accountRecordKey: params.$2,
|
||||
contactListRecordPointer: params.$3,
|
||||
));
|
||||
final waitingInvitationsBlocMapCubitUpdater =
|
||||
BlocUpdater<WaitingInvitationsBlocMapCubit, Locator>(
|
||||
create: (params) => WaitingInvitationsBlocMapCubit(
|
||||
locator: params,
|
||||
));
|
||||
final activeChatCubitUpdater =
|
||||
BlocUpdater<ActiveChatCubit, bool>(create: (_) => ActiveChatCubit(null));
|
||||
final chatListCubitUpdater =
|
||||
BlocUpdater<ChatListCubit, (Locator, TypedKey, OwnedDHTRecordPointer)>(
|
||||
create: (params) => ChatListCubit(
|
||||
locator: params.$1,
|
||||
accountRecordKey: params.$2,
|
||||
chatListRecordPointer: params.$3,
|
||||
));
|
||||
}
|
@ -1,17 +1,14 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../models/models.dart';
|
||||
import '../repository/account_repository.dart';
|
||||
|
||||
typedef UserLoginsState = IList<UserLogin>;
|
||||
|
||||
class UserLoginsCubit extends Cubit<UserLoginsState>
|
||||
with StateMapFollowable<UserLoginsState, TypedKey, UserLogin> {
|
||||
class UserLoginsCubit extends Cubit<UserLoginsState> {
|
||||
UserLoginsCubit(AccountRepository accountRepository)
|
||||
: _accountRepository = accountRepository,
|
||||
super(accountRepository.getUserLogins()) {
|
||||
@ -34,15 +31,6 @@ class UserLoginsCubit extends Cubit<UserLoginsState>
|
||||
await super.close();
|
||||
await _accountRepositorySubscription.cancel();
|
||||
}
|
||||
|
||||
/// StateMapFollowable /////////////////////////
|
||||
@override
|
||||
IMap<TypedKey, UserLogin> getStateMap(UserLoginsState state) {
|
||||
final stateValue = state;
|
||||
return IMap.fromIterable(stateValue,
|
||||
keyMapper: (e) => e.superIdentityRecordKey, valueMapper: (e) => e);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
final AccountRepository _accountRepository;
|
||||
|
@ -1,10 +1,12 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import 'unlocked_account_info.dart';
|
||||
import '../account_manager.dart';
|
||||
|
||||
enum AccountInfoStatus {
|
||||
noAccount,
|
||||
accountInvalid,
|
||||
accountLocked,
|
||||
accountUnlocked,
|
||||
@ -15,13 +17,49 @@ class AccountInfo extends Equatable {
|
||||
const AccountInfo({
|
||||
required this.status,
|
||||
required this.active,
|
||||
required this.unlockedAccountInfo,
|
||||
required this.localAccount,
|
||||
required this.userLogin,
|
||||
});
|
||||
|
||||
final AccountInfoStatus status;
|
||||
final bool active;
|
||||
final UnlockedAccountInfo? unlockedAccountInfo;
|
||||
final LocalAccount localAccount;
|
||||
final UserLogin? userLogin;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status, active, unlockedAccountInfo];
|
||||
List<Object?> get props => [
|
||||
status,
|
||||
active,
|
||||
localAccount,
|
||||
userLogin,
|
||||
];
|
||||
}
|
||||
|
||||
extension AccountInfoExt on AccountInfo {
|
||||
TypedKey get superIdentityRecordKey => localAccount.superIdentity.recordKey;
|
||||
TypedKey get accountRecordKey =>
|
||||
userLogin!.accountRecordInfo.accountRecord.recordKey;
|
||||
TypedKey get identityTypedPublicKey =>
|
||||
localAccount.superIdentity.currentInstance.typedPublicKey;
|
||||
PublicKey get identityPublicKey =>
|
||||
localAccount.superIdentity.currentInstance.publicKey;
|
||||
SecretKey get identitySecretKey => userLogin!.identitySecret.value;
|
||||
KeyPair get identityWriter =>
|
||||
KeyPair(key: identityPublicKey, secret: identitySecretKey);
|
||||
Future<VeilidCryptoSystem> get identityCryptoSystem =>
|
||||
localAccount.superIdentity.currentInstance.cryptoSystem;
|
||||
|
||||
Future<VeilidCrypto> makeConversationCrypto(
|
||||
TypedKey remoteIdentityPublicKey) async {
|
||||
final identitySecret = userLogin!.identitySecret;
|
||||
final cs = await Veilid.instance.getCryptoSystem(identitySecret.kind);
|
||||
final sharedSecret = await cs.generateSharedSecret(
|
||||
remoteIdentityPublicKey.value,
|
||||
identitySecret.value,
|
||||
utf8.encode('VeilidChat Conversation'));
|
||||
|
||||
final messagesCrypto = await VeilidCryptoPrivate.fromSharedSecret(
|
||||
identitySecret.kind, sharedSecret);
|
||||
return messagesCrypto;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
export 'account_info.dart';
|
||||
export 'unlocked_account_info.dart';
|
||||
export 'encryption_key_type.dart';
|
||||
export 'local_account/local_account.dart';
|
||||
export 'new_profile_spec.dart';
|
||||
export 'per_account_collection_state/per_account_collection_state.dart';
|
||||
export 'user_login/user_login.dart';
|
||||
|
@ -0,0 +1,17 @@
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../../contact_invitation/contact_invitation.dart';
|
||||
import '../../../proto/proto.dart' show Account;
|
||||
import '../../account_manager.dart';
|
||||
|
||||
part 'per_account_collection_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class PerAccountCollectionState with _$PerAccountCollectionState {
|
||||
const factory PerAccountCollectionState(
|
||||
{required AccountInfo accountInfo,
|
||||
required AsyncValue<AccountRecordState> avAccountRecordState,
|
||||
required ContactInvitationListCubit? contactInvitationListCubit}) =
|
||||
_PerAccountCollectionState;
|
||||
}
|
@ -0,0 +1,204 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'per_account_collection_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$PerAccountCollectionState {
|
||||
AccountInfo get accountInfo => throw _privateConstructorUsedError;
|
||||
AsyncValue<Account> get avAccountRecordState =>
|
||||
throw _privateConstructorUsedError;
|
||||
ContactInvitationListCubit? get contactInvitationListCubit =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$PerAccountCollectionStateCopyWith<PerAccountCollectionState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $PerAccountCollectionStateCopyWith<$Res> {
|
||||
factory $PerAccountCollectionStateCopyWith(PerAccountCollectionState value,
|
||||
$Res Function(PerAccountCollectionState) then) =
|
||||
_$PerAccountCollectionStateCopyWithImpl<$Res, PerAccountCollectionState>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{AccountInfo accountInfo,
|
||||
AsyncValue<Account> avAccountRecordState,
|
||||
ContactInvitationListCubit? contactInvitationListCubit});
|
||||
|
||||
$AsyncValueCopyWith<Account, $Res> get avAccountRecordState;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$PerAccountCollectionStateCopyWithImpl<$Res,
|
||||
$Val extends PerAccountCollectionState>
|
||||
implements $PerAccountCollectionStateCopyWith<$Res> {
|
||||
_$PerAccountCollectionStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? accountInfo = null,
|
||||
Object? avAccountRecordState = null,
|
||||
Object? contactInvitationListCubit = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
accountInfo: null == accountInfo
|
||||
? _value.accountInfo
|
||||
: accountInfo // ignore: cast_nullable_to_non_nullable
|
||||
as AccountInfo,
|
||||
avAccountRecordState: null == avAccountRecordState
|
||||
? _value.avAccountRecordState
|
||||
: avAccountRecordState // ignore: cast_nullable_to_non_nullable
|
||||
as AsyncValue<Account>,
|
||||
contactInvitationListCubit: freezed == contactInvitationListCubit
|
||||
? _value.contactInvitationListCubit
|
||||
: contactInvitationListCubit // ignore: cast_nullable_to_non_nullable
|
||||
as ContactInvitationListCubit?,
|
||||
) as $Val);
|
||||
}
|
||||
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$AsyncValueCopyWith<Account, $Res> get avAccountRecordState {
|
||||
return $AsyncValueCopyWith<Account, $Res>(_value.avAccountRecordState,
|
||||
(value) {
|
||||
return _then(_value.copyWith(avAccountRecordState: value) as $Val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$PerAccountCollectionStateImplCopyWith<$Res>
|
||||
implements $PerAccountCollectionStateCopyWith<$Res> {
|
||||
factory _$$PerAccountCollectionStateImplCopyWith(
|
||||
_$PerAccountCollectionStateImpl value,
|
||||
$Res Function(_$PerAccountCollectionStateImpl) then) =
|
||||
__$$PerAccountCollectionStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{AccountInfo accountInfo,
|
||||
AsyncValue<Account> avAccountRecordState,
|
||||
ContactInvitationListCubit? contactInvitationListCubit});
|
||||
|
||||
@override
|
||||
$AsyncValueCopyWith<Account, $Res> get avAccountRecordState;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$PerAccountCollectionStateImplCopyWithImpl<$Res>
|
||||
extends _$PerAccountCollectionStateCopyWithImpl<$Res,
|
||||
_$PerAccountCollectionStateImpl>
|
||||
implements _$$PerAccountCollectionStateImplCopyWith<$Res> {
|
||||
__$$PerAccountCollectionStateImplCopyWithImpl(
|
||||
_$PerAccountCollectionStateImpl _value,
|
||||
$Res Function(_$PerAccountCollectionStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? accountInfo = null,
|
||||
Object? avAccountRecordState = null,
|
||||
Object? contactInvitationListCubit = freezed,
|
||||
}) {
|
||||
return _then(_$PerAccountCollectionStateImpl(
|
||||
accountInfo: null == accountInfo
|
||||
? _value.accountInfo
|
||||
: accountInfo // ignore: cast_nullable_to_non_nullable
|
||||
as AccountInfo,
|
||||
avAccountRecordState: null == avAccountRecordState
|
||||
? _value.avAccountRecordState
|
||||
: avAccountRecordState // ignore: cast_nullable_to_non_nullable
|
||||
as AsyncValue<Account>,
|
||||
contactInvitationListCubit: freezed == contactInvitationListCubit
|
||||
? _value.contactInvitationListCubit
|
||||
: contactInvitationListCubit // ignore: cast_nullable_to_non_nullable
|
||||
as ContactInvitationListCubit?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$PerAccountCollectionStateImpl implements _PerAccountCollectionState {
|
||||
const _$PerAccountCollectionStateImpl(
|
||||
{required this.accountInfo,
|
||||
required this.avAccountRecordState,
|
||||
required this.contactInvitationListCubit});
|
||||
|
||||
@override
|
||||
final AccountInfo accountInfo;
|
||||
@override
|
||||
final AsyncValue<Account> avAccountRecordState;
|
||||
@override
|
||||
final ContactInvitationListCubit? contactInvitationListCubit;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PerAccountCollectionState(accountInfo: $accountInfo, avAccountRecordState: $avAccountRecordState, contactInvitationListCubit: $contactInvitationListCubit)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$PerAccountCollectionStateImpl &&
|
||||
(identical(other.accountInfo, accountInfo) ||
|
||||
other.accountInfo == accountInfo) &&
|
||||
(identical(other.avAccountRecordState, avAccountRecordState) ||
|
||||
other.avAccountRecordState == avAccountRecordState) &&
|
||||
(identical(other.contactInvitationListCubit,
|
||||
contactInvitationListCubit) ||
|
||||
other.contactInvitationListCubit ==
|
||||
contactInvitationListCubit));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType, accountInfo,
|
||||
avAccountRecordState, contactInvitationListCubit);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$PerAccountCollectionStateImplCopyWith<_$PerAccountCollectionStateImpl>
|
||||
get copyWith => __$$PerAccountCollectionStateImplCopyWithImpl<
|
||||
_$PerAccountCollectionStateImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _PerAccountCollectionState implements PerAccountCollectionState {
|
||||
const factory _PerAccountCollectionState(
|
||||
{required final AccountInfo accountInfo,
|
||||
required final AsyncValue<Account> avAccountRecordState,
|
||||
required final ContactInvitationListCubit?
|
||||
contactInvitationListCubit}) = _$PerAccountCollectionStateImpl;
|
||||
|
||||
@override
|
||||
AccountInfo get accountInfo;
|
||||
@override
|
||||
AsyncValue<Account> get avAccountRecordState;
|
||||
@override
|
||||
ContactInvitationListCubit? get contactInvitationListCubit;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$PerAccountCollectionStateImplCopyWith<_$PerAccountCollectionStateImpl>
|
||||
get copyWith => throw _privateConstructorUsedError;
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import 'local_account/local_account.dart';
|
||||
import 'user_login/user_login.dart';
|
||||
|
||||
@immutable
|
||||
class UnlockedAccountInfo extends Equatable {
|
||||
const UnlockedAccountInfo({
|
||||
required this.localAccount,
|
||||
required this.userLogin,
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Public Interface
|
||||
|
||||
TypedKey get superIdentityRecordKey => localAccount.superIdentity.recordKey;
|
||||
TypedKey get accountRecordKey =>
|
||||
userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
TypedKey get identityTypedPublicKey =>
|
||||
localAccount.superIdentity.currentInstance.typedPublicKey;
|
||||
PublicKey get identityPublicKey =>
|
||||
localAccount.superIdentity.currentInstance.publicKey;
|
||||
SecretKey get identitySecretKey => userLogin.identitySecret.value;
|
||||
KeyPair get identityWriter =>
|
||||
KeyPair(key: identityPublicKey, secret: identitySecretKey);
|
||||
Future<VeilidCryptoSystem> get identityCryptoSystem =>
|
||||
localAccount.superIdentity.currentInstance.cryptoSystem;
|
||||
|
||||
Future<VeilidCrypto> makeConversationCrypto(
|
||||
TypedKey remoteIdentityPublicKey) async {
|
||||
final identitySecret = userLogin.identitySecret;
|
||||
final cs = await Veilid.instance.getCryptoSystem(identitySecret.kind);
|
||||
final sharedSecret = await cs.generateSharedSecret(
|
||||
remoteIdentityPublicKey.value,
|
||||
identitySecret.value,
|
||||
utf8.encode('VeilidChat Conversation'));
|
||||
|
||||
final messagesCrypto = await VeilidCryptoPrivate.fromSharedSecret(
|
||||
identitySecret.kind, sharedSecret);
|
||||
return messagesCrypto;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Fields
|
||||
|
||||
final LocalAccount localAccount;
|
||||
final UserLogin userLogin;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [localAccount, userLogin];
|
||||
}
|
@ -92,29 +92,15 @@ class AccountRepository {
|
||||
return userLogins[idx];
|
||||
}
|
||||
|
||||
AccountInfo getAccountInfo(TypedKey? superIdentityRecordKey) {
|
||||
AccountInfo? getAccountInfo(TypedKey superIdentityRecordKey) {
|
||||
// Get active account if we have one
|
||||
final activeLocalAccount = getActiveLocalAccount();
|
||||
if (superIdentityRecordKey == null) {
|
||||
if (activeLocalAccount == null) {
|
||||
// No user logged in
|
||||
return const AccountInfo(
|
||||
status: AccountInfoStatus.noAccount,
|
||||
active: false,
|
||||
unlockedAccountInfo: null);
|
||||
}
|
||||
superIdentityRecordKey = activeLocalAccount;
|
||||
}
|
||||
final active = superIdentityRecordKey == activeLocalAccount;
|
||||
|
||||
// Get which local account we want to fetch the profile for
|
||||
final localAccount = fetchLocalAccount(superIdentityRecordKey);
|
||||
if (localAccount == null) {
|
||||
// account does not exist
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.noAccount,
|
||||
active: active,
|
||||
unlockedAccountInfo: null);
|
||||
return null;
|
||||
}
|
||||
|
||||
// See if we've logged into this account or if it is locked
|
||||
@ -124,15 +110,17 @@ class AccountRepository {
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountLocked,
|
||||
active: active,
|
||||
unlockedAccountInfo: null);
|
||||
localAccount: localAccount,
|
||||
userLogin: null,
|
||||
);
|
||||
}
|
||||
|
||||
// Got account, decrypted and decoded
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountUnlocked,
|
||||
active: active,
|
||||
unlockedAccountInfo:
|
||||
UnlockedAccountInfo(localAccount: localAccount, userLogin: userLogin),
|
||||
localAccount: localAccount,
|
||||
userLogin: userLogin,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -164,18 +164,16 @@ class HomeScreenState extends State<HomeScreen> {
|
||||
], child: Builder(builder: _buildAccountReadyDeviceSpecific)));
|
||||
}
|
||||
|
||||
Widget _buildAccount(BuildContext context) {
|
||||
Widget _buildAccount(BuildContext context, TypedKey superIdentityRecordKey) =>
|
||||
BlocProvider<AccountInfoCubit>(
|
||||
key: ValueKey(superIdentityRecordKey),
|
||||
create: (context) => AccountInfoCubit(
|
||||
AccountRepository.instance, superIdentityRecordKey),
|
||||
child: Builder(builder: (context) {
|
||||
// Get active account info status
|
||||
final (
|
||||
accountInfoStatus,
|
||||
accountInfoActive,
|
||||
superIdentityRecordKey
|
||||
) = context
|
||||
.select<AccountInfoCubit, (AccountInfoStatus, bool, TypedKey?)>((c) => (
|
||||
c.state.status,
|
||||
c.state.active,
|
||||
c.state.unlockedAccountInfo?.superIdentityRecordKey
|
||||
));
|
||||
final accountInfoStatus =
|
||||
context.select<AccountInfoCubit, AccountInfoStatus>(
|
||||
(c) => c.state.status);
|
||||
|
||||
switch (accountInfoStatus) {
|
||||
case AccountInfoStatus.noAccount:
|
||||
@ -187,11 +185,10 @@ class HomeScreenState extends State<HomeScreen> {
|
||||
case AccountInfoStatus.accountUnlocked:
|
||||
|
||||
// Get the current active account record cubit
|
||||
final activeAccountRecordCubit =
|
||||
context.select<AccountRecordsBlocMapCubit, AccountRecordCubit?>(
|
||||
(c) => superIdentityRecordKey == null
|
||||
? null
|
||||
: c.tryOperate(superIdentityRecordKey, closure: (x) => x));
|
||||
final activeAccountRecordCubit = context
|
||||
.select<AccountRecordsBlocMapCubit, AccountRecordCubit?>(
|
||||
(c) => c.tryOperate(superIdentityRecordKey,
|
||||
closure: (x) => x));
|
||||
if (activeAccountRecordCubit == null) {
|
||||
return waitingPage();
|
||||
}
|
||||
@ -201,7 +198,7 @@ class HomeScreenState extends State<HomeScreen> {
|
||||
value: activeAccountRecordCubit),
|
||||
], child: Builder(builder: _buildUnlockedAccount));
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
Widget _buildAccountPageView(BuildContext context) {
|
||||
final localAccounts = context.watch<LocalAccountsCubit>().state;
|
||||
@ -221,8 +218,7 @@ class HomeScreenState extends State<HomeScreen> {
|
||||
value.dispose();
|
||||
},
|
||||
child: Builder(
|
||||
builder: (context) => PageView.builder(
|
||||
itemCount: localAccounts.length,
|
||||
builder: (context) => PageView.custom(
|
||||
onPageChanged: (idx) {
|
||||
singleFuture(this, () async {
|
||||
await AccountRepository.instance.switchToAccount(
|
||||
@ -232,15 +228,10 @@ class HomeScreenState extends State<HomeScreen> {
|
||||
controller: context
|
||||
.read<ActiveAccountPageControllerWrapper>()
|
||||
.pageController,
|
||||
itemBuilder: (context, index) {
|
||||
final localAccount = localAccounts[index];
|
||||
return BlocProvider<AccountInfoCubit>(
|
||||
key: ValueKey(localAccount.superIdentity.recordKey),
|
||||
create: (context) => AccountInfoCubit(
|
||||
AccountRepository.instance,
|
||||
localAccount.superIdentity.recordKey),
|
||||
child: Builder(builder: _buildAccount));
|
||||
})));
|
||||
childrenDelegate: SliverChildListDelegate(localAccounts
|
||||
.map((la) =>
|
||||
_buildAccount(context, la.superIdentity.recordKey))
|
||||
.toList()))));
|
||||
}
|
||||
|
||||
@override
|
||||
|
Loading…
Reference in New Issue
Block a user