Files
galaxy/app/src/env_vars/view/menus.rs
T

463 lines
15 KiB
Rust

use galaxy_core::context_flag::ContextFlag;
use galaxyui::keymap::Trigger;
use galaxyui::{SingletonEntity, ViewContext, ViewHandle};
use pathfinder_geometry::vector::Vector2F;
use super::env_var_collection::{EnvVarCollectionAction, EnvVarCollectionView, VariableRowIndex};
use crate::cloud_object::{CloudObject, GenericStringObjectFormat};
use crate::drive::drive_helpers::has_feature_gated_anonymous_user_reached_env_var_limit;
use crate::drive::export::ExportManager;
use crate::drive::CloudObjectTypeAndId;
use crate::env_vars::active_env_var_collection_data::TrashStatus;
use crate::external_secrets::SecretManager;
use crate::local_object_repository::LocalObjectRepository;
use crate::menu::{Event as MenuEvent, Menu, MenuItem, MenuItemFields};
use crate::pane_group::PaneEvent;
use crate::ui_components::icons::Icon;
use crate::util::bindings::{
keybinding_name_to_display_string, trigger_to_keystroke, CustomAction,
};
use crate::{AppContext, CloudModel};
const PANE_MENU_WIDTH: f32 = 200.;
pub struct Menus {
pub(super) secret_menu: ViewHandle<Menu<EnvVarCollectionAction>>,
pub(super) rendered_secret_menu: ViewHandle<Menu<EnvVarCollectionAction>>,
pub(super) rendered_command_menu: ViewHandle<Menu<EnvVarCollectionAction>>,
pub(super) pane_context_menu: ViewHandle<Menu<EnvVarCollectionAction>>,
}
impl EnvVarCollectionView {
pub(super) fn initialize_menus(ctx: &mut ViewContext<Self>) -> Menus {
let command_item = Self::item(
"Command",
EnvVarCollectionAction::DisplayCommandDialog,
None,
Some(Icon::Terminal),
);
let one_password_item = Self::item(
"1Password",
EnvVarCollectionAction::SelectSecretManager(SecretManager::OnePassword),
None,
Some(Icon::OnePassword),
);
let lastpass_item = Self::item(
"LastPass",
EnvVarCollectionAction::SelectSecretManager(SecretManager::LastPass),
None,
Some(Icon::LastPass),
);
let edit_item = Self::item(
"Edit",
EnvVarCollectionAction::EditCommand,
None,
Some(Icon::Terminal),
);
let clear_secret_item = Self::item(
"Clear secret",
EnvVarCollectionAction::ClearSecret,
None,
Some(Icon::Trash),
);
let separator = MenuItem::Separator;
let secret_menu = Self::menu(
vec![
command_item.clone(),
one_password_item.clone(),
lastpass_item.clone(),
],
None,
ctx,
);
let rendered_secret_menu = Self::menu(
vec![
command_item.clone(),
one_password_item.clone(),
lastpass_item.clone(),
separator.clone(),
clear_secret_item.clone(),
],
None,
ctx,
);
let rendered_command_menu = Self::menu(
vec![
edit_item,
one_password_item,
lastpass_item,
separator,
clear_secret_item,
],
None,
ctx,
);
ctx.subscribe_to_view(&secret_menu, |me, _, event, ctx| {
me.handle_secret_menu_event(event, ctx);
});
ctx.subscribe_to_view(&rendered_secret_menu, |me, _, event, ctx| {
me.handle_rendered_secret_menu_event(event, ctx);
});
ctx.subscribe_to_view(&rendered_command_menu, |me, _, event, ctx| {
me.handle_rendered_command_menu_event(event, ctx);
});
let pane_context_menu = Self::menu(Vec::new(), Some(PANE_MENU_WIDTH), ctx);
Menus {
secret_menu,
rendered_secret_menu,
rendered_command_menu,
pane_context_menu,
}
}
pub(super) fn initialize_pane_context_menu(
&self,
ctx: &mut ViewContext<Self>,
) -> ViewHandle<Menu<EnvVarCollectionAction>> {
let split_pane_right = Self::item(
"Split pane right",
EnvVarCollectionAction::EmitPaneEvent(PaneEvent::SplitRight(None)),
keybinding_name_to_display_string("pane_group:add_right", ctx),
None,
);
let split_pane_left = Self::item(
"Split pane left",
EnvVarCollectionAction::EmitPaneEvent(PaneEvent::SplitLeft(None)),
keybinding_name_to_display_string("pane_group:add_left", ctx),
None,
);
let split_pane_down = Self::item(
"Split pane down",
EnvVarCollectionAction::EmitPaneEvent(PaneEvent::SplitDown(None)),
keybinding_name_to_display_string("pane_group:add_down", ctx),
None,
);
let split_pane_up = Self::item(
"Split pane up",
EnvVarCollectionAction::EmitPaneEvent(PaneEvent::SplitUp(None)),
keybinding_name_to_display_string("pane_group:add_up", ctx),
None,
);
let is_maximized = self
.focus_handle
.as_ref()
.is_some_and(|handle| handle.split_pane_state(ctx).is_maximized());
let toggle_maximize_pane = Self::item(
if is_maximized {
"Minimize pane"
} else {
"Maximize pane"
},
EnvVarCollectionAction::EmitPaneEvent(PaneEvent::ToggleMaximized),
keybinding_name_to_display_string("pane_group:toggle_maximize_pane", ctx),
None,
);
let close_pane = Self::item(
"Close pane",
EnvVarCollectionAction::EmitPaneEvent(PaneEvent::Close),
trigger_to_keystroke(&Trigger::Custom(CustomAction::CloseCurrentSession.into()))
.map(|keystroke| keystroke.displayed()),
None,
);
let mut items = Vec::new();
if ContextFlag::CreateNewSession.is_enabled() {
items.extend(vec![
split_pane_right,
split_pane_left,
split_pane_down,
split_pane_up,
]);
}
if self
.focus_handle
.as_ref()
.is_some_and(|handle| handle.is_in_split_pane(ctx))
{
items.extend(vec![toggle_maximize_pane, close_pane]);
}
let pane_context_menu = Self::menu(items, Some(PANE_MENU_WIDTH), ctx);
ctx.subscribe_to_view(&pane_context_menu, |me, _, event, ctx| {
me.handle_pane_context_menu_event(event, ctx);
});
pane_context_menu
}
pub(super) fn display_secret_menu(&mut self, index: usize) {
let row = &mut self.variable_rows[index];
row.secret_menu_is_focused = true;
self.pending_variable_row_index = Some(VariableRowIndex(index));
}
pub(super) fn display_rendered_secret_menu(&mut self, index: usize) {
let row = &mut self.variable_rows[index];
row.rendered_secret_menu_is_focused = true;
self.pending_variable_row_index = Some(VariableRowIndex(index));
}
pub(super) fn display_rendered_command_menu(&mut self, index: usize) {
let row = &mut self.variable_rows[index];
row.rendered_command_menu_is_focused = true;
self.pending_variable_row_index = Some(VariableRowIndex(index));
}
pub(super) fn display_pane_context_menu(
&mut self,
position: &Vector2F,
ctx: &mut ViewContext<Self>,
) {
if let Some(parent_bounds) = ctx.element_position_by_id(self.view_position_id.clone()) {
self.menus.pane_context_menu = self.initialize_pane_context_menu(ctx);
let offset = *position - parent_bounds.origin();
self.pane_context_menu_offset = Some(offset);
ctx.notify();
}
}
fn handle_secret_menu_event(&mut self, event: &MenuEvent, ctx: &mut ViewContext<Self>) {
match event {
MenuEvent::Close { via_select_item: _ } => self.reset_secret_menu(ctx),
MenuEvent::ItemSelected => self.reset_secret_menu(ctx),
MenuEvent::ItemHovered => {}
}
}
fn handle_rendered_secret_menu_event(
&mut self,
event: &MenuEvent,
ctx: &mut ViewContext<Self>,
) {
match event {
MenuEvent::Close { via_select_item: _ } => self.reset_rendered_secret_menu(ctx),
MenuEvent::ItemSelected => self.reset_rendered_secret_menu(ctx),
MenuEvent::ItemHovered => {}
}
}
fn handle_rendered_command_menu_event(
&mut self,
event: &MenuEvent,
ctx: &mut ViewContext<Self>,
) {
match event {
MenuEvent::Close { via_select_item: _ } => self.reset_rendered_command_menu(ctx),
MenuEvent::ItemSelected => self.reset_rendered_command_menu(ctx),
MenuEvent::ItemHovered => {}
}
}
fn handle_pane_context_menu_event(&mut self, event: &MenuEvent, ctx: &mut ViewContext<Self>) {
match event {
MenuEvent::Close { via_select_item: _ } | MenuEvent::ItemSelected => {
self.pane_context_menu_offset = None;
self.menus
.pane_context_menu
.update(ctx, |menu, ctx| menu.reset_selection(ctx));
ctx.notify()
}
MenuEvent::ItemHovered => {}
}
}
fn reset_secret_menu(&mut self, ctx: &mut ViewContext<Self>) {
self.variable_rows.iter_mut().for_each(|row| {
row.secret_menu_is_focused = false;
});
self.menus
.secret_menu
.update(ctx, |menu, ctx| menu.reset_selection(ctx));
ctx.notify()
}
fn reset_rendered_secret_menu(&mut self, ctx: &mut ViewContext<Self>) {
self.variable_rows.iter_mut().for_each(|row| {
row.rendered_secret_menu_is_focused = false;
});
self.menus
.rendered_secret_menu
.update(ctx, |menu, ctx| menu.reset_selection(ctx));
ctx.notify()
}
fn reset_rendered_command_menu(&mut self, ctx: &mut ViewContext<Self>) {
self.variable_rows.iter_mut().for_each(|row| {
row.rendered_command_menu_is_focused = false;
});
self.menus
.rendered_command_menu
.update(ctx, |menu, ctx| menu.reset_selection(ctx));
ctx.notify()
}
fn menu(
items: Vec<MenuItem<EnvVarCollectionAction>>,
width: Option<f32>,
ctx: &mut ViewContext<Self>,
) -> ViewHandle<Menu<EnvVarCollectionAction>> {
ctx.add_typed_action_view(|_| {
let mut menu = Menu::new()
.prevent_interaction_with_other_elements()
.with_drop_shadow();
if let Some(width) = width {
menu = menu.with_width(width);
}
menu.add_items(items);
menu
})
}
fn item(
name: &str,
action: EnvVarCollectionAction,
key_shortcut: Option<String>,
icon: Option<Icon>,
) -> MenuItem<EnvVarCollectionAction> {
let mut field = MenuItemFields::new(name)
.with_on_select_action(action)
.with_key_shortcut_label(key_shortcut);
if let Some(icon) = icon {
field = field.with_icon(icon);
}
field.into_item()
}
// Used for duplicate, copy link, trash etc
pub(super) fn overflow_menu_items(
&self,
ctx: &AppContext,
) -> Vec<MenuItem<EnvVarCollectionAction>> {
let mut menu_items = Vec::new();
let active_collection_data = self.active_env_var_collection_data.as_ref(ctx);
if active_collection_data.id().is_none()
|| active_collection_data.trash_status(ctx) != TrashStatus::Active
{
return menu_items;
}
// Add "Copy Link" to menu
if let Some(link) = self.env_var_collection_link(ctx) {
menu_items.push(
MenuItemFields::new("Copy link")
.with_on_select_action(EnvVarCollectionAction::CopyLink(link))
.with_icon(Icon::Link)
.into_item(),
);
}
// Add "Duplicate" to menu
menu_items.push(
MenuItemFields::new("Duplicate")
.with_on_select_action(EnvVarCollectionAction::Duplicate)
.with_icon(Icon::Duplicate)
.into_item(),
);
// Add "Trash" to menu
menu_items.push(
MenuItemFields::new("Trash")
.with_on_select_action(EnvVarCollectionAction::Trash)
.with_icon(Icon::Trash)
.into_item(),
);
#[cfg(feature = "local_fs")]
menu_items.push(
MenuItemFields::new("Export")
.with_on_select_action(EnvVarCollectionAction::Export)
.with_icon(Icon::Download)
.into_item(),
);
menu_items
}
pub(super) fn env_var_collection_link(&self, ctx: &AppContext) -> Option<String> {
self.env_var_collection_id(ctx)
.and_then(|id| CloudModel::as_ref(ctx).get_env_var_collection(&id))
.map(|env_var_collection| env_var_collection.object_link())?
}
pub(super) fn untrash_env_var_collection(&self, ctx: &mut ViewContext<Self>) {
if let Some(env_var_collection_id) = self.active_env_var_collection_data.as_ref(ctx).id() {
if has_feature_gated_anonymous_user_reached_env_var_limit(ctx) {
return;
}
LocalObjectRepository::handle(ctx).update(ctx, |repository, ctx| {
repository.set_env_var_collection_trashed(env_var_collection_id, false, ctx);
});
}
ctx.notify();
}
pub(super) fn trash_env_var_collection(&mut self, ctx: &mut ViewContext<Self>) {
if let Some(env_var_collection_id) = self.env_var_collection_id(ctx) {
self.close_env_var_collection(ctx);
LocalObjectRepository::handle(ctx).update(ctx, |repository, ctx| {
repository.set_env_var_collection_trashed(env_var_collection_id, true, ctx);
});
ctx.notify();
}
}
pub(super) fn duplicate_env_var_collection(&self, ctx: &mut ViewContext<Self>) {
if let Some(env_var_collection_id) = self.env_var_collection_id(ctx) {
LocalObjectRepository::handle(ctx).update(ctx, |repository, ctx| {
repository.duplicate_env_var_collection(env_var_collection_id, ctx);
});
ctx.notify();
}
}
pub(super) fn export_env_var_collection(&self, ctx: &mut ViewContext<Self>) {
if let Some(env_var_collection_id) = self.env_var_collection_id(ctx) {
let window_id = ctx.window_id();
ExportManager::handle(ctx).update(ctx, |export_manager, ctx| {
export_manager.export(
window_id,
&[CloudObjectTypeAndId::from_generic_string_object(
GenericStringObjectFormat::Json(
crate::cloud_object::JsonObjectType::EnvVarCollection,
),
env_var_collection_id,
)],
ctx,
)
});
}
}
}