# 04 — Direct Messages (DMs) --- ## Inhaltsverzeichnis - [DM starten oder finden](#dm-starten-oder-finden) - [DM-Raum identifizieren](#dm-raum-identifizieren) - [DM-Liste](#dm-liste) - [Gruppen-DM](#gruppen-dm) - [DM-Raum schließen / vergessen](#dm-raum-schließen--vergessen) - [User-Profil für DM laden](#user-profil-für-dm-laden) - [Verschlüsselung in DMs](#verschlüsselung-in-dms) --- ## DM starten oder finden ```dart // Existierenden DM finden oder neuen erstellen // Gibt immer einen joined Room zurück final room = await client.startDirectChat('@alice:server'); print(room.id); // !roomId:server // Mit expliziten Optionen final room = await client.startDirectChat( '@alice:server', // enableEncryption: true, // Standard: true ); // Navigieren zum DM-Raum // (z.B. in Pyramid:) ref.read(activeRoomIdProvider.notifier).state = room.id; ``` --- ## DM-Raum identifizieren ```dart final room = client.getRoomById('!roomId:server')!; // Ist es ein DM? final isDm = room.isDirectChat; // bool // Mit wem? final partner = room.directChatMatrixID; // '@alice:server' oder null // DM zu einem bestimmten User finden final existingDm = client.getDirectChatFromUserId('@alice:server'); // Gibt Room? zurück (null wenn kein DM vorhanden) // Alle DMs des Users (Account-Data) final dmMap = client.directChats; // Map>: { '@alice:server': ['!roomId:server'] } ``` --- ## DM-Liste ```dart // Alle DM-Räume (joined) final dms = client.rooms.where((r) => r.isDirectChat && r.membership == Membership.join, ).toList(); // Sortiert nach letzter Aktivität dms.sort((a, b) => (b.lastEvent?.originServerTs ?? DateTime(0)) .compareTo(a.lastEvent?.originServerTs ?? DateTime(0))); // DMs mit ungelesenen Nachrichten final unreadDms = dms.where((r) => r.notificationCount > 0).toList(); // Display-Info für DM-Liste for (final dm in dms) { final partner = client.getDirectChatFromUserId(dm.directChatMatrixID!); final user = dm.unsafeGetUserFromMemoryOrFallback(dm.directChatMatrixID!); print(user.displayName ?? dm.directChatMatrixID); print(user.avatarUrl); print(dm.lastEvent?.body); // Vorschautext print(dm.lastEvent?.originServerTs); // Zeitstempel print(dm.notificationCount); // Ungelesen } ``` --- ## Gruppen-DM ```dart // Gruppen-DM mit mehreren Personen erstellen final roomId = await client.createRoom( name: 'Gruppenunterhaltung', invite: ['@bob:server', '@charlie:server'], isDirect: true, // Als DM markieren preset: CreateRoomPreset.trustedPrivateChat, enableEncryption: true, ); // Oder mit startDirectChat (für Einzel-DMs) und manuell Teilnehmer hinzufügen final room = await client.startDirectChat('@bob:server'); await room.invite('@charlie:server'); // Gruppe als DM im Account-Data kennzeichnen // (passiert automatisch bei isDirect: true) ``` --- ## DM-Raum schließen / vergessen ```dart final room = client.getRoomById('!roomId:server')!; // Verlassen (Gegenüber kann weiter chatten) await room.leave(); // Aus Raumliste entfernen await room.forget(); // DM aus Account-Daten entfernen final dmMap = Map.from(client.directChats); dmMap.remove('@alice:server'); // oder nur den spezifischen Raum entfernen: final rooms = List.from(dmMap['@alice:server'] ?? []); rooms.remove(room.id); if (rooms.isEmpty) { dmMap.remove('@alice:server'); } else { dmMap['@alice:server'] = rooms; } await client.setAccountData( client.userID!, 'm.direct', dmMap, ); ``` --- ## User-Profil für DM laden ```dart // Profil des DM-Partners laden final partnerMxid = room.directChatMatrixID!; // Aus Room-Membership (gecacht, schnell) final user = room.unsafeGetUserFromMemoryOrFallback(partnerMxid); print(user.displayName); print(user.avatarUrl); // Frisch vom Server (genauer, aber langsamer) final profile = await client.getProfileFromUserId(partnerMxid); print(profile.displayname); print(profile.avatarUrl); // Avatar-URL in Widget verwenden // (mxc:// URI → HTTP-URL via SDK) final httpUrl = user.avatarUrl?.getThumbnail( client, width: 56, height: 56, method: ThumbnailMethod.crop, ); ``` --- ## Verschlüsselung in DMs ```dart // E2EE-Status prüfen final isEncrypted = room.isEncrypted; // E2EE aktivieren (einmalig, nicht mehr rückgängig!) if (!room.isEncrypted) { await room.enableEncryption(); } // Geräte des Partners verifizieren final partnerDevices = await client.getUserDeviceKeys('@alice:server'); for (final device in partnerDevices?.deviceKeys.values ?? []) { print('${device.deviceId}: verified=${device.verified}'); } // Fehlende Schlüssel für Entschlüsselung anfordern await client.requestMissingSessionKeys(); // Schlechte Verschlüsselung erkennen final badEvents = timeline.events .where((e) => e.isBadEncrypted) .toList(); ``` --- ## Einladungen (DM-Invite-Ansicht) ```dart // Eingehende DM-Einladungen final invites = client.rooms.where((r) => r.membership == Membership.invite && r.isDirectChat).toList(); // Einladung anzeigen for (final invite in invites) { final senderId = invite.getState(EventTypes.RoomMember, client.userID!) ?.senderId; // Wer hat eingeladen? final sender = await client.getProfileFromUserId(senderId ?? ''); print('Einladung von ${sender.displayname}'); // Annehmen await invite.join(); // Ablehnen await invite.leave(); await invite.forget(); } ```