General clean up the _random appends. About to work on a new Hosted File Download Svelte component.

This commit is contained in:
Scott Idem
2026-02-03 10:27:01 -05:00
parent d753c8e47a
commit 1ae7b5642d
9 changed files with 211 additions and 650 deletions

View File

@@ -298,7 +298,6 @@ export const properties_to_save = [
'url',
'url_text',
'hosted_file_id',
'hosted_file_id_random',
'file_path',
'filename',
'file_extension',

View File

@@ -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>

View File

@@ -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>

View File

@@ -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}
:

View File

@@ -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;

View File

@@ -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 = [

View File

@@ -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;

View File

@@ -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

View File

@@ -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"
>