Add export config to clipboard

This commit is contained in:
problematicconsumer
2023-11-12 12:52:54 +03:30
parent 0c2e0f4070
commit d3cab28dee
15 changed files with 167 additions and 1 deletions

View File

@@ -90,6 +90,27 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade {
);
}
@override
TaskEither<CoreServiceFailure, String> generateConfig(
String fileName,
) {
return exceptionHandler(
() {
final configPath = filesEditor.configPath(fileName);
final options = configOptions();
return setup()
.andThen(() => changeConfigOptions(options))
.andThen(
() => singbox
.generateConfig(configPath)
.mapLeft(CoreServiceFailure.other),
)
.run();
},
CoreServiceFailure.unexpected,
);
}
@override
TaskEither<CoreServiceFailure, Unit> start(
String fileName,

View File

@@ -18,6 +18,10 @@ abstract interface class SingboxFacade {
ConfigOptions options,
);
TaskEither<CoreServiceFailure, String> generateConfig(
String fileName,
);
TaskEither<CoreServiceFailure, Unit> start(
String fileName,
bool disableMemoryLimit,

View File

@@ -254,6 +254,14 @@ class ProfileActionsMenu extends HookConsumerWidget {
initialOnSuccess: () =>
CustomToast.success(t.profile.update.successMsg).show(context),
);
final exportConfigMutation = useMutation(
initialOnFailure: (err) {
CustomToast.error(t.presentShortError(err)).show(context);
},
initialOnSuccess: () =>
CustomToast.success(t.profile.share.exportConfigToClipboardSuccess)
.show(context),
);
final deleteProfileMutation = useMutation(
initialOnFailure: (err) {
CustomAlertDialog.fromErr(t.presentError(err)).show(context);
@@ -278,6 +286,25 @@ class ProfileActionsMenu extends HookConsumerWidget {
);
},
),
SubmenuButton(
menuChildren: [
MenuItemButton(
child: Text(t.profile.share.exportConfigToClipboard),
onPressed: () async {
if (exportConfigMutation.state.isInProgress) {
return;
}
exportConfigMutation.setFuture(
ref
.read(profilesNotifierProvider.notifier)
.exportConfigToClipboard(profile),
);
},
),
],
leadingIcon: const Icon(Icons.share),
child: Text(t.profile.share.buttonText),
),
MenuItemButton(
leadingIcon: const Icon(Icons.edit),
child: Text(t.profile.edit.buttonTxt),

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:fpdart/fpdart.dart';
import 'package:hiddify/core/prefs/prefs.dart';
import 'package:hiddify/data/data_providers.dart';
@@ -124,4 +125,16 @@ class ProfilesNotifier extends _$ProfilesNotifier with AppLogger {
},
).run();
}
Future<void> exportConfigToClipboard(Profile profile) async {
await ref.read(coreFacadeProvider).generateConfig(profile.id).match(
(err) {
loggy.warning('error generating config', err);
throw err;
},
(configJson) async {
await Clipboard.setData(ClipboardData(text: configJson));
},
).run();
}
}

View File

@@ -934,6 +934,21 @@ class SingboxNativeLibrary {
late final _changeConfigOptions = _changeConfigOptionsPtr
.asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();
ffi.Pointer<ffi.Char> generateConfig(
ffi.Pointer<ffi.Char> path,
) {
return _generateConfig(
path,
);
}
late final _generateConfigPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<ffi.Char> Function(
ffi.Pointer<ffi.Char>)>>('generateConfig');
late final _generateConfig = _generateConfigPtr
.asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();
ffi.Pointer<ffi.Char> start(
ffi.Pointer<ffi.Char> configPath,
int disableMemoryLimit,

View File

@@ -5,6 +5,7 @@ import 'dart:io';
import 'dart:isolate';
import 'package:combine/combine.dart';
import 'package:dartx/dartx.dart';
import 'package:ffi/ffi.dart';
import 'package:fpdart/fpdart.dart';
import 'package:hiddify/domain/connectivity/connectivity.dart';
@@ -137,6 +138,28 @@ class FFISingboxService
);
}
@override
TaskEither<String, String> generateConfig(
String path,
) {
return TaskEither(
() => CombineWorker().execute(
() {
final response = _box
.generateConfig(
path.toNativeUtf8().cast(),
)
.cast<Utf8>()
.toDartString();
if (response.startsWith("error")) {
return left(response.removePrefix("error"));
}
return right(response);
},
),
);
}
@override
TaskEither<String, Unit> start(String configPath, bool disableMemoryLimit) {
loggy.debug("starting, memory limit: [${!disableMemoryLimit}]");

View File

@@ -73,6 +73,24 @@ class MobileSingboxService
);
}
@override
TaskEither<String, String> generateConfig(
String path,
) {
return TaskEither(
() async {
final configJson = await _methodChannel.invokeMethod<String>(
"generate_config",
{"path": path},
);
if (configJson == null || configJson.isEmpty) {
return left("null response");
}
return right(configJson);
},
);
}
@override
TaskEither<String, Unit> start(String configPath, bool disableMemoryLimit) {
return TaskEither(

View File

@@ -33,6 +33,10 @@ abstract interface class SingboxService {
TaskEither<String, Unit> changeConfigOptions(ConfigOptions options);
TaskEither<String, String> generateConfig(
String path,
);
TaskEither<String, Unit> start(String configPath, bool disableMemoryLimit);
TaskEither<String, Unit> stop();