General clean up the _random appends. About to work on a new Hosted File Download Svelte component.
This commit is contained in:
@@ -298,7 +298,6 @@ export const properties_to_save = [
|
||||
'url',
|
||||
'url_text',
|
||||
'hosted_file_id',
|
||||
'hosted_file_id_random',
|
||||
'file_path',
|
||||
'filename',
|
||||
'file_extension',
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
// window.postMessage({ type: 'download_event_file', hosted_file_id: idaa_archive_content_obj.hosted_file_id, filename: idaa_archive_content_obj.filename, auto_download: true }, '*');
|
||||
}}
|
||||
class="novi_btn btn btn-sm lg:btn-md preset-tonal-primary hover:preset-filled-primary-500 min-w-72 lg:min-w-96"
|
||||
title={`Download this file:\n${hosted_file_obj.filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}... Hosted ID: ${hosted_file_obj.hosted_file_id_random}`}
|
||||
title={`Download this file:\n${hosted_file_obj.filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}... Hosted ID: ${hosted_file_obj.hosted_file_id}`}
|
||||
>
|
||||
{#await ae_promises[hosted_file_id]}
|
||||
<span class="fas fa-spinner fa-spin mx-1"></span>
|
||||
|
||||
@@ -1,432 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
|
||||
// Imports
|
||||
// Import components and elements
|
||||
import Element_input_files_tbl from '$lib/elements/element_input_files_tbl.svelte';
|
||||
|
||||
// Import storage, functions, and libraries
|
||||
import type { key_val } from '$lib/stores/ae_stores';
|
||||
|
||||
import { api } from '$lib/api/api';
|
||||
import {
|
||||
ae_snip,
|
||||
ae_loc,
|
||||
ae_sess,
|
||||
ae_api,
|
||||
ae_trig,
|
||||
slct,
|
||||
slct_trigger
|
||||
} from '$lib/stores/ae_stores';
|
||||
|
||||
// Exports
|
||||
|
||||
interface Props {
|
||||
log_lvl?: number;
|
||||
// Expecting these for link_to_type: 'event', 'event_location', 'archive_content', etc
|
||||
link_to_type: string;
|
||||
link_to_id: string;
|
||||
input_name?: string;
|
||||
multiple?: boolean;
|
||||
required?: boolean;
|
||||
accept?: string;
|
||||
class_li_default?: string;
|
||||
class_li?: string;
|
||||
input_class_li?: string[];
|
||||
table_class_li?: string[];
|
||||
upload_complete?: boolean;
|
||||
submit_status?: null | string;
|
||||
hosted_file_id_li?: string[];
|
||||
hosted_file_obj_li?: any[];
|
||||
label?: import('svelte').Snippet;
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
link_to_type,
|
||||
link_to_id,
|
||||
input_name = 'file_list',
|
||||
multiple = true,
|
||||
required = true,
|
||||
accept = 'audio/*, image/*, video/*, .bak, .cfg, .css, .csv, .doc, .docx, .gz, .htm, .html, .ini, .iso, .j2, .json, .key, .keynote, .md, .pdf, .ppt, .pptx, .rar, .rtf, .sql, .svelte, ttf, .txt, .xls, .xlsx, .xz, .zip, .bin, .dmg, .exe, .js, .msi, .php, .py, .sh',
|
||||
class_li_default = 'flex flex-col gap-1 items-center justify-center w-full max-w-2xl mx-auto my-1',
|
||||
class_li = '',
|
||||
input_class_li = ['file_drop_area'],
|
||||
table_class_li = ['table', 'table-sm', 'table-striped', '', 'text-sm'],
|
||||
upload_complete = $bindable(false),
|
||||
submit_status = $bindable(null),
|
||||
hosted_file_id_li = $bindable([]),
|
||||
hosted_file_obj_li = $bindable([]),
|
||||
label
|
||||
}: Props = $props();
|
||||
|
||||
// Local Variables
|
||||
let task_id = $state(link_to_id);
|
||||
let input_file_list: any = $state(null);
|
||||
let ae_promises: key_val = $state({}); // Promise<any>;
|
||||
let ae_triggers: key_val = {};
|
||||
|
||||
let input_element_id = 'ae_comp__hosted_files_upload__input';
|
||||
|
||||
let form_kv: key_val = {
|
||||
start_time: null,
|
||||
end_time: null,
|
||||
reencode: null,
|
||||
video_file: null
|
||||
};
|
||||
let download_clip_src: string = $state('');
|
||||
let download_clip_filename: string = $state('clipped_video.mp4');
|
||||
|
||||
// *** Functions and Logic
|
||||
async function handle_submit_form_files(event: Event) {
|
||||
console.log('*** handle_submit_form() ***');
|
||||
|
||||
$ae_sess.files.disable_submit__hosted_file_obj = true;
|
||||
$ae_sess.files.submit_status = 'saving';
|
||||
submit_status = 'saving';
|
||||
upload_complete = false;
|
||||
|
||||
hosted_file_id_li = [];
|
||||
hosted_file_obj_li = [];
|
||||
|
||||
let hosted_file_results;
|
||||
|
||||
const form = event.target as HTMLFormElement;
|
||||
const formData = new FormData(form);
|
||||
const start_time = formData.get('start_time') as string;
|
||||
const end_time = formData.get('end_time') as string;
|
||||
const reencode = formData.get('reencode') as string;
|
||||
|
||||
// Access file input by name or ID
|
||||
const file_input = form.elements.namedItem(input_name) as HTMLInputElement;
|
||||
|
||||
form_kv = {
|
||||
start_time: start_time,
|
||||
end_time: end_time,
|
||||
reencode: reencode,
|
||||
video_file: file_input?.files?.[0]
|
||||
};
|
||||
|
||||
// const form_data = new FormData();
|
||||
|
||||
// form_data.append('start_time', event.target.start_time.value);
|
||||
// form_data.append('end_time', event.target.end_time.value);
|
||||
|
||||
// if (event.target.reencode.value == '1' || event.target.reencode.value == 'true') {
|
||||
// form_data.append('reencode', 'true');
|
||||
// } else {
|
||||
// form_data.append('reencode', 'false');
|
||||
// }
|
||||
// form_data.append('reencode', event.target.reencode.value);
|
||||
|
||||
// form_data.append(`video_file`, event.target.video_file.files[0]);
|
||||
|
||||
// let download_filename = `clipped_file_test.mp4`;
|
||||
|
||||
// let params = null;
|
||||
|
||||
// let endpoint = '/hosted_file/clip_video';
|
||||
|
||||
// console.log(form_data);
|
||||
|
||||
// params = null;
|
||||
|
||||
if (file_input && file_input.files && file_input.files.length > 0) {
|
||||
task_id = link_to_id; // Ideally this should be the file hash, but we may be uploading multiple files at once. This should be done with a loop instead?
|
||||
|
||||
// Loop through each file and upload them individually in event.target[input_element_id].files
|
||||
// The task_id should be the file hash.
|
||||
// processed_file_list[i] has the file hash_sha256, hash_sha256_match, warnings, uploaded, uploaded_bytes, filename, and file_size_bytes.
|
||||
for (let i = 0; i < file_input.files.length; i++) {
|
||||
let tmp_file = file_input.files[i];
|
||||
|
||||
task_id = $ae_sess.files.processed_file_list[i].hash_sha256;
|
||||
|
||||
hosted_file_results = await handle_input_upload_files([tmp_file], form_kv, task_id);
|
||||
|
||||
if (hosted_file_results) {
|
||||
console.log(`hosted_file_results:`, hosted_file_results);
|
||||
} else {
|
||||
console.log(`hosted_file_results:`, hosted_file_results);
|
||||
}
|
||||
}
|
||||
// hosted_file_results = await handle_input_upload_files(event.target[input_element_id].files, task_id);
|
||||
$ae_sess.files.processed_file_list = [];
|
||||
$ae_sess = $ae_sess;
|
||||
form.reset();
|
||||
// await tick();
|
||||
|
||||
if (log_lvl) {
|
||||
console.log(`hosted_file_id_li: ${hosted_file_id_li}`, hosted_file_id_li);
|
||||
} else if (log_lvl > 1) {
|
||||
console.log('hosted_file_results:', hosted_file_results);
|
||||
}
|
||||
}
|
||||
|
||||
$ae_sess.files.disable_submit__hosted_file_obj = false;
|
||||
$ae_sess.files.submit_status = 'saved';
|
||||
submit_status = 'saved';
|
||||
upload_complete = true;
|
||||
}
|
||||
|
||||
async function handle_input_upload_files(input_upload_files: File[], form_kv: any, task_id: string) {
|
||||
console.log('*** handle_input_upload_files() ***');
|
||||
console.log(input_upload_files);
|
||||
console.log(form_kv);
|
||||
|
||||
const form_data = new FormData();
|
||||
|
||||
// form_data.append('account_id', $ae_loc.account_id);
|
||||
// form_data.append('link_to_type', link_to_type);
|
||||
// form_data.append('link_to_id', link_to_id);
|
||||
|
||||
form_data.append('start_time', form_kv.start_time);
|
||||
form_data.append('end_time', form_kv.end_time);
|
||||
|
||||
if (form_kv.reencode == '1' || form_kv.reencode == 'true') {
|
||||
form_data.append('reencode', 'true');
|
||||
} else {
|
||||
form_data.append('reencode', 'false');
|
||||
}
|
||||
|
||||
// There should really only be one file uploaded at a time for now.
|
||||
for (let i = 0; i < input_upload_files.length; i++) {
|
||||
form_data.append(`video_file`, input_upload_files[i]);
|
||||
}
|
||||
|
||||
// hash_sha256, uploaded, uploaded_bytes
|
||||
// $ae_sess.files.processed_file_list[i] = {
|
||||
// ...$ae_sess.files.processed_file_list[i],
|
||||
// uploaded: $ae_sess.api_upload_kv[link_to_id].percent_completed,
|
||||
// uploaded_bytes: $ae_sess.api_upload_kv[link_to_id].uploaded_bytes,
|
||||
// };
|
||||
|
||||
let params = null;
|
||||
|
||||
let endpoint = '/hosted_file/clip_video';
|
||||
|
||||
console.log(form_data);
|
||||
|
||||
params = null;
|
||||
|
||||
// Uncomment and the post_promise is not seen by the "await" below
|
||||
// post_promise = await api.post_object({api_cfg: $cfg.api, endpoint: endpoint, params: params, data:form_data});
|
||||
// Uncomment so that the post_promise is not seen by the "await" below
|
||||
ae_promises.upload__hosted_file_obj = api
|
||||
.post_object({
|
||||
api_cfg: $ae_api,
|
||||
endpoint: endpoint,
|
||||
params: params,
|
||||
form_data: form_data,
|
||||
return_blob: true,
|
||||
filename: 'clipped_video_test.mp4',
|
||||
auto_download: false,
|
||||
task_id: task_id,
|
||||
log_lvl: log_lvl
|
||||
// retry_count: 1,
|
||||
})
|
||||
.then(async function (result) {
|
||||
console.log(result);
|
||||
|
||||
let file_blob = new Blob([result.data]);
|
||||
// console.log(file_blob);
|
||||
let file_obj_url = window.URL.createObjectURL(file_blob); // The img src
|
||||
// const url = window.URL.createObjectURL(new Blob([result.data]));
|
||||
download_clip_src = file_obj_url;
|
||||
// download_filename = file_obj_url;
|
||||
|
||||
return true;
|
||||
|
||||
// // WARNING!!!! ONLY ONE FILE IS EXPECTED TO BE UPLOADED AT A TIME!!!
|
||||
// // NOTE: The /hosted_file/upload_files endpoint will always return a list of successful files uploaded. In this case we are only uploading one file and expecting a list of one item.
|
||||
// let x = 0;
|
||||
// console.log(result[x]);
|
||||
// let hosted_file_obj = result[x];
|
||||
|
||||
// let hosted_file_id = hosted_file_obj.hosted_file_id_random;
|
||||
|
||||
// hosted_file_id_li.push(hosted_file_id);
|
||||
// hosted_file_obj_li.push(hosted_file_obj);
|
||||
|
||||
// let hosted_file_data: key_val = {};
|
||||
// hosted_file_data['hosted_file_id_random'] = hosted_file_id;
|
||||
// hosted_file_data['for_type'] = link_to_type;
|
||||
// hosted_file_data['for_id_random'] = link_to_id;
|
||||
// hosted_file_data['filename'] = hosted_file_obj.filename;
|
||||
// hosted_file_data['extension'] = hosted_file_obj.extension;
|
||||
// hosted_file_data['enable'] = true;
|
||||
// console.log(hosted_file_data);
|
||||
|
||||
// return hosted_file_data;
|
||||
|
||||
// $ae_sess.files.new_upload_list[i].uploaded_bytes = 10; // fake 10 bytes at least...
|
||||
|
||||
// let event_file_id = await events_func.create_hosted_file_obj_from_hosted_file_async({
|
||||
// api_cfg: $ae_api,
|
||||
// hosted_file_id: hosted_file_id,
|
||||
// data: event_file_data,
|
||||
// log_lvl: log_lvl
|
||||
// })
|
||||
// .then(function (create_result) {
|
||||
// console.log(create_result); // NOTE: This should be the event_file_id string
|
||||
// // let event_file_id = create_result;
|
||||
// return create_result;
|
||||
// });
|
||||
|
||||
// return event_file_id;
|
||||
})
|
||||
// .then(function (hosted_file_data) {
|
||||
// return hosted_file_data;
|
||||
// })
|
||||
.catch(function (error: any) {
|
||||
console.log('Something went wrong.');
|
||||
console.log(error);
|
||||
return false;
|
||||
})
|
||||
.finally(function () {
|
||||
// $slct_trigger = 'load__hosted_file_obj_li';
|
||||
});
|
||||
|
||||
console.log(ae_promises.upload__hosted_file_obj);
|
||||
let hosted_file_result = ae_promises.upload__hosted_file_obj;
|
||||
|
||||
return hosted_file_result;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<!-- class:hidden={!$ae_loc.trusted_access} -->
|
||||
<form onsubmit={preventDefault(handle_submit_form_files)} class="{class_li_default} {class_li}">
|
||||
<label class="label"
|
||||
>Start time (HH:MM:SS) <input
|
||||
type="text"
|
||||
name="start_time"
|
||||
value="00:00:00"
|
||||
placeholder="HH:MM:SS (00:01:30)"
|
||||
class="input w-32"
|
||||
/></label
|
||||
>
|
||||
<label class="label"
|
||||
>End time (HH:MM:SS) <input
|
||||
type="text"
|
||||
name="end_time"
|
||||
value="00:01:15"
|
||||
placeholder="HH:MM:SS (01:05:25)"
|
||||
class="input w-32"
|
||||
/></label
|
||||
>
|
||||
|
||||
<label class="label"
|
||||
>Re-encode (true or false) <input
|
||||
type="text"
|
||||
name="reencode"
|
||||
value="false"
|
||||
placeholder="true"
|
||||
class="input w-32"
|
||||
/></label
|
||||
>
|
||||
|
||||
{#await ae_promises.upload__hosted_file_obj}
|
||||
<div class="text-lg flex flex-row gap-1 items-center justify-center">
|
||||
<span class="fas fa-spinner fa-spin m-1"></span>
|
||||
<span class="">
|
||||
Uploading
|
||||
{#if $ae_sess.api_upload_kv[task_id]}
|
||||
{$ae_sess.api_upload_kv[task_id].percent_completed}%
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
<label
|
||||
for="ae_comp__hosted_files_upload__input"
|
||||
class="svelte_input_file_label text-center"
|
||||
class:hidden={$ae_sess.files.disable_submit__hosted_file_obj}
|
||||
>
|
||||
{#if label}{@render label()}{:else}
|
||||
<div>
|
||||
<span class="fas fa-upload"></span>
|
||||
<!-- Select files to upload -->
|
||||
<!-- <span class="fas fa-file-archive"></span> -->
|
||||
<strong class="bg-blue-300 p-1">Upload files</strong>
|
||||
<!-- (drag and drop) -->
|
||||
</div>
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400 italic">
|
||||
<strong>Video files only</strong><br />
|
||||
(mp4 or mkv)
|
||||
</span>
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
<input
|
||||
id={input_element_id}
|
||||
type="file"
|
||||
bind:files={input_file_list}
|
||||
{multiple}
|
||||
{required}
|
||||
{accept}
|
||||
name={input_name}
|
||||
class="svelte_input_file_element file-dropzone-input block w-full text-lg text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-hidden dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 {input_class_li.join(
|
||||
' '
|
||||
)}"
|
||||
class:hidden={$ae_sess.files.disable_submit__hosted_file_obj}
|
||||
/>
|
||||
|
||||
<Element_input_files_tbl
|
||||
bind:input_file_list
|
||||
bind:file_list_status={$ae_sess.files.status__file_list}
|
||||
bind:processed_file_list={$ae_sess.files.processed_file_list}
|
||||
{table_class_li}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-lg btn-primary preset-tonal-primary border border-primary-500 hover:preset-tonal-success border border-success-500 w-54"
|
||||
disabled={$ae_sess.files.disable_submit__hosted_file_obj ||
|
||||
$ae_sess.files.status__file_list != 'ready'}
|
||||
>
|
||||
{#await ae_promises.upload__hosted_file_obj}
|
||||
<span class="fas fa-spinner fa-spin m-1"></span>
|
||||
<span class="">
|
||||
Uploading
|
||||
{#if $ae_sess.api_upload_kv[task_id]}
|
||||
{$ae_sess.api_upload_kv[task_id].percent_completed}%
|
||||
{/if}
|
||||
</span>
|
||||
{:then}
|
||||
<span class="fas fa-upload m-1"></span>
|
||||
<span class="text-sm"> Upload? </span>
|
||||
<!-- <span class="fas fa-save m-1"></span> -->
|
||||
<span class="grow font-bold">
|
||||
{#if $ae_sess.files.processed_file_list?.length > 0}
|
||||
|
||||
{$ae_sess.files.processed_file_list.length == 1
|
||||
? `${$ae_sess.files.processed_file_list.length} file`
|
||||
: `${$ae_sess.files.processed_file_list.length} files`}
|
||||
{:else}
|
||||
<span class="text-xs"> No files selected </span>
|
||||
{/if}
|
||||
<!-- Files -->
|
||||
</span>
|
||||
{/await}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<hr />
|
||||
|
||||
{#await ae_promises.upload__hosted_file_obj}
|
||||
<span class="fas fa-spinner fa-spin m-1"></span>
|
||||
<p class="highlight">Converting... This may take a few minutes.</p>
|
||||
{:then}
|
||||
{#if ae_promises.upload__hosted_file_obj}
|
||||
<a
|
||||
href={download_clip_src}
|
||||
download={download_clip_filename}
|
||||
class="ae_btn btn_lg btn_primary"
|
||||
><span class="fas fa-download"></span> Ready to Download</a
|
||||
>
|
||||
{:else}
|
||||
<p>Fill out the form and select the video file to clip.</p>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
@@ -52,7 +52,7 @@
|
||||
}
|
||||
|
||||
let {
|
||||
log_lvl = 0,
|
||||
log_lvl = $bindable(0),
|
||||
hosted_file_id,
|
||||
hosted_file_obj,
|
||||
filename = $bindable(null),
|
||||
@@ -76,9 +76,9 @@
|
||||
let ae_promises: key_val = $state({});
|
||||
|
||||
$effect(() => {
|
||||
if ($ae_sess?.api_download_kv[hosted_file_obj?.hosted_file_id_random]?.percent_completed) {
|
||||
if ($ae_sess?.api_download_kv[hosted_file_obj?.hosted_file_id]?.percent_completed) {
|
||||
download_percent =
|
||||
$ae_sess.api_download_kv[hosted_file_obj?.hosted_file_id_random].percent_completed;
|
||||
$ae_sess.api_download_kv[hosted_file_obj?.hosted_file_id].percent_completed;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -91,10 +91,10 @@
|
||||
onclick={() => {
|
||||
download_complete = false;
|
||||
download_status_msg = 'Downloading...';
|
||||
ae_promises[hosted_file_obj.hosted_file_id_random] = api
|
||||
ae_promises[hosted_file_obj.hosted_file_id] = api
|
||||
.download_hosted_file({
|
||||
api_cfg: $ae_api,
|
||||
hosted_file_id: hosted_file_obj.hosted_file_id_random,
|
||||
hosted_file_id: hosted_file_obj.hosted_file_id,
|
||||
return_file: true,
|
||||
filename: filename ?? hosted_file_obj.filename,
|
||||
auto_download: auto_download,
|
||||
@@ -119,14 +119,14 @@
|
||||
return result;
|
||||
});
|
||||
}}
|
||||
title={`Download this file:\n${filename ?? hosted_file_obj?.filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${hosted_file_obj?.hosted_file_id_random}\n Linked to: ${linked_to_type} ID: ${linked_to_id}`}
|
||||
title={`Download this file:\n${filename ?? hosted_file_obj?.filename}\n[API] SHA256: ${hosted_file_obj?.hash_sha256?.slice(0, 10)}...\nHosted ID: ${hosted_file_obj?.hosted_file_id}\n Linked to: ${linked_to_type} ID: ${linked_to_id}`}
|
||||
>
|
||||
{#await ae_promises[hosted_file_obj.hosted_file_id_random]}
|
||||
{#await ae_promises[hosted_file_obj.hosted_file_id]}
|
||||
<span class="fas fa-spinner fa-spin mx-1"></span>
|
||||
<span class="">
|
||||
Downloading
|
||||
{#if $ae_sess.api_download_kv[hosted_file_obj.hosted_file_id_random]}
|
||||
{$ae_sess.api_download_kv[hosted_file_obj.hosted_file_id_random]
|
||||
{#if $ae_sess.api_download_kv[hosted_file_obj.hosted_file_id]}
|
||||
{$ae_sess.api_download_kv[hosted_file_obj.hosted_file_id]
|
||||
.percent_completed}%
|
||||
{/if}
|
||||
:
|
||||
@@ -206,10 +206,8 @@
|
||||
let hosted_file_data: key_val = {};
|
||||
hosted_file_data['id'] = hosted_file_id; // Same as the hosted_file_id
|
||||
hosted_file_data['hosted_file_id'] = hosted_file_id;
|
||||
// hosted_file_data['hosted_file_id_random'] = hosted_file_id;
|
||||
hosted_file_data['for_type'] = link_to_type;
|
||||
hosted_file_data['for_id'] = link_to_id;
|
||||
// hosted_file_data['for_id_random'] = link_to_id;
|
||||
hosted_file_data['hash_sha256'] = hosted_file_obj.hash_sha256;
|
||||
hosted_file_data['filename'] = hosted_file_obj.filename;
|
||||
hosted_file_data['extension'] = hosted_file_obj.extension;
|
||||
|
||||
@@ -565,215 +565,213 @@ export async function update_ae_obj__journal_entry({
|
||||
}
|
||||
}
|
||||
|
||||
// This function will loop through the journal_entry_obj_li and save each one to the DB.
|
||||
// Updated 2025-05-09
|
||||
export async function db_save_ae_obj_li__journal_entry({
|
||||
obj_type,
|
||||
obj_li,
|
||||
log_lvl = 0
|
||||
}: {
|
||||
obj_type: string;
|
||||
obj_li: any;
|
||||
log_lvl?: number;
|
||||
}) {
|
||||
// log_lvl = 1;
|
||||
if (log_lvl) {
|
||||
console.log(`*** db_save_ae_obj_li__journal_entry() *** obj_type=${obj_type}`, obj_li);
|
||||
}
|
||||
// // This function will loop through the journal_entry_obj_li and save each one to the DB.
|
||||
// // Updated 2025-05-09
|
||||
// export async function db_save_ae_obj_li__journal_entry({
|
||||
// obj_type,
|
||||
// obj_li,
|
||||
// log_lvl = 0
|
||||
// }: {
|
||||
// obj_type: string;
|
||||
// obj_li: any;
|
||||
// log_lvl?: number;
|
||||
// }) {
|
||||
// // log_lvl = 1;
|
||||
// if (log_lvl) {
|
||||
// console.log(`*** db_save_ae_obj_li__journal_entry() *** obj_type=${obj_type}`, obj_li);
|
||||
// }
|
||||
|
||||
if (obj_li && obj_li.length) {
|
||||
// let obj_li_id = obj_li.map((obj: any) => obj.journal_entry_id_random);
|
||||
const obj_li_id: string[] = [];
|
||||
// if (obj_li && obj_li.length) {
|
||||
// // let obj_li_id = obj_li.map((obj: any) => obj.journal_entry_id_random);
|
||||
// const obj_li_id: string[] = [];
|
||||
|
||||
for (const obj of obj_li) {
|
||||
// obj_li.forEach(async function (obj: any) {
|
||||
if (log_lvl) {
|
||||
console.log(`Processing ae_obj ${obj_type}:`, obj);
|
||||
}
|
||||
// for (const obj of obj_li) {
|
||||
// // obj_li.forEach(async function (obj: any) {
|
||||
// if (log_lvl) {
|
||||
// console.log(`Processing ae_obj ${obj_type}:`, obj);
|
||||
// }
|
||||
|
||||
let content = obj.content ?? '';
|
||||
// remove the most common zerowidth characters from the start of the file
|
||||
let content_cleaned: null | string = null;
|
||||
let content_md_html: null | string = null; // await marked.parse(content_cleaned ?? '') ?? null;
|
||||
// let content_md_html_alt: null|string = await marked.parse(content_cleaned ?? '', { gfm: false }) ?? null;
|
||||
// let content = obj.content ?? '';
|
||||
// // remove the most common zerowidth characters from the start of the file
|
||||
// let content_cleaned: null | string = null;
|
||||
// let content_md_html: null | string = null; // await marked.parse(content_cleaned ?? '') ?? null;
|
||||
// // let content_md_html_alt: null|string = await marked.parse(content_cleaned ?? '', { gfm: false }) ?? null;
|
||||
|
||||
if (obj.content_encrypted) {
|
||||
// In theory "content" should be null if "content_encrypted" has a value.
|
||||
content = null; // obj.content_encrypted;
|
||||
content_cleaned = null;
|
||||
content_md_html = null;
|
||||
} else {
|
||||
content_cleaned = content.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/, '');
|
||||
content_md_html = (await marked.parse(content_cleaned ?? '')) ?? null;
|
||||
}
|
||||
// if (obj.content_encrypted) {
|
||||
// // In theory "content" should be null if "content_encrypted" has a value.
|
||||
// content = null; // obj.content_encrypted;
|
||||
// content_cleaned = null;
|
||||
// content_md_html = null;
|
||||
// } else {
|
||||
// content_cleaned = content.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/, '');
|
||||
// content_md_html = (await marked.parse(content_cleaned ?? '')) ?? null;
|
||||
// }
|
||||
|
||||
let history = obj.history ?? '';
|
||||
let history_cleaned: null | string = null;
|
||||
let history_md_html: null | string = null; // await marked.parse(history_cleaned ?? '') ?? null;
|
||||
// let history = obj.history ?? '';
|
||||
// let history_cleaned: null | string = null;
|
||||
// let history_md_html: null | string = null; // await marked.parse(history_cleaned ?? '') ?? null;
|
||||
|
||||
if (obj.history_encrypted) {
|
||||
// In theory "history" should be null if "history_encrypted" has a value.
|
||||
history = null; // obj.history_encrypted;
|
||||
history_cleaned = null;
|
||||
history_md_html = null;
|
||||
} else {
|
||||
history_cleaned = history.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/, '');
|
||||
history_md_html = (await marked.parse(history_cleaned ?? '')) ?? null;
|
||||
}
|
||||
// if (obj.history_encrypted) {
|
||||
// // In theory "history" should be null if "history_encrypted" has a value.
|
||||
// history = null; // obj.history_encrypted;
|
||||
// history_cleaned = null;
|
||||
// history_md_html = null;
|
||||
// } else {
|
||||
// history_cleaned = history.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/, '');
|
||||
// history_md_html = (await marked.parse(history_cleaned ?? '')) ?? null;
|
||||
// }
|
||||
|
||||
const obj_record = {
|
||||
id: obj.journal_entry_id_random,
|
||||
journal_entry_id: obj.journal_entry_id_random,
|
||||
journal_entry_id_random: obj.journal_entry_id_random,
|
||||
// const obj_record = {
|
||||
// id: obj.journal_entry_id,
|
||||
// journal_entry_id: obj.journal_entry_id,
|
||||
|
||||
journal_id: obj.journal_id_random,
|
||||
journal_id_random: obj.journal_id_random,
|
||||
// journal_id: obj.journal_id,
|
||||
|
||||
code: obj.code,
|
||||
// code: obj.code,
|
||||
|
||||
for_type: obj.for_type,
|
||||
for_id: obj.for_id,
|
||||
// for_type: obj.for_type,
|
||||
// for_id: obj.for_id,
|
||||
|
||||
journal_entry_type: obj.journal_entry_type,
|
||||
// journal_entry_type: obj.journal_entry_type,
|
||||
|
||||
person_id: obj.person_id_random,
|
||||
// person_id: obj.person_id,
|
||||
|
||||
template: obj.template ?? null, // Allow for a template to be used, otherwise null
|
||||
// template: obj.template ?? null, // Allow for a template to be used, otherwise null
|
||||
|
||||
activity_code: obj.activity_code,
|
||||
category_code: obj.category_code,
|
||||
type_code: obj.type_code,
|
||||
topic_code: obj.topic_code,
|
||||
tags: obj.tags,
|
||||
// activity_code: obj.activity_code,
|
||||
// category_code: obj.category_code,
|
||||
// type_code: obj.type_code,
|
||||
// topic_code: obj.topic_code,
|
||||
// tags: obj.tags,
|
||||
|
||||
public: obj.public,
|
||||
private: obj.private,
|
||||
personal: obj.personal,
|
||||
professional: obj.professional,
|
||||
// public: obj.public,
|
||||
// private: obj.private,
|
||||
// personal: obj.personal,
|
||||
// professional: obj.professional,
|
||||
|
||||
name: obj.name,
|
||||
short_name: obj.short_name ?? null,
|
||||
summary: obj.summary,
|
||||
outline: obj.outline,
|
||||
// description: obj.description,
|
||||
// name: obj.name,
|
||||
// short_name: obj.short_name ?? null,
|
||||
// summary: obj.summary,
|
||||
// outline: obj.outline,
|
||||
// // description: obj.description,
|
||||
|
||||
content: obj.content,
|
||||
content_md_html: content_md_html ?? undefined,
|
||||
// content_md_html_alt: content_md_html_alt,
|
||||
content_html: obj.content_html,
|
||||
content_json: obj.content_json,
|
||||
content_encrypted: obj.content_encrypted,
|
||||
// content: obj.content,
|
||||
// content_md_html: content_md_html ?? undefined,
|
||||
// // content_md_html_alt: content_md_html_alt,
|
||||
// content_html: obj.content_html,
|
||||
// content_json: obj.content_json,
|
||||
// content_encrypted: obj.content_encrypted,
|
||||
|
||||
history: obj.history,
|
||||
history_md_html: history_md_html ?? undefined,
|
||||
history_encrypted: obj.history_encrypted,
|
||||
// history: obj.history,
|
||||
// history_md_html: history_md_html ?? undefined,
|
||||
// history_encrypted: obj.history_encrypted,
|
||||
|
||||
passcode_hash: obj.passcode_hash,
|
||||
// passcode_hash: obj.passcode_hash,
|
||||
|
||||
// url: obj.url,
|
||||
// url_text: obj.url_text,
|
||||
// // url: obj.url,
|
||||
// // url_text: obj.url_text,
|
||||
|
||||
// hosted_file_id: obj.hosted_file_id_random,
|
||||
// // hosted_file_id: obj.hosted_file_id,
|
||||
|
||||
// file_path: obj.file_path,
|
||||
// // file_path: obj.file_path,
|
||||
|
||||
// filename: obj.filename,
|
||||
// file_extension: obj.file_extension,
|
||||
// // filename: obj.filename,
|
||||
// // file_extension: obj.file_extension,
|
||||
|
||||
// start_datetime: obj.start_datetime,
|
||||
// end_datetime: obj.end_datetime,
|
||||
// timezone: obj.timezone,
|
||||
// // start_datetime: obj.start_datetime,
|
||||
// // end_datetime: obj.end_datetime,
|
||||
// // timezone: obj.timezone,
|
||||
|
||||
// original_datetime: obj.original_datetime,
|
||||
// original_timezone: obj.original_timezone,
|
||||
// original_location: obj.original_location,
|
||||
// original_url: obj.original_url,
|
||||
// original_url_text: obj.original_url_text,
|
||||
// // original_datetime: obj.original_datetime,
|
||||
// // original_timezone: obj.original_timezone,
|
||||
// // original_location: obj.original_location,
|
||||
// // original_url: obj.original_url,
|
||||
// // original_url_text: obj.original_url_text,
|
||||
|
||||
// enable_for_public: obj.enable_for_public,
|
||||
// // enable_for_public: obj.enable_for_public,
|
||||
|
||||
alert: obj.alert,
|
||||
alert_msg: obj.alert_msg,
|
||||
// alert: obj.alert,
|
||||
// alert_msg: obj.alert_msg,
|
||||
|
||||
// cfg_json: obj.cfg_json ?? {},
|
||||
data_json: obj.data_json ?? {},
|
||||
// // cfg_json: obj.cfg_json ?? {},
|
||||
// data_json: obj.data_json ?? {},
|
||||
|
||||
// This only allows for basic access to the data.
|
||||
// passcode_read: obj.passcode_read, // For LLM (AI) generated summary...???
|
||||
// passcode_read_expire: obj.passcode_read_expire,
|
||||
// passcode_write: obj.passcode_write,
|
||||
// passcode_write_expire: obj.passcode_write_expire,
|
||||
// // This only allows for basic access to the data.
|
||||
// // passcode_read: obj.passcode_read, // For LLM (AI) generated summary...???
|
||||
// // passcode_read_expire: obj.passcode_read_expire,
|
||||
// // passcode_write: obj.passcode_write,
|
||||
// // passcode_write_expire: obj.passcode_write_expire,
|
||||
|
||||
enable: obj.enable,
|
||||
hide: obj.hide,
|
||||
archive: obj.archive,
|
||||
archive_on: obj.archive_on,
|
||||
priority: obj.priority,
|
||||
sort: obj.sort,
|
||||
group: obj.group,
|
||||
notes: obj.notes,
|
||||
created_on: obj.created_on,
|
||||
updated_on: obj.updated_on,
|
||||
// enable: obj.enable,
|
||||
// hide: obj.hide,
|
||||
// archive: obj.archive,
|
||||
// archive_on: obj.archive_on,
|
||||
// priority: obj.priority,
|
||||
// sort: obj.sort,
|
||||
// group: obj.group,
|
||||
// notes: obj.notes,
|
||||
// created_on: obj.created_on,
|
||||
// updated_on: obj.updated_on,
|
||||
|
||||
// Generated fields for sorting locally only
|
||||
tmp_sort_1: `${obj.group}_${obj.priority}_${obj.sort}_${obj.updated_on}_${obj.created_on}`,
|
||||
tmp_sort_2: `${obj.group}_${obj.priority}_${obj.sort}_${obj.updated_on ?? obj.created_on}`,
|
||||
// tmp_sort_1: `${obj.original_datetime}_${obj.group}_${obj.priority}_${obj.sort}`,
|
||||
// tmp_sort_2: `${obj.group}_${obj.original_datetime}_${obj.priority}_${obj.sort}`,
|
||||
// // Generated fields for sorting locally only
|
||||
// tmp_sort_1: `${obj.group}_${obj.priority}_${obj.sort}_${obj.updated_on}_${obj.created_on}`,
|
||||
// tmp_sort_2: `${obj.group}_${obj.priority}_${obj.sort}_${obj.updated_on ?? obj.created_on}`,
|
||||
// // tmp_sort_1: `${obj.original_datetime}_${obj.group}_${obj.priority}_${obj.sort}`,
|
||||
// // tmp_sort_2: `${obj.group}_${obj.original_datetime}_${obj.priority}_${obj.sort}`,
|
||||
|
||||
// Generated fields for sorting locally only
|
||||
// tmp_sort_1: `${obj.original_datetime}_${obj.group}_${obj.priority}_${obj.sort}`,
|
||||
// tmp_sort_2: `${obj.group}_${obj.original_datetime}_${obj.priority}_${obj.sort}`,
|
||||
// // Generated fields for sorting locally only
|
||||
// // tmp_sort_1: `${obj.original_datetime}_${obj.group}_${obj.priority}_${obj.sort}`,
|
||||
// // tmp_sort_2: `${obj.group}_${obj.original_datetime}_${obj.priority}_${obj.sort}`,
|
||||
|
||||
// From SQL view
|
||||
journal_code: obj.journal_code,
|
||||
journal_name: obj.journal_name
|
||||
// // From SQL view
|
||||
// journal_code: obj.journal_code,
|
||||
// journal_name: obj.journal_name
|
||||
|
||||
// A key value list of the others
|
||||
// journal_other_kv: obj.journal_other_kv,
|
||||
// journal_other_li: obj.journal_other_li,
|
||||
};
|
||||
// // A key value list of the others
|
||||
// // journal_other_kv: obj.journal_other_kv,
|
||||
// // journal_other_li: obj.journal_other_li,
|
||||
// };
|
||||
|
||||
let id_random = null;
|
||||
// let id_random = null;
|
||||
|
||||
try {
|
||||
id_random = await db_journals.journal_entry.update(obj_record.id, obj_record);
|
||||
} catch (error) {
|
||||
console.log(`Error: Failed to update ${obj_record.id}: ${error}`);
|
||||
}
|
||||
if (!id_random) {
|
||||
if (log_lvl) {
|
||||
console.log(`Failed to update record with ID: ${obj_record.id}. Trying put...`);
|
||||
}
|
||||
try {
|
||||
id_random = await db_journals.journal_entry.put(obj_record);
|
||||
} catch (error) {
|
||||
console.log(`Error: Failed to put ${obj.journal_entry_id_random}: ${error}`);
|
||||
}
|
||||
} else {
|
||||
if (log_lvl) {
|
||||
console.log(`Updated record with ID: ${obj_record.id}`);
|
||||
}
|
||||
obj_li_id.push(obj_record.id);
|
||||
}
|
||||
if (!id_random) {
|
||||
console.log(`Failed to save record with ID: ${obj_record.id}`);
|
||||
} else {
|
||||
if (log_lvl) {
|
||||
console.log(`Saved record with ID: ${obj_record.id}`);
|
||||
}
|
||||
}
|
||||
// });
|
||||
}
|
||||
// try {
|
||||
// id_random = await db_journals.journal_entry.update(obj_record.id, obj_record);
|
||||
// } catch (error) {
|
||||
// console.log(`Error: Failed to update ${obj_record.id}: ${error}`);
|
||||
// }
|
||||
// if (!id_random) {
|
||||
// if (log_lvl) {
|
||||
// console.log(`Failed to update record with ID: ${obj_record.id}. Trying put...`);
|
||||
// }
|
||||
// try {
|
||||
// id_random = await db_journals.journal_entry.put(obj_record);
|
||||
// } catch (error) {
|
||||
// console.log(`Error: Failed to put ${obj.journal_entry_id_random}: ${error}`);
|
||||
// }
|
||||
// } else {
|
||||
// if (log_lvl) {
|
||||
// console.log(`Updated record with ID: ${obj_record.id}`);
|
||||
// }
|
||||
// obj_li_id.push(obj_record.id);
|
||||
// }
|
||||
// if (!id_random) {
|
||||
// console.log(`Failed to save record with ID: ${obj_record.id}`);
|
||||
// } else {
|
||||
// if (log_lvl) {
|
||||
// console.log(`Saved record with ID: ${obj_record.id}`);
|
||||
// }
|
||||
// }
|
||||
// // });
|
||||
// }
|
||||
|
||||
return obj_li_id;
|
||||
} else {
|
||||
if (log_lvl) {
|
||||
console.log('No objects to save.');
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
// return obj_li_id;
|
||||
// } else {
|
||||
// if (log_lvl) {
|
||||
// console.log('No objects to save.');
|
||||
// }
|
||||
// return [];
|
||||
// }
|
||||
// }
|
||||
|
||||
// Updated 2025-05-09
|
||||
const properties_to_save = [
|
||||
|
||||
@@ -186,14 +186,12 @@
|
||||
}
|
||||
let hosted_file_obj = result[x];
|
||||
|
||||
let hosted_file_id = hosted_file_obj.hosted_file_id_random;
|
||||
let hosted_file_id = hosted_file_obj.hosted_file_id;
|
||||
|
||||
let event_file_data: key_val = {};
|
||||
event_file_data['hosted_file_id'] = hosted_file_id;
|
||||
// event_file_data['hosted_file_id_random'] = hosted_file_id;
|
||||
event_file_data['for_type'] = link_to_type;
|
||||
event_file_data['for_id'] = link_to_id;
|
||||
// event_file_data['for_id_random'] = link_to_id;
|
||||
event_file_data['filename'] = hosted_file_obj.filename;
|
||||
event_file_data['extension'] = hosted_file_obj.extension;
|
||||
event_file_data['enable'] = true;
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
History:
|
||||
{#each Object.entries($ae_loc.files.uploaded_file_kv) as [hosted_file_id, hosted_file_obj]}
|
||||
<div>
|
||||
<span>{hosted_file_obj.hosted_file_id_random}</span>
|
||||
<span>{hosted_file_obj.hosted_file_id}</span>
|
||||
<span>{hosted_file_obj.filename}</span>
|
||||
<span>{hosted_file_obj.extension}</span>
|
||||
<button
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
* Manages and displays file attachments for Journal Entries.
|
||||
* Ported/Refactored 2026-01-26 to include View mode and strictly snake_case.
|
||||
*/
|
||||
|
||||
|
||||
// *** Import Lucide Icons
|
||||
import {
|
||||
FileUp, Trash2, Download, Paperclip,
|
||||
ExternalLink, Loader2, RefreshCw
|
||||
import {
|
||||
FileUp, Trash2, Download, Paperclip,
|
||||
ExternalLink, Loader2, RefreshCw
|
||||
} from 'lucide-svelte';
|
||||
|
||||
// *** Import Aether specific variables and functions
|
||||
@@ -49,12 +49,12 @@
|
||||
// *** State
|
||||
let ae_promises: Record<string, any> = $state({});
|
||||
let upload_complete: boolean = $state(false);
|
||||
|
||||
|
||||
// Selection state for "Select Existing" mode
|
||||
let slct_hosted_file_kv: key_val = $state({});
|
||||
let slct_hosted_file_id: string | null = $state(null);
|
||||
let slct_hosted_file_obj: any = $state(null);
|
||||
|
||||
|
||||
// Selection state for "Upload" mode
|
||||
let slct_hosted_file_id_li: string[] = $state([]);
|
||||
let slct_hosted_file_obj_li: any[] = $state([]);
|
||||
@@ -63,34 +63,34 @@
|
||||
let unified_file_li = $derived.by(() => {
|
||||
const entry = $lq__journal_entry_obj;
|
||||
if (!entry) return [];
|
||||
|
||||
|
||||
let files: any[] = [];
|
||||
|
||||
|
||||
// 1. Check new linked_li_json first
|
||||
if (entry.linked_li_json && Array.isArray(entry.linked_li_json)) {
|
||||
files = [...entry.linked_li_json];
|
||||
}
|
||||
|
||||
|
||||
// 2. Merge with legacy hosted_file_kv if not already present
|
||||
if (entry.data_json?.hosted_file_kv) {
|
||||
Object.entries(entry.data_json.hosted_file_kv).forEach(([id, obj]: [string, any]) => {
|
||||
if (!files.find(f => (f.hosted_file_id_random || f.id || f.hosted_file_id) === id)) {
|
||||
if (!files.find(f => (f.hosted_file_id || f.id || f.hosted_file_id) === id)) {
|
||||
files.push({ ...obj, id: id });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return files;
|
||||
});
|
||||
|
||||
async function update_journal_entry(updated_files: any[]) {
|
||||
let data_kv: key_val = {
|
||||
let data_kv: key_val = {
|
||||
linked_li_json: JSON.stringify(updated_files),
|
||||
// Maintain data_json.hosted_file_kv for backward compatibility
|
||||
data_json: {
|
||||
...$lq__journal_entry_obj?.data_json,
|
||||
hosted_file_kv: Object.fromEntries(
|
||||
updated_files.map(f => [f.hosted_file_id_random || f.id || f.hosted_file_id, f])
|
||||
updated_files.map(f => [f.hosted_file_id || f.id || f.hosted_file_id, f])
|
||||
)
|
||||
}
|
||||
};
|
||||
@@ -108,16 +108,16 @@
|
||||
}
|
||||
|
||||
// *** Effects for File Management
|
||||
|
||||
|
||||
// Handle "Select Existing" completion
|
||||
$effect(() => {
|
||||
if ($lq__journal_entry_obj && slct_hosted_file_id && slct_hosted_file_obj) {
|
||||
const new_file = { ...slct_hosted_file_obj, id: slct_hosted_file_id };
|
||||
const updated_li = [...unified_file_li, new_file];
|
||||
|
||||
|
||||
slct_hosted_file_id = null;
|
||||
slct_hosted_file_obj = null;
|
||||
|
||||
|
||||
update_journal_entry(updated_li);
|
||||
}
|
||||
});
|
||||
@@ -130,19 +130,19 @@
|
||||
id: id
|
||||
}));
|
||||
const updated_li = [...unified_file_li, ...new_files];
|
||||
|
||||
|
||||
slct_hosted_file_id_li = [];
|
||||
upload_complete = false;
|
||||
|
||||
|
||||
update_journal_entry(updated_li);
|
||||
}
|
||||
});
|
||||
|
||||
async function handle_remove_file(file_id: string) {
|
||||
if (!confirm('Are you sure you want to remove this file attachment?')) return;
|
||||
|
||||
const updated_li = unified_file_li.filter(f => (f.hosted_file_id_random || f.id || f.hosted_file_id) !== file_id);
|
||||
|
||||
|
||||
const updated_li = unified_file_li.filter(f => (f.hosted_file_id || f.id || f.hosted_file_id) !== file_id);
|
||||
|
||||
// Also perform physical/orphan deletion if admin
|
||||
if ($ae_loc.administrator_access) {
|
||||
await core_func.delete_ae_obj_id__hosted_file({
|
||||
@@ -155,12 +155,12 @@
|
||||
log_lvl
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
await update_journal_entry(updated_li);
|
||||
}
|
||||
|
||||
async function download_file(file: any) {
|
||||
const file_id = file.hosted_file_id_random || file.id || file.hosted_file_id;
|
||||
const file_id = file.hosted_file_id || file.id || file.hosted_file_id;
|
||||
ae_promises[file_id] = api.download_hosted_file({
|
||||
api_cfg: $ae_api,
|
||||
hosted_file_id: file_id,
|
||||
@@ -201,7 +201,7 @@
|
||||
{#if unified_file_li.length}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{#each unified_file_li as file}
|
||||
{@const file_id = file.hosted_file_id_random || file.id || file.hosted_file_id}
|
||||
{@const file_id = file.hosted_file_id || file.id || file.hosted_file_id}
|
||||
<div class="flex items-center justify-between p-3 rounded-xl bg-surface-50-950 border border-surface-500/10 group hover:border-primary-500 transition-all shadow-sm">
|
||||
<div class="flex items-center gap-3 overflow-hidden">
|
||||
<div class="text-primary-500 shrink-0">
|
||||
@@ -214,8 +214,8 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<button
|
||||
class="btn btn-sm variant-soft-primary"
|
||||
<button
|
||||
class="btn btn-sm variant-soft-primary"
|
||||
onclick={() => download_file(file)}
|
||||
title="Download file"
|
||||
>
|
||||
@@ -225,10 +225,10 @@
|
||||
<Download size="1.1em" />
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
|
||||
{#if $ae_loc.edit_mode}
|
||||
<button
|
||||
class="btn btn-sm variant-soft-error"
|
||||
<button
|
||||
class="btn btn-sm variant-soft-error"
|
||||
onclick={() => handle_remove_file(file_id)}
|
||||
title="Remove attachment"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user