feat(tasks): group extension folder downloads (#1594)

This commit is contained in:
WuFeng authored and GitHub committed 2026-11-02 15:39:08 +08:00
1 parent fc0a1e0ef9
commit 1e36599d52
84 files changed
+6690 -881

No files matched your search

+45 -18
View File
@@ -1,3 +1,7 @@
import 'package:gopeed/features/tasks/application/pending_download.dart';
import 'package:gopeed/api/model/options.dart' as preview_options;
import 'package:gopeed/api/model/request.dart' as preview_request;
import 'package:gopeed/api/model/resolve_result.dart' as preview_resolve;
import 'dart:async';
import 'dart:io';
@@ -30,6 +34,7 @@ import 'package:gopeed/app/application/app_notification_controller.dart';
import 'package:gopeed/app/application/app_platform_controller.dart';
import 'package:gopeed/app/application/app_runtime_controller.dart';
import 'package:gopeed/api/model/create_task.dart';
import 'package:gopeed/api/model/create_task_batch.dart';
import 'package:gopeed/api/model/install_extension.dart';
import 'package:gopeed/features/extensions/application/pending_extension_install.dart';
import 'package:gopeed/api/model/downloader_config.dart';
@@ -128,9 +133,9 @@ void main() {
),
),
)
..bind(GopeedMethods.createTask, (_) {
..bind(GopeedMethods.createTaskBatch, (_) {
created++;
return 'new-task';
return CreateTaskBatchResult(taskIds: ['new-task']);
});
final container = ProviderContainer(
overrides: [
@@ -3329,7 +3334,7 @@ void main() {
shad.ShadcnApp(
theme: AppTheme.light(),
materialTheme: AppTheme.materialLight(),
home: ResolveFileTree(
home: _singleResolveTree(
files: [
FileInfo(path: '', name: 'root.txt', size: 1),
FileInfo(path: 'folder', name: 'first.txt', size: 2),
@@ -3349,7 +3354,7 @@ void main() {
expect(find.text('third.txt'), findsNothing);
expect(selected, [0, 1, 2, 3]);
await tester.tap(find.byKey(const ValueKey('resolve-tree-node-expand-folder:folder/nested')));
await tester.tap(find.byKey(const ValueKey('resolve-tree-node-expand-folder:source/folder/nested')));
await tester.pumpAndSettle();
expect(find.text('second.txt'), findsOneWidget);
expect(find.text('deeper'), findsOneWidget);
@@ -3362,7 +3367,7 @@ void main() {
await tester.tap(find.byKey(const ValueKey('resolve-tree-expand-toggle')));
await tester.pumpAndSettle();
expect(find.text('first.txt'), findsNothing);
expect(find.text('root.txt'), findsOneWidget);
expect(find.text('root.txt'), findsNothing);
expect(selected, [0, 1, 2, 3]);
expect(tester.takeException(), isNull);
});
@@ -3376,7 +3381,7 @@ void main() {
materialTheme: AppTheme.materialLight(),
home: Padding(
padding: const EdgeInsets.all(24),
child: ResolveFileTree(
child: _singleResolveTree(
files: [
FileInfo(path: 'videos', name: 'clip.mp4', size: 0),
FileInfo(path: 'docs', name: 'notes.txt', size: 0),
@@ -3446,30 +3451,30 @@ void main() {
await tester.pumpAndSettle();
expect(find.text('clip.mp4'), findsOneWidget);
final videosExpand = find.byKey(const ValueKey('resolve-tree-node-expand-folder:videos'));
final videosExpand = find.byKey(const ValueKey('resolve-tree-node-expand-folder:source/videos'));
expect(videosExpand, findsOneWidget);
expect(find.byKey(const ValueKey('resolve-tree-node-expand-file:0')), findsOneWidget);
expect(find.byKey(const ValueKey('resolve-tree-node-expand-file:source:0')), findsOneWidget);
await tester.tap(videosExpand);
await tester.pumpAndSettle();
expect(tester.widget<VirtualTreeView<dynamic>>(treeFinder).nodes.first.expanded, isFalse);
expect((tester.widget<VirtualTreeView<dynamic>>(treeFinder).nodes.first as shad.TreeItemNode<dynamic>).children.first.expanded, isFalse);
expect(find.text('clip.mp4').hitTestable(), findsNothing);
expect(find.text('videos').hitTestable(), findsOneWidget);
expect(selected, [0, 1, 2]);
await tester.tap(find.text('videos'));
await tester.pump();
expect(tester.widget<VirtualTreeView<dynamic>>(treeFinder).nodes.first.expanded, isFalse);
expect((tester.widget<VirtualTreeView<dynamic>>(treeFinder).nodes.first as shad.TreeItemNode<dynamic>).children.first.expanded, isFalse);
expect(selected, [1, 2]);
await tester.tap(find.text('videos'));
await tester.pump();
expect(tester.widget<VirtualTreeView<dynamic>>(treeFinder).nodes.first.expanded, isFalse);
expect((tester.widget<VirtualTreeView<dynamic>>(treeFinder).nodes.first as shad.TreeItemNode<dynamic>).children.first.expanded, isFalse);
expect(selected, [0, 1, 2]);
await tester.tap(videosExpand);
await tester.pumpAndSettle();
expect(tester.widget<VirtualTreeView<dynamic>>(treeFinder).nodes.first.expanded, isTrue);
expect((tester.widget<VirtualTreeView<dynamic>>(treeFinder).nodes.first as shad.TreeItemNode<dynamic>).children.first.expanded, isTrue);
expect(find.text('clip.mp4'), findsOneWidget);
BoxDecoration sortDecoration(String key) {
@@ -3520,7 +3525,7 @@ void main() {
materialTheme: AppTheme.materialLight(),
home: Padding(
padding: const EdgeInsets.all(24),
child: ResolveFileTree(
child: _singleResolveTree(
files: [
FileInfo(path: '', name: 'clip.mp4', size: 100),
FileInfo(path: '', name: 'song.mp3', size: 200),
@@ -3569,7 +3574,7 @@ void main() {
materialTheme: AppTheme.materialLight(),
home: Padding(
padding: const EdgeInsets.all(24),
child: ResolveFileTree(
child: _singleResolveTree(
files: [FileInfo(name: longName, size: 1024)],
initialSelection: const [0],
onSelectionChanged: (_) {},
@@ -3580,7 +3585,7 @@ void main() {
await tester.pumpAndSettle();
final horizontalScroll = find.byKey(const ValueKey('resolve-tree-horizontal-scroll'));
final trailing = find.byKey(const ValueKey('resolve-tree-trailing-file:0'));
final trailing = find.byKey(const ValueKey('resolve-tree-trailing-file:source:0'));
expect(find.byKey(const ValueKey('resolve-tree-horizontal-scrollbar')), findsOneWidget);
expect(horizontalScroll, findsOneWidget);
expect(trailing, findsOneWidget);
@@ -3600,7 +3605,7 @@ void main() {
position.jumpTo(0);
await tester.pumpAndSettle();
final nameArea = find.byKey(const ValueKey('resolve-tree-name-scroll-file:0'));
final nameArea = find.byKey(const ValueKey('resolve-tree-name-scroll-file:source:0'));
await tester.drag(nameArea, const Offset(-100, 0));
await tester.pumpAndSettle();
expect(position.pixels, greaterThan(0));
@@ -3622,7 +3627,7 @@ void main() {
materialTheme: AppTheme.materialLight(),
home: Padding(
padding: const EdgeInsets.all(24),
child: ResolveFileTree(
child: _singleResolveTree(
files: [FileInfo(name: shortName, size: 1024)],
initialSelection: const [0],
onSelectionChanged: (_) {},
@@ -3650,7 +3655,7 @@ void main() {
materialTheme: AppTheme.materialLight(),
home: Padding(
padding: const EdgeInsets.all(24),
child: ResolveFileTree(files: files, initialSelection: const [], onSelectionChanged: (_) {}),
child: _singleResolveTree(files: files, initialSelection: const [], onSelectionChanged: (_) {}),
),
),
);
@@ -6402,3 +6407,25 @@ api_task.Task _apiTransferTask({
updatedAt: DateTime.fromMillisecondsSinceEpoch(0),
);
}
Widget _singleResolveTree({
required List<FileInfo> files,
required List<int> initialSelection,
required ValueChanged<List<int>> onSelectionChanged,
}) {
final source =
PendingDownload(
id: 'source',
request: preview_request.Request(url: 'https://example.com'),
options: preview_options.Options(),
)
..result = preview_resolve.ResolveResult(
res: Resource(name: 'source', files: files),
)
..selected.addAll(initialSelection);
WidgetsBinding.instance.addPostFrameCallback((_) => onSelectionChanged(initialSelection));
return ResolveFileTree(
sources: [source],
onSelectionChanged: (keys) => onSelectionChanged(keys.map((key) => key.$2).toList()..sort()),
);
}