mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-08 09:30:27 +00:00
feat: move native communication to FFI (#1457)
This commit is contained in:
86 files changed
+4558
-886
No files matched your search
@@ -17,4 +17,36 @@ void main() {
|
||||
|
||||
expect(DownloaderConfig.fromJson(json).autoStartTasks, isFalse);
|
||||
});
|
||||
|
||||
test('REST server settings round-trip through the Go downloader config', () {
|
||||
final config = DownloaderConfig()
|
||||
..api = ApiServerConfig(enable: true, network: 'tcp', address: '127.0.0.1:4321', token: 'secret');
|
||||
|
||||
final decoded = DownloaderConfig.fromJson(config.toJson()).api;
|
||||
expect(decoded.enable, isTrue);
|
||||
expect(decoded.network, 'tcp');
|
||||
expect(decoded.address, '127.0.0.1:4321');
|
||||
expect(decoded.token, 'secret');
|
||||
});
|
||||
|
||||
test('Flutter preferences round-trip through the Go-owned extra config', () {
|
||||
final config = DownloaderConfig();
|
||||
config.extra
|
||||
..windowState = WindowStateConfig(isMaximized: true, width: 1280, height: 720)
|
||||
..bookmarks = {'downloads': 'D:/Downloads'}
|
||||
..createHistory = ['https://example.com/file.zip']
|
||||
..runAsMenubarApp = true
|
||||
..analyticsEnabled = false
|
||||
..analyticsClientId = 'client-id';
|
||||
|
||||
final decoded = DownloaderConfig.fromJson(config.toJson()).extra;
|
||||
expect(decoded.windowState.isMaximized, isTrue);
|
||||
expect(decoded.windowState.width, 1280);
|
||||
expect(decoded.windowState.height, 720);
|
||||
expect(decoded.bookmarks, {'downloads': 'D:/Downloads'});
|
||||
expect(decoded.createHistory, ['https://example.com/file.zip']);
|
||||
expect(decoded.runAsMenubarApp, isTrue);
|
||||
expect(decoded.analyticsEnabled, isFalse);
|
||||
expect(decoded.analyticsClientId, 'client-id');
|
||||
});
|
||||
}
|
||||
@@ -1,46 +1,26 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart' as dio;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:gopeed/api/api.dart' as api;
|
||||
import 'package:gopeed/api/model/options.dart';
|
||||
import 'package:gopeed/api/model/request.dart';
|
||||
import 'package:gopeed/api/model/resolve_task.dart';
|
||||
import 'package:gopeed/core/network/gopeed/gopeed_transport.dart';
|
||||
|
||||
void main() {
|
||||
test('resolve sends the complete resolve task for magnet links', () async {
|
||||
final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
|
||||
addTearDown(server.close);
|
||||
|
||||
late Map<String, dynamic> requestBody;
|
||||
final responseHandled = Completer<void>();
|
||||
server.listen((request) async {
|
||||
requestBody = jsonDecode(await utf8.decoder.bind(request).join()) as Map<String, dynamic>;
|
||||
request.response.headers.contentType = ContentType.json;
|
||||
request.response.write(
|
||||
jsonEncode({
|
||||
'code': 0,
|
||||
'data': {
|
||||
'id': 'resolved-id',
|
||||
'res': {'name': 'sample', 'size': 0, 'range': false, 'files': <Object>[], 'hash': ''},
|
||||
},
|
||||
}),
|
||||
);
|
||||
await request.response.close();
|
||||
responseHandled.complete();
|
||||
});
|
||||
|
||||
api.init('tcp', '127.0.0.1:${server.port}', '');
|
||||
final transport = _RecordingTransport();
|
||||
api.setTransportForTesting(transport);
|
||||
final result = await api.resolve(
|
||||
ResolveTask(
|
||||
req: Request(url: 'magnet:?xt=urn:btih:0123456789abcdef0123456789abcdef01234567'),
|
||||
opts: Options(path: '/downloads', selectFiles: const []),
|
||||
),
|
||||
);
|
||||
await responseHandled.future;
|
||||
|
||||
expect(result.id, 'resolved-id');
|
||||
final requestBody = transport.data as Map<String, dynamic>;
|
||||
expect(requestBody.keys, containsAll(<String>['req', 'opts']));
|
||||
expect(
|
||||
(requestBody['req'] as Map<String, dynamic>)['url'],
|
||||
@@ -49,3 +29,32 @@ void main() {
|
||||
expect((requestBody['opts'] as Map<String, dynamic>)['path'], '/downloads');
|
||||
});
|
||||
}
|
||||
|
||||
class _RecordingTransport implements GopeedTransport {
|
||||
dynamic data;
|
||||
|
||||
@override
|
||||
Future<dynamic> request(
|
||||
String path, {
|
||||
String method = 'GET',
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
}) async {
|
||||
this.data = jsonDecode(jsonEncode(data));
|
||||
return {
|
||||
'code': 0,
|
||||
'data': {
|
||||
'id': 'resolved-id',
|
||||
'res': {'name': 'sample', 'size': 0, 'range': false, 'files': <Object>[], 'hash': ''},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String join(String path) => path;
|
||||
|
||||
@override
|
||||
Future<dio.Response<String>> proxyRequest(String uri, {dynamic data, dio.Options? options}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:gopeed/core/common/task_event.dart';
|
||||
|
||||
void main() {
|
||||
test('task event mask combines subscribed event types', () {
|
||||
expect({TaskEventType.done}.mask, 1);
|
||||
expect({TaskEventType.error}.mask, 2);
|
||||
expect({TaskEventType.done, TaskEventType.error}.mask, 3);
|
||||
});
|
||||
|
||||
test('task event decodes terminal payload', () {
|
||||
final event = TaskEvent.fromJson({
|
||||
'type': 'task.error',
|
||||
'taskId': 'task-1',
|
||||
'name': 'archive.zip',
|
||||
'error': 'network error',
|
||||
});
|
||||
|
||||
expect(event.type, TaskEventType.error);
|
||||
expect(event.taskId, 'task-1');
|
||||
expect(event.name, 'archive.zip');
|
||||
expect(event.error, 'network error');
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:gopeed/api/api.dart' as api;
|
||||
import 'package:gopeed/core/network/gopeed/gopeed_transport.dart';
|
||||
import 'package:gopeed/features/auth/application/web_auth_controller.dart';
|
||||
|
||||
void main() {
|
||||
test('successful login relies on the server cookie and unauthorized requires login', () async {
|
||||
api.setTransportForTesting(_LoginTransport());
|
||||
final controller = WebAuthController();
|
||||
addTearDown(controller.dispose);
|
||||
|
||||
final failure = await controller.login(username: 'gopeed', password: 'secret');
|
||||
|
||||
expect(failure, isNull);
|
||||
controller.requireLogin();
|
||||
expect(controller.loginRequired, isTrue);
|
||||
});
|
||||
}
|
||||
|
||||
class _LoginTransport implements GopeedTransport {
|
||||
@override
|
||||
Future<dynamic> request(
|
||||
String path, {
|
||||
String method = 'GET',
|
||||
dynamic data,
|
||||
Map<String, dynamic>? queryParameters,
|
||||
}) async {
|
||||
expect(path, 'api/web/login');
|
||||
expect(method, 'POST');
|
||||
return {'code': 0};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Response<String>> proxyRequest(String uri, {dynamic data, Options? options}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
String join(String path) => path;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import 'package:gopeed/core/capabilities/app_capabilities.dart';
|
||||
import 'package:gopeed/core/capabilities/capability_rpc.dart';
|
||||
import 'package:gopeed/core/capabilities/gopeed_capability.dart';
|
||||
import 'package:gopeed/core/common/start_config.dart';
|
||||
import 'package:gopeed/core/common/api_server_state.dart';
|
||||
import 'package:gopeed/features/settings/application/settings_controller.dart';
|
||||
|
||||
void main() {
|
||||
@@ -36,13 +37,51 @@ void main() {
|
||||
expect((await container.read(settingsControllerProvider.future)).config.downloadDir, 'E:/Created task');
|
||||
expect(getConfigCalls, 2);
|
||||
});
|
||||
|
||||
test('settings save preserves backend-owned client preferences', () async {
|
||||
final backend = DownloaderConfig()
|
||||
..extra.windowState = WindowStateConfig(isMaximized: true, width: 1200, height: 800)
|
||||
..extra.createHistory = ['https://example.com/existing.zip']
|
||||
..extra.analyticsClientId = 'existing-client';
|
||||
DownloaderConfig? saved;
|
||||
final registry = CapabilityRegistry(createAppCapabilityCodecs())
|
||||
..bind(GopeedMethods.getConfig, (_) => DownloaderConfig.fromJson(backend.toJson()))
|
||||
..bind(GopeedMethods.putConfig, (config) {
|
||||
saved = DownloaderConfig.fromJson(config.toJson());
|
||||
return const RpcUnit();
|
||||
});
|
||||
final container = ProviderContainer(
|
||||
overrides: [
|
||||
appCapabilitiesProvider.overrideWithValue(AppCapabilities(LocalCapabilityInvoker(registry))),
|
||||
appRuntimeControllerProvider.overrideWith(_TestRuntimeController.new),
|
||||
],
|
||||
);
|
||||
addTearDown(container.dispose);
|
||||
|
||||
final controller = container.read(settingsControllerProvider.notifier);
|
||||
await container.read(settingsControllerProvider.future);
|
||||
await controller.save(DownloaderConfig(downloadDir: 'E:/New'));
|
||||
|
||||
expect(saved?.downloadDir, 'E:/New');
|
||||
expect(saved?.extra.windowState.isMaximized, isTrue);
|
||||
expect(saved?.extra.createHistory, ['https://example.com/existing.zip']);
|
||||
expect(saved?.extra.analyticsClientId, 'existing-client');
|
||||
});
|
||||
}
|
||||
|
||||
class _TestRuntimeController extends AppRuntimeController {
|
||||
@override
|
||||
Future<AppRuntimeState> build() async => AppRuntimeState(
|
||||
startConfig: StartConfig(),
|
||||
runningPort: 0,
|
||||
apiServerState: const ApiServerState(
|
||||
enabled: false,
|
||||
running: false,
|
||||
network: '',
|
||||
address: '',
|
||||
runningPort: 0,
|
||||
pendingApply: false,
|
||||
lastError: '',
|
||||
),
|
||||
downloaderConfig: DownloaderConfig(downloadDir: 'C:/Stale runtime snapshot'),
|
||||
);
|
||||
}
|
||||
@@ -145,21 +145,30 @@ void main() {
|
||||
final rememberDirectory = find.byKey(const ValueKey('create-task-remember-download-directory-checkbox'));
|
||||
final directDownloadCheckbox = find.byKey(const ValueKey('create-task-direct-download-checkbox'));
|
||||
final rememberDirectoryRow = find.byKey(const ValueKey('create-task-remember-download-directory'));
|
||||
final rememberDirectoryLabel = find.byKey(const ValueKey('create-task-remember-download-directory-label'));
|
||||
final directDownloadLabel = find.descendant(
|
||||
of: find.byKey(const ValueKey('create-task-direct-download-toggle')),
|
||||
matching: find.text('Skip resolving and create tasks immediately'),
|
||||
);
|
||||
final directoryComponent = find.byKey(const ValueKey('create-task-directory-component'));
|
||||
final directoryOptionsRow = find.byKey(const ValueKey('create-task-directory-options-row'));
|
||||
final directoryOptionsLayout = find.byKey(const ValueKey('create-task-directory-options-layout'));
|
||||
final directoryPicker = find.byKey(const ValueKey('create-task-directory-picker'));
|
||||
expect(tester.widget<shad.Checkbox>(rememberDirectory).state, shad.CheckboxState.unchecked);
|
||||
expect(find.text('Save as default download directory'), findsOneWidget);
|
||||
final rememberStyle = tester.widget<Text>(rememberDirectoryLabel).style!;
|
||||
final directStyle = tester.widget<Text>(directDownloadLabel).style!;
|
||||
expect(rememberStyle.color, directStyle.color);
|
||||
expect(rememberStyle.fontSize, directStyle.fontSize);
|
||||
expect(rememberStyle.fontWeight, directStyle.fontWeight);
|
||||
expect(tester.widget<shad.Checkbox>(rememberDirectory).size, isNull);
|
||||
expect(tester.getSize(rememberDirectory), const Size.square(AppDesignTokens.checkboxSize));
|
||||
expect(tester.getCenter(rememberDirectory).dx, closeTo(tester.getCenter(directDownloadCheckbox).dx, 0.5));
|
||||
final rememberDirectoryLabel = tester.widget<Text>(
|
||||
find.byKey(const ValueKey('create-task-remember-download-directory-label')),
|
||||
);
|
||||
final directDownloadLabel = tester.widget<Text>(find.byKey(const ValueKey('create-task-direct-download-label')));
|
||||
expect(rememberDirectoryLabel.style, directDownloadLabel.style);
|
||||
expect(rememberDirectoryLabel.maxLines, directDownloadLabel.maxLines);
|
||||
expect(rememberDirectoryLabel.overflow, directDownloadLabel.overflow);
|
||||
final rememberDirectoryText = tester.widget<Text>(rememberDirectoryLabel);
|
||||
final directDownloadText = tester.widget<Text>(directDownloadLabel);
|
||||
expect(rememberDirectoryText.style, directDownloadText.style);
|
||||
expect(rememberDirectoryText.maxLines, directDownloadText.maxLines);
|
||||
expect(rememberDirectoryText.overflow, directDownloadText.overflow);
|
||||
expect(tester.widget<shad.IconButton>(directoryPicker).variance, same(shad.ButtonVariance.outline));
|
||||
expect(tester.widget<flutter.Row>(directoryOptionsLayout).mainAxisAlignment, MainAxisAlignment.spaceBetween);
|
||||
expect(tester.getCenter(rememberDirectoryRow).dy, closeTo(tester.getCenter(shortcutRow).dy, 0.5));
|
||||
|
||||
@@ -27,6 +27,7 @@ import 'package:gopeed/api/model/resource.dart';
|
||||
import 'package:gopeed/api/model/store_extension.dart';
|
||||
import 'package:gopeed/api/model/task.dart' as api_task;
|
||||
import 'package:gopeed/core/common/start_config.dart';
|
||||
import 'package:gopeed/core/common/api_server_state.dart';
|
||||
import 'package:gopeed/core/icons/gopeed_icons.dart';
|
||||
import 'package:gopeed/core/window/app_window_chrome.dart';
|
||||
import 'package:gopeed/core/window/app_window_frame.dart';
|
||||
@@ -1329,16 +1330,14 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Advanced'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Runtime status'), findsOneWidget);
|
||||
expect(find.text('tcp://192.168.1.20:4321'), findsOneWidget);
|
||||
expect(find.byKey(const ValueKey('api-server-status-dot')), findsOneWidget);
|
||||
|
||||
final host = find.byKey(const ValueKey('api-host-input'));
|
||||
final port = find.byKey(const ValueKey('api-port-input'));
|
||||
final saveButton = find.byKey(const ValueKey('save-api-config-button'));
|
||||
expect(tester.widget<AppLoadingButton>(saveButton).variant, AppLoadingButtonVariant.secondary);
|
||||
final saveButtonClickable = find.descendant(of: saveButton, matching: find.byType(shad.Clickable));
|
||||
final disabledDecoration =
|
||||
tester.widget<shad.Clickable>(saveButtonClickable).decoration!.resolve({WidgetState.disabled}) as BoxDecoration;
|
||||
expect(disabledDecoration.color, AppPalette.dark.surfaceSoft);
|
||||
expect(disabledDecoration.color, isNot(AppPalette.dark.brand));
|
||||
expect(tester.widget<AppLoadingButton>(saveButton).variant, AppLoadingButtonVariant.primary);
|
||||
expect(tester.widget<shad.TextField>(host).controller!.text, '192.168.1.20');
|
||||
expect(tester.widget<shad.TextField>(port).controller!.text, '4321');
|
||||
expect(tester.widget<AppLoadingButton>(saveButton).onPressed, isNull);
|
||||
@@ -1359,6 +1358,10 @@ void main() {
|
||||
await tester.enterText(host, '127.0.0.1');
|
||||
await tester.tap(saveButton);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const ValueKey('restart-api-server-dialog')), findsOneWidget);
|
||||
expect(runtimeController.savedStartConfig, isNull);
|
||||
await tester.tap(find.byKey(const ValueKey('confirm-restart-api-server-button')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(runtimeController.savedStartConfig?.network, 'tcp');
|
||||
expect(runtimeController.savedStartConfig?.address, '127.0.0.1:0');
|
||||
expect(tester.widget<AppLoadingButton>(saveButton).onPressed, isNull);
|
||||
@@ -1378,6 +1381,54 @@ void main() {
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('disabling the API server requires confirmation and keeps listener fields visible', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
await _setTestSize(tester, const Size(1024, 900));
|
||||
final runtimeController = RecordingStartConfigRuntimeController();
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
appRuntimeControllerProvider.overrideWith(() => runtimeController),
|
||||
appPlatformControllerProvider.overrideWith(FakePlatformController.new),
|
||||
settingsControllerProvider.overrideWith(FakeSettingsController.new),
|
||||
],
|
||||
child: const _SettingsTestApp(),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Advanced'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final hostInput = find.byKey(const ValueKey('api-host-input'));
|
||||
await tester.enterText(hostInput, 'unsaved.example.com');
|
||||
final toggleButton = find.byKey(const ValueKey('toggle-api-server-button'));
|
||||
await tester.ensureVisible(toggleButton);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(toggleButton);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byKey(const ValueKey('disable-api-server-dialog')), findsOneWidget);
|
||||
expect(find.byKey(const ValueKey('api-host-input')), findsOneWidget);
|
||||
expect(find.byKey(const ValueKey('api-port-input')), findsOneWidget);
|
||||
await tester.tap(find.byKey(const ValueKey('cancel-disable-api-server-button')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(runtimeController.savedStartConfig, isNull);
|
||||
|
||||
await tester.tap(toggleButton);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byKey(const ValueKey('confirm-disable-api-server-button')));
|
||||
await tester.pumpAndSettle();
|
||||
expect(runtimeController.savedStartConfig?.apiEnable, isFalse);
|
||||
expect(runtimeController.savedStartConfig?.address, '192.168.1.20:4321');
|
||||
expect(tester.widget<shad.TextField>(hostInput).controller!.text, 'unsaved.example.com');
|
||||
final statusDot = tester.widget<AnimatedContainer>(find.byKey(const ValueKey('api-server-status-dot')));
|
||||
expect((statusDot.decoration! as BoxDecoration).color, AppPalette.light.textMuted.withValues(alpha: 0.72));
|
||||
expect(find.text('tcp://192.168.1.20:4321'), findsNothing);
|
||||
await tester.pump(const Duration(seconds: 6));
|
||||
await tester.pumpAndSettle();
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('shared toast appears at top center with content-driven width', (WidgetTester tester) async {
|
||||
await _setTestSize(tester, const Size(1024, 700));
|
||||
await tester.pumpWidget(
|
||||
@@ -2995,10 +3046,9 @@ void main() {
|
||||
expect(urlValue.semanticsLabel, task.url);
|
||||
expect(find.text('Task Details'), findsOneWidget);
|
||||
expect(find.text('Task Name'), findsNothing);
|
||||
final detailHeader = tester.widget<DecoratedBox>(
|
||||
find.descendant(of: find.byKey(const ValueKey('app-detail-drawer-header')), matching: find.byType(DecoratedBox)),
|
||||
);
|
||||
expect(((detailHeader.decoration as BoxDecoration).border! as Border).bottom.width, 1);
|
||||
final detailHeader = tester.widget<SizedBox>(find.byKey(const ValueKey('app-detail-drawer-header')));
|
||||
final detailHeaderDecoration = (detailHeader.child! as DecoratedBox).decoration as BoxDecoration;
|
||||
expect((detailHeaderDecoration.border! as Border).bottom.width, 1);
|
||||
expect(find.text('Details'), findsOneWidget);
|
||||
expect(find.text('Files'), findsOneWidget);
|
||||
expect(find.text('Statistics'), findsOneWidget);
|
||||
@@ -4367,9 +4417,14 @@ class _SettingsTestAppState extends State<_SettingsTestApp> {
|
||||
return shad.ShadcnApp.router(
|
||||
theme: widget.dark ? AppTheme.dark() : AppTheme.light(),
|
||||
materialTheme: widget.dark ? AppTheme.materialDark() : AppTheme.materialLight(),
|
||||
builder: widget.wrapComponentThemes
|
||||
? (context, child) => AppComponentThemes(child: child ?? const SizedBox.shrink())
|
||||
: null,
|
||||
builder: (context, child) {
|
||||
final content = widget.wrapComponentThemes
|
||||
? AppComponentThemes(child: child ?? const SizedBox.shrink())
|
||||
: child ?? const SizedBox.shrink();
|
||||
final mediaQuery = MediaQuery.maybeOf(context);
|
||||
if (mediaQuery == null) return content;
|
||||
return MediaQuery(data: mediaQuery.copyWith(disableAnimations: true), child: content);
|
||||
},
|
||||
routerConfig: _router,
|
||||
);
|
||||
}
|
||||
@@ -4534,11 +4589,16 @@ class FakeRuntimeController extends AppRuntimeController {
|
||||
final cfg = StartConfig()
|
||||
..network = 'tcp'
|
||||
..address = '127.0.0.1:9999'
|
||||
..apiEnable = true
|
||||
..apiToken = ''
|
||||
..storage = 'bolt'
|
||||
..storageDir = ''
|
||||
..refreshInterval = 0;
|
||||
return AppRuntimeState(startConfig: cfg, runningPort: 9999, downloaderConfig: DownloaderConfig());
|
||||
return AppRuntimeState(
|
||||
startConfig: cfg,
|
||||
apiServerState: _testApiServerState(running: true, port: 9999),
|
||||
downloaderConfig: DownloaderConfig(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4566,25 +4626,75 @@ class RecordingStartConfigRuntimeController extends AppRuntimeController {
|
||||
final config = StartConfig()
|
||||
..network = 'tcp'
|
||||
..address = '192.168.1.20:4321'
|
||||
..apiEnable = true
|
||||
..apiToken = 'token'
|
||||
..storage = 'bolt'
|
||||
..storageDir = ''
|
||||
..refreshInterval = 0;
|
||||
return AppRuntimeState(startConfig: config, runningPort: 4321, downloaderConfig: DownloaderConfig());
|
||||
return AppRuntimeState(
|
||||
startConfig: config,
|
||||
apiServerState: _testApiServerState(running: true, port: 4321, address: config.address),
|
||||
downloaderConfig: DownloaderConfig(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveStartConfig(StartConfig config) async {
|
||||
Future<void> saveApiServerConfig(StartConfig config) async =>
|
||||
_record(config, running: state.requireValue.apiServerState.running);
|
||||
|
||||
@override
|
||||
Future<void> startApiServer() async => _record(state.requireValue.startConfig..apiEnable = true, running: true);
|
||||
|
||||
@override
|
||||
Future<void> stopApiServer() async => _record(state.requireValue.startConfig..apiEnable = false, running: false);
|
||||
|
||||
@override
|
||||
Future<void> restartApiServer(StartConfig config) async => _record(config..apiEnable = true, running: true);
|
||||
|
||||
void _record(StartConfig config, {required bool running}) {
|
||||
savedStartConfig = StartConfig()
|
||||
..network = config.network
|
||||
..address = config.address
|
||||
..apiEnable = config.apiEnable
|
||||
..apiToken = config.apiToken
|
||||
..storage = config.storage
|
||||
..storageDir = config.storageDir
|
||||
..refreshInterval = config.refreshInterval;
|
||||
final current = state.requireValue;
|
||||
state = AsyncValue.data(
|
||||
AppRuntimeState(
|
||||
startConfig: savedStartConfig!,
|
||||
apiServerState: _testApiServerState(
|
||||
running: running,
|
||||
port: running ? 54321 : 0,
|
||||
address: config.address,
|
||||
enabled: config.apiEnable,
|
||||
),
|
||||
downloaderConfig: current.downloaderConfig,
|
||||
localBackendStarted: current.localBackendStarted,
|
||||
startupError: current.startupError,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ApiServerState _testApiServerState({
|
||||
required bool running,
|
||||
required int port,
|
||||
String address = '127.0.0.1:9999',
|
||||
bool? enabled,
|
||||
}) {
|
||||
return ApiServerState(
|
||||
enabled: enabled ?? running,
|
||||
running: running,
|
||||
network: running ? 'tcp' : '',
|
||||
address: running ? address : '',
|
||||
runningPort: port,
|
||||
pendingApply: false,
|
||||
lastError: '',
|
||||
);
|
||||
}
|
||||
|
||||
class FakeSettingsController extends SettingsController {
|
||||
@override
|
||||
Future<SettingsState> build() async => SettingsState(config: DownloaderConfig());
|
||||
|
||||
Reference in new issue
Block a user