refactor: DRY `getAttachmentDisplayItems` function + fix UI
This commit is contained in:
parent
1cf5daa8c0
commit
68b653ef45
|
|
@ -145,7 +145,9 @@
|
||||||
|
|
||||||
<div class="flex flex-col gap-0.5">
|
<div class="flex flex-col gap-0.5">
|
||||||
<span
|
<span
|
||||||
class="max-w-24 truncate text-sm font-medium text-foreground group-hover:pr-6 md:max-w-32"
|
class="max-w-24 truncate text-sm font-medium text-foreground {readonly
|
||||||
|
? ''
|
||||||
|
: 'group-hover:pr-6'} md:max-w-32"
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,8 @@
|
||||||
import { ChatAttachmentThumbnailImage, ChatAttachmentThumbnailFile } from '$lib/components/app';
|
import { ChatAttachmentThumbnailImage, ChatAttachmentThumbnailFile } from '$lib/components/app';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import { ChevronLeft, ChevronRight } from '@lucide/svelte';
|
import { ChevronLeft, ChevronRight } from '@lucide/svelte';
|
||||||
import { getFileTypeCategory } from '$lib/utils/file-type';
|
|
||||||
import { FileTypeCategory } from '$lib/enums';
|
|
||||||
import { isImageFile } from '$lib/utils/attachment-type';
|
|
||||||
import { DialogChatAttachmentPreview, DialogChatAttachmentsViewAll } from '$lib/components/app';
|
import { DialogChatAttachmentPreview, DialogChatAttachmentsViewAll } from '$lib/components/app';
|
||||||
|
import { getAttachmentDisplayItems } from '$lib/utils/attachment-display';
|
||||||
import type { ChatAttachmentDisplayItem, ChatAttachmentPreviewItem } from '$lib/types/chat';
|
import type { ChatAttachmentDisplayItem, ChatAttachmentPreviewItem } from '$lib/types/chat';
|
||||||
import type { DatabaseMessageExtra } from '$lib/types/database';
|
import type { DatabaseMessageExtra } from '$lib/types/database';
|
||||||
|
|
||||||
|
|
@ -40,7 +38,7 @@
|
||||||
limitToSingleRow = false
|
limitToSingleRow = false
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
let displayItems = $derived(getDisplayItems());
|
let displayItems = $derived(getAttachmentDisplayItems({ uploadedFiles, attachments }));
|
||||||
|
|
||||||
let canScrollLeft = $state(false);
|
let canScrollLeft = $state(false);
|
||||||
let canScrollRight = $state(false);
|
let canScrollRight = $state(false);
|
||||||
|
|
@ -51,40 +49,6 @@
|
||||||
let showViewAll = $derived(limitToSingleRow && displayItems.length > 0 && isScrollable);
|
let showViewAll = $derived(limitToSingleRow && displayItems.length > 0 && isScrollable);
|
||||||
let viewAllDialogOpen = $state(false);
|
let viewAllDialogOpen = $state(false);
|
||||||
|
|
||||||
function getDisplayItems(): ChatAttachmentDisplayItem[] {
|
|
||||||
const items: ChatAttachmentDisplayItem[] = [];
|
|
||||||
|
|
||||||
// Add uploaded files (ChatForm)
|
|
||||||
for (const file of uploadedFiles) {
|
|
||||||
items.push({
|
|
||||||
id: file.id,
|
|
||||||
name: file.name,
|
|
||||||
size: file.size,
|
|
||||||
preview: file.preview,
|
|
||||||
isImage: getFileTypeCategory(file.type) === FileTypeCategory.IMAGE,
|
|
||||||
uploadedFile: file,
|
|
||||||
textContent: file.textContent
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add stored attachments (ChatMessage)
|
|
||||||
for (const [index, attachment] of attachments.entries()) {
|
|
||||||
const isImage = isImageFile(attachment);
|
|
||||||
|
|
||||||
items.push({
|
|
||||||
id: `attachment-${index}`,
|
|
||||||
name: attachment.name,
|
|
||||||
preview: isImage && 'base64Url' in attachment ? attachment.base64Url : undefined,
|
|
||||||
isImage,
|
|
||||||
attachment,
|
|
||||||
attachmentIndex: index,
|
|
||||||
textContent: 'content' in attachment ? attachment.content : undefined
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return items.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
function openPreview(item: ChatAttachmentDisplayItem, event?: MouseEvent) {
|
function openPreview(item: ChatAttachmentDisplayItem, event?: MouseEvent) {
|
||||||
event?.stopPropagation();
|
event?.stopPropagation();
|
||||||
event?.preventDefault();
|
event?.preventDefault();
|
||||||
|
|
@ -218,7 +182,7 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex flex-wrap justify-end gap-3">
|
<div class="flex flex-wrap items-start justify-end gap-3">
|
||||||
{#each displayItems as item (item.id)}
|
{#each displayItems as item (item.id)}
|
||||||
{#if item.isImage && item.preview}
|
{#if item.isImage && item.preview}
|
||||||
<ChatAttachmentThumbnailImage
|
<ChatAttachmentThumbnailImage
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,8 @@
|
||||||
ChatAttachmentThumbnailFile,
|
ChatAttachmentThumbnailFile,
|
||||||
DialogChatAttachmentPreview
|
DialogChatAttachmentPreview
|
||||||
} from '$lib/components/app';
|
} from '$lib/components/app';
|
||||||
import { FileTypeCategory } from '$lib/enums';
|
import { getAttachmentDisplayItems } from '$lib/utils/attachment-display';
|
||||||
import { getFileTypeCategory } from '$lib/utils/file-type';
|
import type { ChatAttachmentPreviewItem } from '$lib/types/chat';
|
||||||
import { isImageFile } from '$lib/utils/attachment-type';
|
|
||||||
import type { ChatAttachmentDisplayItem, ChatAttachmentPreviewItem } from '$lib/types/chat';
|
|
||||||
import type { DatabaseMessageExtra } from '$lib/types/database';
|
import type { DatabaseMessageExtra } from '$lib/types/database';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -33,42 +31,10 @@
|
||||||
let previewDialogOpen = $state(false);
|
let previewDialogOpen = $state(false);
|
||||||
let previewItem = $state<ChatAttachmentPreviewItem | null>(null);
|
let previewItem = $state<ChatAttachmentPreviewItem | null>(null);
|
||||||
|
|
||||||
let displayItems = $derived(getDisplayItems());
|
let displayItems = $derived(getAttachmentDisplayItems({ uploadedFiles, attachments }));
|
||||||
let imageItems = $derived(displayItems.filter((item) => item.isImage));
|
let imageItems = $derived(displayItems.filter((item) => item.isImage));
|
||||||
let fileItems = $derived(displayItems.filter((item) => !item.isImage));
|
let fileItems = $derived(displayItems.filter((item) => !item.isImage));
|
||||||
|
|
||||||
function getDisplayItems(): ChatAttachmentDisplayItem[] {
|
|
||||||
const items: ChatAttachmentDisplayItem[] = [];
|
|
||||||
|
|
||||||
for (const file of uploadedFiles) {
|
|
||||||
items.push({
|
|
||||||
id: file.id,
|
|
||||||
name: file.name,
|
|
||||||
size: file.size,
|
|
||||||
preview: file.preview,
|
|
||||||
isImage: getFileTypeCategory(file.type) === FileTypeCategory.IMAGE,
|
|
||||||
uploadedFile: file,
|
|
||||||
textContent: file.textContent
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [index, attachment] of attachments.entries()) {
|
|
||||||
const isImage = isImageFile(attachment);
|
|
||||||
|
|
||||||
items.push({
|
|
||||||
id: `attachment-${index}`,
|
|
||||||
name: attachment.name,
|
|
||||||
preview: isImage && 'base64Url' in attachment ? attachment.base64Url : undefined,
|
|
||||||
isImage,
|
|
||||||
attachment,
|
|
||||||
attachmentIndex: index,
|
|
||||||
textContent: 'content' in attachment ? attachment.content : undefined
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return items.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
function openPreview(item: (typeof displayItems)[0], event?: Event) {
|
function openPreview(item: (typeof displayItems)[0], event?: Event) {
|
||||||
if (event) {
|
if (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { FileTypeCategory } from '$lib/enums';
|
||||||
|
import type { ChatAttachmentDisplayItem } from '$lib/types/chat';
|
||||||
|
import type { DatabaseMessageExtra } from '$lib/types/database';
|
||||||
|
import { isImageFile } from '$lib/utils/attachment-type';
|
||||||
|
import { getFileTypeCategory } from '$lib/utils/file-type';
|
||||||
|
|
||||||
|
export interface AttachmentDisplayItemsOptions {
|
||||||
|
uploadedFiles?: ChatUploadedFile[];
|
||||||
|
attachments?: DatabaseMessageExtra[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a unified list of display items from uploaded files and stored attachments.
|
||||||
|
* Items are returned in reverse order (newest first).
|
||||||
|
*/
|
||||||
|
export function getAttachmentDisplayItems(
|
||||||
|
options: AttachmentDisplayItemsOptions
|
||||||
|
): ChatAttachmentDisplayItem[] {
|
||||||
|
const { uploadedFiles = [], attachments = [] } = options;
|
||||||
|
const items: ChatAttachmentDisplayItem[] = [];
|
||||||
|
|
||||||
|
// Add uploaded files (ChatForm)
|
||||||
|
for (const file of uploadedFiles) {
|
||||||
|
items.push({
|
||||||
|
id: file.id,
|
||||||
|
name: file.name,
|
||||||
|
size: file.size,
|
||||||
|
preview: file.preview,
|
||||||
|
isImage: getFileTypeCategory(file.type) === FileTypeCategory.IMAGE,
|
||||||
|
uploadedFile: file,
|
||||||
|
textContent: file.textContent
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add stored attachments (ChatMessage)
|
||||||
|
for (const [index, attachment] of attachments.entries()) {
|
||||||
|
const isImage = isImageFile(attachment);
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
id: `attachment-${index}`,
|
||||||
|
name: attachment.name,
|
||||||
|
preview: isImage && 'base64Url' in attachment ? attachment.base64Url : undefined,
|
||||||
|
isImage,
|
||||||
|
attachment,
|
||||||
|
attachmentIndex: index,
|
||||||
|
textContent: 'content' in attachment ? attachment.content : undefined
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.reverse();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue