mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||
import 'package:flutter_hbb/models/chat_model.dart';
|
||||
import 'package:flutter_hbb/models/cm_file_model.dart';
|
||||
import 'package:flutter_hbb/utils/platform_channel.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||
@@ -482,8 +483,8 @@ class _CmHeaderState extends State<_CmHeader>
|
||||
client.type_() != ClientType.file),
|
||||
child: IconButton(
|
||||
onPressed: () => checkClickTime(client.id, () {
|
||||
if (client.type_() != ClientType.file) {
|
||||
gFFI.chatModel.toggleCMSidePage();
|
||||
if (client.type_() == ClientType.file) {
|
||||
gFFI.chatModel.toggleCMFilePage();
|
||||
} else {
|
||||
gFFI.chatModel
|
||||
.toggleCMChatPage(MessageKey(client.peerId, client.id));
|
||||
@@ -975,6 +976,49 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
|
||||
);
|
||||
}
|
||||
|
||||
iconLabel(CmFileLog item) {
|
||||
switch (item.action) {
|
||||
case CmFileAction.none:
|
||||
return Container();
|
||||
case CmFileAction.localToRemote:
|
||||
case CmFileAction.remoteToLocal:
|
||||
return Column(
|
||||
children: [
|
||||
Transform.rotate(
|
||||
angle: item.action == CmFileAction.remoteToLocal ? 0 : pi,
|
||||
child: SvgPicture.asset(
|
||||
"assets/arrow.svg",
|
||||
color: Theme.of(context).tabBarTheme.labelColor,
|
||||
),
|
||||
),
|
||||
Text(item.action == CmFileAction.remoteToLocal
|
||||
? translate('Send')
|
||||
: translate('Receive'))
|
||||
],
|
||||
);
|
||||
case CmFileAction.remove:
|
||||
return Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.delete,
|
||||
color: Theme.of(context).tabBarTheme.labelColor,
|
||||
),
|
||||
Text(translate('Delete'))
|
||||
],
|
||||
);
|
||||
case CmFileAction.createDir:
|
||||
return Column(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.create_new_folder,
|
||||
color: Theme.of(context).tabBarTheme.labelColor,
|
||||
),
|
||||
Text(translate('Create Folder'))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget statusList() {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size(200, double.infinity),
|
||||
@@ -983,7 +1027,7 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
|
||||
child: Obx(
|
||||
() {
|
||||
final jobTable = gFFI.cmFileModel.currentJobTable;
|
||||
statusListView(List<JobProgress> jobs) => ListView.builder(
|
||||
statusListView(List<CmFileLog> jobs) => ListView.builder(
|
||||
controller: ScrollController(),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = jobs[index];
|
||||
@@ -998,22 +1042,7 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 50,
|
||||
child: Column(
|
||||
children: [
|
||||
Transform.rotate(
|
||||
angle: item.isRemoteToLocal ? 0 : pi,
|
||||
child: SvgPicture.asset(
|
||||
"assets/arrow.svg",
|
||||
color: Theme.of(context)
|
||||
.tabBarTheme
|
||||
.labelColor,
|
||||
),
|
||||
),
|
||||
Text(item.isRemoteToLocal
|
||||
? translate('Send')
|
||||
: translate('Receive'))
|
||||
],
|
||||
),
|
||||
child: iconLabel(item),
|
||||
).paddingOnly(left: 15),
|
||||
const SizedBox(
|
||||
width: 16.0,
|
||||
@@ -1048,8 +1077,9 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> {
|
||||
),
|
||||
),
|
||||
Offstage(
|
||||
offstage:
|
||||
item.state == JobState.inProgress,
|
||||
offstage: !(item.isTransfer() &&
|
||||
item.state !=
|
||||
JobState.inProgress),
|
||||
child: Text(
|
||||
translate(
|
||||
item.display(),
|
||||
|
||||
@@ -285,6 +285,10 @@ class ChatModel with ChangeNotifier {
|
||||
await toggleCMSidePage();
|
||||
}
|
||||
|
||||
toggleCMFilePage() async {
|
||||
await toggleCMSidePage();
|
||||
}
|
||||
|
||||
var _togglingCMSidePage = false; // protect order for await
|
||||
toggleCMSidePage() async {
|
||||
if (_togglingCMSidePage) return false;
|
||||
@@ -296,6 +300,13 @@ class ChatModel with ChangeNotifier {
|
||||
await windowManager.setSizeAlignment(
|
||||
kConnectionManagerWindowSizeClosedChat, Alignment.topRight);
|
||||
} else {
|
||||
final currentSelectedTab =
|
||||
gFFI.serverModel.tabController.state.value.selectedTabInfo;
|
||||
final client = parent.target?.serverModel.clients.firstWhereOrNull(
|
||||
(client) => client.id.toString() == currentSelectedTab.key);
|
||||
if (client != null) {
|
||||
client.unreadChatMessageCount.value = 0;
|
||||
}
|
||||
requestChatInputFocus();
|
||||
await windowManager.show();
|
||||
await windowManager.setSizeAlignment(
|
||||
|
||||
@@ -10,8 +10,8 @@ import 'file_model.dart';
|
||||
|
||||
class CmFileModel {
|
||||
final WeakReference<FFI> parent;
|
||||
final currentJobTable = RxList<JobProgress>();
|
||||
final _jobTables = HashMap<int, RxList<JobProgress>>.fromEntries([]);
|
||||
final currentJobTable = RxList<CmFileLog>();
|
||||
final _jobTables = HashMap<int, RxList<CmFileLog>>.fromEntries([]);
|
||||
Stopwatch stopwatch = Stopwatch();
|
||||
int _lastElapsed = 0;
|
||||
|
||||
@@ -19,14 +19,24 @@ class CmFileModel {
|
||||
|
||||
void updateCurrentClientId(int id) {
|
||||
if (_jobTables[id] == null) {
|
||||
_jobTables[id] = RxList<JobProgress>();
|
||||
_jobTables[id] = RxList<CmFileLog>();
|
||||
}
|
||||
Future.delayed(Duration.zero, () {
|
||||
currentJobTable.value = _jobTables[id]!;
|
||||
});
|
||||
}
|
||||
|
||||
onFileTransferLog(dynamic log) {
|
||||
onFileTransferLog(Map<String, dynamic> evt) {
|
||||
if (evt['transfer'] != null) {
|
||||
_onFileTransfer(evt['transfer']);
|
||||
} else if (evt['remove'] != null) {
|
||||
_onFileRemove(evt['remove']);
|
||||
} else if (evt['create_dir'] != null) {
|
||||
_onDirCreate(evt['create_dir']);
|
||||
}
|
||||
}
|
||||
|
||||
_onFileTransfer(dynamic log) {
|
||||
try {
|
||||
dynamic d = jsonDecode(log);
|
||||
if (!stopwatch.isRunning) stopwatch.start();
|
||||
@@ -56,9 +66,9 @@ class CmFileModel {
|
||||
debugPrint("jobTable should not be null");
|
||||
return;
|
||||
}
|
||||
JobProgress? job = jobTable.firstWhereOrNull((e) => e.id == data.id);
|
||||
CmFileLog? job = jobTable.firstWhereOrNull((e) => e.id == data.id);
|
||||
if (job == null) {
|
||||
job = JobProgress();
|
||||
job = CmFileLog();
|
||||
jobTable.add(job);
|
||||
final currentSelectedTab =
|
||||
gFFI.serverModel.tabController.state.value.selectedTabInfo;
|
||||
@@ -68,14 +78,14 @@ class CmFileModel {
|
||||
}
|
||||
}
|
||||
job.id = data.id;
|
||||
job.isRemoteToLocal = data.isRemote;
|
||||
job.action =
|
||||
data.isRemote ? CmFileAction.remoteToLocal : CmFileAction.localToRemote;
|
||||
job.fileName = data.path;
|
||||
job.totalSize = data.totalSize;
|
||||
job.finishedSize = data.finishedSize;
|
||||
if (job.finishedSize > data.totalSize) {
|
||||
job.finishedSize = data.totalSize;
|
||||
}
|
||||
job.isRemoteToLocal = data.isRemote;
|
||||
|
||||
if (job.finishedSize > 0) {
|
||||
if (job.finishedSize < job.totalSize) {
|
||||
@@ -99,6 +109,112 @@ class CmFileModel {
|
||||
}
|
||||
jobTable.refresh();
|
||||
}
|
||||
|
||||
_onFileRemove(dynamic log) {
|
||||
try {
|
||||
dynamic d = jsonDecode(log);
|
||||
FileActionLog data = FileActionLog.fromJson(d);
|
||||
Client? client =
|
||||
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == data.connId);
|
||||
var jobTable = _jobTables[data.connId];
|
||||
if (jobTable == null) {
|
||||
debugPrint("jobTable should not be null");
|
||||
return;
|
||||
}
|
||||
int removeUnreadCount = 0;
|
||||
if (data.dir) {
|
||||
removeUnreadCount = jobTable
|
||||
.where((e) =>
|
||||
e.action == CmFileAction.remove &&
|
||||
e.fileName.startsWith(data.path))
|
||||
.length;
|
||||
jobTable.removeWhere((e) =>
|
||||
e.action == CmFileAction.remove &&
|
||||
e.fileName.startsWith(data.path));
|
||||
}
|
||||
jobTable.add(CmFileLog()
|
||||
..id = data.id
|
||||
..fileName = data.path
|
||||
..action = CmFileAction.remove
|
||||
..state = JobState.done);
|
||||
final currentSelectedTab =
|
||||
gFFI.serverModel.tabController.state.value.selectedTabInfo;
|
||||
if (!(gFFI.chatModel.isShowCMSidePage &&
|
||||
currentSelectedTab.key == data.connId.toString())) {
|
||||
// Wrong number if unreadCount changes during deletion, which rarely happens
|
||||
RxInt? rx = client?.unreadChatMessageCount;
|
||||
if (rx != null) {
|
||||
if (rx.value >= removeUnreadCount) {
|
||||
rx.value -= removeUnreadCount;
|
||||
}
|
||||
rx.value += 1;
|
||||
}
|
||||
}
|
||||
jobTable.refresh();
|
||||
} catch (e) {
|
||||
debugPrint('$e');
|
||||
}
|
||||
}
|
||||
|
||||
_onDirCreate(dynamic log) {
|
||||
try {
|
||||
dynamic d = jsonDecode(log);
|
||||
FileActionLog data = FileActionLog.fromJson(d);
|
||||
Client? client =
|
||||
gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == data.connId);
|
||||
var jobTable = _jobTables[data.connId];
|
||||
if (jobTable == null) {
|
||||
debugPrint("jobTable should not be null");
|
||||
return;
|
||||
}
|
||||
jobTable.add(CmFileLog()
|
||||
..id = data.id
|
||||
..fileName = data.path
|
||||
..action = CmFileAction.createDir
|
||||
..state = JobState.done);
|
||||
final currentSelectedTab =
|
||||
gFFI.serverModel.tabController.state.value.selectedTabInfo;
|
||||
if (!(gFFI.chatModel.isShowCMSidePage &&
|
||||
currentSelectedTab.key == data.connId.toString())) {
|
||||
client?.unreadChatMessageCount.value += 1;
|
||||
}
|
||||
jobTable.refresh();
|
||||
} catch (e) {
|
||||
debugPrint('$e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CmFileAction {
|
||||
none,
|
||||
remoteToLocal,
|
||||
localToRemote,
|
||||
remove,
|
||||
createDir,
|
||||
}
|
||||
|
||||
class CmFileLog {
|
||||
JobState state = JobState.none;
|
||||
var id = 0;
|
||||
var speed = 0.0;
|
||||
var finishedSize = 0;
|
||||
var totalSize = 0;
|
||||
CmFileAction action = CmFileAction.none;
|
||||
var fileName = "";
|
||||
var err = "";
|
||||
int lastTransferredSize = 0;
|
||||
|
||||
String display() {
|
||||
if (state == JobState.done && err == "skipped") {
|
||||
return translate("Skipped");
|
||||
}
|
||||
return state.display();
|
||||
}
|
||||
|
||||
bool isTransfer() {
|
||||
return action == CmFileAction.remoteToLocal ||
|
||||
action == CmFileAction.localToRemote;
|
||||
}
|
||||
}
|
||||
|
||||
class TransferJobSerdeData {
|
||||
@@ -140,3 +256,25 @@ class TransferJobSerdeData {
|
||||
error: d['error'] ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
class FileActionLog {
|
||||
int id = 0;
|
||||
int connId = 0;
|
||||
String path = '';
|
||||
bool dir = false;
|
||||
|
||||
FileActionLog({
|
||||
required this.connId,
|
||||
required this.id,
|
||||
required this.path,
|
||||
required this.dir,
|
||||
});
|
||||
|
||||
FileActionLog.fromJson(dynamic d)
|
||||
: this(
|
||||
connId: d['connId'] ?? 0,
|
||||
id: d['id'] ?? 0,
|
||||
path: d['path'] ?? '',
|
||||
dir: d['dir'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ class FfiModel with ChangeNotifier {
|
||||
}
|
||||
} else if (name == "cm_file_transfer_log") {
|
||||
if (isDesktop) {
|
||||
gFFI.cmFileModel.onFileTransferLog(evt['log']);
|
||||
gFFI.cmFileModel.onFileTransferLog(evt);
|
||||
}
|
||||
} else {
|
||||
debugPrint('Unknown event name: $name');
|
||||
|
||||
Reference in New Issue
Block a user