From 383f6292409d2ad6b04174f34e04385a696a114f Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 7 Oct 2024 15:20:37 -0400 Subject: [PATCH 1/3] feat: add atsign manager utility functions --- .../onboarding/util/atsign_manager.dart | 146 +++++++++++++++++- packages/dart/npt_flutter/pubspec.lock | 56 +++---- packages/dart/npt_flutter/pubspec.yaml | 1 + 3 files changed, 168 insertions(+), 35 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/onboarding/util/atsign_manager.dart b/packages/dart/npt_flutter/lib/features/onboarding/util/atsign_manager.dart index 73fc2a1a0..4102aeb63 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/util/atsign_manager.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/util/atsign_manager.dart @@ -1,6 +1,31 @@ -abstract class AtsignInformation { - String get atSign; - String get rootDomain; +import 'dart:convert'; +import 'dart:io'; + +import 'package:at_onboarding_flutter/at_onboarding_flutter.dart'; +import 'package:npt_flutter/app.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:path/path.dart' as p; + +class AtsignInformation { + final String atSign; + final String rootDomain; + + AtsignInformation({required this.atSign, required this.rootDomain}); + + Map toJson() => { + "atsign": atSign, + "root-domain": rootDomain, + }; + + static AtsignInformation? fromJson(Map json) { + if (json["atsign"] is! String || json["root-domain"] is! String) { + return null; + } + return AtsignInformation( + atSign: json["atsign"], + rootDomain: json["root-domain"], + ); + } } // This will return a map which looks like: @@ -19,12 +44,119 @@ abstract class AtsignInformation { // Now you have the rootDomain for the existing atSign and can use it to onboard // correctly -Future> getAtsignEntries() { - return Future.value({}); +Future> getAtsignEntries() async { + var keychainAtSigns = await KeychainUtil.getAtsignList() ?? []; + var atSignInfo = []; + try { + atSignInfo = await _getAtsignInformationFromFile(); + } catch (e) { + App.log( + "Failed get Atsign Information, ignoring invalid file: ${e.toString()}" + .loggable, + ); + return {}; + } + var atSignMap = {}; + for (var item in atSignInfo) { + if (keychainAtSigns.contains(item.atSign)) { + atSignMap[item.atSign] = item; + } + } + return atSignMap; } // This class will allow you to store atSign information // you need to call this after onboarding a NEW atSign -Future saveAtsignInformation(AtsignInformation info) { - return Future.value(true); +Future saveAtsignInformation(AtsignInformation info) async { + var f = await _getAtsignInformationFile(); + final List atSignInfo; + try { + atSignInfo = await _getAtsignInformationFromFile(f); + } catch (e) { + // We only end up here if we failed to create, get, or read the file + // we don't want to overwrite it in that scenario, so return false + // + // We won't end up here if it was a json parse error, such as invalid + // json, we do want to overwrite that so that the app can recover as best + // as possible + return false; + } + if (f == null) return false; + + // Replace the existing entry with the new one if it exists + bool found = false; + for (int i = 0; i < atSignInfo.length; i++) { + if (atSignInfo[i].atSign == info.atSign) { + found = true; + atSignInfo[i] = info; + } + } + // Otherwise add it as a new entry + if (!found) { + atSignInfo.add(info); + } + try { + f.writeAsString( + jsonEncode(atSignInfo.map((e) => e.toJson())), + mode: FileMode.writeOnly, + flush: true, + ); + return true; + } catch (e) { + App.log( + "Failed to write Atsign Information to file: ${e.toString()}".loggable, + ); + return false; + } +} + +// Does not throw, returns null if it can't get / create the file +Future _getAtsignInformationFile() async { + final Directory dir; + try { + dir = await getApplicationSupportDirectory(); + dir.create(recursive: true); // This checks if it exists internally + } catch (e) { + App.log( + "Failed to Get Application Support Directory: ${e.toString()}".loggable, + ); + return null; + } + final f = File(p.join(dir.path, "atsign_information.json")); + try { + if (!await f.exists()) { + f.create(recursive: true); + } + return f; + } catch (e) { + App.log( + "Failed to Get Atsign Information File : ${e.toString()}".loggable, + ); + return null; + } +} + +Future> _getAtsignInformationFromFile([File? f]) async { + f ??= await _getAtsignInformationFile(); + if (f == null) throw Exception("Failed to get the Atsign Information File"); + try { + var contents = await f.readAsString(); + var json = jsonDecode(contents); + if (json is! Iterable) { + return []; // The file format is invalid so return as a non-error and we will overwrite it + } + var res = []; + for (var item in json) { + if (item is! Map) continue; + var info = AtsignInformation.fromJson(item); + if (info == null) continue; + res.add(info); + } + return res; + } catch (e) { + App.log( + "Failed to Parse Atsign Information File : ${e.toString()}".loggable, + ); + rethrow; + } } diff --git a/packages/dart/npt_flutter/pubspec.lock b/packages/dart/npt_flutter/pubspec.lock index 3cc2e0107..8a461d80d 100644 --- a/packages/dart/npt_flutter/pubspec.lock +++ b/packages/dart/npt_flutter/pubspec.lock @@ -67,10 +67,10 @@ packages: dependency: transitive description: name: at_auth - sha256: "28f72f0fc26ec7f5f58d28fd29f964c9b2b35ecdc8dd4805ed7174851da2cbcc" + sha256: f4fec32e2a1ca8827604b5e54a7611ddad092c6ba607c138675c1cba5215b038 url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" at_backupkey_flutter: dependency: transitive description: @@ -91,26 +91,26 @@ packages: dependency: transitive description: name: at_chops - sha256: "825171a3132b3756119bd16b6fd1fa6257f74a64babaf13cae2d82d53b8c6be1" + sha256: "0b3d84b8bd2e5027946253d907ff23f967922105efe27432b15743beb74b31f8" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" at_client: dependency: transitive description: name: at_client - sha256: "2e98fe0c0c520b8e7ad6dfd0ad53ecb97f1ceb33c9b117dda69417b72a067c60" + sha256: "2c6aca2b3a2dab16b58330f99bdd00fe05bd05a76ffc5ed6b0d0eb34aaaaab8a" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.2.2" at_client_mobile: dependency: "direct main" description: name: at_client_mobile - sha256: "749b686cf403d4f396fbce5b7684574d983669274553950d92542b50830e9d28" + sha256: "41f45cc094bfc7748303ce263e490d9744bb6014e4c1183df9e983488714fb7e" url: "https://pub.dev" source: hosted - version: "3.2.18" + version: "3.2.19" at_common_flutter: dependency: transitive description: @@ -123,26 +123,26 @@ packages: dependency: transitive description: name: at_commons - sha256: "2d0490a0c5bcd43c6a37911d85b71c133767aec47abc65bd8ecb20c8caaddeab" + sha256: "796eb7f49ab8894782010146368b4ae4f9ed716f2174c29c37d5c53b81281ff6" url: "https://pub.dev" source: hosted - version: "4.0.11" + version: "5.0.0" at_contact: dependency: "direct main" description: name: at_contact - sha256: e1b8904116e6e0fcbc5627409bffe3b620417c62b76bbedc84b1f66acc28adfe + sha256: e67a3545f2df3f0c8e28f0360c8cb301c1677043cd4b797c8d78dc92a69f2e62 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.9" at_contacts_flutter: dependency: "direct main" description: name: at_contacts_flutter - sha256: "530a5112e303fdf8ae26bfbe477112b14c16c8b35f7005ea4919f05584fa8dec" + sha256: e39b77b302c5e12ec7a543b8f1f6b65eed42c36d153f8298dd3b52d7d1ad2051 url: "https://pub.dev" source: hosted - version: "4.0.15" + version: "4.0.16" at_demo_data: dependency: transitive description: @@ -163,27 +163,27 @@ packages: dependency: transitive description: name: at_lookup - sha256: e989099d5f2cd6415097c8e4353340bd2048c9ee1bc82665f2b4f7c4615ad055 + sha256: "2fa727fbdd6d3e5a79132786a74cbf03776833e1671f8cb471d21585f8448f95" url: "https://pub.dev" source: hosted - version: "3.0.47" + version: "3.0.49" at_onboarding_flutter: dependency: "direct main" description: path: "packages/at_onboarding_flutter" ref: trunk - resolved-ref: "8df0a468041dd332534acf5e5e487e018f6ba2da" + resolved-ref: "2a32ac2461673e0df16f5de2e24305309a8fcd95" url: "https://github.com/atsign-foundation/at_widgets" source: git - version: "6.1.8" + version: "6.1.9" at_persistence_secondary_server: dependency: transitive description: name: at_persistence_secondary_server - sha256: "1ec73b56e61b8aee94104ad4610c17cf07e366239337bedd43fa80c7765a391d" + sha256: "387ff2853ee98a8c65526e1df9220fa58c4631b9b1cd6002e9a7372f1a491ed3" url: "https://pub.dev" source: hosted - version: "3.0.63" + version: "3.0.64" at_persistence_spec: dependency: transitive description: @@ -196,10 +196,10 @@ packages: dependency: transitive description: name: at_server_status - sha256: "316c3e6717592677207d4f0a836b013271ca0f729e8b575c9195d19cfc57e71b" + sha256: "2773fa7c4377802b671f6854863214aabe8ee8cd49be87226352dd14562a5d6b" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" at_sync_ui_flutter: dependency: transitive description: @@ -220,10 +220,10 @@ packages: dependency: "direct main" description: name: at_utils - sha256: ec28600e4eec321ee5e22be051109fa7b2e94590dc51d9f957730c2540beb681 + sha256: b4461b0743f323429d57c387e91186537df8a6aeb4608bbeb6c2adf01d9f08f9 url: "https://pub.dev" source: hosted - version: "3.0.16" + version: "3.0.19" biometric_storage: dependency: transitive description: @@ -899,7 +899,7 @@ packages: path: "../noports_core" relative: true source: path - version: "6.1.0" + version: "6.2.0" openssh_ed25519: dependency: transitive description: @@ -933,7 +933,7 @@ packages: source: hosted version: "3.0.1" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" @@ -1509,10 +1509,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" watcher: dependency: transitive description: diff --git a/packages/dart/npt_flutter/pubspec.yaml b/packages/dart/npt_flutter/pubspec.yaml index a2e490743..e698155a1 100644 --- a/packages/dart/npt_flutter/pubspec.yaml +++ b/packages/dart/npt_flutter/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: meta: ^1.15.0 noports_core: path: ../noports_core + path: ^1.9.0 path_provider: ^2.1.4 phosphor_flutter: ^2.1.0 socket_connector: ^2.2.0 From eaa500221447b181a1f174c32b4226a8deb70294 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 7 Oct 2024 16:25:07 -0400 Subject: [PATCH 2/3] feat: add pre_offboard utility function --- packages/dart/npt_flutter/lib/app.dart | 15 +++--- .../features/favorite/bloc/favorite_bloc.dart | 2 + .../onboarding/cubit/at_directory_cubit.dart | 10 ++-- .../onboarding/util/pre_offboard.dart | 14 ++++- .../onboarding_at_directory_selector.dart | 14 +++-- .../onboarding/widgets/onboarding_button.dart | 13 +++-- .../features/profile/bloc/profile_bloc.dart | 53 +++++++++++++------ .../profile/cubit/profile_cache_cubit.dart | 2 + .../profile_list/bloc/profile_list_bloc.dart | 14 +++-- .../cubit/profiles_running_cubit.dart | 7 +++ .../features/settings/bloc/settings_bloc.dart | 2 + .../repository/contact_repository.dart | 11 ++-- .../tray_manager/cubit/tray_cubit.dart | 16 ++++-- .../dart/npt_flutter/lib/styles/sizes.dart | 25 +++++---- .../lib/widgets/custom_text_button.dart | 21 +++++--- 15 files changed, 156 insertions(+), 63 deletions(-) diff --git a/packages/dart/npt_flutter/lib/app.dart b/packages/dart/npt_flutter/lib/app.dart index 1522a593f..6ca42d71e 100644 --- a/packages/dart/npt_flutter/lib/app.dart +++ b/packages/dart/npt_flutter/lib/app.dart @@ -33,6 +33,8 @@ class App extends StatelessWidget { ], child: MultiBlocProvider( providers: [ + // TODO this should be called LocalSettingsCubit and move + // Localization from the SettingsCubit to this BlocProvider( create: (_) => EnableLoggingCubit(), ), @@ -48,6 +50,11 @@ class App extends StatelessWidget { create: (_) => OnboardingCubit(), ), + // A bloc which manages the atDirectory state + BlocProvider( + create: (_) => AtDirectoryCubit(), + ), + /// Settings provider, not much else to say /// - If settings are not found, we automatically load some defaults /// so it is possible that someone's settings get wiped if there is @@ -89,13 +96,9 @@ class App extends StatelessWidget { BlocProvider( create: (ctx) => FavoriteBloc(ctx.read()), ), - - // A bloc which manages the atDirectory state - BlocProvider( - create: (_) => AtDirectoryCubit(), - ), ], - child: BlocSelector(selector: (state) { + child: BlocSelector( + selector: (state) { if (state is SettingsLoadedState) { return state.settings.language; } diff --git a/packages/dart/npt_flutter/lib/features/favorite/bloc/favorite_bloc.dart b/packages/dart/npt_flutter/lib/features/favorite/bloc/favorite_bloc.dart index 0401e3f9d..1f6bd41ce 100644 --- a/packages/dart/npt_flutter/lib/features/favorite/bloc/favorite_bloc.dart +++ b/packages/dart/npt_flutter/lib/features/favorite/bloc/favorite_bloc.dart @@ -15,6 +15,8 @@ class FavoriteBloc extends LoggingBloc { on(_onRemove); } + void clearAll() => emit(const FavoritesInitial()); + FutureOr _onLoad( FavoriteLoadEvent event, Emitter emit) async { emit(const FavoritesLoading()); diff --git a/packages/dart/npt_flutter/lib/features/onboarding/cubit/at_directory_cubit.dart b/packages/dart/npt_flutter/lib/features/onboarding/cubit/at_directory_cubit.dart index 7dcdad21b..6ffd96e92 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/cubit/at_directory_cubit.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/cubit/at_directory_cubit.dart @@ -1,7 +1,9 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:npt_flutter/features/logging/models/loggable.dart'; +import 'package:npt_flutter/features/logging/models/logging_bloc.dart'; -class AtDirectoryCubit extends Cubit { - AtDirectoryCubit() : super('root.atsign.org'); +class AtDirectoryCubit extends LoggingCubit { + AtDirectoryCubit() : super(const LoggableString('root.atsign.org')); - void setRootDomain(String rootDomain) => emit(rootDomain); + void setRootDomain(String rootDomain) => emit(LoggableString(rootDomain)); + String getRootDomain() => (state.string); } diff --git a/packages/dart/npt_flutter/lib/features/onboarding/util/pre_offboard.dart b/packages/dart/npt_flutter/lib/features/onboarding/util/pre_offboard.dart index 267285637..152bfa6ef 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/util/pre_offboard.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/util/pre_offboard.dart @@ -1,8 +1,20 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:npt_flutter/app.dart'; +import 'package:npt_flutter/features/features.dart'; + // Hand this method the atSign you wish to offboard // Returns: a boolean, true = success, false = failed Future preSignout(String atSign) async { // We need to do the following before "signing out" // - Wipe all application state - // - Remove the tray icon + App.navState.currentContext?.read().stopAllAndClear(); + App.navState.currentContext?.read().clear(); + App.navState.currentContext?.read().deselectAll(); + App.navState.currentContext?.read().clearAll(); + App.navState.currentContext?.read().clearAll(); + App.navState.currentContext?.read().clear(); + App.navState.currentContext?.read().offboard(); + // - Reset the tray icon + App.navState.currentContext?.read().initialize(); return true; } diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_at_directory_selector.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_at_directory_selector.dart index 08e0f3225..65864b456 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_at_directory_selector.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_at_directory_selector.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:npt_flutter/features/logging/models/loggable.dart'; import 'package:npt_flutter/features/onboarding/cubit/at_directory_cubit.dart'; typedef OnboardingMapCallback = void Function(Map val); @@ -17,8 +18,9 @@ class OnboardingAtDirectorySelector extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder(builder: (context, rootDomain) { - controller.text = rootDomain; + return BlocBuilder( + builder: (context, rootDomain) { + controller.text = rootDomain.string; return Column( children: [ Row( @@ -26,7 +28,9 @@ class OnboardingAtDirectorySelector extends StatelessWidget { children: [ Flexible( child: DropdownMenu( - initialSelection: options.contains(rootDomain) ? rootDomain : null, + initialSelection: options.contains(rootDomain.string) + ? rootDomain.string + : null, dropdownMenuEntries: options .map>( (o) => DropdownMenuEntry( @@ -56,7 +60,9 @@ class OnboardingAtDirectorySelector extends StatelessWidget { // validator: FormValidator.validateRequiredAtsignField, onChanged: (value) { // prevent the user from adding the default values to the dropdown a second time. - if (value != options[0] || value != options[1]) options.add(value); + if (value != options[0] || value != options[1]) { + options.add(value); + } //removes the third element making the final entry the only additional value in options. This prevents the dropdown from having more than 3 entries. if (options.length > 3) options.removeAt(2); diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_button.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_button.dart index 638eeba70..a44afba50 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_button.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_button.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:npt_flutter/constants.dart'; +import 'package:npt_flutter/features/logging/models/loggable.dart'; import 'package:npt_flutter/features/onboarding/cubit/at_directory_cubit.dart'; import 'package:npt_flutter/features/onboarding/onboarding.dart'; import 'package:npt_flutter/features/onboarding/widgets/at_directory_dialog.dart'; @@ -35,12 +36,13 @@ class _OnboardingButtonState extends State { @override Widget build(BuildContext context) { final strings = AppLocalizations.of(context)!; - return BlocBuilder(builder: (context, rootDomain) { + return BlocBuilder( + builder: (context, rootDomain) { return ElevatedButton.icon( onPressed: () async { final result = await selectOptions(); - if (result && context.mounted) onboard(rootDomain: context.read().state); + if (result && context.mounted) onboard(rootDomain: rootDomain.string); }, icon: PhosphorIcon(PhosphorIcons.arrowUpRight()), label: Text( @@ -51,7 +53,8 @@ class _OnboardingButtonState extends State { }); } - Future onboard({required String rootDomain, bool isFromInitState = false}) async { + Future onboard( + {required String rootDomain, bool isFromInitState = false}) async { AtOnboardingResult onboardingResult = await AtOnboarding.onboard( // ignore: use_build_context_synchronously context: context, @@ -68,7 +71,9 @@ class _OnboardingButtonState extends State { case AtOnboardingResultStatus.success: await initializeContactsService(rootDomain: rootDomain); postOnboard(onboardingResult.atsign!); - Navigator.of(context).pushReplacementNamed(Routes.dashboard); + if (mounted) { + Navigator.of(context).pushReplacementNamed(Routes.dashboard); + } break; case AtOnboardingResultStatus.error: if (isFromInitState) break; diff --git a/packages/dart/npt_flutter/lib/features/profile/bloc/profile_bloc.dart b/packages/dart/npt_flutter/lib/features/profile/bloc/profile_bloc.dart index 480a1e453..2b9c92885 100644 --- a/packages/dart/npt_flutter/lib/features/profile/bloc/profile_bloc.dart +++ b/packages/dart/npt_flutter/lib/features/profile/bloc/profile_bloc.dart @@ -24,7 +24,8 @@ class ProfileBloc extends LoggingBloc { on(_onStart); on(_onStop); } - Future _onLoad(ProfileLoadEvent event, Emitter emit) async { + Future _onLoad( + ProfileLoadEvent event, Emitter emit) async { emit(ProfileLoading(uuid)); Profile? profile; @@ -42,7 +43,8 @@ class ProfileBloc extends LoggingBloc { emit(ProfileLoaded(uuid, profile: profile)); } - Future _onLoadOrCreate(ProfileLoadOrCreateEvent event, Emitter emit) async { + Future _onLoadOrCreate( + ProfileLoadOrCreateEvent event, Emitter emit) async { emit(ProfileLoading(uuid)); Profile? profile; @@ -71,14 +73,16 @@ class ProfileBloc extends LoggingBloc { emit(ProfileLoaded(uuid, profile: profile)); } - Future _onEdit(ProfileEditEvent event, Emitter emit) async { + Future _onEdit( + ProfileEditEvent event, Emitter emit) async { if (state is! ProfileLoaded && state is! ProfileFailedSave) { return; } emit(ProfileLoaded(uuid, profile: event.profile)); } - FutureOr _onSave(ProfileSaveEvent event, Emitter emit) async { + FutureOr _onSave( + ProfileSaveEvent event, Emitter emit) async { emit(ProfileLoading(uuid)); bool res; try { @@ -88,7 +92,9 @@ class ProfileBloc extends LoggingBloc { } if (res) { - App.navState.currentContext?.read().invalidate(uuid); + App.navState.currentContext + ?.read() + .invalidate(uuid); var listBloc = App.navState.currentContext?.read(); if (listBloc != null && listBloc.state is ProfileListLoaded) { @@ -103,12 +109,15 @@ class ProfileBloc extends LoggingBloc { } emit(ProfileLoaded(uuid, profile: event.profile)); } else { - App.navState.currentContext?.read().invalidate(uuid); + App.navState.currentContext + ?.read() + .invalidate(uuid); emit(ProfileFailedSave(uuid, profile: event.profile)); } } - Future _onStart(ProfileStartEvent event, Emitter emit) async { + Future _onStart( + ProfileStartEvent event, Emitter emit) async { if (state is! ProfileLoadedState || state is ProfileStarting || state is ProfileStopping || @@ -125,18 +134,23 @@ class ProfileBloc extends LoggingBloc { String? atSign = atClient.getCurrentAtSign(); if (atSign == null) { emit(ProfileFailedStart(uuid, profile: profile)); - App.navState.currentContext?.read().invalidate(uuid); + App.navState.currentContext + ?.read() + .invalidate(uuid); return; } - SettingsState? currentSettingsState = App.navState.currentContext?.read().state; + SettingsState? currentSettingsState = + App.navState.currentContext?.read().state; if (currentSettingsState is! SettingsLoadedState) { emit(ProfileFailedStart( uuid, profile: profile, reason: "Couldn't fetch settings", )); - App.navState.currentContext?.read().invalidate(uuid); + App.navState.currentContext + ?.read() + .invalidate(uuid); return; } var settings = currentSettingsState.settings; @@ -181,7 +195,9 @@ class ProfileBloc extends LoggingBloc { profile: profile, reason: 'Npt startup timedout', )); - App.navState.currentContext?.read().invalidate(uuid); + App.navState.currentContext + ?.read() + .invalidate(uuid); return; } @@ -192,7 +208,9 @@ class ProfileBloc extends LoggingBloc { profile: profile, reason: 'Socketconnector closed prematurely', )); - App.navState.currentContext?.read().invalidate(uuid); + App.navState.currentContext + ?.read() + .invalidate(uuid); return; } @@ -206,16 +224,21 @@ class ProfileBloc extends LoggingBloc { profile: profile, reason: 'Error during startup: $err', )); - App.navState.currentContext?.read().invalidate(uuid); + App.navState.currentContext + ?.read() + .invalidate(uuid); } finally { await npt?.done; cancel?.call(); - App.navState.currentContext?.read().invalidate(uuid); + App.navState.currentContext + ?.read() + .invalidate(uuid); emit(ProfileLoaded(uuid, profile: profile)); } } - Future _onStop(ProfileStopEvent event, Emitter emit) async { + Future _onStop( + ProfileStopEvent event, Emitter emit) async { if (state is! ProfileStarted) return; var profile = (state as ProfileStarted).profile; emit(ProfileStopping(uuid, profile: profile)); diff --git a/packages/dart/npt_flutter/lib/features/profile/cubit/profile_cache_cubit.dart b/packages/dart/npt_flutter/lib/features/profile/cubit/profile_cache_cubit.dart index 3420ce22f..3f620453a 100644 --- a/packages/dart/npt_flutter/lib/features/profile/cubit/profile_cache_cubit.dart +++ b/packages/dart/npt_flutter/lib/features/profile/cubit/profile_cache_cubit.dart @@ -16,4 +16,6 @@ class ProfileCacheCubit extends LoggingCubit { emit(state.withAdded(uuid, bloc)); return bloc; } + + void clear() => emit(const ProfileCacheState({})); } diff --git a/packages/dart/npt_flutter/lib/features/profile_list/bloc/profile_list_bloc.dart b/packages/dart/npt_flutter/lib/features/profile_list/bloc/profile_list_bloc.dart index c3ea1178c..4cf5f4c19 100644 --- a/packages/dart/npt_flutter/lib/features/profile_list/bloc/profile_list_bloc.dart +++ b/packages/dart/npt_flutter/lib/features/profile_list/bloc/profile_list_bloc.dart @@ -17,7 +17,10 @@ class ProfileListBloc extends LoggingBloc { on(_onAdd); } - Future _onLoad(ProfileListLoadEvent event, Emitter emit) async { + void clearAll() => emit(const ProfileListInitial()); + + Future _onLoad( + ProfileListLoadEvent event, Emitter emit) async { emit(const ProfileListLoading()); Iterable? profiles; @@ -35,11 +38,13 @@ class ProfileListBloc extends LoggingBloc { emit(ProfileListLoaded(profiles: profiles)); } - Future _onUpdate(ProfileListUpdateEvent event, Emitter emit) async { + Future _onUpdate( + ProfileListUpdateEvent event, Emitter emit) async { emit(ProfileListLoaded(profiles: event.profiles)); } - Future _onDelete(ProfileListDeleteEvent event, Emitter emit) async { + Future _onDelete( + ProfileListDeleteEvent event, Emitter emit) async { // Don't allow deletes unless listed is loaded - this reduces the number of edge cases significantly if (state is! ProfileListLoaded) { return; @@ -64,7 +69,8 @@ class ProfileListBloc extends LoggingBloc { bloc?.add(FavoriteRemoveEvent(favoritesToRemove)); } - Future _onAdd(ProfileListAddEvent event, Emitter emit) async { + Future _onAdd( + ProfileListAddEvent event, Emitter emit) async { // Don't allow async bulk adds unless listed is loaded - this reduces the number of edge cases significantly if (state is! ProfileListLoaded) { return; diff --git a/packages/dart/npt_flutter/lib/features/profile_list/cubit/profiles_running_cubit.dart b/packages/dart/npt_flutter/lib/features/profile_list/cubit/profiles_running_cubit.dart index c4578049d..57b5c4da9 100644 --- a/packages/dart/npt_flutter/lib/features/profile_list/cubit/profiles_running_cubit.dart +++ b/packages/dart/npt_flutter/lib/features/profile_list/cubit/profiles_running_cubit.dart @@ -17,4 +17,11 @@ class ProfilesRunningCubit extends LoggingCubit { void invalidate(String uuid) { emit(state.withoutConnector(uuid)); } + + void stopAllAndClear() { + state.socketConnectors.forEach((_, socketConnector) { + socketConnector?.close(); + }); + emit(const ProfilesRunningState({})); + } } diff --git a/packages/dart/npt_flutter/lib/features/settings/bloc/settings_bloc.dart b/packages/dart/npt_flutter/lib/features/settings/bloc/settings_bloc.dart index 1a073303b..01c28217f 100644 --- a/packages/dart/npt_flutter/lib/features/settings/bloc/settings_bloc.dart +++ b/packages/dart/npt_flutter/lib/features/settings/bloc/settings_bloc.dart @@ -12,6 +12,8 @@ class SettingsBloc extends LoggingBloc { on(_onEdit); } + void clear() => emit(const SettingsInitial()); + Future _onLoad( SettingsLoadEvent event, Emitter emit) async { emit(const SettingsLoading()); diff --git a/packages/dart/npt_flutter/lib/features/settings/repository/contact_repository.dart b/packages/dart/npt_flutter/lib/features/settings/repository/contact_repository.dart index 2d5ca1066..bd2e6d291 100644 --- a/packages/dart/npt_flutter/lib/features/settings/repository/contact_repository.dart +++ b/packages/dart/npt_flutter/lib/features/settings/repository/contact_repository.dart @@ -20,7 +20,6 @@ class ContactsService { final AtSignLogger _logger = AtSignLogger(Constants.namespace!); AtClient? atClient; - AtClientService? atClientService; var atClientManager = AtClientManager.getInstance(); static var atContactService = ContactService(); @@ -31,20 +30,24 @@ class ContactsService { /// Fetch the current atsign profile image Future getCurrentAtsignProfileImage() async { - return atContactService.getContactDetails(atClientManager.atClient.getCurrentAtSign(), null).then((value) { + return atContactService + .getContactDetails(atClientManager.atClient.getCurrentAtSign(), null) + .then((value) { return value['image']; }); } /// Fetch details for the current atsign Future> getCurrentAtsignContactDetails() { - return atContactService.getContactDetails(atClientManager.atClient.getCurrentAtSign(), null); + return atContactService.getContactDetails( + atClientManager.atClient.getCurrentAtSign(), null); } /// Delete contact from contact list. Future addContact(String atSign, String? nickname) async { try { - bool isAdded = await atContactService.addAtSign(atSign: atSign, nickName: nickname); + bool isAdded = + await atContactService.addAtSign(atSign: atSign, nickName: nickname); return isAdded; } on AtClientException catch (atClientExcep) { diff --git a/packages/dart/npt_flutter/lib/features/tray_manager/cubit/tray_cubit.dart b/packages/dart/npt_flutter/lib/features/tray_manager/cubit/tray_cubit.dart index ef2f751c5..433b983b2 100644 --- a/packages/dart/npt_flutter/lib/features/tray_manager/cubit/tray_cubit.dart +++ b/packages/dart/npt_flutter/lib/features/tray_manager/cubit/tray_cubit.dart @@ -15,7 +15,8 @@ import 'package:window_manager/window_manager.dart'; part 'tray_cubit.g.dart'; part 'tray_state.dart'; -(String, void Function(MenuItem)) getAction(TrayAction action) => switch (action) { +(String, void Function(MenuItem)) getAction(TrayAction action) => + switch (action) { TrayAction.showDashboard => ('Show Window', (_) => windowManager.focus()), TrayAction.showSettings => ( 'Settings', @@ -85,10 +86,13 @@ class TrayCubit extends LoggingCubit { } Future reloadIcon() async { - final brightness = WidgetsBinding.instance.platformDispatcher.platformBrightness; + final brightness = + WidgetsBinding.instance.platformDispatcher.platformBrightness; await trayManager.setIcon(switch (brightness) { - Brightness.light => Platform.isWindows ? Constants.icoIconLight : Constants.pngIconLight, - Brightness.dark => Platform.isWindows ? Constants.icoIconDark : Constants.pngIconDark, + Brightness.light => + Platform.isWindows ? Constants.icoIconLight : Constants.pngIconLight, + Brightness.dark => + Platform.isWindows ? Constants.icoIconDark : Constants.pngIconDark, }); } @@ -114,7 +118,9 @@ class TrayCubit extends LoggingCubit { /// Generate the new menu based on current state var favMenuItems = await Future.wait( - favorites.where((fav) => fav.isLoadedInProfiles(profiles)).map((fav) async { + favorites + .where((fav) => fav.isLoadedInProfiles(profiles)) + .map((fav) async { /// Make sure to call [e.displayName] and [e.isRunning] only once to /// ensure good performance - these getters call a bunch of nested /// information from elsewhere in the app state diff --git a/packages/dart/npt_flutter/lib/styles/sizes.dart b/packages/dart/npt_flutter/lib/styles/sizes.dart index 3f4d15374..9ab9e4bb4 100644 --- a/packages/dart/npt_flutter/lib/styles/sizes.dart +++ b/packages/dart/npt_flutter/lib/styles/sizes.dart @@ -67,8 +67,8 @@ class Sizes { static const dashboardCardWidthFactor = 941 / 1053; static const profileFieldsWidthFactor = 150 / 1053; static const profileFieldsWidthFactorAlt = 300 / 1053; - static const SettingsCardWidthFactor = 654 / 1053; - static const SettingsCardHeightFactor = 438 / 691; + static const settingsCardWidthFactor = 654 / 1053; + static const settingsCardHeightFactor = 438 / 691; } const gap0 = SizedBox(); @@ -134,11 +134,14 @@ class SizeConfig { double textFactor = 1.0; - bool isMobile(BuildContext context) => MediaQuery.of(context).size.width < 700; + bool isMobile(BuildContext context) => + MediaQuery.of(context).size.width < 700; bool isTablet(BuildContext context) => - MediaQuery.of(context).size.width >= 700 && MediaQuery.of(context).size.width < 1200; - bool isDesktop(BuildContext context) => MediaQuery.of(context).size.width >= 1200; + MediaQuery.of(context).size.width >= 700 && + MediaQuery.of(context).size.width < 1200; + bool isDesktop(BuildContext context) => + MediaQuery.of(context).size.width >= 1200; void init() { _mediaQueryData = MediaQuery.of(App.navState.currentContext!); @@ -155,16 +158,20 @@ class SizeConfig { blockSizeHorizontal = screenWidth / 100; blockSizeVertical = screenHeight / 100; - _safeAreaHorizontal = _mediaQueryData.padding.left + _mediaQueryData.padding.right; - _safeAreaVertical = _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; + _safeAreaHorizontal = + _mediaQueryData.padding.left + _mediaQueryData.padding.right; + _safeAreaVertical = + _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; safeBlockHorizontal = (screenWidth - _safeAreaHorizontal) / 100; safeBlockVertical = (screenHeight - _safeAreaVertical) / 100; } else { blockSizeHorizontal = screenWidth / 120; blockSizeVertical = screenHeight / 120; - _safeAreaHorizontal = _mediaQueryData.padding.left + _mediaQueryData.padding.right; - _safeAreaVertical = _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; + _safeAreaHorizontal = + _mediaQueryData.padding.left + _mediaQueryData.padding.right; + _safeAreaVertical = + _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; safeBlockHorizontal = (screenWidth - _safeAreaHorizontal) / 120; safeBlockVertical = (screenHeight - _safeAreaVertical) / 120; } diff --git a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart index 1af50a9b2..6761fa15c 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:npt_flutter/constants.dart'; +import 'package:npt_flutter/features/logging/models/loggable.dart'; import 'package:npt_flutter/features/onboarding/cubit/at_directory_cubit.dart'; import 'package:npt_flutter/features/onboarding/widgets/onboarding_button.dart'; import 'package:npt_flutter/routes.dart'; @@ -93,13 +94,15 @@ class CustomTextButton extends StatelessWidget { } break; case CustomListTileType.discord: - final Uri url = Uri.parse('https://discord.gg/atsign-778383211214536722'); + final Uri url = + Uri.parse('https://discord.gg/atsign-778383211214536722'); if (!await launchUrl(url)) { throw Exception('Could not launch $url'); } break; case CustomListTileType.faq: - final Uri url = Uri.parse('https://docs.noports.com/ssh-no-ports/faq'); + final Uri url = + Uri.parse('https://docs.noports.com/ssh-no-ports/faq'); if (!await launchUrl(url)) { throw Exception('Could not launch $url'); } @@ -117,7 +120,8 @@ class CustomTextButton extends StatelessWidget { // break; case CustomListTileType.backupYourKey: if (context.mounted) { - BackupKeyWidget(atsign: ContactService().currentAtsign).showBackupDialog(context); + BackupKeyWidget(atsign: ContactService().currentAtsign) + .showBackupDialog(context); } break; case CustomListTileType.resetAtsign: @@ -133,7 +137,8 @@ class CustomTextButton extends StatelessWidget { appAPIKey: Constants.appAPIKey, ), ); - final OnboardingService onboardingService = OnboardingService.getInstance(); + final OnboardingService onboardingService = + OnboardingService.getInstance(); if (context.mounted && result == AtOnboardingResetResult.success) { onboardingService.setAtsign = null; @@ -176,13 +181,15 @@ class CustomTextButton extends StatelessWidget { } } - return BlocBuilder(builder: (context, rootDomain) { + return BlocBuilder( + builder: (context, rootDomain) { return Padding( - padding: const EdgeInsets.only(left: Sizes.p30, right: Sizes.p30, bottom: Sizes.p10), + padding: const EdgeInsets.only( + left: Sizes.p30, right: Sizes.p30, bottom: Sizes.p10), child: TextButton.icon( label: Text(getTitle(strings)), onPressed: () { - onTap(rootDomain); + onTap(rootDomain.string); }, icon: Icon( iconData, From a3712ca09fca1556858adee5894298f634aeffb0 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 7 Oct 2024 16:31:28 -0400 Subject: [PATCH 3/3] chore: add log message to preSignout since we bypass bloc events --- .../npt_flutter/lib/features/onboarding/util/pre_offboard.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dart/npt_flutter/lib/features/onboarding/util/pre_offboard.dart b/packages/dart/npt_flutter/lib/features/onboarding/util/pre_offboard.dart index 152bfa6ef..2705e5650 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/util/pre_offboard.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/util/pre_offboard.dart @@ -5,6 +5,7 @@ import 'package:npt_flutter/features/features.dart'; // Hand this method the atSign you wish to offboard // Returns: a boolean, true = success, false = failed Future preSignout(String atSign) async { + App.log("Resetting all application state before signout".loggable); // We need to do the following before "signing out" // - Wipe all application state App.navState.currentContext?.read().stopAllAndClear();