Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/demo/providers/demo_overrides.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:privacy_gui/core/cloud/providers/geolocation/geolocation_provider.dart';
import 'package:privacy_gui/core/cloud/providers/geolocation/geolocation_state.dart';
import 'package:privacy_gui/core/jnap/models/auto_configuration_settings.dart';
import 'package:privacy_gui/page/instant_setup/models/pnp_ui_models.dart';
import 'package:privacy_gui/demo/jnap/demo_router_repository.dart';
import 'package:privacy_gui/core/data/providers/polling_provider.dart';
import 'package:privacy_gui/core/jnap/router_repository.dart';
Expand Down Expand Up @@ -157,9 +157,9 @@ class _DemoPollingNotifier extends PollingNotifier {
/// Demo PnP notifier - bypasses setup wizard checks
class _DemoPnpNotifier extends PnpNotifier {
@override
Future<AutoConfigurationSettings?> autoConfigurationCheck() async {
Future<AutoConfigurationUIModel?> autoConfigurationCheck() async {
debugPrint('🔌 Demo: Bypassing auto-configuration check');
return const AutoConfigurationSettings(isAutoConfigurationSupported: false);
return const AutoConfigurationUIModel(isSupported: false);
}

@override
Expand Down
70 changes: 70 additions & 0 deletions lib/page/instant_setup/models/pnp_ui_models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,76 @@ class PnpChildNodeUIModel extends Equatable {
List<Object?> get props => [location, modelNumber];
}

/// The auto-configuration method for the device (UI layer).
///
/// This enum is used by the UI layer to determine the configuration flow.
/// - [preConfigured]: Standard PnP flow for pre-configured devices.
/// - [autoParent]: Auto-parent flow for devices that connect to an existing network.
enum AutoConfigurationMethodUI {
preConfigured,
autoParent,
}

/// Represents the auto-configuration settings for UI display.
///
/// This model abstracts the JNAP AutoConfigurationSettings and provides
/// only the data needed by the UI layer for routing decisions.
class AutoConfigurationUIModel extends Equatable {
/// Indicates if auto-configuration is supported by the device.
final bool? isSupported;

/// Indicates if the user has acknowledged the auto-configuration.
final bool? userAcknowledged;

/// The method of auto-configuration.
final AutoConfigurationMethodUI? method;

const AutoConfigurationUIModel({
this.isSupported,
this.userAcknowledged,
this.method,
});

@override
List<Object?> get props => [isSupported, userAcknowledged, method];

/// Creates a copy of this model with optional new values.
AutoConfigurationUIModel copyWith({
bool? isSupported,
bool? userAcknowledged,
AutoConfigurationMethodUI? method,
}) {
return AutoConfigurationUIModel(
isSupported: isSupported ?? this.isSupported,
userAcknowledged: userAcknowledged ?? this.userAcknowledged,
method: method ?? this.method,
);
}

Map<String, dynamic> toMap() {
return {
'isSupported': isSupported,
'userAcknowledged': userAcknowledged,
'method': method?.name,
};
}

factory AutoConfigurationUIModel.fromMap(Map<String, dynamic> map) {
return AutoConfigurationUIModel(
isSupported: map['isSupported'] as bool?,
userAcknowledged: map['userAcknowledged'] as bool?,
method: map['method'] != null
? AutoConfigurationMethodUI.values.byName(map['method'] as String)
: null,
);
}

Map<String, dynamic> toJson() => toMap();

factory AutoConfigurationUIModel.fromJson(Map<String, dynamic> json) =>
AutoConfigurationUIModel.fromMap(json);
}

/// Represents the default Wi-Fi settings for both main and guest networks.
class PnpDefaultSettingsUIModel extends Equatable {
/// The default SSID for the main Wi-Fi network.
Expand Down
41 changes: 20 additions & 21 deletions lib/page/instant_setup/providers/mock_pnp_providers.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:privacy_gui/core/jnap/models/auto_configuration_settings.dart';
import 'package:privacy_gui/core/jnap/router_repository.dart';
import 'package:privacy_gui/core/utils/logger.dart';
import 'package:privacy_gui/page/instant_setup/model/pnp_step.dart';
Expand Down Expand Up @@ -155,13 +154,13 @@ class BaseMockPnpNotifier extends BasePnpNotifier {
}

@override
Future<AutoConfigurationSettings?> autoConfigurationCheck() {
Future<AutoConfigurationUIModel?> autoConfigurationCheck() {
logger.d('[PnP]: Mock - autoConfigurationCheck called');
// Default to configured, acknowledged
return Future.value(const AutoConfigurationSettings(
isAutoConfigurationSupported: true,
userAcknowledgedAutoConfiguration: true,
autoConfigurationMethod: AutoConfigurationMethod.preConfigured,
return Future.value(const AutoConfigurationUIModel(
isSupported: true,
userAcknowledged: true,
method: AutoConfigurationMethodUI.preConfigured,
));
}

Expand Down Expand Up @@ -278,12 +277,12 @@ final unconfiguredPnpProvider = NotifierProvider<BasePnpNotifier, PnpState>(

class UnconfiguredMockPnpNotifier extends BaseMockPnpNotifier {
@override
Future<AutoConfigurationSettings?> autoConfigurationCheck() {
Future<AutoConfigurationUIModel?> autoConfigurationCheck() {
logger.d('[PnP]: Mock (Unconfigured) - autoConfigurationCheck called');
return Future.value(const AutoConfigurationSettings(
isAutoConfigurationSupported: true,
userAcknowledgedAutoConfiguration: false, // This triggers PnP
autoConfigurationMethod: AutoConfigurationMethod.preConfigured,
return Future.value(const AutoConfigurationUIModel(
isSupported: true,
userAcknowledged: false, // This triggers PnP
method: AutoConfigurationMethodUI.preConfigured,
));
}

Expand Down Expand Up @@ -374,12 +373,12 @@ final fwUpdatePnpProvider = NotifierProvider<BasePnpNotifier, PnpState>(

class FwUpdateMockPnpNotifier extends BaseMockPnpNotifier {
@override
Future<AutoConfigurationSettings?> autoConfigurationCheck() {
Future<AutoConfigurationUIModel?> autoConfigurationCheck() {
logger.d('[PnP]: Mock (FW Update) - autoConfigurationCheck called');
return Future.value(const AutoConfigurationSettings(
isAutoConfigurationSupported: true,
userAcknowledgedAutoConfiguration: false, // This triggers PnP
autoConfigurationMethod: AutoConfigurationMethod.preConfigured,
return Future.value(const AutoConfigurationUIModel(
isSupported: true,
userAcknowledged: false, // This triggers PnP
method: AutoConfigurationMethodUI.preConfigured,
));
}

Expand Down Expand Up @@ -415,13 +414,13 @@ final unconfiguredFwUpdatePnpProvider =

class UnconfiguredFwUpdateMockPnpNotifier extends BaseMockPnpNotifier {
@override
Future<AutoConfigurationSettings?> autoConfigurationCheck() {
Future<AutoConfigurationUIModel?> autoConfigurationCheck() {
logger.d(
'[PnP]: Mock (Unconfigured FW Update) - autoConfigurationCheck called');
return Future.value(const AutoConfigurationSettings(
isAutoConfigurationSupported: true,
userAcknowledgedAutoConfiguration: false, // This triggers PnP
autoConfigurationMethod: AutoConfigurationMethod.preConfigured,
return Future.value(const AutoConfigurationUIModel(
isSupported: true,
userAcknowledged: false, // This triggers PnP
method: AutoConfigurationMethodUI.preConfigured,
));
}

Expand Down
6 changes: 3 additions & 3 deletions lib/page/instant_setup/providers/pnp_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:privacy_gui/page/instant_setup/providers/pnp_exception.dart';
import 'package:privacy_gui/page/instant_setup/providers/pnp_state.dart';
import 'package:privacy_gui/page/instant_setup/providers/pnp_step_state.dart';
import 'package:privacy_gui/page/instant_setup/services/pnp_service.dart';
import 'package:privacy_gui/core/jnap/models/auto_configuration_settings.dart';
import 'package:privacy_gui/page/instant_setup/models/pnp_ui_models.dart';
import '../troubleshooter/providers/pnp_troubleshooter_provider.dart';

/// The main Riverpod provider for the PnP feature.
Expand Down Expand Up @@ -140,7 +140,7 @@ abstract class BasePnpNotifier extends Notifier<PnpState> {
Future<ConfigurationResult> checkRouterConfigured();

/// Checks the auto-configuration status of the router.
Future<AutoConfigurationSettings?> autoConfigurationCheck();
Future<AutoConfigurationUIModel?> autoConfigurationCheck();

/// Checks if the admin password has been set by the user.
Future<bool> isRouterPasswordSet();
Expand Down Expand Up @@ -450,7 +450,7 @@ class PnpNotifier extends BasePnpNotifier {
}

@override
Future<AutoConfigurationSettings?> autoConfigurationCheck() {
Future<AutoConfigurationUIModel?> autoConfigurationCheck() {
final pnpService = ref.read(pnpServiceProvider);

return pnpService.autoConfigurationCheck();
Expand Down
42 changes: 34 additions & 8 deletions lib/page/instant_setup/services/pnp_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -207,25 +207,51 @@ class PnpService with AvailabilityChecker {
}

/// Checks the auto-configuration status of the router.
Future<AutoConfigurationSettings?> autoConfigurationCheck() async {
///
/// Returns an [AutoConfigurationUIModel] containing the auto-configuration
/// settings, or null if PnP is not supported or an error occurs.
Future<AutoConfigurationUIModel?> autoConfigurationCheck() async {
if (!serviceHelper.isSupportPnP(_rawDeviceInfo?.services)) {
logger.i('[PnP]: Service - The router does NOT support PNP!');
return null;
}
final repo = _ref.read(routerRepositoryProvider);
final result = await repo
.send(
JNAPAction.getAutoConfigurationSettings,
fetchRemote: true,
cacheLevel: CacheLevel.noCache,
)
.then<AutoConfigurationSettings?>(
(data) => AutoConfigurationSettings.fromMap(data.output))
.onError((error, stackTrace) => null);
JNAPAction.getAutoConfigurationSettings,
fetchRemote: true,
cacheLevel: CacheLevel.noCache,
)
.then<AutoConfigurationUIModel?>((data) {
final jnapModel = AutoConfigurationSettings.fromMap(data.output);
return _convertToUIModel(jnapModel);
}).onError((error, stackTrace) => null);
logger.d('[PnP]: Service - Auto Configuration Check result: $result');
return result;
}

/// Converts a JNAP [AutoConfigurationSettings] to an [AutoConfigurationUIModel].
AutoConfigurationUIModel _convertToUIModel(
AutoConfigurationSettings jnapModel) {
return AutoConfigurationUIModel(
isSupported: jnapModel.isAutoConfigurationSupported,
userAcknowledged: jnapModel.userAcknowledgedAutoConfiguration,
method: _convertMethod(jnapModel.autoConfigurationMethod),
);
}

/// Converts JNAP AutoConfigurationMethod to UI AutoConfigurationMethodUI.
AutoConfigurationMethodUI? _convertMethod(
AutoConfigurationMethod? jnapMethod) {
if (jnapMethod == null) return null;
return switch (jnapMethod) {
AutoConfigurationMethod.preConfigured =>
AutoConfigurationMethodUI.preConfigured,
AutoConfigurationMethod.autoParent =>
AutoConfigurationMethodUI.autoParent,
};
}

/// Checks if the admin password has been set by the user.
Future<bool> isRouterPasswordSet() {
final transaction = JNAPTransactionBuilder(
Expand Down
22 changes: 10 additions & 12 deletions lib/route/router_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'package:privacy_gui/constants/build_config.dart';
import 'package:privacy_gui/constants/pref_key.dart';
import 'package:privacy_gui/core/cache/linksys_cache_manager.dart';
import 'package:privacy_gui/core/jnap/actions/better_action.dart';
import 'package:privacy_gui/core/jnap/models/auto_configuration_settings.dart';
import 'package:privacy_gui/page/instant_setup/models/pnp_ui_models.dart';
import 'package:privacy_gui/core/jnap/models/device_info.dart';
import 'package:privacy_gui/core/data/providers/dashboard_manager_provider.dart';
import 'package:privacy_gui/core/data/providers/polling_provider.dart';
Expand Down Expand Up @@ -175,8 +175,8 @@ class RouterNotifier extends ChangeNotifier {
if (config == null) {
return LocalWhereToGo.login;
}
// If isAutoConfigurationSupported is not true, then go to Login
if (config.isAutoConfigurationSupported != true) {
// If isSupported is not true, then go to Login
if (config.isSupported != true) {
// Retail factory reset case - Check admin password
final isAdminPasswordSet = await pnpNotifier.isRouterPasswordSet();
return isAdminPasswordSet == false
Expand All @@ -186,23 +186,21 @@ class RouterNotifier extends ChangeNotifier {
// AutoConfigurationSupported is true case -

// AutoParent case -
if (config.autoConfigurationMethod ==
AutoConfigurationMethod.autoParent) {
if (config.method == AutoConfigurationMethodUI.autoParent) {
// AutoParent case -
// First Time Login -> AutoConfigurationSupported is true and userAcknowledgedAutoConfiguration is false
// First Time Login -> isSupported is true and userAcknowledged is false
// Login -> else
return config.userAcknowledgedAutoConfiguration == false
return config.userAcknowledged == false
? LocalWhereToGo.firstTimeLogin
: LocalWhereToGo.login;
}

// Prepair case - Check isAutoConfigurationSupported and userAcknowledgedAutoConfiguration
// Prepair case - Check isSupported and userAcknowledged

final userAcknowledgedAutoConfiguration =
config.userAcknowledgedAutoConfiguration;
if (userAcknowledgedAutoConfiguration == false) {
final userAcknowledged = config.userAcknowledged;
if (userAcknowledged == false) {
// PnP case -
// Go PnP -> AutoConfigurationSupported is true and userAcknowledgedAutoConfiguration is false
// Go PnP -> isSupported is true and userAcknowledged is false
// Login -> else
return LocalWhereToGo.pnp;
}
Expand Down
10 changes: 5 additions & 5 deletions test/mocks/pnp_notifier_mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'dart:async' as _i7;
import 'package:flutter_riverpod/flutter_riverpod.dart' as _i2;
import 'package:mockito/mockito.dart' as _i1;
import 'package:mockito/src/dummies.dart' as _i8;
import 'package:privacy_gui/core/jnap/models/auto_configuration_settings.dart'
import 'package:privacy_gui/page/instant_setup/models/pnp_ui_models.dart'
as _i9;
import 'package:privacy_gui/page/instant_setup/model/pnp_step.dart' as _i10;
import 'package:privacy_gui/page/instant_setup/providers/pnp_provider.dart'
Expand Down Expand Up @@ -361,16 +361,16 @@ class MockPnpNotifier extends _i2.Notifier<_i3.PnpState>
) as _i7.Future<_i4.ConfigurationResult>);

@override
_i7.Future<_i9.AutoConfigurationSettings?> autoConfigurationCheck() =>
_i7.Future<_i9.AutoConfigurationUIModel?> autoConfigurationCheck() =>
(super.noSuchMethod(
Invocation.method(
#autoConfigurationCheck,
[],
),
returnValue: _i7.Future<_i9.AutoConfigurationSettings?>.value(),
returnValue: _i7.Future<_i9.AutoConfigurationUIModel?>.value(),
returnValueForMissingStub:
_i7.Future<_i9.AutoConfigurationSettings?>.value(),
) as _i7.Future<_i9.AutoConfigurationSettings?>);
_i7.Future<_i9.AutoConfigurationUIModel?>.value(),
) as _i7.Future<_i9.AutoConfigurationUIModel?>);

@override
_i7.Future<dynamic> fetchDevices() => (super.noSuchMethod(
Expand Down
10 changes: 4 additions & 6 deletions test/mocks/pnp_service_mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import 'dart:async' as _i6;

import 'package:mockito/mockito.dart' as _i1;
import 'package:mockito/src/dummies.dart' as _i10;
import 'package:privacy_gui/core/jnap/models/auto_configuration_settings.dart'
as _i8;
import 'package:privacy_gui/page/instant_setup/models/pnp_ui_models.dart'
as _i2;
import 'package:privacy_gui/page/instant_setup/providers/pnp_state.dart' as _i9;
Expand Down Expand Up @@ -270,16 +268,16 @@ class MockPnpService extends _i1.Mock implements _i3.PnpService {
) as _i6.Future<_i3.ConfigurationResult>);

@override
_i6.Future<_i8.AutoConfigurationSettings?> autoConfigurationCheck() =>
_i6.Future<_i2.AutoConfigurationUIModel?> autoConfigurationCheck() =>
(super.noSuchMethod(
Invocation.method(
#autoConfigurationCheck,
[],
),
returnValue: _i6.Future<_i8.AutoConfigurationSettings?>.value(),
returnValue: _i6.Future<_i2.AutoConfigurationUIModel?>.value(),
returnValueForMissingStub:
_i6.Future<_i8.AutoConfigurationSettings?>.value(),
) as _i6.Future<_i8.AutoConfigurationSettings?>);
_i6.Future<_i2.AutoConfigurationUIModel?>.value(),
) as _i6.Future<_i2.AutoConfigurationUIModel?>);

@override
_i6.Future<bool> isRouterPasswordSet() => (super.noSuchMethod(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:privacy_gui/core/jnap/models/auto_configuration_settings.dart';
import 'package:privacy_gui/page/instant_setup/model/pnp_step.dart';
import 'package:privacy_gui/page/instant_setup/models/pnp_ui_models.dart';
import 'package:privacy_gui/page/instant_setup/pnp_setup_view.dart';
Expand Down Expand Up @@ -105,7 +104,7 @@ class FakePnpNotifier extends BasePnpNotifier {
Future<ConfigurationResult> checkRouterConfigured() async =>
ConfigurationResult(status: ConfigStatus.configured);
@override
Future<AutoConfigurationSettings?> autoConfigurationCheck() async => null;
Future<AutoConfigurationUIModel?> autoConfigurationCheck() async => null;
@override
Future<bool> isRouterPasswordSet() async => true;
@override
Expand Down