From 6c0ea3d569a138b159d8e82d1e226e8352749f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=9Al=C4=99zak?= Date: Fri, 9 Oct 2026 16:43:31 +0200 Subject: [PATCH 1/2] review fixes --- client/assets/next/img/connected_instance.svg | 11 + client/assets/next/img/idle_instance.svg | 5 + .../next/rive/{openid_mfa.riv => open_id.riv} | Bin client/lib/enterprise/config_update.dart | 4 +- .../mfa/openid_mfa_waiting_screen.dart | 9 +- .../screens/add_instance_form.dart | 4 + .../screens/add_instance_qr_screen.dart | 8 + .../screens/instance/instance_screen.dart | 10 +- .../instance/refresh_instance_screen.dart | 205 ++++++++++++++++++ .../widgets/refresh_instance_dialog.dart | 125 ----------- client/lib/open/widgets/dg_instance_card.dart | 20 +- client/lib/open/widgets/dg_location_card.dart | 58 +++-- .../open/widgets/dg_openid_mfa_layout.dart | 6 +- client/lib/router/routes.dart | 15 ++ client/lib/router/routes.g.dart | 32 +++ 15 files changed, 330 insertions(+), 182 deletions(-) create mode 100644 client/assets/next/img/connected_instance.svg create mode 100644 client/assets/next/img/idle_instance.svg rename client/assets/next/rive/{openid_mfa.riv => open_id.riv} (100%) create mode 100644 client/lib/open/screens/instance/refresh_instance_screen.dart delete mode 100644 client/lib/open/screens/instance/widgets/refresh_instance_dialog.dart diff --git a/client/assets/next/img/connected_instance.svg b/client/assets/next/img/connected_instance.svg new file mode 100644 index 0000000..e37cb01 --- /dev/null +++ b/client/assets/next/img/connected_instance.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/client/assets/next/img/idle_instance.svg b/client/assets/next/img/idle_instance.svg new file mode 100644 index 0000000..3813f19 --- /dev/null +++ b/client/assets/next/img/idle_instance.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/client/assets/next/rive/openid_mfa.riv b/client/assets/next/rive/open_id.riv similarity index 100% rename from client/assets/next/rive/openid_mfa.riv rename to client/assets/next/rive/open_id.riv diff --git a/client/lib/enterprise/config_update.dart b/client/lib/enterprise/config_update.dart index 0186ae6..326b538 100644 --- a/client/lib/enterprise/config_update.dart +++ b/client/lib/enterprise/config_update.dart @@ -118,9 +118,7 @@ class ConfigurationUpdater extends HookConsumerWidget { instance.name, updateResult, ); - toaster.show( - message: "Instance ${instance.name} updated: $message", - ); + talker.info("Instance ${instance.logName} updated: $message"); } } } catch (e) { diff --git a/client/lib/enterprise/screens/mfa/openid_mfa_waiting_screen.dart b/client/lib/enterprise/screens/mfa/openid_mfa_waiting_screen.dart index 3a8fe63..e34881b 100644 --- a/client/lib/enterprise/screens/mfa/openid_mfa_waiting_screen.dart +++ b/client/lib/enterprise/screens/mfa/openid_mfa_waiting_screen.dart @@ -9,6 +9,7 @@ import 'package:mobile/open/widgets/dg_app_bar.dart'; import 'package:mobile/open/widgets/dg_mfa_step_label.dart'; import 'package:mobile/open/widgets/dg_button.dart'; import 'package:mobile/open/widgets/dg_icon_button.dart'; +import 'package:mobile/open/widgets/rive_asset_animation.dart'; import 'package:mobile/open/widgets/toaster/toast_manager.dart'; import 'package:mobile/theme/color.dart'; import 'package:mobile/theme/text.dart'; @@ -117,9 +118,13 @@ class OpenIdMfaWaitingScreen extends HookConsumerWidget { const SizedBox(height: 8), Text( "Waiting for authentication in your browser...", - style: DgText.bodyXs400.copyWith(color: DgColor.fgWhite60), + style: DgText.bodySm400.copyWith(color: DgColor.fgWhite60), + ), + const Expanded( + child: RiveAssetAnimation( + "assets/next/rive/open_id.riv", + ), ), - const Spacer(), DgButton( text: 'Cancel', style: DgButtonStyle.outlined, diff --git a/client/lib/open/screens/add_instance/screens/add_instance_form.dart b/client/lib/open/screens/add_instance/screens/add_instance_form.dart index 09d07e2..b80b0ab 100644 --- a/client/lib/open/screens/add_instance/screens/add_instance_form.dart +++ b/client/lib/open/screens/add_instance/screens/add_instance_form.dart @@ -181,6 +181,10 @@ Future _handleSubmit( final requestData = EnrollmentStartRequest(token: token); final uri = Uri.parse(url); final enrolmentResponse = await proxyApi.startEnrollment(uri, requestData); + if (!enrolmentResponse.user.enrolled) { + toaster.showError(message: "Only enrolled users can add devices."); + return; + } final instanceId = enrolmentResponse.instance.id; final dbInstance = await db.managers.defguardInstances .filter((row) => row.uuid.equals(instanceId)) diff --git a/client/lib/open/screens/add_instance/screens/add_instance_qr_screen.dart b/client/lib/open/screens/add_instance/screens/add_instance_qr_screen.dart index cfa49cf..c101649 100644 --- a/client/lib/open/screens/add_instance/screens/add_instance_qr_screen.dart +++ b/client/lib/open/screens/add_instance/screens/add_instance_qr_screen.dart @@ -40,6 +40,14 @@ class AddInstanceQrScreen extends HookConsumerWidget { url, requestData, ); + if (!registrationResponse.user.enrolled) { + toaster.showError( + message: "Only enrolled users can add devices.", + ); + isLoading.value = false; + await controller.resume(); + return; + } final instanceId = registrationResponse.instance.id; final dbInstance = await db.managers.defguardInstances .filter((row) => row.uuid.equals(instanceId)) diff --git a/client/lib/open/screens/instance/instance_screen.dart b/client/lib/open/screens/instance/instance_screen.dart index 9f38415..f0701fa 100644 --- a/client/lib/open/screens/instance/instance_screen.dart +++ b/client/lib/open/screens/instance/instance_screen.dart @@ -12,11 +12,11 @@ import 'package:mobile/open/api.dart'; import 'package:mobile/open/riverpod/biometrics_state.dart'; import 'package:mobile/open/riverpod/plugin/plugin.dart'; import 'package:mobile/open/screens/add_instance/data_gathering_dialog.dart'; +import 'package:mobile/open/screens/instance/refresh_instance_screen.dart'; import 'package:mobile/open/screens/instance/services/tunnel_service.dart'; import 'package:mobile/open/screens/instance/widgets/connect_dialog.dart'; import 'package:mobile/open/screens/instance/widgets/connection_conflict_dialog.dart'; import 'package:mobile/open/screens/instance/widgets/delete_instance_dialog.dart'; -import 'package:mobile/open/screens/instance/widgets/refresh_instance_dialog.dart'; import 'package:mobile/open/screens/mfa/remote_mfa_qr_screen.dart'; import 'package:mobile/open/widgets/dg_app_bar.dart'; import 'package:mobile/open/widgets/dg_bottom_sheet.dart'; @@ -364,11 +364,9 @@ class _InstanceAppBar extends ConsumerWidget implements PreferredSizeWidget { onTap: () { final instance = screenDataAsync.value?.instance; if (instance == null) return; - showDialog( - context: context, - builder: (context) => - RefreshInstanceDialog(instance: instance), - ); + RefreshInstanceScreenRoute( + RefreshInstanceScreenData(instance: instance), + ).push(context); }, ), DgMenuItem( diff --git a/client/lib/open/screens/instance/refresh_instance_screen.dart b/client/lib/open/screens/instance/refresh_instance_screen.dart new file mode 100644 index 0000000..7061b8e --- /dev/null +++ b/client/lib/open/screens/instance/refresh_instance_screen.dart @@ -0,0 +1,205 @@ +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:material_ui/material_ui.dart'; +import 'package:mobile/data/db/database.dart'; +import 'package:mobile/data/proxy/enrollment.dart'; +import 'package:mobile/logging.dart'; +import 'package:mobile/open/api.dart'; +import 'package:mobile/open/widgets/dg_app_bar.dart'; +import 'package:mobile/open/widgets/dg_button.dart'; +import 'package:mobile/open/widgets/dg_icon_button.dart'; +import 'package:mobile/open/widgets/dg_text_form_field.dart'; +import 'package:mobile/open/widgets/toaster/toast_manager.dart'; +import 'package:mobile/theme/color.dart'; +import 'package:mobile/theme/spacing.dart'; +import 'package:mobile/theme/text.dart'; +import 'package:mobile/utils/update_instance.dart'; + +class RefreshInstanceScreenData { + final DefguardInstance instance; + + const RefreshInstanceScreenData({required this.instance}); +} + +class RefreshInstanceScreen extends StatelessWidget { + final DefguardInstance instance; + + const RefreshInstanceScreen({super.key, required this.instance}); + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBodyBehindAppBar: true, + appBar: DgAppBar( + context: context, + showLogo: false, + actionLeft: DgIconButton( + icon: "close", + onTap: () => Navigator.of(context).pop(), + ), + ), + body: Container( + decoration: const BoxDecoration(gradient: DgColor.gradientPrimary), + child: SafeArea(child: _RefreshInstanceContent(instance: instance)), + ), + ); + } +} + +class _RefreshInstanceContent extends HookConsumerWidget { + final DefguardInstance instance; + + const _RefreshInstanceContent({required this.instance}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final db = ref.read(databaseProvider); + final toaster = ref.read(toastManagerProvider.notifier); + final formKey = useMemoized(() => GlobalKey()); + final tokenController = useTextEditingController(); + final urlController = useTextEditingController(text: instance.proxyUrl); + final isLoading = useState(false); + + String? validateUrl(String? value) { + if (value == null || value.trim().isEmpty) { + return "This field is required"; + } + if (!_isValidUri(value.trim())) { + return "Enter valid URL"; + } + return null; + } + + Future submit() async { + if (!(formKey.currentState?.validate() ?? false)) return; + isLoading.value = true; + try { + final refreshed = await _refreshInstance( + db, + toaster, + instance, + Uri.parse(urlController.text.trim()), + tokenController.text.trim(), + ); + if (refreshed && context.mounted) { + Navigator.of(context).pop(); + } + } finally { + if (context.mounted) isLoading.value = false; + } + } + + return Form( + key: formKey, + child: CustomScrollView( + slivers: [ + SliverPadding( + padding: const .fromLTRB(20, 12, 20, 0), + sliver: SliverList( + delegate: SliverChildListDelegate([ + Semantics( + identifier: "refresh_instance_header", + child: Text( + "Refresh Instance", + style: DgText.h4.copyWith(color: DgColor.fgWhite100), + textAlign: .left, + ), + ), + const SizedBox(height: DgSpacing.sm), + Text( + "Enter your proxy URL and instance token to refresh the configuration.", + style: DgText.bodySm400.copyWith(color: DgColor.fgWhite60), + textAlign: .left, + ), + const SizedBox(height: DgSpacing.xl3), + DgTextFormField( + identifier: "refresh_instance_token", + size: .big, + controller: tokenController, + label: "Token", + required: true, + hintText: "Instance token", + validator: (v) => v == null || v.trim().isEmpty + ? "This field is required" + : null, + ), + const SizedBox(height: DgSpacing.xl), + DgTextFormField( + identifier: "refresh_instance_url", + size: .big, + controller: urlController, + label: "URL", + required: true, + hintText: "Proxy URL", + keyboardType: .url, + validator: validateUrl, + ), + ]), + ), + ), + SliverFillRemaining( + hasScrollBody: false, + child: Padding( + padding: const .fromLTRB(20, 0, 20, 20), + child: Align( + alignment: .bottomCenter, + child: DgButton( + identifier: "refresh_instance_submit", + text: "Refresh", + style: .primary, + size: .big, + width: double.infinity, + loading: isLoading.value, + onTap: submit, + ), + ), + ), + ), + ], + ), + ); + } +} + +bool _isValidUri(String value) { + final uri = Uri.tryParse(value); + return uri != null && uri.hasScheme && uri.hasAuthority; +} + +Future _refreshInstance( + AppDatabase db, + ToastManager toaster, + DefguardInstance instance, + Uri uri, + String token, +) async { + talker.debug("Submitting instance refresh form"); + try { + // this is only for dio to capture cookies required for network info call + await proxyApi.startEnrollment(uri, EnrollmentStartRequest(token: token)); + final networkInfo = await proxyApi.networkInfo(uri, instance.pubKey); + talker.debug("Retrieved new instance information from proxy"); + final updateResult = await updateInstance( + db: db, + instance: instance, + configs: networkInfo.configs, + info: networkInfo.instance, + token: networkInfo.token, + ); + if (updateResult != null && updateResult.didChange) { + final message = getInstanceUpdateMessage(instance.name, updateResult); + toaster.show(message: "Instance ${instance.name} updated: $message"); + } else { + toaster.show(message: "Instance information refreshed"); + } + talker.info("Instance information refreshed successfully"); + return true; + } catch (e, st) { + toaster.showError( + message: "Failed to refresh instance information", + error: e, + stackTrace: st, + ); + return false; + } +} diff --git a/client/lib/open/screens/instance/widgets/refresh_instance_dialog.dart b/client/lib/open/screens/instance/widgets/refresh_instance_dialog.dart deleted file mode 100644 index 415dc85..0000000 --- a/client/lib/open/screens/instance/widgets/refresh_instance_dialog.dart +++ /dev/null @@ -1,125 +0,0 @@ -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:material_ui/material_ui.dart'; -import 'package:mobile/data/db/database.dart'; -import 'package:mobile/data/proxy/enrollment.dart'; -import 'package:mobile/logging.dart'; -import 'package:mobile/open/api.dart'; -import 'package:mobile/open/widgets/dg_button.dart'; -import 'package:mobile/open/widgets/dg_dialog.dart'; -import 'package:mobile/open/widgets/dg_text_form_field.dart'; -import 'package:mobile/open/widgets/toaster/toast_manager.dart'; -import 'package:mobile/theme/spacing.dart'; -import 'package:mobile/utils/update_instance.dart'; - -class RefreshInstanceDialog extends HookConsumerWidget { - final DefguardInstance instance; - - const RefreshInstanceDialog({super.key, required this.instance}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final db = ref.read(databaseProvider); - final toaster = ref.read(toastManagerProvider.notifier); - final proxyUrlController = useTextEditingController( - text: instance.proxyUrl, - ); - final tokenController = useTextEditingController(); - final isLoading = useState(false); - - final submit = useCallback(() async { - final url = proxyUrlController.text; - final token = tokenController.text; - final uri = Uri.parse(url); - talker.debug("Submitting instance refresh form"); - - try { - // this is only for dio to capture cookies required for network info call - await proxyApi.startEnrollment( - uri, - EnrollmentStartRequest(token: token), - ); - final networkInfo = await proxyApi.networkInfo(uri, instance.pubKey); - talker.debug("Retrieved new instance information from proxy"); - talker.debug("Updating instance info in DB"); - final updateResult = await updateInstance( - db: db, - instance: instance, - configs: networkInfo.configs, - info: networkInfo.instance, - token: networkInfo.token, - ); - if (updateResult != null && updateResult.didChange) { - final message = getInstanceUpdateMessage(instance.name, updateResult); - toaster.show( - message: "Instance ${instance.name} updated: $message", - ); - } else { - toaster.show(message: "Instance information refreshed"); - } - talker.info("Instance information refreshed successfully"); - } catch (e) { - toaster.showError( - message: "Failed to refresh instance information", - error: e, - ); - rethrow; - } - }, [db, toaster, proxyUrlController, tokenController, isLoading]); - - return DgDialog( - onClose: () => Navigator.of(context).pop(), - children: [ - const DgDialogTitle("Refresh Instance"), - const DgDialogDescription( - "Enter your proxy URL and instance token to refresh the configuration.", - ), - DgTextFormField( - identifier: "refresh_instance_url", - controller: proxyUrlController, - label: "Proxy URL", - hintText: "https://...", - ), - const SizedBox(height: DgSpacing.md), - DgTextFormField( - identifier: "refresh_instance_token", - controller: tokenController, - label: "Instance Token", - hintText: "Enter token", - ), - const SizedBox(height: DgSpacing.xl2), - Row( - spacing: DgSpacing.md, - children: [ - Expanded( - child: DgButton( - text: "Cancel", - style: DgButtonStyle.secondary, - onTap: () => Navigator.of(context).pop(), - ), - ), - Expanded( - child: DgButton( - identifier: "refresh_instance_submit", - text: "Refresh", - style: DgButtonStyle.primary, - loading: isLoading.value, - onTap: () async { - isLoading.value = true; - try { - await submit(); - if (context.mounted) { - Navigator.of(context).pop(); - } - } finally { - isLoading.value = false; - } - }, - ), - ), - ], - ), - ], - ); - } -} diff --git a/client/lib/open/widgets/dg_instance_card.dart b/client/lib/open/widgets/dg_instance_card.dart index 3a4b66c..2caa6c2 100644 --- a/client/lib/open/widgets/dg_instance_card.dart +++ b/client/lib/open/widgets/dg_instance_card.dart @@ -1,5 +1,6 @@ -import 'package:material_ui/material_ui.dart'; import 'package:flutter/widget_previews.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:mobile/open/widgets/dg_preview_wrapper.dart'; import 'package:mobile/theme/color.dart'; import 'package:mobile/theme/spacing.dart'; @@ -22,7 +23,9 @@ class DgInstanceCard extends StatelessWidget { }); bool get isConnected => connectedCount != 0; - String get icon => isConnected ? "connected_devices" : "device_ip"; + String get icon => isConnected + ? "assets/next/img/connected_instance.svg" + : "assets/next/img/idle_instance.svg"; @override Widget build(BuildContext context) { @@ -38,19 +41,10 @@ class DgInstanceCard extends StatelessWidget { crossAxisAlignment: .center, mainAxisAlignment: .start, children: [ - Container( + SizedBox( height: 48, width: 48, - alignment: .center, - decoration: BoxDecoration( - borderRadius: .circular(14), - color: isConnected ? DgColor.bgWhite100 : DgColor.bgDarkBlue20, - ), - child: DgIcon( - icon, - size: 24, - color: isConnected ? DgColor.fgAction : DgColor.fgWhite60, - ), + child: SvgPicture.asset(icon), ), SizedBox(width: DgSpacing.xl), Expanded( diff --git a/client/lib/open/widgets/dg_location_card.dart b/client/lib/open/widgets/dg_location_card.dart index 509d489..c463beb 100644 --- a/client/lib/open/widgets/dg_location_card.dart +++ b/client/lib/open/widgets/dg_location_card.dart @@ -306,43 +306,39 @@ class _InnerInfoCard extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: const EdgeInsets.symmetric( - vertical: DgSpacing.sm, - horizontal: DgSpacing.md, - ), + padding: const .all(DgSpacing.md), decoration: BoxDecoration( color: DgColor.bgWhite5, - borderRadius: BorderRadius.circular(12), + borderRadius: .circular(12), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: DgSpacing.md, + child: Column( + mainAxisSize: .min, + crossAxisAlignment: .start, children: [ Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), + borderRadius: .circular(6), color: DgColor.bgWhite10, ), - width: 36, - height: 36, - alignment: Alignment.center, + width: 24, + height: 24, + alignment: .center, child: getIcon(), ), - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 2, - children: [ - Text( - getLabel(), - style: DgText.bodyXxs400.copyWith(color: DgColor.fgWhite50), - ), - Text( - getText(), - style: DgText.bodyXs500.copyWith(color: getTextColor()), - ), - ], + const SizedBox(height: DgSpacing.md), + Text( + getLabel(), + style: DgText.bodyXxs400.copyWith(color: DgColor.fgWhite50), + ), + const SizedBox(height: 2), + FittedBox( + fit: .scaleDown, + alignment: .centerLeft, + child: Text( + getText(), + maxLines: 1, + style: DgText.bodyXs500.copyWith(color: getTextColor()), + ), ), ], ), @@ -379,17 +375,17 @@ class _InnerInfoCard extends StatelessWidget { break; } } - return DgIcon(iconFileName, size: 20, color: iconColor); + return DgIcon(iconFileName, size: 16, color: iconColor); } String getLabel() { if (isMfa) { - return "MFA"; + return "Active MFA"; } if (isRouting) { - return "Traffic"; + return "Allowed traffic"; } - return "MFA"; + return "Active MFA"; } String getText() { diff --git a/client/lib/open/widgets/dg_openid_mfa_layout.dart b/client/lib/open/widgets/dg_openid_mfa_layout.dart index 8506174..2f8976d 100644 --- a/client/lib/open/widgets/dg_openid_mfa_layout.dart +++ b/client/lib/open/widgets/dg_openid_mfa_layout.dart @@ -64,8 +64,10 @@ class DgOpenIdMfaLayout extends StatelessWidget { "Confirm your identity to continue. You'll be redirected to your identity provider to complete verification.", style: DgText.bodySm400.copyWith(color: DgColor.fgWhite60), ), - Expanded( - child: RiveAssetAnimation("assets/next/rive/openid_mfa.riv"), + const Expanded( + child: RiveAssetAnimation( + "assets/next/rive/open_id.riv", + ), ), DgExternalProviderButton( text: 'Continue with $providerName', diff --git a/client/lib/router/routes.dart b/client/lib/router/routes.dart index 770f6aa..7a86224 100644 --- a/client/lib/router/routes.dart +++ b/client/lib/router/routes.dart @@ -7,6 +7,7 @@ import 'package:mobile/open/screens/add_instance/screens/biometry/biometry_finis import 'package:mobile/open/screens/add_instance/screens/biometry/biometry_setup_failed_screen.dart'; import 'package:mobile/open/screens/add_instance/screens/biometry/biometry_setup_screen.dart'; import 'package:mobile/open/screens/add_instance/screens/name_device_screen.dart'; +import 'package:mobile/open/screens/instance/refresh_instance_screen.dart'; import 'package:mobile/open/screens/instances_list/instances_list_screen.dart'; import 'package:mobile/open/screens/mfa/remote_mfa_qr_screen.dart'; import 'package:mobile/open/screens/splash.dart'; @@ -85,6 +86,20 @@ class InstanceScreenRoute extends GoRouteData with $InstanceScreenRoute { } } +@TypedGoRoute(path: "/refresh_instance") +@immutable +class RefreshInstanceScreenRoute extends GoRouteData + with $RefreshInstanceScreenRoute { + const RefreshInstanceScreenRoute(this.$extra); + + final RefreshInstanceScreenData $extra; + + @override + Widget build(BuildContext context, GoRouterState state) { + return RefreshInstanceScreen(instance: $extra.instance); + } +} + @TypedGoRoute(path: "/add_instance/name_device") @immutable class NameDeviceScreenRoute extends GoRouteData with $NameDeviceScreenRoute { diff --git a/client/lib/router/routes.g.dart b/client/lib/router/routes.g.dart index 5e45d8f..4f91a2a 100644 --- a/client/lib/router/routes.g.dart +++ b/client/lib/router/routes.g.dart @@ -12,6 +12,7 @@ List get $appRoutes => [ $addInstanceQrScreenRoute, $remoteMfaQrScreenRoute, $instanceScreenRoute, + $refreshInstanceScreenRoute, $nameDeviceScreenRoute, $addInstanceFormScreenRoute, $addInstanceScreenRoute, @@ -253,6 +254,37 @@ mixin $BiometryFinishScreenRoute on GoRouteData { void replace(BuildContext context) => context.replace(location); } +RouteBase get $refreshInstanceScreenRoute => GoRouteData.$route( + path: '/refresh_instance', + hasOverriddenOnExit: false, + factory: $RefreshInstanceScreenRoute._fromState, +); + +mixin $RefreshInstanceScreenRoute on GoRouteData { + static RefreshInstanceScreenRoute _fromState(GoRouterState state) => + RefreshInstanceScreenRoute(state.extra as RefreshInstanceScreenData); + + RefreshInstanceScreenRoute get _self => this as RefreshInstanceScreenRoute; + + @override + String get location => GoRouteData.$location('/refresh_instance'); + + @override + void go(BuildContext context) => context.go(location, extra: _self.$extra); + + @override + Future push(BuildContext context) => + context.push(location, extra: _self.$extra); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location, extra: _self.$extra); + + @override + void replace(BuildContext context) => + context.replace(location, extra: _self.$extra); +} + RouteBase get $nameDeviceScreenRoute => GoRouteData.$route( path: '/add_instance/name_device', hasOverriddenOnExit: false, From 3b00aa64ec2ce16cb33017e543cbc0556950129f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=9Al=C4=99zak?= Date: Mon, 12 Oct 2026 11:03:10 +0200 Subject: [PATCH 2/2] fix e2e --- e2e/helpers/coreApi.ts | 7 ++++--- e2e/tests/instance-refresh.spec.ts | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/e2e/helpers/coreApi.ts b/e2e/helpers/coreApi.ts index 98a5fe5..8f5f048 100644 --- a/e2e/helpers/coreApi.ts +++ b/e2e/helpers/coreApi.ts @@ -74,6 +74,7 @@ export class CoreApi { first_name: "E2E", last_name: "Test", email: `${username}@e2e.test`, + password: generatePassword(), }); } @@ -145,13 +146,13 @@ export class CoreApi { return secret; } - async startEnrollment( + async startDesktopConfiguration( username: string, ephemeral = false, ): Promise { const response = await this.request( "POST", - `/api/v1/user/${username}/start_enrollment`, + `/api/v1/user/${username}/start_desktop`, { send_enrollment_notification: false, }, @@ -172,7 +173,7 @@ export class CoreApi { await this.deleteUser(username); } await this.createUser(username); - return this.startEnrollment(username, !pinned); + return this.startDesktopConfiguration(username, !pinned); } } diff --git a/e2e/tests/instance-refresh.spec.ts b/e2e/tests/instance-refresh.spec.ts index 9ace921..6f8364b 100644 --- a/e2e/tests/instance-refresh.spec.ts +++ b/e2e/tests/instance-refresh.spec.ts @@ -26,7 +26,9 @@ describe("instance refresh", () => { await completeEnrollment(fixture); await waitForInstanceScreen(); - await refreshInstance(await core.startEnrollment(fixture.username)); + await refreshInstance( + await core.startDesktopConfiguration(fixture.username), + ); await waitForInstanceScreen(); }); });