Skip to content

Commit d0f11ac

Browse files
committed
server: add recycle, add memory fs, improve base option
app: improve grpc_io server: add test for memory, fix pipe not sync
1 parent dde5e26 commit d0f11ac

File tree

28 files changed

+970
-420
lines changed

28 files changed

+970
-420
lines changed

README.org

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
** 功能
2020
- 支持文件列表查看/复制/移动/删除/重命名/上传/下载
2121
- 支持文件多选及操作
22+
- 支持回收站
2223
- 支持视频、音频、图片和文本文件的预览
2324
- 支持文件加密和压缩(*测试中*)
2425
- 支持各存储之间的备份和同步(*测试中*)

app/lib/api/file/widgets/file_view.dart

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class _FileViewState extends ConsumerState<FileView> {
132132
},
133133
child: ListTile(
134134
leading: _buildIcon(row, setting, 0.8),
135-
title: Text(row.name),
135+
title: _buildName(row),
136136
subtitle: Wrap(
137137
spacing: 8,
138138
children: [
@@ -191,11 +191,7 @@ class _FileViewState extends ConsumerState<FileView> {
191191
children: [
192192
_buildIcon(row, setting, 1),
193193
const SizedBox(height: 8),
194-
Text(
195-
row.name,
196-
maxLines: 2,
197-
overflow: TextOverflow.ellipsis,
198-
),
194+
_buildName(row, maxLines: 2),
199195
],
200196
),
201197
),
@@ -211,6 +207,17 @@ class _FileViewState extends ConsumerState<FileView> {
211207
);
212208
}
213209

210+
_buildName(File row, {int? maxLines}) {
211+
if (row.type == "RECYCLE") {
212+
return Text("回收站".tr());
213+
}
214+
return Text(
215+
row.name,
216+
maxLines: maxLines,
217+
overflow: TextOverflow.ellipsis,
218+
);
219+
}
220+
214221
_buildIcon(File row, FileSetting setting, double size) {
215222
if (setting.icon == FileListIcon.circle) {
216223
return Container(

app/lib/api/file/widgets/repo/base.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class BaseForm extends StatefulWidget {
3434

3535
class _BaseFormState extends State<BaseForm> {
3636
late Map<String, dynamic> _option;
37+
late Map<String, dynamic> _recycleOption;
3738
late Map<String, dynamic> _encryptOption;
3839
late Map<String, dynamic> _compressOption;
3940

@@ -42,6 +43,7 @@ class _BaseFormState extends State<BaseForm> {
4243
super.initState();
4344

4445
_option = widget.form.option == "" ? {} : jsonDecode(widget.form.option);
46+
_recycleOption = _option["recycle_option"] ?? {};
4547
_encryptOption = _option["encrypt_option"] ?? {};
4648
_compressOption = _option["compress_option"] ?? {};
4749
}
@@ -73,6 +75,39 @@ class _BaseFormState extends State<BaseForm> {
7375
widget.form.option = jsonEncode(_option);
7476
},
7577
),
78+
ListTile(
79+
title: Text('回收站'.tr()),
80+
subtitle: Text("是否激活回收站".tr()),
81+
trailing: Switch(
82+
value: _option["recycle"] ?? false,
83+
onChanged: (result) {
84+
setState(() {
85+
_option["recycle"] = result;
86+
if (!result) {
87+
_recycleOption = {};
88+
_option.remove("recycle");
89+
_option.remove("recycle_option");
90+
}
91+
});
92+
93+
widget.form.option = jsonEncode(_option);
94+
},
95+
),
96+
),
97+
if (_option["recycle"] ?? false)
98+
CustomFormField(
99+
label: "回收站路径".tr(),
100+
value: _recycleOption["path"],
101+
subtitle: Text("未设置时将在根目录创建.maplerecycle".tr()),
102+
onTap: (result) {
103+
setState(() {
104+
_recycleOption["path"] = result;
105+
_option["recycle_option"] = _recycleOption;
106+
});
107+
108+
widget.form.option = jsonEncode(_option);
109+
},
110+
),
76111
ListTile(
77112
title: Text('文件加密'.tr()),
78113
trailing: Switch(

app/lib/app/grpc_io.dart

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,82 +7,82 @@ import 'package:ffi/ffi.dart';
77

88
import 'package:flutter/services.dart';
99

10+
import '../common/utils/util.dart';
1011
import '../generated/ffi/libserver.dart';
1112

1213
class GrpcService {
1314
static const platform = MethodChannel("honmaple.com/maple_file");
1415

16+
late String _addr;
17+
late grpcapi.ClientChannel _client;
18+
19+
String get addr {
20+
return _addr;
21+
}
22+
23+
grpcapi.ClientChannel get client {
24+
return _client;
25+
}
26+
1527
Future<void> init() async {
16-
if (Platform.isMacOS) {
17-
await _initDesktop();
28+
if (Util.isDesktop) {
29+
_addr = await _initDesktop();
1830
} else {
19-
await _initMobile();
31+
_addr = await _initMobile();
2032
}
33+
_client = _createChannel(addr);
2134
}
2235

23-
_initMobile() async {
36+
Future<String> _initMobile() async {
2437
final directory = await getApplicationDocumentsDirectory();
2538
final Map<String, dynamic> args = {
2639
'path': directory.path,
2740
};
2841

29-
await platform.invokeMethod("Start", args).then((addr) {
30-
_createChannel(addr);
31-
}).catchError((err) {
42+
return await platform.invokeMethod("Start", args).catchError((err) {
3243
print(err);
3344
});
3445
}
3546

3647
// https://docs.flutter.dev/platform-integration/macos/c-interop
37-
_initDesktop() async {
38-
if (Platform.isMacOS) {}
39-
40-
var libname = "libserver.dylib";
48+
Future<String> _initDesktop() async {
49+
var libname = "libserver";
50+
if (Util.isWindows) {
51+
libname += ".dll";
52+
} else if (Util.isMacOS) {
53+
libname += ".dylib";
54+
} else if (Util.isLinux) {
55+
libname += ".so";
56+
}
4157

42-
final lib = LibserverBind(ffi.DynamicLibrary.open(libname));
4358
final directory = await getApplicationDocumentsDirectory();
4459
final path = directory.path;
45-
print(path);
4660

4761
// ffi.Char
62+
final lib = LibserverBind(ffi.DynamicLibrary.open(libname));
4863
final result = lib.Start(path.toNativeUtf8().cast());
4964
if (result.r1.address == ffi.nullptr.address) {
50-
_createChannel(result.r0.cast<Utf8>().toDartString());
51-
} else {
52-
print(result.r1.cast<Utf8>().toDartString());
65+
return Future.value(result.r0.cast<Utf8>().toDartString());
5366
}
67+
return Future.error(result.r1.cast<Utf8>().toDartString());
5468
}
5569

56-
late String _addr;
57-
late grpcapi.ClientChannel _client;
58-
59-
String get addr {
60-
return _addr;
61-
}
62-
63-
grpcapi.ClientChannel get client {
64-
return _client;
65-
}
66-
67-
_createChannel(String addr) {
70+
grpcapi.ClientChannel _createChannel(String addr) {
6871
const options = ChannelOptions(
6972
credentials: ChannelCredentials.insecure(),
7073
);
7174
if (addr.endsWith(".sock")) {
72-
_addr = addr;
73-
_client = ClientChannel(
75+
return ClientChannel(
7476
InternetAddress(addr, type: InternetAddressType.unix),
7577
options: options,
7678
);
77-
} else {
78-
final addrs = addr.split(":");
79-
80-
_addr = addr;
81-
_client = ClientChannel(
82-
addrs[0],
83-
port: int.parse(addrs[1]),
84-
options: options,
85-
);
8679
}
80+
81+
final addrs = addr.split(":");
82+
return ClientChannel(
83+
addrs[0],
84+
port: int.parse(addrs[1]),
85+
options: options,
86+
);
8787
}
8888
}

app/lib/common/utils/path.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import 'dart:io' show Directory, Platform, File;
22

3-
import 'package:flutter/material.dart';
3+
import 'package:external_path/external_path.dart';
44
import 'package:flutter/foundation.dart';
5+
import 'package:flutter/material.dart';
56
import 'package:mime/mime.dart';
67
import 'package:path/path.dart' as filepath;
78
import 'package:path_provider/path_provider.dart';
8-
import 'package:external_path/external_path.dart';
99

1010
class PathUtil {
1111
static Future<String> getDownloadsPath() async {
@@ -70,7 +70,9 @@ class PathUtil {
7070
if (type == null || type == "") {
7171
type = mimeType(name);
7272
}
73-
if (type == "DIR") {
73+
if (type == "RECYCLE") {
74+
return Icons.recycling;
75+
} else if (type == "DIR") {
7476
return Icons.folder;
7577
} else if (type.startsWith("text/")) {
7678
return Icons.note;

app/lib/common/utils/util.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Util {
1515
return !kIsWeb && Platform.isAndroid;
1616
}
1717

18-
static bool get isMacos {
18+
static bool get isMacOS {
1919
return !kIsWeb && Platform.isMacOS;
2020
}
2121

server/internal/api/file/fs/move.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7-
"strings"
87

98
"github.com/honmaple/maple-file/server/pkg/driver"
109
"github.com/honmaple/maple-file/server/pkg/runner"
@@ -32,8 +31,8 @@ func (opt *MoveTaskOption) Execute(task runner.Task, fs FS) error {
3231
return err
3332
}
3433

35-
if strings.TrimSuffix(opt.SrcPath, srcPath) == strings.TrimSuffix(opt.DstPath, dstPath) {
36-
return srcFS.Move(task.Context(), srcPath, dstPath)
34+
if srcFS == dstFS {
35+
return srcFS.Copy(task.Context(), srcPath, dstPath)
3736
}
3837
return move(task, srcFS, srcPath, dstFS, dstPath)
3938
}

server/internal/api/file/service/file.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"io"
99
"path/filepath"
1010
"strings"
11-
"time"
1211

1312
"github.com/honmaple/maple-file/server/internal/api/file/fs"
1413
pb "github.com/honmaple/maple-file/server/internal/proto/api/file"
@@ -183,8 +182,6 @@ func (srv *Service) upload(ctx context.Context, req *pb.FileRequest, reader io.R
183182
return nil, err
184183
}
185184

186-
time.Sleep(time.Second)
187-
188185
info, err := srv.fs.Get(ctx, filepath.Join(req.GetPath(), filename))
189186
if err != nil {
190187
return nil, err

server/internal/api/file/service/util.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,6 @@ func infoToFile(m driver.File) *pb.File {
5858
CreatedAt: timestamppb.New(m.ModTime()),
5959
UpdatedAt: timestamppb.New(m.ModTime()),
6060
}
61-
if m.IsDir() {
62-
file.Type = "DIR"
63-
}
6461
return file
6562
}
6663

server/pkg/driver/alist/alist.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/honmaple/maple-file/server/pkg/driver"
1313
"github.com/honmaple/maple-file/server/pkg/driver/base"
14+
"github.com/honmaple/maple-file/server/pkg/util"
1415
httputil "github.com/honmaple/maple-file/server/pkg/util/http"
1516
"github.com/tidwall/gjson"
1617
)
@@ -175,7 +176,7 @@ func (d *Alist) Open(path string) (driver.FileReader, error) {
175176
}
176177

177178
func (d *Alist) Create(path string) (driver.FileWriter, error) {
178-
r, w := io.Pipe()
179+
r, w := util.Pipe()
179180
go func() {
180181
resp, err := d.request(context.Background(), http.MethodPut, "/api/fs/put", httputil.WithBody(r), httputil.WithNeverTimeout(), httputil.WithRequest(func(req *http.Request) {
181182
req.Header.Set("File-Path", path)

0 commit comments

Comments
 (0)