feat: replace inline chat-input with custom element and migrate autocomplete logic

Refactor the chat input area in web.html to use the new ChatInputElement custom component instead of inline HTML. Move autocomplete handler from a function-based getInputField() to directly attaching to the chat-input element's autoCompletions property. Remove the legacy initInputField function and its associated keydown/input event listeners, consolidating all input behavior into the component. Add the chat-input.js module script to app.html to ensure the custom element is registered globally.
This commit is contained in:
2025-05-16 22:54:15 +00:00
parent 0a0f817a07
commit 7f0aea4afd
3 changed files with 245 additions and 189 deletions
+21 -131
View File
@@ -4,10 +4,6 @@
{% block main %}
<section class="chat-area">
<message-list class="chat-messages">
{% for message in messages %}
@@ -16,10 +12,7 @@
{% endautoescape %}
{% endfor %}
</message-list>
<div class="chat-input">
<textarea list="chat-input-autocomplete-items" placeholder="Type a message..." rows="2" autocomplete="on"></textarea>
<upload-button channel="{{ channel.uid.value }}"></upload-button>
</div>
<chat-input live-type="false" channel="{{ channel.uid.value }}"></chat-input>
</section>
{% include "dialog_help.html" %}
{% include "dialog_online.html" %}
@@ -27,11 +20,8 @@
import { app } from "/app.js";
import { Schedule } from "/schedule.js";
const channelUid = "{{ channel.uid.value }}";
function getInputField(){
return document.querySelector("textarea")
}
getInputField().autoComplete = {
const chatInputField = document.querySelector("chat-input");
chatInputField.autoCompletions = {
"/online": () =>{
showOnline();
},
@@ -39,117 +29,15 @@
document.querySelector(".chat-messages").innerHTML = '';
},
"/live": () =>{
getInputField().liveType = !getInputField().liveType
chatInputField.liveType = !chatInputField.liveType
},
"/help": () => {
showHelp();
}
}
function initInputField(textBox) {
if(textBox.liveType == undefined){
textBox.liveType = false
}
let typeTimeout = null;
textBox.addEventListener('keydown',async (e) => {
if(typeTimeout){
clearTimeout(typeTimeout)
typeTimeout = null
}
if(e.target.liveType){
typeTimeout = setTimeout(()=>{
e.target.lastMessageUid = null
e.target.value = ''
},3000)
}
if(e.key === "ArrowUp"){
const value = findDivAboveText(e.target.value).querySelector('.text')
e.target.value = value.textContent
console.info("HIERR")
return
}
if (e.key === "Tab") {
const message = e.target.value.trim();
if (!message) {
return
}
let autoCompleteHandler = null;
Object.keys(e.target.autoComplete).forEach((key)=>{
if(key.startsWith(message)){
if(autoCompleteHandler){
return
}
autoCompleteHandler = key
}
})
if(autoCompleteHandler){
e.preventDefault();
e.target.value = autoCompleteHandler;
return
}
}
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
const message = e.target.value.trim();
if (!message) {
return
}
let autoCompleteHandler = e.target.autoComplete[message]
if(autoCompleteHandler){
const value = message;
e.target.value = '';
autoCompleteHandler(value)
return
}
e.target.value = '';
if(textBox.liveType && textBox.lastMessageUid && textBox.lastMessageUid != '?'){
app.rpc.updateMessageText(textBox.lastMessageUid, message)
textBox.lastMessageUid = null
return
}
const messageResponse = await app.rpc.sendMessage(channelUid, message);
}else{
if(textBox.liveType){
if(e.target.value.endsWith("\n") || e.target.value.endsWith(" ")){
return
}
if(e.target.value[0] == "/"){
return
}
if(!textBox.lastMessageUid){
textBox.lastMessageUid = '?'
app.rpc.sendMessage(channelUid, e.target.value).then((messageResponse)=>{
textBox.lastMessageUid = messageResponse
})
}
if(textBox.lastMessageUid == '?'){
return;
}
app.rpc.updateMessageText(textBox.lastMessageUid, e.target.value)
}else{
app.rpc.set_typing(channelUid)
}
}
});
document.querySelector("upload-button").addEventListener("upload",function(e){
getInputField().focus();
})
document.querySelector("upload-button").addEventListener("uploaded",function(e){
let message = ""
e.detail.files.forEach((file)=>{
message += `[${file.name}](/channel/attachment/${file.relative_url})`
})
app.rpc.sendMessage(channelUid,message)
})
}
const textBox = document.querySelector("chat-input").textarea
textBox.addEventListener("paste", async (e) => {
try {
const clipboardItems = await navigator.clipboard.read();
@@ -168,7 +56,7 @@
}
if (dt.items.length > 0) {
const uploadButton = document.querySelector("upload-button");
const uploadButton = chatInputField.uploadButton
const input = uploadButton.shadowRoot.querySelector('.file-input')
input.files = dt.files;
@@ -187,7 +75,7 @@
const dt = e.dataTransfer;
if (dt.items.length > 0) {
const uploadButton = document.querySelector("upload-button");
const uploadButton = chatInputField.uploadButton
const input = uploadButton.shadowRoot.querySelector('.file-input')
input.files = dt.files;
@@ -197,13 +85,16 @@
chatInput.addEventListener("dragover", async (e) => {
e.preventDefault();
e.dataTransfer.dropEffect = "link";
})
textBox.focus();
}
chatInputField.textarea.focus();
function replyMessage(message) {
const field = getInputField()
const field = chatInputField
field.value = "```markdown\n> " + (message || '') + "\n```\n";
field.focus();
}
@@ -294,8 +185,8 @@
lastMessage = messagesContainer.querySelector(".message:last-child");
if (doScrollDown) {
lastMessage?.scrollIntoView({ block: "end", inline: "nearest" });
const inputBox = document.querySelector(".chat-input");
inputBox.scrollIntoView({ block: "end", inline: "nearest" });
chatInputField.scrollIntoView({ block: "end", inline: "nearest" });
}
}
@@ -378,17 +269,17 @@
messagesContainer.querySelector(".message:last-child").scrollIntoView({ block: "end", inline: "nearest" });
setTimeout(() => {
getInputField().focus();
chatInputField.focus();
},500)
}
}
if (event.shiftKey && event.key === 'G') {
if(document.activeElement != getInputField()){
if(chatInputField.isActive()){
updateLayout(true);
setTimeout(() => {
getInputField().focus();
chatInputField.focus();
},500)
}
@@ -432,7 +323,6 @@
document.body.removeChild(overlay);
});
});
initInputField(getInputField());
updateLayout(true);
</script>
{% endblock %}