:drunk: Wrote some useless code
This commit is contained in:
@@ -12,6 +12,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:image_picker_android/image_picker_android.dart';
|
import 'package:image_picker_android/image_picker_android.dart';
|
||||||
|
import 'package:island/modular/miniapp_loader.dart';
|
||||||
import 'package:island/services/analytics_service.dart';
|
import 'package:island/services/analytics_service.dart';
|
||||||
import 'package:island/talker.dart';
|
import 'package:island/talker.dart';
|
||||||
import 'package:island/firebase_options.dart';
|
import 'package:island/firebase_options.dart';
|
||||||
@@ -188,7 +189,7 @@ void main() async {
|
|||||||
|
|
||||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||||
FlutterNativeSplash.remove();
|
FlutterNativeSplash.remove();
|
||||||
talker.info("[SplashScreen] Now hiding the splash screen...");
|
talker.info("[SplashScreen] Now hiding splash screen...");
|
||||||
}
|
}
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ Payment API (`lib/modular/api/payment.dart`) provides a simple interface for min
|
|||||||
import 'package:island/modular/api/payment.dart';
|
import 'package:island/modular/api/payment.dart';
|
||||||
|
|
||||||
// Get singleton instance
|
// Get singleton instance
|
||||||
final paymentAPI = PaymentAPI.instance;
|
final paymentApi = PaymentApi.instance;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Creating a Payment Order
|
### Creating a Payment Order
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
final order = await paymentAPI.createOrder(
|
final order = await paymentApi.createOrder(
|
||||||
CreateOrderRequest(
|
CreateOrderRequest(
|
||||||
amount: 1000, // $10.00 in cents
|
amount: 1000, // $10.00 in cents
|
||||||
currency: 'USD',
|
currency: 'USD',
|
||||||
@@ -35,7 +35,7 @@ final orderId = order.id;
|
|||||||
### Processing Payment with Overlay
|
### Processing Payment with Overlay
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
final result = await paymentAPI.processPaymentWithOverlay(
|
final result = await paymentApi.processPaymentWithOverlay(
|
||||||
context: context,
|
context: context,
|
||||||
createOrderRequest: CreateOrderRequest(
|
createOrderRequest: CreateOrderRequest(
|
||||||
amount: 1000,
|
amount: 1000,
|
||||||
@@ -55,7 +55,7 @@ if (result.success) {
|
|||||||
### Processing Existing Payment
|
### Processing Existing Payment
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
final result = await paymentAPI.processPaymentWithOverlay(
|
final result = await paymentApi.processPaymentWithOverlay(
|
||||||
context: context,
|
context: context,
|
||||||
request: PaymentRequest(
|
request: PaymentRequest(
|
||||||
orderId: 'order_123',
|
orderId: 'order_123',
|
||||||
@@ -71,7 +71,7 @@ final result = await paymentAPI.processPaymentWithOverlay(
|
|||||||
### Processing Payment Without Overlay (Direct)
|
### Processing Payment Without Overlay (Direct)
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
final result = await paymentAPI.processDirectPayment(
|
final result = await paymentApi.processDirectPayment(
|
||||||
PaymentRequest(
|
PaymentRequest(
|
||||||
orderId: 'order_123',
|
orderId: 'order_123',
|
||||||
amount: 1000,
|
amount: 1000,
|
||||||
@@ -300,33 +300,106 @@ class MiniAppPayment extends StatelessWidget {
|
|||||||
|
|
||||||
## Integration with flutter_eval
|
## Integration with flutter_eval
|
||||||
|
|
||||||
To expose this API to mini-apps loaded via flutter_eval:
|
### Current Status
|
||||||
|
|
||||||
1. Add to plugin registry:
|
**FlutterEval Plugin Initialization: ✅ Complete**
|
||||||
|
- `flutterEvalPlugin` is properly initialized in `lib/modular/miniapp_loader.dart`
|
||||||
|
- Initialized from `main()` during app startup
|
||||||
|
- Plugin is shared between `miniapp_loader.dart` and `lib/modular/registry.dart`
|
||||||
|
- Runtime adds plugin when loading miniapps: `runtime.addPlugin(flutterEvalPlugin)`
|
||||||
|
|
||||||
|
**PaymentBridgePlugin: ✅ Created but not yet registered**
|
||||||
|
- Located at `lib/modular/api/payment_bridge.dart`
|
||||||
|
- Provides simplified wrapper around PaymentApi
|
||||||
|
- Designed for easier integration with eval bridge
|
||||||
|
|
||||||
|
**Full Eval Bridge: ⚠️ Requires Additional Setup**
|
||||||
|
To expose PaymentApi to miniapps through eval, you need to:
|
||||||
|
|
||||||
|
1. **Create EvalPlugin Implementation**
|
||||||
|
```dart
|
||||||
|
// In a new file: lib/modular/api/payment_eval_plugin.dart
|
||||||
|
import 'package:dart_eval/dart_eval_bridge.dart';
|
||||||
|
import 'package:island/modular/api/payment.dart';
|
||||||
|
import 'package:island/modular/api/payment_bridge.dart';
|
||||||
|
|
||||||
|
class PaymentEvalPlugin implements EvalPlugin {
|
||||||
|
@override
|
||||||
|
String get identifier => 'package:island/modular/api/payment.dart';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void configureForCompile(BridgeDeclarationRegistry registry) {
|
||||||
|
// Define bridge classes for PaymentRequest, PaymentResult, CreateOrderRequest
|
||||||
|
// Requires using @Bind() annotations or manual bridge definition
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void configureForRuntime(Runtime runtime) {
|
||||||
|
// Register functions that miniapps can call
|
||||||
|
// This requires bridge wrapper classes to be generated or created manually
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Generate or Create Bridge Code**
|
||||||
|
- Option A: Use `dart_eval_annotation` package with `@Bind()` annotations
|
||||||
|
- Add annotations to PaymentApi classes
|
||||||
|
- Run `dart run build_runner build` to generate bridge code
|
||||||
|
- Option B: Manually create bridge wrapper classes
|
||||||
|
- Define `$PaymentRequest`, `$PaymentResult`, etc.
|
||||||
|
- Implement `$Instance` interface for each
|
||||||
|
- Register bridge functions in `configureForRuntime`
|
||||||
|
|
||||||
|
3. **Register Plugin in Registry**
|
||||||
```dart
|
```dart
|
||||||
// In lib/modular/registry.dart
|
// In lib/modular/registry.dart
|
||||||
import 'package:island/modular/api/payment.dart';
|
import 'package:island/modular/api/payment_eval_plugin.dart';
|
||||||
|
|
||||||
Future<PluginLoadResult> loadMiniApp(...) async {
|
Future<PluginLoadResult> loadMiniApp(...) async {
|
||||||
// ... existing code ...
|
// ... existing code ...
|
||||||
|
|
||||||
final runtime = Runtime(ByteData.sublistView(bytecode));
|
final runtime = Runtime(ByteData.sublistView(bytecode));
|
||||||
runtime.addPlugin(flutterEvalPlugin);
|
runtime.addPlugin(flutterEvalPlugin);
|
||||||
|
runtime.addPlugin(PaymentEvalPlugin()); // Add payment API plugin
|
||||||
// Register Payment API
|
|
||||||
final paymentAPI = PaymentAPI.instance;
|
|
||||||
// You'll need to create a bridge to expose this to eval
|
|
||||||
|
|
||||||
// ... rest of loading code
|
// ... rest of loading code
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Mini-app can access API:
|
4. **Mini-app Usage**
|
||||||
```dart
|
```dart
|
||||||
// mini_app/main.dart
|
// mini_app/main.dart
|
||||||
final paymentAPI = PaymentAPI.instance; // Will be exposed via bridge
|
// Once bridge is complete, miniapps can access:
|
||||||
|
final paymentBridge = PaymentBridgePlugin.instance;
|
||||||
|
final result = await paymentBridge.processDirectPayment(
|
||||||
|
orderId: 'order_123',
|
||||||
|
amount: 1000,
|
||||||
|
currency: 'USD',
|
||||||
|
pinCode: '123456',
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Simplified Alternative
|
||||||
|
|
||||||
|
For quick testing without full bridge setup, miniapps can use the example pattern:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// Simulate API calls in miniapp for testing
|
||||||
|
Future<void> _testPayment() async {
|
||||||
|
setState(() => _isLoading = true);
|
||||||
|
try {
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
setState(() => _status = 'Payment successful!');
|
||||||
|
} catch (e) {
|
||||||
|
setState(() => _status = 'Payment failed: $e');
|
||||||
|
} finally {
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This pattern is demonstrated in `packages/miniapp-example/lib/main.dart`.
|
||||||
|
|
||||||
## Security Considerations
|
## Security Considerations
|
||||||
|
|
||||||
- **Never hardcode PIN codes**: Always get from user input
|
- **Never hardcode PIN codes**: Always get from user input
|
||||||
|
|||||||
@@ -59,16 +59,16 @@ sealed class CreateOrderRequest with _$CreateOrderRequest {
|
|||||||
_$CreateOrderRequestFromJson(json);
|
_$CreateOrderRequestFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
class PaymentAPI {
|
class PaymentApi {
|
||||||
static PaymentAPI? _instance;
|
static PaymentApi? _instance;
|
||||||
late Dio _dio;
|
late Dio? _dio;
|
||||||
late String _serverUrl;
|
late String _serverUrl;
|
||||||
String? _token;
|
String? _token;
|
||||||
|
|
||||||
PaymentAPI._internal();
|
PaymentApi._internal();
|
||||||
|
|
||||||
static PaymentAPI get instance {
|
static PaymentApi get instance {
|
||||||
_instance ??= PaymentAPI._internal();
|
_instance ??= PaymentApi._internal();
|
||||||
return _instance!;
|
return _instance!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ class PaymentAPI {
|
|||||||
|
|
||||||
final tokenString = prefs.getString(kTokenPairStoreKey);
|
final tokenString = prefs.getString(kTokenPairStoreKey);
|
||||||
if (tokenString != null) {
|
if (tokenString != null) {
|
||||||
final appToken = AppToken.fromJson(jsonDecode(tokenString!));
|
final appToken = AppToken.fromJson(jsonDecode(tokenString));
|
||||||
_token = await getToken(appToken);
|
_token = await getToken(appToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ class PaymentAPI {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
_dio.interceptors.add(
|
_dio?.interceptors.add(
|
||||||
InterceptorsWrapper(
|
InterceptorsWrapper(
|
||||||
onRequest: (options, handler) async {
|
onRequest: (options, handler) async {
|
||||||
if (_token != null) {
|
if (_token != null) {
|
||||||
@@ -113,7 +113,8 @@ class PaymentAPI {
|
|||||||
await _initialize();
|
await _initialize();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post('/pass/orders', data: request.toJson());
|
if (_dio == null) return null;
|
||||||
|
final response = await _dio!.post('/pass/orders', data: request.toJson());
|
||||||
|
|
||||||
return SnWalletOrder.fromJson(response.data);
|
return SnWalletOrder.fromJson(response.data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -129,7 +130,8 @@ class PaymentAPI {
|
|||||||
await _initialize();
|
await _initialize();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
if (_dio == null) return null;
|
||||||
|
final response = await _dio!.post(
|
||||||
'/pass/orders/$orderId/pay',
|
'/pass/orders/$orderId/pay',
|
||||||
data: {'pin_code': pinCode},
|
data: {'pin_code': pinCode},
|
||||||
);
|
);
|
||||||
@@ -164,13 +166,13 @@ class PaymentAPI {
|
|||||||
order = SnWalletOrder(
|
order = SnWalletOrder(
|
||||||
id: request!.orderId,
|
id: request!.orderId,
|
||||||
status: 0,
|
status: 0,
|
||||||
currency: request!.currency,
|
currency: request.currency,
|
||||||
remarks: request!.remarks,
|
remarks: request.remarks,
|
||||||
appIdentifier: 'mini-app',
|
appIdentifier: 'mini-app',
|
||||||
meta: {},
|
meta: {},
|
||||||
amount: request!.amount,
|
amount: request.amount,
|
||||||
expiredAt: DateTime.now().add(const Duration(hours: 1)),
|
expiredAt: DateTime.now().add(const Duration(hours: 1)),
|
||||||
payeeWalletId: request!.payeeWalletId,
|
payeeWalletId: request.payeeWalletId,
|
||||||
transactionId: null,
|
transactionId: null,
|
||||||
issuerAppId: null,
|
issuerAppId: null,
|
||||||
createdAt: DateTime.now(),
|
createdAt: DateTime.now(),
|
||||||
@@ -179,6 +181,7 @@ class PaymentAPI {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!context.mounted) throw PaymentResult(success: false);
|
||||||
final result = await PaymentOverlay.show(
|
final result = await PaymentOverlay.show(
|
||||||
context: context,
|
context: context,
|
||||||
order: order,
|
order: order,
|
||||||
@@ -198,9 +201,7 @@ class PaymentAPI {
|
|||||||
return PaymentResult(
|
return PaymentResult(
|
||||||
success: false,
|
success: false,
|
||||||
error: errorMessage,
|
error: errorMessage,
|
||||||
errorCode: e is DioException
|
errorCode: e is DioException ? e.response?.statusCode.toString() : null,
|
||||||
? (e as DioException).response?.statusCode.toString()
|
|
||||||
: null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,16 +233,14 @@ class PaymentAPI {
|
|||||||
return PaymentResult(
|
return PaymentResult(
|
||||||
success: false,
|
success: false,
|
||||||
error: errorMessage,
|
error: errorMessage,
|
||||||
errorCode: e is DioException
|
errorCode: e is DioException ? e.response?.statusCode.toString() : null,
|
||||||
? (e as DioException).response?.statusCode.toString()
|
|
||||||
: null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _parsePaymentError(dynamic error) {
|
String _parsePaymentError(dynamic error) {
|
||||||
if (error is DioException) {
|
if (error is DioException) {
|
||||||
final dioError = error as DioException;
|
final dioError = error;
|
||||||
|
|
||||||
if (dioError.response?.statusCode == 403 ||
|
if (dioError.response?.statusCode == 403 ||
|
||||||
dioError.response?.statusCode == 401) {
|
dioError.response?.statusCode == 401) {
|
||||||
@@ -261,17 +260,18 @@ class PaymentAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateServerUrl() async {
|
Future<void> updateServerUrl() async {
|
||||||
|
if (_dio == null) return;
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
_serverUrl =
|
_serverUrl =
|
||||||
prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
|
prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
|
||||||
_dio.options.baseUrl = _serverUrl;
|
_dio!.options.baseUrl = _serverUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateToken() async {
|
Future<void> updateToken() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final tokenString = prefs.getString(kTokenPairStoreKey);
|
final tokenString = prefs.getString(kTokenPairStoreKey);
|
||||||
if (tokenString != null) {
|
if (tokenString != null) {
|
||||||
final appToken = AppToken.fromJson(jsonDecode(tokenString!));
|
final appToken = AppToken.fromJson(jsonDecode(tokenString));
|
||||||
_token = await getToken(appToken);
|
_token = await getToken(appToken);
|
||||||
} else {
|
} else {
|
||||||
_token = null;
|
_token = null;
|
||||||
@@ -279,6 +279,6 @@ class PaymentAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_dio.close();
|
_dio?.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
82
lib/modular/api/payment_bridge.dart
Normal file
82
lib/modular/api/payment_bridge.dart
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import 'package:island/modular/api/payment.dart';
|
||||||
|
|
||||||
|
class PaymentBridgePlugin {
|
||||||
|
static final instance = PaymentBridgePlugin._internal();
|
||||||
|
PaymentBridgePlugin._internal();
|
||||||
|
|
||||||
|
Future<PaymentResult> createOrder({
|
||||||
|
required int amount,
|
||||||
|
required String currency,
|
||||||
|
String? remarks,
|
||||||
|
String? payeeWalletId,
|
||||||
|
String? appIdentifier,
|
||||||
|
Map<String, dynamic>? meta,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final request = CreateOrderRequest(
|
||||||
|
amount: amount,
|
||||||
|
currency: currency,
|
||||||
|
remarks: remarks,
|
||||||
|
payeeWalletId: payeeWalletId,
|
||||||
|
appIdentifier: appIdentifier,
|
||||||
|
meta: meta ?? {},
|
||||||
|
);
|
||||||
|
final order = await PaymentApi.instance.createOrder(request);
|
||||||
|
if (order == null) {
|
||||||
|
return PaymentResult(success: false, error: 'Failed to create order');
|
||||||
|
}
|
||||||
|
return PaymentResult(success: true, order: order);
|
||||||
|
} catch (e) {
|
||||||
|
return PaymentResult(success: false, error: e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<PaymentResult> processPayment({
|
||||||
|
required String orderId,
|
||||||
|
required String pinCode,
|
||||||
|
bool enableBiometric = true,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final order = await PaymentApi.instance.processPayment(
|
||||||
|
orderId: orderId,
|
||||||
|
pinCode: pinCode,
|
||||||
|
enableBiometric: enableBiometric,
|
||||||
|
);
|
||||||
|
if (order == null) {
|
||||||
|
return PaymentResult(
|
||||||
|
success: false,
|
||||||
|
error: 'Failed to process payment',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return PaymentResult(success: true, order: order);
|
||||||
|
} catch (e) {
|
||||||
|
return PaymentResult(success: false, error: e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<PaymentResult> processDirectPayment({
|
||||||
|
required String orderId,
|
||||||
|
required int amount,
|
||||||
|
required String currency,
|
||||||
|
required String pinCode,
|
||||||
|
String? remarks,
|
||||||
|
String? payeeWalletId,
|
||||||
|
bool enableBiometric = true,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final request = PaymentRequest(
|
||||||
|
orderId: orderId,
|
||||||
|
amount: amount,
|
||||||
|
currency: currency,
|
||||||
|
remarks: remarks,
|
||||||
|
payeeWalletId: payeeWalletId,
|
||||||
|
pinCode: pinCode,
|
||||||
|
showOverlay: false,
|
||||||
|
enableBiometric: enableBiometric,
|
||||||
|
);
|
||||||
|
return await PaymentApi.instance.processDirectPayment(request);
|
||||||
|
} catch (e) {
|
||||||
|
return PaymentResult(success: false, error: e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ import 'dart:io';
|
|||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_eval/flutter_eval.dart';
|
|
||||||
import 'package:island/modular/interface.dart';
|
import 'package:island/modular/interface.dart';
|
||||||
import 'package:island/pods/plugin_registry.dart';
|
import 'package:island/modular/registry.dart';
|
||||||
|
import 'package:island/pods/modular/plugin_registry.dart';
|
||||||
import 'package:island/talker.dart';
|
import 'package:island/talker.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/dart_miniapp_display.dart';
|
import 'package:island/widgets/dart_miniapp_display.dart';
|
||||||
@@ -13,8 +13,6 @@ import 'package:island/widgets/miniapp_modal.dart';
|
|||||||
|
|
||||||
typedef ProgressCallback = void Function(double progress, String message);
|
typedef ProgressCallback = void Function(double progress, String message);
|
||||||
|
|
||||||
final flutterEvalPlugin = FlutterEvalPlugin();
|
|
||||||
|
|
||||||
class MiniappLoader {
|
class MiniappLoader {
|
||||||
static Future<void> loadMiniappFromSource(
|
static Future<void> loadMiniappFromSource(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
@@ -35,6 +33,7 @@ class MiniappLoader {
|
|||||||
final file = result.files.first;
|
final file = result.files.first;
|
||||||
final fileName = file.name;
|
final fileName = file.name;
|
||||||
|
|
||||||
|
if (!context.mounted) return;
|
||||||
if (fileName.endsWith('.dart')) {
|
if (fileName.endsWith('.dart')) {
|
||||||
await _loadDartSource(context, file, fileName);
|
await _loadDartSource(context, file, fileName);
|
||||||
} else if (fileName.endsWith('.evc')) {
|
} else if (fileName.endsWith('.evc')) {
|
||||||
|
|||||||
@@ -82,7 +82,14 @@ class PluginRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final runtime = Runtime(ByteData.sublistView(bytecode));
|
final runtime = Runtime(ByteData.sublistView(bytecode));
|
||||||
|
if (flutterEvalPlugin != null) {
|
||||||
|
try {
|
||||||
runtime.addPlugin(flutterEvalPlugin);
|
runtime.addPlugin(flutterEvalPlugin);
|
||||||
|
talker.info('[PluginRegistry] FlutterEvalPlugin added to runtime');
|
||||||
|
} catch (e) {
|
||||||
|
talker.error('[PluginRegistry] Failed to add FlutterEvalPlugin: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (onProgress != null) {
|
if (onProgress != null) {
|
||||||
onProgress(0.8, 'Building entry widget...');
|
onProgress(0.8, 'Building entry widget...');
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ class PluginRegistryNotifier extends _$PluginRegistryNotifier {
|
|||||||
} else if (!enabled && appMetadata.isEnabled == true) {
|
} else if (!enabled && appMetadata.isEnabled == true) {
|
||||||
_registry.unloadMiniApp(id);
|
_registry.unloadMiniApp(id);
|
||||||
final updatedMetadata = appMetadata.copyWith(isEnabled: false);
|
final updatedMetadata = appMetadata.copyWith(isEnabled: false);
|
||||||
final evaluatedMiniApp = miniApp as EvaluatedMiniApp;
|
final evaluatedMiniApp = miniApp;
|
||||||
final updatedMiniApp = EvaluatedMiniApp(
|
final updatedMiniApp = EvaluatedMiniApp(
|
||||||
appMetadata: updatedMetadata,
|
appMetadata: updatedMetadata,
|
||||||
entryFunction: evaluatedMiniApp.entryFunction,
|
entryFunction: evaluatedMiniApp.entryFunction,
|
||||||
@@ -261,11 +261,11 @@ class FileListScreen extends HookConsumerWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
builder: (context) => SheetScaffold(
|
builder: (context) => SheetScaffold(
|
||||||
|
titleText: 'Usage Overview',
|
||||||
child: UsageOverviewWidget(
|
child: UsageOverviewWidget(
|
||||||
usage: usage,
|
usage: usage,
|
||||||
quota: quota,
|
quota: quota,
|
||||||
).padding(horizontal: 8, vertical: 16),
|
).padding(horizontal: 8, vertical: 16),
|
||||||
titleText: 'Usage Overview',
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:island/modular/miniapp_loader.dart';
|
import 'package:island/modular/miniapp_loader.dart';
|
||||||
import 'package:island/pods/message.dart';
|
import 'package:island/pods/message.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/pods/plugin_registry.dart';
|
import 'package:island/pods/modular/plugin_registry.dart';
|
||||||
import 'package:island/services/update_service.dart';
|
import 'package:island/services/update_service.dart';
|
||||||
import 'package:island/widgets/alert.dart';
|
import 'package:island/widgets/alert.dart';
|
||||||
import 'package:island/widgets/content/network_status_sheet.dart';
|
import 'package:island/widgets/content/network_status_sheet.dart';
|
||||||
|
|||||||
@@ -102,16 +102,16 @@ class FileListView extends HookConsumerWidget {
|
|||||||
useEffect(() {
|
useEffect(() {
|
||||||
// Sync query, order, and orderDesc filters
|
// Sync query, order, and orderDesc filters
|
||||||
if (mode.value == FileListMode.unindexed) {
|
if (mode.value == FileListMode.unindexed) {
|
||||||
unindexedNotifier.setQuery(this.query.value);
|
unindexedNotifier.setQuery(query.value);
|
||||||
unindexedNotifier.setOrder(order.value);
|
unindexedNotifier.setOrder(order.value);
|
||||||
unindexedNotifier.setOrderDesc(orderDesc.value);
|
unindexedNotifier.setOrderDesc(orderDesc.value);
|
||||||
} else {
|
} else {
|
||||||
cloudNotifier.setQuery(this.query.value);
|
cloudNotifier.setQuery(query.value);
|
||||||
cloudNotifier.setOrder(order.value);
|
cloudNotifier.setOrder(order.value);
|
||||||
cloudNotifier.setOrderDesc(orderDesc.value);
|
cloudNotifier.setOrderDesc(orderDesc.value);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [this.query.value, order.value, orderDesc.value, mode.value]);
|
}, [query.value, order.value, orderDesc.value, mode.value]);
|
||||||
|
|
||||||
final isRefreshing = ref.watch(
|
final isRefreshing = ref.watch(
|
||||||
mode.value == FileListMode.normal
|
mode.value == FileListMode.normal
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:island/modular/interface.dart';
|
import 'package:island/modular/interface.dart';
|
||||||
import 'package:island/pods/plugin_registry.dart';
|
import 'package:island/pods/modular/plugin_registry.dart';
|
||||||
|
|
||||||
Future<void> showMiniappModal(
|
Future<void> showMiniappModal(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
|
|||||||
@@ -6,7 +6,13 @@ import 'package:flutter/material.dart';
|
|||||||
/// In a real mini-app, PaymentAPI would be accessed through
|
/// In a real mini-app, PaymentAPI would be accessed through
|
||||||
/// eval bridge provided by flutter_eval.
|
/// eval bridge provided by flutter_eval.
|
||||||
Widget buildEntry() {
|
Widget buildEntry() {
|
||||||
return const PaymentDemoHome();
|
Widget result;
|
||||||
|
try {
|
||||||
|
result = const PaymentDemoHome();
|
||||||
|
} catch (e) {
|
||||||
|
result = Center(child: Text('Error: $e'));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PaymentDemoHome extends StatefulWidget {
|
class PaymentDemoHome extends StatefulWidget {
|
||||||
@@ -18,6 +24,7 @@ class PaymentDemoHome extends StatefulWidget {
|
|||||||
|
|
||||||
class PaymentDemoHomeState extends State<PaymentDemoHome> {
|
class PaymentDemoHomeState extends State<PaymentDemoHome> {
|
||||||
String _status = 'Ready';
|
String _status = 'Ready';
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
void _updateStatus(String status) {
|
void _updateStatus(String status) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -25,16 +32,45 @@ class PaymentDemoHomeState extends State<PaymentDemoHome> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _createOrder() {
|
Future<void> _createOrder() async {
|
||||||
_updateStatus('Order created! Order ID: ORD-001');
|
setState(() => _isLoading = true);
|
||||||
|
try {
|
||||||
|
_updateStatus('Creating order...');
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
_updateStatus('Order created! Order ID: ORD-001\nAmount: 100 USD');
|
||||||
|
} catch (e) {
|
||||||
|
_updateStatus('Failed to create order: $e');
|
||||||
|
} finally {
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _processPaymentWithOverlay() {
|
Future<void> _processPaymentWithOverlay() async {
|
||||||
_updateStatus('Payment completed successfully!');
|
setState(() => _isLoading = true);
|
||||||
|
try {
|
||||||
|
_updateStatus('Processing payment with overlay...');
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
_updateStatus(
|
||||||
|
'Payment completed successfully!\nTransaction ID: TXN-12345',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
_updateStatus('Payment failed: $e');
|
||||||
|
} finally {
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _processDirectPayment() {
|
Future<void> _processDirectPayment() async {
|
||||||
_updateStatus('Direct payment successful!');
|
setState(() => _isLoading = true);
|
||||||
|
try {
|
||||||
|
_updateStatus('Processing direct payment...');
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
_updateStatus('Direct payment successful!\nTransaction ID: TXN-67890');
|
||||||
|
} catch (e) {
|
||||||
|
_updateStatus('Payment failed: $e');
|
||||||
|
} finally {
|
||||||
|
setState(() => _isLoading = false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -70,7 +106,11 @@ class PaymentDemoHomeState extends State<PaymentDemoHome> {
|
|||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(_status, textAlign: TextAlign.center),
|
Text(
|
||||||
|
_status,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -79,15 +119,23 @@ class PaymentDemoHomeState extends State<PaymentDemoHome> {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () => _createOrder(),
|
onPressed: _isLoading ? null : () => _createOrder(),
|
||||||
child: const Text('Create Order'),
|
child: _isLoading
|
||||||
|
? const SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: Text('Wait'),
|
||||||
|
)
|
||||||
|
: const Text('Create Order'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () => _processPaymentWithOverlay(),
|
onPressed: _isLoading
|
||||||
|
? null
|
||||||
|
: () => _processPaymentWithOverlay(),
|
||||||
child: const Text('Pay with Overlay'),
|
child: const Text('Pay with Overlay'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -95,7 +143,7 @@ class PaymentDemoHomeState extends State<PaymentDemoHome> {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () => _processDirectPayment(),
|
onPressed: _isLoading ? null : () => _processDirectPayment(),
|
||||||
child: const Text('Direct Payment'),
|
child: const Text('Direct Payment'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user