From 8b46639ef657a54b4d8724a1eb3a5daee6d9abeb Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 8 Mar 2023 00:49:14 +0900 Subject: [PATCH 01/27] refactor file_manager --- .../lib/desktop/pages/file_manager_page.dart | 1276 ++++++++--------- flutter/lib/models/file_model.dart | 69 +- 2 files changed, 683 insertions(+), 662 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 8bb57145f..8d9cd0a5c 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -61,53 +61,15 @@ class FileManagerPage extends StatefulWidget { class _FileManagerPageState extends State with AutomaticKeepAliveClientMixin { - final _localSelectedItems = SelectedItems(); - final _remoteSelectedItems = SelectedItems(); - - final _locationStatusLocal = LocationStatus.bread.obs; - final _locationStatusRemote = LocationStatus.bread.obs; - final _locationNodeLocal = FocusNode(debugLabel: "locationNodeLocal"); - final _locationNodeRemote = FocusNode(debugLabel: "locationNodeRemote"); - final _locationBarKeyLocal = GlobalKey(debugLabel: "locationBarKeyLocal"); - final _locationBarKeyRemote = GlobalKey(debugLabel: "locationBarKeyRemote"); - final _searchTextLocal = "".obs; - final _searchTextRemote = "".obs; - final _breadCrumbScrollerLocal = ScrollController(); - final _breadCrumbScrollerRemote = ScrollController(); final _mouseFocusScope = Rx(MouseFocusScope.none); - final _keyboardNodeLocal = FocusNode(debugLabel: "keyboardNodeLocal"); - final _keyboardNodeRemote = FocusNode(debugLabel: "keyboardNodeRemote"); - final _listSearchBufferLocal = TimeoutStringBuffer(); - final _listSearchBufferRemote = TimeoutStringBuffer(); - final _nameColWidthLocal = kDesktopFileTransferNameColWidth.obs; - final _modifiedColWidthLocal = kDesktopFileTransferModifiedColWidth.obs; - final _nameColWidthRemote = kDesktopFileTransferNameColWidth.obs; - final _modifiedColWidthRemote = kDesktopFileTransferModifiedColWidth.obs; - - /// [_lastClickTime], [_lastClickEntry] help to handle double click - int _lastClickTime = - DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000; - Entry? _lastClickEntry; final _dropMaskVisible = false.obs; // TODO impl drop mask final _overlayKeyState = OverlayKeyState(); - ScrollController getBreadCrumbScrollController(bool isLocal) { - return isLocal ? _breadCrumbScrollerLocal : _breadCrumbScrollerRemote; - } - - GlobalKey getLocationBarKey(bool isLocal) { - return isLocal ? _locationBarKeyLocal : _locationBarKeyRemote; - } - late FFI _ffi; FileModel get model => _ffi.fileModel; - SelectedItems getSelectedItems(bool isLocal) { - return isLocal ? _localSelectedItems : _remoteSelectedItems; - } - @override void initState() { super.initState(); @@ -123,9 +85,6 @@ class _FileManagerPageState extends State } debugPrint("File manager page init success with id ${widget.id}"); model.onDirChanged = breadCrumbScrollToEnd; - // register location listener - _locationNodeLocal.addListener(onLocalLocationFocusChanged); - _locationNodeRemote.addListener(onRemoteLocationFocusChanged); _ffi.dialogManager.setOverlayState(_overlayKeyState); } @@ -138,17 +97,19 @@ class _FileManagerPageState extends State Wakelock.disable(); } Get.delete(tag: 'ft_${widget.id}'); - _locationNodeLocal.removeListener(onLocalLocationFocusChanged); - _locationNodeRemote.removeListener(onRemoteLocationFocusChanged); - _locationNodeLocal.dispose(); - _locationNodeRemote.dispose(); }); super.dispose(); } + @override + bool get wantKeepAlive => true; + @override Widget build(BuildContext context) { super.build(context); + // TODO + final localController = FileController(isLocal: true); + final remoteController = FileController(isLocal: false); return Overlay(key: _overlayKeyState.key, initialEntries: [ OverlayEntry(builder: (_) { return ChangeNotifierProvider.value( @@ -158,8 +119,14 @@ class _FileManagerPageState extends State backgroundColor: Theme.of(context).scaffoldBackgroundColor, body: Row( children: [ - Flexible(flex: 3, child: body(isLocal: true)), - Flexible(flex: 3, child: body(isLocal: false)), + Flexible( + flex: 3, + child: dropArea(FileManagerView( + localController, _ffi, _mouseFocusScope))), + Flexible( + flex: 3, + child: dropArea(FileManagerView( + remoteController, _ffi, _mouseFocusScope))), Flexible(flex: 2, child: statusList()) ], ), @@ -169,401 +136,17 @@ class _FileManagerPageState extends State ]); } - Widget menu({bool isLocal = false}) { - var menuPos = RelativeRect.fill; - - final List> items = [ - MenuEntrySwitch( - switchType: SwitchType.scheckbox, - text: translate("Show Hidden Files"), - getter: () async { - return model.getCurrentShowHidden(isLocal); - }, - setter: (bool v) async { - model.toggleShowHidden(local: isLocal); - }, - padding: kDesktopMenuPadding, - dismissOnClicked: true, - ), - MenuEntryButton( - childBuilder: (style) => Text(translate("Select All"), style: style), - proc: () => setState(() => getSelectedItems(isLocal) - .selectAll(model.getCurrentDir(isLocal).entries)), - padding: kDesktopMenuPadding, - dismissOnClicked: true), - MenuEntryButton( - childBuilder: (style) => - Text(translate("Unselect All"), style: style), - proc: () => setState(() => getSelectedItems(isLocal).clear()), - padding: kDesktopMenuPadding, - dismissOnClicked: true) - ]; - - return Listener( - onPointerDown: (e) { - final x = e.position.dx; - final y = e.position.dy; - menuPos = RelativeRect.fromLTRB(x, y, x, y); - }, - child: MenuButton( - onPressed: () => mod_menu.showMenu( - context: context, - position: menuPos, - items: items - .map( - (e) => e.build( - context, - MenuConfig( - commonColor: CustomPopupMenuTheme.commonColor, - height: CustomPopupMenuTheme.height, - dividerHeight: CustomPopupMenuTheme.dividerHeight), - ), - ) - .expand((i) => i) - .toList(), - elevation: 8, - ), - child: SvgPicture.asset( - "assets/dots.svg", - color: Theme.of(context).tabBarTheme.labelColor, - ), - color: Theme.of(context).cardColor, - hoverColor: Theme.of(context).hoverColor, - ), - ); - } - - Widget body({bool isLocal = false}) { - final scrollController = ScrollController(); - return Container( - margin: const EdgeInsets.all(16.0), - padding: const EdgeInsets.all(8.0), - child: DropTarget( - onDragDone: (detail) => handleDragDone(detail, isLocal), + Widget dropArea(FileManagerView fileView) { + return DropTarget( + onDragDone: (detail) => + handleDragDone(detail, fileView.controller.isLocal), onDragEntered: (enter) { _dropMaskVisible.value = true; }, onDragExited: (exit) { _dropMaskVisible.value = false; }, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - headTools(isLocal), - Expanded( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: _buildFileList(context, isLocal, scrollController), - ) - ], - ), - ), - ], - ), - ), - ); - } - - Widget _buildFileList( - BuildContext context, bool isLocal, ScrollController scrollController) { - final fd = model.getCurrentDir(isLocal); - final entries = fd.entries; - final selectedEntries = getSelectedItems(isLocal); - - return MouseRegion( - onEnter: (evt) { - _mouseFocusScope.value = - isLocal ? MouseFocusScope.local : MouseFocusScope.remote; - if (isLocal) { - _keyboardNodeLocal.requestFocus(); - } else { - _keyboardNodeRemote.requestFocus(); - } - }, - onExit: (evt) { - _mouseFocusScope.value = MouseFocusScope.none; - }, - child: ListSearchActionListener( - node: isLocal ? _keyboardNodeLocal : _keyboardNodeRemote, - buffer: isLocal ? _listSearchBufferLocal : _listSearchBufferRemote, - onNext: (buffer) { - debugPrint("searching next for $buffer"); - assert(buffer.length == 1); - assert(selectedEntries.length <= 1); - var skipCount = 0; - if (selectedEntries.items.isNotEmpty) { - final index = entries.indexOf(selectedEntries.items.first); - if (index < 0) { - return; - } - skipCount = index + 1; - } - var searchResult = entries.skip(skipCount).where( - (element) => element.name.toLowerCase().startsWith(buffer)); - if (searchResult.isEmpty) { - // cannot find next, lets restart search from head - debugPrint("restart search from head"); - searchResult = entries.where( - (element) => element.name.toLowerCase().startsWith(buffer)); - } - if (searchResult.isEmpty) { - setState(() { - getSelectedItems(isLocal).clear(); - }); - return; - } - _jumpToEntry(isLocal, searchResult.first, scrollController, - kDesktopFileTransferRowHeight); - }, - onSearch: (buffer) { - debugPrint("searching for $buffer"); - final selectedEntries = getSelectedItems(isLocal); - final searchResult = entries.where( - (element) => element.name.toLowerCase().startsWith(buffer)); - selectedEntries.clear(); - if (searchResult.isEmpty) { - setState(() { - getSelectedItems(isLocal).clear(); - }); - return; - } - _jumpToEntry(isLocal, searchResult.first, scrollController, - kDesktopFileTransferRowHeight); - }, - child: ObxValue( - (searchText) { - final filteredEntries = searchText.isNotEmpty - ? entries.where((element) { - return element.name.contains(searchText.value); - }).toList(growable: false) - : entries; - final rows = filteredEntries.map((entry) { - final sizeStr = - entry.isFile ? readableFileSize(entry.size.toDouble()) : ""; - final lastModifiedStr = entry.isDrive - ? " " - : "${entry.lastModified().toString().replaceAll(".000", "")} "; - final isSelected = selectedEntries.contains(entry); - return Padding( - padding: EdgeInsets.symmetric(vertical: 1), - child: Container( - decoration: BoxDecoration( - color: isSelected - ? Theme.of(context).hoverColor - : Theme.of(context).cardColor, - borderRadius: BorderRadius.all( - Radius.circular(5.0), - ), - ), - key: ValueKey(entry.name), - height: kDesktopFileTransferRowHeight, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: InkWell( - child: Row( - children: [ - GestureDetector( - child: Obx( - () => Container( - width: isLocal - ? _nameColWidthLocal.value - : _nameColWidthRemote.value, - child: Tooltip( - waitDuration: - Duration(milliseconds: 500), - message: entry.name, - child: Row(children: [ - entry.isDrive - ? Image( - image: iconHardDrive, - fit: BoxFit.scaleDown, - color: Theme.of(context) - .iconTheme - .color - ?.withOpacity(0.7)) - .paddingAll(4) - : SvgPicture.asset( - entry.isFile - ? "assets/file.svg" - : "assets/folder.svg", - color: Theme.of(context) - .tabBarTheme - .labelColor, - ), - Expanded( - child: Text( - entry.name.nonBreaking, - overflow: - TextOverflow.ellipsis)) - ]), - )), - ), - onTap: () { - final items = getSelectedItems(isLocal); - // handle double click - if (_checkDoubleClick(entry)) { - openDirectory(entry.path, - isLocal: isLocal); - items.clear(); - return; - } - _onSelectedChanged( - items, filteredEntries, entry, isLocal); - }, - ), - SizedBox( - width: 2.0, - ), - GestureDetector( - child: Obx( - () => SizedBox( - width: isLocal - ? _modifiedColWidthLocal.value - : _modifiedColWidthRemote.value, - child: Tooltip( - waitDuration: - Duration(milliseconds: 500), - message: lastModifiedStr, - child: Text( - lastModifiedStr, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12, - color: MyTheme.darkGray, - ), - )), - ), - ), - ), - // Divider from header. - SizedBox( - width: 2.0, - ), - Expanded( - // width: 100, - child: GestureDetector( - child: Tooltip( - waitDuration: Duration(milliseconds: 500), - message: sizeStr, - child: Text( - sizeStr, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 10, - color: MyTheme.darkGray), - ), - ), - ), - ), - ], - ), - ), - ), - ], - )), - ); - }).toList(growable: false); - - return Column( - children: [ - // Header - Row( - children: [ - Expanded(child: _buildFileBrowserHeader(context, isLocal)), - ], - ), - // Body - Expanded( - child: ListView.builder( - controller: scrollController, - itemExtent: kDesktopFileTransferRowHeight, - itemBuilder: (context, index) { - return rows[index]; - }, - itemCount: rows.length, - ), - ), - ], - ); - }, - isLocal ? _searchTextLocal : _searchTextRemote, - ), - ), - ); - } - - void _jumpToEntry(bool isLocal, Entry entry, - ScrollController scrollController, double rowHeight) { - final entries = model.getCurrentDir(isLocal).entries; - final index = entries.indexOf(entry); - if (index == -1) { - debugPrint("entry is not valid: ${entry.path}"); - } - final selectedEntries = getSelectedItems(isLocal); - final searchResult = entries.where((element) => element == entry); - selectedEntries.clear(); - if (searchResult.isEmpty) { - return; - } - final offset = min( - max(scrollController.position.minScrollExtent, - entries.indexOf(searchResult.first) * rowHeight), - scrollController.position.maxScrollExtent); - scrollController.jumpTo(offset); - setState(() { - selectedEntries.add(isLocal, searchResult.first); - debugPrint("focused on ${searchResult.first.name}"); - }); - } - - void _onSelectedChanged(SelectedItems selectedItems, List entries, - Entry entry, bool isLocal) { - final isCtrlDown = RawKeyboard.instance.keysPressed - .contains(LogicalKeyboardKey.controlLeft); - final isShiftDown = - RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftLeft); - if (isCtrlDown) { - if (selectedItems.contains(entry)) { - selectedItems.remove(entry); - } else { - selectedItems.add(isLocal, entry); - } - } else if (isShiftDown) { - final List indexGroup = []; - for (var selected in selectedItems.items) { - indexGroup.add(entries.indexOf(selected)); - } - indexGroup.add(entries.indexOf(entry)); - indexGroup.removeWhere((e) => e == -1); - final maxIndex = indexGroup.reduce(max); - final minIndex = indexGroup.reduce(min); - selectedItems.clear(); - entries - .getRange(minIndex, maxIndex + 1) - .forEach((e) => selectedItems.add(isLocal, e)); - } else { - selectedItems.clear(); - selectedItems.add(isLocal, entry); - } - setState(() {}); - } - - bool _checkDoubleClick(Entry entry) { - final current = DateTime.now().millisecondsSinceEpoch; - final elapsed = current - _lastClickTime; - _lastClickTime = current; - if (_lastClickEntry == entry) { - if (elapsed < bind.getDoubleClickTime()) { - return true; - } - } else { - _lastClickEntry = entry; - } - return false; + child: fileView); } Widget generateCard(Widget child) { @@ -749,11 +332,123 @@ class _FileManagerPageState extends State ); } - Widget headTools(bool isLocal) { - final locationStatus = - isLocal ? _locationStatusLocal : _locationStatusRemote; - final locationFocus = isLocal ? _locationNodeLocal : _locationNodeRemote; - final selectedItems = getSelectedItems(isLocal); + void handleDragDone(DropDoneDetails details, bool isLocal) { + if (isLocal) { + // ignore local + return; + } + var items = SelectedItems(); + for (var file in details.files) { + final f = File(file.path); + items.add( + true, + Entry() + ..path = file.path + ..name = file.name + ..size = + FileSystemEntity.isDirectorySync(f.path) ? 0 : f.lengthSync()); + } + model.sendFiles(items, isRemote: false); + } +} + +class FileManagerView extends StatefulWidget { + final FileController controller; + final FFI _ffi; + final Rx _mouseFocusScope; + + FileManagerView(this.controller, this._ffi, this._mouseFocusScope); + + @override + State createState() => _FileManagerViewState(); +} + +class _FileManagerViewState extends State { + final _selectedItems = SelectedItems(); + final _locationStatus = LocationStatus.bread.obs; + final _locationNode = FocusNode(); + final _locationBarKey = GlobalKey(); + final _searchText = "".obs; + final _breadCrumbScroller = ScrollController(); + final _keyboardNode = FocusNode(); + final _listSearchBuffer = TimeoutStringBuffer(); + final _nameColWidth = kDesktopFileTransferNameColWidth.obs; + final _modifiedColWidth = kDesktopFileTransferModifiedColWidth.obs; + final _fileListScrollController = ScrollController(); + + /// [_lastClickTime], [_lastClickEntry] help to handle double click + var _lastClickTime = + DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000; + Entry? _lastClickEntry; + + FileController get controller => widget.controller; + bool get isLocal => widget.controller.isLocal; + FFI get _ffi => widget._ffi; + + @override + void initState() { + super.initState(); + // register location listener + _locationNode.addListener(onLocationFocusChanged); + } + + @override + void dispose() { + _locationNode.removeListener(onLocationFocusChanged); + _locationNode.dispose(); + _keyboardNode.dispose(); + _breadCrumbScroller.dispose(); + _fileListScrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.all(16.0), + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + headTools(), + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: MouseRegion( + onEnter: (evt) { + widget._mouseFocusScope.value = isLocal + ? MouseFocusScope.local + : MouseFocusScope.remote; + _keyboardNode.requestFocus(); + }, + onExit: (evt) => + widget._mouseFocusScope.value = MouseFocusScope.none, + child: _buildFileList( + context, isLocal, _fileListScrollController), + )) + ], + ), + ), + ], + ), + ); + } + + void onLocationFocusChanged() { + debugPrint("focus changed on local"); + if (_locationNode.hasFocus) { + // ignore + } else { + // lost focus, change to bread + if (_locationStatus.value != LocationStatus.fileSearchBar) { + _locationStatus.value = LocationStatus.bread; + } + } + } + + Widget headTools() { return Container( child: Column( children: [ @@ -812,7 +507,7 @@ class _FileManagerPageState extends State color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, onPressed: () { - selectedItems.clear(); + _selectedItems.clear(); model.goBack(isLocal: isLocal); }, ), @@ -827,7 +522,7 @@ class _FileManagerPageState extends State color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, onPressed: () { - selectedItems.clear(); + _selectedItems.clear(); model.goToParentDirectory(isLocal: isLocal); }, ), @@ -847,14 +542,14 @@ class _FileManagerPageState extends State padding: EdgeInsets.symmetric(vertical: 2.5), child: GestureDetector( onTap: () { - locationStatus.value = - locationStatus.value == LocationStatus.bread + _locationStatus.value = + _locationStatus.value == LocationStatus.bread ? LocationStatus.pathLocation : LocationStatus.bread; Future.delayed(Duration.zero, () { - if (locationStatus.value == + if (_locationStatus.value == LocationStatus.pathLocation) { - locationFocus.requestFocus(); + _locationNode.requestFocus(); } }); }, @@ -863,7 +558,7 @@ class _FileManagerPageState extends State child: Row( children: [ Expanded( - child: locationStatus.value == + child: _locationStatus.value == LocationStatus.bread ? buildBread(isLocal) : buildPathLocation(isLocal)), @@ -877,15 +572,13 @@ class _FileManagerPageState extends State ), ), Obx(() { - switch (locationStatus.value) { + switch (_locationStatus.value) { case LocationStatus.bread: return MenuButton( onPressed: () { - locationStatus.value = LocationStatus.fileSearchBar; - final focusNode = - isLocal ? _locationNodeLocal : _locationNodeRemote; + _locationStatus.value = LocationStatus.fileSearchBar; Future.delayed( - Duration.zero, () => focusNode.requestFocus()); + Duration.zero, () => _locationNode.requestFocus()); }, child: SvgPicture.asset( "assets/search.svg", @@ -908,7 +601,7 @@ class _FileManagerPageState extends State return MenuButton( onPressed: () { onSearchText("", isLocal); - locationStatus.value = LocationStatus.bread; + _locationStatus.value = LocationStatus.bread; }, child: SvgPicture.asset( "assets/close.svg", @@ -1027,11 +720,11 @@ class _FileManagerPageState extends State hoverColor: Theme.of(context).hoverColor, ), MenuButton( - onPressed: validItems(selectedItems) + onPressed: validItems(_selectedItems) ? () async { - await (model.removeAction(selectedItems, + await (model.removeAction(_selectedItems, isLocal: isLocal)); - selectedItems.clear(); + _selectedItems.clear(); } : null, child: SvgPicture.asset( @@ -1051,15 +744,15 @@ class _FileManagerPageState extends State ? EdgeInsets.only(left: 10) : EdgeInsets.only(right: 10)), backgroundColor: MaterialStateProperty.all( - selectedItems.length == 0 + _selectedItems.length == 0 ? MyTheme.accent80 : MyTheme.accent, ), ), - onPressed: validItems(selectedItems) + onPressed: validItems(_selectedItems) ? () { - model.sendFiles(selectedItems, isRemote: !isLocal); - selectedItems.clear(); + model.sendFiles(_selectedItems, isRemote: !isLocal); + _selectedItems.clear(); } : null, icon: isLocal @@ -1067,7 +760,7 @@ class _FileManagerPageState extends State translate('Send'), textAlign: TextAlign.right, style: TextStyle( - color: selectedItems.length == 0 + color: _selectedItems.length == 0 ? Theme.of(context).brightness == Brightness.light ? MyTheme.grayBg : MyTheme.darkGray @@ -1078,7 +771,7 @@ class _FileManagerPageState extends State quarterTurns: 2, child: SvgPicture.asset( "assets/arrow.svg", - color: selectedItems.length == 0 + color: _selectedItems.length == 0 ? Theme.of(context).brightness == Brightness.light ? MyTheme.grayBg : MyTheme.darkGray @@ -1089,7 +782,7 @@ class _FileManagerPageState extends State label: isLocal ? SvgPicture.asset( "assets/arrow.svg", - color: selectedItems.length == 0 + color: _selectedItems.length == 0 ? Theme.of(context).brightness == Brightness.light ? MyTheme.grayBg : MyTheme.darkGray @@ -1098,7 +791,7 @@ class _FileManagerPageState extends State : Text( translate('Receive'), style: TextStyle( - color: selectedItems.length == 0 + color: _selectedItems.length == 0 ? Theme.of(context).brightness == Brightness.light ? MyTheme.grayBg : MyTheme.darkGray @@ -1113,39 +806,440 @@ class _FileManagerPageState extends State ); } - bool validItems(SelectedItems items) { - if (items.length > 0) { - // exclude DirDrive type - return items.items.any((item) => !item.isDrive); + Widget menu({bool isLocal = false}) { + var menuPos = RelativeRect.fill; + + final List> items = [ + MenuEntrySwitch( + switchType: SwitchType.scheckbox, + text: translate("Show Hidden Files"), + getter: () async { + return model.getCurrentShowHidden(isLocal); + }, + setter: (bool v) async { + model.toggleShowHidden(local: isLocal); + }, + padding: kDesktopMenuPadding, + dismissOnClicked: true, + ), + MenuEntryButton( + childBuilder: (style) => Text(translate("Select All"), style: style), + proc: () => setState(() => + _selectedItems.selectAll(model.getCurrentDir(isLocal).entries)), + padding: kDesktopMenuPadding, + dismissOnClicked: true), + MenuEntryButton( + childBuilder: (style) => + Text(translate("Unselect All"), style: style), + proc: () => setState(() => _selectedItems.clear()), + padding: kDesktopMenuPadding, + dismissOnClicked: true) + ]; + + return Listener( + onPointerDown: (e) { + final x = e.position.dx; + final y = e.position.dy; + menuPos = RelativeRect.fromLTRB(x, y, x, y); + }, + child: MenuButton( + onPressed: () => mod_menu.showMenu( + context: context, + position: menuPos, + items: items + .map( + (e) => e.build( + context, + MenuConfig( + commonColor: CustomPopupMenuTheme.commonColor, + height: CustomPopupMenuTheme.height, + dividerHeight: CustomPopupMenuTheme.dividerHeight), + ), + ) + .expand((i) => i) + .toList(), + elevation: 8, + ), + child: SvgPicture.asset( + "assets/dots.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + ), + ); + } + + Widget _buildFileList( + BuildContext context, bool isLocal, ScrollController scrollController) { + final fd = model.getCurrentDir(isLocal); + final entries = fd.entries; + + return ListSearchActionListener( + node: _keyboardNode, + buffer: _listSearchBuffer, + onNext: (buffer) { + debugPrint("searching next for $buffer"); + assert(buffer.length == 1); + assert(_selectedItems.length <= 1); + var skipCount = 0; + if (_selectedItems.items.isNotEmpty) { + final index = entries.indexOf(_selectedItems.items.first); + if (index < 0) { + return; + } + skipCount = index + 1; + } + var searchResult = entries + .skip(skipCount) + .where((element) => element.name.toLowerCase().startsWith(buffer)); + if (searchResult.isEmpty) { + // cannot find next, lets restart search from head + debugPrint("restart search from head"); + searchResult = entries.where( + (element) => element.name.toLowerCase().startsWith(buffer)); + } + if (searchResult.isEmpty) { + setState(() => _selectedItems.clear()); + return; + } + _jumpToEntry(isLocal, searchResult.first, scrollController, + kDesktopFileTransferRowHeight); + }, + onSearch: (buffer) { + debugPrint("searching for $buffer"); + final selectedEntries = _selectedItems; + final searchResult = entries + .where((element) => element.name.toLowerCase().startsWith(buffer)); + selectedEntries.clear(); + if (searchResult.isEmpty) { + setState(() => _selectedItems.clear()); + return; + } + _jumpToEntry(isLocal, searchResult.first, scrollController, + kDesktopFileTransferRowHeight); + }, + child: ObxValue( + (searchText) { + final filteredEntries = searchText.isNotEmpty + ? entries.where((element) { + return element.name.contains(searchText.value); + }).toList(growable: false) + : entries; + final rows = filteredEntries.map((entry) { + final sizeStr = + entry.isFile ? readableFileSize(entry.size.toDouble()) : ""; + final lastModifiedStr = entry.isDrive + ? " " + : "${entry.lastModified().toString().replaceAll(".000", "")} "; + final isSelected = _selectedItems.contains(entry); + return Padding( + padding: EdgeInsets.symmetric(vertical: 1), + child: Container( + decoration: BoxDecoration( + color: isSelected + ? Theme.of(context).hoverColor + : Theme.of(context).cardColor, + borderRadius: BorderRadius.all( + Radius.circular(5.0), + ), + ), + key: ValueKey(entry.name), + height: kDesktopFileTransferRowHeight, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded( + child: InkWell( + child: Row( + children: [ + GestureDetector( + child: Obx( + () => Container( + width: _nameColWidth.value, + child: Tooltip( + waitDuration: + Duration(milliseconds: 500), + message: entry.name, + child: Row(children: [ + entry.isDrive + ? Image( + image: iconHardDrive, + fit: BoxFit.scaleDown, + color: Theme.of(context) + .iconTheme + .color + ?.withOpacity(0.7)) + .paddingAll(4) + : SvgPicture.asset( + entry.isFile + ? "assets/file.svg" + : "assets/folder.svg", + color: Theme.of(context) + .tabBarTheme + .labelColor, + ), + Expanded( + child: Text( + entry.name.nonBreaking, + overflow: + TextOverflow.ellipsis)) + ]), + )), + ), + onTap: () { + final items = _selectedItems; + // handle double click + if (_checkDoubleClick(entry)) { + openDirectory(entry.path, isLocal: isLocal); + items.clear(); + return; + } + _onSelectedChanged( + items, filteredEntries, entry, isLocal); + }, + ), + SizedBox( + width: 2.0, + ), + GestureDetector( + child: Obx( + () => SizedBox( + width: _modifiedColWidth.value, + child: Tooltip( + waitDuration: + Duration(milliseconds: 500), + message: lastModifiedStr, + child: Text( + lastModifiedStr, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12, + color: MyTheme.darkGray, + ), + )), + ), + ), + ), + // Divider from header. + SizedBox( + width: 2.0, + ), + Expanded( + // width: 100, + child: GestureDetector( + child: Tooltip( + waitDuration: Duration(milliseconds: 500), + message: sizeStr, + child: Text( + sizeStr, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 10, + color: MyTheme.darkGray), + ), + ), + ), + ), + ], + ), + ), + ), + ], + )), + ); + }).toList(growable: false); + + return Column( + children: [ + // Header + Row( + children: [ + Expanded(child: _buildFileBrowserHeader(context, isLocal)), + ], + ), + // Body + Expanded( + child: ListView.builder( + controller: scrollController, + itemExtent: kDesktopFileTransferRowHeight, + itemBuilder: (context, index) { + return rows[index]; + }, + itemCount: rows.length, + ), + ), + ], + ); + }, + _searchText, + ), + ); + } + + onSearchText(String searchText, bool isLocal) { + _selectedItems.clear(); + _searchText.value = searchText; + } + + void _jumpToEntry(bool isLocal, Entry entry, + ScrollController scrollController, double rowHeight) { + final entries = model.getCurrentDir(isLocal).entries; + final index = entries.indexOf(entry); + if (index == -1) { + debugPrint("entry is not valid: ${entry.path}"); + } + final selectedEntries = _selectedItems; + final searchResult = entries.where((element) => element == entry); + selectedEntries.clear(); + if (searchResult.isEmpty) { + return; + } + final offset = min( + max(scrollController.position.minScrollExtent, + entries.indexOf(searchResult.first) * rowHeight), + scrollController.position.maxScrollExtent); + scrollController.jumpTo(offset); + setState(() { + selectedEntries.add(isLocal, searchResult.first); + debugPrint("focused on ${searchResult.first.name}"); + }); + } + + void _onSelectedChanged(SelectedItems selectedItems, List entries, + Entry entry, bool isLocal) { + final isCtrlDown = RawKeyboard.instance.keysPressed + .contains(LogicalKeyboardKey.controlLeft); + final isShiftDown = + RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftLeft); + if (isCtrlDown) { + if (selectedItems.contains(entry)) { + selectedItems.remove(entry); + } else { + selectedItems.add(isLocal, entry); + } + } else if (isShiftDown) { + final List indexGroup = []; + for (var selected in selectedItems.items) { + indexGroup.add(entries.indexOf(selected)); + } + indexGroup.add(entries.indexOf(entry)); + indexGroup.removeWhere((e) => e == -1); + final maxIndex = indexGroup.reduce(max); + final minIndex = indexGroup.reduce(min); + selectedItems.clear(); + entries + .getRange(minIndex, maxIndex + 1) + .forEach((e) => selectedItems.add(isLocal, e)); + } else { + selectedItems.clear(); + selectedItems.add(isLocal, entry); + } + setState(() {}); + } + + bool _checkDoubleClick(Entry entry) { + final current = DateTime.now().millisecondsSinceEpoch; + final elapsed = current - _lastClickTime; + _lastClickTime = current; + if (_lastClickEntry == entry) { + if (elapsed < bind.getDoubleClickTime()) { + return true; + } + } else { + _lastClickEntry = entry; } return false; } - @override - bool get wantKeepAlive => true; - - void onLocalLocationFocusChanged() { - debugPrint("focus changed on local"); - if (_locationNodeLocal.hasFocus) { - // ignore - } else { - // lost focus, change to bread - if (_locationStatusLocal.value != LocationStatus.fileSearchBar) { - _locationStatusLocal.value = LocationStatus.bread; - } - } + Widget _buildFileBrowserHeader(BuildContext context, bool isLocal) { + final padding = EdgeInsets.all(1.0); + return SizedBox( + height: kDesktopFileTransferHeaderHeight, + child: Row( + children: [ + Obx( + () => headerItemFunc( + _nameColWidth.value, SortBy.name, translate("Name"), isLocal), + ), + DraggableDivider( + axis: Axis.vertical, + onPointerMove: (dx) { + _nameColWidth.value += dx; + _nameColWidth.value = min(kDesktopFileTransferMaximumWidth, + max(kDesktopFileTransferMinimumWidth, _nameColWidth.value)); + }, + padding: padding, + ), + Obx( + () => headerItemFunc(_modifiedColWidth.value, SortBy.modified, + translate("Modified"), isLocal), + ), + DraggableDivider( + axis: Axis.vertical, + onPointerMove: (dx) { + _modifiedColWidth.value += dx; + _modifiedColWidth.value = min( + kDesktopFileTransferMaximumWidth, + max(kDesktopFileTransferMinimumWidth, + _modifiedColWidth.value)); + }, + padding: padding), + Expanded( + child: + headerItemFunc(null, SortBy.size, translate("Size"), isLocal)) + ], + ), + ); } - void onRemoteLocationFocusChanged() { - debugPrint("focus changed on remote"); - if (_locationNodeRemote.hasFocus) { - // ignore - } else { - // lost focus, change to bread - if (_locationStatusRemote.value != LocationStatus.fileSearchBar) { - _locationStatusRemote.value = LocationStatus.bread; + Widget headerItemFunc( + double? width, SortBy sortBy, String name, bool isLocal) { + final headerTextStyle = + Theme.of(context).dataTableTheme.headingTextStyle ?? TextStyle(); + return ObxValue>( + (ascending) => InkWell( + onTap: () { + if (ascending.value == null) { + ascending.value = true; + } else { + ascending.value = !ascending.value!; + } + model.changeSortStyle(sortBy, + isLocal: isLocal, ascending: ascending.value!); + }, + child: SizedBox( + width: width, + height: kDesktopFileTransferHeaderHeight, + child: Row( + children: [ + Flexible( + flex: 2, + child: Text( + name, + style: headerTextStyle, + overflow: TextOverflow.ellipsis, + ).marginSymmetric(horizontal: 4), + ), + Flexible( + flex: 1, + child: ascending.value != null + ? Icon( + ascending.value! + ? Icons.keyboard_arrow_up_rounded + : Icons.keyboard_arrow_down_rounded, + ) + : const Offstage()) + ], + ), + ), + ), () { + if (model.getSortStyle(isLocal) == sortBy) { + return model.getSortAscending(isLocal).obs; + } else { + return Rx(null); } - } + }()); } Widget buildBread(bool isLocal) { @@ -1156,12 +1250,11 @@ class _FileManagerPageState extends State } openDirectory(path, isLocal: isLocal); }); - final locationBarKey = getLocationBarKey(isLocal); return items.isEmpty ? Offstage() : Row( - key: locationBarKey, + key: _locationBarKey, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( @@ -1169,7 +1262,7 @@ class _FileManagerPageState extends State // handle mouse wheel onPointerSignal: (e) { if (e is PointerScrollEvent) { - final sc = getBreadCrumbScrollController(isLocal); + final sc = _breadCrumbScroller; final scale = Platform.isWindows ? 2 : 4; sc.jumpTo(sc.offset + e.scrollDelta.dy / scale); } @@ -1178,7 +1271,7 @@ class _FileManagerPageState extends State items: items, divider: const Icon(Icons.keyboard_arrow_right_rounded), overflow: ScrollableOverflow( - controller: getBreadCrumbScrollController(isLocal), + controller: _breadCrumbScroller, ), ), ), @@ -1187,9 +1280,9 @@ class _FileManagerPageState extends State message: "", icon: Icons.keyboard_arrow_down_rounded, onTap: () async { - final renderBox = locationBarKey.currentContext + final renderBox = _locationBarKey.currentContext ?.findRenderObject() as RenderBox; - locationBarKey.currentContext?.size; + _locationBarKey.currentContext?.size; final size = renderBox.size; final offset = renderBox.localToGlobal(Offset.zero); @@ -1201,7 +1294,7 @@ class _FileManagerPageState extends State final List menuItems = [ MenuEntryButton( childBuilder: (TextStyle? style) => isPeerWindows - ? buildWindowsThisPC(style) + ? buildWindowsThisPC(context, style) : Text( '/', style: style, @@ -1274,15 +1367,6 @@ class _FileManagerPageState extends State ]); } - Widget buildWindowsThisPC([TextStyle? textStyle]) { - final color = Theme.of(context).iconTheme.color?.withOpacity(0.7); - return Row(children: [ - Icon(Icons.computer, size: 20, color: color), - SizedBox(width: 10), - Text(translate('This PC'), style: textStyle) - ]); - } - List getPathBreadCrumbItems( bool isLocal, void Function(List) onPressed) { final path = model.getCurrentDir(isLocal).path; @@ -1291,7 +1375,7 @@ class _FileManagerPageState extends State if (isWindows && path == '/') { breadCrumbList.add(BreadCrumbItem( content: TextButton( - child: buildWindowsThisPC(), + child: buildWindowsThisPC(context), style: ButtonStyle( minimumSize: MaterialStateProperty.all(Size(0, 0))), onPressed: () => onPressed(['/'])) @@ -1321,10 +1405,9 @@ class _FileManagerPageState extends State breadCrumbScrollToEnd(bool isLocal) { Future.delayed(Duration(milliseconds: 200), () { - final breadCrumbScroller = getBreadCrumbScrollController(isLocal); - if (breadCrumbScroller.hasClients) { - breadCrumbScroller.animateTo( - breadCrumbScroller.position.maxScrollExtent, + if (_breadCrumbScroller.hasClients) { + _breadCrumbScroller.animateTo( + _breadCrumbScroller.position.maxScrollExtent, duration: Duration(milliseconds: 200), curve: Curves.fastLinearToSlowEaseIn); } @@ -1332,26 +1415,22 @@ class _FileManagerPageState extends State } Widget buildPathLocation(bool isLocal) { - final searchTextObs = isLocal ? _searchTextLocal : _searchTextRemote; - final locationStatus = - isLocal ? _locationStatusLocal : _locationStatusRemote; - final focusNode = isLocal ? _locationNodeLocal : _locationNodeRemote; - final text = locationStatus.value == LocationStatus.pathLocation + final text = _locationStatus.value == LocationStatus.pathLocation ? model.getCurrentDir(isLocal).path - : searchTextObs.value; + : _searchText.value; final textController = TextEditingController(text: text) ..selection = TextSelection.collapsed(offset: text.length); return Row( children: [ SvgPicture.asset( - locationStatus.value == LocationStatus.pathLocation + _locationStatus.value == LocationStatus.pathLocation ? "assets/folder.svg" : "assets/search.svg", color: Theme.of(context).tabBarTheme.labelColor, ), Expanded( child: TextField( - focusNode: focusNode, + focusNode: _locationNode, decoration: InputDecoration( border: InputBorder.none, isDense: true, @@ -1363,7 +1442,7 @@ class _FileManagerPageState extends State onSubmitted: (path) { openDirectory(path, isLocal: isLocal); }, - onChanged: locationStatus.value == LocationStatus.fileSearchBar + onChanged: _locationStatus.value == LocationStatus.fileSearchBar ? (searchText) => onSearchText(searchText, isLocal) : null, ), @@ -1372,139 +1451,24 @@ class _FileManagerPageState extends State ); } - onSearchText(String searchText, bool isLocal) { - if (isLocal) { - _localSelectedItems.clear(); - _searchTextLocal.value = searchText; - } else { - _remoteSelectedItems.clear(); - _searchTextRemote.value = searchText; - } - } - - openDirectory(String path, {bool isLocal = false}) { - model.openDirectory(path, isLocal: isLocal); - } - - void handleDragDone(DropDoneDetails details, bool isLocal) { - if (isLocal) { - // ignore local - return; - } - var items = SelectedItems(); - for (var file in details.files) { - final f = File(file.path); - items.add( - true, - Entry() - ..path = file.path - ..name = file.name - ..size = - FileSystemEntity.isDirectorySync(f.path) ? 0 : f.lengthSync()); - } - model.sendFiles(items, isRemote: false); - } - - void refocusKeyboardListener(bool isLocal) { - Future.delayed(Duration.zero, () { - if (isLocal) { - _keyboardNodeLocal.requestFocus(); - } else { - _keyboardNodeRemote.requestFocus(); - } - }); - } - - Widget headerItemFunc( - double? width, SortBy sortBy, String name, bool isLocal) { - final headerTextStyle = - Theme.of(context).dataTableTheme.headingTextStyle ?? TextStyle(); - return ObxValue>( - (ascending) => InkWell( - onTap: () { - if (ascending.value == null) { - ascending.value = true; - } else { - ascending.value = !ascending.value!; - } - model.changeSortStyle(sortBy, - isLocal: isLocal, ascending: ascending.value!); - }, - child: SizedBox( - width: width, - height: kDesktopFileTransferHeaderHeight, - child: Row( - children: [ - Flexible( - flex: 2, - child: Text( - name, - style: headerTextStyle, - overflow: TextOverflow.ellipsis, - ).marginSymmetric(horizontal: 4), - ), - Flexible( - flex: 1, - child: ascending.value != null - ? Icon( - ascending.value! - ? Icons.keyboard_arrow_up_rounded - : Icons.keyboard_arrow_down_rounded, - ) - : const Offstage()) - ], - ), - ), - ), () { - if (model.getSortStyle(isLocal) == sortBy) { - return model.getSortAscending(isLocal).obs; - } else { - return Rx(null); - } - }()); - } - - Widget _buildFileBrowserHeader(BuildContext context, bool isLocal) { - final nameColWidth = isLocal ? _nameColWidthLocal : _nameColWidthRemote; - final modifiedColWidth = - isLocal ? _modifiedColWidthLocal : _modifiedColWidthRemote; - final padding = EdgeInsets.all(1.0); - return SizedBox( - height: kDesktopFileTransferHeaderHeight, - child: Row( - children: [ - Obx( - () => headerItemFunc( - nameColWidth.value, SortBy.name, translate("Name"), isLocal), - ), - DraggableDivider( - axis: Axis.vertical, - onPointerMove: (dx) { - nameColWidth.value += dx; - nameColWidth.value = min(kDesktopFileTransferMaximumWidth, - max(kDesktopFileTransferMinimumWidth, nameColWidth.value)); - }, - padding: padding, - ), - Obx( - () => headerItemFunc(modifiedColWidth.value, SortBy.modified, - translate("Modified"), isLocal), - ), - DraggableDivider( - axis: Axis.vertical, - onPointerMove: (dx) { - modifiedColWidth.value += dx; - modifiedColWidth.value = min( - kDesktopFileTransferMaximumWidth, - max(kDesktopFileTransferMinimumWidth, - modifiedColWidth.value)); - }, - padding: padding), - Expanded( - child: - headerItemFunc(null, SortBy.size, translate("Size"), isLocal)) - ], - ), - ); - } + // openDirectory(String path, {bool isLocal = false}) { + // model.openDirectory(path, isLocal: isLocal); + // } +} + +bool validItems(SelectedItems items) { + if (items.length > 0) { + // exclude DirDrive type + return items.items.any((item) => !item.isDrive); + } + return false; +} + +Widget buildWindowsThisPC(BuildContext context, [TextStyle? textStyle]) { + final color = Theme.of(context).iconTheme.color?.withOpacity(0.7); + return Row(children: [ + Icon(Icons.computer, size: 20, color: color), + SizedBox(width: 10), + Text(translate('This PC'), style: textStyle) + ]); } diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index feaecd356..e8f7cf15b 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -24,6 +24,63 @@ enum SortBy { } } +class JobID { + int _count = 0; + int next() { + _count++; + return _count; + } +} + +class SortStyle { + var by = SortBy.name; + var ascending = true; +} + +class FileModelNew { + static final JobID jobID = JobID(); + final jobTable = RxList.empty(growable: true); + final _jobResultListener = JobResultListener>(); + + final localController = FileController(isLocal: true); + final remoteController = FileController(isLocal: false); + + int getJob(int id) { + return jobTable.indexWhere((element) => element.id == id); + } +} + +class FileController extends FileState with FileAction, UIController { + FileController({required super.isLocal}); +} + +class FileState { + final bool isLocal; + final _fileFetcher = FileFetcher(); + + final options = DirectoryOptions().obs; + final directory = FileDirectory().obs; + + final history = RxList.empty(growable: true); + final sortStyle = SortStyle().obs; + + FileState({required this.isLocal}); +} + +mixin FileAction on FileState { + test() { + final a = _fileFetcher; + final b = isLocal; + } +} + +mixin UIController on FileState { + testUI() { + final a = _fileFetcher; + final b = isLocal; + } +} + class FileModel extends ChangeNotifier { /// mobile, current selected page show on mobile screen var _isSelectedLocal = false; @@ -31,8 +88,8 @@ class FileModel extends ChangeNotifier { /// mobile, select mode state var _selectMode = false; - final _localOption = DirectoryOption(); - final _remoteOption = DirectoryOption(); + final _localOption = DirectoryOptions(); + final _remoteOption = DirectoryOptions(); List localHistory = []; List remoteHistory = []; @@ -49,9 +106,9 @@ class FileModel extends ChangeNotifier { RxList get jobTable => _jobTable; - bool get isLocal => _isSelectedLocal; + bool get isLocal => _isSelectedLocal; // TODO parent - bool get selectMode => _selectMode; + bool get selectMode => _selectMode; // TODO parent JobProgress get jobProgress => _jobProgress; @@ -1179,12 +1236,12 @@ class PathUtil { } } -class DirectoryOption { +class DirectoryOptions { String home; bool showHidden; bool isWindows; - DirectoryOption( + DirectoryOptions( {this.home = "", this.showHidden = false, this.isWindows = false}); clear() { From 2dd4545be0ad1a3ac3e7de1a3ef0afd3e5183c7b Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 8 Mar 2023 21:05:55 +0900 Subject: [PATCH 02/27] refactor file_model.dart --- .../lib/desktop/pages/file_manager_page.dart | 2 +- .../lib/mobile/pages/file_manager_page.dart | 2 +- flutter/lib/models/file_model.dart | 1347 +++++++---------- flutter/lib/models/model.dart | 14 +- 4 files changed, 560 insertions(+), 805 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 8d9cd0a5c..0a8ebdd33 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -90,7 +90,7 @@ class _FileManagerPageState extends State @override void dispose() { - model.onClose().whenComplete(() { + model.close().whenComplete(() { _ffi.close(); _ffi.dialogManager.dismissAll(); if (!Platform.isLinux) { diff --git a/flutter/lib/mobile/pages/file_manager_page.dart b/flutter/lib/mobile/pages/file_manager_page.dart index 7aa9a0005..26e024ca4 100644 --- a/flutter/lib/mobile/pages/file_manager_page.dart +++ b/flutter/lib/mobile/pages/file_manager_page.dart @@ -38,7 +38,7 @@ class _FileManagerPageState extends State { @override void dispose() { - model.onClose().whenComplete(() { + model.close().whenComplete(() { gFFI.close(); gFFI.dialogManager.dismissAll(); Wakelock.disable(); diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index e8f7cf15b..621df4d69 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -37,341 +37,64 @@ class SortStyle { var ascending = true; } -class FileModelNew { - static final JobID jobID = JobID(); - final jobTable = RxList.empty(growable: true); - final _jobResultListener = JobResultListener>(); - - final localController = FileController(isLocal: true); - final remoteController = FileController(isLocal: false); - - int getJob(int id) { - return jobTable.indexWhere((element) => element.id == id); - } -} - -class FileController extends FileState with FileAction, UIController { - FileController({required super.isLocal}); -} - -class FileState { - final bool isLocal; - final _fileFetcher = FileFetcher(); - - final options = DirectoryOptions().obs; - final directory = FileDirectory().obs; - - final history = RxList.empty(growable: true); - final sortStyle = SortStyle().obs; - - FileState({required this.isLocal}); -} - -mixin FileAction on FileState { - test() { - final a = _fileFetcher; - final b = isLocal; - } -} - -mixin UIController on FileState { - testUI() { - final a = _fileFetcher; - final b = isLocal; - } -} - -class FileModel extends ChangeNotifier { - /// mobile, current selected page show on mobile screen - var _isSelectedLocal = false; - - /// mobile, select mode state - var _selectMode = false; - - final _localOption = DirectoryOptions(); - final _remoteOption = DirectoryOptions(); - - List localHistory = []; - List remoteHistory = []; - - var _jobId = 0; - - final _jobProgress = JobProgress(); // from rust update - - /// JobTable - final _jobTable = List.empty(growable: true).obs; - - /// `isLocal` bool - Function(bool)? onDirChanged; - - RxList get jobTable => _jobTable; - - bool get isLocal => _isSelectedLocal; // TODO parent - - bool get selectMode => _selectMode; // TODO parent - - JobProgress get jobProgress => _jobProgress; - - JobState get jobState => _jobProgress.state; - - SortBy _sortStyle = SortBy.name; - - SortBy get sortStyle => _sortStyle; - - SortBy _localSortStyle = SortBy.name; - - bool _localSortAscending = true; - - bool _remoteSortAscending = true; - - SortBy _remoteSortStyle = SortBy.name; - - bool get localSortAscending => _localSortAscending; - - SortBy getSortStyle(bool isLocal) { - return isLocal ? _localSortStyle : _remoteSortStyle; - } - - bool getSortAscending(bool isLocal) { - return isLocal ? _localSortAscending : _remoteSortAscending; - } - - FileDirectory _currentLocalDir = FileDirectory(); - - FileDirectory get currentLocalDir => _currentLocalDir; - - FileDirectory _currentRemoteDir = FileDirectory(); - - FileDirectory get currentRemoteDir => _currentRemoteDir; - - FileDirectory get currentDir => - _isSelectedLocal ? currentLocalDir : currentRemoteDir; - - FileDirectory getCurrentDir(bool isLocal) { - return isLocal ? currentLocalDir : currentRemoteDir; - } - - String getCurrentShortPath(bool isLocal) { - final currentDir = getCurrentDir(isLocal); - final currentHome = getCurrentHome(isLocal); - if (currentDir.path.startsWith(currentHome)) { - var path = currentDir.path.replaceFirst(currentHome, ""); - if (path.isEmpty) return ""; - if (path[0] == "/" || path[0] == "\\") { - // remove more '/' or '\' - path = path.replaceFirst(path[0], ""); - } - return path; - } else { - return currentDir.path.replaceFirst(currentHome, ""); - } - } - - String get currentHome => - _isSelectedLocal ? _localOption.home : _remoteOption.home; - - String getCurrentHome(bool isLocal) { - return isLocal ? _localOption.home : _remoteOption.home; - } - - int getJob(int id) { - return jobTable.indexWhere((element) => element.id == id); - } - - String get currentShortPath { - if (currentDir.path.startsWith(currentHome)) { - var path = currentDir.path.replaceFirst(currentHome, ""); - if (path.isEmpty) return ""; - if (path[0] == "/" || path[0] == "\\") { - // remove more '/' or '\' - path = path.replaceFirst(path[0], ""); - } - return path; - } else { - return currentDir.path.replaceFirst(currentHome, ""); - } - } - - String shortPath(bool isLocal) { - final dir = isLocal ? currentLocalDir : currentRemoteDir; - if (dir.path.startsWith(currentHome)) { - var path = dir.path.replaceFirst(currentHome, ""); - if (path.isEmpty) return ""; - if (path[0] == "/" || path[0] == "\\") { - // remove more '/' or '\' - path = path.replaceFirst(path[0], ""); - } - return path; - } else { - return dir.path.replaceFirst(currentHome, ""); - } - } - - bool getCurrentShowHidden([bool? isLocal]) { - final isLocal_ = isLocal ?? _isSelectedLocal; - return isLocal_ ? _localOption.showHidden : _remoteOption.showHidden; - } - - bool getCurrentIsWindows([bool? isLocal]) { - final isLocal_ = isLocal ?? _isSelectedLocal; - return isLocal_ ? _localOption.isWindows : _remoteOption.isWindows; - } - - final _fileFetcher = FileFetcher(); - - final _jobResultListener = JobResultListener>(); - +class FileModel { final WeakReference parent; - - FileModel(this.parent); - - toggleSelectMode() { - if (jobState == JobState.inProgress) { - return; - } - _selectMode = !_selectMode; - notifyListeners(); + late final String sessionID; + FileModel(this.parent) { + sessionID = parent.target?.id ?? ""; } - togglePage() { - _isSelectedLocal = !_isSelectedLocal; - notifyListeners(); + late final fileFetcher = FileFetcher(sessionID); + late final jobController = JobController(sessionID); + + late final localController = FileController( + isLocal: true, + sessionID: sessionID, + dialogManager: parent.target?.dialogManager, + jobController: jobController, + fileFetcher: fileFetcher); + + late final remoteController = FileController( + isLocal: false, + sessionID: sessionID, + dialogManager: parent.target?.dialogManager, + jobController: jobController, + fileFetcher: fileFetcher); + + Future onReady() async { + await localController.onReady(); + await remoteController.onReady(); } - toggleShowHidden({bool? showHidden, bool? local}) { - final isLocal = local ?? _isSelectedLocal; - if (isLocal) { - _localOption.showHidden = showHidden ?? !_localOption.showHidden; - } else { - _remoteOption.showHidden = showHidden ?? !_remoteOption.showHidden; - } - refresh(isLocal: local); + Future close() async { + parent.target?.dialogManager.dismissAll(); + jobController.close(); + await localController.close(); + await remoteController.close(); } - tryUpdateJobProgress(Map evt) { - try { - int id = int.parse(evt['id']); - if (!isDesktop) { - _jobProgress.id = id; - _jobProgress.fileNum = int.parse(evt['file_num']); - _jobProgress.speed = double.parse(evt['speed']); - _jobProgress.finishedSize = int.parse(evt['finished_size']); - } else { - // Desktop uses jobTable - // id = index + 1 - final jobIndex = getJob(id); - if (jobIndex >= 0 && _jobTable.length > jobIndex) { - final job = _jobTable[jobIndex]; - job.fileNum = int.parse(evt['file_num']); - job.speed = double.parse(evt['speed']); - job.finishedSize = int.parse(evt['finished_size']); - debugPrint("update job $id with $evt"); - } - } - notifyListeners(); - } catch (e) { - debugPrint("Failed to tryUpdateJobProgress,evt:${evt.toString()}"); - } + Future refreshAll() async { + await localController.refresh(); + await remoteController.refresh(); } - receiveFileDir(Map evt) { + void receiveFileDir(Map evt) { if (evt['is_local'] == "false") { - // init remote home, the connection will automatic read remote home when established, - try { - final fd = FileDirectory.fromJson(jsonDecode(evt['value'])); - fd.format(_remoteOption.isWindows, sort: _sortStyle); - if (fd.id > 0) { - final jobIndex = getJob(fd.id); - if (jobIndex != -1) { - final job = jobTable[jobIndex]; - var totalSize = 0; - var fileCount = fd.entries.length; - for (var element in fd.entries) { - totalSize += element.size; - } - job.totalSize = totalSize; - job.fileCount = fileCount; - debugPrint("update receive details:${fd.path}"); - } - } else if (_remoteOption.home.isEmpty) { - _remoteOption.home = fd.path; - debugPrint("init remote home:${fd.path}"); - _currentRemoteDir = fd; - } - } catch (e) { - debugPrint("receiveFileDir err=$e"); - } + // init remote home, the remote connection will send one dir event when established. TODO opt + remoteController.initDirAndHome(evt); } - _fileFetcher.tryCompleteTask(evt['value'], evt['is_local']); - notifyListeners(); + fileFetcher.tryCompleteTask(evt['value'], evt['is_local']); } - jobDone(Map evt) async { - if (_jobResultListener.isListening) { - _jobResultListener.complete(evt); - return; - } - if (!isDesktop) { - _selectMode = false; - _jobProgress.state = JobState.done; - } else { - int id = int.parse(evt['id']); - final jobIndex = getJob(id); - if (jobIndex != -1) { - final job = jobTable[jobIndex]; - job.finishedSize = job.totalSize; - job.state = JobState.done; - job.fileNum = int.parse(evt['file_num']); - } - } - await Future.wait([ - refresh(isLocal: false), - refresh(isLocal: true), - ]); - } - - jobError(Map evt) { - final err = evt['err'].toString(); - if (!isDesktop) { - if (_jobResultListener.isListening) { - _jobResultListener.complete(evt); - return; - } - _selectMode = false; - _jobProgress.clear(); - _jobProgress.err = err; - _jobProgress.state = JobState.error; - _jobProgress.fileNum = int.parse(evt['file_num']); - if (err == "skipped") { - _jobProgress.state = JobState.done; - _jobProgress.finishedSize = _jobProgress.totalSize; - } - } else { - int jobIndex = getJob(int.parse(evt['id'])); - if (jobIndex != -1) { - final job = jobTable[jobIndex]; - job.state = JobState.error; - job.err = err; - job.fileNum = int.parse(evt['file_num']); - if (err == "skipped") { - job.state = JobState.done; - job.finishedSize = job.totalSize; - } - } - } - debugPrint("jobError $evt"); - notifyListeners(); - } - - overrideFileConfirm(Map evt) async { + void overrideFileConfirm(Map evt) async { final resp = await showFileConfirmDialog( translate("Overwrite"), "${evt['read_path']}", true); final id = int.tryParse(evt['id']) ?? 0; if (false == resp) { - final jobIndex = getJob(id); + final jobIndex = jobController.getJob(id); if (jobIndex != -1) { - cancelJob(id); - final job = jobTable[jobIndex]; + jobController.cancelJob(id); + final job = jobController.jobTable[jobIndex]; job.state = JobState.done; } } else { @@ -384,7 +107,7 @@ class FileModel extends ChangeNotifier { need_override = true; } bind.sessionSetConfirmOverrideFile( - id: parent.target?.id ?? "", + id: sessionID, actId: id, fileNum: int.parse(evt['file_num']), needOverride: need_override, @@ -393,367 +116,6 @@ class FileModel extends ChangeNotifier { } } - jobReset() { - _jobProgress.clear(); - notifyListeners(); - } - - onReady() async { - _localOption.home = await bind.mainGetHomeDir(); - _localOption.showHidden = (await bind.sessionGetPeerOption( - id: parent.target?.id ?? "", name: "local_show_hidden")) - .isNotEmpty; - _localOption.isWindows = Platform.isWindows; - - _remoteOption.showHidden = (await bind.sessionGetPeerOption( - id: parent.target?.id ?? "", name: "remote_show_hidden")) - .isNotEmpty; - _remoteOption.isWindows = - parent.target?.ffiModel.pi.platform == kPeerPlatformWindows; - - await Future.delayed(Duration(milliseconds: 100)); - - final local = (await bind.sessionGetPeerOption( - id: parent.target?.id ?? "", name: "local_dir")); - final remote = (await bind.sessionGetPeerOption( - id: parent.target?.id ?? "", name: "remote_dir")); - openDirectory(local.isEmpty ? _localOption.home : local, isLocal: true); - openDirectory(remote.isEmpty ? _remoteOption.home : remote, isLocal: false); - await Future.delayed(Duration(seconds: 1)); - if (_currentLocalDir.path.isEmpty) { - openDirectory(_localOption.home, isLocal: true); - } - if (_currentRemoteDir.path.isEmpty) { - openDirectory(_remoteOption.home, isLocal: false); - } - } - - Future onClose() async { - parent.target?.dialogManager.dismissAll(); - jobReset(); - - onDirChanged = null; - - // save config - Map msgMap = {}; - - msgMap["local_dir"] = _currentLocalDir.path; - msgMap["local_show_hidden"] = _localOption.showHidden ? "Y" : ""; - msgMap["remote_dir"] = _currentRemoteDir.path; - msgMap["remote_show_hidden"] = _remoteOption.showHidden ? "Y" : ""; - final id = parent.target?.id ?? ""; - for (final msg in msgMap.entries) { - await bind.sessionPeerOption(id: id, name: msg.key, value: msg.value); - } - _currentLocalDir.clear(); - _currentRemoteDir.clear(); - _localOption.clear(); - _remoteOption.clear(); - } - - Future refresh({bool? isLocal}) async { - if (isDesktop) { - isLocal = isLocal ?? _isSelectedLocal; - isLocal - ? await openDirectory(currentLocalDir.path, isLocal: isLocal) - : await openDirectory(currentRemoteDir.path, isLocal: isLocal); - } else { - await openDirectory(currentDir.path); - } - } - - openDirectory(String path, {bool? isLocal, bool isBack = false}) async { - isLocal = isLocal ?? _isSelectedLocal; - if (path == ".") { - refresh(isLocal: isLocal); - return; - } - if (path == "..") { - goToParentDirectory(isLocal: isLocal); - return; - } - if (!isBack) { - pushHistory(isLocal); - } - final showHidden = getCurrentShowHidden(isLocal); - final isWindows = getCurrentIsWindows(isLocal); - // process /C:\ -> C:\ on Windows - if (isWindows && path.length > 1 && path[0] == '/') { - path = path.substring(1); - if (path[path.length - 1] != '\\') { - path = "$path\\"; - } - } - try { - final fd = await _fileFetcher.fetchDirectory(path, isLocal, showHidden); - fd.format(isWindows, sort: _sortStyle); - if (isLocal) { - _currentLocalDir = fd; - } else { - _currentRemoteDir = fd; - } - notifyListeners(); - onDirChanged?.call(isLocal); - } catch (e) { - debugPrint("Failed to openDirectory $path: $e"); - } - } - - Future fetchDirectory(path, isLocal, showHidden) async { - return await _fileFetcher.fetchDirectory(path, isLocal, showHidden); - } - - void pushHistory(bool isLocal) { - final history = isLocal ? localHistory : remoteHistory; - final currPath = isLocal ? currentLocalDir.path : currentRemoteDir.path; - if (history.isNotEmpty && history.last == currPath) { - return; - } - history.add(currPath); - } - - goHome({bool? isLocal}) { - isLocal = isLocal ?? _isSelectedLocal; - openDirectory(getCurrentHome(isLocal), isLocal: isLocal); - } - - goBack({bool? isLocal}) { - isLocal = isLocal ?? _isSelectedLocal; - final history = isLocal ? localHistory : remoteHistory; - if (history.isEmpty) return; - final path = history.removeAt(history.length - 1); - if (path.isEmpty) return; - final currPath = isLocal ? currentLocalDir.path : currentRemoteDir.path; - if (currPath == path) { - goBack(isLocal: isLocal); - return; - } - openDirectory(path, isLocal: isLocal, isBack: true); - } - - goToParentDirectory({bool? isLocal}) { - isLocal = isLocal ?? _isSelectedLocal; - final isWindows = - isLocal ? _localOption.isWindows : _remoteOption.isWindows; - final currDir = isLocal ? currentLocalDir : currentRemoteDir; - var parent = PathUtil.dirname(currDir.path, isWindows); - // specially for C:\, D:\, goto '/' - if (parent == currDir.path && isWindows) { - openDirectory('/', isLocal: isLocal); - return; - } - openDirectory(parent, isLocal: isLocal); - } - - /// isRemote only for desktop now, [isRemote == true] means [remote -> local] - sendFiles(SelectedItems items, {bool isRemote = false}) { - if (isDesktop) { - // desktop sendFiles - final toPath = isRemote ? currentLocalDir.path : currentRemoteDir.path; - final isWindows = - isRemote ? _localOption.isWindows : _remoteOption.isWindows; - final showHidden = - isRemote ? _localOption.showHidden : _remoteOption.showHidden; - for (var from in items.items) { - final jobId = ++_jobId; - _jobTable.add(JobProgress() - ..fileName = path.basename(from.path) - ..jobName = from.path - ..totalSize = from.size - ..state = JobState.inProgress - ..id = jobId - ..isRemote = isRemote); - bind.sessionSendFiles( - id: '${parent.target?.id}', - actId: _jobId, - path: from.path, - to: PathUtil.join(toPath, from.name, isWindows), - fileNum: 0, - includeHidden: showHidden, - isRemote: isRemote); - debugPrint( - "path:${from.path}, toPath:$toPath, to:${PathUtil.join(toPath, from.name, isWindows)}"); - } - } else { - if (items.isLocal == null) { - debugPrint("Failed to sendFiles ,wrong path state"); - return; - } - _jobProgress.state = JobState.inProgress; - final toPath = - items.isLocal! ? currentRemoteDir.path : currentLocalDir.path; - final isWindows = - items.isLocal! ? _localOption.isWindows : _remoteOption.isWindows; - final showHidden = - items.isLocal! ? _localOption.showHidden : _remoteOption.showHidden; - items.items.forEach((from) async { - _jobId++; - await bind.sessionSendFiles( - id: '${parent.target?.id}', - actId: _jobId, - path: from.path, - to: PathUtil.join(toPath, from.name, isWindows), - fileNum: 0, - includeHidden: showHidden, - isRemote: !(items.isLocal!)); - }); - } - } - - bool removeCheckboxRemember = false; - - removeAction(SelectedItems items, {bool? isLocal}) async { - isLocal = isLocal ?? _isSelectedLocal; - removeCheckboxRemember = false; - if (items.isLocal == null) { - debugPrint("Failed to removeFile, wrong path state"); - return; - } - final isWindows = - items.isLocal! ? _localOption.isWindows : _remoteOption.isWindows; - await Future.forEach(items.items, (Entry item) async { - _jobId++; - var title = ""; - var content = ""; - late final List entries; - if (item.isFile) { - title = translate("Are you sure you want to delete this file?"); - content = item.name; - entries = [item]; - } else if (item.isDirectory) { - title = translate("Not an empty directory"); - parent.target?.dialogManager.showLoading(translate("Waiting")); - final fd = await _fileFetcher.fetchDirectoryRecursive( - _jobId, item.path, items.isLocal!, true); - if (fd.path.isEmpty) { - fd.path = item.path; - } - fd.format(isWindows); - parent.target?.dialogManager.dismissAll(); - if (fd.entries.isEmpty) { - final confirm = await showRemoveDialog( - translate( - "Are you sure you want to delete this empty directory?"), - item.name, - false); - if (confirm == true) { - sendRemoveEmptyDir(item.path, 0, items.isLocal!); - } - return; - } - entries = fd.entries; - } else { - entries = []; - } - - for (var i = 0; i < entries.length; i++) { - final dirShow = item.isDirectory - ? "${translate("Are you sure you want to delete the file of this directory?")}\n" - : ""; - final count = entries.length > 1 ? "${i + 1}/${entries.length}" : ""; - content = "$dirShow\n\n${entries[i].path}".trim(); - final confirm = await showRemoveDialog( - count.isEmpty ? title : "$title ($count)", - content, - item.isDirectory, - ); - try { - if (confirm == true) { - sendRemoveFile(entries[i].path, i, items.isLocal!); - final res = await _jobResultListener.start(); - // handle remove res; - if (item.isDirectory && - res['file_num'] == (entries.length - 1).toString()) { - sendRemoveEmptyDir(item.path, i, items.isLocal!); - } - } - if (removeCheckboxRemember) { - if (confirm == true) { - for (var j = i + 1; j < entries.length; j++) { - sendRemoveFile(entries[j].path, j, items.isLocal!); - final res = await _jobResultListener.start(); - if (item.isDirectory && - res['file_num'] == (entries.length - 1).toString()) { - sendRemoveEmptyDir(item.path, i, items.isLocal!); - } - } - } - break; - } - } catch (e) { - print("remove error: $e"); - } - } - }); - _selectMode = false; - refresh(isLocal: isLocal); - } - - Future showRemoveDialog( - String title, String content, bool showCheckbox) async { - return await parent.target?.dialogManager.show( - (setState, Function(bool v) close) { - cancel() => close(false); - submit() => close(true); - return CustomAlertDialog( - title: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.warning_rounded, color: Colors.red), - Text(title).paddingOnly( - left: 10, - ), - ], - ), - contentBoxConstraints: - BoxConstraints(minHeight: 100, minWidth: 400, maxWidth: 400), - content: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(content), - Text( - translate("This is irreversible!"), - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.red, - ), - ).paddingOnly(top: 20), - showCheckbox - ? CheckboxListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - controlAffinity: ListTileControlAffinity.leading, - title: Text( - translate("Do this for all conflicts"), - ), - value: removeCheckboxRemember, - onChanged: (v) { - if (v == null) return; - setState(() => removeCheckboxRemember = v); - }, - ) - : const SizedBox.shrink() - ], - ), - actions: [ - dialogButton( - "Cancel", - icon: Icon(Icons.close_rounded), - onPressed: cancel, - isOutline: true, - ), - dialogButton( - "OK", - icon: Icon(Icons.done_rounded), - onPressed: submit, - ), - ], - onSubmit: submit, - onCancel: cancel, - ); - }, useAnimation: false); - } - bool fileConfirmCheckboxRemember = false; Future showFileConfirmDialog( @@ -822,65 +184,523 @@ class FileModel extends ChangeNotifier { ); }, useAnimation: false); } +} - sendRemoveFile(String path, int fileNum, bool isLocal) { +class FileController { + final bool isLocal; + final String sessionID; + + final FileFetcher fileFetcher; + + final options = DirectoryOptions().obs; + final directory = FileDirectory().obs; + + final history = RxList.empty(growable: true); + final sortStyle = SortStyle().obs; + final JobController jobController; + final OverlayDialogManager? dialogManager; + + FileController( + {required this.isLocal, + required this.sessionID, + required this.dialogManager, + required this.jobController, + required this.fileFetcher}); + + String get homePath => options.value.home; + + String get shortPath { + final dirPath = directory.value.path; + if (dirPath.startsWith(homePath)) { + var path = dirPath.replaceFirst(homePath, ""); + if (path.isEmpty) return ""; + if (path[0] == "/" || path[0] == "\\") { + // remove more '/' or '\' + path = path.replaceFirst(path[0], ""); + } + return path; + } else { + return dirPath.replaceFirst(homePath, ""); + } + } + + Future onReady() async { + options.value.home = await bind.mainGetHomeDir(); + options.value.showHidden = (await bind.sessionGetPeerOption( + id: sessionID, + name: isLocal ? "local_show_hidden" : "remote_show_hidden")) + .isNotEmpty; + options.value.isWindows = Platform.isWindows; + + await Future.delayed(Duration(milliseconds: 100)); + + final dir = (await bind.sessionGetPeerOption( + id: sessionID, name: isLocal ? "local_dir" : "remote_dir")); + openDirectory(dir.isEmpty ? options.value.home : dir); + + await Future.delayed(Duration(seconds: 1)); + + if (directory.value.path.isEmpty) { + openDirectory(options.value.home); + } + } + + Future close() async { + // save config + Map msgMap = {}; + msgMap[isLocal ? "local_dir" : "remote_dir"] = directory.value.path; + msgMap[isLocal ? "local_show_hidden" : "remote_show_hidden"] = + options.value.showHidden ? "Y" : ""; + for (final msg in msgMap.entries) { + await bind.sessionPeerOption( + id: sessionID, name: msg.key, value: msg.value); + } + directory.value.clear(); + options.value.clear(); + } + + void toggleShowHidden({bool? showHidden}) { + options.value.showHidden = showHidden ?? !options.value.showHidden; + refresh(); + } + + void changeSortStyle(SortBy sort, {bool? isLocal, bool ascending = true}) { + sortStyle.value.by = sort; + sortStyle.value.ascending = ascending; + directory.value.changeSortStyle(sort, ascending: ascending); + } + + Future refresh() async { + await openDirectory(directory.value.path); + } + + Future openDirectory(String path, {bool isBack = false}) async { + if (path == ".") { + refresh(); + return; + } + if (path == "..") { + goToParentDirectory(); + return; + } + if (!isBack) { + pushHistory(); + } + final showHidden = options.value.showHidden; + final isWindows = options.value.isWindows; + // process /C:\ -> C:\ on Windows + if (isWindows && path.length > 1 && path[0] == '/') { + path = path.substring(1); + if (path[path.length - 1] != '\\') { + path = "$path\\"; + } + } + try { + final fd = await fileFetcher.fetchDirectory(path, isLocal, showHidden); + fd.format(isWindows, sort: sortStyle.value.by); + directory.value = fd; + } catch (e) { + debugPrint("Failed to openDirectory $path: $e"); + } + } + + void pushHistory() { + if (history.isNotEmpty && history.last == directory.value.path) { + return; + } + history.add(directory.value.path); + } + + void goToHomeDirectory() { + openDirectory(homePath); + } + + void goBack() { + if (history.isEmpty) return; + final path = history.removeAt(history.length - 1); + if (path.isEmpty) return; + if (directory.value.path == path) { + goBack(); + return; + } + openDirectory(path, isBack: true); + } + + void goToParentDirectory() { + final isWindows = options.value.isWindows; + final dirPath = directory.value.path; + var parent = PathUtil.dirname(dirPath, isWindows); + // specially for C:\, D:\, goto '/' + if (parent == dirPath && isWindows) { + openDirectory('/'); + return; + } + openDirectory(parent); + } + + // TODO deprecated this + void initDirAndHome(Map evt) { + try { + final fd = FileDirectory.fromJson(jsonDecode(evt['value'])); + fd.format(options.value.isWindows, sort: sortStyle.value.by); + if (fd.id > 0) { + final jobIndex = jobController.getJob(fd.id); + if (jobIndex != -1) { + final job = jobController.jobTable[jobIndex]; + var totalSize = 0; + var fileCount = fd.entries.length; + for (var element in fd.entries) { + totalSize += element.size; + } + job.totalSize = totalSize; + job.fileCount = fileCount; + debugPrint("update receive details:${fd.path}"); + } + } else if (options.value.home.isEmpty) { + options.value.home = fd.path; + debugPrint("init remote home:${fd.path}"); + directory.value = fd; + } + } catch (e) { + debugPrint("initDirAndHome err=$e"); + } + } + + /// sendFiles from other side (SelectedItems) to current side (FileController.isLocal). + void sendFiles(SelectedItems items) { + /// ignore same side + if (items.isLocal == isLocal) { + return; + } + + // alias + final isRemoteToLocal = isLocal; + + final toPath = directory.value.path; + final isWindows = options.value.isWindows; + final showHidden = options.value.showHidden; + for (var from in items.items) { + final jobID = jobController.add(from, isRemoteToLocal); + bind.sessionSendFiles( + id: sessionID, + actId: jobID, + path: from.path, + to: PathUtil.join(toPath, from.name, isWindows), + fileNum: 0, + includeHidden: showHidden, + isRemote: isRemoteToLocal); + debugPrint( + "path:${from.path}, toPath:$toPath, to:${PathUtil.join(toPath, from.name, isWindows)}"); + } + } + + bool _removeCheckboxRemember = false; + + Future removeAction(SelectedItems items) async { + _removeCheckboxRemember = false; + if (items.isLocal == isLocal) { + debugPrint("Failed to removeFile, wrong files"); + return; + } + final isWindows = options.value.isWindows; + await Future.forEach(items.items, (Entry item) async { + final jobID = JobController.jobID.next(); + var title = ""; + var content = ""; + late final List entries; + if (item.isFile) { + title = translate("Are you sure you want to delete this file?"); + content = item.name; + entries = [item]; + } else if (item.isDirectory) { + title = translate("Not an empty directory"); + dialogManager?.showLoading(translate("Waiting")); + final fd = await fileFetcher.fetchDirectoryRecursive( + jobID, item.path, items.isLocal!, true); + if (fd.path.isEmpty) { + fd.path = item.path; + } + fd.format(isWindows); + dialogManager?.dismissAll(); + if (fd.entries.isEmpty) { + final confirm = await showRemoveDialog( + translate( + "Are you sure you want to delete this empty directory?"), + item.name, + false); + if (confirm == true) { + sendRemoveEmptyDir(item.path, 0); + } + return; + } + entries = fd.entries; + } else { + entries = []; + } + + for (var i = 0; i < entries.length; i++) { + final dirShow = item.isDirectory + ? "${translate("Are you sure you want to delete the file of this directory?")}\n" + : ""; + final count = entries.length > 1 ? "${i + 1}/${entries.length}" : ""; + content = "$dirShow\n\n${entries[i].path}".trim(); + final confirm = await showRemoveDialog( + count.isEmpty ? title : "$title ($count)", + content, + item.isDirectory, + ); + try { + if (confirm == true) { + sendRemoveFile(entries[i].path, i); + final res = await jobController.jobResultListener.start(); + // handle remove res; + if (item.isDirectory && + res['file_num'] == (entries.length - 1).toString()) { + sendRemoveEmptyDir(item.path, i); + } + } + if (_removeCheckboxRemember) { + if (confirm == true) { + for (var j = i + 1; j < entries.length; j++) { + sendRemoveFile(entries[j].path, j); + final res = await jobController.jobResultListener.start(); + if (item.isDirectory && + res['file_num'] == (entries.length - 1).toString()) { + sendRemoveEmptyDir(item.path, i); + } + } + } + break; + } + } catch (e) { + print("remove error: $e"); + } + } + }); + refresh(); + } + + Future showRemoveDialog( + String title, String content, bool showCheckbox) async { + return await dialogManager?.show((setState, Function(bool v) close) { + cancel() => close(false); + submit() => close(true); + return CustomAlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.warning_rounded, color: Colors.red), + Text(title).paddingOnly( + left: 10, + ), + ], + ), + contentBoxConstraints: + BoxConstraints(minHeight: 100, minWidth: 400, maxWidth: 400), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(content), + Text( + translate("This is irreversible!"), + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.red, + ), + ).paddingOnly(top: 20), + showCheckbox + ? CheckboxListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + controlAffinity: ListTileControlAffinity.leading, + title: Text( + translate("Do this for all conflicts"), + ), + value: _removeCheckboxRemember, + onChanged: (v) { + if (v == null) return; + setState(() => _removeCheckboxRemember = v); + }, + ) + : const SizedBox.shrink() + ], + ), + actions: [ + dialogButton( + "Cancel", + icon: Icon(Icons.close_rounded), + onPressed: cancel, + isOutline: true, + ), + dialogButton( + "OK", + icon: Icon(Icons.done_rounded), + onPressed: submit, + ), + ], + onSubmit: submit, + onCancel: cancel, + ); + }, useAnimation: false); + } + + void sendRemoveFile(String path, int fileNum) { bind.sessionRemoveFile( - id: '${parent.target?.id}', - actId: _jobId, + id: sessionID, + actId: JobController.jobID.next(), path: path, isRemote: !isLocal, fileNum: fileNum); } - sendRemoveEmptyDir(String path, int fileNum, bool isLocal) { - final history = isLocal ? localHistory : remoteHistory; + void sendRemoveEmptyDir(String path, int fileNum) { history.removeWhere((element) => element.contains(path)); bind.sessionRemoveAllEmptyDirs( - id: '${parent.target?.id}', - actId: _jobId, + id: sessionID, + actId: JobController.jobID.next(), path: path, isRemote: !isLocal); } - createDir(String path, {bool? isLocal}) async { - isLocal = isLocal ?? this.isLocal; - _jobId++; + Future createDir(String path) async { bind.sessionCreateDir( - id: '${parent.target?.id}', - actId: _jobId, + id: sessionID, + actId: JobController.jobID.next(), path: path, isRemote: !isLocal); } +} - cancelJob(int id) async { - bind.sessionCancelJob(id: '${parent.target?.id}', actId: id); - jobReset(); +class JobController { + static final JobID jobID = JobID(); + final jobTable = RxList.empty(growable: true); + final jobResultListener = JobResultListener>(); + final String sessionID; + + JobController(this.sessionID); + + int getJob(int id) { + return jobTable.indexWhere((element) => element.id == id); } - changeSortStyle(SortBy sort, {bool? isLocal, bool ascending = true}) { - _sortStyle = sort; - if (isLocal == null) { - // compatible for mobile logic - _currentLocalDir.changeSortStyle(sort, ascending: ascending); - _currentRemoteDir.changeSortStyle(sort, ascending: ascending); - _localSortStyle = sort; - _localSortAscending = ascending; - _remoteSortStyle = sort; - _remoteSortAscending = ascending; - } else if (isLocal) { - _currentLocalDir.changeSortStyle(sort, ascending: ascending); - _localSortStyle = sort; - _localSortAscending = ascending; - } else { - _currentRemoteDir.changeSortStyle(sort, ascending: ascending); - _remoteSortStyle = sort; - _remoteSortAscending = ascending; + // JobProgress? getJob(int id) { + // return jobTable.firstWhere((element) => element.id == id); + // } + + void close() { + jobTable.close(); + jobTable.clear(); + } + + // return jobID + int add(Entry from, bool isRemoteToLocal) { + final jobID = JobController.jobID.next(); + jobTable.add(JobProgress() + ..fileName = path.basename(from.path) + ..jobName = from.path + ..totalSize = from.size + ..state = JobState.inProgress + ..id = jobID + ..isRemoteToLocal = isRemoteToLocal); + return jobID; + } + + void tryUpdateJobProgress(Map evt) { + try { + int id = int.parse(evt['id']); + // id = index + 1 + final jobIndex = getJob(id); + if (jobIndex >= 0 && jobTable.length > jobIndex) { + final job = jobTable[jobIndex]; + job.fileNum = int.parse(evt['file_num']); + job.speed = double.parse(evt['speed']); + job.finishedSize = int.parse(evt['finished_size']); + debugPrint("update job $id with $evt"); + } + } catch (e) { + debugPrint("Failed to tryUpdateJobProgress,evt:${evt.toString()}"); } - notifyListeners(); } - initFileFetcher() { - _fileFetcher.id = parent.target?.id; + void jobDone(Map evt) async { + if (jobResultListener.isListening) { + jobResultListener.complete(evt); + return; + } + + int id = int.parse(evt['id']); + final jobIndex = getJob(id); + if (jobIndex != -1) { + final job = jobTable[jobIndex]; + job.finishedSize = job.totalSize; + job.state = JobState.done; + job.fileNum = int.parse(evt['file_num']); + } + } + + void jobError(Map evt) { + final err = evt['err'].toString(); + int jobIndex = getJob(int.parse(evt['id'])); + if (jobIndex != -1) { + final job = jobTable[jobIndex]; + job.state = JobState.error; + job.err = err; + job.fileNum = int.parse(evt['file_num']); + if (err == "skipped") { + job.state = JobState.done; + job.finishedSize = job.totalSize; + } + } + debugPrint("jobError $evt"); + } + + void cancelJob(int id) async { + bind.sessionCancelJob(id: sessionID, actId: id); + } + + void loadLastJob(Map evt) { + debugPrint("load last job: $evt"); + Map jobDetail = json.decode(evt['value']); + // int id = int.parse(jobDetail['id']); + String remote = jobDetail['remote']; + String to = jobDetail['to']; + bool showHidden = jobDetail['show_hidden']; + int fileNum = jobDetail['file_num']; + bool isRemote = jobDetail['is_remote']; + final currJobId = JobController.jobID.next(); + String fileName = path.basename(isRemote ? remote : to); + var jobProgress = JobProgress() + ..fileName = fileName + ..jobName = isRemote ? remote : to + ..id = currJobId + ..isRemoteToLocal = isRemote + ..fileNum = fileNum + ..remote = remote + ..to = to + ..showHidden = showHidden + ..state = JobState.paused; + jobTable.add(jobProgress); + bind.sessionAddJob( + id: sessionID, + isRemote: isRemote, + includeHidden: showHidden, + actId: currJobId, + path: isRemote ? remote : to, + to: isRemote ? to : remote, + fileNum: fileNum, + ); + } + + void resumeJob(int jobId) { + final jobIndex = getJob(jobId); + if (jobIndex != -1) { + final job = jobTable[jobIndex]; + bind.sessionResumeJob( + id: sessionID, actId: job.id, isRemote: job.isRemoteToLocal); + job.state = JobState.inProgress; + } else { + debugPrint("jobId $jobId is not exists"); + } } void updateFolderFiles(Map evt) { @@ -896,55 +716,6 @@ class FileModel extends ChangeNotifier { job.totalSize = total_size.toInt(); } debugPrint("update folder files: $info"); - notifyListeners(); - } - - bool get remoteSortAscending => _remoteSortAscending; - - void loadLastJob(Map evt) { - debugPrint("load last job: $evt"); - Map jobDetail = json.decode(evt['value']); - // int id = int.parse(jobDetail['id']); - String remote = jobDetail['remote']; - String to = jobDetail['to']; - bool showHidden = jobDetail['show_hidden']; - int fileNum = jobDetail['file_num']; - bool isRemote = jobDetail['is_remote']; - final currJobId = _jobId++; - String fileName = path.basename(isRemote ? remote : to); - var jobProgress = JobProgress() - ..fileName = fileName - ..jobName = isRemote ? remote : to - ..id = currJobId - ..isRemote = isRemote - ..fileNum = fileNum - ..remote = remote - ..to = to - ..showHidden = showHidden - ..state = JobState.paused; - jobTable.add(jobProgress); - bind.sessionAddJob( - id: '${parent.target?.id}', - isRemote: isRemote, - includeHidden: showHidden, - actId: currJobId, - path: isRemote ? remote : to, - to: isRemote ? to : remote, - fileNum: fileNum, - ); - } - - resumeJob(int jobId) { - final jobIndex = getJob(jobId); - if (jobIndex != -1) { - final job = jobTable[jobIndex]; - bind.sessionResumeJob( - id: '${parent.target?.id}', actId: job.id, isRemote: job.isRemote); - job.state = JobState.inProgress; - } else { - debugPrint("jobId $jobId is not exists"); - } - notifyListeners(); } } @@ -993,10 +764,9 @@ class FileFetcher { Map> remoteTasks = {}; Map> readRecursiveTasks = {}; - String? id; + String id; - // if id == null, means to fetch global FFI - FFI get _ffi => ffi(id ?? ""); + FileFetcher(this.id); Future registerReadTask(bool isLocal, String path) { // final jobs = isLocal?localJobs:remoteJobs; // maybe we will use read local dir async later @@ -1015,16 +785,16 @@ class FileFetcher { return c.future; } - Future registerReadRecursiveTask(int id) { + Future registerReadRecursiveTask(int actID) { final tasks = readRecursiveTasks; - if (tasks.containsKey(id)) { + if (tasks.containsKey(actID)) { throw "Failed to registerRemoveTask, already have same ReadRecursive job"; } final c = Completer(); - tasks[id] = c; + tasks[actID] = c; Timer(Duration(seconds: 2), () { - tasks.remove(id); + tasks.remove(actID); if (c.isCompleted) return; c.completeError("Failed to read dir,timeout"); }); @@ -1073,16 +843,16 @@ class FileFetcher { } Future fetchDirectoryRecursive( - int id, String path, bool isLocal, bool showHidden) async { + int actID, String path, bool isLocal, bool showHidden) async { // TODO test Recursive is show hidden default? try { await bind.sessionReadDirRecursive( - id: _ffi.id, - actId: id, + id: id, + actId: actID, path: path, isRemote: !isLocal, showHidden: showHidden); - return registerReadRecursiveTask(id); + return registerReadRecursiveTask(actID); } catch (e) { return Future.error(e); } @@ -1179,7 +949,10 @@ class JobProgress { var finishedSize = 0; var totalSize = 0; var fileCount = 0; - var isRemote = false; + // [isRemote == true] means [remote -> local] + // var isRemote = false; + // to-do use enum + var isRemoteToLocal = false; var jobName = ""; var fileName = ""; var remote = ""; @@ -1252,21 +1025,17 @@ class DirectoryOptions { } class SelectedItems { - bool? _isLocal; + final bool isLocal; final List _items = []; List get items => _items; int get length => _items.length; - bool? get isLocal => _isLocal; + SelectedItems(this.isLocal); - add(bool isLocal, Entry e) { + add(Entry e) { if (e.isDrive) return; - _isLocal ??= isLocal; - if (_isLocal != null && _isLocal != isLocal) { - return; - } if (!_items.contains(e)) { _items.add(e); } @@ -1278,22 +1047,10 @@ class SelectedItems { remove(Entry e) { _items.remove(e); - if (_items.isEmpty) { - _isLocal = null; - } - } - - bool isOtherPage(bool currentIsLocal) { - if (_isLocal == null) { - return false; - } else { - return _isLocal != currentIsLocal; - } } clear() { _items.clear(); - _isLocal = null; } void selectAll(List entries) { diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 802a18a52..0b2ae9dd4 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -166,17 +166,18 @@ class FfiModel with ChangeNotifier { } else if (name == 'file_dir') { parent.target?.fileModel.receiveFileDir(evt); } else if (name == 'job_progress') { - parent.target?.fileModel.tryUpdateJobProgress(evt); + parent.target?.fileModel.jobController.tryUpdateJobProgress(evt); } else if (name == 'job_done') { - parent.target?.fileModel.jobDone(evt); + parent.target?.fileModel.jobController.jobDone(evt); + parent.target?.fileModel.refreshAll(); } else if (name == 'job_error') { - parent.target?.fileModel.jobError(evt); + parent.target?.fileModel.jobController.jobError(evt); } else if (name == 'override_file_confirm') { parent.target?.fileModel.overrideFileConfirm(evt); } else if (name == 'load_last_job') { - parent.target?.fileModel.loadLastJob(evt); + parent.target?.fileModel.jobController.loadLastJob(evt); } else if (name == 'update_folder_files') { - parent.target?.fileModel.updateFolderFiles(evt); + parent.target?.fileModel.jobController.updateFolderFiles(evt); } else if (name == 'add_connection') { parent.target?.serverModel.addConnection(evt); } else if (name == 'on_client_remove') { @@ -1571,9 +1572,6 @@ class FFI { }(); // every instance will bind a stream this.id = id; - if (isFileTransfer) { - fileModel.initFileFetcher(); - } } /// Login with [password], choose if the client should [remember] it. From d867decd9891771bfc8b02dd1d6afcf3b08fdd9f Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 8 Mar 2023 22:32:55 +0900 Subject: [PATCH 03/27] refactor Desktop file_manager_page.dart --- .../lib/desktop/pages/file_manager_page.dart | 440 +++++++++--------- flutter/lib/models/file_model.dart | 83 ++-- 2 files changed, 257 insertions(+), 266 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 0a8ebdd33..99b873cdc 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -69,6 +69,7 @@ class _FileManagerPageState extends State late FFI _ffi; FileModel get model => _ffi.fileModel; + JobController get jobController => model.jobController; @override void initState() { @@ -84,7 +85,6 @@ class _FileManagerPageState extends State Wakelock.enable(); } debugPrint("File manager page init success with id ${widget.id}"); - model.onDirChanged = breadCrumbScrollToEnd; _ffi.dialogManager.setOverlayState(_overlayKeyState); } @@ -107,31 +107,24 @@ class _FileManagerPageState extends State @override Widget build(BuildContext context) { super.build(context); - // TODO - final localController = FileController(isLocal: true); - final remoteController = FileController(isLocal: false); return Overlay(key: _overlayKeyState.key, initialEntries: [ OverlayEntry(builder: (_) { - return ChangeNotifierProvider.value( - value: _ffi.fileModel, - child: Consumer(builder: (context, model, child) { - return Scaffold( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - body: Row( - children: [ - Flexible( - flex: 3, - child: dropArea(FileManagerView( - localController, _ffi, _mouseFocusScope))), - Flexible( - flex: 3, - child: dropArea(FileManagerView( - remoteController, _ffi, _mouseFocusScope))), - Flexible(flex: 2, child: statusList()) - ], - ), - ); - })); + return Scaffold( + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + body: Row( + children: [ + Flexible( + flex: 3, + child: dropArea(FileManagerView( + model.localController, _ffi, _mouseFocusScope))), + Flexible( + flex: 3, + child: dropArea(FileManagerView( + model.remoteController, _ffi, _mouseFocusScope))), + Flexible(flex: 2, child: statusList()) + ], + ), + ); }) ]); } @@ -169,7 +162,7 @@ class _FileManagerPageState extends State child: Container( margin: const EdgeInsets.only(top: 16.0, bottom: 16.0, right: 16.0), padding: const EdgeInsets.all(8.0), - child: model.jobTable.isEmpty + child: jobController.jobTable.isEmpty ? generateCard( Center( child: Column( @@ -195,7 +188,7 @@ class _FileManagerPageState extends State () => ListView.builder( controller: ScrollController(), itemBuilder: (BuildContext context, int index) { - final item = model.jobTable[index]; + final item = jobController.jobTable[index]; return Padding( padding: const EdgeInsets.only(bottom: 5), child: generateCard( @@ -206,7 +199,7 @@ class _FileManagerPageState extends State crossAxisAlignment: CrossAxisAlignment.center, children: [ Transform.rotate( - angle: item.isRemote ? pi : 0, + angle: item.isRemoteToLocal ? pi : 0, child: SvgPicture.asset( "assets/arrow.svg", color: Theme.of(context) @@ -293,7 +286,7 @@ class _FileManagerPageState extends State offstage: item.state != JobState.paused, child: MenuButton( onPressed: () { - model.resumeJob(item.id); + jobController.resumeJob(item.id); }, child: SvgPicture.asset( "assets/refresh.svg", @@ -310,8 +303,8 @@ class _FileManagerPageState extends State color: Colors.white, ), onPressed: () { - model.jobTable.removeAt(index); - model.cancelJob(item.id); + jobController.jobTable.removeAt(index); + jobController.cancelJob(item.id); }, color: MyTheme.accent, hoverColor: MyTheme.accent80, @@ -325,7 +318,7 @@ class _FileManagerPageState extends State ), ); }, - itemCount: model.jobTable.length, + itemCount: jobController.jobTable.length, ), ), ), @@ -337,18 +330,15 @@ class _FileManagerPageState extends State // ignore local return; } - var items = SelectedItems(); + final items = SelectedItems(isLocal: false); for (var file in details.files) { final f = File(file.path); - items.add( - true, - Entry() - ..path = file.path - ..name = file.name - ..size = - FileSystemEntity.isDirectorySync(f.path) ? 0 : f.lengthSync()); + items.add(Entry() + ..path = file.path + ..name = file.name + ..size = FileSystemEntity.isDirectorySync(f.path) ? 0 : f.lengthSync()); } - model.sendFiles(items, isRemote: false); + model.remoteController.sendFiles(items); } } @@ -364,7 +354,6 @@ class FileManagerView extends StatefulWidget { } class _FileManagerViewState extends State { - final _selectedItems = SelectedItems(); final _locationStatus = LocationStatus.bread.obs; final _locationNode = FocusNode(); final _locationBarKey = GlobalKey(); @@ -376,6 +365,8 @@ class _FileManagerViewState extends State { final _modifiedColWidth = kDesktopFileTransferModifiedColWidth.obs; final _fileListScrollController = ScrollController(); + late final _selectedItems = SelectedItems(isLocal: isLocal); + /// [_lastClickTime], [_lastClickEntry] help to handle double click var _lastClickTime = DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000; @@ -390,6 +381,7 @@ class _FileManagerViewState extends State { super.initState(); // register location listener _locationNode.addListener(onLocationFocusChanged); + controller.directory.listen((e) => breadCrumbScrollToEnd()); } @override @@ -508,7 +500,7 @@ class _FileManagerViewState extends State { hoverColor: Theme.of(context).hoverColor, onPressed: () { _selectedItems.clear(); - model.goBack(isLocal: isLocal); + controller.goBack(); }, ), MenuButton( @@ -523,7 +515,7 @@ class _FileManagerViewState extends State { hoverColor: Theme.of(context).hoverColor, onPressed: () { _selectedItems.clear(); - model.goToParentDirectory(isLocal: isLocal); + controller.goToParentDirectory(); }, ), ], @@ -560,8 +552,8 @@ class _FileManagerViewState extends State { Expanded( child: _locationStatus.value == LocationStatus.bread - ? buildBread(isLocal) - : buildPathLocation(isLocal)), + ? buildBread() + : buildPathLocation()), ], ), ), @@ -617,7 +609,7 @@ class _FileManagerViewState extends State { left: 3, ), onPressed: () { - model.refresh(isLocal: isLocal); + controller.refresh(); }, child: SvgPicture.asset( "assets/refresh.svg", @@ -641,7 +633,7 @@ class _FileManagerViewState extends State { right: 3, ), onPressed: () { - model.goHome(isLocal: isLocal); + controller.goToHomeDirectory(); }, child: SvgPicture.asset( "assets/home.svg", @@ -656,12 +648,11 @@ class _FileManagerViewState extends State { _ffi.dialogManager.show((setState, close) { submit() { if (name.value.text.isNotEmpty) { - model.createDir( - PathUtil.join( - model.getCurrentDir(isLocal).path, - name.value.text, - model.getCurrentIsWindows(isLocal)), - isLocal: isLocal); + controller.createDir(PathUtil.join( + controller.directory.value.path, + name.value.text, + controller.options.value.isWindows, + )); close(); } } @@ -722,8 +713,7 @@ class _FileManagerViewState extends State { MenuButton( onPressed: validItems(_selectedItems) ? () async { - await (model.removeAction(_selectedItems, - isLocal: isLocal)); + await (controller.removeAction(_selectedItems)); _selectedItems.clear(); } : null, @@ -751,7 +741,7 @@ class _FileManagerViewState extends State { ), onPressed: validItems(_selectedItems) ? () { - model.sendFiles(_selectedItems, isRemote: !isLocal); + controller.sendFiles(_selectedItems); _selectedItems.clear(); } : null, @@ -814,10 +804,10 @@ class _FileManagerViewState extends State { switchType: SwitchType.scheckbox, text: translate("Show Hidden Files"), getter: () async { - return model.getCurrentShowHidden(isLocal); + return controller.options.value.isWindows; }, setter: (bool v) async { - model.toggleShowHidden(local: isLocal); + controller.toggleShowHidden(); }, padding: kDesktopMenuPadding, dismissOnClicked: true, @@ -825,7 +815,7 @@ class _FileManagerViewState extends State { MenuEntryButton( childBuilder: (style) => Text(translate("Select All"), style: style), proc: () => setState(() => - _selectedItems.selectAll(model.getCurrentDir(isLocal).entries)), + _selectedItems.selectAll(controller.directory.value.entries)), padding: kDesktopMenuPadding, dismissOnClicked: true), MenuEntryButton( @@ -872,7 +862,7 @@ class _FileManagerViewState extends State { Widget _buildFileList( BuildContext context, bool isLocal, ScrollController scrollController) { - final fd = model.getCurrentDir(isLocal); + final fd = controller.directory.value; final entries = fd.entries; return ListSearchActionListener( @@ -919,161 +909,155 @@ class _FileManagerViewState extends State { _jumpToEntry(isLocal, searchResult.first, scrollController, kDesktopFileTransferRowHeight); }, - child: ObxValue( - (searchText) { - final filteredEntries = searchText.isNotEmpty - ? entries.where((element) { - return element.name.contains(searchText.value); - }).toList(growable: false) - : entries; - final rows = filteredEntries.map((entry) { - final sizeStr = - entry.isFile ? readableFileSize(entry.size.toDouble()) : ""; - final lastModifiedStr = entry.isDrive - ? " " - : "${entry.lastModified().toString().replaceAll(".000", "")} "; - final isSelected = _selectedItems.contains(entry); - return Padding( - padding: EdgeInsets.symmetric(vertical: 1), - child: Container( - decoration: BoxDecoration( - color: isSelected - ? Theme.of(context).hoverColor - : Theme.of(context).cardColor, - borderRadius: BorderRadius.all( - Radius.circular(5.0), - ), + child: Obx(() { + final entries = controller.directory.value.entries; + final filteredEntries = _searchText.isNotEmpty + ? entries.where((element) { + return element.name.contains(_searchText.value); + }).toList(growable: false) + : entries; + final rows = filteredEntries.map((entry) { + final sizeStr = + entry.isFile ? readableFileSize(entry.size.toDouble()) : ""; + final lastModifiedStr = entry.isDrive + ? " " + : "${entry.lastModified().toString().replaceAll(".000", "")} "; + final isSelected = _selectedItems.contains(entry); + return Padding( + padding: EdgeInsets.symmetric(vertical: 1), + child: Container( + decoration: BoxDecoration( + color: isSelected + ? Theme.of(context).hoverColor + : Theme.of(context).cardColor, + borderRadius: BorderRadius.all( + Radius.circular(5.0), ), - key: ValueKey(entry.name), - height: kDesktopFileTransferRowHeight, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Expanded( - child: InkWell( - child: Row( - children: [ - GestureDetector( - child: Obx( - () => Container( - width: _nameColWidth.value, - child: Tooltip( - waitDuration: - Duration(milliseconds: 500), - message: entry.name, - child: Row(children: [ - entry.isDrive - ? Image( - image: iconHardDrive, - fit: BoxFit.scaleDown, - color: Theme.of(context) - .iconTheme - .color - ?.withOpacity(0.7)) - .paddingAll(4) - : SvgPicture.asset( - entry.isFile - ? "assets/file.svg" - : "assets/folder.svg", - color: Theme.of(context) - .tabBarTheme - .labelColor, - ), - Expanded( - child: Text( - entry.name.nonBreaking, - overflow: - TextOverflow.ellipsis)) - ]), + ), + key: ValueKey(entry.name), + height: kDesktopFileTransferRowHeight, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded( + child: InkWell( + child: Row( + children: [ + GestureDetector( + child: Obx( + () => Container( + width: _nameColWidth.value, + child: Tooltip( + waitDuration: Duration(milliseconds: 500), + message: entry.name, + child: Row(children: [ + entry.isDrive + ? Image( + image: iconHardDrive, + fit: BoxFit.scaleDown, + color: Theme.of(context) + .iconTheme + .color + ?.withOpacity(0.7)) + .paddingAll(4) + : SvgPicture.asset( + entry.isFile + ? "assets/file.svg" + : "assets/folder.svg", + color: Theme.of(context) + .tabBarTheme + .labelColor, + ), + Expanded( + child: Text(entry.name.nonBreaking, + overflow: + TextOverflow.ellipsis)) + ]), + )), + ), + onTap: () { + final items = _selectedItems; + // handle double click + if (_checkDoubleClick(entry)) { + controller.openDirectory(entry.path); + items.clear(); + return; + } + _onSelectedChanged( + items, filteredEntries, entry, isLocal); + }, + ), + SizedBox( + width: 2.0, + ), + GestureDetector( + child: Obx( + () => SizedBox( + width: _modifiedColWidth.value, + child: Tooltip( + waitDuration: Duration(milliseconds: 500), + message: lastModifiedStr, + child: Text( + lastModifiedStr, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12, + color: MyTheme.darkGray, + ), )), ), - onTap: () { - final items = _selectedItems; - // handle double click - if (_checkDoubleClick(entry)) { - openDirectory(entry.path, isLocal: isLocal); - items.clear(); - return; - } - _onSelectedChanged( - items, filteredEntries, entry, isLocal); - }, ), - SizedBox( - width: 2.0, - ), - GestureDetector( - child: Obx( - () => SizedBox( - width: _modifiedColWidth.value, - child: Tooltip( - waitDuration: - Duration(milliseconds: 500), - message: lastModifiedStr, - child: Text( - lastModifiedStr, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12, - color: MyTheme.darkGray, - ), - )), + ), + // Divider from header. + SizedBox( + width: 2.0, + ), + Expanded( + // width: 100, + child: GestureDetector( + child: Tooltip( + waitDuration: Duration(milliseconds: 500), + message: sizeStr, + child: Text( + sizeStr, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 10, color: MyTheme.darkGray), ), ), ), - // Divider from header. - SizedBox( - width: 2.0, - ), - Expanded( - // width: 100, - child: GestureDetector( - child: Tooltip( - waitDuration: Duration(milliseconds: 500), - message: sizeStr, - child: Text( - sizeStr, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 10, - color: MyTheme.darkGray), - ), - ), - ), - ), - ], - ), + ), + ], ), ), - ], - )), - ); - }).toList(growable: false); - - return Column( - children: [ - // Header - Row( - children: [ - Expanded(child: _buildFileBrowserHeader(context, isLocal)), - ], - ), - // Body - Expanded( - child: ListView.builder( - controller: scrollController, - itemExtent: kDesktopFileTransferRowHeight, - itemBuilder: (context, index) { - return rows[index]; - }, - itemCount: rows.length, - ), - ), - ], + ), + ], + )), ); - }, - _searchText, - ), + }).toList(growable: false); + + return Column( + children: [ + // Header + Row( + children: [ + Expanded(child: _buildFileBrowserHeader(context, isLocal)), + ], + ), + // Body + Expanded( + child: ListView.builder( + controller: scrollController, + itemExtent: kDesktopFileTransferRowHeight, + itemBuilder: (context, index) { + return rows[index]; + }, + itemCount: rows.length, + ), + ), + ], + ); + }), ); } @@ -1084,7 +1068,7 @@ class _FileManagerViewState extends State { void _jumpToEntry(bool isLocal, Entry entry, ScrollController scrollController, double rowHeight) { - final entries = model.getCurrentDir(isLocal).entries; + final entries = controller.directory.value.entries; final index = entries.indexOf(entry); if (index == -1) { debugPrint("entry is not valid: ${entry.path}"); @@ -1101,7 +1085,7 @@ class _FileManagerViewState extends State { scrollController.position.maxScrollExtent); scrollController.jumpTo(offset); setState(() { - selectedEntries.add(isLocal, searchResult.first); + selectedEntries.add(searchResult.first); debugPrint("focused on ${searchResult.first.name}"); }); } @@ -1116,7 +1100,7 @@ class _FileManagerViewState extends State { if (selectedItems.contains(entry)) { selectedItems.remove(entry); } else { - selectedItems.add(isLocal, entry); + selectedItems.add(entry); } } else if (isShiftDown) { final List indexGroup = []; @@ -1130,10 +1114,10 @@ class _FileManagerViewState extends State { selectedItems.clear(); entries .getRange(minIndex, maxIndex + 1) - .forEach((e) => selectedItems.add(isLocal, e)); + .forEach((e) => selectedItems.add(e)); } else { selectedItems.clear(); - selectedItems.add(isLocal, entry); + selectedItems.add(entry); } setState(() {}); } @@ -1205,7 +1189,7 @@ class _FileManagerViewState extends State { } else { ascending.value = !ascending.value!; } - model.changeSortStyle(sortBy, + controller.changeSortStyle(sortBy, isLocal: isLocal, ascending: ascending.value!); }, child: SizedBox( @@ -1234,21 +1218,21 @@ class _FileManagerViewState extends State { ), ), ), () { - if (model.getSortStyle(isLocal) == sortBy) { - return model.getSortAscending(isLocal).obs; + if (controller.sortBy.value == sortBy) { + return controller.sortAscending; } else { return Rx(null); } }()); } - Widget buildBread(bool isLocal) { + Widget buildBread() { final items = getPathBreadCrumbItems(isLocal, (list) { var path = ""; for (var item in list) { - path = PathUtil.join(path, item, model.getCurrentIsWindows(isLocal)); + path = PathUtil.join(path, item, controller.options.value.isWindows); } - openDirectory(path, isLocal: isLocal); + controller.openDirectory(path); }); return items.isEmpty @@ -1290,7 +1274,7 @@ class _FileManagerViewState extends State { final x = offset.dx; final y = offset.dy + size.height + 1; - final isPeerWindows = model.getCurrentIsWindows(isLocal); + final isPeerWindows = controller.options.value.isWindows; final List menuItems = [ MenuEntryButton( childBuilder: (TextStyle? style) => isPeerWindows @@ -1300,7 +1284,7 @@ class _FileManagerViewState extends State { style: style, ), proc: () { - openDirectory('/', isLocal: isLocal); + controller.openDirectory('/'); }, dismissOnClicked: true), MenuEntryDivider() @@ -1311,8 +1295,9 @@ class _FileManagerViewState extends State { loadingTag = _ffi.dialogManager.showLoading("Waiting"); } try { - final fd = - await model.fetchDirectory("/", isLocal, isLocal); + final showHidden = controller.options.value.showHidden; + final fd = await controller.fileFetcher + .fetchDirectory("/", isLocal, showHidden); for (var entry in fd.entries) { menuItems.add(MenuEntryButton( childBuilder: (TextStyle? style) => @@ -1331,8 +1316,7 @@ class _FileManagerViewState extends State { ) ]), proc: () { - openDirectory('${entry.name}\\', - isLocal: isLocal); + controller.openDirectory('${entry.name}\\'); }, dismissOnClicked: true)); } @@ -1369,9 +1353,9 @@ class _FileManagerViewState extends State { List getPathBreadCrumbItems( bool isLocal, void Function(List) onPressed) { - final path = model.getCurrentDir(isLocal).path; + final path = controller.directory.value.path; final breadCrumbList = List.empty(growable: true); - final isWindows = model.getCurrentIsWindows(isLocal); + final isWindows = controller.options.value.isWindows; if (isWindows && path == '/') { breadCrumbList.add(BreadCrumbItem( content: TextButton( @@ -1403,7 +1387,7 @@ class _FileManagerViewState extends State { return breadCrumbList; } - breadCrumbScrollToEnd(bool isLocal) { + breadCrumbScrollToEnd() { Future.delayed(Duration(milliseconds: 200), () { if (_breadCrumbScroller.hasClients) { _breadCrumbScroller.animateTo( @@ -1414,9 +1398,9 @@ class _FileManagerViewState extends State { }); } - Widget buildPathLocation(bool isLocal) { + Widget buildPathLocation() { final text = _locationStatus.value == LocationStatus.pathLocation - ? model.getCurrentDir(isLocal).path + ? controller.directory.value.path : _searchText.value; final textController = TextEditingController(text: text) ..selection = TextSelection.collapsed(offset: text.length); @@ -1440,7 +1424,7 @@ class _FileManagerViewState extends State { ), controller: textController, onSubmitted: (path) { - openDirectory(path, isLocal: isLocal); + controller.openDirectory(path); }, onChanged: _locationStatus.value == LocationStatus.fileSearchBar ? (searchText) => onSearchText(searchText, isLocal) diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 621df4d69..aa4b6253f 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -32,35 +32,38 @@ class JobID { } } -class SortStyle { - var by = SortBy.name; - var ascending = true; -} +typedef GetSessionID = String Function(); class FileModel { final WeakReference parent; - late final String sessionID; + // late final String sessionID; + late final FileFetcher fileFetcher; + late final JobController jobController; + + late final FileController localController; + late final FileController remoteController; + + late final GetSessionID getSessionID; + String get sessionID => getSessionID(); + FileModel(this.parent) { - sessionID = parent.target?.id ?? ""; + getSessionID = () => parent.target?.id ?? ""; + fileFetcher = FileFetcher(getSessionID); + jobController = JobController(getSessionID); + localController = FileController( + isLocal: true, + getSessionID: getSessionID, + dialogManager: parent.target?.dialogManager, + jobController: jobController, + fileFetcher: fileFetcher); + remoteController = FileController( + isLocal: false, + getSessionID: getSessionID, + dialogManager: parent.target?.dialogManager, + jobController: jobController, + fileFetcher: fileFetcher); } - late final fileFetcher = FileFetcher(sessionID); - late final jobController = JobController(sessionID); - - late final localController = FileController( - isLocal: true, - sessionID: sessionID, - dialogManager: parent.target?.dialogManager, - jobController: jobController, - fileFetcher: fileFetcher); - - late final remoteController = FileController( - isLocal: false, - sessionID: sessionID, - dialogManager: parent.target?.dialogManager, - jobController: jobController, - fileFetcher: fileFetcher); - Future onReady() async { await localController.onReady(); await remoteController.onReady(); @@ -188,7 +191,8 @@ class FileModel { class FileController { final bool isLocal; - final String sessionID; + final GetSessionID getSessionID; + String get sessionID => getSessionID(); final FileFetcher fileFetcher; @@ -196,13 +200,14 @@ class FileController { final directory = FileDirectory().obs; final history = RxList.empty(growable: true); - final sortStyle = SortStyle().obs; + final sortBy = SortBy.name.obs; + final sortAscending = true.obs; final JobController jobController; final OverlayDialogManager? dialogManager; FileController( {required this.isLocal, - required this.sessionID, + required this.getSessionID, required this.dialogManager, required this.jobController, required this.fileFetcher}); @@ -265,8 +270,8 @@ class FileController { } void changeSortStyle(SortBy sort, {bool? isLocal, bool ascending = true}) { - sortStyle.value.by = sort; - sortStyle.value.ascending = ascending; + sortBy.value = sort; + sortAscending.value = ascending; directory.value.changeSortStyle(sort, ascending: ascending); } @@ -297,7 +302,7 @@ class FileController { } try { final fd = await fileFetcher.fetchDirectory(path, isLocal, showHidden); - fd.format(isWindows, sort: sortStyle.value.by); + fd.format(isWindows, sort: sortBy.value); directory.value = fd; } catch (e) { debugPrint("Failed to openDirectory $path: $e"); @@ -342,7 +347,7 @@ class FileController { void initDirAndHome(Map evt) { try { final fd = FileDirectory.fromJson(jsonDecode(evt['value'])); - fd.format(options.value.isWindows, sort: sortStyle.value.by); + fd.format(options.value.isWindows, sort: sortBy.value); if (fd.id > 0) { final jobIndex = jobController.getJob(fd.id); if (jobIndex != -1) { @@ -575,9 +580,10 @@ class JobController { static final JobID jobID = JobID(); final jobTable = RxList.empty(growable: true); final jobResultListener = JobResultListener>(); - final String sessionID; + final GetSessionID getSessionID; + String get sessionID => getSessionID(); - JobController(this.sessionID); + JobController(this.getSessionID); int getJob(int id) { return jobTable.indexWhere((element) => element.id == id); @@ -764,9 +770,10 @@ class FileFetcher { Map> remoteTasks = {}; Map> readRecursiveTasks = {}; - String id; + final GetSessionID getSessionID; + String get sessionID => getSessionID(); - FileFetcher(this.id); + FileFetcher(this.getSessionID); Future registerReadTask(bool isLocal, String path) { // final jobs = isLocal?localJobs:remoteJobs; // maybe we will use read local dir async later @@ -829,12 +836,12 @@ class FileFetcher { try { if (isLocal) { final res = await bind.sessionReadLocalDirSync( - id: id ?? "", path: path, showHidden: showHidden); + id: sessionID ?? "", path: path, showHidden: showHidden); final fd = FileDirectory.fromJson(jsonDecode(res)); return fd; } else { await bind.sessionReadRemoteDir( - id: id ?? "", path: path, includeHidden: showHidden); + id: sessionID ?? "", path: path, includeHidden: showHidden); return registerReadTask(isLocal, path); } } catch (e) { @@ -847,7 +854,7 @@ class FileFetcher { // TODO test Recursive is show hidden default? try { await bind.sessionReadDirRecursive( - id: id, + id: sessionID, actId: actID, path: path, isRemote: !isLocal, @@ -1032,7 +1039,7 @@ class SelectedItems { int get length => _items.length; - SelectedItems(this.isLocal); + SelectedItems({required this.isLocal}); add(Entry e) { if (e.isDrive) return; From a962e068f811391cb46aeb4ad5e2463409d9fc8e Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 8 Mar 2023 23:06:34 +0900 Subject: [PATCH 04/27] fix sendFiles wrong direction --- .../lib/desktop/pages/file_manager_page.dart | 7 +++- flutter/lib/models/file_model.dart | 37 +++++++++++++------ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 99b873cdc..1010d56c4 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -338,7 +338,8 @@ class _FileManagerPageState extends State ..name = file.name ..size = FileSystemEntity.isDirectorySync(f.path) ? 0 : f.lengthSync()); } - model.remoteController.sendFiles(items); + final otherSideData = model.localController.directoryData(); + model.remoteController.sendFiles(items, otherSideData); } } @@ -741,7 +742,9 @@ class _FileManagerViewState extends State { ), onPressed: validItems(_selectedItems) ? () { - controller.sendFiles(_selectedItems); + final otherSideData = + controller.getOtherSideDirectoryData(); + controller.sendFiles(_selectedItems, otherSideData); _selectedItems.clear(); } : null, diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index aa4b6253f..2073991ef 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -55,13 +55,15 @@ class FileModel { getSessionID: getSessionID, dialogManager: parent.target?.dialogManager, jobController: jobController, - fileFetcher: fileFetcher); + fileFetcher: fileFetcher, + getOtherSideDirectoryData: () => remoteController.directoryData()); remoteController = FileController( isLocal: false, getSessionID: getSessionID, dialogManager: parent.target?.dialogManager, jobController: jobController, - fileFetcher: fileFetcher); + fileFetcher: fileFetcher, + getOtherSideDirectoryData: () => localController.directoryData()); } Future onReady() async { @@ -189,6 +191,12 @@ class FileModel { } } +class DirectoryData { + final DirectoryOptions options; + final FileDirectory directory; + DirectoryData(this.directory, this.options); +} + class FileController { final bool isLocal; final GetSessionID getSessionID; @@ -205,12 +213,15 @@ class FileController { final JobController jobController; final OverlayDialogManager? dialogManager; + final DirectoryData Function() getOtherSideDirectoryData; + FileController( {required this.isLocal, required this.getSessionID, required this.dialogManager, required this.jobController, - required this.fileFetcher}); + required this.fileFetcher, + required this.getOtherSideDirectoryData}); String get homePath => options.value.home; @@ -229,6 +240,10 @@ class FileController { } } + DirectoryData directoryData() { + return DirectoryData(directory.value, options.value); + } + Future onReady() async { options.value.home = await bind.mainGetHomeDir(); options.value.showHidden = (await bind.sessionGetPeerOption( @@ -372,18 +387,18 @@ class FileController { } /// sendFiles from other side (SelectedItems) to current side (FileController.isLocal). - void sendFiles(SelectedItems items) { - /// ignore same side - if (items.isLocal == isLocal) { + void sendFiles(SelectedItems items, DirectoryData otherSideData) { + /// ignore wrong items side status + if (items.isLocal != isLocal) { return; } // alias - final isRemoteToLocal = isLocal; + final isRemoteToLocal = !isLocal; - final toPath = directory.value.path; - final isWindows = options.value.isWindows; - final showHidden = options.value.showHidden; + final toPath = otherSideData.directory.path; + final isWindows = otherSideData.options.isWindows; + final showHidden = otherSideData.options.showHidden; for (var from in items.items) { final jobID = jobController.add(from, isRemoteToLocal); bind.sessionSendFiles( @@ -403,7 +418,7 @@ class FileController { Future removeAction(SelectedItems items) async { _removeCheckboxRemember = false; - if (items.isLocal == isLocal) { + if (items.isLocal != isLocal) { debugPrint("Failed to removeFile, wrong files"); return; } From b7a0436aa34501a57c49c3f6a5b209b6e961c4a3 Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 8 Mar 2023 23:14:52 +0900 Subject: [PATCH 05/27] fix close error --- flutter/lib/models/file_model.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 2073991ef..6670677d6 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -73,7 +73,6 @@ class FileModel { Future close() async { parent.target?.dialogManager.dismissAll(); - jobController.close(); await localController.close(); await remoteController.close(); } @@ -608,11 +607,6 @@ class JobController { // return jobTable.firstWhere((element) => element.id == id); // } - void close() { - jobTable.close(); - jobTable.clear(); - } - // return jobID int add(Entry from, bool isRemoteToLocal) { final jobID = JobController.jobID.next(); From a2f82b6ea6b5cd20e72de585e067363aabbff046 Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 9 Mar 2023 00:06:24 +0900 Subject: [PATCH 06/27] restore jobTable state mode --- .../lib/desktop/pages/file_manager_page.dart | 336 +++++++++--------- flutter/lib/models/file_model.dart | 6 +- 2 files changed, 165 insertions(+), 177 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 1010d56c4..59dff5518 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -157,171 +157,163 @@ class _FileManagerPageState extends State /// transfer status list /// watch transfer status Widget statusList() { + statusListView() => Obx(() => ListView.builder( + controller: ScrollController(), + itemBuilder: (BuildContext context, int index) { + final item = jobController.jobTable[index]; + return Padding( + padding: const EdgeInsets.only(bottom: 5), + child: generateCard( + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Transform.rotate( + angle: item.isRemoteToLocal ? pi : 0, + child: SvgPicture.asset( + "assets/arrow.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + ).paddingOnly(left: 15), + const SizedBox( + width: 16.0, + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Tooltip( + waitDuration: Duration(milliseconds: 500), + message: item.jobName, + child: Text( + item.fileName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ).paddingSymmetric(vertical: 10), + ), + Text( + '${translate("Total")} ${readableFileSize(item.totalSize.toDouble())}', + style: TextStyle( + fontSize: 12, + color: MyTheme.darkGray, + ), + ), + Offstage( + offstage: item.state != JobState.inProgress, + child: Text( + '${translate("Speed")} ${readableFileSize(item.speed)}/s', + style: TextStyle( + fontSize: 12, + color: MyTheme.darkGray, + ), + ), + ), + Offstage( + offstage: item.state == JobState.inProgress, + child: Text( + translate( + item.display(), + ), + style: TextStyle( + fontSize: 12, + color: MyTheme.darkGray, + ), + ), + ), + Offstage( + offstage: item.state != JobState.inProgress, + child: LinearPercentIndicator( + padding: EdgeInsets.only(right: 15), + animateFromLastPercent: true, + center: Text( + '${(item.finishedSize / item.totalSize * 100).toStringAsFixed(0)}%', + ), + barRadius: Radius.circular(15), + percent: item.finishedSize / item.totalSize, + progressColor: MyTheme.accent, + backgroundColor: Theme.of(context).hoverColor, + lineHeight: kDesktopFileTransferRowHeight, + ).paddingSymmetric(vertical: 15), + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Offstage( + offstage: item.state != JobState.paused, + child: MenuButton( + onPressed: () { + jobController.resumeJob(item.id); + }, + child: SvgPicture.asset( + "assets/refresh.svg", + color: Colors.white, + ), + color: MyTheme.accent, + hoverColor: MyTheme.accent80, + ), + ), + MenuButton( + padding: EdgeInsets.only(right: 15), + child: SvgPicture.asset( + "assets/close.svg", + color: Colors.white, + ), + onPressed: () { + jobController.jobTable.removeAt(index); + jobController.cancelJob(item.id); + }, + color: MyTheme.accent, + hoverColor: MyTheme.accent80, + ), + ], + ), + ], + ), + ], + ).paddingSymmetric(vertical: 10), + ), + ); + }, + itemCount: jobController.jobTable.length, + )); + return PreferredSize( preferredSize: const Size(200, double.infinity), child: Container( - margin: const EdgeInsets.only(top: 16.0, bottom: 16.0, right: 16.0), - padding: const EdgeInsets.all(8.0), - child: jobController.jobTable.isEmpty - ? generateCard( - Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - "assets/transfer.svg", - color: Theme.of(context).tabBarTheme.labelColor, - height: 40, - ).paddingOnly(bottom: 10), - Text( - translate("No transfers in progress"), - textAlign: TextAlign.center, - textScaleFactor: 1.20, - style: TextStyle( - color: Theme.of(context).tabBarTheme.labelColor), + margin: const EdgeInsets.only(top: 16.0, bottom: 16.0, right: 16.0), + padding: const EdgeInsets.all(8.0), + child: Obx( + () => jobController.jobTable.isEmpty + ? generateCard( + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + "assets/transfer.svg", + color: Theme.of(context).tabBarTheme.labelColor, + height: 40, + ).paddingOnly(bottom: 10), + Text( + translate("No transfers in progress"), + textAlign: TextAlign.center, + textScaleFactor: 1.20, + style: TextStyle( + color: + Theme.of(context).tabBarTheme.labelColor), + ), + ], ), - ], - ), - ), - ) - : Obx( - () => ListView.builder( - controller: ScrollController(), - itemBuilder: (BuildContext context, int index) { - final item = jobController.jobTable[index]; - return Padding( - padding: const EdgeInsets.only(bottom: 5), - child: generateCard( - Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Transform.rotate( - angle: item.isRemoteToLocal ? pi : 0, - child: SvgPicture.asset( - "assets/arrow.svg", - color: Theme.of(context) - .tabBarTheme - .labelColor, - ), - ).paddingOnly(left: 15), - const SizedBox( - width: 16.0, - ), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Tooltip( - waitDuration: - Duration(milliseconds: 500), - message: item.jobName, - child: Text( - item.fileName, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ).paddingSymmetric(vertical: 10), - ), - Text( - '${translate("Total")} ${readableFileSize(item.totalSize.toDouble())}', - style: TextStyle( - fontSize: 12, - color: MyTheme.darkGray, - ), - ), - Offstage( - offstage: - item.state != JobState.inProgress, - child: Text( - '${translate("Speed")} ${readableFileSize(item.speed)}/s', - style: TextStyle( - fontSize: 12, - color: MyTheme.darkGray, - ), - ), - ), - Offstage( - offstage: - item.state == JobState.inProgress, - child: Text( - translate( - item.display(), - ), - style: TextStyle( - fontSize: 12, - color: MyTheme.darkGray, - ), - ), - ), - Offstage( - offstage: - item.state != JobState.inProgress, - child: LinearPercentIndicator( - padding: EdgeInsets.only(right: 15), - animateFromLastPercent: true, - center: Text( - '${(item.finishedSize / item.totalSize * 100).toStringAsFixed(0)}%', - ), - barRadius: Radius.circular(15), - percent: item.finishedSize / - item.totalSize, - progressColor: MyTheme.accent, - backgroundColor: - Theme.of(context).hoverColor, - lineHeight: - kDesktopFileTransferRowHeight, - ).paddingSymmetric(vertical: 15), - ), - ], - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Offstage( - offstage: item.state != JobState.paused, - child: MenuButton( - onPressed: () { - jobController.resumeJob(item.id); - }, - child: SvgPicture.asset( - "assets/refresh.svg", - color: Colors.white, - ), - color: MyTheme.accent, - hoverColor: MyTheme.accent80, - ), - ), - MenuButton( - padding: EdgeInsets.only(right: 15), - child: SvgPicture.asset( - "assets/close.svg", - color: Colors.white, - ), - onPressed: () { - jobController.jobTable.removeAt(index); - jobController.cancelJob(item.id); - }, - color: MyTheme.accent, - hoverColor: MyTheme.accent80, - ), - ], - ), - ], - ), - ], - ).paddingSymmetric(vertical: 10), - ), - ); - }, - itemCount: jobController.jobTable.length, - ), - ), - ), + ), + ) + : statusListView(), + )), ); } @@ -418,8 +410,7 @@ class _FileManagerViewState extends State { }, onExit: (evt) => widget._mouseFocusScope.value = MouseFocusScope.none, - child: _buildFileList( - context, isLocal, _fileListScrollController), + child: _buildFileList(context, _fileListScrollController), )) ], ), @@ -864,7 +855,7 @@ class _FileManagerViewState extends State { } Widget _buildFileList( - BuildContext context, bool isLocal, ScrollController scrollController) { + BuildContext context, ScrollController scrollController) { final fd = controller.directory.value; final entries = fd.entries; @@ -1044,7 +1035,7 @@ class _FileManagerViewState extends State { // Header Row( children: [ - Expanded(child: _buildFileBrowserHeader(context, isLocal)), + Expanded(child: _buildFileBrowserHeader(context)), ], ), // Body @@ -1139,7 +1130,7 @@ class _FileManagerViewState extends State { return false; } - Widget _buildFileBrowserHeader(BuildContext context, bool isLocal) { + Widget _buildFileBrowserHeader(BuildContext context) { final padding = EdgeInsets.all(1.0); return SizedBox( height: kDesktopFileTransferHeaderHeight, @@ -1147,7 +1138,7 @@ class _FileManagerViewState extends State { children: [ Obx( () => headerItemFunc( - _nameColWidth.value, SortBy.name, translate("Name"), isLocal), + _nameColWidth.value, SortBy.name, translate("Name")), ), DraggableDivider( axis: Axis.vertical, @@ -1160,7 +1151,7 @@ class _FileManagerViewState extends State { ), Obx( () => headerItemFunc(_modifiedColWidth.value, SortBy.modified, - translate("Modified"), isLocal), + translate("Modified")), ), DraggableDivider( axis: Axis.vertical, @@ -1172,16 +1163,13 @@ class _FileManagerViewState extends State { _modifiedColWidth.value)); }, padding: padding), - Expanded( - child: - headerItemFunc(null, SortBy.size, translate("Size"), isLocal)) + Expanded(child: headerItemFunc(null, SortBy.size, translate("Size"))) ], ), ); } - Widget headerItemFunc( - double? width, SortBy sortBy, String name, bool isLocal) { + Widget headerItemFunc(double? width, SortBy sortBy, String name) { final headerTextStyle = Theme.of(context).dataTableTheme.headingTextStyle ?? TextStyle(); return ObxValue>( @@ -1222,7 +1210,7 @@ class _FileManagerViewState extends State { ), ), () { if (controller.sortBy.value == sortBy) { - return controller.sortAscending; + return controller.sortAscending.obs; } else { return Rx(null); } diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 6670677d6..11720ad56 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -208,7 +208,7 @@ class FileController { final history = RxList.empty(growable: true); final sortBy = SortBy.name.obs; - final sortAscending = true.obs; + var sortAscending = true; final JobController jobController; final OverlayDialogManager? dialogManager; @@ -285,7 +285,7 @@ class FileController { void changeSortStyle(SortBy sort, {bool? isLocal, bool ascending = true}) { sortBy.value = sort; - sortAscending.value = ascending; + sortAscending = ascending; directory.value.changeSortStyle(sort, ascending: ascending); } @@ -592,7 +592,7 @@ class FileController { class JobController { static final JobID jobID = JobID(); - final jobTable = RxList.empty(growable: true); + final jobTable = List.empty(growable: true).obs; final jobResultListener = JobResultListener>(); final GetSessionID getSessionID; String get sessionID => getSessionID(); From 08f9b3760f643de642fd3b81df4cb7e3092185fe Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 9 Mar 2023 06:30:39 +0800 Subject: [PATCH 07/27] fix #3562 set en as default language and add es for built-in Flutter context menu text --- flutter/lib/common.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 666eab0ba..d9c052723 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -430,7 +430,7 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom( ); List supportedLocales = const [ - // specify CN/TW to fix CJK issue in flutter + Locale('en', 'US'), Locale('zh', 'CN'), Locale('zh', 'TW'), Locale('zh', 'SG'), @@ -452,7 +452,7 @@ List supportedLocales = const [ Locale('vi'), Locale('pl'), Locale('kz'), - Locale('en', 'US'), + Locale('es'), ]; String formatDurationToTime(Duration duration) { From 970dfa3c88e42d6b54e18d3b4742d8e2bfa8fbec Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 9 Mar 2023 11:40:06 +0900 Subject: [PATCH 08/27] fix jobTable state can't update --- flutter/lib/desktop/pages/file_manager_page.dart | 8 ++++---- flutter/lib/models/file_model.dart | 11 ++++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 59dff5518..a1a686afe 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -157,10 +157,10 @@ class _FileManagerPageState extends State /// transfer status list /// watch transfer status Widget statusList() { - statusListView() => Obx(() => ListView.builder( + statusListView(List jobs) => ListView.builder( controller: ScrollController(), itemBuilder: (BuildContext context, int index) { - final item = jobController.jobTable[index]; + final item = jobs[index]; return Padding( padding: const EdgeInsets.only(bottom: 5), child: generateCard( @@ -281,7 +281,7 @@ class _FileManagerPageState extends State ); }, itemCount: jobController.jobTable.length, - )); + ); return PreferredSize( preferredSize: const Size(200, double.infinity), @@ -312,7 +312,7 @@ class _FileManagerPageState extends State ), ), ) - : statusListView(), + : statusListView(jobController.jobTable), )), ); } diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 11720ad56..1b5afd956 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -100,6 +100,7 @@ class FileModel { jobController.cancelJob(id); final job = jobController.jobTable[jobIndex]; job.state = JobState.done; + jobController.jobTable.refresh(); } } else { var need_override = false; @@ -286,7 +287,9 @@ class FileController { void changeSortStyle(SortBy sort, {bool? isLocal, bool ascending = true}) { sortBy.value = sort; sortAscending = ascending; - directory.value.changeSortStyle(sort, ascending: ascending); + directory.update((dir) { + dir?.changeSortStyle(sort, ascending: ascending); + }); } Future refresh() async { @@ -374,6 +377,7 @@ class FileController { job.totalSize = totalSize; job.fileCount = fileCount; debugPrint("update receive details:${fd.path}"); + jobController.jobTable.refresh(); } } else if (options.value.home.isEmpty) { options.value.home = fd.path; @@ -631,6 +635,7 @@ class JobController { job.speed = double.parse(evt['speed']); job.finishedSize = int.parse(evt['finished_size']); debugPrint("update job $id with $evt"); + jobTable.refresh(); } } catch (e) { debugPrint("Failed to tryUpdateJobProgress,evt:${evt.toString()}"); @@ -650,6 +655,7 @@ class JobController { job.finishedSize = job.totalSize; job.state = JobState.done; job.fileNum = int.parse(evt['file_num']); + jobTable.refresh(); } } @@ -665,6 +671,7 @@ class JobController { job.state = JobState.done; job.finishedSize = job.totalSize; } + jobTable.refresh(); } debugPrint("jobError $evt"); } @@ -713,6 +720,7 @@ class JobController { bind.sessionResumeJob( id: sessionID, actId: job.id, isRemote: job.isRemoteToLocal); job.state = JobState.inProgress; + jobTable.refresh(); } else { debugPrint("jobId $jobId is not exists"); } @@ -729,6 +737,7 @@ class JobController { final job = jobTable[jobIndex]; job.fileCount = num_entries; job.totalSize = total_size.toInt(); + jobTable.refresh(); } debugPrint("update folder files: $info"); } From 812c0c7fe109f9660a61845fe88f4e3acc3f06fc Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 9 Mar 2023 11:54:17 +0800 Subject: [PATCH 09/27] better interval Signed-off-by: fufesou --- flutter/lib/common/widgets/peers_view.dart | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index 9c98f24b8..695266fb9 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -45,7 +45,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener { var _lastChangeTime = DateTime.now(); var _lastQueryPeers = {}; var _lastQueryTime = DateTime.now().subtract(const Duration(hours: 1)); - var _queryCoun = 0; + var _queryCount = 0; var _exit = false; late final mobileWidth = () { @@ -78,12 +78,12 @@ class _PeersViewState extends State<_PeersView> with WindowListener { @override void onWindowFocus() { - _queryCoun = 0; + _queryCount = 0; } @override void onWindowMinimize() { - _queryCoun = _maxQueryCount; + _queryCount = _maxQueryCount; } @override @@ -149,6 +149,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener { // ignore: todo // TODO: variables walk through async tasks? void _startCheckOnlines() { + final queryInterval = const Duration(seconds: 20); () async { while (!_exit) { final now = DateTime.now(); @@ -158,18 +159,18 @@ class _PeersViewState extends State<_PeersView> with WindowListener { platformFFI.ffiBind .queryOnlines(ids: _curPeers.toList(growable: false)); _lastQueryPeers = {..._curPeers}; - _lastQueryTime = DateTime.now(); - _queryCoun = 0; + _lastQueryTime = DateTime.now().subtract(queryInterval); + _queryCount = 0; } } } else { - if (_queryCoun < _maxQueryCount) { - if (now.difference(_lastQueryTime) > const Duration(seconds: 20)) { + if (_queryCount < _maxQueryCount) { + if (now.difference(_lastQueryTime) > queryInterval) { if (_curPeers.isNotEmpty) { platformFFI.ffiBind .queryOnlines(ids: _curPeers.toList(growable: false)); _lastQueryTime = DateTime.now(); - _queryCoun += 1; + _queryCount += 1; } } } From b566251986c567f91cc0c4c90099b83f101da9ee Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 9 Mar 2023 13:07:56 +0800 Subject: [PATCH 10/27] fix peer online state Signed-off-by: fufesou --- flutter/lib/common/widgets/peers_view.dart | 27 +++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index 695266fb9..720e6727c 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -100,6 +100,19 @@ class _PeersViewState extends State<_PeersView> with WindowListener { ); } + onVisibilityChanged(VisibilityInfo info) { + final peerId = _peerId((info.key as ValueKey).value); + if (info.visibleFraction > 0.00001) { + _curPeers.add(peerId); + } else { + _curPeers.remove(peerId); + } + _lastChangeTime = DateTime.now(); + } + + String _cardId(String id) => widget.peers.name + id; + String _peerId(String cardId) => cardId.replaceAll(widget.peers.name, ''); + Widget _buildPeersView(Peers peers) { final body = ObxValue((searchText) { return FutureBuilder>( @@ -109,16 +122,8 @@ class _PeersViewState extends State<_PeersView> with WindowListener { final cards = []; for (final peer in peers) { final visibilityChild = VisibilityDetector( - key: ValueKey(peer.id), - onVisibilityChanged: (info) { - final peerId = (info.key as ValueKey).value; - if (info.visibleFraction > 0.00001) { - _curPeers.add(peerId); - } else { - _curPeers.remove(peerId); - } - _lastChangeTime = DateTime.now(); - }, + key: ValueKey(_cardId(peer.id)), + onVisibilityChanged: onVisibilityChanged, child: widget.peerCardBuilder(peer), ); cards.add(isDesktop @@ -165,7 +170,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener { } } else { if (_queryCount < _maxQueryCount) { - if (now.difference(_lastQueryTime) > queryInterval) { + if (now.difference(_lastQueryTime) >= queryInterval) { if (_curPeers.isNotEmpty) { platformFFI.ffiBind .queryOnlines(ids: _curPeers.toList(growable: false)); From 265b865fbb9102c5213b8d44a873ce20d83f779c Mon Sep 17 00:00:00 2001 From: solokot Date: Thu, 9 Mar 2023 11:01:42 +0300 Subject: [PATCH 11/27] Update ru.rs --- src/lang/ru.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index afeb8d17d..a11828431 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -461,8 +461,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Resolution", "Разрешение"), ("No transfers in progress", "Передача не осуществляется"), ("Set one-time password length", "Установить длину одноразового пароля"), - ("idd_driver_tip", ""), - ("confirm_idd_driver_tip", ""), - ("RDP Settings", ""), + ("idd_driver_tip", "Установите драйвер виртуального дисплея, который используется при отсутствии физических дисплеев."), + ("confirm_idd_driver_tip", "Включена функция установки драйвера виртуального дисплея. Обратите внимание, что для доверия к драйверу будет установлен тестовый сертификат. Этот сертификат будет использоваться только для подтверждения доверия драйверам Rustdesk."), + ("RDP Settings", "Настройки RDP"), ].iter().cloned().collect(); } From 5ae3d33f3c9aef4672d484a9306f1071165f0dab Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 9 Mar 2023 18:05:09 +0900 Subject: [PATCH 12/27] move selectedItems to file controller model --- .../lib/desktop/pages/file_manager_page.dart | 61 ++++++++----------- flutter/lib/models/file_model.dart | 15 ++++- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index a1a686afe..da88405a9 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -358,8 +358,6 @@ class _FileManagerViewState extends State { final _modifiedColWidth = kDesktopFileTransferModifiedColWidth.obs; final _fileListScrollController = ScrollController(); - late final _selectedItems = SelectedItems(isLocal: isLocal); - /// [_lastClickTime], [_lastClickEntry] help to handle double click var _lastClickTime = DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000; @@ -368,6 +366,7 @@ class _FileManagerViewState extends State { FileController get controller => widget.controller; bool get isLocal => widget.controller.isLocal; FFI get _ffi => widget._ffi; + SelectedItems get selectedItems => controller.selectedItems; @override void initState() { @@ -491,7 +490,7 @@ class _FileManagerViewState extends State { color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, onPressed: () { - _selectedItems.clear(); + selectedItems.clear(); controller.goBack(); }, ), @@ -506,7 +505,7 @@ class _FileManagerViewState extends State { color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, onPressed: () { - _selectedItems.clear(); + selectedItems.clear(); controller.goToParentDirectory(); }, ), @@ -703,10 +702,10 @@ class _FileManagerViewState extends State { hoverColor: Theme.of(context).hoverColor, ), MenuButton( - onPressed: validItems(_selectedItems) + onPressed: selectedItems.valid() ? () async { - await (controller.removeAction(_selectedItems)); - _selectedItems.clear(); + await (controller.removeAction(selectedItems)); + selectedItems.clear(); } : null, child: SvgPicture.asset( @@ -726,17 +725,17 @@ class _FileManagerViewState extends State { ? EdgeInsets.only(left: 10) : EdgeInsets.only(right: 10)), backgroundColor: MaterialStateProperty.all( - _selectedItems.length == 0 + selectedItems.length == 0 ? MyTheme.accent80 : MyTheme.accent, ), ), - onPressed: validItems(_selectedItems) + onPressed: selectedItems.valid() ? () { final otherSideData = controller.getOtherSideDirectoryData(); - controller.sendFiles(_selectedItems, otherSideData); - _selectedItems.clear(); + controller.sendFiles(selectedItems, otherSideData); + selectedItems.clear(); } : null, icon: isLocal @@ -744,7 +743,7 @@ class _FileManagerViewState extends State { translate('Send'), textAlign: TextAlign.right, style: TextStyle( - color: _selectedItems.length == 0 + color: selectedItems.length == 0 ? Theme.of(context).brightness == Brightness.light ? MyTheme.grayBg : MyTheme.darkGray @@ -755,7 +754,7 @@ class _FileManagerViewState extends State { quarterTurns: 2, child: SvgPicture.asset( "assets/arrow.svg", - color: _selectedItems.length == 0 + color: selectedItems.length == 0 ? Theme.of(context).brightness == Brightness.light ? MyTheme.grayBg : MyTheme.darkGray @@ -766,7 +765,7 @@ class _FileManagerViewState extends State { label: isLocal ? SvgPicture.asset( "assets/arrow.svg", - color: _selectedItems.length == 0 + color: selectedItems.length == 0 ? Theme.of(context).brightness == Brightness.light ? MyTheme.grayBg : MyTheme.darkGray @@ -775,7 +774,7 @@ class _FileManagerViewState extends State { : Text( translate('Receive'), style: TextStyle( - color: _selectedItems.length == 0 + color: selectedItems.length == 0 ? Theme.of(context).brightness == Brightness.light ? MyTheme.grayBg : MyTheme.darkGray @@ -809,13 +808,13 @@ class _FileManagerViewState extends State { MenuEntryButton( childBuilder: (style) => Text(translate("Select All"), style: style), proc: () => setState(() => - _selectedItems.selectAll(controller.directory.value.entries)), + selectedItems.selectAll(controller.directory.value.entries)), padding: kDesktopMenuPadding, dismissOnClicked: true), MenuEntryButton( childBuilder: (style) => Text(translate("Unselect All"), style: style), - proc: () => setState(() => _selectedItems.clear()), + proc: () => setState(() => selectedItems.clear()), padding: kDesktopMenuPadding, dismissOnClicked: true) ]; @@ -865,10 +864,10 @@ class _FileManagerViewState extends State { onNext: (buffer) { debugPrint("searching next for $buffer"); assert(buffer.length == 1); - assert(_selectedItems.length <= 1); + assert(selectedItems.length <= 1); var skipCount = 0; - if (_selectedItems.items.isNotEmpty) { - final index = entries.indexOf(_selectedItems.items.first); + if (selectedItems.items.isNotEmpty) { + final index = entries.indexOf(selectedItems.items.first); if (index < 0) { return; } @@ -884,7 +883,7 @@ class _FileManagerViewState extends State { (element) => element.name.toLowerCase().startsWith(buffer)); } if (searchResult.isEmpty) { - setState(() => _selectedItems.clear()); + setState(() => selectedItems.clear()); return; } _jumpToEntry(isLocal, searchResult.first, scrollController, @@ -892,12 +891,12 @@ class _FileManagerViewState extends State { }, onSearch: (buffer) { debugPrint("searching for $buffer"); - final selectedEntries = _selectedItems; + final selectedEntries = selectedItems; final searchResult = entries .where((element) => element.name.toLowerCase().startsWith(buffer)); selectedEntries.clear(); if (searchResult.isEmpty) { - setState(() => _selectedItems.clear()); + setState(() => selectedItems.clear()); return; } _jumpToEntry(isLocal, searchResult.first, scrollController, @@ -916,7 +915,7 @@ class _FileManagerViewState extends State { final lastModifiedStr = entry.isDrive ? " " : "${entry.lastModified().toString().replaceAll(".000", "")} "; - final isSelected = _selectedItems.contains(entry); + final isSelected = selectedItems.contains(entry); return Padding( padding: EdgeInsets.symmetric(vertical: 1), child: Container( @@ -970,7 +969,7 @@ class _FileManagerViewState extends State { )), ), onTap: () { - final items = _selectedItems; + final items = selectedItems; // handle double click if (_checkDoubleClick(entry)) { controller.openDirectory(entry.path); @@ -1056,7 +1055,7 @@ class _FileManagerViewState extends State { } onSearchText(String searchText, bool isLocal) { - _selectedItems.clear(); + selectedItems.clear(); _searchText.value = searchText; } @@ -1067,7 +1066,7 @@ class _FileManagerViewState extends State { if (index == -1) { debugPrint("entry is not valid: ${entry.path}"); } - final selectedEntries = _selectedItems; + final selectedEntries = selectedItems; final searchResult = entries.where((element) => element == entry); selectedEntries.clear(); if (searchResult.isEmpty) { @@ -1431,14 +1430,6 @@ class _FileManagerViewState extends State { // } } -bool validItems(SelectedItems items) { - if (items.length > 0) { - // exclude DirDrive type - return items.items.any((item) => !item.isDrive); - } - return false; -} - Widget buildWindowsThisPC(BuildContext context, [TextStyle? textStyle]) { final color = Theme.of(context).iconTheme.color?.withOpacity(0.7); return Row(children: [ diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 1b5afd956..30a897a56 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -214,6 +214,7 @@ class FileController { final OverlayDialogManager? dialogManager; final DirectoryData Function() getOtherSideDirectoryData; + late final SelectedItems selectedItems = SelectedItems(isLocal: isLocal); FileController( {required this.isLocal, @@ -1059,7 +1060,7 @@ class SelectedItems { SelectedItems({required this.isLocal}); - add(Entry e) { + void add(Entry e) { if (e.isDrive) return; if (!_items.contains(e)) { _items.add(e); @@ -1070,11 +1071,11 @@ class SelectedItems { return _items.contains(e); } - remove(Entry e) { + void remove(Entry e) { _items.remove(e); } - clear() { + void clear() { _items.clear(); } @@ -1082,6 +1083,14 @@ class SelectedItems { _items.clear(); _items.addAll(entries); } + + bool valid() { + if (length > 0) { + // exclude DirDrive type + return items.any((item) => !item.isDrive); + } + return false; + } } // edited from [https://github.com/DevsOnFlutter/file_manager/blob/c1bf7f0225b15bcb86eba602c60acd5c4da90dd8/lib/file_manager.dart#L22] From 61679a107265a826de2066f77853b7640429c239 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 9 Mar 2023 17:22:14 +0800 Subject: [PATCH 13/27] upgrade some crates, fix scrap benchmark on mac, fix compile issue on osx10.14 --- Cargo.lock | 1939 +++++++++++++++++-------------- Cargo.toml | 17 +- build.rs | 9 +- libs/hbb_common/Cargo.toml | 12 +- libs/scrap/Cargo.toml | 2 +- libs/scrap/src/common/quartz.rs | 2 +- src/client.rs | 2 +- src/common.rs | 12 + src/license.rs | 3 +- src/naming.rs | 3 +- src/platform/macos.mm | 4 + src/server/connection.rs | 6 +- src/ui_interface.rs | 4 +- 13 files changed, 1108 insertions(+), 907 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1029bfed4..c48a69e8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.17.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ "gimli", ] @@ -17,12 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - [[package]] name = "ahash" version = "0.7.6" @@ -111,12 +105,12 @@ dependencies = [ [[package]] name = "android_logger" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e9dd62f37dea550caf48c77591dc50bd1a378ce08855be1a0c42a97b7550fb" +checksum = "8619b80c242aa7bd638b5c7ddd952addeecb71f69c75e33f1d47b2804f8f883a" dependencies = [ "android_log-sys", - "env_logger 0.9.3", + "env_logger 0.10.0", "log", "once_cell", ] @@ -141,23 +135,24 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "arboard" -version = "2.1.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc120354d1b5ec6d7aaf4876b602def75595937b5e15d356eb554ab5177e08bb" +checksum = "d6041616acea41d67c4a984709ddab1587fd0b10efe5cc563fee954d2f011854" dependencies = [ "clipboard-win", "core-graphics 0.22.3", - "image 0.23.14", + "image", "log", "objc", "objc-foundation", "objc_id", + "once_cell", "parking_lot 0.12.1", "thiserror", "winapi 0.3.9", @@ -166,13 +161,12 @@ dependencies = [ [[package]] name = "async-broadcast" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d26004fe83b2d1cd3a97609b21e39f9a31535822210fe83205d2ce48866ea61" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" dependencies = [ "event-listener", "futures-core", - "parking_lot 0.12.1", ] [[package]] @@ -200,6 +194,18 @@ dependencies = [ "slab", ] +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock", + "autocfg 1.1.0", + "blocking", + "futures-lite", +] + [[package]] name = "async-io" version = "1.12.0" @@ -215,19 +221,18 @@ dependencies = [ "parking", "polling", "slab", - "socket2 0.4.7", + "socket2 0.4.9", "waker-fn", "windows-sys 0.42.0", ] [[package]] name = "async-lock" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" dependencies = [ "event-listener", - "futures-lite", ] [[package]] @@ -250,13 +255,13 @@ dependencies = [ [[package]] name = "async-recursion" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea" +checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -267,13 +272,13 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.59" +version = "0.1.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364" +checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -284,7 +289,7 @@ checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf" dependencies = [ "atk-sys", "bitflags", - "glib 0.16.5", + "glib 0.16.7", "libc", ] @@ -311,9 +316,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" +checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" [[package]] name = "atty" @@ -321,7 +326,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi 0.3.9", ] @@ -343,24 +348,30 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.5.4", + "miniz_oxide", "object", "rustc-demangle", ] [[package]] -name = "base64" -version = "0.13.1" +name = "base-x" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bindgen" @@ -377,12 +388,12 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "regex", "rustc-hash", "shlex", - "which 4.3.0", + "which", ] [[package]] @@ -397,19 +408,41 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "regex", "rustc-hash", "shlex", - "syn 1.0.105", + "syn 1.0.109", +] + +[[package]] +name = "bindgen" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2 1.0.51", + "quote 1.0.23", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", + "which", ] [[package]] name = "bit_field" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" [[package]] name = "bitflags" @@ -437,9 +470,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] @@ -471,9 +504,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.2" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -487,15 +520,15 @@ checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b" [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "bytemuck" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" [[package]] name = "byteorder" @@ -505,11 +538,11 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" dependencies = [ - "serde 1.0.149", + "serde 1.0.154", ] [[package]] @@ -520,7 +553,7 @@ checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" dependencies = [ "bitflags", "cairo-sys-rs", - "glib 0.16.5", + "glib 0.16.7", "libc", "once_cell", "thiserror", @@ -549,11 +582,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" +checksum = "6031a462f977dd38968b6f23378356512feeace69cef817e1a4475108093cec3" dependencies = [ - "serde 1.0.149", + "serde 1.0.154", ] [[package]] @@ -562,7 +595,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" dependencies = [ - "serde 1.0.149", + "serde 1.0.154", ] [[package]] @@ -573,9 +606,9 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.14", - "serde 1.0.149", - "serde_json 1.0.89", + "semver 1.0.16", + "serde 1.0.154", + "serde_json 1.0.94", ] [[package]] @@ -585,23 +618,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb" dependencies = [ "clap 3.2.23", - "heck 0.4.0", + "heck 0.4.1", "indexmap", "log", - "proc-macro2 1.0.47", - "quote 1.0.21", - "serde 1.0.149", - "serde_json 1.0.89", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "serde 1.0.154", + "serde_json 1.0.94", + "syn 1.0.109", "tempfile", - "toml", + "toml 0.5.11", ] [[package]] name = "cc" -version = "1.0.77" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -659,9 +692,9 @@ dependencies = [ [[package]] name = "cidr-utils" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355d5b5df67e58b523953d0c1a8d3d2c05f5af51f1332b0199b9c92263614ed0" +checksum = "fdfa36f04861d39453affe1cf084ce2d6554021a84eb6f31ebdeafb6fb92a01c" dependencies = [ "debug-helper", "num-bigint", @@ -672,9 +705,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" +checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" dependencies = [ "glob", "libc", @@ -705,7 +738,7 @@ dependencies = [ "atty", "bitflags", "clap_derive", - "clap_lex", + "clap_lex 0.2.4", "indexmap", "once_cell", "strsim 0.10.0", @@ -713,17 +746,30 @@ dependencies = [ "textwrap 0.16.0", ] +[[package]] +name = "clap" +version = "4.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +dependencies = [ + "bitflags", + "clap_lex 0.3.2", + "is-terminal", + "strsim 0.10.0", + "termcolor", +] + [[package]] name = "clap_derive" version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ - "heck 0.4.0", + "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -735,6 +781,15 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "clap_lex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "clipboard" version = "0.1.0" @@ -742,16 +797,16 @@ dependencies = [ "cc", "hbb_common", "lazy_static", - "serde 1.0.149", + "serde 1.0.154", "serde_derive", "thiserror", ] [[package]] name = "clipboard-win" -version = "4.4.2" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4ab1b92798304eedc095b53942963240037c0516452cb11aeba709d420b2219" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" dependencies = [ "error-code", "str-buf", @@ -823,6 +878,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colored" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +dependencies = [ + "atty", + "lazy_static", + "winapi 0.3.9", +] + [[package]] name = "combine" version = "4.6.6" @@ -835,9 +901,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" dependencies = [ "crossbeam-utils", ] @@ -848,9 +914,9 @@ version = "0.4.0" source = "git+https://github.com/open-trade/confy#630cc28a396cb7d01eefdd9f3824486fe4d8554b" dependencies = [ "directories-next", - "serde 1.0.149", + "serde 1.0.154", "thiserror", - "toml", + "toml 0.5.11", ] [[package]] @@ -972,27 +1038,26 @@ dependencies = [ [[package]] name = "cpal" -version = "0.13.5" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74117836a5124f3629e4b474eed03e479abaf98988b4bb317e29f08cfe0e4116" +checksum = "f342c1b63e185e9953584ff2199726bf53850d96610a310e3aca09e9405a2d0b" dependencies = [ "alsa", "core-foundation-sys 0.8.3", "coreaudio-rs", "jni 0.19.0", "js-sys", - "lazy_static", "libc", "mach", - "ndk 0.6.0", - "ndk-glue 0.6.2", - "nix 0.23.2", + "ndk 0.7.0", + "ndk-context", "oboe", - "parking_lot 0.11.2", + "once_cell", + "parking_lot 0.12.1", "stdweb", "thiserror", "web-sys", - "winapi 0.3.9", + "windows 0.37.0", ] [[package]] @@ -1015,9 +1080,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -1025,9 +1090,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", @@ -1036,14 +1101,14 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg 1.1.0", "cfg-if 1.0.0", "crossbeam-utils", - "memoffset 0.7.1", + "memoffset 0.8.0", "scopeguard", ] @@ -1059,9 +1124,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if 1.0.0", ] @@ -1084,12 +1149,12 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.2.4" +version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1631ca6e3c59112501a9d87fd86f21591ff77acd31331e8a73f8d80a65bbdd71" +checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639" dependencies = [ - "nix 0.26.1", - "windows-sys 0.42.0", + "nix 0.26.2", + "windows-sys 0.45.0", ] [[package]] @@ -1100,9 +1165,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "cxx" -version = "1.0.83" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" +checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" dependencies = [ "cc", "cxxbridge-flags", @@ -1112,34 +1177,34 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.83" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" +checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "scratch", - "syn 1.0.105", + "syn 1.0.109", ] [[package]] name = "cxxbridge-flags" -version = "1.0.83" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" +checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" [[package]] name = "cxxbridge-macro" -version = "1.0.83" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" +checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -1187,10 +1252,10 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "strsim 0.9.3", - "syn 1.0.105", + "syn 1.0.109", ] [[package]] @@ -1201,10 +1266,10 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "strsim 0.10.0", - "syn 1.0.105", + "syn 1.0.109", ] [[package]] @@ -1214,8 +1279,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core 0.10.2", - "quote 1.0.21", - "syn 1.0.105", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -1225,8 +1290,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", - "quote 1.0.21", - "syn 1.0.105", + "quote 1.0.23", + "syn 1.0.109", +] + +[[package]] +name = "dart-sys" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8b5680b5c2cc52f50acb2457d9b3a3b58adcca785db13a0e3655626f601de6" +dependencies = [ + "cc", ] [[package]] @@ -1350,9 +1424,9 @@ dependencies = [ [[package]] name = "dbus" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8bcdd56d2e5c4ed26a529c5a9029f5db8290d433497506f958eae3be148eb6" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" dependencies = [ "libc", "libdbus-sys", @@ -1361,9 +1435,9 @@ dependencies = [ [[package]] name = "dbus-crossroads" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554114296d012b33fdaf362a733db6dc5f73c4c9348b8b620ddd42e61b406e30" +checksum = "3a4c83437187544ba5142427746835061b330446ca8902eabd70e4afb8f76de0" dependencies = [ "dbus", ] @@ -1392,25 +1466,15 @@ dependencies = [ "windows 0.30.0", ] -[[package]] -name = "deflate" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" -dependencies = [ - "adler32", - "byteorder", -] - [[package]] name = "delegate" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "082a24a9967533dc5d743c602157637116fc1b52806d694a5a45e6f32567fcdd" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -1419,9 +1483,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -1431,9 +1495,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1cf41b4580a37cca5ef2ada2cc43cf5d6be3983f4522e83010d67ab6925e84b" dependencies = [ "darling 0.10.2", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -1513,6 +1577,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + [[package]] name = "dispatch" version = "0.2.0" @@ -1565,7 +1635,7 @@ checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f" dependencies = [ "lazy_static", "regex", - "serde 1.0.149", + "serde 1.0.154", "strsim 0.10.0", ] @@ -1588,25 +1658,25 @@ dependencies = [ "cc", "hbb_common", "lazy_static", - "serde 1.0.149", + "serde 1.0.154", "serde_derive", "thiserror", ] [[package]] name = "ed25519" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "signature", ] [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "embed-resource" @@ -1616,16 +1686,16 @@ checksum = "e62abb876c07e4754fae5c14cafa77937841f01740637e17d78dc04352f32a5e" dependencies = [ "cc", "rustc_version 0.4.0", - "toml", + "toml 0.5.11", "vswhom", "winreg 0.10.1", ] [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if 1.0.0", ] @@ -1640,7 +1710,7 @@ dependencies = [ "objc", "pkg-config", "rdev", - "serde 1.0.149", + "serde 1.0.154", "serde_derive", "tfc", "unicode-segmentation", @@ -1649,34 +1719,34 @@ dependencies = [ [[package]] name = "enum-map" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a56d54c8dd9b3ad34752ed197a4eb2a6601bc010808eb097a04a58ae4c43e1" +checksum = "50c25992259941eb7e57b936157961b217a4fc8597829ddef0596d6c3cd86e1a" dependencies = [ "enum-map-derive", ] [[package]] name = "enum-map-derive" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9045e2676cd5af83c3b167d917b0a5c90a4d8e266e2683d6631b235c457fc27" +checksum = "2a4da76b3b6116d758c7ba93f7ec6a35d2e2cf24feda76c6e38a375f4d5c59f2" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] name = "enum_dispatch" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb" +checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2" dependencies = [ "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -1686,7 +1756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" dependencies = [ "enumflags2_derive", - "serde 1.0.149", + "serde 1.0.154", ] [[package]] @@ -1695,9 +1765,9 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -1723,6 +1793,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "epoll" version = "4.3.1" @@ -1740,10 +1823,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34a887c8df3ed90498c1c437ce21f211c8e27672921a8ffa293cb8d6d4caa9e" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "rustversion", - "syn 1.0.105", + "syn 1.0.109", "synstructure", ] @@ -1758,6 +1841,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "errno" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -1804,62 +1898,52 @@ dependencies = [ "flume", "half", "lebe", - "miniz_oxide 0.6.2", + "miniz_oxide", "smallvec", "threadpool", "zune-inflate", ] -[[package]] -name = "extend" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5216e387a76eebaaf11f6d871ec8a4aae0b25f05456ee21f228e024b1b3610" -dependencies = [ - "proc-macro-error", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", -] - -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", -] - [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] [[package]] -name = "field-offset" -version = "0.3.4" +name = "fern" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" +checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a" dependencies = [ - "memoffset 0.6.5", - "rustc_version 0.3.3", + "chrono", + "colored", + "log", +] + +[[package]] +name = "field-offset" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" +dependencies = [ + "memoffset 0.8.0", + "rustc_version 0.4.0", ] [[package]] name = "filetime" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -1869,27 +1953,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", - "miniz_oxide 0.6.2", + "miniz_oxide", ] [[package]] name = "flexi_logger" -version = "0.22.6" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c76a80dd14a27fc3d8bc696502132cb52b3f227256fd8601166c3a35e45f409" +checksum = "6eae57842a8221ef13f1f207632d786a175dd13bd8fbdc8be9d852f7c9cf1046" dependencies = [ - "ansi_term", - "atty", "chrono", "crossbeam-channel", "crossbeam-queue", "glob", + "is-terminal", "lazy_static", "log", + "nu-ansi-term", "regex", - "rustversion", "thiserror", - "time 0.3.9", ] [[package]] @@ -1907,9 +1989,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge" -version = "1.61.1" +version = "1.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8079119bbe8fb63d7ebb731fa2aa68c6c8375f4ac95ca26d5868e64c0f4b9244" +checksum = "54f0d71ff30fc2ae7c18508b517488a89051d81e3bfc4d48d4a6cf54b5dab789" dependencies = [ "allo-isolate", "anyhow", @@ -1918,6 +2000,7 @@ dependencies = [ "cc", "chrono", "console_error_panic_hook", + "dart-sys", "flutter_rust_bridge_macros", "js-sys", "lazy_static", @@ -1931,39 +2014,41 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_codegen" -version = "1.61.1" +version = "1.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd7396bc479eae8aa24243e4c0e3d3dbda1909134f8de6bde4f080d262c9a0d" +checksum = "2a2a75a72411f0c5b480e4671417f52780172053128cf87d5614a9757d7680a0" dependencies = [ "anyhow", + "atty", "cargo_metadata", "cbindgen", + "chrono", "clap 3.2.23", "convert_case", "delegate", "enum_dispatch", - "env_logger 0.9.3", - "extend", + "fern", "itertools 0.10.5", "lazy_static", "log", "pathdiff", - "quote 1.0.21", + "quote 1.0.23", "regex", - "serde 1.0.149", + "serde 1.0.154", "serde_yaml", - "syn 1.0.105", + "strum_macros 0.24.3", + "syn 1.0.109", "tempfile", "thiserror", - "toml", + "toml 0.5.11", "topological-sort", ] [[package]] name = "flutter_rust_bridge_macros" -version = "1.61.1" +version = "1.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5cd827645690ef378be57a890d0581e17c28d07b712872af7d744f454fd27d" +checksum = "f6187d1635afede47c23a9979f85c3dc57e3391eb9c690b7fe95715bf945da72" [[package]] name = "fnv" @@ -2038,9 +2123,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -2053,9 +2138,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -2063,15 +2148,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -2080,9 +2165,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-lite" @@ -2101,32 +2186,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -2140,15 +2225,6 @@ dependencies = [ "slab", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - [[package]] name = "gdk" version = "0.16.2" @@ -2160,7 +2236,7 @@ dependencies = [ "gdk-pixbuf", "gdk-sys", "gio", - "glib 0.16.5", + "glib 0.16.7", "libc", "pango", ] @@ -2174,7 +2250,7 @@ dependencies = [ "bitflags", "gdk-pixbuf-sys", "gio", - "glib 0.16.5", + "glib 0.16.7", "libc", ] @@ -2232,7 +2308,7 @@ dependencies = [ "glib-sys 0.16.3", "libc", "system-deps 6.0.3", - "x11 2.20.1", + "x11 2.21.0", ] [[package]] @@ -2280,9 +2356,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.2" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "gio" @@ -2296,7 +2372,7 @@ dependencies = [ "futures-io", "futures-util", "gio-sys", - "glib 0.16.5", + "glib 0.16.7", "libc", "once_cell", "pin-project-lite", @@ -2338,9 +2414,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.16.5" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cd04d150a2c63e6779f43aec7e04f5374252479b7bed5f45146d9c0e821f161" +checksum = "ddd4df61a866ed7259d6189b8bcb1464989a77f1d85d25d002279bbe9dd38b2f" dependencies = [ "bitflags", "futures-channel", @@ -2369,9 +2445,9 @@ dependencies = [ "itertools 0.9.0", "proc-macro-crate 0.1.5", "proc-macro-error", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -2381,12 +2457,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf" dependencies = [ "anyhow", - "heck 0.4.0", - "proc-macro-crate 1.2.1", + "heck 0.4.1", + "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -2411,9 +2487,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gobject-sys" @@ -2584,7 +2660,7 @@ dependencies = [ "gdk", "gdk-pixbuf", "gio", - "glib 0.16.5", + "glib 0.16.7", "gtk-sys", "gtk3-macros", "libc", @@ -2618,18 +2694,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cfd6557b1018b773e43c8de9d0d13581d6b36190d0501916cbec4731db5ccff" dependencies = [ "anyhow", - "proc-macro-crate 1.2.1", + "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] name = "h2" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" dependencies = [ "bytes", "fnv", @@ -2673,7 +2749,7 @@ dependencies = [ "confy", "directories-next", "dirs-next", - "env_logger 0.9.3", + "env_logger 0.10.0", "filetime", "flexi_logger", "futures", @@ -2689,16 +2765,16 @@ dependencies = [ "quinn", "rand 0.8.5", "regex", - "serde 1.0.149", + "serde 1.0.154", "serde_derive", - "serde_json 1.0.89", + "serde_json 1.0.94", "socket2 0.3.19", "sodiumoxide", "sysinfo", "tokio", "tokio-socks", "tokio-util", - "toml", + "toml 0.7.2", "winapi 0.3.9", "zstd", ] @@ -2714,9 +2790,9 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -2727,6 +2803,21 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -2741,13 +2832,13 @@ checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1" [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", - "itoa 1.0.4", + "itoa 1.0.6", ] [[package]] @@ -2782,21 +2873,21 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" version = "0.1.0" -source = "git+https://github.com/21pages/hwcodec#64f885b3787694b16dfcff08256750b0376b2eba" +source = "git+https://github.com/21pages/hwcodec#d55f7761ef692fae738259d8c14506d901eb824c" dependencies = [ "bindgen 0.59.2", "cc", "log", - "serde 1.0.149", + "serde 1.0.154", "serde_derive", - "serde_json 1.0.89", + "serde_json 1.0.94", ] [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -2807,9 +2898,9 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.4", + "itoa 1.0.6", "pin-project-lite", - "socket2 0.4.7", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2869,22 +2960,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "image" -version = "0.23.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-iter", - "num-rational 0.3.2", - "num-traits 0.2.15", - "png 0.16.8", - "tiff 0.6.1", -] - [[package]] name = "image" version = "0.24.5" @@ -2896,12 +2971,12 @@ dependencies = [ "color_quant", "exr", "gif", - "jpeg-decoder 0.3.0", + "jpeg-decoder", "num-rational 0.4.1", "num-traits 0.2.15", - "png 0.17.7", + "png", "scoped_threadpool", - "tiff 0.8.1", + "tiff", ] [[package]] @@ -2927,8 +3002,8 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", ] [[package]] @@ -2973,6 +3048,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "io-lifetimes" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "iovec" version = "0.1.4" @@ -2984,9 +3069,21 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec947b7a4ce12e3b87e353abae7ce124d025b6c7d6c5aea5cc0bcf92e9510ded" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "is-terminal" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] [[package]] name = "itertools" @@ -3014,9 +3111,9 @@ checksum = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" [[package]] name = "itoa" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jni" @@ -3054,19 +3151,13 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" - [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -3078,9 +3169,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -3102,7 +3193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7668b7cff6a51fe61cdde64cd27c8a220786f399501b57ebe36f7d8112fd68" dependencies = [ "bitflags", - "serde 1.0.149", + "serde 1.0.154", "unicode-segmentation", ] @@ -3130,7 +3221,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e1edfdc9b0853358306c6dfb4b77c79c779174256fe93d80c0b5ebca451a2f" dependencies = [ - "glib 0.16.5", + "glib 0.16.7", "gtk", "gtk-sys", "libappindicator-sys", @@ -3150,15 +3241,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.138" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libdbus-sys" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c185b5b7ad900923ef3a8ff594083d4d9b5aea80bb4f32b8342363138c0d456b" +checksum = "9f8d7ae751e1cb825c840ae5e682f59b098cdfd213c350ac268b61449a5f58a0" dependencies = [ "pkg-config", ] @@ -3175,9 +3266,9 @@ dependencies = [ [[package]] name = "libpulse-binding" -version = "2.26.0" +version = "2.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17be42160017e0ae993c03bfdab4ecb6f82ce3f8d515bd8da8fdf18d10703663" +checksum = "1745b20bfc194ac12ef828f144f0ec2d4a7fe993281fa3567a0bd4969aee6890" dependencies = [ "bitflags", "libc", @@ -3189,9 +3280,9 @@ dependencies = [ [[package]] name = "libpulse-simple-binding" -version = "2.25.0" +version = "2.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cbf1a1dfd69a48cb60906399fa1d17f1b75029ef51c0789597be792dfd0bcd5" +checksum = "5ced94199e6e44133431374e4043f34e1f0697ebfb7b7d6c244a65bfaedf0e31" dependencies = [ "libpulse-binding", "libpulse-simple-sys", @@ -3200,9 +3291,9 @@ dependencies = [ [[package]] name = "libpulse-simple-sys" -version = "1.19.2" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c73f96f9ca34809692c4760cfe421225860aa000de50edab68a16221fd27cc1" +checksum = "84e423d9c619c908ce9b4916080e65ab586ca55b8c4939379f15e6e72fb43842" dependencies = [ "libpulse-sys", "pkg-config", @@ -3210,9 +3301,9 @@ dependencies = [ [[package]] name = "libpulse-sys" -version = "1.19.3" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991e6bd0efe2a36e6534e136e7996925e4c1a8e35b7807fe533f2beffff27c30" +checksum = "2191e6880818d1df4cf72eac8e91dce7a5a52ba0da4b2a5cdafabc22b937eadb" dependencies = [ "libc", "num-derive", @@ -3258,14 +3349,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" dependencies = [ "libc", - "x11 2.20.1", + "x11 2.21.0", +] + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", ] [[package]] name = "link-cplusplus" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" dependencies = [ "cc", ] @@ -3276,6 +3376,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -3386,6 +3492,15 @@ dependencies = [ "autocfg 1.1.0", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg 1.1.0", +] + [[package]] name = "mime" version = "0.3.16" @@ -3398,34 +3513,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" -dependencies = [ - "adler32", -] - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg 1.1.0", -] - -[[package]] -name = "miniz_oxide" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.6.2" @@ -3456,14 +3543,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -3518,9 +3605,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.4.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66365a21dc5e322c6b6ba25c735d00153c57dd2eb377926aa50e3caf547b6f6" +checksum = "0ee091608fe840d80c6b25e8f838964b264ee8e02e82ae0a38b2d900464d1582" dependencies = [ "cocoa", "crossbeam-channel", @@ -3531,7 +3618,7 @@ dependencies = [ "libxdo", "objc", "once_cell", - "png 0.17.7", + "png", "thiserror", "windows-sys 0.45.0", ] @@ -3587,7 +3674,7 @@ dependencies = [ "jni-sys", "ndk-sys 0.4.1+23.1.7779620", "num_enum", - "raw-window-handle 0.5.0", + "raw-window-handle 0.5.1", "thiserror", ] @@ -3612,21 +3699,6 @@ dependencies = [ "ndk-sys 0.2.2", ] -[[package]] -name = "ndk-glue" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f" -dependencies = [ - "lazy_static", - "libc", - "log", - "ndk 0.6.0", - "ndk-context", - "ndk-macro", - "ndk-sys 0.3.0", -] - [[package]] name = "ndk-macro" version = "0.3.0" @@ -3634,10 +3706,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" dependencies = [ "darling 0.13.4", - "proc-macro-crate 1.2.1", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -3715,35 +3787,23 @@ dependencies = [ [[package]] name = "nix" -version = "0.25.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "autocfg 1.1.0", "bitflags", "cfg-if 1.0.0", "libc", - "memoffset 0.6.5", + "memoffset 0.7.1", "pin-utils", -] - -[[package]] -name = "nix" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694" -dependencies = [ - "bitflags", - "cfg-if 1.0.0", - "libc", "static_assertions", ] [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -3751,13 +3811,23 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.3.7" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -3771,9 +3841,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" dependencies = [ "num-traits 0.2.15", ] @@ -3784,9 +3854,9 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -3799,17 +3869,6 @@ dependencies = [ "num-traits 0.2.15", ] -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits 0.2.15", -] - [[package]] name = "num-rational" version = "0.3.2" @@ -3852,42 +3911,33 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ - "proc-macro-crate 1.2.1", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -3931,9 +3981,9 @@ dependencies = [ [[package]] name = "object" -version = "0.29.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] @@ -3963,9 +4013,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl-probe" @@ -3985,14 +4035,26 @@ dependencies = [ [[package]] name = "ordered-stream" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ca8c99d73c6e92ac1358f9f692c22c0bfd9c4701fa086f5d365c0d4ea818ea" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" dependencies = [ "futures-core", "pin-project-lite", ] +[[package]] +name = "os-version" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a8a1fed76ac765e39058ca106b6229a93c5a60292a1bd4b602ce2be11e1c020" +dependencies = [ + "anyhow", + "plist", + "uname", + "winapi 0.3.9", +] + [[package]] name = "os_str_bytes" version = "6.4.1" @@ -4005,11 +4067,17 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38731fa859ef679f1aec66ca9562165926b442f298467f76f5990f431efe87dc" dependencies = [ - "serde 1.0.149", + "serde 1.0.154", "serde_derive", - "serde_json 1.0.89", + "serde_json 1.0.94", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "pango" version = "0.16.5" @@ -4018,7 +4086,7 @@ checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" dependencies = [ "bitflags", "gio", - "glib 0.16.5", + "glib 0.16.7", "libc", "once_cell", "pango-sys", @@ -4065,7 +4133,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core 0.8.5", + "parking_lot_core 0.8.6", ] [[package]] @@ -4075,14 +4143,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.5", + "parking_lot_core 0.9.7", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if 1.0.0", "instant", @@ -4094,22 +4162,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "paste" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pathdiff" @@ -4129,16 +4197,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" -[[package]] -name = "pest" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0" -dependencies = [ - "thiserror", - "ucd-trie", -] - [[package]] name = "phf" version = "0.7.24" @@ -4192,9 +4250,9 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -4216,15 +4274,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] -name = "png" -version = "0.16.8" +name = "plist" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +checksum = "ffac6a51110e97610dd3ac73e34a65b27e56a1e305df41bad1f616d8e1cb22f4" dependencies = [ - "bitflags", - "crc32fast", - "deflate", - "miniz_oxide 0.3.7", + "base64", + "indexmap", + "line-wrap", + "quick-xml", + "serde 1.0.154", + "time 0.3.20", ] [[package]] @@ -4236,21 +4296,23 @@ dependencies = [ "bitflags", "crc32fast", "flate2", - "miniz_oxide 0.6.2", + "miniz_oxide", ] [[package]] name = "polling" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "166ca89eb77fd403230b9c156612965a81e094ec6ec3aa13663d4c8b113fa748" +checksum = "7e1f879b2998099c2d69ab9605d145d5b661195627eccc680002c4918a7fb6fa" dependencies = [ "autocfg 1.1.0", + "bitflags", "cfg-if 1.0.0", + "concurrent-queue", "libc", "log", - "wepoll-ffi", - "windows-sys 0.42.0", + "pin-project-lite", + "windows-sys 0.45.0", ] [[package]] @@ -4280,18 +4342,17 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -4301,9 +4362,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", "version_check", ] @@ -4313,8 +4374,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "version_check", ] @@ -4329,9 +4390,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -4376,7 +4437,7 @@ dependencies = [ "protobuf-support", "tempfile", "thiserror", - "which 4.3.0", + "which", ] [[package]] @@ -4402,17 +4463,25 @@ dependencies = [ ] [[package]] -name = "quinn" -version = "0.8.5" +name = "quick-xml" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b435e71d9bfa0d8889927231970c51fb89c58fa63bffcab117c9c7a41e5ef8f" +checksum = "ffc053f057dd768a56f62cd7e434c42c831d296968997e9ac1f76ea7c2d14c41" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445cbfe2382fa023c4f2f3c7e1c95c03dcc1df2bf23cebcb2b13e1402c4394d1" dependencies = [ "bytes", - "futures-channel", - "futures-util", - "fxhash", + "pin-project-lite", "quinn-proto", "quinn-udp", + "rustc-hash", "rustls", "thiserror", "tokio", @@ -4422,17 +4491,16 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.8.4" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fce546b9688f767a57530652488420d419a8b1f44a478b451c3d1ab6d992a55" +checksum = "72ef4ced82a24bb281af338b9e8f94429b6eca01b4e66d899f40031f074e74c9" dependencies = [ "bytes", - "fxhash", "rand 0.8.5", "ring", + "rustc-hash", "rustls", "rustls-native-certs", - "rustls-pemfile 0.2.1", "slab", "thiserror", "tinyvec", @@ -4442,16 +4510,15 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.1.4" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07946277141531aea269befd949ed16b2c85a780ba1043244eda0969e538e54" +checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4" dependencies = [ - "futures-util", "libc", "quinn-proto", - "socket2 0.4.7", - "tokio", + "socket2 0.4.9", "tracing", + "windows-sys 0.42.0", ] [[package]] @@ -4465,11 +4532,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ - "proc-macro2 1.0.47", + "proc-macro2 1.0.51", ] [[package]] @@ -4625,18 +4692,15 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" -dependencies = [ - "cty", -] +checksum = "4f851a03551ceefd30132e447f07f96cb7011d6b658374f3aed847333adb5559" [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -4644,9 +4708,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -4670,12 +4734,12 @@ dependencies = [ "lazy_static", "libc", "log", - "mio 0.8.5", + "mio 0.8.6", "strum 0.24.1", "strum_macros 0.24.3", "widestring 1.0.2", "winapi 0.3.9", - "x11 2.20.1", + "x11 2.21.0", ] [[package]] @@ -4718,9 +4782,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -4733,15 +4797,6 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "repng" version = "0.2.2" @@ -4754,9 +4809,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ "base64", "bytes", @@ -4776,9 +4831,9 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls", - "rustls-pemfile 1.0.1", - "serde 1.0.149", - "serde_json 1.0.89", + "rustls-pemfile", + "serde 1.0.154", + "serde_json 1.0.94", "serde_urlencoded", "tokio", "tokio-rustls", @@ -4852,12 +4907,12 @@ dependencies = [ [[package]] name = "runas" -version = "0.2.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a620b0994a180cdfa25c0439e6d58c0628272571501880d626ffff58e96a0799" +checksum = "ed87390fefd18965ff20baae5aeb9913bcf82d2b59dc04c0f6d8f17f7be56ff2" dependencies = [ "cc", - "which 3.1.1", + "which", ] [[package]] @@ -4892,11 +4947,11 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" -version = "0.3.3" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.11.0", + "semver 0.9.0", ] [[package]] @@ -4905,14 +4960,14 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.14", + "semver 1.0.16", ] [[package]] name = "rustdesk" version = "1.2.0" dependencies = [ - "android_logger 0.11.1", + "android_logger 0.11.3", "arboard", "async-process", "async-trait", @@ -4922,7 +4977,7 @@ dependencies = [ "cfg-if 1.0.0", "chrono", "cidr-utils", - "clap 3.2.23", + "clap 4.1.8", "clipboard", "cocoa", "core-foundation 0.9.3", @@ -4937,7 +4992,7 @@ dependencies = [ "dispatch", "dlopen", "enigo", - "errno", + "errno 0.3.0", "evdev", "flutter_rust_bridge", "flutter_rust_bridge_codegen", @@ -4945,7 +5000,7 @@ dependencies = [ "hbb_common", "hex", "hound", - "image 0.24.5", + "image", "impersonate_system", "include_dir", "jni 0.19.0", @@ -4959,6 +5014,7 @@ dependencies = [ "num_cpus", "objc", "objc_id", + "os-version", "parity-tokio-ipc", "rdev", "repng", @@ -4970,9 +5026,9 @@ dependencies = [ "samplerate", "sciter-rs", "scrap", - "serde 1.0.149", + "serde 1.0.154", "serde_derive", - "serde_json 1.0.89", + "serde_json 1.0.94", "sha2", "shared_memory", "shutdown_hooks", @@ -5021,10 +5077,24 @@ dependencies = [ ] [[package]] -name = "rustls" -version = "0.20.7" +name = "rustix" +version = "0.36.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +dependencies = [ + "bitflags", + "errno 0.2.8", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -5039,40 +5109,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.1", + "rustls-pemfile", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" -version = "0.2.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" -dependencies = [ - "base64", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ "base64", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] name = "same-file" @@ -5094,12 +5161,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] @@ -5136,7 +5202,7 @@ name = "scrap" version = "0.5.0" dependencies = [ "android_logger 0.10.1", - "bindgen 0.59.2", + "bindgen 0.64.0", "block", "cfg-if 1.0.0", "dbus", @@ -5153,8 +5219,8 @@ dependencies = [ "num_cpus", "quest", "repng", - "serde 1.0.149", - "serde_json 1.0.89", + "serde 1.0.154", + "serde_json 1.0.94", "target_build_utils", "tracing", "webm", @@ -5163,9 +5229,9 @@ dependencies = [ [[package]] name = "scratch" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "sct" @@ -5179,9 +5245,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation 0.9.3", @@ -5192,9 +5258,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys 0.8.3", "libc", @@ -5202,30 +5268,27 @@ dependencies = [ [[package]] name = "semver" -version = "0.11.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ "semver-parser", ] [[package]] name = "semver" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" dependencies = [ - "serde 1.0.149", + "serde 1.0.154", ] [[package]] name = "semver-parser" -version = "0.10.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" @@ -5235,22 +5298,22 @@ checksum = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af" [[package]] name = "serde" -version = "1.0.149" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" +checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.149" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" +checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -5267,24 +5330,33 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ - "itoa 1.0.4", + "itoa 1.0.6", "ryu", - "serde 1.0.149", + "serde 1.0.154", ] [[package]] name = "serde_repr" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" +checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", +] + +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde 1.0.154", ] [[package]] @@ -5294,9 +5366,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.4", + "itoa 1.0.6", "ryu", - "serde 1.0.149", + "serde 1.0.154", ] [[package]] @@ -5307,10 +5379,19 @@ checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ "indexmap", "ryu", - "serde 1.0.149", + "serde 1.0.154", "yaml-rust", ] +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + [[package]] name = "sha1" version = "0.10.5" @@ -5322,6 +5403,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.10.6" @@ -5360,9 +5447,9 @@ checksum = "6057adedbec913419c92996f395ba69931acbd50b7d56955394cd3f7bedbfa45" [[package]] name = "signal-hook" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" dependencies = [ "libc", "signal-hook-registry", @@ -5370,9 +5457,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -5395,7 +5482,7 @@ version = "0.1.0" dependencies = [ "confy", "hbb_common", - "serde 1.0.149", + "serde 1.0.154", "serde_derive", "walkdir", ] @@ -5408,9 +5495,9 @@ checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg 1.1.0", ] @@ -5453,9 +5540,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi 0.3.9", @@ -5470,7 +5557,7 @@ dependencies = [ "ed25519", "libc", "libsodium-sys", - "serde 1.0.149", + "serde 1.0.154", ] [[package]] @@ -5496,9 +5583,52 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stdweb" -version = "0.1.3" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version 0.2.3", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2 1.0.51", + "quote 1.0.23", + "serde 1.0.154", + "serde_derive", + "syn 1.0.109", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2 1.0.51", + "quote 1.0.23", + "serde 1.0.154", + "serde_derive", + "serde_json 1.0.94", + "sha1 0.6.1", + "syn 1.0.109", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" [[package]] name = "str-buf" @@ -5549,9 +5679,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -5560,11 +5690,11 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck 0.4.0", - "proc-macro2 1.0.47", - "quote 1.0.21", + "heck 0.4.1", + "proc-macro2 1.0.51", + "quote 1.0.23", "rustversion", - "syn 1.0.105", + "syn 1.0.109", ] [[package]] @@ -5580,12 +5710,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.105" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "unicode-ident", ] @@ -5595,30 +5725,30 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", "unicode-xid 0.2.4", ] [[package]] name = "sys-locale" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3358acbb4acd4146138b9bda219e904a6bb5aaaa237f8eed06f4d6bc1580ecee" +checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee" dependencies = [ "js-sys", "libc", "wasm-bindgen", "web-sys", - "winapi 0.3.9", + "windows-sys 0.45.0", ] [[package]] name = "sysinfo" -version = "0.24.7" +version = "0.26.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cb4ebf3d49308b99e6e9dc95e989e2fdbdc210e4f67c39db0bb89ba927001c" +checksum = "5c18a6156d1f27a9592ee18c1a846ca8dd5c258b7179fc193ae87c74ebb666f5" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys 0.8.3", @@ -5661,7 +5791,7 @@ dependencies = [ "strum 0.18.0", "strum_macros 0.18.0", "thiserror", - "toml", + "toml 0.5.11", "version-compare 0.0.10", ] @@ -5672,19 +5802,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" dependencies = [ "cfg-expr", - "heck 0.4.0", + "heck 0.4.1", "pkg-config", - "toml", + "toml 0.5.11", "version-compare 0.1.1", ] [[package]] name = "system_shutdown" -version = "3.0.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035e081d603551d8d78db27d2232913269c749ea67648c369100049820406a14" +checksum = "7567f71160af5e9abfb4f5a21532cf2174cefe91ac5c336419295685a695cc66" dependencies = [ - "winapi 0.3.9", + "windows 0.44.0", + "zbus", ] [[package]] @@ -5706,10 +5837,10 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gio", - "glib 0.16.5", + "glib 0.16.7", "glib-sys 0.16.3", "gtk", - "image 0.24.5", + "image", "instant", "jni 0.20.0", "lazy_static", @@ -5721,8 +5852,8 @@ dependencies = [ "objc", "once_cell", "parking_lot 0.12.1", - "png 0.17.7", - "raw-window-handle 0.5.0", + "png", + "raw-window-handle 0.5.1", "scopeguard", "tao-macros", "unicode-segmentation", @@ -5737,9 +5868,9 @@ name = "tao-macros" version = "0.1.0" source = "git+https://github.com/tauri-apps/tao?branch=muda#676bd90a80286b893d8850cc4e3813a0c4a27dcf" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -5761,23 +5892,22 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" dependencies = [ "cfg-if 1.0.0", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi 0.3.9", + "rustix", + "windows-sys 0.42.0", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -5809,8 +5939,9 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "tfc" version = "0.6.1" -source = "git+https://github.com/fufesou/The-Fat-Controller#a5f13e6ef80327eb8d860aeb26b0af93eb5aee2b" +source = "git+https://github.com/fufesou/The-Fat-Controller#102f2ec2cb2bbbd64413d20d28323e5e77e0fe71" dependencies = [ + "anyhow", "core-graphics 0.22.3", "unicode-segmentation", "winapi 0.3.9", @@ -5819,22 +5950,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -5846,17 +5977,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "tiff" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" -dependencies = [ - "jpeg-decoder 0.1.22", - "miniz_oxide 0.4.4", - "weezl", -] - [[package]] name = "tiff" version = "0.8.1" @@ -5864,7 +5984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" dependencies = [ "flate2", - "jpeg-decoder 0.3.0", + "jpeg-decoder", "weezl", ] @@ -5881,21 +6001,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.9" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ - "itoa 1.0.4", - "libc", - "num_threads", + "itoa 1.0.6", + "serde 1.0.154", + "time-core", "time-macros", ] [[package]] -name = "time-macros" -version = "0.2.4" +name = "time-core" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +dependencies = [ + "time-core", +] [[package]] name = "tinyvec" @@ -5908,28 +6037,28 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.23.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ "autocfg 1.1.0", "bytes", "libc", "memchr", - "mio 0.8.5", + "mio 0.8.6", "num_cpus", "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.4.7", + "socket2 0.4.9", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -5938,9 +6067,9 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -5972,9 +6101,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -5990,11 +6119,45 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "serde 1.0.149", + "serde 1.0.154", +] + +[[package]] +name = "toml" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6" +dependencies = [ + "serde 1.0.154", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde 1.0.154", +] + +[[package]] +name = "toml_edit" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +dependencies = [ + "indexmap", + "serde 1.0.154", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -6027,9 +6190,9 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -6053,9 +6216,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d62801a4da61bb100b8d3174a5a46fed7b6ea03cc2ae93ee7340793b09a94ce3" +checksum = "f87445e3a107818c17d87e8369db30a6fc25539bface8351efe2132b22e47dbc" dependencies = [ "cocoa", "core-graphics 0.22.3", @@ -6065,7 +6228,7 @@ dependencies = [ "muda", "objc", "once_cell", - "png 0.17.7", + "png", "thiserror", "windows-sys 0.45.0", ] @@ -6081,9 +6244,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" @@ -6091,12 +6254,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - [[package]] name = "uds_windows" version = "1.0.2" @@ -6108,16 +6265,25 @@ dependencies = [ ] [[package]] -name = "unicode-bidi" -version = "0.3.8" +name = "uname" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" [[package]] name = "unicode-ident" -version = "1.0.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -6130,9 +6296,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" @@ -6167,7 +6333,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde 1.0.149", + "serde 1.0.154", ] [[package]] @@ -6273,9 +6439,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -6283,24 +6449,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -6310,32 +6476,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ - "quote 1.0.21", + "quote 1.0.23", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wayland-client" @@ -6394,8 +6560,8 @@ version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro2 1.0.51", + "quote 1.0.23", "xml-rs", ] @@ -6412,9 +6578,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -6463,30 +6629,11 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" -[[package]] -name = "wepoll-ffi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] - [[package]] name = "which" -version = "3.1.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" -dependencies = [ - "failure", - "libc", -] - -[[package]] -name = "which" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -6495,11 +6642,10 @@ dependencies = [ [[package]] name = "whoami" -version = "1.2.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571" +checksum = "45dbc71f0cdca27dc261a9bd37ddec174e4a0af2b900b890f378460f745426e3" dependencies = [ - "bumpalo", "wasm-bindgen", "web-sys", ] @@ -6603,6 +6749,19 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] +[[package]] +name = "windows" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" +dependencies = [ + "windows_aarch64_msvc 0.37.0", + "windows_i686_gnu 0.37.0", + "windows_i686_msvc 0.37.0", + "windows_x86_64_gnu 0.37.0", + "windows_x86_64_msvc 0.37.0", +] + [[package]] name = "windows" version = "0.44.0" @@ -6620,9 +6779,9 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -6631,9 +6790,9 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f" dependencies = [ - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] [[package]] @@ -6661,19 +6820,6 @@ dependencies = [ "windows_x86_64_msvc 0.28.0", ] -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -6739,9 +6885,9 @@ checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" [[package]] name = "windows_aarch64_msvc" @@ -6769,9 +6915,9 @@ checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" [[package]] name = "windows_i686_gnu" @@ -6799,9 +6945,9 @@ checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" [[package]] name = "windows_i686_msvc" @@ -6829,9 +6975,9 @@ checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" [[package]] name = "windows_x86_64_gnu" @@ -6865,9 +7011,9 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" [[package]] name = "windows_x86_64_msvc" @@ -6891,9 +7037,9 @@ dependencies = [ "lazy_static", "libc", "log", - "mio 0.8.5", + "mio 0.8.6", "ndk 0.5.0", - "ndk-glue 0.5.2", + "ndk-glue", "ndk-sys 0.2.2", "objc", "parking_lot 0.11.2", @@ -6908,6 +7054,15 @@ dependencies = [ "x11-dl", ] +[[package]] +name = "winnow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.6.2" @@ -6932,17 +7087,14 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] name = "wol-rs" -version = "0.9.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f97e69b28b256ccfb02472c25057132e234aa8368fea3bb0268def564ce1f2" -dependencies = [ - "clap 3.2.23", -] +checksum = "48dc5e486e34a31515518d370cdd8bf59ec696323fe8f92b858e43942e84a765" [[package]] name = "ws2_32-sys" @@ -6974,9 +7126,9 @@ dependencies = [ [[package]] name = "x11" -version = "2.20.1" +version = "2.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2638d5b9c17ac40575fb54bb461a4b1d2a8d1b4ffcc4ff237d254ec59ddeb82" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" dependencies = [ "libc", "pkg-config", @@ -6995,14 +7147,24 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.9.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e99be55648b3ae2a52342f9a870c0e138709a3493261ce9b469afe6e4df6d8a" +checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" dependencies = [ "gethostname", - "nix 0.22.3", + "nix 0.24.3", "winapi 0.3.9", "winapi-wsapoll", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" +dependencies = [ + "nix 0.24.3", ] [[package]] @@ -7027,7 +7189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5af43ba661cee58bd86b9f81a899e45a15ac7f42fa4401340f73c0c2950030c1" dependencies = [ "derive_setters", - "serde 1.0.149", + "serde 1.0.154", ] [[package]] @@ -7041,13 +7203,13 @@ dependencies = [ [[package]] name = "zbus" -version = "3.6.2" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938ea6da98c75c2c37a86007bd17fd8e208cbec24e086108c87ece98e9edec0d" +checksum = "20aae5dd5b051971cd2f49f9f3b860e57b2b495ba5ba254eaec42d34ede57e97" dependencies = [ "async-broadcast", - "async-channel", "async-executor", + "async-fs", "async-io", "async-lock", "async-recursion", @@ -7062,13 +7224,13 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix 0.25.1", + "nix 0.26.2", "once_cell", "ordered-stream", "rand 0.8.5", - "serde 1.0.149", + "serde 1.0.154", "serde_repr", - "sha1", + "sha1 0.10.5", "static_assertions", "tracing", "uds_windows", @@ -7080,24 +7242,25 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.6.2" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45066039ebf3330820e495e854f8b312abb68f0a39e97972d092bd72e8bb3e8e" +checksum = "9264b3a1bcf5503d4e0348b6e7efe1da58d4f92a913c15ed9e63b52de85faaa1" dependencies = [ - "proc-macro-crate 1.2.1", - "proc-macro2 1.0.47", - "quote 1.0.21", + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.51", + "quote 1.0.23", "regex", - "syn 1.0.105", + "syn 1.0.109", + "zvariant_utils", ] [[package]] name = "zbus_names" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c737644108627748a660d038974160e0cbb62605536091bdfa28fd7f64d43c8" +checksum = "f34f314916bd89bdb9934154627fab152f4f28acdda03e7c4c68181b214fe7e3" dependencies = [ - "serde 1.0.149", + "serde 1.0.154", "static_assertions", "zvariant", ] @@ -7133,35 +7296,47 @@ dependencies = [ [[package]] name = "zune-inflate" -version = "0.2.42" +version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c473377c11c4a3ac6a2758f944cd336678e9c977aa0abf54f6450cf77e902d6d" +checksum = "a01728b79fb9b7e28a8c11f715e1cd8dc2cda7416a007d66cac55cebb3a8ac6b" dependencies = [ "simd-adler32", ] [[package]] name = "zvariant" -version = "3.9.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8c89c183461e11867ded456db252eae90874bc6769b7adbea464caa777e51" +checksum = "46fe4914a985446d6fd287019b5fceccce38303d71407d9e6e711d44954a05d8" dependencies = [ "byteorder", "enumflags2", "libc", - "serde 1.0.149", + "serde 1.0.154", "static_assertions", "zvariant_derive", ] [[package]] name = "zvariant_derive" -version = "3.9.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155247a5d1ab55e335421c104ccd95d64f17cebbd02f50cdbc1c33385f9c4d81" +checksum = "34c20260af4b28b3275d6676c7e2a6be0d4332e8e0aba4616d34007fd84e462a" dependencies = [ - "proc-macro-crate 1.2.1", - "proc-macro2 1.0.47", - "quote 1.0.21", - "syn 1.0.105", + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b" +dependencies = [ + "proc-macro2 1.0.51", + "quote 1.0.23", + "syn 1.0.109", ] diff --git a/Cargo.toml b/Cargo.toml index 47c2bb0e7..7ad979f8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,22 +45,22 @@ lazy_static = "1.4" sha2 = "0.10" repng = "0.2" parity-tokio-ipc = { git = "https://github.com/open-trade/parity-tokio-ipc" } -runas = "0.2" +runas = "1.0" magnum-opus = { git = "https://github.com/rustdesk/magnum-opus" } dasp = { version = "0.11", features = ["signal", "interpolate-linear", "interpolate"], optional = true } rubato = { version = "0.12", optional = true } samplerate = { version = "0.2", optional = true } async-trait = "0.1" uuid = { version = "1.0", features = ["v4"] } -clap = "3.0" +clap = "4.1" rpassword = "7.0" -base64 = "0.13" +base64 = "0.21" num_cpus = "1.13" bytes = { version = "1.2", features = ["serde"] } default-net = "0.12.0" -wol-rs = "0.9.1" +wol-rs = "1.0" flutter_rust_bridge = { version = "1.61.1", optional = true } -errno = "0.2.8" +errno = "0.3" rdev = { git = "https://github.com/fufesou/rdev" } url = { version = "2.1", features = ["serde"] } dlopen = "0.1" @@ -71,7 +71,7 @@ chrono = "0.4.23" cidr-utils = "0.5.9" [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] -cpal = "0.13.5" +cpal = "0.14" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] machine-uid = "0.2" @@ -81,9 +81,9 @@ sys-locale = "0.2" enigo = { path = "libs/enigo", features = [ "with_serde" ] } clipboard = { path = "libs/clipboard" } ctrlc = "3.2" -arboard = "2.0" +arboard = "3.2" #minreq = { version = "2.4", features = ["punycode", "https-native"] } -system_shutdown = "3.0.0" +system_shutdown = "4.0" [target.'cfg(target_os = "windows")'.dependencies] trayicon = { git = "https://github.com/open-trade/trayicon-rs", features = ["winit"] } @@ -148,6 +148,7 @@ cc = "1.0" hbb_common = { path = "libs/hbb_common" } simple_rc = { path = "libs/simple_rc", optional = true } flutter_rust_bridge_codegen = "1.61.1" +os-version = "0.2" [dev-dependencies] hound = "3.5" diff --git a/build.rs b/build.rs index d15f27424..bf141e539 100644 --- a/build.rs +++ b/build.rs @@ -9,7 +9,14 @@ fn build_windows() { #[cfg(target_os = "macos")] fn build_mac() { let file = "src/platform/macos.mm"; - cc::Build::new().file(file).compile("macos"); + let mut b = cc::Build::new(); + if let Ok(os_version::OsVersion::MacOS(v)) = os_version::detect() { + let v = v.version; + if v.contains("10.14") { + b.flag("-DNO_InputMonitoringAuthStatus=1"); + } + } + b.file(file).compile("macos"); println!("cargo:rerun-if-changed={}", file); } diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index c77f11eb5..fd02639ed 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -7,17 +7,17 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -flexi_logger = { version = "0.22", features = ["async", "use_chrono_for_offset"] } +flexi_logger = { version = "0.25", features = ["async"] } protobuf = { version = "3.1", features = ["with-bytes"] } tokio = { version = "1.20", features = ["full"] } tokio-util = { version = "0.7", features = ["full"] } futures = "0.3" bytes = { version = "1.2", features = ["serde"] } log = "0.4" -env_logger = "0.9" +env_logger = "0.10" socket2 = { version = "0.3", features = ["reuseport"] } zstd = "0.9" -quinn = {version = "0.8", optional = true } +quinn = {version = "0.9", optional = true } anyhow = "1.0" futures-util = "0.3" directories-next = "2.0" @@ -34,7 +34,7 @@ tokio-socks = { git = "https://github.com/open-trade/tokio-socks" } chrono = "0.4" backtrace = "0.3" libc = "0.2" -sysinfo = "0.24" +sysinfo = "0.26" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] mac_address = "1.1" @@ -54,5 +54,5 @@ winapi = { version = "0.3", features = ["winuser"] } osascript = "0.3.0" [dev-dependencies] -toml = "0.5" -serde_json = "1.0" +toml = "0.7" +serde_json = "1.0" \ No newline at end of file diff --git a/libs/scrap/Cargo.toml b/libs/scrap/Cargo.toml index 82cb88faf..99e15aeef 100644 --- a/libs/scrap/Cargo.toml +++ b/libs/scrap/Cargo.toml @@ -42,7 +42,7 @@ quest = "0.3" [build-dependencies] target_build_utils = "0.3" -bindgen = "0.59" +bindgen = "0.64" [target.'cfg(target_os = "linux")'.dependencies] dbus = { version = "0.9", optional = true } diff --git a/libs/scrap/src/common/quartz.rs b/libs/scrap/src/common/quartz.rs index a02d55ebb..dc59edac1 100644 --- a/libs/scrap/src/common/quartz.rs +++ b/libs/scrap/src/common/quartz.rs @@ -83,7 +83,7 @@ impl crate::TraitCapturer for Capturer { } } -pub struct Frame<'a>(quartz::Frame, PhantomData<&'a [u8]>); +pub struct Frame<'a>(pub quartz::Frame, PhantomData<&'a [u8]>); impl<'a> ops::Deref for Frame<'a> { type Target = [u8]; diff --git a/src/client.rs b/src/client.rs index ab27c0185..9202a34ba 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2233,7 +2233,7 @@ fn get_pk(pk: &[u8]) -> Option<[u8; 32]> { #[inline] fn get_rs_pk(str_base64: &str) -> Option { - if let Ok(pk) = base64::decode(str_base64) { + if let Ok(pk) = crate::decode64(str_base64) { get_pk(&pk).map(|x| sign::PublicKey(x)) } else { None diff --git a/src/common.rs b/src/common.rs index 28d8dddc4..0b475805d 100644 --- a/src/common.rs +++ b/src/common.rs @@ -787,3 +787,15 @@ pub fn handle_url_scheme(url: String) { let _ = crate::run_me(vec![url]); } } + +#[inline] +pub fn encode64>(input: T) -> String { + #[allow(deprecated)] + base64::encode(input) +} + +#[inline] +pub fn decode64>(input: T) -> Result, base64::DecodeError> { + #[allow(deprecated)] + base64::decode(input) +} diff --git a/src/license.rs b/src/license.rs index f8f5d27d5..8875d2b64 100644 --- a/src/license.rs +++ b/src/license.rs @@ -1,3 +1,4 @@ +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; use hbb_common::{bail, sodiumoxide::crypto::sign, ResultType}; use serde_derive::{Deserialize, Serialize}; @@ -18,7 +19,7 @@ fn get_license_from_string_(s: &str) -> ResultType { 12, 46, 129, 83, 17, 84, 193, 119, 197, 130, 103, ]; let pk = sign::PublicKey(*PK); - let data = base64::decode_config(tmp, base64::URL_SAFE_NO_PAD)?; + let data = URL_SAFE_NO_PAD.decode(tmp)?; if let Ok(lic) = serde_json::from_slice::(&data) { return Ok(lic); } diff --git a/src/naming.rs b/src/naming.rs index 38b514f86..53e675d92 100644 --- a/src/naming.rs +++ b/src/naming.rs @@ -1,10 +1,11 @@ mod license; +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; use hbb_common::ResultType; use license::*; fn gen_name(lic: &License) -> ResultType { let tmp = serde_json::to_vec::(lic)?; - let tmp = base64::encode_config(tmp, base64::URL_SAFE_NO_PAD); + let tmp = URL_SAFE_NO_PAD.encode(&tmp); let tmp: String = tmp.chars().rev().collect(); Ok(tmp) } diff --git a/src/platform/macos.mm b/src/platform/macos.mm index 3c90981c4..a252a9a8f 100644 --- a/src/platform/macos.mm +++ b/src/platform/macos.mm @@ -8,6 +8,9 @@ // https://github.com/codebytere/node-mac-permissions/blob/main/permissions.mm extern "C" bool InputMonitoringAuthStatus(bool prompt) { + #ifdef NO_InputMonitoringAuthStatus + return true; + #else if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_15) { IOHIDAccessType theType = IOHIDCheckAccess(kIOHIDRequestTypeListenEvent); NSLog(@"IOHIDCheckAccess = %d, kIOHIDAccessTypeGranted = %d", theType, kIOHIDAccessTypeGranted); @@ -36,6 +39,7 @@ extern "C" bool InputMonitoringAuthStatus(bool prompt) { return true; } return false; + #endif } extern "C" bool MacCheckAdminAuthorization() { diff --git a/src/server/connection.rs b/src/server/connection.rs index 898939b62..3d419ef08 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -735,7 +735,7 @@ impl Connection { let url = self.server_audit_conn.clone(); let mut v = v; v["id"] = json!(Config::get_id()); - v["uuid"] = json!(base64::encode(hbb_common::get_uuid())); + v["uuid"] = json!(crate::encode64(hbb_common::get_uuid())); v["conn_id"] = json!(self.inner.id); tokio::spawn(async move { allow_err!(Self::post_audit_async(url, v).await); @@ -765,7 +765,7 @@ impl Connection { info["files"] = json!(files); let v = json!({ "id":json!(Config::get_id()), - "uuid":json!(base64::encode(hbb_common::get_uuid())), + "uuid":json!(crate::encode64(hbb_common::get_uuid())), "peer_id":json!(self.lr.my_id), "type": r#type as i8, "path":path, @@ -788,7 +788,7 @@ impl Connection { } let mut v = Value::default(); v["id"] = json!(Config::get_id()); - v["uuid"] = json!(base64::encode(hbb_common::get_uuid())); + v["uuid"] = json!(crate::encode64(hbb_common::get_uuid())); v["typ"] = json!(typ as i8); v["from_remote"] = json!(from_remote); v["info"] = serde_json::Value::String(info.to_string()); diff --git a/src/ui_interface.rs b/src/ui_interface.rs index ec570b42c..fd97f41b0 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -605,7 +605,7 @@ pub fn remove_discovered(id: String) { #[inline] pub fn get_uuid() -> String { - base64::encode(hbb_common::get_uuid()) + crate::encode64(hbb_common::get_uuid()) } #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] @@ -876,7 +876,7 @@ pub async fn change_id_shared(id: String, old_id: String) -> &'static str { #[cfg(not(any(target_os = "android", target_os = "ios")))] let uuid = machine_uid::get().unwrap_or("".to_owned()); #[cfg(any(target_os = "android", target_os = "ios"))] - let uuid = base64::encode(hbb_common::get_uuid()); + let uuid = crate::encode64(hbb_common::get_uuid()); if uuid.is_empty() { log::error!("Failed to change id, uuid is_empty"); From f5d0275bf365757256abf725f63f3ccb4f425553 Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 9 Mar 2023 19:55:38 +0900 Subject: [PATCH 14/27] selectedItems use obs state --- .../lib/desktop/pages/file_manager_page.dart | 182 +++++++++--------- flutter/lib/models/file_model.dart | 26 +-- 2 files changed, 101 insertions(+), 107 deletions(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index da88405a9..44def46c2 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -15,7 +15,6 @@ import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/models/file_model.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:provider/provider.dart'; import 'package:wakelock/wakelock.dart'; import '../../consts.dart'; @@ -701,87 +700,93 @@ class _FileManagerViewState extends State { color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, ), - MenuButton( - onPressed: selectedItems.valid() - ? () async { - await (controller.removeAction(selectedItems)); - selectedItems.clear(); - } - : null, - child: SvgPicture.asset( - "assets/trash.svg", - color: Theme.of(context).tabBarTheme.labelColor, - ), - color: Theme.of(context).cardColor, - hoverColor: Theme.of(context).hoverColor, - ), + Obx(() => MenuButton( + onPressed: SelectedItems.valid(selectedItems.items) + ? () async { + await (controller + .removeAction(selectedItems)); + selectedItems.clear(); + } + : null, + child: SvgPicture.asset( + "assets/trash.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + )), menu(isLocal: isLocal), ], ), ), - ElevatedButton.icon( - style: ButtonStyle( - padding: MaterialStateProperty.all(isLocal - ? EdgeInsets.only(left: 10) - : EdgeInsets.only(right: 10)), - backgroundColor: MaterialStateProperty.all( - selectedItems.length == 0 - ? MyTheme.accent80 - : MyTheme.accent, - ), - ), - onPressed: selectedItems.valid() - ? () { - final otherSideData = - controller.getOtherSideDirectoryData(); - controller.sendFiles(selectedItems, otherSideData); - selectedItems.clear(); - } - : null, - icon: isLocal - ? Text( - translate('Send'), - textAlign: TextAlign.right, - style: TextStyle( - color: selectedItems.length == 0 - ? Theme.of(context).brightness == Brightness.light - ? MyTheme.grayBg - : MyTheme.darkGray - : Colors.white, - ), - ) - : RotatedBox( - quarterTurns: 2, - child: SvgPicture.asset( - "assets/arrow.svg", - color: selectedItems.length == 0 - ? Theme.of(context).brightness == Brightness.light - ? MyTheme.grayBg - : MyTheme.darkGray - : Colors.white, - alignment: Alignment.bottomRight, - ), + Obx(() => ElevatedButton.icon( + style: ButtonStyle( + padding: MaterialStateProperty.all( + isLocal + ? EdgeInsets.only(left: 10) + : EdgeInsets.only(right: 10)), + backgroundColor: MaterialStateProperty.all( + selectedItems.items.isEmpty + ? MyTheme.accent80 + : MyTheme.accent, ), - label: isLocal - ? SvgPicture.asset( - "assets/arrow.svg", - color: selectedItems.length == 0 - ? Theme.of(context).brightness == Brightness.light - ? MyTheme.grayBg - : MyTheme.darkGray - : Colors.white, - ) - : Text( - translate('Receive'), - style: TextStyle( - color: selectedItems.length == 0 - ? Theme.of(context).brightness == Brightness.light - ? MyTheme.grayBg - : MyTheme.darkGray - : Colors.white, - ), - ), - ), + ), + onPressed: SelectedItems.valid(selectedItems.items) + ? () { + final otherSideData = + controller.getOtherSideDirectoryData(); + controller.sendFiles(selectedItems, otherSideData); + selectedItems.clear(); + } + : null, + icon: isLocal + ? Text( + translate('Send'), + textAlign: TextAlign.right, + style: TextStyle( + color: selectedItems.items.isEmpty + ? Theme.of(context).brightness == + Brightness.light + ? MyTheme.grayBg + : MyTheme.darkGray + : Colors.white, + ), + ) + : RotatedBox( + quarterTurns: 2, + child: SvgPicture.asset( + "assets/arrow.svg", + color: selectedItems.items.isEmpty + ? Theme.of(context).brightness == + Brightness.light + ? MyTheme.grayBg + : MyTheme.darkGray + : Colors.white, + alignment: Alignment.bottomRight, + ), + ), + label: isLocal + ? SvgPicture.asset( + "assets/arrow.svg", + color: selectedItems.items.isEmpty + ? Theme.of(context).brightness == + Brightness.light + ? MyTheme.grayBg + : MyTheme.darkGray + : Colors.white, + ) + : Text( + translate('Receive'), + style: TextStyle( + color: selectedItems.items.isEmpty + ? Theme.of(context).brightness == + Brightness.light + ? MyTheme.grayBg + : MyTheme.darkGray + : Colors.white, + ), + ), + )), ], ).marginOnly(top: 8.0) ], @@ -814,7 +819,7 @@ class _FileManagerViewState extends State { MenuEntryButton( childBuilder: (style) => Text(translate("Unselect All"), style: style), - proc: () => setState(() => selectedItems.clear()), + proc: () => selectedItems.clear(), padding: kDesktopMenuPadding, dismissOnClicked: true) ]; @@ -864,7 +869,7 @@ class _FileManagerViewState extends State { onNext: (buffer) { debugPrint("searching next for $buffer"); assert(buffer.length == 1); - assert(selectedItems.length <= 1); + assert(selectedItems.items.length <= 1); var skipCount = 0; if (selectedItems.items.isNotEmpty) { final index = entries.indexOf(selectedItems.items.first); @@ -883,7 +888,7 @@ class _FileManagerViewState extends State { (element) => element.name.toLowerCase().startsWith(buffer)); } if (searchResult.isEmpty) { - setState(() => selectedItems.clear()); + selectedItems.clear(); return; } _jumpToEntry(isLocal, searchResult.first, scrollController, @@ -896,7 +901,7 @@ class _FileManagerViewState extends State { .where((element) => element.name.toLowerCase().startsWith(buffer)); selectedEntries.clear(); if (searchResult.isEmpty) { - setState(() => selectedItems.clear()); + selectedItems.clear(); return; } _jumpToEntry(isLocal, searchResult.first, scrollController, @@ -915,12 +920,11 @@ class _FileManagerViewState extends State { final lastModifiedStr = entry.isDrive ? " " : "${entry.lastModified().toString().replaceAll(".000", "")} "; - final isSelected = selectedItems.contains(entry); return Padding( padding: EdgeInsets.symmetric(vertical: 1), - child: Container( + child: Obx(() => Container( decoration: BoxDecoration( - color: isSelected + color: selectedItems.items.contains(entry) ? Theme.of(context).hoverColor : Theme.of(context).cardColor, borderRadius: BorderRadius.all( @@ -1025,7 +1029,7 @@ class _FileManagerViewState extends State { ), ), ], - )), + ))), ); }).toList(growable: false); @@ -1077,10 +1081,8 @@ class _FileManagerViewState extends State { entries.indexOf(searchResult.first) * rowHeight), scrollController.position.maxScrollExtent); scrollController.jumpTo(offset); - setState(() { - selectedEntries.add(searchResult.first); - debugPrint("focused on ${searchResult.first.name}"); - }); + selectedEntries.add(searchResult.first); + debugPrint("focused on ${searchResult.first.name}"); } void _onSelectedChanged(SelectedItems selectedItems, List entries, @@ -1090,7 +1092,7 @@ class _FileManagerViewState extends State { final isShiftDown = RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftLeft); if (isCtrlDown) { - if (selectedItems.contains(entry)) { + if (selectedItems.items.contains(entry)) { selectedItems.remove(entry); } else { selectedItems.add(entry); diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 30a897a56..4cfe19135 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -1052,40 +1052,32 @@ class DirectoryOptions { class SelectedItems { final bool isLocal; - final List _items = []; - - List get items => _items; - - int get length => _items.length; + final items = RxList.empty(growable: true); SelectedItems({required this.isLocal}); void add(Entry e) { if (e.isDrive) return; - if (!_items.contains(e)) { - _items.add(e); + if (!items.contains(e)) { + items.add(e); } } - bool contains(Entry e) { - return _items.contains(e); - } - void remove(Entry e) { - _items.remove(e); + items.remove(e); } void clear() { - _items.clear(); + items.clear(); } void selectAll(List entries) { - _items.clear(); - _items.addAll(entries); + items.clear(); + items.addAll(entries); } - bool valid() { - if (length > 0) { + static bool valid(RxList items) { + if (items.isNotEmpty) { // exclude DirDrive type return items.any((item) => !item.isDrive); } From d19d4aacdbba337561e920f5fd6fdab8da909446 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 9 Mar 2023 19:38:47 +0800 Subject: [PATCH 15/27] avoid invalid texture width and height Signed-off-by: fufesou --- flutter/lib/models/model.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 802a18a52..a8ed56bc5 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -371,7 +371,11 @@ class FfiModel with ChangeNotifier { _updateSessionWidthHeight(String id) { parent.target?.canvasModel.updateViewStyle(); - bind.sessionSetSize(id: id, width: display.width, height: display.height); + if (display.width < 0 || display.height < 0) { + debugPrintStack(label: 'invalid display size (${display.width},${display.height})'); + } else { + bind.sessionSetSize(id: id, width: display.width, height: display.height); + } } /// Handle the peer info event based on [evt]. From 00b1439f32a0fcb9d4488ae0aad1619e54534bde Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 9 Mar 2023 21:09:17 +0900 Subject: [PATCH 16/27] refactor mobile file manager page --- .../lib/mobile/pages/file_manager_page.dart | 881 ++++++++++-------- 1 file changed, 481 insertions(+), 400 deletions(-) diff --git a/flutter/lib/mobile/pages/file_manager_page.dart b/flutter/lib/mobile/pages/file_manager_page.dart index 26e024ca4..aa8794227 100644 --- a/flutter/lib/mobile/pages/file_manager_page.dart +++ b/flutter/lib/mobile/pages/file_manager_page.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_breadcrumb/flutter_breadcrumb.dart'; import 'package:flutter_hbb/models/file_model.dart'; -import 'package:provider/provider.dart'; +import 'package:get/get.dart'; import 'package:toggle_switch/toggle_switch.dart'; import 'package:wakelock/wakelock.dart'; @@ -20,8 +20,13 @@ class FileManagerPage extends StatefulWidget { class _FileManagerPageState extends State { final model = gFFI.fileModel; - final _selectedItems = SelectedItems(); - final _breadCrumbScroller = ScrollController(); + var showLocal = true; + var isSelecting = false.obs; + + FileController get currentFileController => + showLocal ? model.localController : model.remoteController; + FileDirectory get currentDir => currentFileController.directory.value; + DirectoryOptions get currentOptions => currentFileController.options.value; @override void initState() { @@ -32,7 +37,6 @@ class _FileManagerPageState extends State { .showLoading(translate('Connecting...'), onCancel: closeConnection); }); gFFI.ffiModel.updateEventListener(widget.id); - model.onDirChanged = (_) => breadCrumbScrollToEnd(); Wakelock.enable(); } @@ -47,288 +51,447 @@ class _FileManagerPageState extends State { } @override - Widget build(BuildContext context) => ChangeNotifierProvider.value( - value: model, - child: Consumer(builder: (_context, _model, _child) { - return WillPopScope( - onWillPop: () async { - if (model.selectMode) { - model.toggleSelectMode(); - } else { - model.goBack(); + Widget build(BuildContext context) => WillPopScope( + onWillPop: () async { + if (isSelecting.value) { + isSelecting.value = false; + } else { + currentFileController.goBack(); + } + return false; + }, + child: Scaffold( + // backgroundColor: MyTheme.grayBg, + appBar: AppBar( + leading: Row(children: [ + IconButton( + icon: Icon(Icons.close), + onPressed: () => clientClose(widget.id, gFFI.dialogManager)), + ]), + centerTitle: true, + title: ToggleSwitch( + initialLabelIndex: showLocal ? 0 : 1, + activeBgColor: [MyTheme.idColor], + inactiveBgColor: Theme.of(context).brightness == Brightness.light + ? MyTheme.grayBg + : null, + inactiveFgColor: Theme.of(context).brightness == Brightness.light + ? Colors.black54 + : null, + totalSwitches: 2, + minWidth: 100, + fontSize: 15, + iconSize: 18, + labels: [translate("Local"), translate("Remote")], + icons: [Icons.phone_android_sharp, Icons.screen_share], + onToggle: (index) { + final current = showLocal ? 0 : 1; + if (index != current) { + setState(() => showLocal = !showLocal); } - return false; }, - child: Scaffold( - // backgroundColor: MyTheme.grayBg, - appBar: AppBar( - leading: Row(children: [ - IconButton( - icon: Icon(Icons.close), - onPressed: () => - clientClose(widget.id, gFFI.dialogManager)), - ]), - centerTitle: true, - title: ToggleSwitch( - initialLabelIndex: model.isLocal ? 0 : 1, - activeBgColor: [MyTheme.idColor], - inactiveBgColor: - Theme.of(context).brightness == Brightness.light - ? MyTheme.grayBg - : null, - inactiveFgColor: - Theme.of(context).brightness == Brightness.light - ? Colors.black54 - : null, - totalSwitches: 2, - minWidth: 100, - fontSize: 15, - iconSize: 18, - labels: [translate("Local"), translate("Remote")], - icons: [Icons.phone_android_sharp, Icons.screen_share], - onToggle: (index) { - final current = model.isLocal ? 0 : 1; - if (index != current) { - model.togglePage(); - } - }, - ), - actions: [ - PopupMenuButton( - icon: Icon(Icons.more_vert), - itemBuilder: (context) { - return [ - PopupMenuItem( - child: Row( - children: [ - Icon(Icons.refresh, - color: Theme.of(context).iconTheme.color), - SizedBox(width: 5), - Text(translate("Refresh File")) - ], - ), - value: "refresh", - ), - PopupMenuItem( - enabled: model.currentDir.path != "/", - child: Row( - children: [ - Icon(Icons.check, - color: Theme.of(context).iconTheme.color), - SizedBox(width: 5), - Text(translate("Multi Select")) - ], - ), - value: "select", - ), - PopupMenuItem( - enabled: model.currentDir.path != "/", - child: Row( - children: [ - Icon(Icons.folder_outlined, - color: Theme.of(context).iconTheme.color), - SizedBox(width: 5), - Text(translate("Create Folder")) - ], - ), - value: "folder", - ), - PopupMenuItem( - enabled: model.currentDir.path != "/", - child: Row( - children: [ - Icon( - model.getCurrentShowHidden() - ? Icons.check_box_outlined - : Icons.check_box_outline_blank, - color: Theme.of(context).iconTheme.color), - SizedBox(width: 5), - Text(translate("Show Hidden Files")) - ], - ), - value: "hidden", - ) - ]; - }, - onSelected: (v) { - if (v == "refresh") { - model.refresh(); - } else if (v == "select") { - _selectedItems.clear(); - model.toggleSelectMode(); - } else if (v == "folder") { - final name = TextEditingController(); - gFFI.dialogManager - .show((setState, close) => CustomAlertDialog( - title: Text(translate("Create Folder")), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextFormField( - decoration: InputDecoration( - labelText: translate( - "Please enter the folder name"), - ), - controller: name, - ), - ], + ), + actions: [ + PopupMenuButton( + icon: Icon(Icons.more_vert), + itemBuilder: (context) { + return [ + PopupMenuItem( + child: Row( + children: [ + Icon(Icons.refresh, + color: Theme.of(context).iconTheme.color), + SizedBox(width: 5), + Text(translate("Refresh File")) + ], + ), + value: "refresh", + ), + PopupMenuItem( + enabled: currentDir.path != "/", + child: Row( + children: [ + Icon(Icons.check, + color: Theme.of(context).iconTheme.color), + SizedBox(width: 5), + Text(translate("Multi Select")) + ], + ), + value: "select", + ), + PopupMenuItem( + enabled: currentDir.path != "/", + child: Row( + children: [ + Icon(Icons.folder_outlined, + color: Theme.of(context).iconTheme.color), + SizedBox(width: 5), + Text(translate("Create Folder")) + ], + ), + value: "folder", + ), + PopupMenuItem( + enabled: currentDir.path != "/", + child: Row( + children: [ + Icon( + currentOptions.showHidden + ? Icons.check_box_outlined + : Icons.check_box_outline_blank, + color: Theme.of(context).iconTheme.color), + SizedBox(width: 5), + Text(translate("Show Hidden Files")) + ], + ), + value: "hidden", + ) + ]; + }, + onSelected: (v) { + if (v == "refresh") { + currentFileController.refresh(); + } else if (v == "select") { + model.localController.selectedItems.clear(); + model.remoteController.selectedItems.clear(); + isSelecting.toggle(); + } else if (v == "folder") { + final name = TextEditingController(); + gFFI.dialogManager + .show((setState, close) => CustomAlertDialog( + title: Text(translate("Create Folder")), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + decoration: InputDecoration( + labelText: translate( + "Please enter the folder name"), ), - actions: [ - dialogButton("Cancel", - onPressed: () => close(false), - isOutline: true), - dialogButton("OK", onPressed: () { - if (name.value.text.isNotEmpty) { - model.createDir(PathUtil.join( - model.currentDir.path, - name.value.text, - model.getCurrentIsWindows())); - close(); - } - }) - ])); - } else if (v == "hidden") { - model.toggleShowHidden(); - } - }), - ], - ), - body: body(), - bottomSheet: bottomSheet(), - )); - })); + controller: name, + ), + ], + ), + actions: [ + dialogButton("Cancel", + onPressed: () => close(false), + isOutline: true), + dialogButton("OK", onPressed: () { + if (name.value.text.isNotEmpty) { + currentFileController.createDir( + PathUtil.join( + currentDir.path, + name.value.text, + currentOptions.isWindows)); + close(); + } + }) + ])); + } else if (v == "hidden") { + currentFileController.toggleShowHidden(); + } + }), + ], + ), + body: showLocal + ? FileManagerView( + controller: model.localController, + isSelecting: isSelecting, + showCheckBox: showCheckBox()) + : FileManagerView( + controller: model.remoteController, + isSelecting: isSelecting, + showCheckBox: showCheckBox()), + bottomSheet: bottomSheet(), + )); bool showCheckBox() { - if (!model.selectMode) { - return false; + final selectedItems = getActiveSelectedItems(); + + if (selectedItems != null) { + return selectedItems.isLocal == showLocal; } - return !_selectedItems.isOtherPage(model.isLocal); + return false; } - Widget body() { - final isLocal = model.isLocal; - final fd = model.currentDir; - final entries = fd.entries; + Widget? bottomSheet() { + return Obx(() { + final selectedItems = getActiveSelectedItems(); + + final localLabel = selectedItems?.isLocal == null + ? "" + : " [${selectedItems!.isLocal ? translate("Local") : translate("Remote")}]"; + + if (isSelecting.value) { + final selectedItemsLen = + "${selectedItems?.items.length ?? 0} ${translate("items")}"; + if (selectedItems == null || + selectedItems.items.isEmpty || + showCheckBox()) { + return BottomSheetBody( + leading: Icon(Icons.check), + title: translate("Selected"), + text: selectedItemsLen + localLabel, + onCanceled: () => isSelecting.toggle(), + actions: [ + IconButton( + icon: Icon(Icons.compare_arrows), + onPressed: () => setState(() => showLocal = !showLocal), + ), + IconButton( + icon: Icon(Icons.delete_forever), + onPressed: selectedItems != null + ? () { + if (selectedItems.items.isNotEmpty) { + currentFileController.removeAction(selectedItems); + } + } + : null, + ) + ]); + } else { + return BottomSheetBody( + leading: Icon(Icons.input), + title: translate("Paste here?"), + text: selectedItemsLen + localLabel, + onCanceled: () => isSelecting.toggle(), + actions: [ + IconButton( + icon: Icon(Icons.compare_arrows), + onPressed: () => setState(() => showLocal = !showLocal), + ), + IconButton( + icon: Icon(Icons.paste), + onPressed: () { + isSelecting.toggle(); + final otherSide = showLocal + ? model.remoteController + : model.localController; + final otherSideData = DirectoryData( + otherSide.directory.value, otherSide.options.value); + currentFileController.sendFiles( + selectedItems, otherSideData); + }, + ) + ]); + } + } + + final jobTable = model.jobController.jobTable; + + if (jobTable.isEmpty) { + return Offstage(); + } + + switch (jobTable.last.state) { + case JobState.inProgress: + return Obx(() => BottomSheetBody( + leading: CircularProgressIndicator(), + title: translate("Waiting"), + text: + "${translate("Speed")}: ${readableFileSize(jobTable.last.speed)}/s", + onCanceled: () => + model.jobController.cancelJob(jobTable.last.id), + )); + case JobState.done: + return Obx(() => BottomSheetBody( + leading: Icon(Icons.check), + title: "${translate("Successful")}!", + text: jobTable.last.display(), + onCanceled: () => jobTable.clear(), + )); + case JobState.error: + return Obx(() => BottomSheetBody( + leading: Icon(Icons.error), + title: "${translate("Error")}!", + text: "", + onCanceled: () => jobTable.clear(), + )); + case JobState.none: + break; + case JobState.paused: + // TODO: Handle this case. + break; + } + return Offstage(); + }); + } + + SelectedItems? getActiveSelectedItems() { + final localSelectedItems = model.localController.selectedItems; + final remoteSelectedItems = model.remoteController.selectedItems; + + if (localSelectedItems.items.isNotEmpty && + remoteSelectedItems.items.isNotEmpty) { + // assert unreachable + debugPrint("Wrong SelectedItems state, reset"); + localSelectedItems.clear(); + remoteSelectedItems.clear(); + } + + if (localSelectedItems.items.isEmpty && remoteSelectedItems.items.isEmpty) { + return null; + } + + if (localSelectedItems.items.length > remoteSelectedItems.items.length) { + return localSelectedItems; + } else { + return remoteSelectedItems; + } + } +} + +class FileManagerView extends StatefulWidget { + final FileController controller; + final RxBool isSelecting; + final bool showCheckBox; + + FileManagerView( + {required this.controller, + required this.isSelecting, + required this.showCheckBox}); + + @override + State createState() => _FileManagerViewState(); +} + +class _FileManagerViewState extends State { + final _listScrollController = ScrollController(); + final _breadCrumbScroller = ScrollController(); + + bool get isLocal => widget.controller.isLocal; + FileController get controller => widget.controller; + SelectedItems get _selectedItems => widget.controller.selectedItems; + + @override + void initState() { + super.initState(); + controller.directory.listen((e) => breadCrumbScrollToEnd()); + } + + @override + Widget build(BuildContext context) { return Column(children: [ headTools(), - Expanded( - child: ListView.builder( - controller: ScrollController(), - itemCount: entries.length + 1, - itemBuilder: (context, index) { - if (index >= entries.length) { - return listTail(); - } - var selected = false; - if (model.selectMode) { - selected = _selectedItems.contains(entries[index]); - } + Expanded(child: Obx(() { + final entries = controller.directory.value.entries; + return ListView.builder( + controller: _listScrollController, + itemCount: entries.length + 1, + itemBuilder: (context, index) { + if (index >= entries.length) { + return listTail(); + } + var selected = false; + if (widget.isSelecting.value) { + selected = _selectedItems.items.contains(entries[index]); + } - final sizeStr = entries[index].isFile - ? readableFileSize(entries[index].size.toDouble()) - : ""; - return Card( - child: ListTile( - leading: entries[index].isDrive - ? Padding( - padding: EdgeInsets.symmetric(vertical: 8), - child: Image( - image: iconHardDrive, - fit: BoxFit.scaleDown, - color: Theme.of(context) - .iconTheme - .color - ?.withOpacity(0.7))) - : Icon( - entries[index].isFile - ? Icons.feed_outlined - : Icons.folder, - size: 40), - title: Text(entries[index].name), - selected: selected, - subtitle: entries[index].isDrive - ? null - : Text( - "${entries[index].lastModified().toString().replaceAll(".000", "")} $sizeStr", - style: TextStyle(fontSize: 12, color: MyTheme.darkGray), - ), - trailing: entries[index].isDrive - ? null - : showCheckBox() - ? Checkbox( - value: selected, - onChanged: (v) { - if (v == null) return; - if (v && !selected) { - _selectedItems.add(isLocal, entries[index]); - } else if (!v && selected) { - _selectedItems.remove(entries[index]); - } - setState(() {}); - }) - : PopupMenuButton( - icon: Icon(Icons.more_vert), - itemBuilder: (context) { - return [ - PopupMenuItem( - child: Text(translate("Delete")), - value: "delete", - ), - PopupMenuItem( - child: Text(translate("Multi Select")), - value: "multi_select", - ), - PopupMenuItem( - child: Text(translate("Properties")), - value: "properties", - enabled: false, - ) - ]; - }, - onSelected: (v) { - if (v == "delete") { - final items = SelectedItems(); - items.add(isLocal, entries[index]); - model.removeAction(items); - } else if (v == "multi_select") { - _selectedItems.clear(); - model.toggleSelectMode(); - } - }), - onTap: () { - if (model.selectMode && !_selectedItems.isOtherPage(isLocal)) { - if (selected) { - _selectedItems.remove(entries[index]); - } else { - _selectedItems.add(isLocal, entries[index]); + final sizeStr = entries[index].isFile + ? readableFileSize(entries[index].size.toDouble()) + : ""; + return Card( + child: ListTile( + leading: entries[index].isDrive + ? Padding( + padding: EdgeInsets.symmetric(vertical: 8), + child: Image( + image: iconHardDrive, + fit: BoxFit.scaleDown, + color: Theme.of(context) + .iconTheme + .color + ?.withOpacity(0.7))) + : Icon( + entries[index].isFile + ? Icons.feed_outlined + : Icons.folder, + size: 40), + title: Text(entries[index].name), + selected: selected, + subtitle: entries[index].isDrive + ? null + : Text( + "${entries[index].lastModified().toString().replaceAll(".000", "")} $sizeStr", + style: TextStyle(fontSize: 12, color: MyTheme.darkGray), + ), + trailing: entries[index].isDrive + ? null + : widget.isSelecting.value && widget.showCheckBox + ? Checkbox( + value: selected, + onChanged: (v) { + if (v == null) return; + if (v && !selected) { + _selectedItems.add(entries[index]); + } else if (!v && selected) { + _selectedItems.remove(entries[index]); + } + setState(() {}); + }) + : PopupMenuButton( + icon: Icon(Icons.more_vert), + itemBuilder: (context) { + return [ + PopupMenuItem( + child: Text(translate("Delete")), + value: "delete", + ), + PopupMenuItem( + child: Text(translate("Multi Select")), + value: "multi_select", + ), + PopupMenuItem( + child: Text(translate("Properties")), + value: "properties", + enabled: false, + ) + ]; + }, + onSelected: (v) { + if (v == "delete") { + final items = SelectedItems(isLocal: isLocal); + items.add(entries[index]); + controller.removeAction(items); + } else if (v == "multi_select") { + _selectedItems.clear(); + widget.isSelecting.toggle(); + } + }), + onTap: () { + if (widget.isSelecting.value && widget.showCheckBox) { + if (selected) { + _selectedItems.remove(entries[index]); + } else { + _selectedItems.add(entries[index]); + } + return; } - setState(() {}); - return; - } - if (entries[index].isDirectory || entries[index].isDrive) { - model.openDirectory(entries[index].path); - } else { - // Perform file-related tasks. - } - }, - onLongPress: entries[index].isDrive - ? null - : () { - _selectedItems.clear(); - model.toggleSelectMode(); - if (model.selectMode) { - _selectedItems.add(isLocal, entries[index]); - } - setState(() {}); - }, - ), - ); - }, - )) + if (entries[index].isDirectory || entries[index].isDrive) { + controller.openDirectory(entries[index].path); + } else { + // Perform file-related tasks. + } + }, + onLongPress: entries[index].isDrive + ? null + : () { + _selectedItems.clear(); + widget.isSelecting.toggle(); + if (widget.isSelecting.value) { + _selectedItems.add(entries[index]); + } + setState(() {}); + }, + ), + ); + }, + ); + })) ]); } - breadCrumbScrollToEnd() { + void breadCrumbScrollToEnd() { Future.delayed(Duration(milliseconds: 200), () { if (_breadCrumbScroller.hasClients) { _breadCrumbScroller.animateTo( @@ -342,35 +505,39 @@ class _FileManagerPageState extends State { Widget headTools() => Container( child: Row( children: [ - Expanded( - child: BreadCrumb( - items: getPathBreadCrumbItems(() => model.goHome(), (list) { - var path = ""; - if (model.currentHome.startsWith(list[0])) { - // absolute path - for (var item in list) { - path = PathUtil.join(path, item, model.getCurrentIsWindows()); + Expanded(child: Obx(() { + final home = controller.options.value.home; + final isWindows = controller.options.value.isWindows; + return BreadCrumb( + items: getPathBreadCrumbItems(controller.shortPath, isWindows, + () => controller.goToHomeDirectory(), (list) { + var path = ""; + if (home.startsWith(list[0])) { + // absolute path + for (var item in list) { + path = PathUtil.join(path, item, isWindows); + } + } else { + path += home; + for (var item in list) { + path = PathUtil.join(path, item, isWindows); + } } - } else { - path += model.currentHome; - for (var item in list) { - path = PathUtil.join(path, item, model.getCurrentIsWindows()); - } - } - model.openDirectory(path); - }), - divider: Icon(Icons.chevron_right), - overflow: ScrollableOverflow(controller: _breadCrumbScroller), - )), + controller.openDirectory(path); + }), + divider: Icon(Icons.chevron_right), + overflow: ScrollableOverflow(controller: _breadCrumbScroller), + ); + })), Row( children: [ IconButton( icon: Icon(Icons.arrow_back), - onPressed: model.goBack, + onPressed: controller.goBack, ), IconButton( icon: Icon(Icons.arrow_upward), - onPressed: model.goToParentDirectory, + onPressed: controller.goToParentDirectory, ), PopupMenuButton( icon: Icon(Icons.sort), @@ -382,123 +549,37 @@ class _FileManagerPageState extends State { )) .toList(); }, - onSelected: model.changeSortStyle), + onSelected: controller.changeSortStyle), ], ) ], )); - Widget listTail() { - return Container( - height: 100, - child: Column( - children: [ - Padding( - padding: EdgeInsets.fromLTRB(30, 5, 30, 0), - child: Text( - model.currentDir.path, - style: TextStyle(color: MyTheme.darkGray), - ), - ), - Padding( - padding: EdgeInsets.all(2), - child: Text( - "${translate("Total")}: ${model.currentDir.entries.length} ${translate("items")}", - style: TextStyle(color: MyTheme.darkGray), - ), - ) - ], - ), - ); - } - - Widget? bottomSheet() { - final state = model.jobState; - final isOtherPage = _selectedItems.isOtherPage(model.isLocal); - final selectedItemsLen = "${_selectedItems.length} ${translate("items")}"; - final local = _selectedItems.isLocal == null - ? "" - : " [${_selectedItems.isLocal! ? translate("Local") : translate("Remote")}]"; - - if (model.selectMode) { - if (_selectedItems.length == 0 || !isOtherPage) { - return BottomSheetBody( - leading: Icon(Icons.check), - title: translate("Selected"), - text: selectedItemsLen + local, - onCanceled: () => model.toggleSelectMode(), - actions: [ - IconButton( - icon: Icon(Icons.compare_arrows), - onPressed: model.togglePage, + Widget listTail() => Obx(() => Container( + height: 100, + child: Column( + children: [ + Padding( + padding: EdgeInsets.fromLTRB(30, 5, 30, 0), + child: Text( + controller.directory.value.path, + style: TextStyle(color: MyTheme.darkGray), ), - IconButton( - icon: Icon(Icons.delete_forever), - onPressed: () { - if (_selectedItems.length > 0) { - model.removeAction(_selectedItems); - } - }, - ) - ]); - } else { - return BottomSheetBody( - leading: Icon(Icons.input), - title: translate("Paste here?"), - text: selectedItemsLen + local, - onCanceled: () => model.toggleSelectMode(), - actions: [ - IconButton( - icon: Icon(Icons.compare_arrows), - onPressed: model.togglePage, + ), + Padding( + padding: EdgeInsets.all(2), + child: Text( + "${translate("Total")}: ${controller.directory.value.entries.length} ${translate("items")}", + style: TextStyle(color: MyTheme.darkGray), ), - IconButton( - icon: Icon(Icons.paste), - onPressed: () { - model.toggleSelectMode(); - model.sendFiles(_selectedItems); - }, - ) - ]); - } - } + ) + ], + ), + )); - switch (state) { - case JobState.inProgress: - return BottomSheetBody( - leading: CircularProgressIndicator(), - title: translate("Waiting"), - text: - "${translate("Speed")}: ${readableFileSize(model.jobProgress.speed)}/s", - onCanceled: () => model.cancelJob(model.jobProgress.id), - ); - case JobState.done: - return BottomSheetBody( - leading: Icon(Icons.check), - title: "${translate("Successful")}!", - text: model.jobProgress.display(), - onCanceled: () => model.jobReset(), - ); - case JobState.error: - return BottomSheetBody( - leading: Icon(Icons.error), - title: "${translate("Error")}!", - text: "", - onCanceled: () => model.jobReset(), - ); - case JobState.none: - break; - case JobState.paused: - // TODO: Handle this case. - break; - } - return null; - } - - List getPathBreadCrumbItems( + List getPathBreadCrumbItems(String shortPath, bool isWindows, void Function() onHome, void Function(List) onPressed) { - final path = model.currentShortPath; - final list = PathUtil.split(path, model.getCurrentIsWindows()); + final list = PathUtil.split(shortPath, isWindows); final breadCrumbList = [ BreadCrumbItem( content: IconButton( From 69d84984042116aef754653b9ccc69562a90324f Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 9 Mar 2023 22:34:43 +0900 Subject: [PATCH 17/27] mobile new file SelectMode state --- .../lib/mobile/pages/file_manager_page.dart | 170 +++++++++++------- flutter/lib/models/file_model.dart | 2 +- 2 files changed, 108 insertions(+), 64 deletions(-) diff --git a/flutter/lib/mobile/pages/file_manager_page.dart b/flutter/lib/mobile/pages/file_manager_page.dart index aa8794227..c6ba42d31 100644 --- a/flutter/lib/mobile/pages/file_manager_page.dart +++ b/flutter/lib/mobile/pages/file_manager_page.dart @@ -18,10 +18,46 @@ class FileManagerPage extends StatefulWidget { State createState() => _FileManagerPageState(); } +enum SelectMode { local, remote, none } + +extension SelectModeEq on SelectMode { + bool eq(bool? currentIsLocal) { + if (currentIsLocal == null) { + return false; + } + if (currentIsLocal) { + return this == SelectMode.local; + } else { + return this == SelectMode.remote; + } + } +} + +extension SelectModeExt on Rx { + void toggle(bool currentIsLocal) { + switch (value) { + case SelectMode.local: + value = SelectMode.none; + break; + case SelectMode.remote: + value = SelectMode.none; + break; + case SelectMode.none: + if (currentIsLocal) { + value = SelectMode.local; + } else { + value = SelectMode.remote; + } + break; + } + } +} + class _FileManagerPageState extends State { final model = gFFI.fileModel; + final selectMode = SelectMode.none.obs; + var showLocal = true; - var isSelecting = false.obs; FileController get currentFileController => showLocal ? model.localController : model.remoteController; @@ -53,8 +89,9 @@ class _FileManagerPageState extends State { @override Widget build(BuildContext context) => WillPopScope( onWillPop: () async { - if (isSelecting.value) { - isSelecting.value = false; + if (selectMode.value != SelectMode.none) { + selectMode.value = SelectMode.none; + setState(() {}); } else { currentFileController.goBack(); } @@ -154,7 +191,8 @@ class _FileManagerPageState extends State { } else if (v == "select") { model.localController.selectedItems.clear(); model.remoteController.selectedItems.clear(); - isSelecting.toggle(); + selectMode.toggle(showLocal); + setState(() {}); } else if (v == "folder") { final name = TextEditingController(); gFFI.dialogManager @@ -196,43 +234,38 @@ class _FileManagerPageState extends State { body: showLocal ? FileManagerView( controller: model.localController, - isSelecting: isSelecting, - showCheckBox: showCheckBox()) + selectMode: selectMode, + ) : FileManagerView( controller: model.remoteController, - isSelecting: isSelecting, - showCheckBox: showCheckBox()), + selectMode: selectMode, + ), bottomSheet: bottomSheet(), )); - bool showCheckBox() { - final selectedItems = getActiveSelectedItems(); - - if (selectedItems != null) { - return selectedItems.isLocal == showLocal; - } - return false; - } - Widget? bottomSheet() { return Obx(() { final selectedItems = getActiveSelectedItems(); + final jobTable = model.jobController.jobTable; final localLabel = selectedItems?.isLocal == null ? "" : " [${selectedItems!.isLocal ? translate("Local") : translate("Remote")}]"; - - if (isSelecting.value) { + if (!(selectMode.value == SelectMode.none)) { final selectedItemsLen = "${selectedItems?.items.length ?? 0} ${translate("items")}"; if (selectedItems == null || selectedItems.items.isEmpty || - showCheckBox()) { + selectMode.value.eq(showLocal)) { return BottomSheetBody( leading: Icon(Icons.check), title: translate("Selected"), text: selectedItemsLen + localLabel, - onCanceled: () => isSelecting.toggle(), + onCanceled: () { + selectedItems?.items.clear(); + selectMode.value = SelectMode.none; + setState(() {}); + }, actions: [ IconButton( icon: Icon(Icons.compare_arrows), @@ -241,9 +274,12 @@ class _FileManagerPageState extends State { IconButton( icon: Icon(Icons.delete_forever), onPressed: selectedItems != null - ? () { + ? () async { if (selectedItems.items.isNotEmpty) { - currentFileController.removeAction(selectedItems); + await currentFileController + .removeAction(selectedItems); + selectedItems.items.clear(); + selectMode.value = SelectMode.none; } } : null, @@ -254,7 +290,11 @@ class _FileManagerPageState extends State { leading: Icon(Icons.input), title: translate("Paste here?"), text: selectedItemsLen + localLabel, - onCanceled: () => isSelecting.toggle(), + onCanceled: () { + selectedItems.items.clear(); + selectMode.value = SelectMode.none; + setState(() {}); + }, actions: [ IconButton( icon: Icon(Icons.compare_arrows), @@ -263,50 +303,51 @@ class _FileManagerPageState extends State { IconButton( icon: Icon(Icons.paste), onPressed: () { - isSelecting.toggle(); + selectMode.value = SelectMode.none; final otherSide = showLocal ? model.remoteController : model.localController; - final otherSideData = DirectoryData( - otherSide.directory.value, otherSide.options.value); - currentFileController.sendFiles( - selectedItems, otherSideData); + final thisSideData = + DirectoryData(currentDir, currentOptions); + otherSide.sendFiles(selectedItems, thisSideData); + selectedItems.items.clear(); + selectMode.value = SelectMode.none; }, ) ]); } } - final jobTable = model.jobController.jobTable; - if (jobTable.isEmpty) { return Offstage(); } switch (jobTable.last.state) { case JobState.inProgress: - return Obx(() => BottomSheetBody( - leading: CircularProgressIndicator(), - title: translate("Waiting"), - text: - "${translate("Speed")}: ${readableFileSize(jobTable.last.speed)}/s", - onCanceled: () => - model.jobController.cancelJob(jobTable.last.id), - )); + return BottomSheetBody( + leading: CircularProgressIndicator(), + title: translate("Waiting"), + text: + "${translate("Speed")}: ${readableFileSize(jobTable.last.speed)}/s", + onCanceled: () { + model.jobController.cancelJob(jobTable.last.id); + jobTable.clear(); + }, + ); case JobState.done: - return Obx(() => BottomSheetBody( - leading: Icon(Icons.check), - title: "${translate("Successful")}!", - text: jobTable.last.display(), - onCanceled: () => jobTable.clear(), - )); + return BottomSheetBody( + leading: Icon(Icons.check), + title: "${translate("Successful")}!", + text: jobTable.last.display(), + onCanceled: () => jobTable.clear(), + ); case JobState.error: - return Obx(() => BottomSheetBody( - leading: Icon(Icons.error), - title: "${translate("Error")}!", - text: "", - onCanceled: () => jobTable.clear(), - )); + return BottomSheetBody( + leading: Icon(Icons.error), + title: "${translate("Error")}!", + text: "", + onCanceled: () => jobTable.clear(), + ); case JobState.none: break; case JobState.paused: @@ -343,13 +384,9 @@ class _FileManagerPageState extends State { class FileManagerView extends StatefulWidget { final FileController controller; - final RxBool isSelecting; - final bool showCheckBox; + final Rx selectMode; - FileManagerView( - {required this.controller, - required this.isSelecting, - required this.showCheckBox}); + FileManagerView({required this.controller, required this.selectMode}); @override State createState() => _FileManagerViewState(); @@ -383,13 +420,18 @@ class _FileManagerViewState extends State { return listTail(); } var selected = false; - if (widget.isSelecting.value) { + if (widget.selectMode.value != SelectMode.none) { selected = _selectedItems.items.contains(entries[index]); } final sizeStr = entries[index].isFile ? readableFileSize(entries[index].size.toDouble()) : ""; + + final showCheckBox = () { + return widget.selectMode.value != SelectMode.none && + widget.selectMode.value.eq(controller.selectedItems.isLocal); + }(); return Card( child: ListTile( leading: entries[index].isDrive @@ -417,7 +459,7 @@ class _FileManagerViewState extends State { ), trailing: entries[index].isDrive ? null - : widget.isSelecting.value && widget.showCheckBox + : showCheckBox ? Checkbox( value: selected, onChanged: (v) { @@ -455,16 +497,18 @@ class _FileManagerViewState extends State { controller.removeAction(items); } else if (v == "multi_select") { _selectedItems.clear(); - widget.isSelecting.toggle(); + widget.selectMode.toggle(isLocal); + setState(() {}); } }), onTap: () { - if (widget.isSelecting.value && widget.showCheckBox) { + if (showCheckBox) { if (selected) { _selectedItems.remove(entries[index]); } else { _selectedItems.add(entries[index]); } + setState(() {}); return; } if (entries[index].isDirectory || entries[index].isDrive) { @@ -477,8 +521,8 @@ class _FileManagerViewState extends State { ? null : () { _selectedItems.clear(); - widget.isSelecting.toggle(); - if (widget.isSelecting.value) { + widget.selectMode.toggle(isLocal); + if (widget.selectMode.value != SelectMode.none) { _selectedItems.add(entries[index]); } setState(() {}); diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 4cfe19135..4170a5461 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -390,7 +390,7 @@ class FileController { } } - /// sendFiles from other side (SelectedItems) to current side (FileController.isLocal). + /// sendFiles from current side (FileController.isLocal) to other side (SelectedItems). void sendFiles(SelectedItems items, DirectoryData otherSideData) { /// ignore wrong items side status if (items.isLocal != isLocal) { From e2f5a82bea3741d677c18cd1d361f31184ee3021 Mon Sep 17 00:00:00 2001 From: "Miguel F. G" <116861809+flusheDData@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:36:40 +0100 Subject: [PATCH 18/27] Update es.rs New terms added --- src/lang/es.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lang/es.rs b/src/lang/es.rs index 7443f8640..3543c5155 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -461,8 +461,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Resolution", "Resolución"), ("No transfers in progress", "No hay transferencias en curso"), ("Set one-time password length", "Establecer contraseña de un solo uso"), - ("idd_driver_tip", ""), - ("confirm_idd_driver_tip", ""), - ("RDP Settings", ""), + ("idd_driver_tip", "Instalar controlador virtual de pantalla a usar cuando no hay pantalla física."), + ("confirm_idd_driver_tip", "La opción de instalar el controlador de pantalla virtual está marcada. Hay que tener en cuenta que se instalará un certificado de prueba para confirar en el controlador de pantalla. Este certificado solo se usará para confiar en controladores Rustdesk."), + ("RDP Settings", "Ajustes RDP"), ].iter().cloned().collect(); } From 828854a6cea41a33726a59323e98d4f3d7be45ed Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 9 Mar 2023 23:18:10 +0800 Subject: [PATCH 19/27] add count to avoid frequent "audio frame ignored" message found in issue #3268 --- src/client.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index 9202a34ba..705b387a8 100644 --- a/src/client.rs +++ b/src/client.rs @@ -708,6 +708,7 @@ pub struct AudioHandler { audio_stream: Option>, channels: u16, latency_controller: Arc>, + ignore_count: i32, } impl AudioHandler { @@ -810,7 +811,11 @@ impl AudioHandler { .check_audio(frame.timestamp) .not() { - log::debug!("audio frame {} is ignored", frame.timestamp); + self.ignore_count += 1; + if self.ignore_count == 100 { + self.ignore_count = 0; + log::debug!("100 audio frames are ignored"); + } return; } } From 7833c7ce31a4772d2a6293aa5e4504db98e2b2ea Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 10 Mar 2023 00:15:07 +0800 Subject: [PATCH 20/27] fix: window overflow & prevent setMaximize when fullscreen --- flutter/lib/models/state_model.dart | 13 +++++++++---- flutter/pubspec.yaml | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/flutter/lib/models/state_model.dart b/flutter/lib/models/state_model.dart index aa4fab86e..187b1ffc5 100644 --- a/flutter/lib/models/state_model.dart +++ b/flutter/lib/models/state_model.dart @@ -28,10 +28,9 @@ class StateGlobal { setWindowId(int id) => _windowId = id; setMaximize(bool v) { - if (_maximize != v) { + if (_maximize != v && !_fullscreen) { _maximize = v; - _resizeEdgeSize.value = - _maximize ? kMaximizeEdgeSize : kWindowEdgeSize; + _resizeEdgeSize.value = _maximize ? kMaximizeEdgeSize : kWindowEdgeSize; } } setFullscreen(bool v) { @@ -39,7 +38,13 @@ class StateGlobal { _fullscreen = v; _showTabBar.value = !_fullscreen; _resizeEdgeSize.value = - fullscreen ? kFullScreenEdgeSize : kWindowEdgeSize; + fullscreen + ? kFullScreenEdgeSize + : _maximize + ? kMaximizeEdgeSize + : kWindowEdgeSize; + print( + "fullscreen: ${fullscreen}, resizeEdgeSize: ${_resizeEdgeSize.value}"); _windowBorderWidth.value = fullscreen ? 0 : kWindowBorderWidth; WindowController.fromWindowId(windowId) .setFullscreen(_fullscreen) diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index ccb53cc9c..087fba71e 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -59,7 +59,7 @@ dependencies: desktop_multi_window: git: url: https://github.com/Kingtous/rustdesk_desktop_multi_window - ref: 3e2655677c54f421f9e378680d8171b95a211e0f + ref: e3947d4b4f8edaa655de63cd47f2a59a6e024218 freezed_annotation: ^2.0.3 flutter_custom_cursor: ^0.0.4 window_size: From 4a5be4cd155cc057868c9eaaa937e46ac73b4aa9 Mon Sep 17 00:00:00 2001 From: ilGigioVr88 Date: Thu, 9 Mar 2023 18:47:18 +0100 Subject: [PATCH 21/27] Update it.rs --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 1c531c59f..dc59ccea3 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -463,6 +463,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Set one-time password length", "Imposta la lunghezza della password monouso"), ("idd_driver_tip", ""), ("confirm_idd_driver_tip", ""), - ("RDP Settings", ""), + ("RDP Settings", "Impostazioni RDP"), ].iter().cloned().collect(); } From 08f238770fa265aebc5b68542715e506edd835fb Mon Sep 17 00:00:00 2001 From: tsarmis <126983335+tsarmis@users.noreply.github.com> Date: Thu, 9 Mar 2023 22:20:46 +0200 Subject: [PATCH 22/27] Update el.rs --- src/lang/el.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lang/el.rs b/src/lang/el.rs index 4c3698194..b0c8be87a 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -461,8 +461,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Resolution", "Ανάλυση"), ("No transfers in progress", "Δεν υπάρχει μεταφορά σε εξέλιξη"), ("Set one-time password length", "Μέγεθος κωδικού μιας χρήσης"), - ("idd_driver_tip", ""), - ("confirm_idd_driver_tip", ""), - ("RDP Settings", ""), + ("idd_driver_tip", "Εγκαταστήστε το πρόγραμμα οδήγησης εικονικής οθόνης που χρησιμοποιείται όταν δεν έχετε φυσικές οθόνες."), + ("confirm_idd_driver_tip", "Είναι ενεργοποιημένη η επιλογή εγκατάστασης του προγράμματος οδήγησης εικονικής οθόνης. Λάβετε υπόψη ότι θα εγκατασταθεί ένα δοκιμαστικό πιστοποιητικό για το πρόγραμμα οδήγησης εικονικής οθόνης. Αυτό το πιστοποιητικό θα χρησιμοποιηθεί μόνο για την πιστοποίηση των προγραμμάτων οδήγησης του Rustdesk."), + ("RDP Settings", "Ρυθμίσεις RDP"), ].iter().cloned().collect(); } From d681be04ddb03ca139b776a20a378d9f1629e9c9 Mon Sep 17 00:00:00 2001 From: tsarmis <126983335+tsarmis@users.noreply.github.com> Date: Thu, 9 Mar 2023 22:24:32 +0200 Subject: [PATCH 23/27] Update README-GR.md --- docs/README-GR.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/README-GR.md b/docs/README-GR.md index b4a7d5c47..8ec98030d 100644 --- a/docs/README-GR.md +++ b/docs/README-GR.md @@ -37,9 +37,9 @@ | Σεούλ | AWS lightsail | 1 vCPU / 0.5GB RAM | | Γερμανία | Hetzner | 2 vCPU / 4GB RAM | | Γερμανία | Codext | 4 vCPU / 8GB RAM | -| Φινλανδία (Ελσίνκι) | 0x101 Cyber Security | 4 vCPU / 8GB RAM | -| ΗΠΑ (Άσμπερν) | 0x101 Cyber Security | 4 vCPU / 8GB RAM | -| Ουκρανία (Κίεβο) | dc.volia (2VM) | 2 vCPU / 4GB RAM | +| Φινλανδία (Ελσίνκι) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM | +| ΗΠΑ (Άσμπερν) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM | +| Ουκρανία (Κίεβο) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | ## Dev Container From 6363e3a4ebfdbefdfbce42bd9f44b16feb46e353 Mon Sep 17 00:00:00 2001 From: Chieh Wang <99897242+chiehw@users.noreply.github.com> Date: Fri, 10 Mar 2023 08:02:59 +0800 Subject: [PATCH 24/27] Add keyboard mode to issue_temp --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index f83a72de9..d8781bb0c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -6,7 +6,7 @@ body: id: desc attributes: label: Bug Description - description: A clear and concise description of what the bug is + description: A clear and concise description of what the bug is (if it's a keyboard issue, provide the keyboard mode you're using. e.g. legacy, map, translate) validations: required: true - type: textarea From b042643dfb2c6c9e1b3913caa8c19ef227825bc2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 10 Mar 2023 10:16:36 +0800 Subject: [PATCH 25/27] trivial changes Signed-off-by: fufesou --- src/ui_cm_interface.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index f5c575d43..bd6eab3bf 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -848,7 +848,7 @@ pub fn elevate_portable(_id: i32) { #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn handle_incoming_voice_call(id: i32, accept: bool) { - if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) { + if let Some(client) = CLIENTS.read().unwrap().get(&id) { allow_err!(client.tx.send(Data::VoiceCallResponse(accept))); }; } @@ -856,7 +856,7 @@ pub fn handle_incoming_voice_call(id: i32, accept: bool) { #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn close_voice_call(id: i32) { - if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) { + if let Some(client) = CLIENTS.read().unwrap().get(&id) { allow_err!(client.tx.send(Data::CloseVoiceCall("".to_owned()))); }; } From 8a09abbf719588f54a5814a4652a81ba8ba54590 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 10 Mar 2023 10:53:41 +0800 Subject: [PATCH 26/27] avoid w/h <= 0 Signed-off-by: fufesou --- flutter/lib/models/model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index a8ed56bc5..8f46fdca8 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -371,7 +371,7 @@ class FfiModel with ChangeNotifier { _updateSessionWidthHeight(String id) { parent.target?.canvasModel.updateViewStyle(); - if (display.width < 0 || display.height < 0) { + if (display.width <= 0 || display.height <= 0) { debugPrintStack(label: 'invalid display size (${display.width},${display.height})'); } else { bind.sessionSetSize(id: id, width: display.width, height: display.height); From cdea6daf89f4479d99deb243157dbc103e46a0b3 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 10 Mar 2023 11:24:43 +0800 Subject: [PATCH 27/27] fix frequent load RustDesk_default.toml Signed-off-by: 21pages --- libs/hbb_common/src/config.rs | 48 ++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index ed7270a85..4ce2cf07e 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -4,7 +4,7 @@ use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, path::{Path, PathBuf}, sync::{Arc, Mutex, RwLock}, - time::SystemTime, + time::{Duration, Instant, SystemTime}, }; use anyhow::Result; @@ -51,6 +51,7 @@ lazy_static::lazy_static! { pub static ref APP_NAME: Arc> = Arc::new(RwLock::new("RustDesk".to_owned())); static ref KEY_PAIR: Arc>> = Default::default(); static ref HW_CODEC_CONFIG: Arc> = Arc::new(RwLock::new(HwCodecConfig::load())); + static ref USER_DEFAULT_CONFIG: Arc> = Arc::new(RwLock::new((UserDefaultConfig::load(), Instant::now()))); } lazy_static::lazy_static! { @@ -123,7 +124,7 @@ macro_rules! serde_field_bool { } impl $struct_name { pub fn $func() -> bool { - UserDefaultConfig::load().get($field_name) == "Y" + UserDefaultConfig::read().get($field_name) == "Y" } } }; @@ -980,21 +981,21 @@ impl PeerConfig { serde_field_string!( default_view_style, deserialize_view_style, - UserDefaultConfig::load().get("view_style") + UserDefaultConfig::read().get("view_style") ); serde_field_string!( default_scroll_style, deserialize_scroll_style, - UserDefaultConfig::load().get("scroll_style") + UserDefaultConfig::read().get("scroll_style") ); serde_field_string!( default_image_quality, deserialize_image_quality, - UserDefaultConfig::load().get("image_quality") + UserDefaultConfig::read().get("image_quality") ); fn default_custom_image_quality() -> Vec { - let f: f64 = UserDefaultConfig::load() + let f: f64 = UserDefaultConfig::read() .get("custom_image_quality") .parse() .unwrap_or(50.0); @@ -1020,15 +1021,15 @@ impl PeerConfig { let mut mp: HashMap = de::Deserialize::deserialize(deserializer)?; let mut key = "codec-preference"; if !mp.contains_key(key) { - mp.insert(key.to_owned(), UserDefaultConfig::load().get(key)); + mp.insert(key.to_owned(), UserDefaultConfig::read().get(key)); } key = "custom-fps"; if !mp.contains_key(key) { - mp.insert(key.to_owned(), UserDefaultConfig::load().get(key)); + mp.insert(key.to_owned(), UserDefaultConfig::read().get(key)); } key = "zoom-cursor"; if !mp.contains_key(key) { - mp.insert(key.to_owned(), UserDefaultConfig::load().get(key)); + mp.insert(key.to_owned(), UserDefaultConfig::read().get(key)); } Ok(mp) } @@ -1046,7 +1047,12 @@ serde_field_bool!( default_show_quality_monitor, "ShowQualityMonitor::default_show_quality_monitor" ); -serde_field_bool!(DisableAudio, "disable_audio", default_disable_audio, "DisableAudio::default_disable_audio"); +serde_field_bool!( + DisableAudio, + "disable_audio", + default_disable_audio, + "DisableAudio::default_disable_audio" +); serde_field_bool!( EnableFileTransfer, "enable_file_transfer", @@ -1065,9 +1071,19 @@ serde_field_bool!( default_lock_after_session_end, "LockAfterSessionEnd::default_lock_after_session_end" ); -serde_field_bool!(PrivacyMode, "privacy_mode", default_privacy_mode, "PrivacyMode::default_privacy_mode"); +serde_field_bool!( + PrivacyMode, + "privacy_mode", + default_privacy_mode, + "PrivacyMode::default_privacy_mode" +); -serde_field_bool!(AllowSwapKey, "allow_swap_key", default_allow_swap_key, "AllowSwapKey::default_allow_swap_key"); +serde_field_bool!( + AllowSwapKey, + "allow_swap_key", + default_allow_swap_key, + "AllowSwapKey::default_allow_swap_key" +); #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct LocalConfig { @@ -1282,6 +1298,14 @@ pub struct UserDefaultConfig { } impl UserDefaultConfig { + pub fn read() -> UserDefaultConfig { + let mut cfg = USER_DEFAULT_CONFIG.write().unwrap(); + if cfg.1.elapsed() > Duration::from_secs(1) { + *cfg = (Self::load(), Instant::now()); + } + cfg.0.clone() + } + pub fn load() -> UserDefaultConfig { Config::load_::("_default") }