feat: enhance deep link handling for install extensions (#1249)

This commit is contained in:
Levi authored and GitHub committed 2026-01-21 20:23:04 +08:00
1 parent d90af0b9d1
commit af1c8b2719
7 files changed
+77 -51

No files matched your search

@@ -20,6 +20,7 @@ import 'package:window_manager/window_manager.dart';
import '../../../../api/api.dart';
import '../../../../api/model/create_task.dart';
import '../../../../api/model/downloader_config.dart';
import '../../../../api/model/install_extension.dart';
import '../../../../api/model/request.dart';
import '../../../../api/model/result.dart';
import '../../../../core/common/start_config.dart';
@@ -293,7 +294,7 @@ class AppController extends GetxController with WindowListener, TrayListener {
final jsonMeta = jsonDecode(meta);
final silent = jsonMeta['silent'] as bool? ?? false;
final params = await ctx.readText();
final createTaskParams = _decodeToCreatTaskParams(params);
final createTaskParams = CreateTask.fromJson(_decodeParams(params));
if (!silent) {
await windowManager.show();
_handleToCreate0(createTaskParams);
@@ -382,6 +383,7 @@ class AppController extends GetxController with WindowListener, TrayListener {
Future<void> _handleDeepLink(Uri uri) async {
if (uri.scheme == "gopeed") {
// gopeed:///create?params=eyJyZXEiOnsidXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9maWxlLnR4dCJ9fQ==
if (uri.path == "/create") {
final params = uri.queryParameters["params"];
if (params?.isNotEmpty == true) {
@@ -391,6 +393,16 @@ class AppController extends GetxController with WindowListener, TrayListener {
Get.rootDelegate.offAndToNamed(Routes.CREATE);
return;
}
// gopeed:///extension?params=eyJ1cmwiOiJodHRwczovL2dpdGh1Yi5jb20vbW9ua2V5V2llL2dvcGVlZC1leHRlbnNpb24tYmlsaWJpbGkiLCJkZXZNb2RlIjpmYWxzZX0=
if (uri.path == "/extension") {
final params = uri.queryParameters["params"];
if (params?.isNotEmpty == true) {
_handleToExtension(params!);
return;
}
Get.rootDelegate.offAndToNamed(Routes.EXTENSION);
return;
}
Get.rootDelegate.offAndToNamed(Routes.HOME);
return;
}
@@ -650,15 +662,15 @@ class AppController extends GetxController with WindowListener, TrayListener {
await putConfig(downloaderConfig.value);
}
CreateTask _decodeToCreatTaskParams(String params) {
Map<String, dynamic> _decodeParams(String params) {
final safeParams = params.replaceAll('"', "").replaceAll(" ", "+");
final paramsJson =
String.fromCharCodes(base64Decode(base64.normalize(safeParams)));
return CreateTask.fromJson(jsonDecode(paramsJson));
return jsonDecode(paramsJson);
}
_handleToCreate(String params) {
final createTaskParams = _decodeToCreatTaskParams(params);
final createTaskParams = CreateTask.fromJson(_decodeParams(params));
_handleToCreate0(createTaskParams);
}
@@ -666,4 +678,10 @@ class AppController extends GetxController with WindowListener, TrayListener {
Get.rootDelegate.offAndToNamed(Routes.REDIRECT,
arguments: RedirectArgs(Routes.CREATE, arguments: createTaskParams));
}
_handleToExtension(String params) {
final installExtension = InstallExtension.fromJson(_decodeParams(params));
Get.rootDelegate.offAndToNamed(Routes.REDIRECT,
arguments: RedirectArgs(Routes.EXTENSION, arguments: installExtension));
}
}
@@ -20,6 +20,9 @@ class CreateController extends GetxController
final oldUrl = "".obs;
final fileDataUri = "".obs;
/// Flag to prevent handling pending create task from deep link multiple times
bool pendingCreateHandled = false;
@override
void onInit() {
super.onInit();
@@ -82,17 +82,16 @@ class CreateView extends GetView<CreateController> {
_pathController.text = renderPathPlaceholders(downloadDir);
}
// Initialize archive settings from global config if not already set
if (_autoExtractController.value == null) {
_autoExtractController.value =
appController.downloaderConfig.value.archive.autoExtract;
}
if (_deleteAfterExtractController.value == null) {
_deleteAfterExtractController.value =
appController.downloaderConfig.value.archive.deleteAfterExtract;
}
_autoExtractController.value ??=
appController.downloaderConfig.value.archive.autoExtract;
_deleteAfterExtractController.value ??=
appController.downloaderConfig.value.archive.deleteAfterExtract;
// Handle pending create task from deep link
final CreateTask? routerParams = Get.rootDelegate.arguments();
if (routerParams?.req?.url.isNotEmpty ?? false) {
if ((routerParams?.req?.url.isNotEmpty ?? false) &&
!controller.pendingCreateHandled) {
controller.pendingCreateHandled = true;
// get url from route arguments
final url = routerParams!.req!.url;
_urlController.text = url;
@@ -9,6 +9,9 @@ class ExtensionController extends GetxController {
final devMode = false.obs;
var _devModeCount = 0;
/// Flag to prevent handling pending install multiple times
bool pendingInstallHandled = false;
@override
void onInit() async {
super.onInit();
@@ -30,8 +30,34 @@ class ExtensionView extends GetView<ExtensionController> {
final _installUrlController = TextEditingController();
final _installBtnController = IconButtonLoadingController();
Future<void> _doInstall() async {
if (_installUrlController.text.isEmpty) {
controller.tryOpenDevMode();
return;
}
_installBtnController.start();
try {
await installExtension(InstallExtension(url: _installUrlController.text));
Get.snackbar('tip'.tr, 'extensionInstallSuccess'.tr);
await controller.load();
} catch (e) {
showErrorMessage(e);
} finally {
_installBtnController.stop();
}
}
@override
Widget build(BuildContext context) {
// Handle pending install extension from deep link
final args = Get.rootDelegate.arguments();
if (args is InstallExtension && !controller.pendingInstallHandled) {
controller.pendingInstallHandled = true;
_installUrlController.text = args.url;
WidgetsBinding.instance.addPostFrameCallback((_) {
_doInstall();
});
}
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -71,23 +97,7 @@ class ExtensionView extends GetView<ExtensionController> {
const SizedBox(width: 10),
IconButtonLoading(
controller: _installBtnController,
onPressed: () async {
if (_installUrlController.text.isEmpty) {
controller.tryOpenDevMode();
return;
}
_installBtnController.start();
try {
await installExtension(InstallExtension(
url: _installUrlController.text));
Get.snackbar('tip'.tr, 'extensionInstallSuccess'.tr);
await controller.load();
} catch (e) {
showErrorMessage(e);
} finally {
_installBtnController.stop();
}
},
onPressed: _doInstall,
icon: const Icon(Icons.download)),
controller.devMode.value && Util.isDesktop()
? IconButton(