enh/refac: note image upload
This commit is contained in:
@@ -432,6 +432,14 @@ ${content}
|
||||
note.data.files = null;
|
||||
}
|
||||
|
||||
editor.storage.files = files;
|
||||
// open the settings panel if it is not open
|
||||
selectedPanel = 'settings';
|
||||
|
||||
if (!showPanel) {
|
||||
showPanel = true;
|
||||
}
|
||||
|
||||
changeDebounceHandler();
|
||||
};
|
||||
|
||||
@@ -504,20 +512,39 @@ ${content}
|
||||
|
||||
imageUrl = await compressImageHandler(imageUrl, $settings, $config);
|
||||
|
||||
files = [
|
||||
...files,
|
||||
{
|
||||
type: 'image',
|
||||
url: `${imageUrl}`
|
||||
}
|
||||
];
|
||||
const fileId = uuidv4();
|
||||
const fileItem = {
|
||||
id: fileId,
|
||||
type: 'image',
|
||||
url: `${imageUrl}`
|
||||
};
|
||||
files = [...files, fileItem];
|
||||
note.data.files = files;
|
||||
|
||||
if (imageUrl && editor) {
|
||||
editor.storage.files = files;
|
||||
editor
|
||||
?.chain()
|
||||
.insertContentAt(editor.state.selection.$anchor.pos, {
|
||||
type: 'image',
|
||||
attrs: {
|
||||
file: fileItem,
|
||||
src: `data://${fileId}`
|
||||
|
||||
// src: imageUrl
|
||||
}
|
||||
})
|
||||
.focus()
|
||||
.run();
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(
|
||||
file['type'] === 'image/heic'
|
||||
? await heic2any({ blob: file, toType: 'image/jpeg' })
|
||||
: file
|
||||
);
|
||||
|
||||
changeDebounceHandler();
|
||||
} else {
|
||||
uploadFileHandler(file);
|
||||
}
|
||||
@@ -773,8 +800,47 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
|
||||
inputElement?.insertContent(content);
|
||||
};
|
||||
|
||||
const noteEventHandler = async (_note) => {
|
||||
console.log('noteEventHandler', _note);
|
||||
if (_note.id !== id) return;
|
||||
|
||||
if (_note.access_control && _note.access_control !== note.access_control) {
|
||||
note.access_control = _note.access_control;
|
||||
}
|
||||
|
||||
if (_note.data && _note.data.files) {
|
||||
files = _note.data.files;
|
||||
note.data.files = files;
|
||||
}
|
||||
|
||||
if (_note.title && _note.title) {
|
||||
note.title = _note.title;
|
||||
}
|
||||
|
||||
editor.storage.files = files;
|
||||
await tick();
|
||||
|
||||
for (const file of files) {
|
||||
if (file.type === 'image') {
|
||||
const e = new CustomEvent('data', { files: files });
|
||||
|
||||
const img = document.getElementById(`image:${file.id}`);
|
||||
if (img) {
|
||||
img.dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
await tick();
|
||||
$socket?.emit('join-note', {
|
||||
note_id: id,
|
||||
auth: {
|
||||
token: localStorage.token
|
||||
}
|
||||
});
|
||||
$socket?.on('note-events', noteEventHandler);
|
||||
|
||||
if ($settings?.models) {
|
||||
selectedModelId = $settings?.models[0];
|
||||
@@ -807,6 +873,8 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
|
||||
|
||||
onDestroy(() => {
|
||||
console.log('destroy');
|
||||
$socket?.off('note-events', noteEventHandler);
|
||||
|
||||
const dropzoneElement = document.getElementById('note-editor');
|
||||
|
||||
if (dropzoneElement) {
|
||||
@@ -1111,52 +1179,21 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
|
||||
></div>
|
||||
{/if}
|
||||
|
||||
{#if files && files.length > 0}
|
||||
<div class="mb-2.5 w-full flex gap-1 flex-wrap z-40">
|
||||
{#each files as file, fileIdx}
|
||||
<div class="w-fit">
|
||||
{#if file.type === 'image'}
|
||||
<Image
|
||||
src={file.url}
|
||||
imageClassName=" max-h-96 rounded-lg"
|
||||
dismissible={true}
|
||||
onDismiss={() => {
|
||||
files = files.filter((item, idx) => idx !== fileIdx);
|
||||
note.data.files = files.length > 0 ? files : null;
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<FileItem
|
||||
item={file}
|
||||
dismissible={true}
|
||||
url={file.url}
|
||||
name={file.name}
|
||||
type={file.type}
|
||||
size={file?.size}
|
||||
loading={file.status === 'uploading'}
|
||||
on:dismiss={() => {
|
||||
files = files.filter((item) => item?.id !== file.id);
|
||||
note.data.files = files.length > 0 ? files : null;
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<RichTextInput
|
||||
bind:this={inputElement}
|
||||
bind:editor
|
||||
id={`note-${note.id}`}
|
||||
className="input-prose-sm px-0.5"
|
||||
json={true}
|
||||
bind:value={note.data.content.json}
|
||||
html={note.data?.content?.html}
|
||||
documentId={`note:${note.id}`}
|
||||
{files}
|
||||
collaboration={true}
|
||||
socket={$socket}
|
||||
user={$user}
|
||||
link={true}
|
||||
image={true}
|
||||
placeholder={$i18n.t('Write something...')}
|
||||
editable={versionIdx === null && !editing}
|
||||
onChange={(content) => {
|
||||
@@ -1312,7 +1349,14 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
|
||||
scrollToBottomHandler={scrollToBottom}
|
||||
/>
|
||||
{:else if selectedPanel === 'settings'}
|
||||
<Settings bind:show={showPanel} bind:selectedModelId />
|
||||
<Settings
|
||||
bind:show={showPanel}
|
||||
bind:selectedModelId
|
||||
bind:files
|
||||
onUpdate={() => {
|
||||
changeDebounceHandler();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</NotePanel>
|
||||
</PaneGroup>
|
||||
|
||||
@@ -4,9 +4,17 @@
|
||||
|
||||
import XMark from '$lib/components/icons/XMark.svelte';
|
||||
import { models } from '$lib/stores';
|
||||
import Collapsible from '$lib/components/common/Collapsible.svelte';
|
||||
import FileItem from '$lib/components/common/FileItem.svelte';
|
||||
import Image from '$lib/components/common/Image.svelte';
|
||||
|
||||
export let show = false;
|
||||
export let selectedModelId = '';
|
||||
export let files = [];
|
||||
|
||||
export let onUpdate = (files: any[]) => {
|
||||
// Default no-op function
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="flex items-center mb-1.5 pt-1.5">
|
||||
@@ -23,13 +31,62 @@
|
||||
|
||||
<div class=" font-medium text-base flex items-center gap-1">
|
||||
<div>
|
||||
{$i18n.t('Settings')}
|
||||
{$i18n.t('Controls')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-1">
|
||||
<div>
|
||||
<div class="pb-10">
|
||||
{#if files.length > 0}
|
||||
<div class=" text-xs font-medium pb-1">Files</div>
|
||||
|
||||
<div class="flex flex-col gap-1">
|
||||
{#each files.filter((file) => file.type !== 'image') as file, fileIdx}
|
||||
<FileItem
|
||||
className="w-full"
|
||||
item={file}
|
||||
small={true}
|
||||
edit={true}
|
||||
dismissible={true}
|
||||
url={file.url}
|
||||
name={file.name}
|
||||
type={file.type}
|
||||
size={file?.size}
|
||||
loading={file.status === 'uploading'}
|
||||
on:dismiss={() => {
|
||||
// Remove the file from the files array
|
||||
files = files.filter((item) => item.id !== file.id);
|
||||
files = files;
|
||||
|
||||
onUpdate(files);
|
||||
}}
|
||||
on:click={() => {
|
||||
console.log(file);
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<div class="flex items-center flex-wrap gap-2 mt-1.5">
|
||||
{#each files.filter((file) => file.type === 'image') as file, fileIdx}
|
||||
<Image
|
||||
src={file.url}
|
||||
imageClassName=" size-14 rounded-xl object-cover"
|
||||
dismissible={true}
|
||||
onDismiss={() => {
|
||||
files = files.filter((item) => item.id !== file.id);
|
||||
files = files;
|
||||
|
||||
onUpdate(files);
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-2 border-gray-50 dark:border-gray-700/10" />
|
||||
{/if}
|
||||
|
||||
<div class=" text-xs font-medium mb-1">Model</div>
|
||||
|
||||
<div class="w-full">
|
||||
|
||||
Reference in New Issue
Block a user