Merge pull request #1689 from 21pages/persist-chat
persist cm chat page if chat unanswered
This commit is contained in:
commit
2b75c7b361
@ -158,40 +158,26 @@ class ConnectionManagerState extends State<ConnectionManager> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTab(Client client) {
|
||||
return Tab(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 80,
|
||||
child: Text(
|
||||
client.name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildConnectionCard(Client client) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
key: ValueKey(client.id),
|
||||
children: [
|
||||
_CmHeader(client: client),
|
||||
client.isFileTransfer ? Offstage() : _PrivilegeBoard(client: client),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _CmControlPanel(client: client),
|
||||
))
|
||||
],
|
||||
).paddingSymmetric(vertical: 8.0, horizontal: 8.0);
|
||||
return Consumer<ServerModel>(
|
||||
builder: (context, value, child) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
key: ValueKey(client.id),
|
||||
children: [
|
||||
_CmHeader(client: client),
|
||||
client.isFileTransfer || client.disconnected
|
||||
? Offstage()
|
||||
: _PrivilegeBoard(client: client),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _CmControlPanel(client: client),
|
||||
))
|
||||
],
|
||||
).paddingSymmetric(vertical: 8.0, horizontal: 8.0));
|
||||
}
|
||||
|
||||
class _AppIcon extends StatelessWidget {
|
||||
@ -249,7 +235,7 @@ class _CmHeaderState extends State<_CmHeader>
|
||||
void initState() {
|
||||
super.initState();
|
||||
_timer = Timer.periodic(Duration(seconds: 1), (_) {
|
||||
_time.value = _time.value + 1;
|
||||
if (!client.disconnected) _time.value = _time.value + 1;
|
||||
});
|
||||
}
|
||||
|
||||
@ -303,7 +289,10 @@ class _CmHeaderState extends State<_CmHeader>
|
||||
FittedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
Text(translate("Connected")).marginOnly(right: 8.0),
|
||||
Text(client.disconnected
|
||||
? translate("Disconnected")
|
||||
: translate("Connected"))
|
||||
.marginOnly(right: 8.0),
|
||||
Obx(() => Text(
|
||||
formatDurationToTime(Duration(seconds: _time.value))))
|
||||
],
|
||||
@ -311,15 +300,14 @@ class _CmHeaderState extends State<_CmHeader>
|
||||
],
|
||||
),
|
||||
),
|
||||
Consumer<ServerModel>(
|
||||
builder: (_, model, child) => Offstage(
|
||||
offstage: !client.authorized || client.isFileTransfer,
|
||||
child: IconButton(
|
||||
onPressed: () => checkClickTime(client.id,
|
||||
() => gFFI.chatModel.toggleCMChatPage(client.id)),
|
||||
icon: Icon(Icons.message_outlined),
|
||||
),
|
||||
))
|
||||
Offstage(
|
||||
offstage: !client.authorized || client.isFileTransfer,
|
||||
child: IconButton(
|
||||
onPressed: () => checkClickTime(
|
||||
client.id, () => gFFI.chatModel.toggleCMChatPage(client.id)),
|
||||
icon: Icon(Icons.message_outlined),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -435,11 +423,11 @@ class _CmControlPanel extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<ServerModel>(builder: (_, model, child) {
|
||||
return client.authorized
|
||||
? buildAuthorized(context)
|
||||
: buildUnAuthorized(context);
|
||||
});
|
||||
return client.authorized
|
||||
? client.disconnected
|
||||
? buildDisconnected(context)
|
||||
: buildAuthorized(context)
|
||||
: buildUnAuthorized(context);
|
||||
}
|
||||
|
||||
buildAuthorized(BuildContext context) {
|
||||
@ -468,6 +456,31 @@ class _CmControlPanel extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
buildDisconnected(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Ink(
|
||||
width: 200,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: MyTheme.accent, borderRadius: BorderRadius.circular(10)),
|
||||
child: InkWell(
|
||||
onTap: () => handleClose(context),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
translate("Close"),
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
)),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
buildUnAuthorized(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@ -527,6 +540,13 @@ class _CmControlPanel extends StatelessWidget {
|
||||
final model = Provider.of<ServerModel>(context, listen: false);
|
||||
model.sendLoginResponse(client, true);
|
||||
}
|
||||
|
||||
void handleClose(BuildContext context) async {
|
||||
await bind.cmRemoveDisconnectedConnection(connId: client.id);
|
||||
if (await bind.cmGetClientsLength() == 0) {
|
||||
windowManager.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkClickTime(int id, Function() callback) async {
|
||||
|
@ -376,29 +376,29 @@ class ServerModel with ChangeNotifier {
|
||||
} else {
|
||||
_clients[index].authorized = true;
|
||||
}
|
||||
tabController.add(
|
||||
TabInfo(
|
||||
key: client.id.toString(),
|
||||
label: client.name,
|
||||
closable: false,
|
||||
page: Desktop.buildConnectionCard(client)),
|
||||
authorized: true);
|
||||
scrollToBottom();
|
||||
notifyListeners();
|
||||
} else {
|
||||
if (_clients.any((c) => c.id == client.id)) {
|
||||
return;
|
||||
}
|
||||
_clients.add(client);
|
||||
tabController.add(TabInfo(
|
||||
key: client.id.toString(),
|
||||
label: client.name,
|
||||
closable: false,
|
||||
page: Desktop.buildConnectionCard(client)));
|
||||
scrollToBottom();
|
||||
notifyListeners();
|
||||
if (isAndroid) showLoginDialog(client);
|
||||
}
|
||||
tabController.add(
|
||||
TabInfo(
|
||||
key: client.id.toString(),
|
||||
label: client.name,
|
||||
closable: false,
|
||||
page: Desktop.buildConnectionCard(client)),
|
||||
authorized: client.authorized);
|
||||
// remove disconnected
|
||||
final index_disconnected = _clients
|
||||
.indexWhere((c) => c.disconnected && c.peerId == client.peerId);
|
||||
if (index_disconnected >= 0) {
|
||||
_clients.removeAt(index_disconnected);
|
||||
tabController.remove(index_disconnected);
|
||||
}
|
||||
scrollToBottom();
|
||||
notifyListeners();
|
||||
if (isAndroid && !client.authorized) showLoginDialog(client);
|
||||
} catch (e) {
|
||||
debugPrint("Failed to call loginRequest,error:$e");
|
||||
}
|
||||
@ -477,38 +477,19 @@ class ServerModel with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void onClientAuthorized(Map<String, dynamic> evt) {
|
||||
try {
|
||||
final client = Client.fromJson(jsonDecode(evt['client']));
|
||||
parent.target?.dialogManager.dismissByTag(getLoginDialogTag(client.id));
|
||||
final index = _clients.indexWhere((c) => c.id == client.id);
|
||||
if (index < 0) {
|
||||
_clients.add(client);
|
||||
} else {
|
||||
_clients[index].authorized = true;
|
||||
}
|
||||
tabController.add(
|
||||
TabInfo(
|
||||
key: client.id.toString(),
|
||||
label: client.name,
|
||||
closable: false,
|
||||
page: Desktop.buildConnectionCard(client)),
|
||||
authorized: true);
|
||||
scrollToBottom();
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint("onClientAuthorized:$e");
|
||||
}
|
||||
}
|
||||
|
||||
void onClientRemove(Map<String, dynamic> evt) {
|
||||
try {
|
||||
final id = int.parse(evt['id'] as String);
|
||||
final close = (evt['close'] as String) == 'true';
|
||||
if (_clients.any((c) => c.id == id)) {
|
||||
final index = _clients.indexWhere((client) => client.id == id);
|
||||
if (index >= 0) {
|
||||
_clients.removeAt(index);
|
||||
tabController.remove(index);
|
||||
if (close) {
|
||||
_clients.removeAt(index);
|
||||
tabController.remove(index);
|
||||
} else {
|
||||
_clients[index].disconnected = true;
|
||||
}
|
||||
}
|
||||
parent.target?.dialogManager.dismissByTag(getLoginDialogTag(id));
|
||||
parent.target?.invokeMethod("cancel_notification", id);
|
||||
@ -545,6 +526,7 @@ class Client {
|
||||
bool file = false;
|
||||
bool restart = false;
|
||||
bool recording = false;
|
||||
bool disconnected = false;
|
||||
|
||||
Client(this.id, this.authorized, this.isFileTransfer, this.name, this.peerId,
|
||||
this.keyboard, this.clipboard, this.audio);
|
||||
@ -561,18 +543,20 @@ class Client {
|
||||
file = json['file'];
|
||||
restart = json['restart'];
|
||||
recording = json['recording'];
|
||||
disconnected = json['disconnected'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['id'] = this.id;
|
||||
data['is_start'] = this.authorized;
|
||||
data['is_file_transfer'] = this.isFileTransfer;
|
||||
data['name'] = this.name;
|
||||
data['peer_id'] = this.peerId;
|
||||
data['keyboard'] = this.keyboard;
|
||||
data['clipboard'] = this.clipboard;
|
||||
data['audio'] = this.audio;
|
||||
data['id'] = id;
|
||||
data['is_start'] = authorized;
|
||||
data['is_file_transfer'] = isFileTransfer;
|
||||
data['name'] = name;
|
||||
data['peer_id'] = peerId;
|
||||
data['keyboard'] = keyboard;
|
||||
data['clipboard'] = clipboard;
|
||||
data['audio'] = audio;
|
||||
data['disconnected'] = disconnected;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@ -368,8 +368,11 @@ pub mod connection_manager {
|
||||
self.push_event("add_connection", vec![("client", &client_json)]);
|
||||
}
|
||||
|
||||
fn remove_connection(&self, id: i32) {
|
||||
self.push_event("on_client_remove", vec![("id", &id.to_string())]);
|
||||
fn remove_connection(&self, id: i32, close: bool) {
|
||||
self.push_event(
|
||||
"on_client_remove",
|
||||
vec![("id", &id.to_string()), ("close", &close.to_string())],
|
||||
);
|
||||
}
|
||||
|
||||
fn new_message(&self, id: i32, text: String) {
|
||||
|
@ -779,6 +779,10 @@ pub fn cm_check_clients_length(length: usize) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_get_clients_length() -> usize {
|
||||
crate::ui_cm_interface::get_clients_length()
|
||||
}
|
||||
|
||||
pub fn main_init(app_dir: String) {
|
||||
initialize(&app_dir);
|
||||
}
|
||||
@ -941,6 +945,10 @@ pub fn cm_close_connection(conn_id: i32) {
|
||||
crate::ui_cm_interface::close(conn_id);
|
||||
}
|
||||
|
||||
pub fn cm_remove_disconnected_connection(conn_id: i32) {
|
||||
crate::ui_cm_interface::remove(conn_id);
|
||||
}
|
||||
|
||||
pub fn cm_check_click_time(conn_id: i32) {
|
||||
crate::ui_cm_interface::check_click_time(conn_id)
|
||||
}
|
||||
|
@ -186,6 +186,7 @@ pub enum Data {
|
||||
Theme(String),
|
||||
Language(String),
|
||||
Empty,
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", "以当前用户权限运行软件,可能导致远端在访问本机时,没有足够的权限来操作部分窗口。"),
|
||||
("uac_warning", "暂时无法访问远端设备,因为远端设备正在请求用户账户权限,请等待对方关闭UAC窗口。为避免这个问题,建议在远端设备上安装或者以管理员权限运行本软件。"),
|
||||
("elevated_foreground_window_warning", "暂时无法使用鼠标键盘,因为远端桌面的当前窗口需要更高的权限才能操作, 可以请求对方最小化当前窗口。为避免这个问题,建议在远端设备上安装或者以管理员权限运行本软件。"),
|
||||
("Disconnected", "会话已结束"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", "Monit o podwyższeniu uprawnień"),
|
||||
("uac_warning", "Ostrzeżenie UAC"),
|
||||
("elevated_foreground_window_warning", "Pierwszoplanowe okno ostrzeżenia o podwyższeniu uprawnień"),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", "以當前用戶權限運行軟件,可能導致遠端在訪問本機時,沒有足夠的權限來操作部分窗口。"),
|
||||
("uac_warning", "暂时无法访问远端设备,因为远端设备正在请求用户账户权限,请等待对方关闭UAC窗口。为避免这个问题,建议在远端设备上安装或者以管理员权限运行本软件。"),
|
||||
("elevated_foreground_window_warning", "暫時無法使用鼠標鍵盤,因為遠端桌面的當前窗口需要更高的權限才能操作, 可以請求對方最小化當前窗口。為避免這個問題,建議在遠端設備上安裝或者以管理員權限運行本軟件。"),
|
||||
("Disconnected", "會話已結束"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -362,5 +362,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Enable LAN Discovery", "Увімкнути пошук локальної мережі"),
|
||||
("Deny LAN Discovery", "Заборонити виявлення локальної мережі"),
|
||||
("Write a message", "Написати повідомлення"),
|
||||
("Prompt", ""),
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -366,5 +366,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("elevation_prompt", ""),
|
||||
("uac_warning", ""),
|
||||
("elevated_foreground_window_warning", ""),
|
||||
("Disconnected", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -95,6 +95,8 @@ pub struct Connection {
|
||||
api_server: String,
|
||||
lr: LoginRequest,
|
||||
last_recv_time: Arc<Mutex<Instant>>,
|
||||
chat_unanswered: bool,
|
||||
close_manually: bool,
|
||||
}
|
||||
|
||||
impl Subscriber for ConnInner {
|
||||
@ -184,6 +186,8 @@ impl Connection {
|
||||
api_server: "".to_owned(),
|
||||
lr: Default::default(),
|
||||
last_recv_time: Arc::new(Mutex::new(Instant::now())),
|
||||
chat_unanswered: false,
|
||||
close_manually: false,
|
||||
};
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
tokio::spawn(async move {
|
||||
@ -246,6 +250,7 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
ipc::Data::Close => {
|
||||
conn.close_manually = true;
|
||||
let mut misc = Misc::new();
|
||||
misc.set_close_reason("Closed manually by the peer".into());
|
||||
let mut msg_out = Message::new();
|
||||
@ -264,6 +269,7 @@ impl Connection {
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_misc(misc);
|
||||
conn.send(msg_out).await;
|
||||
conn.chat_unanswered = false;
|
||||
}
|
||||
ipc::Data::SwitchPermission{name, enabled} => {
|
||||
log::info!("Change permission {} -> {}", name, enabled);
|
||||
@ -1245,6 +1251,7 @@ impl Connection {
|
||||
}
|
||||
Some(misc::Union::ChatMessage(c)) => {
|
||||
self.send_to_cm(ipc::Data::ChatMessage { text: c.text });
|
||||
self.chat_unanswered = true;
|
||||
}
|
||||
Some(misc::Union::Option(o)) => {
|
||||
self.update_option(&o).await;
|
||||
@ -1445,7 +1452,15 @@ impl Connection {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
lock_screen().await;
|
||||
}
|
||||
self.tx_to_cm.send(ipc::Data::Close).ok();
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let data = if self.chat_unanswered && !self.close_manually {
|
||||
ipc::Data::Disconnected
|
||||
} else {
|
||||
ipc::Data::Close
|
||||
};
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
let data = ipc::Data::Close;
|
||||
self.tx_to_cm.send(data).ok();
|
||||
self.port_forward_socket.take();
|
||||
}
|
||||
|
||||
|
14
src/ui/cm.rs
14
src/ui/cm.rs
@ -38,8 +38,8 @@ impl InvokeUiCM for SciterHandler {
|
||||
);
|
||||
}
|
||||
|
||||
fn remove_connection(&self, id: i32) {
|
||||
self.call("removeConnection", &make_args!(id));
|
||||
fn remove_connection(&self, id: i32, close: bool) {
|
||||
self.call("removeConnection", &make_args!(id, close));
|
||||
if crate::ui_cm_interface::get_clients_length().eq(&0) {
|
||||
crate::platform::quit_gui();
|
||||
}
|
||||
@ -109,6 +109,14 @@ impl SciterConnectionManager {
|
||||
crate::ui_cm_interface::close(id);
|
||||
}
|
||||
|
||||
fn remove_disconnected_connection(&self, id: i32) {
|
||||
crate::ui_cm_interface::remove(id);
|
||||
}
|
||||
|
||||
fn quit(&self) {
|
||||
crate::platform::quit_gui();
|
||||
}
|
||||
|
||||
fn authorize(&self, id: i32) {
|
||||
crate::ui_cm_interface::authorize(id);
|
||||
}
|
||||
@ -133,6 +141,8 @@ impl sciter::EventHandler for SciterConnectionManager {
|
||||
fn get_click_time();
|
||||
fn get_icon();
|
||||
fn close(i32);
|
||||
fn remove_disconnected_connection(i32);
|
||||
fn quit();
|
||||
fn authorize(i32);
|
||||
fn switch_permission(i32, String, bool);
|
||||
fn send_msg(i32, String);
|
||||
|
@ -26,6 +26,7 @@ class Body: Reactor.Component
|
||||
me.sendMsg(msg);
|
||||
};
|
||||
var right_style = show_chat ? "" : "display: none";
|
||||
var disconnected = c.disconnected;
|
||||
// below size:* is work around for Linux, it alreayd set in css, but not work, shit sciter
|
||||
return <div .content style="size:*">
|
||||
<div .left-panel>
|
||||
@ -36,12 +37,12 @@ class Body: Reactor.Component
|
||||
<div>
|
||||
<div .id style="font-weight: bold; font-size: 1.2em;">{c.name}</div>
|
||||
<div .id>({c.peer_id})</div>
|
||||
<div style="margin-top: 1.2em">{translate('Connected')} {" "} <span #time>{getElaspsed(c.time)}</span></div>
|
||||
<div style="margin-top: 1.2em">{disconnected ? translate('Disconnected') : translate('Connected')} {" "} <span #time>{getElaspsed(c.time, c.now)}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
{c.is_file_transfer || c.port_forward ? "" : <div>{translate('Permissions')}</div>}
|
||||
{c.is_file_transfer || c.port_forward ? "" : <div> <div .permissions>
|
||||
{c.is_file_transfer || c.port_forward || disconnected ? "" : <div>{translate('Permissions')}</div>}
|
||||
{c.is_file_transfer || c.port_forward || disconnected ? "" : <div> <div .permissions>
|
||||
<div class={!c.keyboard ? "disabled" : ""} title={translate('Allow using keyboard and mouse')}><icon .keyboard /></div>
|
||||
<div class={!c.clipboard ? "disabled" : ""} title={translate('Allow using clipboard')}><icon .clipboard /></div>
|
||||
<div class={!c.audio ? "disabled" : ""} title={translate('Allow hearing sound')}><icon .audio /></div>
|
||||
@ -56,7 +57,8 @@ class Body: Reactor.Component
|
||||
<div .buttons>
|
||||
{auth ? "" : <button .button tabindex="-1" #accept>{translate('Accept')}</button>}
|
||||
{auth ? "" : <button .button tabindex="-1" .outline #dismiss>{translate('Dismiss')}</button>}
|
||||
{auth ? <button .button tabindex="-1" #disconnect>{translate('Disconnect')}</button> : ""}
|
||||
{auth && !disconnected ? <button .button tabindex="-1" #disconnect>{translate('Disconnect')}</button> : ""}
|
||||
{auth && disconnected ? <button .button tabindex="-1" #close>{translate('Close')}</button> : ""}
|
||||
</div>
|
||||
{c.is_file_transfer || c.port_forward ? "" : <div .chaticon>{svg_chat}</div>}
|
||||
</div>
|
||||
@ -155,6 +157,25 @@ class Body: Reactor.Component
|
||||
handler.close(cid);
|
||||
});
|
||||
}
|
||||
|
||||
event click $(button#close) {
|
||||
var cid = this.cid;
|
||||
if (this.cur >= 0 && this.cur < connections.length){
|
||||
handler.remove_disconnected_connection(cid);
|
||||
connections.splice(this.cur, 1);
|
||||
if (connections.length > 0) {
|
||||
if (this.cur > 0)
|
||||
this.cur -= 1;
|
||||
else
|
||||
this.cur = connections.length - 1;
|
||||
header.update();
|
||||
body.update();
|
||||
} else {
|
||||
handler.quit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$(body).content(<Body />);
|
||||
@ -299,15 +320,26 @@ handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, na
|
||||
update();
|
||||
return;
|
||||
}
|
||||
var idx = -1;
|
||||
connections.map(function(c, i) {
|
||||
if (c.disconnected && c.peer_id == peer_id) idx = i;
|
||||
});
|
||||
if (!name) name = "NA";
|
||||
connections.push({
|
||||
conn = {
|
||||
id: id, is_file_transfer: is_file_transfer, peer_id: peer_id,
|
||||
port_forward: port_forward,
|
||||
name: name, authorized: authorized, time: new Date(),
|
||||
name: name, authorized: authorized, time: new Date(), now: new Date(),
|
||||
keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0,
|
||||
audio: audio, file: file, restart: restart, recording: recording
|
||||
});
|
||||
body.cur = connections.length - 1;
|
||||
audio: audio, file: file, restart: restart, recording: recording,
|
||||
disconnected: false
|
||||
};
|
||||
if (idx < 0) {
|
||||
connections.push(conn);
|
||||
body.cur = connections.length - 1;
|
||||
} else {
|
||||
connections[idx] = conn;
|
||||
body.cur = idx;
|
||||
}
|
||||
bring_to_top();
|
||||
update();
|
||||
self.timer(1ms, adjustHeader);
|
||||
@ -318,15 +350,20 @@ handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, na
|
||||
}
|
||||
}
|
||||
|
||||
handler.removeConnection = function(id) {
|
||||
handler.removeConnection = function(id, close) {
|
||||
var i = -1;
|
||||
connections.map(function(c, idx) {
|
||||
if (c.id == id) i = idx;
|
||||
});
|
||||
if (i < 0) return;
|
||||
connections.splice(i, 1);
|
||||
if (close) {
|
||||
connections.splice(i, 1);
|
||||
} else {
|
||||
var conn = connections[i];
|
||||
conn.disconnected = true;
|
||||
}
|
||||
if (connections.length > 0) {
|
||||
if (body.cur >= i && body.cur > 0) body.cur -= 1;
|
||||
if (body.cur >= i && body.cur > 0 && close) body.cur -= 1;
|
||||
update();
|
||||
}
|
||||
}
|
||||
@ -361,8 +398,7 @@ function self.ready() {
|
||||
view.move(sw - w, 0, w, h);
|
||||
}
|
||||
|
||||
function getElaspsed(time) {
|
||||
var now = new Date();
|
||||
function getElaspsed(time, now) {
|
||||
var seconds = Date.diff(time, now, #seconds);
|
||||
var hours = seconds / 3600;
|
||||
var days = hours / 24;
|
||||
@ -378,11 +414,15 @@ function getElaspsed(time) {
|
||||
|
||||
function updateTime() {
|
||||
self.timer(1s, function() {
|
||||
var now = new Date();
|
||||
connections.map(function(c) {
|
||||
if (!c.disconnected) c.now = now;
|
||||
});
|
||||
var el = $(#time);
|
||||
if (el) {
|
||||
var c = connections[body.cur];
|
||||
if (c) {
|
||||
el.text = getElaspsed(c.time);
|
||||
if (c && !c.disconnected) {
|
||||
el.text = getElaspsed(c.time, c.now);
|
||||
}
|
||||
}
|
||||
updateTime();
|
||||
|
@ -31,6 +31,7 @@ use hbb_common::{
|
||||
pub struct Client {
|
||||
pub id: i32,
|
||||
pub authorized: bool,
|
||||
pub disconnected: bool,
|
||||
pub is_file_transfer: bool,
|
||||
pub port_forward: String,
|
||||
pub name: String,
|
||||
@ -58,7 +59,7 @@ pub struct ConnectionManager<T: InvokeUiCM> {
|
||||
pub trait InvokeUiCM: Send + Clone + 'static + Sized {
|
||||
fn add_connection(&self, client: &Client);
|
||||
|
||||
fn remove_connection(&self, id: i32);
|
||||
fn remove_connection(&self, id: i32, close: bool);
|
||||
|
||||
fn new_message(&self, id: i32, text: String);
|
||||
|
||||
@ -101,6 +102,7 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
||||
let client = Client {
|
||||
id,
|
||||
authorized,
|
||||
disconnected: false,
|
||||
is_file_transfer,
|
||||
port_forward,
|
||||
name: name.clone(),
|
||||
@ -113,12 +115,24 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
||||
recording,
|
||||
tx,
|
||||
};
|
||||
CLIENTS
|
||||
.write()
|
||||
.unwrap()
|
||||
.retain(|_, c| !(c.disconnected && c.peer_id == client.peer_id));
|
||||
CLIENTS.write().unwrap().insert(id, client.clone());
|
||||
self.ui_handler.add_connection(&client);
|
||||
CLIENTS.write().unwrap().insert(id, client);
|
||||
}
|
||||
|
||||
fn remove_connection(&self, id: i32) {
|
||||
CLIENTS.write().unwrap().remove(&id);
|
||||
fn remove_connection(&self, id: i32, close: bool) {
|
||||
if close {
|
||||
CLIENTS.write().unwrap().remove(&id);
|
||||
} else {
|
||||
CLIENTS
|
||||
.write()
|
||||
.unwrap()
|
||||
.get_mut(&id)
|
||||
.map(|c| c.disconnected = true);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android"))]
|
||||
if CLIENTS
|
||||
@ -136,7 +150,7 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
||||
}
|
||||
}
|
||||
|
||||
self.ui_handler.remove_connection(id);
|
||||
self.ui_handler.remove_connection(id, close);
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +181,11 @@ pub fn close(id: i32) {
|
||||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn remove(id: i32) {
|
||||
CLIENTS.write().unwrap().remove(&id);
|
||||
}
|
||||
|
||||
// server mode send chat to peer
|
||||
#[inline]
|
||||
pub fn send_chat(id: i32, text: String) {
|
||||
@ -243,6 +262,7 @@ pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) {
|
||||
let mut conn_id: i32 = 0;
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<Data>();
|
||||
let mut write_jobs: Vec<fs::TransferJob> = Vec::new();
|
||||
let mut close = true;
|
||||
loop {
|
||||
tokio::select! {
|
||||
res = stream.next() => {
|
||||
@ -264,6 +284,12 @@ pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) {
|
||||
log::info!("cm ipc connection closed from connection request");
|
||||
break;
|
||||
}
|
||||
Data::Disconnected => {
|
||||
close = false;
|
||||
tx_file.send(ClipboardFileData::Enable((conn_id, false))).ok();
|
||||
log::info!("cm ipc connection disconnect");
|
||||
break;
|
||||
}
|
||||
Data::PrivacyModeState((id, _)) => {
|
||||
conn_id = conn_id_tmp;
|
||||
allow_err!(tx.send(data));
|
||||
@ -312,7 +338,7 @@ pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) {
|
||||
}
|
||||
}
|
||||
if conn_id != conn_id_tmp {
|
||||
cm.remove_connection(conn_id);
|
||||
cm.remove_connection(conn_id, close);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user