From 3eb0c615b8bad41bf15fcbb2f7b1d868c9adf1ec Mon Sep 17 00:00:00 2001 From: reasje Date: Wed, 7 Aug 2024 11:34:09 +0330 Subject: [PATCH 1/2] refactor: JS channel, bluetooth channel, cron channel, panel --- .../subfeatures/open_dapp/domain/domain.dart | 4 + .../open_dapp/domain/helpers/helpers.dart | 1 + .../helpers/js_channels/bluetooth_helper.dart | 302 ++++++++ .../bluetooth_listeners_helper.dart | 100 +++ .../helpers/js_channels/cron_helper.dart | 137 ++++ .../js_channels/cron_listeners_helper.dart | 40 + .../helpers/js_channels/js_channel.dart | 5 + .../js_channel_handlers_helper.dart | 82 +++ .../utils/bluetooth_entities_utils.dart | 42 ++ .../domain/utils/js_channel_utils.dart | 29 + .../open_dapp/domain/utils/panel_utils.dart | 44 ++ .../open_dapp/domain/utils/utils.dart | 3 + .../subfeatures/open_dapp/open_dapp.dart | 5 + .../open_dapp/open_dapp_presenter.dart | 694 ++---------------- .../subfeatures/dapp_hooks/dapp_hooks.dart | 6 + .../subfeatures/dapp_hooks/domain/domain.dart | 2 + .../subfeatures/dapp_hooks/utils/utils.dart | 1 + .../widgets/{widget.dart => widgets.dart} | 0 18 files changed, 871 insertions(+), 626 deletions(-) create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/domain.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/helpers/helpers.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/bluetooth_helper.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/bluetooth_listeners_helper.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/cron_helper.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/cron_listeners_helper.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/js_channel.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/js_channel_handlers_helper.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/utils/bluetooth_entities_utils.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/utils/js_channel_utils.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/domain/utils/panel_utils.dart create mode 100644 lib/features/dapps/subfeatures/open_dapp/open_dapp.dart create mode 100644 lib/features/settings/subfeatures/dapp_hooks/dapp_hooks.dart create mode 100644 lib/features/settings/subfeatures/dapp_hooks/domain/domain.dart rename lib/features/settings/subfeatures/dapp_hooks/widgets/{widget.dart => widgets.dart} (100%) diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/domain.dart b/lib/features/dapps/subfeatures/open_dapp/domain/domain.dart new file mode 100644 index 00000000..2745a3a3 --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/domain.dart @@ -0,0 +1,4 @@ +export 'utils/utils.dart'; +export 'helpers/helpers.dart'; +export 'entities/entities.dart'; +export 'dapps_errors.dart'; diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/helpers/helpers.dart b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/helpers.dart new file mode 100644 index 00000000..8d683965 --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/helpers.dart @@ -0,0 +1 @@ +export 'js_channels/js_channel.dart'; diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/bluetooth_helper.dart b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/bluetooth_helper.dart new file mode 100644 index 00000000..30077e87 --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/bluetooth_helper.dart @@ -0,0 +1,302 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:collection/collection.dart'; +import 'package:datadashwallet/features/common/common.dart'; +import 'package:datadashwallet/features/settings/subfeatures/dapp_hooks/dapp_hooks.dart'; +import 'package:flutter/material.dart'; +import 'package:mxc_logic/mxc_logic.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart' as blue_plus; + +import '../../../open_dapp.dart'; + +class BluetoothHelper { + BluetoothHelper({ + required this.state, + required this.context, + required this.translate, + required this.collectLog, + required this.bluetoothUseCase, + required this.navigator, + required this.minerHooksHelper, + required this.loading, + required this.characteristicListenerTimer, + required characteristicValueStreamSubscription, + }); + + OpenDAppState state; + BluetoothUseCase bluetoothUseCase; + void Function(String line) collectLog; + NavigatorState? navigator; + MinerHooksHelper minerHooksHelper; + BuildContext? context; + String? Function(String) translate; + void Function(bool v) loading; + Timer? characteristicListenerTimer; + StreamSubscription>? characteristicValueStreamSubscription; + + Future> handleBluetoothRequestDevice( + Map channelData, + ) async { + // final options = RequestDeviceOptions.fromJson(channelData['data']); + final options = RequestDeviceOptions.fromMap(channelData); + late BluetoothDevice responseDevice; + + await bluetoothUseCase.turnOnBluetoothAndProceed(); + + // Get the options data + bluetoothUseCase.startScanning( + withServices: options.filters != null + ? options.filters! + .expand((filter) => filter.services ?? []) + .toList() + .firstOrNull + : [], + withRemoteIds: + null, // No direct mapping in RequestDeviceOptions, adjust as necessary + withNames: options.filters != null + ? options.filters! + .where((filter) => filter.name != null) + .map((filter) => filter.name!) + .toList() + : [], + withKeywords: options.filters != null + ? options.filters! + .where((filter) => filter.namePrefix != null) + .map((filter) => filter.namePrefix!) + .toList() + : [], + withMsd: options.filters != null + ? options.filters! + .expand((filter) => filter.manufacturerData ?? []) + .toList() + .firstOrNull + : [], + withServiceData: options.filters != null + ? options.filters! + .expand((filter) => filter.serviceData ?? []) + .toList() + .firstOrNull + : [], + continuousUpdates: true, + continuousDivisor: 2, + androidUsesFineLocation: true, + ); + + final blueberryRing = await getBlueberryRing(); + bluetoothUseCase.stopScanner(); + if (blueberryRing == null) { + return {}; + } else { + responseDevice = blueberryRing; + } + + return responseDevice.toMap(); + } + + // GATT server + Future> handleBluetoothRemoteGATTServerGetPrimaryService( + Map data) async { + collectLog('handleBluetoothRemoteGATTServerGetPrimaryService : $data'); + final selectedService = await BluetoothEntitiesUtils.getSelectedService( + data['service'], state.selectedScanResult!); + + final device = BluetoothDevice.getBluetoothDeviceFromScanResult( + state.selectedScanResult!); + final bluetoothRemoteGATTService = + BluetoothRemoteGATTService.fromBluetoothService( + device, selectedService); + return bluetoothRemoteGATTService.toMap(); + } + + void initJSCharacteristicValueEmitter( + blue_plus.BluetoothCharacteristic characteristic, + ) async { + await characteristic.setNotifyValue(true); + + characteristicValueStreamSubscription = + characteristic.lastValueStream.listen((event) async { + final uInt8List = Uint8List.fromList(event); + collectLog('characteristicValueStreamSubscription:event $event'); + collectLog( + 'characteristicValueStreamSubscription:uInt8List ${uInt8List.toString()}'); + final script = ''' + navigator.bluetooth.updateCharacteristicValue('${characteristic.uuid.str}', ${uInt8List.toString()},); + '''; + await state.webviewController!.evaluateJavascript(source: script); + }); + } + + void removeJSCharacteristicValueEmitter( + blue_plus.BluetoothCharacteristic characteristic, + ) async { + await characteristic.setNotifyValue(false); + + characteristicValueStreamSubscription?.cancel(); + } + + Future> handleBluetoothRemoteGATTServerConnect( + Map data) async { + collectLog('handleBluetoothRemoteGATTServerConnect : $data'); + await bluetoothUseCase.connectionHandler(state.selectedScanResult!.device); + + return BluetoothRemoteGATTServer( + device: BluetoothDevice.getBluetoothDeviceFromScanResult( + state.selectedScanResult!), + connected: true) + .toMap(); + } + + // Service + Future> + handleBluetoothRemoteGATTServiceGetCharacteristic( + Map data) async { + collectLog('handleBluetoothRemoteGATTServiceGetCharacteristic : $data'); + final targetCharacteristicUUID = data['characteristic']; + + final selectedService = await BluetoothEntitiesUtils.getSelectedService( + data['this'], state.selectedScanResult!); + final targetCharacteristic = + BluetoothEntitiesUtils.getSelectedCharacteristic( + targetCharacteristicUUID, selectedService); + + final device = BluetoothDevice.getBluetoothDeviceFromScanResult( + state.selectedScanResult!); + final bluetoothRemoteGATTService = + BluetoothRemoteGATTService.fromBluetoothService( + device, selectedService); + final bluetoothRemoteGATTCharacteristic = BluetoothRemoteGATTCharacteristic( + service: bluetoothRemoteGATTService, + properties: + BluetoothCharacteristicProperties.fromCharacteristicProperties( + targetCharacteristic.properties), + uuid: targetCharacteristic.uuid.str, + value: null); + return bluetoothRemoteGATTCharacteristic.toMap(); + } + + Future> + handleBluetoothRemoteGATTCharacteristicStartNotifications( + Map data) async { + collectLog( + 'handleBluetoothRemoteGATTCharacteristicStartNotifications : $data'); + final selectedService = await BluetoothEntitiesUtils.getSelectedService( + data['serviceUUID'], state.selectedScanResult!); + final selectedCharacteristic = + BluetoothEntitiesUtils.getSelectedCharacteristic( + data['this'], selectedService); + + final bluetoothRemoteGATTCharacteristic = + BluetoothEntitiesUtils.getBluetoothRemoteGATTCharacteristic( + selectedCharacteristic, selectedService, state.selectedScanResult!); + + initJSCharacteristicValueEmitter(selectedCharacteristic); + + return bluetoothRemoteGATTCharacteristic.toMap(); + } + + Future> + handleBluetoothRemoteGATTCharacteristicStopNotifications( + Map data) async { + collectLog( + 'handleBluetoothRemoteGATTCharacteristicStopNotifications : $data'); + final selectedService = await BluetoothEntitiesUtils.getSelectedService( + data['serviceUUID'], state.selectedScanResult!); + final selectedCharacteristic = + BluetoothEntitiesUtils.getSelectedCharacteristic( + data['this'], selectedService); + + final bluetoothRemoteGATTCharacteristic = + BluetoothEntitiesUtils.getBluetoothRemoteGATTCharacteristic( + selectedCharacteristic, selectedService, state.selectedScanResult!); + + removeJSCharacteristicValueEmitter(selectedCharacteristic); + + return bluetoothRemoteGATTCharacteristic.toMap(); + } + + Future> handleWrites(Map data, + {bool withResponse = true}) async { + collectLog('handleWrites : $data'); + final selectedService = await BluetoothEntitiesUtils.getSelectedService( + data['serviceUUID'], state.selectedScanResult!); + final selectedCharacteristic = + BluetoothEntitiesUtils.getSelectedCharacteristic( + data['this'], selectedService); + final value = Uint8List.fromList(List.from( + (data['value'] as Map).values.toList())); + + collectLog('handleWrites:value $value'); + if (withResponse) { + await selectedCharacteristic.write(value); + } else { + await selectedCharacteristic.write(value, withoutResponse: true); + } + return {}; + } + + Future> + handleBluetoothRemoteGATTCharacteristicWriteValue( + Map data) async { + return handleWrites(data); + } + + Future> + handleBluetoothRemoteGATTCharacteristicWriteValueWithResponse( + Map data) async { + return handleWrites(data); + } + + Future> + handleBluetoothRemoteGATTCharacteristicWriteValueWithoutResponse( + Map data) async { + return handleWrites(data, withResponse: false); + } + + Future handleBluetoothRemoteGATTCharacteristicReadValue( + Map data) async { + collectLog('handleBluetoothRemoteGATTCharacteristicReadValue : $data'); + final selectedService = await BluetoothEntitiesUtils.getSelectedService( + data['serviceUUID'], state.selectedScanResult!); + final selectedCharacteristic = + BluetoothEntitiesUtils.getSelectedCharacteristic( + data['this'], selectedService); + final value = selectedCharacteristic.lastValue; + + final uInt8List = Uint8List.fromList(value); + + collectLog('handleBluetoothRemoteGATTCharacteristicReadValue:value $value'); + collectLog( + 'handleBluetoothRemoteGATTCharacteristicReadValue:uInt8List ${uInt8List.toString()}'); + + return uInt8List; + } + + Future getBlueberryRing() async { + loading(true); + return Future.delayed(const Duration(seconds: 3), () async { + loading(false); + BluetoothDevice? responseDevice; + final scanResults = bluetoothUseCase.scanResults.value; + if (scanResults.length == 1) { + // only one scan results + final scanResult = scanResults.first; + state.selectedScanResult = scanResult; + } else { + // We need to let the user to choose If two or more devices of rings are available and even If empty maybe let the user to wait + final scanResult = await showBlueberryRingsBottomSheet( + context!, + ); + if (scanResult != null) { + state.selectedScanResult = scanResult; + } + } + if (state.selectedScanResult != null) { + responseDevice = BluetoothDevice.getBluetoothDeviceFromScanResult( + state.selectedScanResult!); + } + + return responseDevice; + }); + } +} diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/bluetooth_listeners_helper.dart b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/bluetooth_listeners_helper.dart new file mode 100644 index 00000000..a0b81953 --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/bluetooth_listeners_helper.dart @@ -0,0 +1,100 @@ +import 'package:datadashwallet/core/core.dart'; +import 'package:datadashwallet/features/settings/subfeatures/dapp_hooks/dapp_hooks.dart'; +import 'package:flutter/material.dart'; +import 'package:mxc_logic/mxc_logic.dart'; + +import '../../../open_dapp.dart'; + +class BluetoothListenersHelper { + BluetoothListenersHelper({ + required this.state, + required this.context, + required this.translate, + required this.bluetoothHelper, + required this.navigator, + required this.minerHooksHelper, + required this.jsChannelHandlerHelper, + }); + + OpenDAppState state; + NavigatorState? navigator; + MinerHooksHelper minerHooksHelper; + BluetoothHelper bluetoothHelper; + BuildContext? context; + String? Function(String) translate; + JsChannelHandlersHelper jsChannelHandlerHelper; + + void injectBluetoothListeners() { + // Bluetooth API + + state.webviewController!.addJavaScriptHandler( + handlerName: JSChannelEvents.requestDevice, + callback: (args) => jsChannelHandlerHelper.jsChannelErrorHandler( + args, bluetoothHelper.handleBluetoothRequestDevice)); + + // BluetoothRemoteGATTServer + + state.webviewController!.addJavaScriptHandler( + handlerName: JSChannelEvents.bluetoothRemoteGATTServerConnect, + callback: (args) => jsChannelHandlerHelper.jsChannelErrorHandler( + args, bluetoothHelper.handleBluetoothRemoteGATTServerConnect)); + + state.webviewController!.addJavaScriptHandler( + handlerName: JSChannelEvents.bluetoothRemoteGATTServerGetPrimaryService, + callback: (args) => jsChannelHandlerHelper.jsChannelErrorHandler(args, + bluetoothHelper.handleBluetoothRemoteGATTServerGetPrimaryService)); + + // BluetoothRemoteGATTService + + state.webviewController!.addJavaScriptHandler( + handlerName: + JSChannelEvents.bluetoothRemoteGATTServiceGetCharacteristic, + callback: (args) => jsChannelHandlerHelper.jsChannelErrorHandler(args, + bluetoothHelper.handleBluetoothRemoteGATTServiceGetCharacteristic)); + + // BluetoothRemoteGATTCharacteristic + + state.webviewController!.addJavaScriptHandler( + handlerName: + JSChannelEvents.bluetoothRemoteGATTCharacteristicStartNotifications, + callback: (args) => jsChannelHandlerHelper.jsChannelErrorHandler( + args, + bluetoothHelper + .handleBluetoothRemoteGATTCharacteristicStartNotifications)); + + state.webviewController!.addJavaScriptHandler( + handlerName: + JSChannelEvents.bluetoothRemoteGATTCharacteristicStopNotifications, + callback: (args) => jsChannelHandlerHelper.jsChannelErrorHandler( + args, + bluetoothHelper + .handleBluetoothRemoteGATTCharacteristicStopNotifications)); + + state.webviewController!.addJavaScriptHandler( + handlerName: + JSChannelEvents.bluetoothRemoteGATTCharacteristicWriteValue, + callback: (args) => jsChannelHandlerHelper.jsChannelErrorHandler(args, + bluetoothHelper.handleBluetoothRemoteGATTCharacteristicWriteValue)); + + state.webviewController!.addJavaScriptHandler( + handlerName: JSChannelEvents + .bluetoothRemoteGATTCharacteristicWriteValueWithResponse, + callback: (args) => jsChannelHandlerHelper.jsChannelErrorHandler( + args, + bluetoothHelper + .handleBluetoothRemoteGATTCharacteristicWriteValueWithResponse)); + + state.webviewController!.addJavaScriptHandler( + handlerName: JSChannelEvents + .bluetoothRemoteGATTCharacteristicWriteValueWithoutResponse, + callback: (args) => jsChannelHandlerHelper.jsChannelErrorHandler( + args, + bluetoothHelper + .handleBluetoothRemoteGATTCharacteristicWriteValueWithoutResponse)); + + state.webviewController!.addJavaScriptHandler( + handlerName: JSChannelEvents.bluetoothRemoteGATTCharacteristicReadValue, + callback: (args) => jsChannelHandlerHelper.jsChannelErrorHandler(args, + bluetoothHelper.handleBluetoothRemoteGATTCharacteristicReadValue)); + } +} diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/cron_helper.dart b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/cron_helper.dart new file mode 100644 index 00000000..a4f9ee78 --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/cron_helper.dart @@ -0,0 +1,137 @@ +import 'package:datadashwallet/core/core.dart'; +import 'package:datadashwallet/features/settings/subfeatures/dapp_hooks/dapp_hooks.dart'; +import 'package:flutter/material.dart'; +import 'package:mxc_logic/mxc_logic.dart'; + +import '../../../open_dapp.dart'; + +class CronHelper { + CronHelper({ + required this.state, + required this.context, + required this.translate, + required this.dAppHooksUseCase, + required this.navigator, + required this.minerHooksHelper, + }); + + OpenDAppState state; + DAppHooksUseCase dAppHooksUseCase; + NavigatorState? navigator; + MinerHooksHelper minerHooksHelper; + BuildContext? context; + String? Function(String) translate; + + // Update via functions & get data via steam & send the data via event eaach time + // ready => updateSystemInfo (service statues, mining service status, time, selected miners, camera permission location permission) + + Future> handleChangeCronTransition( + Map channelData, AXSCronServices axsCronService) async { + final axsCronService = + AXSCronServicesExtension.getCronServiceFromJson(channelData); + if (axsCronService == AXSCronServices.miningAutoClaimCron) { + ChangeCronTransitionRequestModel; + final changeCronTransitionRequestModel = + ChangeCronTransitionRequestModel.fromMap( + channelData['cron'], MiningCronServiceDataModel.fromMap); + + // Here i change the data that won't effect the + final currentDappHooksData = state.dappHooksData; + final newData = changeCronTransitionRequestModel.data; + + if (newData != null) { + final minersList = newData.minersList ?? + currentDappHooksData.minerHooks.selectedMiners; + dAppHooksUseCase.updateMinersList(minersList); + + final newTimeOfDay = TimeOfDay.fromDateTime(newData.time!); + final currentTimeOfDay = + TimeOfDay.fromDateTime(currentDappHooksData.minerHooks.time); + + if (newData.time != null && newTimeOfDay != currentTimeOfDay) { + await minerHooksHelper.changeMinerHookTiming(newTimeOfDay); + } + } + + final miningCronServiceData = + MiningCronServiceDataModel.fromDAppHooksData( + dAppHooksUseCase.dappHooksData.value); + + final responseData = CronServiceDataModel.fromDAppHooksData( + axsCronService, + dAppHooksUseCase.dappHooksData.value, + miningCronServiceData); + + final response = AXSJSChannelResponseModel( + status: AXSJSChannelResponseStatus.success, + data: responseData, + message: null); + return response.toMap(miningCronServiceData.toMapWrapper); + } else { + throw 'Unknown service'; + } + } + + Future> handleChangeCronTransitionStatusEvent( + Map channelData, + AXSCronServices axsCronService, + ) async { + if (axsCronService == AXSCronServices.miningAutoClaimCron) { + final status = channelData['cron']['status']; + + await minerHooksHelper.changeMinerHooksEnabled(status); + final miningCronServiceData = + MiningCronServiceDataModel.fromDAppHooksData( + dAppHooksUseCase.dappHooksData.value); + + final responseData = CronServiceDataModel.fromDAppHooksData( + axsCronService, + dAppHooksUseCase.dappHooksData.value, + miningCronServiceData); + final response = AXSJSChannelResponseModel( + status: AXSJSChannelResponseStatus.success, + message: null, + data: responseData); + return response.toMap(miningCronServiceData.toMapWrapper); + } else { + throw 'Unknown cron service'; + } + } + + Future> handleGetSystemInfoEvent( + Map channelData, + AXSCronServices axsCronService, + ) async { + if (axsCronService == AXSCronServices.miningAutoClaimCron) { + final dappHooksData = state.dappHooksData; + + final miningCronServiceData = + MiningCronServiceDataModel.fromDAppHooksData(dappHooksData); + + final responseData = CronServiceDataModel.fromDAppHooksData( + axsCronService, dappHooksData, miningCronServiceData); + final response = AXSJSChannelResponseModel( + status: AXSJSChannelResponseStatus.success, + message: null, + data: responseData, + ); + return response.toMap(miningCronServiceData.toMapWrapper); + } else { + throw 'Unknown cron service'; + } + } + + Future> handleGoToAdvancedSettingsEvent( + Map channelData, AXSCronServices axsCronService) async { + goToAdvancedSettings(); + final response = AXSJSChannelResponseModel( + status: AXSJSChannelResponseStatus.success, message: null, data: null); + return response.toMap((data) => {}); + } + + void goToAdvancedSettings() { + navigator!.push(route( + const DAppHooksPage(), + )); + } +} diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/cron_listeners_helper.dart b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/cron_listeners_helper.dart new file mode 100644 index 00000000..6281e32e --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/cron_listeners_helper.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:mxc_logic/mxc_logic.dart'; + +import '../../../open_dapp.dart'; + +class CronListenersHelper { + CronListenersHelper( + {required this.state, + required this.context, + required this.jsChannelHandlerHelper, + required this.cronHelper}); + + OpenDAppState state; + BuildContext? context; + JsChannelHandlersHelper jsChannelHandlerHelper; + CronHelper cronHelper; + + // call this on webview created + void injectMinerDappListeners() async { + state.webviewController!.addJavaScriptHandler( + handlerName: JSChannelEvents.changeCronTransitionEvent, + callback: (args) => jsChannelHandlerHelper.jsChannelCronErrorHandler( + args, cronHelper.handleChangeCronTransition)); + + state.webviewController!.addJavaScriptHandler( + handlerName: JSChannelEvents.changeCronTransitionStatusEvent, + callback: (args) => jsChannelHandlerHelper.jsChannelCronErrorHandler( + args, cronHelper.handleChangeCronTransitionStatusEvent)); + + state.webviewController!.addJavaScriptHandler( + handlerName: JSChannelEvents.getSystemInfoEvent, + callback: (args) => jsChannelHandlerHelper.jsChannelCronErrorHandler( + args, cronHelper.handleGetSystemInfoEvent)); + + state.webviewController!.addJavaScriptHandler( + handlerName: JSChannelEvents.goToAdvancedSettingsEvent, + callback: (args) => jsChannelHandlerHelper.jsChannelCronErrorHandler( + args, cronHelper.handleGoToAdvancedSettingsEvent)); + } +} diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/js_channel.dart b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/js_channel.dart new file mode 100644 index 00000000..4a94f697 --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/js_channel.dart @@ -0,0 +1,5 @@ +export 'cron_helper.dart'; +export 'bluetooth_helper.dart'; +export 'js_channel_handlers_helper.dart'; +export 'cron_listeners_helper.dart'; +export 'bluetooth_listeners_helper.dart'; diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/js_channel_handlers_helper.dart b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/js_channel_handlers_helper.dart new file mode 100644 index 00000000..493b3975 --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/helpers/js_channels/js_channel_handlers_helper.dart @@ -0,0 +1,82 @@ +import 'dart:convert'; + +import 'package:datadashwallet/features/common/packages/bluetooth/bluetooth.dart'; +import 'package:flutter/material.dart'; +import 'package:mxc_logic/mxc_logic.dart'; + +import '../../../open_dapp.dart'; + +class JsChannelHandlersHelper { + JsChannelHandlersHelper({ + required this.state, + required this.context, + required this.translate, + required this.addError, + }); + + OpenDAppState state; + BuildContext? context; + String? Function(String) translate; + void Function(dynamic error, [StackTrace? stackTrace]) addError; + + + + Future> jsChannelCronErrorHandler( + List args, + Future> Function( + Map, + AXSCronServices, + ) + callback, + ) async { + try { + Map channelDataMap; + + final channelData = args[0]; + channelDataMap = channelData as Map; + + final axsCronService = + AXSCronServicesExtension.getCronServiceFromJson(channelDataMap); + final callbackRes = await callback(channelDataMap, axsCronService); + return callbackRes; + } catch (e) { + final response = AXSJSChannelResponseModel( + status: AXSJSChannelResponseStatus.failed, + data: null, + message: e.toString()); + return response.toMap((data) => {'message': e.toString()}); + } + } + + Future jsChannelErrorHandler( + List args, + Future Function( + Map, + ) + callback, + ) async { + try { + Map channelDataMap; + + final channelData = args[0]; + channelDataMap = channelData == null + ? {} + : channelData is String + ? json.decode(channelData) as Map + : channelData as Map; + + final callbackRes = await callback(channelDataMap); + return callbackRes; + } catch (e) { + if (e is BluetoothTimeoutError) { + addError(translate('unable_to_continue_bluetooth_is_turned_off')!); + } + + final response = AXSJSChannelResponseModel( + status: AXSJSChannelResponseStatus.failed, + data: null, + message: e.toString()); + return response.toMap((data) => {'message': e}); + } + } +} diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/utils/bluetooth_entities_utils.dart b/lib/features/dapps/subfeatures/open_dapp/domain/utils/bluetooth_entities_utils.dart new file mode 100644 index 00000000..e4782644 --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/utils/bluetooth_entities_utils.dart @@ -0,0 +1,42 @@ +import 'package:datadashwallet/features/common/packages/bluetooth/blue_plus/blue_plus.dart'; + +import '../../open_dapp.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart' as blue_plus; + +class BluetoothEntitiesUtils { + static BluetoothRemoteGATTCharacteristic getBluetoothRemoteGATTCharacteristic( + blue_plus.BluetoothCharacteristic selectedCharacteristic, + blue_plus.BluetoothService selectedService, + blue_plus.ScanResult selectedScanResult) { + final device = + BluetoothDevice.getBluetoothDeviceFromScanResult(selectedScanResult); + final bluetoothRemoteGATTService = + BluetoothRemoteGATTService.fromBluetoothService( + device, selectedService); + final bluetoothRemoteGATTCharacteristic = BluetoothRemoteGATTCharacteristic( + service: bluetoothRemoteGATTService, + properties: + BluetoothCharacteristicProperties.fromCharacteristicProperties( + selectedCharacteristic.properties), + uuid: selectedCharacteristic.uuid.str, + value: null); + return bluetoothRemoteGATTCharacteristic; + } + + static blue_plus.BluetoothCharacteristic getSelectedCharacteristic( + String uuid, blue_plus.BluetoothService selectedService) { + final characteristicUUID = GuidHelper.parse(uuid); + final selectedCharacteristic = + BluePlusBluetoothUtils.getCharacteristicWithService( + selectedService, characteristicUUID); + return selectedCharacteristic; + } + + static Future getSelectedService( + String uuid, blue_plus.ScanResult selectedScanResult) async { + final serviceUUID = GuidHelper.parse(uuid); + final selectedService = await BluePlusBluetoothUtils.getPrimaryService( + selectedScanResult, serviceUUID); + return selectedService; + } +} diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/utils/js_channel_utils.dart b/lib/features/dapps/subfeatures/open_dapp/domain/utils/js_channel_utils.dart new file mode 100644 index 00000000..45746ffa --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/utils/js_channel_utils.dart @@ -0,0 +1,29 @@ +import 'package:mxc_logic/mxc_logic.dart'; + +import '../../open_dapp.dart'; + +class JSChannelUtils { + + + static void injectAXSWalletJSChannel(OpenDAppState state) async { + // Making It easy for accessing axs wallet + // use this way window.axs.callHandler + await state.webviewController!.evaluateJavascript( + source: JSChannelScripts.axsWalletObjectInjectScript( + JSChannelConfig.axsWalletJSObjectName)); + + await state.webviewController!.injectJavascriptFileFromAsset( + assetFilePath: 'assets/js/bluetooth/bluetooth.js'); + + // There is a gap for detecting the axs object in webview, It's intermittent after adding function structure to the scripts + Future.delayed( + const Duration(milliseconds: 500), + () async { + await state.webviewController!.evaluateJavascript( + source: JSChannelScripts.axsWalletReadyInjectScript( + JSChannelEvents.axsReadyEvent, + )); + }, + ); + } +} diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/utils/panel_utils.dart b/lib/features/dapps/subfeatures/open_dapp/domain/utils/panel_utils.dart new file mode 100644 index 00000000..941c409b --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/domain/utils/panel_utils.dart @@ -0,0 +1,44 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import '../../open_dapp.dart'; + +const double maxPanelHeight = 100.0; + +const settleDuration = Duration(milliseconds: 400); +const cancelDuration = Duration(milliseconds: 400); + +class PanelUtils { + static void showPanel(OpenDAppState state, Timer? panelTimer) async { + final status = state.animationController!.status; + if (state.animationController!.value != 1 && + status == AnimationStatus.completed || + status == AnimationStatus.dismissed) { + await state.animationController!.animateTo( + 1.0, + duration: settleDuration, + curve: Curves.ease, + ); + panelTimer = Timer( + const Duration(seconds: 3), + () => hidePanel(state, panelTimer), + ); + } + } + + static void hidePanel(OpenDAppState state, Timer? panelTimer) async { + final status = state.animationController!.status; + if (state.animationController!.value != 0 && + status == AnimationStatus.completed) { + await state.animationController!.animateTo( + 0.0, + duration: cancelDuration, + curve: Curves.easeInExpo, + ); + if (panelTimer != null) { + panelTimer.cancel(); + } + } + } +} diff --git a/lib/features/dapps/subfeatures/open_dapp/domain/utils/utils.dart b/lib/features/dapps/subfeatures/open_dapp/domain/utils/utils.dart index e69de29b..6426d551 100644 --- a/lib/features/dapps/subfeatures/open_dapp/domain/utils/utils.dart +++ b/lib/features/dapps/subfeatures/open_dapp/domain/utils/utils.dart @@ -0,0 +1,3 @@ +export 'bluetooth_entities_utils.dart'; +export 'js_channel_utils.dart'; +export 'panel_utils.dart'; diff --git a/lib/features/dapps/subfeatures/open_dapp/open_dapp.dart b/lib/features/dapps/subfeatures/open_dapp/open_dapp.dart new file mode 100644 index 00000000..3f539d7a --- /dev/null +++ b/lib/features/dapps/subfeatures/open_dapp/open_dapp.dart @@ -0,0 +1,5 @@ +export 'open_dapp_state.dart'; +export 'open_dapp_presenter.dart'; +export 'open_dapp_page.dart'; +export 'widgets/widgets.dart'; +export 'domain/domain.dart'; diff --git a/lib/features/dapps/subfeatures/open_dapp/open_dapp_presenter.dart b/lib/features/dapps/subfeatures/open_dapp/open_dapp_presenter.dart index 25ecd41f..4195c02b 100644 --- a/lib/features/dapps/subfeatures/open_dapp/open_dapp_presenter.dart +++ b/lib/features/dapps/subfeatures/open_dapp/open_dapp_presenter.dart @@ -1,26 +1,18 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:developer'; +import 'package:flutter/services.dart'; + import 'package:clipboard/clipboard.dart'; -import 'package:collection/collection.dart'; + import 'package:datadashwallet/app/logger.dart'; import 'package:datadashwallet/common/common.dart'; import 'package:datadashwallet/core/core.dart'; -import 'package:datadashwallet/features/common/common.dart'; -import 'package:datadashwallet/features/dapps/subfeatures/open_dapp/domain/dapps_errors.dart'; - -import 'package:datadashwallet/features/dapps/subfeatures/open_dapp/widgets/widgets.dart'; -import 'package:datadashwallet/features/settings/subfeatures/dapp_hooks/dapp_hooks_page.dart'; import 'package:datadashwallet/features/settings/subfeatures/dapp_hooks/utils/utils.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_blue_plus/flutter_blue_plus.dart' as blue_plus; + import 'package:mxc_logic/mxc_logic.dart'; import 'package:web3_provider/web3_provider.dart'; -import 'package:eth_sig_util/util/utils.dart'; -import './domain/entities/entities.dart'; -import 'open_dapp_state.dart'; +import 'open_dapp.dart'; final openDAppPageContainer = PresenterContainer( @@ -44,6 +36,9 @@ class OpenDAppPresenter extends CompletePresenter { ref.read(backgroundFetchConfigUseCaseProvider); late final _bluetoothUseCase = ref.read(bluetoothUseCaseProvider); + Timer? characteristicListenerTimer; + StreamSubscription>? characteristicValueStreamSubscription; + MinerHooksHelper get minerHooksHelper => MinerHooksHelper( translate: translate, context: context, @@ -52,6 +47,55 @@ class OpenDAppPresenter extends CompletePresenter { backgroundFetchConfigUseCase: _backgroundFetchConfigUseCase, ); + JsChannelHandlersHelper get jsChannelHandlersHelper => + JsChannelHandlersHelper( + translate: translate, + context: context, + state: state, + addError: addError, + ); + + CronHelper get cronHelper => CronHelper( + translate: translate, + context: context, + dAppHooksUseCase: _dAppHooksUseCase, + minerHooksHelper: minerHooksHelper, + navigator: navigator, + state: state, + ); + + BluetoothHelper get bluetoothHelper => BluetoothHelper( + translate: translate, + context: context, + collectLog: collectLog, + minerHooksHelper: minerHooksHelper, + navigator: navigator, + state: state, + loading: (bool value) => loading = value, + bluetoothUseCase: _bluetoothUseCase, + characteristicListenerTimer: characteristicListenerTimer, + characteristicValueStreamSubscription: + characteristicValueStreamSubscription, + ); + + CronListenersHelper get cronListenersHelper => CronListenersHelper( + context: context, + state: state, + cronHelper: cronHelper, + jsChannelHandlerHelper: jsChannelHandlersHelper, + ); + + BluetoothListenersHelper get bluetoothListenersHelper => + BluetoothListenersHelper( + translate: translate, + context: context, + bluetoothHelper: bluetoothHelper, + minerHooksHelper: minerHooksHelper, + jsChannelHandlerHelper: jsChannelHandlersHelper, + navigator: navigator, + state: state, + ); + @override void initState() { super.initState(); @@ -84,7 +128,7 @@ class OpenDAppPresenter extends CompletePresenter { @override Future dispose() { - characteriticListnerTimer?.cancel(); + cancelCharacteristicListenerTimer(); closeBlueberryConnection(); return super.dispose(); } @@ -97,8 +141,8 @@ class OpenDAppPresenter extends CompletePresenter { void onWebViewCreated(InAppWebViewController controller) async { notify(() => state.webviewController = controller); updateCurrentUrl(null); - injectMinerDappListeners(); - injectBluetoothListeners(); + cronListenersHelper.injectMinerDappListeners(); + bluetoothListenersHelper.injectBluetoothListeners(); } void updateCurrentUrl(Uri? value) async { @@ -237,7 +281,7 @@ class OpenDAppPresenter extends CompletePresenter { }) async { final amountEther = EtherAmount.inWei(bridge.value ?? BigInt.zero); final amount = amountEther.getValueInUnit(EtherUnit.ether).toString(); - final bridgeData = hexToBytes(bridge.data ?? ''); + final bridgeData = MXCType.hexToUint8List(bridge.data ?? ''); EtherAmount? gasPrice; double? gasFee; TransactionGasEstimation? estimatedGasFee; @@ -423,8 +467,7 @@ class OpenDAppPresenter extends CompletePresenter { return newNetwork; } - void signPersonalMessage() {} - + /// This function is used for both sign message and sign personal message void signMessage({ required Map object, required VoidCallback cancel, @@ -600,11 +643,6 @@ class OpenDAppPresenter extends CompletePresenter { return NavigationActionPolicy.ALLOW; } - final double maxPanelHeight = 100.0; - - final cancelDuration = const Duration(milliseconds: 400); - final settleDuration = const Duration(milliseconds: 400); - injectScrollDetector() { state.webviewController! .evaluateJavascript(source: JSChannelScripts.overScrollScript); @@ -621,34 +659,9 @@ class OpenDAppPresenter extends CompletePresenter { Timer? panelTimer; - void showPanel() async { - final status = state.animationController!.status; - if (state.animationController!.value != 1 && - status == AnimationStatus.completed || - status == AnimationStatus.dismissed) { - await state.animationController!.animateTo( - 1.0, - duration: settleDuration, - curve: Curves.ease, - ); - panelTimer = Timer(const Duration(seconds: 3), hidePanel); - } - } + void showPanel() => PanelUtils.showPanel(state, panelTimer); - void hidePanel() async { - final status = state.animationController!.status; - if (state.animationController!.value != 0 && - status == AnimationStatus.completed) { - await state.animationController!.animateTo( - 0.0, - duration: cancelDuration, - curve: Curves.easeInExpo, - ); - if (panelTimer != null) { - panelTimer!.cancel(); - } - } - } + void hidePanel() => PanelUtils.hidePanel(state, panelTimer); void closedApp() { navigator!.pop(); @@ -681,580 +694,9 @@ class OpenDAppPresenter extends CompletePresenter { state.isLoadStopCalled = !state.isLoadStopCalled; } - // call this on webview created - void injectMinerDappListeners() async { - state.webviewController!.addJavaScriptHandler( - handlerName: JSChannelEvents.changeCronTransitionEvent, - callback: (args) => - jsChannelCronErrorHandler(args, handleChangeCronTransition)); - - state.webviewController!.addJavaScriptHandler( - handlerName: JSChannelEvents.changeCronTransitionStatusEvent, - callback: (args) => jsChannelCronErrorHandler( - args, handleChangeCronTransitionStatusEvent)); - - state.webviewController!.addJavaScriptHandler( - handlerName: JSChannelEvents.getSystemInfoEvent, - callback: (args) => - jsChannelCronErrorHandler(args, handleGetSystemInfoEvent)); - - state.webviewController!.addJavaScriptHandler( - handlerName: JSChannelEvents.goToAdvancedSettingsEvent, - callback: (args) => - jsChannelCronErrorHandler(args, handleGoToAdvancedSettingsEvent)); - } - - void injectBluetoothListeners() { - // Bluetooth API - - state.webviewController!.addJavaScriptHandler( - handlerName: JSChannelEvents.requestDevice, - callback: (args) => - jsChannelErrorHandler(args, handleBluetoothRequestDevice)); - - // BluetoothRemoteGATTServer - - state.webviewController!.addJavaScriptHandler( - handlerName: JSChannelEvents.bluetoothRemoteGATTServerConnect, - callback: (args) => jsChannelErrorHandler( - args, handleBluetoothRemoteGATTServerConnect)); - - state.webviewController!.addJavaScriptHandler( - handlerName: JSChannelEvents.bluetoothRemoteGATTServerGetPrimaryService, - callback: (args) => jsChannelErrorHandler( - args, handleBluetoothRemoteGATTServerGetPrimaryService)); - - // BluetoothRemoteGATTService - - state.webviewController!.addJavaScriptHandler( - handlerName: - JSChannelEvents.bluetoothRemoteGATTServiceGetCharacteristic, - callback: (args) => jsChannelErrorHandler( - args, handleBluetoothRemoteGATTServiceGetCharacteristic)); - - // BluetoothRemoteGATTCharacteristic - - state.webviewController!.addJavaScriptHandler( - handlerName: - JSChannelEvents.bluetoothRemoteGATTCharacteristicStartNotifications, - callback: (args) => jsChannelErrorHandler( - args, handleBluetoothRemoteGATTCharacteristicStartNotifications)); - - state.webviewController!.addJavaScriptHandler( - handlerName: - JSChannelEvents.bluetoothRemoteGATTCharacteristicStopNotifications, - callback: (args) => jsChannelErrorHandler( - args, handleBluetoothRemoteGATTCharacteristicStopNotifications)); - - state.webviewController!.addJavaScriptHandler( - handlerName: - JSChannelEvents.bluetoothRemoteGATTCharacteristicWriteValue, - callback: (args) => jsChannelErrorHandler( - args, handleBluetoothRemoteGATTCharacteristicWriteValue)); - - state.webviewController!.addJavaScriptHandler( - handlerName: JSChannelEvents - .bluetoothRemoteGATTCharacteristicWriteValueWithResponse, - callback: (args) => jsChannelErrorHandler(args, - handleBluetoothRemoteGATTCharacteristicWriteValueWithResponse)); - - state.webviewController!.addJavaScriptHandler( - handlerName: JSChannelEvents - .bluetoothRemoteGATTCharacteristicWriteValueWithoutResponse, - callback: (args) => jsChannelErrorHandler(args, - handleBluetoothRemoteGATTCharacteristicWriteValueWithoutResponse)); - - state.webviewController!.addJavaScriptHandler( - handlerName: JSChannelEvents.bluetoothRemoteGATTCharacteristicReadValue, - callback: (args) => jsChannelErrorHandler( - args, handleBluetoothRemoteGATTCharacteristicReadValue)); - } - - // GATT server - Future> handleBluetoothRemoteGATTServerGetPrimaryService( - Map data) async { - collectLog('handleBluetoothRemoteGATTServerGetPrimaryService : $data'); - final selectedService = await getSelectedService(data['service']); - - final device = BluetoothDevice.getBluetoothDeviceFromScanResult( - state.selectedScanResult!); - final bluetoothRemoteGATTService = - BluetoothRemoteGATTService.fromBluetoothService( - device, selectedService); - return bluetoothRemoteGATTService.toMap(); - } - - Future> handleBluetoothRemoteGATTServerConnect( - Map data) async { - collectLog('handleBluetoothRemoteGATTServerConnect : $data'); - await _bluetoothUseCase.connectionHandler(state.selectedScanResult!.device); - - return BluetoothRemoteGATTServer( - device: BluetoothDevice.getBluetoothDeviceFromScanResult( - state.selectedScanResult!), - connected: true) - .toMap(); - } - - Future getSelectedService( - String uuid, - ) async { - final serviceUUID = GuidHelper.parse(uuid); - final selectedService = await BluePlusBluetoothUtils.getPrimaryService( - state.selectedScanResult!, serviceUUID); - return selectedService; - } - - // Util - blue_plus.BluetoothCharacteristic getSelectedCharacteristic( - String uuid, blue_plus.BluetoothService selectedService) { - final characteristicUUID = GuidHelper.parse(uuid); - final selectedCharacteristic = - BluePlusBluetoothUtils.getCharacteristicWithService( - selectedService, characteristicUUID); - return selectedCharacteristic; - } - - // Service - Future> - handleBluetoothRemoteGATTServiceGetCharacteristic( - Map data) async { - collectLog('handleBluetoothRemoteGATTServiceGetCharacteristic : $data'); - final targetCharacteristicUUID = data['characteristic']; - - final selectedService = await getSelectedService(data['this']); - final targetCharacteristic = - getSelectedCharacteristic(targetCharacteristicUUID, selectedService); - - final device = BluetoothDevice.getBluetoothDeviceFromScanResult( - state.selectedScanResult!); - final bluetoothRemoteGATTService = - BluetoothRemoteGATTService.fromBluetoothService( - device, selectedService); - final bluetoothRemoteGATTCharacteristic = BluetoothRemoteGATTCharacteristic( - service: bluetoothRemoteGATTService, - properties: - BluetoothCharacteristicProperties.fromCharacteristicProperties( - targetCharacteristic.properties), - uuid: targetCharacteristic.uuid.str, - value: null); - return bluetoothRemoteGATTCharacteristic.toMap(); - } - - BluetoothRemoteGATTCharacteristic getBluetoothRemoteGATTCharacteristic( - blue_plus.BluetoothCharacteristic selectedCharacteristic, - blue_plus.BluetoothService selectedService) { - final device = BluetoothDevice.getBluetoothDeviceFromScanResult( - state.selectedScanResult!); - final bluetoothRemoteGATTService = - BluetoothRemoteGATTService.fromBluetoothService( - device, selectedService); - final bluetoothRemoteGATTCharacteristic = BluetoothRemoteGATTCharacteristic( - service: bluetoothRemoteGATTService, - properties: - BluetoothCharacteristicProperties.fromCharacteristicProperties( - selectedCharacteristic.properties), - uuid: selectedCharacteristic.uuid.str, - value: null); - return bluetoothRemoteGATTCharacteristic; - } - - Future> - handleBluetoothRemoteGATTCharacteristicStartNotifications( - Map data) async { - collectLog( - 'handleBluetoothRemoteGATTCharacteristicStartNotifications : $data'); - final selectedService = await getSelectedService(data['serviceUUID']); - final selectedCharacteristic = - getSelectedCharacteristic(data['this'], selectedService); - - final bluetoothRemoteGATTCharacteristic = - getBluetoothRemoteGATTCharacteristic( - selectedCharacteristic, selectedService); - - initJSCharacteristicValueEmitter(selectedCharacteristic); - - return bluetoothRemoteGATTCharacteristic.toMap(); - } - - Future> - handleBluetoothRemoteGATTCharacteristicStopNotifications( - Map data) async { - collectLog( - 'handleBluetoothRemoteGATTCharacteristicStopNotifications : $data'); - final selectedService = await getSelectedService(data['serviceUUID']); - final selectedCharacteristic = - getSelectedCharacteristic(data['this'], selectedService); - - final bluetoothRemoteGATTCharacteristic = - getBluetoothRemoteGATTCharacteristic( - selectedCharacteristic, selectedService); - - removeJSCharacteristicValueEmitter(selectedCharacteristic); - - return bluetoothRemoteGATTCharacteristic.toMap(); - } - - Future> handleWrites(Map data, - {bool withResponse = true}) async { - collectLog('handleWrites : $data'); - final selectedService = await getSelectedService(data['serviceUUID']); - final selectedCharacteristic = - getSelectedCharacteristic(data['this'], selectedService); - final value = Uint8List.fromList(List.from( - (data['value'] as Map).values.toList())); - - collectLog('handleWrites:value $value'); - if (withResponse) { - await selectedCharacteristic.write(value); - } else { - await selectedCharacteristic.write(value, withoutResponse: true); - } - return {}; - } - - Future> - handleBluetoothRemoteGATTCharacteristicWriteValue( - Map data) async { - return handleWrites(data); - } - - Future> - handleBluetoothRemoteGATTCharacteristicWriteValueWithResponse( - Map data) async { - return handleWrites(data); - } - - Future> - handleBluetoothRemoteGATTCharacteristicWriteValueWithoutResponse( - Map data) async { - return handleWrites(data, withResponse: false); - } - - Future handleBluetoothRemoteGATTCharacteristicReadValue( - Map data) async { - collectLog('handleBluetoothRemoteGATTCharacteristicReadValue : $data'); - final selectedService = await getSelectedService(data['serviceUUID']); - final selectedCharacteristic = - getSelectedCharacteristic(data['this'], selectedService); - final value = selectedCharacteristic.lastValue; - - final uInt8List = Uint8List.fromList(value); - - collectLog('handleBluetoothRemoteGATTCharacteristicReadValue:value $value'); - collectLog( - 'handleBluetoothRemoteGATTCharacteristicReadValue:uInt8List ${uInt8List.toString()}'); - - return uInt8List; - } - - Timer? characteriticListnerTimer; - StreamSubscription>? characteristicValueStreamSubscription; + void injectAXSWalletJSChannel() => + JSChannelUtils.injectAXSWalletJSChannel(state); - void initJSCharacteristicValueEmitter( - blue_plus.BluetoothCharacteristic characteristic, - ) async { - await characteristic.setNotifyValue(true); - - characteristicValueStreamSubscription = - characteristic.lastValueStream.listen((event) async { - final uInt8List = Uint8List.fromList(event); - collectLog('characteristicValueStreamSubscription:event $event'); - collectLog( - 'characteristicValueStreamSubscription:uInt8List ${uInt8List.toString()}'); - final script = ''' - navigator.bluetooth.updateCharacteristicValue('${characteristic.uuid.str}', ${uInt8List.toString()},); - '''; - await state.webviewController!.evaluateJavascript(source: script); - }); - } - - void removeJSCharacteristicValueEmitter( - blue_plus.BluetoothCharacteristic characteristic, - ) async { - await characteristic.setNotifyValue(false); - - characteristicValueStreamSubscription?.cancel(); - } - - void injectAXSWalletJSChannel() async { - // Making It easy for accessing axs wallet - // use this way window.axs.callHandler - await state.webviewController!.evaluateJavascript( - source: JSChannelScripts.axsWalletObjectInjectScript( - JSChannelConfig.axsWalletJSObjectName)); - - await state.webviewController!.injectJavascriptFileFromAsset( - assetFilePath: 'assets/js/bluetooth/bluetooth.js'); - - // There is a gap for detecting the axs object in webview, It's intermittent after adding function structure to the scripts - Future.delayed( - const Duration(milliseconds: 500), - () async { - await state.webviewController!.evaluateJavascript( - source: JSChannelScripts.axsWalletReadyInjectScript( - JSChannelEvents.axsReadyEvent, - )); - }, - ); - } - - Future> jsChannelCronErrorHandler( - List args, - Future> Function( - Map, - AXSCronServices, - ) - callback, - ) async { - try { - Map channelDataMap; - - final channelData = args[0]; - channelDataMap = channelData as Map; - - final axsCronService = - AXSCronServicesExtension.getCronServiceFromJson(channelDataMap); - final callbackRes = await callback(channelDataMap, axsCronService); - return callbackRes; - } catch (e) { - final response = AXSJSChannelResponseModel( - status: AXSJSChannelResponseStatus.failed, - data: null, - message: e.toString()); - return response.toMap((data) => {'message': e.toString()}); - } - } - - Future jsChannelErrorHandler( - List args, - Future Function( - Map, - ) - callback, - ) async { - try { - Map channelDataMap; - - final channelData = args[0]; - channelDataMap = channelData == null - ? {} - : channelData is String - ? json.decode(channelData) as Map - : channelData as Map; - - final callbackRes = await callback(channelDataMap); - return callbackRes; - } catch (e) { - if (e is BluetoothTimeoutError) { - addError(translate('unable_to_continue_bluetooth_is_turned_off')!); - } - - final response = AXSJSChannelResponseModel( - status: AXSJSChannelResponseStatus.failed, - data: null, - message: e.toString()); - return response.toMap((data) => {'message': e}); - } - } - - // Update via functions & get data via steam & send the data via event eaach time - // ready => updateSystemInfo (service statues, mining service status, time, selected miners, camera permission location permission) - - Future> handleChangeCronTransition( - Map channelData, AXSCronServices axsCronService) async { - final axsCronService = - AXSCronServicesExtension.getCronServiceFromJson(channelData); - if (axsCronService == AXSCronServices.miningAutoClaimCron) { - ChangeCronTransitionRequestModel; - final changeCronTransitionRequestModel = - ChangeCronTransitionRequestModel.fromMap( - channelData['cron'], MiningCronServiceDataModel.fromMap); - - // Here i change the data that won't effect the - final currentDappHooksData = state.dappHooksData; - final newData = changeCronTransitionRequestModel.data; - - if (newData != null) { - final minersList = newData.minersList ?? - currentDappHooksData.minerHooks.selectedMiners; - _dAppHooksUseCase.updateMinersList(minersList); - - final newTimeOfDay = TimeOfDay.fromDateTime(newData.time!); - final currentTimeOfDay = - TimeOfDay.fromDateTime(currentDappHooksData.minerHooks.time); - - if (newData.time != null && newTimeOfDay != currentTimeOfDay) { - await minerHooksHelper.changeMinerHookTiming(newTimeOfDay); - } - } - - final miningCronServiceData = - MiningCronServiceDataModel.fromDAppHooksData( - _dAppHooksUseCase.dappHooksData.value); - - final responseData = CronServiceDataModel.fromDAppHooksData( - axsCronService, - _dAppHooksUseCase.dappHooksData.value, - miningCronServiceData); - - final response = AXSJSChannelResponseModel( - status: AXSJSChannelResponseStatus.success, - data: responseData, - message: null); - return response.toMap(miningCronServiceData.toMapWrapper); - } else { - throw 'Unknown service'; - } - } - - Future> handleBluetoothRequestDevice( - Map channelData, - ) async { - // final options = RequestDeviceOptions.fromJson(channelData['data']); - final options = RequestDeviceOptions.fromMap(channelData); - late BluetoothDevice responseDevice; - - await _bluetoothUseCase.turnOnBluetoothAndProceed(); - - // Get the options data - _bluetoothUseCase.startScanning( - withServices: options.filters != null - ? options.filters! - .expand((filter) => filter.services ?? []) - .toList() - .firstOrNull - : [], - withRemoteIds: - null, // No direct mapping in RequestDeviceOptions, adjust as necessary - withNames: options.filters != null - ? options.filters! - .where((filter) => filter.name != null) - .map((filter) => filter.name!) - .toList() - : [], - withKeywords: options.filters != null - ? options.filters! - .where((filter) => filter.namePrefix != null) - .map((filter) => filter.namePrefix!) - .toList() - : [], - withMsd: options.filters != null - ? options.filters! - .expand((filter) => filter.manufacturerData ?? []) - .toList() - .firstOrNull - : [], - withServiceData: options.filters != null - ? options.filters! - .expand((filter) => filter.serviceData ?? []) - .toList() - .firstOrNull - : [], - continuousUpdates: true, - continuousDivisor: 2, - androidUsesFineLocation: true, - ); - - final blueberryRing = await getBlueberryRing(); - _bluetoothUseCase.stopScanner(); - if (blueberryRing == null) { - return {}; - } else { - responseDevice = blueberryRing; - } - - return responseDevice.toMap(); - } - - Future getBlueberryRing() async { - loading = true; - return Future.delayed(const Duration(seconds: 3), () async { - loading = false; - BluetoothDevice? responseDevice; - final scanResults = _bluetoothUseCase.scanResults.value; - if (scanResults.length == 1) { - // only one scan results - final scanResult = scanResults.first; - state.selectedScanResult = scanResult; - } else { - // We need to let the user to choose If two or more devices of rings are available and even If empty maybe let the user to wait - final scanResult = await showBlueberryRingsBottomSheet( - context!, - ); - if (scanResult != null) { - state.selectedScanResult = scanResult; - } - } - if (state.selectedScanResult != null) { - responseDevice = BluetoothDevice.getBluetoothDeviceFromScanResult( - state.selectedScanResult!); - } - - return responseDevice; - }); - } - - Future> handleChangeCronTransitionStatusEvent( - Map channelData, - AXSCronServices axsCronService, - ) async { - if (axsCronService == AXSCronServices.miningAutoClaimCron) { - final status = channelData['cron']['status']; - - await minerHooksHelper.changeMinerHooksEnabled(status); - final miningCronServiceData = - MiningCronServiceDataModel.fromDAppHooksData( - _dAppHooksUseCase.dappHooksData.value); - - final responseData = CronServiceDataModel.fromDAppHooksData( - axsCronService, - _dAppHooksUseCase.dappHooksData.value, - miningCronServiceData); - final response = AXSJSChannelResponseModel( - status: AXSJSChannelResponseStatus.success, - message: null, - data: responseData); - return response.toMap(miningCronServiceData.toMapWrapper); - } else { - throw 'Unknown cron service'; - } - } - - Future> handleGetSystemInfoEvent( - Map channelData, - AXSCronServices axsCronService, - ) async { - if (axsCronService == AXSCronServices.miningAutoClaimCron) { - final dappHooksData = state.dappHooksData; - - final miningCronServiceData = - MiningCronServiceDataModel.fromDAppHooksData(dappHooksData); - - final responseData = CronServiceDataModel.fromDAppHooksData( - axsCronService, dappHooksData, miningCronServiceData); - final response = AXSJSChannelResponseModel( - status: AXSJSChannelResponseStatus.success, - message: null, - data: responseData, - ); - return response.toMap(miningCronServiceData.toMapWrapper); - } else { - throw 'Unknown cron service'; - } - } - - Future> handleGoToAdvancedSettingsEvent( - Map channelData, AXSCronServices axsCronService) async { - goToAdvancedSettings(); - final response = AXSJSChannelResponseModel( - status: AXSJSChannelResponseStatus.success, message: null, data: null); - return response.toMap((data) => {}); - } - - void goToAdvancedSettings() { - navigator!.push(route( - const DAppHooksPage(), - )); - } + void cancelCharacteristicListenerTimer() => + characteristicListenerTimer?.cancel(); } diff --git a/lib/features/settings/subfeatures/dapp_hooks/dapp_hooks.dart b/lib/features/settings/subfeatures/dapp_hooks/dapp_hooks.dart new file mode 100644 index 00000000..05a9423b --- /dev/null +++ b/lib/features/settings/subfeatures/dapp_hooks/dapp_hooks.dart @@ -0,0 +1,6 @@ +export 'dapp_hooks_page.dart'; +export 'dapp_hooks_presenter.dart'; +export 'dapp_hooks_state.dart'; +export 'domain/domain.dart'; +export 'utils/utils.dart'; +export 'widgets/widgets.dart'; diff --git a/lib/features/settings/subfeatures/dapp_hooks/domain/domain.dart b/lib/features/settings/subfeatures/dapp_hooks/domain/domain.dart new file mode 100644 index 00000000..434fd2b1 --- /dev/null +++ b/lib/features/settings/subfeatures/dapp_hooks/domain/domain.dart @@ -0,0 +1,2 @@ +export 'dapp_hooks_use_case.dart'; +export 'dapp_hooks_repository.dart'; diff --git a/lib/features/settings/subfeatures/dapp_hooks/utils/utils.dart b/lib/features/settings/subfeatures/dapp_hooks/utils/utils.dart index d49561ac..b4235b43 100644 --- a/lib/features/settings/subfeatures/dapp_hooks/utils/utils.dart +++ b/lib/features/settings/subfeatures/dapp_hooks/utils/utils.dart @@ -2,3 +2,4 @@ export 'miner_hooks_helper.dart'; export 'snack_bar_utils.dart'; export 'dapp_hooks_helper.dart'; export 'blueberry_ring_helper.dart'; +export 'wifi_hooks_helper.dart'; diff --git a/lib/features/settings/subfeatures/dapp_hooks/widgets/widget.dart b/lib/features/settings/subfeatures/dapp_hooks/widgets/widgets.dart similarity index 100% rename from lib/features/settings/subfeatures/dapp_hooks/widgets/widget.dart rename to lib/features/settings/subfeatures/dapp_hooks/widgets/widgets.dart From 1dd8307961fff0636bfd2ba7edf64722dd14c487 Mon Sep 17 00:00:00 2001 From: reasje Date: Wed, 7 Aug 2024 11:35:34 +0330 Subject: [PATCH 2/2] fix: Call sign message --- .../subfeatures/open_dapp/open_dapp_page.dart | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/features/dapps/subfeatures/open_dapp/open_dapp_page.dart b/lib/features/dapps/subfeatures/open_dapp/open_dapp_page.dart index e6ea06ca..56747240 100644 --- a/lib/features/dapps/subfeatures/open_dapp/open_dapp_page.dart +++ b/lib/features/dapps/subfeatures/open_dapp/open_dapp_page.dart @@ -125,8 +125,28 @@ class OpenAppPage extends HookConsumerWidget { url: url); break; case EIP1193.signMessage: + Map object = params["object"]; + presenter.signMessage( + object: object, + cancel: () { + controller?.cancel(id); + }, + success: (idHash) { + controller?.sendResult(idHash, id); + }, + ); break; case EIP1193.signPersonalMessage: + Map object = params["object"]; + presenter.signMessage( + object: object, + cancel: () { + controller?.cancel(id); + }, + success: (idHash) { + controller?.sendResult(idHash, id); + }, + ); break; case EIP1193.signTypedMessage: Map object = params["object"];