From d7137990b9698cc368efefb5f5bbc8b6fec7b633 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 27 Mar 2024 11:26:29 +0800 Subject: [PATCH] Refact. Flutter web, peer cards (#7525) Signed-off-by: fufesou --- flutter/lib/common/widgets/peer_card.dart | 37 ++-- flutter/lib/web/bridge.dart | 118 ++++++------ flutter/web/js/src/codec.js | 2 +- flutter/web/js/src/connection.ts | 25 ++- flutter/web/js/src/globals.js | 217 ++++++++++++++++------ 5 files changed, 266 insertions(+), 133 deletions(-) diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 6b41921b8..5530d9b86 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -864,7 +864,7 @@ class RecentPeerCard extends BasePeerCard { BuildContext context) async { final List> menuItems = [ _connectAction(context), - _transferFileAction(context), + if (!isWeb) _transferFileAction(context), ]; final List favs = (await bind.mainGetFav()).toList(); @@ -873,7 +873,9 @@ class RecentPeerCard extends BasePeerCard { menuItems.add(_tcpTunnelingAction(context)); } // menuItems.add(await _openNewConnInOptAction(peer.id)); - menuItems.add(await _forceAlwaysRelayAction(peer.id)); + if (!isWeb) { + menuItems.add(await _forceAlwaysRelayAction(peer.id)); + } if (isWindows && peer.platform == kPeerPlatformWindows) { menuItems.add(_rdpAction(context, peer.id)); } @@ -881,7 +883,10 @@ class RecentPeerCard extends BasePeerCard { menuItems.add(_createShortCutAction(peer.id)); } menuItems.add(MenuEntryDivider()); - menuItems.add(_renameAction(peer.id)); + if (!isWeb) { + // TODO: support web version + menuItems.add(_renameAction(peer.id)); + } if (await bind.mainPeerHasPassword(id: peer.id)) { menuItems.add(_unrememberPasswordAction(peer.id)); } @@ -919,13 +924,15 @@ class FavoritePeerCard extends BasePeerCard { BuildContext context) async { final List> menuItems = [ _connectAction(context), - _transferFileAction(context), + if (!isWeb) _transferFileAction(context), ]; if (isDesktop && peer.platform != kPeerPlatformAndroid) { menuItems.add(_tcpTunnelingAction(context)); } // menuItems.add(await _openNewConnInOptAction(peer.id)); - menuItems.add(await _forceAlwaysRelayAction(peer.id)); + if (!isWeb) { + menuItems.add(await _forceAlwaysRelayAction(peer.id)); + } if (isWindows && peer.platform == kPeerPlatformWindows) { menuItems.add(_rdpAction(context, peer.id)); } @@ -933,7 +940,10 @@ class FavoritePeerCard extends BasePeerCard { menuItems.add(_createShortCutAction(peer.id)); } menuItems.add(MenuEntryDivider()); - menuItems.add(_renameAction(peer.id)); + if (!isWeb) { + // TODO: support web version + menuItems.add(_renameAction(peer.id)); + } if (await bind.mainPeerHasPassword(id: peer.id)) { menuItems.add(_unrememberPasswordAction(peer.id)); } @@ -968,7 +978,7 @@ class DiscoveredPeerCard extends BasePeerCard { BuildContext context) async { final List> menuItems = [ _connectAction(context), - _transferFileAction(context), + if (!isWeb) _transferFileAction(context), ]; final List favs = (await bind.mainGetFav()).toList(); @@ -977,7 +987,9 @@ class DiscoveredPeerCard extends BasePeerCard { menuItems.add(_tcpTunnelingAction(context)); } // menuItems.add(await _openNewConnInOptAction(peer.id)); - menuItems.add(await _forceAlwaysRelayAction(peer.id)); + if (!isWeb) { + menuItems.add(await _forceAlwaysRelayAction(peer.id)); + } if (isWindows && peer.platform == kPeerPlatformWindows) { menuItems.add(_rdpAction(context, peer.id)); } @@ -1019,7 +1031,7 @@ class AddressBookPeerCard extends BasePeerCard { BuildContext context) async { final List> menuItems = [ _connectAction(context), - _transferFileAction(context), + if (!isWeb) _transferFileAction(context), ]; if (isDesktop && peer.platform != kPeerPlatformAndroid) { menuItems.add(_tcpTunnelingAction(context)); @@ -1034,7 +1046,10 @@ class AddressBookPeerCard extends BasePeerCard { } if (gFFI.abModel.current.canWrite()) { menuItems.add(MenuEntryDivider()); - menuItems.add(_renameAction(peer.id)); + if (!isWeb) { + // TODO: support web version + menuItems.add(_renameAction(peer.id)); + } if (gFFI.abModel.current.isPersonal() && peer.hash.isNotEmpty) { menuItems.add(_unrememberPasswordAction(peer.id)); } @@ -1148,7 +1163,7 @@ class MyGroupPeerCard extends BasePeerCard { BuildContext context) async { final List> menuItems = [ _connectAction(context), - _transferFileAction(context), + if (!isWeb) _transferFileAction(context), ]; if (isDesktop && peer.platform != kPeerPlatformAndroid) { menuItems.add(_tcpTunnelingAction(context)); diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index dd745bbaf..42935c782 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:js' as js; import 'dart:convert'; import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; import 'package:uuid/uuid.dart'; final _privateConstructorUsedError = UnsupportedError( @@ -111,7 +112,7 @@ class RustdeskImpl { Future sessionGetOption( {required UuidValue sessionId, required String arg, dynamic hint}) { return Future( - () => js.context.callMethod('getByName', ['option:peer', arg])); + () => js.context.callMethod('getByName', ['option:session', arg])); } Future sessionLogin( @@ -238,13 +239,13 @@ class RustdeskImpl { {required UuidValue sessionId, dynamic hint}) { // TODO: default values return Future(() => - js.context.callMethod('getByName', ['option:peer', 'view_style'])); + js.context.callMethod('getByName', ['option:session', 'view_style'])); } Future sessionSetViewStyle( {required UuidValue sessionId, required String value, dynamic hint}) { return Future(() => js.context.callMethod('setByName', [ - 'option:peer', + 'option:session', jsonEncode({'name': 'view_style', 'value': value}) ])); } @@ -253,13 +254,13 @@ class RustdeskImpl { {required UuidValue sessionId, dynamic hint}) { // TODO: default values return Future(() => - js.context.callMethod('getByName', ['option:peer', 'scroll_style'])); + js.context.callMethod('getByName', ['option:session', 'scroll_style'])); } Future sessionSetScrollStyle( {required UuidValue sessionId, required String value, dynamic hint}) { return Future(() => js.context.callMethod('setByName', [ - 'option:peer', + 'option:session', jsonEncode({'name': 'scroll_style', 'value': value}) ])); } @@ -268,14 +269,14 @@ class RustdeskImpl { // TODO: default values {required UuidValue sessionId, dynamic hint}) { - return Future(() => - js.context.callMethod('getByName', ['option:peer', 'image_quality'])); + return Future(() => js.context + .callMethod('getByName', ['option:session', 'image_quality'])); } Future sessionSetImageQuality( {required UuidValue sessionId, required String value, dynamic hint}) { return Future(() => js.context.callMethod('setByName', [ - 'option:peer', + 'option:session', jsonEncode({'name': 'image_quality', 'value': value}) ])); } @@ -283,14 +284,14 @@ class RustdeskImpl { Future sessionGetKeyboardMode( {required UuidValue sessionId, dynamic hint}) { // TODO: default values - return Future(() => - js.context.callMethod('getByName', ['option:peer', 'keyboard_mode'])); + return Future(() => js.context + .callMethod('getByName', ['option:session', 'keyboard_mode'])); } Future sessionSetKeyboardMode( {required UuidValue sessionId, required String value, dynamic hint}) { return Future(() => js.context.callMethod('setByName', [ - 'option:peer', + 'option:session', jsonEncode({'name': 'keyboard_mode', 'value': value}) ])); } @@ -298,13 +299,13 @@ class RustdeskImpl { String? sessionGetReverseMouseWheelSync( {required UuidValue sessionId, dynamic hint}) { return js.context - .callMethod('getByName', ['option:peer', 'reverse_mouse_wheel']); + .callMethod('getByName', ['option:session', 'reverse_mouse_wheel']); } Future sessionSetReverseMouseWheel( {required UuidValue sessionId, required String value, dynamic hint}) { return Future(() => js.context.callMethod('setByName', [ - 'option:peer', + 'option:session', jsonEncode({'name': 'reverse_mouse_wheel', 'value': value}) ])); } @@ -312,7 +313,7 @@ class RustdeskImpl { String? sessionGetDisplaysAsIndividualWindows( {required UuidValue sessionId, dynamic hint}) { return js.context.callMethod( - 'getByName', ['option:peer', 'displays_as_individual_windows']); + 'getByName', ['option:session', 'displays_as_individual_windows']); } Future sessionSetDisplaysAsIndividualWindows( @@ -335,7 +336,7 @@ class RustdeskImpl { try { return Future(() => Int32List.fromList([ int.parse(js.context.callMethod( - 'getByName', ['option:peer', 'custom_image_quality'])) + 'getByName', ['option:session', 'custom_image_quality'])) ])); } catch (e) { return Future.value(null); @@ -350,7 +351,7 @@ class RustdeskImpl { Future sessionSetCustomImageQuality( {required UuidValue sessionId, required int value, dynamic hint}) { return Future(() => js.context.callMethod('setByName', [ - 'option:peer', + 'option:session', jsonEncode({'name': 'custom_image_quality', 'value': value}) ])); } @@ -358,7 +359,7 @@ class RustdeskImpl { Future sessionSetCustomFps( {required UuidValue sessionId, required int fps, dynamic hint}) { return Future(() => js.context.callMethod('setByName', [ - 'option:peer', + 'option:session', jsonEncode({'name': 'custom_fps', 'value': fps}) ])); } @@ -444,7 +445,7 @@ class RustdeskImpl { required String value, dynamic hint}) { return Future(() => js.context.callMethod('SetByName', [ - 'option:peer', + 'option:session', jsonEncode({'name': name, 'value': value}) ])); } @@ -452,7 +453,7 @@ class RustdeskImpl { Future sessionGetPeerOption( {required UuidValue sessionId, required String name, dynamic hint}) { return Future( - () => js.context.callMethod('getByName', ['option:peer', name])); + () => js.context.callMethod('getByName', ['option:session', name])); } Future sessionInputOsPassword( @@ -715,12 +716,21 @@ class RustdeskImpl { } Future> mainGetFav({dynamic hint}) { - throw UnimplementedError(); + List favs = []; + try { + favs = (jsonDecode(js.context.callMethod('getByName', ['fav'])) + as List) + .map((e) => e.toString()) + .toList(); + } catch (e) { + debugPrint('Failed to load favs: $e'); + } + return Future.value(favs); } Future mainStoreFav({required List favs, dynamic hint}) { - // TODO: - throw UnimplementedError(); + return Future( + () => js.context.callMethod('setByName', ['fav', jsonEncode(favs)])); } String mainGetPeerSync({required String id, dynamic hint}) { @@ -748,7 +758,7 @@ class RustdeskImpl { } Future mainDiscover({dynamic hint}) { - return Future(() => js.context.callMethod('setByName', ['discover'])); + throw UnimplementedError(); } Future mainGetApiServer({dynamic hint}) { @@ -807,7 +817,10 @@ class RustdeskImpl { String mainGetPeerOptionSync( {required String id, required String key, dynamic hint}) { - return js.context.callMethod('getByName', ['option:peer', key]); + return js.context.callMethod('getByName', [ + 'option:peer', + jsonEncode({'id': id, 'name': key}) + ]); } String mainGetPeerFlutterOptionSync( @@ -842,7 +855,7 @@ class RustdeskImpl { dynamic hint}) { js.context.callMethod('setByName', [ 'option:peer', - jsonEncode({'name': key, 'value': value}) + jsonEncode({'id': id, 'name': key, 'value': value}) ]); return true; } @@ -862,8 +875,9 @@ class RustdeskImpl { } Future mainPeerHasPassword({required String id, dynamic hint}) { - return Future( - () => js.context.callMethod('getByName', ['peer_has_password', id])); + return Future(() => + js.context.callMethod('getByName', ['peer_has_password', id]) == + 'true'); } Future mainPeerExists({required String id, dynamic hint}) { @@ -881,7 +895,7 @@ class RustdeskImpl { } String mainLoadLanPeersSync({dynamic hint}) { - return js.context.callMethod('getByName', ['load_lan_peers_sync']); + return '{}'; } Future mainLoadRecentPeersForAb( @@ -894,12 +908,11 @@ class RustdeskImpl { } Future mainLoadLanPeers({dynamic hint}) { - return Future(() => js.context.callMethod('getByName', ['load_lan_peers'])); + throw UnimplementedError(); } Future mainRemoveDiscovered({required String id, dynamic hint}) { - return Future( - () => js.context.callMethod('getByName', ['remove_discovered'])); + throw UnimplementedError(); } Future mainChangeTheme({required String dark, dynamic hint}) { @@ -1102,7 +1115,7 @@ class RustdeskImpl { String sessionGetAuditServerSync( {required UuidValue sessionId, required String typ, dynamic hint}) { - throw UnimplementedError(); + return js.context.callMethod('getByName', ['audit_server', typ]); } Future sessionSendNote( @@ -1112,12 +1125,14 @@ class RustdeskImpl { Future sessionAlternativeCodecs( {required UuidValue sessionId, dynamic hint}) { - throw UnimplementedError(); + return Future( + () => js.context.callMethod('getByName', ['alternative_codecs'])); } Future sessionChangePreferCodec( {required UuidValue sessionId, dynamic hint}) { - throw UnimplementedError(); + return Future( + () => js.context.callMethod('setByName', ['change_prefer_codec'])); } Future sessionOnWaitingForImageDialogShow( @@ -1265,37 +1280,16 @@ class RustdeskImpl { dynamic hint}) {} Future queryOnlines({required List ids, dynamic hint}) { - // TODO: - throw UnimplementedError(); + return Future(() => + js.context.callMethod('setByName', ['query_onlines', jsonEncode(ids)])); } // Dup to the function in hbb_common, lib.rs // Maybe we need to move this function to js part. int versionToNumber({required String v, dynamic hint}) { - List versions = v.split('-'); - - int n = 0; - - // The first part is the version number. - // 1.1.10 -> 1001100, 1.2.3 -> 1001030, multiple the last number by 10 - // to leave space for patch version. - if (versions.isNotEmpty) { - int last = 0; - for (var x in versions[0].split('.')) { - last = int.tryParse(x) ?? 0; - n = n * 1000 + last; - } - n -= last; - n += last * 10; - } - - if (versions.length > 1) { - n += int.tryParse(versions[1]) ?? 0; - } - - // Ignore the rest - - return n; + return int.tryParse( + js.context.callMethod('getByName', ['get_version_number', v])) ?? + 0; } Future optionSynced({dynamic hint}) { @@ -1409,11 +1403,11 @@ class RustdeskImpl { } bool mainHasFileClipboard({dynamic hint}) { - throw UnimplementedError(); + return false; } bool mainHasGpuTextureRender({dynamic hint}) { - throw UnimplementedError(); + return false; } Future cmInit({dynamic hint}) { diff --git a/flutter/web/js/src/codec.js b/flutter/web/js/src/codec.js index 27c9565ec..6b295adbd 100644 --- a/flutter/web/js/src/codec.js +++ b/flutter/web/js/src/codec.js @@ -25,7 +25,7 @@ export async function loadVp9(callback) { // Multithreading is used only if `options.threading` is true. // This requires browser support for the new `SharedArrayBuffer` and `Atomics` APIs, // currently available in Firefox and Chrome with experimental flags enabled. - // 所有主流浏览器均默认于2018年1月5日禁用SharedArrayBuffer + // All major browsers disabled SharedArrayBuffer by default on January 5, 2018 const isSIMD = await simd(); console.log('isSIMD: ' + isSIMD); window.OGVLoader.loadClass( diff --git a/flutter/web/js/src/connection.ts b/flutter/web/js/src/connection.ts index c41e32d00..3f9de4131 100644 --- a/flutter/web/js/src/connection.ts +++ b/flutter/web/js/src/connection.ts @@ -453,6 +453,16 @@ export default class Connection { } } + changePreferCodec() { + const supported_decoding = message.SupportedDecoding.fromPartial({ + ability_vp9: 1, + ability_h264: 1, + }); + const option = message.OptionMessage.fromPartial({ supported_decoding }); + const misc = message.Misc.fromPartial({ option }); + this._ws?.sendMessage({ misc }); + } + async reconnect() { this.close(); await this.start(this._id); @@ -549,7 +559,15 @@ export default class Connection { handlePeerInfo(pi: message.PeerInfo) { localStorage.setItem('last_remote_id', this._id); this._peerInfo = pi; + if (pi.current_display > pi.displays.length) { + pi.current_display = 0; + } + if (globals.getVersionNumber(pi.version) < globals.getVersionNumber("1.1.10")) { + this.setPermission("restart", false); + } if (pi.displays.length == 0) { + this.setOption("info", pi); + globals.pushEvent("update_privacy_mode", {}); this.msgbox("error", "Remote Error", "No Display"); return; } @@ -559,6 +577,7 @@ export default class Connection { if (p) this.inputOsPassword(p); const username = this.getOption("info")?.username; if (username && !pi.username) pi.username = username; + globals.pushEvent("update_privacy_mode", {}); this.setOption("info", pi); if (this.getRemember()) { if (this._password?.length) { @@ -573,6 +592,10 @@ export default class Connection { } } + setPermission(name: string, value: Boolean) { + globals.pushEvent("permission", { [name]: value }); + } + shouldAutoLogin(): string { const l = this.getOption("lock-after-session-end"); const a = !!this.getOption("auto-login"); @@ -608,7 +631,7 @@ export default class Connection { default: return; } - globals.pushEvent("permission", { [name]: p.enabled }); + this.setPermission(name, p.enabled); } else if (misc.switch_display) { this.loadVideoDecoder(); globals.pushEvent("switch_display", misc.switch_display); diff --git a/flutter/web/js/src/globals.js b/flutter/web/js/src/globals.js index 5830a7beb..ffb445ccb 100644 --- a/flutter/web/js/src/globals.js +++ b/flutter/web/js/src/globals.js @@ -288,22 +288,19 @@ window.setByName = (name, value) => { value = JSON.parse(value); localStorage.setItem(name + ':' + value.name, value.value); break; - case 'option:peer': + case 'option:session': value = JSON.parse(value); curConn.setOption(value.name, value.value); break; + case 'option:peer': + setPeerOption(value); + break; case 'input_os_password': curConn.inputOsPassword(value); break; case 'check_conn_status': curConn.checkConnStatus(); break; - case 'remove_discovered': - removeDiscovered(value); - break; - case 'discover': - // TODO: discover - break; case 'session_add_sync': return sessionAdd(value); case 'session_start': @@ -326,6 +323,13 @@ window.setByName = (name, value) => { case 'restart': curConn.restart(); break; + case 'fav': + return localStorage.setItem('fav', value); + case 'query_onlines': + queryOnlines(value); + break; + case 'change_prefer_codec': + curConn.changePreferCodec(value); default: break; } @@ -338,22 +342,8 @@ window.getByName = (name, arg) => { return JSON.stringify(v); } -function getPeersForDart() { - const peers = []; - for (const [id, value] of Object.entries(getPeers())) { - if (!id) continue; - const tm = value['tm']; - const info = value['info']; - if (!tm || !info) continue; - peers.push([tm, id, info]); - } - return peers.sort().reverse().map(x => x.slice(1)); -} - function _getByName(name, arg) { switch (name) { - case 'peers': - return getPeersForDart(); case 'remote_id': return localStorage.getItem('remote-id'); case 'remember': @@ -384,8 +374,10 @@ function _getByName(name, arg) { case 'translate': arg = JSON.parse(arg); return translate(arg.locale, arg.text); - case 'option:peer': + case 'option:session': return curConn.getOption(arg); + case 'option:peer': + return getPeerOption(arg); case 'option:toggle': return curConn.getToggleOption(arg); case 'get_conn_status': @@ -399,31 +391,27 @@ function _getByName(name, arg) { case 'version': return version; case 'load_recent_peers': - const peersRecent = localStorage.getItem('peers-recent'); - if (peersRecent) { - onRegisteredEvent(JSON.stringify({ name: 'load_recent_peers', peers: peersRecent })); - } + loadRecentPeers(); break; case 'load_fav_peers': - const peersFav = localStorage.getItem('peers-fav'); - if (peersFav) { - onRegisteredEvent(JSON.stringify({ name: 'load_fav_peers', peers: peersFav })); - } - break; - case 'load_lan_peers': - const peersLan = localStorage.getItem('peers-lan'); - if (peersLan) { - onRegisteredEvent(JSON.stringify({ name: 'load_lan_peers', peers: peersLan })); - } + loadFavPeers(); break; + case 'fav': + return localStorage.getItem('fav') ?? '[]'; case 'load_recent_peers_sync': - return localStorage.getItem('peers-recent') ?? '{}'; - case 'load_lan_peers_sync': - return localStorage.getItem('peers-lan') ?? '{}'; + return JSON.stringify({ + peers: JSON.stringify(getRecentPeers()) + }); case 'api_server': return getApiServer(); case 'is_using_public_server': return !localStorage.getItem('custom-rendezvous-server'); + case 'get_version_number': + return getVersionNumber(arg); + case 'audit_server': + return getAuditServer(arg); + case 'alternative_codecs': + return getAlternativeCodecs(); } return ''; } @@ -458,14 +446,6 @@ export function getPeers() { return getJsonObj('peers'); } -export function getRecentPeers() { - return getJsonObj('peers-recent'); -} - -export function getLanPeers() { - return getJsonObj('peers-lan'); -} - export function getJsonObj(key) { try { return JSON.parse(localStorage.getItem(key)) || {}; @@ -486,7 +466,6 @@ export function copyToClipboard(text) { if (window.clipboardData && window.clipboardData.setData) { // Internet Explorer-specific code path to prevent textarea being shown while dialog is visible. return window.clipboardData.setData("Text", text); - } else if (document.queryCommandSupported && document.queryCommandSupported("copy")) { var textarea = document.createElement("textarea"); @@ -507,17 +486,117 @@ export function copyToClipboard(text) { } } -// ========================== peers begin ========================== -function removeDiscovered(id) { +// Dup to the function in hbb_common, lib.rs +// Maybe we need to move this function to js part. +export function getVersionNumber(v) { try { - const v = localStorage.getItem('discovered'); - if (!v) return; - const discovered = JSON.parse(localStorage.getItem('discovered')); - delete discovered[id]; - localStorage.setItem('discovered', JSON.stringify(discovered)); - } catch (e) { - console.error(e); + let versions = v.split('-'); + + let n = 0; + + // The first part is the version number. + // 1.1.10 -> 1001100, 1.2.3 -> 1001030, multiple the last number by 10 + // to leave space for patch version. + if (versions.length > 0) { + let last = 0; + for (let x of versions[0].split('.')) { + last = parseInt(x) || 0; + n = n * 1000 + last; + } + n -= last; + n += last * 10; + } + + if (versions.length > 1) { + n += parseInt(versions[1]) || 0; + } + + // Ignore the rest + + return n; } + catch (e) { + console.error('Failed to parse version number: "' + v + '" ' + e.message); + return 0; + } +} + +function getPeerOption(value) { + try { + const obj = JSON.parse(value); + const options = getPeers()[obj.id] || {}; + return options[obj.name] || ''; + } + catch (e) { + console.error('Failed to get peer option: "' + value + '", ' + e.message); + } +} + +function setPeerOption(value) { + try { + const obj = JSON.parse(value); + const id = obj.id; + const name = obj.name; + const value = obj.value; + const peers = getPeers(); + const options = peers[id] || {}; + + if (value == undefined) { + delete options[name]; + } else { + options[name] = value; + } + options["tm"] = new Date().getTime(); + peers[id] = options; + localStorage.setItem("peers", JSON.stringify(peers)); + } + catch (e) { + console.error('Failed to set peer option: "' + value + '", ' + e.message); + } +} + +// ========================== peers begin ========================== +function getRecentPeers() { + const peers = []; + for (const [id, value] of Object.entries(getPeers())) { + if (!id) continue; + const tm = value['tm']; + const info = value['info']; + const cardInfo = { + id: id, + username: info['username'] || '', + hostname: info['hostname'] || '', + platform: info['platform'] || '', + alias: value.alias || '', + }; + if (!tm || !cardInfo) continue; + peers.push([tm, id, cardInfo]); + } + return peers.sort().reverse().map(x => x[2]); +} + +function loadRecentPeers() { + const peersRecent = getRecentPeers(); + if (peersRecent) { + onRegisteredEvent(JSON.stringify({ name: 'load_recent_peers', peers: JSON.stringify(peersRecent) })); + } +} + +function loadFavPeers() { + try { + const fav = localStorage.getItem('fav') ?? '[]'; + const favs = JSON.parse(fav); + const peersFav = getRecentPeers().filter(x => favs.includes(x.id)); + if (peersFav) { + onRegisteredEvent(JSON.stringify({ name: 'load_fav_peers', peers: JSON.stringify(peersFav) })); + } + } catch (e) { + console.error('Failed to load fav peers: ' + e.message); + } +} + +export function queryOnlines(value) { + // TODO: implement this } // ========================== peers end =========================== @@ -587,6 +666,17 @@ function increasePort(host, offset) { return host; } +function getAlternativeCodecs() { + return JSON.stringify({ + vp8: 1, + av1: 0, + h264: 1, + h265: 1, + }); +} +// ========================== settings end =========================== + +// ========================== server begin ========================== function getApiServer() { const api_server = localStorage.getItem('api-server'); if (api_server) { @@ -604,4 +694,15 @@ function getApiServer() { } return 'https://admin.rustdesk.com'; } -// ========================== settings end =========================== + +function getAuditServer(typ) { + if (!localStorage.getItem("access_token")) { + return ''; + } + const api_server = getApiServer(); + if (!api_server || api_server.includes('rustdesk.com')) { + return ''; + } + return api_server + '/api/audit/' + typ; +} +// ========================== server end ============================