mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-02-22 07:49:54 -05:00
refactor and cleanup in prep for profile changing
This commit is contained in:
parent
87bb1657c7
commit
56d65442f4
@ -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.",
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
@ -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';
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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,
|
||||
});
|
@ -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;
|
||||
}
|
||||
|
163
lib/account_manager/views/edit_account_page.dart
Normal file
163
lib/account_manager/views/edit_account_page.dart
Normal 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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
106
lib/account_manager/views/profile_edit_form.dart
Normal file
106
lib/account_manager/views/profile_edit_form.dart
Normal 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,
|
||||
);
|
||||
}
|
@ -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');
|
||||
})
|
||||
|
@ -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) =>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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}) {
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1,3 +1 @@
|
||||
export 'active_single_contact_chat_bloc_map_cubit.dart';
|
||||
export 'active_conversations_bloc_map_cubit.dart';
|
||||
export 'chat_list_cubit.dart';
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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(() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,2 +1 @@
|
||||
export 'contact_list_cubit.dart';
|
||||
export 'conversation_cubit.dart';
|
||||
|
1
lib/conversation/conversation.dart
Normal file
1
lib/conversation/conversation.dart
Normal file
@ -0,0 +1 @@
|
||||
export 'cubits/cubits.dart';
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
3
lib/conversation/cubits/cubits.dart
Normal file
3
lib/conversation/cubits/cubits.dart
Normal 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';
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
)));
|
||||
|
@ -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 {
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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',
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user