mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-14 20:33:41 +00:00
feat: extension support (#78)
This commit is contained in:
118 files changed
+6971
-780
No files matched your search
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:gopeed/api/model/meta.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
import '../../api/api.dart';
|
||||
import '../../api/model/task.dart';
|
||||
@@ -37,8 +38,11 @@ class BuildTaskListView extends GetView {
|
||||
|
||||
Widget buildTaskList(BuildContext context, tasks) {
|
||||
return ListView.builder(
|
||||
itemCount: tasks.length,
|
||||
itemCount: tasks.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == tasks.length) {
|
||||
return const SizedBox(height: 75);
|
||||
}
|
||||
return item(context, tasks[index]);
|
||||
},
|
||||
);
|
||||
@@ -54,14 +58,12 @@ class BuildTaskListView extends GetView {
|
||||
}
|
||||
|
||||
String buildExplorerUrl(Task task) {
|
||||
if (task.meta.res.rootDir.trim().isEmpty) {
|
||||
return path.join(
|
||||
Util.safeDir(task.meta.opts.path),
|
||||
Util.safeDir(task.meta.res.files[0].path),
|
||||
task.meta.res.files[0].name);
|
||||
if (task.meta.res!.name.isEmpty) {
|
||||
return path.join(Util.safeDir(task.meta.opts.path),
|
||||
Util.safeDir(task.meta.res!.files[0].path), fileName(task.meta));
|
||||
} else {
|
||||
return path.join(Util.safeDir(task.meta.opts.path),
|
||||
Util.safeDir(task.meta.res.rootDir));
|
||||
Util.safeDir(fileName(task.meta)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +91,7 @@ class BuildTaskListView extends GetView {
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
'delete'.tr,
|
||||
'confirm'.tr,
|
||||
style: const TextStyle(color: Colors.redAccent),
|
||||
),
|
||||
onPressed: () async {
|
||||
@@ -156,80 +158,85 @@ class BuildTaskListView extends GetView {
|
||||
}
|
||||
|
||||
double getProgress() {
|
||||
return task.size <= 0 ? 1 : task.progress.downloaded / task.size;
|
||||
final totalSize = task.meta.res?.size ?? 0;
|
||||
return totalSize <= 0 ? 0 : task.progress.downloaded / totalSize;
|
||||
}
|
||||
|
||||
Color pickColor() {
|
||||
switch (task.status) {
|
||||
// ready, running, pause, error, done
|
||||
case Status.running:
|
||||
return Get.theme.colorScheme.primary;
|
||||
// case Status.pause:
|
||||
// return Get.theme.colorScheme.secondary;
|
||||
case Status.error:
|
||||
return Get.theme.colorScheme.error;
|
||||
default:
|
||||
return Get.theme.colorScheme.primary;
|
||||
String getProgressText() {
|
||||
if (isDone()) {
|
||||
return Util.fmtByte(task.meta.res!.size);
|
||||
}
|
||||
if (task.meta.res == null) {
|
||||
return "";
|
||||
}
|
||||
final total = task.meta.res!.size;
|
||||
return Util.fmtByte(task.progress.downloaded) +
|
||||
(total > 0 ? " / ${Util.fmtByte(total)}" : "");
|
||||
}
|
||||
|
||||
return InkWell(
|
||||
// onTap: () {},
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
Stack(children: [
|
||||
!isDone()
|
||||
? Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Opacity(
|
||||
opacity: 0.6,
|
||||
child: LinearProgressIndicator(
|
||||
backgroundColor: Colors.transparent,
|
||||
color: pickColor(),
|
||||
// minHeight: 76,
|
||||
value: getProgress(),
|
||||
),
|
||||
))
|
||||
: const SizedBox.shrink(),
|
||||
ListTile(
|
||||
// isThreeLine: true,
|
||||
title: Text(fileName(task.meta),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Get.textTheme.titleSmall),
|
||||
subtitle: Text(
|
||||
"${isDone() ? "" : "${Util.fmtByte(task.progress.downloaded)} / "}${Util.fmtByte(task.size)}",
|
||||
style: context.textTheme.bodyLarge
|
||||
?.copyWith(color: Get.theme.disabledColor),
|
||||
return Card(
|
||||
elevation: 4.0,
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(fileName(task.meta)),
|
||||
leading: (task.meta.res?.name.isNotEmpty ?? false
|
||||
? const Icon(FaIcons.folder)
|
||||
: Icon(FaIcons.allIcons[findIcon(fileName(task.meta))]))),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text(
|
||||
getProgressText(),
|
||||
style: Get.textTheme.bodyLarge
|
||||
?.copyWith(color: Get.theme.disabledColor),
|
||||
).padding(left: 18)),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text("${Util.fmtByte(task.progress.speed)} / s",
|
||||
style: Get.textTheme.titleSmall),
|
||||
...buildActions()
|
||||
],
|
||||
)),
|
||||
],
|
||||
),
|
||||
LinearProgressIndicator(
|
||||
value: getProgress(),
|
||||
),
|
||||
],
|
||||
),
|
||||
leading: (task.meta.res.rootDir.isNotEmpty
|
||||
? const Icon(FaIcons.folder)
|
||||
: Icon(FaIcons.allIcons[findIcon(fileName(task.meta))])),
|
||||
|
||||
trailing: SizedBox(
|
||||
width: 180,
|
||||
child: Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
// textBaseline: DefaultTextStyle.of(context).style.textBaseline,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text("${Util.fmtByte(task.progress.speed)} / s",
|
||||
style: context.textTheme.titleSmall),
|
||||
...buildActions()
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
])
|
||||
]));
|
||||
)).padding(horizontal: 14, top: 8);
|
||||
}
|
||||
|
||||
String fileName(Meta meta) {
|
||||
if (meta.res.files.length > 1) {
|
||||
return meta.res.name;
|
||||
if (meta.opts.name.isNotEmpty) {
|
||||
return meta.opts.name;
|
||||
}
|
||||
return meta.opts.name.isEmpty ? meta.res.files[0].name : meta.opts.name;
|
||||
if (meta.res == null) {
|
||||
final u = Uri.parse(meta.req.url);
|
||||
if (u.scheme.startsWith("http")) {
|
||||
return u.path.isNotEmpty
|
||||
? u.path.substring(u.path.lastIndexOf("/") + 1)
|
||||
: u.host;
|
||||
} else {
|
||||
final params = u.queryParameters;
|
||||
if (params.containsKey("dn")) {
|
||||
return params["dn"]!;
|
||||
} else {
|
||||
return params["xt"]!.split(":").last;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (meta.res!.name.isNotEmpty) {
|
||||
return meta.res!.name;
|
||||
}
|
||||
return meta.res!.files[0].name;
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ class FileListView extends GetView {
|
||||
if (reason == fluent.TreeViewItemInvokeReason.selectionToggle) {
|
||||
List childrenIds = findChildFileIdsR(fileInfo['children']);
|
||||
if (item.selected == true) {
|
||||
parentController.selectedIndexes.addAll(childrenIds);
|
||||
parentController.selectedIndexes.addAll(childrenIds.cast());
|
||||
parentController.fileInfos[fileInfo['id']]['size'] = 0;
|
||||
addDescendantFolderSizeR(item.value);
|
||||
addAncestorFolderSizeR(item.value, fileInfo['size']);
|
||||
@@ -178,13 +178,15 @@ class FileListView extends GetView {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
// style: context.textTheme.titleSmall,
|
||||
)),
|
||||
Text(
|
||||
Util.fmtByte(
|
||||
fileInfo['size'],
|
||||
),
|
||||
// style: context.textTheme.labelMedium,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
fileInfo['size'] > 0
|
||||
? Text(
|
||||
Util.fmtByte(
|
||||
fileInfo['size'],
|
||||
),
|
||||
// style: context.textTheme.labelMedium,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
]),
|
||||
));
|
||||
}
|
||||
@@ -251,7 +253,7 @@ class FileListView extends GetView {
|
||||
idNext++;
|
||||
}
|
||||
parentController.fileInfos.value = infos;
|
||||
parentController.selectedIndexes.value = selectedFileIds;
|
||||
parentController.selectedIndexes.value = selectedFileIds.cast();
|
||||
parentController.openedFolders.value = openedFolders;
|
||||
List<fluent.TreeViewItem> treeItems = buildTreeViewItemsR(0, -1);
|
||||
return treeItems;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class IconButtonLoading extends StatefulWidget {
|
||||
final Widget icon;
|
||||
final VoidCallback? onPressed;
|
||||
final IconButtonLoadingController controller;
|
||||
|
||||
const IconButtonLoading(
|
||||
{Key? key,
|
||||
required this.icon,
|
||||
required this.onPressed,
|
||||
required this.controller})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<IconButtonLoading> createState() => _IconButtonLoadingState();
|
||||
}
|
||||
|
||||
class _IconButtonLoadingState extends State<IconButtonLoading> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: widget.controller,
|
||||
builder: (context, value, child) {
|
||||
return IconButton(
|
||||
key: widget.key,
|
||||
onPressed: value ? null : widget.onPressed,
|
||||
icon: value
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: widget.icon,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class IconButtonLoadingController extends ValueNotifier<bool> {
|
||||
IconButtonLoadingController() : super(false);
|
||||
|
||||
void start() {
|
||||
value = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
value = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TextButtonLoading extends StatefulWidget {
|
||||
final Widget child;
|
||||
final VoidCallback? onPressed;
|
||||
final TextButtonLoadingController controller;
|
||||
|
||||
const TextButtonLoading(
|
||||
{Key? key,
|
||||
required this.child,
|
||||
required this.onPressed,
|
||||
required this.controller})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<TextButtonLoading> createState() => _TextButtonLoadingState();
|
||||
}
|
||||
|
||||
class _TextButtonLoadingState extends State<TextButtonLoading> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: widget.controller,
|
||||
builder: (context, value, child) {
|
||||
return TextButton(
|
||||
key: widget.key,
|
||||
onPressed: value ? null : widget.onPressed,
|
||||
child: value
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: widget.child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextButtonLoadingController extends ValueNotifier<bool> {
|
||||
TextButtonLoadingController() : super(false);
|
||||
|
||||
void start() {
|
||||
value = true;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
value = false;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user