25ed765a03
6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.) als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
265 lines
6.4 KiB
Markdown
265 lines
6.4 KiB
Markdown
# 09 — Profil, Präsenz & Account-Daten
|
|
|
|
---
|
|
|
|
## Inhaltsverzeichnis
|
|
- [Eigenes Profil lesen](#eigenes-profil-lesen)
|
|
- [Profil aktualisieren](#profil-aktualisieren)
|
|
- [Profil anderer User](#profil-anderer-user)
|
|
- [Präsenz (Presence)](#präsenz-presence)
|
|
- [Account-Daten (Account Data)](#account-daten-account-data)
|
|
- [Ignorierte User](#ignorierte-user)
|
|
- [Tags / Favoriten](#tags--favoriten)
|
|
- [Account löschen / deaktivieren](#account-löschen--deaktivieren)
|
|
|
|
---
|
|
|
|
## Eigenes Profil lesen
|
|
|
|
```dart
|
|
// Aus dem Client (gecacht)
|
|
print(client.userID); // '@alice:server'
|
|
print(client.displayName); // 'Alice'
|
|
print(client.avatarUrl); // mxc://...
|
|
|
|
// Frisch vom Server
|
|
final profile = await client.fetchOwnProfile();
|
|
print(profile.displayname);
|
|
print(profile.avatarUrl);
|
|
|
|
// Avatar als HTTP-URL
|
|
final avatarHttp = client.avatarUrl?.getThumbnail(
|
|
client,
|
|
width: 128,
|
|
height: 128,
|
|
method: ThumbnailMethod.crop,
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Profil aktualisieren
|
|
|
|
```dart
|
|
// Displayname setzen
|
|
await client.setDisplayName(client.userID!, 'Neuer Name');
|
|
|
|
// Avatar hochladen und setzen
|
|
final avatarBytes = await File('avatar.png').readAsBytes();
|
|
final mxcUri = await client.uploadContent(
|
|
avatarBytes,
|
|
filename: 'avatar.png',
|
|
contentType: 'image/png',
|
|
);
|
|
await client.setAvatarUrl(client.userID!, mxcUri);
|
|
|
|
// Avatar entfernen
|
|
await client.setAvatarUrl(client.userID!, null);
|
|
```
|
|
|
|
---
|
|
|
|
## Profil anderer User
|
|
|
|
```dart
|
|
// Profil via API
|
|
final profile = await client.getProfileFromUserId('@bob:server');
|
|
print(profile.displayname);
|
|
print(profile.avatarUrl);
|
|
|
|
// Spezifisches Feld (Matrix 1.16+ API)
|
|
final displayname = await client.getProfileField(
|
|
'@bob:server',
|
|
'displayname',
|
|
);
|
|
final avatar = await client.getProfileField(
|
|
'@bob:server',
|
|
'avatar_url',
|
|
);
|
|
|
|
// User in einem Raum (schneller, gecacht)
|
|
final user = room.unsafeGetUserFromMemoryOrFallback('@bob:server');
|
|
print(user.displayName);
|
|
print(user.avatarUrl);
|
|
print(user.id);
|
|
|
|
// User-Suche
|
|
final results = await client.searchUserDirectory(
|
|
'alice',
|
|
limit: 20,
|
|
);
|
|
for (final user in results.results) {
|
|
print(user.userId);
|
|
print(user.displayName);
|
|
print(user.avatarUrl);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Präsenz (Presence)
|
|
|
|
```dart
|
|
// Eigene Präsenz setzen
|
|
await client.setPresence(
|
|
client.userID!,
|
|
PresenceType.online, // .online | .unavailable | .offline
|
|
statusMsg: 'Im Meeting', // Optionaler Status-Text
|
|
);
|
|
|
|
// Präsenz anderer User abrufen
|
|
final presence = await client.fetchCurrentPresence('@bob:server');
|
|
print(presence.presence); // PresenceType
|
|
print(presence.currentlyActive); // bool?
|
|
print(presence.statusMsg); // String?
|
|
print(presence.lastActiveAgo); // int? (Millisekunden seit letzter Aktivität)
|
|
|
|
// Präsenz aus Raum-Mitglieder
|
|
final user = room.unsafeGetUserFromMemoryOrFallback('@bob:server');
|
|
// Präsenz wird nicht direkt im User gecacht — fetchCurrentPresence nötig
|
|
|
|
// Präsenz-Anzeige im UI
|
|
String getPresenceText(PresenceType? type, int? lastActiveAgo) {
|
|
switch (type) {
|
|
case PresenceType.online: return 'Online';
|
|
case PresenceType.unavailable:
|
|
return 'Abwesend';
|
|
case PresenceType.offline:
|
|
if (lastActiveAgo != null) {
|
|
final mins = lastActiveAgo ~/ 60000;
|
|
if (mins < 60) return 'Vor $mins Min. aktiv';
|
|
final hours = mins ~/ 60;
|
|
if (hours < 24) return 'Vor $hours Std. aktiv';
|
|
return 'Vor ${hours ~/ 24} Tagen aktiv';
|
|
}
|
|
return 'Offline';
|
|
default: return '';
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Account-Daten (Account Data)
|
|
|
|
```dart
|
|
// Account-Daten sind private, servergespeicherte Einstellungen.
|
|
// Bleiben über Geräte hinweg synchron.
|
|
|
|
// Lesen
|
|
final data = client.accountData['com.example.my_setting']?.content;
|
|
|
|
// Schreiben
|
|
await client.setAccountData(
|
|
client.userID!,
|
|
'com.example.my_setting',
|
|
{'key': 'value', 'enabled': true},
|
|
);
|
|
|
|
// Wichtige Standard-Account-Daten:
|
|
// 'm.direct' → DM-Raumliste
|
|
// 'm.push_rules' → Push-Regel-Overrides
|
|
// 'm.ignored_user_list' → Ignorierte User
|
|
// 'm.identity_server' → Identity-Server-URL
|
|
// 'im.vector.setting.breadcrumbs' → FluffyChat letzte Räume
|
|
|
|
// Raumspezifische Account-Daten
|
|
await client.setRoomAccountData(
|
|
client.userID!,
|
|
'!roomId:server',
|
|
'm.fully_read',
|
|
{'event_id': eventId},
|
|
);
|
|
|
|
// Tag für Raum setzen (Favorit, Low-Priority)
|
|
await client.setRoomTag(
|
|
client.userID!,
|
|
'!roomId:server',
|
|
'm.favourite',
|
|
TagContent(order: 0.5),
|
|
);
|
|
await client.setRoomTag(client.userID!, '!roomId:server', 'm.lowpriority', TagContent());
|
|
await client.deleteRoomTag(client.userID!, '!roomId:server', 'm.favourite');
|
|
```
|
|
|
|
---
|
|
|
|
## Ignorierte User
|
|
|
|
```dart
|
|
// User ignorieren
|
|
final ignoredList = client.ignoredUsers; // List<String>
|
|
final updated = [...ignoredList, '@spammer:server'];
|
|
await client.setAccountData(
|
|
client.userID!,
|
|
'm.ignored_user_list',
|
|
{'ignored_users': Map.fromIterable(updated, value: (_) => {})},
|
|
);
|
|
|
|
// User entblockieren
|
|
final filtered = ignoredList.where((id) => id != '@spammer:server').toList();
|
|
await client.setAccountData(
|
|
client.userID!,
|
|
'm.ignored_user_list',
|
|
{'ignored_users': Map.fromIterable(filtered, value: (_) => {})},
|
|
);
|
|
|
|
// Ignorierte User abrufen
|
|
final ignored = client.ignoredUsers; // automatisch aus Account-Data
|
|
```
|
|
|
|
---
|
|
|
|
## Tags / Favoriten
|
|
|
|
```dart
|
|
// Raum als Favorit markieren
|
|
await room.addTag('m.favourite', order: 0.1);
|
|
|
|
// Raum als Low-Priority markieren
|
|
await room.addTag('m.lowpriority');
|
|
|
|
// Alle Tags eines Raums
|
|
final tags = room.tags; // Map<String, Tag>
|
|
final isFavourite = tags.containsKey('m.favourite');
|
|
final isLowPriority = tags.containsKey('m.lowpriority');
|
|
|
|
// Tag entfernen
|
|
await room.removeTag('m.favourite');
|
|
|
|
// Eigene Tags
|
|
await room.addTag('com.example.myapp.tag', order: 0.5);
|
|
|
|
// Räume nach Tag filtern
|
|
final favourites = client.rooms
|
|
.where((r) => r.tags.containsKey('m.favourite'))
|
|
.toList();
|
|
```
|
|
|
|
---
|
|
|
|
## Account löschen / deaktivieren
|
|
|
|
```dart
|
|
// Account deaktivieren (PERMANENT, nicht rückgängig!)
|
|
await client.uiaRequestBackground(
|
|
(auth) => client.deactivateAccount(
|
|
auth: auth,
|
|
eraseData: true, // true: alle Nachrichten löschen (soweit möglich)
|
|
),
|
|
);
|
|
|
|
// Passwort ändern (braucht Re-Auth via UIAA)
|
|
await client.uiaRequestBackground(
|
|
(auth) => client.changePassword(
|
|
'neuesPasswort',
|
|
auth: auth,
|
|
oldPassword: 'altesPasswort',
|
|
),
|
|
);
|
|
|
|
// E-Mail-Adresse hinzufügen (über Identity-Server)
|
|
// Dies ist ein mehrstufiger UIAA-Flow und erfordert E-Mail-Bestätigung.
|
|
// Normalerweise über den Homeserver-spezifischen Registration-Flow.
|
|
```
|