refactor and cleanup in prep for profile changing

This commit is contained in:
Christien Rioux 2024-06-13 14:52:34 -04:00
parent 87bb1657c7
commit 56d65442f4
49 changed files with 967 additions and 655 deletions

View File

@ -27,6 +27,22 @@
"name": "Name",
"pronouns": "Pronouns"
},
"edit_account_page": {
"titlebar": "Edit Account",
"header": "Account Profile",
"update": "Update",
"instructions": "This information will be shared with the people you invite to connect with you on VeilidChat.",
"error": "Account modification error",
"name": "Name",
"pronouns": "Pronouns",
"remove_account": "Remove Account",
"delete_identity": "Delete Identity",
"remove_account_confirm": "Confirm Account Removal?",
"remove_account_description": "Remove account from this device only",
"delete_identity_description": "Delete identity and all messages completely",
"delete_identity_confirm_message": "This action is PERMANENT, and your identity will no longer be recoverable with the recovery key. This will not remove your messages you have sent from other people's devices.",
"confirm_are_you_sure": "Are you sure you want to do this?"
},
"show_recovery_key_page": {
"titlebar": "Save Recovery Key",
"instructions": "You must save this recovery key somewhere safe. This key is the ONLY way to recover your VeilidChat account in the event of a forgotton password or a lost, stolen, or compromised device.",

View File

@ -3,13 +3,33 @@ import 'dart:async';
import 'package:veilid_support/veilid_support.dart';
import '../../proto/proto.dart' as proto;
import '../account_manager.dart';
typedef AccountRecordState = proto.Account;
class AccountRecordCubit extends DefaultDHTRecordCubit<AccountRecordState> {
AccountRecordCubit({
required super.open,
}) : super(decodeState: proto.Account.fromBuffer);
AccountRecordCubit(
{required AccountRepository accountRepository,
required TypedKey superIdentityRecordKey})
: 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)!;
// Record not yet open, do it
final pool = DHTRecordPool.instance;
final record = await pool.openRecordOwned(
userLogin.accountRecordInfo.accountRecord,
debugName: 'AccountRecordCubit::_open::AccountRecord',
parent: localAccount.superIdentity.currentInstance.recordKey);
return record;
}
@override
Future<void> close() async {

View File

@ -15,11 +15,13 @@ class AccountRecordsBlocMapCubit extends BlocMapCubit<TypedKey,
: _accountRepository = accountRepository;
// Add account record cubit
Future<void> _addAccountRecordCubit({required UserLogin userLogin}) async =>
Future<void> _addAccountRecordCubit(
{required TypedKey superIdentityRecordKey}) async =>
add(() => MapEntry(
userLogin.superIdentityRecordKey,
superIdentityRecordKey,
AccountRecordCubit(
open: () => _accountRepository.openAccountRecord(userLogin))));
accountRepository: _accountRepository,
superIdentityRecordKey: superIdentityRecordKey)));
/// StateFollower /////////////////////////
@ -28,7 +30,8 @@ class AccountRecordsBlocMapCubit extends BlocMapCubit<TypedKey,
@override
Future<void> updateState(TypedKey key, UserLogin value) async {
await _addAccountRecordCubit(userLogin: value);
await _addAccountRecordCubit(
superIdentityRecordKey: value.superIdentityRecordKey);
}
////////////////////////////////////////////////////////////////////////////

View File

@ -1,23 +1,23 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:veilid_support/veilid_support.dart';
import '../models/models.dart';
import '../repository/account_repository.dart';
class ActiveLocalAccountCubit extends Cubit<TypedKey?> {
ActiveLocalAccountCubit(AccountRepository accountRepository)
class ActiveAccountInfoCubit extends Cubit<AccountInfo> {
ActiveAccountInfoCubit(AccountRepository accountRepository)
: _accountRepository = accountRepository,
super(accountRepository.getActiveLocalAccount()) {
super(accountRepository
.getAccountInfo(accountRepository.getActiveLocalAccount())) {
// Subscribe to streams
_accountRepositorySubscription = _accountRepository.stream.listen((change) {
switch (change) {
case AccountRepositoryChange.activeLocalAccount:
emit(_accountRepository.getActiveLocalAccount());
break;
// Ignore these
case AccountRepositoryChange.localAccounts:
case AccountRepositoryChange.userLogins:
emit(accountRepository
.getAccountInfo(accountRepository.getActiveLocalAccount()));
break;
}
});

View File

@ -1,5 +1,5 @@
export 'account_record_cubit.dart';
export 'account_records_bloc_map_cubit.dart';
export 'active_local_account_cubit.dart';
export 'active_account_info_cubit.dart';
export 'local_accounts_cubit.dart';
export 'user_logins_cubit.dart';

View File

@ -1,6 +1,6 @@
import 'package:meta/meta.dart';
import 'active_account_info.dart';
import 'unlocked_account_info.dart';
enum AccountInfoStatus {
noAccount,
@ -14,10 +14,10 @@ class AccountInfo {
const AccountInfo({
required this.status,
required this.active,
required this.activeAccountInfo,
required this.unlockedAccountInfo,
});
final AccountInfoStatus status;
final bool active;
final ActiveAccountInfo? activeAccountInfo;
final UnlockedAccountInfo? unlockedAccountInfo;
}

View File

@ -1,5 +1,5 @@
export 'account_info.dart';
export 'active_account_info.dart';
export 'unlocked_account_info.dart';
export 'encryption_key_type.dart';
export 'local_account/local_account.dart';
export 'new_profile_spec.dart';

View File

@ -7,8 +7,8 @@ import 'local_account/local_account.dart';
import 'user_login/user_login.dart';
@immutable
class ActiveAccountInfo {
const ActiveAccountInfo({
class UnlockedAccountInfo {
const UnlockedAccountInfo({
required this.localAccount,
required this.userLogin,
});

View File

@ -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;
}

View File

@ -0,0 +1,163 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:go_router/go_router.dart';
import 'package:protobuf/protobuf.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../contacts/cubits/contact_list_cubit.dart';
import '../../conversation/conversation.dart';
import '../../layout/default_app_bar.dart';
import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart';
import '../../tools/tools.dart';
import '../../veilid_processor/veilid_processor.dart';
import '../account_manager.dart';
import 'profile_edit_form.dart';
class EditAccountPage extends StatefulWidget {
const EditAccountPage(
{required this.superIdentityRecordKey,
required this.existingProfile,
super.key});
@override
State createState() => _EditAccountPageState();
final TypedKey superIdentityRecordKey;
final proto.Profile existingProfile;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty<TypedKey>(
'superIdentityRecordKey', superIdentityRecordKey))
..add(DiagnosticsProperty<proto.Profile>(
'existingProfile', existingProfile));
}
}
class _EditAccountPageState extends State<EditAccountPage> {
final _formKey = GlobalKey<FormBuilderState>();
bool _isInAsyncCall = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await changeWindowSetup(
TitleBarStyle.normal, OrientationCapability.portraitOnly);
});
}
Widget _editAccountForm(BuildContext context,
{required Future<void> Function(GlobalKey<FormBuilderState>)
onSubmit}) =>
EditProfileForm(
header: translate('edit_account_page.header'),
instructions: translate('edit_account_page.instructions'),
submitText: translate('edit_account_page.update'),
submitDisabledText: translate('button.waiting_for_network'),
onSubmit: onSubmit);
@override
Widget build(BuildContext context) {
final displayModalHUD = _isInAsyncCall;
final accountRecordCubit = context.read<AccountRecordCubit>();
final activeConversationsBlocMapCubit =
context.read<ActiveConversationsBlocMapCubit>();
final contactListCubit = context.read<ContactListCubit>();
return Scaffold(
// resizeToAvoidBottomInset: false,
appBar: DefaultAppBar(
title: Text(translate('edit_account_page.titlebar')),
actions: [
const SignalStrengthMeterWidget(),
IconButton(
icon: const Icon(Icons.settings),
tooltip: translate('menu.settings_tooltip'),
onPressed: () async {
await GoRouterHelper(context).push('/settings');
})
]),
body: _editAccountForm(
context,
onSubmit: (formKey) async {
// dismiss the keyboard by unfocusing the textfield
FocusScope.of(context).unfocus();
try {
final name = _formKey.currentState!
.fields[EditProfileForm.formFieldName]!.value as String;
final pronouns = _formKey
.currentState!
.fields[EditProfileForm.formFieldPronouns]!
.value as String? ??
'';
final newProfile = widget.existingProfile.deepCopy()
..name = name
..pronouns = pronouns;
setState(() {
_isInAsyncCall = true;
});
try {
// Update account profile DHT record
final newValue = await accountRecordCubit.record
.tryWriteProtobuf(proto.Account.fromBuffer, newProfile);
if (newValue != null) {
if (context.mounted) {
await showErrorModal(
context,
translate('edit_account_page.error'),
'Failed to update profile online');
return;
}
}
// Update local account profile
await AccountRepository.instance.editAccountProfile(
widget.superIdentityRecordKey, newProfile);
// Update all conversations with new profile
final updates = <Future<void>>[];
for (final key in activeConversationsBlocMapCubit.state.keys) {
await activeConversationsBlocMapCubit.operateAsync(key,
closure: (cubit) async {
final newLocalConversation =
cubit.state.asData?.value.localConversation.deepCopy();
if (newLocalConversation != null) {
newLocalConversation.profile = newProfile;
updates.add(cubit.input.writeLocalConversation(
conversation: newLocalConversation));
}
});
}
// Wait for updates
await updates.wait;
// XXX: how to do this for non-chat contacts?
} finally {
if (mounted) {
setState(() {
_isInAsyncCall = false;
});
}
}
} on Exception catch (e) {
if (context.mounted) {
await showErrorModal(context,
translate('edit_account_page.error'), 'Exception: $e');
}
}
},
).paddingSymmetric(horizontal: 24, vertical: 8),
).withModalHUD(context, displayModalHUD);
}
}

View File

@ -1,30 +1,28 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:go_router/go_router.dart';
import '../../layout/default_app_bar.dart';
import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart';
import '../../tools/tools.dart';
import '../../veilid_processor/veilid_processor.dart';
import '../account_manager.dart';
import 'profile_edit_form.dart';
class NewAccountPage extends StatefulWidget {
const NewAccountPage({super.key});
@override
NewAccountPageState createState() => NewAccountPageState();
State createState() => _NewAccountPageState();
}
class NewAccountPageState extends State<NewAccountPage> {
class _NewAccountPageState extends State<NewAccountPage> {
final _formKey = GlobalKey<FormBuilderState>();
late bool isInAsyncCall = false;
static const String formFieldName = 'name';
static const String formFieldPronouns = 'pronouns';
bool _isInAsyncCall = false;
@override
void initState() {
@ -47,70 +45,17 @@ class NewAccountPageState extends State<NewAccountPage> {
false;
final canSubmit = networkReady;
return FormBuilder(
key: _formKey,
child: ListView(
children: [
Text(translate('new_account_page.header'))
.textStyle(context.headlineSmall)
.paddingSymmetric(vertical: 16),
FormBuilderTextField(
autofocus: true,
name: formFieldName,
decoration:
InputDecoration(labelText: translate('account.form_name')),
maxLength: 64,
// The validator receives the text that the user has entered.
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
]),
textInputAction: TextInputAction.next,
),
FormBuilderTextField(
name: formFieldPronouns,
maxLength: 64,
decoration:
InputDecoration(labelText: translate('account.form_pronouns')),
textInputAction: TextInputAction.next,
),
Row(children: [
const Spacer(),
Text(translate('new_account_page.instructions'))
.toCenter()
.flexible(flex: 6),
const Spacer(),
]).paddingSymmetric(vertical: 4),
ElevatedButton(
onPressed: !canSubmit
? null
: () async {
if (_formKey.currentState?.saveAndValidate() ?? false) {
setState(() {
isInAsyncCall = true;
});
try {
await onSubmit(_formKey);
} finally {
if (mounted) {
setState(() {
isInAsyncCall = false;
});
}
}
}
},
child: Text(translate(!networkReady
? 'button.waiting_for_network'
: 'new_account_page.create')),
).paddingSymmetric(vertical: 4).alignAtCenterRight(),
],
),
);
return EditProfileForm(
header: translate('new_account_page.header'),
instructions: translate('new_account_page.instructions'),
submitText: translate('new_account_page.create'),
submitDisabledText: translate('button.waiting_for_network'),
onSubmit: !canSubmit ? null : onSubmit);
}
@override
Widget build(BuildContext context) {
final displayModalHUD = isInAsyncCall;
final displayModalHUD = _isInAsyncCall;
return Scaffold(
// resizeToAvoidBottomInset: false,
@ -120,7 +65,7 @@ class NewAccountPageState extends State<NewAccountPage> {
const SignalStrengthMeterWidget(),
IconButton(
icon: const Icon(Icons.settings),
tooltip: translate('app_bar.settings_tooltip'),
tooltip: translate('menu.settings_tooltip'),
onPressed: () async {
await GoRouterHelper(context).push('/settings');
})
@ -132,19 +77,33 @@ class NewAccountPageState extends State<NewAccountPage> {
FocusScope.of(context).unfocus();
try {
final name =
_formKey.currentState!.fields[formFieldName]!.value as String;
final pronouns = _formKey.currentState!.fields[formFieldPronouns]!
final name = _formKey.currentState!
.fields[EditProfileForm.formFieldName]!.value as String;
final pronouns = _formKey
.currentState!
.fields[EditProfileForm.formFieldPronouns]!
.value as String? ??
'';
final newProfileSpec =
NewProfileSpec(name: name, pronouns: pronouns);
final newProfile = proto.Profile()
..name = name
..pronouns = pronouns;
final superSecret = await AccountRepository.instance
.createWithNewSuperIdentity(newProfileSpec);
GoRouterHelper(context).pushReplacement('/new_account/recovery_key',
extra: superSecret);
setState(() {
_isInAsyncCall = true;
});
try {
final superSecret = await AccountRepository.instance
.createWithNewSuperIdentity(newProfile);
GoRouterHelper(context).pushReplacement(
'/new_account/recovery_key',
extra: superSecret);
} finally {
if (mounted) {
setState(() {
_isInAsyncCall = false;
});
}
}
} on Exception catch (e) {
if (context.mounted) {
await showErrorModal(context, translate('new_account_page.error'),
@ -155,10 +114,4 @@ class NewAccountPageState extends State<NewAccountPage> {
).paddingSymmetric(horizontal: 24, vertical: 8),
).withModalHUD(context, displayModalHUD);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<bool>('isInAsyncCall', isInAsyncCall));
}
}

View File

@ -0,0 +1,106 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
class EditProfileForm extends StatefulWidget {
const EditProfileForm({
required this.header,
required this.instructions,
required this.submitText,
required this.submitDisabledText,
super.key,
this.onSubmit,
});
@override
State createState() => _EditProfileFormState();
final String header;
final String instructions;
final Future<void> Function(GlobalKey<FormBuilderState>)? onSubmit;
final String submitText;
final String submitDisabledText;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(StringProperty('header', header))
..add(StringProperty('instructions', instructions))
..add(ObjectFlagProperty<
Future<void> Function(
GlobalKey<FormBuilderState> p1)?>.has('onSubmit', onSubmit))
..add(StringProperty('submitText', submitText))
..add(StringProperty('submitDisabledText', submitDisabledText));
}
static const String formFieldName = 'name';
static const String formFieldPronouns = 'pronouns';
}
class _EditProfileFormState extends State<EditProfileForm> {
final _formKey = GlobalKey<FormBuilderState>();
@override
void initState() {
super.initState();
}
Widget _editProfileForm(
BuildContext context,
) =>
FormBuilder(
key: _formKey,
child: ListView(
children: [
Text(widget.header)
.textStyle(context.headlineSmall)
.paddingSymmetric(vertical: 16),
FormBuilderTextField(
autofocus: true,
name: EditProfileForm.formFieldName,
decoration:
InputDecoration(labelText: translate('account.form_name')),
maxLength: 64,
// The validator receives the text that the user has entered.
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(),
]),
textInputAction: TextInputAction.next,
),
FormBuilderTextField(
name: EditProfileForm.formFieldPronouns,
maxLength: 64,
decoration: InputDecoration(
labelText: translate('account.form_pronouns')),
textInputAction: TextInputAction.next,
),
Row(children: [
const Spacer(),
Text(widget.instructions).toCenter().flexible(flex: 6),
const Spacer(),
]).paddingSymmetric(vertical: 4),
ElevatedButton(
onPressed: widget.onSubmit == null
? null
: () async {
if (_formKey.currentState?.saveAndValidate() ?? false) {
await widget.onSubmit!(_formKey);
}
},
child: Text((widget.onSubmit == null)
? widget.submitDisabledText
: widget.submitText),
).paddingSymmetric(vertical: 4).alignAtCenterRight(),
],
),
);
@override
Widget build(BuildContext context) => _editProfileForm(
context,
);
}

View File

@ -48,7 +48,7 @@ class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
const SignalStrengthMeterWidget(),
IconButton(
icon: const Icon(Icons.settings),
tooltip: translate('app_bar.settings_tooltip'),
tooltip: translate('menu.settings_tooltip'),
onPressed: () async {
await GoRouterHelper(context).push('/settings');
})

View File

@ -122,9 +122,9 @@ class VeilidChatApp extends StatelessWidget {
create: (context) =>
UserLoginsCubit(AccountRepository.instance),
),
BlocProvider<ActiveLocalAccountCubit>(
BlocProvider<ActiveAccountInfoCubit>(
create: (context) =>
ActiveLocalAccountCubit(AccountRepository.instance),
ActiveAccountInfoCubit(AccountRepository.instance),
),
BlocProvider<PreferencesCubit>(
create: (context) =>

View File

@ -1,13 +1,19 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../router/router.dart';
// XXX: if we ever want to have more than one chat 'open', we should put the
// operations and state for that here.
class ActiveChatCubit extends Cubit<TypedKey?> {
ActiveChatCubit(super.initialState);
ActiveChatCubit(super.initialState, {required RouterCubit routerCubit})
: _routerCubit = routerCubit;
void setActiveChat(TypedKey? activeChatLocalConversationRecordKey) {
emit(activeChatLocalConversationRecordKey);
_routerCubit.setHasActiveChat(activeChatLocalConversationRecordKey != null);
}
final RouterCubit _routerCubit;
}

View File

@ -12,7 +12,7 @@ import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
import '../../chat_list/chat_list.dart';
import '../../conversation/conversation.dart';
import '../../proto/proto.dart' as proto;
import '../models/chat_component_state.dart';
import '../models/message_state.dart';
@ -44,7 +44,7 @@ class ChatComponentCubit extends Cubit<ChatComponentState> {
// ignore: prefer_constructors_over_static_methods
static ChatComponentCubit singleContact(
{required ActiveAccountInfo activeAccountInfo,
{required UnlockedAccountInfo activeAccountInfo,
required proto.Account accountRecordInfo,
required ActiveConversationState activeConversationState,
required SingleContactMessagesCubit messagesCubit}) {

View File

@ -50,7 +50,7 @@ typedef SingleContactMessagesState = AsyncValue<WindowState<MessageState>>;
// Builds the reconciled chat record from the local and remote conversation keys
class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
SingleContactMessagesCubit({
required ActiveAccountInfo activeAccountInfo,
required UnlockedAccountInfo activeAccountInfo,
required TypedKey remoteIdentityPublicKey,
required TypedKey localConversationRecordKey,
required TypedKey localMessagesRecordKey,
@ -402,7 +402,7 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
/////////////////////////////////////////////////////////////////////////
final WaitSet<void> _initWait = WaitSet();
final ActiveAccountInfo _activeAccountInfo;
final UnlockedAccountInfo _activeAccountInfo;
final TypedKey _remoteIdentityPublicKey;
final TypedKey _localConversationRecordKey;
final TypedKey _localMessagesRecordKey;

View File

@ -9,7 +9,7 @@ import 'package:flutter_chat_ui/flutter_chat_ui.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
import '../../chat_list/chat_list.dart';
import '../../conversation/conversation.dart';
import '../../theme/theme.dart';
import '../chat.dart';
@ -23,7 +23,7 @@ class ChatComponentWidget extends StatelessWidget {
{required TypedKey localConversationRecordKey, Key? key}) =>
Builder(builder: (context) {
// Get all watched dependendies
final activeAccountInfo = context.watch<ActiveAccountInfo>();
final activeAccountInfo = context.watch<UnlockedAccountInfo>();
final accountRecordInfo =
context.watch<AccountRecordCubit>().state.asData?.value;
if (accountRecordInfo == null) {

View File

@ -19,15 +19,15 @@ typedef ChatListCubitState = DHTShortArrayBusyState<proto.Chat>;
class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
with StateMapFollowable<ChatListCubitState, TypedKey, proto.Chat> {
ChatListCubit({
required ActiveAccountInfo activeAccountInfo,
required UnlockedAccountInfo unlockedAccountInfo,
required proto.Account account,
required this.activeChatCubit,
}) : super(
open: () => _open(activeAccountInfo, account),
open: () => _open(unlockedAccountInfo, account),
decodeElement: proto.Chat.fromBuffer);
static Future<DHTShortArray> _open(
ActiveAccountInfo activeAccountInfo, proto.Account account) async {
UnlockedAccountInfo activeAccountInfo, proto.Account account) async {
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;

View File

@ -1,3 +1 @@
export 'active_single_contact_chat_bloc_map_cubit.dart';
export 'active_conversations_bloc_map_cubit.dart';
export 'chat_list_cubit.dart';

View File

@ -36,16 +36,16 @@ class ContactInvitationListCubit
StateMapFollowable<ContactInvitiationListState, TypedKey,
proto.ContactInvitationRecord> {
ContactInvitationListCubit({
required ActiveAccountInfo activeAccountInfo,
required UnlockedAccountInfo unlockedAccountInfo,
required proto.Account account,
}) : _activeAccountInfo = activeAccountInfo,
}) : _activeAccountInfo = unlockedAccountInfo,
_account = account,
super(
open: () => _open(activeAccountInfo, account),
open: () => _open(unlockedAccountInfo, account),
decodeElement: proto.ContactInvitationRecord.fromBuffer);
static Future<DHTShortArray> _open(
ActiveAccountInfo activeAccountInfo, proto.Account account) async {
UnlockedAccountInfo activeAccountInfo, proto.Account account) async {
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
@ -318,6 +318,6 @@ class ContactInvitationListCubit
}
//
final ActiveAccountInfo _activeAccountInfo;
final UnlockedAccountInfo _activeAccountInfo;
final proto.Account _account;
}

View File

@ -23,7 +23,7 @@ class ContactRequestInboxCubit
// : super.value(decodeState: proto.SignedContactResponse.fromBuffer);
static Future<DHTRecord> _open(
{required ActiveAccountInfo activeAccountInfo,
{required UnlockedAccountInfo activeAccountInfo,
required proto.ContactInvitationRecord contactInvitationRecord}) async {
final pool = DHTRecordPool.instance;
final accountRecordKey =
@ -42,6 +42,6 @@ class ContactRequestInboxCubit
defaultSubkey: 1);
}
final ActiveAccountInfo activeAccountInfo;
final UnlockedAccountInfo activeAccountInfo;
final proto.ContactInvitationRecord contactInvitationRecord;
}

View File

@ -7,7 +7,7 @@ import 'package:meta/meta.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
import '../../contacts/contacts.dart';
import '../../conversation/conversation.dart';
import '../../proto/proto.dart' as proto;
import '../../tools/tools.dart';
import '../models/accepted_contact.dart';
@ -25,7 +25,7 @@ class InvitationStatus extends Equatable {
class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
proto.SignedContactResponse?> {
WaitingInvitationCubit(ContactRequestInboxCubit super.input,
{required ActiveAccountInfo activeAccountInfo,
{required UnlockedAccountInfo activeAccountInfo,
required proto.Account account,
required proto.ContactInvitationRecord contactInvitationRecord})
: super(
@ -37,7 +37,7 @@ class WaitingInvitationCubit extends AsyncTransformerCubit<InvitationStatus,
static Future<AsyncValue<InvitationStatus>> _transform(
proto.SignedContactResponse? signedContactResponse,
{required ActiveAccountInfo activeAccountInfo,
{required UnlockedAccountInfo activeAccountInfo,
required proto.Account account,
required proto.ContactInvitationRecord contactInvitationRecord}) async {
if (signedContactResponse == null) {

View File

@ -18,7 +18,7 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
StateMapFollower<DHTShortArrayBusyState<proto.ContactInvitationRecord>,
TypedKey, proto.ContactInvitationRecord> {
WaitingInvitationsBlocMapCubit(
{required this.activeAccountInfo, required this.account});
{required this.unlockedAccountInfo, required this.account});
Future<void> _addWaitingInvitation(
{required proto.ContactInvitationRecord
@ -27,9 +27,9 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
contactInvitationRecord.contactRequestInbox.recordKey.toVeilid(),
WaitingInvitationCubit(
ContactRequestInboxCubit(
activeAccountInfo: activeAccountInfo,
activeAccountInfo: unlockedAccountInfo,
contactInvitationRecord: contactInvitationRecord),
activeAccountInfo: activeAccountInfo,
activeAccountInfo: unlockedAccountInfo,
account: account,
contactInvitationRecord: contactInvitationRecord)));
@ -43,6 +43,6 @@ class WaitingInvitationsBlocMapCubit extends BlocMapCubit<TypedKey,
_addWaitingInvitation(contactInvitationRecord: value);
////
final ActiveAccountInfo activeAccountInfo;
final UnlockedAccountInfo unlockedAccountInfo;
final proto.Account account;
}

View File

@ -2,7 +2,7 @@ import 'package:meta/meta.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
import '../../contacts/contacts.dart';
import '../../conversation/conversation.dart';
import '../../proto/proto.dart' as proto;
import '../../tools/tools.dart';
import 'models.dart';
@ -13,7 +13,7 @@ import 'models.dart';
class ValidContactInvitation {
@internal
ValidContactInvitation(
{required ActiveAccountInfo activeAccountInfo,
{required UnlockedAccountInfo activeAccountInfo,
required proto.Account account,
required TypedKey contactRequestInboxKey,
required proto.ContactRequestPrivate contactRequestPrivate,
@ -129,7 +129,7 @@ class ValidContactInvitation {
}
//
final ActiveAccountInfo _activeAccountInfo;
final UnlockedAccountInfo _activeAccountInfo;
final proto.Account _account;
final TypedKey _contactRequestInboxKey;
final SuperIdentity _contactSuperIdentity;

View File

@ -74,7 +74,7 @@ class InvitationDialogState extends State<InvitationDialog> {
Future<void> _onAccept() async {
final navigator = Navigator.of(context);
final activeAccountInfo = widget.modalContext.read<ActiveAccountInfo>();
final activeAccountInfo = widget.modalContext.read<UnlockedAccountInfo>();
final contactList = widget.modalContext.read<ContactListCubit>();
setState(() {

View File

@ -13,15 +13,15 @@ import 'conversation_cubit.dart';
class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
ContactListCubit({
required ActiveAccountInfo activeAccountInfo,
required UnlockedAccountInfo unlockedAccountInfo,
required proto.Account account,
}) : _activeAccountInfo = activeAccountInfo,
}) : _activeAccountInfo = unlockedAccountInfo,
super(
open: () => _open(activeAccountInfo, account),
open: () => _open(unlockedAccountInfo, account),
decodeElement: proto.Contact.fromBuffer);
static Future<DHTShortArray> _open(
ActiveAccountInfo activeAccountInfo, proto.Account account) async {
UnlockedAccountInfo activeAccountInfo, proto.Account account) async {
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
@ -99,5 +99,5 @@ class ContactListCubit extends DHTShortArrayCubit<proto.Contact> {
}
}
final ActiveAccountInfo _activeAccountInfo;
final UnlockedAccountInfo _activeAccountInfo;
}

View File

@ -1,2 +1 @@
export 'contact_list_cubit.dart';
export 'conversation_cubit.dart';

View File

@ -0,0 +1 @@
export 'cubits/cubits.dart';

View File

@ -6,6 +6,7 @@ import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart';
import '../../contacts/contacts.dart';
import '../../conversation/conversation.dart';
import '../../proto/proto.dart' as proto;
import 'cubits.dart';
@ -26,7 +27,9 @@ class ActiveConversationState extends Equatable {
}
typedef ActiveConversationCubit = TransformerCubit<
AsyncValue<ActiveConversationState>, AsyncValue<ConversationState>>;
AsyncValue<ActiveConversationState>,
AsyncValue<ConversationState>,
ConversationCubit>;
typedef ActiveConversationsBlocMapState
= BlocMapState<TypedKey, AsyncValue<ActiveConversationState>>;
@ -41,11 +44,17 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
AsyncValue<ActiveConversationState>, ActiveConversationCubit>
with StateMapFollower<ChatListCubitState, TypedKey, proto.Chat> {
ActiveConversationsBlocMapCubit(
{required ActiveAccountInfo activeAccountInfo,
{required UnlockedAccountInfo unlockedAccountInfo,
required ContactListCubit contactListCubit})
: _activeAccountInfo = activeAccountInfo,
: _activeAccountInfo = unlockedAccountInfo,
_contactListCubit = contactListCubit;
////////////////////////////////////////////////////////////////////////////
// Public Interface
////////////////////////////////////////////////////////////////////////////
// Private Implementation
// Add an active conversation to be tracked for changes
Future<void> _addConversation({required proto.Contact contact}) async =>
add(() => MapEntry(
@ -97,6 +106,6 @@ class ActiveConversationsBlocMapCubit extends BlocMapCubit<TypedKey,
////
final ActiveAccountInfo _activeAccountInfo;
final UnlockedAccountInfo _activeAccountInfo;
final ContactListCubit _contactListCubit;
}

View File

@ -20,10 +20,10 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
StateMapFollower<ActiveConversationsBlocMapState, TypedKey,
AsyncValue<ActiveConversationState>> {
ActiveSingleContactChatBlocMapCubit(
{required ActiveAccountInfo activeAccountInfo,
{required UnlockedAccountInfo unlockedAccountInfo,
required ContactListCubit contactListCubit,
required ChatListCubit chatListCubit})
: _activeAccountInfo = activeAccountInfo,
: _activeAccountInfo = unlockedAccountInfo,
_contactListCubit = contactListCubit,
_chatListCubit = chatListCubit;
@ -95,7 +95,7 @@ class ActiveSingleContactChatBlocMapCubit extends BlocMapCubit<TypedKey,
////
final ActiveAccountInfo _activeAccountInfo;
final UnlockedAccountInfo _activeAccountInfo;
final ContactListCubit _contactListCubit;
final ChatListCubit _chatListCubit;
}

View File

@ -29,11 +29,11 @@ class ConversationState extends Equatable {
class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
ConversationCubit(
{required ActiveAccountInfo activeAccountInfo,
{required UnlockedAccountInfo activeAccountInfo,
required TypedKey remoteIdentityPublicKey,
TypedKey? localConversationRecordKey,
TypedKey? remoteConversationRecordKey})
: _activeAccountInfo = activeAccountInfo,
: _unlockedAccountInfo = activeAccountInfo,
_localConversationRecordKey = localConversationRecordKey,
_remoteIdentityPublicKey = remoteIdentityPublicKey,
_remoteConversationRecordKey = remoteConversationRecordKey,
@ -41,13 +41,13 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
if (_localConversationRecordKey != null) {
_initWait.add(() async {
await _setLocalConversation(() async {
final accountRecordKey = _activeAccountInfo
final accountRecordKey = _unlockedAccountInfo
.userLogin.accountRecordInfo.accountRecord.recordKey;
// Open local record key if it is specified
final pool = DHTRecordPool.instance;
final crypto = await _cachedConversationCrypto();
final writer = _activeAccountInfo.identityWriter;
final writer = _unlockedAccountInfo.identityWriter;
final record = await pool.openRecordWrite(
_localConversationRecordKey!, writer,
debugName: 'ConversationCubit::LocalConversation',
@ -61,7 +61,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
if (_remoteConversationRecordKey != null) {
_initWait.add(() async {
await _setRemoteConversation(() async {
final accountRecordKey = _activeAccountInfo
final accountRecordKey = _unlockedAccountInfo
.userLogin.accountRecordInfo.accountRecord.recordKey;
// Open remote record key if it is specified
@ -217,11 +217,11 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
'must not have a local conversation yet');
final pool = DHTRecordPool.instance;
final accountRecordKey =
_activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
final accountRecordKey = _unlockedAccountInfo
.userLogin.accountRecordInfo.accountRecord.recordKey;
final crypto = await _cachedConversationCrypto();
final writer = _activeAccountInfo.identityWriter;
final writer = _unlockedAccountInfo.identityWriter;
// Open with SMPL scheme for identity writer
late final DHTRecord localConversationRecord;
@ -247,7 +247,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
.deleteScope((localConversation) async {
// Make messages log
return _initLocalMessages(
activeAccountInfo: _activeAccountInfo,
activeAccountInfo: _unlockedAccountInfo,
remoteIdentityPublicKey: _remoteIdentityPublicKey,
localConversationKey: localConversation.key,
callback: (messages) async {
@ -255,7 +255,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
final conversation = proto.Conversation()
..profile = profile
..superIdentityJson = jsonEncode(
_activeAccountInfo.localAccount.superIdentity.toJson())
_unlockedAccountInfo.localAccount.superIdentity.toJson())
..messages = messages.recordKey.toProto();
// Write initial conversation to record
@ -282,7 +282,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
// Initialize local messages
Future<T> _initLocalMessages<T>({
required ActiveAccountInfo activeAccountInfo,
required UnlockedAccountInfo activeAccountInfo,
required TypedKey remoteIdentityPublicKey,
required TypedKey localConversationKey,
required FutureOr<T> Function(DHTLog) callback,
@ -332,14 +332,14 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
if (conversationCrypto != null) {
return conversationCrypto;
}
conversationCrypto = await _activeAccountInfo
conversationCrypto = await _unlockedAccountInfo
.makeConversationCrypto(_remoteIdentityPublicKey);
_conversationCrypto = conversationCrypto;
return conversationCrypto;
}
final ActiveAccountInfo _activeAccountInfo;
final UnlockedAccountInfo _unlockedAccountInfo;
final TypedKey _remoteIdentityPublicKey;
TypedKey? _localConversationRecordKey;
final TypedKey? _remoteConversationRecordKey;

View File

@ -0,0 +1,3 @@
export 'active_conversations_bloc_map_cubit.dart';
export 'active_single_contact_chat_bloc_map_cubit.dart';
export 'conversation_cubit.dart';

View File

@ -1,3 +1,4 @@
import 'package:async_tools/async_tools.dart';
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -9,6 +10,7 @@ import 'package:veilid_support/veilid_support.dart';
import '../../../account_manager/account_manager.dart';
import '../../../theme/theme.dart';
import '../../../tools/tools.dart';
import '../../../veilid_processor/veilid_processor.dart';
import 'menu_item_widget.dart';
class DrawerMenu extends StatefulWidget {
@ -29,8 +31,10 @@ class _DrawerMenuState extends State<DrawerMenu> {
super.dispose();
}
void _doLoginClick(TypedKey superIdentityRecordKey) {
//
void _doSwitchClick(TypedKey superIdentityRecordKey) {
singleFuture(this, () async {
await AccountRepository.instance.switchToAccount(superIdentityRecordKey);
});
}
void _doEditClick(TypedKey superIdentityRecordKey) {
@ -47,10 +51,12 @@ class _DrawerMenuState extends State<DrawerMenu> {
Widget _makeAccountWidget(
{required String name,
required bool selected,
required ScaleColor scale,
required bool loggedIn,
required void Function() clickHandler}) {
required void Function()? callback,
required void Function()? footerCallback}) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!.tertiaryScale;
final abbrev = name.split(' ').map((s) => s.isEmpty ? '' : s[0]).join();
late final String shortname;
if (abbrev.length >= 3) {
@ -65,30 +71,36 @@ class _DrawerMenuState extends State<DrawerMenu> {
foregroundColor: loggedIn ? scale.primaryText : scale.subtleText,
child: Text(shortname, style: theme.textTheme.titleLarge));
return MenuItemWidget(
title: name,
headerWidget: avatar,
titleStyle: theme.textTheme.titleLarge!,
foregroundColor: scale.primary,
backgroundColor: scale.elementBackground,
backgroundHoverColor: scale.hoverElementBackground,
backgroundFocusColor: scale.activeElementBackground,
borderColor: scale.border,
borderHoverColor: scale.hoverBorder,
borderFocusColor: scale.primary,
footerButtonIcon: loggedIn ? Icons.edit_outlined : Icons.login_outlined,
footerCallback: clickHandler,
footerButtonIconColor: scale.border,
footerButtonIconHoverColor: scale.hoverElementBackground,
footerButtonIconFocusColor: scale.activeElementBackground,
);
return AnimatedPadding(
padding: EdgeInsets.fromLTRB(selected ? 0 : 0, 0, selected ? 0 : 8, 0),
duration: const Duration(milliseconds: 50),
child: MenuItemWidget(
title: name,
headerWidget: avatar,
titleStyle: theme.textTheme.titleLarge!,
foregroundColor: scale.primary,
backgroundColor: selected
? scale.activeElementBackground
: scale.elementBackground,
backgroundHoverColor: scale.hoverElementBackground,
backgroundFocusColor: scale.activeElementBackground,
borderColor: scale.border,
borderHoverColor: scale.hoverBorder,
borderFocusColor: scale.primary,
callback: callback,
footerButtonIcon: loggedIn ? Icons.edit_outlined : null,
footerCallback: footerCallback,
footerButtonIconColor: scale.border,
footerButtonIconHoverColor: scale.hoverElementBackground,
footerButtonIconFocusColor: scale.activeElementBackground,
));
}
Widget _getAccountList(
{required TypedKey? activeLocalAccount,
required AccountRecordsBlocMapState accountRecords}) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final scaleScheme = theme.extension<ScaleScheme>()!;
final accountRepo = AccountRepository.instance;
final localAccounts = accountRepo.getLocalAccounts();
@ -104,28 +116,38 @@ class _DrawerMenuState extends State<DrawerMenu> {
final acctRecord = accountRecords.get(superIdentityRecordKey);
if (acctRecord != null) {
// Account is logged in
final scale = theme.extension<ScaleScheme>()!.tertiaryScale;
final loggedInAccount = acctRecord.when(
data: (value) => _makeAccountWidget(
name: value.profile.name,
scale: scale,
selected: superIdentityRecordKey == activeLocalAccount,
loggedIn: true,
clickHandler: () {
callback: () {
_doSwitchClick(superIdentityRecordKey);
},
footerCallback: () {
_doEditClick(superIdentityRecordKey);
}),
loading: () => _wrapInBox(
child: buildProgressIndicator(),
color: scale.grayScale.subtleBorder),
color: scaleScheme.grayScale.subtleBorder),
error: (err, st) => _wrapInBox(
child: errorPage(err, st), color: scale.errorScale.subtleBorder),
child: errorPage(err, st),
color: scaleScheme.errorScale.subtleBorder),
);
loggedInAccounts.add(loggedInAccount);
loggedInAccounts.add(loggedInAccount.paddingLTRB(0, 0, 0, 8));
} else {
// Account is not logged in
final scale = theme.extension<ScaleScheme>()!.grayScale;
final loggedOutAccount = _makeAccountWidget(
name: la.name,
loggedIn: false,
clickHandler: () {
_doLoginClick(superIdentityRecordKey);
});
name: la.name,
scale: scale,
selected: superIdentityRecordKey == activeLocalAccount,
loggedIn: false,
callback: () => {_doSwitchClick(superIdentityRecordKey)},
footerCallback: null,
);
loggedOutAccounts.add(loggedOutAccount);
}
}
@ -208,7 +230,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
final scale = theme.extension<ScaleScheme>()!;
//final textTheme = theme.textTheme;
final accountRecords = context.watch<AccountRecordsBlocMapCubit>().state;
final activeLocalAccount = context.watch<ActiveLocalAccountCubit>().state;
final activeLocalAccount = context.watch<ActiveAccountInfoCubit>().state;
final gradient = LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
@ -249,13 +271,21 @@ class _DrawerMenuState extends State<DrawerMenu> {
])),
const Spacer(),
_getAccountList(
activeLocalAccount: activeLocalAccount,
activeLocalAccount:
activeLocalAccount.unlockedAccountInfo?.superIdentityRecordKey,
accountRecords: accountRecords),
_getBottomButtons(),
const Spacer(),
Text('Version $packageInfoVersion',
style: theme.textTheme.labelMedium!
.copyWith(color: scale.tertiaryScale.hoverBorder))
Row(children: [
Text('Version $packageInfoVersion',
style: theme.textTheme.labelMedium!
.copyWith(color: scale.tertiaryScale.hoverBorder)),
const Spacer(),
SignalStrengthMeterWidget(
color: scale.tertiaryScale.hoverBorder,
inactiveColor: scale.tertiaryScale.border,
),
])
]).paddingAll(16),
);
}

View File

@ -27,7 +27,7 @@ class MenuItemWidget extends StatelessWidget {
@override
Widget build(BuildContext context) => TextButton(
onPressed: () => callback,
onPressed: callback,
style: TextButton.styleFrom(foregroundColor: foregroundColor).copyWith(
backgroundColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.hovered)) {

View File

@ -44,7 +44,7 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
WidgetStateProperty.all(scale.primaryScale.hoverBorder),
shape: WidgetStateProperty.all(const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(16))))),
tooltip: translate('app_bar.settings_tooltip'),
tooltip: translate('menu.settings_tooltip'),
onPressed: () async {
final ctrl = context.read<ZoomDrawerController>();
await ctrl.toggle?.call();

View File

@ -1,15 +1,14 @@
import 'package:async_tools/async_tools.dart';
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../../account_manager/account_manager.dart';
import '../../../chat/chat.dart';
import '../../../chat_list/chat_list.dart';
import '../../../contact_invitation/contact_invitation.dart';
import '../../../contacts/contacts.dart';
import '../../../conversation/conversation.dart';
import '../../../router/router.dart';
import '../../../theme/theme.dart';
@ -18,20 +17,17 @@ class HomeAccountReadyShell extends StatefulWidget {
{required BuildContext context, required Widget child, Key? key}) {
// These must exist in order for the account to
// be considered 'ready' for this widget subtree
final activeLocalAccount = context.read<ActiveLocalAccountCubit>().state!;
final activeAccountInfo = context.read<ActiveAccountInfo>();
final unlockedAccountInfo = context.watch<UnlockedAccountInfo>();
final routerCubit = context.read<RouterCubit>();
return HomeAccountReadyShell._(
activeLocalAccount: activeLocalAccount,
activeAccountInfo: activeAccountInfo,
unlockedAccountInfo: unlockedAccountInfo,
routerCubit: routerCubit,
key: key,
child: child);
}
const HomeAccountReadyShell._(
{required this.activeLocalAccount,
required this.activeAccountInfo,
{required this.unlockedAccountInfo,
required this.routerCubit,
required this.child,
super.key});
@ -40,18 +36,15 @@ class HomeAccountReadyShell extends StatefulWidget {
HomeAccountReadyShellState createState() => HomeAccountReadyShellState();
final Widget child;
final TypedKey activeLocalAccount;
final ActiveAccountInfo activeAccountInfo;
final UnlockedAccountInfo unlockedAccountInfo;
final RouterCubit routerCubit;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty<TypedKey>(
'activeLocalAccount', activeLocalAccount))
..add(DiagnosticsProperty<ActiveAccountInfo>(
'activeAccountInfo', activeAccountInfo))
..add(DiagnosticsProperty<UnlockedAccountInfo>(
'unlockedAccountInfo', unlockedAccountInfo))
..add(DiagnosticsProperty<RouterCubit>('routerCubit', routerCubit));
}
}
@ -115,39 +108,41 @@ class HomeAccountReadyShellState extends State<HomeAccountReadyShell> {
}
return MultiBlocProvider(
providers: [
// Contact Cubits
BlocProvider(
create: (context) => ContactInvitationListCubit(
activeAccountInfo: widget.activeAccountInfo,
unlockedAccountInfo: widget.unlockedAccountInfo,
account: account)),
BlocProvider(
create: (context) => ContactListCubit(
activeAccountInfo: widget.activeAccountInfo,
unlockedAccountInfo: widget.unlockedAccountInfo,
account: account)),
BlocProvider(
create: (context) => ActiveChatCubit(null)
..withStateListen((event) {
widget.routerCubit.setHasActiveChat(event != null);
})),
BlocProvider(
create: (context) => ChatListCubit(
activeAccountInfo: widget.activeAccountInfo,
activeChatCubit: context.read<ActiveChatCubit>(),
account: account)),
BlocProvider(
create: (context) => ActiveConversationsBlocMapCubit(
activeAccountInfo: widget.activeAccountInfo,
contactListCubit: context.read<ContactListCubit>())
..follow(context.read<ChatListCubit>())),
BlocProvider(
create: (context) => ActiveSingleContactChatBlocMapCubit(
activeAccountInfo: widget.activeAccountInfo,
contactListCubit: context.read<ContactListCubit>(),
chatListCubit: context.read<ChatListCubit>())
..follow(context.read<ActiveConversationsBlocMapCubit>())),
BlocProvider(
create: (context) => WaitingInvitationsBlocMapCubit(
activeAccountInfo: widget.activeAccountInfo, account: account)
..follow(context.read<ContactInvitationListCubit>()))
unlockedAccountInfo: widget.unlockedAccountInfo,
account: account)
..follow(context.watch<ContactInvitationListCubit>())),
// Chat Cubits
BlocProvider(
create: (context) => ActiveChatCubit(null,
routerCubit: context.watch<RouterCubit>())),
BlocProvider(
create: (context) => ChatListCubit(
unlockedAccountInfo: widget.unlockedAccountInfo,
activeChatCubit: context.watch<ActiveChatCubit>(),
account: account)),
// Conversation Cubits
BlocProvider(
create: (context) => ActiveConversationsBlocMapCubit(
unlockedAccountInfo: widget.unlockedAccountInfo,
contactListCubit: context.watch<ContactListCubit>())
..follow(context.watch<ChatListCubit>())),
BlocProvider(
create: (context) => ActiveSingleContactChatBlocMapCubit(
unlockedAccountInfo: widget.unlockedAccountInfo,
contactListCubit: context.watch<ContactListCubit>(),
chatListCubit: context.watch<ChatListCubit>())
..follow(context.watch<ActiveConversationsBlocMapCubit>())),
],
child: MultiBlocListener(listeners: [
BlocListener<WaitingInvitationsBlocMapCubit,

View File

@ -1,8 +1,6 @@
import 'dart:math';
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart';
import 'package:provider/provider.dart';
@ -35,17 +33,19 @@ class HomeShellState extends State<HomeShell> {
}
Widget buildWithLogin(BuildContext context) {
final activeLocalAccount = context.watch<ActiveLocalAccountCubit>().state;
final accountInfo = context.watch<ActiveAccountInfoCubit>().state;
final accountRecordsCubit = context.watch<AccountRecordsBlocMapCubit>();
if (activeLocalAccount == null) {
if (!accountInfo.active) {
// If no logged in user is active, show the loading panel
return const HomeNoActive();
}
final accountInfo =
AccountRepository.instance.getAccountInfo(activeLocalAccount);
final activeCubit =
accountRecordsCubit.tryOperate(activeLocalAccount, closure: (c) => c);
final superIdentityRecordKey =
accountInfo.unlockedAccountInfo?.superIdentityRecordKey;
final activeCubit = superIdentityRecordKey == null
? null
: accountRecordsCubit.tryOperate(superIdentityRecordKey,
closure: (c) => c);
if (activeCubit == null) {
return waitingPage();
}
@ -59,8 +59,8 @@ class HomeShellState extends State<HomeShell> {
return const HomeAccountLocked();
case AccountInfoStatus.accountReady:
return MultiProvider(providers: [
Provider<ActiveAccountInfo>.value(
value: accountInfo.activeAccountInfo!,
Provider<UnlockedAccountInfo>.value(
value: accountInfo.unlockedAccountInfo!,
),
Provider<AccountRecordCubit>.value(value: activeCubit),
Provider<ZoomDrawerController>.value(value: _zoomDrawerController),
@ -101,6 +101,7 @@ class HomeShellState extends State<HomeShell> {
// duration: const Duration(milliseconds: 250),
// reverseDuration: const Duration(milliseconds: 250),
menuScreenTapClose: true,
mainScreenTapClose: true,
mainScreenScale: .25,
slideWidth: min(360, MediaQuery.of(context).size.width * 0.9),
)));

View File

@ -20,6 +20,177 @@ import 'veilidchat.pbenum.dart';
export 'veilidchat.pbenum.dart';
class DHTDataReference extends $pb.GeneratedMessage {
factory DHTDataReference() => create();
DHTDataReference._() : super();
factory DHTDataReference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory DHTDataReference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DHTDataReference', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
..aOM<$0.TypedKey>(1, _omitFieldNames ? '' : 'dhtData', subBuilder: $0.TypedKey.create)
..aOM<$0.TypedKey>(2, _omitFieldNames ? '' : 'hash', subBuilder: $0.TypedKey.create)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
DHTDataReference clone() => DHTDataReference()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
DHTDataReference copyWith(void Function(DHTDataReference) updates) => super.copyWith((message) => updates(message as DHTDataReference)) as DHTDataReference;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static DHTDataReference create() => DHTDataReference._();
DHTDataReference createEmptyInstance() => create();
static $pb.PbList<DHTDataReference> createRepeated() => $pb.PbList<DHTDataReference>();
@$core.pragma('dart2js:noInline')
static DHTDataReference getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DHTDataReference>(create);
static DHTDataReference? _defaultInstance;
@$pb.TagNumber(1)
$0.TypedKey get dhtData => $_getN(0);
@$pb.TagNumber(1)
set dhtData($0.TypedKey v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasDhtData() => $_has(0);
@$pb.TagNumber(1)
void clearDhtData() => clearField(1);
@$pb.TagNumber(1)
$0.TypedKey ensureDhtData() => $_ensure(0);
@$pb.TagNumber(2)
$0.TypedKey get hash => $_getN(1);
@$pb.TagNumber(2)
set hash($0.TypedKey v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasHash() => $_has(1);
@$pb.TagNumber(2)
void clearHash() => clearField(2);
@$pb.TagNumber(2)
$0.TypedKey ensureHash() => $_ensure(1);
}
class BlockStoreDataReference extends $pb.GeneratedMessage {
factory BlockStoreDataReference() => create();
BlockStoreDataReference._() : super();
factory BlockStoreDataReference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory BlockStoreDataReference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BlockStoreDataReference', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
..aOM<$0.TypedKey>(1, _omitFieldNames ? '' : 'block', subBuilder: $0.TypedKey.create)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
BlockStoreDataReference clone() => BlockStoreDataReference()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
BlockStoreDataReference copyWith(void Function(BlockStoreDataReference) updates) => super.copyWith((message) => updates(message as BlockStoreDataReference)) as BlockStoreDataReference;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static BlockStoreDataReference create() => BlockStoreDataReference._();
BlockStoreDataReference createEmptyInstance() => create();
static $pb.PbList<BlockStoreDataReference> createRepeated() => $pb.PbList<BlockStoreDataReference>();
@$core.pragma('dart2js:noInline')
static BlockStoreDataReference getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<BlockStoreDataReference>(create);
static BlockStoreDataReference? _defaultInstance;
@$pb.TagNumber(1)
$0.TypedKey get block => $_getN(0);
@$pb.TagNumber(1)
set block($0.TypedKey v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasBlock() => $_has(0);
@$pb.TagNumber(1)
void clearBlock() => clearField(1);
@$pb.TagNumber(1)
$0.TypedKey ensureBlock() => $_ensure(0);
}
enum DataReference_Kind {
dhtData,
blockStoreData,
notSet
}
class DataReference extends $pb.GeneratedMessage {
factory DataReference() => create();
DataReference._() : super();
factory DataReference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory DataReference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static const $core.Map<$core.int, DataReference_Kind> _DataReference_KindByTag = {
1 : DataReference_Kind.dhtData,
2 : DataReference_Kind.blockStoreData,
0 : DataReference_Kind.notSet
};
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DataReference', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
..oo(0, [1, 2])
..aOM<DHTDataReference>(1, _omitFieldNames ? '' : 'dhtData', subBuilder: DHTDataReference.create)
..aOM<BlockStoreDataReference>(2, _omitFieldNames ? '' : 'blockStoreData', subBuilder: BlockStoreDataReference.create)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
DataReference clone() => DataReference()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
DataReference copyWith(void Function(DataReference) updates) => super.copyWith((message) => updates(message as DataReference)) as DataReference;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static DataReference create() => DataReference._();
DataReference createEmptyInstance() => create();
static $pb.PbList<DataReference> createRepeated() => $pb.PbList<DataReference>();
@$core.pragma('dart2js:noInline')
static DataReference getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DataReference>(create);
static DataReference? _defaultInstance;
DataReference_Kind whichKind() => _DataReference_KindByTag[$_whichOneof(0)]!;
void clearKind() => clearField($_whichOneof(0));
@$pb.TagNumber(1)
DHTDataReference get dhtData => $_getN(0);
@$pb.TagNumber(1)
set dhtData(DHTDataReference v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasDhtData() => $_has(0);
@$pb.TagNumber(1)
void clearDhtData() => clearField(1);
@$pb.TagNumber(1)
DHTDataReference ensureDhtData() => $_ensure(0);
@$pb.TagNumber(2)
BlockStoreDataReference get blockStoreData => $_getN(1);
@$pb.TagNumber(2)
set blockStoreData(BlockStoreDataReference v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasBlockStoreData() => $_has(1);
@$pb.TagNumber(2)
void clearBlockStoreData() => clearField(2);
@$pb.TagNumber(2)
BlockStoreDataReference ensureBlockStoreData() => $_ensure(1);
}
enum Attachment_Kind {
media,
notSet
@ -98,7 +269,7 @@ class AttachmentMedia extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'AttachmentMedia', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
..aOS(1, _omitFieldNames ? '' : 'mime')
..aOS(2, _omitFieldNames ? '' : 'name')
..aOM<$1.DataReference>(3, _omitFieldNames ? '' : 'content', subBuilder: $1.DataReference.create)
..aOM<DataReference>(3, _omitFieldNames ? '' : 'content', subBuilder: DataReference.create)
..hasRequiredFields = false
;
@ -142,15 +313,15 @@ class AttachmentMedia extends $pb.GeneratedMessage {
void clearName() => clearField(2);
@$pb.TagNumber(3)
$1.DataReference get content => $_getN(2);
DataReference get content => $_getN(2);
@$pb.TagNumber(3)
set content($1.DataReference v) { setField(3, v); }
set content(DataReference v) { setField(3, v); }
@$pb.TagNumber(3)
$core.bool hasContent() => $_has(2);
@$pb.TagNumber(3)
void clearContent() => clearField(3);
@$pb.TagNumber(3)
$1.DataReference ensureContent() => $_ensure(2);
DataReference ensureContent() => $_ensure(2);
}
class Permissions extends $pb.GeneratedMessage {
@ -276,7 +447,7 @@ class ChatSettings extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ChatSettings', package: const $pb.PackageName(_omitMessageNames ? '' : 'veilidchat'), createEmptyInstance: create)
..aOS(1, _omitFieldNames ? '' : 'title')
..aOS(2, _omitFieldNames ? '' : 'description')
..aOM<$1.DataReference>(3, _omitFieldNames ? '' : 'icon', subBuilder: $1.DataReference.create)
..aOM<DataReference>(3, _omitFieldNames ? '' : 'icon', subBuilder: DataReference.create)
..a<$fixnum.Int64>(4, _omitFieldNames ? '' : 'defaultExpiration', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
..hasRequiredFields = false
;
@ -321,15 +492,15 @@ class ChatSettings extends $pb.GeneratedMessage {
void clearDescription() => clearField(2);
@$pb.TagNumber(3)
$1.DataReference get icon => $_getN(2);
DataReference get icon => $_getN(2);
@$pb.TagNumber(3)
set icon($1.DataReference v) { setField(3, v); }
set icon(DataReference v) { setField(3, v); }
@$pb.TagNumber(3)
$core.bool hasIcon() => $_has(2);
@$pb.TagNumber(3)
void clearIcon() => clearField(3);
@$pb.TagNumber(3)
$1.DataReference ensureIcon() => $_ensure(2);
DataReference ensureIcon() => $_ensure(2);
@$pb.TagNumber(4)
$fixnum.Int64 get defaultExpiration => $_getI64(3);
@ -1224,7 +1395,7 @@ class Profile extends $pb.GeneratedMessage {
..aOS(3, _omitFieldNames ? '' : 'about')
..aOS(4, _omitFieldNames ? '' : 'status')
..e<Availability>(5, _omitFieldNames ? '' : 'availability', $pb.PbFieldType.OE, defaultOrMaker: Availability.AVAILABILITY_UNSPECIFIED, valueOf: Availability.valueOf, enumValues: Availability.values)
..aOM<$0.TypedKey>(6, _omitFieldNames ? '' : 'avatar', subBuilder: $0.TypedKey.create)
..aOM<DataReference>(6, _omitFieldNames ? '' : 'avatar', subBuilder: DataReference.create)
..hasRequiredFields = false
;
@ -1295,15 +1466,15 @@ class Profile extends $pb.GeneratedMessage {
void clearAvailability() => clearField(5);
@$pb.TagNumber(6)
$0.TypedKey get avatar => $_getN(5);
DataReference get avatar => $_getN(5);
@$pb.TagNumber(6)
set avatar($0.TypedKey v) { setField(6, v); }
set avatar(DataReference v) { setField(6, v); }
@$pb.TagNumber(6)
$core.bool hasAvatar() => $_has(5);
@$pb.TagNumber(6)
void clearAvatar() => clearField(6);
@$pb.TagNumber(6)
$0.TypedKey ensureAvatar() => $_ensure(5);
DataReference ensureAvatar() => $_ensure(5);
}
class Account extends $pb.GeneratedMessage {

View File

@ -65,6 +65,51 @@ final $typed_data.Uint8List scopeDescriptor = $convert.base64Decode(
'CgVTY29wZRIMCghXQVRDSEVSUxAAEg0KCU1PREVSQVRFRBABEgsKB1RBTEtFUlMQAhIOCgpNT0'
'RFUkFUT1JTEAMSCgoGQURNSU5TEAQ=');
@$core.Deprecated('Use dHTDataReferenceDescriptor instead')
const DHTDataReference$json = {
'1': 'DHTDataReference',
'2': [
{'1': 'dht_data', '3': 1, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'dhtData'},
{'1': 'hash', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'hash'},
],
};
/// Descriptor for `DHTDataReference`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List dHTDataReferenceDescriptor = $convert.base64Decode(
'ChBESFREYXRhUmVmZXJlbmNlEisKCGRodF9kYXRhGAEgASgLMhAudmVpbGlkLlR5cGVkS2V5Ug'
'dkaHREYXRhEiQKBGhhc2gYAiABKAsyEC52ZWlsaWQuVHlwZWRLZXlSBGhhc2g=');
@$core.Deprecated('Use blockStoreDataReferenceDescriptor instead')
const BlockStoreDataReference$json = {
'1': 'BlockStoreDataReference',
'2': [
{'1': 'block', '3': 1, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'block'},
],
};
/// Descriptor for `BlockStoreDataReference`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List blockStoreDataReferenceDescriptor = $convert.base64Decode(
'ChdCbG9ja1N0b3JlRGF0YVJlZmVyZW5jZRImCgVibG9jaxgBIAEoCzIQLnZlaWxpZC5UeXBlZE'
'tleVIFYmxvY2s=');
@$core.Deprecated('Use dataReferenceDescriptor instead')
const DataReference$json = {
'1': 'DataReference',
'2': [
{'1': 'dht_data', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.DHTDataReference', '9': 0, '10': 'dhtData'},
{'1': 'block_store_data', '3': 2, '4': 1, '5': 11, '6': '.veilidchat.BlockStoreDataReference', '9': 0, '10': 'blockStoreData'},
],
'8': [
{'1': 'kind'},
],
};
/// Descriptor for `DataReference`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List dataReferenceDescriptor = $convert.base64Decode(
'Cg1EYXRhUmVmZXJlbmNlEjkKCGRodF9kYXRhGAEgASgLMhwudmVpbGlkY2hhdC5ESFREYXRhUm'
'VmZXJlbmNlSABSB2RodERhdGESTwoQYmxvY2tfc3RvcmVfZGF0YRgCIAEoCzIjLnZlaWxpZGNo'
'YXQuQmxvY2tTdG9yZURhdGFSZWZlcmVuY2VIAFIOYmxvY2tTdG9yZURhdGFCBgoEa2luZA==');
@$core.Deprecated('Use attachmentDescriptor instead')
const Attachment$json = {
'1': 'Attachment',
@ -89,14 +134,14 @@ const AttachmentMedia$json = {
'2': [
{'1': 'mime', '3': 1, '4': 1, '5': 9, '10': 'mime'},
{'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
{'1': 'content', '3': 3, '4': 1, '5': 11, '6': '.dht.DataReference', '10': 'content'},
{'1': 'content', '3': 3, '4': 1, '5': 11, '6': '.veilidchat.DataReference', '10': 'content'},
],
};
/// Descriptor for `AttachmentMedia`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List attachmentMediaDescriptor = $convert.base64Decode(
'Cg9BdHRhY2htZW50TWVkaWESEgoEbWltZRgBIAEoCVIEbWltZRISCgRuYW1lGAIgASgJUgRuYW'
'1lEiwKB2NvbnRlbnQYAyABKAsyEi5kaHQuRGF0YVJlZmVyZW5jZVIHY29udGVudA==');
'1lEjMKB2NvbnRlbnQYAyABKAsyGS52ZWlsaWRjaGF0LkRhdGFSZWZlcmVuY2VSB2NvbnRlbnQ=');
@$core.Deprecated('Use permissionsDescriptor instead')
const Permissions$json = {
@ -140,7 +185,7 @@ const ChatSettings$json = {
'2': [
{'1': 'title', '3': 1, '4': 1, '5': 9, '10': 'title'},
{'1': 'description', '3': 2, '4': 1, '5': 9, '10': 'description'},
{'1': 'icon', '3': 3, '4': 1, '5': 11, '6': '.dht.DataReference', '9': 0, '10': 'icon', '17': true},
{'1': 'icon', '3': 3, '4': 1, '5': 11, '6': '.veilidchat.DataReference', '9': 0, '10': 'icon', '17': true},
{'1': 'default_expiration', '3': 4, '4': 1, '5': 4, '10': 'defaultExpiration'},
],
'8': [
@ -151,9 +196,9 @@ const ChatSettings$json = {
/// Descriptor for `ChatSettings`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List chatSettingsDescriptor = $convert.base64Decode(
'CgxDaGF0U2V0dGluZ3MSFAoFdGl0bGUYASABKAlSBXRpdGxlEiAKC2Rlc2NyaXB0aW9uGAIgAS'
'gJUgtkZXNjcmlwdGlvbhIrCgRpY29uGAMgASgLMhIuZGh0LkRhdGFSZWZlcmVuY2VIAFIEaWNv'
'bogBARItChJkZWZhdWx0X2V4cGlyYXRpb24YBCABKARSEWRlZmF1bHRFeHBpcmF0aW9uQgcKBV'
'9pY29u');
'gJUgtkZXNjcmlwdGlvbhIyCgRpY29uGAMgASgLMhkudmVpbGlkY2hhdC5EYXRhUmVmZXJlbmNl'
'SABSBGljb26IAQESLQoSZGVmYXVsdF9leHBpcmF0aW9uGAQgASgEUhFkZWZhdWx0RXhwaXJhdG'
'lvbkIHCgVfaWNvbg==');
@$core.Deprecated('Use messageDescriptor instead')
const Message$json = {
@ -365,7 +410,7 @@ const Profile$json = {
{'1': 'about', '3': 3, '4': 1, '5': 9, '10': 'about'},
{'1': 'status', '3': 4, '4': 1, '5': 9, '10': 'status'},
{'1': 'availability', '3': 5, '4': 1, '5': 14, '6': '.veilidchat.Availability', '10': 'availability'},
{'1': 'avatar', '3': 6, '4': 1, '5': 11, '6': '.veilid.TypedKey', '9': 0, '10': 'avatar', '17': true},
{'1': 'avatar', '3': 6, '4': 1, '5': 11, '6': '.veilidchat.DataReference', '9': 0, '10': 'avatar', '17': true},
],
'8': [
{'1': '_avatar'},
@ -376,9 +421,9 @@ const Profile$json = {
final $typed_data.Uint8List profileDescriptor = $convert.base64Decode(
'CgdQcm9maWxlEhIKBG5hbWUYASABKAlSBG5hbWUSGgoIcHJvbm91bnMYAiABKAlSCHByb25vdW'
'5zEhQKBWFib3V0GAMgASgJUgVhYm91dBIWCgZzdGF0dXMYBCABKAlSBnN0YXR1cxI8CgxhdmFp'
'bGFiaWxpdHkYBSABKA4yGC52ZWlsaWRjaGF0LkF2YWlsYWJpbGl0eVIMYXZhaWxhYmlsaXR5Ei'
'0KBmF2YXRhchgGIAEoCzIQLnZlaWxpZC5UeXBlZEtleUgAUgZhdmF0YXKIAQFCCQoHX2F2YXRh'
'cg==');
'bGFiaWxpdHkYBSABKA4yGC52ZWlsaWRjaGF0LkF2YWlsYWJpbGl0eVIMYXZhaWxhYmlsaXR5Ej'
'YKBmF2YXRhchgGIAEoCzIZLnZlaWxpZGNoYXQuRGF0YVJlZmVyZW5jZUgAUgZhdmF0YXKIAQFC'
'CQoHX2F2YXRhcg==');
@$core.Deprecated('Use accountDescriptor instead')
const Account$json = {

View File

@ -47,6 +47,31 @@ enum Scope {
ADMINS = 4;
}
////////////////////////////////////////////////////////////////////////////////////
// Data
////////////////////////////////////////////////////////////////////////////////////
// Reference to data on the DHT
message DHTDataReference {
veilid.TypedKey dht_data = 1;
veilid.TypedKey hash = 2;
}
// Reference to data on the BlockStore
message BlockStoreDataReference {
veilid.TypedKey block = 1;
}
// DataReference
// Pointer to data somewhere in Veilid
// Abstraction over DHTData and BlockStore
message DataReference {
oneof kind {
DHTDataReference dht_data = 1;
BlockStoreDataReference block_store_data = 2;
}
}
////////////////////////////////////////////////////////////////////////////////////
// Attachments
////////////////////////////////////////////////////////////////////////////////////
@ -67,10 +92,9 @@ message AttachmentMedia {
// Title or filename
string name = 2;
// Pointer to the data content
dht.DataReference content = 3;
DataReference content = 3;
}
////////////////////////////////////////////////////////////////////////////////////
// Chat room controls
////////////////////////////////////////////////////////////////////////////////////
@ -106,7 +130,7 @@ message ChatSettings {
// Description for the chat
string description = 2;
// Icon for the chat
optional dht.DataReference icon = 3;
optional DataReference icon = 3;
// Default message expiration duration (in us)
uint64 default_expiration = 4;
}
@ -285,8 +309,8 @@ message Profile {
string status = 4;
// Availability
Availability availability = 5;
// Avatar DHTData
optional veilid.TypedKey avatar = 6;
// Avatar
optional DataReference avatar = 6;
}
// A record of an individual account

View File

@ -1,5 +1,6 @@
import 'package:async_tools/async_tools.dart';
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
@ -11,7 +12,7 @@ import '../../theme/theme.dart';
import '../cubit/connection_state_cubit.dart';
class SignalStrengthMeterWidget extends StatelessWidget {
const SignalStrengthMeterWidget({super.key});
const SignalStrengthMeterWidget({super.key, this.color, this.inactiveColor});
@override
// ignore: prefer_expression_function_bodies
@ -33,32 +34,35 @@ class SignalStrengthMeterWidget extends StatelessWidget {
switch (connectionState.attachment.state) {
case AttachmentState.detached:
iconWidget = Icon(Icons.signal_cellular_nodata,
size: iconSize, color: scale.primaryScale.primaryText);
size: iconSize,
color: this.color ?? scale.primaryScale.primaryText);
return;
case AttachmentState.detaching:
iconWidget = Icon(Icons.signal_cellular_off,
size: iconSize, color: scale.primaryScale.primaryText);
size: iconSize,
color: this.color ?? scale.primaryScale.primaryText);
return;
case AttachmentState.attaching:
value = 0;
color = scale.primaryScale.primaryText;
color = this.color ?? scale.primaryScale.primaryText;
case AttachmentState.attachedWeak:
value = 1;
color = scale.primaryScale.primaryText;
color = this.color ?? scale.primaryScale.primaryText;
case AttachmentState.attachedStrong:
value = 2;
color = scale.primaryScale.primaryText;
color = this.color ?? scale.primaryScale.primaryText;
case AttachmentState.attachedGood:
value = 3;
color = scale.primaryScale.primaryText;
color = this.color ?? scale.primaryScale.primaryText;
case AttachmentState.fullyAttached:
value = 4;
color = scale.primaryScale.primaryText;
color = this.color ?? scale.primaryScale.primaryText;
case AttachmentState.overAttached:
value = 4;
color = scale.primaryScale.primaryText;
color = this.color ?? scale.primaryScale.primaryText;
}
inactiveColor = scale.primaryScale.primaryText;
inactiveColor =
this.inactiveColor ?? scale.primaryScale.primaryText;
iconWidget = SignalStrengthIndicator.bars(
value: value,
@ -86,4 +90,16 @@ class SignalStrengthMeterWidget extends StatelessWidget {
child: iconWidget);
});
}
////////////////////////////////////////////////////////////////////////////
final Color? color;
final Color? inactiveColor;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(ColorProperty('color', color))
..add(ColorProperty('inactiveColor', inactiveColor));
}
}

View File

@ -23,7 +23,6 @@ message DHTData {
uint32 size = 4;
}
// DHTLog - represents a ring buffer of many elements with append/truncate semantics
// Header in subkey 0 of first key follows this structure
message DHTLog {
@ -62,27 +61,6 @@ message DHTShortArray {
// calculated through iteration
}
// Reference to data on the DHT
message DHTDataReference {
veilid.TypedKey dht_data = 1;
veilid.TypedKey hash = 2;
}
// Reference to data on the BlockStore
message BlockStoreDataReference {
veilid.TypedKey block = 1;
}
// DataReference
// Pointer to data somewhere in Veilid
// Abstraction over DHTData and BlockStore
message DataReference {
oneof kind {
DHTDataReference dht_data = 1;
BlockStoreDataReference block_store_data = 2;
}
}
// A pointer to an child DHT record
message OwnedDHTRecordPointer {
// DHT Record key

View File

@ -195,177 +195,6 @@ class DHTShortArray extends $pb.GeneratedMessage {
$core.List<$core.int> get seqs => $_getList(2);
}
class DHTDataReference extends $pb.GeneratedMessage {
factory DHTDataReference() => create();
DHTDataReference._() : super();
factory DHTDataReference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory DHTDataReference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DHTDataReference', package: const $pb.PackageName(_omitMessageNames ? '' : 'dht'), createEmptyInstance: create)
..aOM<$0.TypedKey>(1, _omitFieldNames ? '' : 'dhtData', subBuilder: $0.TypedKey.create)
..aOM<$0.TypedKey>(2, _omitFieldNames ? '' : 'hash', subBuilder: $0.TypedKey.create)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
DHTDataReference clone() => DHTDataReference()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
DHTDataReference copyWith(void Function(DHTDataReference) updates) => super.copyWith((message) => updates(message as DHTDataReference)) as DHTDataReference;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static DHTDataReference create() => DHTDataReference._();
DHTDataReference createEmptyInstance() => create();
static $pb.PbList<DHTDataReference> createRepeated() => $pb.PbList<DHTDataReference>();
@$core.pragma('dart2js:noInline')
static DHTDataReference getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DHTDataReference>(create);
static DHTDataReference? _defaultInstance;
@$pb.TagNumber(1)
$0.TypedKey get dhtData => $_getN(0);
@$pb.TagNumber(1)
set dhtData($0.TypedKey v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasDhtData() => $_has(0);
@$pb.TagNumber(1)
void clearDhtData() => clearField(1);
@$pb.TagNumber(1)
$0.TypedKey ensureDhtData() => $_ensure(0);
@$pb.TagNumber(2)
$0.TypedKey get hash => $_getN(1);
@$pb.TagNumber(2)
set hash($0.TypedKey v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasHash() => $_has(1);
@$pb.TagNumber(2)
void clearHash() => clearField(2);
@$pb.TagNumber(2)
$0.TypedKey ensureHash() => $_ensure(1);
}
class BlockStoreDataReference extends $pb.GeneratedMessage {
factory BlockStoreDataReference() => create();
BlockStoreDataReference._() : super();
factory BlockStoreDataReference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory BlockStoreDataReference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BlockStoreDataReference', package: const $pb.PackageName(_omitMessageNames ? '' : 'dht'), createEmptyInstance: create)
..aOM<$0.TypedKey>(1, _omitFieldNames ? '' : 'block', subBuilder: $0.TypedKey.create)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
BlockStoreDataReference clone() => BlockStoreDataReference()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
BlockStoreDataReference copyWith(void Function(BlockStoreDataReference) updates) => super.copyWith((message) => updates(message as BlockStoreDataReference)) as BlockStoreDataReference;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static BlockStoreDataReference create() => BlockStoreDataReference._();
BlockStoreDataReference createEmptyInstance() => create();
static $pb.PbList<BlockStoreDataReference> createRepeated() => $pb.PbList<BlockStoreDataReference>();
@$core.pragma('dart2js:noInline')
static BlockStoreDataReference getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<BlockStoreDataReference>(create);
static BlockStoreDataReference? _defaultInstance;
@$pb.TagNumber(1)
$0.TypedKey get block => $_getN(0);
@$pb.TagNumber(1)
set block($0.TypedKey v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasBlock() => $_has(0);
@$pb.TagNumber(1)
void clearBlock() => clearField(1);
@$pb.TagNumber(1)
$0.TypedKey ensureBlock() => $_ensure(0);
}
enum DataReference_Kind {
dhtData,
blockStoreData,
notSet
}
class DataReference extends $pb.GeneratedMessage {
factory DataReference() => create();
DataReference._() : super();
factory DataReference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
factory DataReference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
static const $core.Map<$core.int, DataReference_Kind> _DataReference_KindByTag = {
1 : DataReference_Kind.dhtData,
2 : DataReference_Kind.blockStoreData,
0 : DataReference_Kind.notSet
};
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DataReference', package: const $pb.PackageName(_omitMessageNames ? '' : 'dht'), createEmptyInstance: create)
..oo(0, [1, 2])
..aOM<DHTDataReference>(1, _omitFieldNames ? '' : 'dhtData', subBuilder: DHTDataReference.create)
..aOM<BlockStoreDataReference>(2, _omitFieldNames ? '' : 'blockStoreData', subBuilder: BlockStoreDataReference.create)
..hasRequiredFields = false
;
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
'Will be removed in next major version')
DataReference clone() => DataReference()..mergeFromMessage(this);
@$core.Deprecated(
'Using this can add significant overhead to your binary. '
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
'Will be removed in next major version')
DataReference copyWith(void Function(DataReference) updates) => super.copyWith((message) => updates(message as DataReference)) as DataReference;
$pb.BuilderInfo get info_ => _i;
@$core.pragma('dart2js:noInline')
static DataReference create() => DataReference._();
DataReference createEmptyInstance() => create();
static $pb.PbList<DataReference> createRepeated() => $pb.PbList<DataReference>();
@$core.pragma('dart2js:noInline')
static DataReference getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DataReference>(create);
static DataReference? _defaultInstance;
DataReference_Kind whichKind() => _DataReference_KindByTag[$_whichOneof(0)]!;
void clearKind() => clearField($_whichOneof(0));
@$pb.TagNumber(1)
DHTDataReference get dhtData => $_getN(0);
@$pb.TagNumber(1)
set dhtData(DHTDataReference v) { setField(1, v); }
@$pb.TagNumber(1)
$core.bool hasDhtData() => $_has(0);
@$pb.TagNumber(1)
void clearDhtData() => clearField(1);
@$pb.TagNumber(1)
DHTDataReference ensureDhtData() => $_ensure(0);
@$pb.TagNumber(2)
BlockStoreDataReference get blockStoreData => $_getN(1);
@$pb.TagNumber(2)
set blockStoreData(BlockStoreDataReference v) { setField(2, v); }
@$pb.TagNumber(2)
$core.bool hasBlockStoreData() => $_has(1);
@$pb.TagNumber(2)
void clearBlockStoreData() => clearField(2);
@$pb.TagNumber(2)
BlockStoreDataReference ensureBlockStoreData() => $_ensure(1);
}
class OwnedDHTRecordPointer extends $pb.GeneratedMessage {
factory OwnedDHTRecordPointer() => create();
OwnedDHTRecordPointer._() : super();

View File

@ -60,51 +60,6 @@ final $typed_data.Uint8List dHTShortArrayDescriptor = $convert.base64Decode(
'Cg1ESFRTaG9ydEFycmF5EiQKBGtleXMYASADKAsyEC52ZWlsaWQuVHlwZWRLZXlSBGtleXMSFA'
'oFaW5kZXgYAiABKAxSBWluZGV4EhIKBHNlcXMYAyADKA1SBHNlcXM=');
@$core.Deprecated('Use dHTDataReferenceDescriptor instead')
const DHTDataReference$json = {
'1': 'DHTDataReference',
'2': [
{'1': 'dht_data', '3': 1, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'dhtData'},
{'1': 'hash', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'hash'},
],
};
/// Descriptor for `DHTDataReference`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List dHTDataReferenceDescriptor = $convert.base64Decode(
'ChBESFREYXRhUmVmZXJlbmNlEisKCGRodF9kYXRhGAEgASgLMhAudmVpbGlkLlR5cGVkS2V5Ug'
'dkaHREYXRhEiQKBGhhc2gYAiABKAsyEC52ZWlsaWQuVHlwZWRLZXlSBGhhc2g=');
@$core.Deprecated('Use blockStoreDataReferenceDescriptor instead')
const BlockStoreDataReference$json = {
'1': 'BlockStoreDataReference',
'2': [
{'1': 'block', '3': 1, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'block'},
],
};
/// Descriptor for `BlockStoreDataReference`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List blockStoreDataReferenceDescriptor = $convert.base64Decode(
'ChdCbG9ja1N0b3JlRGF0YVJlZmVyZW5jZRImCgVibG9jaxgBIAEoCzIQLnZlaWxpZC5UeXBlZE'
'tleVIFYmxvY2s=');
@$core.Deprecated('Use dataReferenceDescriptor instead')
const DataReference$json = {
'1': 'DataReference',
'2': [
{'1': 'dht_data', '3': 1, '4': 1, '5': 11, '6': '.dht.DHTDataReference', '9': 0, '10': 'dhtData'},
{'1': 'block_store_data', '3': 2, '4': 1, '5': 11, '6': '.dht.BlockStoreDataReference', '9': 0, '10': 'blockStoreData'},
],
'8': [
{'1': 'kind'},
],
};
/// Descriptor for `DataReference`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List dataReferenceDescriptor = $convert.base64Decode(
'Cg1EYXRhUmVmZXJlbmNlEjIKCGRodF9kYXRhGAEgASgLMhUuZGh0LkRIVERhdGFSZWZlcmVuY2'
'VIAFIHZGh0RGF0YRJIChBibG9ja19zdG9yZV9kYXRhGAIgASgLMhwuZGh0LkJsb2NrU3RvcmVE'
'YXRhUmVmZXJlbmNlSABSDmJsb2NrU3RvcmVEYXRhQgYKBGtpbmQ=');
@$core.Deprecated('Use ownedDHTRecordPointerDescriptor instead')
const OwnedDHTRecordPointer$json = {
'1': 'OwnedDHTRecordPointer',

View File

@ -52,11 +52,10 @@ packages:
bloc_advanced_tools:
dependency: "direct main"
description:
name: bloc_advanced_tools
sha256: "0cf9b3a73a67addfe22ec3f97a1ac240f6ad53870d6b21a980260f390d7901cd"
url: "https://pub.dev"
source: hosted
version: "0.1.2"
path: "../../../bloc_advanced_tools"
relative: true
source: path
version: "0.1.3"
boolean_selector:
dependency: transitive
description:

View File

@ -9,7 +9,7 @@ environment:
dependencies:
async_tools: ^0.1.2
bloc: ^8.1.4
bloc_advanced_tools: ^0.1.2
bloc_advanced_tools: ^0.1.3
charcode: ^1.3.1
collection: ^1.18.0
equatable: ^2.0.5
@ -24,11 +24,11 @@ dependencies:
# veilid: ^0.0.1
path: ../../../veilid/veilid-flutter
# dependency_overrides:
dependency_overrides:
# async_tools:
# path: ../../../dart_async_tools
# bloc_advanced_tools:
# path: ../../../bloc_advanced_tools
bloc_advanced_tools:
path: ../../../bloc_advanced_tools
dev_dependencies:
build_runner: ^2.4.10

View File

@ -100,11 +100,10 @@ packages:
bloc_advanced_tools:
dependency: "direct main"
description:
name: bloc_advanced_tools
sha256: "0cf9b3a73a67addfe22ec3f97a1ac240f6ad53870d6b21a980260f390d7901cd"
url: "https://pub.dev"
source: hosted
version: "0.1.2"
path: "../bloc_advanced_tools"
relative: true
source: path
version: "0.1.3"
blurry_modal_progress_hud:
dependency: "direct main"
description:

View File

@ -16,7 +16,7 @@ dependencies:
badges: ^3.1.2
basic_utils: ^5.7.0
bloc: ^8.1.4
bloc_advanced_tools: ^0.1.2
bloc_advanced_tools: ^0.1.3
blurry_modal_progress_hud: ^1.1.1
change_case: ^2.1.0
charcode: ^1.3.1
@ -93,11 +93,11 @@ dependencies:
xterm: ^4.0.0
zxing2: ^0.2.3
# dependency_overrides:
dependency_overrides:
# async_tools:
# path: ../dart_async_tools
# bloc_advanced_tools:
# path: ../bloc_advanced_tools
bloc_advanced_tools:
path: ../bloc_advanced_tools
# flutter_chat_ui:
# path: ../flutter_chat_ui