veilidchat/lib/providers/local_accounts.dart

142 lines
4.4 KiB
Dart
Raw Normal View History

2023-07-20 19:48:55 +00:00
import 'dart:async';
2023-07-19 04:09:57 +00:00
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
2023-07-20 19:48:55 +00:00
import 'package:riverpod_annotation/riverpod_annotation.dart';
2023-07-19 04:09:57 +00:00
import '../entities/entities.dart';
2023-08-05 05:00:46 +00:00
import '../log/loggy.dart';
2023-07-26 18:20:29 +00:00
import '../tools/tools.dart';
import '../veilid_support/veilid_support.dart';
2023-07-23 03:29:10 +00:00
import 'logins.dart';
2023-07-19 04:09:57 +00:00
2023-07-20 19:48:55 +00:00
part 'local_accounts.g.dart';
2023-07-19 04:09:57 +00:00
// Local account manager
2023-07-20 19:48:55 +00:00
@riverpod
2023-07-22 01:25:27 +00:00
class LocalAccounts extends _$LocalAccounts
with AsyncTableDBBacked<IList<LocalAccount>> {
//////////////////////////////////////////////////////////////
/// AsyncTableDBBacked
@override
2023-07-26 18:20:29 +00:00
String tableName() => 'local_account_manager';
2023-07-22 01:25:27 +00:00
@override
2023-07-26 18:20:29 +00:00
String tableKeyName() => 'local_accounts';
2023-07-22 01:25:27 +00:00
@override
2023-07-26 14:06:54 +00:00
IList<LocalAccount> valueFromJson(Object? obj) => obj != null
2023-07-22 01:25:27 +00:00
? IList<LocalAccount>.fromJson(
obj, genericFromJson(LocalAccount.fromJson))
: IList<LocalAccount>();
2023-07-26 14:06:54 +00:00
@override
Object? valueToJson(IList<LocalAccount> val) =>
val.toJson((la) => la.toJson());
2023-07-19 04:09:57 +00:00
2023-07-20 19:48:55 +00:00
/// Get all local account information
@override
2023-08-05 05:00:46 +00:00
FutureOr<IList<LocalAccount>> build() async {
try {
return await load();
} on Exception catch (e) {
log.error('Failed to load LocalAccounts table: $e');
return const IListConst([]);
}
}
2023-07-19 04:09:57 +00:00
2023-07-22 01:25:27 +00:00
//////////////////////////////////////////////////////////////
/// Mutators and Selectors
2023-07-19 04:09:57 +00:00
2023-07-23 03:29:10 +00:00
/// Reorder accounts
Future<void> reorderAccount(int oldIndex, int newIndex) async {
final localAccounts = state.requireValue;
2023-07-26 18:20:29 +00:00
final removedItem = Output<LocalAccount>();
2023-07-23 03:29:10 +00:00
final updated = localAccounts
.removeAt(oldIndex, removedItem)
.insert(newIndex, removedItem.value!);
await store(updated);
state = AsyncValue.data(updated);
2023-07-19 04:09:57 +00:00
}
2023-08-01 04:39:50 +00:00
/// Creates a new Account associated with master identity
/// Adds a logged-out LocalAccount to track its existence on this device
Future<LocalAccount> newLocalAccount(
{required IdentityMaster identityMaster,
required SecretKey identitySecret,
required String name,
required String title,
EncryptionKeyType encryptionKeyType = EncryptionKeyType.none,
String encryptionKey = ''}) async {
final localAccounts = state.requireValue;
/////// Add account with profile to DHT
await identityMaster.newAccount(
identitySecret: identitySecret,
name: name,
title: title,
);
// Encrypt identitySecret with key
2023-08-03 01:09:28 +00:00
final identitySecretBytes = await encryptSecretToBytes(
secret: identitySecret,
2023-08-01 04:39:50 +00:00
cryptoKind: identityMaster.identityRecordKey.kind,
encryptionKey: encryptionKey,
encryptionKeyType: encryptionKeyType);
2023-07-19 04:09:57 +00:00
// Create local account object
2023-08-01 04:39:50 +00:00
// Does not contain the account key or its secret
// as that is not to be persisted, and only pulled from the identity key
// and optionally decrypted with the unlock password
2023-07-19 04:09:57 +00:00
final localAccount = LocalAccount(
identityMaster: identityMaster,
2023-08-01 04:39:50 +00:00
identitySecretBytes: identitySecretBytes,
2023-07-19 04:09:57 +00:00
encryptionKeyType: encryptionKeyType,
biometricsEnabled: false,
hiddenAccount: false,
2023-08-01 04:39:50 +00:00
name: name,
2023-07-19 04:09:57 +00:00
);
// Add local account object to internal store
2023-07-20 19:48:55 +00:00
final newLocalAccounts = localAccounts.add(localAccount);
2023-07-22 01:25:27 +00:00
await store(newLocalAccounts);
2023-07-20 19:48:55 +00:00
state = AsyncValue.data(newLocalAccounts);
2023-07-19 04:09:57 +00:00
// Return local account object
return localAccount;
}
2023-07-20 19:48:55 +00:00
2023-07-23 03:29:10 +00:00
/// Remove an account and wipe the messages for this account from this device
2023-08-01 04:39:50 +00:00
Future<bool> deleteLocalAccount(TypedKey accountMasterRecordKey) async {
2023-07-23 03:29:10 +00:00
final logins = ref.read(loginsProvider.notifier);
await logins.logout(accountMasterRecordKey);
final localAccounts = state.requireValue;
final updated = localAccounts.removeWhere(
(la) => la.identityMaster.masterRecordKey == accountMasterRecordKey);
await store(updated);
state = AsyncValue.data(updated);
2023-07-26 21:42:11 +00:00
// TO DO: wipe messages
2023-07-23 03:29:10 +00:00
return true;
}
2023-07-20 19:48:55 +00:00
/// Import an account from another VeilidChat instance
/// Recover an account with the master identity secret
2023-08-01 04:39:50 +00:00
/// Delete an account from all devices
2023-07-19 04:09:57 +00:00
}
2023-07-29 00:36:05 +00:00
@riverpod
Future<LocalAccount?> fetchLocalAccount(FetchLocalAccountRef ref,
{required TypedKey accountMasterRecordKey}) async {
final localAccounts = await ref.watch(localAccountsProvider.future);
try {
return localAccounts.firstWhere(
(e) => e.identityMaster.masterRecordKey == accountMasterRecordKey);
} on Exception catch (e) {
if (e is StateError) {
return null;
}
rethrow;
}
}