mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge branch 'master' of https://github.com/rustdesk/rustdesk into opt_chat_overlay_and_fix_pageview_2
This commit is contained in:
@@ -5,6 +5,7 @@ import 'package:draggable_float_widget/draggable_float_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:get/get_rx/src/rx_types/rx_types.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import '../consts.dart';
|
||||
@@ -31,10 +32,14 @@ class ChatModel with ChangeNotifier {
|
||||
|
||||
OverlayEntry? chatIconOverlayEntry;
|
||||
OverlayEntry? chatWindowOverlayEntry;
|
||||
|
||||
bool isConnManager = false;
|
||||
|
||||
RxBool isWindowFocus = true.obs;
|
||||
BlockableOverlayState? _blockableOverlayState;
|
||||
final Rx<VoiceCallStatus> _voiceCallStatus = Rx(VoiceCallStatus.notStarted);
|
||||
|
||||
Rx<VoiceCallStatus> get voiceCallStatus => _voiceCallStatus;
|
||||
|
||||
final ChatUser me = ChatUser(
|
||||
id: "",
|
||||
@@ -312,4 +317,34 @@ class ChatModel with ChangeNotifier {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void onVoiceCallWaiting() {
|
||||
_voiceCallStatus.value = VoiceCallStatus.waitingForResponse;
|
||||
}
|
||||
|
||||
void onVoiceCallStarted() {
|
||||
_voiceCallStatus.value = VoiceCallStatus.connected;
|
||||
}
|
||||
|
||||
void onVoiceCallClosed(String reason) {
|
||||
_voiceCallStatus.value = VoiceCallStatus.notStarted;
|
||||
}
|
||||
|
||||
void onVoiceCallIncoming() {
|
||||
if (isConnManager) {
|
||||
_voiceCallStatus.value = VoiceCallStatus.incoming;
|
||||
}
|
||||
}
|
||||
|
||||
void closeVoiceCall(String id) {
|
||||
bind.sessionCloseVoiceCall(id: id);
|
||||
}
|
||||
}
|
||||
|
||||
enum VoiceCallStatus {
|
||||
notStarted,
|
||||
waitingForResponse,
|
||||
connected,
|
||||
// Connection manager only.
|
||||
incoming
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ class GroupModel {
|
||||
await reset();
|
||||
if (gFFI.userModel.userName.isEmpty ||
|
||||
(gFFI.userModel.isAdmin.isFalse && gFFI.userModel.groupName.isEmpty)) {
|
||||
statePeerTab.check();
|
||||
gFFI.peerTabModel.check_dynamic_tabs();
|
||||
return;
|
||||
}
|
||||
userLoading.value = true;
|
||||
@@ -82,7 +82,7 @@ class GroupModel {
|
||||
userLoadError.value = err.toString();
|
||||
} finally {
|
||||
userLoading.value = false;
|
||||
statePeerTab.check();
|
||||
gFFI.peerTabModel.check_dynamic_tabs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -310,7 +310,6 @@ class InputModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int _signOrZero(num x) {
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
@@ -362,7 +361,6 @@ class InputModel {
|
||||
trackpadScrollDistance = Offset.zero;
|
||||
}
|
||||
|
||||
|
||||
void onPointDownImage(PointerDownEvent e) {
|
||||
debugPrint("onPointDownImage");
|
||||
if (e.kind != ui.PointerDeviceKind.mouse) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:flutter_hbb/models/ab_model.dart';
|
||||
import 'package:flutter_hbb/models/chat_model.dart';
|
||||
import 'package:flutter_hbb/models/file_model.dart';
|
||||
import 'package:flutter_hbb/models/group_model.dart';
|
||||
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
||||
import 'package:flutter_hbb/models/server_model.dart';
|
||||
import 'package:flutter_hbb/models/user_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
@@ -197,6 +198,26 @@ class FfiModel with ChangeNotifier {
|
||||
final peer_id = evt['peer_id'].toString();
|
||||
await bind.sessionSwitchSides(id: peer_id);
|
||||
closeConnection(id: peer_id);
|
||||
} else if (name == "on_url_scheme_received") {
|
||||
final url = evt['url'].toString();
|
||||
parseRustdeskUri(url);
|
||||
} else if (name == "on_voice_call_waiting") {
|
||||
// Waiting for the response from the peer.
|
||||
parent.target?.chatModel.onVoiceCallWaiting();
|
||||
} else if (name == "on_voice_call_started") {
|
||||
// Voice call is connected.
|
||||
parent.target?.chatModel.onVoiceCallStarted();
|
||||
} else if (name == "on_voice_call_closed") {
|
||||
// Voice call is closed with reason.
|
||||
final reason = evt['reason'].toString();
|
||||
parent.target?.chatModel.onVoiceCallClosed(reason);
|
||||
} else if (name == "on_voice_call_incoming") {
|
||||
// Voice call is requested by the peer.
|
||||
parent.target?.chatModel.onVoiceCallIncoming();
|
||||
} else if (name == "update_voice_call_state") {
|
||||
parent.target?.serverModel.updateVoiceCallState(evt);
|
||||
} else {
|
||||
debugPrint("Unknown event name: $name");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -242,7 +263,6 @@ class FfiModel with ChangeNotifier {
|
||||
parent.target?.canvasModel.updateViewStyle();
|
||||
}
|
||||
parent.target?.recordingModel.onSwitchDisplay();
|
||||
parent.target?.inputModel.refreshMousePos();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -538,6 +558,7 @@ class CanvasModel with ChangeNotifier {
|
||||
double _y = 0;
|
||||
// image scale
|
||||
double _scale = 1.0;
|
||||
double _devicePixelRatio = 1.0;
|
||||
Size _size = Size.zero;
|
||||
// the tabbar over the image
|
||||
// double tabBarHeight = 0.0;
|
||||
@@ -561,6 +582,7 @@ class CanvasModel with ChangeNotifier {
|
||||
double get x => _x;
|
||||
double get y => _y;
|
||||
double get scale => _scale;
|
||||
double get devicePixelRatio => _devicePixelRatio;
|
||||
Size get size => _size;
|
||||
ScrollStyle get scrollStyle => _scrollStyle;
|
||||
ViewStyle get viewStyle => _lastViewStyle;
|
||||
@@ -609,13 +631,15 @@ class CanvasModel with ChangeNotifier {
|
||||
_lastViewStyle = viewStyle;
|
||||
_scale = viewStyle.scale;
|
||||
|
||||
_devicePixelRatio = ui.window.devicePixelRatio;
|
||||
if (kIgnoreDpi && style == kRemoteViewStyleOriginal) {
|
||||
_scale = 1.0 / ui.window.devicePixelRatio;
|
||||
_scale = 1.0 / _devicePixelRatio;
|
||||
}
|
||||
_x = (size.width - displayWidth * _scale) / 2;
|
||||
_y = (size.height - displayHeight * _scale) / 2;
|
||||
_imageOverflow.value = _x < 0 || y < 0;
|
||||
notifyListeners();
|
||||
parent.target?.inputModel.refreshMousePos();
|
||||
}
|
||||
|
||||
updateScrollStyle() async {
|
||||
@@ -745,7 +769,7 @@ class CanvasModel with ChangeNotifier {
|
||||
class CursorData {
|
||||
final String peerId;
|
||||
final int id;
|
||||
final img2.Image? image;
|
||||
final img2.Image image;
|
||||
double scale;
|
||||
Uint8List? data;
|
||||
final double hotxOrigin;
|
||||
@@ -770,33 +794,40 @@ class CursorData {
|
||||
|
||||
int _doubleToInt(double v) => (v * 10e6).round().toInt();
|
||||
|
||||
double _checkUpdateScale(double scale, bool shouldScale) {
|
||||
double _checkUpdateScale(double scale) {
|
||||
double oldScale = this.scale;
|
||||
if (!shouldScale) {
|
||||
scale = 1.0;
|
||||
} else {
|
||||
if (scale != 1.0) {
|
||||
// Update data if scale changed.
|
||||
if (Platform.isWindows) {
|
||||
final tgtWidth = (width * scale).toInt();
|
||||
final tgtHeight = (width * scale).toInt();
|
||||
if (tgtWidth < kMinCursorSize || tgtHeight < kMinCursorSize) {
|
||||
double sw = kMinCursorSize.toDouble() / width;
|
||||
double sh = kMinCursorSize.toDouble() / height;
|
||||
scale = sw < sh ? sh : sw;
|
||||
}
|
||||
final tgtWidth = (width * scale).toInt();
|
||||
final tgtHeight = (width * scale).toInt();
|
||||
if (tgtWidth < kMinCursorSize || tgtHeight < kMinCursorSize) {
|
||||
double sw = kMinCursorSize.toDouble() / width;
|
||||
double sh = kMinCursorSize.toDouble() / height;
|
||||
scale = sw < sh ? sh : sw;
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.isWindows) {
|
||||
if (_doubleToInt(oldScale) != _doubleToInt(scale)) {
|
||||
if (_doubleToInt(oldScale) != _doubleToInt(scale)) {
|
||||
if (Platform.isWindows) {
|
||||
data = img2
|
||||
.copyResize(
|
||||
image!,
|
||||
image,
|
||||
width: (width * scale).toInt(),
|
||||
height: (height * scale).toInt(),
|
||||
interpolation: img2.Interpolation.average,
|
||||
)
|
||||
.getBytes(format: img2.Format.bgra);
|
||||
} else {
|
||||
data = Uint8List.fromList(
|
||||
img2.encodePng(
|
||||
img2.copyResize(
|
||||
image,
|
||||
width: (width * scale).toInt(),
|
||||
height: (height * scale).toInt(),
|
||||
interpolation: img2.Interpolation.average,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,8 +837,8 @@ class CursorData {
|
||||
return scale;
|
||||
}
|
||||
|
||||
String updateGetKey(double scale, bool shouldScale) {
|
||||
scale = _checkUpdateScale(scale, shouldScale);
|
||||
String updateGetKey(double scale) {
|
||||
scale = _checkUpdateScale(scale);
|
||||
return '${peerId}_${id}_${_doubleToInt(width * scale)}_${_doubleToInt(height * scale)}';
|
||||
}
|
||||
}
|
||||
@@ -865,7 +896,7 @@ class PredefinedCursor {
|
||||
_cache = CursorData(
|
||||
peerId: '',
|
||||
id: id,
|
||||
image: _image2?.clone(),
|
||||
image: _image2!.clone(),
|
||||
scale: scale,
|
||||
data: data,
|
||||
hotxOrigin:
|
||||
@@ -892,9 +923,10 @@ class CursorModel with ChangeNotifier {
|
||||
double _hoty = 0;
|
||||
double _displayOriginX = 0;
|
||||
double _displayOriginY = 0;
|
||||
DateTime? _firstUpdateMouseTime;
|
||||
bool gotMouseControl = true;
|
||||
DateTime _lastPeerMouse = DateTime.now()
|
||||
.subtract(Duration(milliseconds: 2 * kMouseControlTimeoutMSec));
|
||||
.subtract(Duration(milliseconds: 3000 * kMouseControlTimeoutMSec));
|
||||
String id = '';
|
||||
WeakReference<FFI> parent;
|
||||
|
||||
@@ -913,6 +945,15 @@ class CursorModel with ChangeNotifier {
|
||||
DateTime.now().difference(_lastPeerMouse).inMilliseconds <
|
||||
kMouseControlTimeoutMSec;
|
||||
|
||||
bool isConnIn2Secs() {
|
||||
if (_firstUpdateMouseTime == null) {
|
||||
_firstUpdateMouseTime = DateTime.now();
|
||||
return true;
|
||||
} else {
|
||||
return DateTime.now().difference(_firstUpdateMouseTime!).inSeconds < 2;
|
||||
}
|
||||
}
|
||||
|
||||
CursorModel(this.parent);
|
||||
|
||||
Set<String> get cachedKeys => _cacheKeys;
|
||||
@@ -1065,9 +1106,9 @@ class CursorModel with ChangeNotifier {
|
||||
Future<bool> _updateCache(
|
||||
Uint8List rgba, ui.Image image, int id, int w, int h) async {
|
||||
Uint8List? data;
|
||||
img2.Image? imgOrigin;
|
||||
img2.Image imgOrigin =
|
||||
img2.Image.fromBytes(w, h, rgba, format: img2.Format.rgba);
|
||||
if (Platform.isWindows) {
|
||||
imgOrigin = img2.Image.fromBytes(w, h, rgba, format: img2.Format.rgba);
|
||||
data = imgOrigin.getBytes(format: img2.Format.bgra);
|
||||
} else {
|
||||
ByteData? imgBytes =
|
||||
@@ -1109,8 +1150,10 @@ class CursorModel with ChangeNotifier {
|
||||
|
||||
/// Update the cursor position.
|
||||
updateCursorPosition(Map<String, dynamic> evt, String id) async {
|
||||
gotMouseControl = false;
|
||||
_lastPeerMouse = DateTime.now();
|
||||
if (!isConnIn2Secs()) {
|
||||
gotMouseControl = false;
|
||||
_lastPeerMouse = DateTime.now();
|
||||
}
|
||||
_x = double.parse(evt['x']);
|
||||
_y = double.parse(evt['y']);
|
||||
try {
|
||||
@@ -1265,8 +1308,9 @@ class FFI {
|
||||
late final AbModel abModel; // global
|
||||
late final GroupModel groupModel; // global
|
||||
late final UserModel userModel; // global
|
||||
late final PeerTabModel peerTabModel; // global
|
||||
late final QualityMonitorModel qualityMonitorModel; // session
|
||||
late final RecordingModel recordingModel; // recording
|
||||
late final RecordingModel recordingModel; // session
|
||||
late final InputModel inputModel; // session
|
||||
|
||||
FFI() {
|
||||
@@ -1278,6 +1322,7 @@ class FFI {
|
||||
chatModel = ChatModel(WeakReference(this));
|
||||
fileModel = FileModel(WeakReference(this));
|
||||
userModel = UserModel(WeakReference(this));
|
||||
peerTabModel = PeerTabModel(WeakReference(this));
|
||||
abModel = AbModel(WeakReference(this));
|
||||
groupModel = GroupModel(WeakReference(this));
|
||||
qualityMonitorModel = QualityMonitorModel(WeakReference(this));
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:external_path/external_path.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:win32/win32.dart' as win32;
|
||||
@@ -46,6 +47,8 @@ class PlatformFFI {
|
||||
|
||||
static get localeName => Platform.localeName;
|
||||
|
||||
static get isMain => instance._appType == kAppTypeMain;
|
||||
|
||||
static Future<String> getVersion() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
return packageInfo.version;
|
||||
@@ -112,8 +115,15 @@ class PlatformFFI {
|
||||
}
|
||||
_ffiBind = RustdeskImpl(dylib);
|
||||
if (Platform.isLinux) {
|
||||
// start dbus service, no need to await
|
||||
await _ffiBind.mainStartDbusServer();
|
||||
// Start a dbus service, no need to await
|
||||
_ffiBind.mainStartDbusServer();
|
||||
} else if (Platform.isMacOS && isMain) {
|
||||
Future.wait([
|
||||
// Start dbus service.
|
||||
_ffiBind.mainStartDbusServer(),
|
||||
// Start local audio pulseaudio server.
|
||||
_ffiBind.mainStartPa()
|
||||
]);
|
||||
}
|
||||
_startListenEvent(_ffiBind); // global event
|
||||
try {
|
||||
|
||||
275
flutter/lib/models/peer_tab_model.dart
Normal file
275
flutter/lib/models/peer_tab_model.dart
Normal file
@@ -0,0 +1,275 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:scroll_pos/scroll_pos.dart';
|
||||
|
||||
import '../common.dart';
|
||||
import 'model.dart';
|
||||
|
||||
const int groupTabIndex = 4;
|
||||
const String defaultGroupTabname = 'Group';
|
||||
|
||||
class PeerTabModel with ChangeNotifier {
|
||||
WeakReference<FFI> parent;
|
||||
int get currentTab => _currentTab;
|
||||
int _currentTab = 0; // index in tabNames
|
||||
List<int> get visibleOrderedTabs => _visibleOrderedTabs;
|
||||
List<int> _visibleOrderedTabs = List.empty(growable: true);
|
||||
List<int> get tabOrder => _tabOrder;
|
||||
List<int> _tabOrder = List.from([0, 1, 2, 3, 4]); // constant length
|
||||
int get tabHiddenFlag => _tabHiddenFlag;
|
||||
int _tabHiddenFlag = 0;
|
||||
bool get showScrollBtn => _showScrollBtn;
|
||||
bool _showScrollBtn = false;
|
||||
final List<bool> _fullyVisible = List.filled(5, false);
|
||||
bool get leftFullyVisible => _leftFullyVisible;
|
||||
bool _leftFullyVisible = false;
|
||||
bool get rightFullyVisible => _rightFullyVisible;
|
||||
bool _rightFullyVisible = false;
|
||||
ScrollPosController sc = ScrollPosController();
|
||||
List<String> tabNames = [
|
||||
'Recent Sessions',
|
||||
'Favorites',
|
||||
'Discovered',
|
||||
'Address Book',
|
||||
defaultGroupTabname,
|
||||
];
|
||||
|
||||
PeerTabModel(this.parent) {
|
||||
// init tabHiddenFlag
|
||||
_tabHiddenFlag = int.tryParse(
|
||||
bind.getLocalFlutterConfig(k: 'hidden-peer-card'),
|
||||
radix: 2) ??
|
||||
0;
|
||||
var tabs = _notHiddenTabs();
|
||||
// remove dynamic tabs
|
||||
tabs.remove(groupTabIndex);
|
||||
// init tabOrder
|
||||
try {
|
||||
final conf = bind.getLocalFlutterConfig(k: 'peer-tab-order');
|
||||
if (conf.isNotEmpty) {
|
||||
final json = jsonDecode(conf);
|
||||
if (json is List) {
|
||||
final List<int> list =
|
||||
json.map((e) => int.tryParse(e.toString()) ?? -1).toList();
|
||||
if (list.length == _tabOrder.length &&
|
||||
_tabOrder.every((e) => list.contains(e))) {
|
||||
_tabOrder = list;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrintStack(label: '$e');
|
||||
}
|
||||
// init visibleOrderedTabs
|
||||
var tempList = _tabOrder.toList();
|
||||
tempList.removeWhere((e) => !tabs.contains(e));
|
||||
_visibleOrderedTabs = tempList;
|
||||
// init currentTab
|
||||
_currentTab =
|
||||
int.tryParse(bind.getLocalFlutterConfig(k: 'peer-tab-index')) ?? 0;
|
||||
if (!tabs.contains(_currentTab)) {
|
||||
if (tabs.isNotEmpty) {
|
||||
_currentTab = tabs[0];
|
||||
} else {
|
||||
_currentTab = 0;
|
||||
}
|
||||
}
|
||||
sc.itemCount = _visibleOrderedTabs.length;
|
||||
}
|
||||
|
||||
check_dynamic_tabs() {
|
||||
var visible = visibleTabs();
|
||||
_visibleOrderedTabs = _tabOrder.where((e) => visible.contains(e)).toList();
|
||||
if (_visibleOrderedTabs.contains(groupTabIndex) &&
|
||||
int.tryParse(bind.getLocalFlutterConfig(k: 'peer-tab-index')) ==
|
||||
groupTabIndex) {
|
||||
_currentTab = groupTabIndex;
|
||||
}
|
||||
if (gFFI.userModel.isAdmin.isFalse && gFFI.userModel.groupName.isNotEmpty) {
|
||||
tabNames[groupTabIndex] = gFFI.userModel.groupName.value;
|
||||
} else {
|
||||
tabNames[groupTabIndex] = defaultGroupTabname;
|
||||
}
|
||||
sc.itemCount = _visibleOrderedTabs.length;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
setCurrentTab(int index) {
|
||||
if (_currentTab != index) {
|
||||
_currentTab = index;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
setTabFullyVisible(int index, bool visible) {
|
||||
if (index >= 0 && index < _fullyVisible.length) {
|
||||
if (visible != _fullyVisible[index]) {
|
||||
_fullyVisible[index] = visible;
|
||||
bool changed = false;
|
||||
bool show = _visibleOrderedTabs.any((e) => !_fullyVisible[e]);
|
||||
if (show != _showScrollBtn) {
|
||||
_showScrollBtn = show;
|
||||
changed = true;
|
||||
}
|
||||
if (_visibleOrderedTabs.isNotEmpty && _visibleOrderedTabs[0] == index) {
|
||||
if (_leftFullyVisible != visible) {
|
||||
_leftFullyVisible = visible;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (_visibleOrderedTabs.isNotEmpty &&
|
||||
_visibleOrderedTabs.last == index) {
|
||||
if (_rightFullyVisible != visible) {
|
||||
_rightFullyVisible = visible;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onReorder(oldIndex, newIndex) {
|
||||
if (oldIndex < newIndex) {
|
||||
newIndex -= 1;
|
||||
}
|
||||
var list = _visibleOrderedTabs.toList();
|
||||
final int item = list.removeAt(oldIndex);
|
||||
list.insert(newIndex, item);
|
||||
_visibleOrderedTabs = list;
|
||||
|
||||
var tmpTabOrder = _visibleOrderedTabs.toList();
|
||||
var left = _tabOrder.where((e) => !tmpTabOrder.contains(e)).toList();
|
||||
for (var t in left) {
|
||||
_addTabInOrder(tmpTabOrder, t);
|
||||
}
|
||||
_tabOrder = tmpTabOrder;
|
||||
bind.setLocalFlutterConfig(k: 'peer-tab-order', v: jsonEncode(tmpTabOrder));
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
onHideShow(int index, bool show) async {
|
||||
int bitMask = 1 << index;
|
||||
if (show) {
|
||||
_tabHiddenFlag &= ~bitMask;
|
||||
} else {
|
||||
_tabHiddenFlag |= bitMask;
|
||||
}
|
||||
await bind.setLocalFlutterConfig(
|
||||
k: 'hidden-peer-card', v: _tabHiddenFlag.toRadixString(2));
|
||||
var visible = visibleTabs();
|
||||
_visibleOrderedTabs = _tabOrder.where((e) => visible.contains(e)).toList();
|
||||
if (_visibleOrderedTabs.isNotEmpty &&
|
||||
!_visibleOrderedTabs.contains(_currentTab)) {
|
||||
_currentTab = _visibleOrderedTabs[0];
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<int> orderedNotFilteredTabs() {
|
||||
var list = tabOrder.toList();
|
||||
if (_filterGroupCard()) {
|
||||
list.remove(groupTabIndex);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// return index array of tabNames
|
||||
List<int> visibleTabs() {
|
||||
var v = List<int>.empty(growable: true);
|
||||
for (int i = 0; i < tabNames.length; i++) {
|
||||
if (!_isTabHidden(i) && !_isTabFilter(i)) {
|
||||
v.add(i);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
String translatedTabname(int index) {
|
||||
if (index >= 0 && index < tabNames.length) {
|
||||
final name = tabNames[index];
|
||||
if (index == groupTabIndex) {
|
||||
if (name == defaultGroupTabname) {
|
||||
return translate(name);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
} else {
|
||||
return translate(name);
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
return index.toString();
|
||||
}
|
||||
|
||||
bool _isTabHidden(int tabindex) {
|
||||
return _tabHiddenFlag & (1 << tabindex) != 0;
|
||||
}
|
||||
|
||||
bool _isTabFilter(int tabIndex) {
|
||||
if (tabIndex == groupTabIndex) {
|
||||
return _filterGroupCard();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if hide group card
|
||||
bool _filterGroupCard() {
|
||||
if (gFFI.groupModel.users.isEmpty ||
|
||||
(gFFI.userModel.isAdmin.isFalse && gFFI.userModel.groupName.isEmpty)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
List<int> _notHiddenTabs() {
|
||||
var v = List<int>.empty(growable: true);
|
||||
for (int i = 0; i < tabNames.length; i++) {
|
||||
if (!_isTabHidden(i)) {
|
||||
v.add(i);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// add tabIndex to list
|
||||
_addTabInOrder(List<int> list, int tabIndex) {
|
||||
if (!_tabOrder.contains(tabIndex) || list.contains(tabIndex)) {
|
||||
return;
|
||||
}
|
||||
bool sameOrder = true;
|
||||
int lastIndex = -1;
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
var index = _tabOrder.lastIndexOf(list[i]);
|
||||
if (index > lastIndex) {
|
||||
lastIndex = index;
|
||||
continue;
|
||||
} else {
|
||||
sameOrder = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sameOrder) {
|
||||
var indexInTabOrder = _tabOrder.indexOf(tabIndex);
|
||||
var left = List.empty(growable: true);
|
||||
for (int i = 0; i < indexInTabOrder; i++) {
|
||||
left.add(_tabOrder[i]);
|
||||
}
|
||||
int insertIndex = list.lastIndexWhere((e) => left.contains(e));
|
||||
if (insertIndex < 0) {
|
||||
insertIndex = 0;
|
||||
} else {
|
||||
insertIndex += 1;
|
||||
}
|
||||
list.insert(insertIndex, tabIndex);
|
||||
} else {
|
||||
list.add(tabIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,6 +579,26 @@ class ServerModel with ChangeNotifier {
|
||||
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 = client.inVoiceCall;
|
||||
_clients[index].incomingVoiceCall = client.incomingVoiceCall;
|
||||
if (client.incomingVoiceCall) {
|
||||
// Has incoming phone call, let's set the window on top.
|
||||
Future.delayed(Duration.zero, () {
|
||||
window_on_top(null);
|
||||
});
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("updateVoiceCallState failed: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ClientType {
|
||||
@@ -602,6 +622,8 @@ class Client {
|
||||
bool recording = false;
|
||||
bool disconnected = false;
|
||||
bool fromSwitch = false;
|
||||
bool inVoiceCall = false;
|
||||
bool incomingVoiceCall = false;
|
||||
|
||||
RxBool hasUnreadChatMessage = false.obs;
|
||||
|
||||
@@ -623,6 +645,8 @@ class Client {
|
||||
recording = json['recording'];
|
||||
disconnected = json['disconnected'];
|
||||
fromSwitch = json['from_switch'];
|
||||
inVoiceCall = json['in_voice_call'];
|
||||
incomingVoiceCall = json['incoming_voice_call'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
||||
@@ -62,7 +62,7 @@ class UserModel {
|
||||
await gFFI.groupModel.reset();
|
||||
userName.value = '';
|
||||
groupName.value = '';
|
||||
statePeerTab.check();
|
||||
gFFI.peerTabModel.check_dynamic_tabs();
|
||||
}
|
||||
|
||||
Future<void> _parseAndUpdateUser(UserPayload user) async {
|
||||
|
||||
Reference in New Issue
Block a user