mirror of
https://github.com/veeso/termscp.git
synced 2025-12-06 17:15:35 -08:00
Sync browsing prompts to create dir (#85)
This commit is contained in:
@@ -62,6 +62,8 @@ Released on FIXME:
|
||||
- **Less verbose logging**:
|
||||
- By default the log level is now set to `INFO`
|
||||
- It is now possible to enable the `TRACE` level with the `-D` CLI option.
|
||||
- **Synchronized browsing**:
|
||||
- From now on, if synchronized browsing is *enabled* and you try to enter a directory that doesn't exist on the other host, you will be asked whether you'd like to create the directory.
|
||||
- Dependencies:
|
||||
- Updated `tui-realm` to `1.3.0`
|
||||
- Updated `tui-realm-stdlib` to `1.1.4`
|
||||
|
||||
@@ -201,8 +201,6 @@ All the actions are available when working with multiple files, but be aware tha
|
||||
When enabled, synchronized browsing, will allow you to synchronize the navigation between the two panels.
|
||||
This means that whenever you'll change the working directory on one panel, the same action will be reproduced on the other panel. If you want to enable synchronized browsing just press `<Y>`; press twice to disable. While enabled, the synchronized browsing state will be reported on the status bar on `ON`.
|
||||
|
||||
> ❗ at the moment, whenever you try to access an unexisting directory, you won't be prompted to create it. This might change in a future update.
|
||||
|
||||
### Open and Open With 🚪
|
||||
|
||||
Open and open with commands are powered by [open-rs](https://docs.rs/crate/open/1.7.0).
|
||||
|
||||
@@ -201,8 +201,6 @@ Todas las acciones están disponibles cuando se trabaja con varios archivos, per
|
||||
Cuando está habilitada, la navegación sincronizada le permitirá sincronizar la navegación entre los dos paneles.
|
||||
Esto significa que siempre que cambie el directorio de trabajo en un panel, la misma acción se reproducirá en el otro panel. Si desea habilitar la navegación sincronizada, simplemente presione `<Y>`; presione dos veces para deshabilitar. Mientras está habilitado, el estado de navegación sincronizada se informará en la barra de estado en "ON".
|
||||
|
||||
> ❗ Por el momento, cada vez que intente acceder a un directorio que no existe, no se le pedirá que lo cree. Esto podría cambiar en una actualización futura.
|
||||
|
||||
### Abierta y abierta con 🚪
|
||||
|
||||
Al abrir archivos con el comando Ver (`<V>`), se utilizará la aplicación predeterminada del sistema para el tipo de archivo. Para hacerlo, se utilizará el servicio del sistema operativo predeterminado, así que asegúrese de tener al menos uno de estos instalado en su sistema:
|
||||
|
||||
@@ -199,8 +199,6 @@ Toutes les actions sont disponibles lorsque vous travaillez avec plusieurs fichi
|
||||
Lorsqu'elle est activée, la navigation synchronisée vous permettra de synchroniser la navigation entre les deux panneaux.
|
||||
Cela signifie que chaque fois que vous changerez de répertoire de travail sur un panneau, la même action sera reproduite sur l'autre panneau. Si vous souhaitez activer la navigation synchronisée, appuyez simplement sur `<Y>` ; appuyez deux fois pour désactiver. Lorsqu'il est activé, l'état de navigation synchronisé sera signalé dans la barre d'état sur `ON`
|
||||
|
||||
> ❗ pour le moment, chaque fois que vous essayez d'accéder à un répertoire inexistant, vous ne serez pas invité à le créer. Cela pourrait changer dans une future mise à jour.
|
||||
|
||||
### Ouvrir et ouvrir avec 🚪
|
||||
|
||||
Lors de l'ouverture de fichiers avec la commande Afficher (`<V>`), l'application par défaut du système pour le type de fichier sera utilisée. Pour ce faire, le service du système d'exploitation par défaut sera utilisé, alors assurez-vous d'avoir au moins l'un de ceux-ci installé sur votre système :
|
||||
|
||||
@@ -195,8 +195,6 @@ Tutte le azioni sono disponibili quando si lavora sulle selezioni, ma occhio, ch
|
||||
Quando abilitato, ti permetterà di sincronizzare la navigazione tra i due pannelli.
|
||||
Ciò comporta che quando cambierai directory in uno dei due pannelli, lo stesso verrà fatto nell'altro. Per abilitare la modalità è sufficiente premere `<Y>`; fai lo stesso per disabilitarlo. Mentre abilitato, sull'interfaccia dovrebbe essere visualizzato `Sync Browsing: ON` nella barra di stato.
|
||||
|
||||
> ❗ Al momento, se provi ad accedere ad una cartella non esistente su uno dei due host, mentre il sync browsing è attivo, non ti verrà chiesto di crearla, ma semplicemente fallirà. Questo sarà risolto in un aggiornamento futuro.
|
||||
|
||||
### Apri e apri con 🚪
|
||||
|
||||
I comandi "apri" e "apri con" sono forniti da [open-rs](https://docs.rs/crate/open/2.1.0).
|
||||
|
||||
@@ -199,8 +199,6 @@ All the actions are available when working with multiple files, but be aware tha
|
||||
When enabled, synchronized browsing, will allow you to synchronize the navigation between the two panels.
|
||||
This means that whenever you'll change the working directory on one panel, the same action will be reproduced on the other panel. If you want to enable synchronized browsing just press `<Y>`; press twice to disable. While enabled, the synchronized browsing state will be reported on the status bar on `ON`.
|
||||
|
||||
> ❗ at the moment, whenever you try to access an unexisting directory, you won't be prompted to create it. This might change in a future update.
|
||||
|
||||
### Open and Open With 🚪
|
||||
|
||||
Open and open with commands are powered by [open-rs](https://docs.rs/crate/open/1.7.0).
|
||||
|
||||
@@ -193,8 +193,6 @@ termscp中的文件资源管理器是指你与远程建立连接后可以看到
|
||||
|
||||
启用时,同步浏览将允许你在两个面板之间同步导航操作。这意味着,每当你在一个面板上改变工作目录时,同样的动作会在另一个面板上重现。如果你想启用同步浏览,只需按下`<Y>`;按两次就可以禁用。当启用时,同步浏览的状态将在状态栏上显示为`ON`。
|
||||
|
||||
> ❗ 目前,每当你试图访问一个不存在的目录,你不会被提示创建它。这点可能会在未来的更新中改进。
|
||||
|
||||
### 打开/打开方式
|
||||
|
||||
打开和打开方式的功能是由 [open-rs](https://docs.rs/crate/open/2.1.0)提供的。
|
||||
|
||||
@@ -26,84 +26,87 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
// locals
|
||||
use super::FileTransferActivity;
|
||||
use super::{FileExplorerTab, FileTransferActivity, LogLevel, Msg, PendingActionMsg};
|
||||
|
||||
use remotefs::Directory;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Describes destination for sync browsing
|
||||
enum SyncBrowsingDestination {
|
||||
Path(String),
|
||||
ParentDir,
|
||||
PreviousDir,
|
||||
}
|
||||
|
||||
impl FileTransferActivity {
|
||||
/// Enter a directory on local host from entry
|
||||
/// Return true whether the directory changed
|
||||
pub(crate) fn action_enter_local_dir(&mut self, dir: Directory, block_sync: bool) -> bool {
|
||||
pub(crate) fn action_enter_local_dir(&mut self, dir: Directory) {
|
||||
self.local_changedir(dir.path.as_path(), true);
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_change_remote_dir(dir.name, true);
|
||||
if self.browser.sync_browsing {
|
||||
self.synchronize_browsing(SyncBrowsingDestination::Path(dir.name));
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Enter a directory on local host from entry
|
||||
/// Return true whether the directory changed
|
||||
pub(crate) fn action_enter_remote_dir(&mut self, dir: Directory, block_sync: bool) -> bool {
|
||||
pub(crate) fn action_enter_remote_dir(&mut self, dir: Directory) {
|
||||
self.remote_changedir(dir.path.as_path(), true);
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_change_local_dir(dir.name, true);
|
||||
if self.browser.sync_browsing {
|
||||
self.synchronize_browsing(SyncBrowsingDestination::Path(dir.name));
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Change local directory reading value from input
|
||||
pub(crate) fn action_change_local_dir(&mut self, input: String, block_sync: bool) {
|
||||
pub(crate) fn action_change_local_dir(&mut self, input: String) {
|
||||
let dir_path: PathBuf = self.local_to_abs_path(PathBuf::from(input.as_str()).as_path());
|
||||
self.local_changedir(dir_path.as_path(), true);
|
||||
// Check whether to sync
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_change_remote_dir(input, true);
|
||||
if self.browser.sync_browsing {
|
||||
self.synchronize_browsing(SyncBrowsingDestination::Path(input));
|
||||
}
|
||||
}
|
||||
|
||||
/// Change remote directory reading value from input
|
||||
pub(crate) fn action_change_remote_dir(&mut self, input: String, block_sync: bool) {
|
||||
pub(crate) fn action_change_remote_dir(&mut self, input: String) {
|
||||
let dir_path: PathBuf = self.remote_to_abs_path(PathBuf::from(input.as_str()).as_path());
|
||||
self.remote_changedir(dir_path.as_path(), true);
|
||||
// Check whether to sync
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_change_local_dir(input, true);
|
||||
if self.browser.sync_browsing {
|
||||
self.synchronize_browsing(SyncBrowsingDestination::Path(input));
|
||||
}
|
||||
}
|
||||
|
||||
/// Go to previous directory from localhost
|
||||
pub(crate) fn action_go_to_previous_local_dir(&mut self, block_sync: bool) {
|
||||
pub(crate) fn action_go_to_previous_local_dir(&mut self) {
|
||||
if let Some(d) = self.local_mut().popd() {
|
||||
self.local_changedir(d.as_path(), false);
|
||||
// Check whether to sync
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_go_to_previous_remote_dir(true);
|
||||
if self.browser.sync_browsing {
|
||||
self.synchronize_browsing(SyncBrowsingDestination::PreviousDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Go to previous directory from remote host
|
||||
pub(crate) fn action_go_to_previous_remote_dir(&mut self, block_sync: bool) {
|
||||
pub(crate) fn action_go_to_previous_remote_dir(&mut self) {
|
||||
if let Some(d) = self.remote_mut().popd() {
|
||||
self.remote_changedir(d.as_path(), false);
|
||||
// Check whether to sync
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_go_to_previous_local_dir(true);
|
||||
if self.browser.sync_browsing {
|
||||
self.synchronize_browsing(SyncBrowsingDestination::PreviousDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Go to upper directory on local host
|
||||
pub(crate) fn action_go_to_local_upper_dir(&mut self, block_sync: bool) {
|
||||
pub(crate) fn action_go_to_local_upper_dir(&mut self) {
|
||||
// Get pwd
|
||||
let path: PathBuf = self.local().wrkdir.clone();
|
||||
// Go to parent directory
|
||||
if let Some(parent) = path.as_path().parent() {
|
||||
self.local_changedir(parent, true);
|
||||
// If sync is enabled update remote too
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_go_to_remote_upper_dir(true);
|
||||
if self.browser.sync_browsing {
|
||||
self.synchronize_browsing(SyncBrowsingDestination::ParentDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,15 +114,140 @@ impl FileTransferActivity {
|
||||
/// #### action_go_to_remote_upper_dir
|
||||
///
|
||||
/// Go to upper directory on remote host
|
||||
pub(crate) fn action_go_to_remote_upper_dir(&mut self, block_sync: bool) {
|
||||
pub(crate) fn action_go_to_remote_upper_dir(&mut self) {
|
||||
// Get pwd
|
||||
let path: PathBuf = self.remote().wrkdir.clone();
|
||||
// Go to parent directory
|
||||
if let Some(parent) = path.as_path().parent() {
|
||||
self.remote_changedir(parent, true);
|
||||
// If sync is enabled update local too
|
||||
if self.browser.sync_browsing && !block_sync {
|
||||
self.action_go_to_local_upper_dir(true);
|
||||
if self.browser.sync_browsing {
|
||||
self.synchronize_browsing(SyncBrowsingDestination::ParentDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- sync browsing
|
||||
|
||||
/// Synchronize browsing on the target browser.
|
||||
/// If destination doesn't exist, then prompt for directory creation.
|
||||
fn synchronize_browsing(&mut self, destination: SyncBrowsingDestination) {
|
||||
// Get destination path
|
||||
let path = match self.resolve_sync_browsing_destination(&destination) {
|
||||
Some(p) => p,
|
||||
None => return,
|
||||
};
|
||||
trace!("Synchronizing browsing to path {}", path.display());
|
||||
// Check whether destination exists on host
|
||||
let exists = match self.browser.tab() {
|
||||
FileExplorerTab::Local => match self.client.exists(path.as_path()) {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
error!(
|
||||
"Failed to check whether {} exists on remote: {}",
|
||||
path.display(),
|
||||
err
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
FileExplorerTab::Remote => self.host.file_exists(path.as_path()),
|
||||
_ => return,
|
||||
};
|
||||
let name = path
|
||||
.file_name()
|
||||
.map(|x| x.to_string_lossy().to_string())
|
||||
.unwrap();
|
||||
// If file doesn't exist, ask whether to create directory
|
||||
if !exists {
|
||||
trace!("Directory doesn't exist; asking to user if I should create it");
|
||||
// Mount dialog
|
||||
self.mount_sync_browsing_mkdir_popup(&name);
|
||||
// Wait for dialog dismiss
|
||||
if self.wait_for_pending_msg(&[
|
||||
Msg::PendingAction(PendingActionMsg::MakePendingDirectory),
|
||||
Msg::PendingAction(PendingActionMsg::CloseSyncBrowsingMkdirPopup),
|
||||
]) == Msg::PendingAction(PendingActionMsg::MakePendingDirectory)
|
||||
{
|
||||
trace!("User wants to create the unexisting directory");
|
||||
// Make directory
|
||||
match self.browser.tab() {
|
||||
FileExplorerTab::Local => self.action_remote_mkdir(name.clone()),
|
||||
FileExplorerTab::Remote => self.action_local_mkdir(name.clone()),
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
// Do not synchronize, disable sync browsing and return
|
||||
trace!("The user doesn't want to create the directory; disabling synchronized browsing");
|
||||
self.log(
|
||||
LogLevel::Warn,
|
||||
format!(
|
||||
"Refused to create '{}'; synchronized browsing disabled",
|
||||
name
|
||||
),
|
||||
);
|
||||
self.browser.toggle_sync_browsing();
|
||||
self.refresh_remote_status_bar();
|
||||
self.umount_sync_browsing_mkdir_popup();
|
||||
return;
|
||||
}
|
||||
// Umount dialog
|
||||
self.umount_sync_browsing_mkdir_popup();
|
||||
}
|
||||
trace!("Entering on the other explorer directory {}", name);
|
||||
// Enter directory
|
||||
match destination {
|
||||
SyncBrowsingDestination::ParentDir => match self.browser.tab() {
|
||||
FileExplorerTab::Local => self.remote_changedir(path.as_path(), true),
|
||||
FileExplorerTab::Remote => self.local_changedir(path.as_path(), true),
|
||||
_ => {}
|
||||
},
|
||||
SyncBrowsingDestination::Path(_) => match self.browser.tab() {
|
||||
FileExplorerTab::Local => self.remote_changedir(path.as_path(), true),
|
||||
FileExplorerTab::Remote => self.local_changedir(path.as_path(), true),
|
||||
_ => {}
|
||||
},
|
||||
SyncBrowsingDestination::PreviousDir => match self.browser.tab() {
|
||||
FileExplorerTab::Local => self.remote_changedir(path.as_path(), false),
|
||||
FileExplorerTab::Remote => self.local_changedir(path.as_path(), false),
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve synchronized browsing destination
|
||||
fn resolve_sync_browsing_destination(
|
||||
&mut self,
|
||||
destination: &SyncBrowsingDestination,
|
||||
) -> Option<PathBuf> {
|
||||
match (destination, self.browser.tab()) {
|
||||
// NOTE: tab and methods are switched on purpose
|
||||
(SyncBrowsingDestination::ParentDir, FileExplorerTab::Local) => {
|
||||
self.remote().wrkdir.parent().map(|x| x.to_path_buf())
|
||||
}
|
||||
(SyncBrowsingDestination::ParentDir, FileExplorerTab::Remote) => {
|
||||
self.local().wrkdir.parent().map(|x| x.to_path_buf())
|
||||
}
|
||||
(SyncBrowsingDestination::PreviousDir, FileExplorerTab::Local) => {
|
||||
if let Some(p) = self.remote_mut().popd() {
|
||||
Some(p)
|
||||
} else {
|
||||
warn!("Cannot synchronize browsing: remote has no previous directory in stack");
|
||||
None
|
||||
}
|
||||
}
|
||||
(SyncBrowsingDestination::PreviousDir, FileExplorerTab::Remote) => {
|
||||
if let Some(p) = self.local_mut().popd() {
|
||||
Some(p)
|
||||
} else {
|
||||
warn!("Cannot synchronize browsing: local has no previous directory in stack");
|
||||
None
|
||||
}
|
||||
}
|
||||
(SyncBrowsingDestination::Path(p), _) => Some(PathBuf::from(p.as_str())),
|
||||
_ => {
|
||||
warn!("Cannot synchronize browsing for current explorer");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
pub(self) use super::{
|
||||
browser::FileExplorerTab, FileTransferActivity, Id, LogLevel, TransferOpts, TransferPayload,
|
||||
browser::FileExplorerTab, FileTransferActivity, Id, LogLevel, Msg, PendingActionMsg,
|
||||
TransferOpts, TransferPayload,
|
||||
};
|
||||
pub(self) use remotefs::Entry;
|
||||
use tuirealm::{State, StateValue};
|
||||
@@ -41,6 +42,7 @@ pub(crate) mod find;
|
||||
pub(crate) mod mkdir;
|
||||
pub(crate) mod newfile;
|
||||
pub(crate) mod open;
|
||||
mod pending;
|
||||
pub(crate) mod rename;
|
||||
pub(crate) mod save;
|
||||
pub(crate) mod submit;
|
||||
|
||||
63
src/ui/activities/filetransfer/actions/pending.rs
Normal file
63
src/ui/activities/filetransfer/actions/pending.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
//! ## Pending actions
|
||||
//!
|
||||
//! this little module exposes the routine to create a pending action on the file transfer activity.
|
||||
//! A pending action is an action which blocks the execution of the application in await of a certain `Msg`.
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* termscp - Copyright (c) 2021 Christian Visintin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
use super::{FileTransferActivity, Msg};
|
||||
|
||||
use tuirealm::PollStrategy;
|
||||
|
||||
impl FileTransferActivity {
|
||||
/// Block execution of activity, preventing ANY kind of message not specified in the `wait_for` argument.
|
||||
/// Once `wait_for` clause is satisfied, the function returns.
|
||||
///
|
||||
/// Returns the message which satisfied the clause
|
||||
///
|
||||
/// NOTE: The view is redrawn as usual
|
||||
pub(super) fn wait_for_pending_msg(&mut self, wait_for: &[Msg]) -> Msg {
|
||||
self.redraw = true;
|
||||
loop {
|
||||
// Poll
|
||||
match self.app.tick(PollStrategy::Once) {
|
||||
Ok(messages) => {
|
||||
if !messages.is_empty() {
|
||||
self.redraw = true;
|
||||
}
|
||||
if let Some(msg) = messages.into_iter().find(|m| wait_for.contains(m)) {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Application error: {}", err);
|
||||
}
|
||||
}
|
||||
// Redraw
|
||||
if self.redraw {
|
||||
self.view();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ enum SubmitAction {
|
||||
impl FileTransferActivity {
|
||||
/// Decides which action to perform on submit for local explorer
|
||||
/// Return true whether the directory changed
|
||||
pub(crate) fn action_submit_local(&mut self, entry: Entry) -> bool {
|
||||
pub(crate) fn action_submit_local(&mut self, entry: Entry) {
|
||||
let (action, entry) = match &entry {
|
||||
Entry::Directory(_) => (SubmitAction::ChangeDir, entry),
|
||||
Entry::File(File {
|
||||
@@ -67,18 +67,14 @@ impl FileTransferActivity {
|
||||
}
|
||||
Entry::File(_) => (SubmitAction::None, entry),
|
||||
};
|
||||
match (action, entry) {
|
||||
(SubmitAction::ChangeDir, Entry::Directory(dir)) => {
|
||||
self.action_enter_local_dir(dir, false)
|
||||
}
|
||||
(SubmitAction::ChangeDir, _) => false,
|
||||
(SubmitAction::None, _) => false,
|
||||
if let (SubmitAction::ChangeDir, Entry::Directory(dir)) = (action, entry) {
|
||||
self.action_enter_local_dir(dir)
|
||||
}
|
||||
}
|
||||
|
||||
/// Decides which action to perform on submit for remote explorer
|
||||
/// Return true whether the directory changed
|
||||
pub(crate) fn action_submit_remote(&mut self, entry: Entry) -> bool {
|
||||
pub(crate) fn action_submit_remote(&mut self, entry: Entry) {
|
||||
let (action, entry) = match &entry {
|
||||
Entry::Directory(_) => (SubmitAction::ChangeDir, entry),
|
||||
Entry::File(File {
|
||||
@@ -107,12 +103,8 @@ impl FileTransferActivity {
|
||||
}
|
||||
Entry::File(_) => (SubmitAction::None, entry),
|
||||
};
|
||||
match (action, entry) {
|
||||
(SubmitAction::ChangeDir, Entry::Directory(dir)) => {
|
||||
self.action_enter_remote_dir(dir, false)
|
||||
}
|
||||
(SubmitAction::ChangeDir, _) => false,
|
||||
(SubmitAction::None, _) => false,
|
||||
if let (SubmitAction::ChangeDir, Entry::Directory(dir)) = (action, entry) {
|
||||
self.action_enter_remote_dir(dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
use super::{Msg, TransferMsg, UiMsg};
|
||||
use super::{Msg, PendingActionMsg, TransferMsg, UiMsg};
|
||||
|
||||
use tui_realm_stdlib::Phantom;
|
||||
use tuirealm::{
|
||||
@@ -45,7 +45,8 @@ pub use popups::{
|
||||
CopyPopup, DeletePopup, DisconnectPopup, ErrorPopup, ExecPopup, FatalPopup, FileInfoPopup,
|
||||
FindPopup, GoToPopup, KeybindingsPopup, MkdirPopup, NewfilePopup, OpenWithPopup,
|
||||
ProgressBarFull, ProgressBarPartial, QuitPopup, RenamePopup, ReplacePopup,
|
||||
ReplacingFilesListPopup, SaveAsPopup, SortingPopup, StatusBarLocal, StatusBarRemote, WaitPopup,
|
||||
ReplacingFilesListPopup, SaveAsPopup, SortingPopup, StatusBarLocal, StatusBarRemote,
|
||||
SyncBrowsingMkdirPopup, WaitPopup,
|
||||
};
|
||||
pub use transfer::{ExplorerFind, ExplorerLocal, ExplorerRemote};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
use super::super::Browser;
|
||||
use super::{Msg, TransferMsg, UiMsg};
|
||||
use super::{Msg, PendingActionMsg, TransferMsg, UiMsg};
|
||||
use crate::explorer::FileSorting;
|
||||
use crate::utils::fmt::fmt_time;
|
||||
|
||||
@@ -1657,6 +1657,70 @@ fn hidden_files_label(visible: bool) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
pub struct SyncBrowsingMkdirPopup {
|
||||
component: Radio,
|
||||
}
|
||||
|
||||
impl SyncBrowsingMkdirPopup {
|
||||
pub fn new(color: Color, dir_name: &str) -> Self {
|
||||
Self {
|
||||
component: Radio::default()
|
||||
.borders(
|
||||
Borders::default()
|
||||
.color(color)
|
||||
.modifiers(BorderType::Rounded),
|
||||
)
|
||||
.foreground(color)
|
||||
.choices(&["Yes", "No"])
|
||||
.title(
|
||||
format!(
|
||||
r#"Sync browsing: directory "{}" doesn't exist. Do you want to create it?"#,
|
||||
dir_name
|
||||
),
|
||||
Alignment::Center,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component<Msg, NoUserEvent> for SyncBrowsingMkdirPopup {
|
||||
fn on(&mut self, ev: Event<NoUserEvent>) -> Option<Msg> {
|
||||
match ev {
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Left, ..
|
||||
}) => {
|
||||
self.perform(Cmd::Move(Direction::Left));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Right, ..
|
||||
}) => {
|
||||
self.perform(Cmd::Move(Direction::Right));
|
||||
Some(Msg::None)
|
||||
}
|
||||
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => Some(Msg::PendingAction(
|
||||
PendingActionMsg::CloseSyncBrowsingMkdirPopup,
|
||||
)),
|
||||
Event::Keyboard(KeyEvent {
|
||||
code: Key::Enter, ..
|
||||
}) => {
|
||||
if matches!(
|
||||
self.perform(Cmd::Submit),
|
||||
CmdResult::Submit(State::One(StateValue::Usize(0)))
|
||||
) {
|
||||
Some(Msg::PendingAction(PendingActionMsg::MakePendingDirectory))
|
||||
} else {
|
||||
Some(Msg::PendingAction(
|
||||
PendingActionMsg::CloseSyncBrowsingMkdirPopup,
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(MockComponent)]
|
||||
pub struct WaitPopup {
|
||||
component: Paragraph,
|
||||
|
||||
@@ -56,8 +56,10 @@ use tuirealm::{Application, EventListenerCfg, NoUserEvent};
|
||||
|
||||
// -- Storage keys
|
||||
|
||||
const STORAGE_EXPLORER_WIDTH: &str = "FILETRANSFER_EXPLORER_WIDTH";
|
||||
const STORAGE_PENDING_TRANSFER: &str = "FILETRANSFER_PENDING_TRANSFER";
|
||||
/// Stores the explorer width
|
||||
const STORAGE_EXPLORER_WIDTH: &str = "FT_EW";
|
||||
/// Stores the filename of the entry to transfer, when the replace file dialog must be shown
|
||||
const STORAGE_PENDING_TRANSFER: &str = "FT_PT";
|
||||
|
||||
// -- components
|
||||
|
||||
@@ -92,16 +94,24 @@ enum Id {
|
||||
SortingPopup,
|
||||
StatusBarLocal,
|
||||
StatusBarRemote,
|
||||
SyncBrowsingMkdirPopup,
|
||||
WaitPopup,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Msg {
|
||||
PendingAction(PendingActionMsg),
|
||||
Transfer(TransferMsg),
|
||||
Ui(UiMsg),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum PendingActionMsg {
|
||||
CloseSyncBrowsingMkdirPopup,
|
||||
MakePendingDirectory,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum TransferMsg {
|
||||
AbortTransfer,
|
||||
|
||||
@@ -42,6 +42,10 @@ impl Update<Msg> for FileTransferActivity {
|
||||
fn update(&mut self, msg: Option<Msg>) -> Option<Msg> {
|
||||
match msg.unwrap_or(Msg::None) {
|
||||
Msg::None => None,
|
||||
Msg::PendingAction(_) => {
|
||||
// NOTE: Pending actions must be handled directly in the action
|
||||
None
|
||||
}
|
||||
Msg::Transfer(msg) => self.update_transfer(msg),
|
||||
Msg::Ui(msg) => self.update_ui(msg),
|
||||
}
|
||||
@@ -106,24 +110,22 @@ impl FileTransferActivity {
|
||||
}
|
||||
TransferMsg::EnterDirectory if self.browser.tab() == FileExplorerTab::Local => {
|
||||
if let SelectedEntry::One(entry) = self.get_local_selected_entries() {
|
||||
if self.action_submit_local(entry) {
|
||||
// Update file list if sync
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_remote_filelist();
|
||||
}
|
||||
self.update_local_filelist();
|
||||
self.action_submit_local(entry);
|
||||
// Update file list if sync
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_remote_filelist();
|
||||
}
|
||||
self.update_local_filelist();
|
||||
}
|
||||
}
|
||||
TransferMsg::EnterDirectory if self.browser.tab() == FileExplorerTab::Remote => {
|
||||
if let SelectedEntry::One(entry) = self.get_remote_selected_entries() {
|
||||
if self.action_submit_remote(entry) {
|
||||
// Update file list if sync
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_local_filelist();
|
||||
}
|
||||
self.update_remote_filelist();
|
||||
self.action_submit_remote(entry);
|
||||
// Update file list if sync
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_local_filelist();
|
||||
}
|
||||
self.update_remote_filelist();
|
||||
}
|
||||
}
|
||||
TransferMsg::EnterDirectory => {
|
||||
@@ -152,8 +154,8 @@ impl FileTransferActivity {
|
||||
}
|
||||
TransferMsg::GoTo(dir) => {
|
||||
match self.browser.tab() {
|
||||
FileExplorerTab::Local => self.action_change_local_dir(dir, false),
|
||||
FileExplorerTab::Remote => self.action_change_remote_dir(dir, false),
|
||||
FileExplorerTab::Local => self.action_change_local_dir(dir),
|
||||
FileExplorerTab::Remote => self.action_change_remote_dir(dir),
|
||||
_ => panic!("Found tab doesn't support GOTO"),
|
||||
}
|
||||
// Umount
|
||||
@@ -168,7 +170,7 @@ impl FileTransferActivity {
|
||||
TransferMsg::GoToParentDirectory => {
|
||||
match self.browser.tab() {
|
||||
FileExplorerTab::Local => {
|
||||
self.action_go_to_local_upper_dir(false);
|
||||
self.action_go_to_local_upper_dir();
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_remote_filelist();
|
||||
}
|
||||
@@ -176,7 +178,7 @@ impl FileTransferActivity {
|
||||
self.update_local_filelist()
|
||||
}
|
||||
FileExplorerTab::Remote => {
|
||||
self.action_go_to_remote_upper_dir(false);
|
||||
self.action_go_to_remote_upper_dir();
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_local_filelist();
|
||||
}
|
||||
@@ -189,7 +191,7 @@ impl FileTransferActivity {
|
||||
TransferMsg::GoToPreviousDirectory => {
|
||||
match self.browser.tab() {
|
||||
FileExplorerTab::Local => {
|
||||
self.action_go_to_previous_local_dir(false);
|
||||
self.action_go_to_previous_local_dir();
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_remote_filelist();
|
||||
}
|
||||
@@ -197,7 +199,7 @@ impl FileTransferActivity {
|
||||
self.update_local_filelist()
|
||||
}
|
||||
FileExplorerTab::Remote => {
|
||||
self.action_go_to_previous_remote_dir(false);
|
||||
self.action_go_to_previous_remote_dir();
|
||||
if self.browser.sync_browsing {
|
||||
let _ = self.update_local_filelist();
|
||||
}
|
||||
|
||||
@@ -307,6 +307,11 @@ impl FileTransferActivity {
|
||||
f.render_widget(Clear, popup);
|
||||
// make popup
|
||||
self.app.view(&Id::WaitPopup, f, popup);
|
||||
} else if self.app.mounted(&Id::SyncBrowsingMkdirPopup) {
|
||||
let popup = draw_area_in(f.size(), 60, 10);
|
||||
f.render_widget(Clear, popup);
|
||||
// make popup
|
||||
self.app.view(&Id::SyncBrowsingMkdirPopup, f, popup);
|
||||
} else if self.app.mounted(&Id::KeybindingsPopup) {
|
||||
let popup = draw_area_in(f.size(), 50, 80);
|
||||
f.render_widget(Clear, popup);
|
||||
@@ -798,6 +803,23 @@ impl FileTransferActivity {
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
pub(super) fn mount_sync_browsing_mkdir_popup(&mut self, dir_name: &str) {
|
||||
let color = self.theme().misc_info_dialog;
|
||||
assert!(self
|
||||
.app
|
||||
.remount(
|
||||
Id::SyncBrowsingMkdirPopup,
|
||||
Box::new(components::SyncBrowsingMkdirPopup::new(color, dir_name,)),
|
||||
vec![],
|
||||
)
|
||||
.is_ok());
|
||||
assert!(self.app.active(&Id::SyncBrowsingMkdirPopup).is_ok());
|
||||
}
|
||||
|
||||
pub(super) fn umount_sync_browsing_mkdir_popup(&mut self) {
|
||||
let _ = self.app.umount(&Id::SyncBrowsingMkdirPopup);
|
||||
}
|
||||
|
||||
/// Mount help
|
||||
pub(super) fn mount_help(&mut self) {
|
||||
let key_color = self.theme().misc_keys;
|
||||
@@ -949,9 +971,14 @@ impl FileTransferActivity {
|
||||
Box::new(SubClause::Not(Box::new(SubClause::IsMounted(
|
||||
Id::FindPopup,
|
||||
)))),
|
||||
Box::new(SubClause::Not(Box::new(SubClause::IsMounted(
|
||||
Id::WaitPopup,
|
||||
)))),
|
||||
Box::new(SubClause::And(
|
||||
Box::new(SubClause::Not(Box::new(SubClause::IsMounted(
|
||||
Id::SyncBrowsingMkdirPopup,
|
||||
)))),
|
||||
Box::new(SubClause::Not(Box::new(SubClause::IsMounted(
|
||||
Id::WaitPopup,
|
||||
)))),
|
||||
)),
|
||||
)),
|
||||
)),
|
||||
)),
|
||||
|
||||
Reference in New Issue
Block a user