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:
Bernd Steckmeister
2026-07-03 05:47:18 +02:00
parent d706ace35f
commit 25ed765a03
195 changed files with 47784 additions and 1236 deletions
+83
View File
@@ -0,0 +1,83 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class E2eeDiagEntry {
final DateTime timestamp;
final String roomId;
final String eventId;
final String sessionId;
final String error;
const E2eeDiagEntry({
required this.timestamp,
required this.roomId,
required this.eventId,
required this.sessionId,
required this.error,
});
Map<String, dynamic> toJson() => {
'ts': timestamp.toIso8601String(),
'room': roomId,
'event': eventId,
'session': sessionId,
'error': error,
};
}
class E2eeDiagnosticsNotifier extends Notifier<List<E2eeDiagEntry>> {
static const int _maxEntries = 500;
@override
List<E2eeDiagEntry> build() => [];
void add(E2eeDiagEntry entry) {
final next = [...state, entry];
state = next.length > _maxEntries ? next.sublist(next.length - _maxEntries) : next;
}
void clear() => state = [];
Future<String> upload({required String serverUrl, required String token}) async {
if (state.isEmpty) return 'Keine Diagnosedaten vorhanden.';
final payload = utf8.encode(jsonEncode({
'client': 'Pyramid',
'uploaded_at': DateTime.now().toIso8601String(),
'entry_count': state.length,
'entries': state.map((e) => e.toJson()).toList(),
}));
final client = HttpClient();
try {
final uri = Uri.parse('$serverUrl/api/e2ee-diagnostics');
final request = await client.postUrl(uri);
request.headers.set('Content-Type', 'application/json; charset=utf-8');
request.headers.set('Authorization', 'Bearer $token');
request.headers.contentLength = payload.length;
request.add(payload);
final response = await request.close();
await response.drain<void>();
if (response.statusCode == 200) {
final count = state.length;
clear();
return '$count Einträge hochgeladen und lokal gelöscht.';
}
return 'Server-Fehler: HTTP ${response.statusCode}';
} on SocketException catch (e) {
return 'Netzwerkfehler: ${e.message}';
} catch (e) {
return 'Fehler: $e';
} finally {
client.close();
}
}
}
final e2eeDiagnosticsProvider =
NotifierProvider<E2eeDiagnosticsNotifier, List<E2eeDiagEntry>>(
E2eeDiagnosticsNotifier.new,
);