Call in same screen on large screen

This commit is contained in:
2024-08-20 01:10:15 +08:00
parent c82c48dfec
commit 253cd1ecbd
10 changed files with 265 additions and 189 deletions

View File

@ -12,16 +12,15 @@ import 'package:solian/widgets/chat/call/call_participant.dart';
import 'package:livekit_client/livekit_client.dart' as livekit;
class CallScreen extends StatefulWidget {
const CallScreen({super.key});
final bool hideAppBar;
const CallScreen({super.key, this.hideAppBar = false});
@override
State<CallScreen> createState() => _CallScreenState();
}
class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
Timer? _timer;
String _currentDuration = '00:00:00';
int _layoutMode = 0;
bool _showControls = true;
@ -37,26 +36,6 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
curve: Curves.fastOutSlowIn,
);
String _parseDuration() {
final ChatCallProvider provider = Get.find();
if (provider.current.value == null) return '00:00:00';
Duration duration =
DateTime.now().difference(provider.current.value!.createdAt);
String twoDigits(int n) => n.toString().padLeft(2, '0');
String formattedTime = '${twoDigits(duration.inHours)}:'
'${twoDigits(duration.inMinutes.remainder(60))}:'
'${twoDigits(duration.inSeconds.remainder(60))}';
return formattedTime;
}
void _updateDuration() {
setState(() {
_currentDuration = _parseDuration();
});
}
void _switchLayout() {
if (_layoutMode < 1) {
setState(() => _layoutMode++);
@ -191,15 +170,15 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
@override
void initState() {
Get.find<ChatCallProvider>().setupRoom();
super.initState();
_updateDuration();
_planAutoHideControls();
_timer = Timer.periodic(
const Duration(seconds: 1),
(_) => _updateDuration(),
);
Future.delayed(Duration.zero, () {
Get.find<ChatCallProvider>()
..setupRoom()
..enableDurationUpdater();
_planAutoHideControls();
});
}
@override
@ -210,30 +189,34 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
final ChatCallProvider provider = Get.find();
final ChatCallProvider ctrl = Get.find();
return Material(
color: Theme.of(context).colorScheme.surface,
child: Scaffold(
appBar: AppBar(
leading: AppBarLeadingButton.adaptive(context),
centerTitle: true,
toolbarHeight: SolianTheme.toolbarHeight(context),
title: RichText(
textAlign: TextAlign.center,
text: TextSpan(children: [
TextSpan(
text: 'call'.tr,
style: Theme.of(context).textTheme.titleLarge,
appBar: widget.hideAppBar
? null
: AppBar(
leading: AppBarLeadingButton.adaptive(context),
centerTitle: true,
toolbarHeight: SolianTheme.toolbarHeight(context),
title: Obx(
() => RichText(
textAlign: TextAlign.center,
text: TextSpan(children: [
TextSpan(
text: 'call'.tr,
style: Theme.of(context).textTheme.titleLarge,
),
const TextSpan(text: '\n'),
TextSpan(
text: ctrl.lastDuration.value,
style: Theme.of(context).textTheme.bodySmall,
),
]),
),
),
),
const TextSpan(text: '\n'),
TextSpan(
text: _currentDuration,
style: Theme.of(context).textTheme.bodySmall,
),
]),
),
),
body: SafeArea(
child: GestureDetector(
behavior: HitTestBehavior.translucent,
@ -259,13 +242,20 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(call.room.serverRegion ?? 'unknown'),
const SizedBox(width: 6),
Text(call.room.serverVersion ?? 'unknown')
],
),
Obx(() {
return Row(
children: [
Text(
call.channel.value!.name,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 6),
Text(call.lastDuration.value)
],
);
}),
Row(
children: [
Text(
@ -332,7 +322,6 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
Expanded(
child: Material(
color: Theme.of(context).colorScheme.surfaceContainerLow,
elevation: 2,
child: Builder(
builder: (context) {
switch (_layoutMode) {
@ -345,15 +334,15 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
),
),
),
if (provider.room.localParticipant != null)
if (ctrl.room.localParticipant != null)
SizeTransition(
sizeFactor: _controlsAnimation,
axis: Axis.vertical,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: ControlsWidget(
provider.room,
provider.room.localParticipant!,
ctrl.room,
ctrl.room.localParticipant!,
),
),
),
@ -370,17 +359,13 @@ class _CallScreenState extends State<CallScreen> with TickerProviderStateMixin {
@override
void deactivate() {
_timer?.cancel();
_timer = null;
Get.find<ChatCallProvider>().disableDurationUpdater();
super.deactivate();
}
@override
void activate() {
_timer ??= Timer.periodic(
const Duration(seconds: 1),
(_) => _updateDuration(),
);
Get.find<ChatCallProvider>().enableDurationUpdater();
super.activate();
}
}

View File

@ -11,9 +11,11 @@ import 'package:solian/models/channel.dart';
import 'package:solian/models/event.dart';
import 'package:solian/models/packet.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/providers/call.dart';
import 'package:solian/providers/content/channel.dart';
import 'package:solian/providers/websocket.dart';
import 'package:solian/router.dart';
import 'package:solian/screens/channel/call/call.dart';
import 'package:solian/screens/channel/channel_detail.dart';
import 'package:solian/theme.dart';
import 'package:solian/widgets/app_bar_leading.dart';
@ -39,7 +41,7 @@ class ChannelChatScreen extends StatefulWidget {
}
class _ChannelChatScreenState extends State<ChannelChatScreen>
with WidgetsBindingObserver {
with WidgetsBindingObserver, TickerProviderStateMixin {
DateTime? _isOutOfSyncSince;
bool _isBusy = false;
@ -238,77 +240,104 @@ class _ChannelChatScreenState extends State<ChannelChatScreen>
);
}
return Column(
return Row(
children: [
if (_ongoingCall != null)
ChannelCallIndicator(
channel: _channel!,
ongoingCall: _ongoingCall!,
),
Expanded(
child: ChatEventList(
scope: widget.realm,
channel: _channel!,
chatController: _chatController,
onEdit: (item) {
setState(() => _messageToEditing = item);
},
onReply: (item) {
setState(() => _messageToReplying = item);
},
),
),
if (_isOutOfSyncSince != null)
ListTile(
contentPadding: const EdgeInsets.only(left: 16, right: 8),
tileColor: Theme.of(context).colorScheme.surfaceContainerLow,
leading: const Icon(Icons.history_toggle_off),
title: Text('messageOutOfSync'.tr),
subtitle: Text('messageOutOfSyncCaption'.tr),
trailing: IconButton(
icon: const Icon(Icons.close),
onPressed: () {
setState(() => _isOutOfSyncSince = null);
},
),
onTap: _isBusy
? null
: () {
_keepUpdateWithServer();
child: Column(
children: [
if (_ongoingCall != null)
ChannelCallIndicator(
channel: _channel!,
ongoingCall: _ongoingCall!,
onJoin: () {
if (!SolianTheme.isLargeScreen(context)) {
final ChatCallProvider call = Get.find();
call.gotoScreen(context);
}
},
),
Obx(() {
if (_chatController.isLoading.isTrue) {
return const LinearProgressIndicator().animate().slideY();
} else {
return const SizedBox();
}
}),
ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50),
child: SafeArea(
child: ChatMessageInput(
edit: _messageToEditing,
reply: _messageToReplying,
realm: widget.realm,
placeholder: placeholder,
channel: _channel!,
onSent: (Event item) {
setState(() {
_chatController.addPendingEvent(item);
});
},
onReset: () {
setState(() {
_messageToReplying = null;
_messageToEditing = null;
});
},
),
Expanded(
child: ChatEventList(
scope: widget.realm,
channel: _channel!,
chatController: _chatController,
onEdit: (item) {
setState(() => _messageToEditing = item);
},
onReply: (item) {
setState(() => _messageToReplying = item);
},
),
),
),
if (_isOutOfSyncSince != null)
ListTile(
contentPadding: const EdgeInsets.only(left: 16, right: 8),
tileColor:
Theme.of(context).colorScheme.surfaceContainerLow,
leading: const Icon(Icons.history_toggle_off),
title: Text('messageOutOfSync'.tr),
subtitle: Text('messageOutOfSyncCaption'.tr),
trailing: IconButton(
icon: const Icon(Icons.close),
onPressed: () {
setState(() => _isOutOfSyncSince = null);
},
),
onTap: _isBusy
? null
: () {
_keepUpdateWithServer();
},
),
Obx(() {
if (_chatController.isLoading.isTrue) {
return const LinearProgressIndicator().animate().slideY();
} else {
return const SizedBox();
}
}),
ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50),
child: SafeArea(
child: ChatMessageInput(
edit: _messageToEditing,
reply: _messageToReplying,
realm: widget.realm,
placeholder: placeholder,
channel: _channel!,
onSent: (Event item) {
setState(() {
_chatController.addPendingEvent(item);
});
},
onReset: () {
setState(() {
_messageToReplying = null;
_messageToEditing = null;
});
},
),
),
),
),
],
),
),
Obx(() {
final ChatCallProvider call = Get.find();
if (call.isMounted.value && SolianTheme.isLargeScreen(context)) {
return const Expanded(
child: Row(children: [
VerticalDivider(width: 0.3, thickness: 0.3),
Expanded(
child: CallScreen(hideAppBar: true),
),
]),
);
}
return const SizedBox();
}),
],
);
}),