diff --git a/crates/client/src/components/user_action_list.rs b/crates/client/src/components/user_action_list.rs index 3aa148cf6..38a23ec43 100644 --- a/crates/client/src/components/user_action_list.rs +++ b/crates/client/src/components/user_action_list.rs @@ -1,7 +1,10 @@ -use crate::components::icons::{self, ArrowTopRightOnSquare, BookOpen, ClipboardDocumentIcon}; +use crate::components::icons::{ArrowTopRightOnSquare, BookOpen, ClipboardDocumentIcon}; use crate::components::{KeyComponent, ModifierIcon}; use crate::{tauri_invoke, utils}; use gloo::utils::window; +use handlebars::{ + Context, Handlebars, Helper, HelperResult, Output, PathAndJson, RenderContext, RenderError, +}; use shared::accelerator; use shared::config::{self, UserAction, UserActionDefinition}; use shared::event::{ClientInvoke, CopyContext, OpenResultParams}; @@ -81,16 +84,11 @@ pub struct ActionIconProps { #[function_component(ActionIcon)] pub fn action_icon(props: &ActionIconProps) -> Html { match props.actiontype { - UserAction::OpenApplication(_, _) => { + UserAction::OpenApplication(_, _) | UserAction::OpenUrl(_) => { html! { } } - UserAction::OpenUrl(_) => { - html! { - - } - } UserAction::CopyToClipboard(_) => { html! { @@ -156,6 +154,7 @@ pub fn user_actions_list(props: &ActionsListProps) -> Html { pub async fn execute_action(selected: SearchResult, action: UserActionDefinition) { let template_input = SearchResultTemplate::from(selected); let mut reg = handlebars::Handlebars::new(); + reg.register_helper("slice_path", Box::new(slice_path)); reg.register_escape_fn(handlebars::no_escape); match action.action { @@ -257,3 +256,100 @@ pub fn action_button(props: &ActionListBtnProps) -> Html { } } + +// Helper used to take slices of a path +fn slice_path( + helper: &Helper, + _: &Handlebars, + _: &Context, + _rc: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + let path = helper.param(0); + let start = helper.param(1); + let end = helper.param(2); + let count = helper.hash_get("count"); + let full_uri = helper.hash_get("full_uri"); + + log::debug!( + "Path: {path:?} Start: {start:?} End: {end:?} Count: {count:?} Full URI: {full_uri:?}" + ); + if let (Some(path), Some(start)) = (path, start) { + let url = url::Url::parse(path.render().as_str()); + match url { + Ok(mut url) => { + let start_val = start.value(); + if let Some(start_i64) = start_val.as_i64() { + if let Some(segments) = url.path_segments().map(|c| c.collect::>()) { + let start = get_start(segments.len(), start_i64); + let start_usize = start as usize; + let end = get_end(segments.len(), start, end, count) as usize; + log::debug!("Start: {start:?} End: {end:?} Segments {segments:?}"); + if let Some(segment) = segments.get(start_usize..end) { + match full_uri.map(|uri| uri.value().as_bool().unwrap_or(false)) { + Some(true) => { + url.set_path(segment.join("/").as_str()); + out.write(url.as_str())?; + } + _ => { + out.write(segment.join("/").as_str())?; + } + } + } + } + } + } + Err(err) => { + log::error!("Invalid url {:?}", err); + return Err(RenderError::new("Path is an invalid url")); + } + } + } else { + return Err(RenderError::new("A path and start are required")); + } + Ok(()) +} + +// Helper method used to calculate the start of a range based on the size of the array and the +// start index. Note the index can be negative +fn get_start(size: usize, start: i64) -> u64 { + if start < 0 { + if let Some(added) = size.checked_add_signed(start as isize) { + return added.max(0).min(size) as u64; + } + } + start as u64 +} + +// Helper method used to get the end of a sequence from the start, size, end and count. The end and +// count are both optional. +fn get_end(size: usize, start: u64, end: Option<&PathAndJson>, count: Option<&PathAndJson>) -> u64 { + let size = size as i64; + let max_size = size as u64; + if let Some(end) = end { + let value = end.value(); + if value.is_i64() { + if let Some(val_i64) = value.as_i64() { + if val_i64 < 0 { + let end_size = (size + val_i64).max(0) as u64; + return end_size.min(max_size); + } else { + return val_i64 as u64; + } + } + } else if value.is_u64() { + return value.as_u64().unwrap(); + } + } + + if let Some(count) = count { + let count_val = count.value(); + if let Some(count_u64) = count_val.as_u64() { + return start + .checked_add(count_u64) + .unwrap_or(max_size) + .min(max_size); + } + } + max_size +} diff --git a/crates/shared/src/config.rs b/crates/shared/src/config.rs index 643d81ec3..405c0cc92 100644 --- a/crates/shared/src/config.rs +++ b/crates/shared/src/config.rs @@ -76,7 +76,6 @@ impl Limit { // Enum of actions the user can take when a document is selected #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Diff)] pub enum UserAction { - /// OpenApplication(application, args) OpenApplication(String, String), OpenUrl(String), CopyToClipboard(String),