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:
co-authored by
Claude Opus 5.5
parent
de24053167
commit
391ebf0ffa
@@ -70,13 +70,13 @@ class BubbleSim {
|
||||
final active = ids.where((id) => bodies.containsKey(id)).toList();
|
||||
double base;
|
||||
if (active.length <= 1) {
|
||||
base = shortSide * 0.30;
|
||||
base = shortSide * 0.27;
|
||||
} else {
|
||||
// Kreise sollen zusammen gut 40 % der Fläche bedecken.
|
||||
// Kreise sollen zusammen gut ein Drittel der Fläche bedecken.
|
||||
final sumW2 = active.fold<double>(0, (s, id) => s + weight(id) * weight(id));
|
||||
base = math.sqrt(0.42 * w * h / (math.pi * sumW2));
|
||||
base = math.sqrt(0.36 * w * h / (math.pi * sumW2));
|
||||
}
|
||||
final maxR = shortSide * (active.length <= 2 ? 0.30 : 0.36);
|
||||
final maxR = shortSide * (active.length <= 2 ? 0.27 : 0.36);
|
||||
for (final b in bodies.values) {
|
||||
if (b.leaving) {
|
||||
b.targetR = 0;
|
||||
|
||||
@@ -164,6 +164,17 @@ class _Bubble extends StatelessWidget {
|
||||
isScreenShare: data.isScreenShare,
|
||||
child: data.coverVideoWidget!,
|
||||
),
|
||||
const IgnorePointer(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
center: Alignment(-0.45, -0.55),
|
||||
radius: 0.95,
|
||||
colors: [Color(0x2EFFFFFF), Color(0x00FFFFFF)],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showName)
|
||||
Align(
|
||||
alignment: const Alignment(0, 0.62),
|
||||
@@ -303,3 +314,49 @@ class _BubbleRingPainter extends CustomPainter {
|
||||
@override
|
||||
bool shouldRepaint(_BubbleRingPainter old) => true;
|
||||
}
|
||||
|
||||
/// Vorführ-Ansicht der Bubbles mit erfundenen Teilnehmern – nur für
|
||||
/// Screenshots/Tests (UI-Tour), damit man die Ansicht mit mehreren Personen
|
||||
/// sieht, ohne dass so viele Testkonten anrufen müssen.
|
||||
@visibleForTesting
|
||||
class CallBubblesDemo extends StatelessWidget {
|
||||
final int count;
|
||||
const CallBubblesDemo({super.key, this.count = 5});
|
||||
|
||||
static const _names = ['Du', 'Anna', 'Ben', 'Clara', 'David', 'Ela', 'Finn'];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final pt = PyramidTheme.of(context);
|
||||
final participants = [
|
||||
for (var i = 0; i < count.clamp(1, _names.length); i++)
|
||||
_ParticipantData(
|
||||
id: 'demo$i',
|
||||
name: _names[i],
|
||||
isLocal: i == 0,
|
||||
isSpeaking: i == 2,
|
||||
isMuted: i == 3,
|
||||
avatarColor: VoiceChannelView._avatarColors[i % VoiceChannelView._avatarColors.length],
|
||||
),
|
||||
];
|
||||
return _UnifiedCallView(
|
||||
participants: participants,
|
||||
client: null,
|
||||
startedAt: DateTime.now().subtract(const Duration(minutes: 3, seconds: 12)),
|
||||
isLiveKit: true,
|
||||
displayName: 'Familie',
|
||||
isConnecting: false,
|
||||
onHangUp: () {},
|
||||
onMinimize: () {},
|
||||
onToggleMic: () {},
|
||||
onToggleDeafen: () {},
|
||||
onToggleCam: () {},
|
||||
onToggleScreen: () {},
|
||||
isMuted: false,
|
||||
isDeafened: false,
|
||||
isCameraOff: true,
|
||||
isScreenSharing: false,
|
||||
pt: pt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,10 @@ class _UnifiedCallView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _UnifiedCallViewState extends State<_UnifiedCallView> {
|
||||
_CallLayout _mode = _CallLayout.bubbles;
|
||||
/// Zuletzt gewählte Ansicht (Bubbles/Kacheln) – bleibt beim Minimieren
|
||||
/// und für den nächsten Anruf erhalten.
|
||||
static _CallLayout _preferred = _CallLayout.bubbles;
|
||||
_CallLayout _mode = _preferred;
|
||||
String? _heroId;
|
||||
bool _stripCollapsed = false;
|
||||
Set<String> _knownScreens = {};
|
||||
@@ -86,7 +89,7 @@ class _UnifiedCallViewState extends State<_UnifiedCallView> {
|
||||
_knownScreens = screens;
|
||||
if (_mode == _CallLayout.hero &&
|
||||
!widget.participants.any((p) => p.id == _heroId && p.hasVideo)) {
|
||||
_mode = _CallLayout.bubbles;
|
||||
_mode = _preferred;
|
||||
_heroId = null;
|
||||
}
|
||||
}
|
||||
@@ -154,6 +157,7 @@ class _UnifiedCallViewState extends State<_UnifiedCallView> {
|
||||
_mode = _mode == _CallLayout.bubbles
|
||||
? _CallLayout.grid
|
||||
: _CallLayout.bubbles;
|
||||
_preferred = _mode;
|
||||
_heroId = null;
|
||||
}),
|
||||
onMinimize: widget.onMinimize,
|
||||
|
||||
Reference in New Issue
Block a user