chore(control): add progress_bar in InodeElement

This commit is contained in:
E44
2026-01-04 23:25:19 +01:00
parent fa42feb808
commit 2367dc0c9e
@@ -6,7 +6,12 @@
get_shifted_color get_shifted_color
} from '$lib/ts/stores/ui_behavior'; } from '$lib/ts/stores/ui_behavior';
import Button from '$lib/components/Button.svelte'; import Button from '$lib/components/Button.svelte';
import { supported_file_type_icon, type Inode, get_file_primary_key } from '$lib/ts/types'; import {
supported_file_type_icon,
type Inode,
get_file_primary_key,
type FileOnDisplay
} from '$lib/ts/types';
import { import {
is_selected, is_selected,
@@ -26,6 +31,7 @@
import { run_on_all_selected_displays } from '$lib/ts/stores/displays'; import { run_on_all_selected_displays } from '$lib/ts/stores/displays';
import { get_thumbnail_url } from '$lib/ts/stores/thumbnails'; import { get_thumbnail_url } from '$lib/ts/stores/thumbnails';
import { liveQuery, type Observable } from 'dexie'; import { liveQuery, type Observable } from 'dexie';
import { db } from '$lib/ts/files_display.db';
let { file, not_interactable = false }: { file: Inode; not_interactable?: boolean } = $props(); let { file, not_interactable = false }: { file: Inode; not_interactable?: boolean } = $props();
@@ -37,8 +43,19 @@
missing_colliding_displays_ids = liveQuery(() => get_missing_colliding_display_ids(file, s)); missing_colliding_displays_ids = liveQuery(() => get_missing_colliding_display_ids(file, s));
}); });
let thumbnail_url = liveQuery(() => get_thumbnail_url(get_file_primary_key(file))); let loading_data:
| Observable<{
is_loading: boolean;
total_percentage: number;
display_data: { is_loading: boolean; percentage: number }[];
}>
| undefined = $state();
$effect(() => {
const d = $selected_display_ids;
loading_data = liveQuery(() => get_loading_data(get_file_primary_key(file), d));
});
let thumbnail_url = liveQuery(() => get_thumbnail_url(get_file_primary_key(file)));
let date_mapping: Observable<Record<string, Date>> = liveQuery(() => let date_mapping: Observable<Record<string, Date>> = liveQuery(() =>
get_date_mapping(get_file_primary_key(file)) get_date_mapping(get_file_primary_key(file))
); );
@@ -52,21 +69,28 @@
if (keys.length === 1) return get_formated_created_string(date_mapping[keys[0]], full_string); if (keys.length === 1) return get_formated_created_string(date_mapping[keys[0]], full_string);
let out = ""; let out = '';
let is_different = false; let is_different = false;
const first_formated_created_string = get_formated_created_string(date_mapping[keys[0]], full_string); const first_formated_created_string = get_formated_created_string(
date_mapping[keys[0]],
full_string
);
out += `${keys[0]}: ${first_formated_created_string}`; out += `${keys[0]}: ${first_formated_created_string}`;
for (const key of keys.splice(0, 1)) { for (const key of keys.splice(0, 1)) {
const current_formated_created_string = get_formated_created_string(date_mapping[key], full_string); const current_formated_created_string = get_formated_created_string(
if (!is_different && current_formated_created_string !== first_formated_created_string) is_different = true; date_mapping[key],
out += `\n${key}: ${current_formated_created_string}` full_string
);
if (!is_different && current_formated_created_string !== first_formated_created_string)
is_different = true;
out += `\n${key}: ${current_formated_created_string}`;
} }
if (full_string) { if (full_string) {
return is_different ? out : first_formated_created_string; return is_different ? out : first_formated_created_string;
} else { } else {
return is_different ? "versch." : first_formated_created_string; return is_different ? 'versch.' : first_formated_created_string;
} }
} }
@@ -95,6 +119,7 @@
function get_grayed_out_text_color_strings(is_selected: boolean): string { function get_grayed_out_text_color_strings(is_selected: boolean): string {
if (not_interactable) return 'text-stone-400'; if (not_interactable) return 'text-stone-400';
if ($loading_data?.is_loading) return 'text-white/20';
const color = is_selected ? 'text-stone-600' : 'text-stone-400'; const color = is_selected ? 'text-stone-600' : 'text-stone-400';
const factor = is_selected ? -1 : 1; const factor = is_selected ? -1 : 1;
return `${color} group-hover:${get_shifted_color(color, factor * 100)} group-active:${get_shifted_color(color, factor * 150)}`; return `${color} group-hover:${get_shifted_color(color, factor * 100)} group-active:${get_shifted_color(color, factor * 150)}`;
@@ -102,13 +127,14 @@
function get_grayed_out_border_color_strings(is_selected: boolean): string { function get_grayed_out_border_color_strings(is_selected: boolean): string {
if (not_interactable) return 'border-stone-550'; if (not_interactable) return 'border-stone-550';
if ($loading_data?.is_loading) return 'border-white/10';
const color = is_selected ? 'border-stone-450' : 'border-stone-550'; const color = is_selected ? 'border-stone-450' : 'border-stone-550';
const factor = is_selected ? 1 : 1; const factor = is_selected ? 1 : 1;
return `${color} group-hover:${get_shifted_color(color, factor * 100)} group-active:${get_shifted_color(color, factor * 150)}`; return `${color} group-hover:${get_shifted_color(color, factor * 100)} group-active:${get_shifted_color(color, factor * 150)}`;
} }
function onclick(e: Event) { function onclick(e: Event) {
if (not_interactable) return; if (not_interactable || $loading_data?.is_loading) return;
select(selected_file_ids, get_file_primary_key(file), 'toggle'); select(selected_file_ids, get_file_primary_key(file), 'toggle');
e.stopPropagation(); e.stopPropagation();
} }
@@ -121,6 +147,73 @@
await run_on_all_selected_displays(open_file, true, path_to_file); await run_on_all_selected_displays(open_file, true, path_to_file);
} }
} }
function get_main_classes(): string {
let out = '';
if ($loading_data?.is_loading) {
out += 'bg-stone-700 text-white/30';
} else {
out += get_selectable_color_classes(
!not_interactable && is_selected(get_file_primary_key(file), $selected_file_ids),
{
bg: true,
hover: !not_interactable,
active: !not_interactable,
text: true
}
);
}
if (not_interactable) {
out += ' rounded-lg';
} else if ($loading_data?.is_loading) {
out += ' rounded-r-lg';
} else {
out += ' rounded-r-lg cursor-pointer';
}
return out;
}
async function get_loading_data(
file_primary_key: string,
selected_display_ids: string[]
): Promise<{
is_loading: boolean;
total_percentage: number;
display_data: { is_loading: boolean; percentage: number }[];
}> {
const file_on_display_data: FileOnDisplay[] = await db.files_on_display
.where('file_primary_key')
.equals(file_primary_key)
.filter((e) => selected_display_ids.includes(e.display_id))
.toArray();
if (file_on_display_data.length === 0) {
return {
is_loading: true,
total_percentage: 0,
display_data: []
};
}
const display_data = [];
let is_loading = false;
let percentage_sum = 0;
for (const fod of file_on_display_data) {
if (!is_loading) is_loading = fod.is_loading;
percentage_sum += fod.percentage;
display_data.push({
is_loading: fod.is_loading,
percentage: fod.percentage
});
}
let total_percentage = percentage_sum / display_data.length;
return {
is_loading,
total_percentage,
display_data
};
}
</script> </script>
<div data-testid="inode" class="flex flex-row h-{$current_height.file} w-full"> <div data-testid="inode" class="flex flex-row h-{$current_height.file} w-full">
@@ -178,27 +271,25 @@
if (e.key === 'Enter' || e.key === ' ') onclick(e); if (e.key === 'Enter' || e.key === ' ') onclick(e);
}} }}
{onclick} {onclick}
class="{get_selectable_color_classes( class="{get_main_classes()} relative transition-colors duration-200 gap-4 flex flex-row justify-between group w-full min-w-0"
!not_interactable && is_selected(get_file_primary_key(file), $selected_file_ids),
{
bg: true,
hover: !not_interactable,
active: !not_interactable,
text: true
}
)} {not_interactable
? 'rounded-lg'
: 'rounded-r-lg cursor-pointer'} transition-colors duration-200 gap-4 flex flex-row justify-between group w-full min-w-0"
> >
<div class="flex flex-row gap-2 min-w-0 w-full"> {#if $loading_data?.is_loading}
<!-- <div class="pointer-events-none absolute inset-0"> -->
<div
class="absolute pointer-events-none inset-y-0 left-0 transition-[width] duration-200 bg-stone-600 rounded-r-lg"
style={`width: ${$loading_data.total_percentage}%;`}
></div>
<!-- </div> -->
{/if}
<div class="flex flex-row gap-2 min-w-0 w-full z-10">
<div class="aspect-square rounded-md flex justify-center items-center"> <div class="aspect-square rounded-md flex justify-center items-center">
{#if is_folder} {#if is_folder}
<Folder class="size-full p-2" /> <Folder class="size-full p-2" />
{:else if $thumbnail_url || null} {:else if $thumbnail_url || null}
<img <img
src={$thumbnail_url || null} src={$thumbnail_url || null}
alt="file_thumbnail" alt="ERR"
class="object-contain size-full select-none block p-1 rounded-lg" class="object-contain size-full select-none block p-1 rounded-lg text-center content-center text-red-300"
draggable="false" draggable="false"
/> />
{:else if supported_file_type_icon[get_file_type(file)?.display_name || '']} {:else if supported_file_type_icon[get_file_type(file)?.display_name || '']}
@@ -215,7 +306,7 @@
</div> </div>
</div> </div>
<div <div
class=" p-1 flex flex-row items-center gap-1 pr-1 {get_grayed_out_text_color_strings( class=" p-1 flex flex-row items-center gap-1 pr-1 z-10 {get_grayed_out_text_color_strings(
is_selected(get_file_primary_key(file), $selected_file_ids) is_selected(get_file_primary_key(file), $selected_file_ids)
)} duration-200 transition-colors" )} duration-200 transition-colors"
> >