KI-AGENT: Projekträume und Direktnachrichten integrieren
This commit is contained in:
@@ -56,6 +56,7 @@ type MatrixTenantRoomOptions = {
|
|||||||
entityType?: string | null
|
entityType?: string | null
|
||||||
entityId?: number | null
|
entityId?: number | null
|
||||||
entityUuid?: string | null
|
entityUuid?: string | null
|
||||||
|
inviteUserIds?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
type MatrixCachedValue<T = any> = {
|
type MatrixCachedValue<T = any> = {
|
||||||
@@ -247,6 +248,7 @@ export function matrixService(server: FastifyInstance) {
|
|||||||
entityType: options.entityType || null,
|
entityType: options.entityType || null,
|
||||||
entityId: options.entityId || null,
|
entityId: options.entityId || null,
|
||||||
entityUuid: options.entityUuid || null,
|
entityUuid: options.entityUuid || null,
|
||||||
|
inviteUserIds: options.inviteUserIds || [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -885,6 +887,7 @@ export function matrixService(server: FastifyInstance) {
|
|||||||
|
|
||||||
const existing = await getTenantRoomStatus(tenant.id, key, name)
|
const existing = await getTenantRoomStatus(tenant.id, key, name)
|
||||||
const userAccount = await provisionCurrentUser(userId, tenant.id)
|
const userAccount = await provisionCurrentUser(userId, tenant.id)
|
||||||
|
const invitedMatrixUserIds = await matrixUserIdsForInvitees(userId, tenant.id, normalizedOptions.inviteUserIds || [])
|
||||||
const tenantSpace = await provisionCurrentTenantSpace(userId, tenant.id)
|
const tenantSpace = await provisionCurrentTenantSpace(userId, tenant.id)
|
||||||
|
|
||||||
if (existing.exists) {
|
if (existing.exists) {
|
||||||
@@ -902,6 +905,8 @@ export function matrixService(server: FastifyInstance) {
|
|||||||
invitedUserId: userAccount.matrixUserId,
|
invitedUserId: userAccount.matrixUserId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await inviteUsersToRoom(existing.roomId, invitedMatrixUserIds)
|
||||||
|
|
||||||
matrixTenantRoomCache.set(cacheKey, {
|
matrixTenantRoomCache.set(cacheKey, {
|
||||||
exists: true,
|
exists: true,
|
||||||
cachedUntil: Date.now() + 30 * 60 * 1000,
|
cachedUntil: Date.now() + 30 * 60 * 1000,
|
||||||
@@ -923,7 +928,7 @@ export function matrixService(server: FastifyInstance) {
|
|||||||
preset: "private_chat",
|
preset: "private_chat",
|
||||||
visibility: "private",
|
visibility: "private",
|
||||||
room_alias_name: tenantRoomAliasLocalpart(tenant, key),
|
room_alias_name: tenantRoomAliasLocalpart(tenant, key),
|
||||||
invite: [userAccount.matrixUserId],
|
invite: Array.from(new Set([userAccount.matrixUserId, ...invitedMatrixUserIds])),
|
||||||
initial_state: [
|
initial_state: [
|
||||||
{
|
{
|
||||||
type: "m.room.history_visibility",
|
type: "m.room.history_visibility",
|
||||||
@@ -994,6 +999,42 @@ export function matrixService(server: FastifyInstance) {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const matrixUserIdsForInvitees = async (
|
||||||
|
currentUserId: string,
|
||||||
|
tenantId: number,
|
||||||
|
inviteUserIds: string[]
|
||||||
|
) => {
|
||||||
|
const uniqueUserIds = Array.from(new Set(inviteUserIds.filter((id) => id && id !== currentUserId)))
|
||||||
|
|
||||||
|
return await Promise.all(uniqueUserIds.map(async (inviteUserId) => {
|
||||||
|
const account = await provisionCurrentUser(inviteUserId, tenantId)
|
||||||
|
return account.matrixUserId
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const inviteUsersToRoom = async (roomId: string | null, matrixUserIds: string[]) => {
|
||||||
|
if (!roomId || !matrixUserIds.length) return
|
||||||
|
|
||||||
|
const serviceLogin = await ensureServiceAccessToken()
|
||||||
|
|
||||||
|
for (const matrixUserId of matrixUserIds) {
|
||||||
|
try {
|
||||||
|
await requestMatrixJson(
|
||||||
|
`/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/invite`,
|
||||||
|
serviceLogin.accessToken,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ user_id: matrixUserId }),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.statusCode === 403 || err.statusCode === 400) continue
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const ensureCurrentUserJoinedRoom = async (
|
const ensureCurrentUserJoinedRoom = async (
|
||||||
userId: string,
|
userId: string,
|
||||||
tenantId: number | null,
|
tenantId: number | null,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
import { createHash } from "node:crypto"
|
||||||
import { FastifyInstance } from "fastify"
|
import { FastifyInstance } from "fastify"
|
||||||
import { and, eq, ne } from "drizzle-orm"
|
import { and, eq, inArray, ne } from "drizzle-orm"
|
||||||
import { authTenantUsers, authUsers } from "../../db/schema"
|
import { authProfiles, authTenantUsers, authUsers, projects } from "../../db/schema"
|
||||||
import { matrixService } from "../modules/matrix.service"
|
import { matrixService } from "../modules/matrix.service"
|
||||||
import { NotificationService, UserDirectory } from "../modules/notification.service"
|
import { NotificationService, UserDirectory } from "../modules/notification.service"
|
||||||
|
|
||||||
@@ -47,6 +48,22 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectRoomKey = (projectId: number) => `project_${projectId}`
|
||||||
|
|
||||||
|
const directRoomKey = (firstUserId: string, secondUserId: string) => {
|
||||||
|
const hash = createHash("sha256")
|
||||||
|
.update([firstUserId, secondUserId].sort().join(":"))
|
||||||
|
.digest("hex")
|
||||||
|
.slice(0, 16)
|
||||||
|
|
||||||
|
return `direct_${hash}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayUserName = (user: { fullName?: string | null; firstName?: string | null; lastName?: string | null; email?: string | null }) => {
|
||||||
|
const name = user.fullName || [user.firstName, user.lastName].filter(Boolean).join(" ")
|
||||||
|
return name || user.email || "Benutzer"
|
||||||
|
}
|
||||||
|
|
||||||
const callModeFromRequest = (req: any): "audio" | "video" => {
|
const callModeFromRequest = (req: any): "audio" | "video" => {
|
||||||
const body = (req.body || {}) as { mode?: string }
|
const body = (req.body || {}) as { mode?: string }
|
||||||
return body.mode === "audio" ? "audio" : "video"
|
return body.mode === "audio" ? "audio" : "video"
|
||||||
@@ -131,6 +148,186 @@ export default async function communicationRoutes(server: FastifyInstance) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
server.get("/communication/matrix/project-rooms", async (req, reply) => {
|
||||||
|
try {
|
||||||
|
if (!req.user.tenant_id) return reply.code(400).send({ error: "Kein aktiver Mandant" })
|
||||||
|
|
||||||
|
const [roomsRes, projectRows] = await Promise.all([
|
||||||
|
matrix.listTenantRooms(req.user.tenant_id),
|
||||||
|
server.db
|
||||||
|
.select({
|
||||||
|
id: projects.id,
|
||||||
|
name: projects.name,
|
||||||
|
projectNumber: projects.projectNumber,
|
||||||
|
profiles: projects.profiles,
|
||||||
|
})
|
||||||
|
.from(projects)
|
||||||
|
.where(and(
|
||||||
|
eq(projects.tenant, req.user.tenant_id),
|
||||||
|
eq(projects.archived, false)
|
||||||
|
))
|
||||||
|
])
|
||||||
|
const roomsByKey = new Map((roomsRes.rooms || []).map((room: any) => [room.key, room]))
|
||||||
|
|
||||||
|
return {
|
||||||
|
rooms: projectRows.map((project) => {
|
||||||
|
const key = projectRoomKey(project.id)
|
||||||
|
const existing = roomsByKey.get(key) as any
|
||||||
|
return {
|
||||||
|
...(existing || {}),
|
||||||
|
key,
|
||||||
|
name: project.projectNumber ? `${project.projectNumber} · ${project.name}` : project.name,
|
||||||
|
topic: `Projektkommunikation zu ${project.name}`,
|
||||||
|
type: "project",
|
||||||
|
entityType: "project",
|
||||||
|
entityId: project.id,
|
||||||
|
exists: Boolean(existing?.exists),
|
||||||
|
projectId: project.id,
|
||||||
|
projectNumber: project.projectNumber,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
return handleMatrixError(req, reply, err, "Matrix project rooms failed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
server.post("/communication/matrix/project-rooms/:projectId/provision", async (req, reply) => {
|
||||||
|
try {
|
||||||
|
if (!req.user.tenant_id) return reply.code(400).send({ error: "Kein aktiver Mandant" })
|
||||||
|
const params = req.params as { projectId: string }
|
||||||
|
const projectId = Number(params.projectId)
|
||||||
|
const [project] = await server.db
|
||||||
|
.select()
|
||||||
|
.from(projects)
|
||||||
|
.where(and(
|
||||||
|
eq(projects.tenant, req.user.tenant_id),
|
||||||
|
eq(projects.id, projectId)
|
||||||
|
))
|
||||||
|
.limit(1)
|
||||||
|
|
||||||
|
if (!project) return reply.code(404).send({ error: "Projekt nicht gefunden" })
|
||||||
|
|
||||||
|
const profileIds = (project.profiles || []) as string[]
|
||||||
|
const profileRows = profileIds.length
|
||||||
|
? await server.db
|
||||||
|
.select({ userId: authProfiles.user_id })
|
||||||
|
.from(authProfiles)
|
||||||
|
.where(and(
|
||||||
|
eq(authProfiles.tenant_id, req.user.tenant_id),
|
||||||
|
inArray(authProfiles.id, profileIds)
|
||||||
|
))
|
||||||
|
: []
|
||||||
|
const inviteUserIds = profileRows.map((profile) => profile.userId).filter(Boolean) as string[]
|
||||||
|
|
||||||
|
return await matrix.provisionTenantRoom(req.user.user_id, req.user.tenant_id, {
|
||||||
|
key: projectRoomKey(project.id),
|
||||||
|
name: project.projectNumber ? `${project.projectNumber} · ${project.name}` : project.name,
|
||||||
|
topic: `Projektkommunikation zu ${project.name}`,
|
||||||
|
type: "project",
|
||||||
|
entityType: "project",
|
||||||
|
entityId: project.id,
|
||||||
|
inviteUserIds,
|
||||||
|
})
|
||||||
|
} catch (err: any) {
|
||||||
|
return handleMatrixError(req, reply, err, "Matrix project room provisioning failed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
server.get("/communication/matrix/direct-rooms", async (req, reply) => {
|
||||||
|
try {
|
||||||
|
if (!req.user.tenant_id) return reply.code(400).send({ error: "Kein aktiver Mandant" })
|
||||||
|
const [roomsRes, userRows] = await Promise.all([
|
||||||
|
matrix.listTenantRooms(req.user.tenant_id),
|
||||||
|
server.db
|
||||||
|
.select({
|
||||||
|
userId: authTenantUsers.user_id,
|
||||||
|
email: authUsers.email,
|
||||||
|
firstName: authProfiles.first_name,
|
||||||
|
lastName: authProfiles.last_name,
|
||||||
|
fullName: authProfiles.full_name,
|
||||||
|
})
|
||||||
|
.from(authTenantUsers)
|
||||||
|
.innerJoin(authUsers, eq(authUsers.id, authTenantUsers.user_id))
|
||||||
|
.leftJoin(authProfiles, and(
|
||||||
|
eq(authProfiles.user_id, authTenantUsers.user_id),
|
||||||
|
eq(authProfiles.tenant_id, req.user.tenant_id)
|
||||||
|
))
|
||||||
|
.where(and(
|
||||||
|
eq(authTenantUsers.tenant_id, req.user.tenant_id),
|
||||||
|
ne(authTenantUsers.user_id, req.user.user_id)
|
||||||
|
))
|
||||||
|
])
|
||||||
|
const roomsByKey = new Map((roomsRes.rooms || []).map((room: any) => [room.key, room]))
|
||||||
|
|
||||||
|
return {
|
||||||
|
rooms: userRows.map((user) => {
|
||||||
|
const key = directRoomKey(req.user.user_id, user.userId)
|
||||||
|
const existing = roomsByKey.get(key) as any
|
||||||
|
const name = displayUserName(user)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...(existing || {}),
|
||||||
|
key,
|
||||||
|
name,
|
||||||
|
topic: `Direktnachricht mit ${name}`,
|
||||||
|
type: "direct",
|
||||||
|
entityType: "user",
|
||||||
|
entityUuid: user.userId,
|
||||||
|
exists: Boolean(existing?.exists),
|
||||||
|
userId: user.userId,
|
||||||
|
email: user.email,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
return handleMatrixError(req, reply, err, "Matrix direct rooms failed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
server.post("/communication/matrix/direct-rooms/:userId/provision", async (req, reply) => {
|
||||||
|
try {
|
||||||
|
if (!req.user.tenant_id) return reply.code(400).send({ error: "Kein aktiver Mandant" })
|
||||||
|
const params = req.params as { userId: string }
|
||||||
|
const [target] = await server.db
|
||||||
|
.select({
|
||||||
|
userId: authTenantUsers.user_id,
|
||||||
|
email: authUsers.email,
|
||||||
|
firstName: authProfiles.first_name,
|
||||||
|
lastName: authProfiles.last_name,
|
||||||
|
fullName: authProfiles.full_name,
|
||||||
|
})
|
||||||
|
.from(authTenantUsers)
|
||||||
|
.innerJoin(authUsers, eq(authUsers.id, authTenantUsers.user_id))
|
||||||
|
.leftJoin(authProfiles, and(
|
||||||
|
eq(authProfiles.user_id, authTenantUsers.user_id),
|
||||||
|
eq(authProfiles.tenant_id, req.user.tenant_id)
|
||||||
|
))
|
||||||
|
.where(and(
|
||||||
|
eq(authTenantUsers.tenant_id, req.user.tenant_id),
|
||||||
|
eq(authTenantUsers.user_id, params.userId)
|
||||||
|
))
|
||||||
|
.limit(1)
|
||||||
|
|
||||||
|
if (!target || target.userId === req.user.user_id) {
|
||||||
|
return reply.code(404).send({ error: "Benutzer nicht gefunden" })
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetName = displayUserName(target)
|
||||||
|
return await matrix.provisionTenantRoom(req.user.user_id, req.user.tenant_id, {
|
||||||
|
key: directRoomKey(req.user.user_id, target.userId),
|
||||||
|
name: targetName,
|
||||||
|
topic: `Direktnachricht mit ${targetName}`,
|
||||||
|
type: "direct",
|
||||||
|
entityType: "user",
|
||||||
|
entityUuid: target.userId,
|
||||||
|
inviteUserIds: [target.userId],
|
||||||
|
})
|
||||||
|
} catch (err: any) {
|
||||||
|
return handleMatrixError(req, reply, err, "Matrix direct room provisioning failed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
server.post("/communication/matrix/rooms", async (req, reply) => {
|
server.post("/communication/matrix/rooms", async (req, reply) => {
|
||||||
try {
|
try {
|
||||||
return await matrix.provisionTenantRoom(
|
return await matrix.provisionTenantRoom(
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ const { $api } = useNuxtApp()
|
|||||||
const status = ref(null)
|
const status = ref(null)
|
||||||
const identity = ref(null)
|
const identity = ref(null)
|
||||||
const matrixRooms = ref([])
|
const matrixRooms = ref([])
|
||||||
|
const matrixProjectRooms = ref([])
|
||||||
|
const matrixDirectRooms = ref([])
|
||||||
const activeRoomKey = ref("allgemein")
|
const activeRoomKey = ref("allgemein")
|
||||||
const matrixMessages = ref([])
|
const matrixMessages = ref([])
|
||||||
const matrixMembers = ref([])
|
const matrixMembers = ref([])
|
||||||
@@ -35,6 +37,7 @@ const roomCreateForm = ref({
|
|||||||
})
|
})
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const roomProvisioning = ref(false)
|
const roomProvisioning = ref(false)
|
||||||
|
const roomProvisioningKey = ref("")
|
||||||
const roomCreating = ref(false)
|
const roomCreating = ref(false)
|
||||||
const roomMembersSyncing = ref(false)
|
const roomMembersSyncing = ref(false)
|
||||||
const matrixMessagesLoading = ref(false)
|
const matrixMessagesLoading = ref(false)
|
||||||
@@ -54,7 +57,7 @@ const canUseMatrixChat = computed(() =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
const activeRoom = computed(() =>
|
const activeRoom = computed(() =>
|
||||||
matrixRooms.value.find((room) => room.key === activeRoomKey.value) || {
|
rooms.value.find((room) => room.key === activeRoomKey.value) || {
|
||||||
key: activeRoomKey.value,
|
key: activeRoomKey.value,
|
||||||
name: activeRoomKey.value,
|
name: activeRoomKey.value,
|
||||||
description: "Mandantenweiter Austausch",
|
description: "Mandantenweiter Austausch",
|
||||||
@@ -109,26 +112,34 @@ const roomCreateKeyPreview = computed(() =>
|
|||||||
normalizeRoomKey(roomCreateForm.value.key || roomCreateForm.value.name)
|
normalizeRoomKey(roomCreateForm.value.key || roomCreateForm.value.name)
|
||||||
)
|
)
|
||||||
|
|
||||||
const rooms = computed(() => [
|
const rooms = computed(() => {
|
||||||
...matrixRooms.value.map((room) => ({
|
const baseRooms = matrixRooms.value
|
||||||
|
.filter((room) => !["project", "direct"].includes(room.type))
|
||||||
|
.map((room) => ({
|
||||||
...room,
|
...room,
|
||||||
|
group: "Räume",
|
||||||
|
icon: "i-heroicons-chat-bubble-left-right",
|
||||||
description: room.alias || room.roomId || "Mandantenweiter Austausch"
|
description: room.alias || room.roomId || "Mandantenweiter Austausch"
|
||||||
})),
|
}))
|
||||||
{
|
|
||||||
key: "projects",
|
const projectRooms = matrixProjectRooms.value.map((room) => ({
|
||||||
name: "Projekt-Chats",
|
...room,
|
||||||
description: "Nächste Ausbaustufe",
|
group: "Projekte",
|
||||||
exists: false,
|
icon: "i-heroicons-briefcase",
|
||||||
disabled: true
|
description: room.projectNumber || room.topic || "Projektkommunikation",
|
||||||
},
|
provisionEndpoint: `/api/communication/matrix/project-rooms/${encodeURIComponent(room.projectId)}/provision`
|
||||||
{
|
}))
|
||||||
key: "direct",
|
|
||||||
name: "Direktnachrichten",
|
const directRooms = matrixDirectRooms.value.map((room) => ({
|
||||||
description: "Nächste Ausbaustufe",
|
...room,
|
||||||
exists: false,
|
group: "Direkt",
|
||||||
disabled: true
|
icon: "i-heroicons-user-circle",
|
||||||
}
|
description: room.email || room.topic || "Direktnachricht",
|
||||||
])
|
provisionEndpoint: `/api/communication/matrix/direct-rooms/${encodeURIComponent(room.userId)}/provision`
|
||||||
|
}))
|
||||||
|
|
||||||
|
return [...baseRooms, ...projectRooms, ...directRooms]
|
||||||
|
})
|
||||||
|
|
||||||
const normalizeRoomKey = (value) => {
|
const normalizeRoomKey = (value) => {
|
||||||
const normalized = String(value || "")
|
const normalized = String(value || "")
|
||||||
@@ -149,12 +160,61 @@ const normalizeRoomKey = (value) => {
|
|||||||
const setActiveRoom = async (room) => {
|
const setActiveRoom = async (room) => {
|
||||||
if (room.disabled || room.key === activeRoomKey.value) return
|
if (room.disabled || room.key === activeRoomKey.value) return
|
||||||
|
|
||||||
|
if (!room.exists && room.provisionEndpoint) {
|
||||||
|
await provisionRoomFromList(room)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
activeRoomKey.value = room.key
|
activeRoomKey.value = room.key
|
||||||
matrixMessages.value = []
|
matrixMessages.value = []
|
||||||
matrixMembers.value = []
|
matrixMembers.value = []
|
||||||
await loadRoomChat({ includeMembers: true })
|
await loadRoomChat({ includeMembers: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mergeRoomIntoLists = (room) => {
|
||||||
|
upsertRoom({ ...room, exists: true })
|
||||||
|
|
||||||
|
if (room.type === "project") {
|
||||||
|
matrixProjectRooms.value = matrixProjectRooms.value.map((item) =>
|
||||||
|
item.key === room.key ? { ...item, ...room, exists: true } : item
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room.type === "direct") {
|
||||||
|
matrixDirectRooms.value = matrixDirectRooms.value.map((item) =>
|
||||||
|
item.key === room.key ? { ...item, ...room, exists: true } : item
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const provisionRoomFromList = async (room) => {
|
||||||
|
roomProvisioningKey.value = room.key
|
||||||
|
try {
|
||||||
|
const createdRoom = await $api(room.provisionEndpoint, {
|
||||||
|
method: "POST"
|
||||||
|
})
|
||||||
|
|
||||||
|
mergeRoomIntoLists(createdRoom)
|
||||||
|
activeRoomKey.value = createdRoom.key
|
||||||
|
matrixMessages.value = []
|
||||||
|
matrixMembers.value = []
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
title: "Chatraum ist bereit",
|
||||||
|
color: "success"
|
||||||
|
})
|
||||||
|
|
||||||
|
await loadRoomChat({ includeMembers: true })
|
||||||
|
} catch (error) {
|
||||||
|
toast.add({
|
||||||
|
title: "Chatraum konnte nicht erstellt werden",
|
||||||
|
color: "error"
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
roomProvisioningKey.value = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const upsertRoom = (room) => {
|
const upsertRoom = (room) => {
|
||||||
const roomWasKnown = matrixRooms.value.some((item) => item.key === room.key)
|
const roomWasKnown = matrixRooms.value.some((item) => item.key === room.key)
|
||||||
matrixRooms.value = roomWasKnown
|
matrixRooms.value = roomWasKnown
|
||||||
@@ -192,15 +252,19 @@ const scrollMessagesToBottom = async () => {
|
|||||||
const loadChatInfo = async () => {
|
const loadChatInfo = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const [statusRes, identityRes, roomsRes] = await Promise.all([
|
const [statusRes, identityRes, roomsRes, projectRoomsRes, directRoomsRes] = await Promise.all([
|
||||||
$api("/api/communication/matrix/status"),
|
$api("/api/communication/matrix/status"),
|
||||||
$api("/api/communication/matrix/me"),
|
$api("/api/communication/matrix/me"),
|
||||||
$api("/api/communication/matrix/rooms")
|
$api("/api/communication/matrix/rooms"),
|
||||||
|
$api("/api/communication/matrix/project-rooms"),
|
||||||
|
$api("/api/communication/matrix/direct-rooms")
|
||||||
])
|
])
|
||||||
|
|
||||||
status.value = statusRes
|
status.value = statusRes
|
||||||
identity.value = identityRes
|
identity.value = identityRes
|
||||||
matrixRooms.value = roomsRes.rooms || []
|
matrixRooms.value = roomsRes.rooms || []
|
||||||
|
matrixProjectRooms.value = projectRoomsRes.rooms || []
|
||||||
|
matrixDirectRooms.value = directRoomsRes.rooms || []
|
||||||
lastUpdated.value = new Date()
|
lastUpdated.value = new Date()
|
||||||
|
|
||||||
if (activeRoom.value?.exists && canUseMatrixChat.value) {
|
if (activeRoom.value?.exists && canUseMatrixChat.value) {
|
||||||
@@ -869,14 +933,22 @@ onBeforeUnmount(() => {
|
|||||||
@click="setActiveRoom(room)"
|
@click="setActiveRoom(room)"
|
||||||
>
|
>
|
||||||
<span class="flex size-9 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary">
|
<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" />
|
<UIcon :name="room.icon || 'i-heroicons-chat-bubble-left-right'" class="size-5" />
|
||||||
</span>
|
</span>
|
||||||
<span class="min-w-0 flex-1">
|
<span class="min-w-0 flex-1">
|
||||||
<span class="block truncate text-sm font-medium">{{ room.name }}</span>
|
<span class="block truncate text-sm font-medium">{{ room.name }}</span>
|
||||||
<span class="block truncate text-xs text-muted">{{ room.description }}</span>
|
<span class="block truncate text-xs text-muted">{{ room.description }}</span>
|
||||||
</span>
|
</span>
|
||||||
<UBadge
|
<UBadge
|
||||||
v-if="room.exists"
|
v-if="roomProvisioningKey === room.key"
|
||||||
|
color="primary"
|
||||||
|
variant="soft"
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
lädt
|
||||||
|
</UBadge>
|
||||||
|
<UBadge
|
||||||
|
v-else-if="room.exists"
|
||||||
color="success"
|
color="success"
|
||||||
variant="soft"
|
variant="soft"
|
||||||
size="xs"
|
size="xs"
|
||||||
|
|||||||
Reference in New Issue
Block a user