From 31f562119a3701546001af346ddb162afea3f461 Mon Sep 17 00:00:00 2001 From: Christien Rioux Date: Thu, 4 Jan 2024 22:29:43 -0500 Subject: [PATCH] checkpoint --- build.sh | 5 + .../local_accounts_cubit.dart | 2 + .../local_accounts_state.dart | 11 + .../user_logins_cubit/user_logins_cubit.dart | 2 + .../user_logins_cubit/user_logins_state.dart | 11 + .../encryption_key_type.dart | 2 +- .../local_account}/local_account.dart | 2 +- .../local_account}/local_account.freezed.dart | 0 .../local_account}/local_account.g.dart | 0 lib/account_manager/models/models.dart | 4 + .../new_profile_spec.dart | 0 .../user_login}/user_login.dart | 0 .../user_login}/user_login.freezed.dart | 0 .../user_login}/user_login.g.dart | 0 .../account_repository.dart | 12 +- .../account_repository/active_logins.dart | 2 +- .../new_account_page/new_account_page.dart | 22 +- lib/init.dart | 36 +-- .../pages => layout}/chat_only.dart | 0 .../default_app_bar.dart | 0 .../pages => layout}/edit_account.dart | 0 .../pages => layout}/edit_contact.dart | 0 lib/layout/home.dart | 235 +++++++++++++++ .../pages => layout}/index.dart | 0 lib/layout/layout.dart | 1 + .../pages => layout}/main_pager/account.dart | 0 .../pages => layout}/main_pager/chats.dart | 0 .../main_pager/main_pager.dart | 0 .../pages => layout}/settings.dart | 0 .../components/signal_strength_meter.dart | 66 ----- lib/old_to_refactor/pages/home.dart | 269 ------------------ .../providers/connection_state.dart | 29 -- .../providers/connection_state.freezed.dart | 138 --------- lib/processor.dart | 103 ------- lib/router/cubit/router_cubit.dart | 5 +- lib/theme/models/models.dart | 4 + lib/theme/{ => models}/radix_generator.dart | 0 lib/theme/{ => models}/scale_color.dart | 0 lib/theme/{ => models}/scale_scheme.dart | 0 lib/theme/{ => models}/theme_preference.dart | 0 .../theme_preference.freezed.dart | 0 .../{ => models}/theme_preference.g.dart | 0 .../{ => repository}/theme_repository.dart | 3 +- lib/theme/theme.dart | 5 +- lib/tick.dart | 218 +++++++------- lib/tools/loggy.dart | 4 +- lib/tools/stream_wrapper_cubit.dart | 25 ++ lib/tools/tools.dart | 3 +- .../cubit/connection_state_cubit.dart | 12 + lib/veilid_processor/models/models.dart | 1 + .../models/processor_connection_state.dart | 19 ++ .../processor_connection_state.freezed.dart | 184 ++++++++++++ .../repository/processor_repository.dart | 132 +++++++++ lib/veilid_processor/veilid_processor.dart | 4 + .../views}/developer.dart | 6 +- .../views/signal_strength_meter.dart | 88 ++++++ lib/veilid_processor/views/views.dart | 2 + .../lib/dht_support/dht_support.dart | 1 + .../lib/dht_support/src/dht_record.dart | 25 +- .../lib/dht_support/src/dht_record_cubit.dart | 53 ++++ .../lib/dht_support/src/dht_record_pool.dart | 199 ++++++++++--- .../lib/dht_support/src/dht_short_array.dart | 16 +- .../lib/src}/async_table_db_backed_cubit.dart | 5 +- .../veilid_support/lib/src}/async_value.dart | 0 .../lib/src}/async_value.freezed.dart | 0 packages/veilid_support/lib/src/identity.dart | 10 +- .../veilid_support/lib/src/veilid_log.dart | 2 +- .../veilid_support/lib/veilid_support.dart | 1 + packages/veilid_support/pubspec.lock | 8 + packages/veilid_support/pubspec.yaml | 4 +- 70 files changed, 1174 insertions(+), 817 deletions(-) rename lib/account_manager/{repository/account_repository => models}/encryption_key_type.dart (98%) rename lib/account_manager/{repository/account_repository => models/local_account}/local_account.dart (96%) rename lib/account_manager/{repository/account_repository => models/local_account}/local_account.freezed.dart (100%) rename lib/account_manager/{repository/account_repository => models/local_account}/local_account.g.dart (100%) create mode 100644 lib/account_manager/models/models.dart rename lib/account_manager/{repository/account_repository => models}/new_profile_spec.dart (100%) rename lib/account_manager/{repository/account_repository => models/user_login}/user_login.dart (100%) rename lib/account_manager/{repository/account_repository => models/user_login}/user_login.freezed.dart (100%) rename lib/account_manager/{repository/account_repository => models/user_login}/user_login.g.dart (100%) rename lib/{old_to_refactor/pages => layout}/chat_only.dart (100%) rename lib/{old_to_refactor/components => layout}/default_app_bar.dart (100%) rename lib/{old_to_refactor/pages => layout}/edit_account.dart (100%) rename lib/{old_to_refactor/pages => layout}/edit_contact.dart (100%) create mode 100644 lib/layout/home.dart rename lib/{old_to_refactor/pages => layout}/index.dart (100%) create mode 100644 lib/layout/layout.dart rename lib/{old_to_refactor/pages => layout}/main_pager/account.dart (100%) rename lib/{old_to_refactor/pages => layout}/main_pager/chats.dart (100%) rename lib/{old_to_refactor/pages => layout}/main_pager/main_pager.dart (100%) rename lib/{old_to_refactor/pages => layout}/settings.dart (100%) delete mode 100644 lib/old_to_refactor/components/signal_strength_meter.dart delete mode 100644 lib/old_to_refactor/pages/home.dart delete mode 100644 lib/old_to_refactor/providers/connection_state.dart delete mode 100644 lib/old_to_refactor/providers/connection_state.freezed.dart delete mode 100644 lib/processor.dart create mode 100644 lib/theme/models/models.dart rename lib/theme/{ => models}/radix_generator.dart (100%) rename lib/theme/{ => models}/scale_color.dart (100%) rename lib/theme/{ => models}/scale_scheme.dart (100%) rename lib/theme/{ => models}/theme_preference.dart (100%) rename lib/theme/{ => models}/theme_preference.freezed.dart (100%) rename lib/theme/{ => models}/theme_preference.g.dart (100%) rename lib/theme/{ => repository}/theme_repository.dart (98%) create mode 100644 lib/tools/stream_wrapper_cubit.dart create mode 100644 lib/veilid_processor/cubit/connection_state_cubit.dart create mode 100644 lib/veilid_processor/models/models.dart create mode 100644 lib/veilid_processor/models/processor_connection_state.dart create mode 100644 lib/veilid_processor/models/processor_connection_state.freezed.dart create mode 100644 lib/veilid_processor/repository/processor_repository.dart create mode 100644 lib/veilid_processor/veilid_processor.dart rename lib/{old_to_refactor/pages => veilid_processor/views}/developer.dart (98%) create mode 100644 lib/veilid_processor/views/signal_strength_meter.dart create mode 100644 lib/veilid_processor/views/views.dart create mode 100644 packages/veilid_support/lib/dht_support/src/dht_record_cubit.dart rename {lib/tools => packages/veilid_support/lib/src}/async_table_db_backed_cubit.dart (89%) rename {lib/tools => packages/veilid_support/lib/src}/async_value.dart (100%) rename {lib/tools => packages/veilid_support/lib/src}/async_value.freezed.dart (100%) diff --git a/build.sh b/build.sh index 1a6cdf5..569fe28 100755 --- a/build.sh +++ b/build.sh @@ -1,5 +1,10 @@ #!/bin/bash set -e + +pushd packages/veilid_support > /dev/null +./build.sh +popd > /dev/null + dart run build_runner build --delete-conflicting-outputs pushd lib > /dev/null diff --git a/lib/account_manager/cubit/local_accounts_cubit/local_accounts_cubit.dart b/lib/account_manager/cubit/local_accounts_cubit/local_accounts_cubit.dart index 34fdccb..d885a7e 100644 --- a/lib/account_manager/cubit/local_accounts_cubit/local_accounts_cubit.dart +++ b/lib/account_manager/cubit/local_accounts_cubit/local_accounts_cubit.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; +import 'package:veilid_support/veilid_support.dart'; +import '../../models/models.dart'; import '../../repository/account_repository/account_repository.dart'; part 'local_accounts_state.dart'; diff --git a/lib/account_manager/cubit/local_accounts_cubit/local_accounts_state.dart b/lib/account_manager/cubit/local_accounts_cubit/local_accounts_state.dart index 13950c3..3b8d695 100644 --- a/lib/account_manager/cubit/local_accounts_cubit/local_accounts_state.dart +++ b/lib/account_manager/cubit/local_accounts_cubit/local_accounts_state.dart @@ -1,3 +1,14 @@ part of 'local_accounts_cubit.dart'; typedef LocalAccountsState = IList; + +extension LocalAccountsStateExt on LocalAccountsState { + LocalAccount? fetchLocalAccount({required TypedKey accountMasterRecordKey}) { + final idx = indexWhere( + (e) => e.identityMaster.masterRecordKey == accountMasterRecordKey); + if (idx == -1) { + return null; + } + return this[idx]; + } +} diff --git a/lib/account_manager/cubit/user_logins_cubit/user_logins_cubit.dart b/lib/account_manager/cubit/user_logins_cubit/user_logins_cubit.dart index e6ad92a..fd2f4ff 100644 --- a/lib/account_manager/cubit/user_logins_cubit/user_logins_cubit.dart +++ b/lib/account_manager/cubit/user_logins_cubit/user_logins_cubit.dart @@ -2,7 +2,9 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; +import 'package:veilid_support/veilid_support.dart'; +import '../../models/models.dart'; import '../../repository/account_repository/account_repository.dart'; part 'user_logins_state.dart'; diff --git a/lib/account_manager/cubit/user_logins_cubit/user_logins_state.dart b/lib/account_manager/cubit/user_logins_cubit/user_logins_state.dart index 27dec5c..04c70d7 100644 --- a/lib/account_manager/cubit/user_logins_cubit/user_logins_state.dart +++ b/lib/account_manager/cubit/user_logins_cubit/user_logins_state.dart @@ -1,3 +1,14 @@ part of 'user_logins_cubit.dart'; typedef UserLoginsState = IList; + +extension UserLoginsStateExt on UserLoginsState { + UserLogin? fetchUserLogin({required TypedKey accountMasterRecordKey}) { + final idx = + indexWhere((e) => e.accountMasterRecordKey == accountMasterRecordKey); + if (idx == -1) { + return null; + } + return this[idx]; + } +} diff --git a/lib/account_manager/repository/account_repository/encryption_key_type.dart b/lib/account_manager/models/encryption_key_type.dart similarity index 98% rename from lib/account_manager/repository/account_repository/encryption_key_type.dart rename to lib/account_manager/models/encryption_key_type.dart index 2c00f27..22897b4 100644 --- a/lib/account_manager/repository/account_repository/encryption_key_type.dart +++ b/lib/account_manager/models/encryption_key_type.dart @@ -9,7 +9,7 @@ import 'dart:typed_data'; import 'package:change_case/change_case.dart'; import 'package:veilid_support/veilid_support.dart'; -import '../../../../proto/proto.dart' as proto; +import '../../../proto/proto.dart' as proto; enum EncryptionKeyType { none, diff --git a/lib/account_manager/repository/account_repository/local_account.dart b/lib/account_manager/models/local_account/local_account.dart similarity index 96% rename from lib/account_manager/repository/account_repository/local_account.dart rename to lib/account_manager/models/local_account/local_account.dart index eaf0fa8..1998961 100644 --- a/lib/account_manager/repository/account_repository/local_account.dart +++ b/lib/account_manager/models/local_account/local_account.dart @@ -3,7 +3,7 @@ import 'dart:typed_data'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:veilid_support/veilid_support.dart'; -import 'encryption_key_type.dart'; +import '../../models/encryption_key_type.dart'; part 'local_account.g.dart'; part 'local_account.freezed.dart'; diff --git a/lib/account_manager/repository/account_repository/local_account.freezed.dart b/lib/account_manager/models/local_account/local_account.freezed.dart similarity index 100% rename from lib/account_manager/repository/account_repository/local_account.freezed.dart rename to lib/account_manager/models/local_account/local_account.freezed.dart diff --git a/lib/account_manager/repository/account_repository/local_account.g.dart b/lib/account_manager/models/local_account/local_account.g.dart similarity index 100% rename from lib/account_manager/repository/account_repository/local_account.g.dart rename to lib/account_manager/models/local_account/local_account.g.dart diff --git a/lib/account_manager/models/models.dart b/lib/account_manager/models/models.dart new file mode 100644 index 0000000..320e917 --- /dev/null +++ b/lib/account_manager/models/models.dart @@ -0,0 +1,4 @@ +export 'encryption_key_type.dart'; +export 'local_account/local_account.dart'; +export 'new_profile_spec.dart'; +export 'user_login/user_login.dart'; diff --git a/lib/account_manager/repository/account_repository/new_profile_spec.dart b/lib/account_manager/models/new_profile_spec.dart similarity index 100% rename from lib/account_manager/repository/account_repository/new_profile_spec.dart rename to lib/account_manager/models/new_profile_spec.dart diff --git a/lib/account_manager/repository/account_repository/user_login.dart b/lib/account_manager/models/user_login/user_login.dart similarity index 100% rename from lib/account_manager/repository/account_repository/user_login.dart rename to lib/account_manager/models/user_login/user_login.dart diff --git a/lib/account_manager/repository/account_repository/user_login.freezed.dart b/lib/account_manager/models/user_login/user_login.freezed.dart similarity index 100% rename from lib/account_manager/repository/account_repository/user_login.freezed.dart rename to lib/account_manager/models/user_login/user_login.freezed.dart diff --git a/lib/account_manager/repository/account_repository/user_login.g.dart b/lib/account_manager/models/user_login/user_login.g.dart similarity index 100% rename from lib/account_manager/repository/account_repository/user_login.g.dart rename to lib/account_manager/models/user_login/user_login.g.dart diff --git a/lib/account_manager/repository/account_repository/account_repository.dart b/lib/account_manager/repository/account_repository/account_repository.dart index 0e42a55..2be5565 100644 --- a/lib/account_manager/repository/account_repository/account_repository.dart +++ b/lib/account_manager/repository/account_repository/account_repository.dart @@ -2,16 +2,8 @@ import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:veilid_support/veilid_support.dart'; import '../../../../proto/proto.dart' as proto; +import '../../models/models.dart'; import 'active_logins.dart'; -import 'encryption_key_type.dart'; -import 'local_account.dart'; -import 'new_profile_spec.dart'; -import 'user_login.dart'; - -export 'active_logins.dart'; -export 'encryption_key_type.dart'; -export 'local_account.dart'; -export 'user_login.dart'; const String veilidChatAccountKey = 'com.veilid.veilidchat'; @@ -73,7 +65,7 @@ class AccountRepository { return localAccounts[idx]; } - UserLogin? fetchLogin({required TypedKey accountMasterRecordKey}) { + UserLogin? fetchUserLogin({required TypedKey accountMasterRecordKey}) { final userLogins = _activeLogins.requireValue.userLogins; final idx = userLogins .indexWhere((e) => e.accountMasterRecordKey == accountMasterRecordKey); diff --git a/lib/account_manager/repository/account_repository/active_logins.dart b/lib/account_manager/repository/account_repository/active_logins.dart index 2fab41f..9d4e713 100644 --- a/lib/account_manager/repository/account_repository/active_logins.dart +++ b/lib/account_manager/repository/account_repository/active_logins.dart @@ -3,7 +3,7 @@ import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:veilid_support/veilid_support.dart'; -import 'user_login.dart'; +import '../../models/models.dart'; part 'active_logins.g.dart'; part 'active_logins.freezed.dart'; diff --git a/lib/account_manager/view/new_account_page/new_account_page.dart b/lib/account_manager/view/new_account_page/new_account_page.dart index acf3f8d..007bb53 100644 --- a/lib/account_manager/view/new_account_page/new_account_page.dart +++ b/lib/account_manager/view/new_account_page/new_account_page.dart @@ -5,12 +5,12 @@ 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 'package:veilid_support/veilid_support.dart'; -import '../../../components/default_app_bar.dart'; -import '../../../components/signal_strength_meter.dart'; -import '../../../entities/entities.dart'; +import '../../../layout/default_app_bar.dart'; import '../../../tools/tools.dart'; +import '../../../veilid_processor/veilid_processor.dart'; +import '../../account_manager.dart'; +import '../../models/models.dart'; class NewAccountPage extends StatefulWidget { const NewAccountPage({super.key}); @@ -95,8 +95,7 @@ class NewAccountPageState extends State { @override Widget build(BuildContext context) { - final displayModalHUD = - isInAsyncCall || !localAccounts.hasValue || !logins.hasValue; + final displayModalHUD = isInAsyncCall; return Scaffold( // resizeToAvoidBottomInset: false, @@ -116,7 +115,16 @@ class NewAccountPageState extends State { onSubmit: (formKey) async { FocusScope.of(context).unfocus(); try { - await createAccount(); + final name = + _formKey.currentState!.fields[formFieldName]!.value as String; + final pronouns = _formKey.currentState!.fields[formFieldPronouns]! + .value as String? ?? + ''; + final newProfileSpec = + NewProfileSpec(name: name, pronouns: pronouns); + + await AccountRepository.instance + .createMasterIdentity(newProfileSpec); } on Exception catch (e) { if (context.mounted) { await showErrorModal(context, translate('new_account_page.error'), diff --git a/lib/init.dart b/lib/init.dart index 7c0ac92..29c2db2 100644 --- a/lib/init.dart +++ b/lib/init.dart @@ -1,35 +1,29 @@ import 'dart:async'; -import 'app.dart'; -import 'local_account_manager/account_manager.dart'; -import 'processor.dart'; -import 'tools/tools.dart'; -import '../packages/veilid_support/veilid_support.dart'; +import 'package:flutter/foundation.dart'; +import 'package:veilid_support/veilid_support.dart'; -final Completer eventualVeilid = Completer(); -final Processor processor = Processor(); +import 'account_manager/account_manager.dart'; +import 'app.dart'; +import 'tools/tools.dart'; +import 'veilid_processor/veilid_processor.dart'; final Completer eventualInitialized = Completer(); // Initialize Veilid Future initializeVeilid() async { - // Ensure this runs only once - if (eventualVeilid.isCompleted) { - return; - } - // Init Veilid - Veilid.instance - .initializeVeilidCore(getDefaultVeilidPlatformConfig(VeilidChatApp.name)); + Veilid.instance.initializeVeilidCore( + getDefaultVeilidPlatformConfig(kIsWeb, VeilidChatApp.name)); // Veilid logging - initVeilidLog(); + initVeilidLog(kDebugMode); + + // DHT Record Pool + await DHTRecordPool.init(); // Startup Veilid - await processor.startup(); - - // Share the initialized veilid instance to the rest of the app - eventualVeilid.complete(Veilid.instance); + await ProcessorRepository.instance.startup(); } // Initialize repositories @@ -38,9 +32,9 @@ Future initializeRepositories() async { } Future initializeVeilidChat() async { - log.info("Initializing Veilid"); + log.info('Initializing Veilid'); await initializeVeilid(); - log.info("Initializing Repositories"); + log.info('Initializing Repositories'); await initializeRepositories(); eventualInitialized.complete(); diff --git a/lib/old_to_refactor/pages/chat_only.dart b/lib/layout/chat_only.dart similarity index 100% rename from lib/old_to_refactor/pages/chat_only.dart rename to lib/layout/chat_only.dart diff --git a/lib/old_to_refactor/components/default_app_bar.dart b/lib/layout/default_app_bar.dart similarity index 100% rename from lib/old_to_refactor/components/default_app_bar.dart rename to lib/layout/default_app_bar.dart diff --git a/lib/old_to_refactor/pages/edit_account.dart b/lib/layout/edit_account.dart similarity index 100% rename from lib/old_to_refactor/pages/edit_account.dart rename to lib/layout/edit_account.dart diff --git a/lib/old_to_refactor/pages/edit_contact.dart b/lib/layout/edit_contact.dart similarity index 100% rename from lib/old_to_refactor/pages/edit_contact.dart rename to lib/layout/edit_contact.dart diff --git a/lib/layout/home.dart b/lib/layout/home.dart new file mode 100644 index 0000000..a78ebb9 --- /dev/null +++ b/lib/layout/home.dart @@ -0,0 +1,235 @@ +import 'package:awesome_extensions/awesome_extensions.dart'; +import 'package:fast_immutable_collections/fast_immutable_collections.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_translate/flutter_translate.dart'; +import 'package:go_router/go_router.dart'; + +import '../account_manager/account_manager.dart'; +import '../../proto/proto.dart' as proto; +import '../theme/theme.dart'; +import '../tools/tools.dart'; +import 'main_pager/main_pager.dart'; + +class HomePage extends StatefulWidget { + const HomePage({super.key}); + + @override + HomePageState createState() => HomePageState(); + + static Widget buildChatComponent() { + final contactList = ref.watch(fetchContactListProvider).asData?.value ?? + const IListConst([]); + + final activeChat = ref.watch(activeChatStateProvider); + if (activeChat == null) { + return const EmptyChatWidget(); + } + + final activeAccountInfo = + ref.watch(fetchActiveAccountProvider).asData?.value; + if (activeAccountInfo == null) { + return const EmptyChatWidget(); + } + + final activeChatContactIdx = contactList.indexWhere( + (c) => + proto.TypedKeyProto.fromProto(c.remoteConversationRecordKey) == + activeChat, + ); + if (activeChatContactIdx == -1) { + ref.read(activeChatStateProvider.notifier).state = null; + return const EmptyChatWidget(); + } + final activeChatContact = contactList[activeChatContactIdx]; + + return ChatComponent( + activeAccountInfo: activeAccountInfo, + activeChat: activeChat, + activeChatContact: activeChatContact); + } +} + +class HomePageState extends State with TickerProviderStateMixin { + final _unfocusNode = FocusNode(); + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) async { + await changeWindowSetup( + TitleBarStyle.normal, OrientationCapability.normal); + }); + } + + @override + void dispose() { + _unfocusNode.dispose(); + super.dispose(); + } + + // ignore: prefer_expression_function_bodies + Widget buildAccountList() { + return const Column(children: [ + Center(child: Text('Small Profile')), + Center(child: Text('Contact invitations')), + Center(child: Text('Contacts')) + ]); + } + + Widget buildUnlockAccount( + BuildContext context, + IList localAccounts, + // ignore: prefer_expression_function_bodies + ) { + return const Center(child: Text('unlock account')); + } + + /// We have an active, unlocked, user login + Widget buildReadyAccount( + BuildContext context, + IList localAccounts, + TypedKey activeUserLogin, + proto.Account account) { + final theme = Theme.of(context); + final scale = theme.extension()!; + + return Column(children: [ + Row(children: [ + IconButton( + icon: const Icon(Icons.settings), + color: scale.secondaryScale.text, + constraints: const BoxConstraints.expand(height: 64, width: 64), + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all(scale.secondaryScale.border), + shape: MaterialStateProperty.all(const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(16))))), + tooltip: translate('app_bar.settings_tooltip'), + onPressed: () async { + context.go('/home/settings'); + }).paddingLTRB(0, 0, 8, 0), + ProfileWidget( + name: account.profile.name, + pronouns: account.profile.pronouns, + ).expanded(), + ]).paddingAll(8), + MainPager( + localAccounts: localAccounts, + activeUserLogin: activeUserLogin, + account: account) + .expanded() + ]); + } + + Widget buildUserPanel() => Builder(builder: (context) { + final activeUserLogin = context.watch().state; + final localAccounts = context.watch().state; + + if (activeUserLogin == null) { + // If no logged in user is active, show the loading panel + return waitingPage(context); + } + + final accountV = ref.watch( + fetchAccountProvider(accountMasterRecordKey: activeUserLogin)); + if (!accountV.hasValue) { + return waitingPage(context); + } + final account = accountV.requireValue; + switch (account.status) { + case AccountInfoStatus.noAccount: + Future.delayed(0.ms, () async { + await showErrorModal( + context, + translate('home.missing_account_title'), + translate('home.missing_account_text')); + // Delete account + await ref + .read(localAccountsProvider.notifier) + .deleteLocalAccount(activeUserLogin); + // Switch to no active user login + await ref.read(loginsProvider.notifier).switchToAccount(null); + }); + return waitingPage(context); + case AccountInfoStatus.accountInvalid: + Future.delayed(0.ms, () async { + await showErrorModal( + context, + translate('home.invalid_account_title'), + translate('home.invalid_account_text')); + // Delete account + await ref + .read(localAccountsProvider.notifier) + .deleteLocalAccount(activeUserLogin); + // Switch to no active user login + await ref.read(loginsProvider.notifier).switchToAccount(null); + }); + return waitingPage(context); + case AccountInfoStatus.accountLocked: + // Show unlock widget + return buildUnlockAccount(context, localAccounts); + case AccountInfoStatus.accountReady: + return buildReadyAccount( + context, + localAccounts, + activeUserLogin, + account.account!, + ); + } + }); + + Widget buildPhone() => + Material(color: Colors.transparent, child: buildUserPanel()); + + Widget buildTabletLeftPane() => + Material(color: Colors.transparent, child: buildUserPanel()); + + Widget buildTabletRightPane() => HomePage.buildChatComponent(); + + // ignore: prefer_expression_function_bodies + Widget buildTablet() => Builder(builder: (context) { + final w = MediaQuery.of(context).size.width; + final theme = Theme.of(context); + final scale = theme.extension()!; + + final children = [ + ConstrainedBox( + constraints: const BoxConstraints(minWidth: 300, maxWidth: 300), + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: w / 2), + child: buildTabletLeftPane())), + SizedBox( + width: 2, + height: double.infinity, + child: ColoredBox(color: scale.primaryScale.hoverBorder)), + Expanded(child: buildTabletRightPane()), + ]; + + return Row( + children: children, + ); + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final scale = theme.extension()!; + + return SafeArea( + child: GestureDetector( + onTap: () => FocusScope.of(context).requestFocus(_unfocusNode), + child: DecoratedBox( + decoration: BoxDecoration( + color: scale.primaryScale.activeElementBackground), + child: responsiveVisibility( + context: context, + phone: false, + ) + ? buildTablet() + : buildPhone(), + ))); + } +} diff --git a/lib/old_to_refactor/pages/index.dart b/lib/layout/index.dart similarity index 100% rename from lib/old_to_refactor/pages/index.dart rename to lib/layout/index.dart diff --git a/lib/layout/layout.dart b/lib/layout/layout.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/layout/layout.dart @@ -0,0 +1 @@ + diff --git a/lib/old_to_refactor/pages/main_pager/account.dart b/lib/layout/main_pager/account.dart similarity index 100% rename from lib/old_to_refactor/pages/main_pager/account.dart rename to lib/layout/main_pager/account.dart diff --git a/lib/old_to_refactor/pages/main_pager/chats.dart b/lib/layout/main_pager/chats.dart similarity index 100% rename from lib/old_to_refactor/pages/main_pager/chats.dart rename to lib/layout/main_pager/chats.dart diff --git a/lib/old_to_refactor/pages/main_pager/main_pager.dart b/lib/layout/main_pager/main_pager.dart similarity index 100% rename from lib/old_to_refactor/pages/main_pager/main_pager.dart rename to lib/layout/main_pager/main_pager.dart diff --git a/lib/old_to_refactor/pages/settings.dart b/lib/layout/settings.dart similarity index 100% rename from lib/old_to_refactor/pages/settings.dart rename to lib/layout/settings.dart diff --git a/lib/old_to_refactor/components/signal_strength_meter.dart b/lib/old_to_refactor/components/signal_strength_meter.dart deleted file mode 100644 index 2593515..0000000 --- a/lib/old_to_refactor/components/signal_strength_meter.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:signal_strength_indicator/signal_strength_indicator.dart'; -import 'package:veilid_support/veilid_support.dart'; - -import '../providers/connection_state.dart'; -import '../tools/tools.dart'; - -xxx move to feature level - -class SignalStrengthMeterWidget extends Widget { - const SignalStrengthMeterWidget({super.key}); - - @override - // ignore: prefer_expression_function_bodies - Widget build(BuildContext context, WidgetRef ref) { - final theme = Theme.of(context); - final scale = theme.extension()!; - - const iconSize = 16.0; - final connState = ref.watch(connectionStateProvider); - - late final double value; - late final Color color; - late final Color inactiveColor; - switch (connState.attachment.state) { - case AttachmentState.detached: - return Icon(Icons.signal_cellular_nodata, - size: iconSize, color: scale.grayScale.text); - case AttachmentState.detaching: - return Icon(Icons.signal_cellular_off, - size: iconSize, color: scale.grayScale.text); - case AttachmentState.attaching: - value = 0; - color = scale.primaryScale.text; - case AttachmentState.attachedWeak: - value = 1; - color = scale.primaryScale.text; - case AttachmentState.attachedStrong: - value = 2; - color = scale.primaryScale.text; - case AttachmentState.attachedGood: - value = 3; - color = scale.primaryScale.text; - case AttachmentState.fullyAttached: - value = 4; - color = scale.primaryScale.text; - case AttachmentState.overAttached: - value = 4; - color = scale.secondaryScale.subtleText; - } - inactiveColor = scale.grayScale.subtleText; - - return GestureDetector( - onLongPress: () async { - await context.push('/developer'); - }, - child: SignalStrengthIndicator.bars( - value: value, - activeColor: color, - inactiveColor: inactiveColor, - size: iconSize, - barCount: 4, - spacing: 1, - )); - } -} diff --git a/lib/old_to_refactor/pages/home.dart b/lib/old_to_refactor/pages/home.dart deleted file mode 100644 index 6284dfd..0000000 --- a/lib/old_to_refactor/pages/home.dart +++ /dev/null @@ -1,269 +0,0 @@ -import 'package:awesome_extensions/awesome_extensions.dart'; -import 'package:fast_immutable_collections/fast_immutable_collections.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_translate/flutter_translate.dart'; -import 'package:go_router/go_router.dart'; - -import '../../proto/proto.dart' as proto; -import '../../components/chat_component.dart'; -import '../../components/empty_chat_widget.dart'; -import '../../components/profile_widget.dart'; -import '../../entities/local_account.dart'; -import '../providers/account.dart'; -import '../providers/chat.dart'; -import '../providers/contact.dart'; -import '../../local_accounts/local_accounts.dart'; -import '../providers/logins.dart'; -import '../providers/window_control.dart'; -import '../../tools/tools.dart'; -import '../../../packages/veilid_support/veilid_support.dart'; -import 'main_pager/main_pager.dart'; - -class HomePage extends StatefulWidget { - const HomePage({super.key}); - - @override - HomePageState createState() => HomePageState(); - - static Widget buildChatComponent(BuildContext context, WidgetRef ref) { - final contactList = ref.watch(fetchContactListProvider).asData?.value ?? - const IListConst([]); - - final activeChat = ref.watch(activeChatStateProvider); - if (activeChat == null) { - return const EmptyChatWidget(); - } - - final activeAccountInfo = - ref.watch(fetchActiveAccountProvider).asData?.value; - if (activeAccountInfo == null) { - return const EmptyChatWidget(); - } - - final activeChatContactIdx = contactList.indexWhere( - (c) => - proto.TypedKeyProto.fromProto(c.remoteConversationRecordKey) == - activeChat, - ); - if (activeChatContactIdx == -1) { - ref.read(activeChatStateProvider.notifier).state = null; - return const EmptyChatWidget(); - } - final activeChatContact = contactList[activeChatContactIdx]; - - return ChatComponent( - activeAccountInfo: activeAccountInfo, - activeChat: activeChat, - activeChatContact: activeChatContact); - } -} - -class HomePageState extends ConsumerState - with TickerProviderStateMixin { - final _unfocusNode = FocusNode(); - - @override - void initState() { - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((_) async { - setState(() {}); - await ref.read(windowControlProvider.notifier).changeWindowSetup( - TitleBarStyle.normal, OrientationCapability.normal); - }); - } - - @override - void dispose() { - _unfocusNode.dispose(); - super.dispose(); - } - - // ignore: prefer_expression_function_bodies - Widget buildAccountList() { - return const Column(children: [ - Center(child: Text('Small Profile')), - Center(child: Text('Contact invitations')), - Center(child: Text('Contacts')) - ]); - } - - Widget buildUnlockAccount( - BuildContext context, - IList localAccounts, - // ignore: prefer_expression_function_bodies - ) { - return const Center(child: Text('unlock account')); - } - - /// We have an active, unlocked, user login - Widget buildReadyAccount( - BuildContext context, - IList localAccounts, - TypedKey activeUserLogin, - proto.Account account) { - final theme = Theme.of(context); - final scale = theme.extension()!; - - return Column(children: [ - Row(children: [ - IconButton( - icon: const Icon(Icons.settings), - color: scale.secondaryScale.text, - constraints: const BoxConstraints.expand(height: 64, width: 64), - style: ButtonStyle( - backgroundColor: - MaterialStateProperty.all(scale.secondaryScale.border), - shape: MaterialStateProperty.all(const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16))))), - tooltip: translate('app_bar.settings_tooltip'), - onPressed: () async { - context.go('/home/settings'); - }).paddingLTRB(0, 0, 8, 0), - ProfileWidget( - name: account.profile.name, - pronouns: account.profile.pronouns, - ).expanded(), - ]).paddingAll(8), - MainPager( - localAccounts: localAccounts, - activeUserLogin: activeUserLogin, - account: account) - .expanded() - ]); - } - - Widget buildUserPanel() { - final localAccountsV = ref.watch(localAccountsProvider); - final loginsV = ref.watch(loginsProvider); - - if (!localAccountsV.hasValue || !loginsV.hasValue) { - return waitingPage(context); - } - final localAccounts = localAccountsV.requireValue; - final logins = loginsV.requireValue; - - final activeUserLogin = logins.activeUserLogin; - if (activeUserLogin == null) { - // If no logged in user is active, show the list of account - return buildAccountList(); - } - final accountV = ref - .watch(fetchAccountProvider(accountMasterRecordKey: activeUserLogin)); - if (!accountV.hasValue) { - return waitingPage(context); - } - final account = accountV.requireValue; - switch (account.status) { - case AccountInfoStatus.noAccount: - Future.delayed(0.ms, () async { - await showErrorModal(context, translate('home.missing_account_title'), - translate('home.missing_account_text')); - // Delete account - await ref - .read(localAccountsProvider.notifier) - .deleteLocalAccount(activeUserLogin); - // Switch to no active user login - await ref.read(loginsProvider.notifier).switchToAccount(null); - }); - return waitingPage(context); - case AccountInfoStatus.accountInvalid: - Future.delayed(0.ms, () async { - await showErrorModal(context, translate('home.invalid_account_title'), - translate('home.invalid_account_text')); - // Delete account - await ref - .read(localAccountsProvider.notifier) - .deleteLocalAccount(activeUserLogin); - // Switch to no active user login - await ref.read(loginsProvider.notifier).switchToAccount(null); - }); - return waitingPage(context); - case AccountInfoStatus.accountLocked: - // Show unlock widget - return buildUnlockAccount(context, localAccounts); - case AccountInfoStatus.accountReady: - return buildReadyAccount( - context, - localAccounts, - activeUserLogin, - account.account!, - ); - } - } - - // ignore: prefer_expression_function_bodies - Widget buildPhone(BuildContext context) { - return Material(color: Colors.transparent, child: buildUserPanel()); - } - - // ignore: prefer_expression_function_bodies - Widget buildTabletLeftPane(BuildContext context) { - // - return Material(color: Colors.transparent, child: buildUserPanel()); - } - - // ignore: prefer_expression_function_bodies - Widget buildTabletRightPane(BuildContext context) { - // - return HomePage.buildChatComponent(context, ref); - } - - // ignore: prefer_expression_function_bodies - Widget buildTablet(BuildContext context) { - final w = MediaQuery.of(context).size.width; - final theme = Theme.of(context); - final scale = theme.extension()!; - - final children = [ - ConstrainedBox( - constraints: const BoxConstraints(minWidth: 300, maxWidth: 300), - child: ConstrainedBox( - constraints: BoxConstraints(maxWidth: w / 2), - child: buildTabletLeftPane(context))), - SizedBox( - width: 2, - height: double.infinity, - child: ColoredBox(color: scale.primaryScale.hoverBorder)), - Expanded(child: buildTabletRightPane(context)), - ]; - - return Row( - children: children, - ); - - // final theme = MultiSplitViewTheme( - // data: isDesktop - // ? MultiSplitViewThemeData( - // dividerThickness: 1, - // dividerPainter: DividerPainters.grooved2(thickness: 1)) - // : MultiSplitViewThemeData( - // dividerThickness: 3, - // dividerPainter: DividerPainters.grooved2(thickness: 1)), - // child: multiSplitView); - } - - @override - Widget build(BuildContext context) { - ref.watch(windowControlProvider); - - final theme = Theme.of(context); - final scale = theme.extension()!; - - return SafeArea( - child: GestureDetector( - onTap: () => FocusScope.of(context).requestFocus(_unfocusNode), - child: DecoratedBox( - decoration: BoxDecoration( - color: scale.primaryScale.activeElementBackground), - child: responsiveVisibility( - context: context, - phone: false, - ) - ? buildTablet(context) - : buildPhone(context), - ))); - } -} diff --git a/lib/old_to_refactor/providers/connection_state.dart b/lib/old_to_refactor/providers/connection_state.dart deleted file mode 100644 index a663190..0000000 --- a/lib/old_to_refactor/providers/connection_state.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - -import '../../../packages/veilid_support/veilid_support.dart'; - -part 'connection_state.freezed.dart'; - -@freezed -class ConnectionState with _$ConnectionState { - const factory ConnectionState({ - required VeilidStateAttachment attachment, - }) = _ConnectionState; - const ConnectionState._(); - - bool get isAttached => !(attachment.state == AttachmentState.detached || - attachment.state == AttachmentState.detaching || - attachment.state == AttachmentState.attaching); - - bool get isPublicInternetReady => attachment.publicInternetReady; -} - -final connectionState = StateController(const ConnectionState( - attachment: VeilidStateAttachment( - state: AttachmentState.detached, - publicInternetReady: false, - localNetworkReady: false))); -final connectionStateProvider = - StateNotifierProvider, ConnectionState>( - (ref) => connectionState); diff --git a/lib/old_to_refactor/providers/connection_state.freezed.dart b/lib/old_to_refactor/providers/connection_state.freezed.dart deleted file mode 100644 index 8ac0282..0000000 --- a/lib/old_to_refactor/providers/connection_state.freezed.dart +++ /dev/null @@ -1,138 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'connection_state.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -mixin _$ConnectionState { - VeilidStateAttachment get attachment => throw _privateConstructorUsedError; - - @JsonKey(ignore: true) - $ConnectionStateCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $ConnectionStateCopyWith<$Res> { - factory $ConnectionStateCopyWith( - ConnectionState value, $Res Function(ConnectionState) then) = - _$ConnectionStateCopyWithImpl<$Res, ConnectionState>; - @useResult - $Res call({VeilidStateAttachment attachment}); -} - -/// @nodoc -class _$ConnectionStateCopyWithImpl<$Res, $Val extends ConnectionState> - implements $ConnectionStateCopyWith<$Res> { - _$ConnectionStateCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? attachment = freezed, - }) { - return _then(_value.copyWith( - attachment: freezed == attachment - ? _value.attachment - : attachment // ignore: cast_nullable_to_non_nullable - as VeilidStateAttachment, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$ConnectionStateImplCopyWith<$Res> - implements $ConnectionStateCopyWith<$Res> { - factory _$$ConnectionStateImplCopyWith(_$ConnectionStateImpl value, - $Res Function(_$ConnectionStateImpl) then) = - __$$ConnectionStateImplCopyWithImpl<$Res>; - @override - @useResult - $Res call({VeilidStateAttachment attachment}); -} - -/// @nodoc -class __$$ConnectionStateImplCopyWithImpl<$Res> - extends _$ConnectionStateCopyWithImpl<$Res, _$ConnectionStateImpl> - implements _$$ConnectionStateImplCopyWith<$Res> { - __$$ConnectionStateImplCopyWithImpl( - _$ConnectionStateImpl _value, $Res Function(_$ConnectionStateImpl) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? attachment = freezed, - }) { - return _then(_$ConnectionStateImpl( - attachment: freezed == attachment - ? _value.attachment - : attachment // ignore: cast_nullable_to_non_nullable - as VeilidStateAttachment, - )); - } -} - -/// @nodoc - -class _$ConnectionStateImpl extends _ConnectionState { - const _$ConnectionStateImpl({required this.attachment}) : super._(); - - @override - final VeilidStateAttachment attachment; - - @override - String toString() { - return 'ConnectionState(attachment: $attachment)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ConnectionStateImpl && - const DeepCollectionEquality() - .equals(other.attachment, attachment)); - } - - @override - int get hashCode => - Object.hash(runtimeType, const DeepCollectionEquality().hash(attachment)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$ConnectionStateImplCopyWith<_$ConnectionStateImpl> get copyWith => - __$$ConnectionStateImplCopyWithImpl<_$ConnectionStateImpl>( - this, _$identity); -} - -abstract class _ConnectionState extends ConnectionState { - const factory _ConnectionState( - {required final VeilidStateAttachment attachment}) = - _$ConnectionStateImpl; - const _ConnectionState._() : super._(); - - @override - VeilidStateAttachment get attachment; - @override - @JsonKey(ignore: true) - _$$ConnectionStateImplCopyWith<_$ConnectionStateImpl> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/lib/processor.dart b/lib/processor.dart deleted file mode 100644 index d0c359a..0000000 --- a/lib/processor.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'dart:async'; - -import 'package:veilid/veilid.dart'; - -import 'app.dart'; -import 'old_to_refactor/providers/connection_state.dart'; -import 'tools/tools.dart'; -import '../packages/veilid_support/src/config.dart'; -import '../packages/veilid_support/src/veilid_log.dart'; - -class Processor { - Processor(); - String _veilidVersion = ''; - bool _startedUp = false; - Stream? _updateStream; - Future? _updateProcessor; - - Future startup() async { - if (_startedUp) { - return; - } - - try { - _veilidVersion = Veilid.instance.veilidVersionString(); - } on Exception { - _veilidVersion = 'Failed to get veilid version.'; - } - - log.info('Veilid version: $_veilidVersion'); - - // HACK: In case of hot restart shut down first - try { - await Veilid.instance.shutdownVeilidCore(); - } on Exception { - // Do nothing on failure here - } - - final updateStream = await Veilid.instance - .startupVeilidCore(await getVeilidConfig(VeilidChatApp.name)); - _updateStream = updateStream; - _updateProcessor = processUpdates(); - _startedUp = true; - - await Veilid.instance.attach(); - } - - Future shutdown() async { - if (!_startedUp) { - return; - } - await Veilid.instance.shutdownVeilidCore(); - if (_updateProcessor != null) { - await _updateProcessor; - } - _updateProcessor = null; - _updateStream = null; - _startedUp = false; - } - - Future processUpdateAttachment( - VeilidUpdateAttachment updateAttachment) async { - //loggy.info("Attachment: ${updateAttachment.json}"); - - // // Set connection meter and ui state for connection state - - connectionState.state = ConnectionState( - attachment: VeilidStateAttachment( - state: updateAttachment.state, - publicInternetReady: updateAttachment.publicInternetReady, - localNetworkReady: updateAttachment.localNetworkReady)); - } - - Future processUpdateConfig(VeilidUpdateConfig updateConfig) async { - //loggy.info("Config: ${updateConfig.json}"); - } - - Future processUpdateNetwork(VeilidUpdateNetwork updateNetwork) async { - //loggy.info("Network: ${updateNetwork.json}"); - } - - Future processUpdates() async { - final stream = _updateStream; - if (stream != null) { - await for (final update in stream) { - if (update is VeilidLog) { - await processLog(update); - } else if (update is VeilidUpdateAttachment) { - await processUpdateAttachment(update); - } else if (update is VeilidUpdateConfig) { - await processUpdateConfig(update); - } else if (update is VeilidUpdateNetwork) { - await processUpdateNetwork(update); - } else if (update is VeilidAppMessage) { - log.info('AppMessage: ${update.toJson()}'); - } else if (update is VeilidAppCall) { - log.info('AppCall: ${update.toJson()}'); - } else { - log.trace('Update: ${update.toJson()}'); - } - } - } - } -} diff --git a/lib/router/cubit/router_cubit.dart b/lib/router/cubit/router_cubit.dart index 5fcd379..b182a27 100644 --- a/lib/router/cubit/router_cubit.dart +++ b/lib/router/cubit/router_cubit.dart @@ -5,15 +5,14 @@ import 'package:flutter/widgets.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:go_router/go_router.dart'; +import '../../../account_manager/account_manager.dart'; import '../../init.dart'; -import '../../local_account_manager/respository/account_repository/account_repository.dart'; import '../../old_to_refactor/pages/chat_only.dart'; -import '../../old_to_refactor/pages/developer.dart'; import '../../old_to_refactor/pages/home.dart'; import '../../old_to_refactor/pages/index.dart'; -import '../../account_manager/view/new_account_page/new_account_page.dart'; import '../../old_to_refactor/pages/settings.dart'; import '../../tools/tools.dart'; +import '../../veilid_processor/views/developer.dart'; part 'router_cubit.freezed.dart'; part 'router_cubit.g.dart'; diff --git a/lib/theme/models/models.dart b/lib/theme/models/models.dart new file mode 100644 index 0000000..e5e69d9 --- /dev/null +++ b/lib/theme/models/models.dart @@ -0,0 +1,4 @@ +export 'scale_color.dart'; +export 'scale_scheme.dart'; +export 'theme_preference.dart'; +export 'radix_generator.dart'; diff --git a/lib/theme/radix_generator.dart b/lib/theme/models/radix_generator.dart similarity index 100% rename from lib/theme/radix_generator.dart rename to lib/theme/models/radix_generator.dart diff --git a/lib/theme/scale_color.dart b/lib/theme/models/scale_color.dart similarity index 100% rename from lib/theme/scale_color.dart rename to lib/theme/models/scale_color.dart diff --git a/lib/theme/scale_scheme.dart b/lib/theme/models/scale_scheme.dart similarity index 100% rename from lib/theme/scale_scheme.dart rename to lib/theme/models/scale_scheme.dart diff --git a/lib/theme/theme_preference.dart b/lib/theme/models/theme_preference.dart similarity index 100% rename from lib/theme/theme_preference.dart rename to lib/theme/models/theme_preference.dart diff --git a/lib/theme/theme_preference.freezed.dart b/lib/theme/models/theme_preference.freezed.dart similarity index 100% rename from lib/theme/theme_preference.freezed.dart rename to lib/theme/models/theme_preference.freezed.dart diff --git a/lib/theme/theme_preference.g.dart b/lib/theme/models/theme_preference.g.dart similarity index 100% rename from lib/theme/theme_preference.g.dart rename to lib/theme/models/theme_preference.g.dart diff --git a/lib/theme/theme_repository.dart b/lib/theme/repository/theme_repository.dart similarity index 98% rename from lib/theme/theme_repository.dart rename to lib/theme/repository/theme_repository.dart index c717928..2aae8cd 100644 --- a/lib/theme/theme_repository.dart +++ b/lib/theme/repository/theme_repository.dart @@ -5,8 +5,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'radix_generator.dart'; -import 'theme_preference.dart'; +import '../models/models.dart'; //////////////////////////////////////////////////////////////////////// diff --git a/lib/theme/theme.dart b/lib/theme/theme.dart index 064b1bf..21be85e 100644 --- a/lib/theme/theme.dart +++ b/lib/theme/theme.dart @@ -1,3 +1,2 @@ -export 'scale_scheme.dart'; -export 'theme_preference.dart'; -export 'theme_repository.dart'; +export 'models/models.dart'; +export 'repository/theme_repository.dart'; diff --git a/lib/tick.dart b/lib/tick.dart index 82909bc..bb495a2 100644 --- a/lib/tick.dart +++ b/lib/tick.dart @@ -1,18 +1,11 @@ -// XXX Eliminate this when we have ValueChanged import 'dart:async'; -import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:veilid_support/veilid_support.dart'; import 'init.dart'; -import 'old_to_refactor/providers/account.dart'; -import 'old_to_refactor/providers/chat.dart'; -import 'old_to_refactor/providers/connection_state.dart'; -import 'old_to_refactor/providers/contact.dart'; -import 'old_to_refactor/providers/contact_invite.dart'; -import 'old_to_refactor/providers/conversation.dart'; -import 'proto/proto.dart' as proto; +import 'veilid_processor/veilid_processor.dart'; const int ticksPerContactInvitationCheck = 5; const int ticksPerNewMessageCheck = 5; @@ -35,6 +28,9 @@ class BackgroundTicker extends StatefulWidget { class BackgroundTickerState extends State { Timer? _tickTimer; bool _inTick = false; + bool _inDoContactInvitationCheck = false; + bool _inDoNewMessageCheck = false; + int _contactInvitationCheckTick = 0; int _newMessageCheckTick = 0; @@ -65,32 +61,38 @@ class BackgroundTickerState extends State { } Future _onTick() async { - // Don't tick until veilid is started and attached - if (!eventualVeilid.isCompleted) { + // Don't tick until we are initialized + if (!eventualInitialized.isCompleted) { return; } - if (!connectionState.state.isAttached) { + if (!ProcessorRepository + .instance.processorConnectionState.isPublicInternetReady) { return; } _inTick = true; try { - final unord = >[]; + // Tick DHT record pool + if (!DHTRecordPool.instance.inTick) { + unawaited(DHTRecordPool.instance.tick()); + } + // Check extant contact invitations once every N seconds _contactInvitationCheckTick += 1; if (_contactInvitationCheckTick >= ticksPerContactInvitationCheck) { _contactInvitationCheckTick = 0; - unord.add(_doContactInvitationCheck()); + if (!_inDoContactInvitationCheck) { + unawaited(_doContactInvitationCheck()); + } } // Check new messages once every N seconds _newMessageCheckTick += 1; if (_newMessageCheckTick >= ticksPerNewMessageCheck) { _newMessageCheckTick = 0; - unord.add(_doNewMessageCheck()); - } - if (unord.isNotEmpty) { - await Future.wait(unord); + if (!_inDoNewMessageCheck) { + unawaited(_doNewMessageCheck()); + } } } finally { _inTick = false; @@ -98,96 +100,118 @@ class BackgroundTickerState extends State { } Future _doContactInvitationCheck() async { - if (!connectionState.state.isPublicInternetReady) { - return; - } - final contactInvitationRecords = - await ref.read(fetchContactInvitationRecordsProvider.future); - if (contactInvitationRecords == null) { - return; - } - final activeAccountInfo = await ref.read(fetchActiveAccountProvider.future); - if (activeAccountInfo == null) { + if (_inDoContactInvitationCheck) { return; } + _inDoContactInvitationCheck = true; - final allChecks = >[]; - for (final contactInvitationRecord in contactInvitationRecords) { - allChecks.add(() async { - final acceptReject = await checkAcceptRejectContact( - activeAccountInfo: activeAccountInfo, - contactInvitationRecord: contactInvitationRecord); - if (acceptReject != null) { - final acceptedContact = acceptReject.acceptedContact; - if (acceptedContact != null) { - // Accept - await createContact( - activeAccountInfo: activeAccountInfo, - profile: acceptedContact.profile, - remoteIdentity: acceptedContact.remoteIdentity, - remoteConversationRecordKey: - acceptedContact.remoteConversationRecordKey, - localConversationRecordKey: - acceptedContact.localConversationRecordKey, - ); - ref - ..invalidate(fetchContactInvitationRecordsProvider) - ..invalidate(fetchContactListProvider); - } else { - // Reject - ref.invalidate(fetchContactInvitationRecordsProvider); - } - } - }()); + if (!ProcessorRepository + .instance.processorConnectionState.isPublicInternetReady) { + return; + } + // final contactInvitationRecords = + // await ref.read(fetchContactInvitationRecordsProvider.future); + // if (contactInvitationRecords == null) { + // return; + // } + try { + // final activeAccountInfo = + // await ref.read(fetchActiveAccountProvider.future); + // if (activeAccountInfo == null) { + // return; + // } + + // final allChecks = >[]; + // for (final contactInvitationRecord in contactInvitationRecords) { + // allChecks.add(() async { + // final acceptReject = await checkAcceptRejectContact( + // activeAccountInfo: activeAccountInfo, + // contactInvitationRecord: contactInvitationRecord); + // if (acceptReject != null) { + // final acceptedContact = acceptReject.acceptedContact; + // if (acceptedContact != null) { + // // Accept + // await createContact( + // activeAccountInfo: activeAccountInfo, + // profile: acceptedContact.profile, + // remoteIdentity: acceptedContact.remoteIdentity, + // remoteConversationRecordKey: + // acceptedContact.remoteConversationRecordKey, + // localConversationRecordKey: + // acceptedContact.localConversationRecordKey, + // ); + // ref + // ..invalidate(fetchContactInvitationRecordsProvider) + // ..invalidate(fetchContactListProvider); + // } else { + // // Reject + // ref.invalidate(fetchContactInvitationRecordsProvider); + // } + // } + // }()); + // } + // await Future.wait(allChecks); + } finally { + _inDoContactInvitationCheck = true; } - await Future.wait(allChecks); } Future _doNewMessageCheck() async { - if (!connectionState.state.isPublicInternetReady) { - return; - } - final activeChat = ref.read(activeChatStateProvider); - if (activeChat == null) { - return; - } - final activeAccountInfo = await ref.read(fetchActiveAccountProvider.future); - if (activeAccountInfo == null) { + if (_inDoNewMessageCheck) { return; } + _inDoNewMessageCheck = true; - final contactList = ref.read(fetchContactListProvider).asData?.value ?? - const IListConst([]); - - final activeChatContactIdx = contactList.indexWhere( - (c) => - proto.TypedKeyProto.fromProto(c.remoteConversationRecordKey) == - activeChat, - ); - if (activeChatContactIdx == -1) { - return; - } - final activeChatContact = contactList[activeChatContactIdx]; - final remoteIdentityPublicKey = - proto.TypedKeyProto.fromProto(activeChatContact.identityPublicKey); - final remoteConversationRecordKey = proto.TypedKeyProto.fromProto( - activeChatContact.remoteConversationRecordKey); - final localConversationRecordKey = proto.TypedKeyProto.fromProto( - activeChatContact.localConversationRecordKey); - - final newMessages = await getRemoteConversationMessages( - activeAccountInfo: activeAccountInfo, - remoteIdentityPublicKey: remoteIdentityPublicKey, - remoteConversationRecordKey: remoteConversationRecordKey); - if (newMessages != null && newMessages.isNotEmpty) { - final changed = await mergeLocalConversationMessages( - activeAccountInfo: activeAccountInfo, - localConversationRecordKey: localConversationRecordKey, - remoteIdentityPublicKey: remoteIdentityPublicKey, - newMessages: newMessages); - if (changed) { - ref.invalidate(activeConversationMessagesProvider); + try { + if (!ProcessorRepository + .instance.processorConnectionState.isPublicInternetReady) { + return; } + // final activeChat = ref.read(activeChatStateProvider); + // if (activeChat == null) { + // return; + // } + // final activeAccountInfo = + // await ref.read(fetchActiveAccountProvider.future); + // if (activeAccountInfo == null) { + // return; + // } + + // final contactList = ref.read(fetchContactListProvider).asData?.value ?? + // const IListConst([]); + + // final activeChatContactIdx = contactList.indexWhere( + // (c) => + // proto.TypedKeyProto.fromProto(c.remoteConversationRecordKey) == + // activeChat, + // ); + // if (activeChatContactIdx == -1) { + // return; + // } + // final activeChatContact = contactList[activeChatContactIdx]; + // final remoteIdentityPublicKey = + // proto.TypedKeyProto.fromProto(activeChatContact.identityPublicKey); + // final remoteConversationRecordKey = proto.TypedKeyProto.fromProto( + // activeChatContact.remoteConversationRecordKey); + // final localConversationRecordKey = proto.TypedKeyProto.fromProto( + // activeChatContact.localConversationRecordKey); + + // final newMessages = await getRemoteConversationMessages( + // activeAccountInfo: activeAccountInfo, + // remoteIdentityPublicKey: remoteIdentityPublicKey, + // remoteConversationRecordKey: remoteConversationRecordKey); + // if (newMessages != null && newMessages.isNotEmpty) { + // final changed = await mergeLocalConversationMessages( + // activeAccountInfo: activeAccountInfo, + // localConversationRecordKey: localConversationRecordKey, + // remoteIdentityPublicKey: remoteIdentityPublicKey, + // newMessages: newMessages); + // if (changed) { + // ref.invalidate(activeConversationMessagesProvider); + // } + // } + } finally { + _inDoNewMessageCheck = false; } } } diff --git a/lib/tools/loggy.dart b/lib/tools/loggy.dart index 3be0f5c..9947308 100644 --- a/lib/tools/loggy.dart +++ b/lib/tools/loggy.dart @@ -6,9 +6,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:intl/intl.dart'; import 'package:loggy/loggy.dart'; +import 'package:veilid_support/veilid_support.dart'; -import '../old_to_refactor/pages/developer.dart'; -import '../../packages/veilid_support/veilid_support.dart'; +import '../veilid_processor/views/developer.dart'; import 'state_logger.dart'; String wrapWithLogColor(LogLevel? level, String text) { diff --git a/lib/tools/stream_wrapper_cubit.dart b/lib/tools/stream_wrapper_cubit.dart new file mode 100644 index 0000000..a858cb1 --- /dev/null +++ b/lib/tools/stream_wrapper_cubit.dart @@ -0,0 +1,25 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:veilid_support/veilid_support.dart'; + +abstract class StreamWrapperCubit extends Cubit> { + StreamWrapperCubit(Stream stream, {State? defaultState}) + : super(defaultState != null + ? AsyncValue.data(defaultState) + : const AsyncValue.loading()) { + _subscription = stream.listen((event) => emit(AsyncValue.data(event)), + // ignore: avoid_types_on_closure_parameters + onError: (Object error, StackTrace stackTrace) { + emit(AsyncValue.error(error, stackTrace)); + }); + + @override + Future close() async { + await _subscription.cancel(); + await super.close(); + } + } + + late final StreamSubscription _subscription; +} diff --git a/lib/tools/tools.dart b/lib/tools/tools.dart index 203cfc3..147829f 100644 --- a/lib/tools/tools.dart +++ b/lib/tools/tools.dart @@ -1,10 +1,9 @@ export 'animations.dart'; -export 'async_table_db_backed_cubit.dart'; -export 'async_value.dart'; export 'loggy.dart'; export 'phono_byte.dart'; export 'responsive.dart'; export 'scanner_error_widget.dart'; export 'state_logger.dart'; +export 'stream_wrapper_cubit.dart'; export 'widget_helpers.dart'; export 'window_control.dart'; diff --git a/lib/veilid_processor/cubit/connection_state_cubit.dart b/lib/veilid_processor/cubit/connection_state_cubit.dart new file mode 100644 index 0000000..e3ef7fa --- /dev/null +++ b/lib/veilid_processor/cubit/connection_state_cubit.dart @@ -0,0 +1,12 @@ +import '../../tools/tools.dart'; +import '../models/models.dart'; +import '../repository/processor_repository.dart'; + +export '../models/processor_connection_state.dart'; + +class ConnectionStateCubit + extends StreamWrapperCubit { + ConnectionStateCubit(ProcessorRepository processorRepository) + : super(processorRepository.streamProcessorConnectionState(), + defaultState: processorRepository.processorConnectionState); +} diff --git a/lib/veilid_processor/models/models.dart b/lib/veilid_processor/models/models.dart new file mode 100644 index 0000000..4dd8061 --- /dev/null +++ b/lib/veilid_processor/models/models.dart @@ -0,0 +1 @@ +export 'processor_connection_state.dart'; diff --git a/lib/veilid_processor/models/processor_connection_state.dart b/lib/veilid_processor/models/processor_connection_state.dart new file mode 100644 index 0000000..c5220fb --- /dev/null +++ b/lib/veilid_processor/models/processor_connection_state.dart @@ -0,0 +1,19 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:veilid_support/veilid_support.dart'; + +part 'processor_connection_state.freezed.dart'; + +@freezed +class ProcessorConnectionState with _$ProcessorConnectionState { + const factory ProcessorConnectionState({ + required VeilidStateAttachment attachment, + required VeilidStateNetwork network, + }) = _ProcessorConnectionState; + const ProcessorConnectionState._(); + + bool get isAttached => !(attachment.state == AttachmentState.detached || + attachment.state == AttachmentState.detaching || + attachment.state == AttachmentState.attaching); + + bool get isPublicInternetReady => attachment.publicInternetReady; +} diff --git a/lib/veilid_processor/models/processor_connection_state.freezed.dart b/lib/veilid_processor/models/processor_connection_state.freezed.dart new file mode 100644 index 0000000..a6e01fa --- /dev/null +++ b/lib/veilid_processor/models/processor_connection_state.freezed.dart @@ -0,0 +1,184 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'processor_connection_state.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$ProcessorConnectionState { + VeilidStateAttachment get attachment => throw _privateConstructorUsedError; + VeilidStateNetwork get network => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $ProcessorConnectionStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ProcessorConnectionStateCopyWith<$Res> { + factory $ProcessorConnectionStateCopyWith(ProcessorConnectionState value, + $Res Function(ProcessorConnectionState) then) = + _$ProcessorConnectionStateCopyWithImpl<$Res, ProcessorConnectionState>; + @useResult + $Res call({VeilidStateAttachment attachment, VeilidStateNetwork network}); + + $VeilidStateAttachmentCopyWith<$Res> get attachment; + $VeilidStateNetworkCopyWith<$Res> get network; +} + +/// @nodoc +class _$ProcessorConnectionStateCopyWithImpl<$Res, + $Val extends ProcessorConnectionState> + implements $ProcessorConnectionStateCopyWith<$Res> { + _$ProcessorConnectionStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? attachment = null, + Object? network = null, + }) { + return _then(_value.copyWith( + attachment: null == attachment + ? _value.attachment + : attachment // ignore: cast_nullable_to_non_nullable + as VeilidStateAttachment, + network: null == network + ? _value.network + : network // ignore: cast_nullable_to_non_nullable + as VeilidStateNetwork, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $VeilidStateAttachmentCopyWith<$Res> get attachment { + return $VeilidStateAttachmentCopyWith<$Res>(_value.attachment, (value) { + return _then(_value.copyWith(attachment: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $VeilidStateNetworkCopyWith<$Res> get network { + return $VeilidStateNetworkCopyWith<$Res>(_value.network, (value) { + return _then(_value.copyWith(network: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$ProcessorConnectionStateImplCopyWith<$Res> + implements $ProcessorConnectionStateCopyWith<$Res> { + factory _$$ProcessorConnectionStateImplCopyWith( + _$ProcessorConnectionStateImpl value, + $Res Function(_$ProcessorConnectionStateImpl) then) = + __$$ProcessorConnectionStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({VeilidStateAttachment attachment, VeilidStateNetwork network}); + + @override + $VeilidStateAttachmentCopyWith<$Res> get attachment; + @override + $VeilidStateNetworkCopyWith<$Res> get network; +} + +/// @nodoc +class __$$ProcessorConnectionStateImplCopyWithImpl<$Res> + extends _$ProcessorConnectionStateCopyWithImpl<$Res, + _$ProcessorConnectionStateImpl> + implements _$$ProcessorConnectionStateImplCopyWith<$Res> { + __$$ProcessorConnectionStateImplCopyWithImpl( + _$ProcessorConnectionStateImpl _value, + $Res Function(_$ProcessorConnectionStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? attachment = null, + Object? network = null, + }) { + return _then(_$ProcessorConnectionStateImpl( + attachment: null == attachment + ? _value.attachment + : attachment // ignore: cast_nullable_to_non_nullable + as VeilidStateAttachment, + network: null == network + ? _value.network + : network // ignore: cast_nullable_to_non_nullable + as VeilidStateNetwork, + )); + } +} + +/// @nodoc + +class _$ProcessorConnectionStateImpl extends _ProcessorConnectionState { + const _$ProcessorConnectionStateImpl( + {required this.attachment, required this.network}) + : super._(); + + @override + final VeilidStateAttachment attachment; + @override + final VeilidStateNetwork network; + + @override + String toString() { + return 'ProcessorConnectionState(attachment: $attachment, network: $network)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ProcessorConnectionStateImpl && + (identical(other.attachment, attachment) || + other.attachment == attachment) && + (identical(other.network, network) || other.network == network)); + } + + @override + int get hashCode => Object.hash(runtimeType, attachment, network); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$ProcessorConnectionStateImplCopyWith<_$ProcessorConnectionStateImpl> + get copyWith => __$$ProcessorConnectionStateImplCopyWithImpl< + _$ProcessorConnectionStateImpl>(this, _$identity); +} + +abstract class _ProcessorConnectionState extends ProcessorConnectionState { + const factory _ProcessorConnectionState( + {required final VeilidStateAttachment attachment, + required final VeilidStateNetwork network}) = + _$ProcessorConnectionStateImpl; + const _ProcessorConnectionState._() : super._(); + + @override + VeilidStateAttachment get attachment; + @override + VeilidStateNetwork get network; + @override + @JsonKey(ignore: true) + _$$ProcessorConnectionStateImplCopyWith<_$ProcessorConnectionStateImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/veilid_processor/repository/processor_repository.dart b/lib/veilid_processor/repository/processor_repository.dart new file mode 100644 index 0000000..fa99a95 --- /dev/null +++ b/lib/veilid_processor/repository/processor_repository.dart @@ -0,0 +1,132 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:veilid_support/veilid_support.dart'; + +import '../../app.dart'; +import '../../tools/tools.dart'; +import '../models/models.dart'; + +class ProcessorRepository { + ProcessorRepository._() + : startedUp = false, + _controllerConnectionState = StreamController.broadcast(sync: true), + processorConnectionState = ProcessorConnectionState( + attachment: const VeilidStateAttachment( + state: AttachmentState.detached, + publicInternetReady: false, + localNetworkReady: false), + network: VeilidStateNetwork( + started: false, + bpsDown: BigInt.zero, + bpsUp: BigInt.zero, + peers: [])); + + ////////////////////////////////////////////////////////////// + /// Singleton initialization + + static ProcessorRepository instance = ProcessorRepository._(); + + Future startup() async { + if (startedUp) { + return; + } + + var veilidVersion = ''; + + try { + veilidVersion = Veilid.instance.veilidVersionString(); + } on Exception { + veilidVersion = 'Failed to get veilid version.'; + } + + log.info('Veilid version: $veilidVersion'); + + // HACK: In case of hot restart shut down first + try { + await Veilid.instance.shutdownVeilidCore(); + } on Exception { + // Do nothing on failure here + } + + final updateStream = await Veilid.instance + .startupVeilidCore(await getVeilidConfig(kIsWeb, VeilidChatApp.name)); + _updateSubscription = updateStream.listen((update) { + if (update is VeilidLog) { + processLog(update); + } else if (update is VeilidUpdateAttachment) { + processUpdateAttachment(update); + } else if (update is VeilidUpdateConfig) { + processUpdateConfig(update); + } else if (update is VeilidUpdateNetwork) { + processUpdateNetwork(update); + } else if (update is VeilidAppMessage) { + processAppMessage(update); + } else if (update is VeilidAppCall) { + log.info('AppCall: ${update.toJson()}'); + } else if (update is VeilidUpdateValueChange) { + processUpdateValueChange(update); + } else { + log.trace('Update: ${update.toJson()}'); + } + }); + + startedUp = true; + + await Veilid.instance.attach(); + } + + Future shutdown() async { + if (!startedUp) { + return; + } + await Veilid.instance.shutdownVeilidCore(); + await _updateSubscription?.cancel(); + _updateSubscription = null; + + startedUp = false; + } + + Stream streamProcessorConnectionState() => + _controllerConnectionState.stream; + + void processUpdateAttachment(VeilidUpdateAttachment updateAttachment) { + // Set connection meter and ui state for connection state + processorConnectionState = processorConnectionState.copyWith( + attachment: VeilidStateAttachment( + state: updateAttachment.state, + publicInternetReady: updateAttachment.publicInternetReady, + localNetworkReady: updateAttachment.localNetworkReady)); + } + + void processUpdateConfig(VeilidUpdateConfig updateConfig) { + log.debug('VeilidUpdateConfig: ${updateConfig.toJson()}'); + } + + void processUpdateNetwork(VeilidUpdateNetwork updateNetwork) { + // Set connection meter and ui state for connection state + processorConnectionState = processorConnectionState.copyWith( + network: VeilidStateNetwork( + started: updateNetwork.started, + bpsDown: updateNetwork.bpsDown, + bpsUp: updateNetwork.bpsUp, + peers: updateNetwork.peers)); + _controllerConnectionState.add(processorConnectionState); + } + + void processAppMessage(VeilidAppMessage appMessage) { + log.debug('VeilidAppMessage: ${appMessage.toJson()}'); + } + + void processUpdateValueChange(VeilidUpdateValueChange updateValueChange) { + // Send value updates to DHTRecordPool + DHTRecordPool.instance.processUpdateValueChange(updateValueChange); + } + + //////////////////////////////////////////// + + StreamSubscription? _updateSubscription; + final StreamController _controllerConnectionState; + bool startedUp; + ProcessorConnectionState processorConnectionState; +} diff --git a/lib/veilid_processor/veilid_processor.dart b/lib/veilid_processor/veilid_processor.dart new file mode 100644 index 0000000..12d36bd --- /dev/null +++ b/lib/veilid_processor/veilid_processor.dart @@ -0,0 +1,4 @@ +export 'cubit/connection_state_cubit.dart'; +export 'models/models.dart'; +export 'repository/processor_repository.dart'; +export 'views/views.dart'; diff --git a/lib/old_to_refactor/pages/developer.dart b/lib/veilid_processor/views/developer.dart similarity index 98% rename from lib/old_to_refactor/pages/developer.dart rename to lib/veilid_processor/views/developer.dart index 1dc56cd..f738d6c 100644 --- a/lib/old_to_refactor/pages/developer.dart +++ b/lib/veilid_processor/views/developer.dart @@ -6,15 +6,15 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_animate/flutter_animate.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_translate/flutter_translate.dart'; import 'package:go_router/go_router.dart'; import 'package:loggy/loggy.dart'; import 'package:quickalert/quickalert.dart'; +import 'package:veilid_support/veilid_support.dart'; import 'package:xterm/xterm.dart'; +import '../../theme/theme.dart'; import '../../tools/tools.dart'; -import '../../../packages/veilid_support/veilid_support.dart'; final globalDebugTerminal = Terminal( maxLines: 50000, @@ -32,7 +32,7 @@ class DeveloperPage extends StatefulWidget { DeveloperPageState createState() => DeveloperPageState(); } -class DeveloperPageState extends ConsumerState { +class DeveloperPageState extends State { final _terminalController = TerminalController(); final _debugCommandController = TextEditingController(); final _logLevelController = DropdownController(duration: 250.ms); diff --git a/lib/veilid_processor/views/signal_strength_meter.dart b/lib/veilid_processor/views/signal_strength_meter.dart new file mode 100644 index 0000000..9b189f4 --- /dev/null +++ b/lib/veilid_processor/views/signal_strength_meter.dart @@ -0,0 +1,88 @@ +import 'package:awesome_extensions/awesome_extensions.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:quickalert/quickalert.dart'; +import 'package:signal_strength_indicator/signal_strength_indicator.dart'; +import 'package:veilid_support/veilid_support.dart'; + +import '../../theme/theme.dart'; +import '../cubit/connection_state_cubit.dart'; + +class SignalStrengthMeterWidget extends StatelessWidget { + const SignalStrengthMeterWidget({super.key}); + + @override + // ignore: prefer_expression_function_bodies + Widget build(BuildContext context) { + final theme = Theme.of(context); + final scale = theme.extension()!; + + const iconSize = 16.0; + + return BlocBuilder>(builder: (context, state) { + late final Widget iconWidget; + state.when( + data: (connectionState) { + late final double value; + late final Color color; + late final Color inactiveColor; + + switch (connectionState.attachment.state) { + case AttachmentState.detached: + iconWidget = Icon(Icons.signal_cellular_nodata, + size: iconSize, color: scale.grayScale.text); + return; + case AttachmentState.detaching: + iconWidget = Icon(Icons.signal_cellular_off, + size: iconSize, color: scale.grayScale.text); + return; + case AttachmentState.attaching: + value = 0; + color = scale.primaryScale.text; + case AttachmentState.attachedWeak: + value = 1; + color = scale.primaryScale.text; + case AttachmentState.attachedStrong: + value = 2; + color = scale.primaryScale.text; + case AttachmentState.attachedGood: + value = 3; + color = scale.primaryScale.text; + case AttachmentState.fullyAttached: + value = 4; + color = scale.primaryScale.text; + case AttachmentState.overAttached: + value = 4; + color = scale.secondaryScale.subtleText; + } + inactiveColor = scale.grayScale.subtleText; + + iconWidget = SignalStrengthIndicator.bars( + value: value, + activeColor: color, + inactiveColor: inactiveColor, + size: iconSize, + barCount: 4, + spacing: 1); + }, + loading: () => {iconWidget = const Icon(Icons.warning)}, + error: (e, st) => { + iconWidget = const Icon(Icons.error).onTap( + () async => QuickAlert.show( + type: QuickAlertType.error, + context: context, + title: 'Error', + text: 'Error: {e}\n StackTrace: {st}'), + ) + }); + + return GestureDetector( + onLongPress: () async { + await GoRouterHelper(context).push('/developer'); + }, + child: iconWidget); + }); + } +} diff --git a/lib/veilid_processor/views/views.dart b/lib/veilid_processor/views/views.dart new file mode 100644 index 0000000..3d70862 --- /dev/null +++ b/lib/veilid_processor/views/views.dart @@ -0,0 +1,2 @@ +export 'developer.dart'; +export 'signal_strength_meter.dart'; diff --git a/packages/veilid_support/lib/dht_support/dht_support.dart b/packages/veilid_support/lib/dht_support/dht_support.dart index d4f0b09..0d56e45 100644 --- a/packages/veilid_support/lib/dht_support/dht_support.dart +++ b/packages/veilid_support/lib/dht_support/dht_support.dart @@ -4,5 +4,6 @@ library dht_support; export 'src/dht_record.dart'; export 'src/dht_record_crypto.dart'; +export 'src/dht_record_cubit.dart'; export 'src/dht_record_pool.dart'; export 'src/dht_short_array.dart'; diff --git a/packages/veilid_support/lib/dht_support/src/dht_record.dart b/packages/veilid_support/lib/dht_support/src/dht_record.dart index bc6dea2..f810974 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_record.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_record.dart @@ -3,7 +3,7 @@ import 'dart:typed_data'; import 'package:protobuf/protobuf.dart'; -import '../../../../veilid_support.dart'; +import '../../../veilid_support.dart'; class DHTRecord { DHTRecord( @@ -28,6 +28,7 @@ class DHTRecord { final DHTRecordCrypto _crypto; bool _open; bool _valid; + StreamSubscription? _watchSubscription; int subkeyOrDefault(int subkey) => (subkey == -1) ? _defaultSubkey : subkey; @@ -47,9 +48,8 @@ class DHTRecord { if (!_open) { return; } - final pool = await DHTRecordPool.instance(); await _routingContext.closeDHTRecord(_recordDescriptor.key); - pool.recordClosed(_recordDescriptor.key); + await DHTRecordPool.instance.recordClosed(_recordDescriptor.key); _open = false; } @@ -60,8 +60,7 @@ class DHTRecord { if (_open) { await close(); } - final pool = await DHTRecordPool.instance(); - await pool.deleteDeep(key); + await DHTRecordPool.instance.deleteDeep(key); _valid = false; } @@ -253,4 +252,20 @@ class DHTRecord { T Function(List) fromBuffer, Future Function(T) update, {int subkey = -1}) => eventualUpdateBytes(protobufUpdate(fromBuffer, update), subkey: subkey); + + Future watch( + Future Function(VeilidUpdateValueChange update) onUpdate, + {List? subkeys, + Timestamp? expiration, + int? count}) async { + // register watch with pool + _watchSubscription = await DHTRecordPool.instance.recordWatch( + _recordDescriptor.key, onUpdate, + subkeys: subkeys, expiration: expiration, count: count); + } + + Future cancelWatch() async { + // register watch with pool + await _watchSubscription?.cancel(); + } } diff --git a/packages/veilid_support/lib/dht_support/src/dht_record_cubit.dart b/packages/veilid_support/lib/dht_support/src/dht_record_cubit.dart new file mode 100644 index 0000000..494b4e0 --- /dev/null +++ b/packages/veilid_support/lib/dht_support/src/dht_record_cubit.dart @@ -0,0 +1,53 @@ +import 'package:bloc/bloc.dart'; + +import '../../veilid_support.dart'; + +class DhtRecordCubit extends Cubit> { + DhtRecordCubit({ + required DHTRecord record, + required Future Function(DHTRecord, VeilidUpdateValueChange) + stateFunction, + List watchSubkeys = const [], + }) : _record = record, + super(const AsyncValue.loading()) { + Future.delayed(Duration.zero, () async { + await record.watch((update) async { + try { + final newState = await stateFunction(record, update); + if (newState != null) { + emit(AsyncValue.data(newState)); + } + } on Exception catch (e) { + emit(AsyncValue.error(e)); + } + }, subkeys: watchSubkeys); + }); + } + + @override + Future close() async { + await _record.cancelWatch(); + await super.close(); + } + + DHTRecord _record; +} + +class SingleDHTRecordCubit extends DhtRecordCubit { + SingleDHTRecordCubit( + {required super.record, + required T? Function(List data) decodeState, + int singleSubkey = 0}) + : super( + stateFunction: (record, update) async { + // + if (update.subkeys.isNotEmpty) { + final newState = decodeState(update.valueData.data); + return newState; + } + return null; + }, + watchSubkeys: [ + ValueSubkeyRange(low: singleSubkey, high: singleSubkey) + ]); +} diff --git a/packages/veilid_support/lib/dht_support/src/dht_record_pool.dart b/packages/veilid_support/lib/dht_support/src/dht_record_pool.dart index 33960bb..937ec4b 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_record_pool.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_record_pool.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:mutex/mutex.dart'; @@ -35,24 +37,55 @@ class OwnedDHTRecordPointer with _$OwnedDHTRecordPointer { _$OwnedDHTRecordPointerFromJson(json as Map); } +/// Watch state +class _WatchState { + _WatchState( + {required this.subkeys, required this.expiration, required this.count}); + List? subkeys; + Timestamp? expiration; + int? count; + Timestamp? realExpiration; +} + +/// Opened DHTRecord state +class _OpenedDHTRecord { + _OpenedDHTRecord(this.routingContext) + : mutex = Mutex(), + needsWatchStateUpdate = false, + inWatchStateUpdate = false; + + Future close() async { + await watchController?.close(); + } + + Mutex mutex; + StreamController? watchController; + bool needsWatchStateUpdate; + bool inWatchStateUpdate; + _WatchState? watchState; + VeilidRoutingContext routingContext; +} + class DHTRecordPool with TableDBBacked { DHTRecordPool._(Veilid veilid, VeilidRoutingContext routingContext) : _state = DHTRecordPoolAllocations( childrenByParent: IMap(), parentByChild: IMap(), rootRecords: ISet()), - _opened = {}, + _opened = {}, _routingContext = routingContext, _veilid = veilid; // Persistent DHT record list DHTRecordPoolAllocations _state; // Which DHT records are currently open - final Map _opened; + final Map _opened; // Default routing context to use for new keys final VeilidRoutingContext _routingContext; // Convenience accessor final Veilid _veilid; + // If tick is already running or not + bool inTick = false; static DHTRecordPool? _singleton; @@ -71,37 +104,71 @@ class DHTRecordPool with TableDBBacked { Object? valueToJson(DHTRecordPoolAllocations val) => val.toJson(); ////////////////////////////////////////////////////////////// - static Mutex instanceSetupMutex = Mutex(); - // ignore: prefer_expression_function_bodies - static Future instance() async { - return instanceSetupMutex.protect(() async { - if (_singleton == null) { - final routingContext = await Veilid.instance.routingContext(); - final globalPool = DHTRecordPool._(Veilid.instance, routingContext); - globalPool._state = await globalPool.load(); - _singleton = globalPool; - } - return _singleton!; - }); + static DHTRecordPool get instance => _singleton!; + + static Future init() async { + final routingContext = await Veilid.instance.routingContext(); + final globalPool = DHTRecordPool._(Veilid.instance, routingContext); + globalPool._state = await globalPool.load(); + _singleton = globalPool; } Veilid get veilid => _veilid; - Future _recordOpened(TypedKey key) async { + Future _recordOpened( + TypedKey key, VeilidRoutingContext routingContext) async { // no race because dart is single threaded until async breaks - final m = _opened[key] ?? Mutex(); - _opened[key] = m; - await m.acquire(); - _opened[key] = m; + final odr = _opened[key] ?? _OpenedDHTRecord(routingContext); + _opened[key] = odr; + await odr.mutex.acquire(); } - void recordClosed(TypedKey key) { - final m = _opened.remove(key); - if (m == null) { + Future> recordWatch( + TypedKey key, Future Function(VeilidUpdateValueChange) onUpdate, + {required List? subkeys, + required Timestamp? expiration, + required int? count}) async { + final odr = _opened[key]; + if (odr == null) { + throw StateError("can't watch unopened record"); + } + + // Set up watch requirements + odr + ..watchState = + _WatchState(subkeys: subkeys, expiration: expiration, count: count) + ..needsWatchStateUpdate = true + ..watchController ??= + StreamController.broadcast(onCancel: () { + // Request watch state change for cancel + odr + ..watchState = null + ..needsWatchStateUpdate = true; + // If there are no more listeners then we can get rid of the controller + if (!(odr.watchController?.hasListener ?? true)) { + odr.watchController = null; + } + }); + + return odr.watchController!.stream.listen( + (update) { + Future.delayed(Duration.zero, () => onUpdate(update)); + }, + cancelOnError: true, + onError: (e) async { + await odr.watchController!.close(); + odr.watchController = null; + }); + } + + Future recordClosed(TypedKey key) async { + final odr = _opened.remove(key); + if (odr == null) { throw StateError('record already closed'); } - m.release(); + await odr.close(); + odr.mutex.release(); } Future deleteDeep(TypedKey parent) async { @@ -112,7 +179,8 @@ class DHTRecordPool with TableDBBacked { final nextDep = currentDeps.removeLast(); // Ensure we get the exclusive lock on this record - await _recordOpened(nextDep); + // Can use default routing context here because we are only deleting + await _recordOpened(nextDep, _routingContext); // Remove this child from its parent await _removeDependency(nextDep); @@ -127,7 +195,7 @@ class DHTRecordPool with TableDBBacked { final allFutures = >[]; for (final dep in allDeps) { allFutures.add(_routingContext.deleteDHTRecord(dep)); - recordClosed(dep); + await recordClosed(dep); } await Future.wait(allFutures); } @@ -220,7 +288,7 @@ class DHTRecordPool with TableDBBacked { recordDescriptor.ownerTypedKeyPair()!)); await _addDependency(parent, rec.key); - await _recordOpened(rec.key); + await _recordOpened(rec.key, dhtctx); return rec; } @@ -231,7 +299,9 @@ class DHTRecordPool with TableDBBacked { TypedKey? parent, int defaultSubkey = 0, DHTRecordCrypto? crypto}) async { - await _recordOpened(recordKey); + final dhtctx = routingContext ?? _routingContext; + + await _recordOpened(recordKey, dhtctx); late final DHTRecord rec; try { @@ -240,7 +310,6 @@ class DHTRecordPool with TableDBBacked { _validateParent(parent, recordKey); // Open from the veilid api - final dhtctx = routingContext ?? _routingContext; final recordDescriptor = await dhtctx.openDHTRecord(recordKey, null); rec = DHTRecord( routingContext: dhtctx, @@ -251,7 +320,7 @@ class DHTRecordPool with TableDBBacked { // Register the dependency await _addDependency(parent, rec.key); } on Exception catch (_) { - recordClosed(recordKey); + await recordClosed(recordKey); rethrow; } @@ -267,7 +336,9 @@ class DHTRecordPool with TableDBBacked { int defaultSubkey = 0, DHTRecordCrypto? crypto, }) async { - await _recordOpened(recordKey); + final dhtctx = routingContext ?? _routingContext; + + await _recordOpened(recordKey, dhtctx); late final DHTRecord rec; try { @@ -276,7 +347,6 @@ class DHTRecordPool with TableDBBacked { _validateParent(parent, recordKey); // Open from the veilid api - final dhtctx = routingContext ?? _routingContext; final recordDescriptor = await dhtctx.openDHTRecord(recordKey, writer); rec = DHTRecord( routingContext: dhtctx, @@ -290,7 +360,7 @@ class DHTRecordPool with TableDBBacked { // Register the dependency if specified await _addDependency(parent, rec.key); } on Exception catch (_) { - recordClosed(recordKey); + await recordClosed(recordKey); rethrow; } @@ -324,4 +394,69 @@ class DHTRecordPool with TableDBBacked { final childJson = child.toJson(); return _state.parentByChild[childJson]; } + + /// Handle the DHT record updates coming from Veilid + void processUpdateValueChange(VeilidUpdateValueChange updateValueChange) { + if (updateValueChange.subkeys.isNotEmpty) {} + } + + /// Ticker to check watch state change requests + Future tick() async { + if (inTick) { + return; + } + inTick = true; + try { + // See if any opened records need watch state changes + final unord = List>.empty(growable: true); + + for (final kv in _opened.entries) { + // Check if already updating + if (kv.value.inWatchStateUpdate) { + continue; + } + + if (kv.value.needsWatchStateUpdate) { + kv.value.inWatchStateUpdate = true; + + final ws = kv.value.watchState; + if (ws == null) { + unord.add(() async { + // Record needs watch cancel + try { + final done = + await kv.value.routingContext.cancelDHTWatch(kv.key); + assert(done, + 'should always be done when cancelling whole subkey range'); + kv.value.needsWatchStateUpdate = false; + } on VeilidAPIException { + // Failed to cancel DHT watch, try again next tick + } + kv.value.inWatchStateUpdate = false; + }()); + } else { + unord.add(() async { + // Record needs new watch + try { + final realExpiration = await kv.value.routingContext + .watchDHTValues(kv.key, + subkeys: ws.subkeys, + count: ws.count, + expiration: ws.expiration); + kv.value.needsWatchStateUpdate = false; + ws.realExpiration = realExpiration; + } on VeilidAPIException { + // Failed to cancel DHT watch, try again next tick + } + kv.value.inWatchStateUpdate = false; + }()); + } + } + } + + await unord.wait; + } finally { + inTick = false; + } + } } diff --git a/packages/veilid_support/lib/dht_support/src/dht_short_array.dart b/packages/veilid_support/lib/dht_support/src/dht_short_array.dart index f115e5a..ed917d1 100644 --- a/packages/veilid_support/lib/dht_support/src/dht_short_array.dart +++ b/packages/veilid_support/lib/dht_support/src/dht_short_array.dart @@ -69,7 +69,7 @@ class DHTShortArray { DHTRecordCrypto? crypto, KeyPair? smplWriter}) async { assert(stride <= maxElements, 'stride too long'); - final pool = await DHTRecordPool.instance(); + final pool = DHTRecordPool.instance; late final DHTRecord dhtRecord; if (smplWriter != null) { @@ -111,9 +111,7 @@ class DHTShortArray { {VeilidRoutingContext? routingContext, TypedKey? parent, DHTRecordCrypto? crypto}) async { - final pool = await DHTRecordPool.instance(); - - final dhtRecord = await pool.openRead(headRecordKey, + final dhtRecord = await DHTRecordPool.instance.openRead(headRecordKey, parent: parent, routingContext: routingContext, crypto: crypto); try { final dhtShortArray = DHTShortArray._(headRecord: dhtRecord); @@ -132,8 +130,8 @@ class DHTShortArray { TypedKey? parent, DHTRecordCrypto? crypto, }) async { - final pool = await DHTRecordPool.instance(); - final dhtRecord = await pool.openWrite(headRecordKey, writer, + final dhtRecord = await DHTRecordPool.instance.openWrite( + headRecordKey, writer, parent: parent, routingContext: routingContext, crypto: crypto); try { final dhtShortArray = DHTShortArray._(headRecord: dhtRecord); @@ -214,17 +212,15 @@ class DHTShortArray { /// Open a linked record for reading or writing, same as the head record Future _openLinkedRecord(TypedKey recordKey) async { - final pool = await DHTRecordPool.instance(); - final writer = _headRecord.writer; return (writer != null) - ? await pool.openWrite( + ? await DHTRecordPool.instance.openWrite( recordKey, writer, parent: _headRecord.key, routingContext: _headRecord.routingContext, ) - : await pool.openRead( + : await DHTRecordPool.instance.openRead( recordKey, parent: _headRecord.key, routingContext: _headRecord.routingContext, diff --git a/lib/tools/async_table_db_backed_cubit.dart b/packages/veilid_support/lib/src/async_table_db_backed_cubit.dart similarity index 89% rename from lib/tools/async_table_db_backed_cubit.dart rename to packages/veilid_support/lib/src/async_table_db_backed_cubit.dart index d8183a7..ab1c3a8 100644 --- a/lib/tools/async_table_db_backed_cubit.dart +++ b/packages/veilid_support/lib/src/async_table_db_backed_cubit.dart @@ -2,9 +2,7 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; -import '../../tools/tools.dart'; -import '../init.dart'; -import '../../packages/veilid_support/veilid_support.dart'; +import '../veilid_support.dart'; abstract class AsyncTableDBBackedCubit extends Cubit> with TableDBBacked { @@ -14,7 +12,6 @@ abstract class AsyncTableDBBackedCubit extends Cubit> Future _build() async { try { - await eventualVeilid.future; emit(AsyncValue.data(await load())); } on Exception catch (e, stackTrace) { emit(AsyncValue.error(e, stackTrace)); diff --git a/lib/tools/async_value.dart b/packages/veilid_support/lib/src/async_value.dart similarity index 100% rename from lib/tools/async_value.dart rename to packages/veilid_support/lib/src/async_value.dart diff --git a/lib/tools/async_value.freezed.dart b/packages/veilid_support/lib/src/async_value.freezed.dart similarity index 100% rename from lib/tools/async_value.freezed.dart rename to packages/veilid_support/lib/src/async_value.freezed.dart diff --git a/packages/veilid_support/lib/src/identity.dart b/packages/veilid_support/lib/src/identity.dart index c4162df..5a77e2e 100644 --- a/packages/veilid_support/lib/src/identity.dart +++ b/packages/veilid_support/lib/src/identity.dart @@ -78,7 +78,7 @@ class IdentityMaster with _$IdentityMaster { extension IdentityMasterExtension on IdentityMaster { /// Deletes a master identity and the identity record under it Future delete() async { - final pool = await DHTRecordPool.instance(); + final pool = DHTRecordPool.instance; await (await pool.openRead(masterRecordKey)).delete(); } @@ -95,7 +95,7 @@ extension IdentityMasterExtension on IdentityMaster { {required SharedSecret identitySecret, required String accountKey}) async { // Read the identity key to get the account keys - final pool = await DHTRecordPool.instance(); + final pool = DHTRecordPool.instance; final identityRecordCrypto = await DHTRecordCryptoPrivate.fromSecret( identityRecordKey.kind, identitySecret); @@ -129,7 +129,7 @@ extension IdentityMasterExtension on IdentityMaster { required String accountKey, required Future Function(TypedKey parent) createAccountCallback, }) async { - final pool = await DHTRecordPool.instance(); + final pool = DHTRecordPool.instance; /////// Add account with profile to DHT @@ -186,7 +186,7 @@ class IdentityMasterWithSecrets { /// Creates a new master identity and returns it with its secrets static Future create() async { - final pool = await DHTRecordPool.instance(); + final pool = DHTRecordPool.instance; // IdentityMaster DHT record is public/unencrypted return (await pool.create(crypto: const DHTRecordCryptoPublic())) @@ -245,7 +245,7 @@ class IdentityMasterWithSecrets { /// Opens an existing master identity and validates it Future openIdentityMaster( {required TypedKey identityMasterRecordKey}) async { - final pool = await DHTRecordPool.instance(); + final pool = DHTRecordPool.instance; // IdentityMaster DHT record is public/unencrypted return (await pool.openRead(identityMasterRecordKey)) diff --git a/packages/veilid_support/lib/src/veilid_log.dart b/packages/veilid_support/lib/src/veilid_log.dart index b112292..f5db930 100644 --- a/packages/veilid_support/lib/src/veilid_log.dart +++ b/packages/veilid_support/lib/src/veilid_log.dart @@ -39,7 +39,7 @@ class VeilidLoggy implements LoggyType { Loggy get _veilidLoggy => Loggy('Veilid'); -Future processLog(VeilidLog log) async { +void processLog(VeilidLog log) { StackTrace? stackTrace; Object? error; final backtrace = log.backtrace; diff --git a/packages/veilid_support/lib/veilid_support.dart b/packages/veilid_support/lib/veilid_support.dart index f873397..fec2539 100644 --- a/packages/veilid_support/lib/veilid_support.dart +++ b/packages/veilid_support/lib/veilid_support.dart @@ -6,6 +6,7 @@ library veilid_support; export 'package:veilid/veilid.dart'; export 'dht_support/dht_support.dart'; +export 'src/async_value.dart'; export 'src/config.dart'; export 'src/identity.dart'; export 'src/json_tools.dart'; diff --git a/packages/veilid_support/pubspec.lock b/packages/veilid_support/pubspec.lock index cba74d2..d80f0e7 100644 --- a/packages/veilid_support/pubspec.lock +++ b/packages/veilid_support/pubspec.lock @@ -33,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + bloc: + dependency: "direct main" + description: + name: bloc + sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" + url: "https://pub.dev" + source: hosted + version: "8.1.2" boolean_selector: dependency: transitive description: diff --git a/packages/veilid_support/pubspec.yaml b/packages/veilid_support/pubspec.yaml index 397b24b..44e6e08 100644 --- a/packages/veilid_support/pubspec.yaml +++ b/packages/veilid_support/pubspec.yaml @@ -5,9 +5,9 @@ version: 1.0.2+0 environment: sdk: '>=3.0.5 <4.0.0' - flutter: ">=3.10.0" dependencies: + bloc: ^8.1.2 fast_immutable_collections: ^9.1.5 freezed_annotation: ^2.2.0 json_annotation: ^4.8.1 @@ -20,7 +20,7 @@ dependencies: dev_dependencies: build_runner: ^2.4.6 - test: ^1.25.0 freezed: ^2.3.5 json_serializable: ^6.7.1 lint_hard: ^4.0.0 + test: ^1.25.0