diff --git a/lib/config/routes/v2/route_handlers/update_required_handler.dart b/lib/config/routes/v2/route_handlers/update_required_handler.dart index ec63950b..b33daebf 100644 --- a/lib/config/routes/v2/route_handlers/update_required_handler.dart +++ b/lib/config/routes/v2/route_handlers/update_required_handler.dart @@ -6,14 +6,16 @@ import 'package:thingsboard_app/core/auth/login/provider/oauth_provider.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/services/version_service/i_version_service.dart'; +import 'package:thingsboard_app/utils/services/version_service/version_info.dart'; class UpdateRequiredHandler extends RouteHandler { late final ProviderSubscription> _subscription; @override void subscribe(BuildContext context, WidgetRef ref) { _subscription = ref.listenManual(oauthProvider, (prev, next) async { - final versionInfo = next.value?.versionInfo; - if (versionInfo != null) { + final rawVersionInfo = next.value?.versionInfo; + if (rawVersionInfo != null) { + final versionInfo = VersionInfo.fromMobileAppVersionInfo(rawVersionInfo); if (getIt().appUpdateRequired(versionInfo)) { await ref.read(loginProvider.notifier).logout(); } diff --git a/lib/config/routes/v2/routes_config/routes/asset_routes.dart b/lib/config/routes/v2/routes_config/routes/asset_routes.dart index d445a419..fa93001c 100644 --- a/lib/config/routes/v2/routes_config/routes/asset_routes.dart +++ b/lib/config/routes/v2/routes_config/routes/asset_routes.dart @@ -1,5 +1,4 @@ import 'package:go_router/go_router.dart'; -import 'package:thingsboard_app/config/routes/v2/router_2.dart'; import 'package:thingsboard_app/modules/asset/asset_details_page.dart'; import 'package:thingsboard_app/modules/asset/assets_page.dart'; @@ -14,20 +13,15 @@ final assetRoutes = [ path: AssetRoutes.assets, builder: (context, state) { final searchMode = state.uri.queryParameters['search'] == 'true'; - return AssetsPage( searchMode: searchMode); + return AssetsPage(searchMode: searchMode); }, - routes: [ - GoRoute( - parentNavigatorKey: globalNavigatorKey, + ), + GoRoute( path: '${AssetRoutes.asset}/:id', builder: (context, state) { final id = state.pathParameters['id']!; - return AssetDetailsPage( id); + return AssetDetailsPage(id); }, ), - ] - ), - - ]; diff --git a/lib/config/routes/v2/routes_config/routes/login_routes.dart b/lib/config/routes/v2/routes_config/routes/login_routes.dart index 6e14b2b4..4603eebc 100644 --- a/lib/config/routes/v2/routes_config/routes/login_routes.dart +++ b/lib/config/routes/v2/routes_config/routes/login_routes.dart @@ -43,7 +43,7 @@ final loginRoutes = [ final providerQuery = state.uri.queryParameters['selectedProvider']; final providerType = providerQuery != null - ? twoFaProviderTypeFromString(providerQuery) + ? _parseTwoFaProviderType(providerQuery) : null; return TwoFactorAuthenticationPage( @@ -60,9 +60,9 @@ final loginRoutes = [ path: LoginRoutes.mfaForceSuccess, builder: (context, state) { final providerQuery = state.uri.queryParameters['selectedProvider']; - final providerType = twoFaProviderTypeFromString( - providerQuery.toString(), - ); + final providerType = + _parseTwoFaProviderType(providerQuery) ?? + TwoFaProviderType.unknownDefaultOpenApi; final force = bool.tryParse(state.uri.queryParameters['force'].toString()) ?? @@ -79,7 +79,7 @@ final loginRoutes = [ final providerQuery = state.uri.queryParameters['selectedProvider']; final providerType = providerQuery != null - ? twoFaProviderTypeFromString(providerQuery) + ? _parseTwoFaProviderType(providerQuery) : null; return TwoFactorAuthSetup( @@ -95,3 +95,12 @@ final loginRoutes = [ }, ), ]; + +TwoFaProviderType? _parseTwoFaProviderType(String? name) { + if (name == null) return null; + try { + return TwoFaProviderType.valueOf(name); + } catch (_) { + return null; + } +} diff --git a/lib/core/auth/2FA/confirm/providers/2fa_avalible_providers/two_factor_avalible_providers.dart b/lib/core/auth/2FA/confirm/providers/2fa_avalible_providers/two_factor_avalible_providers.dart index f543542d..5cee0f9f 100644 --- a/lib/core/auth/2FA/confirm/providers/2fa_avalible_providers/two_factor_avalible_providers.dart +++ b/lib/core/auth/2FA/confirm/providers/2fa_avalible_providers/two_factor_avalible_providers.dart @@ -10,10 +10,11 @@ part 'two_factor_avalible_providers.g.dart'; Future> twoFaConfirmAvalibleProviders(Ref ref) async { final tbClient = getIt().client; try { - final info = await tbClient - .getTwoFactorAuthService() - .getAvailableLoginTwoFaProviders(); - return info; + final res = + await tbClient + .getTwoFactorAuthControllerApi() + .getAvailableTwoFaProviderInfos(); + return res.data?.toList() ?? []; } catch (e) { return []; } diff --git a/lib/core/auth/2FA/confirm/providers/2fa_avalible_providers/two_factor_avalible_providers.g.dart b/lib/core/auth/2FA/confirm/providers/2fa_avalible_providers/two_factor_avalible_providers.g.dart index a1bf312a..43a7df63 100644 --- a/lib/core/auth/2FA/confirm/providers/2fa_avalible_providers/two_factor_avalible_providers.g.dart +++ b/lib/core/auth/2FA/confirm/providers/2fa_avalible_providers/two_factor_avalible_providers.g.dart @@ -7,7 +7,7 @@ part of 'two_factor_avalible_providers.dart'; // ************************************************************************** String _$twoFaConfirmAvalibleProvidersHash() => - r'9ec4bf288563ff26ccaf25b17ee4f19d4ae11d17'; + r'33b28c823f302339070d33d5cd1b06185afdadb1'; /// See also [twoFaConfirmAvalibleProviders]. @ProviderFor(twoFaConfirmAvalibleProviders) diff --git a/lib/core/auth/2FA/confirm/providers/2fa_confirm_provider/two_factor_confirm_provider.dart b/lib/core/auth/2FA/confirm/providers/2fa_confirm_provider/two_factor_confirm_provider.dart index 96fd259d..cbad8413 100644 --- a/lib/core/auth/2FA/confirm/providers/2fa_confirm_provider/two_factor_confirm_provider.dart +++ b/lib/core/auth/2FA/confirm/providers/2fa_confirm_provider/two_factor_confirm_provider.dart @@ -36,12 +36,25 @@ class TwoFactorConfirm extends _$TwoFactorConfirm { Future verifyCode(String code) async { state = state.copyWith(loading: true); try { - final res = await _tbClient.checkTwoFaVerificationCode( - type, - code, - requestConfig: RequestConfig(ignoreErrors: true), - ); - await ref.read(loginProvider.notifier).twoFaConfirmed(res); + final res = await _tbClient + .getTwoFactorAuthControllerApi() + .checkTwoFaVerificationCode( + providerType: type, + verificationCode: code, + // Handle errors (e.g. 429) in-widget below instead of the global + // error overlay; the new client exposes this via Dio `extra`. + extra: const {'ignoreErrors': true}, + ); + final token = res.data?.token; + if (token == null) { + state = state.copyWith(loading: false, codeState: CodeState.invalid); + return; + } + await ref + .read(loginProvider.notifier) + .twoFaConfirmed( + LoginResponse(token: token, refreshToken: res.data?.refreshToken), + ); state = state.copyWith(loading: false, codeState: CodeState.valid); } catch (e) { state = state.copyWith(loading: false); @@ -62,10 +75,12 @@ class TwoFactorConfirm extends _$TwoFactorConfirm { Future sendCode() async { state = state.copyWith(codeSent: false, loading: true); try { - await _tbClient.getTwoFactorAuthService().requestTwoFaVerificationCode( - type, - requestConfig: RequestConfig(ignoreErrors: true), - ); + await _tbClient + .getTwoFactorAuthControllerApi() + .requestTwoFaVerificationCode( + providerType: type, + extra: const {'ignoreErrors': true}, + ); _resendTimer?.cancel(); _resendTimer = Timer( Duration(seconds: _resendTimerDurationSeconds), diff --git a/lib/core/auth/2FA/confirm/providers/2fa_confirm_provider/two_factor_confirm_provider.g.dart b/lib/core/auth/2FA/confirm/providers/2fa_confirm_provider/two_factor_confirm_provider.g.dart index 6e099076..85dcbc11 100644 --- a/lib/core/auth/2FA/confirm/providers/2fa_confirm_provider/two_factor_confirm_provider.g.dart +++ b/lib/core/auth/2FA/confirm/providers/2fa_confirm_provider/two_factor_confirm_provider.g.dart @@ -6,7 +6,7 @@ part of 'two_factor_confirm_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$twoFactorConfirmHash() => r'282bb3abf59cb4ef6e1dd53add24b4435dc6273b'; +String _$twoFactorConfirmHash() => r'b8f5088c5f25fdc3b38b135fd9e81a852a57e257'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/core/auth/2FA/confirm/two_factor_authentication_page.dart b/lib/core/auth/2FA/confirm/two_factor_authentication_page.dart index 438794f5..920ffe3e 100644 --- a/lib/core/auth/2FA/confirm/two_factor_authentication_page.dart +++ b/lib/core/auth/2FA/confirm/two_factor_authentication_page.dart @@ -35,16 +35,16 @@ class TwoFactorAuthenticationPage extends HookConsumerWidget { } if (providers.value?.length == 1) { context.pushReplacement( - '${LoginRoutes.login}${LoginRoutes.mfaConfirm}?selectedProvider=${providers.value!.first.type.toShortString()}', + '${LoginRoutes.login}${LoginRoutes.mfaConfirm}?selectedProvider=${providers.value!.first.type?.name}', ); return; } final defaultProvider = providers.value?.firstWhereOrNull( - (e) => e.isDefault, + (e) => e.default_ ?? false, ); if (defaultProvider != null) { context.pushReplacement( - '${LoginRoutes.login}${LoginRoutes.mfaConfirm}?selectedProvider=${defaultProvider.type.toShortString()}', + '${LoginRoutes.login}${LoginRoutes.mfaConfirm}?selectedProvider=${defaultProvider.type?.name}', ); return; } @@ -59,9 +59,10 @@ class TwoFactorAuthenticationPage extends HookConsumerWidget { final info = providers.value!.firstWhere( (e) => e.type == selectedProvider, ); + if (info.type == null) return const SizedBox(); final codeLoading = ref.watch( twoFactorConfirmProvider( - info.type, + info.type!, info.minVerificationCodeSendPeriod, ).select((state) => state.loading), ); @@ -81,13 +82,18 @@ class TwoFactorAuthenticationPage extends HookConsumerWidget { return BaseTwoFactorLayout( title: S.of(context).verifyYourIdentity, isLoading: isLoading.value, - child: providers.value != null - ? SelectProviderTypeWidget( - activeProviders: const [], - avalibleTypes: providers.value!.map((e) => e.type).toList(), - type: TwoFaViewType.confirm, - ) - : const SizedBox(), + child: + providers.value != null + ? SelectProviderTypeWidget( + activeProviders: const [], + avalibleTypes: + providers.value! + .where((e) => e.type != null) + .map((e) => e.type!) + .toList(), + type: TwoFaViewType.confirm, + ) + : const SizedBox(), ); } @@ -96,11 +102,12 @@ class TwoFactorAuthenticationPage extends HookConsumerWidget { WidgetRef ref, TwoFaProviderInfo info, ) async { + if (info.type == null) return; isLoading.value = true; await ref .read( twoFactorConfirmProvider( - info.type, + info.type!, info.minVerificationCodeSendPeriod, ).notifier, ) diff --git a/lib/core/auth/2FA/confirm/widgets/select_provider_type_widget.dart b/lib/core/auth/2FA/confirm/widgets/select_provider_type_widget.dart index ed6ee463..8aa45aa6 100644 --- a/lib/core/auth/2FA/confirm/widgets/select_provider_type_widget.dart +++ b/lib/core/auth/2FA/confirm/widgets/select_provider_type_widget.dart @@ -35,7 +35,7 @@ class SelectProviderTypeWidget extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final loading = useState(false); - final isForce = type == TwoFaViewType.force ; + final isForce = type == TwoFaViewType.force; final filtered = switch (type) { TwoFaViewType.setup => avalibleTypes, @@ -122,7 +122,7 @@ class SelectProviderTypeWidget extends HookConsumerWidget { } context.push( - '${LoginRoutes.login}${LoginRoutes.mfaConfigure}?selectedProvider=${provider.toShortString()}&force=$isForce', + '${LoginRoutes.login}${LoginRoutes.mfaConfigure}?selectedProvider=${provider.name}&force=$isForce', ); }, contentPadding: const EdgeInsets.symmetric( @@ -183,10 +183,7 @@ class SelectProviderTypeWidget extends HookConsumerWidget { : OutlinedButton.icon( onPressed: () { context.push( - '${LoginRoutes.login}${type == TwoFaViewType.confirm - ? LoginRoutes.mfaConfirm - : LoginRoutes.mfaConfigure}?selectedProvider=${provider.toShortString()}&force=$isForce', - + '${LoginRoutes.login}${type == TwoFaViewType.confirm ? LoginRoutes.mfaConfirm : LoginRoutes.mfaConfigure}?selectedProvider=${provider.name}&force=$isForce', ); }, style: OutlinedButton.styleFrom( @@ -279,8 +276,14 @@ class SelectProviderTypeWidget extends HookConsumerWidget { loading.value = true; try { await getIt().client - .getTwoFactorAuthService() - .updateTwoFaAccountConfig(info, true); + .getTwoFactorAuthConfigControllerApi() + .updateTwoFaAccountConfig( + providerType: info, + twoFaAccountConfigUpdateRequest: + TwoFaAccountConfigUpdateRequest( + (b) => b..useByDefault = true, + ), + ); await Future.delayed( const Duration(milliseconds: 200), ); @@ -375,8 +378,8 @@ class SelectProviderTypeWidget extends HookConsumerWidget { } try { await getIt().client - .getTwoFactorAuthService() - .deleteTwoFaAccountConfig(providerType); + .getTwoFactorAuthConfigControllerApi() + .deleteTwoFaAccountConfig(providerType: providerType); ref.invalidate(acountTwoFactorSettingsProvider); } catch (e) { diff --git a/lib/core/auth/2FA/confirm/widgets/two_factor_auth_code_widget.dart b/lib/core/auth/2FA/confirm/widgets/two_factor_auth_code_widget.dart index 3a2f6fef..e6d882d8 100644 --- a/lib/core/auth/2FA/confirm/widgets/two_factor_auth_code_widget.dart +++ b/lib/core/auth/2FA/confirm/widgets/two_factor_auth_code_widget.dart @@ -25,7 +25,8 @@ class EnterACodeWidget extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final data = getTwoFaProviderData(selectedProvider.type); + final providerType = selectedProvider.type!; + final data = getTwoFaProviderData(providerType); final timerDuration = selectedProvider.minVerificationCodeSendPeriod ?? 0; final form = useMemoized( () => FormGroup({ @@ -43,13 +44,13 @@ class EnterACodeWidget extends HookConsumerWidget { )..forward(); final state = ref.watch( twoFactorConfirmProvider( - selectedProvider.type, + providerType, selectedProvider.minVerificationCodeSendPeriod, ), ); ref.listen( twoFactorConfirmProvider( - selectedProvider.type, + providerType, selectedProvider.minVerificationCodeSendPeriod, ), (prev, next) { @@ -105,7 +106,7 @@ class EnterACodeWidget extends HookConsumerWidget { type: data.textInputType, autoFillHints: const [AutofillHints.oneTimeCode], ), - if (!noResendCodeProviders.contains(selectedProvider.type)) + if (!noResendCodeProviders.contains(providerType)) AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: @@ -167,7 +168,7 @@ class EnterACodeWidget extends HookConsumerWidget { await ref .read( twoFactorConfirmProvider( - selectedProvider.type, + selectedProvider.type!, selectedProvider.minVerificationCodeSendPeriod, ).notifier, ) @@ -178,7 +179,7 @@ class EnterACodeWidget extends HookConsumerWidget { ref .read( twoFactorConfirmProvider( - selectedProvider.type, + selectedProvider.type!, selectedProvider.minVerificationCodeSendPeriod, ).notifier, ) diff --git a/lib/core/auth/2FA/setup/providers/two_factor_setup_account_settings_provider/two_factor_setup_account_settings_provider.dart b/lib/core/auth/2FA/setup/providers/two_factor_setup_account_settings_provider/two_factor_setup_account_settings_provider.dart index b545b6fb..0928dc02 100644 --- a/lib/core/auth/2FA/setup/providers/two_factor_setup_account_settings_provider/two_factor_setup_account_settings_provider.dart +++ b/lib/core/auth/2FA/setup/providers/two_factor_setup_account_settings_provider/two_factor_setup_account_settings_provider.dart @@ -8,6 +8,9 @@ part 'two_factor_setup_account_settings_provider.g.dart'; @riverpod Future acountTwoFactorSettings(Ref ref) async { final client = getIt().client; - final res = await client.getTwoFactorAuthService().getAccountTwoFaSettings(); - return res; + final res = + await client + .getTwoFactorAuthConfigControllerApi() + .getAccountTwoFaSettings(); + return res.data; } diff --git a/lib/core/auth/2FA/setup/providers/two_factor_setup_account_settings_provider/two_factor_setup_account_settings_provider.g.dart b/lib/core/auth/2FA/setup/providers/two_factor_setup_account_settings_provider/two_factor_setup_account_settings_provider.g.dart index 38136fed..4effe33b 100644 --- a/lib/core/auth/2FA/setup/providers/two_factor_setup_account_settings_provider/two_factor_setup_account_settings_provider.g.dart +++ b/lib/core/auth/2FA/setup/providers/two_factor_setup_account_settings_provider/two_factor_setup_account_settings_provider.g.dart @@ -7,7 +7,7 @@ part of 'two_factor_setup_account_settings_provider.dart'; // ************************************************************************** String _$acountTwoFactorSettingsHash() => - r'0a9e643aa0941c600edd5b452b60bf1cd6fdaf1c'; + r'ade1c7e88e4a8de33c47fc285e38023e94298bc3'; /// See also [acountTwoFactorSettings]. @ProviderFor(acountTwoFactorSettings) diff --git a/lib/core/auth/2FA/setup/providers/two_factor_setup_avalible_providers/two_factor_setup_avalible_providers.dart b/lib/core/auth/2FA/setup/providers/two_factor_setup_avalible_providers/two_factor_setup_avalible_providers.dart index 2efa2d93..257fdbf7 100644 --- a/lib/core/auth/2FA/setup/providers/two_factor_setup_avalible_providers/two_factor_setup_avalible_providers.dart +++ b/lib/core/auth/2FA/setup/providers/two_factor_setup_avalible_providers/two_factor_setup_avalible_providers.dart @@ -9,10 +9,11 @@ part 'two_factor_setup_avalible_providers.g.dart'; Future> twoFactorSetupAvalibleProviders(Ref ref) async { final tbClient = getIt().client; try { - final info = await tbClient - .getTwoFactorAuthService() - .getAvailableTwoFaProviders(); - return info; + final res = + await tbClient + .getTwoFactorAuthConfigControllerApi() + .getAvailableTwoFaProviderTypes(); + return res.data?.toList() ?? []; } catch (e) { return []; } diff --git a/lib/core/auth/2FA/setup/providers/two_factor_setup_avalible_providers/two_factor_setup_avalible_providers.g.dart b/lib/core/auth/2FA/setup/providers/two_factor_setup_avalible_providers/two_factor_setup_avalible_providers.g.dart index af556486..2515dfd9 100644 --- a/lib/core/auth/2FA/setup/providers/two_factor_setup_avalible_providers/two_factor_setup_avalible_providers.g.dart +++ b/lib/core/auth/2FA/setup/providers/two_factor_setup_avalible_providers/two_factor_setup_avalible_providers.g.dart @@ -7,7 +7,7 @@ part of 'two_factor_setup_avalible_providers.dart'; // ************************************************************************** String _$twoFactorSetupAvalibleProvidersHash() => - r'd6d760a291c656026f0b913e74c3982de589a368'; + r'a9fdf60b6fdaa889d68372611ebae6423058c05b'; /// See also [twoFactorSetupAvalibleProviders]. @ProviderFor(twoFactorSetupAvalibleProviders) diff --git a/lib/core/auth/2FA/setup/providers/two_factor_setup_provider/two_factor_setup_provider.dart b/lib/core/auth/2FA/setup/providers/two_factor_setup_provider/two_factor_setup_provider.dart index 44b3ea20..e1a3c20e 100644 --- a/lib/core/auth/2FA/setup/providers/two_factor_setup_provider/two_factor_setup_provider.dart +++ b/lib/core/auth/2FA/setup/providers/two_factor_setup_provider/two_factor_setup_provider.dart @@ -1,4 +1,3 @@ - import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:thingsboard_app/locator.dart'; @@ -12,9 +11,10 @@ Future generateConfig( TwoFaProviderType type, ) async { final tbClient = getIt().client; - return await tbClient.getTwoFactorAuthService().generateTwoFaAccountConfig( - type, - ); + final res = await tbClient + .getTwoFactorAuthConfigControllerApi() + .generateTwoFaAccountConfig(providerType: type.name); + return res.data!; } // @riverpod diff --git a/lib/core/auth/2FA/setup/providers/two_factor_setup_provider/two_factor_setup_provider.g.dart b/lib/core/auth/2FA/setup/providers/two_factor_setup_provider/two_factor_setup_provider.g.dart index 5396184c..200eab0f 100644 --- a/lib/core/auth/2FA/setup/providers/two_factor_setup_provider/two_factor_setup_provider.g.dart +++ b/lib/core/auth/2FA/setup/providers/two_factor_setup_provider/two_factor_setup_provider.g.dart @@ -6,7 +6,7 @@ part of 'two_factor_setup_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$generateConfigHash() => r'b4b4c43cc92d5355d07a5d888e84c486ecaba8d1'; +String _$generateConfigHash() => r'421ef736fa840869c566e9ea0655906656c34649'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/core/auth/2FA/setup/two_factor_auth_config_success.dart b/lib/core/auth/2FA/setup/two_factor_auth_config_success.dart index cb86f018..2cccbe1f 100644 --- a/lib/core/auth/2FA/setup/two_factor_auth_config_success.dart +++ b/lib/core/auth/2FA/setup/two_factor_auth_config_success.dart @@ -86,12 +86,15 @@ class TwoFactorAuthConfigSuccess extends ConsumerWidget { context.pop(); }, child: Text( - isForce ? S.of(context).loginToApp : S.of(context).done, + isForce + ? S.of(context).loginToApp + : S.of(context).done, ), ), ), - if (isForce && (userSettings.value?.configs.length != platformConfig.value?.length)) - + if (isForce && + (userSettings.value?.configs?.length != + platformConfig.value?.length)) SizedBox( width: double.infinity, child: FilledButton( @@ -109,8 +112,8 @@ class TwoFactorAuthConfigSuccess extends ConsumerWidget { ], ), ), - if(platformConfig.isLoading || userSettings.isLoading) - const FullScreenLoader(), + if (platformConfig.isLoading || userSettings.isLoading) + const FullScreenLoader(), ], ), ); diff --git a/lib/core/auth/2FA/setup/two_factor_auth_setup.dart b/lib/core/auth/2FA/setup/two_factor_auth_setup.dart index 23b5795b..d79b34ec 100644 --- a/lib/core/auth/2FA/setup/two_factor_auth_setup.dart +++ b/lib/core/auth/2FA/setup/two_factor_auth_setup.dart @@ -18,7 +18,7 @@ import 'package:thingsboard_app/core/auth/2FA/two_fa_providers_helper.dart'; import 'package:thingsboard_app/core/auth/2FA/two_fa_view_type.dart'; import 'package:thingsboard_app/core/auth/login/provider/login_provider.dart'; import 'package:thingsboard_app/generated/l10n.dart'; -import 'package:thingsboard_client/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart'; class TwoFactorAuthSetup extends HookConsumerWidget { const TwoFactorAuthSetup({ @@ -99,24 +99,29 @@ class TwoFactorAuthSetup extends HookConsumerWidget { ), ); } - return BaseTwoFactorLayout( - title: S.of(context).twofactorAuthentication, - isLoading: isLoading.value, - child: - providers.value != null && - !(userConfig.isLoading || userConfig.isRefreshing) - ? SelectProviderTypeWidget( - defaultProvider: - userConfig.value?.configs.entries - .firstWhereOrNull((e) => e.value.useByDefault) - ?.key, - activeProviders: - userConfig.value?.configs.keys.toList() ?? [], - avalibleTypes: providers.value!, - type: isForce ? TwoFaViewType.force : TwoFaViewType.setup, - ) - : const SizedBox(), - ); + return BaseTwoFactorLayout( + title: S.of(context).twofactorAuthentication, + isLoading: isLoading.value, + child: + providers.value != null && + !(userConfig.isLoading || userConfig.isRefreshing) + ? SelectProviderTypeWidget( + defaultProvider: _parseProviderType( + userConfig.value?.configs?.entries + .firstWhereOrNull((e) => e.value.useByDefault ?? false) + ?.key, + ), + activeProviders: + userConfig.value?.configs?.keys + .map(_parseProviderType) + .whereType() + .toList() ?? + [], + avalibleTypes: providers.value!, + type: isForce ? TwoFaViewType.force : TwoFaViewType.setup, + ) + : const SizedBox(), + ); } Widget _buildProviderWidget( @@ -149,6 +154,7 @@ class TwoFactorAuthSetup extends HookConsumerWidget { loading: loading, config: config as BackupCodeTwoFaAccountConfig, ), + _ => const SizedBox.shrink(), }; } @@ -172,7 +178,16 @@ class TwoFactorAuthSetup extends HookConsumerWidget { ) { ref.invalidate(acountTwoFactorSettingsProvider); final route = - '${LoginRoutes.login}${LoginRoutes.mfaForceSuccess}${'?force=$isForce'}&selectedProvider=${configuredType.toShortString()}'; + '${LoginRoutes.login}${LoginRoutes.mfaForceSuccess}${'?force=$isForce'}&selectedProvider=${configuredType.name}'; context.pushReplacement(route, extra: configuredType); } } + +TwoFaProviderType? _parseProviderType(String? name) { + if (name == null) return null; + try { + return TwoFaProviderType.valueOf(name); + } catch (_) { + return null; + } +} diff --git a/lib/core/auth/2FA/setup/widgets/backup_code_configure_widget.dart b/lib/core/auth/2FA/setup/widgets/backup_code_configure_widget.dart index b884c14a..14ddfc52 100644 --- a/lib/core/auth/2FA/setup/widgets/backup_code_configure_widget.dart +++ b/lib/core/auth/2FA/setup/widgets/backup_code_configure_widget.dart @@ -26,8 +26,8 @@ class BackupCodeConfigureWidget Future saveConfig() async { try { await getIt().client - .getTwoFactorAuthService() - .verifyAndSaveTwoFaAccountConfig(config); + .getTwoFactorAuthConfigControllerApi() + .verifyAndSaveTwoFaAccountConfig(twoFaAccountConfig: config); } catch (e) { // getIt().showErrorNotification( // (ctx) => "Error saving codes: $e", @@ -80,17 +80,19 @@ class BackupCodeConfigureWidget selectionControls: MaterialTextSelectionControls(), child: Column( spacing: 12, - children: chunkList(config.codes, 2) - .map( - (e) => Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text(e[0], style: TbTextStyles.labelLarge), - Text(e[1], style: TbTextStyles.labelLarge), - ], - ), - ) - .toList(), + children: + chunkList(config.codes, 2) + .map( + (e) => Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + Text(e[0], style: TbTextStyles.labelLarge), + Text(e[1], style: TbTextStyles.labelLarge), + ], + ), + ) + .toList(), ), ), ), @@ -146,8 +148,8 @@ class BackupCodeConfigureWidget loading.value = true; try { await getIt().client - .getTwoFactorAuthService() - .generateTwoFaAccountConfig(config.getProviderType()); + .getTwoFactorAuthConfigControllerApi() + .generateTwoFaAccountConfig(providerType: config.providerType); } catch (e) { // getIt().showErrorNotification( // (ctx) => "Error sending code: $e", diff --git a/lib/core/auth/2FA/setup/widgets/email_configure_widget.dart b/lib/core/auth/2FA/setup/widgets/email_configure_widget.dart index f92c54c6..01f07bc7 100644 --- a/lib/core/auth/2FA/setup/widgets/email_configure_widget.dart +++ b/lib/core/auth/2FA/setup/widgets/email_configure_widget.dart @@ -73,17 +73,18 @@ class EmailConfigureWidget extends TwoFaConfigWidget { return FilledButton( onPressed: formGroup.control('email').invalid || loading.value - ? null - : () async { - await _handleSendCode(form, codeSent); - }, - child: loading.value - ? const SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator(strokeWidth: 2), - ) - : Text(S.of(context).sendCode), + ? null + : () async { + await _handleSendCode(form, codeSent); + }, + child: + loading.value + ? const SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator(strokeWidth: 2), + ) + : Text(S.of(context).sendCode), ); }, ), @@ -125,10 +126,10 @@ class EmailConfigureWidget extends TwoFaConfigWidget { throw Exception('Email is required'); } - config.email = email; + final updatedConfig = config.rebuild((b) => b..email = email); await getIt().client - .getTwoFactorAuthService() - .submitTwoFaAccountConfig(config); + .getTwoFactorAuthConfigControllerApi() + .submitTwoFaAccountConfig(twoFaAccountConfig: updatedConfig); return true; } catch (e) { return false; @@ -153,10 +154,13 @@ class EmailConfigureWidget extends TwoFaConfigWidget { throw Exception('Email is required'); } - config.email = email; + final updatedConfig = config.rebuild((b) => b..email = email); await getIt().client - .getTwoFactorAuthService() - .verifyAndSaveTwoFaAccountConfig(config, verificationCode: code); + .getTwoFactorAuthConfigControllerApi() + .verifyAndSaveTwoFaAccountConfig( + twoFaAccountConfig: updatedConfig, + verificationCode: code, + ); onConfigured(); } catch (e) { control.setErrors({TbValicationMessages.invalidCode: {}}); diff --git a/lib/core/auth/2FA/setup/widgets/phone_configure_widget.dart b/lib/core/auth/2FA/setup/widgets/phone_configure_widget.dart index 65f17d41..af8ba95d 100644 --- a/lib/core/auth/2FA/setup/widgets/phone_configure_widget.dart +++ b/lib/core/auth/2FA/setup/widgets/phone_configure_widget.dart @@ -148,10 +148,12 @@ class PhoneConfigureWidget extends TwoFaConfigWidget { throw Exception('Phone number is required'); } - config.phoneNumber = phoneNumber.international; + final updatedConfig = config.rebuild( + (b) => b..phoneNumber = phoneNumber.international, + ); await getIt().client - .getTwoFactorAuthService() - .submitTwoFaAccountConfig(config); + .getTwoFactorAuthConfigControllerApi() + .submitTwoFaAccountConfig(twoFaAccountConfig: updatedConfig); return true; } catch (e) { // getIt().showErrorNotification( @@ -176,10 +178,15 @@ class PhoneConfigureWidget extends TwoFaConfigWidget { try { final phoneNumber = form.control('phone').value as PhoneNumber; - config.phoneNumber = phoneNumber.international; + final updatedConfig = config.rebuild( + (b) => b..phoneNumber = phoneNumber.international, + ); await getIt().client - .getTwoFactorAuthService() - .verifyAndSaveTwoFaAccountConfig(config, verificationCode: code); + .getTwoFactorAuthConfigControllerApi() + .verifyAndSaveTwoFaAccountConfig( + twoFaAccountConfig: updatedConfig, + verificationCode: code, + ); onConfigured(); } catch (e) { control.setErrors({TbValicationMessages.invalidCode: {}}); @@ -196,7 +203,6 @@ class PhoneTextField extends HookWidget { @override Widget build(BuildContext context) { - return ReactivePhoneFormField( controller: phoneController, countryButtonStyle: CountryButtonStyle( @@ -206,12 +212,11 @@ class PhoneTextField extends HookWidget { ), style: TbTextStyles.bodyLarge, decoration: InputDecoration( - hintText: S.of(context).phone, isDense: true, helperText: S.of(context).phoneNumberHelperText, ), - + formControlName: 'phone', focusNode: FocusNode(), valueAccessor: PhoneNumberValueAccessor(), diff --git a/lib/core/auth/2FA/setup/widgets/totp_configure_widget.dart b/lib/core/auth/2FA/setup/widgets/totp_configure_widget.dart index 4a6d5a47..c95fd8c1 100644 --- a/lib/core/auth/2FA/setup/widgets/totp_configure_widget.dart +++ b/lib/core/auth/2FA/setup/widgets/totp_configure_widget.dart @@ -59,9 +59,10 @@ class TotpConfigureWidget extends TwoFaConfigWidget { children: [ StepChip( leading: '1', - title: S - .of(context) - .copy32digitsKeyToYourAuthenticationAppOrScanQrcode, + title: + S + .of(context) + .copy32digitsKeyToYourAuthenticationAppOrScanQrcode, content: Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 20, @@ -125,9 +126,9 @@ class TotpConfigureWidget extends TwoFaConfigWidget { loading.value = true; try { await getIt().client - .getTwoFactorAuthService() + .getTwoFactorAuthConfigControllerApi() .verifyAndSaveTwoFaAccountConfig( - config, + twoFaAccountConfig: config, verificationCode: verificationCode, ); onConfigured(); diff --git a/lib/core/auth/login/models/login_state.dart b/lib/core/auth/login/models/login_state.dart index 8df24190..913d56a7 100644 --- a/lib/core/auth/login/models/login_state.dart +++ b/lib/core/auth/login/models/login_state.dart @@ -1,5 +1,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:thingsboard_client/thingsboard_client.dart'; +import 'package:thingsboard_app/core/auth/login/models/mobile_basic_info.dart'; +import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client_extensions.dart'; part 'login_state.freezed.dart'; diff --git a/lib/core/auth/login/models/mobile_basic_info.dart b/lib/core/auth/login/models/mobile_basic_info.dart new file mode 100644 index 00000000..60b897e4 --- /dev/null +++ b/lib/core/auth/login/models/mobile_basic_info.dart @@ -0,0 +1,40 @@ +import 'package:built_value/json_object.dart'; +import 'package:thingsboard_app/utils/services/layouts/pages_layout.dart'; +import 'package:thingsboard_app/utils/services/version_service/version_info.dart'; +import 'package:thingsboard_ce_client/thingsboard_ce_client.dart'; + +class MobileBasicInfo { + const MobileBasicInfo({ + this.user, + this.homeDashboardInfo, + this.pages, + this.storeInfo, + this.versionInfo, + }); + + factory MobileBasicInfo.fromUserMobileInfo(UserMobileInfo info) { + return MobileBasicInfo( + user: info.user, + homeDashboardInfo: info.homeDashboardInfo, + pages: _parsePages(info.pages), + storeInfo: info.storeInfo, + versionInfo: info.versionInfo != null + ? VersionInfo.fromMobileAppVersionInfo(info.versionInfo!) + : null, + ); + } + + static List? _parsePages(JsonObject? pages) { + if (pages == null || !pages.isList) return null; + return pages.asList + .whereType>() + .map(PageLayout.fromJson) + .toList(); + } + + final User? user; + final HomeDashboardInfo? homeDashboardInfo; + final List? pages; + final StoreInfo? storeInfo; + final VersionInfo? versionInfo; +} diff --git a/lib/core/auth/login/provider/login_provider.dart b/lib/core/auth/login/provider/login_provider.dart index 68d24e21..51e1f9cd 100644 --- a/lib/core/auth/login/provider/login_provider.dart +++ b/lib/core/auth/login/provider/login_provider.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:thingsboard_app/core/auth/login/models/login_state.dart'; +import 'package:thingsboard_app/core/auth/login/models/mobile_basic_info.dart'; import 'package:thingsboard_app/core/auth/oauth2/i_oauth2_client.dart'; import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; @@ -15,7 +16,7 @@ import 'package:thingsboard_app/utils/services/firebase/i_firebase_service.dart' import 'package:thingsboard_app/utils/services/notification_service.dart'; import 'package:thingsboard_app/utils/services/overlay_service/i_overlay_service.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; -import 'package:thingsboard_client/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart'; part 'login_provider.g.dart'; @@ -66,7 +67,9 @@ class Login extends _$Login { Future login(String email, String password) async { try { - final res = await _tbClient.login(LoginRequest(email, password)); + final res = await _tbClient.login( + LoginRequest(userName: email, password: password), + ); final user = _tbClient.getAuthUser(); if (user != null && (user.isMfaConfigurationToken() || user.isPreVerificationToken())) { @@ -79,15 +82,19 @@ class Login extends _$Login { } Future loadUser() async { - final mobileInfo = await _tbClient.getMobileService().getUserMobileInfo( - MobileInfoQuery( - platformType: _deviceInfoService.getPlatformType(), - packageName: _deviceInfoService.getApplicationId(), - ), - ); + final mobileResp = await _tbClient + .getMobileAppControllerApi() + .getUserMobileInfo( + pkgName: _deviceInfoService.getApplicationId(), + platform: _deviceInfoService.getPlatformType().name, + ); + final mobileInfo = MobileBasicInfo.fromUserMobileInfo(mobileResp.data!); - final userInfo = await _tbClient.getUserService().getUser(); - final lang = userInfo.additionalInfo?['lang']; + final userInfo = (await _tbClient.getAuthControllerApi().getUser()).data!; + final lang = + userInfo.additionalInfo?.isMap == true + ? userInfo.additionalInfo?.asMap['lang'] + : null; final langStr = lang?.toString(); final locale = S.delegate.supportedLocales.firstWhereOrNull( (l) => l.toString() == langStr || l.languageCode == langStr, @@ -98,7 +105,7 @@ class Login extends _$Login { state = state.copyWith( isUserLoaded: true, user: userInfo, - userScope: userInfo.authority, + userScope: authorityFromString(userInfo.authority?.name ?? 'ANONYMOUS'), mobileLoginInfo: mobileInfo, ); } diff --git a/lib/core/auth/login/provider/login_provider.g.dart b/lib/core/auth/login/provider/login_provider.g.dart index c7ecddd0..6597a90f 100644 --- a/lib/core/auth/login/provider/login_provider.g.dart +++ b/lib/core/auth/login/provider/login_provider.g.dart @@ -6,7 +6,7 @@ part of 'login_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$loginHash() => r'0db0d0e86c1b872dd69b3438ee2915b5a47d2e35'; +String _$loginHash() => r'6aea9a4e0e56ecd174fecf5836f1bfdf29c9a713'; /// See also [Login]. @ProviderFor(Login) diff --git a/lib/core/auth/login/provider/oauth_provider.dart b/lib/core/auth/login/provider/oauth_provider.dart index fab2d970..a2eaf020 100644 --- a/lib/core/auth/login/provider/oauth_provider.dart +++ b/lib/core/auth/login/provider/oauth_provider.dart @@ -1,3 +1,4 @@ +import 'package:built_collection/built_collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:thingsboard_app/locator.dart'; @@ -7,34 +8,41 @@ import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_ser part 'oauth_provider.g.dart'; -final qrCode = OAuth2ClientInfo.fromJson({ - 'name': "qr", - 'icon': 'qr-code', - 'url': '', -}); +final qrCode = OAuth2ClientLoginInfo( + (b) => + b + ..name = 'qr' + ..icon = 'qr-code' + ..url = '', +); + @riverpod Future oauth(Ref ref) async { final tbClient = getIt().client; final deviceInfoService = getIt(); try { - final loginInfo = await tbClient.getMobileService().getLoginMobileInfo( - MobileInfoQuery( - packageName: deviceInfoService.getApplicationId(), - platformType: deviceInfoService.getPlatformType(), - ), - ); - + final response = await tbClient + .getMobileAppControllerApi() + .getLoginMobileInfo( + pkgName: deviceInfoService.getApplicationId(), + platform: deviceInfoService.getPlatformType().name, + ); + final loginInfo = response.data; if (loginInfo != null) { - return LoginMobileInfo( - oAuth2Clients: [...loginInfo.oAuth2Clients, qrCode], - storeInfo: loginInfo.storeInfo, - versionInfo: loginInfo.versionInfo, + final existingClients = + loginInfo.oAuth2ClientLoginInfos?.toList() ?? + []; + return loginInfo.rebuild( + (b) => + b.oAuth2ClientLoginInfos = ListBuilder([ + ...existingClients, + qrCode, + ]), ); } } catch (_) {} return LoginMobileInfo( - oAuth2Clients: [qrCode], - storeInfo: null, - versionInfo: null, + (b) => + b.oAuth2ClientLoginInfos = ListBuilder([qrCode]), ); } diff --git a/lib/core/auth/login/provider/oauth_provider.g.dart b/lib/core/auth/login/provider/oauth_provider.g.dart index 6dcbbf85..63740477 100644 --- a/lib/core/auth/login/provider/oauth_provider.g.dart +++ b/lib/core/auth/login/provider/oauth_provider.g.dart @@ -6,7 +6,7 @@ part of 'oauth_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$oauthHash() => r'06f8c9370dcd7fb9ff5b2b9e71b82c665da4f6a7'; +String _$oauthHash() => r'5a3a6e496bac6010c0c359b05a2f7c1556b3a303'; /// See also [oauth]. @ProviderFor(oauth) diff --git a/lib/core/auth/login/widgets/login_widget.dart b/lib/core/auth/login/widgets/login_widget.dart index dd3d64ed..89dc3078 100644 --- a/lib/core/auth/login/widgets/login_widget.dart +++ b/lib/core/auth/login/widgets/login_widget.dart @@ -87,7 +87,10 @@ class LoginWidget extends HookConsumerWidget { loading, ref, ), - clients: providers.value?.oAuth2Clients ?? [], + clients: + providers.value?.oAuth2ClientLoginInfos + ?.toList() ?? + [], ), TextDivider(text: S.of(context).or), @@ -148,7 +151,7 @@ class LoginWidget extends HookConsumerWidget { context, form, ref, - loading + loading, ); }, child: Text( @@ -195,8 +198,10 @@ Future onLoginPressed( final String password = form.control('password').value.toString(); try { loading.value = true; - final res = await ref.read(loginProvider.notifier).login(username, password); - + final res = await ref + .read(loginProvider.notifier) + .login(username, password); + loading.value = res; } catch (e) { form.setErrors({"err": {}}); @@ -219,7 +224,7 @@ Future onLoginWithBarcode(BuildContext context) async { } Future onOauth2ButtonPressed( - OAuth2ClientInfo client, + OAuth2ClientLoginInfo client, BuildContext context, ValueNotifier loading, WidgetRef ref, @@ -230,7 +235,9 @@ Future onOauth2ButtonPressed( return; } loading.value = true; -final res = await ref.read(loginProvider.notifier).oauthLogin(client.url); + final res = await ref + .read(loginProvider.notifier) + .oauthLogin(client.url ?? ''); loading.value = res; } diff --git a/lib/core/auth/login/widgets/o_auth_buttons.dart b/lib/core/auth/login/widgets/o_auth_buttons.dart index fea784d6..609148cc 100644 --- a/lib/core/auth/login/widgets/o_auth_buttons.dart +++ b/lib/core/auth/login/widgets/o_auth_buttons.dart @@ -12,40 +12,41 @@ class OAuthButtons extends StatelessWidget { required this.clients, required this.onButtonPressed, }); - final void Function(OAuth2ClientInfo) onButtonPressed; - final List clients; + final void Function(OAuth2ClientLoginInfo) onButtonPressed; + final List clients; @override Widget build(BuildContext context) { return clients.length == 1 && clients.first.name == 'qr' ? Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - OAuthButton( - logo: SvgPicture.asset( - ThingsboardImage.oauth2Logos['qr-code-logo']!, - ), - onTap: () => onButtonPressed(clients.first), - title: S.of(context).scanQrCode, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + OAuthButton( + logo: SvgPicture.asset( + ThingsboardImage.oauth2Logos['qr-code-logo']!, ), - ], - ) + onTap: () => onButtonPressed(clients.first), + title: S.of(context).scanQrCode, + ), + ], + ) : Row( - spacing: 10, - children: clients - .map( - (e) => Expanded( - child: OAuthButton( - logo: _getOauth2ButtonLogo(context, e), - onTap: () => onButtonPressed(e), + spacing: 10, + children: + clients + .map( + (e) => Expanded( + child: OAuthButton( + logo: _getOauth2ButtonLogo(context, e), + onTap: () => onButtonPressed(e), + ), ), - ), - ) - .toList(), - ); + ) + .toList(), + ); } } -Widget _getOauth2ButtonLogo(BuildContext context, OAuth2ClientInfo info) { +Widget _getOauth2ButtonLogo(BuildContext context, OAuth2ClientLoginInfo info) { final String? svgPath = ThingsboardImage.oauth2Logos[info.icon]; if (svgPath != null) { @@ -57,9 +58,6 @@ Widget _getOauth2ButtonLogo(BuildContext context, OAuth2ClientInfo info) { return SizedBox( width: 24, height: 24, - child: Icon( - iconData, - color: Colors.black, - ), + child: Icon(iconData, color: Colors.black), ); } diff --git a/lib/core/auth/noauth/provider/noauth_provider.dart b/lib/core/auth/noauth/provider/noauth_provider.dart index aeeb6208..fe12bb26 100644 --- a/lib/core/auth/noauth/provider/noauth_provider.dart +++ b/lib/core/auth/noauth/provider/noauth_provider.dart @@ -1,3 +1,4 @@ +import 'package:dio/dio.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:thingsboard_app/constants/app_constants.dart'; @@ -44,10 +45,27 @@ class NoauthProvider extends _$NoauthProvider { message: 'Getting data from your host $host', ); - final data = await _client.getLoginDataBySecretKey(host: host, key: key); - - final authUserFromJwt = _client.getAuthUserFromJwt(data.token); - final currentlyAuthUser = _client.getAuthUser(); + // Fetch JWT pair using secret key from the TARGET host via the QR code + // API. A raw Dio is used (instead of the typed + // `getQrCodeSettingsControllerApi().getUserTokenByMobileSecret`) because + // the request must hit `host`, not the current client's endpoint. + final tempDio = Dio( + BaseOptions( + baseUrl: host, + connectTimeout: const Duration(seconds: 20), + receiveTimeout: const Duration(seconds: 20), + ), + ); + final secretResponse = await tempDio.get('/api/noauth/qr/${key ?? ''}'); + final data = secretResponse.data; + final tokenStr = data is Map ? data['token'] as String? : null; + final refreshTokenStr = + data is Map ? data['refreshToken'] as String? : null; + if (tokenStr == null) { + throw ThingsboardError( + message: 'Failed to obtain a login token from $host', + ); + } // if (_client.isAuthenticated()) { // state = NoAuthState( // error: null, @@ -73,8 +91,8 @@ class NoauthProvider extends _$NoauthProvider { } await getIt().client.setUserFromJwtToken( - data.token, - data.refreshToken, + tokenStr, + refreshTokenStr, false, ); await getIt().setEndpoint(host); diff --git a/lib/core/auth/noauth/provider/noauth_provider.g.dart b/lib/core/auth/noauth/provider/noauth_provider.g.dart index 6d2715f5..b119f9e5 100644 --- a/lib/core/auth/noauth/provider/noauth_provider.g.dart +++ b/lib/core/auth/noauth/provider/noauth_provider.g.dart @@ -6,7 +6,7 @@ part of 'noauth_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$noauthProviderHash() => r'5151e2320cd66b6921e34e5ca88c094198f2eea9'; +String _$noauthProviderHash() => r'098288b187c0dc0d812e6674e8f86501e956ec99'; /// See also [NoauthProvider]. @ProviderFor(NoauthProvider) diff --git a/lib/core/auth/oauth2/tb_oauth2_client.dart b/lib/core/auth/oauth2/tb_oauth2_client.dart index 510a1a37..cd77b91b 100644 --- a/lib/core/auth/oauth2/tb_oauth2_client.dart +++ b/lib/core/auth/oauth2/tb_oauth2_client.dart @@ -9,8 +9,6 @@ import 'package:thingsboard_app/core/auth/oauth2/tb_o_auth2_authenticate_result. import 'package:thingsboard_app/core/auth/web/tb_web_auth.dart'; import 'package:thingsboard_app/core/logger/tb_logger.dart'; import 'package:thingsboard_app/locator.dart'; -import 'package:thingsboard_app/thingsboard_client.dart' - show PlatformTypeToString; import 'package:thingsboard_app/utils/services/device_info/i_device_info_service.dart'; import 'package:thingsboard_app/utils/services/endpoint/i_endpoint_service.dart'; @@ -46,7 +44,7 @@ class TbOAuth2Client implements IOAuth2Client { final params = Map.from(url.queryParameters); params['pkg'] = pkgName; params['appToken'] = appToken; - params['platform'] = _deviceInfoService.getPlatformType().toShortString(); + params['platform'] = _deviceInfoService.getPlatformType().name; url = url.replace(queryParameters: params); _logger.debug('TbOAuth2Client::authenticate() request url -> $url'); diff --git a/lib/core/auth/reset_password/reset_password_request_page.dart b/lib/core/auth/reset_password/reset_password_request_page.dart index 0d3c1b1b..5d9c92b2 100644 --- a/lib/core/auth/reset_password/reset_password_request_page.dart +++ b/lib/core/auth/reset_password/reset_password_request_page.dart @@ -9,6 +9,7 @@ import 'package:thingsboard_app/core/auth/login/widgets/text_field.dart'; import 'package:thingsboard_app/core/logger/tb_logger.dart'; import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; +import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/services/overlay_service/i_overlay_service.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; import 'package:thingsboard_app/utils/ui/visibility_widget.dart'; @@ -112,13 +113,19 @@ class ResetPasswordRequestPage extends HookConsumerWidget { isLoading.value = true; final formValue = form.control('email').value.toString(); try { - await getIt().client.sendResetPasswordLink(formValue); + await getIt().client + .getAuthControllerApi() + .requestResetPasswordByEmail( + resetPasswordEmailRequest: ResetPasswordEmailRequest( + (b) => b..email = formValue, + ), + ); getIt().showSuccessNotification( (_) => S.of(context).emailVerificationSentText, ); } catch (e) { getIt().error(e); - getIt().showErrorNotification( + getIt().showErrorNotification( (_) => "${S.of(context).fatalError}: $e", ); } diff --git a/lib/core/context/tb_context.dart b/lib/core/context/tb_context.dart index 1a7ade2f..de79953e 100644 --- a/lib/core/context/tb_context.dart +++ b/lib/core/context/tb_context.dart @@ -12,6 +12,7 @@ import 'package:thingsboard_app/core/logger/tb_logger.dart'; import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/version_service/version_info.dart'; import 'package:thingsboard_app/utils/services/device_info/i_device_info_service.dart'; import 'package:thingsboard_app/utils/services/endpoint/i_endpoint_service.dart'; import 'package:thingsboard_app/utils/services/firebase/i_firebase_service.dart'; @@ -26,6 +27,7 @@ class TbContext implements PopEntry { bool isUserLoaded = false; final _isAuthenticated = ValueNotifier(false); List? twoFactorAuthProviders; + bool _dashboardAccessDenied = false; User? userDetails; HomeDashboardInfo? homeDashboard; VersionInfo? versionInfo; @@ -152,8 +154,10 @@ class TbContext implements PopEntry { Future checkDasboardAccess(String id) async { try { - final dashboard = await tbClient.getDashboardService().getDashboard(id); - if (dashboard == null) { + final response = await tbClient + .getDashboardControllerApi() + .getDashboardById(dashboardId: id); + if (response.data == null) { return false; } } catch (e) { @@ -172,24 +176,29 @@ class TbContext implements PopEntry { log.debug('authUser: ${tbClient.getAuthUser()}'); if (tbClient.getAuthUser()!.userId != null) { try { - final mobileInfo = await tbClient - .getMobileService() + final mobileResp = await tbClient + .getMobileAppControllerApi() .getUserMobileInfo( - MobileInfoQuery( - platformType: _deviceInfoService.getPlatformType(), - packageName: _deviceInfoService.getApplicationId(), - ), + pkgName: _deviceInfoService.getApplicationId(), + platform: _deviceInfoService.getPlatformType().name, ); + final mobileInfo = mobileResp.data; userDetails = mobileInfo?.user; homeDashboard = mobileInfo?.homeDashboardInfo; - versionInfo = mobileInfo?.versionInfo; + versionInfo = + mobileInfo?.versionInfo != null + ? VersionInfo.fromMobileAppVersionInfo( + mobileInfo!.versionInfo!, + ) + : null; storeInfo = mobileInfo?.storeInfo; + _dashboardAccessDenied = false; if (_defaultDashboardId() != null) { final hasAccess = await checkDasboardAccess( _defaultDashboardId()!, ); if (!hasAccess) { - userDetails?.additionalInfo?['defaultDashboardId'] = null; + _dashboardAccessDenied = true; } } } catch (e) { @@ -204,10 +213,11 @@ class TbContext implements PopEntry { } else { if (tbClient.isPreVerificationToken()) { log.debug('authUser: ${tbClient.getAuthUser()}'); - twoFactorAuthProviders = + final tfaResp = await tbClient - .getTwoFactorAuthService() - .getAvailableLoginTwoFaProviders(); + .getTwoFactorAuthControllerApi() + .getAvailableTwoFaProviderInfos(); + twoFactorAuthProviders = tfaResp.data?.toList(); } else { twoFactorAuthProviders = null; } @@ -246,9 +256,7 @@ class TbContext implements PopEntry { await updateRouteState(); } - if (isAuthenticated) { - - } + if (isAuthenticated) {} } catch (e, s) { log.error('TbContext.onUserLoaded: $e', e, s); @@ -363,17 +371,18 @@ class TbContext implements PopEntry { } String? _defaultDashboardId() { - if (userDetails != null && userDetails!.additionalInfo != null) { - return userDetails!.additionalInfo!['defaultDashboardId']?.toString(); - } - return null; + if (_dashboardAccessDenied) return null; + final info = userDetails?.additionalInfo; + if (info == null || !info.isMap) return null; + return info.asMap['defaultDashboardId']?.toString(); } bool _userForceFullscreen() { + final info = userDetails?.additionalInfo; return tbClient.getAuthUser()!.isPublic! || - (userDetails != null && - userDetails!.additionalInfo != null && - userDetails!.additionalInfo!['defaultDashboardFullscreen'] == true); + (info != null && + info.isMap && + info.asMap['defaultDashboardFullscreen'] == true); } static String userAgent() { diff --git a/lib/core/entity/entities_base.dart b/lib/core/entity/entities_base.dart index 94cee6ff..008c7d59 100644 --- a/lib/core/entity/entities_base.dart +++ b/lib/core/entity/entities_base.dart @@ -9,7 +9,8 @@ import 'package:thingsboard_app/config/themes/design_tokens.dart'; import 'package:thingsboard_app/config/themes/tb_text_styles.dart'; import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; -import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart' hide Direction; +import 'package:thingsboard_ce_client/src/model/page/sort_order.dart'; import 'package:thingsboard_app/utils/services/overlay_service/i_overlay_service.dart'; import 'package:thingsboard_app/utils/ui/pagination_widgets/first_page_exception_widget.dart'; import 'package:thingsboard_app/utils/utils.dart'; @@ -59,9 +60,10 @@ mixin EntitiesBase { void onEntityTap(T entity, WidgetRef ref); } -mixin ContactBasedBase on EntitiesBase { +mixin ContactBasedBase on EntitiesBase { @override Widget buildEntityListCard(BuildContext context, T contact) { + final e = contact as dynamic; final address = Utils.contactToShortAddress(contact); return Container( constraints: const BoxConstraints(minHeight: 56), @@ -82,7 +84,9 @@ mixin ContactBasedBase on EntitiesBase { children: [ Expanded( child: Text( - contact.getName(), + contact is HasName + ? contact.getName() + : (e.name as String? ?? ''), maxLines: 2, overflow: TextOverflow.ellipsis, style: TbTextStyles.labelLarge.copyWith( @@ -94,7 +98,7 @@ mixin ContactBasedBase on EntitiesBase { Text( entityDateFormat.format( DateTime.fromMillisecondsSinceEpoch( - contact.createdTime!, + e.createdTime as int? ?? 0, ), ), style: TbTextStyles.bodyMedium.copyWith( @@ -104,9 +108,9 @@ mixin ContactBasedBase on EntitiesBase { ], ), - if (contact.email != null) + if ((e.email as String?) != null) Text( - contact.email!, + e.email as String, style: TbTextStyles.labelSmall.copyWith( color: AppColors.textTertiary, ), @@ -204,7 +208,8 @@ abstract class BaseEntitiesWidget extends ConsumerStatefulWidget : null; } -abstract class BaseEntitiesState extends ConsumerState> { +abstract class BaseEntitiesState + extends ConsumerState> { BaseEntitiesState(); late final PagingController pagingController; Completer? _refreshCompleter; @@ -221,7 +226,17 @@ abstract class BaseEntitiesState extends ConsumerState extends ConsumerState extends StatefulWidget { +abstract class EntityDetailsPage extends StatefulWidget { const EntityDetailsPage({ required String defaultTitle, required String entityId, @@ -50,7 +50,7 @@ abstract class EntityDetailsPage extends StatefulWidget { Widget buildEntityDetails(BuildContext context, T entity); } -class _EntityDetailsPageState +class _EntityDetailsPageState extends State> { late Future entityFuture; late ValueNotifier titleValue; @@ -138,7 +138,7 @@ class _EntityDetailsPageState } } -abstract class ContactBasedDetailsPage +abstract class ContactBasedDetailsPage extends EntityDetailsPage { const ContactBasedDetailsPage({ required super.defaultTitle, @@ -152,16 +152,20 @@ abstract class ContactBasedDetailsPage @override Widget buildEntityDetails(BuildContext context, T entity) { + final e = entity as dynamic; return Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(S.of(context).title, style: labelTextStyle), - Text(entity.getName(), style: valueTextStyle), + Text( + entity is HasName ? entity.getName() : (e.name as String? ?? ''), + style: valueTextStyle, + ), const SizedBox(height: 16), Text(S.of(context).country, style: labelTextStyle), - Text(entity.country ?? '', style: valueTextStyle), + Text(e.country as String? ?? '', style: valueTextStyle), const SizedBox(height: 16), Row( children: [ @@ -171,7 +175,7 @@ abstract class ContactBasedDetailsPage crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(S.of(context).city, style: labelTextStyle), - Text(entity.city ?? '', style: valueTextStyle), + Text(e.city as String? ?? '', style: valueTextStyle), ], ), ), @@ -181,7 +185,7 @@ abstract class ContactBasedDetailsPage crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(S.of(context).stateOrProvince, style: labelTextStyle), - Text(entity.state ?? '', style: valueTextStyle), + Text(e.state as String? ?? '', style: valueTextStyle), ], ), ), @@ -189,19 +193,19 @@ abstract class ContactBasedDetailsPage ), const SizedBox(height: 16), Text(S.of(context).postalCode, style: labelTextStyle), - Text(entity.zip ?? '', style: valueTextStyle), + Text(e.zip as String? ?? '', style: valueTextStyle), const SizedBox(height: 16), Text(S.of(context).address, style: labelTextStyle), - Text(entity.address ?? '', style: valueTextStyle), + Text(e.address as String? ?? '', style: valueTextStyle), const SizedBox(height: 16), Text(S.of(context).address2, style: labelTextStyle), - Text(entity.address2 ?? '', style: valueTextStyle), + Text(e.address2 as String? ?? '', style: valueTextStyle), const SizedBox(height: 16), Text(S.of(context).phone, style: labelTextStyle), - Text(entity.phone ?? '', style: valueTextStyle), + Text(e.phone as String? ?? '', style: valueTextStyle), const SizedBox(height: 16), Text(S.of(context).email, style: labelTextStyle), - Text(entity.email ?? '', style: valueTextStyle), + Text(e.email as String? ?? '', style: valueTextStyle), ], ), ); diff --git a/lib/modules/alarm/alarms_base.dart b/lib/modules/alarm/alarms_base.dart index 0911d9bf..4f1b671d 100644 --- a/lib/modules/alarm/alarms_base.dart +++ b/lib/modules/alarm/alarms_base.dart @@ -13,6 +13,7 @@ extension AlarmSeverityColors on AlarmSeverity { AlarmSeverity.WARNING => const Color(0xFFFAA405), AlarmSeverity.INDETERMINATE => Colors.black.withValues(alpha: 0.38), + _ => Colors.transparent, }; } } diff --git a/lib/modules/alarm/alarms_list.dart b/lib/modules/alarm/alarms_list.dart index bfc6a39f..b38260df 100644 --- a/lib/modules/alarm/alarms_list.dart +++ b/lib/modules/alarm/alarms_list.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:flutter/material.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:thingsboard_app/core/entity/entity_list_card.dart'; diff --git a/lib/modules/alarm/data/datasource/alarm_types/alarm_types_datasource.dart b/lib/modules/alarm/data/datasource/alarm_types/alarm_types_datasource.dart index ca5b428d..bfdd2586 100644 --- a/lib/modules/alarm/data/datasource/alarm_types/alarm_types_datasource.dart +++ b/lib/modules/alarm/data/datasource/alarm_types/alarm_types_datasource.dart @@ -1,5 +1,6 @@ import 'package:thingsboard_app/modules/alarm/data/datasource/alarm_types/i_alarm_types_datasource.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/new_client_page_data.dart'; class AlarmTypesDatasource implements IAlarmTypesDatasource { const AlarmTypesDatasource({required this.tbClient}); @@ -7,7 +8,13 @@ class AlarmTypesDatasource implements IAlarmTypesDatasource { final ThingsboardClient tbClient; @override - Future> fetchAlarmTypes(PageLink pageKey) { - return tbClient.getAlarmService().getAlarmTypes(pageKey); + Future> fetchAlarmTypes(PageLink pageKey) async { + final response = await tbClient.getAlarmControllerApi().getAlarmTypes( + pageSize: pageKey.pageSize, + page: pageKey.page, + textSearch: pageKey.textSearch, + ); + final page = response.data!; + return toPageData(page.data, page.totalPages, page.totalElements, page.hasNext); } } diff --git a/lib/modules/alarm/data/datasource/alarm_types/i_alarm_types_datasource.dart b/lib/modules/alarm/data/datasource/alarm_types/i_alarm_types_datasource.dart index 031821f5..79536756 100644 --- a/lib/modules/alarm/data/datasource/alarm_types/i_alarm_types_datasource.dart +++ b/lib/modules/alarm/data/datasource/alarm_types/i_alarm_types_datasource.dart @@ -1,5 +1,5 @@ import 'package:thingsboard_app/thingsboard_client.dart'; abstract interface class IAlarmTypesDatasource { - Future> fetchAlarmTypes(PageLink pageKey); + Future> fetchAlarmTypes(PageLink pageKey); } diff --git a/lib/modules/alarm/data/datasource/alarms/alarms_datasource.dart b/lib/modules/alarm/data/datasource/alarms/alarms_datasource.dart index 585c7380..f5c133a9 100644 --- a/lib/modules/alarm/data/datasource/alarms/alarms_datasource.dart +++ b/lib/modules/alarm/data/datasource/alarms/alarms_datasource.dart @@ -1,5 +1,8 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/modules/alarm/data/datasource/alarms/i_alarms_datasource.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/new_client_page_data.dart'; class AlarmsDatasource implements IAlarmsDatasource { const AlarmsDatasource({required this.thingsboardClient}); @@ -7,12 +10,45 @@ class AlarmsDatasource implements IAlarmsDatasource { final ThingsboardClient thingsboardClient; @override - Future> fetchAlarms(AlarmQueryV2 query) { - return thingsboardClient.getAlarmService().getAllAlarmsV2(query); + Future> fetchAlarms(AlarmQueryV2 query) async { + final response = await thingsboardClient + .getAlarmControllerApi() + .getAllAlarmsV2( + pageSize: query.pageLink.pageSize, + page: query.pageLink.page, + textSearch: query.pageLink.textSearch, + sortProperty: query.pageLink.sortOrder?.property, + sortOrder: query.pageLink.sortOrder?.direction.name, + startTime: query.pageLink.startTime, + endTime: query.pageLink.endTime, + statusList: + query.statusList != null + ? BuiltList(query.statusList!.map((s) => s.name)) + : null, + severityList: + query.severityList != null + ? BuiltList(query.severityList!.map((s) => s.name)) + : null, + typeList: + query.typeList != null + ? BuiltList(query.typeList!) + : null, + assigneeId: query.assigneeId?.id, + ); + final page = response.data!; + return toPageData( + page.data, + page.totalPages, + page.totalElements, + page.hasNext, + ); } @override - Future getAlarmInfo(String id) { - return thingsboardClient.getAlarmService().getAlarmInfo(id); + Future getAlarmInfo(String id) async { + final response = await thingsboardClient + .getAlarmControllerApi() + .getAlarmInfoById(alarmId: id); + return response.data; } } diff --git a/lib/modules/alarm/data/datasource/alarms/i_alarms_datasource.dart b/lib/modules/alarm/data/datasource/alarms/i_alarms_datasource.dart index 4fc136c0..705e65a9 100644 --- a/lib/modules/alarm/data/datasource/alarms/i_alarms_datasource.dart +++ b/lib/modules/alarm/data/datasource/alarms/i_alarms_datasource.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; abstract interface class IAlarmsDatasource { diff --git a/lib/modules/alarm/data/datasource/assignee/assignee_datasource.dart b/lib/modules/alarm/data/datasource/assignee/assignee_datasource.dart index f5144554..84ebb2ea 100644 --- a/lib/modules/alarm/data/datasource/assignee/assignee_datasource.dart +++ b/lib/modules/alarm/data/datasource/assignee/assignee_datasource.dart @@ -1,5 +1,6 @@ import 'package:thingsboard_app/modules/alarm/data/datasource/assignee/i_assignee_datasource.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/new_client_page_data.dart'; class AssigneeDatasource implements IAssigneeDatasource { const AssigneeDatasource({required this.tbClient}); @@ -7,7 +8,20 @@ class AssigneeDatasource implements IAssigneeDatasource { final ThingsboardClient tbClient; @override - Future> fetchAssignee(PageLink pageKey) { - return tbClient.getUserService().getUsersInfo(pageKey); + Future> fetchAssignee(PageLink pageKey) async { + // Pre-migration this hit GET /api/users/info (findUsersByQuery), which + // returns users scoped to the current user's tenant/customer hierarchy. + final response = await tbClient.getUserControllerApi().findUsersByQuery( + pageSize: pageKey.pageSize, + page: pageKey.page, + textSearch: pageKey.textSearch, + ); + final page = response.data!; + return toPageData( + page.data, + page.totalPages, + page.totalElements, + page.hasNext, + ); } } diff --git a/lib/modules/alarm/data/datasource/assignee/i_assignee_datasource.dart b/lib/modules/alarm/data/datasource/assignee/i_assignee_datasource.dart index 14ba0520..081f5aca 100644 --- a/lib/modules/alarm/data/datasource/assignee/i_assignee_datasource.dart +++ b/lib/modules/alarm/data/datasource/assignee/i_assignee_datasource.dart @@ -1,5 +1,5 @@ import 'package:thingsboard_app/thingsboard_client.dart'; abstract interface class IAssigneeDatasource { - Future> fetchAssignee(PageLink pageKey); + Future> fetchAssignee(PageLink pageKey); } diff --git a/lib/modules/alarm/data/datasource/details/alarm_details_datasource.dart b/lib/modules/alarm/data/datasource/details/alarm_details_datasource.dart index 7074aad3..d7daa9b1 100644 --- a/lib/modules/alarm/data/datasource/details/alarm_details_datasource.dart +++ b/lib/modules/alarm/data/datasource/details/alarm_details_datasource.dart @@ -1,5 +1,8 @@ +import 'package:built_value/json_object.dart'; import 'package:thingsboard_app/modules/alarm/data/datasource/details/i_alarm_details_datasource.dart'; +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/new_client_page_data.dart'; class AlarmDetailsDatasource implements IAlarmDetailsDatasource { const AlarmDetailsDatasource(this.thingsboardClient); @@ -9,36 +12,51 @@ class AlarmDetailsDatasource implements IAlarmDetailsDatasource { @override Future> fetchAlarmComments( AlarmCommentsQuery query, - ) { - return thingsboardClient.getAlarmService().getAlarmComments(query); + ) async { + final res = await thingsboardClient + .getAlarmCommentControllerApi() + .getAlarmComments( + alarmId: query.id.id!, + pageSize: query.pageLink.pageSize, + page: query.pageLink.page, + sortProperty: query.pageLink.sortOrder?.property, + sortOrder: query.pageLink.sortOrder?.direction.name, + ); + final pd = res.data!; + return toPageData(pd.data, pd.totalPages, pd.totalElements, pd.hasNext); } @override - Future acknowledgeAlarm(AlarmId id) { - return thingsboardClient.getAlarmService().ackAlarm(id.id!); + Future acknowledgeAlarm(AlarmId id) async { + final res = await thingsboardClient.getAlarmControllerApi().ackAlarm( + alarmId: id.id!, + ); + return res.data!; } @override - Future clearAlarm(AlarmId id) { - return thingsboardClient.getAlarmService().clearAlarm(id.id!); + Future clearAlarm(AlarmId id) async { + final res = await thingsboardClient.getAlarmControllerApi().clearAlarm( + alarmId: id.id!, + ); + return res.data!; } @override Future postComment( AlarmId alarmId, { required String comment, - }) { - return thingsboardClient.getAlarmService().postAlarmComment( - AlarmComment(null, null, alarmId, null, AlarmCommentType.OTHER, {'text': comment}, null), - ); - } - - @override - Future deleteComment(AlarmId id, {required String commentId}) { - return thingsboardClient.getAlarmService().deleteAlarmComment( - commentId, - alarmId: id, + }) async { + final body = AlarmComment( + (b) => + b + ..type = AlarmCommentType.OTHER + ..comment = JsonObject({'text': comment}), ); + final res = await thingsboardClient + .getAlarmCommentControllerApi() + .saveAlarmComment(alarmId: alarmId.id!, alarmComment: body); + return _alarmCommentToInfo(res.data!); } @override @@ -46,24 +64,82 @@ class AlarmDetailsDatasource implements IAlarmDetailsDatasource { AlarmId alarmId, { required String id, required String comment, - }) { - return thingsboardClient.getAlarmService().postAlarmComment( - AlarmComment(id, null, alarmId, null, AlarmCommentType.OTHER, {'text': comment, 'edited': 'true'}, null), + }) async { + final body = AlarmComment( + (b) => + b + // The server upserts based on the presence of `id`: setting it + // updates the existing comment, omitting it creates a new one. + ..id = AlarmCommentId((idb) => idb..id = id).toBuilder() + ..type = AlarmCommentType.OTHER + ..comment = JsonObject({ + 'text': comment, + 'edited': 'true', + }), + ); + final res = await thingsboardClient + .getAlarmCommentControllerApi() + .saveAlarmComment(alarmId: alarmId.id!, alarmComment: body); + return _alarmCommentToInfo(res.data!); + } + + @override + Future deleteComment(AlarmId id, {required String commentId}) async { + await thingsboardClient.getAlarmCommentControllerApi().deleteAlarmComment( + alarmId: id.id!, + commentId: commentId, ); } @override - Future> fetchAssignee(UsersAssignQuery query) { - return thingsboardClient.getUserService().getUsersAssign(query); + Future> fetchAssignee(UsersAssignQuery query) async { + final res = await thingsboardClient + .getUserControllerApi() + .getUsersForAssign( + alarmId: query.id.id!, + pageSize: query.pageLink.pageSize, + page: query.pageLink.page, + textSearch: query.pageLink.textSearch, + ); + final pd = res.data!; + return toPageData(pd.data, pd.totalPages, pd.totalElements, pd.hasNext); } @override - Future assignAlarm(String alarmId, String assigneeId) { - return thingsboardClient.getAlarmService().assignAlarm(alarmId, assigneeId); + Future assignAlarm(String alarmId, String assigneeId) async { + final res = await thingsboardClient.getAlarmControllerApi().assignAlarm( + alarmId: alarmId, + assigneeId: assigneeId, + ); + // assignAlarm returns Alarm (not AlarmInfo), so fetch the full AlarmInfo. + final alarm = res.data!; + final infoRes = await thingsboardClient + .getAlarmControllerApi() + .getAlarmInfoById(alarmId: alarm.id?.id ?? alarmId); + return infoRes.data!; } @override - Future unassignAlarm(String alarmId) { - return thingsboardClient.getAlarmService().unassignAlarm(alarmId); + Future unassignAlarm(String alarmId) async { + final res = await thingsboardClient.getAlarmControllerApi().unassignAlarm( + alarmId: alarmId, + ); + final alarm = res.data!; + final infoRes = await thingsboardClient + .getAlarmControllerApi() + .getAlarmInfoById(alarmId: alarm.id?.id ?? alarmId); + return infoRes.data!; } } + +AlarmCommentInfo _alarmCommentToInfo(AlarmComment c) => AlarmCommentInfo( + (b) => + b + ..id = c.id?.toBuilder() + ..createdTime = c.createdTime + ..alarmId = c.alarmId?.toBuilder() + ..userId = c.userId?.toBuilder() + ..type = c.type + ..comment = c.comment, +); + diff --git a/lib/modules/alarm/data/datasource/details/i_alarm_details_datasource.dart b/lib/modules/alarm/data/datasource/details/i_alarm_details_datasource.dart index 80a35c78..c2396dcb 100644 --- a/lib/modules/alarm/data/datasource/details/i_alarm_details_datasource.dart +++ b/lib/modules/alarm/data/datasource/details/i_alarm_details_datasource.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; abstract interface class IAlarmDetailsDatasource { @@ -22,7 +23,7 @@ abstract interface class IAlarmDetailsDatasource { Future deleteComment(AlarmId id, {required String commentId}); - Future> fetchAssignee(UsersAssignQuery query); + Future> fetchAssignee(UsersAssignQuery query); Future assignAlarm(String alarmId, String assigneeId); diff --git a/lib/modules/alarm/data/repository/alarm_types/alarm_types_repository.dart b/lib/modules/alarm/data/repository/alarm_types/alarm_types_repository.dart index ecbdd787..168a1ca2 100644 --- a/lib/modules/alarm/data/repository/alarm_types/alarm_types_repository.dart +++ b/lib/modules/alarm/data/repository/alarm_types/alarm_types_repository.dart @@ -8,7 +8,7 @@ class AlarmTypesRepository implements IAlarmTypesRepository { final IAlarmTypesDatasource datasource; @override - Future> fetchAlarmTypes(PageLink pageKey) { + Future> fetchAlarmTypes(PageLink pageKey) { return datasource.fetchAlarmTypes(pageKey); } } diff --git a/lib/modules/alarm/data/repository/alarms/alarms_repository.dart b/lib/modules/alarm/data/repository/alarms/alarms_repository.dart index 59f74b02..b59dcf25 100644 --- a/lib/modules/alarm/data/repository/alarms/alarms_repository.dart +++ b/lib/modules/alarm/data/repository/alarms/alarms_repository.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/modules/alarm/data/datasource/alarms/i_alarms_datasource.dart'; import 'package:thingsboard_app/modules/alarm/domain/repository/alarms/i_alarms_repository.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; diff --git a/lib/modules/alarm/data/repository/assignee/assignee_repository.dart b/lib/modules/alarm/data/repository/assignee/assignee_repository.dart index b51d752b..d13984cb 100644 --- a/lib/modules/alarm/data/repository/assignee/assignee_repository.dart +++ b/lib/modules/alarm/data/repository/assignee/assignee_repository.dart @@ -8,7 +8,7 @@ class AssigneeRepository implements IAssigneeRepository { final IAssigneeDatasource datasource; @override - Future> fetchAssignee(PageLink pageKey) { + Future> fetchAssignee(PageLink pageKey) { return datasource.fetchAssignee(pageKey); } } diff --git a/lib/modules/alarm/data/repository/details/alarm_details_repository.dart b/lib/modules/alarm/data/repository/details/alarm_details_repository.dart index 9d7fcd78..df6c8164 100644 --- a/lib/modules/alarm/data/repository/details/alarm_details_repository.dart +++ b/lib/modules/alarm/data/repository/details/alarm_details_repository.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/modules/alarm/data/datasource/details/i_alarm_details_datasource.dart'; import 'package:thingsboard_app/modules/alarm/domain/repository/details/i_alarm_details_repository.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; @@ -47,7 +48,7 @@ class AlarmDetailsRepository implements IAlarmDetailsRepository { } @override - Future> fetchAssignee(UsersAssignQuery query) { + Future> fetchAssignee(UsersAssignQuery query) { return datasource.fetchAssignee(query); } diff --git a/lib/modules/alarm/di/alarm_types_di.dart b/lib/modules/alarm/di/alarm_types_di.dart index a9020da6..1c19ceb3 100644 --- a/lib/modules/alarm/di/alarm_types_di.dart +++ b/lib/modules/alarm/di/alarm_types_di.dart @@ -31,7 +31,7 @@ class AlarmTypesDi { () => FetchAlarmTypesUseCase(repository: locator()), ); - locator.registerFactory>( + locator.registerFactory>( () => AlarmTypesPaginationRepository( alarmTypesQueryCtrl: locator(), onFetchPageData: locator(), @@ -50,7 +50,7 @@ class AlarmTypesDi { } static void dispose(String scopeName) { - getIt>().dispose(); + getIt>().dispose(); getIt().close(); getIt.dropScope(scopeName); } diff --git a/lib/modules/alarm/di/alarms_di.dart b/lib/modules/alarm/di/alarms_di.dart index 7f366854..bd7572ec 100644 --- a/lib/modules/alarm/di/alarms_di.dart +++ b/lib/modules/alarm/di/alarms_di.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/modules/alarm/data/datasource/alarms/alarms_datasource.dart'; import 'package:thingsboard_app/modules/alarm/data/datasource/alarms/i_alarms_datasource.dart'; diff --git a/lib/modules/alarm/di/assignee_di.dart b/lib/modules/alarm/di/assignee_di.dart index b3ee6065..de4a202e 100644 --- a/lib/modules/alarm/di/assignee_di.dart +++ b/lib/modules/alarm/di/assignee_di.dart @@ -53,7 +53,7 @@ class AssigneeDi { } static void dispose(String scopeName) { - getIt>().dispose(); + getIt>().dispose(); getIt().close(); getIt.dropScope(scopeName); } diff --git a/lib/modules/alarm/domain/entities/alarm_filters_entity.dart b/lib/modules/alarm/domain/entities/alarm_filters_entity.dart index 3701ba5f..cf6a6bb7 100644 --- a/lib/modules/alarm/domain/entities/alarm_filters_entity.dart +++ b/lib/modules/alarm/domain/entities/alarm_filters_entity.dart @@ -24,7 +24,15 @@ class AlarmFiltersEntity extends Equatable { typeList: typeList.isNotEmpty ? typeList : null, statusList: status.isNotEmpty ? status : null, severityList: severity.isNotEmpty ? severity : null, - assigneeId: userId != null ? UserId(userId) : null, + assigneeId: + userId != null + ? UserId( + (b) => + b + ..id = userId + ..entityType = EntityType.USER, + ) + : null, ); } diff --git a/lib/modules/alarm/domain/entities/assignee_entity.dart b/lib/modules/alarm/domain/entities/assignee_entity.dart index b285c21a..41631671 100644 --- a/lib/modules/alarm/domain/entities/assignee_entity.dart +++ b/lib/modules/alarm/domain/entities/assignee_entity.dart @@ -10,14 +10,14 @@ class AssigneeEntity extends Equatable { }); factory AssigneeEntity.fromUserInfo( - UserInfo info, { + UserEmailInfo info, { required UserDetailsUseCase detailsUseCase, }) { final details = detailsUseCase( UserDetailsParams( firstName: info.firstName, lastName: info.lastName, - email: info.email, + email: info.email ?? '', ), ); @@ -28,7 +28,7 @@ class AssigneeEntity extends Equatable { ); } - final UserInfo userInfo; + final UserEmailInfo userInfo; final String shortName; final String displayName; diff --git a/lib/modules/alarm/domain/pagination/activity/alarm_activity_pagination_repository.dart b/lib/modules/alarm/domain/pagination/activity/alarm_activity_pagination_repository.dart index 8548fb95..2ad43ec4 100644 --- a/lib/modules/alarm/domain/pagination/activity/alarm_activity_pagination_repository.dart +++ b/lib/modules/alarm/domain/pagination/activity/alarm_activity_pagination_repository.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/modules/alarm/domain/pagination/activity/alarm_activity_query_ctrl.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/services/pagination_repository.dart'; diff --git a/lib/modules/alarm/domain/pagination/activity/alarm_activity_query_ctrl.dart b/lib/modules/alarm/domain/pagination/activity/alarm_activity_query_ctrl.dart index 15544478..b0672458 100644 --- a/lib/modules/alarm/domain/pagination/activity/alarm_activity_query_ctrl.dart +++ b/lib/modules/alarm/domain/pagination/activity/alarm_activity_query_ctrl.dart @@ -1,5 +1,7 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/core/entity/entities_base.dart'; -import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart' hide Direction; +import 'package:thingsboard_ce_client/src/model/page/sort_order.dart'; class AlarmActivityQueryCtrl extends PageKeyController { AlarmActivityQueryCtrl({ diff --git a/lib/modules/alarm/domain/pagination/alarm_query_keys.dart b/lib/modules/alarm/domain/pagination/alarm_query_keys.dart new file mode 100644 index 00000000..c89f88f2 --- /dev/null +++ b/lib/modules/alarm/domain/pagination/alarm_query_keys.dart @@ -0,0 +1,43 @@ +import 'package:thingsboard_app/thingsboard_client.dart'; + +/// App-side page-key holders. +/// +/// The new autogenerated client exposes flat method parameters instead of query +/// objects. Where a query object still exists in the client it is typed against the +/// handwritten enums/ids that the barrel hides (so it is unusable), and the app's +/// pagination framework (`PageKeyController

`) needs a single key object anyway. +/// These lightweight holders preserve that key shape; the datasources decompose them +/// into the controllers' flat params. All referenced types (`TimePageLink`, `PageLink`, +/// `AlarmSearchStatus`, `AlarmSeverity`, `AlarmId`, `UserId`, `EntityId`) resolve to the +/// built_value versions the rest of the app uses. +class AlarmQueryV2 { + AlarmQueryV2( + this.pageLink, { + this.affectedEntityId, + this.typeList, + this.statusList, + this.severityList, + this.assigneeId, + }); + + EntityId? affectedEntityId; + TimePageLink pageLink; + List? typeList; + List? statusList; + List? severityList; + UserId? assigneeId; +} + +class AlarmCommentsQuery { + const AlarmCommentsQuery({required this.pageLink, required this.id}); + + final PageLink pageLink; + final AlarmId id; +} + +class UsersAssignQuery { + const UsersAssignQuery({required this.pageLink, required this.id}); + + final PageLink pageLink; + final AlarmId id; +} diff --git a/lib/modules/alarm/domain/pagination/alarm_types/alarm_types_pagination_repository.dart b/lib/modules/alarm/domain/pagination/alarm_types/alarm_types_pagination_repository.dart index fb2c8d22..abd09b84 100644 --- a/lib/modules/alarm/domain/pagination/alarm_types/alarm_types_pagination_repository.dart +++ b/lib/modules/alarm/domain/pagination/alarm_types/alarm_types_pagination_repository.dart @@ -3,16 +3,16 @@ import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/services/pagination_repository.dart'; final class AlarmTypesPaginationRepository - extends PaginationRepository { + extends PaginationRepository { AlarmTypesPaginationRepository({ required AlarmTypesQueryCtrl alarmTypesQueryCtrl, required this.onFetchPageData, }) : super(pageKeyController: alarmTypesQueryCtrl); - final Future> Function(PageLink) onFetchPageData; + final Future> Function(PageLink) onFetchPageData; @override - Future> fetchPageData(PageLink pageKey) { + Future> fetchPageData(PageLink pageKey) { return onFetchPageData(pageKey); } } diff --git a/lib/modules/alarm/domain/pagination/alarm_types/alarm_types_query_ctrl.dart b/lib/modules/alarm/domain/pagination/alarm_types/alarm_types_query_ctrl.dart index b1ac2135..79a8c0e7 100644 --- a/lib/modules/alarm/domain/pagination/alarm_types/alarm_types_query_ctrl.dart +++ b/lib/modules/alarm/domain/pagination/alarm_types/alarm_types_query_ctrl.dart @@ -1,5 +1,6 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; -import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart' hide Direction; +import 'package:thingsboard_ce_client/src/model/page/sort_order.dart'; class AlarmTypesQueryCtrl extends PageKeyController { AlarmTypesQueryCtrl({ diff --git a/lib/modules/alarm/domain/pagination/alarms/alarms_pagination_repository.dart b/lib/modules/alarm/domain/pagination/alarms/alarms_pagination_repository.dart index 86ec1939..add0b69d 100644 --- a/lib/modules/alarm/domain/pagination/alarms/alarms_pagination_repository.dart +++ b/lib/modules/alarm/domain/pagination/alarms/alarms_pagination_repository.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/modules/alarm/domain/pagination/alarms/alarms_query_ctrl.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/services/pagination_repository.dart'; diff --git a/lib/modules/alarm/domain/pagination/alarms/alarms_query_ctrl.dart b/lib/modules/alarm/domain/pagination/alarms/alarms_query_ctrl.dart index 51303042..666c1146 100644 --- a/lib/modules/alarm/domain/pagination/alarms/alarms_query_ctrl.dart +++ b/lib/modules/alarm/domain/pagination/alarms/alarms_query_ctrl.dart @@ -1,6 +1,8 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/core/entity/entities_base.dart'; import 'package:thingsboard_app/modules/alarm/domain/entities/alarm_filters_entity.dart'; -import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart' hide Direction; +import 'package:thingsboard_ce_client/src/model/page/sort_order.dart'; class AlarmQueryController extends PageKeyController { AlarmQueryController({int pageSize = 20, String? searchText}) diff --git a/lib/modules/alarm/domain/pagination/assignee/alarm_assignee_pagiation_repository.dart b/lib/modules/alarm/domain/pagination/assignee/alarm_assignee_pagiation_repository.dart index e1f1e17b..82537af4 100644 --- a/lib/modules/alarm/domain/pagination/assignee/alarm_assignee_pagiation_repository.dart +++ b/lib/modules/alarm/domain/pagination/assignee/alarm_assignee_pagiation_repository.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/modules/alarm/domain/entities/assignee_entity.dart'; import 'package:thingsboard_app/modules/alarm/domain/pagination/assignee/alarm_assignee_query_ctrl.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; diff --git a/lib/modules/alarm/domain/pagination/assignee/alarm_assignee_query_ctrl.dart b/lib/modules/alarm/domain/pagination/assignee/alarm_assignee_query_ctrl.dart index b03406eb..c9e34332 100644 --- a/lib/modules/alarm/domain/pagination/assignee/alarm_assignee_query_ctrl.dart +++ b/lib/modules/alarm/domain/pagination/assignee/alarm_assignee_query_ctrl.dart @@ -1,5 +1,7 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/core/entity/entities_base.dart'; -import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart' hide Direction; +import 'package:thingsboard_ce_client/src/model/page/sort_order.dart'; class AlarmAssigneeQueryCtrl extends PageKeyController { AlarmAssigneeQueryCtrl({ diff --git a/lib/modules/alarm/domain/pagination/assignee/assignee_query_ctrl.dart b/lib/modules/alarm/domain/pagination/assignee/assignee_query_ctrl.dart index ff8d2435..f80c3171 100644 --- a/lib/modules/alarm/domain/pagination/assignee/assignee_query_ctrl.dart +++ b/lib/modules/alarm/domain/pagination/assignee/assignee_query_ctrl.dart @@ -1,5 +1,6 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; -import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart' hide Direction; +import 'package:thingsboard_ce_client/src/model/page/sort_order.dart'; class AssigneeQueryCtrl extends PageKeyController { AssigneeQueryCtrl({ diff --git a/lib/modules/alarm/domain/repository/alarm_types/i_alarm_types_repository.dart b/lib/modules/alarm/domain/repository/alarm_types/i_alarm_types_repository.dart index abca99b7..fa4d1623 100644 --- a/lib/modules/alarm/domain/repository/alarm_types/i_alarm_types_repository.dart +++ b/lib/modules/alarm/domain/repository/alarm_types/i_alarm_types_repository.dart @@ -1,5 +1,5 @@ import 'package:thingsboard_app/thingsboard_client.dart'; abstract interface class IAlarmTypesRepository { - Future> fetchAlarmTypes(PageLink pageKey); + Future> fetchAlarmTypes(PageLink pageKey); } diff --git a/lib/modules/alarm/domain/repository/alarms/i_alarms_repository.dart b/lib/modules/alarm/domain/repository/alarms/i_alarms_repository.dart index 85906596..158e7418 100644 --- a/lib/modules/alarm/domain/repository/alarms/i_alarms_repository.dart +++ b/lib/modules/alarm/domain/repository/alarms/i_alarms_repository.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; abstract interface class IAlarmsRepository { diff --git a/lib/modules/alarm/domain/repository/assignee/i_assigne_repository.dart b/lib/modules/alarm/domain/repository/assignee/i_assigne_repository.dart index 7f9f7f8a..b733b88a 100644 --- a/lib/modules/alarm/domain/repository/assignee/i_assigne_repository.dart +++ b/lib/modules/alarm/domain/repository/assignee/i_assigne_repository.dart @@ -1,5 +1,5 @@ import 'package:thingsboard_app/thingsboard_client.dart'; abstract interface class IAssigneeRepository { - Future> fetchAssignee(PageLink pageKey); + Future> fetchAssignee(PageLink pageKey); } diff --git a/lib/modules/alarm/domain/repository/details/i_alarm_details_repository.dart b/lib/modules/alarm/domain/repository/details/i_alarm_details_repository.dart index b339fb2e..e7add000 100644 --- a/lib/modules/alarm/domain/repository/details/i_alarm_details_repository.dart +++ b/lib/modules/alarm/domain/repository/details/i_alarm_details_repository.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; abstract interface class IAlarmDetailsRepository { @@ -22,7 +23,7 @@ abstract interface class IAlarmDetailsRepository { Future deleteComment(AlarmId id, {required String commentId}); - Future> fetchAssignee(UsersAssignQuery query); + Future> fetchAssignee(UsersAssignQuery query); Future assignAlarm(String alarmId, String assigneeId); diff --git a/lib/modules/alarm/domain/usecases/alarm_types/fetch_alarm_types_usecase.dart b/lib/modules/alarm/domain/usecases/alarm_types/fetch_alarm_types_usecase.dart index 87f104c6..06c973aa 100644 --- a/lib/modules/alarm/domain/usecases/alarm_types/fetch_alarm_types_usecase.dart +++ b/lib/modules/alarm/domain/usecases/alarm_types/fetch_alarm_types_usecase.dart @@ -3,13 +3,13 @@ import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/usecase.dart'; class FetchAlarmTypesUseCase - extends UseCase>, PageLink> { + extends UseCase>, PageLink> { const FetchAlarmTypesUseCase({required this.repository}); final IAlarmTypesRepository repository; @override - Future> call(PageLink params) { + Future> call(PageLink params) { return repository.fetchAlarmTypes(params); } } diff --git a/lib/modules/alarm/domain/usecases/alarms/fetch_alarms_usecase.dart b/lib/modules/alarm/domain/usecases/alarms/fetch_alarms_usecase.dart index 2065c93f..099dfe2a 100644 --- a/lib/modules/alarm/domain/usecases/alarms/fetch_alarms_usecase.dart +++ b/lib/modules/alarm/domain/usecases/alarms/fetch_alarms_usecase.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/modules/alarm/domain/repository/alarms/i_alarms_repository.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/usecase.dart'; diff --git a/lib/modules/alarm/domain/usecases/assignee/fetch_alarm_assignee_usecase.dart b/lib/modules/alarm/domain/usecases/assignee/fetch_alarm_assignee_usecase.dart index 1ccd4a77..bb77b712 100644 --- a/lib/modules/alarm/domain/usecases/assignee/fetch_alarm_assignee_usecase.dart +++ b/lib/modules/alarm/domain/usecases/assignee/fetch_alarm_assignee_usecase.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/core/usecases/user_details_usecase.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/modules/alarm/domain/entities/assignee_entity.dart'; diff --git a/lib/modules/alarm/domain/usecases/details/fetch_alarm_comments_usecase.dart b/lib/modules/alarm/domain/usecases/details/fetch_alarm_comments_usecase.dart index 89cc7de2..2d3f335f 100644 --- a/lib/modules/alarm/domain/usecases/details/fetch_alarm_comments_usecase.dart +++ b/lib/modules/alarm/domain/usecases/details/fetch_alarm_comments_usecase.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:thingsboard_app/modules/alarm/domain/repository/details/i_alarm_details_repository.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/usecase.dart'; diff --git a/lib/modules/alarm/presentation/bloc/activity/alarm_activity_bloc.dart b/lib/modules/alarm/presentation/bloc/activity/alarm_activity_bloc.dart index e987cf1f..46f2647f 100644 --- a/lib/modules/alarm/presentation/bloc/activity/alarm_activity_bloc.dart +++ b/lib/modules/alarm/presentation/bloc/activity/alarm_activity_bloc.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/lib/modules/alarm/presentation/bloc/activity/alarm_activity_events.dart b/lib/modules/alarm/presentation/bloc/activity/alarm_activity_events.dart index 26dff7e8..abc23a9c 100644 --- a/lib/modules/alarm/presentation/bloc/activity/alarm_activity_events.dart +++ b/lib/modules/alarm/presentation/bloc/activity/alarm_activity_events.dart @@ -46,7 +46,7 @@ final class AlarmEditCommentEvent extends AlarmActivityEvent { final AlarmId alarmId; final String commentId; - final AlarmComment comment; + final AlarmCommentInfo comment; @override List get props => [alarmId, commentId, comment]; diff --git a/lib/modules/alarm/presentation/bloc/activity/alarm_activity_states.dart b/lib/modules/alarm/presentation/bloc/activity/alarm_activity_states.dart index e19590e4..a21ce18d 100644 --- a/lib/modules/alarm/presentation/bloc/activity/alarm_activity_states.dart +++ b/lib/modules/alarm/presentation/bloc/activity/alarm_activity_states.dart @@ -36,7 +36,7 @@ final class AlarmCommentEditState extends AlarmActivityState { final AlarmId alarmId; final String commentId; - final AlarmComment commentToEdit; + final AlarmCommentInfo commentToEdit; @override List get props => [commentId, alarmId, commentToEdit]; diff --git a/lib/modules/alarm/presentation/bloc/alarm_assignee/alarm_assignee_bloc.dart b/lib/modules/alarm/presentation/bloc/alarm_assignee/alarm_assignee_bloc.dart index 2507af23..40532a6a 100644 --- a/lib/modules/alarm/presentation/bloc/alarm_assignee/alarm_assignee_bloc.dart +++ b/lib/modules/alarm/presentation/bloc/alarm_assignee/alarm_assignee_bloc.dart @@ -54,7 +54,9 @@ class AlarmAssigneeBloc extends Bloc { ); final assignee = paginationRepository.pagingController.itemList - ?.firstWhere((assignee) => assignee.userInfo.id.id == event.userId); + ?.firstWhere( + (assignee) => assignee.userInfo.id?.id == event.userId, + ); emit(AlarmAssigneeSelectedState(assignee!)); getIt().fire( @@ -81,11 +83,13 @@ class AlarmAssigneeBloc extends Bloc { case AlarmFetchAssigneeEvent(): final alarmInfo = await fetchAlarmUseCase(id); if (alarmInfo?.assignee != null && alarmInfo?.assignee?.id != null) { - final userInfo = UserInfo( - alarmInfo!.assignee!.id!, - email: alarmInfo.assignee!.email, - firstName: alarmInfo.assignee!.firstName, - lastName: alarmInfo.assignee!.lastName, + final userInfo = UserEmailInfo( + (b) => + b + ..id = alarmInfo!.assignee!.id?.toBuilder() + ..email = alarmInfo.assignee!.email ?? '' + ..firstName = alarmInfo.assignee!.firstName + ..lastName = alarmInfo.assignee!.lastName, ); final assignee = AssigneeEntity.fromUserInfo( diff --git a/lib/modules/alarm/presentation/bloc/alarm_types/alarm_types_bloc.dart b/lib/modules/alarm/presentation/bloc/alarm_types/alarm_types_bloc.dart index 9e6d35e2..0044615c 100644 --- a/lib/modules/alarm/presentation/bloc/alarm_types/alarm_types_bloc.dart +++ b/lib/modules/alarm/presentation/bloc/alarm_types/alarm_types_bloc.dart @@ -14,7 +14,7 @@ class AlarmTypesBloc extends Bloc { on(_onEvent); } - final PaginationRepository paginationRepository; + final PaginationRepository paginationRepository; final FetchAlarmTypesUseCase fetchAlarmTypesUseCase; final IAlarmFiltersService filtersService; diff --git a/lib/modules/alarm/presentation/bloc/alarms_bloc.dart b/lib/modules/alarm/presentation/bloc/alarms_bloc.dart index d76bb785..c8242e0f 100644 --- a/lib/modules/alarm/presentation/bloc/alarms_bloc.dart +++ b/lib/modules/alarm/presentation/bloc/alarms_bloc.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:thingsboard_app/modules/alarm/domain/entities/alarm_filters_entity.dart'; import 'package:thingsboard_app/modules/alarm/domain/pagination/alarms/alarms_query_ctrl.dart'; diff --git a/lib/modules/alarm/presentation/bloc/assignee/assignee_bloc.dart b/lib/modules/alarm/presentation/bloc/assignee/assignee_bloc.dart index 1d2e2705..577b22c8 100644 --- a/lib/modules/alarm/presentation/bloc/assignee/assignee_bloc.dart +++ b/lib/modules/alarm/presentation/bloc/assignee/assignee_bloc.dart @@ -29,7 +29,7 @@ class AssigneeBloc extends Bloc { queryCtrl.onSearchText(null); final assignee = paginationRepository.pagingController.itemList - ?.firstWhere((assignee) => assignee.userInfo.id.id == event.userId); + ?.firstWhere((assignee) => assignee.userInfo.id?.id == event.userId); if (assignee != null) { if (event.selfAssignment) { diff --git a/lib/modules/alarm/presentation/view/alarm_details_page.dart b/lib/modules/alarm/presentation/view/alarm_details_page.dart index 7b30ec1c..4ec035f5 100644 --- a/lib/modules/alarm/presentation/view/alarm_details_page.dart +++ b/lib/modules/alarm/presentation/view/alarm_details_page.dart @@ -126,7 +126,10 @@ class _AlarmDetailsPageState extends State { @override void initState() { - AlarmDetailsDi.init(getIt().client, id: AlarmId(widget.id)); + AlarmDetailsDi.init( + getIt().client, + id: AlarmId((b) => b..id = widget.id..entityType = EntityType.ALARM), + ); super.initState(); } diff --git a/lib/modules/alarm/presentation/widgets/activity/alarm_activity_widget.dart b/lib/modules/alarm/presentation/widgets/activity/alarm_activity_widget.dart index 17285a18..0d1ad9fa 100644 --- a/lib/modules/alarm/presentation/widgets/activity/alarm_activity_widget.dart +++ b/lib/modules/alarm/presentation/widgets/activity/alarm_activity_widget.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; diff --git a/lib/modules/alarm/presentation/widgets/activity/alarm_edit_comment_textfield.dart b/lib/modules/alarm/presentation/widgets/activity/alarm_edit_comment_textfield.dart index 21860093..74ad437f 100644 --- a/lib/modules/alarm/presentation/widgets/activity/alarm_edit_comment_textfield.dart +++ b/lib/modules/alarm/presentation/widgets/activity/alarm_edit_comment_textfield.dart @@ -5,6 +5,7 @@ import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/modules/alarm/presentation/bloc/activity/alarm_activity_bloc.dart'; import 'package:thingsboard_app/modules/alarm/presentation/bloc/activity/alarm_activity_events.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client_extensions.dart'; class AlarmEditCommentTextField extends StatefulWidget { const AlarmEditCommentTextField( @@ -16,7 +17,7 @@ class AlarmEditCommentTextField extends StatefulWidget { final AlarmId alarmId; final String commentId; - final AlarmComment commentToEdit; + final AlarmCommentInfo commentToEdit; @override State createState() => _AlarmEditCommentState(); @@ -78,7 +79,7 @@ class _AlarmEditCommentState extends State { @override void initState() { - textController.text = (widget.commentToEdit.comment as AlarmCommentJsonNode).text; + textController.text = widget.commentToEdit.commentNode?.text ?? ''; super.initState(); } diff --git a/lib/modules/alarm/presentation/widgets/activity/system_activity_widget.dart b/lib/modules/alarm/presentation/widgets/activity/system_activity_widget.dart index 62a6c4c5..63e40bdd 100644 --- a/lib/modules/alarm/presentation/widgets/activity/system_activity_widget.dart +++ b/lib/modules/alarm/presentation/widgets/activity/system_activity_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:thingsboard_app/config/themes/tb_text_styles.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client_extensions.dart'; import 'package:timeago/timeago.dart' as timeago; class SystemActivityWidget extends StatelessWidget { @@ -24,7 +25,7 @@ class SystemActivityWidget extends StatelessWidget { ), ), Text( - (activity.comment as AlarmCommentJsonNode).text, + activity.commentNode?.text ?? '', style: TbTextStyles.bodyLarge.copyWith( color: Colors.black.withValues(alpha: .54), ), diff --git a/lib/modules/alarm/presentation/widgets/activity/user_comment_widget.dart b/lib/modules/alarm/presentation/widgets/activity/user_comment_widget.dart index 5ae8adec..b547f2b9 100644 --- a/lib/modules/alarm/presentation/widgets/activity/user_comment_widget.dart +++ b/lib/modules/alarm/presentation/widgets/activity/user_comment_widget.dart @@ -10,6 +10,7 @@ import 'package:thingsboard_app/modules/alarm/presentation/bloc/activity/alarm_a import 'package:thingsboard_app/modules/alarm/presentation/bloc/activity/alarm_activity_events.dart'; import 'package:thingsboard_app/modules/alarm/presentation/widgets/assignee/user_info_avatar_widget.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client_extensions.dart'; import 'package:thingsboard_app/utils/ui/tb_alert_dialog.dart'; import 'package:thingsboard_app/utils/ui/ui_utils.dart'; import 'package:timeago/timeago.dart' as timeago; @@ -59,14 +60,14 @@ class _UserCommentState extends State { color: Colors.black.withValues(alpha: .54), ), onPressed: () { - if(widget.activity.id != null) { + if (widget.activity.id != null) { context.read().add( - AlarmEditCommentEvent( - widget.activity.id! , - alarmId: widget.activity.alarmId, - comment: widget.activity, - ), - ); + AlarmEditCommentEvent( + widget.activity.id!.id, + alarmId: widget.activity.alarmId!, + comment: widget.activity, + ), + ); } }, ), @@ -117,11 +118,13 @@ class _UserCommentState extends State { }, ); - if (delete == true && context.mounted && widget.activity.id != null) { + if (delete == true && + context.mounted && + widget.activity.id != null) { context.read().add( DeleteAlarmCommentEvent( - alarmId: widget.activity.alarmId, - commentId: widget.activity.id!, + alarmId: widget.activity.alarmId!, + commentId: widget.activity.id!.id, ), ); } @@ -134,6 +137,7 @@ class _UserCommentState extends State { } Widget _buildComment(UserDetailsOutput userInfo, Duration diff) { + final commentNode = widget.activity.commentNode; return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -163,7 +167,7 @@ class _UserCommentState extends State { ), const SizedBox(width: 4), Visibility( - visible: (widget.activity.comment as AlarmCommentJsonNode).edited, + visible: commentNode?.edited ?? false, child: Text( ' ${S.of(context).edited}', style: TbTextStyles.bodyMedium.copyWith( @@ -174,7 +178,7 @@ class _UserCommentState extends State { ], ), Text( - (widget.activity.comment as AlarmCommentJsonNode).text, + commentNode?.text ?? '', style: TbTextStyles.bodyLarge.copyWith( color: Colors.black.withValues(alpha: .54), ), diff --git a/lib/modules/alarm/presentation/widgets/alarm_assignee/alarm_assignee_list_widget.dart b/lib/modules/alarm/presentation/widgets/alarm_assignee/alarm_assignee_list_widget.dart index 891038f6..519bc83a 100644 --- a/lib/modules/alarm/presentation/widgets/alarm_assignee/alarm_assignee_list_widget.dart +++ b/lib/modules/alarm/presentation/widgets/alarm_assignee/alarm_assignee_list_widget.dart @@ -1,3 +1,4 @@ +import 'package:thingsboard_app/modules/alarm/domain/pagination/alarm_query_keys.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; @@ -131,8 +132,8 @@ class _AssigneeListWidgetState extends State { Widget? userInfoWidget; if (state is AlarmAssigneeSelectedState) { - final selectedId = state.assignee.userInfo.id.id; - if (selectedId == item.userInfo.id.id) { + final selectedId = state.assignee.userInfo.id?.id; + if (selectedId == item.userInfo.id?.id) { userInfoWidget = const SizedBox.shrink(); } } @@ -179,7 +180,7 @@ class _AssigneeListWidgetState extends State { ).toColor(), ), name: item.displayName, - email: item.userInfo.email, + email: item.userInfo.email ?? '', showEmail: !item.displayName.isValidEmail(), searchText: textEditingController.text, onUserTap: (id) { @@ -188,7 +189,7 @@ class _AssigneeListWidgetState extends State { AlarmAssigneeSelectedEvent(id), ); }, - id: item.userInfo.id.id!, + id: item.userInfo.id?.id ?? '', ), ], ); diff --git a/lib/modules/alarm/presentation/widgets/alarm_assignee/alarm_assignee_widget.dart b/lib/modules/alarm/presentation/widgets/alarm_assignee/alarm_assignee_widget.dart index 387b121c..0e40cc3b 100644 --- a/lib/modules/alarm/presentation/widgets/alarm_assignee/alarm_assignee_widget.dart +++ b/lib/modules/alarm/presentation/widgets/alarm_assignee/alarm_assignee_widget.dart @@ -83,7 +83,7 @@ class AlarmAssigneeWidget extends StatelessWidget { children: [ Expanded( child: UserInfoWidget( - id: state.assignee.userInfo.id.id ?? '', + id: state.assignee.userInfo.id?.id ?? '', avatar: UserInfoAvatarWidget( shortName: state.assignee.shortName, color: UiUtils.colorFromString( diff --git a/lib/modules/alarm/presentation/widgets/alarm_types/types_list_widget.dart b/lib/modules/alarm/presentation/widgets/alarm_types/types_list_widget.dart index a7d6fdec..e4e58ee3 100644 --- a/lib/modules/alarm/presentation/widgets/alarm_types/types_list_widget.dart +++ b/lib/modules/alarm/presentation/widgets/alarm_types/types_list_widget.dart @@ -49,7 +49,7 @@ class TypesListWidget extends StatelessWidget { ), ), Flexible( - child: PagedListView.separated( + child: PagedListView.separated( pagingController: getIt() .paginationRepository @@ -66,7 +66,7 @@ class TypesListWidget extends StatelessWidget { onTap: () { Navigator.of(context).pop(); getIt().add( - AlarmTypesSelectedEvent(type: item.type), + AlarmTypesSelectedEvent(type: item.type ?? ''), ); onChanged(); }, @@ -74,7 +74,7 @@ class TypesListWidget extends StatelessWidget { children: [ Flexible( child: Text( - item.type, + item.type ?? '', style: const TextStyle(fontSize: 16, height: 1.5), ), ), diff --git a/lib/modules/alarm/presentation/widgets/assignee/alarm_assignee_widget.dart b/lib/modules/alarm/presentation/widgets/assignee/alarm_assignee_widget.dart index 27c5234b..385f48a7 100644 --- a/lib/modules/alarm/presentation/widgets/assignee/alarm_assignee_widget.dart +++ b/lib/modules/alarm/presentation/widgets/assignee/alarm_assignee_widget.dart @@ -68,7 +68,7 @@ class AlarmAssigneeFilterWidget extends StatelessWidget { ); case AssigneeSelectedState(): return UserInfoWidget( - id: state.assignee.userInfo.id.id ?? '', + id: state.assignee.userInfo.id?.id ?? '', avatar: UserInfoAvatarWidget( shortName: state.assignee.shortName, color: diff --git a/lib/modules/alarm/presentation/widgets/assignee/assignee_list_widget.dart b/lib/modules/alarm/presentation/widgets/assignee/assignee_list_widget.dart index a990023c..af537ab9 100644 --- a/lib/modules/alarm/presentation/widgets/assignee/assignee_list_widget.dart +++ b/lib/modules/alarm/presentation/widgets/assignee/assignee_list_widget.dart @@ -122,8 +122,8 @@ class AssigneeListWidget extends StatelessWidget { Widget? userInfoWidget; if (state is AssigneeSelectedState) { - final selectedId = state.assignee.userInfo.id.id; - if (selectedId == item.userInfo.id.id) { + final selectedId = state.assignee.userInfo.id?.id; + if (selectedId == item.userInfo.id?.id) { userInfoWidget = const SizedBox.shrink(); } } @@ -176,7 +176,7 @@ class AssigneeListWidget extends StatelessWidget { ).toColor(), ), name: item.displayName, - email: item.userInfo.email, + email: item.userInfo.email ?? '', showEmail: !item.displayName.isValidEmail(), onUserTap: (id) { Navigator.of(context).pop(); @@ -186,7 +186,7 @@ class AssigneeListWidget extends StatelessWidget { onChanged(); }, - id: item.userInfo.id.id!, + id: item.userInfo.id?.id ?? '', ), ], ); @@ -205,13 +205,13 @@ class AssigneeListWidget extends StatelessWidget { final state = getIt().state; if (state is AssigneeSelectedState) { - final selectedId = state.assignee.userInfo.id.id; + final selectedId = state.assignee.userInfo.id?.id; final userId = getIt() .paginationRepository .pagingController .itemList?[index]; - if (selectedId == userId?.userInfo.id.id) { + if (selectedId == userId?.userInfo.id?.id) { return const SizedBox.shrink(); } } diff --git a/lib/modules/asset/asset_details_page.dart b/lib/modules/asset/asset_details_page.dart index 2c3b4bcb..9dedcf6f 100644 --- a/lib/modules/asset/asset_details_page.dart +++ b/lib/modules/asset/asset_details_page.dart @@ -15,8 +15,11 @@ class AssetDetailsPage extends EntityDetailsPage { ); final tbClient = getIt().client; @override - Future fetchEntity(String id) { - return tbClient.getAssetService().getAssetInfo(id); + Future fetchEntity(String id) async { + final r = await tbClient.getAssetControllerApi().getAssetInfoById( + assetId: id, + ); + return r.data; } @override @@ -27,10 +30,10 @@ class AssetDetailsPage extends EntityDetailsPage { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(S.of(context).assetName, style: labelTextStyle), - Text(entity.name, style: valueTextStyle), + Text(entity.name ?? '', style: valueTextStyle), const SizedBox(height: 16), Text(S.of(context).type, style: labelTextStyle), - Text(entity.type, style: valueTextStyle), + Text(entity.type ?? '', style: valueTextStyle), const SizedBox(height: 16), Text(S.of(context).label, style: labelTextStyle), Text(entity.label ?? '', style: valueTextStyle), diff --git a/lib/modules/asset/assets_base.dart b/lib/modules/asset/assets_base.dart index 178f8e0b..b8ae7b11 100644 --- a/lib/modules/asset/assets_base.dart +++ b/lib/modules/asset/assets_base.dart @@ -5,6 +5,7 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/new_client_page_data.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; mixin AssetsBase on EntitiesBase { @@ -19,21 +20,31 @@ mixin AssetsBase on EntitiesBase { Future> fetchEntities( PageLink pageLink, { bool refresh = false, - }) { + }) async { if (tbClient.isTenantAdmin()) { - return tbClient.getAssetService().getTenantAssetInfos(pageLink); + final r = await tbClient.getAssetControllerApi().getTenantAssetInfos( + pageSize: pageLink.pageSize, + page: pageLink.page, + textSearch: pageLink.textSearch, + ); + final p = r.data!; + return toPageData(p.data, p.totalPages, p.totalElements, p.hasNext); } else { - return tbClient.getAssetService().getCustomerAssetInfos( - tbClient.getAuthUser()!.customerId!, - pageLink, + final r = await tbClient.getAssetControllerApi().getCustomerAssetInfos( + customerId: tbClient.getAuthUser()!.customerId!, + pageSize: pageLink.pageSize, + page: pageLink.page, + textSearch: pageLink.textSearch, ); + final p = r.data!; + return toPageData(p.data, p.totalPages, p.totalElements, p.hasNext); } } @override void onEntityTap(AssetInfo asset, WidgetRef ref) { if (asset.id?.id != null) { - getIt().navigateTo('/assets/asset/${asset.id!.id}'); + getIt().navigateTo('/asset/${asset.id!.id}'); } } @@ -49,7 +60,7 @@ mixin AssetsBase on EntitiesBase { @override Widget buildEntityGridCard(BuildContext context, AssetInfo asset) { - return Text(asset.name); + return Text(asset.name ?? ''); } Widget _buildCard(BuildContext context, AssetInfo asset) { @@ -72,7 +83,7 @@ mixin AssetsBase on EntitiesBase { children: [ Flexible( child: Text( - asset.name, + asset.name ?? '', maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( @@ -116,7 +127,7 @@ mixin AssetsBase on EntitiesBase { ), const SizedBox(height: 4), Text( - asset.type, + asset.type ?? '', style: const TextStyle( color: Color(0xFFAFAFAF), fontSize: 12, @@ -180,7 +191,7 @@ mixin AssetsBase on EntitiesBase { ), ), Text( - asset.type, + asset.type ?? '', style: const TextStyle( color: Color(0xFFAFAFAF), fontSize: 12, diff --git a/lib/modules/audit_log/audit_log_details_page.dart b/lib/modules/audit_log/audit_log_details_page.dart index d568fe0c..b201eaa9 100644 --- a/lib/modules/audit_log/audit_log_details_page.dart +++ b/lib/modules/audit_log/audit_log_details_page.dart @@ -66,22 +66,29 @@ class _AuditLogDetailsPageState extends State { children: [ Text(S.of(context).entityType, style: labelTextStyle), Text( - widget.auditLog.entityId.entityType.getTranslatedEntityType( - context, - ), + widget.auditLog.entityId?.entityType.getTranslatedEntityType( + context, + ) ?? + '', style: valueTextStyle, ), const SizedBox(height: 16), Text(S.of(context).type, style: labelTextStyle), Text( - widget.auditLog.actionType.getTranslatedActionType(context), + widget.auditLog.actionType?.getTranslatedActionType(context) ?? + '', style: valueTextStyle, ), const SizedBox(height: 16), Flexible( child: buildBorderedText( S.of(context).actionData, - encoder.convert(widget.auditLog.actionData), + // `actionData` is a built_value [JsonObject] (MapJsonObject at + // runtime); JsonEncoder can't serialize the wrapper, so unwrap + // it to the underlying value first. + widget.auditLog.actionData != null + ? encoder.convert(widget.auditLog.actionData!.value) + : '', ), ), if (widget.auditLog.actionStatus == ActionStatus.FAILURE) diff --git a/lib/modules/audit_log/audit_logs_base.dart b/lib/modules/audit_log/audit_logs_base.dart index 2d8832d3..78b4aca0 100644 --- a/lib/modules/audit_log/audit_logs_base.dart +++ b/lib/modules/audit_log/audit_logs_base.dart @@ -9,6 +9,7 @@ import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/modules/audit_log/audit_log_details_page.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/new_client_page_data.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; import 'package:thingsboard_app/utils/translation_utils.dart'; @@ -24,8 +25,16 @@ mixin AuditLogsBase on EntitiesBase { Future> fetchEntities( TimePageLink pageLink, { bool refresh = false, - }) { - return tbClient.getAuditLogService().getAuditLogs(pageLink); + }) async { + final r = await tbClient.getAuditLogControllerApi().getAuditLogs( + pageSize: pageLink.pageSize, + page: pageLink.page, + textSearch: pageLink.textSearch, + startTime: pageLink.startTime, + endTime: pageLink.endTime, + ); + final p = r.data!; + return toPageData(p.data, p.totalPages, p.totalElements, p.hasNext); } @override @@ -136,8 +145,9 @@ class _AuditLogCardState extends State { Flexible( fit: FlexFit.tight, child: Text( - widget.auditLog.entityId.entityType - .getTranslatedEntityType(context), + widget.auditLog.entityId?.entityType + .getTranslatedEntityType(context) ?? + '', style: const TextStyle( color: Color(0xFFAFAFAF), fontWeight: FontWeight.normal, @@ -148,7 +158,10 @@ class _AuditLogCardState extends State { ), Text( widget.auditLog.actionStatus - .getTranslatedActionStatus(context), + ?.getTranslatedActionStatus( + context, + ) ?? + '', style: TextStyle( color: widget.auditLog.actionStatus == @@ -176,9 +189,10 @@ class _AuditLogCardState extends State { Flexible( fit: FlexFit.tight, child: Text( - widget.auditLog.actionType.getTranslatedActionType( - context, - ), + widget.auditLog.actionType?.getTranslatedActionType( + context, + ) ?? + '', style: const TextStyle( color: Color(0xFF282828), fontWeight: FontWeight.normal, diff --git a/lib/modules/customer/customer_details_page.dart b/lib/modules/customer/customer_details_page.dart index 19937300..3331225f 100644 --- a/lib/modules/customer/customer_details_page.dart +++ b/lib/modules/customer/customer_details_page.dart @@ -12,7 +12,10 @@ class CustomerDetailsPage extends ContactBasedDetailsPage { ); final tbClient = getIt().client; @override - Future fetchEntity(String id) { - return tbClient.getCustomerService().getCustomer(id); + Future fetchEntity(String id) async { + final r = await tbClient.getCustomerControllerApi().getCustomerById( + customerId: id, + ); + return r.data; } } diff --git a/lib/modules/customer/customers_base.dart b/lib/modules/customer/customers_base.dart index 4df339b6..661bf3f0 100644 --- a/lib/modules/customer/customers_base.dart +++ b/lib/modules/customer/customers_base.dart @@ -6,6 +6,7 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/new_client_page_data.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; mixin CustomersBase on EntitiesBase { @@ -20,8 +21,14 @@ mixin CustomersBase on EntitiesBase { Future> fetchEntities( PageLink pageLink, { bool refresh = false, - }) { - return tbClient.getCustomerService().getCustomers(pageLink); + }) async { + final r = await tbClient.getCustomerControllerApi().getCustomers( + pageSize: pageLink.pageSize, + page: pageLink.page, + textSearch: pageLink.textSearch, + ); + final p = r.data!; + return toPageData(p.data, p.totalPages, p.totalElements, p.hasNext); } @override diff --git a/lib/modules/dashboard/domain/usecases/fetch_dashboards_usecase.dart b/lib/modules/dashboard/domain/usecases/fetch_dashboards_usecase.dart index cc522fae..5683df1b 100644 --- a/lib/modules/dashboard/domain/usecases/fetch_dashboards_usecase.dart +++ b/lib/modules/dashboard/domain/usecases/fetch_dashboards_usecase.dart @@ -1,4 +1,5 @@ import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/new_client_page_data.dart'; import 'package:thingsboard_app/utils/usecase.dart'; class FetchDashboardsUseCase @@ -8,18 +9,39 @@ class FetchDashboardsUseCase final ThingsboardClient tbClient; @override - Future> call(PageLink params) { - + Future> call(PageLink params) async { if (tbClient.isTenantAdmin()) { - return tbClient.getDashboardService().getTenantDashboards( - params, - mobile: true, + final response = await tbClient + .getDashboardControllerApi() + .getTenantDashboards( + pageSize: params.pageSize, + page: params.page, + textSearch: params.textSearch, + mobile: true, + ); + final page = response.data!; + return toPageData( + page.data, + page.totalPages, + page.totalElements, + page.hasNext, ); } else { - return tbClient.getDashboardService().getCustomerDashboards( - tbClient.getAuthUser()!.customerId!, - params, - mobile: true, + final response = await tbClient + .getDashboardControllerApi() + .getCustomerDashboards( + customerId: tbClient.getAuthUser()!.customerId!, + pageSize: params.pageSize, + page: params.page, + textSearch: params.textSearch, + mobile: true, + ); + final page = response.data!; + return toPageData( + page.data, + page.totalPages, + page.totalElements, + page.hasNext, ); } } diff --git a/lib/modules/dashboard/presentation/view/home_dashboard_page.dart b/lib/modules/dashboard/presentation/view/home_dashboard_page.dart index 69d2ea31..1ba9fde7 100644 --- a/lib/modules/dashboard/presentation/view/home_dashboard_page.dart +++ b/lib/modules/dashboard/presentation/view/home_dashboard_page.dart @@ -35,7 +35,7 @@ class _HomeDashboardState extends State { DashboardController? _dashboardController; ValueNotifier canGoback = ValueNotifier(false); late final home = - '${getIt().getCachedEndpoint()}/dashboards/${widget.dashboard.dashboardId!}${widget.dashboard.hideDashboardToolbar ? '?hideToolbar=true' : ''}'; + '${getIt().getCachedEndpoint()}/dashboards/${widget.dashboard.dashboardId!}${(widget.dashboard.hideDashboardToolbar ?? false) ? '?hideToolbar=true' : ''}'; @override Widget build(BuildContext context) { diff --git a/lib/modules/dashboard/presentation/widgets/dashboard_widget.dart b/lib/modules/dashboard/presentation/widgets/dashboard_widget.dart index d3f58a7c..0687a509 100644 --- a/lib/modules/dashboard/presentation/widgets/dashboard_widget.dart +++ b/lib/modules/dashboard/presentation/widgets/dashboard_widget.dart @@ -14,7 +14,7 @@ import 'package:thingsboard_app/utils/services/endpoint/i_endpoint_service.dart' import 'package:thingsboard_app/utils/services/mobile_actions/widget_action_handler.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; import 'package:thingsboard_app/widgets/tb_progress_indicator.dart'; -import 'package:thingsboard_client/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:universal_platform/universal_platform.dart'; import 'package:url_launcher/url_launcher_string.dart'; diff --git a/lib/modules/device/device_details_page.dart b/lib/modules/device/device_details_page.dart index 8da11b40..8a1c5728 100644 --- a/lib/modules/device/device_details_page.dart +++ b/lib/modules/device/device_details_page.dart @@ -9,12 +9,18 @@ class DeviceDetailsPage extends EntityDetailsPage { : super(entityId: deviceId, defaultTitle: 'Device'); final tbClient = getIt().client; @override - Future fetchEntity(String id) { - return tbClient.getDeviceService().getDeviceInfo(id); + Future fetchEntity(String id) async { + final r = await tbClient.getDeviceControllerApi().getDeviceInfoById( + deviceId: id, + ); + return r.data; } @override Widget buildEntityDetails(BuildContext context, DeviceInfo entity) { - return ListTile(title: Text(entity.name), subtitle: Text(entity.type)); + return ListTile( + title: Text(entity.name ?? ''), + subtitle: Text(entity.type ?? ''), + ); } } diff --git a/lib/modules/device/device_profiles_base.dart b/lib/modules/device/device_profiles_base.dart index f2d6ed97..2888514c 100644 --- a/lib/modules/device/device_profiles_base.dart +++ b/lib/modules/device/device_profiles_base.dart @@ -41,7 +41,7 @@ mixin DeviceProfilesBase on EntitiesBase { @override void onEntityTap(DeviceProfileInfo deviceProfile, WidgetRef ref) { getIt().navigateTo( - '/devices/deviceList?deviceType=${Uri.encodeComponent(deviceProfile.name)}', + '/devices/deviceList?deviceType=${Uri.encodeComponent(deviceProfile.name ?? '')}', ); } @@ -313,7 +313,7 @@ class _DeviceProfileCardState extends State { void _countDevices() { countedProfile = DeviceProfileCache.getDevicesCount( tbClient, - widget.deviceProfile.name, + widget.deviceProfile.name ?? '' ?? '', ); } @@ -351,7 +351,7 @@ class _DeviceProfileCardState extends State { padding: const EdgeInsets.symmetric(horizontal: 6), child: Center( child: AutoSizeText( - entity.name, + entity.name ?? '', textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, @@ -400,7 +400,7 @@ class _DeviceProfileCardState extends State { onTap: () { getIt().navigateTo( // translate-me-ignore-next-line - '/devices/deviceList?active=true&deviceType=${Uri.encodeComponent(entity.name)}', + '/devices/deviceList?active=true&deviceType=${Uri.encodeComponent(entity.name ?? '')}', ); }, ), @@ -440,7 +440,7 @@ class _DeviceProfileCardState extends State { onTap: () { getIt().navigateTo( // translate-me-ignore-next-line - '/devices/deviceList?active=false&deviceType=${Uri.encodeComponent(entity.name)}', + '/devices/deviceList?active=false&deviceType=${Uri.encodeComponent(entity.name ?? '')}', ); }, ), diff --git a/lib/modules/device/devices_base.dart b/lib/modules/device/devices_base.dart index ff8d8d56..15835591 100644 --- a/lib/modules/device/devices_base.dart +++ b/lib/modules/device/devices_base.dart @@ -13,9 +13,11 @@ import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/modules/dashboard/domain/entites/dashboard_arguments.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client_extensions.dart'; import 'package:thingsboard_app/utils/services/device_profile/device_profile_cache.dart'; import 'package:thingsboard_app/utils/services/device_profile/model/cached_device_profile.dart'; import 'package:thingsboard_app/utils/services/entity_query_api.dart'; +import 'package:thingsboard_app/utils/services/new_client_page_data.dart'; import 'package:thingsboard_app/utils/services/overlay_service/i_overlay_service.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; import 'package:thingsboard_app/utils/utils.dart'; @@ -26,15 +28,18 @@ mixin DevicesBase on EntitiesBase { String title(BuildContext context) => S.of(context).devices(2); @override - String noItemsFoundText(BuildContext context) => - S.of(context).noDevicesFound; + String noItemsFoundText(BuildContext context) => S.of(context).noDevicesFound; final tbClient = getIt().client; @override Future> fetchEntities( EntityDataQuery dataQuery, { bool refresh = false, - }) { - return tbClient.getEntityQueryService().findEntityDataByQuery(dataQuery); + }) async { + final response = await tbClient + .getEntityQueryControllerApi() + .findEntityDataByQuery(entityDataQuery: dataQuery); + final pd = response.data!; + return toPageData(pd.data, pd.totalPages, pd.totalElements, pd.hasNext); } @override @@ -42,10 +47,10 @@ mixin DevicesBase on EntitiesBase { final profile = await DeviceProfileCache.getDeviceProfileInfo( tbClient, device.field('type')!, - device.entityId.id!, + device.entityId?.id ?? '', ); if (profile.info.defaultDashboardId != null) { - final dashboardId = profile.info.defaultDashboardId!.id!; + final dashboardId = profile.info.defaultDashboardId?.id ?? ''; final state = Utils.createDashboardEntityState( device.entityId, entityName: device.field('name'), @@ -117,11 +122,24 @@ class DeviceQueryController extends PageKeyController { ); @override - EntityDataQuery nextPageKey(EntityDataQuery pageKey) => pageKey.next(); + EntityDataQuery nextPageKey(EntityDataQuery pageKey) { + final currentPage = pageKey.pageLink?.page ?? 0; + return pageKey.rebuild( + (b) => b.pageLink.update((pl) => pl..page = currentPage + 1), + ); + } void onSearchText(String searchText) { - value.pageKey.pageLink.page = 0; - value.pageKey.pageLink.textSearch = searchText; + value = PageKeyValue( + value.pageKey.rebuild( + (b) => b.pageLink.update( + (pl) => + pl + ..page = 0 + ..textSearch = searchText, + ), + ), + ); notifyListeners(); } } @@ -153,7 +171,7 @@ class _DeviceCardState extends State { deviceProfileFuture = DeviceProfileCache.getDeviceProfileInfo( tbClient, widget.device.field('type')!, - widget.device.entityId.id!, + widget.device.entityId?.id ?? '', ); } } @@ -168,7 +186,7 @@ class _DeviceCardState extends State { deviceProfileFuture = DeviceProfileCache.getDeviceProfileInfo( tbClient, widget.device.field('type')!, - widget.device.entityId.id!, + widget.device.entityId?.id ?? '', ); } } @@ -292,7 +310,13 @@ class _DeviceCardState extends State { Text( entityDateFormat.format( DateTime.fromMillisecondsSinceEpoch( - widget.device.createdTime!, + int.tryParse( + widget.device.field( + 'createdTime', + ) ?? + '0', + ) ?? + 0, ), ), style: const TextStyle( @@ -304,7 +328,10 @@ class _DeviceCardState extends State { ), ], ), - if (widget.device.field('label')?.isNotEmpty == true) + if (widget.device + .field('label') + ?.isNotEmpty == + true) Padding( padding: const EdgeInsets.only(top: 2), child: Text( diff --git a/lib/modules/device/provisioning/bloc/device_provisioning_bloc.dart b/lib/modules/device/provisioning/bloc/device_provisioning_bloc.dart index 4f2c27dc..8da1c16f 100644 --- a/lib/modules/device/provisioning/bloc/device_provisioning_bloc.dart +++ b/lib/modules/device/provisioning/bloc/device_provisioning_bloc.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:plugin_wifi_connect/plugin_wifi_connect.dart'; @@ -107,12 +108,12 @@ class DeviceProvisioningBloc try { final response = await tbClient - .getDeviceService() + .getDeviceControllerApi() .claimDevice( - deviceName, - ClaimRequest(secretKey: deviceSecretKey), - // ignore errors is for handling errors by myself - requestConfig: RequestConfig(ignoreErrors: true), + deviceName: deviceName, + claimRequest: ClaimRequest( + (b) => b..secretKey = deviceSecretKey, + ), ) .timeout( const Duration(seconds: 20), @@ -120,20 +121,20 @@ class DeviceProvisioningBloc () => throw Exception('Device claiming timeout reached'), ); - if (response.response == ClaimResponse.CLAIMED || - response.response == ClaimResponse.SUCCESS) { - communicationService.fire( - const DeviceProvisioningStatusChangedEvent( - DeviceProvisioningStatus.done, - ), - ); - } else { - emit( - const DeviceProvisioningClaimingErrorState( - 'Something went wrong. Please try again.', - ), - ); + // A 2xx no longer guarantees success: the endpoint can return a + // ClaimResult body whose `response` is FAILURE. Only treat an + // explicit non-success result as a failure; an empty/unparseable + // body (e.g. the CLAIMED case) is still considered success. + if (_isClaimFailure(response.data)) { + emit(const DeviceProvisioningClaimingErrorState(null)); + return; } + + communicationService.fire( + const DeviceProvisioningStatusChangedEvent( + DeviceProvisioningStatus.done, + ), + ); } catch (e) { logger.error('Device claiming error: $e'); @@ -159,6 +160,25 @@ class DeviceProvisioningBloc } } + /// Returns true only when the claim response body explicitly reports a + /// non-success result. Tolerant of empty/non-JSON bodies so legitimate + /// claims are never reported as failures. + bool _isClaimFailure(String? body) { + if (body == null || body.isEmpty) { + return false; + } + try { + final decoded = jsonDecode(body); + if (decoded is Map && decoded['response'] is String) { + final result = (decoded['response'] as String).toUpperCase(); + return result != 'CLAIMED' && result != 'SUCCESS'; + } + } catch (_) { + // Not a JSON ClaimResult body; assume success. + } + return false; + } + @override Future close() { subscription.cancel(); diff --git a/lib/modules/main/providers/navigation_helper.dart b/lib/modules/main/providers/navigation_helper.dart index cfc16eea..2514ad45 100644 --- a/lib/modules/main/providers/navigation_helper.dart +++ b/lib/modules/main/providers/navigation_helper.dart @@ -4,6 +4,7 @@ import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/modules/main/model/navigation_item_data.dart'; import 'package:thingsboard_app/modules/notification/widgets/notification_icon.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/layouts/pages_layout.dart'; class NavigationHelper { static String getLocalizedTitle( diff --git a/lib/modules/main/providers/navigation_provider.dart b/lib/modules/main/providers/navigation_provider.dart index 5bfde19f..f8acb7db 100644 --- a/lib/modules/main/providers/navigation_provider.dart +++ b/lib/modules/main/providers/navigation_provider.dart @@ -13,6 +13,7 @@ import 'package:thingsboard_app/modules/main/model/navigation_item_data.dart'; import 'package:thingsboard_app/modules/main/model/navigation_state.dart'; import 'package:thingsboard_app/modules/main/providers/navigation_helper.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/layouts/pages_layout.dart'; part 'navigation_provider.g.dart'; diff --git a/lib/modules/more/profle_widget.dart b/lib/modules/more/profle_widget.dart index 2e2d84d1..b5bc8e1e 100644 --- a/lib/modules/more/profle_widget.dart +++ b/lib/modules/more/profle_widget.dart @@ -86,7 +86,10 @@ class ProfileWidget extends StatelessWidget { String getAuthorityName(BuildContext context, User? user) { var name = ''; if (user != null) { - final authority = user.authority; + // `user.authority` is the generated built_value enum, while the `Authority` + // constants below come from the hand-written enum the barrel re-exports. + // Bridge by name so the switch actually matches (see login_provider). + final authority = authorityFromString(user.authority.name); switch (authority) { case Authority.SYS_ADMIN: name = S.of(context).systemAdministrator; diff --git a/lib/modules/notification/controllers/notification_query_ctrl.dart b/lib/modules/notification/controllers/notification_query_ctrl.dart index 208f7f4e..23329d6e 100644 --- a/lib/modules/notification/controllers/notification_query_ctrl.dart +++ b/lib/modules/notification/controllers/notification_query_ctrl.dart @@ -1,5 +1,6 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; -import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart' hide Direction; +import 'package:thingsboard_ce_client/src/model/page/sort_order.dart'; class NotificationQueryCtrl extends PageKeyController { NotificationQueryCtrl({int pageSize = 20, String? searchText}) diff --git a/lib/modules/notification/repository/i_notification_query_repository.dart b/lib/modules/notification/repository/i_notification_query_repository.dart index 3a23320d..2d6e7cbd 100644 --- a/lib/modules/notification/repository/i_notification_query_repository.dart +++ b/lib/modules/notification/repository/i_notification_query_repository.dart @@ -1,9 +1,9 @@ abstract interface class INotificationQueryRepository { - Future markAllAsRead(); + Future markAllAsRead(); - Future markNotificationAsRead(String id); + Future markNotificationAsRead(String id); - Future deleteNotification(String id); + Future deleteNotification(String id); Future searchNotification(String searchText); diff --git a/lib/modules/notification/repository/notification_pagination_repository.dart b/lib/modules/notification/repository/notification_pagination_repository.dart index a37a56eb..f2d60378 100644 --- a/lib/modules/notification/repository/notification_pagination_repository.dart +++ b/lib/modules/notification/repository/notification_pagination_repository.dart @@ -5,7 +5,7 @@ import 'package:thingsboard_app/modules/notification/controllers/notification_qu import 'package:thingsboard_app/thingsboard_client.dart'; class NotificationPaginationRepository { - NotificationPaginationRepository( { + NotificationPaginationRepository({ required this.notificationQueryPageCtrl, required this.tbClient, }); @@ -37,11 +37,23 @@ class NotificationPaginationRepository { bool refresh = false, }) async { try { - final pageData = await tbClient.getNotificationService().getNotifications( - pageKey, - ); + final response = await tbClient + .getNotificationControllerApi() + .getNotifications( + pageSize: pageKey.pageLink.pageSize, + page: pageKey.pageLink.page, + textSearch: pageKey.pageLink.textSearch, + unreadOnly: pageKey.unreadOnly, + deliveryMethod: pageKey.deliveryMethod, + ); - final isLastPage = !pageData.hasNext; + final page = response.data!; + final items = (page.data?.toList() ?? []) + .map((n) => _toPushNotification(n)) + .whereType() + .toList(); + + final isLastPage = !(page.hasNext ?? false); if (refresh) { final state = pagingController.value; if (state.itemList != null) { @@ -49,16 +61,36 @@ class NotificationPaginationRepository { } } if (isLastPage) { - pagingController.appendLastPage(pageData.data); + pagingController.appendLastPage(items); } else { final nextPageKey = notificationQueryPageCtrl.nextPageKey(pageKey); - pagingController.appendPage(pageData.data, nextPageKey); + pagingController.appendPage(items, nextPageKey); } } catch (error) { pagingController.error = error; } } + PushNotification? _toPushNotification(Notification n) { + try { + final json = { + 'id': {'id': n.id?.id, 'entityType': 'NOTIFICATION'}, + 'createdTime': n.createdTime, + 'requestId': {'id': n.requestId?.id ?? '', 'entityType': 'NOTIFICATION_REQUEST'}, + 'recipientId': {'id': n.recipientId?.id ?? '', 'entityType': 'USER'}, + 'subject': n.subject ?? '', + 'text': n.text ?? '', + 'type': n.type?.name ?? 'GENERAL', + 'status': n.status?.name ?? 'UNREAD', + if (n.additionalConfig != null && n.additionalConfig!.isMap) + 'additionalConfig': n.additionalConfig!.asMap, + }; + return PushNotification.fromJson(json); + } catch (_) { + return null; + } + } + void _didChangePageKeyValue() { _refreshPagingController(); } diff --git a/lib/modules/notification/repository/notification_repository.dart b/lib/modules/notification/repository/notification_repository.dart index e8dc0193..e8db8995 100644 --- a/lib/modules/notification/repository/notification_repository.dart +++ b/lib/modules/notification/repository/notification_repository.dart @@ -15,25 +15,25 @@ class NotificationRepository implements INotificationQueryRepository { final INotificationsLocalService localService; @override - Future deleteNotification(String id) { - return thingsboardClient.getNotificationService().deleteNotification(id); + Future deleteNotification(String id) { + return thingsboardClient + .getNotificationControllerApi() + .deleteNotification(id: id); } @override - Future markAllAsRead() async { - final response = await thingsboardClient - .getNotificationService() - .markAllNotificationsAsRead('MOBILE_APP'); + Future markAllAsRead() async { + await thingsboardClient + .getNotificationControllerApi() + .markAllNotificationsAsRead(deliveryMethod: 'MOBILE_APP'); localService.clearNotificationBadgeCount(); - - return response; } @override - Future markNotificationAsRead(String id) { - return thingsboardClient.getNotificationService().markNotificationAsRead( - id, - ); + Future markNotificationAsRead(String id) { + return thingsboardClient + .getNotificationControllerApi() + .markNotificationAsRead(id: id); } @override diff --git a/lib/modules/notification/widgets/notification_slidable_widget.dart b/lib/modules/notification/widgets/notification_slidable_widget.dart index 4ca53f18..19fe7d8d 100644 --- a/lib/modules/notification/widgets/notification_slidable_widget.dart +++ b/lib/modules/notification/widgets/notification_slidable_widget.dart @@ -169,7 +169,7 @@ class _NotificationSlidableWidget extends State { loading = true; }); try { - await widget.thingsboardClient.getAlarmService().ackAlarm(alarmId); + await widget.thingsboardClient.getAlarmControllerApi().ackAlarm(alarmId: alarmId); } catch (_) {} setState(() { @@ -196,7 +196,7 @@ class _NotificationSlidableWidget extends State { }); try { - await widget.thingsboardClient.getAlarmService().clearAlarm(alarmId); + await widget.thingsboardClient.getAlarmControllerApi().clearAlarm(alarmId: alarmId); } catch (_) {} setState(() { diff --git a/lib/modules/notification/widgets/notification_widget.dart b/lib/modules/notification/widgets/notification_widget.dart index b9e0d183..2bfaa517 100644 --- a/lib/modules/notification/widgets/notification_widget.dart +++ b/lib/modules/notification/widgets/notification_widget.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:thingsboard_app/locator.dart'; -import 'package:thingsboard_app/modules/alarm/alarms_base.dart'; + import 'package:thingsboard_app/modules/notification/usecase/handle_notification_tap_params.dart'; import 'package:thingsboard_app/modules/notification/usecase/handle_notification_tap_usecase.dart'; import 'package:thingsboard_app/modules/notification/widgets/notification_icon.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; -import 'package:thingsboard_app/utils/translation_utils.dart'; + import 'package:timeago/timeago.dart' as timeago; class NotificationWidget extends StatelessWidget { @@ -37,7 +37,6 @@ class NotificationWidget extends StatelessWidget { DateTime.fromMillisecondsSinceEpoch(notification.createdTime!), ); - final severity = notification.info?.alarmSeverity; return InkWell( onTap: isSelectionMode @@ -51,14 +50,7 @@ class NotificationWidget extends StatelessWidget { child: Container( padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), decoration: BoxDecoration( - border: - notification.info?.alarmSeverity != null - ? Border.all( - color: - notification.info?.alarmSeverity?.toColor() ?? - Colors.transparent, - ) - : null, + border: null, borderRadius: BorderRadius.circular(5), ), child: Column( @@ -147,23 +139,6 @@ class NotificationWidget extends StatelessWidget { ), ], ), - Visibility( - visible: severity != null, - child: Container( - decoration: BoxDecoration( - color: severity?.toColor().withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(5), - ), - padding: const EdgeInsets.all(5), - child: Text( - severity?.getTranslatedAlarmSeverity(context) ?? '', - style: TextStyle( - color: AlarmSeverity.CRITICAL.toColor(), - fontWeight: FontWeight.w600, - ), - ), - ), - ), ], ), ], diff --git a/lib/modules/profile/change_password_page.dart b/lib/modules/profile/change_password_page.dart index dc19911e..c89ba4f3 100644 --- a/lib/modules/profile/change_password_page.dart +++ b/lib/modules/profile/change_password_page.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; +import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/services/overlay_service/i_overlay_service.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; import 'package:thingsboard_app/widgets/tb_app_bar.dart'; @@ -202,10 +203,25 @@ class _ChangePasswordPageState extends State { _isLoadingNotifier.value = true; try { await Future.delayed(const Duration(milliseconds: 300)); - await getIt().client.changePassword( - currentPassword, - newPassword, + final client = getIt().client; + final response = await client.getAuthControllerApi().changePassword( + changePasswordRequest: ChangePasswordRequest( + (b) => + b + ..currentPassword = currentPassword + ..newPassword = newPassword, + ), ); + // The server rotates the JWT pair on a password change; apply it so + // the stored refresh token stays valid and the session isn't dropped. + final jwtPair = response.data; + if (jwtPair?.token != null) { + await client.setUserFromJwtToken( + jwtPair!.token, + jwtPair.refreshToken, + false, + ); + } if (mounted) { context.pop(true); } diff --git a/lib/modules/profile/widget/profile_edit_page.dart b/lib/modules/profile/widget/profile_edit_page.dart index 1335a284..5d5c9bd0 100644 --- a/lib/modules/profile/widget/profile_edit_page.dart +++ b/lib/modules/profile/widget/profile_edit_page.dart @@ -1,3 +1,4 @@ +import 'package:built_value/json_object.dart'; import 'package:country_picker/country_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -52,14 +53,15 @@ class ProfileEditPage extends HookConsumerWidget { validators: [], value: bool.tryParse( - (user.additionalInfo?['homeDashboardHideToolbar']).toString(), + (user.additionalInfo?.asMap?['homeDashboardHideToolbar']) + .toString(), ) ?? true, ), "unitSystem": FormControl( validators: [Validators.required], value: UnitSystems.fromString( - user.additionalInfo?['unitSystem']?.toString(), + user.additionalInfo?.asMap?['unitSystem']?.toString(), ), ), "lang": FormControl( @@ -83,7 +85,8 @@ class ProfileEditPage extends HookConsumerWidget { }), ); useEffect(() { - final String? id = user.additionalInfo?['homeDashboardId']?.toString(); + final String? id = + user.additionalInfo?.asMap?['homeDashboardId']?.toString(); getHomeDashboardInfo(id, loading, form); return null; }, []); @@ -442,9 +445,10 @@ class ProfileEditPage extends HookConsumerWidget { if (id != null) { try { loading.value = true; - final info = await getIt().client - .getDashboardService() - .getDashboardInfo(id); + final res = await getIt().client + .getDashboardControllerApi() + .getDashboardInfoById(dashboardId: id); + final info = res.data!; form .control('additionalInfo.homeDashboardId') .patchValue(info, updateParent: false, emitEvent: true); @@ -457,7 +461,10 @@ class ProfileEditPage extends HookConsumerWidget { } } - Future onDiscardPressed(BuildContext context, Locale initialLocale) async { + Future onDiscardPressed( + BuildContext context, + Locale initialLocale, + ) async { final original = S.of(context).discardChanges; String titleString = original; if (original.isNotEmpty) { @@ -516,29 +523,39 @@ Future _saveProfile( if (form.invalid) { return; } - final newUser = user; - newUser.email = form.control('email').value.toString(); - newUser.firstName = form.control('firstName').value?.toString(); - newUser.lastName = form.control('lastName').value?.toString(); - newUser.phone = (form.control('phone').value as PhoneNumber?)?.international; - - newUser.additionalInfo ??= {}; final additionalInfoForm = form.control('additionalInfo') as FormGroup; - newUser.additionalInfo!['homeDashboardHideToolbar'] = - additionalInfoForm.control('homeDashboardHideToolbar').value; - newUser.additionalInfo!['lang'] = - (additionalInfoForm.control('lang').value as Locale).toString(); - newUser.additionalInfo!['unitSystem'] = - (additionalInfoForm.control('unitSystem').value as UnitSystems).name - .toUpperCase(); - newUser.additionalInfo!['homeDashboardId'] = - (additionalInfoForm.control('homeDashboardId').value as DashboardInfo?) - ?.id - ?.id; + final existingAdditionalInfo = + user.additionalInfo?.asMap ?? {}; + final updatedAdditionalInfo = + Map.from(existingAdditionalInfo) + ..['homeDashboardHideToolbar'] = + additionalInfoForm.control('homeDashboardHideToolbar').value + ..['lang'] = + (additionalInfoForm.control('lang').value as Locale).toString() + ..['unitSystem'] = + (additionalInfoForm.control('unitSystem').value as UnitSystems).name + .toUpperCase() + ..['homeDashboardId'] = + (additionalInfoForm.control('homeDashboardId').value + as DashboardInfo?) + ?.id + ?.id; + + final newUser = user.rebuild( + (b) => + b + ..email = form.control('email').value.toString() + ..firstName = form.control('firstName').value?.toString() + ..lastName = form.control('lastName').value?.toString() + ..phone = (form.control('phone').value as PhoneNumber?)?.international + ..additionalInfo = JsonObject(updatedAdditionalInfo), + ); isLoading.value = true; final overlayService = getIt(); try { - await getIt().client.getUserService().saveUser(newUser); + await getIt().client.getUserControllerApi().saveUser( + user: newUser, + ); await ref.read(loginProvider.notifier).loadUser(); isLoading.value = false; canPop.value = true; diff --git a/lib/modules/profile/widget/profile_preview_widget.dart b/lib/modules/profile/widget/profile_preview_widget.dart index 2627bfd4..6c970015 100644 --- a/lib/modules/profile/widget/profile_preview_widget.dart +++ b/lib/modules/profile/widget/profile_preview_widget.dart @@ -176,10 +176,8 @@ class ProfilePreviewWidget extends HookConsumerWidget { } String getUnitName(BuildContext context) => UnitSystems.fromString( - user.additionalInfo?['unitSystem']?.toString(), + user.additionalInfo?.asMap?['unitSystem']?.toString(), ).getLocalizedName(context); - - } List getAllLanguages(BuildContext context) { @@ -197,7 +195,7 @@ String getLocalizedLanguageName(Locale locale, BuildContext context) { Locale getCurrentLocale(BuildContext context, User user) { final locale = S.delegate.supportedLocales.firstWhereOrNull( - (l) => l.toString() == user.additionalInfo?['lang']?.toString(), + (l) => l.toString() == user.additionalInfo?.asMap?['lang']?.toString(), ); return locale ?? Localizations.localeOf(context); diff --git a/lib/modules/profile/widget/user_delete/user_delete_button_ce.dart b/lib/modules/profile/widget/user_delete/user_delete_button_ce.dart index ecf0ca8a..9bd55578 100644 --- a/lib/modules/profile/widget/user_delete/user_delete_button_ce.dart +++ b/lib/modules/profile/widget/user_delete/user_delete_button_ce.dart @@ -9,6 +9,7 @@ import 'package:thingsboard_app/modules/more/profle_widget.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/services/overlay_service/i_overlay_service.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; + /// Example of user deletion feature for CE, it's not needed since there is no self registration /// on CE app Widget getDeleteButton(BuildContext context, WidgetRef ref, User user) { @@ -60,13 +61,22 @@ Future deleteAccount( if (delete == true) { final client = getIt().client; try { + // `user.authority` is the generated built_value enum, while the `Authority` + // constants below come from the hand-written enum the barrel re-exports. + // Bridge by name so the comparisons actually match (see profle_widget). + final authority = authorityFromString(user.authority.name); + /// More strict way. Deletes overall tenant account. - if (user.authority == Authority.TENANT_ADMIN) { - client.getTenantService().deleteTenant(user.tenantId!.id!); + if (authority == Authority.TENANT_ADMIN) { + await client.getTenantControllerApi().deleteTenant( + tenantId: user.tenantId?.id ?? '', + ); } -// You can use this for tenant user's deletion as well, if you want to keep your tenant - if (user.authority == Authority.CUSTOMER_USER) { - client.getUserService().deleteUser(user.id!.id!); + // You can use this for tenant user's deletion as well, if you want to keep your tenant + if (authority == Authority.CUSTOMER_USER) { + await client.getUserControllerApi().deleteUser( + userId: user.id?.id ?? '', + ); } await ref.read(loginProvider.notifier).logout(); } catch (e) { diff --git a/lib/modules/tenant/tenant_details_page.dart b/lib/modules/tenant/tenant_details_page.dart index a29b0151..440bbb3c 100644 --- a/lib/modules/tenant/tenant_details_page.dart +++ b/lib/modules/tenant/tenant_details_page.dart @@ -12,7 +12,10 @@ class TenantDetailsPage extends ContactBasedDetailsPage { ); final tbClient = getIt().client; @override - Future fetchEntity(String id) { - return tbClient.getTenantService().getTenant(id); + Future fetchEntity(String id) async { + final r = await tbClient.getTenantControllerApi().getTenantById( + tenantId: id, + ); + return r.data; } } diff --git a/lib/modules/tenant/tenants_base.dart b/lib/modules/tenant/tenants_base.dart index d8f65b1d..fab78021 100644 --- a/lib/modules/tenant/tenants_base.dart +++ b/lib/modules/tenant/tenants_base.dart @@ -5,6 +5,7 @@ import 'package:thingsboard_app/core/entity/entities_base.dart'; import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/new_client_page_data.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; mixin TenantsBase on EntitiesBase { @@ -12,19 +13,26 @@ mixin TenantsBase on EntitiesBase { String title(BuildContext context) => S.of(context).tenants; @override - String noItemsFoundText(BuildContext context) => - S.of(context).noTenantsFound; + String noItemsFoundText(BuildContext context) => S.of(context).noTenantsFound; final tbClient = getIt().client; @override Future> fetchEntities( PageLink pageLink, { bool refresh = false, - }) { - return tbClient.getTenantService().getTenants(pageLink); + }) async { + final r = await tbClient.getTenantControllerApi().getTenants( + pageSize: pageLink.pageSize, + page: pageLink.page, + textSearch: pageLink.textSearch, + ); + final p = r.data!; + return toPageData(p.data, p.totalPages, p.totalElements, p.hasNext); } @override void onEntityTap(Tenant tenant, WidgetRef ref) { - getIt().navigateTo('/tenants/tenant/${tenant.id!.id}'); + getIt().navigateTo( + '/tenants/tenant/${tenant.id!.id}', + ); } } diff --git a/lib/modules/version/route/version_route_arguments.dart b/lib/modules/version/route/version_route_arguments.dart index 3f7d1124..ff74f221 100644 --- a/lib/modules/version/route/version_route_arguments.dart +++ b/lib/modules/version/route/version_route_arguments.dart @@ -1,5 +1,5 @@ -import 'package:thingsboard_app/thingsboard_client.dart' - show StoreInfo, VersionInfo; +import 'package:thingsboard_app/utils/services/version_service/version_info.dart'; +import 'package:thingsboard_ce_client/src/model/store_info.dart'; class VersionRouteArguments { const VersionRouteArguments({ diff --git a/lib/modules/version/version_redirect.dart b/lib/modules/version/version_redirect.dart index e34ce454..cc50ef3c 100644 --- a/lib/modules/version/version_redirect.dart +++ b/lib/modules/version/version_redirect.dart @@ -7,6 +7,7 @@ import 'package:thingsboard_app/config/routes/v2/routes_config/routes/ui_utils_r import 'package:thingsboard_app/core/auth/login/provider/oauth_provider.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/utils/services/version_service/i_version_service.dart'; +import 'package:thingsboard_app/utils/services/version_service/version_info.dart'; class VersionRedirect implements Redirect { @override @@ -20,8 +21,9 @@ class VersionRedirect implements Redirect { return null; } final oauth = ref.read(oauthProvider); - final versionInfo = oauth.value?.versionInfo; - if (versionInfo != null) { + final rawVersionInfo = oauth.value?.versionInfo; + if (rawVersionInfo != null) { + final versionInfo = VersionInfo.fromMobileAppVersionInfo(rawVersionInfo); if (getIt().appUpdateRequired(versionInfo)) { return LoginRoutes.login + LoginRoutes.updateRequired; } diff --git a/lib/modules/version/view/update_required_page.dart b/lib/modules/version/view/update_required_page.dart index 92c537f7..acb5f589 100644 --- a/lib/modules/version/view/update_required_page.dart +++ b/lib/modules/version/view/update_required_page.dart @@ -8,6 +8,7 @@ import 'package:thingsboard_app/modules/version/view/widgets/version_compare_wid import 'package:thingsboard_app/modules/version/view/widgets/version_empty_widget.dart'; import 'package:thingsboard_app/modules/version/view/widgets/version_single_widget.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/version_service/version_info.dart'; class UpdateRequiredPage extends ConsumerWidget { const UpdateRequiredPage({super.key}); @@ -26,7 +27,7 @@ class UpdateRequiredPage extends ConsumerWidget { VersionInfoFetchEvent( VersionRouteArguments( storeInfo: proivders.value.storeInfo, - versionInfo: proivders.value.versionInfo!, + versionInfo: VersionInfo.fromMobileAppVersionInfo(proivders.value.versionInfo!), ), ), ), diff --git a/lib/thingsboard_client.dart b/lib/thingsboard_client.dart index 36bf8a7f..e274d590 100644 --- a/lib/thingsboard_client.dart +++ b/lib/thingsboard_client.dart @@ -7,4 +7,19 @@ /// thus minimizing merge conflicts. library; -export 'package:thingsboard_client/thingsboard_client.dart'; +// Hide generated built_value types that conflict with handwritten counterparts. +export 'package:thingsboard_ce_client/thingsboard_ce_client.dart' + hide Authority, LoginRequest, LoginResponse, RefreshTokenRequest, + // Hide handwritten alarm query objects; app-local replacements live in + // lib/modules/alarm/domain/pagination/alarm_query_keys.dart. + AlarmQueryV2, AlarmCommentsQuery, UsersAssignQuery, + // Hide client-side Font models that shadow Flutter's built-in types. + FontWeight, FontStyle, Font, + // Hide TB NotificationSettings which shadows firebase_messaging's class. + NotificationSettings; + +// Use the handwritten auth models (plain Dart enum + positional-ish LoginRequest). +export 'package:thingsboard_ce_client/src/model/authority_enum.dart' + show Authority, authorityFromString; +export 'package:thingsboard_ce_client/src/model/login_models.dart' + show LoginRequest, LoginResponse, RefreshTokenRequest; diff --git a/lib/thingsboard_client_extensions.dart b/lib/thingsboard_client_extensions.dart new file mode 100644 index 00000000..29445f3a --- /dev/null +++ b/lib/thingsboard_client_extensions.dart @@ -0,0 +1,18 @@ +import 'package:thingsboard_ce_client/thingsboard_ce_client.dart'; + +/// Extension that restores typed access to an [AlarmCommentInfo]'s free-form +/// `comment` payload on the new built_value model. +/// +/// The new client types `comment` as a [JsonObject] (a `MapJsonObject` at +/// runtime), whereas the old client exposed it pre-parsed. This decodes the +/// underlying map back into the handwritten [AlarmCommentJsonNode] consumed by +/// the alarm activity widgets. +extension AlarmCommentInfoExt on AlarmCommentInfo { + /// `comment` is nullable on the new built_value model, and system-generated + /// activity entries may not carry a payload, so callers must handle null. + AlarmCommentJsonNode? get commentNode { + final raw = comment?.asMap; + if (raw == null) return null; + return AlarmCommentJsonNode.fromJson(raw.cast()); + } +} diff --git a/lib/utils/services/device_info/device_info_service.dart b/lib/utils/services/device_info/device_info_service.dart index 7ce08a96..3e797490 100644 --- a/lib/utils/services/device_info/device_info_service.dart +++ b/lib/utils/services/device_info/device_info_service.dart @@ -2,6 +2,7 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; import 'package:thingsboard_app/utils/services/device_info/i_device_info_service.dart'; +import 'package:thingsboard_app/utils/services/device_info/platform_version.dart'; import 'package:universal_platform/universal_platform.dart'; class DeviceInfoService implements IDeviceInfoService { diff --git a/lib/utils/services/device_info/i_device_info_service.dart b/lib/utils/services/device_info/i_device_info_service.dart index 43515a1c..01544ee2 100644 --- a/lib/utils/services/device_info/i_device_info_service.dart +++ b/lib/utils/services/device_info/i_device_info_service.dart @@ -1,5 +1,6 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/device_info/platform_version.dart'; abstract interface class IDeviceInfoService { String getApplicationId(); diff --git a/lib/utils/services/device_info/platform_version.dart b/lib/utils/services/device_info/platform_version.dart new file mode 100644 index 00000000..541a2b66 --- /dev/null +++ b/lib/utils/services/device_info/platform_version.dart @@ -0,0 +1,95 @@ +class PlatformVersionMatcher { + static const int minPlatformVersionInt = 3900; + + static bool isSupportedPlatformVersion( + PlatformVersion platformVersion, { + required String type, + }) { + if (type != 'PE' && type != 'PAAS') { + return false; + } + + try { + if (platformVersion.versionInt() < minPlatformVersionInt) { + return false; + } + } catch (e) { + return false; + } + return true; + } +} + +class PlatformVersion { + static RegExp versionRegExp = RegExp(r'([\d|.]+)([A-Z]*)(-SNAPSHOT)?'); + + int major; + int minor; + int patch; + int? minorPatch; + String? versionCode; + bool isSnapshot; + + PlatformVersion({ + required this.major, + required this.minor, + required this.patch, + this.minorPatch, + this.versionCode, + this.isSnapshot = false, + }); + + factory PlatformVersion.fromString(String version) { + final match = versionRegExp.firstMatch(version); + if (match != null) { + final versionStr = match.group(1); + if (versionStr != null) { + final versionParts = versionStr.split('.'); + if (versionParts.length >= 3) { + final major = int.parse(versionParts[0]); + final minor = int.parse(versionParts[1]); + final patch = int.parse(versionParts[2]); + int? minorPatch; + if (versionParts.length > 3) { + minorPatch = int.parse(versionParts[3]); + } + final versionCode = match.group(2); + final isSnapshot = match.group(3) != null; + return PlatformVersion( + major: major, + minor: minor, + patch: patch, + minorPatch: minorPatch, + versionCode: versionCode?.isEmpty == true ? null : versionCode, + isSnapshot: isSnapshot, + ); + } + } + } + throw ArgumentError('Invalid platform version string: $version'); + } + + int versionInt() { + return major * 1000 + + minor * 100 + + patch * 10 + + (minorPatch ?? 0); + } + + String versionString() { + var version = '$major.$minor.$patch'; + if (minorPatch != null) { + version += '.$minorPatch'; + } + if (versionCode != null && versionCode!.isNotEmpty) { + version += versionCode!; + } + if (isSnapshot) { + version += '-SNAPSHOT'; + } + return version; + } + + @override + String toString() => versionString(); +} diff --git a/lib/utils/services/device_profile/device_profile_cache.dart b/lib/utils/services/device_profile/device_profile_cache.dart index bf561d34..7b80d2af 100644 --- a/lib/utils/services/device_profile/device_profile_cache.dart +++ b/lib/utils/services/device_profile/device_profile_cache.dart @@ -12,14 +12,19 @@ abstract class DeviceProfileCache { ) async { final deviceProfile = _cache[name]; if (deviceProfile == null) { - final device = await tbClient.getDeviceService().getDevice(deviceId); - final info = await tbClient - .getDeviceProfileService() - .getDeviceProfileInfo(device!.deviceProfileId!.id!); + final deviceR = await tbClient.getDeviceControllerApi().getDeviceById( + deviceId: deviceId, + ); + final device = deviceR.data!; + final infoR = await tbClient + .getDeviceProfileControllerApi() + .getDeviceProfileInfoById( + deviceProfileId: device.deviceProfileId!.id!, + ); final cache = CachedDeviceProfileInfo( activeCount: null, inactiveCount: null, - info: info!, + info: infoR.data!, ); _cache[name] = cache; return cache; @@ -63,16 +68,27 @@ abstract class DeviceProfileCache { PageLink pageLink, { bool invalidateCache = false, }) async { - final deviceProfileInfos = await tbClient - .getDeviceProfileService() - .getDeviceProfileInfos(pageLink); + final r = await tbClient + .getDeviceProfileControllerApi() + .getDeviceProfileInfos( + pageSize: pageLink.pageSize, + page: pageLink.page, + textSearch: pageLink.textSearch, + ); + final p = r.data!; + final deviceProfileInfos = PageData( + p.data?.toList() ?? [], + p.totalPages ?? 0, + p.totalElements ?? 0, + p.hasNext ?? false, + ); if (invalidateCache) { _cache.clear(); } for (final deviceProfile in deviceProfileInfos.data) { - final existing = _cache[deviceProfile.name]; + final existing = _cache[deviceProfile.name ?? '']; if (existing == null) { - _cache[deviceProfile.name] = CachedDeviceProfileInfo( + _cache[deviceProfile.name ?? ''] = CachedDeviceProfileInfo( activeCount: null, inactiveCount: null, info: deviceProfile, diff --git a/lib/utils/services/entity_query_api.dart b/lib/utils/services/entity_query_api.dart index 98e19da2..6f4318a6 100644 --- a/lib/utils/services/entity_query_api.dart +++ b/lib/utils/services/entity_query_api.dart @@ -1,60 +1,128 @@ +import 'package:built_collection/built_collection.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; abstract class EntityQueryApi { static final activeDeviceKeyFilter = KeyFilter( - key: EntityKey(type: EntityKeyType.ATTRIBUTE, key: 'active'), - valueType: EntityKeyValueType.BOOLEAN, - predicate: BooleanFilterPredicate( - operation: BooleanOperation.EQUAL, - value: FilterPredicateValue(true), - ), + (b) => + b + ..key = + EntityKey( + (b) => + b + ..type = EntityKeyType.ATTRIBUTE + ..key = 'active', + ).toBuilder() + ..valueType = EntityKeyValueType.BOOLEAN + ..predicate = BooleanFilterPredicate( + (b) => + b + ..type = 'BOOLEAN' + ..operation = BooleanOperation.EQUAL + ..value = + FilterPredicateValueBoolean( + (b) => b..defaultValue = true, + ).toBuilder(), + ), ); static final inactiveDeviceKeyFilter = KeyFilter( - key: EntityKey(type: EntityKeyType.ATTRIBUTE, key: 'active'), - valueType: EntityKeyValueType.BOOLEAN, - predicate: BooleanFilterPredicate( - operation: BooleanOperation.EQUAL, - value: FilterPredicateValue(false), - ), + (b) => + b + ..key = + EntityKey( + (b) => + b + ..type = EntityKeyType.ATTRIBUTE + ..key = 'active', + ).toBuilder() + ..valueType = EntityKeyValueType.BOOLEAN + ..predicate = BooleanFilterPredicate( + (b) => + b + ..type = 'BOOLEAN' + ..operation = BooleanOperation.EQUAL + ..value = + FilterPredicateValueBoolean( + (b) => b..defaultValue = false, + ).toBuilder(), + ), ); - static final defaultDeviceFields = [ - EntityKey(type: EntityKeyType.ENTITY_FIELD, key: 'name'), - EntityKey(type: EntityKeyType.ENTITY_FIELD, key: 'type'), - EntityKey(type: EntityKeyType.ENTITY_FIELD, key: 'label'), - EntityKey(type: EntityKeyType.ENTITY_FIELD, key: 'createdTime'), - ]; + static final defaultDeviceFields = BuiltList([ + EntityKey( + (b) => + b + ..type = EntityKeyType.ENTITY_FIELD + ..key = 'name', + ), + EntityKey( + (b) => + b + ..type = EntityKeyType.ENTITY_FIELD + ..key = 'type', + ), + EntityKey( + (b) => + b + ..type = EntityKeyType.ENTITY_FIELD + ..key = 'label', + ), + EntityKey( + (b) => + b + ..type = EntityKeyType.ENTITY_FIELD + ..key = 'createdTime', + ), + ]); - static final defaultDeviceAttributes = [ - EntityKey(type: EntityKeyType.ATTRIBUTE, key: 'active'), - ]; + static final defaultDeviceAttributes = BuiltList([ + EntityKey( + (b) => + b + ..type = EntityKeyType.ATTRIBUTE + ..key = 'active', + ), + ]); static Future countDevices( ThingsboardClient tbClient, { String? deviceType, bool? active, - }) { - EntityFilter deviceFilter; - if (deviceType != null) { - deviceFilter = DeviceTypeFilter( - deviceTypes: [deviceType], - deviceNameFilter: '', - ); - } else { - deviceFilter = EntityTypeFilter(entityType: EntityType.DEVICE); - } - final EntityCountQuery deviceCountQuery = EntityCountQuery( - entityFilter: deviceFilter, - ); + }) async { + final EntityFilter deviceFilter = + deviceType != null + ? DeviceTypeFilter( + (b) => + b + ..type = 'deviceType' + ..deviceTypes = ListBuilder([deviceType]) + ..deviceNameFilter = '', + ) + : EntityTypeFilter( + (b) => + b + ..type = 'entityType' + ..entityType = EntityType.DEVICE, + ); + + BuiltList? keyFilters; if (active != null) { - deviceCountQuery.keyFilters = [ + keyFilters = BuiltList([ if (active) activeDeviceKeyFilter else inactiveDeviceKeyFilter, - ]; + ]); } - return tbClient.getEntityQueryService().countEntitiesByQuery( - deviceCountQuery, + + final deviceCountQuery = EntityCountQuery( + (b) => + b + ..entityFilter = deviceFilter + ..keyFilters = keyFilters?.toBuilder(), ); + + final response = await tbClient + .getEntityQueryControllerApi() + .countEntitiesByQuery(entityCountQuery: deviceCountQuery); + return response.data ?? 0; } static EntityDataQuery createDefaultDeviceQuery({ @@ -63,35 +131,64 @@ abstract class EntityQueryApi { String? deviceType, bool? active, }) { - EntityFilter deviceFilter; - List? keyFilters; - if (deviceType != null) { - deviceFilter = DeviceTypeFilter( - deviceTypes: [deviceType], - deviceType: deviceType, - deviceNameFilter: '', - ); - } else { - deviceFilter = EntityTypeFilter(entityType: EntityType.DEVICE); - } + final EntityFilter deviceFilter = + deviceType != null + ? DeviceTypeFilter( + (b) => + b + ..type = 'deviceType' + ..deviceTypes = ListBuilder([deviceType]) + ..deviceNameFilter = '', + ) + : EntityTypeFilter( + (b) => + b + ..type = 'entityType' + ..entityType = EntityType.DEVICE, + ); + + BuiltList? keyFilters; if (active != null) { - keyFilters = [ + keyFilters = BuiltList([ if (active) activeDeviceKeyFilter else inactiveDeviceKeyFilter, - ]; + ]); } + return EntityDataQuery( - entityFilter: deviceFilter, - keyFilters: keyFilters, - entityFields: defaultDeviceFields, - latestValues: defaultDeviceAttributes, - pageLink: EntityDataPageLink( - pageSize: pageSize, - textSearch: searchText, - sortOrder: EntityDataSortOrder( - key: EntityKey(type: EntityKeyType.ENTITY_FIELD, key: 'createdTime'), - direction: EntityDataSortOrderDirection.DESC, - ), - ), + (b) => + b + ..entityFilter = deviceFilter + ..keyFilters = keyFilters?.toBuilder() + ..entityFields = defaultDeviceFields.toBuilder() + ..latestValues = defaultDeviceAttributes.toBuilder() + ..pageLink = + EntityDataPageLink( + (b) => + b + ..pageSize = pageSize + ..textSearch = searchText + ..sortOrder = + EntityDataSortOrder( + (b) => + b + ..key = + EntityKey( + (b) => + b + ..type = + EntityKeyType.ENTITY_FIELD + ..key = 'createdTime', + ).toBuilder() + ..direction = Direction.DESC, + ).toBuilder(), + ).toBuilder(), ); } } + +/// Extension that restores the old `field()` / `attribute()` convenience +/// accessors on the built_value [EntityData] model. +extension EntityDataHelpers on EntityData { + String? field(String key) => latest?['ENTITY_FIELD']?[key]?.value; + String? attribute(String key) => latest?['ATTRIBUTE']?[key]?.value; +} diff --git a/lib/utils/services/layouts/i_layout_service.dart b/lib/utils/services/layouts/i_layout_service.dart index f06f2008..c5e5ad07 100644 --- a/lib/utils/services/layouts/i_layout_service.dart +++ b/lib/utils/services/layouts/i_layout_service.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/modules/main/model/main_navigation_item.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/layouts/pages_layout.dart'; abstract interface class ILayoutService { List getBottomBarItems(); diff --git a/lib/utils/services/layouts/layout_service.dart b/lib/utils/services/layouts/layout_service.dart index 44e01706..15109217 100644 --- a/lib/utils/services/layouts/layout_service.dart +++ b/lib/utils/services/layouts/layout_service.dart @@ -2,8 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:thingsboard_app/core/context/tb_context.dart'; import 'package:thingsboard_app/core/logger/tb_logger.dart'; import 'package:thingsboard_app/modules/main/model/main_navigation_item.dart'; -import 'package:thingsboard_app/thingsboard_client.dart' - show Authority, PageLayout, Pages; +import 'package:thingsboard_app/thingsboard_client.dart' show Authority; +import 'package:thingsboard_app/utils/services/layouts/pages_layout.dart'; import 'package:thingsboard_app/utils/services/layouts/i_layout_service.dart'; class LayoutService implements ILayoutService { diff --git a/lib/utils/services/layouts/pages_layout.dart b/lib/utils/services/layouts/pages_layout.dart new file mode 100644 index 00000000..e2395f64 --- /dev/null +++ b/lib/utils/services/layouts/pages_layout.dart @@ -0,0 +1,48 @@ +enum Pages { + home, + alarms, + devices, + customers, + assets, + audit_logs, + notifications, + device_list, + dashboards, + undefined, +} + +Pages pagesFromString(String? value) { + return Pages.values.firstWhere( + (e) => e.toString().split('.')[1].toUpperCase() == value?.toUpperCase(), + orElse: () => Pages.undefined, + ); +} + +class PageLayout { + const PageLayout({ + this.id, + this.label, + this.icon, + this.dashboardId, + this.path, + this.url, + }); + + final Pages? id; + final String? label; + final String? icon; + final String? dashboardId; + final String? path; + final String? url; + + factory PageLayout.fromJson(Map json) { + return PageLayout( + id: pagesFromString(json['id'] as String?), + label: json['label'] as String?, + icon: json['icon'] as String?, + dashboardId: json['dashboardId'] as String?, + path: json['path'] as String?, + url: json['url'] as String?, + ); + } +} diff --git a/lib/utils/services/new_client_page_data.dart b/lib/utils/services/new_client_page_data.dart new file mode 100644 index 00000000..5a5881a8 --- /dev/null +++ b/lib/utils/services/new_client_page_data.dart @@ -0,0 +1,18 @@ +import 'package:thingsboard_app/thingsboard_client.dart'; + +/// Converts the new autogenerated client's per-type `built_value` page-data objects +/// (`PageDataAlarmInfo`, `PageDataUserInfo`, …) into the generic `PageData` the +/// app's pagination framework consumes. Pass the `built_value` getters directly. +PageData toPageData( + Iterable? data, + int? totalPages, + int? totalElements, + bool? hasNext, +) { + return PageData( + data?.toList() ?? [], + totalPages ?? 0, + totalElements ?? 0, + hasNext ?? false, + ); +} diff --git a/lib/utils/services/notification_service.dart b/lib/utils/services/notification_service.dart index 597aa82f..1a5a2e0c 100644 --- a/lib/utils/services/notification_service.dart +++ b/lib/utils/services/notification_service.dart @@ -53,14 +53,15 @@ class NotificationService { _onTokenRefreshSubscription = FirebaseMessaging.instance.onTokenRefresh .listen((token) { if (_fcmToken != null) { - _tbClient.getUserService().removeMobileSession(_fcmToken!).then(( - _, - ) { - _fcmToken = token; - if (_fcmToken != null) { - _saveToken(_fcmToken!); - } - }); + _tbClient + .getUserControllerApi() + .removeMobileSession(xMobileToken: _fcmToken!) + .then((_) { + _fcmToken = token; + if (_fcmToken != null) { + _saveToken(_fcmToken!); + } + }); } }); @@ -97,7 +98,9 @@ class NotificationService { getIt().debug( 'NotificationService::logout() removeMobileSession', ); - _tbClient.getUserService().removeMobileSession(_fcmToken!, requestConfig: RequestConfig(ignoreErrors: true)); + _tbClient.getUserControllerApi().removeMobileSession( + xMobileToken: _fcmToken!, + ); } await _foregroundMessageSubscription?.cancel(); @@ -170,7 +173,7 @@ class NotificationService { Future _resetToken(String? token) async { if (token != null) { - _tbClient.getUserService().removeMobileSession(token); + _tbClient.getUserControllerApi().removeMobileSession(xMobileToken: token); } await _messaging.deleteToken(); @@ -182,13 +185,14 @@ class NotificationService { _log.debug('FCM token: $fcmToken'); if (fcmToken != null) { - final MobileSessionInfo? mobileInfo = await _tbClient - .getUserService() - .getMobileSession(fcmToken); + final mobileInfo = + (await _tbClient.getUserControllerApi().getMobileSession( + xMobileToken: fcmToken, + )).data; if (mobileInfo != null) { final int timeAfterCreatedToken = DateTime.now().millisecondsSinceEpoch - - mobileInfo.fcmTokenTimestamp; + (mobileInfo.fcmTokenTimestamp ?? 0); if (timeAfterCreatedToken > const Duration(days: 30).inMilliseconds) { fcmToken = await _resetToken(fcmToken); if (fcmToken != null) { @@ -202,9 +206,11 @@ class NotificationService { } Future _saveToken(String token) async { - await _tbClient.getUserService().saveMobileSession( - token, - MobileSessionInfo(DateTime.now().millisecondsSinceEpoch), + await _tbClient.getUserControllerApi().saveMobileSession( + xMobileToken: token, + mobileSessionInfo: MobileSessionInfo( + (b) => b..fcmTokenTimestamp = DateTime.now().millisecondsSinceEpoch, + ), ); } @@ -257,13 +263,17 @@ class NotificationService { null && (data['stateEntityType'] ?? data['onClick.stateEntityType']) != null) { - entityId = EntityId.fromTypeAndUuid( - entityTypeFromString( - (data['stateEntityType'] ?? data['onClick.stateEntityType']) - .toString(), - ), - (data['stateEntityId'] ?? data['onClick.stateEntityId']) - .toString(), + entityId = $EntityId( + (b) => + b + ..entityType = EntityType.valueOf( + (data['stateEntityType'] ?? + data['onClick.stateEntityType']) + .toString(), + ) + ..id = + (data['stateEntityId'] ?? data['onClick.stateEntityId']) + .toString(), ); } @@ -306,10 +316,10 @@ class NotificationService { Future _getNotificationsCountRemote() async { try { - return _tbClient.getNotificationService().getUnreadNotificationsCount( - 'MOBILE_APP', - requestConfig: RequestConfig(ignoreErrors: true), - ); + final resp = await _tbClient + .getNotificationControllerApi() + .getUnreadNotificationsCount(deliveryMethod: 'MOBILE_APP'); + return resp.data ?? 0; } catch (_) { return 0; } diff --git a/lib/utils/services/tb_client_service/tb_client_service.dart b/lib/utils/services/tb_client_service/tb_client_service.dart index 63d125fb..47e45546 100644 --- a/lib/utils/services/tb_client_service/tb_client_service.dart +++ b/lib/utils/services/tb_client_service/tb_client_service.dart @@ -11,7 +11,7 @@ import 'package:thingsboard_app/utils/services/loading_service/i_loading_service import 'package:thingsboard_app/utils/services/overlay_service/i_overlay_service.dart'; import 'package:thingsboard_app/utils/services/tb_client_service/i_tb_client_service.dart'; import 'package:thingsboard_app/utils/utils.dart'; -import 'package:thingsboard_client/thingsboard_client.dart'; +import 'package:thingsboard_app/thingsboard_client.dart'; class TbClientService implements ITbClientService { late ThingsboardClient _client; @@ -109,7 +109,25 @@ class TbClientService implements ITbClientService { required ErrorCallback onAuthError, }) async { log('TbClient:reinit()'); - await _client.reInit(endpoint); + _client = ThingsboardClient( + endpoint, + storage: getIt(), + onUserLoaded: () { + onUserLoaded(); + }, + onError: (e) { + onAuthError(e); + onClientError(e); + }, + onLoadStarted: () { + onLoadStarted(); + }, + onLoadFinished: () { + onLoadFinished(); + }, + computeFunc: (callback, message) => compute(callback, message), + ); + await _client.init(); onDone(); } } diff --git a/lib/utils/services/tb_image_gallery_service/tb_image_gallery_service.dart b/lib/utils/services/tb_image_gallery_service/tb_image_gallery_service.dart index 86dcbaef..162053fd 100644 --- a/lib/utils/services/tb_image_gallery_service/tb_image_gallery_service.dart +++ b/lib/utils/services/tb_image_gallery_service/tb_image_gallery_service.dart @@ -1,5 +1,6 @@ import 'dart:typed_data'; +import 'package:dio/dio.dart'; import 'package:thingsboard_app/generated/l10n.dart'; import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/thingsboard_client.dart'; @@ -16,11 +17,15 @@ class TbImageGalleryService implements ITbImageGalleryService { }) async { try { final tbClient = getIt().client; - await tbClient.getImageService().uploadImageBytes( - imageBytes, - title: title, - mimeType: mimeType, - ); + final file = MultipartFile.fromBytes( + imageBytes, + filename: title, + contentType: DioMediaType.parse(mimeType), + ); + await tbClient.getImageControllerApi().uploadImage( + file: file, + title: title, + ); getIt().showSuccessNotification( (context) => S.of(context).imageSavedToGallery, diff --git a/lib/utils/services/version_service/i_version_service.dart b/lib/utils/services/version_service/i_version_service.dart index 1a7bd44b..5d322c32 100644 --- a/lib/utils/services/version_service/i_version_service.dart +++ b/lib/utils/services/version_service/i_version_service.dart @@ -1,4 +1,4 @@ -import 'package:thingsboard_client/thingsboard_client.dart'; +import 'package:thingsboard_app/utils/services/version_service/version_info.dart'; abstract interface class IVersionService { bool appUpdateRequired(VersionInfo info); diff --git a/lib/utils/services/version_service/version_info.dart b/lib/utils/services/version_service/version_info.dart new file mode 100644 index 00000000..d10e524f --- /dev/null +++ b/lib/utils/services/version_service/version_info.dart @@ -0,0 +1,38 @@ +import 'package:thingsboard_app/utils/services/device_info/platform_version.dart'; +import 'package:thingsboard_ce_client/src/model/mobile_app_version_info.dart'; + +class VersionInfo { + const VersionInfo({ + this.minVersion, + this.minVersionReleaseNotes, + this.latestVersion, + this.latestVersionReleaseNotes, + }); + + factory VersionInfo.fromMobileAppVersionInfo(MobileAppVersionInfo info) { + return VersionInfo( + minVersion: info.minVersion != null && info.minVersion!.isNotEmpty + ? _tryParse(info.minVersion!) + : null, + minVersionReleaseNotes: info.minVersionReleaseNotes, + latestVersion: + info.latestVersion != null && info.latestVersion!.isNotEmpty + ? _tryParse(info.latestVersion!) + : null, + latestVersionReleaseNotes: info.latestVersionReleaseNotes, + ); + } + + static PlatformVersion? _tryParse(String v) { + try { + return PlatformVersion.fromString(v); + } catch (_) { + return null; + } + } + + final PlatformVersion? minVersion; + final String? minVersionReleaseNotes; + final PlatformVersion? latestVersion; + final String? latestVersionReleaseNotes; +} diff --git a/lib/utils/services/version_service/version_service.dart b/lib/utils/services/version_service/version_service.dart index 422bdd50..69c5ae94 100644 --- a/lib/utils/services/version_service/version_service.dart +++ b/lib/utils/services/version_service/version_service.dart @@ -1,7 +1,7 @@ import 'package:thingsboard_app/locator.dart'; import 'package:thingsboard_app/utils/services/device_info/i_device_info_service.dart'; import 'package:thingsboard_app/utils/services/version_service/i_version_service.dart'; -import 'package:thingsboard_client/src/model/mobile/version_info.dart'; +import 'package:thingsboard_app/utils/services/version_service/version_info.dart'; class VersionService implements IVersionService { final _deviceInfoService = getIt(); diff --git a/lib/utils/translation_utils.dart b/lib/utils/translation_utils.dart index 26998098..3405649f 100644 --- a/lib/utils/translation_utils.dart +++ b/lib/utils/translation_utils.dart @@ -59,7 +59,7 @@ extension EntityTypeTranslationUtils on EntityType { EntityType.NOTIFICATION_RULE => S.of(context).notificationRule, EntityType.CALCULATED_FIELD => S.of(context).calculatedField, EntityType.QUEUE_STATS => S.of(context).queueStats, - EntityType.OAUTH2_CLIENT => S.of(context).oauth2Client, + EntityType.oAUTH2CLIENT => S.of(context).oauth2Client, EntityType.DOMAIN => S.of(context).domain, EntityType.MOBILE_APP => S.of(context).mobileApp, EntityType.MOBILE_APP_BUNDLE => S.of(context).mobileAppBundle, @@ -68,6 +68,7 @@ extension EntityTypeTranslationUtils on EntityType { EntityType.AI_MODEL => S.of(context).aiModel, EntityType.API_KEY => S.of(context).apiKey, + _ => '', }; } } @@ -83,6 +84,7 @@ extension AlarmStatusTranslationUtils on AlarmStatus { '${S.of(context).cleared} ${S.of(context).acknowledged}', AlarmStatus.CLEARED_UNACK => '${S.of(context).cleared} ${S.of(context).unacknowledged}', + _ => '', }; } } @@ -95,6 +97,7 @@ extension AlarmSeverityTranslationUtils on AlarmSeverity { AlarmSeverity.MINOR => S.of(context).minor, AlarmSeverity.WARNING => S.of(context).warning, AlarmSeverity.INDETERMINATE => S.of(context).indeterminate, + _ => '', }; } } @@ -104,6 +107,7 @@ extension ActionStatusTranslationUtils on ActionStatus { return switch (this) { ActionStatus.SUCCESS => S.of(context).actionStatusSuccess, ActionStatus.FAILURE => S.of(context).actionStatusFailure, + _ => '', }; } } @@ -157,6 +161,7 @@ extension ActionTypeTranslationUtils on ActionType { ActionType.UPDATED_COMMENT => S.of(context).actionTypeUpdatedComment, ActionType.DELETED_COMMENT => S.of(context).actionTypeDeletedComment, ActionType.SMS_SENT => S.of(context).actionTypeSmsSent, + _ => '', }; } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 3589b27e..bc86720b 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -41,7 +41,10 @@ abstract class Utils { final List> stateObj = [{}]; final params = {}; if (entityId != null) { - params['entityId'] = entityId.toJson(); + params['entityId'] = { + 'entityType': entityId.entityType.name, + 'id': entityId.id, + }; } if (entityName != null) { params['entityName'] = entityName; @@ -62,16 +65,16 @@ abstract class Utils { return Uri.encodeComponent(base64.encode(utf8.encode(encodedUri))); } - static String? contactToShortAddress(ContactBased contact) { + static String? contactToShortAddress(dynamic contact) { final addressParts = []; if (contact.country != null) { - addressParts.add(contact.country!); + addressParts.add(contact.country as String); } if (contact.city != null) { - addressParts.add(contact.city!); + addressParts.add(contact.city as String); } if (contact.address != null) { - addressParts.add(contact.address!); + addressParts.add(contact.address as String); } if (addressParts.isNotEmpty) { return addressParts.join(', '); diff --git a/pubspec.lock b/pubspec.lock index 3af3a801..aa431799 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1645,6 +1645,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + one_of: + dependency: transitive + description: + name: one_of + sha256: "25fe0fcf181e761c6fcd604caf9d5fdf952321be17584ba81c72c06bdaa511f0" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + one_of_serializer: + dependency: transitive + description: + name: one_of_serializer + sha256: "3f3dfb5c1578ba3afef1cb47fcc49e585e797af3f2b6c2cc7ed90aad0c5e7b83" + url: "https://pub.dev" + source: hosted + version: "1.5.0" open_settings_plus: dependency: "direct main" description: @@ -1934,6 +1950,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + quiver: + dependency: transitive + description: + name: quiver + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.dev" + source: hosted + version: "3.2.2" reactive_forms: dependency: "direct main" description: @@ -2219,14 +2243,13 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.8" - thingsboard_client: + thingsboard_ce_client: dependency: "direct main" description: - name: thingsboard_client - sha256: "6d5bf839593791f8130ce00e0cb2f4d8f3335f321172ceb84291847fc877735e" - url: "https://pub.dev" - source: hosted - version: "4.2.1" + path: "../thingsboard-dart-client/ce" + relative: true + source: path + version: "4.4.0" time: dependency: transitive description: @@ -2483,14 +2506,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "3.0.3" webdriver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index eb6fda6c..3c0bec4f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,7 +12,8 @@ environment: dependencies: flutter: sdk: flutter - thingsboard_client: ^4.2.1 + thingsboard_ce_client: + path: ../thingsboard-dart-client/ce intl: ^0.19.0 flutter_secure_storage: ^9.0.0 flutter_speed_dial: ^7.0.0