mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-01-11 15:49:29 -05:00
account work
This commit is contained in:
parent
b7236befd1
commit
b502bc20a7
@ -5,15 +5,23 @@
|
|||||||
"app_bar": {
|
"app_bar": {
|
||||||
"settings_tooltip": "Settings"
|
"settings_tooltip": "Settings"
|
||||||
},
|
},
|
||||||
|
"account": {
|
||||||
|
"form_name": "Name",
|
||||||
|
"form_title": "Title (optional)",
|
||||||
|
"form_lock_type": "Lock Type",
|
||||||
|
"lock_type_none": "none",
|
||||||
|
"lock_type_pin": "pin",
|
||||||
|
"lock_type_password": "password"
|
||||||
|
},
|
||||||
"new_account_page": {
|
"new_account_page": {
|
||||||
"titlebar": "Create a new account",
|
"titlebar": "Create a new account",
|
||||||
"header": "Account Profile",
|
"header": "Account Profile",
|
||||||
"form_name": "Name",
|
|
||||||
"form_title": "Title (optional)",
|
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"instructions": "This information will be shared with the people you invite to connect with you on VeilidChat."
|
"instructions": "This information will be shared with the people you invite to connect with you on VeilidChat.",
|
||||||
|
"error": "Account creation error"
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
|
"ok": "Ok",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"change_language": "Change Language"
|
"change_language": "Change Language"
|
||||||
},
|
},
|
||||||
|
@ -15,12 +15,12 @@ class AccountRecordInfo with _$AccountRecordInfo {
|
|||||||
required KeyPair owner,
|
required KeyPair owner,
|
||||||
}) = _AccountRecordInfo;
|
}) = _AccountRecordInfo;
|
||||||
|
|
||||||
factory AccountRecordInfo.fromJson(Map<String, dynamic> json) =>
|
factory AccountRecordInfo.fromJson(dynamic json) =>
|
||||||
_$AccountRecordInfoFromJson(json);
|
_$AccountRecordInfoFromJson(json as Map<String, dynamic>);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identity Key points to accounts associated with this identity
|
// Identity Key points to accounts associated with this identity
|
||||||
// accounts field has a map of service name or uuid to account key pairs
|
// accounts field has a map of bundle id or uuid to account key pairs
|
||||||
// DHT Schema: DFLT(1)
|
// DHT Schema: DFLT(1)
|
||||||
// DHT Key (Private): identityRecordKey
|
// DHT Key (Private): identityRecordKey
|
||||||
// DHT Owner Key: identityPublicKey
|
// DHT Owner Key: identityPublicKey
|
||||||
@ -32,8 +32,8 @@ class Identity with _$Identity {
|
|||||||
required IMap<String, ISet<AccountRecordInfo>> accountRecords,
|
required IMap<String, ISet<AccountRecordInfo>> accountRecords,
|
||||||
}) = _Identity;
|
}) = _Identity;
|
||||||
|
|
||||||
factory Identity.fromJson(Map<String, dynamic> json) =>
|
factory Identity.fromJson(dynamic json) =>
|
||||||
_$IdentityFromJson(json);
|
_$IdentityFromJson(json as Map<String, dynamic>);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identity Master key structure for created account
|
// Identity Master key structure for created account
|
||||||
@ -66,8 +66,8 @@ class IdentityMaster with _$IdentityMaster {
|
|||||||
// Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
// Signature of masterRecordKey and masterPublicKey by identityPublicKey
|
||||||
required Signature masterSignature}) = _IdentityMaster;
|
required Signature masterSignature}) = _IdentityMaster;
|
||||||
|
|
||||||
factory IdentityMaster.fromJson(Map<String, dynamic> json) =>
|
factory IdentityMaster.fromJson(dynamic json) =>
|
||||||
_$IdentityMasterFromJson(json);
|
_$IdentityMasterFromJson(json as Map<String, dynamic>);
|
||||||
}
|
}
|
||||||
|
|
||||||
extension IdentityMasterExtension on IdentityMaster {
|
extension IdentityMasterExtension on IdentityMaster {
|
||||||
@ -79,15 +79,3 @@ extension IdentityMasterExtension on IdentityMaster {
|
|||||||
return KeyPair(key: masterPublicKey, secret: secret);
|
return KeyPair(key: masterPublicKey, secret: secret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identity Master with secrets
|
|
||||||
// Not freezed because we never persist this class in its entirety
|
|
||||||
class IdentityMasterWithSecrets {
|
|
||||||
IdentityMaster identityMaster;
|
|
||||||
SecretKey masterSecret;
|
|
||||||
SecretKey identitySecret;
|
|
||||||
IdentityMasterWithSecrets(
|
|
||||||
{required this.identityMaster,
|
|
||||||
required this.masterSecret,
|
|
||||||
required this.identitySecret});
|
|
||||||
}
|
|
||||||
|
@ -24,9 +24,7 @@ _$_Identity _$$_IdentityFromJson(Map<String, dynamic> json) => _$_Identity(
|
|||||||
json['account_records'] as Map<String, dynamic>,
|
json['account_records'] as Map<String, dynamic>,
|
||||||
(value) => value as String,
|
(value) => value as String,
|
||||||
(value) => ISet<AccountRecordInfo>.fromJson(
|
(value) => ISet<AccountRecordInfo>.fromJson(
|
||||||
value,
|
value, (value) => AccountRecordInfo.fromJson(value))),
|
||||||
(value) =>
|
|
||||||
AccountRecordInfo.fromJson(value as Map<String, dynamic>))),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$$_IdentityToJson(_$_Identity instance) =>
|
Map<String, dynamic> _$$_IdentityToJson(_$_Identity instance) =>
|
||||||
|
@ -20,8 +20,8 @@ enum EncryptionKeyType {
|
|||||||
password;
|
password;
|
||||||
|
|
||||||
String toJson() => name.toPascalCase();
|
String toJson() => name.toPascalCase();
|
||||||
factory EncryptionKeyType.fromJson(String j) =>
|
factory EncryptionKeyType.fromJson(dynamic j) =>
|
||||||
EncryptionKeyType.values.byName(j.toCamelCase());
|
EncryptionKeyType.values.byName((j as String).toCamelCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local Accounts are stored in a table locally and not backed by a DHT key
|
// Local Accounts are stored in a table locally and not backed by a DHT key
|
||||||
@ -49,6 +49,6 @@ class LocalAccount with _$LocalAccount {
|
|||||||
required bool hiddenAccount,
|
required bool hiddenAccount,
|
||||||
}) = _LocalAccount;
|
}) = _LocalAccount;
|
||||||
|
|
||||||
factory LocalAccount.fromJson(Map<String, dynamic> json) =>
|
factory LocalAccount.fromJson(dynamic json) =>
|
||||||
_$LocalAccountFromJson(json);
|
_$LocalAccountFromJson(json as Map<String, dynamic>);
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,13 @@ part of 'local_account.dart';
|
|||||||
|
|
||||||
_$_LocalAccount _$$_LocalAccountFromJson(Map<String, dynamic> json) =>
|
_$_LocalAccount _$$_LocalAccountFromJson(Map<String, dynamic> json) =>
|
||||||
_$_LocalAccount(
|
_$_LocalAccount(
|
||||||
identityMaster: IdentityMaster.fromJson(
|
identityMaster: IdentityMaster.fromJson(json['identity_master']),
|
||||||
json['identity_master'] as Map<String, dynamic>),
|
|
||||||
identitySecretKeyBytes: const Uint8ListJsonConverter()
|
identitySecretKeyBytes: const Uint8ListJsonConverter()
|
||||||
.fromJson(json['identity_secret_key_bytes'] as String),
|
.fromJson(json['identity_secret_key_bytes'] as String),
|
||||||
identitySecretSaltBytes: const Uint8ListJsonConverter()
|
identitySecretSaltBytes: const Uint8ListJsonConverter()
|
||||||
.fromJson(json['identity_secret_salt_bytes'] as String),
|
.fromJson(json['identity_secret_salt_bytes'] as String),
|
||||||
encryptionKeyType:
|
encryptionKeyType:
|
||||||
EncryptionKeyType.fromJson(json['encryption_key_type'] as String),
|
EncryptionKeyType.fromJson(json['encryption_key_type']),
|
||||||
biometricsEnabled: json['biometrics_enabled'] as bool,
|
biometricsEnabled: json['biometrics_enabled'] as bool,
|
||||||
hiddenAccount: json['hidden_account'] as bool,
|
hiddenAccount: json['hidden_account'] as bool,
|
||||||
);
|
);
|
||||||
|
@ -12,8 +12,8 @@ enum DarkModePreference {
|
|||||||
dark;
|
dark;
|
||||||
|
|
||||||
String toJson() => name.toPascalCase();
|
String toJson() => name.toPascalCase();
|
||||||
factory DarkModePreference.fromJson(String j) =>
|
factory DarkModePreference.fromJson(dynamic j) =>
|
||||||
DarkModePreference.values.byName(j.toCamelCase());
|
DarkModePreference.values.byName((j as String).toCamelCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock preference changes how frequently the messenger locks its
|
// Lock preference changes how frequently the messenger locks its
|
||||||
@ -26,8 +26,8 @@ class LockPreference with _$LockPreference {
|
|||||||
required bool lockWithSystemLock,
|
required bool lockWithSystemLock,
|
||||||
}) = _LockPreference;
|
}) = _LockPreference;
|
||||||
|
|
||||||
factory LockPreference.fromJson(Map<String, dynamic> json) =>
|
factory LockPreference.fromJson(dynamic json) =>
|
||||||
_$LockPreferenceFromJson(json);
|
_$LockPreferenceFromJson(json as Map<String, dynamic>);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Theme supports multiple color variants based on 'Radix'
|
// Theme supports multiple color variants based on 'Radix'
|
||||||
@ -62,8 +62,8 @@ enum ColorPreference {
|
|||||||
yellow;
|
yellow;
|
||||||
|
|
||||||
String toJson() => name.toPascalCase();
|
String toJson() => name.toPascalCase();
|
||||||
factory ColorPreference.fromJson(String j) =>
|
factory ColorPreference.fromJson(dynamic j) =>
|
||||||
ColorPreference.values.byName(j.toCamelCase());
|
ColorPreference.values.byName((j as String).toCamelCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Theme supports multiple translations
|
// Theme supports multiple translations
|
||||||
@ -71,8 +71,8 @@ enum LanguagePreference {
|
|||||||
englishUS;
|
englishUS;
|
||||||
|
|
||||||
String toJson() => name.toPascalCase();
|
String toJson() => name.toPascalCase();
|
||||||
factory LanguagePreference.fromJson(String j) =>
|
factory LanguagePreference.fromJson(dynamic j) =>
|
||||||
LanguagePreference.values.byName(j.toCamelCase());
|
LanguagePreference.values.byName((j as String).toCamelCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preferences are stored in a table locally and globally affect all
|
// Preferences are stored in a table locally and globally affect all
|
||||||
@ -87,6 +87,6 @@ class Preferences with _$Preferences {
|
|||||||
required LockPreference locking,
|
required LockPreference locking,
|
||||||
}) = _Preferences;
|
}) = _Preferences;
|
||||||
|
|
||||||
factory Preferences.fromJson(Map<String, dynamic> json) =>
|
factory Preferences.fromJson(dynamic json) =>
|
||||||
_$PreferencesFromJson(json);
|
_$PreferencesFromJson(json as Map<String, dynamic>);
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,11 @@ Map<String, dynamic> _$$_LockPreferenceToJson(_$_LockPreference instance) =>
|
|||||||
|
|
||||||
_$_Preferences _$$_PreferencesFromJson(Map<String, dynamic> json) =>
|
_$_Preferences _$$_PreferencesFromJson(Map<String, dynamic> json) =>
|
||||||
_$_Preferences(
|
_$_Preferences(
|
||||||
darkMode: DarkModePreference.fromJson(json['dark_mode'] as String),
|
darkMode: DarkModePreference.fromJson(json['dark_mode']),
|
||||||
themeColor: ColorPreference.fromJson(json['theme_color'] as String),
|
themeColor: ColorPreference.fromJson(json['theme_color']),
|
||||||
language: LanguagePreference.fromJson(json['language'] as String),
|
language: LanguagePreference.fromJson(json['language']),
|
||||||
displayScale: json['display_scale'] as int,
|
displayScale: json['display_scale'] as int,
|
||||||
locking: LockPreference.fromJson(json['locking'] as Map<String, dynamic>),
|
locking: LockPreference.fromJson(json['locking']),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$$_PreferencesToJson(_$_Preferences instance) =>
|
Map<String, dynamic> _$$_PreferencesToJson(_$_Preferences instance) =>
|
||||||
|
@ -1038,7 +1038,7 @@ class Profile extends $pb.GeneratedMessage {
|
|||||||
..aOS(2, _omitFieldNames ? '' : 'title')
|
..aOS(2, _omitFieldNames ? '' : 'title')
|
||||||
..aOS(3, _omitFieldNames ? '' : 'status')
|
..aOS(3, _omitFieldNames ? '' : 'status')
|
||||||
..e<Availability>(4, _omitFieldNames ? '' : 'availability', $pb.PbFieldType.OE, defaultOrMaker: Availability.AVAILABILITY_UNSPECIFIED, valueOf: Availability.valueOf, enumValues: Availability.values)
|
..e<Availability>(4, _omitFieldNames ? '' : 'availability', $pb.PbFieldType.OE, defaultOrMaker: Availability.AVAILABILITY_UNSPECIFIED, valueOf: Availability.valueOf, enumValues: Availability.values)
|
||||||
..aOM<TypedKey>(5, _omitFieldNames ? '' : 'icon', subBuilder: TypedKey.create)
|
..aOM<TypedKey>(5, _omitFieldNames ? '' : 'avatar', subBuilder: TypedKey.create)
|
||||||
..hasRequiredFields = false
|
..hasRequiredFields = false
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -1100,15 +1100,15 @@ class Profile extends $pb.GeneratedMessage {
|
|||||||
void clearAvailability() => clearField(4);
|
void clearAvailability() => clearField(4);
|
||||||
|
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
TypedKey get icon => $_getN(4);
|
TypedKey get avatar => $_getN(4);
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
set icon(TypedKey v) { setField(5, v); }
|
set avatar(TypedKey v) { setField(5, v); }
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
$core.bool hasIcon() => $_has(4);
|
$core.bool hasAvatar() => $_has(4);
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
void clearIcon() => clearField(5);
|
void clearAvatar() => clearField(5);
|
||||||
@$pb.TagNumber(5)
|
@$pb.TagNumber(5)
|
||||||
TypedKey ensureIcon() => $_ensure(4);
|
TypedKey ensureAvatar() => $_ensure(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Account extends $pb.GeneratedMessage {
|
class Account extends $pb.GeneratedMessage {
|
||||||
|
@ -279,7 +279,10 @@ const Profile$json = {
|
|||||||
{'1': 'title', '3': 2, '4': 1, '5': 9, '10': 'title'},
|
{'1': 'title', '3': 2, '4': 1, '5': 9, '10': 'title'},
|
||||||
{'1': 'status', '3': 3, '4': 1, '5': 9, '10': 'status'},
|
{'1': 'status', '3': 3, '4': 1, '5': 9, '10': 'status'},
|
||||||
{'1': 'availability', '3': 4, '4': 1, '5': 14, '6': '.Availability', '10': 'availability'},
|
{'1': 'availability', '3': 4, '4': 1, '5': 14, '6': '.Availability', '10': 'availability'},
|
||||||
{'1': 'icon', '3': 5, '4': 1, '5': 11, '6': '.TypedKey', '10': 'icon'},
|
{'1': 'avatar', '3': 5, '4': 1, '5': 11, '6': '.TypedKey', '9': 0, '10': 'avatar', '17': true},
|
||||||
|
],
|
||||||
|
'8': [
|
||||||
|
{'1': '_avatar'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -287,7 +290,8 @@ const Profile$json = {
|
|||||||
final $typed_data.Uint8List profileDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List profileDescriptor = $convert.base64Decode(
|
||||||
'CgdQcm9maWxlEhIKBG5hbWUYASABKAlSBG5hbWUSFAoFdGl0bGUYAiABKAlSBXRpdGxlEhYKBn'
|
'CgdQcm9maWxlEhIKBG5hbWUYASABKAlSBG5hbWUSFAoFdGl0bGUYAiABKAlSBXRpdGxlEhYKBn'
|
||||||
'N0YXR1cxgDIAEoCVIGc3RhdHVzEjEKDGF2YWlsYWJpbGl0eRgEIAEoDjINLkF2YWlsYWJpbGl0'
|
'N0YXR1cxgDIAEoCVIGc3RhdHVzEjEKDGF2YWlsYWJpbGl0eRgEIAEoDjINLkF2YWlsYWJpbGl0'
|
||||||
'eVIMYXZhaWxhYmlsaXR5Eh0KBGljb24YBSABKAsyCS5UeXBlZEtleVIEaWNvbg==');
|
'eVIMYXZhaWxhYmlsaXR5EiYKBmF2YXRhchgFIAEoCzIJLlR5cGVkS2V5SABSBmF2YXRhcogBAU'
|
||||||
|
'IJCgdfYXZhdGFy');
|
||||||
|
|
||||||
@$core.Deprecated('Use accountDescriptor instead')
|
@$core.Deprecated('Use accountDescriptor instead')
|
||||||
const Account$json = {
|
const Account$json = {
|
||||||
|
@ -19,8 +19,8 @@ class UserLogin with _$UserLogin {
|
|||||||
required Timestamp lastActive,
|
required Timestamp lastActive,
|
||||||
}) = _UserLogin;
|
}) = _UserLogin;
|
||||||
|
|
||||||
factory UserLogin.fromJson(Map<String, dynamic> json) =>
|
factory UserLogin.fromJson(dynamic json) =>
|
||||||
_$UserLoginFromJson(json);
|
_$UserLoginFromJson(json as Map<String, dynamic>);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents a set of user logins
|
// Represents a set of user logins
|
||||||
@ -37,6 +37,6 @@ class ActiveLogins with _$ActiveLogins {
|
|||||||
factory ActiveLogins.empty() =>
|
factory ActiveLogins.empty() =>
|
||||||
const ActiveLogins(userLogins: IListConst([]));
|
const ActiveLogins(userLogins: IListConst([]));
|
||||||
|
|
||||||
factory ActiveLogins.fromJson(Map<String, dynamic> json) =>
|
factory ActiveLogins.fromJson(dynamic json) =>
|
||||||
_$ActiveLoginsFromJson(json);
|
_$ActiveLoginsFromJson(json as Map<String, dynamic>);
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,8 @@ Map<String, dynamic> _$$_UserLoginToJson(_$_UserLogin instance) =>
|
|||||||
|
|
||||||
_$_ActiveLogins _$$_ActiveLoginsFromJson(Map<String, dynamic> json) =>
|
_$_ActiveLogins _$$_ActiveLoginsFromJson(Map<String, dynamic> json) =>
|
||||||
_$_ActiveLogins(
|
_$_ActiveLogins(
|
||||||
userLogins: IList<UserLogin>.fromJson(json['user_logins'],
|
userLogins: IList<UserLogin>.fromJson(
|
||||||
(value) => UserLogin.fromJson(value as Map<String, dynamic>)),
|
json['user_logins'], (value) => UserLogin.fromJson(value)),
|
||||||
activeUserLogin: json['active_user_login'] == null
|
activeUserLogin: json['active_user_login'] == null
|
||||||
? null
|
? null
|
||||||
: Typed<FixedEncodedString43>.fromJson(json['active_user_login']),
|
: Typed<FixedEncodedString43>.fromJson(json['active_user_login']),
|
||||||
|
@ -196,7 +196,7 @@ message Contact {
|
|||||||
bool show_availability = 6;
|
bool show_availability = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contact availability as specified by the
|
// Contact availability
|
||||||
enum Availability {
|
enum Availability {
|
||||||
AVAILABILITY_UNSPECIFIED = 0;
|
AVAILABILITY_UNSPECIFIED = 0;
|
||||||
AVAILABILITY_OFFLINE = 1;
|
AVAILABILITY_OFFLINE = 1;
|
||||||
@ -222,8 +222,8 @@ message Profile {
|
|||||||
string status = 3;
|
string status = 3;
|
||||||
// Availability
|
// Availability
|
||||||
Availability availability = 4;
|
Availability availability = 4;
|
||||||
// Icon DHTData
|
// Avatar DHTData
|
||||||
TypedKey icon = 5;
|
optional TypedKey avatar = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A record of an individual account
|
// A record of an individual account
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||||
|
import 'package:quickalert/quickalert.dart';
|
||||||
|
|
||||||
import '../components/default_app_bar.dart';
|
import '../components/default_app_bar.dart';
|
||||||
|
import '../entities/proto.dart' as proto;
|
||||||
import '../providers/local_accounts.dart';
|
import '../providers/local_accounts.dart';
|
||||||
|
import '../providers/logins.dart';
|
||||||
import '../tools/tools.dart';
|
import '../tools/tools.dart';
|
||||||
|
import '../veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
class NewAccountPage extends ConsumerStatefulWidget {
|
class NewAccountPage extends ConsumerStatefulWidget {
|
||||||
const NewAccountPage({super.key});
|
const NewAccountPage({super.key});
|
||||||
@ -24,6 +26,34 @@ class NewAccountPage extends ConsumerStatefulWidget {
|
|||||||
class NewAccountPageState extends ConsumerState<NewAccountPage> {
|
class NewAccountPageState extends ConsumerState<NewAccountPage> {
|
||||||
final _formKey = GlobalKey<FormBuilderState>();
|
final _formKey = GlobalKey<FormBuilderState>();
|
||||||
late bool isInAsyncCall = false;
|
late bool isInAsyncCall = false;
|
||||||
|
static const String formFieldName = "name";
|
||||||
|
static const String formFieldTitle = "title";
|
||||||
|
|
||||||
|
Future<void> createAccount() async {
|
||||||
|
final imws = await newIdentityMaster();
|
||||||
|
try {
|
||||||
|
final localAccounts = ref.read(localAccountsProvider.notifier);
|
||||||
|
final logins = ref.read(loginsProvider.notifier);
|
||||||
|
|
||||||
|
final profile = proto.Profile();
|
||||||
|
profile.name = _formKey.currentState!.fields[formFieldName]!.value;
|
||||||
|
profile.title = _formKey.currentState!.fields[formFieldTitle]!.value;
|
||||||
|
final account = proto.Account();
|
||||||
|
account.profile = profile;
|
||||||
|
final localAccount = await localAccounts.newAccount(
|
||||||
|
identityMaster: imws.identityMaster,
|
||||||
|
identitySecret: imws.identitySecret,
|
||||||
|
account: account);
|
||||||
|
|
||||||
|
// Log in the new account by default with no pin
|
||||||
|
final ok = await logins
|
||||||
|
.loginWithNone(localAccount.identityMaster.masterRecordKey);
|
||||||
|
assert(ok == true);
|
||||||
|
} catch (e) {
|
||||||
|
await imws.delete();
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _newAccountForm(BuildContext context,
|
Widget _newAccountForm(BuildContext context,
|
||||||
{required Future<void> Function(GlobalKey<FormBuilderState>) onSubmit}) {
|
{required Future<void> Function(GlobalKey<FormBuilderState>) onSubmit}) {
|
||||||
@ -36,9 +66,9 @@ class NewAccountPageState extends ConsumerState<NewAccountPage> {
|
|||||||
.paddingSymmetric(vertical: 16),
|
.paddingSymmetric(vertical: 16),
|
||||||
FormBuilderTextField(
|
FormBuilderTextField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
name: 'name',
|
name: formFieldName,
|
||||||
decoration: InputDecoration(
|
decoration:
|
||||||
hintText: translate("new_account_page.form_name")),
|
InputDecoration(hintText: translate("account.form_name")),
|
||||||
maxLength: 64,
|
maxLength: 64,
|
||||||
// The validator receives the text that the user has entered.
|
// The validator receives the text that the user has entered.
|
||||||
validator: FormBuilderValidators.compose([
|
validator: FormBuilderValidators.compose([
|
||||||
@ -46,10 +76,10 @@ class NewAccountPageState extends ConsumerState<NewAccountPage> {
|
|||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
FormBuilderTextField(
|
FormBuilderTextField(
|
||||||
name: 'title',
|
name: formFieldTitle,
|
||||||
maxLength: 64,
|
maxLength: 64,
|
||||||
decoration: InputDecoration(
|
decoration:
|
||||||
hintText: translate("new_account_page.form_title")),
|
InputDecoration(hintText: translate("account.form_title")),
|
||||||
),
|
),
|
||||||
Row(children: [
|
Row(children: [
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
@ -85,7 +115,8 @@ class NewAccountPageState extends ConsumerState<NewAccountPage> {
|
|||||||
enableTitleBar(true);
|
enableTitleBar(true);
|
||||||
portraitOnly();
|
portraitOnly();
|
||||||
|
|
||||||
final localAccounts = ref.watch(localAccountsProvider);
|
// final localAccountsData = ref.watch(localAccountsProvider);
|
||||||
|
final displayModalHUD = isInAsyncCall; // || !localAccountsData.hasValue;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
// resizeToAvoidBottomInset: false,
|
// resizeToAvoidBottomInset: false,
|
||||||
@ -96,9 +127,21 @@ class NewAccountPageState extends ConsumerState<NewAccountPage> {
|
|||||||
onSubmit: (formKey) async {
|
onSubmit: (formKey) async {
|
||||||
debugPrint(_formKey.currentState?.value.toString());
|
debugPrint(_formKey.currentState?.value.toString());
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
await Future.delayed(Duration(seconds: 5));
|
try {
|
||||||
|
await createAccount();
|
||||||
|
} catch (e) {
|
||||||
|
QuickAlert.show(
|
||||||
|
context: context,
|
||||||
|
type: QuickAlertType.error,
|
||||||
|
title: translate("new_account_page.error"),
|
||||||
|
text: 'Exception: ${e.toString()}',
|
||||||
|
//backgroundColor: Colors.black,
|
||||||
|
//titleColor: Colors.white,
|
||||||
|
//textColor: Colors.white,
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
).paddingSymmetric(horizontal: 24, vertical: 8),
|
).paddingSymmetric(horizontal: 24, vertical: 8),
|
||||||
).withModalHUD(context, isInAsyncCall);
|
).withModalHUD(context, displayModalHUD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,11 @@ class LocalAccounts extends _$LocalAccounts
|
|||||||
|
|
||||||
/// Creates a new account associated with master identity
|
/// Creates a new account associated with master identity
|
||||||
Future<LocalAccount> newAccount(
|
Future<LocalAccount> newAccount(
|
||||||
IdentityMaster identityMaster,
|
{required IdentityMaster identityMaster,
|
||||||
SecretKey identitySecret,
|
required SecretKey identitySecret,
|
||||||
EncryptionKeyType encryptionKeyType,
|
EncryptionKeyType encryptionKeyType = EncryptionKeyType.none,
|
||||||
String encryptionKey,
|
String encryptionKey = "",
|
||||||
proto.Account account) async {
|
required proto.Account account}) async {
|
||||||
final veilid = await eventualVeilid.future;
|
final veilid = await eventualVeilid.future;
|
||||||
final localAccounts = state.requireValue;
|
final localAccounts = state.requireValue;
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ class LocalAccounts extends _$LocalAccounts
|
|||||||
await identityRec.eventualUpdateJson(Identity.fromJson,
|
await identityRec.eventualUpdateJson(Identity.fromJson,
|
||||||
(oldIdentity) async {
|
(oldIdentity) async {
|
||||||
final accountRecords = IMapOfSets.from(oldIdentity.accountRecords)
|
final accountRecords = IMapOfSets.from(oldIdentity.accountRecords)
|
||||||
.add("VeilidChat", newAccountRecordInfo)
|
.add("com.veilid.veilidchat", newAccountRecordInfo)
|
||||||
.asIMap();
|
.asIMap();
|
||||||
return oldIdentity.copyWith(accountRecords: accountRecords);
|
return oldIdentity.copyWith(accountRecords: accountRecords);
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@ part of 'local_accounts.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$localAccountsHash() => r'1faa6b22284a402e4f47b2629e54a39ffda9a4ad';
|
String _$localAccountsHash() => r'37ed2ab40b6ed9063c7d1d00f067b7006c9a7670';
|
||||||
|
|
||||||
/// See also [LocalAccounts].
|
/// See also [LocalAccounts].
|
||||||
@ProviderFor(LocalAccounts)
|
@ProviderFor(LocalAccounts)
|
||||||
|
@ -152,11 +152,6 @@ class DHTRecord {
|
|||||||
// Get existing identity key
|
// Get existing identity key
|
||||||
ValueData? valueData;
|
ValueData? valueData;
|
||||||
do {
|
do {
|
||||||
// Ensure it exists already
|
|
||||||
if (valueData == null) {
|
|
||||||
throw const FormatException("value does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new data
|
// Set the new data
|
||||||
valueData =
|
valueData =
|
||||||
await _dhtctx.setDHTValue(_recordDescriptor.key, subkey, newValue);
|
await _dhtctx.setDHTValue(_recordDescriptor.key, subkey, newValue);
|
||||||
|
@ -41,8 +41,10 @@ class DHTRecordCryptoPrivate implements DHTRecordCrypto {
|
|||||||
// generate nonce
|
// generate nonce
|
||||||
final nonce = await _cryptoSystem.randomNonce();
|
final nonce = await _cryptoSystem.randomNonce();
|
||||||
// crypt and append nonce
|
// crypt and append nonce
|
||||||
return (await _cryptoSystem.cryptNoAuth(data, nonce, _secretKey))
|
var b = BytesBuilder();
|
||||||
..addAll(nonce.decode());
|
b.add(await _cryptoSystem.cryptNoAuth(data, nonce, _secretKey));
|
||||||
|
b.add(nonce.decode());
|
||||||
|
return b.toBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -1,13 +1,35 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||||
import 'package:veilid/veilid.dart';
|
import 'package:veilid/veilid.dart';
|
||||||
|
|
||||||
import '../entities/identity.dart';
|
import '../entities/identity.dart';
|
||||||
import 'veilid_support.dart';
|
import 'veilid_support.dart';
|
||||||
|
|
||||||
|
// Identity Master with secrets
|
||||||
|
// Not freezed because we never persist this class in its entirety
|
||||||
|
class IdentityMasterWithSecrets {
|
||||||
|
IdentityMaster identityMaster;
|
||||||
|
SecretKey masterSecret;
|
||||||
|
SecretKey identitySecret;
|
||||||
|
IdentityMasterWithSecrets(
|
||||||
|
{required this.identityMaster,
|
||||||
|
required this.masterSecret,
|
||||||
|
required this.identitySecret});
|
||||||
|
|
||||||
|
Future<void> delete() async {
|
||||||
|
final veilid = await eventualVeilid.future;
|
||||||
|
final dhtctx = (await veilid.routingContext())
|
||||||
|
.withPrivacy()
|
||||||
|
.withSequencing(Sequencing.ensureOrdered);
|
||||||
|
await dhtctx.deleteDHTRecord(identityMaster.masterRecordKey);
|
||||||
|
await dhtctx.deleteDHTRecord(identityMaster.identityRecordKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new master identity and returns it with its secrets
|
/// Creates a new master identity and returns it with its secrets
|
||||||
Future<IdentityMasterWithSecrets> newIdentityMaster() async {
|
Future<IdentityMasterWithSecrets> newIdentityMaster() async {
|
||||||
final veilid = await eventualVeilid.future;
|
final veilid = await eventualVeilid.future;
|
||||||
final crypto = await veilid.bestCryptoSystem();
|
|
||||||
final dhtctx = (await veilid.routingContext())
|
final dhtctx = (await veilid.routingContext())
|
||||||
.withPrivacy()
|
.withPrivacy()
|
||||||
.withSequencing(Sequencing.ensureOrdered);
|
.withSequencing(Sequencing.ensureOrdered);
|
||||||
@ -20,18 +42,23 @@ Future<IdentityMasterWithSecrets> newIdentityMaster() async {
|
|||||||
// Make IdentityMaster
|
// Make IdentityMaster
|
||||||
final masterRecordKey = masterRec.key();
|
final masterRecordKey = masterRec.key();
|
||||||
final masterOwner = masterRec.ownerKeyPair()!;
|
final masterOwner = masterRec.ownerKeyPair()!;
|
||||||
final masterSigBuf = masterRecordKey.decode()
|
final masterSigBuf = BytesBuilder();
|
||||||
..addAll(masterOwner.key.decode());
|
masterSigBuf.add(masterRecordKey.decode());
|
||||||
|
masterSigBuf.add(masterOwner.key.decode());
|
||||||
|
|
||||||
final identityRecordKey = identityRec.key();
|
final identityRecordKey = identityRec.key();
|
||||||
final identityOwner = identityRec.ownerKeyPair()!;
|
final identityOwner = identityRec.ownerKeyPair()!;
|
||||||
final identitySigBuf = identityRecordKey.decode()
|
final identitySigBuf = BytesBuilder();
|
||||||
..addAll(identityOwner.key.decode());
|
identitySigBuf.add(identityRecordKey.decode());
|
||||||
|
identitySigBuf.add(identityOwner.key.decode());
|
||||||
|
|
||||||
|
assert(masterRecordKey.kind == identityRecordKey.kind);
|
||||||
|
final crypto = await veilid.getCryptoSystem(masterRecordKey.kind);
|
||||||
|
|
||||||
final identitySignature =
|
final identitySignature =
|
||||||
await crypto.signWithKeyPair(masterOwner, identitySigBuf);
|
await crypto.signWithKeyPair(masterOwner, identitySigBuf.toBytes());
|
||||||
final masterSignature =
|
final masterSignature =
|
||||||
await crypto.signWithKeyPair(identityOwner, masterSigBuf);
|
await crypto.signWithKeyPair(identityOwner, masterSigBuf.toBytes());
|
||||||
|
|
||||||
final identityMaster = IdentityMaster(
|
final identityMaster = IdentityMaster(
|
||||||
identityRecordKey: identityRecordKey,
|
identityRecordKey: identityRecordKey,
|
||||||
|
@ -813,6 +813,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.3"
|
version: "1.2.3"
|
||||||
|
quickalert:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: quickalert
|
||||||
|
sha256: "33a52870b2a87c55d0649d0cd228efaa2368d5df39231fdecebb71f349a9b221"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
radix_colors:
|
radix_colors:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -49,6 +49,7 @@ dependencies:
|
|||||||
form_builder_validators: ^9.0.0
|
form_builder_validators: ^9.0.0
|
||||||
blurry_modal_progress_hud: ^1.1.0
|
blurry_modal_progress_hud: ^1.1.0
|
||||||
flutter_spinkit: ^5.2.0
|
flutter_spinkit: ^5.2.0
|
||||||
|
quickalert: ^1.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
Reference in New Issue
Block a user