import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:material_symbols_icons/symbols.dart'; import '../i18n/strings.g.dart'; import '../services/onboarding_store.dart'; import '../services/sharing_switch.dart'; import 'seed_glyph.dart'; import 'sharing_invite_sheet.dart'; import 'theme.dart'; import 'unread_badge.dart'; /// The app's navigation drawer (redesign screen 05). A white sheet: Inventory is /// the live destination (green seed glyph), the social items sit below a divider, /// and Settings is pinned at the bottom. /// /// While sharing is off the social items stay visible but quiet, with a small /// padlock: tapping one explains what it does and offers to join. The Market is /// the exception — it is always live, because it is the door people go through /// to join in the first place. class AppDrawer extends StatelessWidget { const AppDrawer({this.sharing, this.onboarding, super.key}); /// The sharing on/off switch. Null when the social layer isn't there at all /// (identity derivation failed) — then the social items are not drawn, since /// there is nothing the person could do to turn them on. final SharingSwitch? sharing; /// Needed to run the community-rules step when someone accepts the invite. final OnboardingStore? onboarding; @override Widget build(BuildContext context) { final sharing = this.sharing; if (sharing == null) return _build(context, social: false, sharingOn: false); return ValueListenableBuilder( valueListenable: sharing.on, builder: (context, on, _) => _build(context, social: true, sharingOn: on), ); } Widget _build( BuildContext context, { required bool social, required bool sharingOn, }) { final t = context.t; // While sharing is off, tapping a social entry invites the person in rather // than doing nothing. Future invite() async { final sharing = this.sharing; final onboarding = this.onboarding; if (sharing == null || onboarding == null) return; // Close the drawer first, then run the sheet off the navigator's own // context — this one dies with the drawer. final navigator = Navigator.of(context); navigator.pop(); await showSharingInvite( navigator.context, onboarding: onboarding, sharing: sharing, ); } return Drawer( child: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const _DrawerHeader(), // The destinations scroll; the header above and the utility footer // below stay pinned, so Settings/About are always reachable even on // a short screen (drawer used to overflow once Calendar was added). Expanded( child: ListView( padding: EdgeInsets.zero, children: [ _DrawerItem( icon: const SeedGlyph(SeedGlyphs.jars, size: 23), label: t.menu.inventory, onTap: () { Navigator.of(context).pop(); context.push('/inventory'); }, ), // Local, works offline — a reproduction-commitment ledger. _DrawerItem( icon: const Icon(Icons.volunteer_activism_outlined), label: t.menu.plantares, onTap: () { Navigator.of(context).pop(); context.push('/plantares'); }, ), // Local sales ledger — also offline. _DrawerItem( icon: const Icon(Icons.sell_outlined), label: t.menu.sales, onTap: () { Navigator.of(context).pop(); context.push('/sales'); }, ), // "What's due this month" — the aggregate crop calendar. _DrawerItem( icon: const Icon(Icons.calendar_month), label: t.menu.calendar, onTap: () { Navigator.of(context).pop(); context.push('/calendar'); }, ), // The market is always live when the social layer exists: it // is where people join sharing, so locking it would lock the // only door. if (social) _DrawerItem( icon: const Icon(Symbols.storefront), label: t.menu.market, divider: true, onTap: () { Navigator.of(context).pop(); context.push('/market'); }, ), if (social) _DrawerItem( icon: const Icon(Icons.person), label: t.menu.profile, locked: !sharingOn, onTap: sharingOn ? () { Navigator.of(context).pop(); context.push('/profile'); } : invite, ), if (social) _DrawerItem( icon: const UnreadBadge(child: Icon(Icons.chat_bubble)), label: t.menu.chat, locked: !sharingOn, onTap: sharingOn ? () { Navigator.of(context).pop(); context.push('/messages'); } : invite, ), if (social) _DrawerItem( icon: const Icon(Icons.favorite), label: t.menu.wishlist, locked: !sharingOn, onTap: sharingOn ? () { Navigator.of(context).pop(); context.push('/favorites'); } : invite, ), // The ego-centric web of trust ("your people"). if (social) _DrawerItem( icon: const Icon(Icons.group), label: t.menu.following, locked: !sharingOn, onTap: sharingOn ? () { Navigator.of(context).pop(); context.push('/your-people'); } : invite, ), ], ), ), const Divider(height: 1), _DrawerItem( icon: const Icon(Icons.auto_stories_outlined), label: t.intro.menuEntry, onTap: () { Navigator.of(context).pop(); context.push('/intro'); }, ), _DrawerItem( icon: const Icon(Icons.settings), label: t.menu.settings, onTap: () { Navigator.of(context).pop(); context.push('/settings'); }, ), _DrawerItem( icon: const Icon(Icons.info_outline), label: t.about.title, onTap: () { Navigator.of(context).pop(); context.push('/about'); }, ), ], ), ), ); } } /// The drawer's brand header. Tapping it closes the drawer and returns to the /// home screen. class _DrawerHeader extends StatelessWidget { const _DrawerHeader(); @override Widget build(BuildContext context) { return InkWell( onTap: () { Navigator.of(context).pop(); context.go('/'); }, child: Container( padding: const EdgeInsets.fromLTRB(22, 18, 22, 16), decoration: const BoxDecoration( border: Border(bottom: BorderSide(color: seedDivider)), ), child: Row( children: [ Image.asset('assets/logo.png', height: 34), const SizedBox(width: 12), Text( context.t.app.title, style: const TextStyle( color: seedGreen, fontSize: 22, fontWeight: FontWeight.w700, letterSpacing: -0.2, ), ), ], ), ), ); } } /// A 52-tall drawer row. A live destination has an [onTap] (dark text, green /// icon); a disabled Block-2 destination is greyed with a "soon" tag. [divider] /// draws a hairline under the row (separating live from upcoming items). class _DrawerItem extends StatelessWidget { const _DrawerItem({ required this.icon, required this.label, this.onTap, this.divider = false, this.locked = false, }); final Widget icon; final String label; final VoidCallback? onTap; final bool divider; /// Drawn quiet, with a padlock, but still tappable — it leads to the /// invitation to join sharing rather than to the destination itself. final bool locked; @override Widget build(BuildContext context) { final enabled = onTap != null && !locked; final fg = enabled ? seedOnSurface : const Color(0xFF9AA88F); final iconColor = enabled ? seedGreen : const Color(0xFF9AA88F); final row = InkWell( onTap: onTap, child: Container( height: 52, padding: const EdgeInsets.symmetric(horizontal: 22), decoration: divider ? const BoxDecoration( border: Border(bottom: BorderSide(color: seedDivider)), ) : null, child: Row( children: [ SizedBox( width: 24, child: Center( child: IconTheme.merge( data: IconThemeData(size: 23, color: iconColor), child: icon, ), ), ), const SizedBox(width: 18), Expanded( child: Text( label, style: TextStyle( color: fg, fontSize: 15, fontWeight: enabled ? FontWeight.w500 : FontWeight.w400, ), ), ), if (locked) const Icon( Icons.lock_outline, size: 16, color: Color(0xFFB3BDA8), ), ], ), ), ); return row; } }