From feb6f7930e8e16d8c1523d65f8584c6f2aef4aa2 Mon Sep 17 00:00:00 2001 From: Sahil Yeole Date: Tue, 24 Oct 2023 05:30:43 +0530 Subject: [PATCH] common code autocomplete Signed-off-by: Sahil Yeole --- flutter/lib/common/widgets/autocomplete.dart | 196 ++++++++++++++++++ .../lib/desktop/pages/connection_page.dart | 164 +-------------- flutter/lib/mobile/pages/connection_page.dart | 163 +-------------- 3 files changed, 202 insertions(+), 321 deletions(-) create mode 100644 flutter/lib/common/widgets/autocomplete.dart diff --git a/flutter/lib/common/widgets/autocomplete.dart b/flutter/lib/common/widgets/autocomplete.dart new file mode 100644 index 000000000..9c14eab7f --- /dev/null +++ b/flutter/lib/common/widgets/autocomplete.dart @@ -0,0 +1,196 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:flutter_hbb/common/formatter/id_formatter.dart'; +import '../../../models/platform_model.dart'; +import 'package:flutter_hbb/models/peer_model.dart'; +import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/common/widgets/peer_card.dart'; + + + Future> getAllPeers() async { + Map recentPeers = jsonDecode(await bind.mainLoadRecentPeersSync()); + Map lanPeers = jsonDecode(await bind.mainLoadLanPeersSync()); + Map abPeers = jsonDecode(await bind.mainLoadAbSync()); + Map groupPeers = jsonDecode(await bind.mainLoadGroupSync()); + + Map combinedPeers = {}; + + void _mergePeers(Map peers) { + if (peers.containsKey("peers")) { + dynamic peerData = peers["peers"]; + + if (peerData is String) { + try { + peerData = jsonDecode(peerData); + } catch (e) { + print("Error decoding peers: $e"); + return; + } + } + + if (peerData is List) { + for (var peer in peerData) { + if (peer is Map && peer.containsKey("id")) { + String id = peer["id"]; + if (id != null && !combinedPeers.containsKey(id)) { + combinedPeers[id] = peer; + } + } + } + } + } + } + + _mergePeers(recentPeers); + _mergePeers(lanPeers); + _mergePeers(abPeers); + _mergePeers(groupPeers); + + List parsedPeers = []; + + for (var peer in combinedPeers.values) { + parsedPeers.add(Peer.fromJson(peer)); + } + return parsedPeers; + } + + class AutocompletePeerTile extends StatefulWidget { + final IDTextEditingController idController; + final Peer peer; + + const AutocompletePeerTile({ + Key? key, + required this.idController, + required this.peer, + }) : super(key: key); + + @override + _AutocompletePeerTileState createState() => _AutocompletePeerTileState(); +} + +class _AutocompletePeerTileState extends State{ + List _frontN(List list, int n) { + if (list.length <= n) { + return list; + } else { + return list.sublist(0, n); + } + } + @override + Widget build(BuildContext context){ + final double _tileRadius = 5; + final name = + '${widget.peer.username}${widget.peer.username.isNotEmpty && widget.peer.hostname.isNotEmpty ? '@' : ''}${widget.peer.hostname}'; + final greyStyle = TextStyle( + fontSize: 11, + color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6)); + final child = GestureDetector( + onTap: () { + setState(() { + widget.idController.id = widget.peer.id; + FocusScope.of(context).unfocus(); + }); + }, + child: + Container( + height: 42, + margin: EdgeInsets.only(bottom: 5), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + decoration: BoxDecoration( + color: str2color('${widget.peer.id}${widget.peer.platform}', 0x7f), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(_tileRadius), + bottomLeft: Radius.circular(_tileRadius), + ), + ), + alignment: Alignment.center, + width: 42, + height: null, + child: Padding( + padding: EdgeInsets.all(6), + child: getPlatformImage(widget.peer.platform, size: 30) + ) + ), + Expanded( + child: Container( + padding: EdgeInsets.only(left: 10), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: BorderRadius.only( + topRight: Radius.circular(_tileRadius), + bottomRight: Radius.circular(_tileRadius), + ), + ), + child: Row( + children: [ + Expanded( + child: Container( + margin: EdgeInsets.only(top: 2), + child: Container( + margin: EdgeInsets.only(top: 2), + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: 2), + child: Row(children: [ + getOnline(8, widget.peer.online), + Expanded( + child: Text( + widget.peer.alias.isEmpty ? formatID(widget.peer.id) : widget.peer.alias, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.titleSmall, + )), + !widget.peer.alias.isEmpty? + Padding( + padding: const EdgeInsets.only(left: 5, right: 5), + child: Text( + "(${widget.peer.id})", + style: greyStyle, + overflow: TextOverflow.ellipsis, + ) + ) + : Container(), + ])), + Align( + alignment: Alignment.centerLeft, + child: Text( + name, + style: greyStyle, + textAlign: TextAlign.start, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ) + ))), + ], + ) + ), + ) + ], + ))); + final colors = + _frontN(widget.peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList(); + return Tooltip( + message: isMobile + ? '' + : widget.peer.tags.isNotEmpty + ? '${translate('Tags')}: ${widget.peer.tags.join(', ')}' + : '', + child: Stack(children: [ + child, + if (colors.isNotEmpty) + Positioned( + top: 5, + right: 10, + child: CustomPaint( + painter: TagPainter(radius: 3, colors: colors), + ), + ) + ]), + ); + } + } \ No newline at end of file diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index ec74a8042..670f330bb 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -16,7 +16,7 @@ import 'package:flutter_hbb/models/peer_model.dart'; import '../../common.dart'; import '../../common/formatter/id_formatter.dart'; import '../../common/widgets/peer_tab_page.dart'; -import '../../common/widgets/peer_card.dart'; +import '../../common/widgets/autocomplete.dart'; import '../../models/platform_model.dart'; import '../widgets/button.dart'; @@ -152,60 +152,13 @@ class _ConnectionPageState extends State isPeersLoading = true; }); await Future.delayed(Duration(milliseconds: 100)); - await _getAllPeers(); + peers = await getAllPeers(); setState(() { isPeersLoading = false; isPeersLoaded = true; }); } - Future _getAllPeers() async { - Map recentPeers = jsonDecode(await bind.mainLoadRecentPeersSync()); - Map lanPeers = jsonDecode(await bind.mainLoadLanPeersSync()); - Map abPeers = jsonDecode(await bind.mainLoadAbSync()); - Map groupPeers = jsonDecode(await bind.mainLoadGroupSync()); - - Map combinedPeers = {}; - - void mergePeers(Map peers) { - if (peers.containsKey("peers")) { - dynamic peerData = peers["peers"]; - - if (peerData is String) { - try { - peerData = jsonDecode(peerData); - } catch (e) { - debugPrint("Error decoding peers: $e"); - return; - } - } - - if (peerData is List) { - for (var peer in peerData) { - if (peer is Map && peer.containsKey("id")) { - String id = peer["id"]; - if (id != null && !combinedPeers.containsKey(id)) { - combinedPeers[id] = peer; - } - } - } - } - } - } - - mergePeers(recentPeers); - mergePeers(lanPeers); - mergePeers(abPeers); - mergePeers(groupPeers); - - List parsedPeers = []; - - for (var peer in combinedPeers.values) { - parsedPeers.add(Peer.fromJson(peer)); - } - peers = parsedPeers; - } - /// UI for the remote ID TextField. /// Search for a peer. Widget _buildRemoteIDTextField(BuildContext context) { @@ -359,9 +312,7 @@ class _ConnectionPageState extends State : Padding( padding: const EdgeInsets.only(top: 5), child: ListView( - children: options - .map((peer) => _buildPeerTile(context, peer)) - .toList() + children: options.map((peer) => AutocompletePeerTile(idController: _idController, peer: peer)).toList(), ), ), ), @@ -397,115 +348,6 @@ class _ConnectionPageState extends State constraints: const BoxConstraints(maxWidth: 600), child: w); } - Widget _buildPeerTile( - BuildContext context, Peer peer) { - final double _tileRadius = 5; - final name = - '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; - final greyStyle = TextStyle( - fontSize: 11, - color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6)); - final child = GestureDetector( - onTap: () { - setState(() { - _idController.id = peer.id; - FocusScope.of(context).unfocus(); - }); - }, - child: - Container( - height: 42, - margin: EdgeInsets.only(bottom: 5), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - decoration: BoxDecoration( - color: str2color('${peer.id}${peer.platform}', 0x7f), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(_tileRadius), - bottomLeft: Radius.circular(_tileRadius), - ), - ), - alignment: Alignment.center, - width: 42, - height: null, - child: getPlatformImage(peer.platform, size: 30) - .paddingAll(6), - ), - Expanded( - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: BorderRadius.only( - topRight: Radius.circular(_tileRadius), - bottomRight: Radius.circular(_tileRadius), - ), - ), - child: Row( - children: [ - Expanded( - child: Column( - children: [ - Row(children: [ - getOnline(8, peer.online), - Expanded( - child: Text( - peer.alias.isEmpty ? formatID(peer.id) : peer.alias, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.titleSmall, - )), - !peer.alias.isEmpty? - Padding( - padding: const EdgeInsets.only(left: 5, right: 5), - child: Text( - "(${peer.id})", - style: greyStyle, - overflow: TextOverflow.ellipsis, - ) - ) - : Container(), - ]).marginOnly(top: 2), - Align( - alignment: Alignment.centerLeft, - child: Text( - name, - style: greyStyle, - textAlign: TextAlign.start, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ).marginOnly(top: 2), - ), - ], - ).paddingOnly(left: 10.0, top: 3.0), - ), - ) - ], - ))); - final colors = - _frontN(peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList(); - return Tooltip( - message: isMobile - ? '' - : peer.tags.isNotEmpty - ? '${translate('Tags')}: ${peer.tags.join(', ')}' - : '', - child: Stack(children: [ - child, - if (colors.isNotEmpty) - Positioned( - top: 5, - right: 10, - child: CustomPaint( - painter: TagPainter(radius: 3, colors: colors), - ), - ) - ]), - ); - } - Widget buildStatus() { final em = 14.0; return Container( diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index 09532f724..1c1dec8fc 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:convert'; import 'package:auto_size_text_field/auto_size_text_field.dart'; import 'package:flutter/material.dart'; @@ -12,7 +11,7 @@ import 'package:flutter_hbb/models/peer_model.dart'; import '../../common.dart'; import '../../common/widgets/login.dart'; import '../../common/widgets/peer_tab_page.dart'; -import '../../common/widgets/peer_card.dart'; +import '../../common/widgets/autocomplete.dart'; import '../../consts.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; @@ -134,60 +133,13 @@ class _ConnectionPageState extends State { isPeersLoading = true; }); await Future.delayed(Duration(milliseconds: 100)); - await _getAllPeers(); + peers = await getAllPeers(); setState(() { isPeersLoading = false; isPeersLoaded = true; }); } - Future _getAllPeers() async { - Map recentPeers = jsonDecode(await bind.mainLoadRecentPeersSync()); - Map lanPeers = jsonDecode(await bind.mainLoadLanPeersSync()); - Map abPeers = jsonDecode(await bind.mainLoadAbSync()); - Map groupPeers = jsonDecode(await bind.mainLoadGroupSync()); - - Map combinedPeers = {}; - - void mergePeers(Map peers) { - if (peers.containsKey("peers")) { - dynamic peerData = peers["peers"]; - - if (peerData is String) { - try { - peerData = jsonDecode(peerData); - } catch (e) { - debugPrint("Error decoding peers: $e"); - return; - } - } - - if (peerData is List) { - for (var peer in peerData) { - if (peer is Map && peer.containsKey("id")) { - String id = peer["id"]; - if (id != null && !combinedPeers.containsKey(id)) { - combinedPeers[id] = peer; - } - } - } - } - } - } - - mergePeers(recentPeers); - mergePeers(lanPeers); - mergePeers(abPeers); - mergePeers(groupPeers); - - List parsedPeers = []; - - for (var peer in combinedPeers.values) { - parsedPeers.add(Peer.fromJson(peer)); - } - peers = parsedPeers; - } - /// UI for the remote ID TextField. /// Search for a peer and connect to it if the id exists. Widget _buildRemoteIDTextField() { @@ -316,7 +268,7 @@ class _ConnectionPageState extends State { ))) : ListView( padding: EdgeInsets.only(top: 5), - children: options.map((peer) => _buildPeerTile(context, peer)).toList(), + children: options.map((peer) => AutocompletePeerTile(idController: _idController, peer: peer)).toList(), )))) ); }, @@ -352,115 +304,6 @@ class _ConnectionPageState extends State { child: Container(constraints: kMobilePageConstraints, child: w)); } - Widget _buildPeerTile( - BuildContext context, Peer peer) { - final double _tileRadius = 5; - final name = - '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; - final greyStyle = TextStyle( - fontSize: 11, - color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6)); - final child = GestureDetector( - onTap: () { - setState(() { - _idController.id = peer.id; - FocusScope.of(context).unfocus(); - }); - }, - child: - Container( - height: 42, - margin: EdgeInsets.only(bottom: 5), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - decoration: BoxDecoration( - color: str2color('${peer.id}${peer.platform}', 0x7f), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(_tileRadius), - bottomLeft: Radius.circular(_tileRadius), - ), - ), - alignment: Alignment.center, - width: 42, - height: null, - child: getPlatformImage(peer.platform, size: 30) - .paddingAll(6), - ), - Expanded( - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: BorderRadius.only( - topRight: Radius.circular(_tileRadius), - bottomRight: Radius.circular(_tileRadius), - ), - ), - child: Row( - children: [ - Expanded( - child: Column( - children: [ - Row(children: [ - getOnline(8, peer.online), - Expanded( - child: Text( - peer.alias.isEmpty ? formatID(peer.id) : peer.alias, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.titleSmall, - )), - !peer.alias.isEmpty? - Padding( - padding: const EdgeInsets.only(left: 5, right: 5), - child: Text( - "(${peer.id})", - style: greyStyle, - overflow: TextOverflow.ellipsis, - ) - ) - : Container(), - ]).marginOnly(top: 2), - Align( - alignment: Alignment.centerLeft, - child: Text( - name, - style: greyStyle, - textAlign: TextAlign.start, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ).marginOnly(top: 2), - ), - ], - ).paddingOnly(left: 10.0, top: 3.0), - ), - ) - ], - ))); - final colors = - _frontN(peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList(); - return Tooltip( - message: isMobile - ? '' - : peer.tags.isNotEmpty - ? '${translate('Tags')}: ${peer.tags.join(', ')}' - : '', - child: Stack(children: [ - child, - if (colors.isNotEmpty) - Positioned( - top: 5, - right: 10, - child: CustomPaint( - painter: TagPainter(radius: 3, colors: colors), - ), - ) - ]), - ); - } - @override void dispose() { _idController.dispose();