diff --git a/src/ui/activities/auth/bookmarks.rs b/src/ui/activities/auth/bookmarks.rs index 2549903..9b575c1 100644 --- a/src/ui/activities/auth/bookmarks.rs +++ b/src/ui/activities/auth/bookmarks.rs @@ -228,11 +228,12 @@ impl AuthActivity { fn load_bookmark_s3_into_gui(&mut self, params: AwsS3Params) { self.mount_s3_bucket(params.bucket_name.as_str()); self.mount_s3_region(params.region.as_deref().unwrap_or("")); + self.mount_s3_endpoint(params.endpoint.as_deref().unwrap_or("")); self.mount_s3_profile(params.profile.as_deref().unwrap_or("")); self.mount_s3_access_key(params.access_key.as_deref().unwrap_or("")); self.mount_s3_secret_access_key(params.secret_access_key.as_deref().unwrap_or("")); self.mount_s3_security_token(params.security_token.as_deref().unwrap_or("")); self.mount_s3_session_token(params.session_token.as_deref().unwrap_or("")); - // TODO: add mount + self.mount_s3_new_path_style(params.new_path_style); } } diff --git a/src/ui/activities/auth/components/form.rs b/src/ui/activities/auth/components/form.rs index 4a0ac15..a0dd264 100644 --- a/src/ui/activities/auth/components/form.rs +++ b/src/ui/activities/auth/components/form.rs @@ -328,6 +328,102 @@ impl Component for InputS3Region { } } +// -- s3 endpoint + +#[derive(MockComponent)] +pub struct InputS3Endpoint { + component: Input, +} + +impl InputS3Endpoint { + pub fn new(endpoint: &str, color: Color) -> Self { + Self { + component: Input::default() + .borders( + Borders::default() + .color(color) + .modifiers(BorderType::Rounded), + ) + .foreground(color) + .placeholder( + "http://localhost:9000", + Style::default().fg(Color::Rgb(128, 128, 128)), + ) + .title("Endpoint", Alignment::Left) + .input_type(InputType::Text) + .value(endpoint), + } + } +} + +impl Component for InputS3Endpoint { + fn on(&mut self, ev: Event) -> Option { + handle_input_ev( + self, + ev, + Msg::Ui(UiMsg::S3EndpointBlurDown), + Msg::Ui(UiMsg::S3EndpointBlurUp), + ) + } +} + +// -- s3 new path style + +#[derive(MockComponent)] +pub struct RadioS3NewPathStyle { + component: Radio, +} + +impl RadioS3NewPathStyle { + pub fn new(new_path_style: bool, color: Color) -> Self { + Self { + component: Radio::default() + .borders( + Borders::default() + .color(color) + .modifiers(BorderType::Rounded), + ) + .choices(&["Yes", "No"]) + .foreground(color) + .rewind(true) + .title("New path style", Alignment::Left) + .value(if new_path_style { 0 } else { 1 }), + } + } +} + +impl Component for RadioS3NewPathStyle { + fn on(&mut self, ev: Event) -> Option { + 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::Enter, .. + }) => Some(Msg::Form(FormMsg::Connect)), + Event::Keyboard(KeyEvent { + code: Key::Down, .. + }) => Some(Msg::Ui(UiMsg::S3NewPathStyleBlurDown)), + Event::Keyboard(KeyEvent { code: Key::Up, .. }) => { + Some(Msg::Ui(UiMsg::S3NewPathStyleBlurUp)) + } + Event::Keyboard(KeyEvent { code: Key::Tab, .. }) => { + Some(Msg::Ui(UiMsg::ParamsFormBlur)) + } + _ => None, + } + } +} + // -- s3 profile #[derive(MockComponent)] diff --git a/src/ui/activities/auth/components/mod.rs b/src/ui/activities/auth/components/mod.rs index d42bf60..2c509ed 100644 --- a/src/ui/activities/auth/components/mod.rs +++ b/src/ui/activities/auth/components/mod.rs @@ -37,9 +37,9 @@ pub use bookmarks::{ RecentsList, }; pub use form::{ - InputAddress, InputPassword, InputPort, InputS3AccessKey, InputS3Bucket, InputS3Profile, - InputS3Region, InputS3SecretAccessKey, InputS3SecurityToken, InputS3SessionToken, - InputUsername, ProtocolRadio, + InputAddress, InputPassword, InputPort, InputS3AccessKey, InputS3Bucket, InputS3Endpoint, + InputS3Profile, InputS3Region, InputS3SecretAccessKey, InputS3SecurityToken, + InputS3SessionToken, InputUsername, ProtocolRadio, RadioS3NewPathStyle, }; pub use popup::{ ErrorPopup, InfoPopup, InstallUpdatePopup, Keybindings, QuitPopup, ReleaseNotes, WaitPopup, diff --git a/src/ui/activities/auth/mod.rs b/src/ui/activities/auth/mod.rs index 7d2ea8a..490ffce 100644 --- a/src/ui/activities/auth/mod.rs +++ b/src/ui/activities/auth/mod.rs @@ -68,6 +68,8 @@ pub enum Id { RecentsList, S3AccessKey, S3Bucket, + S3Endpoint, + S3NewPathStyle, S3Profile, S3Region, S3SecretAccessKey, @@ -127,6 +129,10 @@ pub enum UiMsg { S3AccessKeyBlurUp, S3BucketBlurDown, S3BucketBlurUp, + S3EndpointBlurDown, + S3EndpointBlurUp, + S3NewPathStyleBlurDown, + S3NewPathStyleBlurUp, S3ProfileBlurDown, S3ProfileBlurUp, S3RegionBlurDown, diff --git a/src/ui/activities/auth/update.rs b/src/ui/activities/auth/update.rs index 76124c2..1e7e2c7 100644 --- a/src/ui/activities/auth/update.rs +++ b/src/ui/activities/auth/update.rs @@ -203,7 +203,7 @@ impl AuthActivity { .app .active(match self.input_mask() { InputMask::Generic => &Id::Password, - InputMask::AwsS3 => &Id::S3SessionToken, + InputMask::AwsS3 => &Id::S3NewPathStyle, }) .is_ok()); } @@ -217,16 +217,22 @@ impl AuthActivity { assert!(self.app.active(&Id::Protocol).is_ok()); } UiMsg::S3RegionBlurDown => { - assert!(self.app.active(&Id::S3Profile).is_ok()); + assert!(self.app.active(&Id::S3Endpoint).is_ok()); } UiMsg::S3RegionBlurUp => { assert!(self.app.active(&Id::S3Bucket).is_ok()); } + UiMsg::S3EndpointBlurDown => { + assert!(self.app.active(&Id::S3Profile).is_ok()); + } + UiMsg::S3EndpointBlurUp => { + assert!(self.app.active(&Id::S3Region).is_ok()); + } UiMsg::S3ProfileBlurDown => { assert!(self.app.active(&Id::S3AccessKey).is_ok()); } UiMsg::S3ProfileBlurUp => { - assert!(self.app.active(&Id::S3Region).is_ok()); + assert!(self.app.active(&Id::S3Endpoint).is_ok()); } UiMsg::S3AccessKeyBlurDown => { assert!(self.app.active(&Id::S3SecretAccessKey).is_ok()); @@ -247,11 +253,17 @@ impl AuthActivity { assert!(self.app.active(&Id::S3SecretAccessKey).is_ok()); } UiMsg::S3SessionTokenBlurDown => { - assert!(self.app.active(&Id::Protocol).is_ok()); + assert!(self.app.active(&Id::S3NewPathStyle).is_ok()); } UiMsg::S3SessionTokenBlurUp => { assert!(self.app.active(&Id::S3SecurityToken).is_ok()); } + UiMsg::S3NewPathStyleBlurDown => { + assert!(self.app.active(&Id::Protocol).is_ok()); + } + UiMsg::S3NewPathStyleBlurUp => { + assert!(self.app.active(&Id::S3SessionToken).is_ok()); + } UiMsg::SaveBookmarkPasswordBlur => { assert!(self.app.active(&Id::BookmarkName).is_ok()); } diff --git a/src/ui/activities/auth/view.rs b/src/ui/activities/auth/view.rs index 368b883..7ef7ee1 100644 --- a/src/ui/activities/auth/view.rs +++ b/src/ui/activities/auth/view.rs @@ -74,10 +74,12 @@ impl AuthActivity { self.mount_s3_bucket(""); self.mount_s3_profile(""); self.mount_s3_region(""); + self.mount_s3_endpoint(""); self.mount_s3_access_key(""); self.mount_s3_secret_access_key(""); self.mount_s3_security_token(""); self.mount_s3_session_token(""); + self.mount_s3_new_path_style(false); // Version notice if let Some(version) = self .context() @@ -648,61 +650,85 @@ impl AuthActivity { .is_ok()); } - pub(crate) fn mount_s3_profile(&mut self, profile: &str) { + pub(crate) fn mount_s3_endpoint(&mut self, endpoint: &str) { let username_color = self.theme().auth_username; + assert!(self + .app + .remount( + Id::S3Endpoint, + Box::new(components::InputS3Endpoint::new(endpoint, username_color)), + vec![] + ) + .is_ok()); + } + + pub(crate) fn mount_s3_profile(&mut self, profile: &str) { + let color = self.theme().auth_password; assert!(self .app .remount( Id::S3Profile, - Box::new(components::InputS3Profile::new(profile, username_color)), + Box::new(components::InputS3Profile::new(profile, color)), vec![] ) .is_ok()); } pub(crate) fn mount_s3_access_key(&mut self, key: &str) { - let password_color = self.theme().auth_password; + let color = self.theme().auth_address; assert!(self .app .remount( Id::S3AccessKey, - Box::new(components::InputS3AccessKey::new(key, password_color)), + Box::new(components::InputS3AccessKey::new(key, color)), vec![] ) .is_ok()); } pub(crate) fn mount_s3_secret_access_key(&mut self, key: &str) { - let addr_color = self.theme().auth_address; + let color = self.theme().auth_port; assert!(self .app .remount( Id::S3SecretAccessKey, - Box::new(components::InputS3SecretAccessKey::new(key, addr_color)), + Box::new(components::InputS3SecretAccessKey::new(key, color)), vec![] ) .is_ok()); } pub(crate) fn mount_s3_security_token(&mut self, token: &str) { - let port_color = self.theme().auth_port; + let color = self.theme().auth_username; assert!(self .app .remount( Id::S3SecurityToken, - Box::new(components::InputS3SecurityToken::new(token, port_color)), + Box::new(components::InputS3SecurityToken::new(token, color)), vec![] ) .is_ok()); } pub(crate) fn mount_s3_session_token(&mut self, token: &str) { - let username_color = self.theme().auth_username; + let color = self.theme().auth_password; assert!(self .app .remount( Id::S3SessionToken, - Box::new(components::InputS3SessionToken::new(token, username_color)), + Box::new(components::InputS3SessionToken::new(token, color)), + vec![] + ) + .is_ok()); + } + + pub(crate) fn mount_s3_new_path_style(&mut self, new_path_style: bool) { + let color = self.theme().auth_address; + assert!(self + .app + .remount( + Id::S3NewPathStyle, + Box::new(components::RadioS3NewPathStyle::new(new_path_style, color)), vec![] ) .is_ok()); @@ -727,17 +753,20 @@ impl AuthActivity { pub(super) fn get_s3_params_input(&self) -> AwsS3Params { let bucket: String = self.get_input_s3_bucket(); let region: Option = self.get_input_s3_region(); + let endpoint = self.get_input_s3_endpoint(); let profile: Option = self.get_input_s3_profile(); let access_key = self.get_input_s3_access_key(); let secret_access_key = self.get_input_s3_secret_access_key(); let security_token = self.get_input_s3_security_token(); let session_token = self.get_input_s3_session_token(); - // TODO: collect + let new_path_style = self.get_input_s3_new_path_style(); AwsS3Params::new(bucket, region, profile) + .endpoint(endpoint) .access_key(access_key) .secret_access_key(secret_access_key) .security_token(security_token) .session_token(session_token) + .new_path_style(new_path_style) } pub(super) fn get_input_addr(&self) -> String { @@ -785,6 +814,13 @@ impl AuthActivity { } } + pub(super) fn get_input_s3_endpoint(&self) -> Option { + match self.app.state(&Id::S3Endpoint) { + Ok(State::One(StateValue::String(x))) if !x.is_empty() => Some(x), + _ => None, + } + } + pub(super) fn get_input_s3_profile(&self) -> Option { match self.app.state(&Id::S3Profile) { Ok(State::One(StateValue::String(x))) if !x.is_empty() => Some(x), @@ -820,6 +856,13 @@ impl AuthActivity { } } + pub(super) fn get_input_s3_new_path_style(&self) -> bool { + matches!( + self.app.state(&Id::S3NewPathStyle), + Ok(State::One(StateValue::Usize(0))) + ) + } + /// Get new bookmark params pub(super) fn get_new_bookmark(&self) -> (String, bool) { let name = match self.app.state(&Id::BookmarkName) { @@ -888,13 +931,24 @@ impl AuthActivity { /// Get the visible element in the aws-s3 form, based on current focus fn get_s3_view(&self) -> [Id; 4] { match self.app.focus() { - Some(&Id::S3SecretAccessKey | &Id::S3SecurityToken | &Id::S3SessionToken) => [ + Some(&Id::S3AccessKey) => [ Id::S3AccessKey, Id::S3SecretAccessKey, Id::S3SecurityToken, Id::S3SessionToken, ], - _ => [Id::S3Bucket, Id::S3Region, Id::S3Profile, Id::S3AccessKey], + Some( + &Id::S3SecretAccessKey + | &Id::S3SecurityToken + | &Id::S3SessionToken + | &Id::S3NewPathStyle, + ) => [ + Id::S3SecretAccessKey, + Id::S3SecurityToken, + Id::S3SessionToken, + Id::S3NewPathStyle, + ], + _ => [Id::S3Bucket, Id::S3Region, Id::S3Endpoint, Id::S3Profile], } }