Call button

This commit is contained in:
2024-06-01 01:25:45 +08:00
parent 9a2e0756b8
commit 508cba8ed3
14 changed files with 393 additions and 24 deletions

View File

@ -129,7 +129,12 @@ class AuthProvider extends GetConnect {
client.httpClient.addAuthenticator(requestAuthenticator);
final resp = await client.get('/api/users/me');
_cacheUserProfileResponse = resp;
if (resp.statusCode != 200) {
throw Exception(resp.bodyString);
} else {
_cacheUserProfileResponse = resp;
}
return resp;
}
}

View File

@ -21,6 +21,24 @@ class ChannelProvider extends GetxController {
return resp;
}
Future<Response?> getChannelOngoingCall(String alias,
{String realm = 'global'}) async {
final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) throw Exception('unauthorized');
final client = GetConnect(maxAuthRetries: 3);
client.httpClient.baseUrl = ServiceFinder.services['messaging'];
final resp = await client.get('/api/channels/$realm/$alias/calls/ongoing');
if (resp.statusCode == 404) {
return null;
} else if (resp.statusCode != 200) {
throw Exception(resp.bodyString);
}
return resp;
}
Future<Response> listChannel({String scope = 'global'}) async {
final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) throw Exception('unauthorized');

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:solian/exts.dart';
import 'package:solian/models/call.dart';
import 'package:solian/models/channel.dart';
import 'package:solian/models/message.dart';
import 'package:solian/models/packet.dart';
@ -14,6 +15,7 @@ import 'package:solian/providers/content/channel.dart';
import 'package:solian/router.dart';
import 'package:solian/services.dart';
import 'package:solian/theme.dart';
import 'package:solian/widgets/chat/call/chat_call_action.dart';
import 'package:solian/widgets/chat/chat_message.dart';
import 'package:solian/widgets/chat/chat_message_action.dart';
import 'package:solian/widgets/chat/chat_message_input.dart';
@ -39,6 +41,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
String? _overrideAlias;
Channel? _channel;
Call? _ongoingCall;
StreamSubscription<NetworkPackage>? _subscription;
final PagingController<int, Message> _pagingController =
@ -72,6 +75,26 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
setState(() => _isBusy = false);
}
getOngoingCall() async {
final ChannelProvider provider = Get.find();
setState(() => _isBusy = true);
try {
final resp = await provider.getChannelOngoingCall(
_overrideAlias ?? widget.alias,
realm: widget.realm,
);
if (resp != null) {
setState(() => _ongoingCall = Call.fromJson(resp.body));
}
} catch (e) {
context.showErrorDialog(e);
}
setState(() => _isBusy = false);
}
Future<void> getMessages(int pageKey) async {
final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) return;
@ -135,6 +158,13 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
}
}
break;
case 'calls.new':
final payload = Call.fromJson(event.payload!);
_ongoingCall = payload;
break;
case 'calls.end':
_ongoingCall = null;
break;
}
setState(() {});
});
@ -150,7 +180,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
Message? _messageToReplying;
Message? _messageToEditing;
Widget chatHistoryBuilder(context, item, index) {
Widget buildHistory(context, item, index) {
bool isMerged = false, hasMerged = false;
if (index > 0) {
hasMerged = checkMessageMergeable(
@ -203,6 +233,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
listenMessages();
_pagingController.addPageRequestListener(getMessages);
});
getOngoingCall();
}
@override
@ -231,6 +262,14 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
title: Text(title),
centerTitle: false,
actions: [
Builder(builder: (context) {
if (_isBusy) return const SizedBox();
return ChatCallButton(
realm: _channel!.realm,
channel: _channel!,
ongoingCall: _ongoingCall,
);
}),
IconButton(
icon: const Icon(Icons.more_vert),
onPressed: () {
@ -265,7 +304,7 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
clipBehavior: Clip.none,
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<Message>(
itemBuilder: chatHistoryBuilder,
itemBuilder: buildHistory,
noItemsFoundIndicatorBuilder: (_) => Container(),
),
).paddingOnly(bottom: 64),
@ -295,6 +334,20 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
},
),
),
if (_ongoingCall != null)
MaterialBanner(
padding: const EdgeInsets.only(left: 10, right: 20),
leading: const Icon(Icons.call_received),
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
dividerColor: const Color.fromARGB(1, 0, 0, 0),
content: Text('callOngoing'.tr),
actions: [
TextButton(
child: Text('callJoin'.tr),
onPressed: () {},
),
],
),
],
),
);

View File

@ -153,6 +153,8 @@ class SolianMessages extends Translations {
'messageDeletionConfirm': 'Confirm message deletion',
'messageDeletionConfirmCaption':
'Are your sure to delete message @id? This action cannot be undone!',
'callOngoing': 'A call is ongoing...',
'callJoin': 'Join',
},
'zh_CN': {
'hide': '隐藏',
@ -294,6 +296,8 @@ class SolianMessages extends Translations {
'messageActionList': '消息的操作',
'messageDeletionConfirm': '确认删除消息',
'messageDeletionConfirmCaption': '你确定要删除消息 @id 吗?该操作不可撤销。',
'callOngoing': '一则通话正在进行中…',
'callJoin': '加入',
}
};
}

View File

@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:solian/exts.dart';
import 'package:solian/models/call.dart';
import 'package:solian/models/channel.dart';
import 'package:solian/models/realm.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/services.dart';
class ChatCallButton extends StatefulWidget {
final Realm? realm;
final Channel channel;
final Call? ongoingCall;
final Function? onStarted;
final Function? onEnded;
const ChatCallButton({
super.key,
required this.realm,
required this.channel,
required this.ongoingCall,
this.onStarted,
this.onEnded,
});
@override
State<ChatCallButton> createState() => _ChatCallButtonState();
}
class _ChatCallButtonState extends State<ChatCallButton> {
bool _isBusy = false;
Future<void> makeCall() async {
final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) return;
final client = GetConnect(maxAuthRetries: 3);
client.httpClient.baseUrl = ServiceFinder.services['messaging'];
client.httpClient.addAuthenticator(auth.requestAuthenticator);
setState(() => _isBusy = true);
final scope = (widget.realm?.alias.isNotEmpty ?? false)
? widget.realm?.alias
: 'global';
final resp = await client.post(
'/api/channels/$scope/${widget.channel.alias}/calls',
{},
);
if (resp.statusCode == 200) {
if (widget.onStarted != null) widget.onStarted!();
} else {
context.showErrorDialog(resp.bodyString);
}
setState(() => _isBusy = false);
}
Future<void> endsCall() async {
final AuthProvider auth = Get.find();
if (!await auth.isAuthorized) return;
final client = GetConnect(maxAuthRetries: 3);
client.httpClient.baseUrl = ServiceFinder.services['messaging'];
client.httpClient.addAuthenticator(auth.requestAuthenticator);
setState(() => _isBusy = true);
final scope = (widget.realm?.alias.isNotEmpty ?? false)
? widget.realm?.alias
: 'global';
final resp = await client
.delete('/api/channels/${scope}/${widget.channel.alias}/calls/ongoing');
if (resp.statusCode == 200) {
if (widget.onEnded != null) widget.onEnded!();
} else {
context.showErrorDialog(resp.bodyString);
}
setState(() => _isBusy = false);
}
@override
Widget build(BuildContext context) {
return IconButton(
onPressed: _isBusy
? null
: widget.ongoingCall == null
? makeCall
: endsCall,
icon: widget.ongoingCall == null
? const Icon(Icons.call)
: const Icon(Icons.call_end),
);
}
}