From 6634c9aef04f60a3b560f802743bd01b9fee04b6 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Tue, 3 Feb 2026 15:20:32 -0500 Subject: [PATCH] feat(hosted-files): introduce direct download mode and automate filename shortening - Add 'show_direct_download' prop to support native browser downloads via V3 Action paths (/v3/action/...). - Integrate 'shorten_filename' logic directly into the component via 'max_filename' prop. - Refactor internal UI to snippets for consistent rendering across button and anchor modes. - Update 'ae_HostedFile' type with new SQL view fields: 'filename_no_ext' and 'filename_w_ext'. - Add direct download toggle and edit mode indicator to testing dashboard. - Simplify 'ae_comp__event_file_obj_tbl' by removing manual snippets in favor of standardized component logic. --- ..._comp__hosted_files_download_button.svelte | 227 ++++++++++-------- src/lib/types/ae_types.ts | 2 + .../events/ae_comp__event_file_obj_tbl.svelte | 21 +- src/routes/testing/hosted_files/+page.svelte | 37 ++- 4 files changed, 168 insertions(+), 119 deletions(-) diff --git a/src/lib/ae_core/ae_comp__hosted_files_download_button.svelte b/src/lib/ae_core/ae_comp__hosted_files_download_button.svelte index 7b12470d..9449026b 100644 --- a/src/lib/ae_core/ae_comp__hosted_files_download_button.svelte +++ b/src/lib/ae_core/ae_comp__hosted_files_download_button.svelte @@ -18,7 +18,7 @@ hosted_file_id: null | string; hosted_file_obj: null | key_val; filename?: null | string; - max_length?: number; + max_filename?: number; auto_download?: boolean; linked_to_type?: null | string; linked_to_id?: null | string; @@ -28,16 +28,17 @@ variant?: 'tonal' | 'filled' | 'outline' | 'ghost'; color?: 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error' | 'surface'; show_divider?: boolean; + show_direct_download?: boolean; classes?: string; label?: import('svelte').Snippet; } - let { + let { log_lvl = 0, hosted_file_id, hosted_file_obj, filename = $bindable(null), - max_length = $bindable(30), + max_filename = $bindable(30), auto_download = true, linked_to_type = $bindable(null), linked_to_id = $bindable(null), @@ -47,6 +48,7 @@ variant = 'tonal', color = 'primary', show_divider = true, + show_direct_download = false, classes = '', label }: Props = $props(); @@ -98,7 +100,7 @@ }; let variant_classes = $derived.by(() => { - const base = 'btn btn-sm lg:btn-md min-w-48 transition-all overflow-hidden'; + const base = 'btn btn-sm lg:btn-md min-w-48 transition-all overflow-hidden px-3'; const style = color_map[color]?.[variant] || color_map.primary.tonal; return `${base} ${style} ${classes}`.trim(); }); @@ -151,109 +153,134 @@ } }; }); + + let final_filename = $derived(filename ?? hosted_file_obj?.filename ?? 'unknown'); + let shortened_filename = $derived(ae_util.shorten_filename({ + filename: final_filename, + max_length: max_filename + })); + + let direct_download_url = $derived.by(() => { + if (!show_direct_download || !hosted_file_obj) return ''; + // IMPORTANT: For Direct Link Mode, we MUST use the V3 Action endpoint to support Random String IDs. + // Legacy endpoints often expect integer IDs and will return 404 for string IDs. + const file_id = hosted_file_obj.event_file_id || hosted_file_obj.hosted_file_id || hosted_file_id; + const obj_type_path = hosted_file_obj.event_file_id ? 'event_file' : 'hosted_file'; + return `${$ae_api.base_url}/v3/action/${obj_type_path}/${file_id}/download?filename=${ae_util.clean_filename(final_filename)}&x_no_account_id_token=direct-download`; + }); -{#if hosted_file_id && hosted_file_obj} - {@const file_id = hosted_file_obj.id || hosted_file_obj.hosted_file_id || hosted_file_id} - + {/await} + + {#if download_complete === null} + File not found + {:else if download_complete === false} + Failed! + {/if} +{/snippet} + +{#if hosted_file_id && hosted_file_obj} + {@const file_id = hosted_file_obj.id || hosted_file_obj.hosted_file_id || hosted_file_id} + + {#if show_direct_download} + + {@render content()} + + {:else} + + {/if} {:else} -{/if} +{/if} \ No newline at end of file diff --git a/src/lib/types/ae_types.ts b/src/lib/types/ae_types.ts index d9aa7707..d00dced9 100644 --- a/src/lib/types/ae_types.ts +++ b/src/lib/types/ae_types.ts @@ -674,6 +674,8 @@ export interface ae_HostedFile extends ae_BaseObj { hash_sha256?: string; subdirectory_path?: string; filename?: string; + filename_no_ext?: string; + filename_w_ext?: string; extension?: string; mimetype?: string; size?: number; diff --git a/src/routes/events/ae_comp__event_file_obj_tbl.svelte b/src/routes/events/ae_comp__event_file_obj_tbl.svelte index 720d1263..a000d08a 100644 --- a/src/routes/events/ae_comp__event_file_obj_tbl.svelte +++ b/src/routes/events/ae_comp__event_file_obj_tbl.svelte @@ -375,24 +375,11 @@ - {#snippet label()} - - {ae_util.shorten_filename({ - filename: event_file_obj?.filename, - max_length: 30 - })} - - - - {event_file_obj?.file_purpose} - - {/snippet} - + />
-
+

Hosted Files Testing

Testing the AE_Comp_Hosted_Files_Download_Button component

+ +
+
+ Edit Mode + + {$ae_loc.edit_mode ? 'ON' : 'OFF'} + +
+ +
+ Direct Download + +
+
@@ -79,7 +101,12 @@

Large File Progress Test

- Testing Percentage +
+ Testing Percentage + {#if test_direct_download} + Direct Link + {/if} +

@@ -103,6 +130,7 @@