mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-01-11 07:39:32 -05:00
more refactor work
This commit is contained in:
parent
0a922e97b6
commit
6e8ba551ad
16
lib/account_manager/cubit/account_record_cubit.dart
Normal file
16
lib/account_manager/cubit/account_record_cubit.dart
Normal file
@ -0,0 +1,16 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../proto/proto.dart' as proto;
|
||||
|
||||
class AccountRecordCubit extends DefaultDHTRecordCubit<proto.Account> {
|
||||
AccountRecordCubit({
|
||||
required super.record,
|
||||
}) : super(decodeState: proto.Account.fromBuffer);
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await super.close();
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
export 'account_record_cubit.dart';
|
||||
export 'active_user_login_cubit/active_user_login_cubit.dart';
|
||||
export 'local_accounts_cubit/local_accounts_cubit.dart';
|
||||
export 'user_logins_cubit/user_logins_cubit.dart';
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import 'active_account_info.dart';
|
||||
|
||||
enum AccountInfoStatus {
|
||||
noAccount,
|
||||
@ -13,10 +14,10 @@ class AccountInfo {
|
||||
const AccountInfo({
|
||||
required this.status,
|
||||
required this.active,
|
||||
this.accountRecord,
|
||||
required this.activeAccountInfo,
|
||||
});
|
||||
|
||||
final AccountInfoStatus status;
|
||||
final bool active;
|
||||
final DHTRecord? accountRecord;
|
||||
final ActiveAccountInfo? activeAccountInfo;
|
||||
}
|
||||
|
@ -82,66 +82,40 @@ class AccountRepository {
|
||||
return userLogins[idx];
|
||||
}
|
||||
|
||||
AccountInfo getAccountInfo({required TypedKey accountMasterRecordKey}) {
|
||||
AccountInfo? getAccountInfo({TypedKey? accountMasterRecordKey}) {
|
||||
// Get active user if we have one
|
||||
if (accountMasterRecordKey == null) {
|
||||
final activeUserLogin = getActiveUserLogin();
|
||||
if (activeUserLogin == null) {
|
||||
// No user logged in
|
||||
return null;
|
||||
}
|
||||
accountMasterRecordKey = activeUserLogin;
|
||||
}
|
||||
|
||||
// Get which local account we want to fetch the profile for
|
||||
final localAccount =
|
||||
fetchLocalAccount(accountMasterRecordKey: accountMasterRecordKey);
|
||||
if (localAccount == null) {
|
||||
// Local account does not exist
|
||||
return const AccountInfo(
|
||||
status: AccountInfoStatus.noAccount, active: false);
|
||||
status: AccountInfoStatus.noAccount,
|
||||
active: false,
|
||||
activeAccountInfo: null);
|
||||
}
|
||||
|
||||
// See if we've logged into this account or if it is locked
|
||||
final activeUserLogin = getActiveUserLogin();
|
||||
final active = activeUserLogin == accountMasterRecordKey;
|
||||
|
||||
final login =
|
||||
final userLogin =
|
||||
fetchUserLogin(accountMasterRecordKey: accountMasterRecordKey);
|
||||
if (login == null) {
|
||||
// Account was locked
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountLocked, active: active);
|
||||
}
|
||||
|
||||
// Pull the account DHT key, decode it and return it
|
||||
final pool = DHTRecordPool.instance;
|
||||
final accountRecord =
|
||||
pool.getOpenedRecord(login.accountRecordInfo.accountRecord.recordKey);
|
||||
if (accountRecord == null) {
|
||||
// Account could not be read or decrypted from DHT
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountInvalid, active: active);
|
||||
}
|
||||
|
||||
// Got account, decrypted and decoded
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountReady,
|
||||
active: active,
|
||||
accountRecord: accountRecord);
|
||||
}
|
||||
|
||||
Future<ActiveAccountInfo?> fetchActiveAccountInfo() async {
|
||||
// See if we've logged into this account or if it is locked
|
||||
final activeUserLogin = getActiveUserLogin();
|
||||
if (activeUserLogin == null) {
|
||||
// No user logged in
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the user login
|
||||
final userLogin = fetchUserLogin(accountMasterRecordKey: activeUserLogin);
|
||||
if (userLogin == null) {
|
||||
// Account was locked
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get which local account we want to fetch the profile for
|
||||
final localAccount =
|
||||
fetchLocalAccount(accountMasterRecordKey: activeUserLogin);
|
||||
if (localAccount == null) {
|
||||
// Local account does not exist
|
||||
return null;
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountLocked,
|
||||
active: active,
|
||||
activeAccountInfo: null);
|
||||
}
|
||||
|
||||
// Pull the account DHT key, decode it and return it
|
||||
@ -149,14 +123,21 @@ class AccountRepository {
|
||||
final accountRecord = pool
|
||||
.getOpenedRecord(userLogin.accountRecordInfo.accountRecord.recordKey);
|
||||
if (accountRecord == null) {
|
||||
return null;
|
||||
// Account could not be read or decrypted from DHT
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountInvalid,
|
||||
active: active,
|
||||
activeAccountInfo: null);
|
||||
}
|
||||
|
||||
// Got account, decrypted and decoded
|
||||
return ActiveAccountInfo(
|
||||
localAccount: localAccount,
|
||||
userLogin: userLogin,
|
||||
accountRecord: accountRecord,
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountReady,
|
||||
active: active,
|
||||
activeAccountInfo: ActiveAccountInfo(
|
||||
localAccount: localAccount,
|
||||
userLogin: userLogin,
|
||||
accountRecord: accountRecord),
|
||||
);
|
||||
}
|
||||
|
||||
@ -411,24 +392,21 @@ class AccountRepository {
|
||||
// For all user logins if they arent open yet
|
||||
final activeLogins = await _activeLogins.get();
|
||||
for (final userLogin in activeLogins.userLogins) {
|
||||
//// Account record key /////////////////////////////
|
||||
final accountRecordKey =
|
||||
userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
final existingAccountRecord = pool.getOpenedRecord(accountRecordKey);
|
||||
if (existingAccountRecord != null) {
|
||||
continue;
|
||||
if (existingAccountRecord == null) {
|
||||
final localAccount = fetchLocalAccount(
|
||||
accountMasterRecordKey: userLogin.accountMasterRecordKey);
|
||||
|
||||
// Record not yet open, do it
|
||||
final record = await pool.openOwned(
|
||||
userLogin.accountRecordInfo.accountRecord,
|
||||
parent: localAccount!.identityMaster.identityRecordKey);
|
||||
// Watch the record's only (default) key
|
||||
await record.watch();
|
||||
}
|
||||
final localAccount = fetchLocalAccount(
|
||||
accountMasterRecordKey: userLogin.accountMasterRecordKey);
|
||||
|
||||
// Record not yet open, do it
|
||||
final record = await pool.openOwned(
|
||||
userLogin.accountRecordInfo.accountRecord,
|
||||
parent: localAccount!.identityMaster.identityRecordKey);
|
||||
// Watch the record's only (default) key
|
||||
await record.watch();
|
||||
|
||||
// .scope(
|
||||
// (accountRec) => accountRec.getProtobuf(proto.Account.fromBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
@ -437,6 +415,7 @@ class AccountRepository {
|
||||
|
||||
final activeLogins = await _activeLogins.get();
|
||||
for (final userLogin in activeLogins.userLogins) {
|
||||
//// Account record key /////////////////////////////
|
||||
final accountRecordKey =
|
||||
userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
final accountRecord = pool.getOpenedRecord(accountRecordKey);
|
||||
|
141
lib/contact_invitation/valid_contact_invitation.dart
Normal file
141
lib/contact_invitation/valid_contact_invitation.dart
Normal file
@ -0,0 +1,141 @@
|
||||
//////////////////////////////////////////////////
|
||||
///
|
||||
|
||||
class ValidContactInvitation {
|
||||
ValidContactInvitation._(
|
||||
{required ContactInvitationListManager contactInvitationManager,
|
||||
required proto.SignedContactInvitation signedContactInvitation,
|
||||
required proto.ContactInvitation contactInvitation,
|
||||
required TypedKey contactRequestInboxKey,
|
||||
required proto.ContactRequest contactRequest,
|
||||
required proto.ContactRequestPrivate contactRequestPrivate,
|
||||
required IdentityMaster contactIdentityMaster,
|
||||
required KeyPair writer})
|
||||
: _contactInvitationManager = contactInvitationManager,
|
||||
_signedContactInvitation = signedContactInvitation,
|
||||
_contactInvitation = contactInvitation,
|
||||
_contactRequestInboxKey = contactRequestInboxKey,
|
||||
_contactRequest = contactRequest,
|
||||
_contactRequestPrivate = contactRequestPrivate,
|
||||
_contactIdentityMaster = contactIdentityMaster,
|
||||
_writer = writer;
|
||||
|
||||
Future<AcceptedContact?> accept() async {
|
||||
final pool = await DHTRecordPool.instance();
|
||||
final activeAccountInfo = _contactInvitationManager._activeAccountInfo;
|
||||
try {
|
||||
// Ensure we don't delete this if we're trying to chat to self
|
||||
final isSelf = _contactIdentityMaster.identityPublicKey ==
|
||||
activeAccountInfo.localAccount.identityMaster.identityPublicKey;
|
||||
final accountRecordKey =
|
||||
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
|
||||
return (await pool.openWrite(_contactRequestInboxKey, _writer,
|
||||
parent: accountRecordKey))
|
||||
// ignore: prefer_expression_function_bodies
|
||||
.maybeDeleteScope(!isSelf, (contactRequestInbox) async {
|
||||
// Create local conversation key for this
|
||||
// contact and send via contact response
|
||||
return createConversation(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey:
|
||||
_contactIdentityMaster.identityPublicTypedKey(),
|
||||
callback: (localConversation) async {
|
||||
final contactResponse = proto.ContactResponse()
|
||||
..accept = true
|
||||
..remoteConversationRecordKey = localConversation.key.toProto()
|
||||
..identityMasterRecordKey = activeAccountInfo
|
||||
.localAccount.identityMaster.masterRecordKey
|
||||
.toProto();
|
||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||
|
||||
final cs = await pool.veilid
|
||||
.getCryptoSystem(_contactRequestInboxKey.kind);
|
||||
|
||||
final identitySignature = await cs.sign(
|
||||
activeAccountInfo
|
||||
.localAccount.identityMaster.identityPublicKey,
|
||||
activeAccountInfo.userLogin.identitySecret.value,
|
||||
contactResponseBytes);
|
||||
|
||||
final signedContactResponse = proto.SignedContactResponse()
|
||||
..contactResponse = contactResponseBytes
|
||||
..identitySignature = identitySignature.toProto();
|
||||
|
||||
// Write the acceptance to the inbox
|
||||
if (await contactRequestInbox.tryWriteProtobuf(
|
||||
proto.SignedContactResponse.fromBuffer,
|
||||
signedContactResponse,
|
||||
subkey: 1) !=
|
||||
null) {
|
||||
throw Exception('failed to accept contact invitation');
|
||||
}
|
||||
return AcceptedContact(
|
||||
profile: _contactRequestPrivate.profile,
|
||||
remoteIdentity: _contactIdentityMaster,
|
||||
remoteConversationRecordKey: proto.TypedKeyProto.fromProto(
|
||||
_contactRequestPrivate.chatRecordKey),
|
||||
localConversationRecordKey: localConversation.key,
|
||||
);
|
||||
});
|
||||
});
|
||||
} on Exception catch (e) {
|
||||
log.debug('exception: $e', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> reject() async {
|
||||
final pool = await DHTRecordPool.instance();
|
||||
final activeAccountInfo = _contactInvitationManager._activeAccountInfo;
|
||||
|
||||
// Ensure we don't delete this if we're trying to chat to self
|
||||
final isSelf = _contactIdentityMaster.identityPublicKey ==
|
||||
activeAccountInfo.localAccount.identityMaster.identityPublicKey;
|
||||
final accountRecordKey =
|
||||
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
|
||||
return (await pool.openWrite(_contactRequestInboxKey, _writer,
|
||||
parent: accountRecordKey))
|
||||
.maybeDeleteScope(!isSelf, (contactRequestInbox) async {
|
||||
final cs =
|
||||
await pool.veilid.getCryptoSystem(_contactRequestInboxKey.kind);
|
||||
|
||||
final contactResponse = proto.ContactResponse()
|
||||
..accept = false
|
||||
..identityMasterRecordKey = activeAccountInfo
|
||||
.localAccount.identityMaster.masterRecordKey
|
||||
.toProto();
|
||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||
|
||||
final identitySignature = await cs.sign(
|
||||
activeAccountInfo.localAccount.identityMaster.identityPublicKey,
|
||||
activeAccountInfo.userLogin.identitySecret.value,
|
||||
contactResponseBytes);
|
||||
|
||||
final signedContactResponse = proto.SignedContactResponse()
|
||||
..contactResponse = contactResponseBytes
|
||||
..identitySignature = identitySignature.toProto();
|
||||
|
||||
// Write the rejection to the inbox
|
||||
if (await contactRequestInbox.tryWriteProtobuf(
|
||||
proto.SignedContactResponse.fromBuffer, signedContactResponse,
|
||||
subkey: 1) !=
|
||||
null) {
|
||||
log.error('failed to reject contact invitation');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
ContactInvitationListManager _contactInvitationManager;
|
||||
proto.SignedContactInvitation _signedContactInvitation;
|
||||
proto.ContactInvitation _contactInvitation;
|
||||
TypedKey _contactRequestInboxKey;
|
||||
proto.ContactRequest _contactRequest;
|
||||
proto.ContactRequestPrivate _contactRequestPrivate;
|
||||
IdentityMaster _contactIdentityMaster;
|
||||
KeyPair _writer;
|
||||
}
|
@ -67,8 +67,7 @@ class HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
|
||||
return BlocProvider(
|
||||
create: (context) => DefaultDHTRecordCubit(
|
||||
record: accountRecord, decodeState: proto.Account.fromBuffer),
|
||||
create: (context) => AccountRecordCubit(record: accountRecord),
|
||||
child: Column(children: <Widget>[
|
||||
Row(children: [
|
||||
IconButton(
|
||||
@ -87,7 +86,7 @@ class HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
||||
context.go('/home/settings');
|
||||
}).paddingLTRB(0, 0, 8, 0),
|
||||
context
|
||||
.watch<DefaultDHTRecordCubit<proto.Account>>()
|
||||
.watch<AccountRecordCubit>()
|
||||
.state
|
||||
.builder((context, account) => ProfileWidget(
|
||||
name: account.profile.name,
|
||||
@ -96,7 +95,7 @@ class HomePageState extends State<HomePage> with TickerProviderStateMixin {
|
||||
.expanded(),
|
||||
]).paddingAll(8),
|
||||
context
|
||||
.watch<DefaultDHTRecordCubit<proto.Account>>()
|
||||
.watch<AccountRecordCubit>()
|
||||
.state
|
||||
.builder((context, account) => MainPager(
|
||||
localAccounts: localAccounts,
|
||||
|
@ -11,6 +11,7 @@ import '../../../proto/proto.dart' as proto;
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../contact_invitation/contact_invitation.dart';
|
||||
import '../../contacts/contacts.dart';
|
||||
import '../../theme/theme.dart';
|
||||
|
||||
class AccountPage extends StatefulWidget {
|
||||
const AccountPage({
|
||||
|
@ -439,145 +439,3 @@ class ContactInvitationListManager extends _$ContactInvitationListManager {
|
||||
final DHTShortArray _dhtRecord;
|
||||
IList<proto.ContactInvitationRecord> _records;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
///
|
||||
|
||||
class ValidContactInvitation {
|
||||
ValidContactInvitation._(
|
||||
{required ContactInvitationListManager contactInvitationManager,
|
||||
required proto.SignedContactInvitation signedContactInvitation,
|
||||
required proto.ContactInvitation contactInvitation,
|
||||
required TypedKey contactRequestInboxKey,
|
||||
required proto.ContactRequest contactRequest,
|
||||
required proto.ContactRequestPrivate contactRequestPrivate,
|
||||
required IdentityMaster contactIdentityMaster,
|
||||
required KeyPair writer})
|
||||
: _contactInvitationManager = contactInvitationManager,
|
||||
_signedContactInvitation = signedContactInvitation,
|
||||
_contactInvitation = contactInvitation,
|
||||
_contactRequestInboxKey = contactRequestInboxKey,
|
||||
_contactRequest = contactRequest,
|
||||
_contactRequestPrivate = contactRequestPrivate,
|
||||
_contactIdentityMaster = contactIdentityMaster,
|
||||
_writer = writer;
|
||||
|
||||
Future<AcceptedContact?> accept() async {
|
||||
final pool = await DHTRecordPool.instance();
|
||||
final activeAccountInfo = _contactInvitationManager._activeAccountInfo;
|
||||
try {
|
||||
// Ensure we don't delete this if we're trying to chat to self
|
||||
final isSelf = _contactIdentityMaster.identityPublicKey ==
|
||||
activeAccountInfo.localAccount.identityMaster.identityPublicKey;
|
||||
final accountRecordKey =
|
||||
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
|
||||
return (await pool.openWrite(_contactRequestInboxKey, _writer,
|
||||
parent: accountRecordKey))
|
||||
// ignore: prefer_expression_function_bodies
|
||||
.maybeDeleteScope(!isSelf, (contactRequestInbox) async {
|
||||
// Create local conversation key for this
|
||||
// contact and send via contact response
|
||||
return createConversation(
|
||||
activeAccountInfo: activeAccountInfo,
|
||||
remoteIdentityPublicKey:
|
||||
_contactIdentityMaster.identityPublicTypedKey(),
|
||||
callback: (localConversation) async {
|
||||
final contactResponse = proto.ContactResponse()
|
||||
..accept = true
|
||||
..remoteConversationRecordKey = localConversation.key.toProto()
|
||||
..identityMasterRecordKey = activeAccountInfo
|
||||
.localAccount.identityMaster.masterRecordKey
|
||||
.toProto();
|
||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||
|
||||
final cs = await pool.veilid
|
||||
.getCryptoSystem(_contactRequestInboxKey.kind);
|
||||
|
||||
final identitySignature = await cs.sign(
|
||||
activeAccountInfo
|
||||
.localAccount.identityMaster.identityPublicKey,
|
||||
activeAccountInfo.userLogin.identitySecret.value,
|
||||
contactResponseBytes);
|
||||
|
||||
final signedContactResponse = proto.SignedContactResponse()
|
||||
..contactResponse = contactResponseBytes
|
||||
..identitySignature = identitySignature.toProto();
|
||||
|
||||
// Write the acceptance to the inbox
|
||||
if (await contactRequestInbox.tryWriteProtobuf(
|
||||
proto.SignedContactResponse.fromBuffer,
|
||||
signedContactResponse,
|
||||
subkey: 1) !=
|
||||
null) {
|
||||
throw Exception('failed to accept contact invitation');
|
||||
}
|
||||
return AcceptedContact(
|
||||
profile: _contactRequestPrivate.profile,
|
||||
remoteIdentity: _contactIdentityMaster,
|
||||
remoteConversationRecordKey: proto.TypedKeyProto.fromProto(
|
||||
_contactRequestPrivate.chatRecordKey),
|
||||
localConversationRecordKey: localConversation.key,
|
||||
);
|
||||
});
|
||||
});
|
||||
} on Exception catch (e) {
|
||||
log.debug('exception: $e', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> reject() async {
|
||||
final pool = await DHTRecordPool.instance();
|
||||
final activeAccountInfo = _contactInvitationManager._activeAccountInfo;
|
||||
|
||||
// Ensure we don't delete this if we're trying to chat to self
|
||||
final isSelf = _contactIdentityMaster.identityPublicKey ==
|
||||
activeAccountInfo.localAccount.identityMaster.identityPublicKey;
|
||||
final accountRecordKey =
|
||||
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||
|
||||
return (await pool.openWrite(_contactRequestInboxKey, _writer,
|
||||
parent: accountRecordKey))
|
||||
.maybeDeleteScope(!isSelf, (contactRequestInbox) async {
|
||||
final cs =
|
||||
await pool.veilid.getCryptoSystem(_contactRequestInboxKey.kind);
|
||||
|
||||
final contactResponse = proto.ContactResponse()
|
||||
..accept = false
|
||||
..identityMasterRecordKey = activeAccountInfo
|
||||
.localAccount.identityMaster.masterRecordKey
|
||||
.toProto();
|
||||
final contactResponseBytes = contactResponse.writeToBuffer();
|
||||
|
||||
final identitySignature = await cs.sign(
|
||||
activeAccountInfo.localAccount.identityMaster.identityPublicKey,
|
||||
activeAccountInfo.userLogin.identitySecret.value,
|
||||
contactResponseBytes);
|
||||
|
||||
final signedContactResponse = proto.SignedContactResponse()
|
||||
..contactResponse = contactResponseBytes
|
||||
..identitySignature = identitySignature.toProto();
|
||||
|
||||
// Write the rejection to the inbox
|
||||
if (await contactRequestInbox.tryWriteProtobuf(
|
||||
proto.SignedContactResponse.fromBuffer, signedContactResponse,
|
||||
subkey: 1) !=
|
||||
null) {
|
||||
log.error('failed to reject contact invitation');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
ContactInvitationListManager _contactInvitationManager;
|
||||
proto.SignedContactInvitation _signedContactInvitation;
|
||||
proto.ContactInvitation _contactInvitation;
|
||||
TypedKey _contactRequestInboxKey;
|
||||
proto.ContactRequest _contactRequest;
|
||||
proto.ContactRequestPrivate _contactRequestPrivate;
|
||||
IdentityMaster _contactIdentityMaster;
|
||||
KeyPair _writer;
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ class _DHTShortArrayCache {
|
||||
}
|
||||
}
|
||||
|
||||
xxxx add listening to head and linked records
|
||||
|
||||
class DHTShortArray {
|
||||
DHTShortArray._({required DHTRecord headRecord})
|
||||
: _headRecord = headRecord,
|
||||
|
@ -0,0 +1,84 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
|
||||
import '../../veilid_support.dart';
|
||||
|
||||
class DHTShortArrayCubit<T> extends Cubit<AsyncValue<IList<T>>> {
|
||||
DHTShortArrayCubit({
|
||||
required DHTShortArray shortArray,
|
||||
required T Function(List<int> data) decodeElement,
|
||||
}) : super(const AsyncValue.loading()) {
|
||||
Future.delayed(Duration.zero, () async {
|
||||
// Make initial state update
|
||||
try {
|
||||
final initialState = await initialStateFunction(record);
|
||||
if (initialState != null) {
|
||||
emit(AsyncValue.data(initialState));
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
emit(AsyncValue.error(e));
|
||||
}
|
||||
|
||||
shortArray. xxx add listen to head and linked records in dht_short_array
|
||||
|
||||
_subscription = await record.listen((update) async {
|
||||
try {
|
||||
final newState =
|
||||
await stateFunction(record, update.subkeys, update.valueData);
|
||||
if (newState != null) {
|
||||
emit(AsyncValue.data(newState));
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
emit(AsyncValue.error(e));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _subscription?.cancel();
|
||||
_subscription = null;
|
||||
await super.close();
|
||||
}
|
||||
|
||||
StreamSubscription<VeilidUpdateValueChange>? _subscription;
|
||||
}
|
||||
|
||||
// Cubit that watches the default subkey value of a dhtrecord
|
||||
class DefaultDHTRecordCubit<T> extends DHTRecordCubit<T> {
|
||||
DefaultDHTRecordCubit({
|
||||
required super.record,
|
||||
required T Function(List<int> data) decodeState,
|
||||
}) : super(
|
||||
initialStateFunction: (record) async {
|
||||
final initialData = await record.get();
|
||||
if (initialData == null) {
|
||||
return null;
|
||||
}
|
||||
return decodeState(initialData);
|
||||
},
|
||||
stateFunction: (record, subkeys, valueData) async {
|
||||
final defaultSubkey = record.subkeyOrDefault(-1);
|
||||
if (subkeys.containsSubkey(defaultSubkey)) {
|
||||
final Uint8List data;
|
||||
final firstSubkey = subkeys.firstOrNull!.low;
|
||||
if (firstSubkey != defaultSubkey) {
|
||||
final maybeData = await record.get(forceRefresh: true);
|
||||
if (maybeData == null) {
|
||||
return null;
|
||||
}
|
||||
data = maybeData;
|
||||
} else {
|
||||
data = valueData.data;
|
||||
}
|
||||
final newState = decodeState(data);
|
||||
return newState;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user