From eaf538bf451e9a1ec013c526ec6b9b9fc2c0e53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20M=C3=BCllner?= Date: Tue, 24 Feb 2026 16:38:35 +0100 Subject: [PATCH 01/14] feat: implement collections feature with screens for viewing, managing, and creating collections --- .gitignore | 1 + lib/screens/collections_screen.dart | 409 +++++++++++++++++++++ lib/screens/garage_screen.dart | 20 +- lib/screens/home_shell.dart | 16 +- lib/screens/manage_collection_screen.dart | 425 ++++++++++++++++++++++ lib/screens/scan_tab.dart | 131 ++++++- lib/services/collection_service.dart | 236 ++++++++++++ 7 files changed, 1213 insertions(+), 25 deletions(-) create mode 100644 lib/screens/collections_screen.dart create mode 100644 lib/screens/manage_collection_screen.dart create mode 100644 lib/services/collection_service.dart diff --git a/.gitignore b/.gitignore index f815956..84c1439 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ app.*.map.json /android/app/release TPB.md +*sql diff --git a/lib/screens/collections_screen.dart b/lib/screens/collections_screen.dart new file mode 100644 index 0000000..ef24d59 --- /dev/null +++ b/lib/screens/collections_screen.dart @@ -0,0 +1,409 @@ +import 'package:flutter/material.dart'; +import '../main.dart'; +import '../services/collection_service.dart'; +import '../theme/app_colors.dart'; +import 'garage_screen.dart'; +import 'manage_collection_screen.dart'; + +/// Lists all collections the current user is a member of. +class CollectionsScreen extends StatefulWidget { + const CollectionsScreen({super.key}); + + @override + State createState() => CollectionsScreenState(); +} + +class CollectionsScreenState extends State { + List _collections = []; + bool _isLoading = true; + String? _error; + + @override + void initState() { + super.initState(); + _load(); + } + + /// Public so other tabs can trigger a refresh. + void refresh() => _load(); + + Future _load() async { + setState(() { + _isLoading = true; + _error = null; + }); + + try { + final list = await CollectionService.getMyCollections(); + if (!mounted) return; + setState(() { + _collections = list; + _isLoading = false; + }); + } catch (e) { + if (!mounted) return; + setState(() { + _error = e.toString(); + _isLoading = false; + }); + } + } + + Future _createCollection() async { + final nameCtrl = TextEditingController(); + final descCtrl = TextEditingController(); + + final result = await showDialog( + context: context, + builder: (_) => AlertDialog( + icon: Container( + padding: const EdgeInsets.all(12), + decoration: const BoxDecoration( + gradient: AppColors.brandGradient, + shape: BoxShape.circle, + ), + child: const Icon(Icons.add, color: Colors.white, size: 28), + ), + title: const Text('New Collection'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: nameCtrl, + autofocus: true, + decoration: const InputDecoration( + labelText: 'Name', + hintText: 'e.g. Hot Wheels, Matchbox…', + ), + ), + const SizedBox(height: 12), + TextField( + controller: descCtrl, + decoration: const InputDecoration( + labelText: 'Description (optional)', + hintText: 'What is this collection for?', + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Cancel'), + ), + ElevatedButton( + onPressed: () { + if (nameCtrl.text.trim().isEmpty) return; + Navigator.pop(context, true); + }, + child: const Text('Create'), + ), + ], + ), + ); + + if (result != true) return; + + try { + await CollectionService.create( + name: nameCtrl.text.trim(), + description: descCtrl.text.trim(), + ); + showGlobalSnackBar('Collection created!'); + _load(); + } catch (e) { + showGlobalSnackBar('Failed: $e', isError: true); + } + } + + void _openCollection(Collection c) { + navigatorKey.currentState!.push( + MaterialPageRoute( + builder: (_) => GarageScreen( + collectionId: c.id, + collectionName: c.name, + isOwner: c.isOwner, + ), + ), + ).then((_) => _load()); // refresh counts when coming back + } + + void _manageCollection(Collection c) { + navigatorKey.currentState!.push( + MaterialPageRoute( + builder: (_) => ManageCollectionScreen(collection: c), + ), + ).then((_) => _load()); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: CustomScrollView( + slivers: [ + // ── Header ── + SliverAppBar( + expandedHeight: 140, + pinned: true, + flexibleSpace: FlexibleSpaceBar( + titlePadding: const EdgeInsets.only(left: 20, bottom: 16), + title: Text( + 'Collections', + style: TextStyle( + fontFamily: 'Poppins', + fontWeight: FontWeight.w700, + fontSize: 22, + color: Colors.white, + shadows: [ + Shadow( + color: Colors.black.withValues(alpha: 0.3), + blurRadius: 4, + ), + ], + ), + ), + background: Container( + decoration: const BoxDecoration( + gradient: AppColors.brandGradient, + ), + child: Align( + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.only(right: 24), + child: Icon( + Icons.collections_bookmark, + size: 72, + color: Colors.white.withValues(alpha: 0.15), + ), + ), + ), + ), + ), + ), + + // ── Content ── + if (_isLoading) + const SliverFillRemaining( + child: Center(child: CircularProgressIndicator()), + ) + else if (_error != null) + SliverFillRemaining( + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.error_outline, + size: 56, color: AppColors.error), + const SizedBox(height: 16), + Text(_error!, + textAlign: TextAlign.center, + style: + const TextStyle(color: AppColors.textSecondary)), + const SizedBox(height: 16), + ElevatedButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh), + label: const Text('Retry'), + ), + ], + ), + ), + ) + else if (_collections.isEmpty) + SliverFillRemaining( + child: Center( + child: Padding( + padding: const EdgeInsets.all(40), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.collections_bookmark_outlined, + size: 64, color: AppColors.textHint), + const SizedBox(height: 16), + const Text( + 'No collections yet', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: AppColors.textSecondary, + ), + ), + const SizedBox(height: 8), + const Text( + 'Create your first collection to start tracking!', + textAlign: TextAlign.center, + style: TextStyle(color: AppColors.textHint), + ), + const SizedBox(height: 24), + ElevatedButton.icon( + onPressed: _createCollection, + icon: const Icon(Icons.add), + label: const Text('Create Collection'), + ), + ], + ), + ), + ), + ) + else + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 100), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final c = _collections[index]; + return _CollectionCard( + collection: c, + onTap: () => _openCollection(c), + onManage: () => _manageCollection(c), + ); + }, + childCount: _collections.length, + ), + ), + ), + ], + ), + floatingActionButton: _collections.isNotEmpty + ? FloatingActionButton( + onPressed: _createCollection, + child: const Icon(Icons.add), + ) + : null, + ); + } +} + +// ── Collection Card ────────────────────────────────────────────────── + +class _CollectionCard extends StatelessWidget { + final Collection collection; + final VoidCallback onTap; + final VoidCallback onManage; + + const _CollectionCard({ + required this.collection, + required this.onTap, + required this.onManage, + }); + + @override + Widget build(BuildContext context) { + final c = collection; + return Card( + margin: const EdgeInsets.only(bottom: 12), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(16), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + // Icon + Container( + width: 56, + height: 56, + decoration: BoxDecoration( + gradient: c.isOwner + ? AppColors.brandGradient + : const LinearGradient( + colors: [AppColors.navy, AppColors.navyLight], + ), + borderRadius: BorderRadius.circular(14), + ), + child: Icon( + c.isOwner + ? Icons.collections_bookmark + : Icons.group, + color: Colors.white, + size: 28, + ), + ), + const SizedBox(width: 16), + + // Info + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + c.name, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 4), + Row( + children: [ + _MiniStat( + icon: Icons.directions_car, + value: '${c.itemCount}'), + const SizedBox(width: 16), + _MiniStat( + icon: Icons.people, + value: '${c.memberCount}'), + const SizedBox(width: 16), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: c.isOwner + ? AppColors.orange.withValues(alpha: 0.15) + : AppColors.navy.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + c.isOwner ? 'Owner' : 'Member', + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: c.isOwner + ? AppColors.orange + : AppColors.navy, + ), + ), + ), + ], + ), + ], + ), + ), + + // Manage button + IconButton( + icon: const Icon(Icons.settings_outlined, + color: AppColors.textHint), + onPressed: onManage, + ), + ], + ), + ), + ), + ); + } +} + +class _MiniStat extends StatelessWidget { + final IconData icon; + final String value; + const _MiniStat({required this.icon, required this.value}); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 14, color: AppColors.textHint), + const SizedBox(width: 4), + Text( + value, + style: const TextStyle( + fontSize: 13, + color: AppColors.textSecondary, + ), + ), + ], + ); + } +} diff --git a/lib/screens/garage_screen.dart b/lib/screens/garage_screen.dart index 6eb2a37..63d2c3e 100644 --- a/lib/screens/garage_screen.dart +++ b/lib/screens/garage_screen.dart @@ -7,9 +7,18 @@ import '../services/storage_service.dart'; import '../theme/app_colors.dart'; import '../widgets/car_card.dart'; -/// The "My Garage" screen — shows the user's collected cars in a grid. +/// The "My Garage" screen — shows a collection's cars in a grid. class GarageScreen extends StatefulWidget { - const GarageScreen({super.key}); + final String collectionId; + final String collectionName; + final bool isOwner; + + const GarageScreen({ + super.key, + required this.collectionId, + required this.collectionName, + this.isOwner = true, + }); @override State createState() => GarageScreenState(); @@ -44,13 +53,10 @@ class GarageScreenState extends State { }); try { - final userId = supabase.auth.currentUser?.id; - if (userId == null) return; - final data = await supabase .from('hotwheels') .select() - .eq('user_id', userId) + .eq('collection_id', widget.collectionId) .order('created_at', ascending: false); if (!mounted) return; @@ -91,7 +97,7 @@ class GarageScreenState extends State { titlePadding: const EdgeInsets.only(left: 20, bottom: 16), title: Text( - 'My Garage', + widget.collectionName, style: TextStyle( fontFamily: 'Poppins', fontWeight: FontWeight.w700, diff --git a/lib/screens/home_shell.dart b/lib/screens/home_shell.dart index d90343c..22d4a2b 100644 --- a/lib/screens/home_shell.dart +++ b/lib/screens/home_shell.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'garage_screen.dart'; +import 'collections_screen.dart'; import 'scan_tab.dart'; import 'profile_screen.dart'; @@ -13,19 +13,19 @@ class HomeShell extends StatefulWidget { class _HomeShellState extends State { int _currentIndex = 0; - final _garageKey = GlobalKey(); + final _collectionsKey = GlobalKey(); late final List _pages = [ - GarageScreen(key: _garageKey), + CollectionsScreen(key: _collectionsKey), const ScanTab(), const ProfileScreen(), ]; void _onTabSelected(int i) { setState(() => _currentIndex = i); - // Refresh garage whenever user switches to it. + // Refresh collections whenever user switches to it. if (i == 0) { - _garageKey.currentState?.refresh(); + _collectionsKey.currentState?.refresh(); } } @@ -41,9 +41,9 @@ class _HomeShellState extends State { onDestinationSelected: _onTabSelected, destinations: const [ NavigationDestination( - icon: Icon(Icons.garage_outlined), - selectedIcon: Icon(Icons.garage), - label: 'Garage', + icon: Icon(Icons.collections_bookmark_outlined), + selectedIcon: Icon(Icons.collections_bookmark), + label: 'Collections', ), NavigationDestination( icon: Icon(Icons.qr_code_scanner_outlined), diff --git a/lib/screens/manage_collection_screen.dart b/lib/screens/manage_collection_screen.dart new file mode 100644 index 0000000..27976be --- /dev/null +++ b/lib/screens/manage_collection_screen.dart @@ -0,0 +1,425 @@ +import 'package:flutter/material.dart'; +import '../main.dart'; +import '../services/collection_service.dart'; +import '../theme/app_colors.dart'; + +/// Screen to manage a collection: rename, invite/remove members, delete. +class ManageCollectionScreen extends StatefulWidget { + final Collection collection; + + const ManageCollectionScreen({super.key, required this.collection}); + + @override + State createState() => + _ManageCollectionScreenState(); +} + +class _ManageCollectionScreenState extends State { + late Collection _collection; + List _members = []; + bool _isLoading = true; + + @override + void initState() { + super.initState(); + _collection = widget.collection; + _loadMembers(); + } + + Future _loadMembers() async { + setState(() => _isLoading = true); + try { + final members = + await CollectionService.getMembers(_collection.id); + if (!mounted) return; + setState(() { + _members = members; + _isLoading = false; + }); + } catch (e) { + if (!mounted) return; + setState(() => _isLoading = false); + showGlobalSnackBar('Failed to load members: $e', isError: true); + } + } + + Future _rename() async { + final ctrl = TextEditingController(text: _collection.name); + final descCtrl = + TextEditingController(text: _collection.description ?? ''); + + final result = await showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('Rename Collection'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: ctrl, + autofocus: true, + decoration: const InputDecoration(labelText: 'Name'), + ), + const SizedBox(height: 12), + TextField( + controller: descCtrl, + decoration: + const InputDecoration(labelText: 'Description (optional)'), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Cancel'), + ), + ElevatedButton( + onPressed: () { + if (ctrl.text.trim().isEmpty) return; + Navigator.pop(context, true); + }, + child: const Text('Save'), + ), + ], + ), + ); + + if (result != true) return; + + try { + await CollectionService.update( + collectionId: _collection.id, + name: ctrl.text.trim(), + description: descCtrl.text.trim(), + ); + setState(() { + _collection = Collection( + id: _collection.id, + name: ctrl.text.trim(), + description: descCtrl.text.trim(), + ownerId: _collection.ownerId, + createdAt: _collection.createdAt, + role: _collection.role, + itemCount: _collection.itemCount, + memberCount: _collection.memberCount, + ); + }); + showGlobalSnackBar('Collection renamed!'); + } catch (e) { + showGlobalSnackBar('Failed: $e', isError: true); + } + } + + Future _inviteMember() async { + final emailCtrl = TextEditingController(); + + final result = await showDialog( + context: context, + builder: (_) => AlertDialog( + icon: Container( + padding: const EdgeInsets.all(12), + decoration: const BoxDecoration( + gradient: AppColors.brandGradient, + shape: BoxShape.circle, + ), + child: const Icon(Icons.person_add, color: Colors.white, size: 28), + ), + title: const Text('Invite Member'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Enter the email address of the person you want to invite. ' + 'They must already have an account.', + style: TextStyle( + fontSize: 13, + color: AppColors.textSecondary, + ), + ), + const SizedBox(height: 16), + TextField( + controller: emailCtrl, + autofocus: true, + keyboardType: TextInputType.emailAddress, + decoration: const InputDecoration( + labelText: 'Email address', + hintText: 'user@example.com', + prefixIcon: Icon(Icons.email_outlined), + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Cancel'), + ), + ElevatedButton( + onPressed: () { + if (emailCtrl.text.trim().isEmpty) return; + Navigator.pop(context, true); + }, + child: const Text('Invite'), + ), + ], + ), + ); + + if (result != true) return; + + try { + await CollectionService.inviteByEmail( + collectionId: _collection.id, + email: emailCtrl.text.trim(), + ); + showGlobalSnackBar('Member invited!'); + _loadMembers(); + } catch (e) { + showGlobalSnackBar('$e', isError: true); + } + } + + Future _removeMember(CollectionMember member) async { + final confirmed = await showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('Remove Member?'), + content: Text( + 'Remove ${member.email} from this collection?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text('Cancel'), + ), + ElevatedButton( + onPressed: () => Navigator.pop(context, true), + style: ElevatedButton.styleFrom(backgroundColor: AppColors.error), + child: const Text('Remove'), + ), + ], + ), + ); + + if (confirmed != true) return; + + try { + await CollectionService.removeMember( + collectionId: _collection.id, + membershipId: member.id, + ); + showGlobalSnackBar('Member removed.'); + _loadMembers(); + } catch (e) { + showGlobalSnackBar('Failed: $e', isError: true); + } + } + + Future _leaveCollection() async { + final confirmed = await showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('Leave Collection?'), + content: Text( + 'You will lose access to "${_collection.name}". ' + 'This action cannot be undone.'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text('Cancel'), + ), + ElevatedButton( + onPressed: () => Navigator.pop(context, true), + style: ElevatedButton.styleFrom(backgroundColor: AppColors.error), + child: const Text('Leave'), + ), + ], + ), + ); + + if (confirmed != true) return; + + try { + await CollectionService.leave(_collection.id); + showGlobalSnackBar('Left "${_collection.name}".'); + if (mounted) Navigator.pop(context); + } catch (e) { + showGlobalSnackBar('Failed: $e', isError: true); + } + } + + Future _deleteCollection() async { + final confirmed = await showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('Delete Collection?'), + content: Text( + 'Delete "${_collection.name}" and ALL its items? ' + 'This cannot be undone.'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text('Cancel'), + ), + ElevatedButton( + onPressed: () => Navigator.pop(context, true), + style: ElevatedButton.styleFrom(backgroundColor: AppColors.error), + child: const Text('Delete Forever'), + ), + ], + ), + ); + + if (confirmed != true) return; + + try { + await CollectionService.delete(_collection.id); + showGlobalSnackBar('Collection deleted.'); + if (mounted) Navigator.pop(context); + } catch (e) { + showGlobalSnackBar('Failed: $e', isError: true); + } + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final currentUserId = supabase.auth.currentUser?.id; + + return Scaffold( + appBar: AppBar( + title: Text(_collection.name), + actions: [ + if (_collection.isOwner) + IconButton( + icon: const Icon(Icons.edit), + tooltip: 'Rename', + onPressed: _rename, + ), + ], + ), + body: ListView( + padding: const EdgeInsets.all(16), + children: [ + // ── Description ── + if (_collection.description != null && + _collection.description!.isNotEmpty) ...[ + Text( + _collection.description!, + style: const TextStyle( + fontSize: 14, color: AppColors.textSecondary), + ), + const SizedBox(height: 16), + ], + + // ── Members section ── + Row( + children: [ + Text( + 'Members', + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + const Spacer(), + if (_collection.isOwner) + TextButton.icon( + onPressed: _inviteMember, + icon: const Icon(Icons.person_add, size: 18), + label: const Text('Invite'), + ), + ], + ), + const SizedBox(height: 8), + + if (_isLoading) + const Center( + child: Padding( + padding: EdgeInsets.all(24), + child: CircularProgressIndicator(), + ), + ) + else + ...List.generate(_members.length, (i) { + final m = _members[i]; + return Card( + margin: const EdgeInsets.only(bottom: 8), + child: ListTile( + leading: CircleAvatar( + backgroundColor: m.isOwner + ? AppColors.orange + : AppColors.navy, + child: Icon( + m.isOwner ? Icons.star : Icons.person, + color: Colors.white, + size: 20, + ), + ), + title: Text( + m.email, + style: const TextStyle(fontWeight: FontWeight.w500), + ), + subtitle: Text( + m.isOwner ? 'Owner' : 'Member', + style: const TextStyle(fontSize: 12), + ), + trailing: (!m.isOwner && + _collection.isOwner && + m.userId != currentUserId) + ? IconButton( + icon: const Icon(Icons.remove_circle_outline, + color: AppColors.error), + onPressed: () => _removeMember(m), + ) + : null, + ), + ); + }), + + const SizedBox(height: 32), + const Divider(), + const SizedBox(height: 16), + + // ── Danger zone ── + Text( + 'Danger Zone', + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w600, + color: AppColors.error, + ), + ), + const SizedBox(height: 12), + + if (!_collection.isOwner) + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: _leaveCollection, + icon: const Icon(Icons.exit_to_app, color: AppColors.error), + label: const Text('Leave Collection', + style: TextStyle(color: AppColors.error)), + style: OutlinedButton.styleFrom( + side: const BorderSide(color: AppColors.error), + padding: const EdgeInsets.symmetric(vertical: 14), + ), + ), + ), + + if (_collection.isOwner) + SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + onPressed: _deleteCollection, + icon: const Icon(Icons.delete_forever, color: AppColors.error), + label: const Text('Delete Collection', + style: TextStyle(color: AppColors.error)), + style: OutlinedButton.styleFrom( + side: const BorderSide(color: AppColors.error), + padding: const EdgeInsets.symmetric(vertical: 14), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/scan_tab.dart b/lib/screens/scan_tab.dart index 1b3d9f0..feaf62c 100644 --- a/lib/screens/scan_tab.dart +++ b/lib/screens/scan_tab.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import '../main.dart'; import '../scanner_screen.dart'; +import '../services/collection_service.dart'; import '../services/storage_service.dart'; import '../theme/app_colors.dart'; @@ -17,6 +18,30 @@ class ScanTab extends StatefulWidget { class _ScanTabState extends State { bool _isBusy = false; + List _collections = []; + Collection? _selectedCollection; + bool _loadingCollections = true; + + @override + void initState() { + super.initState(); + _loadCollections(); + } + + Future _loadCollections() async { + try { + final list = await CollectionService.getMyCollections(); + if (!mounted) return; + setState(() { + _collections = list; + _selectedCollection = list.isNotEmpty ? list.first : null; + _loadingCollections = false; + }); + } catch (e) { + if (!mounted) return; + setState(() => _loadingCollections = false); + } + } @override Widget build(BuildContext context) { @@ -51,7 +76,7 @@ class _ScanTabState extends State { ), const SizedBox(height: 10), const Text( - 'Point your camera at the model ID on the\npackaging to instantly add it to your garage.', + 'Point your camera at the model ID on the\npackaging to instantly add it to your collection.', textAlign: TextAlign.center, style: TextStyle( fontSize: 14, @@ -59,7 +84,72 @@ class _ScanTabState extends State { height: 1.5, ), ), - const SizedBox(height: 36), + const SizedBox(height: 24), + + // ── Collection picker ── + if (_loadingCollections) + const Padding( + padding: EdgeInsets.symmetric(vertical: 8), + child: SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator(strokeWidth: 2), + ), + ) + else if (_collections.isEmpty) + const Text( + 'Create a collection first!', + style: TextStyle(color: AppColors.error), + ) + else + Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all( + color: AppColors.orange.withValues(alpha: 0.4), + ), + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + isExpanded: true, + value: _selectedCollection?.id, + icon: const Icon(Icons.arrow_drop_down, + color: AppColors.orange), + items: _collections + .map((c) => DropdownMenuItem( + value: c.id, + child: Row( + children: [ + Icon( + c.isOwner + ? Icons.collections_bookmark + : Icons.group, + size: 18, + color: AppColors.orange, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + c.name, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + )) + .toList(), + onChanged: (id) { + setState(() { + _selectedCollection = _collections + .firstWhere((c) => c.id == id); + }); + }, + ), + ), + ), + const SizedBox(height: 24), // ── Scan button (gradient) ── SizedBox( @@ -78,7 +168,9 @@ class _ScanTabState extends State { ], ), child: ElevatedButton.icon( - onPressed: _isBusy ? null : _openScanner, + onPressed: _isBusy || _selectedCollection == null + ? null + : _openScanner, icon: _isBusy ? const SizedBox( width: 22, @@ -113,7 +205,9 @@ class _ScanTabState extends State { SizedBox( width: double.infinity, child: OutlinedButton.icon( - onPressed: _isBusy ? null : _manualEntry, + onPressed: _isBusy || _selectedCollection == null + ? null + : _manualEntry, icon: const Icon(Icons.keyboard), label: const Text('Enter ID Manually'), ), @@ -171,13 +265,18 @@ class _ScanTabState extends State { } Future _processHwId(String hwId) async { + final collection = _selectedCollection; + if (collection == null) return; + setState(() => _isBusy = true); try { + // Check if this hw_id already exists in the selected collection. final data = await supabase .from('hotwheels') .select() .eq('hw_id', hwId) + .eq('collection_id', collection.id) .maybeSingle(); if (!mounted) return; @@ -190,9 +289,9 @@ class _ScanTabState extends State { builder: (_) => AlertDialog( icon: const Icon(Icons.check_circle, color: AppColors.success, size: 48), - title: const Text('Already in Garage!'), + title: const Text('Already in Collection!'), content: - Text('$hwId is already in your collection.'), + Text('$hwId is already in "${collection.name}".'), actions: [ ElevatedButton( onPressed: () => Navigator.pop(context), @@ -205,10 +304,14 @@ class _ScanTabState extends State { // New — offer to add final added = await showDialog( context: context, - builder: (_) => _AddCarDialog(hwId: hwId), + builder: (_) => _AddCarDialog( + hwId: hwId, + collectionId: collection.id, + collectionName: collection.name, + ), ); if (added == true) { - showGlobalSnackBar('$hwId added to your garage! 🎉'); + showGlobalSnackBar('$hwId added to "${collection.name}"! 🎉'); } } } catch (e) { @@ -221,7 +324,13 @@ class _ScanTabState extends State { // ── Add Car Dialog (inline, styled) ────────────────────────────────── class _AddCarDialog extends StatefulWidget { final String hwId; - const _AddCarDialog({required this.hwId}); + final String collectionId; + final String collectionName; + const _AddCarDialog({ + required this.hwId, + required this.collectionId, + required this.collectionName, + }); @override State<_AddCarDialog> createState() => _AddCarDialogState(); @@ -263,6 +372,7 @@ class _AddCarDialogState extends State<_AddCarDialog> { await supabase.from('hotwheels').insert({ 'hw_id': widget.hwId, 'user_id': supabase.auth.currentUser!.id, + 'collection_id': widget.collectionId, }); if (!mounted) return; Navigator.pop(context, true); @@ -280,6 +390,7 @@ class _AddCarDialogState extends State<_AddCarDialog> { final row = { 'hw_id': widget.hwId, 'user_id': supabase.auth.currentUser!.id, + 'collection_id': widget.collectionId, }; // Optional fields — only include if filled in. @@ -441,7 +552,7 @@ class _AddCarDialogState extends State<_AddCarDialog> { height: 18, child: CircularProgressIndicator(strokeWidth: 2), ) - : const Text('Add to Garage'), + : const Text('Add to Collection'), ), ], ); diff --git a/lib/services/collection_service.dart b/lib/services/collection_service.dart new file mode 100644 index 0000000..23d4efd --- /dev/null +++ b/lib/services/collection_service.dart @@ -0,0 +1,236 @@ +import 'package:supabase_flutter/supabase_flutter.dart'; +import '../main.dart'; + +/// Data model for a collection. +class Collection { + final String id; + final String name; + final String? description; + final String ownerId; + final DateTime createdAt; + final String role; // 'owner' or 'member' + final int itemCount; + final int memberCount; + + Collection({ + required this.id, + required this.name, + this.description, + required this.ownerId, + required this.createdAt, + required this.role, + this.itemCount = 0, + this.memberCount = 1, + }); + + bool get isOwner => role == 'owner'; +} + +/// Member of a collection. +class CollectionMember { + final String id; + final String userId; + final String email; + final String role; + final DateTime joinedAt; + + CollectionMember({ + required this.id, + required this.userId, + required this.email, + required this.role, + required this.joinedAt, + }); + + bool get isOwner => role == 'owner'; +} + +/// Service for managing collections and membership. +class CollectionService { + CollectionService._(); + + /// Fetch all collections the current user is a member of, + /// including item count and member count. + static Future> getMyCollections() async { + final userId = supabase.auth.currentUser!.id; + + // Get memberships with collection data. + final memberships = await supabase + .from('collection_members') + .select('role, collections(id, name, description, owner_id, created_at)') + .eq('user_id', userId); + + final collections = []; + + for (final m in memberships) { + final c = m['collections'] as Map; + + // Count items in this collection. + final itemCount = await supabase + .from('hotwheels') + .select('id') + .eq('collection_id', c['id']) + .count(CountOption.exact); + + // Count members. + final memberCount = await supabase + .from('collection_members') + .select('id') + .eq('collection_id', c['id']) + .count(CountOption.exact); + + collections.add(Collection( + id: c['id'] as String, + name: c['name'] as String, + description: c['description'] as String?, + ownerId: c['owner_id'] as String, + createdAt: DateTime.parse(c['created_at'] as String), + role: m['role'] as String, + itemCount: itemCount.count, + memberCount: memberCount.count, + )); + } + + // Sort: owned first, then by name. + collections.sort((a, b) { + if (a.isOwner && !b.isOwner) return -1; + if (!a.isOwner && b.isOwner) return 1; + return a.name.compareTo(b.name); + }); + + return collections; + } + + /// Create a new collection. The caller is automatically added as owner. + static Future create({ + required String name, + String? description, + }) async { + final userId = supabase.auth.currentUser!.id; + + final row = await supabase + .from('collections') + .insert({ + 'name': name, + 'owner_id': userId, + if (description != null && description.isNotEmpty) + 'description': description, + }) + .select() + .single(); + + // Add owner as a member. + await supabase.from('collection_members').insert({ + 'collection_id': row['id'], + 'user_id': userId, + 'role': 'owner', + }); + + return Collection( + id: row['id'] as String, + name: row['name'] as String, + description: row['description'] as String?, + ownerId: userId, + createdAt: DateTime.parse(row['created_at'] as String), + role: 'owner', + itemCount: 0, + memberCount: 1, + ); + } + + /// Update a collection's name/description. Owner only. + static Future update({ + required String collectionId, + required String name, + String? description, + }) async { + await supabase.from('collections').update({ + 'name': name, + 'description': description, + }).eq('id', collectionId); + } + + /// Delete a collection. Owner only. Cascade deletes members & items. + static Future delete(String collectionId) async { + await supabase.from('collections').delete().eq('id', collectionId); + } + + /// Get members of a collection (with emails via RPC). + static Future> getMembers( + String collectionId) async { + final rows = await supabase.rpc('get_collection_members', params: { + 'p_collection_id': collectionId, + }); + + final members = []; + for (final r in rows) { + members.add(CollectionMember( + id: r['id'] as String, + userId: r['user_id'] as String, + email: r['email'] as String? ?? 'unknown', + role: r['role'] as String, + joinedAt: DateTime.parse(r['created_at'] as String), + )); + } + + return members; + } + + /// Invite a user by email. Looks up auth.users via an RPC function, + /// then inserts a collection_members row. + static Future inviteByEmail({ + required String collectionId, + required String email, + }) async { + // Call an RPC to look up the user ID by email. + final result = await supabase.rpc('get_user_id_by_email', params: { + 'lookup_email': email.trim().toLowerCase(), + }); + + if (result == null || (result is List && result.isEmpty)) { + throw Exception( + 'No user found with that email. They must create an account first.'); + } + + final userId = result is List ? result.first['id'] as String : result as String; + + // Check if already a member. + final existing = await supabase + .from('collection_members') + .select('id') + .eq('collection_id', collectionId) + .eq('user_id', userId) + .maybeSingle(); + + if (existing != null) { + throw Exception('This user is already a member of this collection.'); + } + + await supabase.from('collection_members').insert({ + 'collection_id': collectionId, + 'user_id': userId, + 'role': 'member', + }); + } + + /// Remove a member from a collection. + static Future removeMember({ + required String collectionId, + required String membershipId, + }) async { + await supabase + .from('collection_members') + .delete() + .eq('id', membershipId); + } + + /// Leave a collection (for non-owners). + static Future leave(String collectionId) async { + final userId = supabase.auth.currentUser!.id; + await supabase + .from('collection_members') + .delete() + .eq('collection_id', collectionId) + .eq('user_id', userId); + } +} From 4b9cefb319a3435912899a6257d6cd3439bc36a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20M=C3=BCllner?= Date: Tue, 24 Feb 2026 17:28:44 +0100 Subject: [PATCH 02/14] feat: multi-collection support, shared collections, invite by email - Add collections & collection_members tables (SQL migration) - Create CollectionService for CRUD, invite/remove members - Add CollectionsScreen with create/manage/open flow - Add ManageCollectionScreen (rename, invite by email, leave/delete) - Update GarageScreen to filter by collection_id - Update ScanTab with collection picker dropdown - Update HomeShell: Collections tab replaces Garage tab - Fix RLS policies (infinite recursion, owner visibility) - Change package ID to com.dltw.derkauzigekoala.hwhub - Fix unused variable warning in AboutScreen --- android/app/build.gradle.kts | 4 ++-- .../derkauzigekoala/hwhub/MainActivity.kt | 2 +- flutter_01.png | Bin 0 -> 117406 bytes ios/Runner.xcodeproj/project.pbxproj | 12 ++++++------ lib/screens/about_screen.dart | 1 - lib/screens/home_shell.dart | 7 +++++-- lib/screens/scan_tab.dart | 9 ++++++--- linux/CMakeLists.txt | 2 +- macos/Runner.xcodeproj/project.pbxproj | 6 +++--- macos/Runner/Configs/AppInfo.xcconfig | 2 +- 10 files changed, 25 insertions(+), 20 deletions(-) rename android/app/src/main/kotlin/com/{ => dltw}/derkauzigekoala/hwhub/MainActivity.kt (70%) create mode 100644 flutter_01.png diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 8377968..f70a53e 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } android { - namespace = "com.derkauzigekoala.hwhub" + namespace = "com.dltw.derkauzigekoala.hwhub" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -21,7 +21,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.derkauzigekoala.hwhub" + applicationId = "com.dltw.derkauzigekoala.hwhub" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion diff --git a/android/app/src/main/kotlin/com/derkauzigekoala/hwhub/MainActivity.kt b/android/app/src/main/kotlin/com/dltw/derkauzigekoala/hwhub/MainActivity.kt similarity index 70% rename from android/app/src/main/kotlin/com/derkauzigekoala/hwhub/MainActivity.kt rename to android/app/src/main/kotlin/com/dltw/derkauzigekoala/hwhub/MainActivity.kt index b6bc733..4745079 100644 --- a/android/app/src/main/kotlin/com/derkauzigekoala/hwhub/MainActivity.kt +++ b/android/app/src/main/kotlin/com/dltw/derkauzigekoala/hwhub/MainActivity.kt @@ -1,4 +1,4 @@ -package com.derkauzigekoala.hwhub +package com.dltw.derkauzigekoala.hwhub import io.flutter.embedding.android.FlutterActivity diff --git a/flutter_01.png b/flutter_01.png new file mode 100644 index 0000000000000000000000000000000000000000..40efc80b08cecbbf2ad3c0efc09f7a5b9e05dc50 GIT binary patch literal 117406 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%0AbEZJLvDw^Zgs8s zmz_ZArR>W_BagbrJ6<@%A!}9}`BlYVU$jWapZ&I=atx!m7_X?i=`{MXy2~5T`E1ds z8*iXD|Frnj520>0{NwQC{Js3|yjpksscEFIA2ChSr(-0yd=D#X>N3)?fa>uGi(FYc zs}s)b@aBmp=fP2d*JD>i&%vu|0gp3&p03P9rou5$!z`34=wt~$xTkmR+U10u7A5`Rr zW1WvLKyzr~kXv_1ho^5QHu6QY6XbIhPr@Q8((5@k?%#Z3-;l>4c6i}yZueMyZ$|41 zR`B$71=d5shJTFSg)~HQ$|lPT9w~#=!_{D<$FBX4SjL_>!D!_E{IeflfmYbp$nd=X zFx~%cnBb!qBmeL$kdamIOAfr{yFcsj2l>AV2mXJ5BW)izaZ7d;hu(}vhq3`JEDZ>wVAYFRCDh<)XO+ubGg9U^-zg)C0wRc(Cu7#Xh`EiJ7(F{>D7 zHBThsfn$wLp`@gwx58^H!i$%t;Z+wHU9v_FkjqDjqobqL7cPjHddSK7)ktM%DN9MGIKQGCNClY-fh{c7{6I+D5<3oFpY;n`!{x4BGk+TC^wwuYMD79g{} zHhXPi*=f1s&8Z6#mJ*&Dx&HqCxB(XvlRLt~!k@FVHDzRc#Kgr-uYZh;h)AB9u{uvp z%_$uFk$h?{iL07i{$+H*#`xtbOu@Q&Mt3U(Vj%{wCp> zmDS3P>?SjZ%DFdh-uMRvt!+M9UW^lI8)EvG1K{npv5)}##0k*(;8|s5WfT7lJULH+H2VNH35vWpRO2*Yj%wE6!otGBDPYpPr>Y7R_I{CZaZC&If?AT2lBDhB zmWOI;*TyR*Cd`CIL^6_-?|={!6T;`DfBx6^*UzU_m9+5Y|~ zv7!!lx7O#o@T$_10)*J+KYv0hNYgy}WzN?1UdZ65r?-|k5LTc`&w$!i%FlzsPX@Sd z3*qk5U&?cGT6VUu(r5#q*v$hrTr)|7v~L8N6eve{io{SGGtF3Wgl_Nd-MjsLKt&JT z%+Afd>g41!1)ogM&URed+mI{YB@w;j|8{2Sk~aPZvn!~nsd4l2YIu5n11`Xrm>3Op zbykZRmWj$;V&%$jdfN?0qwCm9JCkzuHyBDC2tghNKY(7<^Q7`Lk+@2b-qZH-@|v>- z`qS6I-e~gWQGd&?g@s%(F|idmv!RLf7mlYN{OklfjrH~Q4-^#x`1QVCRsi}(EU@za z{W9d}rA71C>Eq1q&K79}uR2h?KGXA8`0@umL*Us1LiY0)E~EmxHHgaO+H4;k9o+*c zJlS2A-cyH~fFj_0n9wkO_AEmRjxRVlJC_y%)g%`OhaB_^TBxq9{JyuZ&k0yDVH}P~ zyn1p)Mn*>L`RHr75t~9ow1AeG8P4kZM|XF34K=k9?|5W{-@kwFK3mvD&BP@2_qjfQ zG*BdFg@v8d&eBBIIjm~Ik4=`gwp~!)CM+S5Sy3UyDCw?&UKT+oo#)YXl^pb}AiQ5G zK1Ozx0u;;(RUKVjCm_LGmwjqvl(05808+*lyt6hxlmmD5f*KQ~3wO;rF?nd=en-Z{mB>V7OhO1t35xR?QBqT?6D?) zv?37NzX2I%vj7fMAm9|iojwl}Ut~L!4Rv>l&ShTp5pOX<*RNZGF0?IQjViJk5Sx9Y zX=(Y{dnr8rDX6iq-|kMHpc+7l9oTi=rQf@57zWafgThv)-kX2Xvv%0@Cz@OU$mE z`q+d6sd@%<=wZKcJj!zlqrqU1O68Q2Q&M36e^A}VW-daEk0tG6S-=m0l&O^a$!GU|Ou3zDnnZR&aGk_ig+weIn3%lBlm_^_aO~tlRQBfsn8Mw)U zSqw{_o3mLzo?dr*T=EjP6B*fUDhP}oYo0!Rx)i-vk^F`0?AF?B_^&6J#cR--&LBqd zZK*DY7D>Xca~C}@0wJ1lXV&ZC!;XS-dYan~4*51i6@0)Qf3FSdKvK(HzZ>d(7ZlagptMfbWKfj*rx)Ynbi2V#()YkcvElj`t@s2`xS?_Ha0sUF8}_*J7-@A zTFm^k63&E%V@40QOCd36-{pxq@l9OZWH|b>!b~E|g47S~au6gErYIefd>k#cNIab1 z+}w1?HDdelr<^rf+y!irwRydrrG98vxUR7`YkoDf|G+xt_iuCD+qZ@BjNgviP6R%qcwS#GNP_!N_!L@i&R#`d5dC(N~<;xePfHLFZ zr+b~ViDVQQp?l70edkcX}-iSvs3R z!B=%=ZoPeX$Gb3sWhbt>`W2RsR$X{5j72d#6|CJ~?NLF^k=jJ8mf8bXyWZ{W?Cy$J z+;6sSZn$D#%!uhGwsJ@}svrLj4!Q?T!@=+N!)NY$_+h1)v2hm6Y6#ChTaxYUoYd+E z4U4HLD0Bz3_+P$kHD>z0Xf2L!?$zeRCA)+|c|Cyzur`aLLuIcdJ(2rP8=Ri&3N8)X z$nlnar_Wm^i@D2gF;_GDmt3EG5l>yQJ@i!9rWsfs7#+?0aEBIT=Y0T5=m!gpX88ex z*o)LG;WM+bE$wMtMnfCtKD>bv{kbf7)oowpIUk=VaN(c!zLyb#UNA+1)5?P#2@kCh_B@@c3dPwzL} zG4N_og^#$6j}Ji<_g&zyZ$krv?%2~Ky{hBg(L&enT=~^ChZQw4j*jK(jF$~HX@g+p zt|3}pKdJuA^0e;G>^hkc*PdL&2T6xcA7Mh2dsh?sUpc;x9a=DBqM@Pb?O5Ccby1gl zygptrf5J*b(>~wQ-t_a&Q8k`=kSyM^553G*`=0{E+$i$Zi7J(sFJA^VyAIB_1ay|y zw>I)L#PaCmQ0!)rM2jtlXSHxP^D$R=ZouN|>dw$$EWe*sV0ifa$saJZ3Uf3`{V)S)DJd-`cXe3% z{^Y@Q2c=f`2CdkzpGrvU&gw()FP|Ewf2UVNUd5kV&#b{pf$bmJY?eZ*_h+ucys9y3 z|Mty1VBbJr|F^@Xn6@_Inm5Gisx_|T^SbG?eyR^3kI?3WWj+zr`X9-3*sr7}2ClVh&s+~0a(b%u(eDA<@s`lI-x;t1@1Iw2z)zxF+Wb{M` znXa!|{XN|PW&(QYz&jTjLn9+SlZ7&{6n-8|IB$vVS=N-8j`u9~rdIhEv6xS}AO1Oo zAQft5Xg>3#vz54?PPTj|q~_`%cB_er$Ma4p3Mwj5N%_A*gYgw9C@7GF>E%FkLr)yDwLk&4K>b1jZbn4~ zEDqz5wl#2SI3btwP~vOtxS*h*BBQ}4YL)gk~Esk0ZdVR6|@P#6wWc{A*E$88Be)Ol)Z4Jj<@WWQQSXe|vIwbRqtIa2i zgliWLbEJ5GR4KDe20QygG>3!q&dv^e@FtrW1&9Vt3;zUA#p5W7l(<35P31E$?bzLo%wjMXng z(uu8q&12UT^M^T3f43j%+fFxz%98&NSK|K^*CF(SlDPFE23BWBcud(K|G)p zc&NzO25$uq+)(h~z^;&!l#Dw%I&v_{+Xp|Ant?$E`V@gH?Ckck0>C2csaUVh92rpt z6a!)bJF4&mH8m69MPyyhR?OFYWz-I-be*od49L6%#~HpovbdyKv(mJ;{||WB3qMsM95|>2>J?fH(^fnPfR@d(3J4; z<24%fYE_Vdl(ck@=Ky`wTl-G@MYHQD>=99V`t<5-Kkt`;fgAQVd({p{#{$gvdw%{H zyIHA;y7*E?29mLt)kT878V@JDLA!pr?7qgd%h5*5TDMTmeiQAlKJq&#&(<+}aXiOo zo7oG+lNa+Nn+E%2iB@jVokLDpgx~)$=9lvMv#dhihg!0G_l!K%>J!TUbMv6+{U)Sp zLyO5Lj;qYPS-KmHjG<^17e#A9>m$Ga4^9hL{*N2;KS<`8ME?h*Wx-a(F$i!aGQ9E% zcCZ7BxJA$23;R6QMLCO&c8zf4k_OSOaHdNY7dd_*rWO2|9b;2s5Bw=^@iv;Lw0iWL7x_ef|yV!BEg&L13vMHVg(x1q~e39vyeY3l{ zu2vL+p{M|CbCQLVod$fOlJ;mF-VEUCq1m!fNO`LZ%iTI;;(9UlCec1vtnrZKsrC3o zmpr_XM0ytR#n%A)p#}46U#R0lZa74?{d|E`%cKAKNa^^HO79cZIS%*)a*zW3dtf+F z=oZ1;i4`{n(@XbtKV_WV)TaA;#{}-2j@BI0L#t%qrGH&l3+9d@X5YEc{t`zTodsJu zg|MkEg9@p|9J5&TURC#q9;P&X?KX1ny-3uyOR-+?eXH3r5;>$op2&lUBL(6KjqS^P zz;W_*oE}Wp1mExB_=;l*w^4QU<9ZhLm|pkKV|gWAKj~cOV7^w|g`CxgQq)e#>7kTq zKHX_-fWm(KxIsj8>~^UFo#V#MV7!$ZeDw`sT&>zYy~_+^qoU8VgM?)G?T|7*yCE;S zFuU$dV;+yL)1sv(boCAn-bR)}G8XItSDWpFJ~=tI?{`}f@jlL5tjClW<4uD;5sx8<3-T^DKZ&_#i5Xp_yNkj zwt8IGEZZTJ_5E8%bo2 zV2`92-Cw#Ln1V<;Mf^J2wc1-ZgO@ODaqP*-w8Zj6=tA6_of1zrvN2z++das^KV*K5 z)J&xJoaU)rSo=`T+M!|U$>8b9`2E{D&SGM75G!(%&hvsdQ#F>I;Sb*l=f!l&y?}?k zDIHHK?~CPplPzmb&7%tiObjK`ed={LCH{8AWfeV>Hg(mJB)8+=mlK%~-9T5l@hP#m zGsA7)$FA~v3BKXI!+^?^ve-OiAn@O=ZS%Z{uW0x=;fChtgr{~YUvDigI&XcHQGmPd zEx2{fN3Y)t|0B7rW#Y*RmfiKey-Oj6XRk98X=5Ay;07u2)#5-9(Cq>HseGO9!A~*$ zFA=Fw(6A!$hj+zaM85xER&M61K9IF85{3Tk_T6&=|C9;$f!n`G5Z^?AP{qja*L!!Q z@c&`k{(m463olooH0UGRbtBquBW|q;h}pdxTx4&Yr5QK2*7iFAFbg90(R#c<^1D`=zt-b{9!DQZ7t&YFAJGWmF5u9XYyzD8 z&L|OxatgcViKqh&TdmhEiGhS4mujUU?xcZi2zlc?16RRw%>#^&z=?Y67QV!WU0)Gi z`I|?wCtT5Hiqa{3d9mUeT-0bqzk1bbZ-HdN9S6s});w!~E+7N~tx&Z`_fE6KzMV@X zopO(-dw|RZMSCv-y8Ah|Uf=aX0)A^}u3WvUrOa~eZRX3n=cj=(M&<1R&IuRe%-Af97=mzpech8={)`l|9=Mdl&DAFKhq7&*CD#=sFcH#h#fE|R+4AElTfv)|B%(cyeO&AFI8T}k=>p|ZiInSxro2cgZy3lC89k{Biy}( z0Cahq<}|k_a#6Zg&*>sQK&8mdMNVOeR!ac9Y|0krA-w-aCheeLf+H`9F)>NceZDUCO62OBo7+pdq9sBa-F;xLgviK5<&cpS zh9NI?&hwl0Q-i%VF`{XHrC}Gi-ECA#-@T9~W4Ff9m9I75KRmoV5fc-W7R?^3`YF<{ zZPB{?g5UNwxyeu-)i+$EoX--#+J;TqRoNMu35A)DP=Bzdc0v3}Eg(YaqKkTE7j zNLY2S`F;J!5ldIABw%6G?EB8q7CyPq=-2G-H)u45CaaNyTcQt1N%78n6fy#$wZ*5+G!HQjf!FJ0*2>g8)xIWM%dp&)2Gg z^(NMa8^9Ek(Sb*djJ$dsx4uu`l@b+Adgba+Au#n*Ogl-;X^$(}%j+)S#-sBG5GSyP z)7ajudv$Pkb-76MWr}$06M2cw`imOyWprTHlL%s96pV8NzO;ODoWe?MW!;zF+-w`W z7vPINh^z{noZVVZVq%GF6WKBn5;d36d~^!mQZq40gT%KRZf*)XuB)hFXtY<*yj6BhLCA1QW#)jPEPcFr5LlEwN<>%Gppg|8c^l2a%IgP;PN z+kL+^U;!>o|b>` zum!k>xwdvOJ8swSVSF~WyMSmdrgn=K*{=4PCuXfwXJp{PoTaW9kI$paMCM{>XjZXN zI5l)@kD~te=J!zu2zaowcm2{40jyIR2KQn9kUZ^485?`Hx0;c0IiULX2b~uG9a#H7 zX4MB7FRl}Rbyyi3WKP@NHATj=(I6GlK8W&2Q>%Edo&Whf+g@8}#G01dmRLH7LbXUv z%yc}X8?FI&64<2Koul@W&0%x1vhwol*Yd05ham!`QVN3!R`WUOsd}|qlhxI{#YNWX z2?^rV^4d#p?=Vq9gVZX{n_;sPnX1CDg>_se<>Aiu_Hx}DT{$_5`EnA1pK_2R@wgv{>$Q`d>E;X~W z-CfMgecwN}Ql_2TK%d~@K?=RGxVx%sGNBrm%7S5W<6p4=v95eQif%#hq1#P&wnh^X zgRQxRii!uDoV?L~%+x?>pFccUN0-8Ab%bMY_2h&U(7j~J>1OTKRZa++z-MH%dBibB z^`Ys8IVLQH5c?tmT#$O-0~G;*=yLPvlrJ6mJY?+ba?69cpTo6s)lc;sJ;*ujyRu|b zbHuMOQ&iaHN`W2@8ecPKevjD)D?ZheYId#lP_*ljJ3E6hWG|V_o_r!#D>b(gE~OuC zkLo9q34w8_oPXb{xy2Y365Xw#Q`^gzy>}K?As z+xFeX2+whrKUCn&Km?ypU*E+5?*u6RPrvBb(o#N<)7t2)OftyY-Qpqv54NajM%fVc z^C9EcMBjUvnC%Y6FC-NfDwLQFcBK19;!TFxmUa3E`YAm-N#6;7R({Q5#@$gOz`QuS9vijq=t za>VACetxSAyGDxF;AJ9APAjWkj+E7Xqsr5!(%}>b(UwK)c1o5=leQM^;D%dnQQ6`I z=WY-a(-b-!#5}%tuXQc%RafkMJW(U?L;L!>HFG=+613mq5BXI?h6vho_|#tRY|IigFDVaz%;->)L5 zp?z}sVYTAQ1S6DyZh}}`aW!s&uwYSAQ@`v{Y-n7sq1|(8AwHO=Oj9sie-yJxD%9QWQAHG=m1`zxVWA~s z4_c%*f}x2w2hQC?iTG{QI9|GzkuW3mrlYY@5e%))Kc96AA%riV=v1_%r}5{UMsU!~ zc{X*v`mr!MOh)b_$!XJqdqhMjU^H$U-D>_B$m;Qg9e-gNr;S3qPFUz&>}v@qnS>4o z{3luY0V9nmm{4HWuqVGusFBZBPC;z1lliGSlSosKFn@od;ne_s{4Ay5d7GUN5u&Pw zwv^^9{MIK&iRo0JOEU84TIO3yitKYVf@0TCx3uy<&ld%Wq!}8on4f<6LXZlowk7}v zSI6qKFogd)q6}Oigqim##|oJ4p%rGSNsUH_yF2HIX(=eAJGqws?9a?p;1NcT{IJs2 zmW6nuGP8%Ekou`kpEL&t&lc;7nQAn7=lNV8wO&o24%`LLK8;8x_JX2GXh~`vTrZ=sv%`rEClDcBu0%%E0Vd2eH%`tEc z0$huPZhs%+|KgPa4PT2x;tHy~R*WIsLxAN5k!e4u{7r{@Qz0Stus7D_)2mGG{{F85 z43wgna34K<*q++r4ZaQp$k`i5Emi^4;NAk!N!TyMQ$_ST!_u;|N2^SFQ_fdywqYeF zKj=8H4{lAurH1T6{7!04&ekgU3Cwp+e$F>AaB?aUhV{0@G;ey*vIp@A;i4W7dB@@i@y~}9J5#)c8JWZx(AWzXo`X7VGc10@#wMMfzM)*jHcB@1 zDe3E1QwV92+u7@M4LmyDvqPE;ZwqO0V!d+E@mX3@ww~{}08vf``rmSX|E2?LlM?o( zlF#k1BBgV;Si4pL8$#uLHKXikkWQfnp5{ug8Oj&uxXtU53I$C#tgjQqMqqDeUcY#q8 z{4f`c_lHFmTGdWYrl#fUnVDnNF6xucGHg2c6wL_yaa=Z<>=3cGI9OWt=tgt>-WeQ( zfU`I-B@R?$yPfyO3BeKvFM1@m1%$8|Eb(NyE8b+SC9+*NTPZk|(WsIcdv%?-2X^aQ z?nc={kfK4KUaVR_1jme#X@${tc5TkxE9ClGT`iX?affzs9zi`kwBBQ2-v=Q)Q7cc@ zYU#)1Auw=;h6JJSAuJ;G*DrA}a}o{?!tOaaJ!UK}Zii;4 zf*Z4cJOtVXLQ$SGo61tt(mq#GYK8St!P?pj1_6W&NNH*L7#czL;r*XZY-2pu<-Xm*M^0O?`_SRnS(gpTLK_pac84Un%@26wMls+MXLK`iDx+? zD0hdaEG#9o6CYF3(*ZHlIREc}e(){Ne*f(R1Ter}&LJ?_|BiemN#hp~ zc!Q+LfC&D9+dSJAf1UwwDEBg;RV2H85pAc4$Injyj6nb^uyll^$nZpIjQb)D_rOkY=)MGXRG_Gmla(wAb#$B2z1~*Je zcA;n|FC<-xXZdrAPyh${`EzJDJU$`lvOZTmCixZkR|{VHOL0+?=0=?BIA(q(k`SVku1Vf`W*$V zp2+McVe5Ss+G;FW?9m7VV&*UV*NtNwH;!!{XQJ3Ef34xuYik@^cofBj&eR3NqRUpa zH^d;)HUR5g8&ZVz6Y6gyeLNNLzDi+Au40M&RCj;B4zbo<{w?IlN>mzyo_*l=IqgW+ z^O)AR&SP2MGMhsB0i}Q4*#^P*QK%dL%55V5SCW+Azw(^)PL84%2iy>^Dx}=CT&=dR!|5xg2YYKC+if05$<_QRv~GoD1-h*lo-sy zuex`=dlrcf_27XxHP+KAqhqjm9tcHe?D&Q?%Vop%$4cDJKb$zN6_3Ro57 zp%+iDJ+G=#QFehWHx$pPU}`T60Z%M)92lRe73{0R5lE-@1?-ra@L`c-!qvBwkOGh5 znZ>ard3TrcKK)rU;=xs8|m5%!12QnJaT zblUKR@!wXq!g?^{1+$!I6JaLFugekm-pqN?==s{Z7+!>5pF0&+BhV?D9*bum{#2^Y zqjXC6#djOKo2JMELP!&)sLm=h#^{yBM!QI$&zh2o+(R*aa7X4SDvwfBikH9~Hz9eU z2Km?`;Dn0s6iASJ8ok#NGy7BBv0x;GEiKK^^1{V9!rx|2MQ3T$@ZW!$IQo=-*_2fe zx6#D@((_1psQ+^Q^Lq>!#pE0?huw;K89dgHwWL znF7eMh3dSj$jT&JvQ3kU3SOae{tAl_?0$5Sz(!Ui?yNF>GZ#(=r;jX5`Zw&Kp`Q~V zRIKAK-Xvc>pCg-7Wa4z(c~**~o85ec^PW?S_i>fOAeY$`eT6o*b>F#jEfgQIKHD~V z2ZuI8756)Wd}kudfdU#c5fgv)un3^`0;9>`q$Km?i1AyM&Lt2;_(8ne;Pim^&32?w|Mn zRN0DmHce1*L|^*x2DU@1V=8z3f2RHNb$3t1sL8;5LWg`jPZcf|+n9QuO36pwOtPu8 zTHzm$WlfMfC_EF>GP<>*YU2_|tFh>HO%COAIFcx{Qur`)`m@?2HEqa}E4I`k)u=E> z_xyjhg~h$Ra?a;^jrki(5~K*8!gd;bF9K3}!#xs6REJ;V6;c6~k7Jx?6A6W~n7w0J z>(^cJrg%5+#Tj3R^1Z|7-*&g|4r}-{=|ArT7T3R*)z6{-McF(q4ZJQs> zyl66*LqE7XG$i?<1DP{|{$)DPVSw!H{Oq5M8XU9C>`W_WW1G@2#F)|SjJa|=h4zr~ zX+ic%bxceVq^%U`b$UTKaav01{LbN|`oNDLy|=#@XpL1LE4Boz7nh`_&a2onLBTwh ziZXHsju^Z?{ZWZGZ2*ZpEs>9Xk@*obQut0P<+zG(i*vGvNB$C8F`T$Jz9gex#yrW9 zQge_w*f%(czqDUh#}&`${G!AP&{#VXtv66__aQ z;-A`W<%x^u=E~$_J)f3^95`(W+X-=w_sl5IyE;tf;}RrZNER=YHH(uIj~UB_dJN5S zbIlfJn()ir-1w=fX+t{ai*1Z@DJZ@!Z!f;d!3c2MTc7A^YrD$n*mqDP%e>t50n_DR zaZWxKS?NCsQZG|hcqRsCvfLg1FoCb{cXE>LI3F#uyW5-k{UG*qOWk_9-lqKrM=unS zos(nadQz67T9ybwA8!5qnR}|88{uva%HG`(ZVt$>d1UTy5bZ+2F4p3|5>1_o%Fc5; zO)9Stkx6ttJrq$^RlUN&72LX{elkbR=}@4tj(=}Bg^-;e&oKsGM$L3)jO1mout^Ds zOIa}8S?%i~ESeY^64~t>HWt55+#&BG$`l3EP6;D@{`d~Zbz<+>l&5TLN#hkQjP&$b zfq|ruA4V0$SX@+;Fg*N{)0rjpa8GZ0rip)Hp2OZ;7_vmes*bl}-4foueIV2WseOkS?l8M)ftNo$~+l6Ss6 z_+c@l?&{iFPe*iO6Rs&n=j7;U_T1c^$Y_@ofq-Auhr3$Y+1Z2JNAOX#x=QW*>`DTc z3B+MR56xB)5qqg1|LRfrRRv+&)e)ER%0FEbSfd}UtgQw0I*A4+s)BX)cD4mwT;9GS zekie4;^d)PZY>fa%G9^zbk93}-DO<|PN>L_BqHXwvkMphGL*}p_sZ5KCM+iA9HcTW z)Orveoa_YvUakarLd=1l{#8y7w|7TRsK>T5=ElFsx%|K z>c%|5s6S?8IXiK{+h>vKWEZefQc1AB)YWxK*7Pi{NxtUeidA63r-|US(2RB6P8{ST zhR;qc)RWLZJs|{id&}{J!+z;wtSnXX-h`7F%=vfQrH>8{OP1bT@6ey`=9WGd$0|i`r&v|HBd>S1Z=J9~!sT6xZVnSOZkO^b2`*N3rk7;F zt%f$mT@o{Gp%su**8n+%^_#_`CD)h6?bIRlz~$b7jYP~Ctq_n`IZs|y!iMx=%fC&9 zCmiNOsK-jpr^^AOSC$_x$Pet^n$Lu2=X^Nm!ykxT8}>>#Rz(IH8J|V$JGZ|fO%n8d z4oRWr-P-z)qct!vz{p&Zrcy$*$bIBv*FQQ+CIoT$zK~ssBO?bNNRb+NjuR&pv6e8d?h{Z+)=2&J0u3W?MgFg%)=_#6*!Dc7D0TE|nx+ENHh-~H5jRd-8#%Ks>-CA7Fbg%3TA-Pz9o?Pjh4pr$oa#x& z=2tD32zb8yv`hgpa1Zy&eVIJK_m01SW{b@6APl+lgE`~9*($y98TW11i3;61bgdwh zK`O~=bm-f{Qoj>2LV_UdA!WN_(&nw9x#L`0^fy1rhJ{rzfT^N$2H%ypea}`2SaE+I z1R{36;16AgM@Se%zB}MQ5->LB3A6d{#Cb`m257gbV;G>&zve2KhvTB}kaB}AE?1=ufvdW3+ zfq`K%dcenNYRhEL)}nju`t=bRQ}V^u;7yH$$oinllue-2`3 znRgN9Rqjm~waq1T(ugJtyPcGn?p%LQ#Py{Y#>@cw`j;SC`}MQ63g!&5{vejg_BP+o z2iBkl-F4&8(A*Wj&2R1YWT?BjsK9c7XqxK1Dzc^Z$@}S&-5b+BlMs_Rd72`c;>_mD zaBX2x$Or4;Qk@d1l*E8^791DJH2JNtNBAM+`N|;X&*AD#JfbJtg8+Kg9C*Ys}=pkmJDOwa}BSQf_0U4E5Bc!%`TOw6PHm4G{;P zJ$HpZ#h^UJs_%K9>m4CUlckQ;(O$%esIUuIhBRkF7P>hM<>nS2MlP-_=+XG)!S&@2 zwAFrnKM*j5#{~$3D+QI6DUcpSeLR19q_A7f>D26U!GCU!v7&x@r1=W# ztAQ}@2zJHpDB;zw(kWyeujb4iElCX(cT3LCe+}vK`jgc{?8ZS!;p!9F>FH9(HK%(m zDPIt!@=dUnu*QB)s+*w(TqljQ&NIv(q7*7!PlUB2LvV8WOBDfc(Doo5sWCIMj z(^)s?gsu5pn8`vtU~{_suEWxC*O$LwlFPK`Pww{@3WX0Rqn<{cJ zOPsp^yNi6OGPAemVYl;_vqxM#cSpc)yY8c5t-!VoX;m)A2N!5)4|9EI7@x^vPKnJ`*At<^Z;#lx9J1(_ORYG@g#bLs64YNN7uWXhvY(V zkX?7vhe4O{{+(dqKnc}$x9_wwm#oHCl$UR>Rph2su&d%W*S(R+F`tDFuKvCfnPQ+Y2zv#* zfF@=ij4fK1WTt19s3Xv!d-MG%l=9DmokQEn%R-J@?TK(SgkDpN@#sJ*Z0^A5#2)^A z@mIT#M~mA=6Ye0-VZ-&wzPdM|>WL-H*@)f%!*InVoObfPSnrRB2)%=)%&6VnlVkSn zlfy|Fb#=3*7DAT1=fuwD%@ZASmdF5D&xe_kGqnJcWplPyXqxASsc&?+C=>P(@lha* zKQ;BVrucw7(!e;6E>3k^(7=YQf2NRA}!hA2fQVrxSx8z<6#K}F!Nbi{DR7wTnlpD@>y#lcFW!0~Hi_R{f^ zg8CASilWpQ0&+(p#TcQ%3VD7S-Omd}pcx1~3 zxa}G%Arf^t-dW9PQI6qK8)%QZjyOjA{72!THX}pdmOjwtEbP_P7%M!M?ODOV5^9?h zg+mIS)6itYW((5WV zg)@X^6gi66Yc4FckTm!iSwUG!<(@x3ZqpT`dGchrH8|CA-&}rg zt(_|T`%729rY2m78({^`Lk^Z+BTLH{V7GvlNB}v(QVPCT|Ms@$+Jp-M)sQ}8?~i3L z{lGx<0F5>X?gv;t17A&!g;Ohv!SnOU-T1UU@(afRc(WPVDu@3- z+e_Le*@aX}#M~!e`%@)#t!s<)o;b6Q!|>R^pFRU#P%qAQ3xvM;ZwI#OQ}#Yn`3vDM zxg&_!{|-^+5t*|D-yA{J-Pxulcl8<1V-P?XS9Xwz@R#Ub;3`Ur}PXIg*`fve>Yxe zNi{+gx+ZAWlNkha(ezHI^o7viMtP>Fcw#iTf_>8q-Sa+W1E=VH5mSL-V}97PM^p*vf~Cf1V&I_z#gdO0lEeDRxeq4;{S-#t6&m}$KNHAb z@&ntr$fMh%XFcy`OF5xJ-Sp9e0L)FaQbO&*w|_ijt)M8>w3671h@hSrHu|ZBQVgv0 z-yc%Y1P=2(u^Q;K@2bgKBhPT94e6^7U{FQhH5DYpGtbHbD4EZHS35FAv{#lXO3KcT z^e4#*Jjjz;;CYSDZ+Ea2lRp^?lVzcyn>CAwa3CAk9=0nOZn?|f(%uK-iwED*`%amv zcuE9W*8)lOgxmA`Q(Z%G>3|vH`t!4ITqZ)Q?J{L!oLU&l&Cv_TFpEw{#tzDz9G{SG z_2sjyeA9Zxo`fxLsdsAc$`o9hZ- z-N=7DUszi*M8zy-d(D6E&XW8@uAv&6CIY{VOM9eK&h0{KZ)58Xyw$QvD>f~QJ1f}l=QSqs zdOtdJmWU&nZx_sK}pMP+r+o#+Hz_-|Qw0=T${?7Ui_y=VvtVYGQ9{8cU0<7h!hqu)^!B zNd%`Cj+xdWSrn9kifW9@DA?y6;Ty&Zb}ct{n9sm8aeY6^GUt{~%b z6?D5o^M2mOLY?;^Efn=wp8f|UQ$tCD zte4{X@BIg3#{S|OJ38Ca8}~$hKXj&V+(;=TtkM`b!Y>4A(b<@M+{qHUNH4}CFgvQK zs;dbPCxZ>0&G30Gjx^^ROun;^(&r9rfC|d*Po;TkoK=Y}muBu6mk87(6*{hqLB*FoyQ{1#$ZB!{=ZD+v^MWOHOJX13gyOB2t3C#QOOuaBk>*bBG4UxBaEZ?d< zc+j>Tnt|M}W_vo7c9+Vyz>rKdk2mdXAy`c{9Kx|U5Y|w{Nr+6m`P|pWg)1^}w>vi@QMn7}>pg=e18PDd%OaQBZeR1n0gX5R z^sstzhQH%QLN9`o5vHlMw5{d$;X5fj)CBhJVXaBtwc<;7tbW2+dFZ zO!vfzY5t@PKkPbw$u!=9GoiH35`1TEI6pr``)DQYmUUlMWa4u^ti=9@lCkcxrbTe)S<+R!>uj<0hqblSuRqfcJYUapRG2Axc}ak?!Ov0N zrsMhH7& zwAv-=O=(wSom@|)zB@#u4 z>;9F}0}8{qo>=VP(rz)?-|(q5gkh?p>16)m3Ufwr2|BLnt$DFaoB@-SRAz2|^X87W+lm8Je-ZC5+qR zJi$en)1t7@8Avs;?kq{;!s^`8U@{U+7Kn6J;>t1D=Sygp z$kl~>e`e;IwmlPbIs`T?p4jLvCG2JS+R4emL2T01sU+ttUXR2pq>T?_uPcXR;Wz$) z9Nu-unorj;9#b(s-Qjpy>n~~7q&#!p-|;QJEuQ@9pG-Lu``F%%H|%wp*v7ZSj2Yf` z2s{u z_8wMQww4TbzHwZYcUM`?zr4m=zdf49^q^@k|8+`&{Tn}C8E&rf_`LwSrnq2)K^ivh zm~fp7U6Lb652}qtIXbxX7t^vV{EjHUe`=9NT!Ts)prr1P zC6Bm)Yc3l}abt<)?KNhqkiCnqvixs75pHTe6d2f<)xyUk{w7C5$>*f>>QyhmRI3Qi z1ZYFvj1~gBo`GO%V{$rAhrOfEJ_KXafpA!{w=<@5@f-h8+;izr18Q6+D|;+c?Qela zq=uUEQU->lINSSFa`?QN3HxfFb47J*4E6XalY!q16~;B~R)WsYR(QplMx)|LE|gno z$#Z&;>xRW~Qap$?%-spxjvxMYvYKBD3!eZQ9|XvYpWnN0ax&9+ps%Ory{_J!o!`@6 z0JR;q?(G3+6q3dGf`c1ipCj17HDF-imA>g(84+hB>>Aq(v}Jw(H^3+pZl_MWyOYP` zxjO*suUipwuq4A_;@>Au5Fi|&w4{0}MnHB93=Lm2G`xX8OWK|Njrx=cBqn641+OJp znv&2pKOa2Y( zp{X@R;TN?yOk-&W1pW1&;>;Tgx##|nJ{tRdm5BmPEP5oAM&9^tTU3fPzpw*CZb)F7 zCthD2Bv`_(r#Zw&Q%U5o&DjM6KFjf_(V6d%S{-J1eKxkrRC88JNdRvENT+mnE&6dq zZmIxmVcOr=Xp7+O%ggxX_8Z<1hl*Rqr|o?7;EetsVf?klb^$gs?E)mw5sr~WiiX@n zLp4X=yZ=E@ig6=XY~2f)u5+1$zevnaSdffS;bk#8TM`y8>N+2 zJ_G@KUIaQma#vW}$T@!cy42!47(>sh{9VRiTq>2D;TMu5!(Qm9L!~0PB)!&#NyWl8 z7)osi`*8ANUNa7rMQ7zY-nJaJNR8nJ63jN<`N+!?m>TU^|j->+(6v{E)@wRV_`a+6oNt}pyLfH;SxFs zF*_cd&wN@hZ>6ZEy#A>h-*V`X1MSjV0^U$!Axu_HLUVHZvESJvL%K^pW%+|gH=90G zRa!QT@Xsr$cfwwx%Lm_UIB3%8#O4qXO++M_)fs{9rfRXA4#&!ja&&a55kAxUzv45W zJuPT=9gjU;$#UKJAW>YfnJ}|V!2Y#l#RkwEo4GbMga9cnwuwlAOjsHO7-|hZgw0~p z^7F&DgJUa`GZog~I&!;;N3%^~Fayttu=z|(PorYaHeI+_K*j7##d?h_xxpxtt)M!^ zh_gR6J4DXP>q`OOH>n%T?hOi8>-{z;tj%0uO}#gW0^Kpai=yA)VP@ypHg*f94h1Qc5}8sxyEY z*xnf|*;&f;vEE&|;s>;;AG5!gql7Co;@?y#(NG0qVWp)S&ayA>&h0P?_4rM+^%Eq2 zdfs*i?Q^w^yz*VkMkV7YF0X8zA<ADhhGuZGu_RDEY^#Sn%s)VQs!>-GfcgU14~^^B;Uu?ijrcS{?$YGRs&vx{Vc8c*xy)If4d@i1 z68$jz5&5U2rb-YA=5bG<|3K8`8{K}H?)-jN;&Qal)S%C(XSAdm1QQ-Jc<)dF! z(($24uO1P{XC&8|ucl*XdCz9CC%dcV5Oz|vxby-f&%^M7aNY0M(}TrAR==N5aa+=o zi4)%vxE>z!`Pc5<+#D+PCdj|W;`|bw+0(VVpNQbW*l)2G>9_m|++2~}`#j>(8M0-8 zd$OjAp#*=Nv zn;2`Wy3k`0MoI(S+qqmy&3+_HlKfrS9eC+f(Lp zmMjgJiKW>gn}7HoZ4UG#yqZwlA(UZj`O4Xl_ zdza!_alhaB&UO5;uZ_ef>sf2o%sn&r%$P7uoZgPHogDNN=Wi(c8m+1_EjCIG(;a;2 zLS*2DE46?;EpFrW;}8m$#FwqgXl!@HDNq=JVEa>ghYPtB2M9;zn*P0|I27P`7FjOx zNf?8i%1$RhP9L)F+u%S*+iBHjk#Ew((J}XT6UCP)OmiTIp`Q8nt2sEk7gjjDUkxSB z{lZijs*M>+S9d8$RXsE4eO)l&4!h+GVNXh6C{|ouh^^#!z* z^OXprz34AthZVBO(UOS?=Q9K%@!>b7dmpk#gA#Ic*$-wve$+o&9y^9_>?izM@y7!W zRp(;G>%Wq)VhuaahNY!%<|qXQbMwVsI?BQp?wW3g*6wAMELV)#9ZvLO5^n=z$G2mA z7vbb`S_QeOy!s$7bpelzY0{FoCBiaNZ_{xl9q53orC1SNW+uvI{?C`Hysx#^L}KVh<^{3IFUEU*3N4SqQ+a6UU@Fv% zR`3IB4I;!FTRUPW6XGh7Lxp@J!br(>Zx}iK>HxfxtNSkULYi!!?jNc>v8e*}{LAV# zX=a<0SlLbEQ)-Gl+v7^9RKA7Fe?qh#0Bf(Q)f>EB8|Lkio-y!nxY(s>ulKJ;zag)J z`i1KPR4fn!^|tzN?+t&kJ+0)0G2A2PtlUi}E7iBs;IKE}Iz=M+8L8Xa%!i6H^+N0c zS?K6b|LmfzmG1NB=FH5z6@+}V+q~v4DXICJJm1uo)YG=$1x0CYxgm90FX=T}l%dgm zq!2@)-WxVND}D-RkrCXz5XM+i>@O9S9h=csytI*zJ_h^Lv6IH>WeCZZq|u@;PDrMr zT4)hVG_to(sSS>Dk4etzx7nfjXyE2nqMJ+!xe9w%;`|TQRa+#AX^;cTJBiNhcg4W> z0$fwp82iPa2&IGzcBf#c5_C!@O5ihg!F@_CCwtmJUgK!9 zxmgtSt9Ud$JGZ!4__x^Tx#IW5P&LQ-`DdUb^~dIHAz;8jVmY0Sl2fdIikcx_C-R9M zy4pJPiP6FyOt0+^4h3(e^76-g>z+&HG4iGIfy~gQGFU~AM)2LS^Vx7w^~vZ}p&qU& zew>~h8Y1~scV*1;)_iEC`((Y;(FC2Iug765V(QOg_3^t(_w`8{_nnvfQ>!C(0v8X4 z%zf=a&S%VneeVHHKy`l4y@3ZU>otZ`1Qy6=u&+};UU@Bjb&E9%z&C;$3x zHMcXC9~9gI)Cmw7h4uCG9jK{y1%=#IO8-=V%@wogSqZ5OrFrFwznO_oEIW;*)oiDA zgrL0c`>Qb*lx-|@-gsWBjVw1! zoRNvz)M>Gnw1FV5U@-W~z!bPKAm6F`aKB}jlM(mYZEqrq!fFB}#q`or6HTq*ApL}% zv1$w$F#!7O140n*_97+W8t3VeIJroH4~kNyDRGC>{H|kwR#D)z`hmiTqEJ0rTO;L# zvi~oXTI!F8jivY%TTGrdWheh-fqq`sS3?fA?>rSH6@R(5-7p@6py4zvLp`>!(AU| ztQ}~J-w(>GIG=&6u=@Wf4yV{)fO!KFt`D}hD4mUNN|6!p4AuaV?eX()58<%hi9*cC zz>vD&G`A5=AJbaYViSF+&W8&!nnc(7^!FtHW+UN~?yzq@5U%9Bko;Mn{9QR695%G} z@>hD$C$(?v?~(O(pH|cMMq7CrYjtig008R1^z$3#m;TJQn`%4_x)IG&J%lSeh%f>Z z1MU~7S4XJGs8AMj&uqE!dQ~?G>$=yFb`94Zxr*|o6{D6w=zDRw z2jsWqrc-&OBvfJ#ctugqo*8gv5k%$Jawj%(_exx^Pu%|g6F^cqh`7U5 zY`!{rLE(i}|JvHuB8&O1*4r|U-@tPJaRFK*Dc<8;SG3hwY+v^kcS;a4NZWBnZdAEN zKX6rY_(vABf6!mH}=Ifw(P@FOirn!q0=F7t+@@R&(~l|$Vh=SU&`4OdeqPciZ|Id?_~JI@Bd%>?`di&u)7 zLwMu*J2KtNstOft(Q5Fx#%;TGm(2#6*>o2!y6NY1L9j4%kDn5Q zJ#q6fig;ruY^Z^nX110gsq(TnIXzWF-_N~5a;kH(=;eS^Nm*L3MSS;A@r?9fc%JaN zte)yTO$k4x@*|wVIY}eZZG#uG4UOLz2~ImBPY3rgHerhACo5Kaeq^}PqLt`qt4ARs z8L`rI|FS|)zx>%IpC9`?^>e{&kZH>!p8>TyvyA_)Ml-sLF_OrNyoT*DH}l{9fl^hi zXxcm8sBB6|P*r`2r0o7i-EiS_y04IHD}^?dt)Hv!3CxkjKST z>goZrN|Irdo*)`LeH56gh4R9I9w&d{;e6gxig;A=rqa-OIjs*IkEzW+H$Un&xr8sjA@lXD~ zl6tKjLQ{slhh+rR+&4dsr}8H9((Xg2++!2oxO`U*9EBV^?fag6tg8Crg%N`0iGoun z&3R-*U8Xg{E{~t-{q+=cbJHFo3;5uj0>Ptd#zB z2|dx$y%!7Vb+v_R>)?fV3avTNHa!fw1+OAe&-vh|#iUf1&L2ErmG$spq!Vbc7A9roCUiUNVk}a)S#zeN#}N z@n;8PJ4$}=nJ#JN?eh}2ePPe-kUi|9nTn6)*4!P^Mz6DRDn4*igdJ!=f7>RLggOO(H?m8%X@XN+e-djlam|Hk5Dpv)zm_@ zw40*;Nw@7+uT0x-*$;0}d4mfyt5^3pHK)9c3q~RTUBVM7KCI9k=LbV(Jo;92jA5Vn zqt(~@jsu|*%vudyt*grrU}dY~J1W}5w@0y78r98LB_6E&yrs8Ls=QFcuI|Uazcr1; zc))SCdqCPk5Y}|GsQ54~SmI6iufM$6&@{fP4Q|=y#`_ z7GA>r$N>vsT^)XN_b_&x^&W9HI=a%f9Gl`V9`nAy__1a$tOX*8~7U4 zA?KAQT)Q=S{_n}2y^?cpo_$x|o#)ZlYg4UM_Q*Z6J%0a`nySq%IcR#cNQMp^VYeCY zsF)VzFy4OPUaVmsv#gF~Ug=fxAKu7_rR?NrE3Ju9a}BYVISt;*=@5ysJ4KLtp_uFG zvj2U7raBq*Ogxt)eU|FK%W#6RN9y?E%LwJZeA0%c5=nX0r`(@o^@bKz{2-q|uvJd&N$Xuf)AE~qMg}vRGD3> z(P#Ou9POMr*%VjBQ0$6*8jB)~$3nAEG{3Y;CxGtMwrue5K%Pa4YQan2@)B;wN8%gZ zB2hZ03Gf$+7jWdgBDa|Yh1h@|m&MHSuT;nm`Ck_Q@pe+YUK|#?AAyv`V(EldO>XXl zT`VDvqq3TM_pFty^YIYtwUks<^a60HHSN0cnwWNDqFcXk>HP?*(JEypX2;$Fne&x^ zt~JpA1>>j@Xty<rD+PAT#1F8Btm$%$rO2ri-p=+NhGB@ z`-G3WkG|pU#W~cwN21)yHLbo0-)X1Pa@b~=Epo$QxiPw>z|){SZtl9xT&c37If^4f zeja-CqkJ+dZ70cm?`vd2?nz`gjCG@KUBD`Z_52=jlEGs?iA8g7hm5s^G@xjmX5?Jx zXq^&$-rtj}OPf|2ENh9m6V~>BY8xLTkB6%=sC>D$l)%U8R$As(awpR2Q+mAWR0s8e zqTHFge(xN#R};2Z5ub;M_f2(x{_Rh*I*CO55RyL~nuofyC<>ClZF6s(J`MozG)lSg zDSW?{zE_{2Y(;_hpWX44&dh_`?xLrYQ_o1XJ9$>xz3ng?Vjw;>5@bgiNV}hxs!l

ebJd{HuC@#_em|DuBQK=qE zQMtUZ3UK9HX~B8ynDJ)C2F0Q-i2=#nCg-?1hT^br^*1+0oA-VgpBD<+Ssa=0R4N(i zh)(|L$g%KMxaE(F+s{ktrOKI9zGF{NbcD$N2u)AFMdGmG^nuyA8+A^RsPCQ2-)v4I zuO3*JtYlreK@542FWJ7fH6CO)oHpG&OAG-gKI~POyIhPz#oN~fFQtYqy&R^TUeNsr z`B{whQ?Oh)+=+y6m9M_AVj~Qov9j~k z%0>40vFLAaM||A^63X^=`R4ZreV|l`@0j z!++QOn+l|LP7yRml{(m@y{UYjrECx!yhiL|0i-CwS;2uZ@}#5fZ{T*C@ktMi z3y&~)av4y@U!MrFh#Byd2|-j*47>F*E0oZBacn&$*;a-S3sFn-ok;z8exWA5w#3O* z6q0Mpu~)(!8A<0XocNxjyQObF<}4>F*cGe6$gGdU%2tZB?K<)PBlX;j5O=`&qYdc@ z_lMIQ-4eA1moG1E+Q-_BjJnCtj)V@#D_=m*d|p$tMDrB}ZvZt;UZg#+q+q3!2)H(p z+1NXv3%fPV?Q>g=6Pauf`ERlENfGATJOwWv0p07IS3%`V|I@2Ze6;s-olQkKm2a|b z-JlmqV_qNsp0CL9yjBPO!rQ(8_wN&bZ~T_wx0`|)H55yeR5+1|t@sp1s1zdvjb-@1 z)V!F()x9CUKZ|2_b*|>Hi+DI^B6jMRX#p#nhL^~lalZkv!Y}7YjCy&si(yGoXY@X) zfqjMYSS!++&=Dq|yoU3^v=MNiVmE%oviOYXs5eYW-1tYedX#6svBmJramEKIVqW(a z7O?EJdY}UV{sZFt={qYlaq5)!d4!pjFtZ?CO7d%bij2p3;;GN;HEukiYw6$5r;*Pu zbmT?=o6HeDR)Up^~0SOlPSuUB1{9k_Cu6bmZB@*gEEr1syk?k}$=I{ki>3e1F8 z9i~)YoLAx$4%@tcD20Hlc$}ULn)?0o6zgzW{BqK1f*F_YU)b-l3!+P6=*)^WHLS3~ zb|`gm4B`LfEOHOHb>bU=h9L-;-;g-io*4LK{F-V#_S=>9t0|&X%`5HzN3tuPMY_!R zkkiwf7fL58ub;+YQ|znf%lv3SJc3X7T|w3HH$bNZf4-io#h)^Fn_sTc9~+uNH0=p} zrSk1ntt(ufOPyGz+_fOXY7~*vBft(Foz{Iw?NjFO@SQ#_-n3^nqfdrIo{n%%&Gzft zAPT1UOMYQ}cY&cyMMi2jijgpePDG}{o_4JAO&*u{Iegbo?W^qF9X}!TJk>zOc!(*& zb!0*XNMBL;tPlHNR%li$&1{2NXRgXCI@)EH zE+3O&*J|^m$Menr!1Qk(Y>hjASxI%wPKb_>4lFiAgx+mAbTY9A8j+!F!By!<5bV642KyVp(FEK zUMcKlT=E;AKU7c~m(7#26HF+8mVzpm$NoP7TE0P{O%DV-aiVjVU7OAX zFKn)cp3>37RFa|^gfC3UIZQOUI%M@IZBNDS*q3(}PD0*HC-+D$(_=rpI;t`byQEml zNcJLS%=)n4;5e(2dlZP4v^fckxzcDh6*5X2LlfFz{2u}KT-)mhA0X`0I#UwJ8M(o* zNy2*aVIv7>#aO_hQ>3ZGZjpl&XXJP}^Y7$;!WtF&QZ{G>KFNaH7oRD&4g2M zUw()T8vDUW1cplf#V>UgEcENqeS~BMfbOE@I`c(8A=7WX*HtF?c6L7JP!q=W9uJ_? z&^7vHu2$0s;C`ZL&w^|7e}R&dwh0~WqBGES#6$K_3sF*GAtvo#3lnic1y#VD{Q#lY znD83$l9@rkD}ae+g}ej*lb#9g?lB|O*5cvvgoY>P1+D>O{)i$Fjy7YVt>55zCvN3M`aR%GbTnF~Sz?98 zvO-4JlZ$O-fI@+$$A=OGw#Rc^?L$XU7g_$ySpVXKQ+tbK&QuJz0p|{gbbbg2xZ@HP zU*D4kGrof65jW(R{;@OP((#HeaG^Ud<+mP*s5yo+;;@nz#Sn?Xnu28t8fTF_%BdB}w2cN_YiFbL?Q-n`k8!*~-Q6AX zeFdbnI()RyGO}U&(?uey>D~YJW+}AYHNK1f4UvmC7{gjgSUsO=nJk(ZELzF7t z;XQ0TTt&?fBXxkuo+f_*9>*c@dx5K#8LOgU)exN1B7{gPuoU5@)bgCTEi)<8JwPu;rj_9Q%c+o6I64BlRq(_;HZ8SF;zPI3rh0y-52H&$%}wa=7G+Q&6GtJ+ ze6aSlrvCyj?6^++xt(clk9m)BPxA-0pUy=i+xXzp)yd~7_DdN_AdR2agU!nNHc3u1 z-~_3ivg9^cM9++;?EvG&#_ucQ}(dkW46kg&8eJE1PGc_APtav zSOt+8TpBdlacBG=%K4uccr!v9AquSR^*-mHzbJ|sm|*cy5Z3^*2D~H;jyrMqbmEMD zI^LJGq}Zyy4w?qt`ncB8c`sSGx_f5qUogn5vHiA>Xi5O)vXu;LAHtV zrnglo$7%We^8djvN+48F2b{C{!C(~T`Yj|>g2~EF&ETU8ci#M4($GQr--FJBLv)o+ zB{X?$I4)mJbzRNs%=Yj9#nZmZ?ToLDs%0OR0P%;<@t?ig+t@&C?Ad1RfN@Cpsh>rr zI58;AC{4iBm&3%XyAvx8|MS4f2+@qG(;)gjJSZ+8mkus*1yE2wUH=J7JpuDfe`rc- zwScJd4{@d2)&Uc@kUFtaqby#P@xJcqyrZYyeDvWDp`I9B_Qqtv140$&No3o@)14Q< zeo*&&k=_vjIHbJuqdbeMS-JXH=*HOJg5+U$1AbP`qJkEkN#eW0~eSG%6oqVzu)(^0@Ivp)h z?fE*$mjh%4kgpp^*gbbTIM~YIVo5cUDHOCTk>Fq}`bhQRR~Zj4BeSR4klyA45ezGK z+r|lS^O)6?>Hib#pY+WAUL4v+zPuvW3VSr(Dp@X7&if?MT+U{NN~PiVD|e|9)jX=B zE2hlCwM2!B4Hy7!N5H6=i0r#X=7kKv2B%*EZXVI*$WI2-xG&|?RszN(BUBl6PxRL-VV{L7*q%J{^NyLj`UhB9XZOX~(nM(i z+LrQ2e*jb6ZJ2-bsshmB-*f}uDMBla7|!00^}p*ZY@|n!;1vXRVsDGgJ^aOWIPDUT zB~u#D3hfT#7axq})YwVP}w zfAupbLgHNzu;|?|z;KkhS(3tOsZ^iq^hx;M`1#FeXY8%`qb^P#jHBmevip4NxCXeF zv7O41U^}ZP$LvUJngmOUEiBrWfsX>~mwHveqF<~3pO{HVBJjTRn|#bgLMMyft0}Il zd8AZasn2yUA8@ThH1fw~PhJwQ-5P7w>YS>qu63;He&@j)9(kMeo}-oO@&n-u6%X~6 z8vYk2cytyhU{Op2{qfFAYp1nKX9E?y=?kI5(h3*hx7=AB7V79dB2le{o>qfD!5O?h z1by8k#q%~R=(Yk*Q9p;_j8;c1uwzUg6>f4qUGPEI;|l zPVxu*v%K*Gn-H68gM?-0;K0uuF3PvNLM-@0OP)O#Wax0e>Z-rWhdcZ8MA~mUdq%U; zLjClzlv%N4I;G;3N{5RjBgxfzm175tkcQALa!SPwJ{)!$1^FJjHSX6&WCI`t_b+Jp zG}SGu&v#N;TPNREB$RtGRe9tyDdWm*^-BNNDh!`N!9y+O80AVFid0}I_{X~4uHQu~ z=dIyACY{xA%}&gZvuWFH=9ZDD?ILx;PQ&^pA^_Zl)FxkQV9eh3XjHI5TZk+s=kkQ` zPom~ByOy>>TXStm#}K<4P5t>nr12d^X27VU6@jKA?&w*)i4NvU<>AHME|gd!IXI1^yt4#bG(RN{iq&jygd#c_Z79g zu47KTVWel*GQ67w_3t{ikbWY!P4)MqOq+3X^4+8-9uu6I3w zumxatBnc^5qufVTKiopTBqvWprY{#Z_I9k6nO+kf*YFF--mTO7`G&VJ4SuDlTBS4% z#h&pmcKWmM8t@|ZPX<(LRF~~V&Qyw+!I*SD&GX7QuY8*7@V5rHgV614R*+zYWgw1l$*m+bz}ld3=YoElmHa-EH|BlK@#Z2zeSt$)}>yXM~GMK)%OKzSqxl-2Mgy4l=)4KEdqkIw!=jz&4bn?WbAy6|p zx%=zaFM4`G5s|^Hws~rA)F(g>T3VdZ{p6kP4d!M4jSX3UH3PR@T~pei+Pt7Des{VrqW5KDC*}-Y#~E%+`83A{BCJ@)Bkxee)jvOo zx<*bv?&>UVvITKuGbR#-#@d(2-?=(y;koV#x>;tS{#I-4NkzrHc_AhycGdp8sw67ov0W=us*vnDSuM z#{6fMtX9a(p0~VYsma$#WBIuj<=}coSKH9-rSRpJnnR-p5!bI@{}dn3D0u(AbE%mO z;O#xy1p_RDJB$rYO`mLS8y)?1e%HP;?iWIr`Pmg4ni+c~A80KU@H5Ii96nuBlmEzl z5w@UaecNqpDZFIEd;?-><9a%P@$KCaosoKplrbjKWNG~-C)F7dPkqMC@YWrkh<>#B zi(l)TLPRK_$jYz}a8rsU9u-=MY8^hx$rc#iJ7E@SHzS9B_;;xcBQzn`-4i<5A6uVm zzWy9PPTF~zkGq5zTfi?|70v%uYdXxG^V>DLE=u6=*2PU<<{vOH`X{pL9dA`!>U0Uz zwtgl`2wb-e`Y=8iubN^DhTNW?p81oP(jTo|K{N_qRh9A8kl;^*G_)SonsMi+r@lr# z>>&&`b*XBW=JLK!_km!>`u9&%8T?IZto6>dlm6Ph6=m(Q8KP0p^=dLIvQt6P!CP0x(kMP-)yuM2MTd znB?aSMtX?2@%Z?iu1Y*-k`&$9tM?p3)=K~q)3(KdA*u@qgPdtO}cxcA32mmdRd z$aeUG&s5D--aOE3>bW2m>nsG}64;TG;^h_D*q>|JLT^vsjKBhG^F>#`K{|w(MR;%! z@9?Y>-$sOx1}!APD81+^ZTu*l{PeoH-16!o#y)g0^|}yjofRT!weawQx@buAJ0TslN;%17LW{i2Q@&^j*{rOUt3rCg*)&R& zSttML6>9q~mk-8cjM!MKct7dBUaV%=m0%|qX{sVIc+jrxq8BMefXS_ z6h{Bq3`2Mb6=v4YPd#v_kx3W&sh_a@WqX{P4E&(mi;Hd4fIsrJ;{&t>#V10IfIkRx z#YVHt1p@mgzC%Zc!9%Xr;V1h)sr1*>Qo++s=B^9FvU- zzj)!ybBk?e+#t-wa_e)8te&1|QqrAacODj!n$htwKu2S1N)B(81;{BPT3@H5%GjFK z(Qaret_ufuNeg&CT57miYCEizmFoi=CTfFzKQ?ymqZ{Uta+&93zkiMayELI0S>aeA zper%T1hb4JEQ-osD_=+(59cHU!}jBIhcQQ36hZr5|H||E^ZRX;jGLuyuOA?6%CZG^ zvY9C;#UbE>%Lm9dpA_$oA{;sI|1~(rv%66YV_h&9au)j0`#K{;D{Jy8F736V)ma^d{Vt4_>vwpVpYtB=M^d&816?0|O0? zdU8naEizot?F}@ff+dFgu43rl2l$pfgf&}=TD|3C7>mB{jePKBCVaO ztX8+r3K09F*3cbqNx#xyx5QsZ3>c3(bPL39wcs9-an+x2&znR)e-Ux;`b|1CF4fLM zubu< z9{#jTxBRQ4V}}kfqxbD-#c=-Ya#F3BW1p#R$RZ36RdC;%m*5~<8o$bSkeN_EK z!>Y+Rw|aS_hEpFP7p4YYA}~!{tLn#-4ozeQ3u}is?>;dnLlys?Nso`!=^w_F*r!t% zgW4sgxku{}7)JV(F`4V zC8C=>A4Gbc>^6BUVl~~0O$fM3kWVTe2?H3d{js{b&2(E+IB}{TcO<%?j+5;@P7Hjm zmv?BcXiyUDtlbSSeP^5o8xqSB7j4oN>`QFSelga{gJ?Z|=d; zfK3|848NO+WmWH|HeeVw$Nn2saxETqw6L@vuwnB&-4f+e85PsuzT7ujtMx#&3#U0h zJ!DZkz7kMfvhNFh)+CBrNsCon)Dc#6cdrFD?`eJ(jo5}rqoZ~M<#OMG7#F6ZE4Dp2?J1^w4R&OAhsH+F(#{74fq7#Pt9Tk<6_XUQ z74I;za8!G&@QI6wpPZsvDHw+(T`fS2hwpYK>R#`*c++v}1N7rZt*gDzXOEG^f(Q_R zdP=H)#j)TPq(hJEiKM?Dh9yOqTMCO9Nr~%A4)1{4+eW%?fZlYD9oZ}Rth&bq)E2?4 z8m4f)P-5b978@kk;ewQ?^3i7aC_Sy1aPjk2Nr!8$a>jmFzE#2z|0LgHVBrjED zdP6?GL(XsadZg9({OA^RdX@h$+1PUp&x&jZqDg+Jr$bbB&N99g7W5k<1vVo{G(z6G zTtI&w<59xW%l3f_0~Y0T7lD+W-?>(Yzs3d|lIlPDBHeTbN-yGZKl89)0pr&5G*OdU z?8=U{Su0BAr}7{8{zkd9wKdZ*2VhILDZ!4%qyz{2m<_)(Pt_*mzU~$?Zf7SyW!~%~ zD|ZhO7WDw1ph4M%hpN9O0#3P1}Wk! zuY}^fbAeYa{spwrQTsc6*CCx*t_)$JAT!x`LrpU2g$Q!Kkn;yb6GAum{xcf zIkM+@P~UpU%w_?$0gYGXI~~+5Xxc!v(6Fhds~Wm0oQ*Y%J>W02A9=@r$iQ@Knp;Bg zr3liNTHTS5U`Akmqmw*JaifLRZdeEl(cYD)`qphIr&#M;a2TGj7;at?%x&b=OQ3<9 z9YYtruRSaOAvoO}wyKuvdDUHvdiV^bJ40Ky^rr*Om16o{z*m&o1Pw^QHdoVj1FnRu zs|=w&mDA{;(+BztX44JIZR&s?47$>B2EutjWYIJqlRFaq01As4S{H_&1Z zW`P&hGTS`MfeRt6-v3bnIcQ-jYpyAqyE$?Q%4l4J9mWH zN;VI(!i2x`l#*#Q_W1zx)i_9ICMooC%kTIPi zo}@wSauY!^{D25yZqg$f!by8>E$j3%vy?qWn5kRHG9-Tsl?-NJZBI9w+Gw<*h22tN z#>(EcX|{2ZspR3KFHFvq6=tI}W(lIrJO}AL=wZ7^4b%KQ^p?W_gyx&|Jfu>YNv~x{ zU^fQ8l9u-Bzmm$3;EtY{4;e5d9eyl21KB2+>)V+nWPx;o_d<)ZTUEpqbD|+*r~1yU zY0|&X#$;Lr;x*kxrAu-(RCH>TbCihChS{i?yerE2k)$;@15ZK-)qY4G{lEP9n{HLs zOFgLrex`_l6bCSZEzgJ^D_g*7E1P*lx;>pTz_VWB#-3oB`4IV7abEAl1+bh)W@w)) zh40_{)X3G=Y@hP~a$woxVo(Yab#2_^ex{dgWw$@m9N5Y(n-TJU5z&S{l6ozV6Ax}< z94N1T%Hfo+CgTnKPAZeq-PgTxUdbwNUY>>0(HSq-Yg83>2UE%e)9?>;7_qX#%0$62GlJ>8xu{3-z`(Q_y)c;|3OIUj+IEs7X`R z$SsyV80QXG6hR7$7uk>9`QMJD=8~n)LKG@q6$kQ{i-W^^Fzq)T^BZa}>$7!OGcgDE|TKe1DltetO2;aVs5#?_D2~_yi*dlrca6zi-?bFr_<*yr@YlzOa;$ zMp?+dMza#BL8nyyyC4-mM!!I*_uq9EMtr$yareT>Cdqc5InzJjnI}}z8xo{*1U*j? z&i=I3qtXBOKq22y>6~0JL*%0A3mVx?daeH72K_%;2FgU4;yf+s?*YEj+q1hLsTwCE z+=c2EMh^1$S@;GtoKF#+{FHwu$KJ#31~Q?K{*Xl;U=IJZS5?97lVG%g##eS3&Fo#CV}U!fF9p3Q0M>OPW;Vjzy#d_&JJTh6Nt0`)cWC2U7}CYhK%SFKoSjK=+gApP zM46nHqd!@Kq2&`@@=K#}{63JVVltV~W*!B?JIhsIA78!ugx9gy<-D6pLh2Iy{B$^k zdiN9cku@vs?)-{+`c`P{-MoZXQ$J1b#11~R^GY8AU;eaM`tABwmBkWs#3voj$4yzm zsRLzq)YZCbCNE>}hG;TE=pMo@`~GWpks2n}##!5j!cRngbJ`>uqCtM^iRO^n3fSIdYlGPooL4$37Z zI^;or8l}HRS<{O9AlxkpCS~6wE8G47S}vF5rXi(xiXeuH4B1$-J8fBU3w*A761=m= zQCEEZodjt@DLuCRofrV4C+?n6Rl7zgWdObajMc;HJDzg<(aWP@{89Axg^Y~y@amy2 zP7$u%_J;hi1fRdJN3^5^aULOY>kx`_{3c9cI$1VDL?jdsD{2KD}bKdSfS|?^N>EmYiHErsnj5|Ht_xl6n?#IQfVb*@VYSKs;keGs(Dx6J4g{7jp%Hr2k zVc7m3gSIy(&IK&Lqp}K|n22}O1Dp2Kn8=Qt9|(Odx99*4_iobb^4;wVFb#VdnbS67 z?!|=%55No1VoimwmhX1he+iG!xjCKEX}vsL%3X`A=cHU%CH>~cx%);&CoO|w*0{k3jhy|bT`m0$f3 zqOMyiSY$nR83N0~W75@RJv~Jm8ylylTkLF>2Iid?GYxIF=H`=rsHbLU!zdpF{~*=* z_}#lIxTnYAM(2d=(xBb##>P9p?E$YiJiVKUSs4<$;VVbQ4DE9NG|GqG8BwaNQMmyr zX8^TK+BPqHqpXH*SXa4lgC_oK-ztAM;?1X6R!exYs&MALc1K5p8l|@Pi!5HU6_0r+ z$jfW{uX^m_omWa%8xo(ZUNF&;ba$_`S zdEgG(DbF=2r;HDXJa|)~S;Ii2_D>r^qgi| z?n5#%o?PptU_|5*@KN?71J>Btm`K0GzV3VQ!mRoOW zXLRZ1>P-nff?q_MpY#)yY&}!e+H0tU=+Kq}E$vPd)k9}%i=l#|lI7`^{B^S3SO5p| zMT7UnDNxdFh`kR1V^Im|*`v(Gqd1Js7C3gnw)@$fSA@uZ=uLV*G-&sNA-JBf*JM^+dP|Aa+Y8`KK zm1-ebF)n~2B49{4{qC42Qrq*Wec@hRoy_|2?idT`(qJekCRTZ~pE!MdU@dSF=D6y? zAB$TchDY*xc03QeGqp4rkn6M>?7ZGkxjwo(ipgj!`FlJ(1(s1= zPF!lYlxuRjMabyj6JDYI<5iQ%4j5enQflMTrAzG}5TMJpq1&pfFQt#(s}?A8M}2&k zyoFX2-?5-a%tSKw<(qBLr?Fr-7U4UU7`DU6_Hb{UrGeUi9~9i;!LE|_;9+eIb#$KT zpf3Q}{dE)19dyN?1+50lDACM-VXJc7Xg|8G$PzM{6y6$-nymF6OckI=PR9|7%WlU8_Q} zSND?#Wif9#jBD}T3p8_Q#*TNM+Z-Qi$jSmid{DJ|U=_P726{D{XK2BITFaI%W*7t1cUa$P@|pPzT0Ar>zz=~x;VJcxDqy*)VKxLqoO5E zJ=_~>`4>e+wrx??th}xjmcxu64_G8b%ez}K>X!G?=bqXw^prYJ2d`fH+mpUWv0~r+ zNt*ps&@|ZG>#>8;8t3K0!ON31RI8ufy({&`R#rxoETnZbG-X$N+z0wmQ>s(TnuX7m z%zZgLz+qAC$sM7C&R<=m4%i+360E*tQnE>MeuiFLg*0luAPQu@9&X93`cuyE$3qj4 zPN4QSG6Ypd4A;BDlqw%{f$8E*hD`*8T!V&389EZsU9z33LuuLm@- zc}k7`Juc@kFm~xixDx7XyqdJc6+Jy&-a4%tEQ0=!pryv$b5I$XO=}O$)Eox$nuXp? z>*<%)iJwj4+&O*#odX&xP=w~{z}EJ5qXKOCuBbS|amgO6As%Q6Y_zyYF&2bJfDrog zj%1}Lr-WujpNd)9?xzdQwBJ~98hgp8aW}EM)vq8tJmv4MduMFj`n=-9E1nx%V+3{G zLAJ1a#NESzf;T#3r?Jd+j)xmn&VLV9i*S@MdVB#Bve*~edhMPN!u!P+pu<0M>G}Z? zXdHj#nW0zByHa)FQ^>qs1S)2IWQT^_Y!iyITi`i^`TGo|dx}{Z> zi;3iByEzzH(U3$~ouB_m=dm_AkZUOFwl^|%WE(93Q!5%P1Np{ANcLT+(emA=b>EG_ zfdK>N-!_n?BSG#FIeFr{fuMqsTt8vyqLBnR5K)sK+J0QI&hdMDE9KUowilzKtTui} z`A)V+Y&J9jYs_y{$+>5)}; zhA3EKYg7;+Jf{uwS7|%#rU&^;;VAG@6-mwG627kuwUvuVJ zgPPhZuv-Sel2L@H-kpLAsA${o6}tlb)v(>an&Nsav072Iv_Aif(y0CG5vr+ZgxPj$ zXE*zBcDc$g(XsA#>ZHISBzvcL->j8}&Z*z+*rLdF)}Q9m>!}|S*?dwSn;msFll2iv zgpOGb0xN>0m~4xADe_phzUA{jd)W_vU;t3bwC%ouw+v_=56m@TpvY1N=&2p!Zvd5o zdvGHPmK~r|Zsi9y-ZIy?V+s99HTg#a7#rNnA&y#*;OsEwXS&PD5Cd7|xtCyLRguj1uzj_4|AzN51ZN#&Od`!rwQ%YA zQL$Cz<vm69|uuB{)?s?o1W|we&?{F=giayQ%fw>=nXArnF=?bYyfiVK(+am+Zca zrx(oMXr?>-UTJh7ub|MBeKa&{4t>a|cf1kTLbFrB$g1Hz;2*yF$nXanx)Z&0 zM@Oty0Bz0ioBOe5C4kd;Jl8V!fC}+@xaVbE4~*Xb!`^$oH5GLI!dOuhP!W-iAksl8 z(u*RX^j@Sy>C$@(5EK!mN^jCcK)Upvp!D9Pmms0H7wDCV{8R!v zP0C3LV*p$#b-JoBznHMDmdP<*zU+kk!{}dbH>oBGtSQr(Th)_vE!^`mQ2^^`k(PE{ z*}Dtzp{uJ#62~j9z%iE;%(YOD1k99vGED)Z$hw;CuJBTe?&BK3^T1@jQnqjR%Ll>p zSzZ8TCyZURj~ zaLUe<4w#MB{Y_faoh5f5^<5p7C-d{I7=oumC+Q=$kCwjE9+B#BBo;|e8_wtZ$B+=$ z0%c@oH{&B$2$t=2i)ccwQMI9_YeSt^20BI48G(_T5;!R@wck98&}SaoW&%*dYKFar_OUftt4fEw5$#lSaiabw{wWL4G4(mfDb573h^E_tHHt3eCk=BuNB zr9AV-fO!$-O#(4LQMb<6CAuJ?x8UhlwH&^Q$Z#{{7PKAQX)0rx10O`1YE9rKQTMag z-s8VqN@u>U&pHd2TqPWH>(-&VOxM_o4kD}!{E%=n{`cfyaa8C0K{n%-XDC4L6cf58 z>MZNR^$4fFL!0?ajlcd4FR+0h;STXBCru)aPyrc#&(71c;Y7!#48+L>wkiC-tOk}Jx18V|A7wKGg0ZL|Jd!v8)^Vvxo z>psAWhXeE!t>isHv^&FNUQ{+5+{0SWCrKbYNbo)x$?TuovqF&Ds}&WFm5 zzAF)*KC(Dq_hS-<@IS7sbnMDA$3`KXaR8F!gJ?!=uo{39Tq_e#}TxZ;b=0(Nh_d;yk%H683WOu@owr$rkitlp#VqWHV;eF~?F zpMaR%kb>rjeuT%ECPa1WDwtWOnYvOuAR+neU*(a=QA(PFe(vEx76WJA7D+*)r$*v-hvEWDj}V zZuH8Z1LBwTWdokaaVd_=ddS3|!tH%$r_w)wn9QYi)dO<=+53P%GNARf0b|OL9Kj?~ zpEf;dKP$+vI@CL?wE}7zi+vzS_VY*wW19Fr*13$Vv&z%2crVhq-6qJ~z!3l{;D zPLAcRMZ#7(lo=XzW?^US&`02tj z_iBP&*|5``KdN(=un58Q_Eq4bV$;vEnC?a9glG+``Oq?i?xEdaU)-1b@da1EnH{)JZx*MfGWbQ&0$26=x5TIEO8 z+Qds3LZ_-D+j|WknC`3rSz?Ju-3-->+-tuUe0jc8Sg&O^X2Ju)DPv6V-Bz9o+re+c z4&cL>pHqEoQV9Q6x9JKCX8)B4*PAynm3n$@C6aZ#CSv?QViT?o2ol7}EoxR-${#x` zfI&#OaUT2Zr~o{imahfs*l+`Z-P^-VAz#vJ)uzccC|if__*=NIov{GrL0DL8{D_vt$!RmfjkDNDm=Zr93d$zSC=gsr8LE{} zA7Q)j7@a?DVWfkUGrM z22_4&&00vOh;Z5`zuYp+Z+Ir5H8XtPC{6bMvzAoxHq_5`zP72V$aWpt2WE0G-iY*d zFrEAD1g3C=!lZFA>m#vmV!#oJLiy%eR+BW_=4`&&}j$}m*K#Hi9Sy%twu z;)3&R8BzplaJzCHyJFO4J-^ybu_0g&wy3)Db)1Op79ya%LJWN#u3F=Mk&6 z51)LnA!{j_@#65u@uh!+mN0Nl!F?W5=@qAhos=}*#m`CR&U+#nAN%i>t}$g9aGkhf z1jtiEA~(eht4>4WhMh+!V=!A_6#a0tIX2g^FcRtGVsDh}AW^xrMw&3c49A;GIL)-? zFYA&Th1fne3L&5Nk@CltJj1q8tj`NGKxnCWE!NUp_g?Wzfq6MR6z|jClQx)059KMy zorl}D&F3B;1T2~GV0c^k~Htkx-Ou44jz z2*jpnt+Vv2bb`nagq1lr4n@xRX(X`r!!pkBAfm0vupOnuS8TJIOya-F(mo%ti6T8X zNeWhN6{6*Bl|E%mVbRN#_!?&$`SGx&UbTI5stv_>I=BRwBBiDuFfxWw%IChr)Z@Y5 zq9Bt*P>sdce^f)8(3{09jxbc)r+mck1OmS0p|E)JtbhSO8*!2*mW2JdG(lTX3)6?gv;aA-z6iqpBF*dE-^n(++17GKaG-O(^X=6Nf; zmn*ifdJHVF+`P$0NAnSJa$<o&2b2V9PDcr z{E-mnO$6q!PQ<6!fAGXns+1R3-S5DQ^F9pH8B9pxsjNXKm+DVQ8{XW`b1eBJ$+i|O z3g$YWrb)_8*1cRS;|m8T@aMuvExVV6!qlo}p;-o!kA~^We=|y_Os{_(A}B8)3t{!e zcGKJyL*>M@2bc2fe%E^Ic$fc9`{=cDylo4^LKn4EqQ8hBX@O?iBwF62L@##=)20hB z&&OpgqTldJQ>{EclcYAz3*lW&X7`E=9~oD6btwhY29w{sdGyh1caPGQrX#&hz1}CQ zroVODPj@##SjM>CR1C!1jr-RU_%WmBwd<#UkJ zOr|D0n|IZ(@o1&@qi_bO9tSk8SsW=pdQy}@cETvUahWZhPmT20(1KRvGcmmA7 z&U-|=aHGF9=z*U7g!Ukn8HbJuYH37kFr~A7S3)+3Vi}6c=dvuY^xuyZZhtGP?3eWN zi!w<(@U00(0JiCxQBkB8z1sS%E7YMVF5&!4rp)AH#A-RNgXH1^ov@OrTSrKfF# z^}=yAM08fWyf5i;_NF1n!fxxVh{z<>)L35wBJQ+yyw-tp|H~H$V}xD=S<4Jdxt|=b zNmt8GaPqp+`HHqPLN39vLr!#30w)to#i=Na=N7H~DjiTjt&X_y%)@X-Nh9n|4EdJ` zOy~B!R+f??AaH~AeeyY<=oYu4N^8uZP(6~Pw)Mp^+^g1n&KHl1l0z69P(~B!LnhR5 z1=S7sr5zRH?zr^_D4^;>V4Mjjm+}ej$E&1zAU25I5w5PP(?-wq5X5QUV9u}LO4(ry z4BPZ6|3eb6)5rM?6cF!PiThqi6{>l&`o7p^3on|4uHmmp*~VU>tFigEg;rvRllK`Hu)fby$P|J0)CYy@i_5{!n{g+kW+t zNBxX+VSETdqpD3RDJNzePBzzc_;jd^n4RmIH~8z|rC1L3mr_v=+lGcGj~C*0$UxfPpX=>Q!wLR6GWr!uw3K%B^`x z3F9d3B6#d$6_dp$f#5U|pOg;vyf`CAINrE&{^;Xjc8Sr=<_oolKgIhft6>Bs`_{1k z%(YxSm37|sb=*BzG~3C~t2tOJ9j}G^IN|^9G-J9U5Xg=@?sTm9BVZ$j6s2Q#?C%S? zCzPqbn-#u`^zySHsFi}tuFfY_pURLb&su;wO_sLJ_(F&q>sqo`(+;g5<9C6F zhq7W2)7hW4pL#G~Wltd#@nx?dvM3Y+vokhIE|#oboU?{t1N-ehL5|YNsRG}9OnQ5& zfEG*f>H(JLBf)gzx0~COeT75K(7^T(p2Y8#vXpvQFHI63Z(|0l)59a-jr-0JFMl2E z_E^bt-z_kvGb4pKvTg6m0oerjO-2JMhN8;-Fy#1GN_3*Os%`5oZLb1z^g(b8Ow%t$fSaaC_wz`6$FYs|%g1NykG8!o@yF>gM?94JWs|1T*TY@qP3;iBQ_4@co|W zwdwwUwV(YalOS~BUBqMX(4BJ%!Gs`oAmruG59($|`BWZP#Ny)vvdXWBh=l=l6JUA& z5^c7Y+IyOrOx^K&EBZ9&Bl?)=1K_y$%@dA`|^zwwnBiWp}^b(bh z?EYA-sYx%nZ!d-_;u!J29}g$p`AG6VZ)T2$+TTs&*Nb}+Op+0FZ*GOV@${4n?_JOL z&Y-!O7DxqQkL7E{uKh}_n~$_AF$kD{j8mW}*R(ic$~Fq1HvWC-(t}${8ShCV$Ux0y zqKx{ZZ>g=Zzx(AZ2Bz~si%A|3FwqaQ_@@ssYW5TkKQaIrZ&%~GZBa2uBfaVP3Ni9; zQ5>A~1P>x$OPdCSjE?NH_;mUCfh2%rhAig0bag=*cZ|UG4#_cQEp4$-LcRy<@-C-l zP7dxMh__p#BH2HOry|NJ(BqcNk1qtq$WVDt>vsMgad!|w<4?-eJYjo5BFbb>;OHMM zTh!DP4g=2b=>P%>ZpZ7dHkG=`T;1H~iY(?U{^0^VckFIp?xe5WfW(N+;LWQGK)J|Oh?lJ3h!jl&k&_`ohsvX%R19eG7yiNnT#z{Q-35ZR6= zYy^pDI?r|eSXu40C@1NrvQ}oyS!3tk@G1&FR0#qgIiW4>!)H^!Z^sJ^Yp*;zYl$iQ zLgRmi)M}ATlKPww@?85SIQLL8yBwfFW+#(*E4>TgXl(a~H=J(C@G}X`%U{Cmyv53|s9WBhS%(sUMunJbP2pW>!F)*XNK$Zml zv;kGL1)c0e+JK0FDM+nM1+--=Y!U)HiOOni#~-DvFVi{gT5e-X{GA?vT^%*{Q_rw1 zr@1I9#@QOCvB}%HM6|C>e z9^7+|esFHez-iw&UeBQHzHLgBpuhnRcQ;b7oBoZZU~;YJ(vgTl`%t66fxjS&k}G5f z*C5TKQ90V~W|NI`VnuYIZ9)KyCM6y5?}V z%5#S4HS^zbYukUK1+f*Zr+aF3ZtcUQGCi{=J7)e(q@mnVQJK9-?{ZQW|eVXvE(*&mcPrEQDT%rwjC+=>w^vL&qtZ9(E zY@Xj^_-Tr+-=A;=H^nhud?>kzV1Byf=(nVmZ8e>r+t0}auZXBSoS8`?l6hf!W4JTF z#Z-d-Ow>(>=R@TR?~WJL>UW!^r5;w>>nLf{>h-QfodyLxl^Pyho_+XzEG zenQ&g>^&~#wavtl!wNMuy8J=M<2AQ2fo%IPBu&%=W+eLYY-AFaQ8}yWS|oI^_txwj zy+r)!Efhvp=y>$hp5!=W7>bJIm59ITfSvy&@3%Nc&#CJfd2KXkZ!CdvWAUa~Du z>Hy8)A}!nApdRVEt@z{~k~|^DIe+TDuI9v$c#-3@;nrYA^1m$ta@s1a#~v?326P!? zkQJ21bIkYx&q}Jr=_ysR)k!6J&8p3fg5g})Y~F;v^V1K)jpk3&{buIAR@)X=?sn=i zj zZKucO_*GsT#<0mIU+=Cn_GhruwSFulA?msphT9jC6*C*bZr7k;N8h*nt4yF|x_adX zO{5_dj)biYI^4Cy3PLIxQQuNLtIRK68#CemS}!$J(r+aCty@?%zkuUqtm~1oA#7*u zUh1qQBfD~M@^J^uvjBEqJ=XQxNkO{XDv%A!_q3V~xg94szsfWbqnNVOwQKGC4u+k# zxQKOZ`Dkj0&*nei-6rkTl|X#0;ssP*>mMLAGsq|frbQ=B*IW$pVP z&3Sn%?fw;-k&OABE|J-_?rJgqbscUgm^WJKIB%=2s&3VEZBJL{r}RX%1m6ts)_pa) z;z2{W`#bm%%-$gPQ-hnIe!6e(=dHRb2qR+S`y{PtM0Ix?XqXLGt%N`D^^1pc&j4ZU zdm|LjMmA>7EuJD_e@W)eKc`#2Z1A(1@bpY^cQJbK1c6W#mKwVfUYOPPm5(= zjzk;AHMeagh2?yOObr$r9&kehhOK)B%)GTfOyi%f{r*NV#oWqpD?sjFi%PN6o*+Tp z4P}MvGx(>Ca2Km!laugD9~9!t_kJY&X+POzEeB7Ru|oLv5=%aY&xFOfGf#^{MGkM~ z8H(G)Reg<=G(bAZL`lR$>AxtaejPTlqJ%y#uFUp)skyh8{`TZQJ--vrBPo3M_kl;% z$|m09ofK>;B=*#*BxzKREofWHLC~_J&tj|NR${FcZS;Lak{DBg&o*h3(3$Qb;;x@a z%p~Xl#0)aI?DPvoE>$DC7$+tdlg}~h`ty;o-=EU2k&h-m^ch_8tIcI!?bT9{ZG|vX zHU0Y6!0ndLxBpIXFoxDH*M5jCaW#1AF*Ymaw{4Zj6^A{_ZuXfmpzsbChFFytNccw0 zM>c2Ys$|%VMCE%)tIR1=@8UOTZ!)-#U%6bh|q~n(#vev3lZS$G`i1nYX@id!( zpJwNT#@gJrJ`Ia`+FO3!SrixF-H3YCwy!AHbWS+)tXv8EyAtH^;{$J@AUZ#VK%ghw zt(0}VQ8`!rmb&hp4(llFmMR4c{0gs6dowVGe#5u&<&@>zDZ)@Ur?AlF=$SJqI~9=(1uCXB;V{j_5W5TU|@#*IX?8cd@xPu*(u2uKD|L;6pvuujxgtD>=e z$zPKFf$?R2SCZ05wb@iJS8E452EOYhp|#}#1K z9#?NxTB+IqF9Dd>(v>x~+VL4kuBR_F+@wybP)Rj)=tXMz+UiGZ?u}uJV4K~hQ1`l> zq|3=6=Xqt$Y|rh^EnHoGa#}R6CAE(^tcYO(QvVHwMegj0p~`9RG^X;bb|bb15@0i> zbDIt|^~S=}#^Y{xUyvv$&m1WSZu#b9&Y5X{C2uq}-*P>6Y$8qkN-zPpJBu4J*G_E6 zMAcKTh|K+mi=*yC7H+y^3=P6AIQ%nxXRojQ%;Jn8*8aM%$`N4rPv6MJN~i>Jl4%D5 z+v^3R$VmK;69Y1+=bszanv?&JSg@*T0Sj%sG)UA6ityE(p~6J!=AS$(lXBiE5-wdm zn@@G~&S7oq$03T%;8Gl78iWf(M4W?Q?6o8jy^?=o2QF+0NGuLhr_ZQwy{<6rWGZX2^xOcWb1eHqM!E zq6~PY^eYJ`*$Ax@7s2@_dPU6&xvtrIj_<3_1(PRdQ@~xmz!|Zi!%oEaoNVd)Y(UCC zN27a#_Moa?h;9aMADhSdH*7&BVvV$h{@-xU3(^IReN$7(^ts@maMnt!j8F3*8h)al zq;S!^x|Xfy_iZn-e-ATvf)K0yoUjtvTru3+om%1iLIfdTB4W=ha5PboCaZbrgA8x! z=bH2QN7he+kry1~kfdJ!Tr73dT~q)qYcMS+Ks6`%?-6QW)U3x-vKo1xnkV4FoB!VQ z;60twgHg+4zcx(TB6in7HBg>XGWt4=w$jE?|&U z=gAW8&4Dg8JteT+3+5FWl6T?TO#gL9hWAUp=(od5n_D=sV>j)q;5PGr-nj-W+_raxeV7w?$}20S%Ssbg+M_9i^D2oC1CZS(vh?c&q_ zejxjJfJ?q2+tcS~=pSRK?XuMJ*Ky;=2k$Fu*(9pDA7*}l-gK(Db}qR4Oms8!Z&7ur z>d>o3^Tkq}yArR)-1NNTj`@9---ID~{-5b1;!bE0j1S5epJVuRgT*`K=5$cNe-R@) z`upGK&x_fahz$H!-(DL2&=CCJw}~3wg0DR;;A9Ws?^PalkpX;mP_-ry&6&v85)*0j z5SYa1r)b5GPJW^$voA1b+K5`>*H_e)37i<)ioNz~`9`4m1tQKDSWiEnjMw598Q(L= zWD*n&`zNngQPh^-9LA>wcsixv?@?~Y;aa-jPK}9=`C{c`Je5;E-6h4FxuVlsG$Yb4gN!{fH4bl zAwWn%?O}SB9wH+1=NB1Pbr?Q51^#IEyCOm$i3R9SQ^8PR_>p>`%mpI#kH8rEV3E(< z6?`m+pD2Jc&i~WeICbU+b6eMbL68$K!y4|dRsa;XPMZ-mu@1e1%>PWONffo6dJ#CH z=`fKmc15f!@P9}<9r5Y{(Ht2Bkt?J$@T5b7^CI0AU2EGGZ+g0;-yyBl_-wq4EN0nF18!59`}vDYf|llgLnZZ&W)ELoX*rpC|hMe5yM9 zjcb1xfqFoBzfde*Vp<3|NIK)M#i(h==Fz3JhkpQp=j~ThM^(_&1N-(Y?bYA*)6X}A z0pL!Cvdiv)4`{ASjG2f?vGYwAs4??bj1b)GJdm8ea?^o`D1T_*-mGqEUSqpw5N{)Y80|Lc+#GIE150Z$eDBYU|V|;y5 z^~ipel)|NHn83D_{Z%p2)s=ozrQ4+7xM4!ws-!|iws2vKw(3(dFM?x}@oT~r-_{(i z0s79d{*j2tsbF&|g1%(_^O7uC6YW_ecDR|`XEn~ft!9f1VD}{<6p$Bgv|{2|`22l7 zK#w5goSjoOG!Na5p;C!iAUEmfsV1Wz&zcwX;L9P#1Qv=_IZOX zh*uQfD4}{K~Fi{P{E8 zDmL`YvtrU)fVohMQrd!?$bZ+oMY!;M`9+lL{c2E0Qqg0{pTC1AY9IFC&>@w6m}6 z_VT}&T7ucnfzOd#=T`ZF;!(BlV>9Ritg3^Io^!1>Y&3?_tGA!OO2HFbT6kJbxo70W zh@4=I@*YCx+LcFcXfIR=^Q~5m^hq4iyZ({qM^ZB~&wam@0=3+ogNxm?%Q!>X)1njO z%lv3ZXqEmTHiG%RL8z2^&P|>Ru*RO^ zUjDkr-PY1t@>-E$bq*7QFE|95pc|JAnxW?8QtZ$L=bO)b)H0>p<(qAmH+g99H^p?+O$egUaQ2c=H3f=nV5WyYP!}_smK1eZfZgN zpoG3T+%BxRcN>B&*@n2pgtb+6i#)A)vXPWx5h;~a=j@|jiuB8qxbUR z{*D~_^z+6~V>uc+Bdh&HW`s{n&+J&I^-DH!KtUv*dGO^fNq!=o_kiOAI87_kn6q}K zgB)+$MNjx;f_YA`RCl^33s1mGANAGch$s}Gb?0=1xm-@B%*mx+#+v;KVvZ?GJLz|c zJa17^Yb9TQf(n->UCPb4ekPw$)(`7ygV(~JCfV|KZhF{4WScZvdq^FPA5Q<+Kh@O5 z_gh3tGD2RCUGMmgjvuU)TjD@RYamm-b7iuyQ#iRONP|f-!^^h83+(9&*`{vMTdhcAKfqDj>RjVV@fF;2NM)f%0~3d30ZQ>En1icjh9Smu0t%El`#grdmzd% z<+&eBIknemq@Y#Rp2=<_Zc*%0OW@0jbXK9l=syK9^bDURk1IB^$h*bdN;b^;Z+rMQ zvt{i_Bo*+6NK5h1n*XloPLtwH?f?p3ik%yi3 z(WTs>!q@I2c9pL2?aC1A-MCiad;8hiE zrd~hlpkuod5XmL54%MBE}w?? zQ1JFn>y2@^L)cSs3T45JI&KHK@uLa%#+i{h(ys7ClhPM$Au1SkDN!3 zdY|rc^+h=jx0@H&inv06_@U!VY|Gsyq1j0qtqk}Nrv200a=9Tc{>Y@M>{nWC9#AK} zhR8NkNlIplkWF00ZPHH9(O`i{*d)|K@`SN0QnGRGb=Ro~_lq9}@NgJ2vh6?#S9d1a z^4*=9==BAy2IdF*^?ZwzZizE|G3YOm;;Nzo=feRtvbPcxqc`#bLnN`ahj=ThVr$Lg z<;l+zCq74gcv|Ycf}y@K=B!2YB-@hDeW$jq4D}|ZY;X>PsPp|g##O~BgJTBouUWJZ zQg>@#-FdHPVOK7D0Z5D5L}0yuBXVaF+h4jAN7Arl74b)2EDL3KskjD604(vq``e*Q z^;<3n=j+1!zdbteWhGs(Kg+BF`U6MtsefY7J)7WHxe-)85jK+x;%EvIBMb{Q4;zo) z6_rMQHE+G82y6X8Bvy`rl*PVv-&bdUpg98C>+Wdu>c%3BF8-Ibq}*FQ{4;pD^!!~1 zkw;_jmv6ZoyT3nnl6tEKS7D67mof6rsQs*)?&V=id)_m>5~~Lm z6=})HTE$QY!JYqGokcotK!)Q>p^%E?8TpVjv%;^hUV~EOpyp%x?wlsNWeRoHYnBhW(={p zq^lOT#uk$5{MuG+w_D21v+J>~psURX@1dz{@ByWVYhoxB3D}MnK~>2WqbT2wNhh_H$o~DBa>r3aqz~blt(WF`v>cHw*Q<+sW^Hr7x7Y zW9gIT#9iwP;prU=eq~LA=h-h2Ea$S_DvzpJmyK!KUnup|#^29{D6K0S7%Fpe$@^J5 z4ZlzJ;P-L^nmvK&2^PCgTSZD_j%y*KCI6-zwmSTHVL2tAl#6}sn+Am=yYoZmRn$}I z($gL3Yh?F-|2bS^P?6*xgTzf~$4eLlqvPB~7|1TYR14K>xL>NkpyjsAc1Pla2I3`d z2jAzM4#sL4X-wtVAtu0mv}RtB%83M)qxO$|QoD_$99*E9YG1XyM0E+1M%r0;!Si`} z`?j=6n$eEP?GA|}a&tGku5ky(qoS|m{twK&jk27Jd{c_u=@9q^gvP>tb@qPumB-!H z6GxcRePqsf8Z7Oax_KhMSYN#3umZiF>bB!?qU8b3t@xAEBmanrMFWcF2bV+ zF+KIgwfgd?PuCcw2ks+RVH-8w;4X7@qul5eDw2+JAzlSFex)069 z@H>ZP`BkiPxnRx;S56sw>u!Yt1Jr4)A9ElQ)r^yjy%dd>EW^hGj*Cg?Y(587kdxx}y1{Ln>8 zO=z;t0p}Q^66_R9fc=`Zyc%PrT^H|A@6Aw8Z3cQ~`}>W1Cx?{!{8@r6bzS?GT-Adj zm(E{t#J>zt7C{bHnM=B7nX2K|-?gi0RP(P3hOGFFwIy)&{-UXY3?gwBTg#l6AVt1x z2m!Nl$y#v9SK`Dh_mDrewB5l;#4`MPa7e10sF9OmS61oDt(sM5_JE1XiATO4rLaer z+Igom?fUh;P4rA9+G?I;9@0Hklxq%=Je=6*=24IhIo(R1R~pLkOEg~QJCx+1rIHvc zRe&>zyRn~m@8j+>_HPD@4NYhRmr<)d>>lGNbWb7m8&72 za+y3g&~y1Hd0ZL9w{@!;ilyaB!@`QXf@aV@F3tOqt$Hzw_q(N8dsHH2?}hdC{Afy>Uak?-_|Wmb zP&P^D4j|(a;`?T76n4*fG_;zVMrNWpl5=}lNkHoB0e)iM%Gb`gTp%(W0bdgvA*RCHRs=)wDWn{<% zXJ;!9`?%`}b1Rc8mS$Ghl6<=ng~cfh!B6kn=UO=I2zS6%4_xYD-JMBOV*TO5o0VVr zlfUGCGLR)-6jrBVKXA2=M0_6B7J{0Vpgfw99g9#CyBC5BGOL8ZTm5I5Dx;`VE^4z# z_Mqd5VF*EcSZt!iUS z`Aw4c&HaV9Um{;KLSHJv4#dW9L*!V}momtP@84fut{ykq6S>`Ft)E(3S|z*0JY0Wg zu_VE=-NO8&mala;lNlV1NnOvEih8lU`2ghm6E+J0F*aq7(aBm*xIk9fWlzyrDJc~T zwI%0K#0vvt&^1AG1~!S<{)ZOJlURXnK@#z)){m&#HW&D2+0K}#W507(#J7znnpGTZ z2IB@#nX^!Z4G~&5s3gfQT@^XtGbnq;5@;uVK&GQ#thco+Hnwy_Fp9mS4}J>^yKslF zgz@)_F*E^T4rjiYVj;HM-|<+IwJ*N%U^J{q{v?TiQdkUW@mRp9!*h?MIkT%pBi@wp|IJ-rM+buZ&M!<#ZbcW0B!d2lf?*h{IM#u#5cE57>_FQjx%LZGqSk4|aYxPs&zz0_LGjI}c)4{qqE z)AJyN=}eeSZ-KD6&d>s5r#0KIwMjy)`sX8MRH~p(hkc|hd?QbmUrZBEu1}2C-R<+F zTt1v&2>bw?@9Dnvej%49-`{Q%7SHB5E|;XZSnfCE)U;3iUGkzp2u2hnB>E?@8AVUM zwG{_Wf>iaCrNQOV4R3pggM@p?57i4^Q;P}q28OPSqhGch1^2o({0-VX);kl=K2+{2gl};2d6#sKOY?NVxOvl9m3W*zE-ej@+mtN4+{9) zX<`Xo7UdM|?qJ>9*Y5-XwWXy590L5sc;eqym#DSL@GX&h)(Fg_*<_!DLo;!pT01 zunE0yld8*s)H%Mn2sQXHGU0^CQr>*;blf=szX*vN^tT+XPcAUw#gD>zPK~_FQfU}R z-MD>o_4P}aVV_GUtc|#U%Nbfi@~7pYMcTeeQZ8KF6=JSAdTv=U%&3K-{-?7XW%-E( z33is;ydu5y?>Q{#Hsr7OQ6Kg*%3=L1PLK1@Jdh+i`f-_2ImsnvPW%AOq!=HDvlW48 zYQ;am2SIucpQG7sLNW0@7Y=O}rm&uxRVH*epQFcq%JQhmFme#2MR;HQz=}&n zTu5Un2#G&=t*mkMaQWcweseIyTN2#*GRtLQdD*fw zY`pI>-hS!PsL|IZN@-wJVNsG!5MP{MhKA({aJm|lmOdpcOQgLY7eL0;Zsmt<0`TK} z0zMKL%7Q{aL>Qm=o6F{cdmTmxAF%IYpT3VUx8twpiyicaZ)6ge&-VH(LAO(V+~s5B z%9j-{(Ts8KkGq#$W{kF!M}?bB33o{xrSd}|%U%uR6v)_LftO&k`wLQXjMxegIcT;? zWo-&xDsE5#z6`4^(jKGZ33Onhf8Oi3p1vK6v||Qp70JGKGE(;Al`*=5+D+#UCA*iU z?TC9|g48DL1k>2@7Sh^wW5_NsuWECJNc7*%4gWk}D&N^so1Z>Q4<=&rhBXUu>0PsK zMPbLk;j~MWdfoUgTFR-N{<26MuRoapQ9^!~>I}doK$<^+@F7-YKY5^_gob8jV%UVW@u{-c!6|PKtf7 zH{Op6?unV@&D#6jgApuA06@Wl|7`0Ap&F%8S3Jkgf~Wb`b-}Lc)hx{FBUa0Bi7kB^ zTCcsLGKKV5KkWG)24D431%y~NGHtwIyg5C_aVM95%a(u&w&LS=?rvm3Z$tSHrmV~) z@v^PSV^&4K9tt<8EPbe6^c(>39TfMB#L7fQY0H;x`?Vwb#{@ikBDjESxb1k75N5ozX3wwZ81wb@#2*&S8?I~GPOS_$ z-_bY-$*E{xFpxy^>hco#2d)>@<*|q702E6zZWlH}CGJptfQ%Y*E)4V(WyBMF7x~=V zjcArjC8n?F>x4_D6c~@86D*NbUd2$7I;81_tf$qCRfT;l0ca@Tiyus!5V*I$>-hx= zS#ZUD@&^D_!0ptRL(UP5D!^Czt{-m`+Rz)uS%-?WAQwHbec^;eeI~Zm9Xu&<|B}y4 zPtE|MRlv?xq{6pTG&bPUar9yfz9G1DuKgO>!>GBEP~kvh_GzzUacW8RXr4tNT50~p zQ92E1`FS%3+a&ow8^Y>^lVt8uLuYu;=PqZt=+)N-WGW<7rT~kAwTp&3o~GP~ENz$X zcc4P1j~w3kh?ML+gY8h2ku6Yx9O%k}^|R|D@F4wuyiBPi{tL3!#awNdis8!mBRNn1 zr+{%a#%BN`y1QR(rEIHzOFVxZje7y6%Y4iD5iRwy7wP)Kau@)>8&xVfdVmVc2?{NzgbrqATG=a%veIwat&L6?&qkl_v(jL^ z#-KRv)u3YVMR7O%LF07og$U1%G%@vYZz@`V<53>1E`~^M%e9a2l||SuILo0=J~DeY z7=yH&-d_e%BV~&>z+5{(3&6;vmfO31Fn8qqJzBs1k;h(q1az6)ptwH-z!bINZ#G2b zJh4%x0|Uj0O%Sfno;;ye^VM3qS`s~2x#v}`DN6&>*gw2hwvXgp|9h&*Z8T7` z^y~p8bRo1X?GzqH0aLR)Py&$KJ(lqY(0REDiqkT(S^9Jb0YA4l%UwZ21z;kgMm%kO z#h#Ac$;(ra_w(Ee7V#}9e0yTymc|AA7k;t+s6m(Luu0X4a$jn6@=09P9V-$L`E~;UGoer+LD>TVgg}~chQ)T}-b6F>{uJk7Y_Tt$3uP+! z4efm68{NjIOI=0$Vvk#M-4mZoWXb|)ix%>BWsYnD#kb@~0xU_{Wq+=$bpj$7O~52y zs>W*!^NLHbL)QW}GJ`D`MRAAvQ4(bE-Egr8ZFK<#; zuf_*95HtnZO?b>hY`0KL1;N&WBt!w%zHC?9nkVO6XTja0*1>#eX=>66MP6ZTChWu~ z{wB=jYGVr4V!B>iK|ZgtJDuSLY$Ar4_~|G2Y&8Aq+Q}a=g6(rKcm~w8 zB3kPJ#wI`U9{(dP?MEtYZvkEaG~~l!RKeK+$a9jN_y@`VITQW#wtb3Uh5ZkBlFs6* zPa zv}kn5x2x6kr~*}DOF!(sXjGeDs_qezPD5e0$z+lZPFBB4_}*?X^%hStDs_Q3n&Jx! zNM!xoHGbeH5~Fq^(SNl@NfTj#VBe(+%4l~u$U&qD_R#!q4a2@{kP<2+J}p@4Ex^2> zz$Mv|dAhE~t+`b*80e5}bM;zNgDG&3Q$NJ?hyRqdp$-RH{?$g3CNJC^_jeXU+CEzMGX$SI18+7D>@ zwUd14x{rlX>7v>sy-w~>rbTk`-kC#jtcY_;Pv-@$)ZT>`)-ymngI}k!oT+K(L$~kN z9c5D7MJ1JTaY4q&?o1n{A9yDeGyT2YM!wVcr6m4Jb-bb(>Ng2P^oz)XZ9bY2ZJs~{ z{eprvB@|(Z`s%6W{>UH(TgW8A{vsg(|05NdwfP5}_xTqDZ;6^b7fW+4ldjMM+TvHd zDC?&+g82i_2es9o{e#r~29+L*lY@JPbLMwcY#t7hBfw75 zIa7wR%G*;Ew@V~8qiT+rW2Adr4;UIfXHD=dDVcy=*jk&Z?=@AKG@eYn*?_&QL)$yJ z1sx=YJ?r>pi3p<=c64%mz6bL}#?UK_hI6DcA434BK^Is<-7HVW3LPFfWpmwDAn(?b zv&6AnwsfgHRZG{wiOO6=wARoF0$BrTW+Yb3!6G)hPe4Ua!MVZ;B@TXT(T=x`===oE zVkxu&B|Sp1zC_)x=e0n~IsytyZb6M*#Un&rkeqwheBCc>{0J8e({7+#B$5=fD&l`T zDSnR)cIvM!uV-R0njEm(YLG9+!iGr9&G}lSd+n5KY$g&)Y1Zby0>lLvLyN-ZvjKJH z+1bJr|9cR1%Rj+)9fhy!9T8y1UL3Y626Hr`Zh7+#b=J&eq@H7qrSePHwD{){E*24Y z?`ATeKKS$x3Vx8J)YKNG=T0IkvfWd*{vn(aD3H;?mTchMr&|Z;iLSwx5FnWQX9tys zIb8v5i{s8x&u1j)Py!^Od61ZN?p?i|80&NP+J16wddafY6&&pn;--K}E_`Xn^$xcn zFMAqAR8EF!4!ILMd?Qf4m{>GjOg7kdXD)6Jdy|HvW6w!<2aeKaA%qd$wNENmKUZ!( z1+^!t`hi0qJP0G0n2Mh8kb07)eMApQ|D86(wq5^#rv9OAZLoMiFKuZB7_;WqW)dh? zircOJI1Me~Et+P;OZ(XHU+fI|lzVBJd@q`ux!FlJz>y4pB7}TuJ-3T~l}w*A^$sth zBzp9J0ZYLY%U3_S;R&st(*3|^7KT7nHCcGy84C2cfXGpL4e0*`>EacIMOpkTe3{To z{`p_9$q1|Fc&gy8Ll!^E%&FP&aT`=B-Y#4GzhH0l-rxv5y4385b$Bgz#dny+fFAqv z1ZQHX;7ig|Ovnt!L*qOroWW3%h+sNFOhVbNwGBAg;Hg&MBqJUG)BFb%^*8NTwnbGY zgKiMFnz$6FA$whY7$2gF-_+K0>ETr91_oc$( z&Zfn#&Bv2p|H2{-izD;-6`awubEd}!bc9dR>pUz1RV{@lI4?9?2b;tp5h%tyS7^GY zqWy-pl@S`d2jjgl=i&V;Kf_;5Car*m4QVs_f_|?MuJZ9?+jCx$97BvA6!HWjlXUf* zhj;sP<@SeQF|*mYx^h>MqtC6*^zZN?F0Bq|((GC~qc9FHWyt+&iR^+I3osc;vJraG z>UsZ~=Nr^LA^r}u$U@Lm$Gx>;y08w%Ct~czF&E)rw{LMoY!Nivd7c z5L;g357~^g@A+oG0`EZ*W&F-v{jwjWFjhsNZ@jeegLl0d8jWU-S+HVCghbqZoA!p*CevS0(_%amDp`{EPpC=wnVg?f#2R z1rCZ>Kq=JabDIGHeE17erlmwk|5%|yp7a4kph z7tR9Enx(!SKwbb<8*9%GG`!2R>s-`rh`;N=>Mi^P+$_Jno4YIBwkWyySN=ck2q}Ev zQukm*74_Q%)5X6w;5in+?8yMd3$EP`Xc`K3M|hiK5z-d8-nxha)u68B4;xIoGHr{R zaXmcwAbTq);JI;bJvmx<)C*vufNSPG_=RlSqIur?+pnpV)m-^O$8bW=P<9W%r*uJ6 zFG2AX_n)C|6R9Vyb^)ExZnGvrLJo5LIsjNmB*f~rFY032&R_-z$C7?NMjM8DLH(|A zlrI)qxZFP-x&L|RQLbNyFnH%5)VvFRAq3t5V>ukWgUU|_l+XQtBy+(-T(t0ibJT$; zAl9YBAG|~VD0SlOEXR%DmK)4Zw6vNcLOR~->)iGx84KJ;g>>Zp=RPvZr2_%patOG3 z0OTKBg!dQ2U=Lkp6HLYlOJ(Rgd-dPid*WR$!n}efTYBDt?f6 zB=wu^qP_B^P3A{U^q|B#b(_TeS1cr;4ph22@I(L{hbUH1qgmXvT5@n$^V{=duNxz9pquFMIlXrQ*g=4_;GEE;`%}6^}5GnsHmt! zM-ZHpW``p@r~i9=`D6Y1XO{NAiO&1vXc93E4b$Ue)y$Qb0?l4`X9fY)fw1#>zWu>I z+*|~m8x>>-!)e&Fz_RB?k0FYFkx}39&3l<@T92RDMZ2LPB;O@Zs6`LbUs}z5<02LB z=dagWo1ZIpUvPo)Y6TPWRPFd>Bz0BO87YQ3NIUFFsx#0_&(M+f`JUzMJOWXEanVqA zi3S6@i>||i00OvH!mnFQ<;ePa9V^Q<{{T;* zowM`kG?ZoKaG=$cA6LqL!eg_3?Ig)#=3o4&#NY`<&lj(Fb?O?=x#_EdYLS{s)f|Jh zsIyf=rF7Mzxx4P3Xuu{9wYMqKQ&O^X{%XThz>u{)_Uo5JpI3k6#`=gw+812=rLMWe zPq|R2255X*8nZ*gb9F_Ol331pRERGURHWPM{MP^{Aj)E_ASUz+`72qAb3GU>KmLH6 zn8is+NF{nJ6ND~-%1K@`_(npuF3!QvPZo)k<4XI>&&&J3rR_1wy6e%%S5*^q`Q^`n z)g-Vg$*FQjB8QAz%imf*Ie%niUuKV=i>m@8n9f&rR1kl%pvyxFMy*S60|ca(#D0u> zSF63MHC4ya(Pek5pumqbtjdNc{Pkx$ zyAm~F-8TkZ_Xx>}D2m8-b_RrYYE0p9cu`A>Zedo7DvNGSqQ5>w+D-bb1uZRT(J?WC4&p_Pz^1ImZ85>DX zzRSxuQCdLHr~rfozWhes!a@rS?1)VAzs#VNnxBb@4oa^TMu&%sKr;N)?_jzQ!M3(H zH&=Z8nDsKU4`|xRBr!V6XF2LVPJP%Y71{=-D)l&7IR?TA>4OF}^FGC=5A+K((Efgx z|5emydJm{EAu{tjv!|e6HioUywHKcaw^rY;y|)M&kX_QkRFT@W&AOSTOyl)@^Dc-o zjzo(QjWB$EPmgn5@9Be2L)Lw3QK$NmovfYa$9F{zV)2ImEIM4N-8 z6U-_~8Tr){%ETV?-S`tim=wu_#-hPZIlW=^AswGI2cg~qsCe@`T)0|*{dP7*Iu;hq zY3W-;_m;iR`Gv{hKYu3DGe`je(PiYJL6|l%lQ9C*0M)-DNZ9L=;$qeEa+S?>o49#1 zeR&Gp3;4*$*O-_yyZ1oJb#z#eQDzJ;zxn(PqXI;50cC6+_rvNQTG4Z{EHz3>HbZEM zRQs#_LZeHhSh?|3MG^~wm;3QZTbl+RK`hIYC1S!ER5F7QihpR}Q}1bJr7kylsQ4&P zdB=ZGkCGeyy~c_?=j9pUJUyO1`LOwZzZ?ENk8Ms{Ej2P6lSsNsHjMYik8;c=@jBqk^!E+s48Qfc?W8OEIZgu4FCM--u8^m^t;Go zUY^Gp&S_H*YM>=_bC)(k=9`Pi!*2XDbHiD#d1BjFWkP1P(rjSW(nEL;a~#Y86B7kr zoWuo50kz6qd}QO~Ju0e-^6JBK+h0jQp)cW+lO8Yrl*(uzVjp4T{c%EI$Fif!9f#?03WdV zWkyChn5G8n90BpbD4(bv+ATz5>Wd7)sCbO6QrgnOXNmA#%g`+t$xF-3WCAX@L=9T{ zJ2q%lv|GB;nji<@R_a}q-Kd+vQ_|5SYn5L+2w{rx%1j5=bCTJA?&d;9J%Qm+Tr6gB zxxW4nJ{5(gfbs9qIh-m-qot(;GU}}H#SW-pbpf}9owB?8d%#YQ0K*uE?JsBfk`UFW~hwt#woiH=Y|F zi?SFb1}7T7;o@S>=zhrG8lYK<|A}cpWM@lU+e{)6%g!RMjOI{A|333en~7JbgKh4h=C5oB zT(k4V3ky4Qt8@0+Smm?tyXi3`)8c(fz@hhvd=A9>WJ4mD@v}b{&4G_DO>FPtQ!_JF zY^4ctFQB$#OqLWHC8GYBY(Ox5l=H!{wRc%fye_mSkRbls;DpX#vPRakt7Kcroq^+_ zk;2y}Iiw-m4v5kB1Y#z;uUWrx1&gEBIO<#p2{|8C``e$2zk@WfJmMxy@`GW~?6o$T^+`75E)lZY3* zcOW78e>j*Ft$-y3Y!={%(*EKCkjzRQLk}PjrS2<8;&@NbXJ8?Cdes8WVG4z|yAj-n zB>&pd()pVFOr~EnQoELIK-Rr=J=RY^*cuh$(D}Lsnz1mTX928xWs|l8hn}|wuiya1 z9~s_S1Xe!^0_My-{lfb_cpz>C(%EUI1HSrDklS5c>}XC(j% z4)P865AnC7WeH|x5b~feb`^RDq#_zl!01ICXs>U2l?3idAG%3Ap8@12M1}6OapA}* zhtjap7)Ad<^5K#Vp+nTsQTAryy+FYd9351c!xPkEolcrpZY38)=Y)WGHt;s?Jt1a- zcF?vJ)TALn`q0CN8g+Z?qz=KM#i9ghi;QE`fxfc z13T0*8C6%Ew%hM#ol^L%sbn6Q0r^xqmQg=f#bXV-_8l?7sfGVqT2v1sEOiNJe}MS0 z5WDU_AhkER+740XEVx3}{&!viFq?tvfA12w{c6D%RX@1?A1x?E1RVMTg~5Q8T00|! zYD6-a&eZ>%gp=O9*|v9f)_D4WacCig=NbgW&5g{>eFZ1v$;W2~xK$PuNc!$n;)bb; zolEob!j10hmkKBO~`wYBfHF8Xkd-|CB&1OOP0>4@@Y5H4jV#*0&N8g_#Xr z>@y!aSyhz(d4o8F6_?}ntsM>oQh~VFw+A5RF?4gw9zHr+)Yat&%A(hNhG4t1N8O8{1dPgO$_B&4@<~x+lt+U9%oQS(=(Mg!YNX>b*q!MY5 zr5!B9nA**S@AZvjPMI=&rDP6mugH<1Kw4ZJT68RdeEjZwFl`6tNt0Q#>Ex=FEC$9y zaE3~4Ew;6wGDP4>?AW-^_^`g2%0n}$XU-&|%8Iq6{TAIQy z0-IR$=hm@JK!Ey)P0-owyfMGC}aHKAnf3rf1gC&*+^d>K5eKO2F_GB z-8*rCyiyx;l?o21plxjoe7WhBhV4T}w!!UT*;FP+?u0yiXQorY4H3|Bf@y)j_H4C| z4%d85K~ewO0>ko&ogB}5;pxK3_Rxk*akU;Ea>~=<$*qgk^gF&HPD%YlsaDQmyk}k@ zh1SCn8~wGR*ghxcOtO+ma>e}b%qe(qJ6^#IRo`azPV>NWi$~6_fvQ89<+c_y@RgBZ z#AoIdmRTxH$o5u1uuiboh7Hv5;?82PX_Q;vR z4DV^#3*2T=?U6lQQEO3Shz||i?pPFO*V&OMQT`1NA0H6 zHas&WJFj6%D{eYQMs${3M%I4#!%i@kNB4^>If^9OSYU{JJGgqiu?~BRo>0a-3}2QZ z9nj%0V^Xcwuvb>xRY%hPqOH-z=k>CW>e9fteRt5&( zh9OL#MQ-Y~igUti5tNbA?>OvA&X(N-bxPqntjU0*iK@<&7oIJ~fzzM?PjFbs@Osw{ z`u(Jm5#UB)njwE=N3TDmChf7JJ}6=df3X zxS-ysFpaSA=C0`R)?$MZMteiSWhoi{B+m);a+pDpw8_S5J&l?9b02?!aj4FmHq1XQP z>tmAkAEoy(ftICmR53;bhyj#A?AMV`^z_PX>=m}|+_~@Rxl;P`-Pgj1B5EetTm`n> z8Z)rNkz|xRlU=fgN=jRlopGvKoFv2NhcpL1Gn5vGKE{!5Ik1WfnkvHXO7RAIYx^a; zZS)rg5uMg`)mF)?1p8GnR+a{S2|A5xcxqu8D7dH9F>^z9>H0fQJd=4#ICm$f7Ga?G z(`<4jCo+?Mrr~s)<9!+pq@ifU#)~dORto!WAQnhIJ9uoObFatkBnE!cfcmB#sv}omuTm8|5k=B7< zI+`TAo;q`q7Syh1(FGh0T@%0G-d;50ltLwLYUe-FQky&uN<$(8$tXT5##4zJ##h;| zTrwxvuuc?$GqntF2Hza?SsikHY%peTl+0PJR|@Z_I9!ukIjlcL3c>1URZZ$R*|Uan zm93Ogl}(|BCaQ%}aq{wU^6Gr)Dq~|bc06Q`jjCS3LsnMw%N|1GHas;|ak^q=@wR-y z#9?9P2R_E2>JrmRY9V?^bK$-w`^MZ_>UD2{ks9Bu|9oB4yyfP6Yl7a-V4vrlMBIcY ztH%UpSf{vxrgKSE#IUjPEK_DXI`=ED_j(L{2Lc56-(gZ|H**AGRH&$#56s(qzPQ-BqX3llfW*p*;q#S)-Y^`{YGRHU=v*0V z60;&L7jsIwI}|Y+pv6gn&q#s4p5a|^Jiw3R;n3c4PoTl3v9gAiE+K zSs52f-*9}Kn4H2dk$@?Y{h^jgfi9sI=4+NJfc7TWMTe5D_*} z{@CRW6(+h^YC*AFY%;J0rkxNo%CJ!8gaj$TXi87xKl@^W83W0mVyVUT_z>5DD)iM2 z0lu0+!S0~OjZB48G>QS;{6JWj{O@6JIhG4Q_}IfBiZ1$%i*s|ix~fcfJGQmN4)$o7 zkF~k%8o?%$gT%*&DKQC{qIk%KfHmJEyV(M0nHDrm1=-&4V{R2HOVgd@U)vE4ESLNm zE!lyB53M=w`zN7)&#y}|>5UB)sl}zyLK0q8PZ8nr2`BMP9{Q^dOG&-pB`bLHL^nxs zu-n#n-`!8seJ8q+;7p6Shb9Yo|9V%4loqQzuN-aKYGA%M?j@&4X}` zjyZuv(q|<1_CA~1IhmNOHxrFAuPW<~O)<1cPa{k2SQPj>-QS;1x!6^!&5XDp<%1pG z5!$V$yq8)~zZR2YSYD8nlmTIU@YibwOTyVmwJ|Z6Y5DA0Tj6HW-{@jjUZ$#TWXS4E z6nWZ5C>CC8!1mCvqG!M{^nTl z&KNNP#)Ww7)kS41{s#3LddawHVCFS4P3{I6B!APiFjkq;d@=7HG>deUhx=W|#N%N6 z1A{aa*0bc+?;`9ccbwF+Ds0@jo1N=M?Cq~}-5O2E$jLvA&%lva=Upv1GC~<}p#J0Y z)O#vhS{72HA5TaEb%@~LVK%Zm6On_kusWxxEDfpst&=9!0OuC!t(wE%)+!Rvk_oy>FPZ$r^GF6-#e{+ z7aARXlmn9P*sY%*swl+wuLyX27c0}1dtT*!X}b8+E6X!|VB!MlZ$Mw2KYz7a6?B;1 z%@HYx_ATB}%0CPfNbJ10-e~aET=Ml8oDJFTb3XiS>5s(aV0h)Eh~Lp@RdN^;(yzDF znki+%eQ+Wequp>zj6K}L^&8S~zRdTi)Y??X8?QZ)2V{<;Uc2%m3XL?mX;{}n<`*^& zG)WO8Pp|!~`r!I*$PF?)xZzQ=Y3gtT9BzcMavmF_Z?-}M#mqFHR|U6eFzY0$?jctv zYE2CfspV)N#ScsbY(t^T&rY3oc`D#(YGhUVXWHF&xBh%kBz9~a7R!(ntewCQH?DBW zbGHLTijbD2p+O$_&4ND$-KdVe)0)U)s$-eM6MXz)0~J5(?r@bjdG32XRygIvJ6OF| zhusyf2h8YiGoEW~9QAkGoh0{ee`Fy%*h~LI9JMl$FcQR^R00e?3j9Xniox>94UZNm z&FY^{S2nhCmUK3o)5kfsd;*jA^k=N*a#}A{)b+ai8+d-7pHiH_Jw3lxls>j(D=@xD zv2KZx%wuolp>hlk@@r5Vz}MOoi2rUhZkB@BAKi^!yWD$V)GGx2;^irF{W>$>#PMyf zQ$J}+OJ&Avyi+(6Y)m^}_7~d1a;Rib^Wosp+f&eVAPJ&>^?lrte(JAAjO?$M?uDkk z_R2S>*w`nJ4#N)pesO{BXz%ojA3cWM&WYQ-cSY!&`aMtwcd$GEj4+h#yc5%`E?QAg z@oH@C0(y2D+lA`%(1hK3>$3ts{nGpGUFGk3XhqE=VI$1n8|Evpiw#&e8B27e#O77A zYm9Q#$N&6kpl1kQKJ?|`<}OyuFVSW$G_EKLXvvd(KY`ws{?x2$D@E<Ra1(D%s{3~GxL ze-#jFx8|tYIBqob95VMNOhXT9s3T_Px4&w1nXY^pIQY`faUhriv1JuVPL!mniQ2MwgM zz~=Xn%~-iyx>G&nsLSu+l^i=Gp9OdRBZe_LkrnbQYgua*mPe5m>9Q z5PhF8w7A*Ie4XL~_TiiJfnGGd6bdiDx$Q_eJX08V>h1|jX@E^7&mdNkBU}5eg$dv8 zk>5JZHv+s2Uj{SGA`);3yjp(;pE#(TU+qVh?CvgCUD_k;QzZF!u7xP^8)IDjWHk@( z#dtI^6ijL`5*{xrXhcP^-+31OLuj`Emk{iI%TGaR8YM46!z&d<3-#FVL*_`DYgff% zc4oH+b|YE3uN<_(cY@kskJ03aSKNf(%8F2ld9}T+o-*Y4n7>nQH6ZTMQ3SPnaTbtC zV>Y&isGn1qxNF;*Tx`A%Z|owGRRM$8>vF%Pw))$pek5W?XjD)hhJa(+c}-FZ;)K0s z%-~B@RD&L4p3f~2<3m2(LoM#hNWL=}dB+ahmwZ}iLB6-NCgM(rbGoS!&^yK2ar1jg zh#k#h?bW*bs+gcFNs5+Wx!!618V^@GNqIGf$PE{-ljp??+G2wc8z(2%$?}5H4YuRc zQzi=ho+Vf63vf3EXpvL-Uz;j2aM*!{@++?oQ6-{eCin97hX1J((w*SQ+@58# ztf?%Bu$Y*Kgg6(@0j1nMp{oJxdY}&9y}ZfUvI4xZ<56eUQS&$zo}*}nw%!L}FKM2Q5ZlJV!DZ6(5lCtdJG_m3B}#pEd|j{xqaCWapx{%`dLb3;74ZZPanWwB0fRxAe%_CgM@Y!UhV4^T z6`G1lw2hrz9^CYa52>&~Lz!`fO;C^yG5begrf=-PhHPD3Rf@HphMp$@P6FZ<6oeTC z|Bi~iJ5M$aXOD4T7Xb=jb@+^6HP(~U0{E2io@aSwAz@Ug z8AUM3*jV9EQm;p?=CO~6qlqq95^vY3(I@BuU1P(O2aIB!QYdkwHBd`;d7>V=Dty>?PF8hn1~-YX??n2}5}7|M|SSrZ*i!=Fcc>#|;#;CClb#pj_H63p^+4y>uCKg~%U|ME$gtra z>~q(rNS?)s38ik$C-Fet7^i*Q`4u5vr#}8mUB_OqaGOtyCXiPKc^qT!=m5paGpqCy zl1zeyvor{(!xbl(a-GH`zEy}(XMBMy2%t2kcGznhS67p@!DvQ`6MH{DZQ%Rz+~!Jl zAR~<^ZEdaJqD>#ztFOIFiyo8tNs-r^#-a6hMkW>f?Ceu~ZyA`~?Vt_7&m}?yoD%X| zoei{r!Zv}T^?Oia;>nd`Kd_&{1H_8;iSF|_+h&*JbCJ!xJ^TE+d`)f_!Dwz5VE7&$ zvY^%rU~QVhu2S<3yQ8Rg!vmPFb}pRL*v`Nqb}|)YwK-MI3miGV%}!u15%_#s1%fo~ z;cFxmA8WDM=^hz4I_-)^plpsCH5EL2N4E2?Y^BAT#nZaWN1UDJ1~@pSfK}Q~Fr^d@ zH74LAN*`!FGY}$*z%*)Z&0ig2N)eu&C6BMEP1fciOGfOBgN4=exK~@3doBWm;76g( zQJn|HdyHr<2b*{sDY1bUFlXXrx9wjmWBVlqOa9@>MnvI3$7P4>5lprx#b$vm0Y-m^ zsmqGR>rO}7r00r*)ApKlbL_@Avd%m#3Vu(NOssJ75^a64NEz#P26)JM-CNlo82I?^ zW$VS|B@jj;>~m;+sz%lCvff3n#_{?2LW%72CQWudUh!6D?2qOkIkM>Uq&{Fa*arll z7SQY|QXL(a)XhC>N`|I$d!08ie$5r(1jM|8f<MBcxNxP$#w>3AJs8;G7?h{nAJf3X>vX2q zjSSd7?(R`wAm}_@&@nSp4-CwhM|rXt)RNU|JYR%VR|8+zni&>Q4>isYsvPN-Mz@>P zLIVw9Pw>*Ouf1QIWU<>e+$@-ws6BQi`- zN-Y38A`2f-Lhexj#-KK{4w!PlL{U64axJ=-I?^f<8@?d!Y(j8 zL!0oXXjl5Y=y34a@=Xi}ga`upipL!;y5)k9L9^w(HY!q!X+P!~$O=^Izng3c7CzCc zpz+i<7!W+3-wo%Pb+&nK$4FIF`dV=+UaFt8aP_ehQ3;<)w?P|vo9hwqZ>=krm(wNo zIxvypu~3}!-#TW&NVKyrEmF3+)W;U)ci}YR5gK&95B6LD!jGo`Z1bV6ewu~b|B&Q= zlY)Z1y#GR2_5TZD{RSYcw?BIIKliH+{=4zD^52cG|G%LBTQnM?@7+Fm`Y#Jiv4sD# zo%sI^RqI~H#6TeTvlXOYYX6_X^Fb#9NZYda>*I~WtP*`zF97TV z*Dx=if)xx4ZNY|~p59|?f^Fc}uO6#vsF;|raA~agwSGZw7B|?y)i9}dli}v(PW$|M zy>E$wgTvm*N%eGZey?u{!v=%S1WtH)!MxE3i1HrVNnV*5VdySWWaXMG(>5 zu4r;U(D3Ec%hTqemoT(8gVfKThoSb99><$HolE-@-7qteZPPl21O-u_zY^8e)rN+K zLWfo8XI_nND~f6$UA}wFRXagYHWmTZw*(9y6-)|=SO9MuDK~-vy*=_+tp)NeD>apL zw#m18>1?LoYkp#WzS4U>P>UpeG^_P3Tr{V{vM0qcPCoY zU3C2HZqU8i2G3H4EFZL2{H^nq)YpH*D|yYP9QaIH0t^7j2vzR7Sb6PnZ4u3uV4ycC z+!F|)aywjAX};WTq!2_KJ_M9M>Y%<>Pe@2$aBJRSn_gO4x|kXY>Z$_34i^W|W`a7m zl}M-gmU17F!3sQxsI!%otoX%9tSa5dj4xltGlad5M&zg;f=Htfiz;t_NE^&RglZD? zIF}kJk3=FtLPfv1pFe*Vhms=zqX26t3{j-xs zZtePUmz5eo1e-`fbGf~Crg@P^h2n6tt4^~A;5Ug0W%aIvpur7+mwfJY5QxAdI~R!c za*-n;S~f9|Ar!H!uj?i&%_f`uPi>xoqVPNM@fQ-$kwMKj*Ov|MYxvz7;3P%>oNnX` z_B^ZtM~r%YdygR?gQC3uUUN688zhy`c#UNJFMJrqfvqT??R{sNUC zPv;AFSVH)J9B)lFilvye2ATBt_j9RKmo5R>ZU*6o0x$P{jA2f*qwrF_#%psB<1K_QSMew$av%}Cfcd$gcu4G0nL6O_TPhKs;hjZD zTZ96Af?~erELB)?N($H)_zTglH`hoff|u^@?z5<% zk9Ai}injaQDp~~8V&hoU0oiH*T9L0=jF3VPndA}{MjT7#p61w zJv=IatZJyV^`;L}q z`e2uReEw=+zDS=n^THxYF`wI?6$HLm0d9fKm-6j!>YyGlRFl62&g}BM57#WtBM`q* zS5i_cJON!^7yw$(Bh@x7Ej@iqE$HC!Y_oq#?RPL9y?R{9x^+;rMpexMWXSIyK$-38 zpJM#jfjYFBFG&8y6gz9 zXXfco5|R&$9Z}{~**$eooxul5c2-K^m2`bQBzNu!@Yz|yWj{32I8e!yz$MBPyA=E}XiqQVPR`V{SmKi;}) zP^%fg^W1pjUQ6TJPQIo>@UdR742ulJ1co$!Mit9K72Cge3RWM+n$<0U^x9mh4n(?E zbLhit!RIN##bAQ*_9ux?lRH9nYE^oH7qt2baA)y|iB%wA&H!alu)~*02*y(kaNuu% z@opko@x7_3tiE1wwlz4*lfnfZn0HZAL0m0?m$m { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final isDark = theme.brightness == Brightness.dark; final version = _packageInfo != null ? 'v${_packageInfo!.version} (${_packageInfo!.buildNumber})' : '…'; diff --git a/lib/screens/home_shell.dart b/lib/screens/home_shell.dart index 22d4a2b..1a0590e 100644 --- a/lib/screens/home_shell.dart +++ b/lib/screens/home_shell.dart @@ -14,19 +14,22 @@ class HomeShell extends StatefulWidget { class _HomeShellState extends State { int _currentIndex = 0; final _collectionsKey = GlobalKey(); + final _scanKey = GlobalKey(); late final List _pages = [ CollectionsScreen(key: _collectionsKey), - const ScanTab(), + ScanTab(key: _scanKey), const ProfileScreen(), ]; void _onTabSelected(int i) { setState(() => _currentIndex = i); - // Refresh collections whenever user switches to it. if (i == 0) { _collectionsKey.currentState?.refresh(); } + if (i == 1) { + _scanKey.currentState?.refresh(); + } } @override diff --git a/lib/screens/scan_tab.dart b/lib/screens/scan_tab.dart index feaf62c..812ffb2 100644 --- a/lib/screens/scan_tab.dart +++ b/lib/screens/scan_tab.dart @@ -13,10 +13,10 @@ class ScanTab extends StatefulWidget { const ScanTab({super.key}); @override - State createState() => _ScanTabState(); + State createState() => ScanTabState(); } -class _ScanTabState extends State { +class ScanTabState extends State { bool _isBusy = false; List _collections = []; Collection? _selectedCollection; @@ -28,6 +28,9 @@ class _ScanTabState extends State { _loadCollections(); } + /// Public so HomeShell can trigger a refresh when switching to this tab. + void refresh() => _loadCollections(); + Future _loadCollections() async { try { final list = await CollectionService.getMyCollections(); @@ -552,7 +555,7 @@ class _AddCarDialogState extends State<_AddCarDialog> { height: 18, child: CircularProgressIndicator(strokeWidth: 2), ) - : const Text('Add to Collection'), + : const Text('Add'), ), ], ); diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index fbfbf4e..b46c0a5 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "hwhub") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.derkauzigekoala.hwhub") +set(APPLICATION_ID "com.dltw.derkauzigekoala.hwhub") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 0f8f2ed..a582a12 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -385,7 +385,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hwhub.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hwhub"; @@ -399,7 +399,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hwhub.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hwhub"; @@ -413,7 +413,7 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hwhub.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hwhub"; diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig index bf0dd59..db314bd 100644 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -8,7 +8,7 @@ PRODUCT_NAME = hwhub // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.derkauzigekoala.hwhub +PRODUCT_BUNDLE_IDENTIFIER = com.dltw.derkauzigekoala.hwhub // The copyright displayed in application information PRODUCT_COPYRIGHT = Copyright © 2026 com.example. All rights reserved. From ca11df0152b675375c07a28e827b69f9a40e6575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20M=C3=BCllner?= <79001016+derkauzigekoala@users.noreply.github.com> Date: Tue, 24 Feb 2026 20:13:39 +0100 Subject: [PATCH 03/14] Update lib/services/collection_service.dart n+1 performance Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/services/collection_service.dart | 60 ++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/lib/services/collection_service.dart b/lib/services/collection_service.dart index 23d4efd..9fd93dd 100644 --- a/lib/services/collection_service.dart +++ b/lib/services/collection_service.dart @@ -60,34 +60,60 @@ class CollectionService { .select('role, collections(id, name, description, owner_id, created_at)') .eq('user_id', userId); + // If the user has no memberships, return early. + if (memberships.isEmpty) { + return []; + } + + // Collect all distinct collection IDs from memberships. + final collectionIds = {}; + for (final m in memberships) { + final c = m['collections'] as Map; + final collectionId = c['id'] as String; + collectionIds.add(collectionId); + } + + final collectionIdList = collectionIds.toList(); + + // Fetch all items for these collections in a single query and count them in memory. + final items = await supabase + .from('hotwheels') + .select('id, collection_id') + .in_('collection_id', collectionIdList); + + final itemCounts = {}; + for (final item in items) { + final collectionId = item['collection_id'] as String; + itemCounts[collectionId] = (itemCounts[collectionId] ?? 0) + 1; + } + + // Fetch all members for these collections in a single query and count them in memory. + final members = await supabase + .from('collection_members') + .select('id, collection_id') + .in_('collection_id', collectionIdList); + + final memberCounts = {}; + for (final member in members) { + final collectionId = member['collection_id'] as String; + memberCounts[collectionId] = (memberCounts[collectionId] ?? 0) + 1; + } + final collections = []; for (final m in memberships) { final c = m['collections'] as Map; - - // Count items in this collection. - final itemCount = await supabase - .from('hotwheels') - .select('id') - .eq('collection_id', c['id']) - .count(CountOption.exact); - - // Count members. - final memberCount = await supabase - .from('collection_members') - .select('id') - .eq('collection_id', c['id']) - .count(CountOption.exact); + final collectionId = c['id'] as String; collections.add(Collection( - id: c['id'] as String, + id: collectionId, name: c['name'] as String, description: c['description'] as String?, ownerId: c['owner_id'] as String, createdAt: DateTime.parse(c['created_at'] as String), role: m['role'] as String, - itemCount: itemCount.count, - memberCount: memberCount.count, + itemCount: itemCounts[collectionId] ?? 0, + memberCount: memberCounts[collectionId] ?? 1, )); } From ae39f2cbaf604ca3cd8e95255986bffd97b7a500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20M=C3=BCllner?= <79001016+derkauzigekoala@users.noreply.github.com> Date: Tue, 24 Feb 2026 20:14:10 +0100 Subject: [PATCH 04/14] Update lib/screens/scan_tab.dart better error message on failed collection loading Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/screens/scan_tab.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/screens/scan_tab.dart b/lib/screens/scan_tab.dart index 812ffb2..df7d0f1 100644 --- a/lib/screens/scan_tab.dart +++ b/lib/screens/scan_tab.dart @@ -43,6 +43,11 @@ class ScanTabState extends State { } catch (e) { if (!mounted) return; setState(() => _loadingCollections = false); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to load collections: $e'), + ), + ); } } From 94e0ab436b47e085323bf414e6cb6f30fab1cec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20M=C3=BCllner?= <79001016+derkauzigekoala@users.noreply.github.com> Date: Tue, 24 Feb 2026 20:14:48 +0100 Subject: [PATCH 05/14] Update lib/screens/scan_tab.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/screens/scan_tab.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/screens/scan_tab.dart b/lib/screens/scan_tab.dart index df7d0f1..a77b0e4 100644 --- a/lib/screens/scan_tab.dart +++ b/lib/screens/scan_tab.dart @@ -150,8 +150,10 @@ class ScanTabState extends State { .toList(), onChanged: (id) { setState(() { - _selectedCollection = _collections - .firstWhere((c) => c.id == id); + final matching = + _collections.where((c) => c.id == id); + _selectedCollection = + matching.isNotEmpty ? matching.first : null; }); }, ), From 05bdd281b65e1132db158d5e79ac705a3ecf6b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20M=C3=BCllner?= <79001016+derkauzigekoala@users.noreply.github.com> Date: Tue, 24 Feb 2026 20:15:17 +0100 Subject: [PATCH 06/14] Update lib/screens/manage_collection_screen.dart add email regex Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/screens/manage_collection_screen.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/screens/manage_collection_screen.dart b/lib/screens/manage_collection_screen.dart index 27976be..5119ce7 100644 --- a/lib/screens/manage_collection_screen.dart +++ b/lib/screens/manage_collection_screen.dart @@ -167,10 +167,17 @@ class _ManageCollectionScreenState extends State { if (result != true) return; + final email = emailCtrl.text.trim(); + final emailRegex = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$'); + if (!emailRegex.hasMatch(email)) { + showGlobalSnackBar('Please enter a valid email address.', isError: true); + return; + } + try { await CollectionService.inviteByEmail( collectionId: _collection.id, - email: emailCtrl.text.trim(), + email: email, ); showGlobalSnackBar('Member invited!'); _loadMembers(); From 60d440703bb6ab584b9a0824534953f4fecd0fba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:16:08 +0000 Subject: [PATCH 07/14] Initial plan From 2699242fdfabd1ff340a2560f4e8840a49997402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20M=C3=BCllner?= <79001016+derkauzigekoala@users.noreply.github.com> Date: Tue, 24 Feb 2026 20:16:21 +0100 Subject: [PATCH 08/14] Update lib/services/collection_service.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/services/collection_service.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/services/collection_service.dart b/lib/services/collection_service.dart index 9fd93dd..d3beb38 100644 --- a/lib/services/collection_service.dart +++ b/lib/services/collection_service.dart @@ -172,7 +172,8 @@ class CollectionService { }) async { await supabase.from('collections').update({ 'name': name, - 'description': description, + if (description != null && description.isNotEmpty) + 'description': description, }).eq('id', collectionId); } From 040a8bd1f29543bdd7fbc142d5974ac9785fe742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20M=C3=BCllner?= <79001016+derkauzigekoala@users.noreply.github.com> Date: Tue, 24 Feb 2026 20:17:02 +0100 Subject: [PATCH 09/14] Update lib/screens/manage_collection_screen.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/screens/manage_collection_screen.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/screens/manage_collection_screen.dart b/lib/screens/manage_collection_screen.dart index 5119ce7..faa93b1 100644 --- a/lib/screens/manage_collection_screen.dart +++ b/lib/screens/manage_collection_screen.dart @@ -86,17 +86,21 @@ class _ManageCollectionScreenState extends State { if (result != true) return; + final trimmedDesc = descCtrl.text.trim(); + final String? description = + trimmedDesc.isEmpty ? null : trimmedDesc; + try { await CollectionService.update( collectionId: _collection.id, name: ctrl.text.trim(), - description: descCtrl.text.trim(), + description: description, ); setState(() { _collection = Collection( id: _collection.id, name: ctrl.text.trim(), - description: descCtrl.text.trim(), + description: description, ownerId: _collection.ownerId, createdAt: _collection.createdAt, role: _collection.role, From 44951de8f52c51ce8057d915cf69fcc16b8613cb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:17:30 +0000 Subject: [PATCH 10/14] Initial plan From 0539a7725aff34ef0b1439f867d4a0ba7d0e0756 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:17:53 +0000 Subject: [PATCH 11/14] Initial plan From 4fbd77660517c7ce5169bbd46bf1ccb52fa71293 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:18:27 +0000 Subject: [PATCH 12/14] Add client-side ownership checks to update and delete methods Co-authored-by: derkauzigekoala <79001016+derkauzigekoala@users.noreply.github.com> --- lib/services/collection_service.dart | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/services/collection_service.dart b/lib/services/collection_service.dart index 9fd93dd..f59705c 100644 --- a/lib/services/collection_service.dart +++ b/lib/services/collection_service.dart @@ -170,6 +170,21 @@ class CollectionService { required String name, String? description, }) async { + final userId = supabase.auth.currentUser!.id; + + final collection = await supabase + .from('collections') + .select('owner_id') + .eq('id', collectionId) + .maybeSingle(); + + if (collection == null) { + throw Exception('Collection not found.'); + } + if (collection['owner_id'] != userId) { + throw Exception('Only the collection owner can perform this action.'); + } + await supabase.from('collections').update({ 'name': name, 'description': description, @@ -178,6 +193,21 @@ class CollectionService { /// Delete a collection. Owner only. Cascade deletes members & items. static Future delete(String collectionId) async { + final userId = supabase.auth.currentUser!.id; + + final collection = await supabase + .from('collections') + .select('owner_id') + .eq('id', collectionId) + .maybeSingle(); + + if (collection == null) { + throw Exception('Collection not found.'); + } + if (collection['owner_id'] != userId) { + throw Exception('Only the collection owner can perform this action.'); + } + await supabase.from('collections').delete().eq('id', collectionId); } From b9e298851ba68992dcf35efdf86ea96bac0fd7c9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:19:49 +0000 Subject: [PATCH 13/14] Add 30-second debounce to tab refresh in HomeShell Co-authored-by: derkauzigekoala <79001016+derkauzigekoala@users.noreply.github.com> --- lib/screens/home_shell.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/screens/home_shell.dart b/lib/screens/home_shell.dart index 1a0590e..e833375 100644 --- a/lib/screens/home_shell.dart +++ b/lib/screens/home_shell.dart @@ -15,6 +15,8 @@ class _HomeShellState extends State { int _currentIndex = 0; final _collectionsKey = GlobalKey(); final _scanKey = GlobalKey(); + final Map _lastRefreshed = {}; + static const _refreshDebounce = Duration(seconds: 30); late final List _pages = [ CollectionsScreen(key: _collectionsKey), @@ -24,10 +26,13 @@ class _HomeShellState extends State { void _onTabSelected(int i) { setState(() => _currentIndex = i); + final now = DateTime.now(); + final last = _lastRefreshed[i]; + if (last != null && now.difference(last) < _refreshDebounce) return; + _lastRefreshed[i] = now; if (i == 0) { _collectionsKey.currentState?.refresh(); - } - if (i == 1) { + } else if (i == 1) { _scanKey.currentState?.refresh(); } } From f789bb7eff0c3772da5d512e22460c3f5134dac5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:20:50 +0000 Subject: [PATCH 14/14] Add input validation for collection names (min 2, max 50 chars) Co-authored-by: derkauzigekoala <79001016+derkauzigekoala@users.noreply.github.com> --- lib/screens/collections_screen.dart | 53 ++++++++++++++--------- lib/screens/manage_collection_screen.dart | 47 ++++++++++++-------- 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/lib/screens/collections_screen.dart b/lib/screens/collections_screen.dart index ef24d59..f64b8b7 100644 --- a/lib/screens/collections_screen.dart +++ b/lib/screens/collections_screen.dart @@ -50,6 +50,7 @@ class CollectionsScreenState extends State { } Future _createCollection() async { + final formKey = GlobalKey(); final nameCtrl = TextEditingController(); final descCtrl = TextEditingController(); @@ -65,26 +66,37 @@ class CollectionsScreenState extends State { child: const Icon(Icons.add, color: Colors.white, size: 28), ), title: const Text('New Collection'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: nameCtrl, - autofocus: true, - decoration: const InputDecoration( - labelText: 'Name', - hintText: 'e.g. Hot Wheels, Matchbox…', + content: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + controller: nameCtrl, + autofocus: true, + maxLength: 50, + decoration: const InputDecoration( + labelText: 'Name', + hintText: 'e.g. Hot Wheels, Matchbox…', + ), + validator: (value) { + final trimmed = value?.trim() ?? ''; + if (trimmed.isEmpty) return 'Name is required'; + if (trimmed.length < 2) return 'Name must be at least 2 characters'; + if (trimmed.length > 50) return 'Name must be 50 characters or fewer'; + return null; + }, ), - ), - const SizedBox(height: 12), - TextField( - controller: descCtrl, - decoration: const InputDecoration( - labelText: 'Description (optional)', - hintText: 'What is this collection for?', + const SizedBox(height: 12), + TextField( + controller: descCtrl, + decoration: const InputDecoration( + labelText: 'Description (optional)', + hintText: 'What is this collection for?', + ), ), - ), - ], + ], + ), ), actions: [ TextButton( @@ -93,8 +105,9 @@ class CollectionsScreenState extends State { ), ElevatedButton( onPressed: () { - if (nameCtrl.text.trim().isEmpty) return; - Navigator.pop(context, true); + if (formKey.currentState!.validate()) { + Navigator.pop(context, true); + } }, child: const Text('Create'), ), diff --git a/lib/screens/manage_collection_screen.dart b/lib/screens/manage_collection_screen.dart index faa93b1..1614e54 100644 --- a/lib/screens/manage_collection_screen.dart +++ b/lib/screens/manage_collection_screen.dart @@ -44,6 +44,7 @@ class _ManageCollectionScreenState extends State { } Future _rename() async { + final formKey = GlobalKey(); final ctrl = TextEditingController(text: _collection.name); final descCtrl = TextEditingController(text: _collection.description ?? ''); @@ -52,21 +53,32 @@ class _ManageCollectionScreenState extends State { context: context, builder: (_) => AlertDialog( title: const Text('Rename Collection'), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: ctrl, - autofocus: true, - decoration: const InputDecoration(labelText: 'Name'), - ), - const SizedBox(height: 12), - TextField( - controller: descCtrl, - decoration: - const InputDecoration(labelText: 'Description (optional)'), - ), - ], + content: Form( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + controller: ctrl, + autofocus: true, + maxLength: 50, + decoration: const InputDecoration(labelText: 'Name'), + validator: (value) { + final trimmed = value?.trim() ?? ''; + if (trimmed.isEmpty) return 'Name is required'; + if (trimmed.length < 2) return 'Name must be at least 2 characters'; + if (trimmed.length > 50) return 'Name must be 50 characters or fewer'; + return null; + }, + ), + const SizedBox(height: 12), + TextField( + controller: descCtrl, + decoration: + const InputDecoration(labelText: 'Description (optional)'), + ), + ], + ), ), actions: [ TextButton( @@ -75,8 +87,9 @@ class _ManageCollectionScreenState extends State { ), ElevatedButton( onPressed: () { - if (ctrl.text.trim().isEmpty) return; - Navigator.pop(context, true); + if (formKey.currentState!.validate()) { + Navigator.pop(context, true); + } }, child: const Text('Save'), ),