diff --git a/frontend/pages/communication/chat.vue b/frontend/pages/communication/chat.vue index a7d6960..682089c 100644 --- a/frontend/pages/communication/chat.vue +++ b/frontend/pages/communication/chat.vue @@ -10,8 +10,15 @@ const matrixMessages = ref([]) const matrixMembers = ref([]) const matrixMessageDraft = ref("") const matrixMessagesViewport = ref(null) +const roomCreateOpen = ref(false) +const roomCreateForm = ref({ + name: "", + key: "", + topic: "" +}) const loading = ref(false) const roomProvisioning = ref(false) +const roomCreating = ref(false) const matrixMessagesLoading = ref(false) const matrixMembersLoading = ref(false) const matrixMessageSending = ref(false) @@ -40,6 +47,10 @@ const activeRoomEndpoint = computed(() => `/api/communication/matrix/rooms/${encodeURIComponent(activeRoomKey.value)}` ) +const roomCreateKeyPreview = computed(() => + normalizeRoomKey(roomCreateForm.value.key || roomCreateForm.value.name) +) + const rooms = computed(() => [ ...matrixRooms.value.map((room) => ({ ...room, @@ -61,6 +72,22 @@ const rooms = computed(() => [ } ]) +const normalizeRoomKey = (value) => { + const normalized = String(value || "") + .toLowerCase() + .normalize("NFKD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/ä/g, "a") + .replace(/ö/g, "o") + .replace(/ü/g, "u") + .replace(/ß/g, "ss") + .replace(/[^a-z0-9._=-]+/g, "_") + .replace(/_+/g, "_") + .replace(/^[._=-]+|[._=-]+$/g, "") + + return normalized || "raum" +} + const setActiveRoom = async (room) => { if (room.disabled || room.key === activeRoomKey.value) return @@ -70,6 +97,13 @@ const setActiveRoom = async (room) => { await loadRoomChat({ includeMembers: true }) } +const upsertRoom = (room) => { + const roomWasKnown = matrixRooms.value.some((item) => item.key === room.key) + matrixRooms.value = roomWasKnown + ? matrixRooms.value.map((item) => item.key === room.key ? { ...item, ...room } : item) + : [...matrixRooms.value, room] +} + const mergeMatrixMessages = (incomingMessages) => { const byId = new Map() @@ -131,10 +165,7 @@ const provisionActiveRoom = async () => { method: "POST" }) - 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 }] + upsertRoom({ ...res, exists: true }) toast.add({ title: res.alreadyExisted ? "Chatraum ist bereit" : "Chatraum erstellt", @@ -152,6 +183,58 @@ const provisionActiveRoom = async () => { } } +const createRoom = async () => { + const name = roomCreateForm.value.name.trim() + const key = roomCreateKeyPreview.value + const topic = roomCreateForm.value.topic.trim() + + if (!name) { + toast.add({ + title: "Bitte gib einen Raumnamen ein", + color: "warning" + }) + return + } + + roomCreating.value = true + try { + const room = await $api("/api/communication/matrix/rooms", { + method: "POST", + body: { + key, + name, + topic: topic || undefined, + type: "room" + } + }) + + upsertRoom({ ...room, exists: true }) + activeRoomKey.value = room.key + matrixMessages.value = [] + matrixMembers.value = [] + roomCreateForm.value = { + name: "", + key: "", + topic: "" + } + roomCreateOpen.value = false + + toast.add({ + title: room.alreadyExisted ? "Chatraum ist bereit" : "Chatraum erstellt", + color: "success" + }) + + await loadRoomChat({ includeMembers: true }) + } catch (error) { + toast.add({ + title: "Chatraum konnte nicht erstellt werden", + color: "error" + }) + } finally { + roomCreating.value = false + } +} + const loadRoomMessages = async ({ silent = false } = {}) => { if (!canUseMatrixChat.value || !activeRoom.value?.exists) return if (matrixMessagesRequestActive) return @@ -333,13 +416,74 @@ onBeforeUnmount(stopMatrixAutoRefresh) +