wip: Sicherungs-Commit aller Änderungen seit April + Arbeitsstruktur (CLAUDE.md, ROADMAP.md, PROGRESS.md, Autopilot)
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
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
# 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.
|
||||
```
|
||||
Reference in New Issue
Block a user