KI-AGENT: Matrix-Raum-API verallgemeinern
This commit is contained in:
@@ -4,7 +4,8 @@ const { $api } = useNuxtApp()
|
||||
|
||||
const status = ref(null)
|
||||
const identity = ref(null)
|
||||
const generalRoom = ref(null)
|
||||
const matrixRooms = ref([])
|
||||
const activeRoomKey = ref("allgemein")
|
||||
const matrixMessages = ref([])
|
||||
const matrixMembers = ref([])
|
||||
const matrixMessageDraft = ref("")
|
||||
@@ -24,17 +25,26 @@ const canUseMatrixChat = computed(() =>
|
||||
Boolean(status.value?.reachable && status.value?.provisioningConfigured)
|
||||
)
|
||||
|
||||
const activeRoom = computed(() => ({
|
||||
key: "general",
|
||||
name: generalRoom.value?.name || "Allgemeiner Chat",
|
||||
description: generalRoom.value?.alias || "Mandantenweiter Austausch",
|
||||
exists: Boolean(generalRoom.value?.exists),
|
||||
roomId: generalRoom.value?.roomId || "",
|
||||
unread: 0
|
||||
}))
|
||||
const activeRoom = computed(() =>
|
||||
matrixRooms.value.find((room) => room.key === activeRoomKey.value) || {
|
||||
key: activeRoomKey.value,
|
||||
name: activeRoomKey.value,
|
||||
description: "Mandantenweiter Austausch",
|
||||
exists: false,
|
||||
roomId: "",
|
||||
unread: 0
|
||||
}
|
||||
)
|
||||
|
||||
const activeRoomEndpoint = computed(() =>
|
||||
`/api/communication/matrix/rooms/${encodeURIComponent(activeRoomKey.value)}`
|
||||
)
|
||||
|
||||
const rooms = computed(() => [
|
||||
activeRoom.value,
|
||||
...matrixRooms.value.map((room) => ({
|
||||
...room,
|
||||
description: room.alias || room.roomId || "Mandantenweiter Austausch"
|
||||
})),
|
||||
{
|
||||
key: "projects",
|
||||
name: "Projekt-Chats",
|
||||
@@ -51,6 +61,15 @@ const rooms = computed(() => [
|
||||
}
|
||||
])
|
||||
|
||||
const setActiveRoom = async (room) => {
|
||||
if (room.disabled || room.key === activeRoomKey.value) return
|
||||
|
||||
activeRoomKey.value = room.key
|
||||
matrixMessages.value = []
|
||||
matrixMembers.value = []
|
||||
await loadRoomChat({ includeMembers: true })
|
||||
}
|
||||
|
||||
const mergeMatrixMessages = (incomingMessages) => {
|
||||
const byId = new Map()
|
||||
|
||||
@@ -81,19 +100,19 @@ const scrollMessagesToBottom = async () => {
|
||||
const loadChatInfo = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const [statusRes, identityRes, roomRes] = await Promise.all([
|
||||
const [statusRes, identityRes, roomsRes] = await Promise.all([
|
||||
$api("/api/communication/matrix/status"),
|
||||
$api("/api/communication/matrix/me"),
|
||||
$api("/api/communication/matrix/rooms/general")
|
||||
$api("/api/communication/matrix/rooms")
|
||||
])
|
||||
|
||||
status.value = statusRes
|
||||
identity.value = identityRes
|
||||
generalRoom.value = roomRes
|
||||
matrixRooms.value = roomsRes.rooms || []
|
||||
lastUpdated.value = new Date()
|
||||
|
||||
if (roomRes?.exists && canUseMatrixChat.value) {
|
||||
await loadGeneralChat({ silent: true, includeMembers: true })
|
||||
if (activeRoom.value?.exists && canUseMatrixChat.value) {
|
||||
await loadRoomChat({ silent: true, includeMembers: true })
|
||||
}
|
||||
} catch (error) {
|
||||
toast.add({
|
||||
@@ -105,33 +124,27 @@ const loadChatInfo = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const provisionGeneralRoom = async () => {
|
||||
const provisionActiveRoom = async () => {
|
||||
roomProvisioning.value = true
|
||||
try {
|
||||
const res = await $api("/api/communication/matrix/rooms/general/provision", {
|
||||
const res = await $api(`${activeRoomEndpoint.value}/provision`, {
|
||||
method: "POST"
|
||||
})
|
||||
|
||||
generalRoom.value = {
|
||||
tenantId: res.tenantId,
|
||||
tenantName: res.tenantName,
|
||||
key: res.key,
|
||||
name: res.name,
|
||||
alias: res.alias,
|
||||
exists: true,
|
||||
roomId: res.roomId,
|
||||
servers: res.servers || []
|
||||
}
|
||||
const roomWasKnown = matrixRooms.value.some((room) => room.key === res.key)
|
||||
matrixRooms.value = roomWasKnown
|
||||
? matrixRooms.value.map((room) => room.key === res.key ? { ...room, ...res, exists: true } : room)
|
||||
: [...matrixRooms.value, { ...res, exists: true }]
|
||||
|
||||
toast.add({
|
||||
title: res.alreadyExisted ? "Allgemeiner Chat ist bereit" : "Allgemeiner Chat erstellt",
|
||||
title: res.alreadyExisted ? "Chatraum ist bereit" : "Chatraum erstellt",
|
||||
color: "success"
|
||||
})
|
||||
|
||||
await loadGeneralChat({ includeMembers: true })
|
||||
await loadRoomChat({ includeMembers: true })
|
||||
} catch (error) {
|
||||
toast.add({
|
||||
title: "Allgemeiner Chat konnte nicht erstellt werden",
|
||||
title: "Chatraum konnte nicht erstellt werden",
|
||||
color: "error"
|
||||
})
|
||||
} finally {
|
||||
@@ -139,22 +152,22 @@ const provisionGeneralRoom = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const loadGeneralMessages = async ({ silent = false } = {}) => {
|
||||
if (!canUseMatrixChat.value || !generalRoom.value?.exists) return
|
||||
const loadRoomMessages = async ({ silent = false } = {}) => {
|
||||
if (!canUseMatrixChat.value || !activeRoom.value?.exists) return
|
||||
if (matrixMessagesRequestActive) return
|
||||
|
||||
matrixMessagesRequestActive = true
|
||||
if (!silent) matrixMessagesLoading.value = true
|
||||
|
||||
try {
|
||||
const res = await $api("/api/communication/matrix/rooms/general/messages")
|
||||
const res = await $api(`${activeRoomEndpoint.value}/messages`)
|
||||
mergeMatrixMessages(res.messages || [])
|
||||
generalRoom.value = {
|
||||
...generalRoom.value,
|
||||
alias: res.alias || generalRoom.value?.alias,
|
||||
matrixRooms.value = matrixRooms.value.map((room) => room.key === activeRoomKey.value ? {
|
||||
...room,
|
||||
alias: res.alias || room.alias,
|
||||
exists: true,
|
||||
roomId: res.roomId || generalRoom.value?.roomId
|
||||
}
|
||||
roomId: res.roomId || room.roomId
|
||||
} : room)
|
||||
await scrollMessagesToBottom()
|
||||
} catch (error) {
|
||||
if (!silent) {
|
||||
@@ -169,15 +182,15 @@ const loadGeneralMessages = async ({ silent = false } = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
const loadGeneralMembers = async ({ silent = false } = {}) => {
|
||||
if (!canUseMatrixChat.value || !generalRoom.value?.exists) return
|
||||
const loadRoomMembers = async ({ silent = false } = {}) => {
|
||||
if (!canUseMatrixChat.value || !activeRoom.value?.exists) return
|
||||
if (matrixMembersRequestActive) return
|
||||
|
||||
matrixMembersRequestActive = true
|
||||
if (!silent) matrixMembersLoading.value = true
|
||||
|
||||
try {
|
||||
const res = await $api("/api/communication/matrix/rooms/general/members")
|
||||
const res = await $api(`${activeRoomEndpoint.value}/members`)
|
||||
matrixMembers.value = res.members || []
|
||||
} catch (error) {
|
||||
if (!silent) {
|
||||
@@ -192,17 +205,17 @@ const loadGeneralMembers = async ({ silent = false } = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
const loadGeneralChat = async ({ silent = false, includeMembers = false } = {}) => {
|
||||
await loadGeneralMessages({ silent })
|
||||
const loadRoomChat = async ({ silent = false, includeMembers = false } = {}) => {
|
||||
await loadRoomMessages({ silent })
|
||||
|
||||
if (includeMembers) {
|
||||
await loadGeneralMembers({ silent })
|
||||
await loadRoomMembers({ silent })
|
||||
}
|
||||
}
|
||||
|
||||
const sendMatrixMessage = async () => {
|
||||
const text = matrixMessageDraft.value.trim()
|
||||
if (!text || !canUseMatrixChat.value || !generalRoom.value?.exists) return
|
||||
if (!text || !canUseMatrixChat.value || !activeRoom.value?.exists) return
|
||||
|
||||
const optimisticId = `pending-${Date.now()}`
|
||||
const optimisticMessage = {
|
||||
@@ -225,7 +238,7 @@ const sendMatrixMessage = async () => {
|
||||
|
||||
matrixMessageSending.value = true
|
||||
try {
|
||||
const message = await $api("/api/communication/matrix/rooms/general/messages", {
|
||||
const message = await $api(`${activeRoomEndpoint.value}/messages`, {
|
||||
method: "POST",
|
||||
body: { text }
|
||||
})
|
||||
@@ -251,8 +264,8 @@ const startMatrixAutoRefresh = () => {
|
||||
|
||||
matrixAutoRefreshActive.value = true
|
||||
matrixRefreshInterval = window.setInterval(() => {
|
||||
if (!document.hidden && canUseMatrixChat.value && generalRoom.value?.exists) {
|
||||
loadGeneralChat({ silent: true })
|
||||
if (!document.hidden && canUseMatrixChat.value && activeRoom.value?.exists) {
|
||||
loadRoomChat({ silent: true })
|
||||
}
|
||||
}, 15000)
|
||||
}
|
||||
@@ -315,7 +328,7 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
variant="ghost"
|
||||
:loading="loading || matrixMessagesLoading"
|
||||
:disabled="!canUseMatrixChat"
|
||||
@click="loadGeneralChat({ includeMembers: true })"
|
||||
@click="loadRoomChat({ includeMembers: true })"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -328,6 +341,7 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
class="mb-1 flex w-full items-center gap-3 rounded-md px-3 py-2 text-left transition"
|
||||
:class="room.disabled ? 'cursor-not-allowed opacity-50' : 'bg-muted text-highlighted'"
|
||||
:disabled="room.disabled"
|
||||
@click="setActiveRoom(room)"
|
||||
>
|
||||
<span class="flex size-9 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary">
|
||||
<UIcon name="i-heroicons-chat-bubble-left-right" class="size-5" />
|
||||
@@ -368,7 +382,7 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
{{ activeRoom.name }}
|
||||
</h2>
|
||||
<UBadge
|
||||
v-if="matrixAutoRefreshActive && generalRoom?.exists"
|
||||
v-if="matrixAutoRefreshActive && activeRoom?.exists"
|
||||
color="success"
|
||||
variant="soft"
|
||||
size="xs"
|
||||
@@ -395,8 +409,8 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
:loading="matrixMessagesLoading"
|
||||
:disabled="!canUseMatrixChat || !generalRoom?.exists"
|
||||
@click="loadGeneralChat({ includeMembers: true })"
|
||||
:disabled="!canUseMatrixChat || !activeRoom?.exists"
|
||||
@click="loadRoomChat({ includeMembers: true })"
|
||||
>
|
||||
Laden
|
||||
</UButton>
|
||||
@@ -417,7 +431,7 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="status && !generalRoom?.exists"
|
||||
v-else-if="status && !activeRoom?.exists"
|
||||
class="border-b border-default bg-default px-4 py-3"
|
||||
>
|
||||
<UAlert
|
||||
@@ -431,7 +445,7 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
<UButton
|
||||
icon="i-heroicons-plus"
|
||||
:loading="roomProvisioning"
|
||||
@click="provisionGeneralRoom"
|
||||
@click="provisionActiveRoom"
|
||||
>
|
||||
Chat erstellen
|
||||
</UButton>
|
||||
@@ -487,14 +501,14 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
v-model="matrixMessageDraft"
|
||||
class="min-w-0 flex-1"
|
||||
placeholder="Nachricht schreiben"
|
||||
:disabled="matrixMessageSending || !canUseMatrixChat || !generalRoom?.exists"
|
||||
:disabled="matrixMessageSending || !canUseMatrixChat || !activeRoom?.exists"
|
||||
@keydown.enter.exact.prevent="sendMatrixMessage"
|
||||
/>
|
||||
<UButton
|
||||
type="submit"
|
||||
icon="i-heroicons-paper-airplane"
|
||||
:loading="matrixMessageSending"
|
||||
:disabled="!matrixMessageDraft.trim() || !canUseMatrixChat || !generalRoom?.exists"
|
||||
:disabled="!matrixMessageDraft.trim() || !canUseMatrixChat || !activeRoom?.exists"
|
||||
>
|
||||
Senden
|
||||
</UButton>
|
||||
@@ -507,7 +521,7 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
Raumdetails
|
||||
</h3>
|
||||
<p class="mt-1 break-all font-mono text-xs text-muted">
|
||||
{{ generalRoom?.roomId || generalRoom?.alias || "Noch kein Matrix-Raum" }}
|
||||
{{ activeRoom?.roomId || activeRoom?.alias || "Noch kein Matrix-Raum" }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -523,26 +537,26 @@ onBeforeUnmount(stopMatrixAutoRefresh)
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
:loading="matrixMembersLoading"
|
||||
:disabled="!canUseMatrixChat || !generalRoom?.exists"
|
||||
@click="loadGeneralMembers"
|
||||
:disabled="!canUseMatrixChat || !activeRoom?.exists"
|
||||
@click="loadRoomMembers"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="member in matrixMembers"
|
||||
:key="member.userId"
|
||||
:key="member.matrixUserId"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<span class="flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-xs font-medium text-highlighted">
|
||||
{{ (member.displayName || member.userId || "?").slice(0, 1).toUpperCase() }}
|
||||
{{ (member.displayName || member.matrixUserId || "?").slice(0, 1).toUpperCase() }}
|
||||
</span>
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-sm font-medium text-highlighted">
|
||||
{{ member.displayName || member.userId }}
|
||||
{{ member.displayName || member.matrixUserId }}
|
||||
</p>
|
||||
<p class="truncate text-xs text-muted">
|
||||
{{ member.userId }}
|
||||
{{ member.matrixUserId }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user