Files
eveAI/eveai_chat_client/static/assets/vue-components/MessageHistory.vue

316 lines
9.1 KiB
Vue

<template>
<div class="message-history-container">
<!-- Messages container -->
<div class="chat-messages" ref="messagesContainer">
<!-- Loading indicator for load more -->
<div v-if="$slots.loading" class="load-more-indicator">
<slot name="loading"></slot>
</div>
<!-- Empty state -->
<div v-if="messages.length === 0" class="empty-state">
<div class="empty-icon">💬</div>
<div class="empty-text">Nog geen berichten</div>
<div class="empty-subtext">Start een gesprek door een bericht te typen!</div>
</div>
<!-- Message list -->
<template v-else>
<!-- Messages -->
<template v-for="(message, index) in messages" :key="message.id">
<!-- The actual message -->
<chat-message
:message="message"
:is-submitting-form="isSubmittingForm"
:api-prefix="apiPrefix"
@image-loaded="handleImageLoaded"
@specialist-complete="$emit('specialist-complete', $event)"
@specialist-error="$emit('specialist-error', $event)"
></chat-message>
</template>
</template>
<!-- Typing indicator -->
<typing-indicator v-if="isTyping"></typing-indicator>
</div>
</div>
</template>
<script>
import ChatMessage from './ChatMessage.vue';
import TypingIndicator from './TypingIndicator.vue';
export default {
name: 'MessageHistory',
components: {
'chat-message': ChatMessage,
'typing-indicator': TypingIndicator
},
props: {
messages: {
type: Array,
default: () => []
},
isTyping: {
type: Boolean,
default: false
},
isSubmittingForm: {
type: Boolean,
default: false
},
apiPrefix: {
type: String,
default: ''
},
autoScroll: {
type: Boolean,
default: true
}
},
emits: ['load-more', 'specialist-complete', 'specialist-error'],
data() {
return {
isAtBottom: true,
showScrollButton: false,
unreadCount: 0,
languageChangeHandler: null
};
},
watch: {
messages: {
handler(newMessages, oldMessages) {
// Auto-scroll when new messages are added
if (this.autoScroll && newMessages.length > (oldMessages?.length || 0)) {
this.$nextTick(() => {
this.scrollToBottom();
});
}
},
deep: true
},
isTyping(newVal) {
if (newVal && this.autoScroll) {
this.$nextTick(() => {
this.scrollToBottom();
});
}
}
},
created() {
// Maak een benoemde handler voor betere cleanup
this.languageChangeHandler = (event) => {
if (event.detail && event.detail.language) {
this.handleLanguageChange(event);
}
};
// Luister naar taalwijzigingen
document.addEventListener('language-changed', this.languageChangeHandler);
},
mounted() {
this.setupScrollListener();
// Initial scroll to bottom
if (this.autoScroll) {
this.$nextTick(() => {
this.scrollToBottom();
});
}
},
beforeUnmount() {
// Cleanup scroll listener
const container = this.$refs.messagesContainer;
if (container) {
container.removeEventListener('scroll', this.handleScroll);
}
// Cleanup language change listener
if (this.languageChangeHandler) {
document.removeEventListener('language-changed', this.languageChangeHandler);
}
},
methods: {
async handleLanguageChange(event) {
// Controleer of dit het eerste bericht is in een gesprek met maar één bericht
if (this.messages.length === 1 && this.messages[0].sender === 'ai') {
const firstMessage = this.messages[0];
// Controleer of we een originele inhoud hebben om te vertalen
if (firstMessage.originalContent) {
console.log('Vertaling van eerste AI bericht naar:', event.detail.language);
try {
// Controleer of TranslationClient beschikbaar is
if (!window.TranslationClient || typeof window.TranslationClient.translate !== 'function') {
console.error('TranslationClient.translate is niet beschikbaar');
return;
}
// Gebruik TranslationClient
const response = await window.TranslationClient.translate(
firstMessage.originalContent,
event.detail.language,
null, // source_lang (auto-detect)
'chat_message', // context
this.apiPrefix // API prefix voor tenant routing
);
if (response.success) {
// Update de inhoud van het bericht
firstMessage.content = response.translated_text;
console.log('Eerste bericht succesvol vertaald');
} else {
console.error('Vertaling mislukt:', response.error);
}
} catch (error) {
console.error('Fout bij vertalen eerste bericht:', error);
}
}
}
},
scrollToBottom() {
const container = this.$refs.messagesContainer;
if (container) {
container.scrollTop = container.scrollHeight;
this.isAtBottom = true;
this.showScrollButton = false;
this.unreadCount = 0;
}
},
setupScrollListener() {
const container = this.$refs.messagesContainer;
if (!container) return;
container.addEventListener('scroll', this.handleScroll);
},
handleScroll() {
const container = this.$refs.messagesContainer;
if (!container) return;
const threshold = 100; // pixels from bottom
const isNearBottom = container.scrollHeight - container.scrollTop - container.clientHeight < threshold;
this.isAtBottom = isNearBottom;
// Load more messages when scrolled to top
if (container.scrollTop === 0) {
this.$emit('load-more');
}
},
handleImageLoaded() {
// Auto-scroll when images load to maintain position
if (this.isAtBottom) {
this.$nextTick(() => this.scrollToBottom());
}
},
searchMessages(query) {
// Simple message search
if (!query.trim()) return this.messages;
const searchTerm = query.toLowerCase();
return this.messages.filter(message =>
message.content &&
message.content.toLowerCase().includes(searchTerm)
);
}
}
};
</script>
<style scoped>
.message-history-container {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 10px;
scroll-behavior: smooth;
}
.load-more-indicator {
text-align: center;
padding: 10px;
color: #666;
font-size: 0.9rem;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
text-align: center;
color: #666;
padding: 40px 20px;
}
.empty-icon {
font-size: 3rem;
margin-bottom: 16px;
opacity: 0.5;
}
.empty-text {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 8px;
color: #333;
}
.empty-subtext {
font-size: 0.9rem;
color: #666;
max-width: 300px;
line-height: 1.4;
}
/* Scrollbar styling */
.chat-messages::-webkit-scrollbar {
width: 6px;
}
.chat-messages::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.chat-messages::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.chat-messages::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.chat-messages {
padding: 8px;
}
.empty-state {
padding: 20px 16px;
}
.empty-icon {
font-size: 2.5rem;
}
.empty-text {
font-size: 1.1rem;
}
}
</style>