gitui 0.22.1

blazing fast terminal-ui for git
use anyhow::Result;
use crossterm::event::Event;
use tui::{backend::Backend, layout::Rect, Frame};

use asyncgit::sync::cred::BasicAuthCredential;

use crate::components::{EventState, InputType, TextInputComponent};
use crate::keys::key_match;
use crate::{
	components::{
		visibility_blocking, CommandBlocking, CommandInfo, Component,
		DrawableComponent,
	},
	keys::SharedKeyConfig,
	strings,
	ui::style::SharedTheme,
};

///
pub struct CredComponent {
	visible: bool,
	key_config: SharedKeyConfig,
	input_username: TextInputComponent,
	input_password: TextInputComponent,
	cred: BasicAuthCredential,
}

impl CredComponent {
	///
	pub fn new(
		theme: SharedTheme,
		key_config: SharedKeyConfig,
	) -> Self {
		Self {
			visible: false,
			input_username: TextInputComponent::new(
				theme.clone(),
				key_config.clone(),
				&strings::username_popup_title(&key_config),
				&strings::username_popup_msg(&key_config),
				false,
			)
			.with_input_type(InputType::Singleline),
			input_password: TextInputComponent::new(
				theme,
				key_config.clone(),
				&strings::password_popup_title(&key_config),
				&strings::password_popup_msg(&key_config),
				false,
			)
			.with_input_type(InputType::Password),
			key_config,
			cred: BasicAuthCredential::new(None, None),
		}
	}

	pub fn set_cred(&mut self, cred: BasicAuthCredential) {
		self.cred = cred;
	}

	pub const fn get_cred(&self) -> &BasicAuthCredential {
		&self.cred
	}
}

impl DrawableComponent for CredComponent {
	fn draw<B: Backend>(
		&self,
		f: &mut Frame<B>,
		rect: Rect,
	) -> Result<()> {
		if self.visible {
			self.input_username.draw(f, rect)?;
			self.input_password.draw(f, rect)?;
		}
		Ok(())
	}
}

impl Component for CredComponent {
	fn commands(
		&self,
		out: &mut Vec<CommandInfo>,
		force_all: bool,
	) -> CommandBlocking {
		if self.is_visible() || force_all {
			if !force_all {
				out.clear();
			}

			out.push(CommandInfo::new(
				strings::commands::validate_msg(&self.key_config),
				true,
				true,
			));
			out.push(CommandInfo::new(
				strings::commands::close_popup(&self.key_config),
				true,
				true,
			));
		}

		visibility_blocking(self)
	}

	fn event(&mut self, ev: &Event) -> Result<EventState> {
		if self.visible {
			if let Event::Key(e) = ev {
				if key_match(e, self.key_config.keys.exit_popup) {
					self.hide();
					return Ok(EventState::Consumed);
				}
				if self.input_username.event(ev)?.is_consumed()
					|| self.input_password.event(ev)?.is_consumed()
				{
					return Ok(EventState::Consumed);
				} else if key_match(e, self.key_config.keys.enter) {
					if self.input_username.is_visible() {
						self.cred = BasicAuthCredential::new(
							Some(
								self.input_username
									.get_text()
									.to_string(),
							),
							None,
						);
						self.input_username.hide();
						self.input_password.show()?;
					} else if self.input_password.is_visible() {
						self.cred = BasicAuthCredential::new(
							self.cred.username.clone(),
							Some(
								self.input_password
									.get_text()
									.to_string(),
							),
						);
						self.input_password.hide();
						self.input_password.clear();
						return Ok(EventState::NotConsumed);
					} else {
						self.hide();
					}
				}
			}
			return Ok(EventState::Consumed);
		}
		Ok(EventState::NotConsumed)
	}

	fn is_visible(&self) -> bool {
		self.visible
	}

	fn hide(&mut self) {
		self.cred = BasicAuthCredential::new(None, None);
		self.visible = false;
	}

	fn show(&mut self) -> Result<()> {
		self.visible = true;
		if self.cred.username.is_none() {
			self.input_username.show()
		} else if self.cred.password.is_none() {
			self.input_password.show()
		} else {
			Ok(())
		}
	}
}