From ca84816604f650b11b964786336277273f383a65 Mon Sep 17 00:00:00 2001 From: atavism Date: Sun, 26 Nov 2023 14:33:56 -0800 Subject: [PATCH] re-use custom bottom bar on desktop and Android. update ffi subscriber to work with boolean values --- desktop/lib.go | 10 ++ lib/common/ffi_subscriber.dart | 11 +- lib/common/session_model.dart | 21 ++- lib/custom_bottom_bar.dart | 7 +- lib/custom_bottom_item.dart | 9 +- lib/desktop/custom_bottom_bar.dart | 223 ---------------------------- lib/desktop/custom_bottom_item.dart | 148 ------------------ lib/desktop/ffi.dart | 5 + lib/desktop/home.dart | 2 +- 9 files changed, 54 insertions(+), 382 deletions(-) delete mode 100644 lib/desktop/custom_bottom_bar.dart delete mode 100644 lib/desktop/custom_bottom_item.dart diff --git a/desktop/lib.go b/desktop/lib.go index c366aaa4d..6bbf27fab 100644 --- a/desktop/lib.go +++ b/desktop/lib.go @@ -128,6 +128,16 @@ func EmailAddress() *C.char { return C.CString("") } +//export ChatEnabled +func ChatEnabled() *C.char { + return C.CString("false") +} + +//export ReplicaAddr +func ReplicaAddr() *C.char { + return C.CString("") +} + // loadSettings loads the initial settings at startup, either from disk or using defaults. func loadSettings(configDir string) *app.Settings { path := filepath.Join(configDir, "settings.yaml") diff --git a/lib/common/ffi_subscriber.dart b/lib/common/ffi_subscriber.dart index d307e29eb..55acefe1e 100644 --- a/lib/common/ffi_subscriber.dart +++ b/lib/common/ffi_subscriber.dart @@ -1,6 +1,11 @@ import 'common.dart'; import 'common_desktop.dart'; +extension BoolParsing on String { + bool parseBool() { + return this.toLowerCase() == 'true'; + } +} class FfiValueNotifier extends SubscribedNotifier { FfiValueNotifier( @@ -10,7 +15,11 @@ class FfiValueNotifier extends SubscribedNotifier { bool details = false, T Function(Uint8List serialized)? deserialize, }) : super(defaultValue, removeFromCache) { - value = ffiFunction().toDartString() as T?; + if (defaultValue is String?) { + value = ffiFunction().toDartString() as T?; + } else { + value = ffiFunction().toDartString().parseBool() as T?; + } } } diff --git a/lib/common/session_model.dart b/lib/common/session_model.dart index 9e3cada91..194e94083 100644 --- a/lib/common/session_model.dart +++ b/lib/common/session_model.dart @@ -240,9 +240,16 @@ class SessionModel extends Model { } Widget replicaAddr(ValueWidgetBuilder builder) { - return subscribedSingleValueBuilder( + if (Platform.isAndroid) { + return subscribedSingleValueBuilder( + 'replicaAddr', + defaultValue: '', + builder: builder, + ); + } + return ffiValueBuilder( 'replicaAddr', - defaultValue: '', + ffiReplicaAddr, builder: builder, ); } @@ -264,8 +271,16 @@ class SessionModel extends Model { } Widget chatEnabled(ValueWidgetBuilder builder) { - return subscribedSingleValueBuilder( + if (Platform.isAndroid) { + return subscribedSingleValueBuilder( + 'chatEnabled', + defaultValue: false, + builder: builder, + ); + } + return ffiValueBuilder( 'chatEnabled', + ffiChatEnabled, defaultValue: false, builder: builder, ); diff --git a/lib/custom_bottom_bar.dart b/lib/custom_bottom_bar.dart index 19db4901f..4b1887b00 100644 --- a/lib/custom_bottom_bar.dart +++ b/lib/custom_bottom_bar.dart @@ -16,9 +16,7 @@ class CustomBottomBar extends StatelessWidget { @override Widget build(BuildContext context) { - return messagingModel - .getOnBoardingStatus((context, hasBeenOnboarded, child) { - return sessionModel.chatEnabled((context, chatEnabled, _) { + return sessionModel.chatEnabled((context, chatEnabled, _) { return sessionModel.replicaAddr((context, replicaAddr, child) { final replicaEnabled = replicaAddr != ''; @@ -57,14 +55,13 @@ class CustomBottomBar extends StatelessWidget { currentIndex, chatEnabled, replicaEnabled, - hasBeenOnboarded!, + true, isDevelop, isTesting, replicaAddr, ), ); }); - }); }); } diff --git a/lib/custom_bottom_item.dart b/lib/custom_bottom_item.dart index 7158a066d..61703fb96 100644 --- a/lib/custom_bottom_item.dart +++ b/lib/custom_bottom_item.dart @@ -1,4 +1,5 @@ import 'package:lantern/common/common.dart'; +import 'package:lantern/common/common_desktop.dart'; class CustomBottomBarItem extends StatelessWidget { const CustomBottomBarItem({ @@ -51,7 +52,13 @@ class CustomBottomBarItem extends StatelessWidget { ), ), onTap: (() { - sessionModel.setSelectedTab(name); + if (Platform.isAndroid) { + sessionModel.setSelectedTab(name); + } else { + final tab = name.toNativeUtf8(); + setSelectTab(tab); + context.pushRoute(DesktopHome()); + } }), child: Container( decoration: ShapeDecoration( diff --git a/lib/desktop/custom_bottom_bar.dart b/lib/desktop/custom_bottom_bar.dart deleted file mode 100644 index db63381b0..000000000 --- a/lib/desktop/custom_bottom_bar.dart +++ /dev/null @@ -1,223 +0,0 @@ -import 'package:lantern/desktop/custom_bottom_item.dart'; -import 'package:lantern/messaging/messaging.dart'; -import 'package:lantern/replica/common.dart'; - -class CustomBottomBar extends StatelessWidget { - final String selectedTab; - final bool isDevelop; - final bool isTesting; - - const CustomBottomBar({ - required this.selectedTab, - required this.isDevelop, - this.isTesting = false, - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - - final indexToTab = {}; - final tabToIndex = {}; - - var nextIndex = 0; - indexToTab[nextIndex] = TAB_VPN; - tabToIndex[TAB_VPN] = nextIndex++; - indexToTab[nextIndex] = TAB_ACCOUNT; - tabToIndex[TAB_ACCOUNT] = nextIndex++; - if (isDevelop && !isTesting) { - indexToTab[nextIndex] = TAB_DEVELOPER; - tabToIndex[TAB_DEVELOPER] = nextIndex++; - } - - final currentIndex = tabToIndex[selectedTab] ?? tabToIndex[TAB_VPN]!; - return BottomNavigationBar( - currentIndex: currentIndex, - elevation: 0.0, - unselectedFontSize: 0, - selectedFontSize: 0, - showSelectedLabels: false, - type: BottomNavigationBarType.fixed, - items: buildItems( - indexToTab, - tabToIndex, - currentIndex, - false, - false, - true, - isDevelop, - isTesting, - "", - ), - ); - } - - List buildItems( - Map indexToTab, - Map tabToIndex, - int currentIndex, - bool chatEnabled, - bool replicaEnabled, - bool hasBeenOnboarded, - bool isDevelop, - bool isTesting, - String replicaAddr, - ) { - final items = []; - if (chatEnabled) { - items.add( - BottomNavigationBarItem( - icon: messagingModel.getFirstShownTryLanternChatModalTS( - (context, ts, _) => NowBuilder( - calculate: (now) => - hasBeenOnboarded != true && - (now.millisecondsSinceEpoch - ts) < oneWeekInMillis, - builder: (BuildContext context, bool showNewBadge) => - CustomBottomBarItem( - name: TAB_CHATS, - currentTabIndex: currentIndex, - indexToTab: indexToTab, - tabToIndex: tabToIndex, - label: 'chats'.i18n, - icon: ImagePaths.messages, - addBadge: (child) { - if (!chatEnabled || !showNewBadge) { - return messagingModel.contactsByActivity( - builder: ( - context, - Iterable> contacts, - Widget? _, - ) { - final totalUnviewed = contacts.isNotEmpty - ? contacts - .map( - (e) => e.value.isAccepted() - ? e.value.numUnviewedMessages - : 0, - ) - .reduce((value, element) => value + element) - : 0; - return CBadge( - showBadge: totalUnviewed > 0, - count: totalUnviewed, - child: child, - ); - }, - ); - } - - return CBadge( - end: -20, - top: -5, - showBadge: true, - customBadge: Container( - padding: const EdgeInsetsDirectional.only( - top: 2.0, - bottom: 2.0, - start: 5.0, - end: 5.0, - ), - decoration: BoxDecoration( - color: blue3, - borderRadius: const BorderRadius.all( - Radius.circular(80.0), - ), - ), - child: Text( - 'new'.i18n.toUpperCase(), - style: TextStyle( - fontSize: 10, - color: white, - ), - ), - ), - child: child, - ); - }, - ), - ), - ), - label: '', - tooltip: 'chats'.i18n, - ), - ); - } - - items.add( - BottomNavigationBarItem( - icon: CustomBottomBarItem( - name: TAB_VPN, - currentTabIndex: currentIndex, - indexToTab: indexToTab, - tabToIndex: tabToIndex, - label: 'VPN'.i18n, - icon: ImagePaths.key, - labelWidget: vpnModel.vpnStatus( - (context, value, child) => Padding( - padding: const EdgeInsetsDirectional.only(start: 4.0), - child: CircleAvatar( - maxRadius: activeIconSize - 4, - backgroundColor: (value.toLowerCase() == - 'Disconnecting'.i18n.toLowerCase() || - value == 'connected'.i18n.toLowerCase()) - ? indicatorGreen - : indicatorRed, - ), - ), - ), - ), - label: '', - tooltip: 'VPN'.i18n, - ), - ); - - items.add( - BottomNavigationBarItem( - icon: CustomBottomBarItem( - key: AppKeys.bottom_bar_account_tap_key, - name: TAB_ACCOUNT, - currentTabIndex: currentIndex, - indexToTab: indexToTab, - tabToIndex: tabToIndex, - label: 'Account'.i18n, - icon: ImagePaths.account, - addBadge: (child) { - if (hasBeenOnboarded != true) { - return child; - } - - return messagingModel.getCopiedRecoveryStatus( - (context, hasCopiedRecoveryKey, _) => CBadge( - count: 1, - showBadge: !hasCopiedRecoveryKey, - child: child, - ), - ); - }, - ), - label: '', - tooltip: 'Account'.i18n, - ), - ); - - if (isDevelop && !isTesting) { - items.add( - BottomNavigationBarItem( - icon: CustomBottomBarItem( - key: AppKeys.bottom_bar_developer_tap_key, - name: TAB_DEVELOPER, - currentTabIndex: currentIndex, - indexToTab: indexToTab, - tabToIndex: tabToIndex, - label: 'Developer'.i18n, - icon: ImagePaths.devices, - ), - label: '', - tooltip: 'Developer'.i18n, - ), - ); - } - - return items; - } -} diff --git a/lib/desktop/custom_bottom_item.dart b/lib/desktop/custom_bottom_item.dart deleted file mode 100644 index c62e07a8d..000000000 --- a/lib/desktop/custom_bottom_item.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:lantern/common/common.dart'; -import 'package:lantern/desktop/account_tab.dart'; -import 'package:lantern/desktop/ffi.dart'; -import 'dart:ffi' as ffi; // For FFI -import 'package:ffi/ffi.dart'; -import 'package:ffi/src/utf8.dart'; -import 'dart:convert'; - -class CustomBottomBarItem extends StatelessWidget { - const CustomBottomBarItem({ - required this.name, - required this.currentTabIndex, - required this.indexToTab, - required this.tabToIndex, - required this.icon, - required this.label, - this.labelWidget, - this.addBadge = defaultAddBadge, - Key? key, - }) : super(key: key); - - final String name; - final int currentTabIndex; - final Map indexToTab; - final Map tabToIndex; - final String label; - final String icon; - final Widget? labelWidget; - final Widget Function(Widget) addBadge; - - int get totalTabs => tabToIndex.length; - int get tabIndex => tabToIndex[name]!; - bool get active => currentTabIndex == tabIndex; - - static Widget defaultAddBadge(Widget child) => child; - - @override - Widget build(BuildContext context) { - return Container( - height: 68, - color: transparent, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - fit: FlexFit.tight, - flex: 1, - child: CInkWell( - customBorder: RoundedRectangleBorder( - borderRadius: BorderRadiusDirectional.only( - topStart: Radius.circular( - currentTabIndex != 0 ? borderRadius : 0, - ), - topEnd: Radius.circular( - currentTabIndex != totalTabs ? borderRadius : 0, - ), - ), - ), - onTap: (() { - final tab = name.toNativeUtf8(); - setSelectTab(tab); - context.pushRoute(DesktopHome()); - }), - child: Container( - decoration: ShapeDecoration( - color: tabIndex == currentTabIndex - ? selectedTabColor - : unselectedTabColor, - shape: CRoundedRectangleBorder( - topSide: tabIndex == currentTabIndex - ? null - : BorderSide( - color: borderColor, - width: 1, - ), - endSide: currentTabIndex == tabIndex + 1 - ? BorderSide( - color: borderColor, - width: 1, - ) - : null, - startSide: currentTabIndex == tabIndex - 1 - ? BorderSide( - color: borderColor, - width: 1, - ) - : null, - topStartCornerSide: BorderSide( - color: currentTabIndex == tabIndex - 1 - ? borderColor - : Colors.white, - ), - topEndCornerSide: BorderSide( - color: currentTabIndex == tabIndex + 1 - ? borderColor - : Colors.white, - ), - borderRadius: BorderRadiusDirectional.only( - topStart: Radius.circular( - currentTabIndex == tabIndex - 1 ? borderRadius : 0, - ), - topEnd: Radius.circular( - currentTabIndex == tabIndex + 1 ? borderRadius : 0, - ), - ), - ), - ), - child: Padding( - padding: const EdgeInsetsDirectional.only( - top: 12, - bottom: 12, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: addBadge( - CAssetImage( - path: icon, - color: active - ? selectedTabIconColor - : unselectedTabIconColor, - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CText( - label, - style: tsFloatingLabel.copiedWith( - color: active ? black : grey5, - ), - ), - labelWidget ?? const SizedBox(), - ], - ), - ], - ), - ), - ), - ), - ), - ], - ), - ); - } -} diff --git a/lib/desktop/ffi.dart b/lib/desktop/ffi.dart index b15931aa5..ae4328b2d 100644 --- a/lib/desktop/ffi.dart +++ b/lib/desktop/ffi.dart @@ -18,6 +18,9 @@ typedef SelectTab = void Function(ffi.Pointer); typedef selectedtab_func = ffi.Pointer Function(); // FFI fn signature typedef SelectedTab = ffi.Pointer Function(); // Dart fn signature +typedef setting_func = ffi.Pointer Function(); +typedef Setting = ffi.Pointer Function(); + final dylib = ffi.DynamicLibrary.open('liblantern.dylib'); final Start start = @@ -38,6 +41,8 @@ final SelectedTab selectedTab = final ProFunc getPlans = dylib.lookup>('Plans').asFunction(); final ProFunc getUserData = dylib.lookup>('UserData').asFunction(); final ProFunc ffiEmailAddress = dylib.lookup>('EmailAddress').asFunction(); +final ProFunc ffiReplicaAddr = dylib.lookup>('ReplicaAddr').asFunction(); +final ProFunc ffiChatEnabled = dylib.lookup>('ChatEnabled').asFunction(); void loadLibrary() { start(); diff --git a/lib/desktop/home.dart b/lib/desktop/home.dart index 2e6091b34..3c5f6c53f 100644 --- a/lib/desktop/home.dart +++ b/lib/desktop/home.dart @@ -2,7 +2,7 @@ import 'package:lantern/desktop/account_tab.dart'; import 'package:lantern/account/developer_settings.dart'; import 'package:lantern/account/privacy_disclosure.dart'; import 'package:lantern/common/common.dart'; -import 'package:lantern/desktop/custom_bottom_bar.dart'; +import 'package:lantern/custom_bottom_bar.dart'; import 'package:lantern/desktop/ffi.dart'; import 'dart:ffi' as ffi; // For FFI import 'package:ffi/ffi.dart';