More work on encryption of content and history.
This commit is contained in:
@@ -606,8 +606,9 @@ export function db_save_ae_obj_li__journal(
|
|||||||
updated_on: obj.updated_on,
|
updated_on: obj.updated_on,
|
||||||
|
|
||||||
// Generated fields for sorting locally only
|
// Generated fields for sorting locally only
|
||||||
tmp_sort_1: `${obj.group}_${obj.priority}_${obj.sort}_${obj.updated_on}_${obj.created_on}`,
|
tmp_sort_1: `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${obj.sort ?? '0'}_${obj.updated_on}_${obj.created_on}`,
|
||||||
tmp_sort_2: `${obj.group}_${obj.priority}_${obj.sort}_${obj.updated_on ?? obj.created_on}`,
|
tmp_sort_2: `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${obj.sort ?? '0'}_${obj.updated_on ?? obj.created_on}`,
|
||||||
|
tmp_sort_3: `${obj.group ?? '0'}_${obj.priority ? 1 : 0}_${obj.sort ?? '0'}_${obj.name}_${obj.updated_on ?? obj.created_on}`,
|
||||||
// tmp_sort_1: `${obj.original_datetime}_${obj.group}_${obj.priority}_${obj.sort}`,
|
// tmp_sort_1: `${obj.original_datetime}_${obj.group}_${obj.priority}_${obj.sort}`,
|
||||||
// tmp_sort_2: `${obj.group}_${obj.original_datetime}_${obj.priority}_${obj.sort}`,
|
// tmp_sort_2: `${obj.group}_${obj.original_datetime}_${obj.priority}_${obj.sort}`,
|
||||||
|
|
||||||
|
|||||||
@@ -356,6 +356,20 @@ export async function db_save_ae_obj_li__journal_entry(
|
|||||||
content_md_html = await marked.parse(content_cleaned ?? '') ?? null;
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
let obj_record = {
|
let obj_record = {
|
||||||
id: obj.journal_entry_id_random,
|
id: obj.journal_entry_id_random,
|
||||||
journal_entry_id: obj.journal_entry_id_random,
|
journal_entry_id: obj.journal_entry_id_random,
|
||||||
@@ -397,6 +411,12 @@ export async function db_save_ae_obj_li__journal_entry(
|
|||||||
content_json: obj.content_json,
|
content_json: obj.content_json,
|
||||||
content_encrypted: obj.content_encrypted,
|
content_encrypted: obj.content_encrypted,
|
||||||
|
|
||||||
|
history: obj.history,
|
||||||
|
history_md_html: history_md_html,
|
||||||
|
history_encrypted: obj.history_encrypted,
|
||||||
|
|
||||||
|
passcode_hash: obj.passcode_hash,
|
||||||
|
|
||||||
// url: obj.url,
|
// url: obj.url,
|
||||||
// url_text: obj.url_text,
|
// url_text: obj.url_text,
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ let journals_session_data_struct: key_val = {
|
|||||||
show__modal_view__journal_entry_id: null,
|
show__modal_view__journal_entry_id: null,
|
||||||
show__modal_edit__journal_entry_id: null,
|
show__modal_edit__journal_entry_id: null,
|
||||||
|
|
||||||
|
show__content__journal_entry_history: false,
|
||||||
|
|
||||||
journal: {
|
journal: {
|
||||||
edit: false,
|
edit: false,
|
||||||
edit_kv: {},
|
edit_kv: {},
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export interface Journal {
|
|||||||
// Generated fields for sorting locally only
|
// Generated fields for sorting locally only
|
||||||
tmp_sort_1?: null|string;
|
tmp_sort_1?: null|string;
|
||||||
tmp_sort_2?: null|string;
|
tmp_sort_2?: null|string;
|
||||||
|
tmp_sort_3?: null|string;
|
||||||
|
|
||||||
// Additional fields for convenience (database views)
|
// Additional fields for convenience (database views)
|
||||||
file_count?: null|number; // Only files directly under a journal
|
file_count?: null|number; // Only files directly under a journal
|
||||||
@@ -198,6 +199,11 @@ export interface Journal_Entry {
|
|||||||
content_json?: null|string;
|
content_json?: null|string;
|
||||||
content_encrypted?: null|string; // This is the encrypted content of the journal entry
|
content_encrypted?: null|string; // This is the encrypted content of the journal entry
|
||||||
|
|
||||||
|
history?: null|string; // This is the history of the journal entry; a log
|
||||||
|
history_encrypted?: null|string; // This is the encrypted history of the journal entry
|
||||||
|
|
||||||
|
passcode_hash?: null|string; // This is the passcode hash for the journal entry to look up the passcode
|
||||||
|
|
||||||
start_datetime?: null|Date;
|
start_datetime?: null|Date;
|
||||||
end_datetime?: null|Date;
|
end_datetime?: null|Date;
|
||||||
timezone?: null|string;
|
timezone?: null|string;
|
||||||
@@ -239,6 +245,7 @@ export interface Journal_Entry {
|
|||||||
// Generated fields for sorting locally only
|
// Generated fields for sorting locally only
|
||||||
tmp_sort_1?: null|string;
|
tmp_sort_1?: null|string;
|
||||||
tmp_sort_2?: null|string;
|
tmp_sort_2?: null|string;
|
||||||
|
tmp_sort_3?: null|string;
|
||||||
|
|
||||||
// Additional fields for convenience (database views)
|
// Additional fields for convenience (database views)
|
||||||
file_count?: null|number; // Only files directly under a journal
|
file_count?: null|number; // Only files directly under a journal
|
||||||
@@ -299,7 +306,7 @@ export class MySubClassedDexie extends Dexie {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('ae_journals_db');
|
super('ae_journals_db');
|
||||||
this.version(1).stores({
|
this.version(3).stores({
|
||||||
journal: `
|
journal: `
|
||||||
id, journal_id,
|
id, journal_id,
|
||||||
code,
|
code,
|
||||||
@@ -309,7 +316,7 @@ export class MySubClassedDexie extends Dexie {
|
|||||||
name,
|
name,
|
||||||
start_datetime, end_datetime,
|
start_datetime, end_datetime,
|
||||||
timezone,
|
timezone,
|
||||||
tmp_sort_1, tmp_sort_2,
|
tmp_sort_1, tmp_sort_2, tmp_sort_3,
|
||||||
enable, hide, priority, sort, group, notes, created_on, updated_on`,
|
enable, hide, priority, sort, group, notes, created_on, updated_on`,
|
||||||
journal_entry: `
|
journal_entry: `
|
||||||
id, journal_entry_id,
|
id, journal_entry_id,
|
||||||
@@ -320,7 +327,7 @@ export class MySubClassedDexie extends Dexie {
|
|||||||
name,
|
name,
|
||||||
start_datetime, end_datetime,
|
start_datetime, end_datetime,
|
||||||
timezone,
|
timezone,
|
||||||
tmp_sort_1, tmp_sort_2,
|
tmp_sort_1, tmp_sort_2, tmp_sort_3,
|
||||||
enable, hide, priority, sort, group, notes, created_on, updated_on`,
|
enable, hide, priority, sort, group, notes, created_on, updated_on`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,10 +45,13 @@ let lq__journal_obj_li = $derived(liveQuery(async () => {
|
|||||||
let results = await db_journals.journal
|
let results = await db_journals.journal
|
||||||
.where('person_id')
|
.where('person_id')
|
||||||
.equals($ae_loc.person_id)
|
.equals($ae_loc.person_id)
|
||||||
|
// .sortBy('group')
|
||||||
|
// .sortBy('priority')
|
||||||
|
// .sortBy('sort')
|
||||||
.reverse()
|
.reverse()
|
||||||
.sortBy('tmp_sort_2')
|
.sortBy('tmp_sort_3')
|
||||||
|
|
||||||
// .orderBy('tmp_sort_2')
|
// .orderBy('tmp_sort_3')
|
||||||
// .reverse()
|
// .reverse()
|
||||||
// .toArray()
|
// .toArray()
|
||||||
|
|
||||||
|
|||||||
@@ -661,7 +661,7 @@ async function handle_update_journal() {
|
|||||||
data_kv: data_kv,
|
data_kv: data_kv,
|
||||||
log_lvl: log_lvl
|
log_lvl: log_lvl
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
alert('Journal sort order incremented!');
|
// alert('Journal sort order incremented!');
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error('Error updating journal sort order:', error);
|
console.error('Error updating journal sort order:', error);
|
||||||
alert('Failed to update journal sort order.');
|
alert('Failed to update journal sort order.');
|
||||||
@@ -691,7 +691,7 @@ async function handle_update_journal() {
|
|||||||
data_kv: data_kv,
|
data_kv: data_kv,
|
||||||
log_lvl: log_lvl
|
log_lvl: log_lvl
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
alert('Journal sort order decremented!');
|
// alert('Journal sort order decremented!');
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error('Error updating journal sort order:', error);
|
console.error('Error updating journal sort order:', error);
|
||||||
alert('Failed to update journal sort order.');
|
alert('Failed to update journal sort order.');
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Eye, EyeOff,
|
Eye, EyeOff,
|
||||||
Flag, FlagOff, FileX, Fingerprint,
|
Flag, FlagOff, FileX, Fingerprint,
|
||||||
Globe, Group,
|
Globe, Group,
|
||||||
|
History,
|
||||||
LockKeyhole, LockKeyholeOpen,
|
LockKeyhole, LockKeyholeOpen,
|
||||||
MessageSquareWarning, Minus,
|
MessageSquareWarning, Minus,
|
||||||
NotebookPen, NotebookText, NotepadTextDashed,
|
NotebookPen, NotebookText, NotepadTextDashed,
|
||||||
@@ -145,6 +146,8 @@ async function update_journal_entry() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_lvl = 1;
|
||||||
|
|
||||||
let data_kv: key_val = {
|
let data_kv: key_val = {
|
||||||
alert: tmp_entry_obj?.alert,
|
alert: tmp_entry_obj?.alert,
|
||||||
personal: tmp_entry_obj?.personal,
|
personal: tmp_entry_obj?.personal,
|
||||||
@@ -162,25 +165,107 @@ async function update_journal_entry() {
|
|||||||
category_code: tmp_entry_obj?.category_code,
|
category_code: tmp_entry_obj?.category_code,
|
||||||
content: tmp_entry_obj?.content,
|
content: tmp_entry_obj?.content,
|
||||||
content_encrypted: null, // This should only be generated below.
|
content_encrypted: null, // This should only be generated below.
|
||||||
|
history: tmp_entry_obj?.history,
|
||||||
|
history_encrypted: null, // This should only be generated below.
|
||||||
|
|
||||||
group: tmp_entry_obj?.group,
|
group: tmp_entry_obj?.group,
|
||||||
archive_on: tmp_entry_obj?.archive_on,
|
archive_on: tmp_entry_obj?.archive_on,
|
||||||
name: tmp_entry_obj?.name,
|
name: tmp_entry_obj?.name,
|
||||||
tags: tmp_entry_obj?.tags,
|
tags: tmp_entry_obj?.tags,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (tmp_entry_obj?.content) {
|
||||||
|
|
||||||
|
let { left_over_string, cut_out_string } = handle_cut_string(tmp_entry_obj?.content);
|
||||||
|
if (!tmp_entry_obj?.private) {
|
||||||
|
// let new_content_string = '';
|
||||||
|
// let cut_content_string = '';
|
||||||
|
// {new_content_string, cut_content_string}
|
||||||
|
|
||||||
|
data_kv.content = left_over_string;
|
||||||
|
data_kv.history = data_kv.history + '\n\n' + cut_out_string;
|
||||||
|
} else if (tmp_entry_obj?.private) {
|
||||||
|
// Need to decrypt the current content and current history. Assume the history has not been decrypted yet.
|
||||||
|
|
||||||
|
if (log_lvl) {
|
||||||
|
console.log('TEST: Decrypting the history before saving it...');
|
||||||
|
}
|
||||||
|
decrypted_history = await handle_decrypt_string(tmp_entry_obj?.history_encrypted, journal_key);
|
||||||
|
data_kv.history = decrypted_history + '\n\n' + cut_out_string;
|
||||||
|
|
||||||
|
if (log_lvl) {
|
||||||
|
console.log('TEST: Encrypting the history before saving it...');
|
||||||
|
}
|
||||||
|
let encrypted_combined_data = await handle_encrypt_string(data_kv.history, journal_key);
|
||||||
|
data_kv.history_encrypted = encrypted_combined_data;
|
||||||
|
data_kv.history = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Check if the content contains a set of special "cut" XML tags. Anything inside the tags will be moved (appended) to the history field. Anything outside the tags should stay. The content may need to be merged back together if something was cut out of the middle. If no closing tag is found, then cut everything to the end of the content. Be sure to prefix the new history content with a timestamp header in Markdown.
|
||||||
|
// // Example: Hello <cut>Old</cut> World!
|
||||||
|
// // Example header: # Cut on 2024-11-06T12:34:56Z
|
||||||
|
// if (tmp_entry_obj?.content) {
|
||||||
|
// const cutTag = '<cut>';
|
||||||
|
// const cutEndTag = '</cut>';
|
||||||
|
// const cutIndex = tmp_entry_obj.content.indexOf(cutTag);
|
||||||
|
// const cutEndIndex = tmp_entry_obj.content.indexOf(cutEndTag);
|
||||||
|
|
||||||
|
// if (cutIndex !== -1) {
|
||||||
|
// let current_timestamp = new Date().toISOString();
|
||||||
|
// if (cutEndIndex !== -1) {
|
||||||
|
// // Cut everything between the cut tags
|
||||||
|
// const cutContent = tmp_entry_obj.content.substring(cutIndex + cutTag.length, cutEndIndex);
|
||||||
|
// data_kv.history = data_kv.history + '\n\n' + `# Cut on ${current_timestamp}\n` + cutContent;
|
||||||
|
// data_kv.content = tmp_entry_obj.content.replace(cutTag + cutContent + cutEndTag, '');
|
||||||
|
// } else {
|
||||||
|
// // Cut everything after the cut tag
|
||||||
|
// const cutContent = tmp_entry_obj.content.substring(cutIndex + cutTag.length);
|
||||||
|
// data_kv.history = data_kv.history + '\n\n' + `# Cut on ${current_timestamp}\n` + cutContent;
|
||||||
|
// data_kv.content = tmp_entry_obj.content.substring(0, cutIndex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // We also need to support a Markdown variant of the cut tag: ~~cut cut~~.
|
||||||
|
// // Example: Hello ~~CUT: Old :CUT~~ World!
|
||||||
|
// // Example: Hello ~~:: Old ::~~ World!
|
||||||
|
// if (tmp_entry_obj?.content) {
|
||||||
|
// const cutTag = '~~::';
|
||||||
|
// const cutEndTag = '::~~';
|
||||||
|
// const cutIndex = tmp_entry_obj.content.indexOf(cutTag);
|
||||||
|
// const cutEndIndex = tmp_entry_obj.content.indexOf(cutEndTag);
|
||||||
|
|
||||||
|
// if (cutIndex !== -1) {
|
||||||
|
// let current_timestamp = new Date().toISOString();
|
||||||
|
// if (cutEndIndex !== -1) {
|
||||||
|
// // Cut everything between the cut tags
|
||||||
|
// const cutContent = tmp_entry_obj.content.substring(cutIndex + cutTag.length, cutEndIndex);
|
||||||
|
// data_kv.history = data_kv.history + '\n\n' + `# Cut on ${current_timestamp}\n` + cutContent;
|
||||||
|
// data_kv.content = tmp_entry_obj.content.replace(cutTag + cutContent + cutEndTag, '');
|
||||||
|
// } else {
|
||||||
|
// // Cut everything after the cut tag
|
||||||
|
// const cutContent = tmp_entry_obj.content.substring(cutIndex + cutTag.length);
|
||||||
|
// data_kv.history = data_kv.history + '\n\n' + `# Cut on ${current_timestamp}\n` + cutContent;
|
||||||
|
// data_kv.content = tmp_entry_obj.content.substring(0, cutIndex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (tmp_entry_obj?.content && tmp_entry_obj?.private) {
|
if (tmp_entry_obj?.content && tmp_entry_obj?.private) {
|
||||||
// console.log('TEST: Saving encrypted content', tmp_entry_obj?.content);
|
// console.log('TEST: Saving encrypted content', tmp_entry_obj?.content);
|
||||||
content = tmp_entry_obj?.content;
|
content = tmp_entry_obj?.content;
|
||||||
// console.log('TEST: journal_key', journal_key);
|
// console.log('TEST: journal_key', journal_key);
|
||||||
|
|
||||||
// Encrypt the content
|
// // Encrypt the content
|
||||||
let encrypted_base64 = await ae_util.encrypt_content(content, journal_key);
|
// let encrypted_base64 = await ae_util.encrypt_content(content, journal_key);
|
||||||
encrypted_base64_content = encrypted_base64.base64;
|
// encrypted_base64_content = encrypted_base64.base64;
|
||||||
encryption_iv = encrypted_base64.iv;
|
// encryption_iv = encrypted_base64.iv;
|
||||||
console.log(`IV: ${encryption_iv}; Encrypted: ${encrypted_base64_content}`);
|
// console.log(`IV: ${encryption_iv}; Encrypted: ${encrypted_base64_content}`);
|
||||||
|
|
||||||
// Combine the IV and encrypted content
|
// // Combine the IV and encrypted content
|
||||||
const combined_data = Array.from(encryption_iv).map(byte => byte.toString(16).padStart(2, '0')).join('') + ':' + encrypted_base64_content;
|
// const combined_data = Array.from(encryption_iv).map(byte => byte.toString(16).padStart(2, '0')).join('') + ':' + encrypted_base64_content;
|
||||||
|
|
||||||
|
let combined_data = await handle_encrypt_string(content, journal_key);
|
||||||
|
|
||||||
data_kv.content_encrypted = combined_data;
|
data_kv.content_encrypted = combined_data;
|
||||||
data_kv.content = null;
|
data_kv.content = null;
|
||||||
@@ -193,7 +278,7 @@ async function update_journal_entry() {
|
|||||||
decrypted_content = '';
|
decrypted_content = '';
|
||||||
} else if (tmp_entry_obj?.content_encrypted && !tmp_entry_obj?.private) {
|
} else if (tmp_entry_obj?.content_encrypted && !tmp_entry_obj?.private) {
|
||||||
console.log('TEST: Decrypting the content before saving it...');
|
console.log('TEST: Decrypting the content before saving it...');
|
||||||
await handle_decrypt_content();
|
decrypted_content = await handle_decrypt_string(tmp_entry_obj?.content_encrypted, journal_key);
|
||||||
data_kv.content = decrypted_content; // tmp_entry_obj.content
|
data_kv.content = decrypted_content; // tmp_entry_obj.content
|
||||||
decrypted_content = '';
|
decrypted_content = '';
|
||||||
// return false;
|
// return false;
|
||||||
@@ -298,6 +383,7 @@ let encrypted_base64_content: string = $state('');
|
|||||||
let encryption_iv: null|Uint8Array<ArrayBuffer> = $state(null);
|
let encryption_iv: null|Uint8Array<ArrayBuffer> = $state(null);
|
||||||
let decrypted_content: string = $state('');
|
let decrypted_content: string = $state('');
|
||||||
let trigger_decrypt: boolean = $state(false);
|
let trigger_decrypt: boolean = $state(false);
|
||||||
|
let decrypted_history: string = $state('');
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if ($lq__journal_obj?.passcode) {
|
if ($lq__journal_obj?.passcode) {
|
||||||
@@ -306,76 +392,194 @@ $effect(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(async () => {
|
// $effect(async () => {
|
||||||
if (tmp_entry_obj?.content_encrypted && trigger_decrypt) {
|
// if (tmp_entry_obj?.content_encrypted && trigger_decrypt) {
|
||||||
trigger_decrypt = false;
|
// trigger_decrypt = false;
|
||||||
|
|
||||||
handle_decrypt_content();
|
// handle_decrypt_content();
|
||||||
|
|
||||||
|
|
||||||
}
|
// }
|
||||||
|
|
||||||
// if (tmp_entry_obj?.content) {
|
// // if (tmp_entry_obj?.content) {
|
||||||
// content = tmp_entry_obj?.content;
|
// // content = tmp_entry_obj?.content;
|
||||||
|
|
||||||
// let encrypted_base64 = await ae_util.encrypt_content(content, journal_key);
|
// // let encrypted_base64 = await ae_util.encrypt_content(content, journal_key);
|
||||||
// encrypted_base64_content = encrypted_base64.base64;
|
// // encrypted_base64_content = encrypted_base64.base64;
|
||||||
// encryption_iv = encrypted_base64.iv;
|
// // encryption_iv = encrypted_base64.iv;
|
||||||
|
|
||||||
// let decrypted = await ae_util.decrypt_content(encrypted_base64_content, encryption_iv, journal_key);
|
// // let decrypted = await ae_util.decrypt_content(encrypted_base64_content, encryption_iv, journal_key);
|
||||||
// decrypted_content = decrypted;
|
// // decrypted_content = decrypted;
|
||||||
// if (log_lvl) {
|
// // if (log_lvl) {
|
||||||
// console.log('Decrypted content:', decrypted_content);
|
// // console.log('Decrypted content:', decrypted_content);
|
||||||
// }
|
// // }
|
||||||
// }
|
// // }
|
||||||
// console.log('Encrypted content:', base64);
|
// // console.log('Encrypted content:', base64);
|
||||||
// console.log('IV:', iv);
|
// // console.log('IV:', iv);
|
||||||
});
|
// });
|
||||||
|
|
||||||
async function handle_decrypt_content() {
|
// async function handle_decrypt_content() {
|
||||||
|
// log_lvl = 1;
|
||||||
|
// if (log_lvl) {
|
||||||
|
// console.log('TEST: handle_decrypt_content');
|
||||||
|
// }
|
||||||
|
// let combined_data = tmp_entry_obj?.content_encrypted;
|
||||||
|
// let [encryption_iv_hex, encrypted_base64_content] = combined_data.split(':');
|
||||||
|
// encryption_iv = new Uint8Array(encryption_iv_hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
|
||||||
|
// if (log_lvl) {
|
||||||
|
// console.log(`IV: ${encryption_iv}; Encrypted: ${encrypted_base64_content}`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let decrypted: string|null = null;
|
||||||
|
// try {
|
||||||
|
// decrypted = await ae_util.decrypt_content(encrypted_base64_content, encryption_iv, journal_key);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Error decrypting content:', error);
|
||||||
|
// alert('Failed to decrypt content. Please check the passcode.');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// // let decrypted = await ae_util.decrypt_content(encrypted_base64_content, encryption_iv, journal_key);
|
||||||
|
// // decrypted_content = 'XXX '+decrypted+' XXX';
|
||||||
|
|
||||||
|
// if (!decrypted) {
|
||||||
|
// alert('Failed to decrypt content. Please check the passcode.');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// decrypted_content = decrypted;
|
||||||
|
// if (log_lvl) {
|
||||||
|
// console.log('Decrypted content:', decrypted_content);
|
||||||
|
// }
|
||||||
|
// tmp_entry_obj.content = decrypted_content;
|
||||||
|
// // orig_entry_obj.content = decrypted_content;
|
||||||
|
// // tmp_entry_obj_changed = false;
|
||||||
|
|
||||||
|
// // tmp_entry_obj.content_encrypted = null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
async function handle_decrypt_string(encrypted_string: string, passcode: string) {
|
||||||
log_lvl = 1;
|
log_lvl = 1;
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log('TEST: handle_decrypt_content');
|
console.log(`TEST: handle_decrypt_string: ${passcode}`, encrypted_string);
|
||||||
}
|
}
|
||||||
let combined_data = tmp_entry_obj?.content_encrypted;
|
if (!encrypted_string) {
|
||||||
|
console.log('TEST: No encrypted string provided');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (!passcode) {
|
||||||
|
console.log('TEST: No journal key provided');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let combined_data = encrypted_string;
|
||||||
let [encryption_iv_hex, encrypted_base64_content] = combined_data.split(':');
|
let [encryption_iv_hex, encrypted_base64_content] = combined_data.split(':');
|
||||||
encryption_iv = new Uint8Array(encryption_iv_hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
|
encryption_iv = new Uint8Array(encryption_iv_hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
|
||||||
if (log_lvl) {
|
if (log_lvl) {
|
||||||
console.log(`IV: ${encryption_iv}; Encrypted: ${encrypted_base64_content}`);
|
console.log(`IV: ${encryption_iv}; Encrypted: ${encrypted_base64_content}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let decrypted: string|null = null;
|
// Decrypt the string using the journal key
|
||||||
|
let decrypted_string = '';
|
||||||
try {
|
try {
|
||||||
decrypted = await ae_util.decrypt_content(encrypted_base64_content, encryption_iv, journal_key);
|
decrypted_string = await ae_util.decrypt_content(encrypted_base64_content, encryption_iv, passcode);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error decrypting content:', error);
|
console.error('Error decrypting content:', error);
|
||||||
alert('Failed to decrypt content. Please check the passcode.');
|
alert('Failed to decrypt content. Please check the passcode.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// let decrypted = await ae_util.decrypt_content(encrypted_base64_content, encryption_iv, journal_key);
|
return decrypted_string;
|
||||||
// decrypted_content = 'XXX '+decrypted+' XXX';
|
|
||||||
|
|
||||||
if (!decrypted) {
|
|
||||||
alert('Failed to decrypt content. Please check the passcode.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
decrypted_content = decrypted;
|
|
||||||
if (log_lvl) {
|
|
||||||
console.log('Decrypted content:', decrypted_content);
|
|
||||||
}
|
|
||||||
tmp_entry_obj.content = decrypted_content;
|
|
||||||
// orig_entry_obj.content = decrypted_content;
|
|
||||||
// tmp_entry_obj_changed = false;
|
|
||||||
|
|
||||||
// tmp_entry_obj.content_encrypted = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handle_encrypt_string(text_string: string, passcode: string) {
|
||||||
|
log_lvl = 1;
|
||||||
|
if (log_lvl) {
|
||||||
|
console.log('TEST: handle_encrypt_string');
|
||||||
|
}
|
||||||
|
if (!text_string) {
|
||||||
|
console.log('TEST: No text string provided');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (!passcode) {
|
||||||
|
console.log('TEST: No journal key provided');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt the string using the journal key
|
||||||
|
let encrypted_base64 = await ae_util.encrypt_content(text_string, passcode);
|
||||||
|
let encrypted_base64_content = encrypted_base64.base64;
|
||||||
|
let encryption_iv = encrypted_base64.iv;
|
||||||
|
console.log(`IV: ${encryption_iv}; Encrypted: ${encrypted_base64_content}`);
|
||||||
|
|
||||||
|
// Combine the IV and encrypted content
|
||||||
|
const combined_data = Array.from(encryption_iv).map(byte => byte.toString(16).padStart(2, '0')).join('') + ':' + encrypted_base64_content;
|
||||||
|
|
||||||
|
return combined_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return new_string and cut_string
|
||||||
|
function handle_cut_string(old_string: string) {
|
||||||
|
// Check if the string contains a set of special "cut" XML tags or Markdown. Anything inside the tags or Markdown will be moved (appended) to the history field. Anything outside the tags should stay. The string may need to be merged back together if something was cut out of the middle. If no closing tag is found, then cut everything to the end of the string. Be sure to prefix the new history sting with a timestamp header in Markdown.
|
||||||
|
// Example: Hello <cut>Old</cut> World!
|
||||||
|
// Example header: # Cut on 2024-11-06T12:34:56Z
|
||||||
|
// We also need to support a Markdown variant of the cut tag: ~~cut cut~~.
|
||||||
|
// Example: Hello ~~CUT: Old :CUT~~ World!
|
||||||
|
// Example: Hello ~~:: Old ::~~ World!
|
||||||
|
|
||||||
|
let left_over_string = old_string; // Will be for the updated content field
|
||||||
|
let cut_out_string = ''; // Will be for the history field
|
||||||
|
|
||||||
|
if (old_string) {
|
||||||
|
let cut_tag = '<cut>';
|
||||||
|
let cut_end_tag = '</cut>';
|
||||||
|
let cut_index = old_string.indexOf(cut_tag);
|
||||||
|
let cut_end_index = old_string.indexOf(cut_end_tag);
|
||||||
|
let cut_prefix = `# Cut on ${new Date().toISOString()}\n`;
|
||||||
|
|
||||||
|
if (cut_index !== -1) {
|
||||||
|
if (cut_end_index !== -1) {
|
||||||
|
// Cut everything between the cut tags
|
||||||
|
const cut_content = old_string.substring(cut_index + cut_tag.length, cut_end_index);
|
||||||
|
cut_out_string = cut_prefix + cut_content;
|
||||||
|
left_over_string = old_string.replace(cut_tag + cut_content + cut_end_tag, '');
|
||||||
|
} else {
|
||||||
|
// Cut everything after the cut tag
|
||||||
|
const cut_content = old_string.substring(cut_index + cut_tag.length);
|
||||||
|
cut_out_string = cut_prefix + cut_content;
|
||||||
|
left_over_string = old_string.substring(0, cut_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cut_tag = '~~::';
|
||||||
|
cut_end_tag = '::~~';
|
||||||
|
cut_index = old_string.indexOf(cut_tag);
|
||||||
|
cut_end_index = old_string.indexOf(cut_end_tag);
|
||||||
|
cut_prefix = `# Cut on ${new Date().toISOString()}\n`;
|
||||||
|
if (cut_index !== -1) {
|
||||||
|
if (cut_end_index !== -1) {
|
||||||
|
// Cut everything between the cut tags
|
||||||
|
const cut_content = old_string.substring(cut_index + cut_tag.length, cut_end_index);
|
||||||
|
cut_out_string = cut_prefix + cut_content;
|
||||||
|
left_over_string = old_string.replace(cut_tag + cut_content + cut_end_tag, '');
|
||||||
|
} else {
|
||||||
|
// Cut everything after the cut tag
|
||||||
|
const cut_content = old_string.substring(cut_index + cut_tag.length);
|
||||||
|
cut_out_string = cut_prefix + cut_content;
|
||||||
|
left_over_string = old_string.substring(0, cut_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return { left_over_string, cut_out_string };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<section class="svelte_component ae_section ae_view journal_entry_obj view__journal_entry_obj bg-white flex flex-col flex-grow items-center justify-center rounded-lg w-full h-full p-2 m-2 space-y-2" bind:clientHeight={$ae_loc.iframe_height_modal_body}>
|
<section class="svelte_component ae_section ae_view journal_entry_obj view__journal_entry_obj bg-white flex flex-col flex-grow items-center justify-center rounded-lg w-full h-full p-2 m-2 space-y-2" bind:clientHeight={$ae_loc.iframe_height_modal_body}>
|
||||||
|
|
||||||
|
|
||||||
{#if $lq__journal_entry_obj}
|
{#if $lq__journal_entry_obj}
|
||||||
|
|
||||||
<header class="ae_header journal_entry__header flex flex-row flex-wrap gap-2 items-center justify-between w-full">
|
<header class="ae_header journal_entry__header flex flex-row flex-wrap gap-2 items-center justify-between w-full">
|
||||||
|
|
||||||
<div class="flex flex-row flex-wrap gap-2 items-center justify-start">
|
<div class="flex flex-row flex-wrap gap-2 items-center justify-start">
|
||||||
@@ -421,7 +625,7 @@ async function handle_decrypt_content() {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={tmp_entry_obj.name}
|
bind:value={tmp_entry_obj.name}
|
||||||
class="input input-bordered"
|
class="input input-bordered w-96"
|
||||||
placeholder="Journal Entry Name"
|
placeholder="Journal Entry Name"
|
||||||
title="Edit the name of this journal entry"
|
title="Edit the name of this journal entry"
|
||||||
/>
|
/>
|
||||||
@@ -670,7 +874,8 @@ async function handle_decrypt_content() {
|
|||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div
|
|
||||||
|
<section
|
||||||
class="flex flex-row flex-wrap gap-1 items-center justify-evenly w-full max-w-sm "
|
class="flex flex-row flex-wrap gap-1 items-center justify-evenly w-full max-w-sm "
|
||||||
>
|
>
|
||||||
<!-- Entry alert status -->
|
<!-- Entry alert status -->
|
||||||
@@ -762,7 +967,8 @@ async function handle_decrypt_content() {
|
|||||||
// tmp_entry_obj.content = null;
|
// tmp_entry_obj.content = null;
|
||||||
} else if (tmp_entry_obj?.private && tmp_entry_obj?.content_encrypted) {
|
} else if (tmp_entry_obj?.private && tmp_entry_obj?.content_encrypted) {
|
||||||
if (confirm('Are you sure you want to decrypt the content to view/edit?')) {
|
if (confirm('Are you sure you want to decrypt the content to view/edit?')) {
|
||||||
trigger_decrypt = true;
|
// trigger_decrypt = true;
|
||||||
|
handle_decrypt_content();
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -885,38 +1091,41 @@ async function handle_decrypt_content() {
|
|||||||
{/if}
|
{/if}
|
||||||
</button> -->
|
</button> -->
|
||||||
|
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
{#if (!$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id])}
|
|
||||||
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
||||||
<div
|
<!-- ondblclick={() => {
|
||||||
ondblclick={() => {
|
|
||||||
if ($ae_loc.trusted_access && $ae_loc.edit_mode) {
|
if ($ae_loc.trusted_access && $ae_loc.edit_mode) {
|
||||||
// Toggle edit mode
|
// Toggle edit mode
|
||||||
$journals_loc.entry.edit = !$journals_loc.entry.edit;
|
$journals_loc.entry.edit = !$journals_loc.entry.edit;
|
||||||
$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id] = $journals_loc.entry.edit;
|
$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id] = $journals_loc.entry.edit;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
role={$ae_loc.edit_mode ? 'button' : 'article'}
|
role={$ae_loc.edit_mode ? 'button' : 'article'}
|
||||||
tabindex={$ae_loc.edit_mode ? 0 : -1}
|
tabindex={$ae_loc.edit_mode ? 0 : -1} -->
|
||||||
|
<section
|
||||||
class="
|
class="
|
||||||
flex-grow
|
flex-grow
|
||||||
flex flex-col items-center justify-center
|
basis-full
|
||||||
|
flex flex-col flex-wrap items-center justify-center
|
||||||
|
h-full min-h-max max-h-full
|
||||||
w-full max-w-6xl
|
w-full max-w-6xl
|
||||||
"
|
"
|
||||||
|
|
||||||
>
|
>
|
||||||
|
{#if (!$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id])}
|
||||||
|
|
||||||
|
|
||||||
<article
|
<article
|
||||||
class:border-orange-200={$ae_loc.edit_mode}
|
class:border-orange-200={$ae_loc.edit_mode}
|
||||||
class:hover:border-orange-500={$ae_loc.edit_mode}
|
class:hover:border-orange-500={$ae_loc.edit_mode}
|
||||||
class="
|
class="
|
||||||
space-y-1
|
|
||||||
prose
|
|
||||||
flex-grow
|
flex-grow
|
||||||
h-full min-h-48 w-full min-w-full max-w-6xl
|
basis-full
|
||||||
|
h-full min-h-max max-h-full
|
||||||
|
w-full min-w-full max-w-6xl
|
||||||
p-2
|
p-2
|
||||||
|
space-y-1
|
||||||
font-mono
|
font-mono
|
||||||
!bg-slate-100 !text-gray-900
|
!bg-slate-100 !text-gray-900
|
||||||
dark:!bg-slate-900 dark:!text-gray-100
|
dark:!bg-slate-900 dark:!text-gray-100
|
||||||
@@ -924,6 +1133,7 @@ async function handle_decrypt_content() {
|
|||||||
shadow-md rounded-lg
|
shadow-md rounded-lg
|
||||||
border border-gray-200 dark:border-gray-700
|
border border-gray-200 dark:border-gray-700
|
||||||
hover:border-gray-500 dark:hover:border-gray-500
|
hover:border-gray-500 dark:hover:border-gray-500
|
||||||
|
prose
|
||||||
prose-h1:underline prose-h1:decoration-double
|
prose-h1:underline prose-h1:decoration-double
|
||||||
prose-h2:underline
|
prose-h2:underline
|
||||||
prose-h1:text-2xl prose-h2:text-xl prose-h3:text-lg
|
prose-h1:text-2xl prose-h2:text-xl prose-h3:text-lg
|
||||||
@@ -954,15 +1164,19 @@ async function handle_decrypt_content() {
|
|||||||
<!-- {@html encrypt_content($lq__journal_entry_obj?.content, journal_key)} -->
|
<!-- {@html encrypt_content($lq__journal_entry_obj?.content, journal_key)} -->
|
||||||
<!-- --{@html encrypted_base64_content}-- -->
|
<!-- --{@html encrypted_base64_content}-- -->
|
||||||
<!-- {@html marked.parse($lq__journal_entry_obj?.content)} -->
|
<!-- {@html marked.parse($lq__journal_entry_obj?.content)} -->
|
||||||
</div>
|
|
||||||
{:else if ($journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id])}
|
{:else if ($journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id])}
|
||||||
<!-- && !($lq__journal_entry_obj?.content_encrypted && decrypted_content)) -->
|
<!-- && !($lq__journal_entry_obj?.content_encrypted && decrypted_content)) -->
|
||||||
<!-- class="flex flex-row flex-wrap gap-1 items-center justify-center w-full max-w-sm" -->
|
<!-- class="flex flex-row flex-wrap gap-1 items-center justify-center w-full max-w-sm" -->
|
||||||
{#if ($lq__journal_entry_obj?.content_encrypted && !decrypted_content)}
|
{#if ($lq__journal_entry_obj?.content_encrypted && !decrypted_content)}
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
flex-grow min-h-48 h-full w-full
|
flex-grow
|
||||||
|
basis-full
|
||||||
|
h-full min-h-max max-h-full
|
||||||
|
w-full min-w-full max-w-6xl
|
||||||
p-2
|
p-2
|
||||||
|
space-y-1
|
||||||
bg-red-100 text-gray-900
|
bg-red-100 text-gray-900
|
||||||
dark:bg-red-900 dark:text-gray-100
|
dark:bg-red-900 dark:text-gray-100
|
||||||
shadow-lg rounded-lg
|
shadow-lg rounded-lg
|
||||||
@@ -991,31 +1205,33 @@ async function handle_decrypt_content() {
|
|||||||
</button> -->
|
</button> -->
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
|
||||||
|
<!-- <div
|
||||||
class="
|
class="
|
||||||
flex-grow
|
flex-grow
|
||||||
flex flex-col items-center justify-center
|
h-full max-h-full
|
||||||
w-full max-w-6xl
|
w-full min-w-full max-w-6xl"
|
||||||
"
|
> -->
|
||||||
>
|
|
||||||
|
|
||||||
<textarea
|
<textarea
|
||||||
bind:value={tmp_entry_obj.content}
|
bind:value={tmp_entry_obj.content}
|
||||||
ondblclick={() => {
|
ondblclick={() => {
|
||||||
if ($ae_loc.trusted_access && $ae_loc.edit_mode) {
|
// if ($ae_loc.trusted_access && $ae_loc.edit_mode) {
|
||||||
// Toggle edit mode
|
// // Toggle edit mode
|
||||||
$journals_loc.entry.edit = !$journals_loc.entry.edit;
|
// $journals_loc.entry.edit = !$journals_loc.entry.edit;
|
||||||
$journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id] = $journals_loc.entry.edit;
|
// $journals_loc.entry.edit_kv[$lq__journal_entry_obj?.journal_entry_id] = $journals_loc.entry.edit;
|
||||||
}
|
// }
|
||||||
}}
|
}}
|
||||||
disabled={tmp_entry_obj.content_encrypted && !decrypted_content}
|
disabled={tmp_entry_obj.content_encrypted && !decrypted_content}
|
||||||
class:border-orange-200={$ae_loc.edit_mode}
|
class:border-orange-200={$ae_loc.edit_mode}
|
||||||
class:hover:border-orange-500={$ae_loc.edit_mode}
|
class:hover:border-orange-500={$ae_loc.edit_mode}
|
||||||
class="
|
class="
|
||||||
space-y-1
|
|
||||||
flex-grow
|
flex-grow
|
||||||
h-full min-h-max max-h-full w-full min-w-full max-w-6xl
|
flex-shrink-0
|
||||||
|
basis-full
|
||||||
|
h-max min-h-max max-h-full
|
||||||
|
w-full min-w-full max-w-6xl
|
||||||
p-2
|
p-2
|
||||||
|
space-y-1
|
||||||
font-mono
|
font-mono
|
||||||
bg-slate-100 text-gray-900
|
bg-slate-100 text-gray-900
|
||||||
dark:bg-slate-900 dark:text-gray-100
|
dark:bg-slate-900 dark:text-gray-100
|
||||||
@@ -1055,16 +1271,149 @@ async function handle_decrypt_content() {
|
|||||||
Updated obj? {updated_obj} -->
|
Updated obj? {updated_obj} -->
|
||||||
|
|
||||||
<!-- && $lq__journal_entry_obj?.updated_on !== orig_entry_obj?.updated_on -->
|
<!-- && $lq__journal_entry_obj?.updated_on !== orig_entry_obj?.updated_on -->
|
||||||
|
<!-- </div> -->
|
||||||
|
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- If there is history, then we want a toggle button to show and hide the history. -->
|
||||||
|
{#if ($lq__journal_entry_obj?.history || $lq__journal_entry_obj?.history_encrypted)}
|
||||||
|
<div
|
||||||
|
class="
|
||||||
|
flex flex-col flex-wrap gap-1 items-center justify-center h-full w-full
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={async () => {
|
||||||
|
if ($journals_sess.show__content__journal_entry_history == 'view') {
|
||||||
|
$journals_sess.show__content__journal_entry_history = 'hide';
|
||||||
|
} else if ($journals_sess.show__content__journal_entry_history == 'edit') {
|
||||||
|
$journals_sess.show__content__journal_entry_history = 'hide';
|
||||||
|
} else if ($journals_sess.show__content__journal_entry_history == 'hide') {
|
||||||
|
$journals_sess.show__content__journal_entry_history = 'view';
|
||||||
|
} else if (!$journals_sess.show__content__journal_entry_history) {
|
||||||
|
$journals_sess.show__content__journal_entry_history = 'view';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tmp_entry_obj?.history && tmp_entry_obj?.history_encrypted) {
|
||||||
|
decrypted_history = await handle_decrypt_string(tmp_entry_obj.history_encrypted, journal_key);
|
||||||
|
console.log('Decrypted history:', decrypted_history);
|
||||||
|
tmp_entry_obj.history = decrypted_history;
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
class="btn btn-sm variant-soft-secondary hover:variant-filled-secondary *:hover:inline lg:text-xs"
|
||||||
|
title="Toggle history of this journal entry"
|
||||||
|
>
|
||||||
|
<History strokeWidth="2.5" color="blue" />
|
||||||
|
<span class="hidden sm:inline">History</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class:hidden={!$journals_sess.show__content__journal_entry_history}
|
||||||
|
onclick={() => {
|
||||||
|
if ($journals_sess.show__content__journal_entry_history == 'view') {
|
||||||
|
$journals_sess.show__content__journal_entry_history = 'edit';
|
||||||
|
} else if ($journals_sess.show__content__journal_entry_history == 'edit') {
|
||||||
|
$journals_sess.show__content__journal_entry_history = 'view';
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
class="btn btn-sm variant-soft-secondary hover:variant-filled-secondary *:hover:inline lg:text-xs"
|
||||||
|
title="Toggle edit mode for history of this journal entry"
|
||||||
|
>
|
||||||
|
{#if $journals_sess.show__content__journal_entry_history == 'view'}
|
||||||
|
<PenLine strokeWidth="2.5" color="blue" class="inline-block" />
|
||||||
|
{:else if $journals_sess.show__content__journal_entry_history == 'edit'}
|
||||||
|
<Pencil strokeWidth="2.5" color="blue" class="inline-block" />
|
||||||
|
{/if}
|
||||||
|
<span class="hidden sm:inline">
|
||||||
|
{#if $journals_sess.show__content__journal_entry_history == 'view'}
|
||||||
|
Edit
|
||||||
|
{:else if $journals_sess.show__content__journal_entry_history == 'edit'}
|
||||||
|
View
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="
|
||||||
|
flex-grow
|
||||||
|
basis-full
|
||||||
|
flex flex-col items-center justify-center
|
||||||
|
h-full
|
||||||
|
w-full max-w-6xl
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{#if $journals_sess?.show__content__journal_entry_history == 'view'}
|
||||||
|
<article
|
||||||
|
class:border-orange-200={$ae_loc.edit_mode}
|
||||||
|
class:hover:border-orange-500={$ae_loc.edit_mode}
|
||||||
|
class="
|
||||||
|
space-y-1
|
||||||
|
h-full min-h-max max-h-full
|
||||||
|
w-full min-w-full max-w-6xl
|
||||||
|
p-2
|
||||||
|
font-mono
|
||||||
|
!bg-slate-100 !text-gray-900
|
||||||
|
dark:!bg-slate-900 dark:!text-gray-100
|
||||||
|
dark:prose-invert
|
||||||
|
shadow-md rounded-lg
|
||||||
|
border border-gray-200 dark:border-gray-700
|
||||||
|
hover:border-gray-500 dark:hover:border-gray-500
|
||||||
|
prose
|
||||||
|
prose-h1:underline prose-h1:decoration-double
|
||||||
|
prose-h2:underline
|
||||||
|
prose-h1:text-2xl prose-h2:text-xl prose-h3:text-lg
|
||||||
|
prose-h1:m-0 prose-h2:m-0 prose-h3:m-0 prose-h4:m-0 prose-h5:m-0 prose-h6:m-0
|
||||||
|
prose-li:m-0 prose-li:p-0 prose-li:line-height-none
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{@html $lq__journal_entry_obj?.history_md_html}
|
||||||
|
</article>
|
||||||
|
{:else if $journals_sess?.show__content__journal_entry_history == 'edit'}
|
||||||
|
<textarea
|
||||||
|
bind:value={tmp_entry_obj.history}
|
||||||
|
class:border-orange-200={$ae_loc.edit_mode}
|
||||||
|
class:hover:border-orange-500={$ae_loc.edit_mode}
|
||||||
|
class="
|
||||||
|
space-y-1
|
||||||
|
flex-grow
|
||||||
|
basis-full
|
||||||
|
h-full min-h-max max-h-full
|
||||||
|
w-full min-w-full max-w-6xl
|
||||||
|
p-2
|
||||||
|
font-mono
|
||||||
|
bg-slate-100 text-gray-900
|
||||||
|
dark:bg-slate-900 dark:text-gray-100
|
||||||
|
shadow-lg rounded-lg
|
||||||
|
border border-gray-200 dark:border-gray-700
|
||||||
|
hover:border-gray-500 dark:hover:border-gray-500
|
||||||
|
"
|
||||||
|
placeholder="Edit journal entry content here..."
|
||||||
|
></textarea>
|
||||||
{/if}
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- <div>
|
<!-- <div>
|
||||||
{@html test_html}
|
{@html test_html}
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<div class="ae_meta flex flex-row flex-wrap gap-1 items-center justify-between w-full">
|
<section class="ae_meta flex flex-row flex-wrap gap-1 items-center justify-between w-full">
|
||||||
<!-- {$lq__journal_entry_obj?.priority}
|
<!-- {$lq__journal_entry_obj?.priority}
|
||||||
{$lq__journal_entry_obj?.sort}
|
{$lq__journal_entry_obj?.sort}
|
||||||
{$lq__journal_entry_obj?.group}
|
{$lq__journal_entry_obj?.group}
|
||||||
@@ -1333,7 +1682,7 @@ async function handle_decrypt_content() {
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@@ -140,11 +140,11 @@ $effect(() => {
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if (journals_journal_entry_obj.priority)}
|
{#if (journals_journal_entry_obj.priority)}
|
||||||
<Flag size="1.25emem" class="mx-1 inline-block text-yellow-500"/>
|
<Flag size="1.25em" class="mx-1 inline-block text-yellow-500"/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if (journals_journal_entry_obj.group)}
|
{#if (journals_journal_entry_obj.group)}
|
||||||
<Group size="1.25emem" class="mx-1 inline-block text-green-500"/>
|
<Group size="1.25em" class="mx-1 inline-block text-green-500"/>
|
||||||
<span class="text-xs text-gray-500 hidden">Group:</span>
|
<span class="text-xs text-gray-500 hidden">Group:</span>
|
||||||
<span class="font-semibold text-sm text-gray-500 hidden md:inline">
|
<span class="font-semibold text-sm text-gray-500 hidden md:inline">
|
||||||
{journals_journal_entry_obj.group}
|
{journals_journal_entry_obj.group}
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ let { lq__journal_obj_li }: Props = $props();
|
|||||||
{/if}
|
{/if}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<!-- {journals_journal_obj?.tmp_sort_3} -->
|
||||||
|
|
||||||
{#if journals_journal_obj.description}
|
{#if journals_journal_obj.description}
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
|
|||||||
Reference in New Issue
Block a user