mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-12-11 06:26:03 -05:00
refactor and cleanup in prep for profile changing
This commit is contained in:
parent
87bb1657c7
commit
56d65442f4
49 changed files with 967 additions and 655 deletions
|
|
@ -45,19 +45,6 @@ class AccountRepository {
|
|||
valueToJson: (val) => val?.toJson(),
|
||||
makeInitialValue: () => null);
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Fields
|
||||
|
||||
final TableDBValue<IList<LocalAccount>> _localAccounts;
|
||||
final TableDBValue<IList<UserLogin>> _userLogins;
|
||||
final TableDBValue<TypedKey?> _activeLocalAccount;
|
||||
final StreamController<AccountRepositoryChange> _streamController;
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Singleton initialization
|
||||
|
||||
static AccountRepository instance = AccountRepository._();
|
||||
|
||||
Future<void> init() async {
|
||||
await _localAccounts.get();
|
||||
await _userLogins.get();
|
||||
|
|
@ -71,12 +58,10 @@ class AccountRepository {
|
|||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Streams
|
||||
|
||||
/// Public Interface
|
||||
///
|
||||
Stream<AccountRepositoryChange> get stream => _streamController.stream;
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Selectors
|
||||
IList<LocalAccount> getLocalAccounts() => _localAccounts.value;
|
||||
TypedKey? getActiveLocalAccount() => _activeLocalAccount.value;
|
||||
IList<UserLogin> getUserLogins() => _userLogins.value;
|
||||
|
|
@ -116,7 +101,7 @@ class AccountRepository {
|
|||
return const AccountInfo(
|
||||
status: AccountInfoStatus.noAccount,
|
||||
active: false,
|
||||
activeAccountInfo: null);
|
||||
unlockedAccountInfo: null);
|
||||
}
|
||||
superIdentityRecordKey = activeLocalAccount;
|
||||
}
|
||||
|
|
@ -129,7 +114,7 @@ class AccountRepository {
|
|||
return AccountInfo(
|
||||
status: AccountInfoStatus.noAccount,
|
||||
active: active,
|
||||
activeAccountInfo: null);
|
||||
unlockedAccountInfo: null);
|
||||
}
|
||||
|
||||
// See if we've logged into this account or if it is locked
|
||||
|
|
@ -139,21 +124,18 @@ class AccountRepository {
|
|||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountLocked,
|
||||
active: active,
|
||||
activeAccountInfo: null);
|
||||
unlockedAccountInfo: null);
|
||||
}
|
||||
|
||||
// Got account, decrypted and decoded
|
||||
return AccountInfo(
|
||||
status: AccountInfoStatus.accountReady,
|
||||
active: active,
|
||||
activeAccountInfo:
|
||||
ActiveAccountInfo(localAccount: localAccount, userLogin: userLogin),
|
||||
unlockedAccountInfo:
|
||||
UnlockedAccountInfo(localAccount: localAccount, userLogin: userLogin),
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Mutators
|
||||
|
||||
/// Reorder accounts
|
||||
Future<void> reorderAccount(int oldIndex, int newIndex) async {
|
||||
final localAccounts = await _localAccounts.get();
|
||||
|
|
@ -168,15 +150,14 @@ class AccountRepository {
|
|||
/// Creates a new super identity, an identity instance, an account associated
|
||||
/// with the identity instance, stores the account in the identity key and
|
||||
/// then logs into that account with no password set at this time
|
||||
Future<SecretKey> createWithNewSuperIdentity(
|
||||
NewProfileSpec newProfileSpec) async {
|
||||
Future<SecretKey> createWithNewSuperIdentity(proto.Profile newProfile) async {
|
||||
log.debug('Creating super identity');
|
||||
final wsi = await WritableSuperIdentity.create();
|
||||
try {
|
||||
final localAccount = await _newLocalAccount(
|
||||
superIdentity: wsi.superIdentity,
|
||||
identitySecret: wsi.identitySecret,
|
||||
newProfileSpec: newProfileSpec);
|
||||
newProfile: newProfile);
|
||||
|
||||
// Log in the new account by default with no pin
|
||||
final ok = await login(
|
||||
|
|
@ -190,85 +171,18 @@ class AccountRepository {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a new Account associated with the current instance of the identity
|
||||
/// Adds a logged-out LocalAccount to track its existence on this device
|
||||
Future<LocalAccount> _newLocalAccount(
|
||||
{required SuperIdentity superIdentity,
|
||||
required SecretKey identitySecret,
|
||||
required NewProfileSpec newProfileSpec,
|
||||
EncryptionKeyType encryptionKeyType = EncryptionKeyType.none,
|
||||
String encryptionKey = ''}) async {
|
||||
log.debug('Creating new local account');
|
||||
Future<void> editAccountProfile(
|
||||
TypedKey superIdentityRecordKey, proto.Profile newProfile) async {
|
||||
log.debug('Editing profile for $superIdentityRecordKey');
|
||||
|
||||
final localAccounts = await _localAccounts.get();
|
||||
|
||||
// Add account with profile to DHT
|
||||
await superIdentity.currentInstance.addAccount(
|
||||
superRecordKey: superIdentity.recordKey,
|
||||
secretKey: identitySecret,
|
||||
accountKey: veilidChatAccountKey,
|
||||
createAccountCallback: (parent) async {
|
||||
// Make empty contact list
|
||||
log.debug('Creating contacts list');
|
||||
final contactList = await (await DHTShortArray.create(
|
||||
debugName: 'AccountRepository::_newLocalAccount::Contacts',
|
||||
parent: parent))
|
||||
.scope((r) async => r.recordPointer);
|
||||
|
||||
// Make empty contact invitation record list
|
||||
log.debug('Creating contact invitation records list');
|
||||
final contactInvitationRecords = await (await DHTShortArray.create(
|
||||
debugName:
|
||||
'AccountRepository::_newLocalAccount::ContactInvitations',
|
||||
parent: parent))
|
||||
.scope((r) async => r.recordPointer);
|
||||
|
||||
// Make empty chat record list
|
||||
log.debug('Creating chat records list');
|
||||
final chatRecords = await (await DHTShortArray.create(
|
||||
debugName: 'AccountRepository::_newLocalAccount::Chats',
|
||||
parent: parent))
|
||||
.scope((r) async => r.recordPointer);
|
||||
|
||||
// Make account object
|
||||
final account = proto.Account()
|
||||
..profile = (proto.Profile()
|
||||
..name = newProfileSpec.name
|
||||
..pronouns = newProfileSpec.pronouns)
|
||||
..contactList = contactList.toProto()
|
||||
..contactInvitationRecords = contactInvitationRecords.toProto()
|
||||
..chatList = chatRecords.toProto();
|
||||
return account.writeToBuffer();
|
||||
});
|
||||
|
||||
// Encrypt identitySecret with key
|
||||
final identitySecretBytes = await encryptionKeyType.encryptSecretToBytes(
|
||||
secret: identitySecret,
|
||||
cryptoKind: superIdentity.currentInstance.recordKey.kind,
|
||||
encryptionKey: encryptionKey,
|
||||
);
|
||||
|
||||
// Create local account object
|
||||
// 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
|
||||
final localAccount = LocalAccount(
|
||||
superIdentity: superIdentity,
|
||||
identitySecretBytes: identitySecretBytes,
|
||||
encryptionKeyType: encryptionKeyType,
|
||||
biometricsEnabled: false,
|
||||
hiddenAccount: false,
|
||||
name: newProfileSpec.name,
|
||||
);
|
||||
|
||||
// Add local account object to internal store
|
||||
final newLocalAccounts = localAccounts.add(localAccount);
|
||||
final newLocalAccounts = localAccounts.replaceFirstWhere(
|
||||
(x) => x.superIdentity.recordKey == superIdentityRecordKey,
|
||||
(localAccount) => localAccount!.copyWith(name: newProfile.name));
|
||||
|
||||
await _localAccounts.set(newLocalAccounts);
|
||||
_streamController.add(AccountRepositoryChange.localAccounts);
|
||||
|
||||
// Return local account object
|
||||
return localAccount;
|
||||
}
|
||||
|
||||
/// Remove an account and wipe the messages for this account from this device
|
||||
|
|
@ -310,6 +224,88 @@ class AccountRepository {
|
|||
_streamController.add(AccountRepositoryChange.activeLocalAccount);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Internal Implementation
|
||||
|
||||
/// Creates a new Account associated with the current instance of the identity
|
||||
/// Adds a logged-out LocalAccount to track its existence on this device
|
||||
Future<LocalAccount> _newLocalAccount(
|
||||
{required SuperIdentity superIdentity,
|
||||
required SecretKey identitySecret,
|
||||
required proto.Profile newProfile,
|
||||
EncryptionKeyType encryptionKeyType = EncryptionKeyType.none,
|
||||
String encryptionKey = ''}) async {
|
||||
log.debug('Creating new local account');
|
||||
|
||||
final localAccounts = await _localAccounts.get();
|
||||
|
||||
// Add account with profile to DHT
|
||||
await superIdentity.currentInstance.addAccount(
|
||||
superRecordKey: superIdentity.recordKey,
|
||||
secretKey: identitySecret,
|
||||
accountKey: veilidChatAccountKey,
|
||||
createAccountCallback: (parent) async {
|
||||
// Make empty contact list
|
||||
log.debug('Creating contacts list');
|
||||
final contactList = await (await DHTShortArray.create(
|
||||
debugName: 'AccountRepository::_newLocalAccount::Contacts',
|
||||
parent: parent))
|
||||
.scope((r) async => r.recordPointer);
|
||||
|
||||
// Make empty contact invitation record list
|
||||
log.debug('Creating contact invitation records list');
|
||||
final contactInvitationRecords = await (await DHTShortArray.create(
|
||||
debugName:
|
||||
'AccountRepository::_newLocalAccount::ContactInvitations',
|
||||
parent: parent))
|
||||
.scope((r) async => r.recordPointer);
|
||||
|
||||
// Make empty chat record list
|
||||
log.debug('Creating chat records list');
|
||||
final chatRecords = await (await DHTShortArray.create(
|
||||
debugName: 'AccountRepository::_newLocalAccount::Chats',
|
||||
parent: parent))
|
||||
.scope((r) async => r.recordPointer);
|
||||
|
||||
// Make account object
|
||||
final account = proto.Account()
|
||||
..profile = newProfile
|
||||
..contactList = contactList.toProto()
|
||||
..contactInvitationRecords = contactInvitationRecords.toProto()
|
||||
..chatList = chatRecords.toProto();
|
||||
return account.writeToBuffer();
|
||||
});
|
||||
|
||||
// Encrypt identitySecret with key
|
||||
final identitySecretBytes = await encryptionKeyType.encryptSecretToBytes(
|
||||
secret: identitySecret,
|
||||
cryptoKind: superIdentity.currentInstance.recordKey.kind,
|
||||
encryptionKey: encryptionKey,
|
||||
);
|
||||
|
||||
// Create local account object
|
||||
// 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
|
||||
final localAccount = LocalAccount(
|
||||
superIdentity: superIdentity,
|
||||
identitySecretBytes: identitySecretBytes,
|
||||
encryptionKeyType: encryptionKeyType,
|
||||
biometricsEnabled: false,
|
||||
hiddenAccount: false,
|
||||
name: newProfile.name,
|
||||
);
|
||||
|
||||
// Add local account object to internal store
|
||||
final newLocalAccounts = localAccounts.add(localAccount);
|
||||
|
||||
await _localAccounts.set(newLocalAccounts);
|
||||
_streamController.add(AccountRepositoryChange.localAccounts);
|
||||
|
||||
// Return local account object
|
||||
return localAccount;
|
||||
}
|
||||
|
||||
Future<bool> _decryptedLogin(
|
||||
SuperIdentity superIdentity, SecretKey identitySecret) async {
|
||||
// Verify identity secret works and return the valid cryptosystem
|
||||
|
|
@ -402,16 +398,13 @@ class AccountRepository {
|
|||
_streamController.add(AccountRepositoryChange.userLogins);
|
||||
}
|
||||
|
||||
Future<DHTRecord> openAccountRecord(UserLogin userLogin) async {
|
||||
final localAccount = fetchLocalAccount(userLogin.superIdentityRecordKey)!;
|
||||
//////////////////////////////////////////////////////////////
|
||||
/// Fields
|
||||
|
||||
// Record not yet open, do it
|
||||
final pool = DHTRecordPool.instance;
|
||||
final record = await pool.openRecordOwned(
|
||||
userLogin.accountRecordInfo.accountRecord,
|
||||
debugName: 'AccountRepository::openAccountRecord::AccountRecord',
|
||||
parent: localAccount.superIdentity.currentInstance.recordKey);
|
||||
static AccountRepository instance = AccountRepository._();
|
||||
|
||||
return record;
|
||||
}
|
||||
final TableDBValue<IList<LocalAccount>> _localAccounts;
|
||||
final TableDBValue<IList<UserLogin>> _userLogins;
|
||||
final TableDBValue<TypedKey?> _activeLocalAccount;
|
||||
final StreamController<AccountRepositoryChange> _streamController;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue