From 2281c9c868617b055c0959917a439be79303878d Mon Sep 17 00:00:00 2001 From: alexa Date: Sat, 29 Jun 2024 23:46:42 +0200 Subject: [PATCH] send attachments --- ui/classes.css | 40 +++++++++++++++++++++++++ ui/components/common.mjs | 10 +++++++ ui/components/pages/chat.mjs | 57 ++++++++++++++++++++++++++++++++++-- 3 files changed, 105 insertions(+), 2 deletions(-) diff --git a/ui/classes.css b/ui/classes.css index ba7d7cc..6a64b47 100644 --- a/ui/classes.css +++ b/ui/classes.css @@ -630,4 +630,44 @@ top: 3em; left: var(--gap-v); z-index: 999; +} + +.attachment-preview { + padding-top: calc(var(--gap-v) * 2); + padding-left: var(--gap-v); + padding-right: var(--gap-v); + background: var(--bg-2); + border-top: var(--input-border-width) solid var(--bg-4); + gap: calc(var(--gap-v) * 2); +} + +.attachment-preview-image { + height: 4em; + width: auto; + border-radius: var(--input-border-radius); +} + +.small-icon-button { + cursor: pointer; + border-radius: 999px; + background: var(--bg-3); + border: var(--input-border-width) solid var(--text-4); + padding: 0.2em; + height: max-content; + box-sizing: border-box; + align-self: center; + align-items: center; +} + +.small-icon-button .material-symbols-outlined { + font-size: 0.8em !important; +} + +.attachment-remove-button { + color: var(--red); + border-color: var(--red); + position: absolute; + top: 0; + right: 0; + transform: translateX(50%) translateY(-50%); } \ No newline at end of file diff --git a/ui/components/common.mjs b/ui/components/common.mjs index 15ec149..01fc986 100644 --- a/ui/components/common.mjs +++ b/ui/components/common.mjs @@ -412,4 +412,14 @@ export class CommonTemplates { .build() ).build(); } + + static smallIconButton(icon, title, onclick, classes = []) { + return create("div") + .classes("small-icon-button", "flex", "align-center", ...classes) + .onclick(onclick) + .title(title) + .children( + CommonTemplates.icon(icon) + ).build(); + } } \ No newline at end of file diff --git a/ui/components/pages/chat.mjs b/ui/components/pages/chat.mjs index f32d7fb..2fcfe25 100644 --- a/ui/components/pages/chat.mjs +++ b/ui/components/pages/chat.mjs @@ -69,6 +69,11 @@ export class ChatComponent { }); }); const menuShownForMessageId = signal(null); + const toBeSentAttachments = signal([]); + const hasAttachments = computedSignal(toBeSentAttachments, attachments => { + console.log(attachments); + return attachments.length > 0; + }); return create("div") .classes("flex-v", "full-height") @@ -79,9 +84,17 @@ export class ChatComponent { signalMap(messages, create("div") .classes("chat-messages","flex-v", "flex-grow", "no-gap"), message => ChatComponent.message(message, messages, menuShownForMessageId)), + ifjs(hasAttachments, create("div") + .classes("flex", "align-center", "full-width") + .children( + signalMap(toBeSentAttachments, create("div") + .classes("flex", "align-center", "attachment-preview", "full-width"), + attachment => ChatComponent.attachmentPreview(attachment)), + ).build()), create("div") .classes("background-2", "chat-input", "flex", "align-center") .children( + ChatComponent.attachmentButton(activeChannel, messageText, toBeSentAttachments), CommonTemplates.textArea(messageText, "message", null, "Write something nice...", ["flex-grow"], ["full-width-h", "message-input"], () => { if (!messageText.value || messageText.value.trim() === "" || sending.value) { return; @@ -92,20 +105,22 @@ export class ChatComponent { type: "message", channelId: activeChannel.value, text: messageText.value, + attachments: toBeSentAttachments.value, }); sending.value = false; messageText.value = ""; + toBeSentAttachments.value = []; }), create("div") .children( - ChatComponent.sendButton(sending, messages, activeChannel, messageText), + ChatComponent.sendButton(sending, messages, toBeSentAttachments, activeChannel, messageText), ).build() ).build(), ).build(), ).build(); } - static sendButton(sending, messages, activeChannel, messageText) { + static sendButton(sending, messages, toBeSentAttachments, activeChannel, messageText) { return CommonTemplates.buttonWithSpinner("send", "Send", "send", () => { if (!messageText.value || messageText.value.trim() === "" || sending.value) { return; @@ -116,9 +131,11 @@ export class ChatComponent { type: "message", channelId: activeChannel.value, text: messageText.value, + attachments: toBeSentAttachments.value, }); sending.value = false; messageText.value = ""; + toBeSentAttachments.value = []; }, sending, ["rounded-max", "double"]); } @@ -360,4 +377,40 @@ export class ChatComponent { }), ).build(); } + + static attachmentButton(activeChannel, messageText, toBeSentAttachments) { + return CommonTemplates.buttonWithIcon("attach_file", "", () => { + const input = document.createElement('input'); + input.type = 'file'; + input.multiple = true; + input.onchange = () => { + for (const file of input.files) { + const reader = new FileReader(); + reader.onload = () => { + const base64 = reader.result.split(',')[1]; + toBeSentAttachments.value = [...toBeSentAttachments.value, { + type: file.type, + data: file.type.startsWith("image") ? `data:${file.type};base64,${base64}` : null, + }]; + }; + reader.readAsDataURL(file); + } + }; + input.click(); + }, ["rounded-max", "double"]); + } + + static attachmentPreview(attachment, toBeSentAttachments) { + return create("div") + .classes("flex-v", "align-center", "relative") + .children( + create("img") + .classes("attachment-preview-image") + .src(attachment.data) + .build(), + CommonTemplates.smallIconButton("delete", "Delete", () => { + toBeSentAttachments.value = toBeSentAttachments.value.filter(a => a.filename !== attachment.filename); + }, ["attachment-remove-button"]), + ).build(); + } } \ No newline at end of file