Files
chanora/apps/chanora_flutter/lib/widgets/chat_panel.dart
T
Edison Jwa 5e8b7915db feat(ui): adaptive 3-panel layout, chat panel switching, audio metering fix
- Add responsive breakpoints (compact <600, medium 600-1023, expanded >=1024)
- Add ViewportInfo InheritedWidget for layout-aware descendants
- Add inline ChatPanel (380dp right column) for expanded desktop layout
- Add channel right-click context menu with Chat option for in-place switching
- Add per-target draft persistence via restoredDraft/onDraftChanged callbacks
- Fix header chat button to switch to current voice channel when panel open
- Fix close = dismiss (preserves last target and draft for reopen)
- Add unread dot indicator on channel tiles when chat is closed
- Fix audio regression: decimate dBFS computation to every 3rd callback (~31 Hz)
  to avoid buffer underruns on macOS CoreAudio real-time thread
- Add tools/build-macos.sh release build script (7-step process)
- Add chat panel switching implementation plan and 3-panel design spec

Tests: 183 passed, 2 skipped. Flutter analyze clean.
2026-06-07 23:12:07 +09:00

87 lines
2.5 KiB
Dart

// SPDX-License-Identifier: Apache-2.0
import 'package:flutter/material.dart';
import '../design/breakpoints.dart';
import '../services/snapshot_state_mapper.dart';
import '../services/ts3_server_link.dart';
import '../src/rust/api.dart' as rust;
import 'chat_views.dart';
/// Fixed-width inline chat panel for expanded desktop layouts.
class ChatPanel extends StatelessWidget {
/// Construct an inline chat panel.
const ChatPanel({
super.key,
required this.messages,
required this.snapshot,
required this.target,
required this.clientName,
required this.onClose,
this.restoredDraft,
this.onDraftChanged,
this.onTs3ServerLink,
});
/// Backing chat messages shared with the chat route.
final List<ChatEntry> messages;
/// Latest TeamSpeak snapshot.
final rust.BridgeSnapshot snapshot;
/// Chat target shown in the panel.
final rust.BridgeMessageTarget target;
/// Client display name for direct-message and poke targets.
final String clientName;
/// Handle TeamSpeak server links embedded in chat messages.
final Ts3ServerLinkHandler? onTs3ServerLink;
/// Called when the user closes the inline panel.
final VoidCallback onClose;
/// External draft text to restore in the chat detail view.
final String? restoredDraft;
/// Called when the draft text changes.
final ValueChanged<String>? onDraftChanged;
@override
Widget build(BuildContext context) {
final currentChannelId = ownClientSnapshotState(snapshot)?.channelId;
final channelName = snapshotChannelName(snapshot, currentChannelId);
return SizedBox(
width: ChanoraBreakpoints.chatPanelWidth,
child: DecoratedBox(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border(
left: BorderSide(
color: Theme.of(context).colorScheme.outlineVariant,
),
),
),
child: ChatDetailView(
messages: messages,
snapshot: snapshot,
target: target,
clientName: clientName,
currentChannelId: currentChannelId,
channelName: channelName,
onTs3ServerLink: onTs3ServerLink,
restoredDraft: restoredDraft,
onDraftChanged: onDraftChanged,
messageMaxWidth: 500,
headerTrailing: IconButton(
tooltip: 'Close chat',
icon: const Icon(Icons.close),
onPressed: onClose,
),
),
),
);
}
}