:drunk: Werid miniapp runtime
This commit is contained in:
199
lib/modular/miniapp_loader.dart
Normal file
199
lib/modular/miniapp_loader.dart
Normal file
@@ -0,0 +1,199 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_eval/flutter_eval.dart';
|
||||
import 'package:island/modular/interface.dart';
|
||||
import 'package:island/pods/plugin_registry.dart';
|
||||
import 'package:island/talker.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/dart_miniapp_display.dart';
|
||||
import 'package:island/widgets/miniapp_modal.dart';
|
||||
|
||||
typedef ProgressCallback = void Function(double progress, String message);
|
||||
|
||||
final flutterEvalPlugin = FlutterEvalPlugin();
|
||||
|
||||
class MiniappLoader {
|
||||
static Future<void> loadMiniappFromSource(
|
||||
BuildContext context,
|
||||
WidgetRef ref, {
|
||||
ProgressCallback? onProgress,
|
||||
}) async {
|
||||
try {
|
||||
final result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.custom,
|
||||
allowedExtensions: ['dart', 'evc'],
|
||||
withData: true,
|
||||
);
|
||||
|
||||
if (result == null || result.files.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final file = result.files.first;
|
||||
final fileName = file.name;
|
||||
|
||||
if (fileName.endsWith('.dart')) {
|
||||
await _loadDartSource(context, file, fileName);
|
||||
} else if (fileName.endsWith('.evc')) {
|
||||
await _loadBytecodeFile(context, ref, file, fileName, onProgress);
|
||||
} else {
|
||||
showErrorAlert('Unsupported file type. Please use .dart or .evc files');
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
talker.error('[MiniappLoader] Failed to load from source', e, stackTrace);
|
||||
showErrorAlert('Failed to load miniapp: $e');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _loadDartSource(
|
||||
BuildContext context,
|
||||
PlatformFile file,
|
||||
String fileName,
|
||||
) async {
|
||||
try {
|
||||
final sourceCode = file.bytes;
|
||||
|
||||
if (sourceCode == null) {
|
||||
showErrorAlert('Unable to read file contents');
|
||||
return;
|
||||
}
|
||||
|
||||
final package = _generatePackageName(fileName);
|
||||
|
||||
await showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
useSafeArea: true,
|
||||
builder: (modalContext) {
|
||||
return DartMiniappDisplay(
|
||||
package: package,
|
||||
sourceCode: String.fromCharCodes(sourceCode),
|
||||
);
|
||||
},
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
talker.error('[MiniappLoader] Failed to load dart source', e, stackTrace);
|
||||
showErrorAlert('Failed to load miniapp: $e');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _loadBytecodeFile(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
PlatformFile file,
|
||||
String fileName,
|
||||
ProgressCallback? onProgress,
|
||||
) async {
|
||||
try {
|
||||
final bytecode = file.bytes;
|
||||
|
||||
if (bytecode == null) {
|
||||
showErrorAlert('Unable to read file contents');
|
||||
return;
|
||||
}
|
||||
|
||||
if (onProgress != null) {
|
||||
onProgress(0.3, 'Saving bytecode...');
|
||||
}
|
||||
|
||||
final registryNotifier = ref.read(pluginRegistryProvider.notifier);
|
||||
final cacheDirPath = await registryNotifier.getMiniAppCacheDirectory();
|
||||
final appId = _generateAppId(fileName);
|
||||
final cachePath = '$cacheDirPath/$appId.evc';
|
||||
|
||||
final cacheFile = File(cachePath);
|
||||
await cacheFile.writeAsBytes(bytecode);
|
||||
|
||||
if (onProgress != null) {
|
||||
onProgress(0.5, 'Loading miniapp...');
|
||||
}
|
||||
|
||||
final metadata = MiniAppMetadata(
|
||||
id: appId,
|
||||
name: appId,
|
||||
version: '0.0.1-dev',
|
||||
description: 'Loaded from file: $fileName',
|
||||
downloadUrl: '',
|
||||
localCachePath: cachePath,
|
||||
lastUpdated: DateTime.now(),
|
||||
isEnabled: true,
|
||||
);
|
||||
|
||||
final success = await registryNotifier.loadMiniappFromCache(
|
||||
metadata,
|
||||
onProgress: onProgress,
|
||||
);
|
||||
|
||||
if (success && context.mounted) {
|
||||
if (onProgress != null) {
|
||||
onProgress(1.0, 'Loaded successfully');
|
||||
}
|
||||
showInfoAlert('Miniapp loaded successfully from file', 'Load Complete');
|
||||
await showMiniappModal(context, ref, appId);
|
||||
} else {
|
||||
showErrorAlert('Failed to load miniapp');
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
talker.error(
|
||||
'[MiniappLoader] Failed to load bytecode file',
|
||||
e,
|
||||
stackTrace,
|
||||
);
|
||||
showErrorAlert('Failed to load miniapp: $e');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> loadMiniappFromUrl(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
String url, {
|
||||
ProgressCallback? onProgress,
|
||||
}) async {
|
||||
try {
|
||||
if (onProgress != null) {
|
||||
onProgress(0.1, 'Downloading from URL...');
|
||||
}
|
||||
|
||||
final registryNotifier = ref.read(pluginRegistryProvider.notifier);
|
||||
final success = await registryNotifier.loadMiniappFromUrl(
|
||||
url,
|
||||
onProgress: onProgress,
|
||||
);
|
||||
|
||||
if (success) {
|
||||
if (onProgress != null) {
|
||||
onProgress(1.0, 'Loaded successfully');
|
||||
}
|
||||
showInfoAlert('Miniapp loaded successfully from URL', 'Load Complete');
|
||||
|
||||
if (context.mounted) {
|
||||
final appId = registryNotifier.generateAppIdFromUrl(url);
|
||||
await showMiniappModal(context, ref, appId);
|
||||
}
|
||||
} else {
|
||||
showErrorAlert('Failed to load miniapp');
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
talker.error('[MiniappLoader] Failed to load from URL', e, stackTrace);
|
||||
showErrorAlert('Failed to load miniapp: $e');
|
||||
}
|
||||
}
|
||||
|
||||
static String _generateAppId(String fileName) {
|
||||
final baseName = fileName
|
||||
.replaceAll('.dart', '')
|
||||
.replaceAll('.evc', '')
|
||||
.replaceAll(RegExp(r'[^\w-]'), '_');
|
||||
return 'dev_${baseName}_${DateTime.now().millisecondsSinceEpoch}';
|
||||
}
|
||||
|
||||
static String _generatePackageName(String fileName) {
|
||||
final baseName = fileName
|
||||
.replaceAll('.dart', '')
|
||||
.replaceAll(RegExp(r'[^\w-]'), '_');
|
||||
return 'dev_${baseName}_${DateTime.now().millisecondsSinceEpoch}';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user