From 332e12eac256ef449e3adbe052e13f69ea8ab8c9 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Wed, 25 Sep 2024 15:32:31 -0400 Subject: [PATCH 01/60] fix: profile header aligned with profile list in the simple view. --- .../lib/features/profile/view/profile_header_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dart/npt_flutter/lib/features/profile/view/profile_header_view.dart b/packages/dart/npt_flutter/lib/features/profile/view/profile_header_view.dart index 7100478c2..b959a68e6 100644 --- a/packages/dart/npt_flutter/lib/features/profile/view/profile_header_view.dart +++ b/packages/dart/npt_flutter/lib/features/profile/view/profile_header_view.dart @@ -50,7 +50,7 @@ class ProfileHeaderView extends StatelessWidget { null => const Center(child: Spinner()), PreferredViewLayout.minimal => CustomCard.profileHeader( child: Padding( - padding: const EdgeInsets.all(Sizes.p10), + padding: const EdgeInsets.symmetric(vertical: Sizes.p10), child: Row( children: [ const ProfileSelectAllBox(), From a1ed8fb8a24faae29d3d1086e035c3cfe5bfec80 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Thu, 26 Sep 2024 07:57:42 -0400 Subject: [PATCH 02/60] fix: appBar alignment and padding updated. --- .../features/profile_list/cubit/profiles_running_cubit.dart | 2 -- packages/dart/npt_flutter/lib/styles/sizes.dart | 3 ++- packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart | 5 +++-- 3 files changed, 5 insertions(+), 5 deletions(-) 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 64bdb8290..c4578049d 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 @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:npt_flutter/app.dart'; import 'package:socket_connector/socket_connector.dart'; diff --git a/packages/dart/npt_flutter/lib/styles/sizes.dart b/packages/dart/npt_flutter/lib/styles/sizes.dart index 20c681dcc..78f5cf7af 100644 --- a/packages/dart/npt_flutter/lib/styles/sizes.dart +++ b/packages/dart/npt_flutter/lib/styles/sizes.dart @@ -35,6 +35,7 @@ class Sizes { static const p43 = 43.0; static const p50 = 50.0; static const p54 = 54.0; + static const p70 = 70.0; static const p80 = 80.0; // static const p99 = 99.0; @@ -94,7 +95,7 @@ const gapH30 = SizedBox(height: Sizes.p30); // const gapH36 = SizedBox(height: Sizes.p36); const gapH40 = SizedBox(height: Sizes.p40); // const gapH46 = SizedBox(height: Sizes.p46); -// const gapH60 = SizedBox(height: Sizes.p60); + const gapH108 = SizedBox(height: Sizes.p108); const kWindowsMinWindowSize = Size(684, 541); diff --git a/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart b/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart index 158fe05ef..06fcf74e2 100644 --- a/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart +++ b/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart @@ -27,13 +27,13 @@ class NptAppBar extends StatelessWidget implements PreferredSizeWidget { children: [ Column( children: [ - gapH40, + gapH16, SvgPicture.asset( 'assets/noports_logo.svg', height: Sizes.p54, width: Sizes.p175, ), - gapH25, + gapH16, TextButton.icon( onPressed: () { Navigator.pop(context); @@ -72,6 +72,7 @@ class NptAppBar extends StatelessWidget implements PreferredSizeWidget { ), actions: [ IconButton( + padding: const EdgeInsets.only(bottom: Sizes.p30), color: settingsSelectedColor, icon: const Icon(Icons.settings_outlined), onPressed: () { From 2b284282159fd620fce076a1e7cc43c4b41539bd Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Thu, 26 Sep 2024 08:02:41 -0400 Subject: [PATCH 03/60] fix: back button shown conditionally on appBar. --- .../npt_flutter/lib/widgets/npt_app_bar.dart | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart b/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart index 06fcf74e2..7bb797010 100644 --- a/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart +++ b/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart @@ -34,18 +34,20 @@ class NptAppBar extends StatelessWidget implements PreferredSizeWidget { width: Sizes.p175, ), gapH16, - TextButton.icon( - onPressed: () { - Navigator.pop(context); - }, - label: Text( - strings.back, - ), - icon: const Icon( - Icons.arrow_back_ios, - ), - style: StyleConstants.backButtonStyle, - ), + isNavigateBack + ? TextButton.icon( + onPressed: () { + Navigator.pop(context); + }, + label: Text( + strings.back, + ), + icon: const Icon( + Icons.arrow_back_ios, + ), + style: StyleConstants.backButtonStyle, + ) + : gap0, ], ), gapW27, From d790bd6246f1bfe734568d204e334ffb3c382524 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Thu, 26 Sep 2024 09:58:14 -0400 Subject: [PATCH 04/60] fix: profile, device name, service mapping and status updated to support scrolling. --- .../features/profile/view/profile_view.dart | 2 ++ .../profile/view/profile_view_minimal.dart | 1 - .../profile/view/profile_view_ssh_style.dart | 1 - .../profile/widgets/profile_device_name.dart | 19 +++++++----- .../profile/widgets/profile_display_name.dart | 25 +++++++++------- .../profile/widgets/profile_service_view.dart | 19 +++++++----- .../widgets/profile_status_indicator.dart | 30 ++++++++++--------- 7 files changed, 54 insertions(+), 43 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/profile/view/profile_view.dart b/packages/dart/npt_flutter/lib/features/profile/view/profile_view.dart index 9d8e829ca..16b47cda0 100644 --- a/packages/dart/npt_flutter/lib/features/profile/view/profile_view.dart +++ b/packages/dart/npt_flutter/lib/features/profile/view/profile_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:npt_flutter/features/profile/profile.dart'; import 'package:npt_flutter/features/settings/settings.dart'; +import 'package:npt_flutter/styles/sizes.dart'; import 'package:npt_flutter/widgets/loader_bar.dart'; import 'package:npt_flutter/widgets/spinner.dart'; @@ -21,6 +22,7 @@ class ProfileView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ LoaderBar(), + gapW10, ProfileRefreshButton(), ], ); diff --git a/packages/dart/npt_flutter/lib/features/profile/view/profile_view_minimal.dart b/packages/dart/npt_flutter/lib/features/profile/view/profile_view_minimal.dart index ec4c8eb73..adbb58171 100644 --- a/packages/dart/npt_flutter/lib/features/profile/view/profile_view_minimal.dart +++ b/packages/dart/npt_flutter/lib/features/profile/view/profile_view_minimal.dart @@ -14,7 +14,6 @@ class ProfileViewMinimal extends StatelessWidget { gapW10, ProfileStatusIndicator(), gapW10, - Spacer(), ProfileRunButton(), gapW10, ProfileFavoriteButton(), diff --git a/packages/dart/npt_flutter/lib/features/profile/view/profile_view_ssh_style.dart b/packages/dart/npt_flutter/lib/features/profile/view/profile_view_ssh_style.dart index a32bed57f..88689560f 100644 --- a/packages/dart/npt_flutter/lib/features/profile/view/profile_view_ssh_style.dart +++ b/packages/dart/npt_flutter/lib/features/profile/view/profile_view_ssh_style.dart @@ -18,7 +18,6 @@ class ProfileViewSshStyle extends StatelessWidget { gapW10, ProfileStatusIndicator(), gapW10, - Spacer(), ProfileRunButton(), gapW10, ProfileFavoriteButton(), diff --git a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_device_name.dart b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_device_name.dart index 30c1f3700..c92bb97b0 100644 --- a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_device_name.dart +++ b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_device_name.dart @@ -11,14 +11,17 @@ class ProfileDeviceName extends StatelessWidget { Widget build(BuildContext context) { return SizedBox( width: Sizes.p150, - child: BlocSelector(selector: (state) { - if (state is! ProfileLoadedState) return null; - return (state.profile.deviceName, state.profile.sshnpdAtsign); - }, builder: (BuildContext context, (String, String)? tuple) { - if (tuple == null) return gap0; - var (deviceName, sshnpdAtSign) = tuple; - return Text('$deviceName$sshnpdAtSign'); - }), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: BlocSelector(selector: (state) { + if (state is! ProfileLoadedState) return null; + return (state.profile.deviceName, state.profile.sshnpdAtsign); + }, builder: (BuildContext context, (String, String)? tuple) { + if (tuple == null) return gap0; + var (deviceName, sshnpdAtSign) = tuple; + return Text('$deviceName$sshnpdAtSign'); + }), + ), ); } } diff --git a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_display_name.dart b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_display_name.dart index a04292431..5dadf4a8b 100644 --- a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_display_name.dart +++ b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_display_name.dart @@ -11,17 +11,20 @@ class ProfileDisplayName extends StatelessWidget { Widget build(BuildContext context) { return SizedBox( width: Sizes.p150, - child: BlocSelector( - selector: (ProfileState state) { - if (state is ProfileLoadedState) { - return state.profile.displayName; - } - return null; - }, - builder: (BuildContext context, String? displayName) { - if (displayName == null) return gap0; - return Text(displayName); - }, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: BlocSelector( + selector: (ProfileState state) { + if (state is ProfileLoadedState) { + return state.profile.displayName; + } + return null; + }, + builder: (BuildContext context, String? displayName) { + if (displayName == null) return gap0; + return Text(displayName); + }, + ), ), ); } diff --git a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_service_view.dart b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_service_view.dart index 34f4f9803..6895b6113 100644 --- a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_service_view.dart +++ b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_service_view.dart @@ -11,14 +11,17 @@ class ProfileServiceView extends StatelessWidget { Widget build(BuildContext context) { return SizedBox( width: Sizes.p150, - child: BlocSelector(selector: (state) { - if (state is! ProfileLoadedState) return null; - return (state.profile.localPort, state.profile.remoteHost, state.profile.remotePort); - }, builder: (BuildContext context, (int, String, int)? triple) { - if (triple == null) return gap0; - var (localPort, remoteHost, remotePort) = triple; - return Text('$localPort:$remoteHost:$remotePort'); - }), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: BlocSelector(selector: (state) { + if (state is! ProfileLoadedState) return null; + return (state.profile.localPort, state.profile.remoteHost, state.profile.remotePort); + }, builder: (BuildContext context, (int, String, int)? triple) { + if (triple == null) return gap0; + var (localPort, remoteHost, remotePort) = triple; + return Text('$localPort:$remoteHost:$remotePort'); + }), + ), ); } } diff --git a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_status_indicator.dart b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_status_indicator.dart index 1e1085625..9bf2c41b7 100644 --- a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_status_indicator.dart +++ b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_status_indicator.dart @@ -8,23 +8,25 @@ class ProfileStatusIndicator extends StatelessWidget { @override Widget build(BuildContext context) { - return SizedBox( - width: Sizes.p150, - child: BlocBuilder(builder: (BuildContext context, ProfileState state) { - if (state is ProfileFailedSave) { - return const Tooltip(message: 'error saving profile', child: Text("Failed")); - } + return Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: BlocBuilder(builder: (BuildContext context, ProfileState state) { + if (state is ProfileFailedSave) { + return const Tooltip(message: 'error saving profile', child: Text("Failed")); + } - if (state is ProfileFailedStart) { - return Tooltip(message: state.reason ?? 'No Reason Provided', child: const Text("Failed")); - } + if (state is ProfileFailedStart) { + return Tooltip(message: state.reason ?? 'No Reason Provided', child: const Text("Failed")); + } - if (state is ProfileStarting && state.status != null) { - return Text(state.status!); - } + if (state is ProfileStarting && state.status != null) { + return Text(state.status!); + } - return gap0; - }), + return gap0; + }), + ), ); } } From 859b4712741253ef426b9a92bafa9d9463628213 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 30 Sep 2024 08:14:22 -0400 Subject: [PATCH 05/60] fix: dashboard page resizes based on user device size. --- .../profile/view/profile_header_view.dart | 10 ++++++---- .../profile/view/profile_view_minimal.dart | 3 ++- .../profile/widgets/profile_device_name.dart | 3 ++- .../profile/widgets/profile_display_name.dart | 10 ++++++++-- .../widgets/profile_header_column.dart | 19 +++++++++++++++++++ .../profile/widgets/profile_service_view.dart | 3 ++- .../profile_list/view/profile_list_view.dart | 10 +++++++++- .../settings/widgets/contact_list_tile.dart | 2 +- .../npt_flutter/lib/pages/dashboard_page.dart | 1 + .../dart/npt_flutter/lib/styles/sizes.dart | 10 ++++++++-- .../npt_flutter/lib/widgets/custom_card.dart | 6 +++--- 11 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 packages/dart/npt_flutter/lib/features/profile/widgets/profile_header_column.dart diff --git a/packages/dart/npt_flutter/lib/features/profile/view/profile_header_view.dart b/packages/dart/npt_flutter/lib/features/profile/view/profile_header_view.dart index b959a68e6..75efd1e6f 100644 --- a/packages/dart/npt_flutter/lib/features/profile/view/profile_header_view.dart +++ b/packages/dart/npt_flutter/lib/features/profile/view/profile_header_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:npt_flutter/features/profile/widgets/profile_header_column.dart'; import 'package:npt_flutter/features/profile_list/profile_list.dart'; import 'package:npt_flutter/features/settings/settings.dart'; import 'package:npt_flutter/styles/sizes.dart'; @@ -14,6 +15,7 @@ class ProfileHeaderView extends StatelessWidget { @override Widget build(BuildContext context) { final strings = AppLocalizations.of(context)!; + return BlocBuilder(builder: (context, state) { if (state is ProfileListInitial) { context.read().add(const ProfileListLoadEvent()); @@ -55,7 +57,7 @@ class ProfileHeaderView extends StatelessWidget { children: [ const ProfileSelectAllBox(), gapW10, - SizedBox(width: Sizes.p150, child: Text(strings.profileName)), + ProfileHeaderColumn(title: strings.profileName, layout: PreferredViewLayout.minimal), gapW10, Text(strings.status), // gapW10, @@ -74,11 +76,11 @@ class ProfileHeaderView extends StatelessWidget { children: [ const ProfileSelectAllBox(), gapW10, - SizedBox(width: Sizes.p150, child: Text(strings.profileName)), + ProfileHeaderColumn(title: strings.profileName), gapW10, - SizedBox(width: Sizes.p150, child: Text(strings.deviceName)), + ProfileHeaderColumn(title: strings.deviceName), gapW10, - SizedBox(width: Sizes.p150, child: Text(strings.serviceMapping)), + ProfileHeaderColumn(title: strings.serviceMapping), gapW10, Text(strings.status), // gapW10, diff --git a/packages/dart/npt_flutter/lib/features/profile/view/profile_view_minimal.dart b/packages/dart/npt_flutter/lib/features/profile/view/profile_view_minimal.dart index adbb58171..e147f682f 100644 --- a/packages/dart/npt_flutter/lib/features/profile/view/profile_view_minimal.dart +++ b/packages/dart/npt_flutter/lib/features/profile/view/profile_view_minimal.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:npt_flutter/features/profile/profile.dart'; +import 'package:npt_flutter/features/settings/models/settings.dart'; import 'package:npt_flutter/styles/sizes.dart'; class ProfileViewMinimal extends StatelessWidget { @@ -10,7 +11,7 @@ class ProfileViewMinimal extends StatelessWidget { return const Row(children: [ ProfileSelectBox(), gapW10, - ProfileDisplayName(), + ProfileDisplayName(layout: PreferredViewLayout.minimal), gapW10, ProfileStatusIndicator(), gapW10, diff --git a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_device_name.dart b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_device_name.dart index c92bb97b0..0d898bdcb 100644 --- a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_device_name.dart +++ b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_device_name.dart @@ -9,8 +9,9 @@ class ProfileDeviceName extends StatelessWidget { @override Widget build(BuildContext context) { + final deviceWidth = MediaQuery.of(context).size.width; return SizedBox( - width: Sizes.p150, + width: deviceWidth * Sizes.profileFieldsWidthFactor, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: BlocSelector(selector: (state) { diff --git a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_display_name.dart b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_display_name.dart index 5dadf4a8b..79c35a3ea 100644 --- a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_display_name.dart +++ b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_display_name.dart @@ -1,16 +1,22 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:npt_flutter/features/profile/profile.dart'; +import 'package:npt_flutter/features/settings/models/settings.dart'; import '../../../styles/sizes.dart'; class ProfileDisplayName extends StatelessWidget { - const ProfileDisplayName({super.key}); + const ProfileDisplayName({super.key, this.layout = PreferredViewLayout.sshStyle}); + + final PreferredViewLayout layout; @override Widget build(BuildContext context) { + final deviceWidth = MediaQuery.of(context).size.width; + final double widthFactor = + layout == PreferredViewLayout.sshStyle ? Sizes.profileFieldsWidthFactor : Sizes.profileFieldsWidthFactorAlt; return SizedBox( - width: Sizes.p150, + width: deviceWidth * widthFactor, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: BlocSelector( diff --git a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_header_column.dart b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_header_column.dart new file mode 100644 index 000000000..e00f1882b --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_header_column.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:npt_flutter/features/settings/models/settings.dart'; +import 'package:npt_flutter/styles/sizes.dart'; + +class ProfileHeaderColumn extends StatelessWidget { + const ProfileHeaderColumn({super.key, required this.title, this.layout = PreferredViewLayout.sshStyle}); + + final String title; + final PreferredViewLayout layout; + + @override + Widget build(BuildContext context) { + final deviceWidth = MediaQuery.of(context).size.width; + final double widthFactor = + layout == PreferredViewLayout.sshStyle ? Sizes.profileFieldsWidthFactor : Sizes.profileFieldsWidthFactorAlt; + + return SizedBox(width: deviceWidth * widthFactor, child: Text(title)); + } +} diff --git a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_service_view.dart b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_service_view.dart index 6895b6113..51da54c86 100644 --- a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_service_view.dart +++ b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_service_view.dart @@ -9,8 +9,9 @@ class ProfileServiceView extends StatelessWidget { @override Widget build(BuildContext context) { + final deviceWidth = MediaQuery.of(context).size.width; return SizedBox( - width: Sizes.p150, + width: deviceWidth * Sizes.profileFieldsWidthFactor, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: BlocSelector(selector: (state) { diff --git a/packages/dart/npt_flutter/lib/features/profile_list/view/profile_list_view.dart b/packages/dart/npt_flutter/lib/features/profile_list/view/profile_list_view.dart index bfe02ac40..6f42a1603 100644 --- a/packages/dart/npt_flutter/lib/features/profile_list/view/profile_list_view.dart +++ b/packages/dart/npt_flutter/lib/features/profile_list/view/profile_list_view.dart @@ -16,6 +16,9 @@ class ProfileListView extends StatelessWidget { @override Widget build(BuildContext context) { final strings = AppLocalizations.of(context)!; + final deviceSize = MediaQuery.of(context).size; + final bodyMedium = Theme.of(context).textTheme.labelSmall; + SizeConfig().init(); return BlocBuilder(builder: (context, state) { return switch (state) { ProfileListInitial() || ProfileListLoading() => const Center(child: Spinner()), @@ -52,6 +55,8 @@ class ProfileListView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ CustomCard.dashboardContent( + height: deviceSize.height * Sizes.dashboardCardHeightFactor, + width: deviceSize.width * Sizes.dashboardCardWidthFactor, child: Column( children: [ isFullProfile @@ -113,7 +118,10 @@ class ProfileListView extends StatelessWidget { ), ), gapH16, - Text(strings.allRightsReserved) + Text( + strings.allRightsReserved, + style: bodyMedium?.copyWith(fontSize: bodyMedium.fontSize?.toFont), + ), ], ), ), diff --git a/packages/dart/npt_flutter/lib/features/settings/widgets/contact_list_tile.dart b/packages/dart/npt_flutter/lib/features/settings/widgets/contact_list_tile.dart index 336eba6b2..10b0255a2 100644 --- a/packages/dart/npt_flutter/lib/features/settings/widgets/contact_list_tile.dart +++ b/packages/dart/npt_flutter/lib/features/settings/widgets/contact_list_tile.dart @@ -9,7 +9,7 @@ class ContactListTile extends StatelessWidget { @override Widget build(BuildContext context) { - SizeConfig().init(context); + SizeConfig().init(); final contactRepo = ContactsService.getInstance(); final bodyMedium = Theme.of(context).textTheme.bodyMedium!; diff --git a/packages/dart/npt_flutter/lib/pages/dashboard_page.dart b/packages/dart/npt_flutter/lib/pages/dashboard_page.dart index 8b6fe312f..86866072b 100644 --- a/packages/dart/npt_flutter/lib/pages/dashboard_page.dart +++ b/packages/dart/npt_flutter/lib/pages/dashboard_page.dart @@ -9,6 +9,7 @@ class DashboardPage extends StatelessWidget { @override Widget build(BuildContext context) { final strings = AppLocalizations.of(context)!; + return Scaffold( appBar: NptAppBar( title: strings.dashboard, diff --git a/packages/dart/npt_flutter/lib/styles/sizes.dart b/packages/dart/npt_flutter/lib/styles/sizes.dart index 78f5cf7af..098a19f2c 100644 --- a/packages/dart/npt_flutter/lib/styles/sizes.dart +++ b/packages/dart/npt_flutter/lib/styles/sizes.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:npt_flutter/app.dart'; /// Constant sizes to be used in the app (paddings, gaps, rounded corners etc.) class Sizes { @@ -61,6 +62,11 @@ class Sizes { static const p654 = 654.0; static const p664 = 664.0; static const p941 = 941.0; + // The below size factors are constants that are used to determine the height or width based on the device size. + static const dashboardCardHeightFactor = 489 / 691; + static const dashboardCardWidthFactor = 941 / 1053; + static const profileFieldsWidthFactor = 150 / 1053; + static const profileFieldsWidthFactorAlt = 300 / 1053; } const gap0 = SizedBox(); @@ -132,8 +138,8 @@ class SizeConfig { MediaQuery.of(context).size.width >= 700 && MediaQuery.of(context).size.width < 1200; bool isDesktop(BuildContext context) => MediaQuery.of(context).size.width >= 1200; - void init(BuildContext context) { - _mediaQueryData = MediaQuery.of(context); + void init() { + _mediaQueryData = MediaQuery.of(App.navState.currentContext!); screenWidth = _mediaQueryData.size.width; screenHeight = _mediaQueryData.size.height; refHeight = 505; diff --git a/packages/dart/npt_flutter/lib/widgets/custom_card.dart b/packages/dart/npt_flutter/lib/widgets/custom_card.dart index 7b741b596..bcace83f9 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_card.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_card.dart @@ -69,10 +69,10 @@ class CustomCard extends StatelessWidget { bottomBorderSide = BorderSide.none; const CustomCard.dashboardContent({ required this.child, + this.height = Sizes.p500, + this.width = Sizes.p941, super.key, - }) : height = Sizes.p500, - width = Sizes.p941, - color = AppColor.cardColorDark, + }) : color = AppColor.cardColorDark, radiusTopLeft = const Radius.circular(Sizes.p20), radiusTopRight = const Radius.circular(Sizes.p20), radiusBottomLeft = const Radius.circular(Sizes.p20), From a1299f59b7c4eaa5dd18dc750303ed513a1f1b03 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 30 Sep 2024 08:21:06 -0400 Subject: [PATCH 06/60] fix: auto spacing occurs between dashboard content and all rights reserved. --- .../lib/features/profile_list/view/profile_list_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dart/npt_flutter/lib/features/profile_list/view/profile_list_view.dart b/packages/dart/npt_flutter/lib/features/profile_list/view/profile_list_view.dart index 6f42a1603..6c689682a 100644 --- a/packages/dart/npt_flutter/lib/features/profile_list/view/profile_list_view.dart +++ b/packages/dart/npt_flutter/lib/features/profile_list/view/profile_list_view.dart @@ -53,6 +53,7 @@ class ProfileListView extends StatelessWidget { alignment: Alignment.topCenter, child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ CustomCard.dashboardContent( height: deviceSize.height * Sizes.dashboardCardHeightFactor, @@ -117,7 +118,6 @@ class ProfileListView extends StatelessWidget { ], ), ), - gapH16, Text( strings.allRightsReserved, style: bodyMedium?.copyWith(fontSize: bodyMedium.fontSize?.toFont), From 9adbb445e7210c38679e9ddd818b2d9400373708 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 30 Sep 2024 08:39:51 -0400 Subject: [PATCH 07/60] fix: profile form view sizes based on device size. --- .../lib/features/profile_form/view/profile_form_view.dart | 6 ++++-- packages/dart/npt_flutter/lib/widgets/custom_card.dart | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/profile_form/view/profile_form_view.dart b/packages/dart/npt_flutter/lib/features/profile_form/view/profile_form_view.dart index 7f7ed730f..7f1ede75c 100644 --- a/packages/dart/npt_flutter/lib/features/profile_form/view/profile_form_view.dart +++ b/packages/dart/npt_flutter/lib/features/profile_form/view/profile_form_view.dart @@ -14,21 +14,24 @@ class ProfileFormView extends StatelessWidget { Widget build(BuildContext context) { final strings = AppLocalizations.of(context)!; final GlobalKey formkey = GlobalKey(); + final deviceSize = MediaQuery.of(context).size; return BlocProvider( create: (BuildContext context) => /// Local copy of the profile which is used by the form ProfileBloc(context.read(), uuid)..add(const ProfileLoadOrCreateEvent()), child: Padding( - padding: const EdgeInsets.only(left: Sizes.p100, right: Sizes.p100, top: Sizes.p20), + padding: const EdgeInsets.only(left: Sizes.p100, right: Sizes.p100), child: Stack( children: [ Align( alignment: Alignment.topCenter, child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ CustomCard.profileFormContent( + height: deviceSize.height * Sizes.dashboardCardHeightFactor, child: SingleChildScrollView( child: Form( key: formkey, @@ -91,7 +94,6 @@ class ProfileFormView extends StatelessWidget { ), ), ), - gapH16, Text(strings.allRightsReserved), ], ), diff --git a/packages/dart/npt_flutter/lib/widgets/custom_card.dart b/packages/dart/npt_flutter/lib/widgets/custom_card.dart index bcace83f9..396cb931c 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_card.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_card.dart @@ -54,9 +54,9 @@ class CustomCard extends StatelessWidget { bottomBorderSide = BorderSide.none; const CustomCard.profileFormContent({ required this.child, + this.height = Sizes.p500, super.key, - }) : height = Sizes.p450, - width = null, + }) : width = null, color = AppColor.cardColorDark, radiusTopLeft = const Radius.circular(Sizes.p20), radiusTopRight = const Radius.circular(Sizes.p20), From 81f071749c51ccd7b3cc449989b4b36eaeb5c297 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Tue, 1 Oct 2024 07:25:44 -0400 Subject: [PATCH 08/60] fix: settings screen resizes based on device size. --- .../features/settings/view/settings_view.dart | 70 +++++++++---------- .../settings_dashboard_layout_selector.dart | 40 ++++++----- .../dart/npt_flutter/lib/styles/sizes.dart | 2 + .../npt_flutter/lib/widgets/custom_card.dart | 12 ++-- 4 files changed, 63 insertions(+), 61 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart b/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart index a43939245..84550e741 100644 --- a/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart +++ b/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart @@ -19,6 +19,7 @@ class SettingsView extends StatelessWidget { @override Widget build(BuildContext context) { final strings = AppLocalizations.of(context)!; + final deviceSize = MediaQuery.of(context).size; return BlocBuilder( builder: (context, state) { if (state is SettingsInitial) { @@ -29,14 +30,34 @@ class SettingsView extends StatelessWidget { case SettingsLoading(): return const Center(child: Spinner()); case SettingsLoadedState(): - return Padding( - padding: const EdgeInsets.only(top: 18, bottom: 92, left: 120, right: 77), - child: Stack( - clipBehavior: Clip.none, - children: [ - Positioned( - left: Sizes.p192, - child: CustomCard.settingsContent( + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomCard.settingsRail( + height: deviceSize.height * Sizes.SettingsCardHeightFactor, + child: const Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + // gapH30, + CustomTextButton.discord(), + CustomTextButton.email(), + CustomTextButton.faq(), + CustomTextButton.privacyPolicy(), + CustomTextButton.feedback(), + CustomTextButton.backUpYourKey(), + CustomTextButton.resetAtsign(), + ContactListTile(), + ], + ), + ), + CustomCard.settingsContent( + height: deviceSize.height * Sizes.SettingsCardHeightFactor, + width: deviceSize.width * Sizes.SettingsCardWidthFactor, child: Padding( padding: const EdgeInsets.only( left: Sizes.p43, @@ -55,35 +76,10 @@ class SettingsView extends StatelessWidget { ]), ), ), - ), - const Positioned( - left: 0, - child: CustomCard.settingsRail( - child: Padding( - padding: EdgeInsets.all(0.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - gapH30, - CustomTextButton.discord(), - CustomTextButton.email(), - CustomTextButton.faq(), - CustomTextButton.privacyPolicy(), - CustomTextButton.feedback(), - CustomTextButton.backUpYourKey(), - CustomTextButton.resetAtsign(), - ContactListTile(), - ], - ), - ), - ), - ), - Positioned( - top: Sizes.p470, - child: Text(strings.allRightsReserved), - ), - ], - ), + ], + ), + Text(strings.allRightsReserved) + ], ); } }, diff --git a/packages/dart/npt_flutter/lib/features/settings/widgets/settings_dashboard_layout_selector.dart b/packages/dart/npt_flutter/lib/features/settings/widgets/settings_dashboard_layout_selector.dart index d1b4680dd..0adc07e15 100644 --- a/packages/dart/npt_flutter/lib/features/settings/widgets/settings_dashboard_layout_selector.dart +++ b/packages/dart/npt_flutter/lib/features/settings/widgets/settings_dashboard_layout_selector.dart @@ -46,25 +46,29 @@ class SettingsDashboardLayoutSelector extends StatelessWidget { ], ), gapH18, - CustomCard.settingsPreview( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - gapH13, - Padding( - padding: const EdgeInsets.only(left: Sizes.p20), - child: Align( - alignment: Alignment.centerLeft, - child: Text(strings.preview), + SizedBox( + height: 295, + width: 537, + child: CustomCard.settingsPreview( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + gapH13, + Padding( + padding: const EdgeInsets.only(left: Sizes.p20), + child: Align( + alignment: Alignment.centerLeft, + child: Text(strings.preview), + ), ), - ), - gapH10, - viewLayout == PreferredViewLayout.minimal - ? SvgPicture.asset('assets/simple.svg') - : SvgPicture.asset('assets/advance.svg'), - gapH16, - ], - )) + gapH10, + viewLayout == PreferredViewLayout.minimal + ? SvgPicture.asset('assets/simple.svg') + : SvgPicture.asset('assets/advance.svg'), + gapH16, + ], + )), + ) ], ); }); diff --git a/packages/dart/npt_flutter/lib/styles/sizes.dart b/packages/dart/npt_flutter/lib/styles/sizes.dart index 098a19f2c..1fbab9989 100644 --- a/packages/dart/npt_flutter/lib/styles/sizes.dart +++ b/packages/dart/npt_flutter/lib/styles/sizes.dart @@ -67,6 +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; } const gap0 = SizedBox(); diff --git a/packages/dart/npt_flutter/lib/widgets/custom_card.dart b/packages/dart/npt_flutter/lib/widgets/custom_card.dart index 396cb931c..8c5dd2a01 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_card.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_card.dart @@ -23,10 +23,10 @@ class CustomCard extends StatelessWidget { const CustomCard.settingsRail({ required this.child, + this.height = Sizes.p436, + this.width = Sizes.p202, super.key, - }) : height = Sizes.p436, - width = Sizes.p202, - color = Colors.white, + }) : color = Colors.white, radiusTopLeft = const Radius.circular(Sizes.p10), radiusTopRight = const Radius.circular(Sizes.p10), radiusBottomLeft = const Radius.circular(Sizes.p10), @@ -39,10 +39,10 @@ class CustomCard extends StatelessWidget { const CustomCard.settingsContent({ required this.child, + this.height = Sizes.p470, + this.width = Sizes.p664, super.key, - }) : height = Sizes.p436, - width = Sizes.p664, - color = AppColor.cardColorDark, + }) : color = AppColor.cardColorDark, radiusTopLeft = Radius.zero, radiusTopRight = const Radius.circular(Sizes.p20), radiusBottomLeft = Radius.zero, From 02dafb90cd5ccea98b755351c91789c1816fa530 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Fri, 4 Oct 2024 20:22:07 -0400 Subject: [PATCH 09/60] feat: onboarding flow added --- .../dart/npt_flutter/assets/onboarding_bg.svg | 114 ++++++++++++++++++ packages/dart/npt_flutter/lib/app.dart | 6 + .../onboarding/cubit/at_directory_cubit.dart | 7 ++ .../onboarding/view/onboaring_view.dart | 52 ++++++++ .../widgets/at_directory_dialog.dart | 61 ++++++++++ .../onboarding_at_directory_selector.dart | 74 ++++++++++++ .../onboarding/widgets/onboarding_button.dart | 64 ++++++---- .../npt_flutter/lib/localization/app_en.arb | 7 ++ .../lib/pages/onboarding_page.dart | 13 +- .../npt_flutter/lib/styles/app_theme.dart | 8 ++ .../dart/npt_flutter/lib/styles/sizes.dart | 2 +- .../lib/widgets/custom_text_button.dart | 35 ++++-- .../npt_flutter/lib/widgets/npt_app_bar.dart | 27 +++-- 13 files changed, 422 insertions(+), 48 deletions(-) create mode 100644 packages/dart/npt_flutter/assets/onboarding_bg.svg create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/cubit/at_directory_cubit.dart create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_at_directory_selector.dart diff --git a/packages/dart/npt_flutter/assets/onboarding_bg.svg b/packages/dart/npt_flutter/assets/onboarding_bg.svg new file mode 100644 index 000000000..c925206fa --- /dev/null +++ b/packages/dart/npt_flutter/assets/onboarding_bg.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/dart/npt_flutter/lib/app.dart b/packages/dart/npt_flutter/lib/app.dart index 72ccacfd4..1522a593f 100644 --- a/packages/dart/npt_flutter/lib/app.dart +++ b/packages/dart/npt_flutter/lib/app.dart @@ -2,6 +2,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/features/features.dart'; +import 'package:npt_flutter/features/onboarding/cubit/at_directory_cubit.dart'; import 'package:npt_flutter/routes.dart'; import 'package:npt_flutter/styles/app_theme.dart'; @@ -88,6 +89,11 @@ class App extends StatelessWidget { BlocProvider( create: (ctx) => FavoriteBloc(ctx.read()), ), + + // A bloc which manages the atDirectory state + BlocProvider( + create: (_) => AtDirectoryCubit(), + ), ], child: BlocSelector(selector: (state) { if (state is SettingsLoadedState) { 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 new file mode 100644 index 000000000..7dcdad21b --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/cubit/at_directory_cubit.dart @@ -0,0 +1,7 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + +class AtDirectoryCubit extends Cubit { + AtDirectoryCubit() : super('root.atsign.org'); + + void setRootDomain(String rootDomain) => emit(rootDomain); +} diff --git a/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart b/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart new file mode 100644 index 000000000..f7fc2090a --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:npt_flutter/features/onboarding/widgets/onboarding_button.dart'; +import 'package:npt_flutter/styles/sizes.dart'; +import 'package:npt_flutter/widgets/custom_text_button.dart'; + +class OnboardingView extends StatelessWidget { + const OnboardingView({super.key}); + + @override + Widget build(BuildContext context) { + final strings = AppLocalizations.of(context)!; + final textTheme = Theme.of(context).textTheme; + return Stack( + children: [ + Positioned.fill( + child: SvgPicture.asset( + 'assets/onboarding_bg.svg', + fit: BoxFit.cover, + ), + ), + Align( + child: Column( + children: [ + gapH108, + Text( + strings.onboardingTitle, + style: textTheme.headlineLarge!.copyWith( + color: Colors.black, + ), + ), + Text(strings.onboardingSubTitle, style: textTheme.headlineMedium), + gapH20, + const OnboardingButton(), + ], + ), + ), + const Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only( + bottom: Sizes.p44, + right: Sizes.p44, + ), + child: CustomTextButton.resetAtsign(), + ), + ) + ], + ); + } +} diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart new file mode 100644 index 000000000..5c4be911f --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:npt_flutter/features/onboarding/widgets/onboarding_at_directory_selector.dart'; +import 'package:npt_flutter/styles/sizes.dart'; +import 'package:npt_flutter/widgets/custom_container.dart'; + +class AtDirectoryDialog extends StatefulWidget { + const AtDirectoryDialog({super.key}); + + @override + State createState() => _AtDirectoryDialogState(); +} + +class _AtDirectoryDialogState extends State { + @override + Widget build(BuildContext context) { + final strings = AppLocalizations.of(context)!; + return AlertDialog( + backgroundColor: Colors.white, + content: Padding( + padding: const EdgeInsets.symmetric(vertical: Sizes.p12, horizontal: Sizes.p16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CustomContainer.background( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(strings.atDirectory), + Text(strings.atDirectorySubtitle), + gapH16, + OnboardingAtDirectorySelector(), + ], + ), + ), + gapH10, + CustomContainer.background( + child: Row( + children: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(strings.cancel), + ), + const Spacer(), + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(true); + }, + child: Text(strings.onboard), + ), + ], + )) + ], + ), + ), + ); + } +} 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 new file mode 100644 index 000000000..08e0f3225 --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_at_directory_selector.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:npt_flutter/features/onboarding/cubit/at_directory_cubit.dart'; + +typedef OnboardingMapCallback = void Function(Map val); + +class OnboardingAtDirectorySelector extends StatelessWidget { + OnboardingAtDirectorySelector({ + super.key, + }); + + final options = ['root.atsign.org', 'vip.ve.atsign.zone']; + + final FocusNode focusNode = FocusNode(); + final TextEditingController controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + return BlocBuilder(builder: (context, rootDomain) { + controller.text = rootDomain; + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: DropdownMenu( + initialSelection: options.contains(rootDomain) ? rootDomain : null, + dropdownMenuEntries: options + .map>( + (o) => DropdownMenuEntry( + value: o, + label: o, + ), + ) + .toList(), + onSelected: (value) { + if (value == null) return; + + context.read().setRootDomain(value); + }, + ), + ), + Flexible( + child: KeyboardListener( + focusNode: focusNode, + onKeyEvent: (value) { + if (value.logicalKey == LogicalKeyboardKey.backspace) { + if (options.length > 2) options.removeLast(); + } + }, + child: TextFormField( + controller: controller, + autovalidateMode: AutovalidateMode.onUserInteraction, + // 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); + //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); + + context.read().setRootDomain(value); + }, + ), + ), + ) + ], + ), + ], + ); + }); + } +} 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 53d269738..638eeba70 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 @@ -1,15 +1,21 @@ import 'package:at_contacts_flutter/at_contacts_flutter.dart'; import 'package:at_onboarding_flutter/at_onboarding_flutter.dart'; 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/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'; +import 'package:npt_flutter/routes.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:phosphor_flutter/phosphor_flutter.dart'; -Future loadAtClientPreference() async { +Future loadAtClientPreference(String rootDomain) async { var dir = await getApplicationSupportDirectory(); return AtClientPreference() - ..rootDomain = Constants.rootDomain + ..rootDomain = rootDomain ..namespace = Constants.namespace ..hiveStoragePath = dir.path ..commitLogPath = dir.path @@ -17,38 +23,42 @@ Future loadAtClientPreference() async { } class OnboardingButton extends StatefulWidget { - const OnboardingButton({super.key, required this.nextRoute}); - final String nextRoute; + const OnboardingButton({ + super.key, + }); @override State createState() => _OnboardingButtonState(); } class _OnboardingButtonState extends State { - final Future futurePreference = loadAtClientPreference(); - - @override - void initState() { - super.initState(); - onboard(isFromInitState: true); - } - @override Widget build(BuildContext context) { - return ElevatedButton( - onPressed: onboard, - child: const Text('Login'), - ); + final strings = AppLocalizations.of(context)!; + return BlocBuilder(builder: (context, rootDomain) { + return ElevatedButton.icon( + onPressed: () async { + final result = await selectOptions(); + + if (result && context.mounted) onboard(rootDomain: context.read().state); + }, + icon: PhosphorIcon(PhosphorIcons.arrowUpRight()), + label: Text( + strings.getStarted, + ), + iconAlignment: IconAlignment.end, + ); + }); } - Future onboard({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, config: AtOnboardingConfig( - atClientPreference: await futurePreference, + atClientPreference: await loadAtClientPreference(rootDomain), rootEnvironment: RootEnvironment.Testing, - domain: Constants.rootDomain, + domain: rootDomain, appAPIKey: Constants.appAPIKey, ), ); @@ -56,16 +66,16 @@ class _OnboardingButtonState extends State { if (mounted) { switch (onboardingResult.status) { case AtOnboardingResultStatus.success: - await initializeContactsService(rootDomain: Constants.rootDomain); + await initializeContactsService(rootDomain: rootDomain); postOnboard(onboardingResult.atsign!); - Navigator.of(context).pushReplacementNamed(widget.nextRoute); + Navigator.of(context).pushReplacementNamed(Routes.dashboard); break; case AtOnboardingResultStatus.error: if (isFromInitState) break; ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( + SnackBar( backgroundColor: Colors.red, - content: Text('An error has occurred'), + content: Text(AppLocalizations.of(context)!.onboardingError), ), ); break; @@ -74,4 +84,12 @@ class _OnboardingButtonState extends State { } } } + + Future selectOptions() async { + final results = await showDialog( + context: context, + builder: (BuildContext context) => const AtDirectoryDialog(), + ); + return results ?? false; + } } diff --git a/packages/dart/npt_flutter/lib/localization/app_en.arb b/packages/dart/npt_flutter/lib/localization/app_en.arb index 1bb1406b5..ca172fb49 100644 --- a/packages/dart/npt_flutter/lib/localization/app_en.arb +++ b/packages/dart/npt_flutter/lib/localization/app_en.arb @@ -68,5 +68,12 @@ "validationErrorRemoteHostField" : "Field must be partially or fully qualified hostname or an IP address", "profileRunningActionDeniedMessage" : "Cannot perform this action while profile is running", "emptyProfileMessage" : "No profiles found\nCreate or Import a profile to start using NoPorts.", + "onboardingTitle" : "Welcome", + "onboardingSubTitle" : "to NoPorts Desktop", + "getStarted" : "Get Started", + "atDirectory" : "AtDirectory", + "atDirectorySubtitle" : "Select the domain you want to use", + "onboard" : "Onboard", + "onboardingError" : "An error has occurred", "yaml" : "YAML" } \ No newline at end of file diff --git a/packages/dart/npt_flutter/lib/pages/onboarding_page.dart b/packages/dart/npt_flutter/lib/pages/onboarding_page.dart index 0e24abfde..8a52b2d28 100644 --- a/packages/dart/npt_flutter/lib/pages/onboarding_page.dart +++ b/packages/dart/npt_flutter/lib/pages/onboarding_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:npt_flutter/features/onboarding/onboarding.dart'; +import 'package:npt_flutter/features/onboarding/view/onboaring_view.dart'; +import 'package:npt_flutter/widgets/npt_app_bar.dart'; class OnboardingPage extends StatelessWidget { const OnboardingPage({super.key, required this.nextRoute}); @@ -7,6 +8,14 @@ class OnboardingPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold(body: OnboardingButton(nextRoute: nextRoute)); + return const Scaffold( + extendBodyBehindAppBar: true, + extendBody: true, + appBar: NptAppBar( + isNavigateBack: false, + showSettings: false, + ), + body: OnboardingView(), + ); } } diff --git a/packages/dart/npt_flutter/lib/styles/app_theme.dart b/packages/dart/npt_flutter/lib/styles/app_theme.dart index 01d8210eb..65d0c7a2d 100644 --- a/packages/dart/npt_flutter/lib/styles/app_theme.dart +++ b/packages/dart/npt_flutter/lib/styles/app_theme.dart @@ -4,6 +4,14 @@ import 'package:npt_flutter/styles/sizes.dart'; class AppTheme { static TextTheme lightTextTheme = const TextTheme( + headlineLarge: TextStyle( + fontSize: Sizes.p32, + fontWeight: FontWeight.w600, + ), + headlineMedium: TextStyle( + fontSize: Sizes.p24, + fontWeight: FontWeight.w500, + ), titleMedium: TextStyle( fontSize: Sizes.p18, fontWeight: FontWeight.w600, diff --git a/packages/dart/npt_flutter/lib/styles/sizes.dart b/packages/dart/npt_flutter/lib/styles/sizes.dart index 1fbab9989..3f4d15374 100644 --- a/packages/dart/npt_flutter/lib/styles/sizes.dart +++ b/packages/dart/npt_flutter/lib/styles/sizes.dart @@ -19,7 +19,7 @@ class Sizes { static const p20 = 20.0; // static const p21 = 21.0; // static const p28 = 28.0; - // static const p24 = 24.0; + static const p24 = 24.0; static const p25 = 25.0; static const p27 = 27.0; static const p28 = 28.0; 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 454f4bfb7..1af50a9b2 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -1,13 +1,17 @@ +import 'dart:developer'; + import 'package:at_contacts_flutter/services/contact_service.dart'; import 'package:at_onboarding_flutter/at_onboarding_flutter.dart'; import 'package:at_onboarding_flutter/services/onboarding_service.dart'; 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/onboarding/cubit/at_directory_cubit.dart'; +import 'package:npt_flutter/features/onboarding/widgets/onboarding_button.dart'; import 'package:npt_flutter/routes.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../features/onboarding/onboarding.dart'; import '../styles/sizes.dart'; import 'custom_snack_bar.dart'; @@ -77,7 +81,7 @@ class CustomTextButton extends StatelessWidget { // final bodyMedium = Theme.of(context).textTheme.bodyMedium!; // final bodySmall = Theme.of(context).textTheme.bodySmall!; final strings = AppLocalizations.of(context)!; - Future onTap() async { + Future onTap(String rootDomain) async { switch (type) { case CustomListTileType.email: Uri emailUri = Uri( @@ -117,14 +121,15 @@ class CustomTextButton extends StatelessWidget { } break; case CustomListTileType.resetAtsign: - final futurePreference = await loadAtClientPreference(); + log(rootDomain); + final futurePreference = await loadAtClientPreference(rootDomain); if (context.mounted) { final result = await AtOnboarding.reset( context: context, config: AtOnboardingConfig( atClientPreference: futurePreference, rootEnvironment: RootEnvironment.Testing, - domain: Constants.rootDomain, + domain: rootDomain, appAPIKey: Constants.appAPIKey, ), ); @@ -171,16 +176,20 @@ class CustomTextButton extends StatelessWidget { } } - return Padding( - padding: const EdgeInsets.only(left: Sizes.p30, right: Sizes.p30, bottom: Sizes.p10), - child: TextButton.icon( - label: Text(getTitle(strings)), - onPressed: onTap, - icon: Icon( - iconData, + return BlocBuilder(builder: (context, rootDomain) { + return Padding( + padding: const EdgeInsets.only(left: Sizes.p30, right: Sizes.p30, bottom: Sizes.p10), + child: TextButton.icon( + label: Text(getTitle(strings)), + onPressed: () { + onTap(rootDomain); + }, + icon: Icon( + iconData, + ), ), - ), - ); + ); + }); } } diff --git a/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart b/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart index 7bb797010..8f9e9382e 100644 --- a/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart +++ b/packages/dart/npt_flutter/lib/widgets/npt_app_bar.dart @@ -10,8 +10,15 @@ class NptAppBar extends StatelessWidget implements PreferredSizeWidget { final String title; final Color? settingsSelectedColor; final bool isNavigateBack; + final bool showSettings; - const NptAppBar({super.key, required this.title, this.settingsSelectedColor, this.isNavigateBack = true}); + const NptAppBar({ + super.key, + this.title = '', + this.settingsSelectedColor, + this.isNavigateBack = true, + this.showSettings = true, + }); @override Size get preferredSize => Size.fromHeight(isNavigateBack ? Sizes.p150 : Sizes.p100); @@ -73,14 +80,16 @@ class NptAppBar extends StatelessWidget implements PreferredSizeWidget { ], ), actions: [ - IconButton( - padding: const EdgeInsets.only(bottom: Sizes.p30), - color: settingsSelectedColor, - icon: const Icon(Icons.settings_outlined), - onPressed: () { - Navigator.pushNamed(context, '/settings'); - }, - ), + showSettings + ? IconButton( + padding: const EdgeInsets.only(bottom: Sizes.p30), + color: settingsSelectedColor, + icon: const Icon(Icons.settings_outlined), + onPressed: () { + Navigator.pushNamed(context, '/settings'); + }, + ) + : gap0, ], centerTitle: true, ); From a5c7abe792257f03c4704368863e0c0cf3754176 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 7 Oct 2024 09:43:37 -0400 Subject: [PATCH 10/60] fix: localization added for relay selection. --- .../features/settings/models/settings.dart | 34 +++++++++++++++++++ .../widgets/settings_relay_quick_buttons.dart | 9 +++-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/settings/models/settings.dart b/packages/dart/npt_flutter/lib/features/settings/models/settings.dart index f96e374b4..46b35105c 100644 --- a/packages/dart/npt_flutter/lib/features/settings/models/settings.dart +++ b/packages/dart/npt_flutter/lib/features/settings/models/settings.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:npt_flutter/app.dart'; @@ -107,4 +108,37 @@ extension LanguageExtension on Language { } } +enum RelayOptions { + am, + eu, + ap, +} + +extension RelayOptionsExtension on RelayOptions { + String get relayAtsign { + switch (this) { + case RelayOptions.am: + return '@rv_am'; + case RelayOptions.eu: + return '@rv_eu'; + case RelayOptions.ap: + return '@rv_ap'; + } + } + + String get regions { + final strings = AppLocalizations.of(App.navState.currentContext!)!; + switch (this) { + case RelayOptions.am: + return strings.americas; + case RelayOptions.eu: + return strings.europe; + case RelayOptions.ap: + return strings.asiaPacific; + } + } +} + + + // ['English', 'Spanish', 'Br portuguese', 'Mandarin', 'Cantonese'] \ No newline at end of file diff --git a/packages/dart/npt_flutter/lib/features/settings/widgets/settings_relay_quick_buttons.dart b/packages/dart/npt_flutter/lib/features/settings/widgets/settings_relay_quick_buttons.dart index 7c2b57bab..cd6228752 100644 --- a/packages/dart/npt_flutter/lib/features/settings/widgets/settings_relay_quick_buttons.dart +++ b/packages/dart/npt_flutter/lib/features/settings/widgets/settings_relay_quick_buttons.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:npt_flutter/constants.dart'; import 'package:npt_flutter/features/settings/settings.dart'; import 'package:npt_flutter/widgets/custom_container.dart'; @@ -29,16 +28,16 @@ class SettingsRelayQuickButtons extends StatelessWidget { controller: controller, scrollDirection: Axis.horizontal, children: [ - ...Constants.defaultRelayOptions.entries.map( + ...RelayOptions.values.map( (e) => Padding( padding: const EdgeInsets.only(right: Sizes.p10), child: CustomContainer.foreground( - key: Key(e.key), + key: Key(e.name), child: SizedBox( width: Sizes.p180, child: RadioListTile( - title: Text(e.value), - value: e.key, + title: Text(e.regions), + value: e.relayAtsign, groupValue: relayAtsign, onChanged: (value) { var bloc = context.read(); From 59a41d31d9eeb1de70e400a5f317438d7c623322 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 7 Oct 2024 11:07:22 -0400 Subject: [PATCH 11/60] localization added for strings missing localization. --- .../profile/widgets/profile_status_indicator.dart | 6 ++++-- .../features/settings/widgets/contact_list_tile.dart | 10 ++++++---- packages/dart/npt_flutter/lib/localization/app_en.arb | 6 ++++++ .../npt_flutter/lib/widgets/custom_text_button.dart | 6 +++--- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_status_indicator.dart b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_status_indicator.dart index 9bf2c41b7..17aacdced 100644 --- a/packages/dart/npt_flutter/lib/features/profile/widgets/profile_status_indicator.dart +++ b/packages/dart/npt_flutter/lib/features/profile/widgets/profile_status_indicator.dart @@ -1,5 +1,6 @@ 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/features/profile/profile.dart'; import 'package:npt_flutter/styles/sizes.dart'; @@ -8,16 +9,17 @@ class ProfileStatusIndicator extends StatelessWidget { @override Widget build(BuildContext context) { + final strings = AppLocalizations.of(context)!; return Expanded( child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: BlocBuilder(builder: (BuildContext context, ProfileState state) { if (state is ProfileFailedSave) { - return const Tooltip(message: 'error saving profile', child: Text("Failed")); + return Tooltip(message: strings.profileFailedSaveMessage, child: Text(strings.failed)); } if (state is ProfileFailedStart) { - return Tooltip(message: state.reason ?? 'No Reason Provided', child: const Text("Failed")); + return Tooltip(message: state.reason ?? strings.profileFailedUnknownMessage, child: Text(strings.failed)); } if (state is ProfileStarting && state.status != null) { diff --git a/packages/dart/npt_flutter/lib/features/settings/widgets/contact_list_tile.dart b/packages/dart/npt_flutter/lib/features/settings/widgets/contact_list_tile.dart index 10b0255a2..297d4e079 100644 --- a/packages/dart/npt_flutter/lib/features/settings/widgets/contact_list_tile.dart +++ b/packages/dart/npt_flutter/lib/features/settings/widgets/contact_list_tile.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import '../../../styles/app_color.dart'; import '../../../styles/sizes.dart'; @@ -11,6 +12,7 @@ class ContactListTile extends StatelessWidget { Widget build(BuildContext context) { SizeConfig().init(); final contactRepo = ContactsService.getInstance(); + final strings = AppLocalizations.of(context)!; final bodyMedium = Theme.of(context).textTheme.bodyMedium!; final bodySmall = Theme.of(context).textTheme.bodySmall!; @@ -45,12 +47,12 @@ class ContactListTile extends StatelessWidget { ), ); } else { - return const ListTile( - leading: CircleAvatar( + return ListTile( + leading: const CircleAvatar( child: Icon(Icons.person), ), - title: Text('No Name'), - subtitle: Text('No Atsign'), + title: Text(strings.noName), + subtitle: Text(strings.noAtsign), ); } })); diff --git a/packages/dart/npt_flutter/lib/localization/app_en.arb b/packages/dart/npt_flutter/lib/localization/app_en.arb index ca172fb49..8438f323c 100644 --- a/packages/dart/npt_flutter/lib/localization/app_en.arb +++ b/packages/dart/npt_flutter/lib/localization/app_en.arb @@ -75,5 +75,11 @@ "atDirectorySubtitle" : "Select the domain you want to use", "onboard" : "Onboard", "onboardingError" : "An error has occurred", + "failed": "Failed", + "profileFailedSaveMessage": "Profile failed to save", + "profileFailedUnknownMessage" : "No reason provided", + "noName" : "No Name", + "noAtsign" : "No Atsign", + "noEmailClientAvailable" : "No email client available", "yaml" : "YAML" } \ No newline at end of file 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..b63ee2f8b 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -89,7 +89,7 @@ class CustomTextButton extends StatelessWidget { path: 'info@noports.com', ); if (!await launchUrl(emailUri)) { - CustomSnackBar.notification(content: 'No email client available'); + CustomSnackBar.notification(content: strings.noEmailClientAvailable); } break; case CustomListTileType.discord: @@ -146,11 +146,11 @@ class CustomTextButton extends StatelessWidget { final emailUri = Uri( scheme: 'mailto', path: 'info@noports.com', - query: 'subject=SSH No Ports Desktop Feedback', + query: 'subject=No Port Desktop Feedback', ); if (!await launchUrl(emailUri)) { - CustomSnackBar.notification(content: 'No email client available'); + CustomSnackBar.notification(content: strings.noEmailClientAvailable); } } } From 17d129faa52716659066661c3057a9355d7aed83 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 7 Oct 2024 11:21:09 -0400 Subject: [PATCH 12/60] language support for spanish and portuguese added for recently added strings. --- .../npt_flutter/lib/localization/app_en.arb | 36 ++++----- .../npt_flutter/lib/localization/app_es.arb | 81 ++++++++++++------- .../npt_flutter/lib/localization/app_pt.arb | 57 ++++++++----- 3 files changed, 108 insertions(+), 66 deletions(-) diff --git a/packages/dart/npt_flutter/lib/localization/app_en.arb b/packages/dart/npt_flutter/lib/localization/app_en.arb index 8438f323c..ffc5e5aed 100644 --- a/packages/dart/npt_flutter/lib/localization/app_en.arb +++ b/packages/dart/npt_flutter/lib/localization/app_en.arb @@ -6,6 +6,8 @@ "allRightsReserved":"@ 2024 Atsign, All Rights Reserved", "americas" : "Americas", "asiaPacific" : "Asia-Pacific", + "atDirectory" : "AtDirectory", + "atDirectorySubtitle" : "Select the domain you want to use", "back" : "Back", "backupYourKey" : "Backup Your Key", "cancel" : "Cancel", @@ -21,12 +23,15 @@ "discord" : "Discord", "edit" : "Edit", "email" : "Email", + "emptyProfileMessage" : "No profiles found\nCreate or Import a profile to start using NoPorts.", "enableLogging" : "Enable Logging", "europe" : "Europe", "export" : "Export", "exportLogs" : "Export Logs", + "failed": "Failed", "faq" : "faq", "feedback" : "Feedback", + "getStarted" : "Get Started", "import" : "Import", "json" : "JSON", "language" : "Language", @@ -34,7 +39,14 @@ "localPortDescription" : "", "logs" : "Logs", "minimal" : "Simple", + "noAtsign" : "No Atsign", + "noEmailClientAvailable" : "No email client available", + "noName" : "No Name", "noPorts" : "NoPorts", + "onboard" : "Onboard", + "onboardingError" : "An error has occurred", + "onboardingSubTitle" : "to NoPorts Desktop", + "onboardingTitle" : "Welcome", "overrideAllProfile":"Override all profiles with default relay selection", "preview" : "Preview", "privacyPolicy" : "Privacy Policy", @@ -43,8 +55,11 @@ "profileExportDialogTitle" : "Choose Filetype", "profileExportMessage" : "What filetype would you like to export as?", "profileExportSelectedMessage" : "What filetype would you like to export selected profiles as?", + "profileFailedSaveMessage": "Profile failed to save", + "profileFailedUnknownMessage" : "No reason provided", "profileName" : "Profile Name", "profileNameDescription" : "This will be the name of you configurations", + "profileRunningActionDeniedMessage" : "Cannot perform this action while profile is running", "refresh" : "Refresh", "relay" : "Relay", "relayDescription" : "You can pick from our existing RV's or create a new one", @@ -59,27 +74,12 @@ "sshStyle" : "Advanced", "status" : "Status", "submit" : "Submit", - "validationErrorEmptyField" : "Field cannot be left Blank", "validationErrorAtsignField" : "Field must be a valid atsign that starts with @", "validationErrorDeviceNameField" : "Field can only contain lowercase letters, digits, underscores.", - "validationErrorLongField" : "Field must be 1-36 characters long", + "validationErrorEmptyField" : "Field cannot be left Blank", "validationErrorLocalPortField" : "Field must be between 1024-65535", - "validationErrorRemotePortField" : "Field must be between 1-65535", + "validationErrorLongField" : "Field must be 1-36 characters long", "validationErrorRemoteHostField" : "Field must be partially or fully qualified hostname or an IP address", - "profileRunningActionDeniedMessage" : "Cannot perform this action while profile is running", - "emptyProfileMessage" : "No profiles found\nCreate or Import a profile to start using NoPorts.", - "onboardingTitle" : "Welcome", - "onboardingSubTitle" : "to NoPorts Desktop", - "getStarted" : "Get Started", - "atDirectory" : "AtDirectory", - "atDirectorySubtitle" : "Select the domain you want to use", - "onboard" : "Onboard", - "onboardingError" : "An error has occurred", - "failed": "Failed", - "profileFailedSaveMessage": "Profile failed to save", - "profileFailedUnknownMessage" : "No reason provided", - "noName" : "No Name", - "noAtsign" : "No Atsign", - "noEmailClientAvailable" : "No email client available", + "validationErrorRemotePortField" : "Field must be between 1-65535", "yaml" : "YAML" } \ No newline at end of file diff --git a/packages/dart/npt_flutter/lib/localization/app_es.arb b/packages/dart/npt_flutter/lib/localization/app_es.arb index 1105f96b2..f8696dbe2 100644 --- a/packages/dart/npt_flutter/lib/localization/app_es.arb +++ b/packages/dart/npt_flutter/lib/localization/app_es.arb @@ -1,64 +1,85 @@ { - "addNew" : "Agregar Nuevo", - "addNewProfile" : "Agregar Nuevo Perfil", + "addNew" : "Agregar nuevo", + "addNewProfile" : "Agregar nuevo perfil", "advanced": "Avanzado", "alertDialogTitle": "¿Estás seguro?", "allRightsReserved":"@ 2024 Atsign, Todos los derechos reservados", "americas" : "Américas", "asiaPacific" : "Asia-Pacífico", + "atDirectory" : "AtDirectory", + "atDirectorySubtitle" : "Seleccione el dominio que desea usar", "back" : "Atrás", - "backupYourKey" : "Respaldar tu Clave", + "backupYourKey" : "Respalda tu clave", "cancel" : "Cancelar", "confirm" : "Confirmar", "dashboard" : "Tablero", - "dashboardView" : "Vista del Tablero", - "defaultRelaySelection" : "Selección de Relevo Predeterminado", + "dashboardView" : "Vista del tablero", + "defaultRelaySelection" : "Selección de relé predeterminada", "delete" : "Eliminar", - "deviceAtsign" : "Atsign del Dispositivo", + "deviceAtsign" : "Dispositivo atSign", "deviceAtsignDescription" : "Este es el atSign asociado con tu dispositivo", - "deviceName": "Nombre del Dispositivo", + "deviceName": "Nombre del dispositivo", "deviceNameDescription" : "Este es el nombre de tu dispositivo remoto", "discord" : "Discord", "edit" : "Editar", - "email" : "Correo Electrónico", - "enableLogging" : "Habilitar Registro", + "email" : "Correo electrónico", + "emptyProfileMessage" : "No se encontraron perfiles\nCree o importe un perfil para comenzar a usar NoPorts.", + "enableLogging" : "Habilitar registro", "europe" : "Europa", "export" : "Exportar", - "exportLogs" : "Exportar Registros", - "faq" : "Preguntas Frecuentes", - "feedback" : "Comentarios", + "exportLogs" : "Exportar registros", + "failed": "Fallido", + "faq" : "Preguntas frecuentes", + "feedback" : "Retroalimentación", + "getStarted" : "Comenzar", "import" : "Importar", - "invalidRelayAtsignMsg" : "Por favor ingresa un atsign válido", "json" : "JSON", "language" : "Idioma", - "localPort" : "Puerto Local", + "localPort" : "Puerto local", "localPortDescription" : "", "logs" : "Registros", "minimal" : "Simple", - "noPorts" : "Sin Puertos", - "overrideAllProfile":"Anular todos los perfiles con la selección de relevo predeterminada", - "preview" : "Vista Previa", - "privacyPolicy" : "Política de Privacidad", - "profileDeleteMessage" : "Este perfil será eliminado permanentemente.", - "profileDeleteSelectedMessage" : "Los perfiles seleccionados serán eliminados permanentemente.", - "profileExportDialogTitle" : "Elegir Tipo de Archivo", - "profileExportMessage" : "¿En qué tipo de archivo te gustaría exportar?", - "profileExportSelectedMessage" : "¿En qué tipo de archivo te gustaría exportar los perfiles seleccionados?", - "profileName" : "Nombre del Perfil", + "noAtsign" : "Sin atSign", + "noEmailClientAvailable" : "No hay cliente de correo electrónico disponible", + "noName" : "Sin nombre", + "noPorts" : "NoPorts", + "onboard" : "A bordo", + "onboardingError" : "Ha ocurrido un error", + "onboardingSubTitle" : "a NoPorts Desktop", + "onboardingTitle" : "Bienvenido", + "overrideAllProfile":"Sobrescribir todos los perfiles con la selección de relé predeterminada", + "preview" : "Vista previa", + "privacyPolicy" : "Política de privacidad", + "profileDeleteMessage" : "Este perfil se eliminará permanentemente.", + "profileDeleteSelectedMessage" : "Los perfiles seleccionados se eliminarán permanentemente.", + "profileExportDialogTitle" : "Elegir tipo de archivo", + "profileExportMessage" : "¿Qué tipo de archivo te gustaría exportar?", + "profileExportSelectedMessage" : "¿Qué tipo de archivo te gustaría exportar los perfiles seleccionados?", + "profileFailedSaveMessage": "El perfil no se pudo guardar", + "profileFailedUnknownMessage" : "No se proporcionó ninguna razón", + "profileName" : "Nombre del perfil", "profileNameDescription" : "Este será el nombre de tus configuraciones", + "profileRunningActionDeniedMessage" : "No se puede realizar esta acción mientras el perfil está en ejecución", "refresh" : "Actualizar", - "relay" : "Relevo", + "relay" : "Relé", "relayDescription" : "Puedes elegir entre nuestros RV existentes o crear uno nuevo", - "remoteHost" : "Host Remoto", + "remoteHost" : "Host remoto", "remoteHostDescription" : "", - "remotePort" : "Puerto Remoto", + "remotePort" : "Puerto remoto", "remotePortDescription" : "", - "resetAtsign" : "Restablecer Atsign", - "selectExportFile": "Por favor selecciona un archivo para exportar:", - "serviceMapping" : "Mapeo de Servicios", + "resetAtsign" : "Restablecer atSign", + "selectExportFile": "Por favor selecciona un archivo para exportar a:", + "serviceMapping" : "Mapeo de servicios", "settings" : "Configuraciones", "sshStyle" : "Avanzado", "status" : "Estado", "submit" : "Enviar", + "validationErrorAtsignField" : "El campo debe ser un atSign válido que comience con @", + "validationErrorDeviceNameField" : "El campo solo puede contener letras minúsculas, dígitos, guiones bajos.", + "validationErrorEmptyField" : "El campo no puede estar vacío", + "validationErrorLocalPortField" : "El campo debe estar entre 1024-65535", + "validationErrorLongField" : "El campo debe tener entre 1-36 caracteres", + "validationErrorRemoteHostField" : "El campo debe ser un nombre de host parcial o completamente calificado o una dirección IP", + "validationErrorRemotePortField" : "El campo debe estar entre 1-65535", "yaml" : "YAML" } \ No newline at end of file diff --git a/packages/dart/npt_flutter/lib/localization/app_pt.arb b/packages/dart/npt_flutter/lib/localization/app_pt.arb index 443446bc7..156ea401c 100644 --- a/packages/dart/npt_flutter/lib/localization/app_pt.arb +++ b/packages/dart/npt_flutter/lib/localization/app_pt.arb @@ -2,50 +2,64 @@ "addNew" : "Adicionar Novo", "addNewProfile" : "Adicionar Novo Perfil", "advanced": "Avançado", - "alertDialogTitle": "Tem certeza?", - "allRightsReserved":"@ 2024 Atsign, Todos os direitos reservados", + "alertDialogTitle": "Você tem certeza?", + "allRightsReserved":"@ 2024 Atsign, Todos os Direitos Reservados", "americas" : "Américas", "asiaPacific" : "Ásia-Pacífico", + "atDirectory" : "AtDirectory", + "atDirectorySubtitle" : "Selecione o domínio que você deseja usar", "back" : "Voltar", - "backupYourKey" : "Fazer Backup da sua Chave", + "backupYourKey" : "Faça Backup da Sua Chave", "cancel" : "Cancelar", "confirm" : "Confirmar", - "dashboard" : "Painel de Controle", - "dashboardView" : "Visualização do Painel de Controle", - "defaultRelaySelection" : "Seleção Padrão de Relay", + "dashboard" : "Painel", + "dashboardView" : "Visualização do Painel", + "defaultRelaySelection" : "Seleção de Relay Padrão", "delete" : "Excluir", - "deviceAtsign" : "AtSign do Dispositivo", - "deviceAtsignDescription" : "Este é o AtSign associado ao seu dispositivo", + "deviceAtsign" : "Atsign do Dispositivo", + "deviceAtsignDescription" : "Este é o atSign associado ao seu dispositivo", "deviceName": "Nome do Dispositivo", "deviceNameDescription" : "Este é o nome do seu dispositivo remoto", "discord" : "Discord", "edit" : "Editar", "email" : "Email", - "enableLogging" : "Ativar Registro de Logs", + "emptyProfileMessage" : "Nenhum perfil encontrado\nCrie ou Importe um perfil para começar a usar NoPorts.", + "enableLogging" : "Habilitar Registro", "europe" : "Europa", "export" : "Exportar", - "exportLogs" : "Exportar Logs", - "faq" : "Perguntas Frequentes", + "exportLogs" : "Exportar Registros", + "failed": "Falhou", + "faq" : "faq", "feedback" : "Feedback", + "getStarted" : "Começar", "import" : "Importar", - "invalidRelayAtsignMsg" : "Por favor, insira um AtSign válido", "json" : "JSON", "language" : "Idioma", "localPort" : "Porta Local", "localPortDescription" : "", - "logs" : "Logs", + "logs" : "Registros", "minimal" : "Simples", - "noPorts" : "Sem Portas", - "overrideAllProfile":"Substituir todos os perfis pela seleção padrão de relay", - "preview" : "Visualizar", + "noAtsign" : "Sem Atsign", + "noEmailClientAvailable" : "Nenhum cliente de email disponível", + "noName" : "Sem Nome", + "noPorts" : "NoPorts", + "onboard" : "Onboard", + "onboardingError" : "Ocorreu um erro", + "onboardingSubTitle" : "para NoPorts Desktop", + "onboardingTitle" : "Bem-vindo", + "overrideAllProfile":"Substituir todos os perfis com a seleção de relay padrão", + "preview" : "Pré-visualização", "privacyPolicy" : "Política de Privacidade", "profileDeleteMessage" : "Este perfil será excluído permanentemente.", "profileDeleteSelectedMessage" : "Os perfis selecionados serão excluídos permanentemente.", - "profileExportDialogTitle" : "Escolher Tipo de Arquivo", + "profileExportDialogTitle" : "Escolha o Tipo de Arquivo", "profileExportMessage" : "Qual tipo de arquivo você gostaria de exportar?", "profileExportSelectedMessage" : "Qual tipo de arquivo você gostaria de exportar os perfis selecionados?", + "profileFailedSaveMessage": "Falha ao salvar o perfil", + "profileFailedUnknownMessage" : "Nenhuma razão fornecida", "profileName" : "Nome do Perfil", "profileNameDescription" : "Este será o nome das suas configurações", + "profileRunningActionDeniedMessage" : "Não é possível realizar esta ação enquanto o perfil está em execução", "refresh" : "Atualizar", "relay" : "Relay", "relayDescription" : "Você pode escolher entre nossos RVs existentes ou criar um novo", @@ -53,12 +67,19 @@ "remoteHostDescription" : "", "remotePort" : "Porta Remota", "remotePortDescription" : "", - "resetAtsign" : "Redefinir AtSign", + "resetAtsign" : "Redefinir Atsign", "selectExportFile": "Por favor, selecione um arquivo para exportar:", "serviceMapping" : "Mapeamento de Serviço", "settings" : "Configurações", "sshStyle" : "Avançado", "status" : "Status", "submit" : "Enviar", + "validationErrorAtsignField" : "O campo deve ser um atsign válido que começa com @", + "validationErrorDeviceNameField" : "O campo só pode conter letras minúsculas, dígitos, sublinhados.", + "validationErrorEmptyField" : "O campo não pode ficar em branco", + "validationErrorLocalPortField" : "O campo deve estar entre 1024-65535", + "validationErrorLongField" : "O campo deve ter entre 1-36 caracteres", + "validationErrorRemoteHostField" : "O campo deve ser um nome de host parcial ou totalmente qualificado ou um endereço IP", + "validationErrorRemotePortField" : "O campo deve estar entre 1-65535", "yaml" : "YAML" } \ No newline at end of file From dc0440d346eccc5a0dc51f0d998f19499bc22263 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 7 Oct 2024 11:42:22 -0400 Subject: [PATCH 13/60] localization added for profile relay atsign selection. --- packages/dart/npt_flutter/lib/constants.dart | 6 ------ .../widgets/profile_relay_quick_buttons.dart | 11 +++++------ .../settings/repository/settings_repository.dart | 11 ++++------- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/packages/dart/npt_flutter/lib/constants.dart b/packages/dart/npt_flutter/lib/constants.dart index 58c1eb465..82fda7132 100644 --- a/packages/dart/npt_flutter/lib/constants.dart +++ b/packages/dart/npt_flutter/lib/constants.dart @@ -9,11 +9,5 @@ class Constants { static const pngIconLight = 'assets/noports-icon64-light.png'; static const icoIconLight = 'assets/noports-icon64-light.ico'; - static const Map defaultRelayOptions = { - "@rv_am": "Los Angeles", - "@rv_eu": "London", - "@rv_ap": "Singapore", - }; - static const languages = ['English', 'Spanish', 'Br portuguese', 'Mandarin', 'Cantonese']; } diff --git a/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart b/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart index 2e5ffcfdf..d9cfa9dbf 100644 --- a/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart +++ b/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart @@ -1,8 +1,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/profile/profile.dart'; +import 'package:npt_flutter/features/features.dart'; import 'package:npt_flutter/features/profile_form/widgets/profile_relay_at_sign_text_field.dart'; import 'package:npt_flutter/styles/sizes.dart'; import 'package:npt_flutter/widgets/custom_container.dart'; @@ -44,17 +43,17 @@ class ProfileRelayQuickButtons extends StatelessWidget { scrollDirection: Axis.horizontal, controller: controller, children: [ - ...Constants.defaultRelayOptions.entries.map( + ...RelayOptions.values.map( (e) => Padding( padding: const EdgeInsets.only(right: Sizes.p10), child: CustomContainer.foreground( - key: Key(e.key), + key: Key(e.name), child: SizedBox( width: Sizes.p200, height: Sizes.p50, child: RadioListTile( - title: Text(e.value), - value: e.key, + title: Text(e.regions), + value: e.relayAtsign, groupValue: relayAtsign, onChanged: (value) { var bloc = context.read(); diff --git a/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart b/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart index 9c6adfd1a..0adea4c71 100644 --- a/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart +++ b/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart @@ -6,11 +6,10 @@ import 'package:npt_flutter/features/settings/settings.dart'; class SettingsRepository { const SettingsRepository(); - AtKey get settingsAtKey => - AtKey.self('settings', namespace: Constants.namespace).build(); + AtKey get settingsAtKey => AtKey.self('settings', namespace: Constants.namespace).build(); Settings get defaultSettings => Settings( - relayAtsign: Constants.defaultRelayOptions.values.first, + relayAtsign: RelayOptions.am.relayAtsign, viewLayout: PreferredViewLayout.minimal, overrideRelay: false, ); @@ -18,8 +17,7 @@ class SettingsRepository { Future getSettings() async { AtClient atClient = AtClientManager.getInstance().atClient; try { - var value = await atClient - .get(settingsAtKey..sharedBy = atClient.getCurrentAtSign()); + var value = await atClient.get(settingsAtKey..sharedBy = atClient.getCurrentAtSign()); if (value.value == null) { // No settings saved, so use the defaults return defaultSettings; @@ -43,8 +41,7 @@ class SettingsRepository { Future deleteSettings(Settings settings) async { AtClient atClient = AtClientManager.getInstance().atClient; try { - return await atClient - .delete(settingsAtKey..sharedBy = atClient.getCurrentAtSign()); + return await atClient.delete(settingsAtKey..sharedBy = atClient.getCurrentAtSign()); } catch (_) { return false; } From 84b7568af9b6acdca6d03e3d31886ba0b2a3c75f Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 7 Oct 2024 11:52:04 -0400 Subject: [PATCH 14/60] fix: override relay switch widget is horizontally scrollable. --- .../settings_override_relay_switch.dart | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/settings/widgets/settings_override_relay_switch.dart b/packages/dart/npt_flutter/lib/features/settings/widgets/settings_override_relay_switch.dart index 76e81c637..7298249e7 100644 --- a/packages/dart/npt_flutter/lib/features/settings/widgets/settings_override_relay_switch.dart +++ b/packages/dart/npt_flutter/lib/features/settings/widgets/settings_override_relay_switch.dart @@ -19,22 +19,25 @@ class SettingsOverrideRelaySwitch extends StatelessWidget { return null; }, builder: (context, overrideRelay) { if (overrideRelay == null) return const Center(child: Spinner()); - return Row( - children: [ - Checkbox( - value: overrideRelay, - onChanged: (value) { - var bloc = context.read(); - bloc.add(SettingsEditEvent( - settings: (bloc.state as SettingsLoadedState).settings.copyWith(overrideRelay: value), - save: true, - )); - }, - ), - Text( - strings.overrideAllProfile, - ), - ], + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + Checkbox( + value: overrideRelay, + onChanged: (value) { + var bloc = context.read(); + bloc.add(SettingsEditEvent( + settings: (bloc.state as SettingsLoadedState).settings.copyWith(overrideRelay: value), + save: true, + )); + }, + ), + Text( + strings.overrideAllProfile, + ), + ], + ), ); }); } From 0af206d23a7dc5a457f5fae8b0b97967a798fe6b Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Thu, 26 Sep 2024 07:57:42 -0400 Subject: [PATCH 15/60] fix: appBar alignment and padding updated. --- packages/dart/npt_flutter/pubspec.lock | 50 +++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/dart/npt_flutter/pubspec.lock b/packages/dart/npt_flutter/pubspec.lock index 3cc2e0107..f5c168297 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: "69585d39813bf09faa94edad15b3de5a38cce750" 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: From 46a99f6a01316bc533e0d38e0e00d005cdf5bd6a Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Fri, 4 Oct 2024 20:22:07 -0400 Subject: [PATCH 16/60] feat: onboarding flow added --- packages/dart/npt_flutter/lib/localization/app_en.arb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/dart/npt_flutter/lib/localization/app_en.arb b/packages/dart/npt_flutter/lib/localization/app_en.arb index ffc5e5aed..e64e0614d 100644 --- a/packages/dart/npt_flutter/lib/localization/app_en.arb +++ b/packages/dart/npt_flutter/lib/localization/app_en.arb @@ -80,6 +80,14 @@ "validationErrorLocalPortField" : "Field must be between 1024-65535", "validationErrorLongField" : "Field must be 1-36 characters long", "validationErrorRemoteHostField" : "Field must be partially or fully qualified hostname or an IP address", - "validationErrorRemotePortField" : "Field must be between 1-65535", + "profileRunningActionDeniedMessage" : "Cannot perform this action while profile is running", + "emptyProfileMessage" : "No profiles found\nCreate or Import a profile to start using NoPorts.", + "onboardingTitle" : "Welcome", + "onboardingSubTitle" : "to NoPorts Desktop", + "getStarted" : "Get Started", + "atDirectory" : "AtDirectory", + "atDirectorySubtitle" : "Select the domain you want to use", + "onboard" : "Onboard", + "onboardingError" : "An error has occurred", "yaml" : "YAML" } \ No newline at end of file From e1f733fcf35d9c983c7bd7a5fa360d7af76028c1 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 7 Oct 2024 12:22:23 -0400 Subject: [PATCH 17/60] feat: add interface for new atSign management utilities --- .../onboarding/util/atsign_manager.dart | 30 +++++++++++++++++++ .../onboarding/util/pre_offboard.dart | 8 +++++ 2 files changed, 38 insertions(+) create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/util/atsign_manager.dart create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/util/pre_offboard.dart 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 new file mode 100644 index 000000000..73fc2a1a0 --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/util/atsign_manager.dart @@ -0,0 +1,30 @@ +abstract class AtsignInformation { + String get atSign; + String get rootDomain; +} + +// This will return a map which looks like: +// +// { +// "@alice": AtsignInformation{ atSign: "@alice", rootDomain: "root.atsign.org" }, +// "@bob": AtsignInformation{ atSign: "@alice", rootDomain: "vip.ve.atsign.zone" }, +// } +// +// Note: AtsignInformation is a class, so usage will look like +// +// var atSign = "@alice"; +// var atSignInfo = await getAtsignEntries(); +// var rootDomain = atSignInfo[atSign].rootDomain; +// +// Now you have the rootDomain for the existing atSign and can use it to onboard +// correctly + +Future> getAtsignEntries() { + return Future.value({}); +} + +// 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); +} 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 new file mode 100644 index 000000000..267285637 --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/util/pre_offboard.dart @@ -0,0 +1,8 @@ +// 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 + return true; +} From da1a0e5aa00e79403cc84e3e5cbda58305d8fd4e Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 7 Oct 2024 15:20:37 -0400 Subject: [PATCH 18/60] feat: add atsign manager utility functions --- .../onboarding/util/atsign_manager.dart | 146 +++++++++++++++++- packages/dart/npt_flutter/pubspec.lock | 8 +- packages/dart/npt_flutter/pubspec.yaml | 1 + 3 files changed, 144 insertions(+), 11 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 f5c168297..8a461d80d 100644 --- a/packages/dart/npt_flutter/pubspec.lock +++ b/packages/dart/npt_flutter/pubspec.lock @@ -172,7 +172,7 @@ packages: description: path: "packages/at_onboarding_flutter" ref: trunk - resolved-ref: "69585d39813bf09faa94edad15b3de5a38cce750" + resolved-ref: "2a32ac2461673e0df16f5de2e24305309a8fcd95" url: "https://github.com/atsign-foundation/at_widgets" source: git version: "6.1.9" @@ -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 91f0672424020d4abafe3907dbb8c9e44bab844a Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 7 Oct 2024 16:25:07 -0400 Subject: [PATCH 19/60] 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 b63ee2f8b..6baa98074 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 c37cc7e7e3ea25b479bfe78a4621da706405fda4 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 7 Oct 2024 16:31:28 -0400 Subject: [PATCH 20/60] 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(); From 0cfe80e013f120b2f08e06eaf6f0c42f978843ee Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 7 Oct 2024 16:47:43 -0400 Subject: [PATCH 21/60] fix: forgot this file --- .../lib/features/settings/view/settings_view.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart b/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart index 84550e741..f4817f64a 100644 --- a/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart +++ b/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart @@ -38,7 +38,8 @@ class SettingsView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ CustomCard.settingsRail( - height: deviceSize.height * Sizes.SettingsCardHeightFactor, + height: + deviceSize.height * Sizes.settingsCardHeightFactor, child: const Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -56,8 +57,9 @@ class SettingsView extends StatelessWidget { ), ), CustomCard.settingsContent( - height: deviceSize.height * Sizes.SettingsCardHeightFactor, - width: deviceSize.width * Sizes.SettingsCardWidthFactor, + height: + deviceSize.height * Sizes.settingsCardHeightFactor, + width: deviceSize.width * Sizes.settingsCardWidthFactor, child: Padding( padding: const EdgeInsets.only( left: Sizes.p43, From cdd8e297f9ee603b6a1569e920935da5c4f18279 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Tue, 8 Oct 2024 08:47:56 -0400 Subject: [PATCH 22/60] feat: signout button added to the settings screen --- .../features/settings/view/settings_view.dart | 8 ++-- .../npt_flutter/lib/pages/loading_page.dart | 21 ++++++++++ .../dart/npt_flutter/lib/pages/pages.dart | 1 + packages/dart/npt_flutter/lib/routes.dart | 2 + .../lib/widgets/custom_text_button.dart | 41 ++++++++++++------- 5 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 packages/dart/npt_flutter/lib/pages/loading_page.dart diff --git a/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart b/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart index f4817f64a..c1b63347f 100644 --- a/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart +++ b/packages/dart/npt_flutter/lib/features/settings/view/settings_view.dart @@ -38,8 +38,7 @@ class SettingsView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ CustomCard.settingsRail( - height: - deviceSize.height * Sizes.settingsCardHeightFactor, + height: deviceSize.height * Sizes.settingsCardHeightFactor, child: const Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -51,14 +50,13 @@ class SettingsView extends StatelessWidget { CustomTextButton.privacyPolicy(), CustomTextButton.feedback(), CustomTextButton.backUpYourKey(), - CustomTextButton.resetAtsign(), + CustomTextButton.signOut(), ContactListTile(), ], ), ), CustomCard.settingsContent( - height: - deviceSize.height * Sizes.settingsCardHeightFactor, + height: deviceSize.height * Sizes.settingsCardHeightFactor, width: deviceSize.width * Sizes.settingsCardWidthFactor, child: Padding( padding: const EdgeInsets.only( diff --git a/packages/dart/npt_flutter/lib/pages/loading_page.dart b/packages/dart/npt_flutter/lib/pages/loading_page.dart new file mode 100644 index 000000000..b26038519 --- /dev/null +++ b/packages/dart/npt_flutter/lib/pages/loading_page.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:npt_flutter/widgets/npt_app_bar.dart'; + +class LoadingPage extends StatelessWidget { + const LoadingPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + extendBodyBehindAppBar: true, + extendBody: true, + appBar: NptAppBar( + isNavigateBack: false, + showSettings: false, + ), + body: Center( + child: CircularProgressIndicator.adaptive(), + ), + ); + } +} diff --git a/packages/dart/npt_flutter/lib/pages/pages.dart b/packages/dart/npt_flutter/lib/pages/pages.dart index c5290f584..9a42a3181 100644 --- a/packages/dart/npt_flutter/lib/pages/pages.dart +++ b/packages/dart/npt_flutter/lib/pages/pages.dart @@ -1,4 +1,5 @@ export 'dashboard_page.dart'; +export 'loading_page.dart'; export 'onboarding_page.dart'; export 'profile_form_page.dart'; export 'settings_page.dart'; diff --git a/packages/dart/npt_flutter/lib/routes.dart b/packages/dart/npt_flutter/lib/routes.dart index ba5db84ab..765c46c68 100644 --- a/packages/dart/npt_flutter/lib/routes.dart +++ b/packages/dart/npt_flutter/lib/routes.dart @@ -7,11 +7,13 @@ class Routes { static const dashboard = '/dashboard'; static const settings = '/settings'; static const profileForm = '/profile'; + static const loadingPage = '/loading'; static final Map routes = { onboarding: (_) => const OnboardingPage(nextRoute: dashboard), dashboard: (_) => const DashboardPage(), settings: (_) => const SettingsPage(), profileForm: (_) => const ProfileFormPage(), + loadingPage: (_) => const LoadingPage(), }; } 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 6baa98074..66ff73028 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -1,15 +1,16 @@ -import 'dart:developer'; - import 'package:at_contacts_flutter/services/contact_service.dart'; import 'package:at_onboarding_flutter/at_onboarding_flutter.dart'; import 'package:at_onboarding_flutter/services/onboarding_service.dart'; 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/app.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/util/pre_offboard.dart'; import 'package:npt_flutter/features/onboarding/widgets/onboarding_button.dart'; +import 'package:npt_flutter/pages/loading_page.dart'; import 'package:npt_flutter/routes.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -65,6 +66,11 @@ class CustomTextButton extends StatelessWidget { this.title = 'Reset App', this.type = CustomListTileType.resetAtsign, super.key}); + const CustomTextButton.signOut( + {this.iconData = Icons.logout_outlined, + this.title = 'Sign Out', + this.type = CustomListTileType.signOut, + super.key}); const CustomTextButton.feedback( {this.iconData = Icons.feedback_outlined, @@ -94,15 +100,13 @@ 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'); } @@ -120,12 +124,10 @@ 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: - log(rootDomain); final futurePreference = await loadAtClientPreference(rootDomain); if (context.mounted) { final result = await AtOnboarding.reset( @@ -137,8 +139,7 @@ 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; @@ -157,6 +158,14 @@ class CustomTextButton extends StatelessWidget { if (!await launchUrl(emailUri)) { CustomSnackBar.notification(content: strings.noEmailClientAvailable); } + break; + + case CustomListTileType.signOut: + Navigator.of(context) + .pushAndRemoveUntil(MaterialPageRoute(builder: (context) => const LoadingPage()), (route) => false); + await preSignout(ContactService().currentAtsign); + Navigator.of(context).pushReplacementNamed(Routes.onboarding); + break; } } @@ -178,14 +187,15 @@ class CustomTextButton extends StatelessWidget { return strings.resetAtsign; case CustomListTileType.feedback: return strings.feedback; + case CustomListTileType.signOut: + // TODO Localize in the next PR. + return 'signOut'; } } - 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: () { @@ -209,4 +219,5 @@ enum CustomListTileType { backupYourKey, resetAtsign, feedback, + signOut, } From f36071f45e6d48ab5f1e65a1f47a01abc83a8183 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Tue, 8 Oct 2024 08:55:53 -0400 Subject: [PATCH 23/60] fix: parameter removed from preSignout. --- .../npt_flutter/lib/features/onboarding/util/pre_offboard.dart | 2 +- packages/dart/npt_flutter/lib/widgets/custom_text_button.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 2705e5650..5661b47e5 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 @@ -4,7 +4,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 { +Future preSignout() async { App.log("Resetting all application state before signout".loggable); // We need to do the following before "signing out" // - Wipe all application state 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 66ff73028..1c1c490ed 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -163,7 +163,7 @@ class CustomTextButton extends StatelessWidget { case CustomListTileType.signOut: Navigator.of(context) .pushAndRemoveUntil(MaterialPageRoute(builder: (context) => const LoadingPage()), (route) => false); - await preSignout(ContactService().currentAtsign); + await preSignout(); Navigator.of(context).pushReplacementNamed(Routes.onboarding); break; } From 84362e5d004ff91c27256ffdb3ff11b1e7b01f1d Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Tue, 8 Oct 2024 19:23:11 -0400 Subject: [PATCH 24/60] feat: root domain removed from onboarding flow. --- .../onboarding/util/atsign_manager.dart | 5 +- ...boaring_view.dart => onboarding_view.dart} | 8 +- .../widgets/at_directory_dialog.dart | 59 +++---------- .../onboarding/widgets/atsign_dialog.dart | 22 +++++ .../onboarding/widgets/atsign_selector.dart | 83 +++++++++++++++++++ .../onboarding/widgets/onboarding_button.dart | 58 +++++++------ .../onboarding/widgets/onboarding_dialog.dart | 64 ++++++++++++++ .../lib/pages/onboarding_page.dart | 2 +- .../lib/widgets/custom_text_button.dart | 16 ++++ 9 files changed, 238 insertions(+), 79 deletions(-) rename packages/dart/npt_flutter/lib/features/onboarding/view/{onboaring_view.dart => onboarding_view.dart} (86%) create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_dialog.dart 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 4102aeb63..198c0c54f 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 @@ -3,8 +3,8 @@ 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; +import 'package:path_provider/path_provider.dart'; class AtsignInformation { final String atSign; @@ -51,8 +51,7 @@ Future> getAtsignEntries() async { atSignInfo = await _getAtsignInformationFromFile(); } catch (e) { App.log( - "Failed get Atsign Information, ignoring invalid file: ${e.toString()}" - .loggable, + "Failed get Atsign Information, ignoring invalid file: ${e.toString()}".loggable, ); return {}; } diff --git a/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart b/packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart similarity index 86% rename from packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart rename to packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart index f7fc2090a..1f5b5db67 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart @@ -43,7 +43,13 @@ class OnboardingView extends StatelessWidget { bottom: Sizes.p44, right: Sizes.p44, ), - child: CustomTextButton.resetAtsign(), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + CustomTextButton.resetAtsign(), + CustomTextButton.selectRootDomain(), + ], + ), ), ) ], diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart index 5c4be911f..a365208f6 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart @@ -1,61 +1,22 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:npt_flutter/features/onboarding/widgets/onboarding_at_directory_selector.dart'; -import 'package:npt_flutter/styles/sizes.dart'; -import 'package:npt_flutter/widgets/custom_container.dart'; +import 'package:npt_flutter/features/onboarding/widgets/onboarding_dialog.dart'; -class AtDirectoryDialog extends StatefulWidget { +class AtDirectoryDialog extends StatelessWidget { const AtDirectoryDialog({super.key}); - @override - State createState() => _AtDirectoryDialogState(); -} - -class _AtDirectoryDialogState extends State { @override Widget build(BuildContext context) { final strings = AppLocalizations.of(context)!; - return AlertDialog( - backgroundColor: Colors.white, - content: Padding( - padding: const EdgeInsets.symmetric(vertical: Sizes.p12, horizontal: Sizes.p16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - CustomContainer.background( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(strings.atDirectory), - Text(strings.atDirectorySubtitle), - gapH16, - OnboardingAtDirectorySelector(), - ], - ), - ), - gapH10, - CustomContainer.background( - child: Row( - children: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(false); - }, - child: Text(strings.cancel), - ), - const Spacer(), - ElevatedButton( - onPressed: () { - Navigator.of(context).pop(true); - }, - child: Text(strings.onboard), - ), - ], - )) - ], - ), - ), + return OnboardingDialog( + title: strings.atDirectory, + subtitle: strings.atDirectorySubtitle, + // TODO: Add success button text to the AppLocalizations + successButtonText: 'select', + children: [ + OnboardingAtDirectorySelector(), + ], ); } } diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart new file mode 100644 index 000000000..f3c840111 --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:npt_flutter/features/onboarding/widgets/atsign_selector.dart'; +import 'package:npt_flutter/features/onboarding/widgets/onboarding_dialog.dart'; + +class AtSignDialog extends StatelessWidget { + const AtSignDialog({super.key}); + + @override + Widget build(BuildContext context) { + final strings = AppLocalizations.of(context)!; + return OnboardingDialog( + // TODO: Add title, subtitle and success button text to the AppLocalizations + title: 'AtSign', + subtitle: 'Please select your @sign', + successButtonText: 'Next', + children: [ + AtsignSelector(), + ], + ); + } +} diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart new file mode 100644 index 000000000..b429b6843 --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart @@ -0,0 +1,83 @@ +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); + +class AtsignSelector extends StatefulWidget { + AtsignSelector({ + super.key, + }); + + final List options = []; + + @override + State createState() => _AtsignSelectorState(); +} + +class _AtsignSelectorState extends State { + final FocusNode focusNode = FocusNode(); + + final TextEditingController controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + return BlocBuilder(builder: (context, rootDomain) { + controller.text = rootDomain.string; + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: DropdownMenu( + initialSelection: widget.options.contains(rootDomain.string) ? rootDomain.string : null, + dropdownMenuEntries: widget.options + .map>( + (o) => DropdownMenuEntry( + value: o, + label: o, + ), + ) + .toList(), + onSelected: (value) { + if (value == null) return; + + context.read().setRootDomain(value); + }, + ), + ), + Flexible( + child: KeyboardListener( + focusNode: focusNode, + onKeyEvent: (value) { + if (value.logicalKey == LogicalKeyboardKey.backspace) { + if (widget.options.length > 2) widget.options.removeLast(); + } + }, + child: TextFormField( + controller: controller, + autovalidateMode: AutovalidateMode.onUserInteraction, + // validator: FormValidator.validateRequiredAtsignField, + onChanged: (value) { + // prevent the user from adding the default values to the dropdown a second time. + if (value != widget.options[0] || value != widget.options[1]) { + widget.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 (widget.options.length > 3) widget.options.removeAt(2); + + context.read().setRootDomain(value); + }, + ), + ), + ) + ], + ), + ], + ); + }); + } +} 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 a44afba50..0ffa954e8 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 @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:at_contacts_flutter/at_contacts_flutter.dart'; import 'package:at_onboarding_flutter/at_onboarding_flutter.dart'; import 'package:flutter/material.dart'; @@ -7,7 +9,8 @@ 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'; +import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; +import 'package:npt_flutter/features/onboarding/widgets/atsign_dialog.dart'; import 'package:npt_flutter/routes.dart'; import 'package:path_provider/path_provider.dart'; import 'package:phosphor_flutter/phosphor_flutter.dart'; @@ -33,28 +36,7 @@ class OnboardingButton extends StatefulWidget { } class _OnboardingButtonState extends State { - @override - Widget build(BuildContext context) { - final strings = AppLocalizations.of(context)!; - return BlocBuilder( - builder: (context, rootDomain) { - return ElevatedButton.icon( - onPressed: () async { - final result = await selectOptions(); - - if (result && context.mounted) onboard(rootDomain: rootDomain.string); - }, - icon: PhosphorIcon(PhosphorIcons.arrowUpRight()), - label: Text( - strings.getStarted, - ), - iconAlignment: IconAlignment.end, - ); - }); - } - - 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, @@ -71,6 +53,10 @@ class _OnboardingButtonState extends State { case AtOnboardingResultStatus.success: await initializeContactsService(rootDomain: rootDomain); postOnboard(onboardingResult.atsign!); + final result = + await saveAtsignInformation(AtsignInformation(atSign: onboardingResult.atsign!, rootDomain: rootDomain)); + log('atsign result is:$result'); + if (mounted) { Navigator.of(context).pushReplacementNamed(Routes.dashboard); } @@ -90,11 +76,33 @@ class _OnboardingButtonState extends State { } } - Future selectOptions() async { + Future selectAtsign() async { final results = await showDialog( context: context, - builder: (BuildContext context) => const AtDirectoryDialog(), + builder: (BuildContext context) => const AtSignDialog(), ); return results ?? false; } + + @override + Widget build(BuildContext context) { + final strings = AppLocalizations.of(context)!; + return BlocBuilder(builder: (context, rootDomain) { + return ElevatedButton.icon( + onPressed: () async { + final isEmptyAtsignList = (await getAtsignEntries()).isNotEmpty; + log(isEmptyAtsignList.toString()); + + if (isEmptyAtsignList) await selectAtsign(); + + onboard(rootDomain: rootDomain.string); + }, + icon: PhosphorIcon(PhosphorIcons.arrowUpRight()), + label: Text( + strings.getStarted, + ), + iconAlignment: IconAlignment.end, + ); + }); + } } diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_dialog.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_dialog.dart new file mode 100644 index 000000000..796f8f46b --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_dialog.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:npt_flutter/styles/sizes.dart'; +import 'package:npt_flutter/widgets/custom_container.dart'; + +class OnboardingDialog extends StatelessWidget { + const OnboardingDialog( + {required this.title, + required this.subtitle, + required this.successButtonText, + required this.children, + super.key}); + final String title; + final String subtitle; + final String successButtonText; + final List children; + + @override + Widget build(BuildContext context) { + final strings = AppLocalizations.of(context)!; + return AlertDialog( + backgroundColor: Colors.white, + content: Padding( + padding: const EdgeInsets.symmetric(vertical: Sizes.p12, horizontal: Sizes.p16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CustomContainer.background( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title), + Text(subtitle), + gapH16, + ...children, + ], + ), + ), + gapH10, + CustomContainer.background( + child: Row( + children: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(strings.cancel), + ), + const Spacer(), + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(true); + }, + child: Text(successButtonText), + ), + ], + )) + ], + ), + ), + ); + } +} diff --git a/packages/dart/npt_flutter/lib/pages/onboarding_page.dart b/packages/dart/npt_flutter/lib/pages/onboarding_page.dart index 8a52b2d28..4c55a08a8 100644 --- a/packages/dart/npt_flutter/lib/pages/onboarding_page.dart +++ b/packages/dart/npt_flutter/lib/pages/onboarding_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:npt_flutter/features/onboarding/view/onboaring_view.dart'; +import 'package:npt_flutter/features/onboarding/view/onboarding_view.dart'; import 'package:npt_flutter/widgets/npt_app_bar.dart'; class OnboardingPage extends StatelessWidget { 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 1c1c490ed..75bfc64c1 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -9,6 +9,7 @@ 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/util/pre_offboard.dart'; +import 'package:npt_flutter/features/onboarding/widgets/at_directory_dialog.dart'; import 'package:npt_flutter/features/onboarding/widgets/onboarding_button.dart'; import 'package:npt_flutter/pages/loading_page.dart'; import 'package:npt_flutter/routes.dart'; @@ -77,6 +78,11 @@ class CustomTextButton extends StatelessWidget { this.title = 'Feedback', this.type = CustomListTileType.feedback, super.key}); + const CustomTextButton.selectRootDomain( + {this.iconData = Icons.dns_outlined, + this.title = 'Select Root Domain', + this.type = CustomListTileType.selectRootDomain, + super.key}); final IconData iconData; final String title; @@ -166,6 +172,12 @@ class CustomTextButton extends StatelessWidget { await preSignout(); Navigator.of(context).pushReplacementNamed(Routes.onboarding); break; + case CustomListTileType.selectRootDomain: + await showDialog( + context: context, + builder: (BuildContext context) => const AtDirectoryDialog(), + ); + break; } } @@ -190,6 +202,9 @@ class CustomTextButton extends StatelessWidget { case CustomListTileType.signOut: // TODO Localize in the next PR. return 'signOut'; + case CustomListTileType.selectRootDomain: + // TODO Localize in the next PR. + return 'Select Root Domain'; } } @@ -220,4 +235,5 @@ enum CustomListTileType { resetAtsign, feedback, signOut, + selectRootDomain, } From 2a2cb8cea58bd35a6835c0b33bcc65c2f9735dcd Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Tue, 8 Oct 2024 19:40:04 -0400 Subject: [PATCH 25/60] fix: handle empty file case --- .../npt_flutter/lib/features/onboarding/util/atsign_manager.dart | 1 + 1 file changed, 1 insertion(+) 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 198c0c54f..6e9dbd2a1 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 @@ -140,6 +140,7 @@ Future> _getAtsignInformationFromFile([File? f]) async { if (f == null) throw Exception("Failed to get the Atsign Information File"); try { var contents = await f.readAsString(); + if (contents.trim().isEmpty) return []; 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 From 71a93a88ea6a367cee5028f99f43c515c73e2180 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Tue, 8 Oct 2024 20:10:32 -0400 Subject: [PATCH 26/60] chore: localize relay selection --- .../npt_flutter/lib/localization/app_en.arb | 5 ++++- packages/dart/npt_flutter/lib/util/relay.dart | 17 +++++++++++++++++ .../lib/widgets/custom_text_button.dart | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 packages/dart/npt_flutter/lib/util/relay.dart diff --git a/packages/dart/npt_flutter/lib/localization/app_en.arb b/packages/dart/npt_flutter/lib/localization/app_en.arb index e64e0614d..423586348 100644 --- a/packages/dart/npt_flutter/lib/localization/app_en.arb +++ b/packages/dart/npt_flutter/lib/localization/app_en.arb @@ -68,6 +68,9 @@ "remotePort" : "Remote Port", "remotePortDescription" : "", "resetAtsign" : "Reset Atsign", + "rvAmDisplayName" : "Americas", + "rvApDisplayName" : "Asia-Pacific", + "rvEuDisplayName" : "Europe", "selectExportFile": "Please select a file to export to:", "serviceMapping" : "Service Mapping", "settings" : "Settings", @@ -90,4 +93,4 @@ "onboard" : "Onboard", "onboardingError" : "An error has occurred", "yaml" : "YAML" -} \ No newline at end of file +} diff --git a/packages/dart/npt_flutter/lib/util/relay.dart b/packages/dart/npt_flutter/lib/util/relay.dart new file mode 100644 index 000000000..4df31f2f9 --- /dev/null +++ b/packages/dart/npt_flutter/lib/util/relay.dart @@ -0,0 +1,17 @@ +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter/material.dart'; + +class RelayUtil { + static Map getRelayDisplayNameMap(BuildContext context) { + final strings = AppLocalizations.of(context)!; + return { + "@rv_am": strings.rvAmDisplayName, + "@rv_ap": strings.rvApDisplayName, + "@rv_eu": strings.rvEuDisplayName, + }; + } + + static List getRelayAtsignList() { + return ["@rv_am", "@rv_ap", "@rv_eu"]; + } +} 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 75bfc64c1..a09357c69 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -170,7 +170,7 @@ class CustomTextButton extends StatelessWidget { Navigator.of(context) .pushAndRemoveUntil(MaterialPageRoute(builder: (context) => const LoadingPage()), (route) => false); await preSignout(); - Navigator.of(context).pushReplacementNamed(Routes.onboarding); + if (context.mounted) Navigator.of(context).pushReplacementNamed(Routes.onboarding); break; case CustomListTileType.selectRootDomain: await showDialog( From 5f8ffe5385cfc8ce0b73577a520258f49bc42849 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Wed, 9 Oct 2024 12:00:07 -0400 Subject: [PATCH 27/60] fix: user can select atsign if available in keychain when onboarding. --- packages/dart/npt_flutter/lib/app.dart | 3 +- .../onboarding/cubit/at_directory_cubit.dart | 13 +++-- .../onboarding/util/atsign_manager.dart | 14 ++++-- .../widgets/at_directory_dialog.dart | 2 +- .../onboarding/widgets/atsign_selector.dart | 49 +++++++++++++------ .../onboarding_at_directory_selector.dart | 12 ++--- .../onboarding/widgets/onboarding_button.dart | 7 ++- .../lib/widgets/custom_text_button.dart | 45 +++++++++++------ 8 files changed, 92 insertions(+), 53 deletions(-) diff --git a/packages/dart/npt_flutter/lib/app.dart b/packages/dart/npt_flutter/lib/app.dart index 6ca42d71e..ba17559d9 100644 --- a/packages/dart/npt_flutter/lib/app.dart +++ b/packages/dart/npt_flutter/lib/app.dart @@ -97,8 +97,7 @@ class App extends StatelessWidget { create: (ctx) => FavoriteBloc(ctx.read()), ), ], - 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/onboarding/cubit/at_directory_cubit.dart b/packages/dart/npt_flutter/lib/features/onboarding/cubit/at_directory_cubit.dart index 6ffd96e92..85177e8ba 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,9 +1,12 @@ -import 'package:npt_flutter/features/logging/models/loggable.dart'; import 'package:npt_flutter/features/logging/models/logging_bloc.dart'; +import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; -class AtDirectoryCubit extends LoggingCubit { - AtDirectoryCubit() : super(const LoggableString('root.atsign.org')); +class AtDirectoryCubit extends LoggingCubit { + AtDirectoryCubit() : super(const AtsignInformation(atSign: '', rootDomain: 'root.atsign.org')); - void setRootDomain(String rootDomain) => emit(LoggableString(rootDomain)); - String getRootDomain() => (state.string); + void setRootDomain(String rootDomain) => emit(AtsignInformation(atSign: state.atSign, rootDomain: rootDomain)); + String getRootDomain() => (state.rootDomain); + + void setAtSign(String atSign) => emit(AtsignInformation(atSign: atSign, rootDomain: state.rootDomain)); + String getAtSign() => (state.atSign); } 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 6e9dbd2a1..1290e5b8e 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 @@ -6,11 +6,11 @@ import 'package:npt_flutter/app.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; -class AtsignInformation { +class AtsignInformation extends Loggable { final String atSign; final String rootDomain; - AtsignInformation({required this.atSign, required this.rootDomain}); + const AtsignInformation({required this.atSign, required this.rootDomain}); Map toJson() => { "atsign": atSign, @@ -26,6 +26,14 @@ class AtsignInformation { rootDomain: json["root-domain"], ); } + + @override + List get props => [atSign, rootDomain]; + + @override + String toString() { + return 'AtsignInformation($atSign, $rootDomain)'; + } } // This will return a map which looks like: @@ -96,7 +104,7 @@ Future saveAtsignInformation(AtsignInformation info) async { } try { f.writeAsString( - jsonEncode(atSignInfo.map((e) => e.toJson())), + jsonEncode(atSignInfo.map((e) => e.toJson()).toList()), mode: FileMode.writeOnly, flush: true, ); diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart index a365208f6..d46c64bd5 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart @@ -13,7 +13,7 @@ class AtDirectoryDialog extends StatelessWidget { title: strings.atDirectory, subtitle: strings.atDirectorySubtitle, // TODO: Add success button text to the AppLocalizations - successButtonText: 'select', + successButtonText: 'Done', children: [ OnboardingAtDirectorySelector(), ], diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart index b429b6843..4e311e074 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart @@ -1,18 +1,16 @@ 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'; +import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; typedef OnboardingMapCallback = void Function(Map val); class AtsignSelector extends StatefulWidget { - AtsignSelector({ + const AtsignSelector({ super.key, }); - final List options = []; - @override State createState() => _AtsignSelectorState(); } @@ -21,11 +19,29 @@ class _AtsignSelectorState extends State { final FocusNode focusNode = FocusNode(); final TextEditingController controller = TextEditingController(); + List options = []; + late final List originalOptions; + + late int originalOptionsLength; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) async { + options = (await getAtsignEntries()).keys.toList(); + if (mounted) context.read().setAtSign(options[0]); + + controller.text = options[0]; + originalOptions = List.from(options); + originalOptionsLength = options.length; + setState(() {}); + }); + } @override Widget build(BuildContext context) { - return BlocBuilder(builder: (context, rootDomain) { - controller.text = rootDomain.string; + return BlocBuilder(builder: (context, atsignInformation) { + controller.text = atsignInformation.atSign; return Column( children: [ Row( @@ -33,8 +49,9 @@ class _AtsignSelectorState extends State { children: [ Flexible( child: DropdownMenu( - initialSelection: widget.options.contains(rootDomain.string) ? rootDomain.string : null, - dropdownMenuEntries: widget.options + initialSelection: + options.contains(atsignInformation.atSign) ? atsignInformation.atSign : controller.text, + dropdownMenuEntries: options .map>( (o) => DropdownMenuEntry( value: o, @@ -45,7 +62,7 @@ class _AtsignSelectorState extends State { onSelected: (value) { if (value == null) return; - context.read().setRootDomain(value); + context.read().setAtSign(value); }, ), ), @@ -54,7 +71,7 @@ class _AtsignSelectorState extends State { focusNode: focusNode, onKeyEvent: (value) { if (value.logicalKey == LogicalKeyboardKey.backspace) { - if (widget.options.length > 2) widget.options.removeLast(); + if (options.length > originalOptionsLength) options.removeLast(); } }, child: TextFormField( @@ -63,13 +80,15 @@ class _AtsignSelectorState extends State { // validator: FormValidator.validateRequiredAtsignField, onChanged: (value) { // prevent the user from adding the default values to the dropdown a second time. - if (value != widget.options[0] || value != widget.options[1]) { - widget.options.add(value); + if (!originalOptions.contains(value)) { + options.add(value); + + setState(() {}); } - //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 (widget.options.length > 3) widget.options.removeAt(2); + //removes the last element making the final entry the only additional value in options. This prevents the dropdown from having more than original options + one entries. + if (options.length > originalOptionsLength + 1) options.removeAt(originalOptionsLength); - context.read().setRootDomain(value); + context.read().setAtSign(value); }, ), ), 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 65864b456..2455ff46e 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,8 +1,8 @@ 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'; +import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; typedef OnboardingMapCallback = void Function(Map val); @@ -18,9 +18,8 @@ class OnboardingAtDirectorySelector extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, rootDomain) { - controller.text = rootDomain.string; + return BlocBuilder(builder: (context, atsignInformation) { + controller.text = atsignInformation.rootDomain; return Column( children: [ Row( @@ -28,9 +27,8 @@ class OnboardingAtDirectorySelector extends StatelessWidget { children: [ Flexible( child: DropdownMenu( - initialSelection: options.contains(rootDomain.string) - ? rootDomain.string - : null, + initialSelection: + options.contains(atsignInformation.rootDomain) ? atsignInformation.rootDomain : null, dropdownMenuEntries: options .map>( (o) => DropdownMenuEntry( 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 0ffa954e8..d040e814e 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 @@ -6,7 +6,6 @@ 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/util/atsign_manager.dart'; @@ -87,15 +86,15 @@ class _OnboardingButtonState extends State { @override Widget build(BuildContext context) { final strings = AppLocalizations.of(context)!; - return BlocBuilder(builder: (context, rootDomain) { + return BlocBuilder(builder: (context, atsignInformation) { return ElevatedButton.icon( onPressed: () async { final isEmptyAtsignList = (await getAtsignEntries()).isNotEmpty; - log(isEmptyAtsignList.toString()); + log('atsign entries is empty state: $isEmptyAtsignList'); if (isEmptyAtsignList) await selectAtsign(); - onboard(rootDomain: rootDomain.string); + onboard(rootDomain: atsignInformation.rootDomain); }, icon: PhosphorIcon(PhosphorIcons.arrowUpRight()), label: Text( 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 a09357c69..329a7a538 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -4,10 +4,9 @@ import 'package:at_onboarding_flutter/services/onboarding_service.dart'; 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/app.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/util/atsign_manager.dart'; import 'package:npt_flutter/features/onboarding/util/pre_offboard.dart'; import 'package:npt_flutter/features/onboarding/widgets/at_directory_dialog.dart'; import 'package:npt_flutter/features/onboarding/widgets/onboarding_button.dart'; @@ -94,7 +93,7 @@ class CustomTextButton extends StatelessWidget { // final bodyMedium = Theme.of(context).textTheme.bodyMedium!; // final bodySmall = Theme.of(context).textTheme.bodySmall!; final strings = AppLocalizations.of(context)!; - Future onTap(String rootDomain) async { + Future onTap({String? rootDomain}) async { switch (type) { case CustomListTileType.email: Uri emailUri = Uri( @@ -134,7 +133,7 @@ class CustomTextButton extends StatelessWidget { } break; case CustomListTileType.resetAtsign: - final futurePreference = await loadAtClientPreference(rootDomain); + final futurePreference = await loadAtClientPreference(rootDomain!); if (context.mounted) { final result = await AtOnboarding.reset( context: context, @@ -208,20 +207,34 @@ class CustomTextButton extends StatelessWidget { } } - return BlocBuilder(builder: (context, rootDomain) { - return Padding( - padding: const EdgeInsets.only(left: Sizes.p30, right: Sizes.p30, bottom: Sizes.p10), - child: TextButton.icon( - label: Text(getTitle(strings)), - onPressed: () { - onTap(rootDomain.string); - }, - icon: Icon( - iconData, + if (type == CustomListTileType.resetAtsign) { + return BlocBuilder(builder: (context, atsignInformation) { + return Padding( + padding: const EdgeInsets.only(left: Sizes.p30, right: Sizes.p30, bottom: Sizes.p10), + child: TextButton.icon( + label: Text(getTitle(strings)), + onPressed: () { + onTap(rootDomain: atsignInformation.rootDomain); + }, + icon: Icon( + iconData, + ), ), + ); + }); + } + return Padding( + padding: const EdgeInsets.only(left: Sizes.p30, right: Sizes.p30, bottom: Sizes.p10), + child: TextButton.icon( + label: Text(getTitle(strings)), + onPressed: () { + onTap(); + }, + icon: Icon( + iconData, ), - ); - }); + ), + ); } } From b7630971ac93ac75f0c600f1930556d377aae5e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 04:10:11 +0000 Subject: [PATCH 28/60] build(deps): Bump actions/upload-artifact in the github-actions group Bumps the github-actions group with 1 update: [actions/upload-artifact](https://github.com/actions/upload-artifact). Updates `actions/upload-artifact` from 4.4.1 to 4.4.2 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/604373da6381bf24206979c74d06a550515601b9...84480863f228bb9747b473957fcc9e309aa96097) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/c_release.yml | 8 ++++---- .github/workflows/multibuild.yaml | 8 ++++---- .github/workflows/python-sshnpd-build-publish.yml | 2 +- .github/workflows/scorecards.yml | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/c_release.yml b/.github/workflows/c_release.yml index c1d13a758..1817060c5 100644 --- a/.github/workflows/c_release.yml +++ b/.github/workflows/c_release.yml @@ -91,7 +91,7 @@ jobs: Compress-Archive -Path sshnpd -Destination tarball/${{ matrix.output-name }}.zip # upload the build - - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: ${{ matrix.output-name }}_${{ matrix.compiler @@ -131,7 +131,7 @@ jobs: --platform ${{ matrix.platform }} -o type=tar,dest=bins.tar . mkdir tarballs tar -xvf bins.tar -C tarballs - - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: ${{ @@ -169,7 +169,7 @@ jobs: --platform ${{ matrix.platform }} -o type=tar,dest=bins.tar . mkdir tarballs tar -xvf bins.tar -C tarballs - - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: ${{ @@ -191,7 +191,7 @@ jobs: cd ./packages mv c csshnpd-${{ github.ref_name }} tar -cvzf ../tarball/csshnpd-${{ github.ref_name }}.tar.gz csshnpd-${{ github.ref_name }} - - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: csshnpd-src-${{github.ref_name}}-${{github.run_number}}-${{github.run_attempt}} path: ./tarball/csshnpd-${{ github.ref_name }}.tar.gz diff --git a/.github/workflows/multibuild.yaml b/.github/workflows/multibuild.yaml index 4ab926f7b..d9451f5de 100644 --- a/.github/workflows/multibuild.yaml +++ b/.github/workflows/multibuild.yaml @@ -168,7 +168,7 @@ jobs: --password "$MACOS_APPLE_ID_PASSWORD" \ --wait # upload the build - - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: ${{ matrix.output-name @@ -205,7 +205,7 @@ jobs: mkdir tarballs tar -xvf bins.tar -C tarballs - if: ${{ ! inputs.main_build_only }} - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: ${{ matrix.output-name @@ -234,7 +234,7 @@ jobs: REF=${{ github.ref }} TAG=${REF:11} write_metadata universal.sh sshnp_version "$TAG" - - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: universal.sh-${{github.ref_name}}-${{github.run_number}}-${{github.run_attempt}} path: ./packages/dart/sshnoports/bundles/universal.sh @@ -248,7 +248,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: universal.ps1-${{github.ref_name}}-${{github.run_number}}-${{github.run_attempt}} path: ./packages/dart/sshnoports/bundles/universal.ps1 diff --git a/.github/workflows/python-sshnpd-build-publish.yml b/.github/workflows/python-sshnpd-build-publish.yml index 83d4b3410..1f777e101 100644 --- a/.github/workflows/python-sshnpd-build-publish.yml +++ b/.github/workflows/python-sshnpd-build-publish.yml @@ -49,7 +49,7 @@ jobs: cp -r dist/ $GITHUB_WORKSPACE - name: Store the distribution packages - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: sshnpd-python-package path: dist/ diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 876b6a000..5ae9c4479 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -59,7 +59,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: SARIF file path: results.sarif From 4478f8c5f9cf1b031ead2aa867e808c3d818936b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 04:58:44 +0000 Subject: [PATCH 29/60] build(deps): Bump the pub group across 1 directory with 2 updates Bumps the pub group with 2 updates in the /packages/dart/sshnoports directory: [at_cli_commons](https://github.com/atsign-foundation/at_libraries/tree/trunk/packages) and [at_onboarding_cli](https://github.com/atsign-foundation/at_libraries). Updates `at_cli_commons` from 1.1.0 to 1.2.0 - [Release notes](https://github.com/atsign-foundation/at_libraries/releases) - [Commits](https://github.com/atsign-foundation/at_libraries/commits/HEAD/packages) Updates `at_onboarding_cli` from 1.6.4 to 1.7.0 - [Release notes](https://github.com/atsign-foundation/at_libraries/releases) - [Commits](https://github.com/atsign-foundation/at_libraries/commits) --- updated-dependencies: - dependency-name: at_cli_commons dependency-type: direct:production update-type: version-update:semver-minor dependency-group: pub - dependency-name: at_onboarding_cli dependency-type: direct:production update-type: version-update:semver-minor dependency-group: pub ... Signed-off-by: dependabot[bot] --- packages/dart/sshnoports/pubspec.lock | 8 ++++---- packages/dart/sshnoports/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/dart/sshnoports/pubspec.lock b/packages/dart/sshnoports/pubspec.lock index b73b0e318..fb9994551 100644 --- a/packages/dart/sshnoports/pubspec.lock +++ b/packages/dart/sshnoports/pubspec.lock @@ -78,10 +78,10 @@ packages: dependency: "direct main" description: name: at_cli_commons - sha256: "23db4c959e5cefdc8dbcfb563172eeee1c3c42a16974cf2f6df5fa2d8b91747a" + sha256: "36bb90335b6066cac33e5dcae1f4cc0087a9a0098bdffd9ff4536836fe13b6f3" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" at_client: dependency: "direct main" description: @@ -118,10 +118,10 @@ packages: dependency: "direct main" description: name: at_onboarding_cli - sha256: "9797347880162490efd47f06df8b26f667bff9e00d31d6ea176660c9b9ced06e" + sha256: "5af020d0fb5b8d17d822154c936115846d8019ae9c7d854aa7eb23898beaff84" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.7.0" at_persistence_secondary_server: dependency: transitive description: diff --git a/packages/dart/sshnoports/pubspec.yaml b/packages/dart/sshnoports/pubspec.yaml index b2e5415bd..b80a4d5ee 100644 --- a/packages/dart/sshnoports/pubspec.yaml +++ b/packages/dart/sshnoports/pubspec.yaml @@ -10,8 +10,8 @@ dependencies: noports_core: path: "../noports_core" version: 6.2.0 - at_onboarding_cli: 1.6.4 - at_cli_commons: ^1.1.0 + at_onboarding_cli: 1.7.0 + at_cli_commons: ^1.2.0 at_client: ^3.2.2 args: 2.5.0 socket_connector: ^2.2.0 From b9f234044bea73f16b9e63ea473b33530edf35a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 04:09:28 +0000 Subject: [PATCH 30/60] build(deps): Bump actions/upload-artifact in the github-actions group Bumps the github-actions group with 1 update: [actions/upload-artifact](https://github.com/actions/upload-artifact). Updates `actions/upload-artifact` from 4.4.2 to 4.4.3 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/84480863f228bb9747b473957fcc9e309aa96097...b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/c_release.yml | 8 ++++---- .github/workflows/multibuild.yaml | 8 ++++---- .github/workflows/python-sshnpd-build-publish.yml | 2 +- .github/workflows/scorecards.yml | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/c_release.yml b/.github/workflows/c_release.yml index 1817060c5..64dd09943 100644 --- a/.github/workflows/c_release.yml +++ b/.github/workflows/c_release.yml @@ -91,7 +91,7 @@ jobs: Compress-Archive -Path sshnpd -Destination tarball/${{ matrix.output-name }}.zip # upload the build - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: ${{ matrix.output-name }}_${{ matrix.compiler @@ -131,7 +131,7 @@ jobs: --platform ${{ matrix.platform }} -o type=tar,dest=bins.tar . mkdir tarballs tar -xvf bins.tar -C tarballs - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: ${{ @@ -169,7 +169,7 @@ jobs: --platform ${{ matrix.platform }} -o type=tar,dest=bins.tar . mkdir tarballs tar -xvf bins.tar -C tarballs - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: ${{ @@ -191,7 +191,7 @@ jobs: cd ./packages mv c csshnpd-${{ github.ref_name }} tar -cvzf ../tarball/csshnpd-${{ github.ref_name }}.tar.gz csshnpd-${{ github.ref_name }} - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: csshnpd-src-${{github.ref_name}}-${{github.run_number}}-${{github.run_attempt}} path: ./tarball/csshnpd-${{ github.ref_name }}.tar.gz diff --git a/.github/workflows/multibuild.yaml b/.github/workflows/multibuild.yaml index d9451f5de..1a9a176cd 100644 --- a/.github/workflows/multibuild.yaml +++ b/.github/workflows/multibuild.yaml @@ -168,7 +168,7 @@ jobs: --password "$MACOS_APPLE_ID_PASSWORD" \ --wait # upload the build - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: ${{ matrix.output-name @@ -205,7 +205,7 @@ jobs: mkdir tarballs tar -xvf bins.tar -C tarballs - if: ${{ ! inputs.main_build_only }} - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: ${{ matrix.output-name @@ -234,7 +234,7 @@ jobs: REF=${{ github.ref }} TAG=${REF:11} write_metadata universal.sh sshnp_version "$TAG" - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: universal.sh-${{github.ref_name}}-${{github.run_number}}-${{github.run_attempt}} path: ./packages/dart/sshnoports/bundles/universal.sh @@ -248,7 +248,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: universal.ps1-${{github.ref_name}}-${{github.run_number}}-${{github.run_attempt}} path: ./packages/dart/sshnoports/bundles/universal.ps1 diff --git a/.github/workflows/python-sshnpd-build-publish.yml b/.github/workflows/python-sshnpd-build-publish.yml index 1f777e101..7e22fa082 100644 --- a/.github/workflows/python-sshnpd-build-publish.yml +++ b/.github/workflows/python-sshnpd-build-publish.yml @@ -49,7 +49,7 @@ jobs: cp -r dist/ $GITHUB_WORKSPACE - name: Store the distribution packages - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: sshnpd-python-package path: dist/ diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 5ae9c4479..ea925a9be 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -59,7 +59,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: SARIF file path: results.sarif From a1e2684b4758d63473ed15c35d7e1331e48f9dda Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Thu, 10 Oct 2024 16:26:06 -0400 Subject: [PATCH 31/60] fix: default language is based on the device language or english if not supported. --- packages/dart/npt_flutter/lib/app.dart | 17 ++++-- .../onboarding/view/onboaring_view.dart | 57 +++++++++++++++++++ .../widgets/at_directory_dialog.dart | 3 +- .../onboarding/widgets/atsign_dialog.dart | 9 ++- .../onboarding/widgets/onboarding_button.dart | 6 +- .../features/settings/models/settings.dart | 38 +------------ .../repository/settings_repository.dart | 7 +++ .../widgets/settings_language_selector.dart | 1 + .../npt_flutter/lib/localization/app_en.arb | 11 ++-- .../npt_flutter/lib/localization/app_es.arb | 40 +++++++------ .../npt_flutter/lib/localization/app_pt.arb | 16 ++++-- .../dart/npt_flutter/lib/util/language.dart | 53 +++++++++++++++++ .../lib/widgets/custom_text_button.dart | 6 +- packages/dart/npt_flutter/pubspec.lock | 4 +- 14 files changed, 186 insertions(+), 82 deletions(-) create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart create mode 100644 packages/dart/npt_flutter/lib/util/language.dart diff --git a/packages/dart/npt_flutter/lib/app.dart b/packages/dart/npt_flutter/lib/app.dart index ba17559d9..86f452919 100644 --- a/packages/dart/npt_flutter/lib/app.dart +++ b/packages/dart/npt_flutter/lib/app.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -5,6 +7,7 @@ import 'package:npt_flutter/features/features.dart'; import 'package:npt_flutter/features/onboarding/cubit/at_directory_cubit.dart'; import 'package:npt_flutter/routes.dart'; import 'package:npt_flutter/styles/app_theme.dart'; +import 'package:npt_flutter/util/language.dart'; export 'package:npt_flutter/features/logging/logging.dart'; @@ -97,21 +100,27 @@ class App extends StatelessWidget { create: (ctx) => FavoriteBloc(ctx.read()), ), ], - child: BlocSelector(selector: (state) { + child: BlocSelector(selector: (state) { if (state is SettingsLoadedState) { return state.settings.language; } - return Language.english; + return null; }, builder: (context, language) { + Locale defaultLocal = Language.english.locale; + if (language == null) { + //check if the device language is supported or not use english as the default. + final deviceLocal = Locale(Platform.localeName.split('_').first); + defaultLocal = LanguageUtil.getLanguageFromLocale(deviceLocal).locale; + } return TrayManager( child: MaterialApp( theme: AppTheme.light(), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, - locale: language.locale, + locale: defaultLocal, localeResolutionCallback: (locale, supportedLocales) { - return language.locale; + return language != null ? language.locale : locale; }, navigatorKey: navState, initialRoute: Routes.onboarding, diff --git a/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart b/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart new file mode 100644 index 000000000..2770041e8 --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:npt_flutter/features/onboarding/widgets/onboarding_button.dart'; +import 'package:npt_flutter/styles/sizes.dart'; +import 'package:npt_flutter/widgets/custom_text_button.dart'; + +class OnboardingView extends StatelessWidget { + const OnboardingView({super.key}); + + @override + Widget build(BuildContext context) { + final strings = AppLocalizations.of(context)!; + final textTheme = Theme.of(context).textTheme; + return Stack( + children: [ + Positioned.fill( + child: SvgPicture.asset( + 'assets/onboarding_bg.svg', + fit: BoxFit.cover, + ), + ), + Align( + child: Column( + children: [ + gapH108, + Text( + strings.onboardingTitle, + style: textTheme.headlineLarge!.copyWith( + color: Colors.black, + ), + ), + Text(strings.onboardingSubTitle, style: textTheme.headlineMedium), + gapH20, + const OnboardingButton(), + ], + ), + ), + const Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only( + bottom: Sizes.p44, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + CustomTextButton.selectRootDomain(), + CustomTextButton.resetAtsign(), + ], + ), + ), + ) + ], + ); + } +} diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart index d46c64bd5..cd27c8ebf 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart @@ -12,8 +12,7 @@ class AtDirectoryDialog extends StatelessWidget { return OnboardingDialog( title: strings.atDirectory, subtitle: strings.atDirectorySubtitle, - // TODO: Add success button text to the AppLocalizations - successButtonText: 'Done', + successButtonText: strings.done, children: [ OnboardingAtDirectorySelector(), ], diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart index f3c840111..a60db7800 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart @@ -10,11 +10,10 @@ class AtSignDialog extends StatelessWidget { Widget build(BuildContext context) { final strings = AppLocalizations.of(context)!; return OnboardingDialog( - // TODO: Add title, subtitle and success button text to the AppLocalizations - title: 'AtSign', - subtitle: 'Please select your @sign', - successButtonText: 'Next', - children: [ + title: strings.atsignDialogTitle, + subtitle: strings.atsignDialogSubtitle, + successButtonText: strings.next, + children: const [ AtsignSelector(), ], ); 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 d040e814e..c8da1fb13 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 @@ -90,11 +90,11 @@ class _OnboardingButtonState extends State { return ElevatedButton.icon( onPressed: () async { final isEmptyAtsignList = (await getAtsignEntries()).isNotEmpty; - log('atsign entries is empty state: $isEmptyAtsignList'); - if (isEmptyAtsignList) await selectAtsign(); + bool proceedToOnboard = false; + if (isEmptyAtsignList) proceedToOnboard = await selectAtsign(); - onboard(rootDomain: atsignInformation.rootDomain); + if (proceedToOnboard) onboard(rootDomain: atsignInformation.rootDomain); }, icon: PhosphorIcon(PhosphorIcons.arrowUpRight()), label: Text( diff --git a/packages/dart/npt_flutter/lib/features/settings/models/settings.dart b/packages/dart/npt_flutter/lib/features/settings/models/settings.dart index 46b35105c..5fa003f09 100644 --- a/packages/dart/npt_flutter/lib/features/settings/models/settings.dart +++ b/packages/dart/npt_flutter/lib/features/settings/models/settings.dart @@ -1,7 +1,7 @@ -import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:npt_flutter/app.dart'; +import 'package:npt_flutter/util/language.dart'; part 'settings.g.dart'; @@ -16,16 +16,6 @@ enum PreferredViewLayout { final String displayName; } -@JsonEnum() -enum Language { - @JsonValue("en") - english, - @JsonValue("es") - spanish, - @JsonValue("pt-br") - portuguese, -} - @JsonSerializable() class Settings extends Loggable { final String relayAtsign; @@ -43,7 +33,7 @@ class Settings extends Loggable { required this.overrideRelay, required this.viewLayout, this.darkMode = false, - this.language = Language.english, + required this.language, }); Settings copyWith({ @@ -84,30 +74,6 @@ class Settings extends Loggable { } } -extension LanguageExtension on Language { - Locale get locale { - switch (this) { - case Language.english: - return const Locale('en'); - case Language.spanish: - return const Locale('es'); - case Language.portuguese: - return const Locale('pt', 'BR'); - } - } - - String get displayName { - switch (this) { - case Language.english: - return 'English'; - case Language.spanish: - return 'Español'; - case Language.portuguese: - return 'Português'; - } - } -} - enum RelayOptions { am, eu, diff --git a/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart b/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart index 0adea4c71..c7c863a32 100644 --- a/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart +++ b/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart @@ -1,8 +1,11 @@ import 'dart:convert'; +import 'dart:io'; import 'package:at_client_mobile/at_client_mobile.dart'; +import 'package:flutter/material.dart'; import 'package:npt_flutter/constants.dart'; import 'package:npt_flutter/features/settings/settings.dart'; +import 'package:npt_flutter/util/language.dart'; class SettingsRepository { const SettingsRepository(); @@ -12,6 +15,10 @@ class SettingsRepository { relayAtsign: RelayOptions.am.relayAtsign, viewLayout: PreferredViewLayout.minimal, overrideRelay: false, + // set the default language to the device's language + language: LanguageUtil.getLanguageFromLocale( + Locale(Platform.localeName.split('_').first), + ), ); Future getSettings() async { diff --git a/packages/dart/npt_flutter/lib/features/settings/widgets/settings_language_selector.dart b/packages/dart/npt_flutter/lib/features/settings/widgets/settings_language_selector.dart index ecb506130..cb81d32e8 100644 --- a/packages/dart/npt_flutter/lib/features/settings/widgets/settings_language_selector.dart +++ b/packages/dart/npt_flutter/lib/features/settings/widgets/settings_language_selector.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:npt_flutter/features/settings/settings.dart'; +import 'package:npt_flutter/util/language.dart'; import 'package:npt_flutter/widgets/spinner.dart'; class SettingsLanguageSelector extends StatelessWidget { diff --git a/packages/dart/npt_flutter/lib/localization/app_en.arb b/packages/dart/npt_flutter/lib/localization/app_en.arb index 423586348..2bcd0396f 100644 --- a/packages/dart/npt_flutter/lib/localization/app_en.arb +++ b/packages/dart/npt_flutter/lib/localization/app_en.arb @@ -8,6 +8,8 @@ "asiaPacific" : "Asia-Pacific", "atDirectory" : "AtDirectory", "atDirectorySubtitle" : "Select the domain you want to use", + "atsignDialogSubtitle" : "Please select your atSign", + "atsignDialogTitle" : "AtSign", "back" : "Back", "backupYourKey" : "Backup Your Key", "cancel" : "Cancel", @@ -21,6 +23,7 @@ "deviceName": "Device Name", "deviceNameDescription" : "This is the name of your remote device", "discord" : "Discord", + "done": "Done", "edit" : "Edit", "email" : "Email", "emptyProfileMessage" : "No profiles found\nCreate or Import a profile to start using NoPorts.", @@ -39,6 +42,7 @@ "localPortDescription" : "", "logs" : "Logs", "minimal" : "Simple", + "next" : "Next", "noAtsign" : "No Atsign", "noEmailClientAvailable" : "No email client available", "noName" : "No Name", @@ -62,18 +66,17 @@ "profileRunningActionDeniedMessage" : "Cannot perform this action while profile is running", "refresh" : "Refresh", "relay" : "Relay", - "relayDescription" : "You can pick from our existing RV's or create a new one", + "relayDescription" : "You can pick from our existing RV''s or create a new one", "remoteHost" : "Remote Host", "remoteHostDescription" : "", "remotePort" : "Remote Port", "remotePortDescription" : "", "resetAtsign" : "Reset Atsign", - "rvAmDisplayName" : "Americas", - "rvApDisplayName" : "Asia-Pacific", - "rvEuDisplayName" : "Europe", "selectExportFile": "Please select a file to export to:", + "selectRootDomain" : "Select Root Domain", "serviceMapping" : "Service Mapping", "settings" : "Settings", + "signout" : "Sign Out", "sshStyle" : "Advanced", "status" : "Status", "submit" : "Submit", diff --git a/packages/dart/npt_flutter/lib/localization/app_es.arb b/packages/dart/npt_flutter/lib/localization/app_es.arb index f8696dbe2..684c788d9 100644 --- a/packages/dart/npt_flutter/lib/localization/app_es.arb +++ b/packages/dart/npt_flutter/lib/localization/app_es.arb @@ -1,4 +1,4 @@ -{ +{ "addNew" : "Agregar nuevo", "addNewProfile" : "Agregar nuevo perfil", "advanced": "Avanzado", @@ -8,6 +8,8 @@ "asiaPacific" : "Asia-Pacífico", "atDirectory" : "AtDirectory", "atDirectorySubtitle" : "Seleccione el dominio que desea usar", + "atsignDialogSubtitle" : "Por favor seleccione su atSign", + "atsignDialogTitle" : "AtSign", "back" : "Atrás", "backupYourKey" : "Respalda tu clave", "cancel" : "Cancelar", @@ -17,10 +19,11 @@ "defaultRelaySelection" : "Selección de relé predeterminada", "delete" : "Eliminar", "deviceAtsign" : "Dispositivo atSign", - "deviceAtsignDescription" : "Este es el atSign asociado con tu dispositivo", + "deviceAtsignDescription" : "Este es el atSign asociado con su dispositivo", "deviceName": "Nombre del dispositivo", - "deviceNameDescription" : "Este es el nombre de tu dispositivo remoto", + "deviceNameDescription" : "Este es el nombre de su dispositivo remoto", "discord" : "Discord", + "done": "Hecho", "edit" : "Editar", "email" : "Correo electrónico", "emptyProfileMessage" : "No se encontraron perfiles\nCree o importe un perfil para comenzar a usar NoPorts.", @@ -30,8 +33,8 @@ "exportLogs" : "Exportar registros", "failed": "Fallido", "faq" : "Preguntas frecuentes", - "feedback" : "Retroalimentación", - "getStarted" : "Comenzar", + "feedback" : "Comentarios", + "getStarted" : "Empezar", "import" : "Importar", "json" : "JSON", "language" : "Idioma", @@ -39,7 +42,8 @@ "localPortDescription" : "", "logs" : "Registros", "minimal" : "Simple", - "noAtsign" : "Sin atSign", + "next" : "Siguiente", + "noAtsign" : "Sin Atsign", "noEmailClientAvailable" : "No hay cliente de correo electrónico disponible", "noName" : "Sin nombre", "noPorts" : "NoPorts", @@ -50,35 +54,37 @@ "overrideAllProfile":"Sobrescribir todos los perfiles con la selección de relé predeterminada", "preview" : "Vista previa", "privacyPolicy" : "Política de privacidad", - "profileDeleteMessage" : "Este perfil se eliminará permanentemente.", - "profileDeleteSelectedMessage" : "Los perfiles seleccionados se eliminarán permanentemente.", + "profileDeleteMessage" : "Este perfil será eliminado permanentemente.", + "profileDeleteSelectedMessage" : "Los perfiles seleccionados serán eliminados permanentemente.", "profileExportDialogTitle" : "Elegir tipo de archivo", - "profileExportMessage" : "¿Qué tipo de archivo te gustaría exportar?", - "profileExportSelectedMessage" : "¿Qué tipo de archivo te gustaría exportar los perfiles seleccionados?", - "profileFailedSaveMessage": "El perfil no se pudo guardar", + "profileExportMessage" : "¿Qué tipo de archivo le gustaría exportar?", + "profileExportSelectedMessage" : "¿Qué tipo de archivo le gustaría exportar los perfiles seleccionados?", + "profileFailedSaveMessage": "Error al guardar el perfil", "profileFailedUnknownMessage" : "No se proporcionó ninguna razón", "profileName" : "Nombre del perfil", - "profileNameDescription" : "Este será el nombre de tus configuraciones", + "profileNameDescription" : "Este será el nombre de sus configuraciones", "profileRunningActionDeniedMessage" : "No se puede realizar esta acción mientras el perfil está en ejecución", "refresh" : "Actualizar", "relay" : "Relé", - "relayDescription" : "Puedes elegir entre nuestros RV existentes o crear uno nuevo", + "relayDescription" : "Puede elegir entre nuestros RV existentes o crear uno nuevo", "remoteHost" : "Host remoto", "remoteHostDescription" : "", "remotePort" : "Puerto remoto", "remotePortDescription" : "", - "resetAtsign" : "Restablecer atSign", - "selectExportFile": "Por favor selecciona un archivo para exportar a:", + "resetAtsign" : "Restablecer Atsign", + "selectExportFile": "Por favor seleccione un archivo para exportar a:", + "selectRootDomain" : "Seleccionar dominio raíz", "serviceMapping" : "Mapeo de servicios", "settings" : "Configuraciones", + "signout" : "Cerrar sesión", "sshStyle" : "Avanzado", "status" : "Estado", "submit" : "Enviar", - "validationErrorAtsignField" : "El campo debe ser un atSign válido que comience con @", + "validationErrorAtsignField" : "El campo debe ser un atsign válido que comience con @", "validationErrorDeviceNameField" : "El campo solo puede contener letras minúsculas, dígitos, guiones bajos.", "validationErrorEmptyField" : "El campo no puede estar vacío", "validationErrorLocalPortField" : "El campo debe estar entre 1024-65535", - "validationErrorLongField" : "El campo debe tener entre 1-36 caracteres", + "validationErrorLongField" : "El campo debe tener entre 1 y 36 caracteres", "validationErrorRemoteHostField" : "El campo debe ser un nombre de host parcial o completamente calificado o una dirección IP", "validationErrorRemotePortField" : "El campo debe estar entre 1-65535", "yaml" : "YAML" diff --git a/packages/dart/npt_flutter/lib/localization/app_pt.arb b/packages/dart/npt_flutter/lib/localization/app_pt.arb index 156ea401c..0ba04386e 100644 --- a/packages/dart/npt_flutter/lib/localization/app_pt.arb +++ b/packages/dart/npt_flutter/lib/localization/app_pt.arb @@ -1,4 +1,4 @@ -{ +{ "addNew" : "Adicionar Novo", "addNewProfile" : "Adicionar Novo Perfil", "advanced": "Avançado", @@ -8,6 +8,8 @@ "asiaPacific" : "Ásia-Pacífico", "atDirectory" : "AtDirectory", "atDirectorySubtitle" : "Selecione o domínio que você deseja usar", + "atsignDialogSubtitle" : "Por favor, selecione seu atSign", + "atsignDialogTitle" : "AtSign", "back" : "Voltar", "backupYourKey" : "Faça Backup da Sua Chave", "cancel" : "Cancelar", @@ -16,11 +18,12 @@ "dashboardView" : "Visualização do Painel", "defaultRelaySelection" : "Seleção de Relay Padrão", "delete" : "Excluir", - "deviceAtsign" : "Atsign do Dispositivo", + "deviceAtsign" : "Dispositivo atSign", "deviceAtsignDescription" : "Este é o atSign associado ao seu dispositivo", "deviceName": "Nome do Dispositivo", "deviceNameDescription" : "Este é o nome do seu dispositivo remoto", "discord" : "Discord", + "done": "Concluído", "edit" : "Editar", "email" : "Email", "emptyProfileMessage" : "Nenhum perfil encontrado\nCrie ou Importe um perfil para começar a usar NoPorts.", @@ -39,6 +42,7 @@ "localPortDescription" : "", "logs" : "Registros", "minimal" : "Simples", + "next" : "Próximo", "noAtsign" : "Sem Atsign", "noEmailClientAvailable" : "Nenhum cliente de email disponível", "noName" : "Sem Nome", @@ -50,8 +54,8 @@ "overrideAllProfile":"Substituir todos os perfis com a seleção de relay padrão", "preview" : "Pré-visualização", "privacyPolicy" : "Política de Privacidade", - "profileDeleteMessage" : "Este perfil será excluído permanentemente.", - "profileDeleteSelectedMessage" : "Os perfis selecionados serão excluídos permanentemente.", + "profileDeleteMessage" : "Este perfil será permanentemente excluído.", + "profileDeleteSelectedMessage" : "Os perfis selecionados serão permanentemente excluídos.", "profileExportDialogTitle" : "Escolha o Tipo de Arquivo", "profileExportMessage" : "Qual tipo de arquivo você gostaria de exportar?", "profileExportSelectedMessage" : "Qual tipo de arquivo você gostaria de exportar os perfis selecionados?", @@ -69,8 +73,10 @@ "remotePortDescription" : "", "resetAtsign" : "Redefinir Atsign", "selectExportFile": "Por favor, selecione um arquivo para exportar:", + "selectRootDomain" : "Selecione o Domínio Raiz", "serviceMapping" : "Mapeamento de Serviço", "settings" : "Configurações", + "signout" : "Sair", "sshStyle" : "Avançado", "status" : "Status", "submit" : "Enviar", @@ -82,4 +88,4 @@ "validationErrorRemoteHostField" : "O campo deve ser um nome de host parcial ou totalmente qualificado ou um endereço IP", "validationErrorRemotePortField" : "O campo deve estar entre 1-65535", "yaml" : "YAML" -} \ No newline at end of file + } \ No newline at end of file diff --git a/packages/dart/npt_flutter/lib/util/language.dart b/packages/dart/npt_flutter/lib/util/language.dart new file mode 100644 index 000000000..17ecbfb06 --- /dev/null +++ b/packages/dart/npt_flutter/lib/util/language.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; + +@JsonEnum() +enum Language { + @JsonValue("en") + english, + @JsonValue("es") + spanish, + @JsonValue("pt-br") + portuguese, +} + +extension LanguageExtension on Language { + Locale get locale { + switch (this) { + case Language.english: + return const Locale('en'); + case Language.spanish: + return const Locale('es'); + case Language.portuguese: + return const Locale('pt', 'BR'); + } + } + + String get displayName { + switch (this) { + case Language.english: + return 'English'; + case Language.spanish: + return 'Español'; + case Language.portuguese: + return 'Português'; + } + } +} + +class LanguageUtil { + // Static method to get the Language enum from a Locale. + // Returns English if the language code is not supported. + static Language getLanguageFromLocale(Locale locale) { + switch (locale.languageCode) { + case 'en': + return Language.english; + case 'es': + return Language.spanish; + case 'pt': + return Language.portuguese; + default: + return Language.english; + } + } +} 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 329a7a538..025c6705e 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -199,11 +199,9 @@ class CustomTextButton extends StatelessWidget { case CustomListTileType.feedback: return strings.feedback; case CustomListTileType.signOut: - // TODO Localize in the next PR. - return 'signOut'; + return strings.signout; case CustomListTileType.selectRootDomain: - // TODO Localize in the next PR. - return 'Select Root Domain'; + return strings.selectRootDomain; } } diff --git a/packages/dart/npt_flutter/pubspec.lock b/packages/dart/npt_flutter/pubspec.lock index 8a461d80d..12ced0cd1 100644 --- a/packages/dart/npt_flutter/pubspec.lock +++ b/packages/dart/npt_flutter/pubspec.lock @@ -1509,10 +1509,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.2.4" watcher: dependency: transitive description: From 0f8ccfac2202b8578246bcbfe47d551b75f0a1ee Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Fri, 11 Oct 2024 11:47:11 -0400 Subject: [PATCH 32/60] feat: support for cantonese and mandarin added. --- packages/dart/npt_flutter/lib/app.dart | 8 +- .../lib/localization/app_pt_BR.arb | 91 ++++++++++++++++++ .../npt_flutter/lib/localization/app_zh.arb | 93 +++++++++++++++++++ .../lib/localization/app_zh_Hans_CH.arb | 93 +++++++++++++++++++ .../lib/localization/app_zh_Hant_HK.arb | 91 ++++++++++++++++++ .../dart/npt_flutter/lib/util/language.dart | 34 +++++-- 6 files changed, 400 insertions(+), 10 deletions(-) create mode 100644 packages/dart/npt_flutter/lib/localization/app_pt_BR.arb create mode 100644 packages/dart/npt_flutter/lib/localization/app_zh.arb create mode 100644 packages/dart/npt_flutter/lib/localization/app_zh_Hans_CH.arb create mode 100644 packages/dart/npt_flutter/lib/localization/app_zh_Hant_HK.arb diff --git a/packages/dart/npt_flutter/lib/app.dart b/packages/dart/npt_flutter/lib/app.dart index 86f452919..715feeb46 100644 --- a/packages/dart/npt_flutter/lib/app.dart +++ b/packages/dart/npt_flutter/lib/app.dart @@ -107,11 +107,11 @@ class App extends StatelessWidget { return null; }, builder: (context, language) { - Locale defaultLocal = Language.english.locale; + Locale? defaultLocal; if (language == null) { - //check if the device language is supported or not use english as the default. - final deviceLocal = Locale(Platform.localeName.split('_').first); - defaultLocal = LanguageUtil.getLanguageFromLocale(deviceLocal).locale; + //check if the device language is supported, if not use english as the default. + + defaultLocal = LanguageUtil.getLanguageFromLocale(Locale(Platform.localeName)).locale; } return TrayManager( child: MaterialApp( diff --git a/packages/dart/npt_flutter/lib/localization/app_pt_BR.arb b/packages/dart/npt_flutter/lib/localization/app_pt_BR.arb new file mode 100644 index 000000000..0ba04386e --- /dev/null +++ b/packages/dart/npt_flutter/lib/localization/app_pt_BR.arb @@ -0,0 +1,91 @@ +{ + "addNew" : "Adicionar Novo", + "addNewProfile" : "Adicionar Novo Perfil", + "advanced": "Avançado", + "alertDialogTitle": "Você tem certeza?", + "allRightsReserved":"@ 2024 Atsign, Todos os Direitos Reservados", + "americas" : "Américas", + "asiaPacific" : "Ásia-Pacífico", + "atDirectory" : "AtDirectory", + "atDirectorySubtitle" : "Selecione o domínio que você deseja usar", + "atsignDialogSubtitle" : "Por favor, selecione seu atSign", + "atsignDialogTitle" : "AtSign", + "back" : "Voltar", + "backupYourKey" : "Faça Backup da Sua Chave", + "cancel" : "Cancelar", + "confirm" : "Confirmar", + "dashboard" : "Painel", + "dashboardView" : "Visualização do Painel", + "defaultRelaySelection" : "Seleção de Relay Padrão", + "delete" : "Excluir", + "deviceAtsign" : "Dispositivo atSign", + "deviceAtsignDescription" : "Este é o atSign associado ao seu dispositivo", + "deviceName": "Nome do Dispositivo", + "deviceNameDescription" : "Este é o nome do seu dispositivo remoto", + "discord" : "Discord", + "done": "Concluído", + "edit" : "Editar", + "email" : "Email", + "emptyProfileMessage" : "Nenhum perfil encontrado\nCrie ou Importe um perfil para começar a usar NoPorts.", + "enableLogging" : "Habilitar Registro", + "europe" : "Europa", + "export" : "Exportar", + "exportLogs" : "Exportar Registros", + "failed": "Falhou", + "faq" : "faq", + "feedback" : "Feedback", + "getStarted" : "Começar", + "import" : "Importar", + "json" : "JSON", + "language" : "Idioma", + "localPort" : "Porta Local", + "localPortDescription" : "", + "logs" : "Registros", + "minimal" : "Simples", + "next" : "Próximo", + "noAtsign" : "Sem Atsign", + "noEmailClientAvailable" : "Nenhum cliente de email disponível", + "noName" : "Sem Nome", + "noPorts" : "NoPorts", + "onboard" : "Onboard", + "onboardingError" : "Ocorreu um erro", + "onboardingSubTitle" : "para NoPorts Desktop", + "onboardingTitle" : "Bem-vindo", + "overrideAllProfile":"Substituir todos os perfis com a seleção de relay padrão", + "preview" : "Pré-visualização", + "privacyPolicy" : "Política de Privacidade", + "profileDeleteMessage" : "Este perfil será permanentemente excluído.", + "profileDeleteSelectedMessage" : "Os perfis selecionados serão permanentemente excluídos.", + "profileExportDialogTitle" : "Escolha o Tipo de Arquivo", + "profileExportMessage" : "Qual tipo de arquivo você gostaria de exportar?", + "profileExportSelectedMessage" : "Qual tipo de arquivo você gostaria de exportar os perfis selecionados?", + "profileFailedSaveMessage": "Falha ao salvar o perfil", + "profileFailedUnknownMessage" : "Nenhuma razão fornecida", + "profileName" : "Nome do Perfil", + "profileNameDescription" : "Este será o nome das suas configurações", + "profileRunningActionDeniedMessage" : "Não é possível realizar esta ação enquanto o perfil está em execução", + "refresh" : "Atualizar", + "relay" : "Relay", + "relayDescription" : "Você pode escolher entre nossos RVs existentes ou criar um novo", + "remoteHost" : "Host Remoto", + "remoteHostDescription" : "", + "remotePort" : "Porta Remota", + "remotePortDescription" : "", + "resetAtsign" : "Redefinir Atsign", + "selectExportFile": "Por favor, selecione um arquivo para exportar:", + "selectRootDomain" : "Selecione o Domínio Raiz", + "serviceMapping" : "Mapeamento de Serviço", + "settings" : "Configurações", + "signout" : "Sair", + "sshStyle" : "Avançado", + "status" : "Status", + "submit" : "Enviar", + "validationErrorAtsignField" : "O campo deve ser um atsign válido que começa com @", + "validationErrorDeviceNameField" : "O campo só pode conter letras minúsculas, dígitos, sublinhados.", + "validationErrorEmptyField" : "O campo não pode ficar em branco", + "validationErrorLocalPortField" : "O campo deve estar entre 1024-65535", + "validationErrorLongField" : "O campo deve ter entre 1-36 caracteres", + "validationErrorRemoteHostField" : "O campo deve ser um nome de host parcial ou totalmente qualificado ou um endereço IP", + "validationErrorRemotePortField" : "O campo deve estar entre 1-65535", + "yaml" : "YAML" + } \ No newline at end of file diff --git a/packages/dart/npt_flutter/lib/localization/app_zh.arb b/packages/dart/npt_flutter/lib/localization/app_zh.arb new file mode 100644 index 000000000..2b9807cce --- /dev/null +++ b/packages/dart/npt_flutter/lib/localization/app_zh.arb @@ -0,0 +1,93 @@ +{ + + "addNew" : "新增", + "addNewProfile" : "新增配置文件", + "advanced": "高级", + "alertDialogTitle": "你确定吗?", + "allRightsReserved":"@ 2024 Atsign,版权所有", + "americas" : "美洲", + "asiaPacific" : "亚太地区", + "atDirectory" : "AtDirectory", + "atDirectorySubtitle" : "选择您要使用的域", + "atsignDialogSubtitle" : "请选择您的atSign", + "atsignDialogTitle" : "AtSign", + "back" : "返回", + "backupYourKey" : "备份您的密钥", + "cancel" : "取消", + "confirm" : "确认", + "dashboard" : "仪表板", + "dashboardView" : "仪表板视图", + "defaultRelaySelection" : "默认中继选择", + "delete" : "删除", + "deviceAtsign" : "设备atSign", + "deviceAtsignDescription" : "这是与您设备关联的atSign", + "deviceName": "设备名称", + "deviceNameDescription" : "这是您的远程设备的名称", + "discord" : "Discord", + "done": "完成", + "edit" : "编辑", + "email" : "电子邮件", + "emptyProfileMessage" : "未找到配置文件\n创建或导入配置文件以开始使用NoPorts。", + "enableLogging" : "启用日志记录", + "europe" : "欧洲", + "export" : "导出", + "exportLogs" : "导出日志", + "failed": "失败", + "faq" : "常见问题", + "feedback" : "反馈", + "getStarted" : "开始", + "import" : "导入", + "json" : "JSON", + "language" : "语言", + "localPort" : "本地端口", + "localPortDescription" : "", + "logs" : "日志", + "minimal" : "简单", + "next" : "下一步", + "noAtsign" : "没有Atsign", + "noEmailClientAvailable" : "没有可用的电子邮件客户端", + "noName" : "无名称", + "noPorts" : "NoPorts", + "onboard" : "入职", + "onboardingError" : "发生错误", + "onboardingSubTitle" : "到NoPorts桌面", + "onboardingTitle" : "欢迎", + "overrideAllProfile":"用默认中继选择覆盖所有配置文件", + "preview" : "预览", + "privacyPolicy" : "隐私政策", + "profileDeleteMessage" : "此配置文件将被永久删除。", + "profileDeleteSelectedMessage" : "选定的配置文件将被永久删除。", + "profileExportDialogTitle" : "选择文件类型", + "profileExportMessage" : "您想导出为哪种文件类型?", + "profileExportSelectedMessage" : "您想将选定的配置文件导出为哪种文件类型?", + "profileFailedSaveMessage": "配置文件保存失败", + "profileFailedUnknownMessage" : "未提供原因", + "profileName" : "配置文件名称", + "profileNameDescription" : "这将是您的配置名称", + "profileRunningActionDeniedMessage" : "配置文件运行时无法执行此操作", + "refresh" : "刷新", + "relay" : "中继", + "relayDescription" : "您可以从我们现有的RV中选择或创建一个新的", + "remoteHost" : "远程主机", + "remoteHostDescription" : "", + "remotePort" : "远程端口", + "remotePortDescription" : "", + "resetAtsign" : "重置Atsign", + "selectExportFile": "请选择要导出的文件:", + "selectRootDomain" : "选择根域", + "serviceMapping" : "服务映射", + "settings" : "设置", + "signout" : "登出", + "sshStyle" : "高级", + "status" : "状态", + "submit" : "提交", + "validationErrorAtsignField" : "字段必须是以@开头的有效atSign", + "validationErrorDeviceNameField" : "字段只能包含小写字母、数字、下划线。", + "validationErrorEmptyField" : "字段不能为空", + "validationErrorLocalPortField" : "字段必须在1024-65535之间", + "validationErrorLongField" : "字段长度必须为1-36个字符", + "validationErrorRemoteHostField" : "字段必须是部分或完全合格的主机名或IP地址", + "validationErrorRemotePortField" : "字段必须在1-65535之间", + "yaml" : "YAML" + +} diff --git a/packages/dart/npt_flutter/lib/localization/app_zh_Hans_CH.arb b/packages/dart/npt_flutter/lib/localization/app_zh_Hans_CH.arb new file mode 100644 index 000000000..1f5b2528b --- /dev/null +++ b/packages/dart/npt_flutter/lib/localization/app_zh_Hans_CH.arb @@ -0,0 +1,93 @@ + +{ + "addNew" : "新增", + "addNewProfile" : "新增配置文件", + "advanced": "高级", + "alertDialogTitle": "你确定吗?", + "allRightsReserved":"@ 2024 Atsign,版权所有", + "americas" : "美洲", + "asiaPacific" : "亚太地区", + "atDirectory" : "AtDirectory", + "atDirectorySubtitle" : "选择您要使用的域", + "atsignDialogSubtitle" : "请选择您的atSign", + "atsignDialogTitle" : "AtSign", + "back" : "返回", + "backupYourKey" : "备份您的密钥", + "cancel" : "取消", + "confirm" : "确认", + "dashboard" : "仪表板", + "dashboardView" : "仪表板视图", + "defaultRelaySelection" : "默认中继选择", + "delete" : "删除", + "deviceAtsign" : "设备atSign", + "deviceAtsignDescription" : "这是与您设备关联的atSign", + "deviceName": "设备名称", + "deviceNameDescription" : "这是您的远程设备的名称", + "discord" : "Discord", + "done": "完成", + "edit" : "编辑", + "email" : "电子邮件", + "emptyProfileMessage" : "未找到配置文件\n创建或导入配置文件以开始使用NoPorts。", + "enableLogging" : "启用日志记录", + "europe" : "欧洲", + "export" : "导出", + "exportLogs" : "导出日志", + "failed": "失败", + "faq" : "常见问题", + "feedback" : "反馈", + "getStarted" : "开始", + "import" : "导入", + "json" : "JSON", + "language" : "语言", + "localPort" : "本地端口", + "localPortDescription" : "", + "logs" : "日志", + "minimal" : "简单", + "next" : "下一步", + "noAtsign" : "没有Atsign", + "noEmailClientAvailable" : "没有可用的电子邮件客户端", + "noName" : "无名称", + "noPorts" : "NoPorts", + "onboard" : "入职", + "onboardingError" : "发生错误", + "onboardingSubTitle" : "到NoPorts桌面", + "onboardingTitle" : "欢迎", + "overrideAllProfile":"用默认中继选择覆盖所有配置文件", + "preview" : "预览", + "privacyPolicy" : "隐私政策", + "profileDeleteMessage" : "此配置文件将被永久删除。", + "profileDeleteSelectedMessage" : "选定的配置文件将被永久删除。", + "profileExportDialogTitle" : "选择文件类型", + "profileExportMessage" : "您想导出为哪种文件类型?", + "profileExportSelectedMessage" : "您想将选定的配置文件导出为哪种文件类型?", + "profileFailedSaveMessage": "配置文件保存失败", + "profileFailedUnknownMessage" : "未提供原因", + "profileName" : "配置文件名称", + "profileNameDescription" : "这将是您的配置名称", + "profileRunningActionDeniedMessage" : "配置文件运行时无法执行此操作", + "refresh" : "刷新", + "relay" : "中继", + "relayDescription" : "您可以从我们现有的RV中选择或创建一个新的", + "remoteHost" : "远程主机", + "remoteHostDescription" : "", + "remotePort" : "远程端口", + "remotePortDescription" : "", + "resetAtsign" : "重置Atsign", + "selectExportFile": "请选择要导出的文件:", + "selectRootDomain" : "选择根域", + "serviceMapping" : "服务映射", + "settings" : "设置", + "signout" : "登出", + "sshStyle" : "高级", + "status" : "状态", + "submit" : "提交", + "validationErrorAtsignField" : "字段必须是以@开头的有效atSign", + "validationErrorDeviceNameField" : "字段只能包含小写字母、数字、下划线。", + "validationErrorEmptyField" : "字段不能为空", + "validationErrorLocalPortField" : "字段必须在1024-65535之间", + "validationErrorLongField" : "字段长度必须为1-36个字符", + "validationErrorRemoteHostField" : "字段必须是部分或完全合格的主机名或IP地址", + "validationErrorRemotePortField" : "字段必须在1-65535之间", + "yaml" : "YAML" +} + diff --git a/packages/dart/npt_flutter/lib/localization/app_zh_Hant_HK.arb b/packages/dart/npt_flutter/lib/localization/app_zh_Hant_HK.arb new file mode 100644 index 000000000..760dc83d6 --- /dev/null +++ b/packages/dart/npt_flutter/lib/localization/app_zh_Hant_HK.arb @@ -0,0 +1,91 @@ +{ + "addNew" : "新增", + "addNewProfile" : "新增配置文件", + "advanced": "高級", + "alertDialogTitle": "你確定嗎?", + "allRightsReserved":"@ 2024 Atsign,版權所有", + "americas" : "美洲", + "asiaPacific" : "亞太地區", + "atDirectory" : "AtDirectory", + "atDirectorySubtitle" : "選擇你想使用的域名", + "atsignDialogSubtitle" : "請選擇你的atSign", + "atsignDialogTitle" : "AtSign", + "back" : "返回", + "backupYourKey" : "備份你的密鑰", + "cancel" : "取消", + "confirm" : "確認", + "dashboard" : "儀表板", + "dashboardView" : "儀表板視圖", + "defaultRelaySelection" : "默認中繼選擇", + "delete" : "刪除", + "deviceAtsign" : "設備atSign", + "deviceAtsignDescription" : "這是與你設備相關聯的atSign", + "deviceName": "設備名稱", + "deviceNameDescription" : "這是你的遠程設備的名稱", + "discord" : "Discord", + "done": "完成", + "edit" : "編輯", + "email" : "電子郵件", + "emptyProfileMessage" : "未找到配置文件\n創建或導入配置文件以開始使用NoPorts。", + "enableLogging" : "啟用日誌記錄", + "europe" : "歐洲", + "export" : "導出", + "exportLogs" : "導出日誌", + "failed": "失敗", + "faq" : "常見問題", + "feedback" : "反饋", + "getStarted" : "開始", + "import" : "導入", + "json" : "JSON", + "language" : "語言", + "localPort" : "本地端口", + "localPortDescription" : "", + "logs" : "日誌", + "minimal" : "簡單", + "next" : "下一步", + "noAtsign" : "沒有Atsign", + "noEmailClientAvailable" : "沒有可用的電子郵件客戶端", + "noName" : "無名", + "noPorts" : "NoPorts", + "onboard" : "入門", + "onboardingError" : "發生錯誤", + "onboardingSubTitle" : "到NoPorts桌面", + "onboardingTitle" : "歡迎", + "overrideAllProfile":"用默認中繼選擇覆蓋所有配置文件", + "preview" : "預覽", + "privacyPolicy" : "隱私政策", + "profileDeleteMessage" : "此配置文件將被永久刪除。", + "profileDeleteSelectedMessage" : "選定的配置文件將被永久刪除。", + "profileExportDialogTitle" : "選擇文件類型", + "profileExportMessage" : "你想導出為什麼文件類型?", + "profileExportSelectedMessage" : "你想將選定的配置文件導出為什麼文件類型?", + "profileFailedSaveMessage": "配置文件保存失敗", + "profileFailedUnknownMessage" : "未提供原因", + "profileName" : "配置文件名稱", + "profileNameDescription" : "這將是你的配置名稱", + "profileRunningActionDeniedMessage" : "配置文件運行時無法執行此操作", + "refresh" : "刷新", + "relay" : "中繼", + "relayDescription" : "你可以從我們現有的RV中選擇或創建一個新的", + "remoteHost" : "遠程主機", + "remoteHostDescription" : "", + "remotePort" : "遠程端口", + "remotePortDescription" : "", + "resetAtsign" : "重置Atsign", + "selectExportFile": "請選擇要導出的文件:", + "selectRootDomain" : "選擇根域", + "serviceMapping" : "服務映射", + "settings" : "設置", + "signout" : "登出", + "sshStyle" : "高級", + "status" : "狀態", + "submit" : "提交", + "validationErrorAtsignField" : "字段必須是以@開頭的有效atSign", + "validationErrorDeviceNameField" : "字段只能包含小寫字母、數字、下劃線。", + "validationErrorEmptyField" : "字段不能為空", + "validationErrorLocalPortField" : "字段必須在1024-65535之間", + "validationErrorLongField" : "字段必須為1-36個字符長", + "validationErrorRemoteHostField" : "字段必須是部分或完全合格的主機名或IP地址", + "validationErrorRemotePortField" : "字段必須在1-65535之間", + "yaml" : "YAML" +} diff --git a/packages/dart/npt_flutter/lib/util/language.dart b/packages/dart/npt_flutter/lib/util/language.dart index 17ecbfb06..e2543a82a 100644 --- a/packages/dart/npt_flutter/lib/util/language.dart +++ b/packages/dart/npt_flutter/lib/util/language.dart @@ -9,6 +9,10 @@ enum Language { spanish, @JsonValue("pt-br") portuguese, + @JsonValue("cn") + mandarin, + @JsonValue("hk") + cantonese, } extension LanguageExtension on Language { @@ -20,6 +24,10 @@ extension LanguageExtension on Language { return const Locale('es'); case Language.portuguese: return const Locale('pt', 'BR'); + case Language.cantonese: + return const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK'); + case Language.mandarin: + return const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'); } } @@ -31,6 +39,10 @@ extension LanguageExtension on Language { return 'Español'; case Language.portuguese: return 'Português'; + case Language.cantonese: + return '廣東話'; + case Language.mandarin: + return '普通话'; } } } @@ -40,14 +52,24 @@ class LanguageUtil { // Returns English if the language code is not supported. static Language getLanguageFromLocale(Locale locale) { switch (locale.languageCode) { - case 'en': - return Language.english; - case 'es': - return Language.spanish; - case 'pt': + case 'pt_BR': return Language.portuguese; + case 'zh_Hans_CH': + return Language.mandarin; + case 'zh_Hant_HK': + return Language.cantonese; default: - return Language.english; + if (locale.languageCode.startsWith('zh_Hans')) { + return Language.mandarin; + } else if (locale.languageCode.startsWith('zh_Hant')) { + return Language.cantonese; + } else if (locale.languageCode.startsWith('pt')) { + return Language.portuguese; + } else if (locale.languageCode.startsWith('es')) { + return Language.spanish; + } else { + return Language.english; + } } } } From 21eedb44e76406510f548e6e4e3c80e15f6c027e Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 14 Oct 2024 07:17:47 -0400 Subject: [PATCH 33/60] fix: updates made due to rebase. --- .../npt_flutter/lib/localization/app_en.arb | 10 +--------- packages/dart/npt_flutter/lib/util/relay.dart | 17 ----------------- 2 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 packages/dart/npt_flutter/lib/util/relay.dart diff --git a/packages/dart/npt_flutter/lib/localization/app_en.arb b/packages/dart/npt_flutter/lib/localization/app_en.arb index 2bcd0396f..6da85cd61 100644 --- a/packages/dart/npt_flutter/lib/localization/app_en.arb +++ b/packages/dart/npt_flutter/lib/localization/app_en.arb @@ -86,14 +86,6 @@ "validationErrorLocalPortField" : "Field must be between 1024-65535", "validationErrorLongField" : "Field must be 1-36 characters long", "validationErrorRemoteHostField" : "Field must be partially or fully qualified hostname or an IP address", - "profileRunningActionDeniedMessage" : "Cannot perform this action while profile is running", - "emptyProfileMessage" : "No profiles found\nCreate or Import a profile to start using NoPorts.", - "onboardingTitle" : "Welcome", - "onboardingSubTitle" : "to NoPorts Desktop", - "getStarted" : "Get Started", - "atDirectory" : "AtDirectory", - "atDirectorySubtitle" : "Select the domain you want to use", - "onboard" : "Onboard", - "onboardingError" : "An error has occurred", + "validationErrorRemotePortField" : "Field must be between 1-65535", "yaml" : "YAML" } diff --git a/packages/dart/npt_flutter/lib/util/relay.dart b/packages/dart/npt_flutter/lib/util/relay.dart deleted file mode 100644 index 4df31f2f9..000000000 --- a/packages/dart/npt_flutter/lib/util/relay.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter/material.dart'; - -class RelayUtil { - static Map getRelayDisplayNameMap(BuildContext context) { - final strings = AppLocalizations.of(context)!; - return { - "@rv_am": strings.rvAmDisplayName, - "@rv_ap": strings.rvApDisplayName, - "@rv_eu": strings.rvEuDisplayName, - }; - } - - static List getRelayAtsignList() { - return ["@rv_am", "@rv_ap", "@rv_eu"]; - } -} From 1d3d40a6bc5c896f26e53d90f7aa31ea704d366a Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 30 Sep 2024 08:14:22 -0400 Subject: [PATCH 34/60] fix: dashboard page resizes based on user device size. --- .../dart/npt_flutter/lib/styles/sizes.dart | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/dart/npt_flutter/lib/styles/sizes.dart b/packages/dart/npt_flutter/lib/styles/sizes.dart index 9ab9e4bb4..9d3d0c8f3 100644 --- a/packages/dart/npt_flutter/lib/styles/sizes.dart +++ b/packages/dart/npt_flutter/lib/styles/sizes.dart @@ -134,14 +134,11 @@ 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!); @@ -158,20 +155,16 @@ 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; } From 10628a0988d8c46b755c2717a9fec0f49620de2e Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Fri, 4 Oct 2024 20:22:07 -0400 Subject: [PATCH 35/60] feat: onboarding flow added --- packages/dart/npt_flutter/lib/app.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/dart/npt_flutter/lib/app.dart b/packages/dart/npt_flutter/lib/app.dart index 715feeb46..57959b923 100644 --- a/packages/dart/npt_flutter/lib/app.dart +++ b/packages/dart/npt_flutter/lib/app.dart @@ -99,6 +99,11 @@ class App extends StatelessWidget { BlocProvider( create: (ctx) => FavoriteBloc(ctx.read()), ), + + // A bloc which manages the atDirectory state + BlocProvider( + create: (_) => AtDirectoryCubit(), + ), ], child: BlocSelector(selector: (state) { if (state is SettingsLoadedState) { From 9618548b5d527dab56891968d6a6b72731d59720 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 7 Oct 2024 15:20:37 -0400 Subject: [PATCH 36/60] feat: add atsign manager utility functions --- packages/dart/npt_flutter/pubspec.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dart/npt_flutter/pubspec.lock b/packages/dart/npt_flutter/pubspec.lock index 12ced0cd1..8a461d80d 100644 --- a/packages/dart/npt_flutter/pubspec.lock +++ b/packages/dart/npt_flutter/pubspec.lock @@ -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: From 7acd9a977e639d115f7da340677d651dce64bc41 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 7 Oct 2024 16:25:07 -0400 Subject: [PATCH 37/60] feat: add pre_offboard utility function --- packages/dart/npt_flutter/lib/app.dart | 5 ----- .../dart/npt_flutter/lib/styles/sizes.dart | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/dart/npt_flutter/lib/app.dart b/packages/dart/npt_flutter/lib/app.dart index 57959b923..715feeb46 100644 --- a/packages/dart/npt_flutter/lib/app.dart +++ b/packages/dart/npt_flutter/lib/app.dart @@ -99,11 +99,6 @@ class App extends StatelessWidget { BlocProvider( create: (ctx) => FavoriteBloc(ctx.read()), ), - - // A bloc which manages the atDirectory state - BlocProvider( - create: (_) => AtDirectoryCubit(), - ), ], child: BlocSelector(selector: (state) { if (state is SettingsLoadedState) { diff --git a/packages/dart/npt_flutter/lib/styles/sizes.dart b/packages/dart/npt_flutter/lib/styles/sizes.dart index 9d3d0c8f3..9ab9e4bb4 100644 --- a/packages/dart/npt_flutter/lib/styles/sizes.dart +++ b/packages/dart/npt_flutter/lib/styles/sizes.dart @@ -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; } From 95027c04d5fc4e8231a30db348d90e558d3792bc Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Tue, 8 Oct 2024 19:23:11 -0400 Subject: [PATCH 38/60] feat: root domain removed from onboarding flow. --- .../onboarding/view/onboaring_view.dart | 57 ------------------- .../lib/widgets/custom_text_button.dart | 6 ++ 2 files changed, 6 insertions(+), 57 deletions(-) delete mode 100644 packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart diff --git a/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart b/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart deleted file mode 100644 index 2770041e8..000000000 --- a/packages/dart/npt_flutter/lib/features/onboarding/view/onboaring_view.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:npt_flutter/features/onboarding/widgets/onboarding_button.dart'; -import 'package:npt_flutter/styles/sizes.dart'; -import 'package:npt_flutter/widgets/custom_text_button.dart'; - -class OnboardingView extends StatelessWidget { - const OnboardingView({super.key}); - - @override - Widget build(BuildContext context) { - final strings = AppLocalizations.of(context)!; - final textTheme = Theme.of(context).textTheme; - return Stack( - children: [ - Positioned.fill( - child: SvgPicture.asset( - 'assets/onboarding_bg.svg', - fit: BoxFit.cover, - ), - ), - Align( - child: Column( - children: [ - gapH108, - Text( - strings.onboardingTitle, - style: textTheme.headlineLarge!.copyWith( - color: Colors.black, - ), - ), - Text(strings.onboardingSubTitle, style: textTheme.headlineMedium), - gapH20, - const OnboardingButton(), - ], - ), - ), - const Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.only( - bottom: Sizes.p44, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - CustomTextButton.selectRootDomain(), - CustomTextButton.resetAtsign(), - ], - ), - ), - ) - ], - ); - } -} 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 025c6705e..afdce37f4 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -177,6 +177,12 @@ class CustomTextButton extends StatelessWidget { builder: (BuildContext context) => const AtDirectoryDialog(), ); break; + case CustomListTileType.selectRootDomain: + await showDialog( + context: context, + builder: (BuildContext context) => const AtDirectoryDialog(), + ); + break; } } From 3f85513ae4945e398fa589133a8469284f999a24 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Tue, 8 Oct 2024 20:10:32 -0400 Subject: [PATCH 39/60] chore: localize relay selection --- .../npt_flutter/lib/localization/app_en.arb | 3 +++ packages/dart/npt_flutter/lib/util/relay.dart | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 packages/dart/npt_flutter/lib/util/relay.dart diff --git a/packages/dart/npt_flutter/lib/localization/app_en.arb b/packages/dart/npt_flutter/lib/localization/app_en.arb index 6da85cd61..5f62fa06e 100644 --- a/packages/dart/npt_flutter/lib/localization/app_en.arb +++ b/packages/dart/npt_flutter/lib/localization/app_en.arb @@ -72,6 +72,9 @@ "remotePort" : "Remote Port", "remotePortDescription" : "", "resetAtsign" : "Reset Atsign", + "rvAmDisplayName" : "Americas", + "rvApDisplayName" : "Asia-Pacific", + "rvEuDisplayName" : "Europe", "selectExportFile": "Please select a file to export to:", "selectRootDomain" : "Select Root Domain", "serviceMapping" : "Service Mapping", diff --git a/packages/dart/npt_flutter/lib/util/relay.dart b/packages/dart/npt_flutter/lib/util/relay.dart new file mode 100644 index 000000000..4df31f2f9 --- /dev/null +++ b/packages/dart/npt_flutter/lib/util/relay.dart @@ -0,0 +1,17 @@ +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter/material.dart'; + +class RelayUtil { + static Map getRelayDisplayNameMap(BuildContext context) { + final strings = AppLocalizations.of(context)!; + return { + "@rv_am": strings.rvAmDisplayName, + "@rv_ap": strings.rvApDisplayName, + "@rv_eu": strings.rvEuDisplayName, + }; + } + + static List getRelayAtsignList() { + return ["@rv_am", "@rv_ap", "@rv_eu"]; + } +} From 4a8c7dda50344a3a66ad6cb3cfb883acf58ecb10 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Wed, 16 Oct 2024 16:14:13 -0400 Subject: [PATCH 40/60] fix: onboarding wrapper edge cases --- packages/dart/npt_flutter/lib/app.dart | 8 +- packages/dart/npt_flutter/lib/constants.dart | 1 - .../onboarding/cubit/at_directory_cubit.dart | 12 -- .../onboarding/cubit/onboarding_cubit.dart | 51 ++++++- .../onboarding/cubit/onboarding_state.dart | 30 ---- .../onboarding/util/post_onboard.dart | 20 ++- .../onboarding/util/pre_offboard.dart | 2 +- .../onboarding/view/onboarding_view.dart | 1 - .../widgets/at_directory_dialog.dart | 21 --- .../widgets/at_directory_selector.dart | 73 ++++++++++ .../onboarding/widgets/atsign_dialog.dart | 21 --- .../onboarding/widgets/atsign_selector.dart | 131 +++++++----------- .../onboarding_at_directory_selector.dart | 78 ----------- .../onboarding/widgets/onboarding_button.dart | 97 ++++++++++--- .../onboarding/widgets/onboarding_dialog.dart | 29 ++-- .../features/profile/bloc/profile_bloc.dart | 54 +++----- .../lib/features/profile/models/profile.dart | 8 +- .../tray_manager/cubit/tray_cubit.dart | 22 ++- .../lib/widgets/custom_text_button.dart | 11 +- 19 files changed, 303 insertions(+), 367 deletions(-) delete mode 100644 packages/dart/npt_flutter/lib/features/onboarding/cubit/at_directory_cubit.dart delete mode 100644 packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_state.dart delete mode 100644 packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart create mode 100644 packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_selector.dart delete mode 100644 packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart delete mode 100644 packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_at_directory_selector.dart diff --git a/packages/dart/npt_flutter/lib/app.dart b/packages/dart/npt_flutter/lib/app.dart index 715feeb46..bbe67a1f8 100644 --- a/packages/dart/npt_flutter/lib/app.dart +++ b/packages/dart/npt_flutter/lib/app.dart @@ -4,7 +4,6 @@ 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/features/features.dart'; -import 'package:npt_flutter/features/onboarding/cubit/at_directory_cubit.dart'; import 'package:npt_flutter/routes.dart'; import 'package:npt_flutter/styles/app_theme.dart'; import 'package:npt_flutter/util/language.dart'; @@ -48,16 +47,11 @@ class App extends StatelessWidget { create: (_) => LogsCubit(), ), - /// A cubit which manages the onboarding status + // A bloc which manages the atDirectory state BlocProvider( 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 diff --git a/packages/dart/npt_flutter/lib/constants.dart b/packages/dart/npt_flutter/lib/constants.dart index 82fda7132..f16a5c1f1 100644 --- a/packages/dart/npt_flutter/lib/constants.dart +++ b/packages/dart/npt_flutter/lib/constants.dart @@ -1,5 +1,4 @@ class Constants { - static const rootDomain = 'root.atsign.org'; static String? get namespace => 'noports'; // TODO: issue & secure API key properly static String? get appAPIKey => 'asdf'; 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 deleted file mode 100644 index 85177e8ba..000000000 --- a/packages/dart/npt_flutter/lib/features/onboarding/cubit/at_directory_cubit.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:npt_flutter/features/logging/models/logging_bloc.dart'; -import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; - -class AtDirectoryCubit extends LoggingCubit { - AtDirectoryCubit() : super(const AtsignInformation(atSign: '', rootDomain: 'root.atsign.org')); - - void setRootDomain(String rootDomain) => emit(AtsignInformation(atSign: state.atSign, rootDomain: rootDomain)); - String getRootDomain() => (state.rootDomain); - - void setAtSign(String atSign) => emit(AtsignInformation(atSign: atSign, rootDomain: state.rootDomain)); - String getAtSign() => (state.atSign); -} diff --git a/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_cubit.dart b/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_cubit.dart index 65f4a9bbc..d23812dd4 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_cubit.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_cubit.dart @@ -1,10 +1,49 @@ -import 'package:npt_flutter/features/logging/logging.dart'; - -part 'onboarding_state.dart'; +import 'package:npt_flutter/features/logging/models/logging_bloc.dart'; +import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; class OnboardingCubit extends LoggingCubit { - OnboardingCubit() : super(const OnboardingInitial()); + OnboardingCubit() + : super(const OnboardingState(atSign: '', status: OnboardingStatus.offboarded, rootDomain: 'root.atsign.org')); + + void setRootDomain(String rootDomain) => + emit(OnboardingState(atSign: state.atSign, status: state.status, rootDomain: rootDomain)); + String getRootDomain() => (state.rootDomain); + + void setAtSign(String atSign) => + emit(OnboardingState(atSign: atSign, status: state.status, rootDomain: state.rootDomain)); + String getAtSign() => (state.atSign); + + void setStatus(OnboardingStatus status) => + emit(OnboardingState(atSign: state.atSign, status: status, rootDomain: state.rootDomain)); + OnboardingStatus getStatus() => (state.status); + + /// If state is passed, all other arguments are ignored + /// If individual arguments (atsign, rootDomain, status) are passed + /// then they will override the value of the current state + /// keeping unspecified values the same + void setState({ + String? atSign, + OnboardingStatus? status, + String? rootDomain, + }) => + emit(OnboardingState( + atSign: atSign ?? this.state.atSign, + status: status ?? this.state.status, + rootDomain: rootDomain ?? this.state.rootDomain, + )); +} + +enum OnboardingStatus { onboarded, offboarded } + +class OnboardingState extends AtsignInformation { + final OnboardingStatus status; + const OnboardingState({required this.status, required super.atSign, required super.rootDomain}); + + @override + List get props => [atSign, status, rootDomain]; - void onboard(String atSign) => emit(Onboarded(atSign)); - void offboard() => emit(const OnboardingInitial()); + @override + String toString() { + return 'OnboardingState($atSign, ${status.name}, $rootDomain)'; + } } diff --git a/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_state.dart b/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_state.dart deleted file mode 100644 index 760bf8f38..000000000 --- a/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_state.dart +++ /dev/null @@ -1,30 +0,0 @@ -part of 'onboarding_cubit.dart'; - -sealed class OnboardingState extends Loggable { - const OnboardingState(); - - @override - List get props => []; -} - -final class OnboardingInitial extends OnboardingState { - const OnboardingInitial(); - - @override - String toString() { - return 'OnboardingInitial'; - } -} - -final class Onboarded extends OnboardingState { - final String atSign; - const Onboarded(this.atSign); - - @override - List get props => [atSign]; - - @override - String toString() { - return 'Onboarded($atSign)'; - } -} diff --git a/packages/dart/npt_flutter/lib/features/onboarding/util/post_onboard.dart b/packages/dart/npt_flutter/lib/features/onboarding/util/post_onboard.dart index 16777c09b..4e9ebeee8 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/util/post_onboard.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/util/post_onboard.dart @@ -2,16 +2,14 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:npt_flutter/app.dart'; import 'package:npt_flutter/features/features.dart'; -Future postOnboard(String atSign) async { - App.navState.currentContext?.read().onboard(atSign); +Future postOnboard(String atSign, String rootDomain) async { + App.navState.currentContext?.read().setState( + atSign: atSign, + rootDomain: rootDomain, + status: OnboardingStatus.onboarded, + ); // Start loading application data in the background as soon as we have an atClient - App.navState.currentContext - ?.read() - .add(const ProfileListLoadEvent()); - App.navState.currentContext - ?.read() - .add(const SettingsLoadEvent()); - App.navState.currentContext - ?.read() - .add(const FavoriteLoadEvent()); + App.navState.currentContext?.read().add(const ProfileListLoadEvent()); + App.navState.currentContext?.read().add(const SettingsLoadEvent()); + App.navState.currentContext?.read().add(const FavoriteLoadEvent()); } 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 5661b47e5..a26eb76e8 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 @@ -14,7 +14,7 @@ Future preSignout() async { App.navState.currentContext?.read().clearAll(); App.navState.currentContext?.read().clearAll(); App.navState.currentContext?.read().clear(); - App.navState.currentContext?.read().offboard(); + App.navState.currentContext?.read().setStatus(OnboardingStatus.offboarded); // - Reset the tray icon App.navState.currentContext?.read().initialize(); return true; diff --git a/packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart b/packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart index 1f5b5db67..b9581ba05 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart @@ -47,7 +47,6 @@ class OnboardingView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ CustomTextButton.resetAtsign(), - CustomTextButton.selectRootDomain(), ], ), ), diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart deleted file mode 100644 index cd27c8ebf..000000000 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_dialog.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:npt_flutter/features/onboarding/widgets/onboarding_at_directory_selector.dart'; -import 'package:npt_flutter/features/onboarding/widgets/onboarding_dialog.dart'; - -class AtDirectoryDialog extends StatelessWidget { - const AtDirectoryDialog({super.key}); - - @override - Widget build(BuildContext context) { - final strings = AppLocalizations.of(context)!; - return OnboardingDialog( - title: strings.atDirectory, - subtitle: strings.atDirectorySubtitle, - successButtonText: strings.done, - children: [ - OnboardingAtDirectorySelector(), - ], - ); - } -} diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_selector.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_selector.dart new file mode 100644 index 000000000..ef2a22f60 --- /dev/null +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/at_directory_selector.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:npt_flutter/constants.dart'; +import 'package:npt_flutter/features/onboarding/cubit/onboarding_cubit.dart'; +import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; + +class AtDirectorySelector extends StatefulWidget { + const AtDirectorySelector({ + required this.options, + super.key, + }); + final Map options; + + @override + State createState() => _AtDirectorySelectorState(); +} + +class _AtDirectorySelectorState extends State { + final focusNode = FocusNode(); + final controller = TextEditingController(); + + @override + Widget build(BuildContext context) { + final rootDomains = Constants.getRootDomains(context); + return BlocBuilder(builder: (context, state) { + controller.value = TextEditingValue(text: state.rootDomain); + return TextFormField( + enabled: !widget.options.containsKey(state.atSign), + controller: controller, + onChanged: (rootDomain) { + context.read().setRootDomain(rootDomain); + }, + decoration: InputDecoration( + /// This menuAnchor is a dropdown button that allows you to quickly select + /// existing values from [options] + suffixIcon: rootDomains.isNotEmpty + ? Directionality( + textDirection: TextDirection.rtl, + child: MenuAnchor( + style: const MenuStyle(alignment: AlignmentDirectional.bottomStart), + childFocusNode: focusNode, + menuChildren: rootDomains.entries.map((e) { + return Directionality( + textDirection: TextDirection.ltr, + child: MenuItemButton( + child: Text(e.value), + onPressed: () { + context.read().setRootDomain(e.key); + }, + ), + ); + }).toList(), + builder: (BuildContext context, MenuController controller, Widget? child) { + return IconButton( + focusNode: focusNode, + onPressed: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + icon: const Icon(Icons.arrow_drop_down), + ); + }, + ), + ) + : null, + ), + ); + }); + } +} diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart deleted file mode 100644 index a60db7800..000000000 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_dialog.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:npt_flutter/features/onboarding/widgets/atsign_selector.dart'; -import 'package:npt_flutter/features/onboarding/widgets/onboarding_dialog.dart'; - -class AtSignDialog extends StatelessWidget { - const AtSignDialog({super.key}); - - @override - Widget build(BuildContext context) { - final strings = AppLocalizations.of(context)!; - return OnboardingDialog( - title: strings.atsignDialogTitle, - subtitle: strings.atsignDialogSubtitle, - successButtonText: strings.next, - children: const [ - AtsignSelector(), - ], - ); - } -} diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart index 4e311e074..92464dd73 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/atsign_selector.dart @@ -1,101 +1,74 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:npt_flutter/features/onboarding/cubit/at_directory_cubit.dart'; +import 'package:npt_flutter/features/onboarding/cubit/onboarding_cubit.dart'; import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; -typedef OnboardingMapCallback = void Function(Map val); - class AtsignSelector extends StatefulWidget { const AtsignSelector({ + required this.options, super.key, }); - + final Map options; @override State createState() => _AtsignSelectorState(); } class _AtsignSelectorState extends State { - final FocusNode focusNode = FocusNode(); - - final TextEditingController controller = TextEditingController(); - List options = []; - late final List originalOptions; - - late int originalOptionsLength; - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) async { - options = (await getAtsignEntries()).keys.toList(); - if (mounted) context.read().setAtSign(options[0]); - - controller.text = options[0]; - originalOptions = List.from(options); - originalOptionsLength = options.length; - setState(() {}); - }); - } + final focusNode = FocusNode(); + final controller = TextEditingController(); @override Widget build(BuildContext context) { - return BlocBuilder(builder: (context, atsignInformation) { - controller.text = atsignInformation.atSign; - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: DropdownMenu( - initialSelection: - options.contains(atsignInformation.atSign) ? atsignInformation.atSign : controller.text, - dropdownMenuEntries: options - .map>( - (o) => DropdownMenuEntry( - value: o, - label: o, + return BlocBuilder(builder: (context, state) { + controller.value = TextEditingValue(text: state.atSign); + return TextFormField( + controller: controller, + onChanged: (atsign) { + context.read().setState( + atSign: atsign, + rootDomain: widget.options[atsign]?.rootDomain, + ); + }, + decoration: InputDecoration( + /// This menuAnchor is a dropdown button that allows you to quickly select + /// existing values from [options] + suffixIcon: widget.options.isNotEmpty + ? Directionality( + textDirection: TextDirection.rtl, + child: MenuAnchor( + style: const MenuStyle(alignment: AlignmentDirectional.bottomStart), + childFocusNode: focusNode, + menuChildren: widget.options.keys.map((atsign) { + return Directionality( + textDirection: TextDirection.ltr, + child: MenuItemButton( + child: Text(atsign), + onPressed: () { + context.read().setState( + atSign: atsign, + rootDomain: widget.options[atsign]?.rootDomain, + ); + }, ), - ) - .toList(), - onSelected: (value) { - if (value == null) return; - - context.read().setAtSign(value); - }, - ), - ), - Flexible( - child: KeyboardListener( - focusNode: focusNode, - onKeyEvent: (value) { - if (value.logicalKey == LogicalKeyboardKey.backspace) { - if (options.length > originalOptionsLength) options.removeLast(); - } - }, - child: TextFormField( - controller: controller, - autovalidateMode: AutovalidateMode.onUserInteraction, - // validator: FormValidator.validateRequiredAtsignField, - onChanged: (value) { - // prevent the user from adding the default values to the dropdown a second time. - if (!originalOptions.contains(value)) { - options.add(value); - - setState(() {}); - } - //removes the last element making the final entry the only additional value in options. This prevents the dropdown from having more than original options + one entries. - if (options.length > originalOptionsLength + 1) options.removeAt(originalOptionsLength); - - context.read().setAtSign(value); + ); + }).toList(), + builder: (BuildContext context, MenuController controller, Widget? child) { + return IconButton( + focusNode: focusNode, + onPressed: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + icon: const Icon(Icons.arrow_drop_down), + ); }, ), - ), - ) - ], - ), - ], + ) + : null, + ), ); }); } 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 deleted file mode 100644 index 2455ff46e..000000000 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_at_directory_selector.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:npt_flutter/features/onboarding/cubit/at_directory_cubit.dart'; -import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; - -typedef OnboardingMapCallback = void Function(Map val); - -class OnboardingAtDirectorySelector extends StatelessWidget { - OnboardingAtDirectorySelector({ - super.key, - }); - - final options = ['root.atsign.org', 'vip.ve.atsign.zone']; - - final FocusNode focusNode = FocusNode(); - final TextEditingController controller = TextEditingController(); - - @override - Widget build(BuildContext context) { - return BlocBuilder(builder: (context, atsignInformation) { - controller.text = atsignInformation.rootDomain; - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: DropdownMenu( - initialSelection: - options.contains(atsignInformation.rootDomain) ? atsignInformation.rootDomain : null, - dropdownMenuEntries: options - .map>( - (o) => DropdownMenuEntry( - value: o, - label: o, - ), - ) - .toList(), - onSelected: (value) { - if (value == null) return; - - context.read().setRootDomain(value); - }, - ), - ), - Flexible( - child: KeyboardListener( - focusNode: focusNode, - onKeyEvent: (value) { - if (value.logicalKey == LogicalKeyboardKey.backspace) { - if (options.length > 2) options.removeLast(); - } - }, - child: TextFormField( - controller: controller, - autovalidateMode: AutovalidateMode.onUserInteraction, - // 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); - } - //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); - - context.read().setRootDomain(value); - }, - ), - ), - ) - ], - ), - ], - ); - }); - } -} 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 c8da1fb13..68aa1b8a1 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 @@ -2,14 +2,14 @@ import 'dart:developer'; import 'package:at_contacts_flutter/at_contacts_flutter.dart'; import 'package:at_onboarding_flutter/at_onboarding_flutter.dart'; +import 'package:at_onboarding_flutter/screen/at_onboarding_home_screen.dart'; 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/onboarding/cubit/at_directory_cubit.dart'; import 'package:npt_flutter/features/onboarding/onboarding.dart'; import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; -import 'package:npt_flutter/features/onboarding/widgets/atsign_dialog.dart'; +import 'package:npt_flutter/features/onboarding/widgets/onboarding_dialog.dart'; import 'package:npt_flutter/routes.dart'; import 'package:path_provider/path_provider.dart'; import 'package:phosphor_flutter/phosphor_flutter.dart'; @@ -35,23 +35,62 @@ class OnboardingButton extends StatefulWidget { } class _OnboardingButtonState extends State { - Future onboard({required String rootDomain, bool isFromInitState = false}) async { - AtOnboardingResult onboardingResult = await AtOnboarding.onboard( - // ignore: use_build_context_synchronously - context: context, - config: AtOnboardingConfig( - atClientPreference: await loadAtClientPreference(rootDomain), - rootEnvironment: RootEnvironment.Testing, - domain: rootDomain, - appAPIKey: Constants.appAPIKey, - ), + Future onboard({String? atsign, required String rootDomain, bool isFromInitState = false}) async { + var atSigns = await KeyChainManager.getInstance().getAtSignListFromKeychain(); + var config = AtOnboardingConfig( + atClientPreference: await loadAtClientPreference(rootDomain), + rootEnvironment: RootEnvironment.Production, + domain: rootDomain, + appAPIKey: Constants.appAPIKey, ); + AtOnboardingResult? onboardingResult; + if (!atSigns.contains(atsign)) { + // This is a hack. + // Ideally it should be possible to skip the home screen in onboarding + // and go straight to either of the following (based on current atSign status): + // A) opening the file picker + // B) activating the atSign + // But unfortunately that code is SO coupled to the widget that it is really + // not worth the effort to fix right now. + // + // Assumptions made in at_onboarding_flutter which have caused this problem: + // if [atsign] is non-null the atSign already exists in the keychain + // thus any atsign that isn't in the keychain isn't handled if explicitly passed... + // this means there is no edge case handling for new or unactivated atSigns + // nor for atSigns that are activated but not in the keychain... + // + // Given that we are working on new user flows, I'm not going to waste countless + // hours for this tiny UX fix + + // TODO: fix localizations + await AtOnboardingLocalizations.load(const Locale("en")); + onboardingResult = await Navigator.push( + // ignore: use_build_context_synchronously + context, + MaterialPageRoute( + builder: (BuildContext context) { + return AtOnboardingHomeScreen( + config: config, + isFromIntroScreen: false, + ); + }, + ), + ); + } else { + onboardingResult = await AtOnboarding.onboard( + atsign: atsign, + // ignore: use_build_context_synchronously + context: context, + config: config, + ); + } + if (mounted) { - switch (onboardingResult.status) { + switch (onboardingResult?.status ?? AtOnboardingResultStatus.cancel) { case AtOnboardingResultStatus.success: await initializeContactsService(rootDomain: rootDomain); - postOnboard(onboardingResult.atsign!); + postOnboard(onboardingResult!.atsign!, rootDomain); final result = await saveAtsignInformation(AtsignInformation(atSign: onboardingResult.atsign!, rootDomain: rootDomain)); log('atsign result is:$result'); @@ -76,11 +115,31 @@ class _OnboardingButtonState extends State { } Future selectAtsign() async { - final results = await showDialog( - context: context, - builder: (BuildContext context) => const AtSignDialog(), - ); - return results ?? false; + var options = await getAtsignEntries(); + if (mounted) { + final cubit = context.read(); + String atsign = cubit.state.atSign; + String? rootDomain = cubit.state.rootDomain; + + if (options.isEmpty) { + atsign = ""; + } else if (atsign.isEmpty) { + atsign = options.keys.first; + } + if (options.keys.contains(atsign)) { + rootDomain = options[atsign]?.rootDomain; + } else { + rootDomain = Constants.getRootDomains(context).keys.first; + } + + cubit.setState(atSign: atsign, rootDomain: rootDomain); + final results = await showDialog( + context: context, + builder: (BuildContext context) => OnboardingDialog(options: options), + ); + return results ?? false; + } + return false; } @override diff --git a/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_dialog.dart b/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_dialog.dart index 796f8f46b..ebbfd857f 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_dialog.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/widgets/onboarding_dialog.dart @@ -1,19 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; +import 'package:npt_flutter/features/onboarding/widgets/at_directory_selector.dart'; +import 'package:npt_flutter/features/onboarding/widgets/atsign_selector.dart'; import 'package:npt_flutter/styles/sizes.dart'; import 'package:npt_flutter/widgets/custom_container.dart'; class OnboardingDialog extends StatelessWidget { - const OnboardingDialog( - {required this.title, - required this.subtitle, - required this.successButtonText, - required this.children, - super.key}); - final String title; - final String subtitle; - final String successButtonText; - final List children; + const OnboardingDialog({required this.options, super.key}); + final Map options; @override Widget build(BuildContext context) { @@ -30,10 +25,16 @@ class OnboardingDialog extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(title), - Text(subtitle), + const Text("Select or type the client atSign"), gapH16, - ...children, + AtsignSelector( + options: options, + ), + gapH16, + const Text("Select or type the root domain"), + AtDirectorySelector( + options: options, + ), ], ), ), @@ -52,7 +53,7 @@ class OnboardingDialog extends StatelessWidget { onPressed: () { Navigator.of(context).pop(true); }, - child: Text(successButtonText), + child: const Text("Next"), ), ], )) 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 2b9c92885..4c1167bab 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,8 +24,7 @@ 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; @@ -43,8 +42,7 @@ 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; @@ -73,16 +71,14 @@ 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 { @@ -92,9 +88,7 @@ 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) { @@ -109,15 +103,12 @@ 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 || @@ -134,23 +125,18 @@ 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; @@ -163,6 +149,7 @@ class ProfileBloc extends LoggingBloc { atClient: atClient, params: profile.toNptParams( clientAtsign: atSign, + rootDomain: atClient.getPreferences()!.rootDomain, fallbackRelayAtsign: settings.relayAtsign, overrideRelayWithFallback: settings.overrideRelay, ), @@ -195,9 +182,7 @@ class ProfileBloc extends LoggingBloc { profile: profile, reason: 'Npt startup timedout', )); - App.navState.currentContext - ?.read() - .invalidate(uuid); + App.navState.currentContext?.read().invalidate(uuid); return; } @@ -208,9 +193,7 @@ class ProfileBloc extends LoggingBloc { profile: profile, reason: 'Socketconnector closed prematurely', )); - App.navState.currentContext - ?.read() - .invalidate(uuid); + App.navState.currentContext?.read().invalidate(uuid); return; } @@ -224,21 +207,16 @@ 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/models/profile.dart b/packages/dart/npt_flutter/lib/features/profile/models/profile.dart index 0bf929839..73798499c 100644 --- a/packages/dart/npt_flutter/lib/features/profile/models/profile.dart +++ b/packages/dart/npt_flutter/lib/features/profile/models/profile.dart @@ -1,6 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:noports_core/npt.dart'; -import 'package:npt_flutter/constants.dart'; import 'package:npt_flutter/app.dart'; import 'package:npt_flutter/features/favorite/favorite.dart'; import 'package:npt_flutter/util/uuid.dart'; @@ -87,13 +86,12 @@ final class Profile extends Loggable with Favoritable { NptParams toNptParams({ required String clientAtsign, + required String rootDomain, required String fallbackRelayAtsign, bool overrideRelayWithFallback = false, }) { String srvdAtSign = fallbackRelayAtsign; - if (!overrideRelayWithFallback && - relayAtsign != null && - relayAtsign!.isNotEmpty) { + if (!overrideRelayWithFallback && relayAtsign != null && relayAtsign!.isNotEmpty) { srvdAtSign = relayAtsign!; } return NptParams( @@ -104,7 +102,7 @@ final class Profile extends Loggable with Favoritable { remotePort: remotePort, device: deviceName, localPort: localPort, - rootDomain: Constants.rootDomain, + rootDomain: rootDomain, // hardcoded for now, because it makes the app simpler // and there's very few use-cases where you wouldn't want these settings 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 433b983b2..3ed3c2beb 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,8 +15,7 @@ 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', @@ -26,7 +25,7 @@ part 'tray_state.dart'; if (context == null) return; if (context.mounted) { var cubit = context.read(); - if (cubit.state is! Onboarded) return; + if (cubit.getStatus() != OnboardingStatus.onboarded) return; Navigator.of(context).pushNamedAndRemoveUntil( Routes.settings, (route) => route.isFirst, @@ -71,7 +70,7 @@ class TrayCubit extends LoggingCubit { if (state is! TrayInitial) return; var context = App.navState.currentContext; if (context == null) return; - var showSettings = context.read().state is Onboarded; + var showSettings = context.read().getStatus() == OnboardingStatus.onboarded; await reloadIcon(); @@ -86,13 +85,10 @@ 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, }); } @@ -102,7 +98,7 @@ class TrayCubit extends LoggingCubit { var init = initialize(); /// Access the context before any awaited function calls - var showSettings = context.read().state is Onboarded; + var showSettings = context.read().getStatus() == OnboardingStatus.onboarded; var favoriteBloc = context.read(); var profilesList = context.read(); @@ -118,9 +114,7 @@ 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/widgets/custom_text_button.dart b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart index afdce37f4..fad7cb223 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -5,10 +5,9 @@ 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/onboarding/cubit/at_directory_cubit.dart'; +import 'package:npt_flutter/features/onboarding/cubit/onboarding_cubit.dart'; import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; import 'package:npt_flutter/features/onboarding/util/pre_offboard.dart'; -import 'package:npt_flutter/features/onboarding/widgets/at_directory_dialog.dart'; import 'package:npt_flutter/features/onboarding/widgets/onboarding_button.dart'; import 'package:npt_flutter/pages/loading_page.dart'; import 'package:npt_flutter/routes.dart'; @@ -77,11 +76,6 @@ class CustomTextButton extends StatelessWidget { this.title = 'Feedback', this.type = CustomListTileType.feedback, super.key}); - const CustomTextButton.selectRootDomain( - {this.iconData = Icons.dns_outlined, - this.title = 'Select Root Domain', - this.type = CustomListTileType.selectRootDomain, - super.key}); final IconData iconData; final String title; @@ -212,7 +206,7 @@ class CustomTextButton extends StatelessWidget { } if (type == CustomListTileType.resetAtsign) { - return BlocBuilder(builder: (context, atsignInformation) { + return BlocBuilder(builder: (context, atsignInformation) { return Padding( padding: const EdgeInsets.only(left: Sizes.p30, right: Sizes.p30, bottom: Sizes.p10), child: TextButton.icon( @@ -252,5 +246,4 @@ enum CustomListTileType { resetAtsign, feedback, signOut, - selectRootDomain, } From 6aa2e36d847eff1ca21f8416c671556fab9d8543 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Wed, 16 Oct 2024 16:30:03 -0400 Subject: [PATCH 41/60] chore: cleanup window manager --- packages/dart/npt_flutter/lib/main.dart | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/dart/npt_flutter/lib/main.dart b/packages/dart/npt_flutter/lib/main.dart index e74895cc1..a4dfdd900 100644 --- a/packages/dart/npt_flutter/lib/main.dart +++ b/packages/dart/npt_flutter/lib/main.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:window_manager/window_manager.dart'; @@ -8,11 +6,10 @@ import 'app.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); windowManager.ensureInitialized(); - try { - await windowManager.setSkipTaskbar(true); // Don't show the app icon in dock - } catch (_) { - log("Failed to setSkipTaskbar"); - } finally { - runApp(const App()); - } + var windowOptions = const WindowOptions( + title: "NoPorts Desktop", + skipTaskbar: true, + ); + windowManager.waitUntilReadyToShow(windowOptions); + runApp(const App()); } From 22267ea0941b08abad1d9cb0350ad070521ab9db Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Wed, 16 Oct 2024 16:30:20 -0400 Subject: [PATCH 42/60] cleanup onboarding cubit --- .../lib/features/onboarding/cubit/onboarding_cubit.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_cubit.dart b/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_cubit.dart index d23812dd4..890e5a93b 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_cubit.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/cubit/onboarding_cubit.dart @@ -27,9 +27,9 @@ class OnboardingCubit extends LoggingCubit { String? rootDomain, }) => emit(OnboardingState( - atSign: atSign ?? this.state.atSign, - status: status ?? this.state.status, - rootDomain: rootDomain ?? this.state.rootDomain, + atSign: atSign ?? state.atSign, + status: status ?? state.status, + rootDomain: rootDomain ?? state.rootDomain, )); } From ceea725cd8057ececa4daa730932b51e524c5664 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 05:04:43 +0000 Subject: [PATCH 43/60] build(deps): Bump anchore/sbom-action in the github-actions group Bumps the github-actions group with 1 update: [anchore/sbom-action](https://github.com/anchore/sbom-action). Updates `anchore/sbom-action` from 0.17.2 to 0.17.3 - [Release notes](https://github.com/anchore/sbom-action/releases) - [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/sbom-action/compare/61119d458adab75f756bc0b9e4bde25725f86a7a...f5e124a5e5e1d497a692818ae907d3c45829d033) --- updated-dependencies: - dependency-name: anchore/sbom-action dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/multibuild.yaml | 2 +- .github/workflows/python-sshnpd-build-publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/multibuild.yaml b/.github/workflows/multibuild.yaml index 1a9a176cd..abd7661ad 100644 --- a/.github/workflows/multibuild.yaml +++ b/.github/workflows/multibuild.yaml @@ -272,7 +272,7 @@ jobs: sparse-checkout: packages/dart/sshnoports/pubspec.lock sparse-checkout-cone-mode: false - name: Install Syft - uses: anchore/sbom-action/download-syft@61119d458adab75f756bc0b9e4bde25725f86a7a # v0.17.2 + uses: anchore/sbom-action/download-syft@f5e124a5e5e1d497a692818ae907d3c45829d033 # v0.17.3 - name: Download all the tarballs uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: diff --git a/.github/workflows/python-sshnpd-build-publish.yml b/.github/workflows/python-sshnpd-build-publish.yml index 7e22fa082..c7da2004d 100644 --- a/.github/workflows/python-sshnpd-build-publish.yml +++ b/.github/workflows/python-sshnpd-build-publish.yml @@ -126,7 +126,7 @@ jobs: name: sshnpd-python-package path: dist/ - name: Install Syft - uses: anchore/sbom-action/download-syft@61119d458adab75f756bc0b9e4bde25725f86a7a # v0.17.2 + uses: anchore/sbom-action/download-syft@f5e124a5e5e1d497a692818ae907d3c45829d033 # v0.17.3 - name: Generate SBOMs run: | syft scan file:./packages/python/sshnpd/requirements.txt \ From aa6c160d639c15ec981276f969add31f9426faac Mon Sep 17 00:00:00 2001 From: Chris Swan <478926+cpswan@users.noreply.github.com> Date: Mon, 14 Oct 2024 09:52:01 +0100 Subject: [PATCH 44/60] ci: Raise command_timeout to 15m from 10m default --- .github/workflows/e2e_all.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/e2e_all.yaml b/.github/workflows/e2e_all.yaml index 52ac78bfe..e71f7b8d7 100644 --- a/.github/workflows/e2e_all.yaml +++ b/.github/workflows/e2e_all.yaml @@ -37,6 +37,7 @@ jobs: username: ubuntu key: ${{ secrets.NOPORTS_CICD_SSH_KEY }} envs: SHA + command_timeout: 15m script: | cd noports rm -rf tests/e2e_all/runtime From b6c714c9b08247960d35af953c176c43e7aadb81 Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 14 Oct 2024 13:36:30 +0100 Subject: [PATCH 45/60] build: require npm version 10.9.0 for the policy webapp. Addresses #1411 --- .github/workflows/multibuild.yaml | 1 + apps/admin/webapp/.npmrc | 1 + apps/admin/webapp/package-lock.json | 4 ++++ apps/admin/webapp/package.json | 4 ++++ packages/dart/sshnoports/buildArchive | 1 + tools/multibuild/Dockerfile.package | 3 ++- 6 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 apps/admin/webapp/.npmrc diff --git a/.github/workflows/multibuild.yaml b/.github/workflows/multibuild.yaml index abd7661ad..6c42d5159 100644 --- a/.github/workflows/multibuild.yaml +++ b/.github/workflows/multibuild.yaml @@ -103,6 +103,7 @@ jobs: - name: build admin webapp working-directory: ./apps/admin/webapp run: | + npm install -g npm@10.9.0 npm install npm run build - if: ${{ matrix.os != 'windows-latest' }} diff --git a/apps/admin/webapp/.npmrc b/apps/admin/webapp/.npmrc new file mode 100644 index 000000000..b6f27f135 --- /dev/null +++ b/apps/admin/webapp/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/apps/admin/webapp/package-lock.json b/apps/admin/webapp/package-lock.json index 55d5919a7..384211287 100644 --- a/apps/admin/webapp/package-lock.json +++ b/apps/admin/webapp/package-lock.json @@ -11,6 +11,10 @@ "@sveltejs/vite-plugin-svelte": "^3.1.1", "svelte": "^4.2.19", "vite": "^5.4.6" + }, + "engines": { + "node": ">=v20.15.0 <23", + "npm": "10.9.0" } }, "node_modules/@ampproject/remapping": { diff --git a/apps/admin/webapp/package.json b/apps/admin/webapp/package.json index 4673bb1ef..3f8af9563 100644 --- a/apps/admin/webapp/package.json +++ b/apps/admin/webapp/package.json @@ -12,5 +12,9 @@ "@sveltejs/vite-plugin-svelte": "^3.1.1", "svelte": "^4.2.19", "vite": "^5.4.6" + }, + "engines": { + "node": ">=v20.15.0 <23", + "npm": "10.9.0" } } diff --git a/packages/dart/sshnoports/buildArchive b/packages/dart/sshnoports/buildArchive index 37d714ee1..7bc246d13 100755 --- a/packages/dart/sshnoports/buildArchive +++ b/packages/dart/sshnoports/buildArchive @@ -38,6 +38,7 @@ echo "Compiling admin_api"; dart compile exe --verbosity error bin/np_admin.dart wait cd ../webapp || exit 1 echo "Building admin webapp" +npm install -g npm@10.9.0 || exit 1 npm install || exit 1 npm run build || exit 1 diff --git a/tools/multibuild/Dockerfile.package b/tools/multibuild/Dockerfile.package index 42b86d04c..f8922b193 100644 --- a/tools/multibuild/Dockerfile.package +++ b/tools/multibuild/Dockerfile.package @@ -9,7 +9,8 @@ WORKDIR /noports # install node for later (keep at the top file to increase cache hits) # hadolint ignore=DL3008 RUN apt-get update; \ - apt-get install -y --no-install-recommends npm + apt-get install -y --no-install-recommends npm; \ + npm install -g npm@10.9.0 COPY . . From 0db89289622d401c47f5c0f0e6bffbafbbff2c6f Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 14 Oct 2024 13:49:30 +0100 Subject: [PATCH 46/60] build: pin other dependencies for the admin webapp --- apps/admin/webapp/package-lock.json | 6 +++--- apps/admin/webapp/package.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/admin/webapp/package-lock.json b/apps/admin/webapp/package-lock.json index 384211287..74ae4659d 100644 --- a/apps/admin/webapp/package-lock.json +++ b/apps/admin/webapp/package-lock.json @@ -8,9 +8,9 @@ "name": "webapp_proto", "version": "0.0.0", "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.1.1", - "svelte": "^4.2.19", - "vite": "^5.4.6" + "@sveltejs/vite-plugin-svelte": "3.1.1", + "svelte": "4.2.19", + "vite": "5.4.6" }, "engines": { "node": ">=v20.15.0 <23", diff --git a/apps/admin/webapp/package.json b/apps/admin/webapp/package.json index 3f8af9563..52d4f2771 100644 --- a/apps/admin/webapp/package.json +++ b/apps/admin/webapp/package.json @@ -9,9 +9,9 @@ "preview": "vite preview" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.1.1", - "svelte": "^4.2.19", - "vite": "^5.4.6" + "@sveltejs/vite-plugin-svelte": "3.1.1", + "svelte": "4.2.19", + "vite": "5.4.6" }, "engines": { "node": ">=v20.15.0 <23", From 5f1d976b33a553f695d97e1ea3dd3c7bf784f71c Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 14 Oct 2024 13:53:20 +0100 Subject: [PATCH 47/60] build: add an `npm` section to the dependabot config --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0ffe58fbd..c2b0a152e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -40,3 +40,7 @@ updates: pip: patterns: - "*" + - package-ecosystem: 'npm' + directory: "/apps/admin/webapp/" + schedule: + interval: "daily" From 9e47d56e053320771830e8ad039ad078be7f35ad Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 14 Oct 2024 13:59:40 +0100 Subject: [PATCH 48/60] build: dependabot.yml also use `groups` directive for the `npm` ecosystem --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c2b0a152e..5110a3c9e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -44,3 +44,7 @@ updates: directory: "/apps/admin/webapp/" schedule: interval: "daily" + groups: + npm: + patterns: + - "*" From 7a4184adfb45878642778134ac5ebb5af273e9e7 Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 14 Oct 2024 14:31:28 +0100 Subject: [PATCH 49/60] build: pin npm to 10.8.2 as a temporary measure because Dependabot uses 10.8.2 --- .github/workflows/multibuild.yaml | 2 +- apps/admin/webapp/package-lock.json | 2 +- apps/admin/webapp/package.json | 2 +- packages/dart/sshnoports/buildArchive | 2 +- tools/multibuild/Dockerfile.package | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/multibuild.yaml b/.github/workflows/multibuild.yaml index 6c42d5159..818da3ef8 100644 --- a/.github/workflows/multibuild.yaml +++ b/.github/workflows/multibuild.yaml @@ -103,7 +103,7 @@ jobs: - name: build admin webapp working-directory: ./apps/admin/webapp run: | - npm install -g npm@10.9.0 + npm install -g npm@10.8.2 npm install npm run build - if: ${{ matrix.os != 'windows-latest' }} diff --git a/apps/admin/webapp/package-lock.json b/apps/admin/webapp/package-lock.json index 74ae4659d..64b17136d 100644 --- a/apps/admin/webapp/package-lock.json +++ b/apps/admin/webapp/package-lock.json @@ -14,7 +14,7 @@ }, "engines": { "node": ">=v20.15.0 <23", - "npm": "10.9.0" + "npm": "10.8.2" } }, "node_modules/@ampproject/remapping": { diff --git a/apps/admin/webapp/package.json b/apps/admin/webapp/package.json index 52d4f2771..90c98b4ba 100644 --- a/apps/admin/webapp/package.json +++ b/apps/admin/webapp/package.json @@ -15,6 +15,6 @@ }, "engines": { "node": ">=v20.15.0 <23", - "npm": "10.9.0" + "npm": "10.8.2" } } diff --git a/packages/dart/sshnoports/buildArchive b/packages/dart/sshnoports/buildArchive index 7bc246d13..8fb0bf54d 100755 --- a/packages/dart/sshnoports/buildArchive +++ b/packages/dart/sshnoports/buildArchive @@ -38,7 +38,7 @@ echo "Compiling admin_api"; dart compile exe --verbosity error bin/np_admin.dart wait cd ../webapp || exit 1 echo "Building admin webapp" -npm install -g npm@10.9.0 || exit 1 +npm install -g npm@10.8.2 || exit 1 npm install || exit 1 npm run build || exit 1 diff --git a/tools/multibuild/Dockerfile.package b/tools/multibuild/Dockerfile.package index f8922b193..cccaf5659 100644 --- a/tools/multibuild/Dockerfile.package +++ b/tools/multibuild/Dockerfile.package @@ -10,7 +10,7 @@ WORKDIR /noports # hadolint ignore=DL3008 RUN apt-get update; \ apt-get install -y --no-install-recommends npm; \ - npm install -g npm@10.9.0 + npm install -g npm@10.8.2 COPY . . From 89cb39b05c08ca16b68268bad70a59d1c796d206 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:23:43 +0000 Subject: [PATCH 50/60] build(deps): Bump github/codeql-action in the github-actions group Bumps the github-actions group with 1 update: [github/codeql-action](https://github.com/github/codeql-action). Updates `github/codeql-action` from 3.26.12 to 3.26.13 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/c36620d31ac7c881962c3d9dd939c40ec9434f2b...f779452ac5af1c261dce0346a8f964149f49322b) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql.yml | 6 +++--- .github/workflows/scorecards.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 84b7ede77..2c94b1b06 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -50,7 +50,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/init@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -60,7 +60,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/autobuild@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -73,6 +73,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/analyze@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index ea925a9be..cabd79d6a 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: sarif_file: results.sarif From e7ba981143585eab2e9850e5086d40befbbb4f10 Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 14 Oct 2024 14:51:52 +0100 Subject: [PATCH 51/60] build: for the policy webapp, use `npm ci` rather than `npm install` in order to respect package-lock.json --- .github/workflows/multibuild.yaml | 2 +- packages/dart/sshnoports/buildArchive | 2 +- tools/multibuild/Dockerfile.package | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/multibuild.yaml b/.github/workflows/multibuild.yaml index 818da3ef8..91ff9b665 100644 --- a/.github/workflows/multibuild.yaml +++ b/.github/workflows/multibuild.yaml @@ -104,7 +104,7 @@ jobs: working-directory: ./apps/admin/webapp run: | npm install -g npm@10.8.2 - npm install + npm ci npm run build - if: ${{ matrix.os != 'windows-latest' }} run: | diff --git a/packages/dart/sshnoports/buildArchive b/packages/dart/sshnoports/buildArchive index 8fb0bf54d..07ab4cd5d 100755 --- a/packages/dart/sshnoports/buildArchive +++ b/packages/dart/sshnoports/buildArchive @@ -39,7 +39,7 @@ wait cd ../webapp || exit 1 echo "Building admin webapp" npm install -g npm@10.8.2 || exit 1 -npm install || exit 1 +npm ci || exit 1 npm run build || exit 1 wait diff --git a/tools/multibuild/Dockerfile.package b/tools/multibuild/Dockerfile.package index cccaf5659..238a94ab0 100644 --- a/tools/multibuild/Dockerfile.package +++ b/tools/multibuild/Dockerfile.package @@ -42,7 +42,7 @@ RUN dart pub get --enforce-lockfile; \ # Build apps/admin/webapp WORKDIR /noports/apps/admin/webapp -RUN npm install; \ +RUN npm ci; \ npm run build; \ mkdir -p /sshnp/web/admin; \ cp -r ./dist/* /sshnp/web/admin/ From 9d097a4370916b533add5e3b0c4d6b1222f423f8 Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 14 Oct 2024 15:15:33 +0100 Subject: [PATCH 52/60] build: for the policy webapp: don't run `npm install -g npm@version` to choose the version of npm being used - just require that it be >= 10.8.2 --- .github/workflows/multibuild.yaml | 1 - apps/admin/webapp/package-lock.json | 4 ++-- apps/admin/webapp/package.json | 2 +- packages/dart/sshnoports/buildArchive | 1 - tools/multibuild/Dockerfile.package | 3 +-- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/multibuild.yaml b/.github/workflows/multibuild.yaml index 91ff9b665..a6682f97d 100644 --- a/.github/workflows/multibuild.yaml +++ b/.github/workflows/multibuild.yaml @@ -103,7 +103,6 @@ jobs: - name: build admin webapp working-directory: ./apps/admin/webapp run: | - npm install -g npm@10.8.2 npm ci npm run build - if: ${{ matrix.os != 'windows-latest' }} diff --git a/apps/admin/webapp/package-lock.json b/apps/admin/webapp/package-lock.json index 64b17136d..326b417e5 100644 --- a/apps/admin/webapp/package-lock.json +++ b/apps/admin/webapp/package-lock.json @@ -13,8 +13,8 @@ "vite": "5.4.6" }, "engines": { - "node": ">=v20.15.0 <23", - "npm": "10.8.2" + "node": ">=v20.15.0", + "npm": ">=10.8.2" } }, "node_modules/@ampproject/remapping": { diff --git a/apps/admin/webapp/package.json b/apps/admin/webapp/package.json index 90c98b4ba..aff46a6ee 100644 --- a/apps/admin/webapp/package.json +++ b/apps/admin/webapp/package.json @@ -15,6 +15,6 @@ }, "engines": { "node": ">=v20.15.0 <23", - "npm": "10.8.2" + "npm": ">=10.8.2" } } diff --git a/packages/dart/sshnoports/buildArchive b/packages/dart/sshnoports/buildArchive index 07ab4cd5d..9fc07cff7 100755 --- a/packages/dart/sshnoports/buildArchive +++ b/packages/dart/sshnoports/buildArchive @@ -38,7 +38,6 @@ echo "Compiling admin_api"; dart compile exe --verbosity error bin/np_admin.dart wait cd ../webapp || exit 1 echo "Building admin webapp" -npm install -g npm@10.8.2 || exit 1 npm ci || exit 1 npm run build || exit 1 diff --git a/tools/multibuild/Dockerfile.package b/tools/multibuild/Dockerfile.package index 238a94ab0..3417adcc0 100644 --- a/tools/multibuild/Dockerfile.package +++ b/tools/multibuild/Dockerfile.package @@ -9,8 +9,7 @@ WORKDIR /noports # install node for later (keep at the top file to increase cache hits) # hadolint ignore=DL3008 RUN apt-get update; \ - apt-get install -y --no-install-recommends npm; \ - npm install -g npm@10.8.2 + apt-get install -y --no-install-recommends npm COPY . . From ea6395fe1f06585b5e5e335b25cc1e2f7574e586 Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 14 Oct 2024 14:19:36 +0100 Subject: [PATCH 53/60] build: pin dependencies in packages/dart/sshnoports fix: fix compile error introduced by a package change --- .../dart/sshnoports/bin/npp_atserver.dart | 4 +- packages/dart/sshnoports/pubspec.lock | 69 +++++++++++-------- packages/dart/sshnoports/pubspec.yaml | 14 ++-- 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/packages/dart/sshnoports/bin/npp_atserver.dart b/packages/dart/sshnoports/bin/npp_atserver.dart index 13028ae71..d66d92225 100644 --- a/packages/dart/sshnoports/bin/npp_atserver.dart +++ b/packages/dart/sshnoports/bin/npp_atserver.dart @@ -6,7 +6,7 @@ import 'package:at_utils/at_logger.dart'; import 'package:logging/logging.dart'; import 'package:noports_core/admin.dart'; import 'package:noports_core/npa.dart'; -import 'package:noports_core/sshnp_foundation.dart'; +import 'package:noports_core/sshnp_foundation.dart' hide standardAtClientStoragePath; import 'package:sshnoports/src/create_at_client_cli.dart'; late AtSignLogger logger; @@ -32,7 +32,7 @@ void main(List args) async { atServiceFactory: ServiceFactoryWithNoOpSyncService(), namespace: DefaultArgs.namespace, storagePath: standardAtClientStoragePath( - homeDirectory: p.homeDirectory, + baseDir: p.homeDirectory, atSign: p.authorizerAtsign, progName: '.${DefaultArgs.namespace}', uniqueID: 'single'), diff --git a/packages/dart/sshnoports/pubspec.lock b/packages/dart/sshnoports/pubspec.lock index fb9994551..02407fb9f 100644 --- a/packages/dart/sshnoports/pubspec.lock +++ b/packages/dart/sshnoports/pubspec.lock @@ -5,18 +5,23 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77" url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "73.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.2" analyzer: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a" url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "6.8.0" archive: dependency: transitive description: @@ -38,10 +43,10 @@ packages: dependency: transitive description: name: asn1lib - sha256: "58082b3f0dca697204dbab0ef9ff208bfaea7767ea771076af9a343488428dda" + sha256: bf1a19d6ebea1b3b6151304936955d7d73e1f00b75e544c01a60fb2e832ffe1d url: "https://pub.dev" source: hosted - version: "1.5.3" + version: "1.5.6" async: dependency: transitive description: @@ -102,10 +107,10 @@ packages: dependency: transitive description: name: at_demo_data - sha256: "0f59a24b83f0cd6d0e0557021511602ff167ece0ac69f12b8612c03263dff9ea" + sha256: "685f237e82af669d1f2f470a0c02b7ba4647b0452412aa41cf5da4faf58ba22b" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" at_lookup: dependency: transitive description: @@ -302,10 +307,10 @@ packages: dependency: transitive description: name: coverage - sha256: "576aaab8b1abdd452e0f656c3e73da9ead9d7880e15bdc494189d9c1a1baf0db" + sha256: c1fb2dce3c0085f39dc72668e85f8e0210ec7de05345821ff58530567df345a5 url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.2" cron: dependency: transitive description: @@ -350,10 +355,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "2.3.7" dartssh2: dependency: "direct main" description: @@ -375,18 +380,18 @@ packages: dependency: transitive description: name: ecdsa - sha256: b71687a843151255fced9fead63b09816cc59e9ae7b954e6a852bdc344ae1aca + sha256: "13b4e01ac140575cf88ef5e5268f92b8dd0a67a26c60a1b74e67d146068dc246" url: "https://pub.dev" source: hosted - version: "0.1.0" + version: "0.1.2" elliptic: dependency: transitive description: name: elliptic - sha256: "98e2fa89a714c649174553c823db2612dc9581814477fe1264a499d448237b6b" + sha256: "0c303d810603953a65dc39c4c542fb7538defd9e212403c54c266140819523b6" url: "https://pub.dev" source: hosted - version: "0.3.10" + version: "0.3.11" encrypt: dependency: transitive description: @@ -531,6 +536,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + macros: + dependency: transitive + description: + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + url: "https://pub.dev" + source: hosted + version: "0.1.2-main.4" matcher: dependency: transitive description: @@ -543,18 +556,18 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" mime: dependency: transitive description: name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "2.0.0" mocktail: dependency: "direct dev" description: @@ -710,10 +723,10 @@ packages: dependency: transitive description: name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" shelf_web_socket: dependency: transitive description: @@ -758,10 +771,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -854,10 +867,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.0" watcher: dependency: transitive description: @@ -870,10 +883,10 @@ packages: dependency: transitive description: name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" web_socket: dependency: transitive description: diff --git a/packages/dart/sshnoports/pubspec.yaml b/packages/dart/sshnoports/pubspec.yaml index b80a4d5ee..4e86e273c 100644 --- a/packages/dart/sshnoports/pubspec.yaml +++ b/packages/dart/sshnoports/pubspec.yaml @@ -11,16 +11,16 @@ dependencies: path: "../noports_core" version: 6.2.0 at_onboarding_cli: 1.7.0 - at_cli_commons: ^1.2.0 - at_client: ^3.2.2 + at_cli_commons: 1.2.0 + at_client: 3.2.2 args: 2.5.0 - socket_connector: ^2.2.0 + socket_connector: 2.2.0 dartssh2: 2.8.2 - duration: ^4.0.3 + duration: 4.0.3 at_utils: 3.0.19 - logging: ^1.2.0 - chalkdart: ^2.2.1 - yaml: ^3.1.2 + logging: 1.2.0 + chalkdart: 2.2.1 + yaml: 3.1.2 dependency_overrides: dartssh2: From 9d8c8630ffec253a66d5e072651cab03d94f41f0 Mon Sep 17 00:00:00 2001 From: gkc Date: Mon, 14 Oct 2024 15:20:18 +0100 Subject: [PATCH 54/60] build: updated package-lock.json --- apps/admin/webapp/package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin/webapp/package-lock.json b/apps/admin/webapp/package-lock.json index 326b417e5..bc3b5c9b0 100644 --- a/apps/admin/webapp/package-lock.json +++ b/apps/admin/webapp/package-lock.json @@ -13,7 +13,7 @@ "vite": "5.4.6" }, "engines": { - "node": ">=v20.15.0", + "node": ">=v20.15.0 <23", "npm": ">=10.8.2" } }, From 0e4cb1ec27e45b5a4a65ff3e982a3636be7ccb34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 04:23:13 +0000 Subject: [PATCH 55/60] build(deps): Bump anchore/sbom-action in the github-actions group Bumps the github-actions group with 1 update: [anchore/sbom-action](https://github.com/anchore/sbom-action). Updates `anchore/sbom-action` from 0.17.3 to 0.17.4 - [Release notes](https://github.com/anchore/sbom-action/releases) - [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/sbom-action/compare/f5e124a5e5e1d497a692818ae907d3c45829d033...8d0a6505bf28ced3e85154d13dc6af83299e13f1) --- updated-dependencies: - dependency-name: anchore/sbom-action dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/multibuild.yaml | 2 +- .github/workflows/python-sshnpd-build-publish.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/multibuild.yaml b/.github/workflows/multibuild.yaml index a6682f97d..4bd1fdc27 100644 --- a/.github/workflows/multibuild.yaml +++ b/.github/workflows/multibuild.yaml @@ -272,7 +272,7 @@ jobs: sparse-checkout: packages/dart/sshnoports/pubspec.lock sparse-checkout-cone-mode: false - name: Install Syft - uses: anchore/sbom-action/download-syft@f5e124a5e5e1d497a692818ae907d3c45829d033 # v0.17.3 + uses: anchore/sbom-action/download-syft@8d0a6505bf28ced3e85154d13dc6af83299e13f1 # v0.17.4 - name: Download all the tarballs uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: diff --git a/.github/workflows/python-sshnpd-build-publish.yml b/.github/workflows/python-sshnpd-build-publish.yml index c7da2004d..76bbbddbb 100644 --- a/.github/workflows/python-sshnpd-build-publish.yml +++ b/.github/workflows/python-sshnpd-build-publish.yml @@ -126,7 +126,7 @@ jobs: name: sshnpd-python-package path: dist/ - name: Install Syft - uses: anchore/sbom-action/download-syft@f5e124a5e5e1d497a692818ae907d3c45829d033 # v0.17.3 + uses: anchore/sbom-action/download-syft@8d0a6505bf28ced3e85154d13dc6af83299e13f1 # v0.17.4 - name: Generate SBOMs run: | syft scan file:./packages/python/sshnpd/requirements.txt \ From 392f0cb0de1a04ade770d01a88ea6531a039e17d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 04:42:27 +0000 Subject: [PATCH 56/60] build(deps): Bump the docker group across 3 directories with 3 updates Bumps the docker group with 2 updates in the /packages/dart/sshnoports/tools directory: dart and debian. Bumps the docker group with 2 updates in the /tests/end2end_tests/image directory: dart and debian. Bumps the docker group with 1 update in the /tools/multibuild directory: atsigncompany/buildimage. Updates `dart` from `93ce3d3` to `9b7b8e5` Updates `debian` from stable-20240926-slim to stable-20241016-slim Updates `dart` from `93ce3d3` to `9b7b8e5` Updates `debian` from stable-20240926-slim to stable-20241016-slim Updates `atsigncompany/buildimage` from 3.5.2 to 3.5.2_3.6.0-149.3.beta --- updated-dependencies: - dependency-name: dart dependency-type: direct:production update-type: version-update:semver-patch dependency-group: docker - dependency-name: debian dependency-type: direct:production dependency-group: docker - dependency-name: dart dependency-type: direct:production update-type: version-update:semver-patch dependency-group: docker - dependency-name: debian dependency-type: direct:production dependency-group: docker - dependency-name: atsigncompany/buildimage dependency-type: direct:production update-type: version-update:semver-patch dependency-group: docker ... Signed-off-by: dependabot[bot] --- packages/dart/sshnoports/tools/Dockerfile | 4 ++-- packages/dart/sshnoports/tools/Dockerfile.activate | 4 ++-- packages/dart/sshnoports/tools/Dockerfile.sshnpd-slim | 2 +- tests/end2end_tests/image/Dockerfile | 8 ++++---- tools/multibuild/Dockerfile.package | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/dart/sshnoports/tools/Dockerfile b/packages/dart/sshnoports/tools/Dockerfile index 64fe53e11..dc5d747f0 100644 --- a/packages/dart/sshnoports/tools/Dockerfile +++ b/packages/dart/sshnoports/tools/Dockerfile @@ -1,7 +1,7 @@ # Dockerfile # Build image for a containerized instance of sshnpd -FROM dart:3.5.2@sha256:93ce3d39d7ec4914f9035e853ebb6c09eb7c5ea20d06a62f28abb807d3513391 AS buildimage +FROM dart:3.5.2@sha256:9b7b8e5bc033862858cd0cae9bec360864517820dc4338c4dd537dd2e2f39f56 AS buildimage ENV PACKAGEDIR=packages/dart/sshnoports ENV BINARYDIR=/usr/local/at SHELL ["/bin/bash", "-c"] @@ -17,7 +17,7 @@ RUN \ dart compile exe bin/srv.dart -o ${BINARYDIR}/srv # Second stage of build FROM debian-slim -FROM debian:stable-20240926-slim@sha256:939e69ef5aa4dc178893a718ea567f1ca390df60793fd08c0bc7008362f72a57 +FROM debian:stable-20241016-slim@sha256:fffe16098bcefa876d01862a61f8f30ef4292c9485940e905d41a15d8459828b ENV USER=atsign ENV HOMEDIR=/${USER} ENV BINARYDIR=/usr/local/at diff --git a/packages/dart/sshnoports/tools/Dockerfile.activate b/packages/dart/sshnoports/tools/Dockerfile.activate index 3562f8463..5cf1d8f6a 100644 --- a/packages/dart/sshnoports/tools/Dockerfile.activate +++ b/packages/dart/sshnoports/tools/Dockerfile.activate @@ -1,6 +1,6 @@ # Dockerfile.activate # Build image for a containerized call of the at_activate binary -FROM dart:3.5.2@sha256:93ce3d39d7ec4914f9035e853ebb6c09eb7c5ea20d06a62f28abb807d3513391 AS buildimage +FROM dart:3.5.2@sha256:9b7b8e5bc033862858cd0cae9bec360864517820dc4338c4dd537dd2e2f39f56 AS buildimage ENV PACKAGEDIR=packages/dart/sshnoports ENV BINARYDIR=/usr/local/at SHELL ["/bin/bash", "-c"] @@ -15,7 +15,7 @@ RUN \ dart compile exe bin/activate_cli.dart -o ${BINARYDIR}/at_activate # Second stage of build FROM debian-slim -FROM debian:stable-20240926-slim@sha256:939e69ef5aa4dc178893a718ea567f1ca390df60793fd08c0bc7008362f72a57 +FROM debian:stable-20241016-slim@sha256:fffe16098bcefa876d01862a61f8f30ef4292c9485940e905d41a15d8459828b ENV USER=atsign ENV HOMEDIR=/${USER} ENV BINARYDIR=/usr/local/at diff --git a/packages/dart/sshnoports/tools/Dockerfile.sshnpd-slim b/packages/dart/sshnoports/tools/Dockerfile.sshnpd-slim index a29fe2455..32495a089 100644 --- a/packages/dart/sshnoports/tools/Dockerfile.sshnpd-slim +++ b/packages/dart/sshnoports/tools/Dockerfile.sshnpd-slim @@ -9,7 +9,7 @@ # as of 5th Feb 2024 - Will check state as 3.3 Stable is released #FROM dart:beta-sdk AS buildimage -FROM dart:3.5.2@sha256:93ce3d39d7ec4914f9035e853ebb6c09eb7c5ea20d06a62f28abb807d3513391 AS buildimage +FROM dart:3.5.2@sha256:9b7b8e5bc033862858cd0cae9bec360864517820dc4338c4dd537dd2e2f39f56 AS buildimage ENV PACKAGEDIR=packages/dart/sshnoports ENV OPENSSH=tools/static-openssh ENV BINARYDIR=/usr/local/at diff --git a/tests/end2end_tests/image/Dockerfile b/tests/end2end_tests/image/Dockerfile index de4ce1090..9549143e0 100644 --- a/tests/end2end_tests/image/Dockerfile +++ b/tests/end2end_tests/image/Dockerfile @@ -1,5 +1,5 @@ # BASE -FROM debian:stable-20240926-slim@sha256:939e69ef5aa4dc178893a718ea567f1ca390df60793fd08c0bc7008362f72a57 AS base +FROM debian:stable-20241016-slim@sha256:fffe16098bcefa876d01862a61f8f30ef4292c9485940e905d41a15d8459828b AS base ENV USER=atsign ENV HOMEDIR=/${USER} @@ -26,7 +26,7 @@ RUN set -eux ; \ # BRANCH # BUILD BRANCH -FROM dart:3.5.2@sha256:93ce3d39d7ec4914f9035e853ebb6c09eb7c5ea20d06a62f28abb807d3513391 AS build-branch +FROM dart:3.5.2@sha256:9b7b8e5bc033862858cd0cae9bec360864517820dc4338c4dd537dd2e2f39f56 AS build-branch ENV URL=https://github.com/atsign-foundation/noports.git ENV REPO_DIR=/app/repo @@ -65,7 +65,7 @@ ENTRYPOINT cp -r /mount/. ${HOMEDIR} && sudo service ssh start && sh ${HOMEDIR}/ # LOCAL # BUILD LOCAL -FROM dart:3.5.2@sha256:93ce3d39d7ec4914f9035e853ebb6c09eb7c5ea20d06a62f28abb807d3513391 AS build-local +FROM dart:3.5.2@sha256:9b7b8e5bc033862858cd0cae9bec360864517820dc4338c4dd537dd2e2f39f56 AS build-local ENV REPO_DIR=/app/repo ENV PACKAGE_DIR=${REPO_DIR}/packages/dart/sshnoports @@ -101,7 +101,7 @@ ENTRYPOINT cp -r /mount/. ${HOMEDIR} && sudo service ssh start && sh ${HOMEDIR}/ # RELEASE # BUILD RELEASE -FROM debian:stable-20240926-slim@sha256:939e69ef5aa4dc178893a718ea567f1ca390df60793fd08c0bc7008362f72a57 AS build-release +FROM debian:stable-20241016-slim@sha256:fffe16098bcefa876d01862a61f8f30ef4292c9485940e905d41a15d8459828b AS build-release ARG release diff --git a/tools/multibuild/Dockerfile.package b/tools/multibuild/Dockerfile.package index 3417adcc0..92f60d8bd 100644 --- a/tools/multibuild/Dockerfile.package +++ b/tools/multibuild/Dockerfile.package @@ -1,7 +1,7 @@ # Dockerfile.package # A dockerfile for packaging SSH No Ports releases using docker buildx -FROM atsigncompany/buildimage:3.5.2@sha256:3edb21e4d12e11d7a7a9a52af694b739eb3579c4deff2aa1ca6c31699a8af64c AS build +FROM atsigncompany/buildimage:3.5.2_3.6.0-149.3.beta@sha256:df67b9e3271381fc0c5b20e7350cf4de8dad6ac62e075b49b1a866c49af47409 AS build # Using atsigncompany/buildimage until official dart image has RISC-V support # See https://github.com/atsign-company/at_dockerfiles for source and automated builds WORKDIR /noports From 206e50eb3b52a4ade7b8774456bb2a03983dc0e5 Mon Sep 17 00:00:00 2001 From: Chris Swan <478926+cpswan@users.noreply.github.com> Date: Thu, 17 Oct 2024 09:39:23 +0100 Subject: [PATCH 57/60] build(deps): Don't bump to older buildimage --- tools/multibuild/Dockerfile.package | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/multibuild/Dockerfile.package b/tools/multibuild/Dockerfile.package index 92f60d8bd..3417adcc0 100644 --- a/tools/multibuild/Dockerfile.package +++ b/tools/multibuild/Dockerfile.package @@ -1,7 +1,7 @@ # Dockerfile.package # A dockerfile for packaging SSH No Ports releases using docker buildx -FROM atsigncompany/buildimage:3.5.2_3.6.0-149.3.beta@sha256:df67b9e3271381fc0c5b20e7350cf4de8dad6ac62e075b49b1a866c49af47409 AS build +FROM atsigncompany/buildimage:3.5.2@sha256:3edb21e4d12e11d7a7a9a52af694b739eb3579c4deff2aa1ca6c31699a8af64c AS build # Using atsigncompany/buildimage until official dart image has RISC-V support # See https://github.com/atsign-company/at_dockerfiles for source and automated builds WORKDIR /noports From 61d6f25109e744dc8dd2f5bb59867f4a4d001b18 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Thu, 17 Oct 2024 19:30:12 -0400 Subject: [PATCH 58/60] feat: localizations added for remainder strings. --- packages/dart/npt_flutter/lib/app.dart | 3 ++- packages/dart/npt_flutter/lib/constants.dart | 9 ++++++++- .../onboarding/view/onboarding_view.dart | 7 +------ .../onboarding/widgets/onboarding_button.dart | 4 +++- .../widgets/profile_relay_quick_buttons.dart | 1 + .../lib/features/settings/models/settings.dart | 2 +- .../repository/settings_repository.dart | 4 +--- .../npt_flutter/lib/localization/app_en.arb | 5 ++--- .../npt_flutter/lib/localization/app_es.arb | 2 ++ .../npt_flutter/lib/localization/app_pt.arb | 2 ++ .../npt_flutter/lib/localization/app_pt_BR.arb | 2 ++ .../npt_flutter/lib/localization/app_zh.arb | 2 ++ .../lib/localization/app_zh_Hans_CH.arb | 2 ++ .../lib/localization/app_zh_Hant_HK.arb | 2 ++ packages/dart/npt_flutter/lib/util/relay.dart | 17 ----------------- 15 files changed, 31 insertions(+), 33 deletions(-) delete mode 100644 packages/dart/npt_flutter/lib/util/relay.dart diff --git a/packages/dart/npt_flutter/lib/app.dart b/packages/dart/npt_flutter/lib/app.dart index bbe67a1f8..56170cf10 100644 --- a/packages/dart/npt_flutter/lib/app.dart +++ b/packages/dart/npt_flutter/lib/app.dart @@ -107,12 +107,13 @@ class App extends StatelessWidget { defaultLocal = LanguageUtil.getLanguageFromLocale(Locale(Platform.localeName)).locale; } + return TrayManager( child: MaterialApp( theme: AppTheme.light(), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, - locale: defaultLocal, + locale: language != null ? language.locale : defaultLocal, localeResolutionCallback: (locale, supportedLocales) { return language != null ? language.locale : locale; }, diff --git a/packages/dart/npt_flutter/lib/constants.dart b/packages/dart/npt_flutter/lib/constants.dart index f16a5c1f1..1856b27f2 100644 --- a/packages/dart/npt_flutter/lib/constants.dart +++ b/packages/dart/npt_flutter/lib/constants.dart @@ -1,3 +1,6 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + class Constants { static String? get namespace => 'noports'; // TODO: issue & secure API key properly @@ -8,5 +11,9 @@ class Constants { static const pngIconLight = 'assets/noports-icon64-light.png'; static const icoIconLight = 'assets/noports-icon64-light.ico'; - static const languages = ['English', 'Spanish', 'Br portuguese', 'Mandarin', 'Cantonese']; + static Map getRootDomains(BuildContext context) { + AppLocalizations strings = AppLocalizations.of(context)!; + + return {'root.atsign.org': strings.rootDomainDefault, 'vip.ve.atsign.zone': strings.rootDomainDemo}; + } } diff --git a/packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart b/packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart index b9581ba05..f7fc2090a 100644 --- a/packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart +++ b/packages/dart/npt_flutter/lib/features/onboarding/view/onboarding_view.dart @@ -43,12 +43,7 @@ class OnboardingView extends StatelessWidget { bottom: Sizes.p44, right: Sizes.p44, ), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - CustomTextButton.resetAtsign(), - ], - ), + child: CustomTextButton.resetAtsign(), ), ) ], 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 68aa1b8a1..c96eb2c0e 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 @@ -1,4 +1,5 @@ import 'dart:developer'; +import 'dart:io'; import 'package:at_contacts_flutter/at_contacts_flutter.dart'; import 'package:at_onboarding_flutter/at_onboarding_flutter.dart'; @@ -11,6 +12,7 @@ import 'package:npt_flutter/features/onboarding/onboarding.dart'; import 'package:npt_flutter/features/onboarding/util/atsign_manager.dart'; import 'package:npt_flutter/features/onboarding/widgets/onboarding_dialog.dart'; import 'package:npt_flutter/routes.dart'; +import 'package:npt_flutter/util/language.dart'; import 'package:path_provider/path_provider.dart'; import 'package:phosphor_flutter/phosphor_flutter.dart'; @@ -64,7 +66,7 @@ class _OnboardingButtonState extends State { // hours for this tiny UX fix // TODO: fix localizations - await AtOnboardingLocalizations.load(const Locale("en")); + await AtOnboardingLocalizations.load(LanguageUtil.getLanguageFromLocale(Locale(Platform.localeName)).locale); onboardingResult = await Navigator.push( // ignore: use_build_context_synchronously context, diff --git a/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart b/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart index d9cfa9dbf..c4b563716 100644 --- a/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart +++ b/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:npt_flutter/features/features.dart'; import 'package:npt_flutter/features/profile_form/widgets/profile_relay_at_sign_text_field.dart'; +import 'package:npt_flutter/features/settings/models/settings.dart'; import 'package:npt_flutter/styles/sizes.dart'; import 'package:npt_flutter/widgets/custom_container.dart'; diff --git a/packages/dart/npt_flutter/lib/features/settings/models/settings.dart b/packages/dart/npt_flutter/lib/features/settings/models/settings.dart index 5fa003f09..c22c3aa80 100644 --- a/packages/dart/npt_flutter/lib/features/settings/models/settings.dart +++ b/packages/dart/npt_flutter/lib/features/settings/models/settings.dart @@ -29,7 +29,7 @@ class Settings extends Loggable { final Language language; const Settings({ - this.relayAtsign = '@rv_am', + required this.relayAtsign, required this.overrideRelay, required this.viewLayout, this.darkMode = false, diff --git a/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart b/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart index c7c863a32..942bb20aa 100644 --- a/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart +++ b/packages/dart/npt_flutter/lib/features/settings/repository/settings_repository.dart @@ -16,9 +16,7 @@ class SettingsRepository { viewLayout: PreferredViewLayout.minimal, overrideRelay: false, // set the default language to the device's language - language: LanguageUtil.getLanguageFromLocale( - Locale(Platform.localeName.split('_').first), - ), + language: LanguageUtil.getLanguageFromLocale(Locale(Platform.localeName)), ); Future getSettings() async { diff --git a/packages/dart/npt_flutter/lib/localization/app_en.arb b/packages/dart/npt_flutter/lib/localization/app_en.arb index 5f62fa06e..7e3099b8c 100644 --- a/packages/dart/npt_flutter/lib/localization/app_en.arb +++ b/packages/dart/npt_flutter/lib/localization/app_en.arb @@ -72,9 +72,8 @@ "remotePort" : "Remote Port", "remotePortDescription" : "", "resetAtsign" : "Reset Atsign", - "rvAmDisplayName" : "Americas", - "rvApDisplayName" : "Asia-Pacific", - "rvEuDisplayName" : "Europe", + "rootDomainDefault" : "Default (Prod)", + "rootDomainDemo" : "Demo (VE)", "selectExportFile": "Please select a file to export to:", "selectRootDomain" : "Select Root Domain", "serviceMapping" : "Service Mapping", diff --git a/packages/dart/npt_flutter/lib/localization/app_es.arb b/packages/dart/npt_flutter/lib/localization/app_es.arb index 684c788d9..3b769b07e 100644 --- a/packages/dart/npt_flutter/lib/localization/app_es.arb +++ b/packages/dart/npt_flutter/lib/localization/app_es.arb @@ -72,6 +72,8 @@ "remotePort" : "Puerto remoto", "remotePortDescription" : "", "resetAtsign" : "Restablecer Atsign", + "rootDomainDefault" : "Predeterminado (Producción)", + "rootDomainDemo" : "Demostración (VE)", "selectExportFile": "Por favor seleccione un archivo para exportar a:", "selectRootDomain" : "Seleccionar dominio raíz", "serviceMapping" : "Mapeo de servicios", diff --git a/packages/dart/npt_flutter/lib/localization/app_pt.arb b/packages/dart/npt_flutter/lib/localization/app_pt.arb index 0ba04386e..d2a3d660a 100644 --- a/packages/dart/npt_flutter/lib/localization/app_pt.arb +++ b/packages/dart/npt_flutter/lib/localization/app_pt.arb @@ -72,6 +72,8 @@ "remotePort" : "Porta Remota", "remotePortDescription" : "", "resetAtsign" : "Redefinir Atsign", + "rootDomainDefault" : "Padrão (Prod)", + "rootDomainDemo" : "Demo (VE)", "selectExportFile": "Por favor, selecione um arquivo para exportar:", "selectRootDomain" : "Selecione o Domínio Raiz", "serviceMapping" : "Mapeamento de Serviço", diff --git a/packages/dart/npt_flutter/lib/localization/app_pt_BR.arb b/packages/dart/npt_flutter/lib/localization/app_pt_BR.arb index 0ba04386e..d2a3d660a 100644 --- a/packages/dart/npt_flutter/lib/localization/app_pt_BR.arb +++ b/packages/dart/npt_flutter/lib/localization/app_pt_BR.arb @@ -72,6 +72,8 @@ "remotePort" : "Porta Remota", "remotePortDescription" : "", "resetAtsign" : "Redefinir Atsign", + "rootDomainDefault" : "Padrão (Prod)", + "rootDomainDemo" : "Demo (VE)", "selectExportFile": "Por favor, selecione um arquivo para exportar:", "selectRootDomain" : "Selecione o Domínio Raiz", "serviceMapping" : "Mapeamento de Serviço", diff --git a/packages/dart/npt_flutter/lib/localization/app_zh.arb b/packages/dart/npt_flutter/lib/localization/app_zh.arb index 2b9807cce..adffa2b42 100644 --- a/packages/dart/npt_flutter/lib/localization/app_zh.arb +++ b/packages/dart/npt_flutter/lib/localization/app_zh.arb @@ -73,6 +73,8 @@ "remotePort" : "远程端口", "remotePortDescription" : "", "resetAtsign" : "重置Atsign", + "rootDomainDefault" : "默认(生产)", + "rootDomainDemo" : "演示(VE)", "selectExportFile": "请选择要导出的文件:", "selectRootDomain" : "选择根域", "serviceMapping" : "服务映射", diff --git a/packages/dart/npt_flutter/lib/localization/app_zh_Hans_CH.arb b/packages/dart/npt_flutter/lib/localization/app_zh_Hans_CH.arb index 1f5b2528b..912ebd1f9 100644 --- a/packages/dart/npt_flutter/lib/localization/app_zh_Hans_CH.arb +++ b/packages/dart/npt_flutter/lib/localization/app_zh_Hans_CH.arb @@ -73,6 +73,8 @@ "remotePort" : "远程端口", "remotePortDescription" : "", "resetAtsign" : "重置Atsign", + "rootDomainDefault" : "默认(生产)", + "rootDomainDemo" : "演示(VE)", "selectExportFile": "请选择要导出的文件:", "selectRootDomain" : "选择根域", "serviceMapping" : "服务映射", diff --git a/packages/dart/npt_flutter/lib/localization/app_zh_Hant_HK.arb b/packages/dart/npt_flutter/lib/localization/app_zh_Hant_HK.arb index 760dc83d6..dc3a34968 100644 --- a/packages/dart/npt_flutter/lib/localization/app_zh_Hant_HK.arb +++ b/packages/dart/npt_flutter/lib/localization/app_zh_Hant_HK.arb @@ -72,6 +72,8 @@ "remotePort" : "遠程端口", "remotePortDescription" : "", "resetAtsign" : "重置Atsign", + "rootDomainDefault" : "默認(生產)", + "rootDomainDemo" : "演示(VE)", "selectExportFile": "請選擇要導出的文件:", "selectRootDomain" : "選擇根域", "serviceMapping" : "服務映射", diff --git a/packages/dart/npt_flutter/lib/util/relay.dart b/packages/dart/npt_flutter/lib/util/relay.dart deleted file mode 100644 index 4df31f2f9..000000000 --- a/packages/dart/npt_flutter/lib/util/relay.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter/material.dart'; - -class RelayUtil { - static Map getRelayDisplayNameMap(BuildContext context) { - final strings = AppLocalizations.of(context)!; - return { - "@rv_am": strings.rvAmDisplayName, - "@rv_ap": strings.rvApDisplayName, - "@rv_eu": strings.rvEuDisplayName, - }; - } - - static List getRelayAtsignList() { - return ["@rv_am", "@rv_ap", "@rv_eu"]; - } -} From 348de42e907eeb4f85581da2fb8e09efa5c433ee Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Fri, 18 Oct 2024 10:25:17 -0400 Subject: [PATCH 59/60] fix: removed code no longer needed. --- .../onboarding/widgets/onboarding_button.dart | 31 +++++++++---------- .../lib/widgets/custom_text_button.dart | 14 --------- 2 files changed, 14 insertions(+), 31 deletions(-) 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 c96eb2c0e..cb835a271 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 @@ -147,22 +147,19 @@ class _OnboardingButtonState extends State { @override Widget build(BuildContext context) { final strings = AppLocalizations.of(context)!; - return BlocBuilder(builder: (context, atsignInformation) { - return ElevatedButton.icon( - onPressed: () async { - final isEmptyAtsignList = (await getAtsignEntries()).isNotEmpty; - - bool proceedToOnboard = false; - if (isEmptyAtsignList) proceedToOnboard = await selectAtsign(); - - if (proceedToOnboard) onboard(rootDomain: atsignInformation.rootDomain); - }, - icon: PhosphorIcon(PhosphorIcons.arrowUpRight()), - label: Text( - strings.getStarted, - ), - iconAlignment: IconAlignment.end, - ); - }); + return ElevatedButton.icon( + onPressed: () async { + bool shouldOnboard = await selectAtsign(); + if (shouldOnboard && context.mounted) { + var atsignInformation = context.read().state; + onboard(atsign: atsignInformation.atSign, rootDomain: atsignInformation.rootDomain); + } + }, + icon: PhosphorIcon(PhosphorIcons.arrowUpRight()), + label: Text( + strings.getStarted, + ), + iconAlignment: IconAlignment.end, + ); } } 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 fad7cb223..237249600 100644 --- a/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart +++ b/packages/dart/npt_flutter/lib/widgets/custom_text_button.dart @@ -165,18 +165,6 @@ class CustomTextButton extends StatelessWidget { await preSignout(); if (context.mounted) Navigator.of(context).pushReplacementNamed(Routes.onboarding); break; - case CustomListTileType.selectRootDomain: - await showDialog( - context: context, - builder: (BuildContext context) => const AtDirectoryDialog(), - ); - break; - case CustomListTileType.selectRootDomain: - await showDialog( - context: context, - builder: (BuildContext context) => const AtDirectoryDialog(), - ); - break; } } @@ -200,8 +188,6 @@ class CustomTextButton extends StatelessWidget { return strings.feedback; case CustomListTileType.signOut: return strings.signout; - case CustomListTileType.selectRootDomain: - return strings.selectRootDomain; } } From 2557dece9e3808d784d90351f1d74cd74f247065 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Fri, 18 Oct 2024 10:59:22 -0400 Subject: [PATCH 60/60] fix: added missing ) due to merge conflict resolution. --- .../widgets/profile_relay_quick_buttons.dart | 4 ++-- .../widgets/settings_relay_quick_buttons.dart | 3 ++- packages/dart/npt_flutter/lib/util/relay.dart | 17 ----------------- 3 files changed, 4 insertions(+), 20 deletions(-) delete mode 100644 packages/dart/npt_flutter/lib/util/relay.dart diff --git a/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart b/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart index bf7d00f0a..d9cfa9dbf 100644 --- a/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart +++ b/packages/dart/npt_flutter/lib/features/profile_form/widgets/profile_relay_quick_buttons.dart @@ -3,9 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:npt_flutter/features/features.dart'; import 'package:npt_flutter/features/profile_form/widgets/profile_relay_at_sign_text_field.dart'; -import 'package:npt_flutter/features/settings/models/settings.dart'; import 'package:npt_flutter/styles/sizes.dart'; -import 'package:npt_flutter/util/relay.dart'; import 'package:npt_flutter/widgets/custom_container.dart'; class ProfileRelayQuickButtons extends StatelessWidget { @@ -66,6 +64,8 @@ class ProfileRelayQuickButtons extends StatelessWidget { ), ), ), + ), + ), const ProfileRelayAtSignTextField(), ], ), diff --git a/packages/dart/npt_flutter/lib/features/settings/widgets/settings_relay_quick_buttons.dart b/packages/dart/npt_flutter/lib/features/settings/widgets/settings_relay_quick_buttons.dart index 3517e99bf..cd6228752 100644 --- a/packages/dart/npt_flutter/lib/features/settings/widgets/settings_relay_quick_buttons.dart +++ b/packages/dart/npt_flutter/lib/features/settings/widgets/settings_relay_quick_buttons.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:npt_flutter/features/settings/settings.dart'; -import 'package:npt_flutter/util/relay.dart'; import 'package:npt_flutter/widgets/custom_container.dart'; import '../../../styles/sizes.dart'; @@ -50,6 +49,8 @@ class SettingsRelayQuickButtons extends StatelessWidget { ), ), ), + ), + ), const SettingsRelayAtSignTextField(), ], ), diff --git a/packages/dart/npt_flutter/lib/util/relay.dart b/packages/dart/npt_flutter/lib/util/relay.dart deleted file mode 100644 index 4df31f2f9..000000000 --- a/packages/dart/npt_flutter/lib/util/relay.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:flutter/material.dart'; - -class RelayUtil { - static Map getRelayDisplayNameMap(BuildContext context) { - final strings = AppLocalizations.of(context)!; - return { - "@rv_am": strings.rvAmDisplayName, - "@rv_ap": strings.rvApDisplayName, - "@rv_eu": strings.rvEuDisplayName, - }; - } - - static List getRelayAtsignList() { - return ["@rv_am", "@rv_ap", "@rv_eu"]; - } -}