feat(ui): untere Leiste (Chats/Server/Du) als Option, Server-Uebersicht, Du-Tab, Feinschliff

- Einstellung 'Navigation: Seitenmenue | Untere Leiste' (Handy, Standard
  bleibt Seitenmenue): Tabs Chats/Server/Du, Server-Uebersicht als Karten,
  Du-Tab mit Profil + Einstellungen, Zurueck-Taste ueber die Tabs
- CallDock als eigenes Widget (Anrufleiste auch ueber der Tab-Leiste)
- Sprachkanaele am Handy als Kachel mit 'wer ist drin'
- Suchfeld mit Lupe, 'Kanaele durchsuchen' im Server, Filter-Chips am PC
  umbrechend, '#' nur bei Server-Kanaelen, englische Reste entfernt
- Anrufansicht merkt sich Bubbles/Kacheln, Bubbles mit leichtem Glanz
- UI-Tour: Gruppenanruf (echter zweiter LiveKit-Teilnehmer), Bubbles mit
  5/7 Personen, untere Leiste; Version 0.1.42

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Bernd Steckmeister
2026-10-10 14:22:02 +02:00
co-authored by Claude Opus 5.5
parent de24053167
commit 391ebf0ffa
19 changed files with 1139 additions and 87 deletions
+22 -7
View File
@@ -18,6 +18,8 @@ import 'package:pyramid/features/members/members_panel.dart';
import 'package:pyramid/features/rooms/rooms_panel.dart';
import 'package:pyramid/features/spaces/spaces_rail.dart';
import 'package:pyramid/layout/mobile_page_stack.dart';
import 'package:pyramid/layout/mobile_tabs.dart';
import 'package:pyramid/core/settings_prefs.dart' show bottomNavProvider;
import 'package:pyramid/widgets/spring_press.dart' show motionDuration;
import 'package:pyramid/widgets/mxc_image.dart';
import 'package:pyramid/widgets/search_modal.dart';
@@ -395,11 +397,17 @@ class _MobileShell extends ConsumerWidget {
: activeRoomId != null
? ChatView(roomId: activeRoomId!)
: null;
final bottomNav = ref.watch(bottomNavProvider);
final tab = ref.watch(mobileTabProvider);
final body = MobilePageStack(
base: const RoomsPanel(),
base: bottomNav ? const MobileTabsBase() : const RoomsPanel(),
front: front,
background: pt.bg1,
);
// Untere Leiste: Zurück führt erst über die Tabs (Server → Übersicht →
// Chats), bevor die App geschlossen wird.
final tabsAtRoot = !bottomNav ||
(tab == MobileTab.chats);
// Symbole der Statusleiste passend zum Theme (hell auf dunkel und umgekehrt).
final overlayStyle = (pt.isDark
@@ -413,7 +421,10 @@ class _MobileShell extends ConsumerWidget {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: overlayStyle,
child: PopScope(
canPop: activeRoomId == null && viewMode == ViewMode.chat && !membersOpen,
canPop: activeRoomId == null &&
viewMode == ViewMode.chat &&
!membersOpen &&
tabsAtRoot,
onPopInvokedWithResult: (didPop, _) {
if (!didPop) {
if (membersOpen) {
@@ -422,6 +433,8 @@ class _MobileShell extends ConsumerWidget {
ref.read(viewModeProvider.notifier).state = ViewMode.chat;
} else if (activeRoomId != null) {
ref.read(activeRoomIdProvider.notifier).state = null;
} else if (bottomNav) {
handleMobileTabBack(ref);
}
}
},
@@ -429,11 +442,13 @@ class _MobileShell extends ConsumerWidget {
// bg1 = Farbe der Kopfzeilen → der Streifen hinter der Statusleiste
// geht nahtlos in sie über.
backgroundColor: pt.bg1,
drawer: Drawer(
width: 80,
backgroundColor: pt.bg0,
child: const SafeArea(child: SpacesRail()),
),
drawer: bottomNav
? null
: Drawer(
width: 80,
backgroundColor: pt.bg0,
child: const SafeArea(child: SpacesRail()),
),
// SafeArea: Abstand zu Statusleiste/Kamera-Aussparung oben und zur
// Gesten-/Navigationsleiste unten (edge-to-edge, siehe main.dart).
body: SafeArea(child: Stack(