feat: cm interface
This commit is contained in:
parent
5e21a81a5c
commit
2943d2d0cc
@ -521,6 +521,38 @@ class _CmControlPanel extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
|
Offstage(
|
||||||
|
offstage: !client.inVoiceCall,
|
||||||
|
child: buildButton(context,
|
||||||
|
color: Colors.purple,
|
||||||
|
onClick: () => closeVoiceCall(),
|
||||||
|
icon: Icon(Icons.reply, color: Colors.white),
|
||||||
|
text: "Stop voice call",
|
||||||
|
textColor: Colors.white),
|
||||||
|
),
|
||||||
|
Offstage(
|
||||||
|
offstage: !client.incomingVoiceCall,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: buildButton(context,
|
||||||
|
color: MyTheme.accent,
|
||||||
|
onClick: () => handleVoiceCall(true),
|
||||||
|
icon: Icon(Icons.phone, color: Colors.white),
|
||||||
|
text: "Accept",
|
||||||
|
textColor: Colors.white),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: buildButton(context,
|
||||||
|
color: Colors.red,
|
||||||
|
onClick: () => handleVoiceCall(false),
|
||||||
|
icon: Icon(Icons.phone, color: Colors.white),
|
||||||
|
text: "Deny",
|
||||||
|
textColor: Colors.white),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
Offstage(
|
Offstage(
|
||||||
offstage: !client.fromSwitch,
|
offstage: !client.fromSwitch,
|
||||||
child: buildButton(context,
|
child: buildButton(context,
|
||||||
@ -626,7 +658,7 @@ class _CmControlPanel extends StatelessWidget {
|
|||||||
.marginSymmetric(horizontal: showElevation ? 0 : bigMargin);
|
.marginSymmetric(horizontal: showElevation ? 0 : bigMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildButton(
|
Widget buildButton(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required Color? color,
|
required Color? color,
|
||||||
required Function() onClick,
|
required Function() onClick,
|
||||||
@ -692,6 +724,14 @@ class _CmControlPanel extends StatelessWidget {
|
|||||||
void handleSwitchBack(BuildContext context) {
|
void handleSwitchBack(BuildContext context) {
|
||||||
bind.cmSwitchBack(connId: client.id);
|
bind.cmSwitchBack(connId: client.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleVoiceCall(bool accept) {
|
||||||
|
bind.cmHandleIncomingVoiceCall(id: client.id, accept: accept);
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeVoiceCall() {
|
||||||
|
bind.cmCloseVoiceCall(id: client.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkClickTime(int id, Function() callback) async {
|
void checkClickTime(int id, Function() callback) async {
|
||||||
|
@ -713,19 +713,27 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
() {
|
() {
|
||||||
switch (widget.ffi.chatModel.voiceCallStatus.value) {
|
switch (widget.ffi.chatModel.voiceCallStatus.value) {
|
||||||
case VoiceCallStatus.waitingForResponse:
|
case VoiceCallStatus.waitingForResponse:
|
||||||
return SvgPicture.asset(
|
return IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
widget.ffi.chatModel.closeVoiceCall(widget.id);
|
||||||
|
},
|
||||||
|
icon: SvgPicture.asset(
|
||||||
"assets/voice_call_waiting.svg",
|
"assets/voice_call_waiting.svg",
|
||||||
color: _MenubarTheme.commonColor,
|
color: Colors.red,
|
||||||
width: Theme.of(context).iconTheme.size ?? 24.0,
|
width: Theme.of(context).iconTheme.size ?? 24.0,
|
||||||
height: Theme.of(context).iconTheme.size ?? 24.0,
|
height: Theme.of(context).iconTheme.size ?? 24.0,
|
||||||
);
|
));
|
||||||
break;
|
|
||||||
case VoiceCallStatus.connected:
|
case VoiceCallStatus.connected:
|
||||||
return SvgPicture.asset(
|
return IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
widget.ffi.chatModel.closeVoiceCall(widget.id);
|
||||||
|
},
|
||||||
|
icon: SvgPicture.asset(
|
||||||
"assets/voice_call.svg",
|
"assets/voice_call.svg",
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
width: Theme.of(context).iconTheme.size ?? 24.0,
|
width: Theme.of(context).iconTheme.size ?? 24.0,
|
||||||
height: Theme.of(context).iconTheme.size ?? 24.0,
|
height: Theme.of(context).iconTheme.size ?? 24.0,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return const Offstage();
|
return const Offstage();
|
||||||
|
@ -316,6 +316,10 @@ class ChatModel with ChangeNotifier {
|
|||||||
_voiceCallStatus.value = VoiceCallStatus.incoming;
|
_voiceCallStatus.value = VoiceCallStatus.incoming;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void closeVoiceCall(String id) {
|
||||||
|
bind.sessionCloseVoiceCall(id: id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum VoiceCallStatus {
|
enum VoiceCallStatus {
|
||||||
|
@ -216,6 +216,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
} else if (name == "on_voice_call_incoming") {
|
} else if (name == "on_voice_call_incoming") {
|
||||||
// Voice call is requested by the peer.
|
// Voice call is requested by the peer.
|
||||||
parent.target?.chatModel.onVoiceCallIncoming();
|
parent.target?.chatModel.onVoiceCallIncoming();
|
||||||
|
} else if (name == "update_voice_call_state") {
|
||||||
|
parent.target?.serverModel.updateVoiceCallState(evt);
|
||||||
} else {
|
} else {
|
||||||
debugPrint("Unknown event name: $name");
|
debugPrint("Unknown event name: $name");
|
||||||
}
|
}
|
||||||
|
@ -579,6 +579,20 @@ class ServerModel with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateVoiceCallState(Map<String, dynamic> evt) {
|
||||||
|
try {
|
||||||
|
final client = Client.fromJson(jsonDecode(evt["client"]));
|
||||||
|
final index = _clients.indexWhere((element) => element.id == client.id);
|
||||||
|
if (index != -1) {
|
||||||
|
_clients[index].inVoiceCall = evt['in_voice_call'];
|
||||||
|
_clients[index].incomingVoiceCall = evt['incoming_voice_call'];
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("updateVoiceCallState failed: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ClientType {
|
enum ClientType {
|
||||||
@ -602,6 +616,8 @@ class Client {
|
|||||||
bool recording = false;
|
bool recording = false;
|
||||||
bool disconnected = false;
|
bool disconnected = false;
|
||||||
bool fromSwitch = false;
|
bool fromSwitch = false;
|
||||||
|
bool inVoiceCall = false;
|
||||||
|
bool incomingVoiceCall = false;
|
||||||
|
|
||||||
RxBool hasUnreadChatMessage = false.obs;
|
RxBool hasUnreadChatMessage = false.obs;
|
||||||
|
|
||||||
@ -623,6 +639,8 @@ class Client {
|
|||||||
recording = json['recording'];
|
recording = json['recording'];
|
||||||
disconnected = json['disconnected'];
|
disconnected = json['disconnected'];
|
||||||
fromSwitch = json['from_switch'];
|
fromSwitch = json['from_switch'];
|
||||||
|
inVoiceCall = json['in_voice_call'];
|
||||||
|
incomingVoiceCall = json['incoming_voice_call'];
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
@ -754,6 +754,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
Data::CloseVoiceCall => {
|
Data::CloseVoiceCall => {
|
||||||
self.stop_voice_call();
|
self.stop_voice_call();
|
||||||
let msg = new_voice_call_request(false);
|
let msg = new_voice_call_request(false);
|
||||||
|
self.handler.on_voice_call_closed("Closed manually by the peer");
|
||||||
allow_err!(peer.send(&msg).await);
|
allow_err!(peer.send(&msg).await);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -538,16 +538,9 @@ pub mod connection_manager {
|
|||||||
self.push_event("show_elevation", vec![("show", &show.to_string())]);
|
self.push_event("show_elevation", vec![("show", &show.to_string())]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn voice_call_started(&self, id: i32) {
|
fn update_voice_call_state(&self, client: &crate::ui_cm_interface::Client) {
|
||||||
self.push_event("voice_call_started", vec![("show", &id.to_string())]);
|
let client_json = serde_json::to_string(&client).unwrap_or("".into());
|
||||||
}
|
self.push_event("update_voice_call_state", vec![("client", &client_json)]);
|
||||||
|
|
||||||
fn voice_call_incoming(&self, id: i32) {
|
|
||||||
self.push_event("voice_call_incoming", vec![("id", &id.to_string())]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn voice_call_closed(&self, id: i32, reason: &str) {
|
|
||||||
self.push_event("voice_call_closed", vec![("id", &id.to_string()), ("reason", &reason.to_string())]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -838,6 +838,14 @@ pub fn session_close_voice_call(id: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cm_handle_incoming_voice_call(id: i32, accept: bool) {
|
||||||
|
crate::ui_cm_interface::handle_incoming_voice_call(id, accept);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cm_close_voice_call(id: i32) {
|
||||||
|
crate::ui_cm_interface::close_voice_call(id);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main_get_last_remote_id() -> String {
|
pub fn main_get_last_remote_id() -> String {
|
||||||
LocalConfig::get_remote_id()
|
LocalConfig::get_remote_id()
|
||||||
}
|
}
|
||||||
|
@ -1636,7 +1636,7 @@ impl Connection {
|
|||||||
if let Some(sound_input) = std::mem::replace(&mut self.audio_input_device_before_voice_call, None) {
|
if let Some(sound_input) = std::mem::replace(&mut self.audio_input_device_before_voice_call, None) {
|
||||||
set_sound_input(sound_input);
|
set_sound_input(sound_input);
|
||||||
// Notify the connection manager.
|
// Notify the connection manager.
|
||||||
self.send_to_cm(Data::CloseVoiceCall("Closed manually by the peer".to_owned()));
|
self.send_to_cm(Data::CloseVoiceCall("".to_owned()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
19
src/ui/cm.rs
19
src/ui/cm.rs
@ -56,16 +56,15 @@ impl InvokeUiCM for SciterHandler {
|
|||||||
self.call("showElevation", &make_args!(show));
|
self.call("showElevation", &make_args!(show));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn voice_call_started(&self, id: i32) {
|
fn update_voice_call_state(&self, client: &crate::ui_cm_interface::Client) {
|
||||||
self.call("voice_call_started", &make_args!(id));
|
self.call(
|
||||||
}
|
"updateVoiceCallState",
|
||||||
|
&make_args!(
|
||||||
fn voice_call_incoming(&self, id: i32) {
|
client.id,
|
||||||
self.call("voice_call_incoming", &make_args!(id));
|
client.in_voice_call,
|
||||||
}
|
client.incoming_voice_call
|
||||||
|
),
|
||||||
fn voice_call_closed(&self, id: i32, reason: &str) {
|
);
|
||||||
self.call("voice_call_incoming", &make_args!(id, reason));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +49,8 @@ pub struct Client {
|
|||||||
pub restart: bool,
|
pub restart: bool,
|
||||||
pub recording: bool,
|
pub recording: bool,
|
||||||
pub from_switch: bool,
|
pub from_switch: bool,
|
||||||
|
pub in_voice_call: bool,
|
||||||
|
pub incoming_voice_call: bool,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
tx: UnboundedSender<Data>,
|
tx: UnboundedSender<Data>,
|
||||||
}
|
}
|
||||||
@ -89,11 +91,7 @@ pub trait InvokeUiCM: Send + Clone + 'static + Sized {
|
|||||||
|
|
||||||
fn show_elevation(&self, show: bool);
|
fn show_elevation(&self, show: bool);
|
||||||
|
|
||||||
fn voice_call_started(&self, id: i32);
|
fn update_voice_call_state(&self, client: &Client);
|
||||||
|
|
||||||
fn voice_call_incoming(&self, id: i32);
|
|
||||||
|
|
||||||
fn voice_call_closed(&self, id: i32, reason: &str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: InvokeUiCM> Deref for ConnectionManager<T> {
|
impl<T: InvokeUiCM> Deref for ConnectionManager<T> {
|
||||||
@ -144,6 +142,8 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
|||||||
recording,
|
recording,
|
||||||
from_switch,
|
from_switch,
|
||||||
tx,
|
tx,
|
||||||
|
in_voice_call: false,
|
||||||
|
incoming_voice_call: false
|
||||||
};
|
};
|
||||||
CLIENTS
|
CLIENTS
|
||||||
.write()
|
.write()
|
||||||
@ -188,15 +188,27 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn voice_call_started(&self, id: i32) {
|
fn voice_call_started(&self, id: i32) {
|
||||||
self.ui_handler.voice_call_started(id);
|
if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) {
|
||||||
|
client.incoming_voice_call = false;
|
||||||
|
client.in_voice_call = true;
|
||||||
|
self.ui_handler.update_voice_call_state(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn voice_call_incoming(&self, id: i32) {
|
fn voice_call_incoming(&self, id: i32) {
|
||||||
self.ui_handler.voice_call_incoming(id);
|
if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) {
|
||||||
|
client.incoming_voice_call = true;
|
||||||
|
client.in_voice_call = false;
|
||||||
|
self.ui_handler.update_voice_call_state(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn voice_call_closed(&self, id: i32, reason: &str) {
|
fn voice_call_closed(&self, id: i32, _reason: &str) {
|
||||||
self.ui_handler.voice_call_closed(id, reason);
|
if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) {
|
||||||
|
client.incoming_voice_call = false;
|
||||||
|
client.in_voice_call = false;
|
||||||
|
self.ui_handler.update_voice_call_state(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -832,3 +844,17 @@ pub fn elevate_portable(_id: i32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn handle_incoming_voice_call(id: i32, accept: bool) {
|
||||||
|
if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) {
|
||||||
|
allow_err!(client.tx.send(Data::VoiceCallResponse(accept)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn close_voice_call(id: i32) {
|
||||||
|
if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) {
|
||||||
|
allow_err!(client.tx.send(Data::CloseVoiceCall("".to_owned())));
|
||||||
|
};
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user