From ca3739ec7176e52f01b59913734e24e5455ce215 Mon Sep 17 00:00:00 2001 From: Marvin M <39344769+M123-dev@users.noreply.github.com> Date: Sat, 11 Dec 2021 12:54:01 +0100 Subject: [PATCH] feat: #681 Stateful tabs (#703) * feat: first working stateful tab * feat: enum instead of String * Update tab_navigator.dart * feat: Working camera disable for other tabs * fix: #640 stop camera when navigating further in the stack * doc + null safety * fix: activating scanner when rebuilding from ground up * Updated goldens The navbar got removed from the images because it is now not directly added by the pages anymore * doc: * Minor changes * fix: Updating product with multiple navigators * Minor fixes * Simplification * Simplification * Simplification * Update main.dart * Update product_list_page.dart Co-authored-by: monsieurtanuki --- .../data_models/continuous_scan_model.dart | 11 +- packages/smooth_app/lib/main.dart | 4 +- .../smooth_app/lib/pages/page_manager.dart | 110 ++++++++++++++++++ .../product/common/product_dialog_helper.dart | 3 +- .../product/common/product_list_page.dart | 4 - .../lib/pages/product/new_product_page.dart | 2 - .../lib/pages/product/product_page.dart | 2 - .../lib/pages/scan/continuous_scan_page.dart | 22 ++-- .../smooth_app/lib/pages/scan/scan_page.dart | 29 ++++- .../pages/smooth_bottom_navigation_bar.dart | 85 -------------- .../lib/pages/user_preferences_page.dart | 4 - .../smooth_app/lib/widgets/tab_navigator.dart | 38 ++++++ .../user_preferences_page-blue-dark.png | Bin 5286 -> 4547 bytes .../user_preferences_page-blue-light.png | Bin 5387 -> 4570 bytes .../user_preferences_page-brown-dark.png | Bin 5286 -> 4547 bytes .../user_preferences_page-brown-light.png | Bin 5431 -> 4568 bytes .../user_preferences_page-green-dark.png | Bin 5286 -> 4547 bytes .../user_preferences_page-green-light.png | Bin 5448 -> 4568 bytes 18 files changed, 201 insertions(+), 113 deletions(-) create mode 100644 packages/smooth_app/lib/pages/page_manager.dart delete mode 100644 packages/smooth_app/lib/pages/smooth_bottom_navigation_bar.dart create mode 100644 packages/smooth_app/lib/widgets/tab_navigator.dart diff --git a/packages/smooth_app/lib/data_models/continuous_scan_model.dart b/packages/smooth_app/lib/data_models/continuous_scan_model.dart index 58f13349b52..158a808e1db 100644 --- a/packages/smooth_app/lib/data_models/continuous_scan_model.dart +++ b/packages/smooth_app/lib/data_models/continuous_scan_model.dart @@ -37,6 +37,8 @@ class ContinuousScanModel with ChangeNotifier { final String languageCode; final String countryCode; + QRViewController? _qrViewController; + bool get hasMoreThanOneProduct => getBarcodes().length > 1; ProductList get productList => _productList; @@ -92,8 +94,13 @@ class ContinuousScanModel with ChangeNotifier { Product getProduct(final String barcode) => _productList.getProduct(barcode); - void setupScanner(QRViewController controller) => - controller.scannedDataStream.listen((Barcode barcode) => onScan(barcode)); + void setupScanner(QRViewController controller) { + _qrViewController = controller; + controller.scannedDataStream.listen((Barcode barcode) => onScan(barcode)); + } + + //Used when navigating away from the QRView itself + void stopQRView() => _qrViewController?.stopCamera(); Future onScan(final Barcode barcode) async { if (barcode.code == null) { diff --git a/packages/smooth_app/lib/main.dart b/packages/smooth_app/lib/main.dart index e2f8917c528..e47ef077f1d 100644 --- a/packages/smooth_app/lib/main.dart +++ b/packages/smooth_app/lib/main.dart @@ -11,7 +11,7 @@ import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/data_models/user_preferences.dart'; import 'package:smooth_app/database/local_database.dart'; -import 'package:smooth_app/pages/smooth_bottom_navigation_bar.dart'; +import 'package:smooth_app/pages/page_manager.dart'; import 'package:smooth_app/themes/smooth_theme.dart'; import 'package:smooth_app/themes/theme_provider.dart'; @@ -173,7 +173,7 @@ class SmoothAppGetLanguage extends StatelessWidget { DefaultAssetBundle.of(context), languageCode, ); - return SmoothBottomNavigationBar.getDefaultPage(); + return PageManager(); } Future _refresh( diff --git a/packages/smooth_app/lib/pages/page_manager.dart b/packages/smooth_app/lib/pages/page_manager.dart new file mode 100644 index 00000000000..a81d8db88ef --- /dev/null +++ b/packages/smooth_app/lib/pages/page_manager.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; +import 'package:smooth_app/widgets/tab_navigator.dart'; + +enum BottomNavigationTab { + Profile, + Scan, + History, +} + +/// Here the different tabs in the bottom navigation bar are taken care of, +/// so that they are stateful, that is not only things like the scroll position +/// but also keeping the navigation on the different tabs. +/// +/// Scan Page is an exception here as it needs a little more work so that the +/// camera is not kept unnecessarily kept active. +class PageManager extends StatefulWidget { + @override + State createState() => PageManagerState(); +} + +class PageManagerState extends State { + static const List _pageKeys = [ + BottomNavigationTab.Profile, + BottomNavigationTab.Scan, + BottomNavigationTab.History, + ]; + + final Map> _navigatorKeys = + >{ + BottomNavigationTab.Profile: GlobalKey(), + BottomNavigationTab.Scan: GlobalKey(), + BottomNavigationTab.History: GlobalKey(), + }; + + BottomNavigationTab _currentPage = BottomNavigationTab.Scan; + + void _selectTab(BottomNavigationTab tabItem, int index) { + if (tabItem == _currentPage) { + _navigatorKeys[tabItem]! + .currentState! + .popUntil((Route route) => route.isFirst); + } else { + setState(() { + _currentPage = _pageKeys[index]; + }); + } + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + final bool isFirstRouteInCurrentTab = + !await _navigatorKeys[_currentPage]!.currentState!.maybePop(); + if (isFirstRouteInCurrentTab) { + if (_currentPage != BottomNavigationTab.Scan) { + _selectTab(BottomNavigationTab.Scan, 1); + return false; + } + } + // let system handle back button if we're on the first route + return isFirstRouteInCurrentTab; + }, + child: Scaffold( + body: Stack(children: [ + _buildOffstageNavigator(BottomNavigationTab.Profile), + _buildOffstageNavigator(BottomNavigationTab.Scan), + _buildOffstageNavigator(BottomNavigationTab.History), + ]), + bottomNavigationBar: BottomNavigationBar( + showSelectedLabels: false, + showUnselectedLabels: false, + selectedItemColor: Colors.white, + backgroundColor: Theme.of(context).appBarTheme.backgroundColor, + onTap: (int index) { + _selectTab(_pageKeys[index], index); + }, + currentIndex: _currentPage.index, + items: const [ + // TODO(M123): Translate + BottomNavigationBarItem( + icon: Icon(Icons.account_circle), + label: 'Profile', + ), + BottomNavigationBarItem( + icon: Icon(Icons.search), + label: 'Scan or Search', + ), + BottomNavigationBarItem( + icon: Icon(Icons.history), + label: 'History', + ), + ], + ), + ), + ); + } + + Widget _buildOffstageNavigator(BottomNavigationTab tabItem) { + final bool offstage = _currentPage != tabItem; + return Offstage( + offstage: offstage, + child: TabNavigator( + offstage: offstage, + navigatorKey: _navigatorKeys[tabItem]!, + tabItem: tabItem, + ), + ); + } +} diff --git a/packages/smooth_app/lib/pages/product/common/product_dialog_helper.dart b/packages/smooth_app/lib/pages/product/common/product_dialog_helper.dart index b6c7f293d07..dca09938585 100644 --- a/packages/smooth_app/lib/pages/product/common/product_dialog_helper.dart +++ b/packages/smooth_app/lib/pages/product/common/product_dialog_helper.dart @@ -55,7 +55,8 @@ class ProductDialogHelper { return; } _popEd = true; - Navigator.pop(context, fetchedProduct); + // Here we use the root navigator so that we can pop dialog while using multiple navigators. + Navigator.of(context, rootNavigator: true).pop(fetchedProduct); } Widget _getSearchingDialog() => SmoothAlertDialog( diff --git a/packages/smooth_app/lib/pages/product/common/product_list_page.dart b/packages/smooth_app/lib/pages/product/common/product_list_page.dart index 7dde3dcd974..05e2fb66d49 100644 --- a/packages/smooth_app/lib/pages/product/common/product_list_page.dart +++ b/packages/smooth_app/lib/pages/product/common/product_list_page.dart @@ -9,7 +9,6 @@ import 'package:smooth_app/pages/personalized_ranking_page.dart'; import 'package:smooth_app/pages/product/common/product_list_dialog_helper.dart'; import 'package:smooth_app/pages/product/common/product_list_item_simple.dart'; import 'package:smooth_app/pages/product/common/product_query_page_helper.dart'; -import 'package:smooth_app/pages/smooth_bottom_navigation_bar.dart'; class ProductListPage extends StatefulWidget { const ProductListPage(this.productList); @@ -47,9 +46,6 @@ class _ProductListPageState extends State { dismissible = false; } return Scaffold( - bottomNavigationBar: const SmoothBottomNavigationBar( - tab: SmoothBottomNavigationTab.History, - ), appBar: AppBar( title: Row( children: [ diff --git a/packages/smooth_app/lib/pages/product/new_product_page.dart b/packages/smooth_app/lib/pages/product/new_product_page.dart index 412b25579db..7550e3351ae 100644 --- a/packages/smooth_app/lib/pages/product/new_product_page.dart +++ b/packages/smooth_app/lib/pages/product/new_product_page.dart @@ -15,7 +15,6 @@ import 'package:smooth_app/helpers/launch_url_helper.dart'; import 'package:smooth_app/helpers/product_cards_helper.dart'; import 'package:smooth_app/pages/product/common/product_dialog_helper.dart'; import 'package:smooth_app/pages/product/summary_card.dart'; -import 'package:smooth_app/pages/smooth_bottom_navigation_bar.dart'; import 'package:smooth_app/themes/smooth_theme.dart'; import 'package:smooth_app/themes/theme_provider.dart'; import 'package:smooth_ui_library/util/ui_helpers.dart'; @@ -54,7 +53,6 @@ class _ProductPageState extends State { final MaterialColor materialColor = SmoothTheme.getMaterialColor(themeProvider); return Scaffold( - bottomNavigationBar: const SmoothBottomNavigationBar(), backgroundColor: SmoothTheme.getColor( colorScheme, materialColor, diff --git a/packages/smooth_app/lib/pages/product/product_page.dart b/packages/smooth_app/lib/pages/product/product_page.dart index d55f75d53f6..4f4b0c161f0 100644 --- a/packages/smooth_app/lib/pages/product/product_page.dart +++ b/packages/smooth_app/lib/pages/product/product_page.dart @@ -21,7 +21,6 @@ import 'package:smooth_app/helpers/product_translation_helper.dart'; import 'package:smooth_app/pages/product/common/product_dialog_helper.dart'; import 'package:smooth_app/pages/product/common/product_query_page_helper.dart'; import 'package:smooth_app/pages/product/new_product_page.dart'; -import 'package:smooth_app/pages/smooth_bottom_navigation_bar.dart'; import 'package:smooth_app/pages/user_preferences_page.dart'; import 'package:smooth_app/themes/constant_icons.dart'; import 'package:smooth_app/themes/smooth_theme.dart'; @@ -103,7 +102,6 @@ class _ProductPageState extends State { } } return Scaffold( - bottomNavigationBar: const SmoothBottomNavigationBar(), appBar: AppBar( title: Text(_getProductName(appLocalizations)), actions: [ diff --git a/packages/smooth_app/lib/pages/scan/continuous_scan_page.dart b/packages/smooth_app/lib/pages/scan/continuous_scan_page.dart index fd8c446a4f5..94240d722ab 100644 --- a/packages/smooth_app/lib/pages/scan/continuous_scan_page.dart +++ b/packages/smooth_app/lib/pages/scan/continuous_scan_page.dart @@ -4,19 +4,26 @@ import 'package:provider/provider.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:smooth_app/data_models/continuous_scan_model.dart'; import 'package:smooth_app/pages/personalized_ranking_page.dart'; -import 'package:smooth_app/pages/smooth_bottom_navigation_bar.dart'; import 'package:smooth_app/widgets/smooth_product_carousel.dart'; import 'package:smooth_ui_library/smooth_ui_library.dart'; import 'package:smooth_ui_library/util/ui_helpers.dart'; -class ContinuousScanPage extends StatelessWidget { +class ContinuousScanPage extends StatefulWidget { + const ContinuousScanPage(); + + @override + State createState() => _ContinuousScanPageState(); +} + +class _ContinuousScanPageState extends State { final GlobalKey _scannerViewKey = GlobalKey(debugLabel: 'Barcode Scanner'); + ContinuousScanModel? model; @override Widget build(BuildContext context) { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { - final ContinuousScanModel model = context.watch(); + model = context.watch(); final Size screenSize = MediaQuery.of(context).size; final Size scannerSize = Size( screenSize.width * 0.6, @@ -24,7 +31,7 @@ class ContinuousScanPage extends StatelessWidget { ); final double carouselHeight = constraints.maxHeight / 1.81; // roughly 55% of the available height - final double buttonRowHeight = areButtonsRendered(model) ? 48 : 0; + final double buttonRowHeight = areButtonsRendered(model!) ? 48 : 0; final double availableScanHeight = constraints.maxHeight - carouselHeight - buttonRowHeight; // Padding for the qr code scanner. This ensures the scanner has equal spacing between buttons and carousel. @@ -34,9 +41,6 @@ class ContinuousScanPage extends StatelessWidget { final double viewFinderBottomOffset = carouselHeight / 2.0; return Scaffold( appBar: AppBar(toolbarHeight: 0.0), - bottomNavigationBar: const SmoothBottomNavigationBar( - tab: SmoothBottomNavigationTab.Scan, - ), body: Stack( children: [ Container( @@ -64,7 +68,7 @@ class ContinuousScanPage extends StatelessWidget { cutOutBottomOffset: viewFinderBottomOffset, ), key: _scannerViewKey, - onQRViewCreated: model.setupScanner, + onQRViewCreated: model!.setupScanner, ), ), SmoothRevealAnimation( @@ -91,7 +95,7 @@ class ContinuousScanPage extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ - _buildButtonsRow(context, model), + _buildButtonsRow(context, model!), const Spacer(), SmoothProductCarousel( showSearchCard: true, diff --git a/packages/smooth_app/lib/pages/scan/scan_page.dart b/packages/smooth_app/lib/pages/scan/scan_page.dart index ab10ca852c5..d11473dc09c 100644 --- a/packages/smooth_app/lib/pages/scan/scan_page.dart +++ b/packages/smooth_app/lib/pages/scan/scan_page.dart @@ -6,7 +6,10 @@ import 'package:smooth_app/database/product_query.dart'; import 'package:smooth_app/pages/scan/continuous_scan_page.dart'; class ScanPage extends StatefulWidget { - const ScanPage(); + const ScanPage({required this.offstage, required this.navigatorKey}); + + final bool offstage; + final GlobalKey navigatorKey; @override State createState() => _ScanPageState(); @@ -39,9 +42,31 @@ class _ScanPageState extends State { if (_model == null) { return const Center(child: CircularProgressIndicator()); } + return ChangeNotifierProvider( create: (BuildContext context) => _model!, - child: ContinuousScanPage(), + child: Navigator( + key: widget.navigatorKey, + onGenerateRoute: (RouteSettings routeSettings) { + return MaterialPageRoute( + builder: (BuildContext context) => _buildChild(), + ); + }, + ), ); } + + //This has to be build inside of the ChangeNotifierProvider to prevent the model to be disposed. + Widget _buildChild() { + //Don't build Scanner (+activate camera) when not on the Scan Tab + if (widget.offstage) { + _model?.stopQRView(); + return const Center( + child: Text( + "This shouldn't be visible since only build when offstage, when you see this page send a email to contact@openfoodfacts.org", + )); + } else { + return const ContinuousScanPage(); + } + } } diff --git a/packages/smooth_app/lib/pages/smooth_bottom_navigation_bar.dart b/packages/smooth_app/lib/pages/smooth_bottom_navigation_bar.dart deleted file mode 100644 index d28492f0312..00000000000 --- a/packages/smooth_app/lib/pages/smooth_bottom_navigation_bar.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:smooth_app/pages/history_page.dart'; -import 'package:smooth_app/pages/scan/scan_page.dart'; -import 'package:smooth_app/pages/user_preferences_page.dart'; - -class _Page { - const _Page({required this.name, required this.icon, required this.body}); - final String name; - final IconData icon; - final Widget body; -} - -enum SmoothBottomNavigationTab { - Profile, - Scan, - History, -} - -class SmoothBottomNavigationBar extends StatelessWidget { - const SmoothBottomNavigationBar({ - this.tab = _defaultTab, - }); - - final SmoothBottomNavigationTab tab; - - static const SmoothBottomNavigationTab _defaultTab = - SmoothBottomNavigationTab.Scan; - - static const List _tabs = - [ - SmoothBottomNavigationTab.Profile, - SmoothBottomNavigationTab.Scan, - SmoothBottomNavigationTab.History, - ]; - - static const Map _pages = - { - SmoothBottomNavigationTab.Profile: _Page( - name: 'Profile', // TODO(monsieurtanuki): translate - icon: Icons.account_circle, - body: UserPreferencesPage(), - ), - SmoothBottomNavigationTab.Scan: _Page( - name: 'Scan or Search', - icon: Icons.search, - body: ScanPage(), - ), - SmoothBottomNavigationTab.History: _Page( - name: 'History', - icon: Icons.history, - body: HistoryPage(), - ), - }; - - static Widget getDefaultPage() => _getTabPage(_defaultTab); - - static Widget _getTabPage(final SmoothBottomNavigationTab tab) => - _pages[tab]!.body; - - @override - Widget build(BuildContext context) => BottomNavigationBar( - showSelectedLabels: false, - showUnselectedLabels: false, - selectedItemColor: Colors.white, - backgroundColor: Theme.of(context).appBarTheme.backgroundColor, - currentIndex: _tabs.indexOf(tab), - onTap: (final int index) async => Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => _getTabPage(_tabs[index]), - ), - ), - items: [ - _buildItem(_pages[_tabs[0]]!), - _buildItem(_pages[_tabs[1]]!), - _buildItem(_pages[_tabs[2]]!), - ], - ); - - BottomNavigationBarItem _buildItem(final _Page page) => - BottomNavigationBarItem( - icon: Icon(page.icon, size: 28), - label: page.name, - ); -} diff --git a/packages/smooth_app/lib/pages/user_preferences_page.dart b/packages/smooth_app/lib/pages/user_preferences_page.dart index c0ef2c9a64f..744d842a747 100644 --- a/packages/smooth_app/lib/pages/user_preferences_page.dart +++ b/packages/smooth_app/lib/pages/user_preferences_page.dart @@ -6,7 +6,6 @@ import 'package:provider/provider.dart'; import 'package:smooth_app/data_models/product_preferences.dart'; import 'package:smooth_app/data_models/user_preferences.dart'; import 'package:smooth_app/pages/settings_page.dart'; -import 'package:smooth_app/pages/smooth_bottom_navigation_bar.dart'; import 'package:smooth_app/themes/smooth_theme.dart'; import 'package:smooth_app/widgets/attribute_button.dart'; @@ -36,9 +35,6 @@ class UserPreferencesPage extends StatelessWidget { final List orderedImportantAttributeIds = productPreferences.getOrderedImportantAttributeIds(); return Scaffold( - bottomNavigationBar: const SmoothBottomNavigationBar( - tab: SmoothBottomNavigationTab.Profile, - ), appBar: AppBar( title: Text(appLocalizations.myPreferences), actions: [ diff --git a/packages/smooth_app/lib/widgets/tab_navigator.dart b/packages/smooth_app/lib/widgets/tab_navigator.dart new file mode 100644 index 00000000000..56cbeea3bc3 --- /dev/null +++ b/packages/smooth_app/lib/widgets/tab_navigator.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:smooth_app/pages/history_page.dart'; +import 'package:smooth_app/pages/page_manager.dart'; +import 'package:smooth_app/pages/scan/scan_page.dart'; +import 'package:smooth_app/pages/user_preferences_page.dart'; + +class TabNavigator extends StatelessWidget { + const TabNavigator({ + required this.navigatorKey, + required this.tabItem, + required this.offstage, + }); + final GlobalKey navigatorKey; + final BottomNavigationTab tabItem; + final bool offstage; + + @override + Widget build(BuildContext context) { + late final Widget child; + + if (tabItem == BottomNavigationTab.Profile) { + child = const UserPreferencesPage(); + } else if (tabItem == BottomNavigationTab.History) { + child = const HistoryPage(); + } else if (tabItem == BottomNavigationTab.Scan) { + // The ScanPage doesn't use the here build Navigator, so that it can update when its offstage status changes + return ScanPage(offstage: offstage, navigatorKey: navigatorKey); + } + + return Navigator( + key: navigatorKey, + onGenerateRoute: (RouteSettings routeSettings) { + return MaterialPageRoute( + builder: (BuildContext context) => child); + }, + ); + } +} diff --git a/packages/smooth_app/test/pages/goldens/user_preferences_page-blue-dark.png b/packages/smooth_app/test/pages/goldens/user_preferences_page-blue-dark.png index 6bb1d6d701bd6f9ec6f3406bbd1c3f931bff27f7..9661c1764db420bee84006d93b6f008d531a46f7 100644 GIT binary patch literal 4547 zcmeHLX;70_6n#n1phCeiZiq;=Eu~e0%ShY+6{TdrSVdtVK-4MR||9afUx0 zKxiV()p>=-?hI)|-XR}P?9EpT1ap%r+UX(osf#WcjQ`>k&W3WZf6OE26M|jS8cK30d9wUzM@e*j48}(ou#WEUl!N~`LKFs_;&VvcQjtuVYgx6%vNP{cXu}p zgOFjriv(WE6{djB=?H?Kw-^{AWIEdkA;we;7NHx4rU+TBVF1Q?SObI(O#JxLT^u}Y zMp+iy`(mw7@kS|=wPu(^87T?21cJEnHn2eqx3{;Ci4fWu!MFWOQdF5#ICCD}v7-3m zq1k^z^>M-lSAb12B)TM-&ZYu@i&UEk&XP z@?~9+4l2r5!+@O&;Nn6i$%@UxaqJ!u#@A^ORrZ`;slL4?d4V@$=); zmIFyd69EPSbo%Irvsu=jUFsh=CC z#`blc*l#k^GESI`H@Q<;xq44e`_agZ1|4qEO zmcN})z3_x5*RLQGN)9+fXZ#WGpR)OUyfJtJ_tu;rCxH1p(U^eHgBtLumxFqsN8O)S z%k@vsA+vAt`VEGp#lW;wTzp?c497ZbY(jt2{u(ocy7r;P4|w8)dO9}1iGfhgHM2RR zj+;RoX-(|pOJ+OJmN$jIHDeq$BC8>!mJa?btyU@f1lH`h^&rPSaUy}ohCTA|Jn*~?! z^oB~(DJm-|E)Klx4EW1$$Ese-6IG8x%TzVggN`^j+*+2yPWYCc@Or1>`9&_t=G?$Q z&;2$yA8;u>6gNPgYb%ypvZ654VMVwlZx`s}J7*N1$b1iXf&YnjorIo)jt;RV6w zX!|OL#f}2+f=q-^ev=+?va5i=%)Zb&DRyZ)4NxA+#orHlj+&aNR_iYwcH47jEf;~)9`7(i>Q2|$@ zt#aLnf(VP`)dhQ#cN6NRg>nc_Q>*>nT!^RADKLnY&wFVwVfgWDP#(T#eNZRT-f>C8 zCC%PUW4K*Wph%OBcT;pTtv05rMSLnN`-$fZDU*jm zQ|3%Q{HQNp8q_=AeZ#b>Jh>4h=yVb8tV}uIXq7Ywy5cn5h|G)u-HVhpy`>7poCZm|7E?F1CorH8tikR;U$Mf@$H zRi)7A*Zv?!K;@_OkYR9Z!b1N?iIKEC5`a<8q4zO}82u0x#mM~j^uKJX=vcP&8B6tU0NNw!rQR@)FzQUL@ZOwF+HpNtpLq3^PRQPw8(-OYY|nIf&O4omo$&1T UbQ+1lg1HU3u3qbWX(cu3Ph=030ssI2 literal 5286 zcmc&&dpOkT9)DdjN|ClA=3r}2Jx>(TMKVJ-)gxnf?PNqu=#cAda>=+%x5X)R9;?F2 z?y;Hrjf`>&P2>5zn6E4<7IQe(!wW&-e5Fe7@f| z;cMKk1@rXg0RR^4*}dai091?sP$sCW!Iku|6D0UYDdgK-+dy6&x*PtZ8nSKAck1vb zO5HmifW=?$*|GJzh@X1-Ht|1ol-IP9ISEOgM3Tw2#oDLhtExQn_p92k@eEMaZno1d zVR7tCiWcwOn5n*$M!d5!wb4a&4SH)~{$iKKYAdoIB&9i>lC%$s(H2`IF@m&A;p%E^ zeTdjhUz##_Kd39OT$H{P0NV;Xo(haaxSaxjuTxe9AYR)DuAc=;0C=jZ1F&#~EdUf< zC1n8as4oP-$nN8hvI5C)8^Ro)`b*bJS|a^L(K)QcjNu;IzynjJ2@%OCF_CD%J#Nk& zYz}e6ym_9Y@l@DbB1l5ryHRW7Olc0|1%~#u{dTN)NQa8cL=Yr9JNtC+n{H|rr?s`U zO6vOSp;-A#ALhnE+I|t;13Zi9oOdpytBy?;GfJ+w==Jf#1#FUK0Qu~g-x7BlUzT~S zT)CpP^`OJbl#JoV#Kgohxs<1W$#-MmL-9Na0J{3OGdjW^Hf__u^i$|83ViEU2^0R=r8K?htz2DQ zS;cV>YtG44Ml-J4>3!Mfs=@oa=E%YgUqLWeb}HhfOfTM88QHdT;6--wKBgde_@dXc zM7z*|9(HjJWO31>2ui<9t~X!J!4PsP7dYpCTB`Q8+x(ydfG_v5{e_;9HD?UJCU9 zWAp-_dJg=UKy}Q@LlF27JER`t3~BEawN!!Ivs2}y)+(E}?4G-)4cxMcjG35(!K|Pj+GK|VMh#l!ekm0s7rajeK zHUWp0D%5UObCEx{XMpPwOg@VonWF)K`l64Mb^WMU#IEdbSi8_X^Ll8VtG;PhIs#Lk zlMJ!hcgmPB9hxg>6UlJ*)C7RIAVL`iF{OD)N|-2b_l%4Twsi$nA%+_O7&NLu4$(1k~>plD^{E<_%{v!S!r`onJ?TMmgvB7M!{ z@y0o0T)o8P(|-Rxf9)tRMQ^)^?v2qEzU%#lOYr-AnCFc=C2yXKNQXwUA$+5k$%mlF!lw{moJv{vvl{jsdWGRqKDJe>96 z$7?_A0|Pd`nTPbedPVBoKzki?)vBcn1xOaK8_?9$^dpnYd)E=PTTB7SBf`?z`f*+U z#9+0zq+11=(0>`BIa)IV3*C&MJ-+d*C$>*0_~%yJCuC*v>K~^T%fTS5vohdNULDqWQcC&%aRkIA5V7V`)Kavso;+MG2mae2NqALg##Q;ND z?g$o-cg}N5BWnQ){!blko;Ee57h~XXR4Xjw>zm5F%{Tu;h#(Uj zG{u2Zd(cexNnIU|=VfyFt%Fv?{y7EQo#5t-4}IsMIINO++{u$Cn_60e$O`{iHhFbf z;Wx;VEn%81)!w$|Q>DBN&S!!3u;s~}JG~bsmD{)F!!Je|wLkUAx~Ah1_5Cm8(Y_kP z(AKKcr$uuMg%39gawT2$rbp|8net-yV;!mE%PEf7t8h4g%Ual>pIc(bvNZ=2jB(8!&Va=IsVKMT!}DRjn~r-gX>iKzI>jD-)HAZ#Oj(UhYhX({SuBvg@}d zbWZK_IO)S9l6oP$Zpl2xuQ7~ugM-QO1j?cQyZXUTHVL9ESm`hxu0Nf@Y1mP5qclby ziILP=SkTEGsm?vuYYhB}%g<*57bm7%>M9>VsWjsLMXF0zFKO$I82BZ?a;Cn~N8qn7_X%CXX9Bm_Dy zyfNb3@isq9Rwd4HW5z`PRjP_J5YTdy>o^?FAD1t4Kl5H~@|vB>R7SL#@t4}|C5J?i PfMCx~+>X3$#1sDl_XSN( diff --git a/packages/smooth_app/test/pages/goldens/user_preferences_page-blue-light.png b/packages/smooth_app/test/pages/goldens/user_preferences_page-blue-light.png index bb5c90f514e6c895168fbfca745165b2d2ce3c29..75e5803b649ef12a15e831c589563cefded0c6e3 100644 GIT binary patch literal 4570 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 UbQ+1lg1HU3u3qbWX(cu3Ph=030ssI2 literal 5286 zcmc&&dpOkT9)DdjN|ClA=3r}2Jx>(TMKVJ-)gxnf?PNqu=#cAda>=+%x5X)R9;?F2 z?y;Hrjf`>&P2>5zn6E4<7IQe(!wW&-e5Fe7@f| z;cMKk1@rXg0RR^4*}dai091?sP$sCW!Iku|6D0UYDdgK-+dy6&x*PtZ8nSKAck1vb zO5HmifW=?$*|GJzh@X1-Ht|1ol-IP9ISEOgM3Tw2#oDLhtExQn_p92k@eEMaZno1d zVR7tCiWcwOn5n*$M!d5!wb4a&4SH)~{$iKKYAdoIB&9i>lC%$s(H2`IF@m&A;p%E^ zeTdjhUz##_Kd39OT$H{P0NV;Xo(haaxSaxjuTxe9AYR)DuAc=;0C=jZ1F&#~EdUf< zC1n8as4oP-$nN8hvI5C)8^Ro)`b*bJS|a^L(K)QcjNu;IzynjJ2@%OCF_CD%J#Nk& zYz}e6ym_9Y@l@DbB1l5ryHRW7Olc0|1%~#u{dTN)NQa8cL=Yr9JNtC+n{H|rr?s`U zO6vOSp;-A#ALhnE+I|t;13Zi9oOdpytBy?;GfJ+w==Jf#1#FUK0Qu~g-x7BlUzT~S zT)CpP^`OJbl#JoV#Kgohxs<1W$#-MmL-9Na0J{3OGdjW^Hf__u^i$|83ViEU2^0R=r8K?htz2DQ zS;cV>YtG44Ml-J4>3!Mfs=@oa=E%YgUqLWeb}HhfOfTM88QHdT;6--wKBgde_@dXc zM7z*|9(HjJWO31>2ui<9t~X!J!4PsP7dYpCTB`Q8+x(ydfG_v5{e_;9HD?UJCU9 zWAp-_dJg=UKy}Q@LlF27JER`t3~BEawN!!Ivs2}y)+(E}?4G-)4cxMcjG35(!K|Pj+GK|VMh#l!ekm0s7rajeK zHUWp0D%5UObCEx{XMpPwOg@VonWF)K`l64Mb^WMU#IEdbSi8_X^Ll8VtG;PhIs#Lk zlMJ!hcgmPB9hxg>6UlJ*)C7RIAVL`iF{OD)N|-2b_l%4Twsi$nA%+_O7&NLu4$(1k~>plD^{E<_%{v!S!r`onJ?TMmgvB7M!{ z@y0o0T)o8P(|-Rxf9)tRMQ^)^?v2qEzU%#lOYr-AnCFc=C2yXKNQXwUA$+5k$%mlF!lw{moJv{vvl{jsdWGRqKDJe>96 z$7?_A0|Pd`nTPbedPVBoKzki?)vBcn1xOaK8_?9$^dpnYd)E=PTTB7SBf`?z`f*+U z#9+0zq+11=(0>`BIa)IV3*C&MJ-+d*C$>*0_~%yJCuC*v>K~^T%fTS5vohdNULDqWQcC&%aRkIA5V7V`)Kavso;+MG2mae2NqALg##Q;ND z?g$o-cg}N5BWnQ){!blko;Ee57h~XXR4Xjw>zm5F%{Tu;h#(Uj zG{u2Zd(cexNnIU|=VfyFt%Fv?{y7EQo#5t-4}IsMIINO++{u$Cn_60e$O`{iHhFbf z;Wx;VEn%81)!w$|Q>DBN&S!!3u;s~}JG~bsmD{)F!!Je|wLkUAx~Ah1_5Cm8(Y_kP z(AKKcr$uuMg%39gawT2$rbp|8net-yV;!mE%PEf7t8h4g%Ual>pIc(bvNZ=2jB(8!&Va=IsVKMT!}DRjn~r-gX>iKzI>jD-)HAZ#Oj(UhYhX({SuBvg@}d zbWZK_IO)S9l6oP$Zpl2xuQ7~ugM-QO1j?cQyZXUTHVL9ESm`hxu0Nf@Y1mP5qclby ziILP=SkTEGsm?vuYYhB}%g<*57bm7%>M9>VsWjsLMXF0zFKO$I82BZ?a;Cn~N8qn7_X%CXX9Bm_Dy zyfNb3@isq9Rwd4HW5z`PRjP_J5YTdy>o^?FAD1t4Kl5H~@|vB>R7SL#@t4}|C5J?i PfMCx~+>X3$#1sDl_XSN( diff --git a/packages/smooth_app/test/pages/goldens/user_preferences_page-brown-light.png b/packages/smooth_app/test/pages/goldens/user_preferences_page-brown-light.png index 37f298db813c8bcd7802ca31bc15fcbb620cf26a..330e0122bcdfe24502549a6f1a5efde794c7d59a 100644 GIT binary patch literal 4568 zcmcgvYfzJC7JfqzP(fs=3UW(pXO;B=QdXo8h=Ot%3l=P*XhonPmv{lCAVjjbt57J* zjg%1{DCGT z4}QsdVQB~%rFyw<*_Cin&|O&;7J+MeA$)XIa`@QOzcA9XKRDN<7f?31DZR4b(7_V+ z75%{K4DWTvKT6Z_U$fjAkC*GFM&1o*-sZG;eG|?hoAlrk>+-XLKKrh%dY%N9QyfRe z&QB~Fi=9Xoo~gcPgYml01y!xEfmlh65mAN^Ox9V1LnuUB522;2NC;6)a9RlcOK&Mc zgzZ##VWXBdLTN?>*pm77S4*!F;oYXbg5H@IG-r&Hl058JB)TOz*Z_N|tHoot8{(`v zSCs${yss`Hl6wE>ym)}3%oz9yU*8Rr<> z3UgveSg8vT3YvV<$MPMYmPuTTq6J?sSUiWo)a207X$dihiU)XKNBKVUDxdD5!g^y5 z)tOeAw*jHfk4Xse7j~cOr*VzrZyL(>-vMZ5p^A%*mh^2C7$MYnY*c412&oA~4epNC zw0)hHz>vA_5r{^Wozfv$y##8E@0oyyxZayG%>q(W2=))}TDl|$zUrxCiQ+7YCwF;U z_y7AzS(75$Fp~$e`F*E!!8yV=lOX% z5{cvH9MkDI*xmH4nUm{XPS-J)%Q?+czaY{#YXjBS;XIOR@#>ve!SH|*xPQm&J|}v79A*TGVKiFz`Gg+83ekKhO&EXplcF=NfO;Ix;;Z;2I;e;cuEze1VIH zfDhJ5rDlxP$t}wJo6NE;D1zbMD@1_Da=1h!8k4goysc@@qLH}%M?;ah77#(NtW9|{ z-+vmDPmdK8_iV5#R8(Sh?8&{Srp`Jv|0ErsT&@6w-&ok4h`pR#)1Pw0t@Tx{|5q6b zdBaci)TL1RqG#$;8h-eLLO*qdQ!DBx(<>c#yPIe7=B0&>?YG?Gb8 zG*m;s9#F>p1g<}%Z%H8Y{`oM>PP`E`QSW<&Uca{_fr|u>Y%2Inu<-N+pUaHZP}?N! z2U+1&d*I35imurw`~D}x@5=7Y&1KKf&FoHgsZf-~b~ozCq3V54i=gjIUd#!yw;bwb z_4q{hV~-ee@ogMx{P5Eunf#IjFy(L73=9>rl}xEz+o4Zs45?g0<}IPL1k}*imOZ&9 z(MM?W%G0`@Jmyz+`T7)C+G^2*A;Y;DxXNvjQHCZgcDR4i4_oqQS+FJGZk9Zc34kYi zW?8T$yh|Wd!hJJKp9yfG4#gaW5>VTg5}fvK$W!p!cxXOwu^;Om-|rwc3gTg8sGc+* zX??>LB6Q^DC7OZpI`k^J*t=QX2wdmQW4c*Y<lJ__%f^Ie*6=-K=1l zN!3`;nhsYz?<^X1FYSmFEQFbf6#wS}c9d_zN5kVa5>SkziV5g}nh4)D#lZw*8!wuw zRUBqw037$n?nwf$GS6rhvvP`r;v{vQaI>mpP^XRWP=JI>V z(JtFUpWD(Cp;NUnG8rBvnE%=jTFb6?4VRzl4s@~M$>m@&_m#t~N;ADLJ3IW78lSN- z5!cwnsCWh9KUu|BW)zsqWkgmpHc-?KU1NY^_xVNTx~YS8WY*f42w^k6l(Y%3>G zJovdaEvIaE#mOuzQ|?I1i4_g7V+CABVKE-v5MIKyPf#44oOn}?7>0o%`}q12@5gPQ zxd`5W>}69c2f6}3Z=HL4PUp|n3vHtQ4$hNx+JW$!4Yt2I>MEM3q`lNsO1&5IIhSc6 zJzsu&|L%aYF9fC_SN1xF*-UbCn4PKA1fgJU&5T?rS_SM<$zSX23w;#!5o`!e!Va(E z8cW(w*3rS%sl=b=D>celWI&XtP+r@WFef(m-<;LSc~pb~8T#-Umyl>#tpSXDZ7Sa! z^Hq-5X+9r5h+m$uEs_^M7|Luvv42l_03!nYwavtt#AU8O_b_ZQ6hzrb=^|+w=@fZW z0SMDShK}zA*C2fXAXPoTF45i|R`+EW3{*RF=V2qtqB>tHp;i`fu-U;t?^h`V#_ct$ ro!LVHm>Rm7G_719+60q<`^heC literal 5431 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 UbQ+1lg1HU3u3qbWX(cu3Ph=030ssI2 literal 5286 zcmc&&dpOkT9)DdjN|ClA=3r}2Jx>(TMKVJ-)gxnf?PNqu=#cAda>=+%x5X)R9;?F2 z?y;Hrjf`>&P2>5zn6E4<7IQe(!wW&-e5Fe7@f| z;cMKk1@rXg0RR^4*}dai091?sP$sCW!Iku|6D0UYDdgK-+dy6&x*PtZ8nSKAck1vb zO5HmifW=?$*|GJzh@X1-Ht|1ol-IP9ISEOgM3Tw2#oDLhtExQn_p92k@eEMaZno1d zVR7tCiWcwOn5n*$M!d5!wb4a&4SH)~{$iKKYAdoIB&9i>lC%$s(H2`IF@m&A;p%E^ zeTdjhUz##_Kd39OT$H{P0NV;Xo(haaxSaxjuTxe9AYR)DuAc=;0C=jZ1F&#~EdUf< zC1n8as4oP-$nN8hvI5C)8^Ro)`b*bJS|a^L(K)QcjNu;IzynjJ2@%OCF_CD%J#Nk& zYz}e6ym_9Y@l@DbB1l5ryHRW7Olc0|1%~#u{dTN)NQa8cL=Yr9JNtC+n{H|rr?s`U zO6vOSp;-A#ALhnE+I|t;13Zi9oOdpytBy?;GfJ+w==Jf#1#FUK0Qu~g-x7BlUzT~S zT)CpP^`OJbl#JoV#Kgohxs<1W$#-MmL-9Na0J{3OGdjW^Hf__u^i$|83ViEU2^0R=r8K?htz2DQ zS;cV>YtG44Ml-J4>3!Mfs=@oa=E%YgUqLWeb}HhfOfTM88QHdT;6--wKBgde_@dXc zM7z*|9(HjJWO31>2ui<9t~X!J!4PsP7dYpCTB`Q8+x(ydfG_v5{e_;9HD?UJCU9 zWAp-_dJg=UKy}Q@LlF27JER`t3~BEawN!!Ivs2}y)+(E}?4G-)4cxMcjG35(!K|Pj+GK|VMh#l!ekm0s7rajeK zHUWp0D%5UObCEx{XMpPwOg@VonWF)K`l64Mb^WMU#IEdbSi8_X^Ll8VtG;PhIs#Lk zlMJ!hcgmPB9hxg>6UlJ*)C7RIAVL`iF{OD)N|-2b_l%4Twsi$nA%+_O7&NLu4$(1k~>plD^{E<_%{v!S!r`onJ?TMmgvB7M!{ z@y0o0T)o8P(|-Rxf9)tRMQ^)^?v2qEzU%#lOYr-AnCFc=C2yXKNQXwUA$+5k$%mlF!lw{moJv{vvl{jsdWGRqKDJe>96 z$7?_A0|Pd`nTPbedPVBoKzki?)vBcn1xOaK8_?9$^dpnYd)E=PTTB7SBf`?z`f*+U z#9+0zq+11=(0>`BIa)IV3*C&MJ-+d*C$>*0_~%yJCuC*v>K~^T%fTS5vohdNULDqWQcC&%aRkIA5V7V`)Kavso;+MG2mae2NqALg##Q;ND z?g$o-cg}N5BWnQ){!blko;Ee57h~XXR4Xjw>zm5F%{Tu;h#(Uj zG{u2Zd(cexNnIU|=VfyFt%Fv?{y7EQo#5t-4}IsMIINO++{u$Cn_60e$O`{iHhFbf z;Wx;VEn%81)!w$|Q>DBN&S!!3u;s~}JG~bsmD{)F!!Je|wLkUAx~Ah1_5Cm8(Y_kP z(AKKcr$uuMg%39gawT2$rbp|8net-yV;!mE%PEf7t8h4g%Ual>pIc(bvNZ=2jB(8!&Va=IsVKMT!}DRjn~r-gX>iKzI>jD-)HAZ#Oj(UhYhX({SuBvg@}d zbWZK_IO)S9l6oP$Zpl2xuQ7~ugM-QO1j?cQyZXUTHVL9ESm`hxu0Nf@Y1mP5qclby ziILP=SkTEGsm?vuYYhB}%g<*57bm7%>M9>VsWjsLMXF0zFKO$I82BZ?a;Cn~N8qn7_X%CXX9Bm_Dy zyfNb3@isq9Rwd4HW5z`PRjP_J5YTdy>o^?FAD1t4Kl5H~@|vB>R7SL#@t4}|C5J?i PfMCx~+>X3$#1sDl_XSN( diff --git a/packages/smooth_app/test/pages/goldens/user_preferences_page-green-light.png b/packages/smooth_app/test/pages/goldens/user_preferences_page-green-light.png index f7a787eeec27353cc3ef37f2b288f472c1891cbc..4fa8829ad5b2ab19d17b561d3c2b9f2a0c8bf19a 100644 GIT binary patch literal 4568 zcmeHLc{tQ*9Dj$2Ng*Am#I&}RS}C=Tgh`G_DW%l9l31*x6lu({v}!x>gd$h%hFaxp z#yE0Rj)+-DCL>2}ITlf)8D>ng@6h(lQ)sYH`_Ill^Stl#&hPzxzn|m#{mm~scbG~_ zERg^JNSbXmvI0O94S{bLQvmXO$lx`Qf14xr0Hj%j4|NVo$YT?6YMJF(SG7F}NaLK@2s;mf6bl zKbSVu!R=?hNb+|m{FWb-L#iJ9(pD5f0&qqK4FKjmKmg!`L;)~Q83O=O9w7q21JpbK z&?ZE9cHP8Bqm!BNfc&|{!VJpA&XHH%sjCBOBIu{N{GTY+r_u<4E2uXYp*b0Ci{R~O zmrgBojsvHCmEYWsMr?XR!ax+0Tcp%*r=NgFXnXN=2m?lKybIZ`Vh6I~4{+>CL;z83 zQqT=x({J?hyua4h`0V=Vns7@xvQM8G7G%7-f6~xDwAUyj&dDg^2`FD<5 zFm8BLcum{~Qji*`PpC2O^^A%{(X7-QJaeyGvtQRCa@}#e08WlKEdi!%dT0Q+HIpsA ziMSR9mnOxdBArC^>xT^P&jFy}ZHH3pDkW}DKaWM~q2luk81C;}p9g%*)pt@Ap_;sR z6ecpXcrcbqO8~%U1+pTvb%?9(VcCuv8I~jbu7?2a=9Fp4I{7Bw%j6q%(+lme_v8u? zFbSsuL{lv*S-lZn2EvZL9yZQpJv7*+nzV^Em{ZxkJH^@$6~655@O_8J{1Xn{B`MOC zJ^e+y*60`UiO6nn8?GLqy^KjWSkU%dLKYH((yW9VG7gM~4(_!gy160Q|?n$um^`#DwV z><~_3i=`(_UMg<2jWDhhHll-u$?q!_%0r@ExsT`($8k zj!dr@lX!CSVm7hVX&0Tmj2(UZnBBg;sn&gqp`bR1Nr#g?tDH3!v*iJBMB0|X45!@A z>l+l;`o&f!GfxF;jG^F&5pg!nZn_0`vc^zw#LU=;uh2!yyz~?KP5^#ej`6!=yM~W1 zv34fqx^hjrE6=M-nM|fOL-x#zAH)&%W8nniqcFWyXei9eX<%0sM!pT>>$#g0luJmb~QjVjdk7`Q%LN3DI}*;1ozg zAF4HLV_hbhLX^u(W}vTebNNzsbi;9Dyw}^FvNr)uOqMtpSovNL$W=D!wC}ljnSu6X zE*AkdofXb>$8FD}K*BMam>Em$B&n?ZN?#omXD&eN9$0hv!_s$J#yaLDUs4?7!Zl?P z2=(S6@Khlu&=Cbd#K}p97xq`R8C6lopfU?id zK{JqF2ft<0m7(YT*)BPqcP@d<1=`zxQ+j~_U@QC*#&}SD`wb z))Xn2#Q#IWWbgbRh6JRE^Tv4oguOEgPEtQ=rt1A9vp=0iQUPGkOMZiz@;?azto||$ aihwBF+>xHSjQSKdDPU&2!zj(rG3alhDU)>o literal 5448 zcmeHLX;f2Z8va08YE4mj@utmgH7@MbAx zm&-wYc*W}noB`mS5trRN55{H>bdt{881t(emS@KWVz1yz8T5$VpKf{gfz!ICDx)tr zH#VQOI8)kXXsAPP_}k9Ecz^Y0Z1qQETCH(bjdOoEnd^h(n{saK1l z*M2@I?DCV-WHc$KVORX%#yqw_B?#gYdpf*N3k)jmG>Z9P2(1@}?%;9M!);8WUzn9u z?Dgo1cCn!)09#W?>JD*160KW&v`?yfnrT*GpnjQZRQdUKzXAEtY8aDWJoqbPbSYmG zTYp1bl(^=ES|*O&iyvbNyEsq7+hyb=O{{~4&SO=s!i|l8Wh!+(#TeUJ|Gh&V*CA@= zDn@cB+c7YfCqYQ}&*$S3mw@Dz+3x$YMhdNRw>2Enp|D7N zB7u-)-tE+;H?=H`f{(%ND5n8ow)dJ||lBY7pF%VUaRq}OHs;`+*ENZ%MnX0eX zX-WdLeSd#Rz@{~S9~~XNW(MyIf#Hei#|y=K)0vG1&7iZ-h!Om+_pQ29c^GzAfh%C2)N_o^n)dR9frUwL6sJ$ zi**vpV$_l`E-}S+C`+zN>pwcG*{6RX%OB1=s|+J8>0P!>tu84U3yaj-B+(net+LlHn zXU?XNi-L%;`Kk$bOEP6}xGffmbApG zt}fyV-IP6eiR;%jhc~QWf1C#;zwq2?y&wEH6$}gv?7_o(FRg{GKUZpDru{R|)%Doh z+mmrvx+~c}$D`%W)OZVpK;Uw@91f?Ix)OkU8( zdU}pPWvLR~)kJZTTJE2TQhDu9o`m4Ars+%s329H5)~wOg)YN7Jfunxg+@^PI>O^ep zV-1jQZT4M+SOz{aShF%UEwPsVu_6K@&NTcRr|TiYy^thi)f%0CLr*fpIdco`5?t+Z zM+dXo|5k5Zc75d5!jzO0mJpH#r+5ggU9QXF@p!#;aP5s%Z??9Z5L0-2IM&zM*||t+ zk>TRTXdz#YHdDvtF_HGp&LViVQ6O@3U!6*60t@LMANNXzU_SC@V48LC%PeEBFL^|E zbab@00OAWl{=FBGC=9m*RQ0d=e=c7q&#%knAt{1H$Hat&hBDFh05oO*dUbuh%Ya;7 zmb)E?D?$(=yAxAT0yScTnHdH$n!R+fE+D^;dXms4L&-rw3{(`mgM}dZA!EaNCm`)p zC?)7}xQ{DsUd;vagxklpAH<2!(4 zpT$IaMhsq)-YY)mf97502zuZ|Dai~}70oW$LVc1o05b{M`D@#@8K!V8>D62QAF6ss z_C6#<^ywlHG*{$i@zUJ%|JR2u$mLJ02gzR=uebo&d!NI^^G2!Y?y>6#62Tp?lWq-q zA-IW9hpP309H65!T(r#26OFK00=OWTJWnq+R{(#{p)Hn^S~MEXcFl4lNwIf*|M9({-T1WiJ}}uc>JEkC1O9 zl0ey7XqoZQ1SIlFsi~=cY}g@eta+v9n5Un9b$YiL;D>{S+cj<0{lx;vwFT7X8 zR8D=sXAj1K&Iq2)gG@Qa#5|a1M4U6gGZyv~2^IpX@(x}>QiMbe1G7vF1_9yZj!V9O~k z{@nOV8bpj`IXt8YT~@Vhyxo}i52}vGnSZ_3IkY