Skip to content
2 changes: 1 addition & 1 deletion dist/hellotext.js

Large diffs are not rendered by default.

38 changes: 29 additions & 9 deletions lib/controllers/webchat_controller.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,11 @@ let _default = /*#__PURE__*/function (_Controller) {
value: function onPopoverOpened() {
this.inputTarget.focus();
if (!this.scrolled) {
this.messagesContainerTarget.scroll({
top: this.messagesContainerTarget.scrollHeight,
behavior: 'instant'
requestAnimationFrame(() => {
this.messagesContainerTarget.scroll({
top: this.messagesContainerTarget.scrollHeight,
behavior: 'instant'
});
});
this.scrolled = true;
}
Expand Down Expand Up @@ -277,7 +279,14 @@ let _default = /*#__PURE__*/function (_Controller) {
body: this.inputTarget.value,
attachments: this.files
};
formData.append('message[body]', this.inputTarget.value);
if (this.inputTarget.value.trim().length === 0 && this.files.length === 0) {
return;
}
if (this.inputTarget.value.trim().length > 0) {
formData.append('message[body]', this.inputTarget.value);
} else {
delete message.body;
}
this.files.forEach(file => {
formData.append('message[attachments][]', file);
});
Expand All @@ -287,7 +296,11 @@ let _default = /*#__PURE__*/function (_Controller) {
element.classList.add('received');
element.style.removeProperty('display');
element.setAttribute('data-hellotext--webchat-target', 'message');
element.querySelector('[data-body]').innerText = this.inputTarget.value;
if (this.inputTarget.value.trim().length > 0) {
element.querySelector('[data-body]').innerText = this.inputTarget.value;
} else {
element.querySelector('[data-message-bubble]').remove();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Null Element Error in Message Template

When sending a message with an empty body (e.g., only attachments), the code attempts to remove the [data-message-bubble] element from the cloned message template. If this element is not found, querySelector returns null, leading to a TypeError when .remove() is called on null.

Additional Locations (1)
Fix in Cursor Fix in Web

}
const attachments = this.attachmentContainerTarget.querySelectorAll('img');
if (attachments.length > 0) {
attachments.forEach(attachment => {
Expand All @@ -304,6 +317,8 @@ let _default = /*#__PURE__*/function (_Controller) {
});
this.inputTarget.value = '';
this.files = [];
this.attachmentInputTarget.value = '';
this.attachmentContainerTarget.innerHTML = '';
this.attachmentContainerTarget.style.display = 'none';
this.errorMessageContainerTarget.style.display = 'none';
this.inputTarget.focus();
Expand Down Expand Up @@ -334,8 +349,9 @@ let _default = /*#__PURE__*/function (_Controller) {
key: "onFileInputChange",
value: function onFileInputChange() {
this.errorMessageContainerTarget.style.display = 'none';
this.files = Array.from(this.attachmentInputTarget.files);
const fileMaxSizeTooMuch = this.files.find(file => {
const newFiles = Array.from(this.attachmentInputTarget.files);
this.attachmentInputTarget.value = '';
const fileMaxSizeTooMuch = newFiles.find(file => {
const type = file.type.split('/')[0];
if (['image', 'video', 'audio'].includes(type)) {
return this.mediaValue[type].max_size < file.size;
Expand All @@ -347,10 +363,11 @@ let _default = /*#__PURE__*/function (_Controller) {
const type = fileMaxSizeTooMuch.type.split('/')[0];
const mediaType = ['image', 'audio', 'video'].includes(type) ? type : 'document';
this.errorMessageContainerTarget.innerText = this.fileSizeErrorMessageValue.replace('%{limit}', this.byteToMegabyte(this.mediaValue[mediaType].max_size));
return;
return this.errorMessageContainerTarget.style.display = 'block';
}
this.files = [...this.files, ...newFiles];
this.errorMessageContainerTarget.innerText = '';
this.files.forEach(file => this.createAttachmentElement(file));
newFiles.forEach(file => this.createAttachmentElement(file));
this.inputTarget.focus();
}
}, {
Expand All @@ -373,6 +390,8 @@ let _default = /*#__PURE__*/function (_Controller) {
main.style.backgroundColor = '#e5e7eb';
main.style.padding = '0.25rem';
element.querySelector('p[data-attachment-name]').innerText = file.name;
this.attachmentContainerTarget.appendChild(element);
this.attachmentContainerTarget.style.display = 'flex';
}
}
}, {
Expand All @@ -382,6 +401,7 @@ let _default = /*#__PURE__*/function (_Controller) {
}) {
const attachment = currentTarget.closest("[data-hellotext--webchat-target='attachment']");
this.files = this.files.filter(file => file.name !== attachment.dataset.name);
this.attachmentInputTarget.value = '';
attachment.remove();
this.inputTarget.focus();
}
Expand Down
38 changes: 29 additions & 9 deletions lib/controllers/webchat_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,11 @@ var _default = /*#__PURE__*/function (_Controller) {
value: function onPopoverOpened() {
this.inputTarget.focus();
if (!this.scrolled) {
this.messagesContainerTarget.scroll({
top: this.messagesContainerTarget.scrollHeight,
behavior: 'instant'
requestAnimationFrame(() => {
this.messagesContainerTarget.scroll({
top: this.messagesContainerTarget.scrollHeight,
behavior: 'instant'
});
});
this.scrolled = true;
}
Expand Down Expand Up @@ -281,7 +283,14 @@ var _default = /*#__PURE__*/function (_Controller) {
body: this.inputTarget.value,
attachments: this.files
};
formData.append('message[body]', this.inputTarget.value);
if (this.inputTarget.value.trim().length === 0 && this.files.length === 0) {
return;
}
if (this.inputTarget.value.trim().length > 0) {
formData.append('message[body]', this.inputTarget.value);
} else {
delete message.body;
}
this.files.forEach(file => {
formData.append('message[attachments][]', file);
});
Expand All @@ -291,7 +300,11 @@ var _default = /*#__PURE__*/function (_Controller) {
element.classList.add('received');
element.style.removeProperty('display');
element.setAttribute('data-hellotext--webchat-target', 'message');
element.querySelector('[data-body]').innerText = this.inputTarget.value;
if (this.inputTarget.value.trim().length > 0) {
element.querySelector('[data-body]').innerText = this.inputTarget.value;
} else {
element.querySelector('[data-message-bubble]').remove();
}
var attachments = this.attachmentContainerTarget.querySelectorAll('img');
if (attachments.length > 0) {
attachments.forEach(attachment => {
Expand All @@ -308,6 +321,8 @@ var _default = /*#__PURE__*/function (_Controller) {
});
this.inputTarget.value = '';
this.files = [];
this.attachmentInputTarget.value = '';
this.attachmentContainerTarget.innerHTML = '';
this.attachmentContainerTarget.style.display = 'none';
this.errorMessageContainerTarget.style.display = 'none';
this.inputTarget.focus();
Expand Down Expand Up @@ -343,8 +358,9 @@ var _default = /*#__PURE__*/function (_Controller) {
key: "onFileInputChange",
value: function onFileInputChange() {
this.errorMessageContainerTarget.style.display = 'none';
this.files = Array.from(this.attachmentInputTarget.files);
var fileMaxSizeTooMuch = this.files.find(file => {
var newFiles = Array.from(this.attachmentInputTarget.files);
this.attachmentInputTarget.value = '';
var fileMaxSizeTooMuch = newFiles.find(file => {
var type = file.type.split('/')[0];
if (['image', 'video', 'audio'].includes(type)) {
return this.mediaValue[type].max_size < file.size;
Expand All @@ -356,10 +372,11 @@ var _default = /*#__PURE__*/function (_Controller) {
var type = fileMaxSizeTooMuch.type.split('/')[0];
var mediaType = ['image', 'audio', 'video'].includes(type) ? type : 'document';
this.errorMessageContainerTarget.innerText = this.fileSizeErrorMessageValue.replace('%{limit}', this.byteToMegabyte(this.mediaValue[mediaType].max_size));
return;
return this.errorMessageContainerTarget.style.display = 'block';
}
this.files = [...this.files, ...newFiles];
this.errorMessageContainerTarget.innerText = '';
this.files.forEach(file => this.createAttachmentElement(file));
newFiles.forEach(file => this.createAttachmentElement(file));
this.inputTarget.focus();
}
}, {
Expand All @@ -382,6 +399,8 @@ var _default = /*#__PURE__*/function (_Controller) {
main.style.backgroundColor = '#e5e7eb';
main.style.padding = '0.25rem';
element.querySelector('p[data-attachment-name]').innerText = file.name;
this.attachmentContainerTarget.appendChild(element);
this.attachmentContainerTarget.style.display = 'flex';
}
}
}, {
Expand All @@ -392,6 +411,7 @@ var _default = /*#__PURE__*/function (_Controller) {
} = _ref;
var attachment = currentTarget.closest("[data-hellotext--webchat-target='attachment']");
this.files = this.files.filter(file => file.name !== attachment.dataset.name);
this.attachmentInputTarget.value = '';
attachment.remove();
this.inputTarget.focus();
}
Expand Down
46 changes: 37 additions & 9 deletions src/controllers/webchat_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,11 @@ export default class extends Controller {
this.inputTarget.focus()

if (!this.scrolled) {
this.messagesContainerTarget.scroll({
top: this.messagesContainerTarget.scrollHeight,
behavior: 'instant',
requestAnimationFrame(() => {
this.messagesContainerTarget.scroll({
top: this.messagesContainerTarget.scrollHeight,
behavior: 'instant',
})
})

this.scrolled = true
Expand Down Expand Up @@ -328,7 +330,15 @@ export default class extends Controller {
attachments: this.files,
}

formData.append('message[body]', this.inputTarget.value)
if (this.inputTarget.value.trim().length === 0 && this.files.length === 0) {
return
}

if (this.inputTarget.value.trim().length > 0) {
formData.append('message[body]', this.inputTarget.value)
} else {
delete message.body
}

this.files.forEach(file => {
formData.append('message[attachments][]', file)
Expand All @@ -344,7 +354,12 @@ export default class extends Controller {
element.style.removeProperty('display')

element.setAttribute('data-hellotext--webchat-target', 'message')
element.querySelector('[data-body]').innerText = this.inputTarget.value

if (this.inputTarget.value.trim().length > 0) {
element.querySelector('[data-body]').innerText = this.inputTarget.value
} else {
element.querySelector('[data-message-bubble]').remove()
}

const attachments = this.attachmentContainerTarget.querySelectorAll('img')

Expand All @@ -364,6 +379,10 @@ export default class extends Controller {

this.inputTarget.value = ''
this.files = []

this.attachmentInputTarget.value = ''
this.attachmentContainerTarget.innerHTML = ''

this.attachmentContainerTarget.style.display = 'none'
this.errorMessageContainerTarget.style.display = 'none'

Expand Down Expand Up @@ -401,9 +420,10 @@ export default class extends Controller {
onFileInputChange() {
this.errorMessageContainerTarget.style.display = 'none'

this.files = Array.from(this.attachmentInputTarget.files)
const newFiles = Array.from(this.attachmentInputTarget.files)
this.attachmentInputTarget.value = ''

const fileMaxSizeTooMuch = this.files.find(file => {
const fileMaxSizeTooMuch = newFiles.find(file => {
const type = file.type.split('/')[0]

if (['image', 'video', 'audio'].includes(type)) {
Expand All @@ -421,11 +441,14 @@ export default class extends Controller {
'%{limit}',
this.byteToMegabyte(this.mediaValue[mediaType].max_size),
)
return

return (this.errorMessageContainerTarget.style.display = 'block')
}

this.files = [...this.files, ...newFiles]
this.errorMessageContainerTarget.innerText = ''
this.files.forEach(file => this.createAttachmentElement(file))

newFiles.forEach(file => this.createAttachmentElement(file))
this.inputTarget.focus()
}

Expand All @@ -448,19 +471,24 @@ export default class extends Controller {
this.attachmentContainerTarget.style.display = 'flex'
} else {
const main = element.querySelector('main')

main.style.height = '5rem'
main.style.borderRadius = '0.375rem'
main.style.backgroundColor = '#e5e7eb'
main.style.padding = '0.25rem'

element.querySelector('p[data-attachment-name]').innerText = file.name

this.attachmentContainerTarget.appendChild(element)
Comment thread
cursor[bot] marked this conversation as resolved.
this.attachmentContainerTarget.style.display = 'flex'
}
}

removeAttachment({ currentTarget }) {
const attachment = currentTarget.closest("[data-hellotext--webchat-target='attachment']")

this.files = this.files.filter(file => file.name !== attachment.dataset.name)
this.attachmentInputTarget.value = ''
Comment thread
rockwellll marked this conversation as resolved.

attachment.remove()
this.inputTarget.focus()
Expand Down