feat(hosted-files): finalize download button UI with fixed spinner, divider, and alternating status view
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
// *** Import Svelte specific
|
||||
import * as Lucide from 'lucide-svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
// *** Import Aether specific variables and functions
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
@@ -26,6 +27,7 @@
|
||||
download_status_msg?: string;
|
||||
variant?: 'tonal' | 'filled' | 'outline' | 'ghost';
|
||||
color?: 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error' | 'surface';
|
||||
show_divider?: boolean;
|
||||
classes?: string;
|
||||
label?: import('svelte').Snippet;
|
||||
}
|
||||
@@ -44,6 +46,7 @@
|
||||
download_status_msg = $bindable('Not started'),
|
||||
variant = 'tonal',
|
||||
color = 'primary',
|
||||
show_divider = true,
|
||||
classes = '',
|
||||
label
|
||||
}: Props = $props();
|
||||
@@ -95,11 +98,14 @@
|
||||
};
|
||||
|
||||
let variant_classes = $derived.by(() => {
|
||||
const base = 'btn btn-sm lg:btn-md min-w-48 transition-all';
|
||||
const base = 'btn btn-sm lg:btn-md min-w-48 transition-all overflow-hidden';
|
||||
const style = color_map[color]?.[variant] || color_map.primary.tonal;
|
||||
return `${base} ${style} ${classes}`.trim();
|
||||
});
|
||||
|
||||
let show_filename_view = $state(true);
|
||||
let status_interval: any;
|
||||
|
||||
$effect(() => {
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
@@ -118,6 +124,33 @@
|
||||
$ae_sess.api_download_kv[file_id].percent_completed;
|
||||
}
|
||||
});
|
||||
|
||||
// Reactive timer to alternate views during active download
|
||||
$effect(() => {
|
||||
const file_id = hosted_file_obj?.id || hosted_file_obj?.hosted_file_id || hosted_file_id;
|
||||
const is_actively_downloading = ae_promises[file_id] && download_complete === undefined;
|
||||
|
||||
if (is_actively_downloading) {
|
||||
if (!status_interval) {
|
||||
status_interval = setInterval(() => {
|
||||
show_filename_view = !show_filename_view;
|
||||
}, 3000);
|
||||
}
|
||||
} else {
|
||||
if (status_interval) {
|
||||
clearInterval(status_interval);
|
||||
status_interval = null;
|
||||
}
|
||||
show_filename_view = true; // Default view when not downloading
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (status_interval) {
|
||||
clearInterval(status_interval);
|
||||
status_interval = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if hosted_file_id && hosted_file_obj}
|
||||
@@ -159,39 +192,77 @@
|
||||
title={`Download this file:\n${filename ?? hosted_file_obj?.filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${file_id}\n Linked to: ${linked_to_type} ID: ${linked_to_id}`}
|
||||
>
|
||||
{#await ae_promises[file_id]}
|
||||
<Lucide.Loader2 class="animate-spin mr-2" size={18} />
|
||||
<span class="">
|
||||
Downloading
|
||||
{#if $ae_sess.api_download_kv[file_id]}
|
||||
{$ae_sess.api_download_kv[file_id]
|
||||
.percent_completed}%
|
||||
{/if}
|
||||
:
|
||||
</span>
|
||||
<div class="flex items-center w-full min-h-[1.5rem]">
|
||||
<div
|
||||
class="flex items-center pr-2 shrink-0 {show_divider ? 'border-r border-surface-500/30 mr-2' : ''}"
|
||||
>
|
||||
<Lucide.Loader2 class="animate-spin" size={18} />
|
||||
</div>
|
||||
<div class="grow relative text-left h-full">
|
||||
{#if show_filename_view}
|
||||
<div in:fade={{ duration: 250 }} out:fade={{ duration: 250 }} class="flex items-center h-full">
|
||||
<span class="truncate">
|
||||
{ae_util.shorten_filename({
|
||||
filename: filename ?? hosted_file_obj?.filename,
|
||||
max_length: max_length
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
{:else}
|
||||
<div in:fade={{ duration: 250 }} out:fade={{ duration: 250 }} class="absolute inset-0 flex items-center h-full">
|
||||
<span class="font-bold whitespace-nowrap">
|
||||
Downloading:
|
||||
{#if $ae_sess.api_download_kv[file_id]}
|
||||
{$ae_sess.api_download_kv[file_id].percent_completed}%
|
||||
{:else}
|
||||
...
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:then}
|
||||
{#if label}
|
||||
{@render label()}
|
||||
{:else}
|
||||
{@const IconComp = ae_util.file_extension_icon_lucide(hosted_file_obj?.extension)}
|
||||
<IconComp size={18} class="mr-2" />
|
||||
<span class="grow">
|
||||
{ae_util.shorten_filename({
|
||||
filename: filename ?? hosted_file_obj?.filename,
|
||||
max_length: max_length
|
||||
})}
|
||||
</span>
|
||||
<div class="flex items-center w-full">
|
||||
<div
|
||||
class="flex items-center pr-2 shrink-0 {show_divider ? 'border-r border-surface-500/30 mr-2' : ''}"
|
||||
>
|
||||
<IconComp size={18} />
|
||||
</div>
|
||||
<span class="grow truncate text-left">
|
||||
{ae_util.shorten_filename({
|
||||
filename: filename ?? hosted_file_obj?.filename,
|
||||
max_length: max_length
|
||||
})}
|
||||
</span>
|
||||
{#if hosted_file_obj?.file_purpose || hosted_file_obj?.group}
|
||||
<span class="badge preset-tonal-success ml-2 text-[10px] uppercase font-bold shrink-0">
|
||||
{hosted_file_obj.file_purpose || hosted_file_obj.group}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
|
||||
{#if download_complete === null}
|
||||
<span class="text-red-800 dark:text-red-200 ml-2">File not found</span>
|
||||
<span class="text-red-800 dark:text-red-200 ml-2 whitespace-nowrap">File not found</span>
|
||||
{:else if download_complete === false}
|
||||
<span class="text-red-800 dark:text-red-200 ml-2">Failed to download!</span>
|
||||
<span class="text-red-800 dark:text-red-200 ml-2 whitespace-nowrap text-xs">Failed!</span>
|
||||
{/if}
|
||||
</button>
|
||||
{:else}
|
||||
<button type="button" disabled class={classes ?? 'btn'} title="No file selected">
|
||||
<Lucide.FileX size={18} class="mr-2" />
|
||||
<span class="grow"> No file info </span>
|
||||
<button type="button" disabled class={variant_classes} title="No file selected">
|
||||
<div class="flex items-center w-full">
|
||||
<div
|
||||
class="flex items-center pr-2 shrink-0 {show_divider ? 'border-r border-surface-500/30 mr-2' : ''}"
|
||||
>
|
||||
<Lucide.FileX size={18} />
|
||||
</div>
|
||||
<span class="grow text-left"> No file info </span>
|
||||
</div>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user