import 'dart:ui'; import 'package:flutter/material.dart'; import '../../../../core/locator/locator.dart'; import '../../../../core/theme/app_theme.dart'; import '../../data/data_source/remote/chat_api_provider.dart'; import '../../domain/entities/chat_pack.dart'; /// پنلِ «بسته چت»: قابِ لاجوردیِ طلاکوب، تبِ حبّه‌ایِ هر بسته (با قفلِ بسته‌های /// نخریده)، و گریدِ پیام/شکلک. بسته‌های قفل با محوکردنِ گرید و کارتِ خرید نمایش /// داده می‌شوند (سکه/VIP). class ChatPanel extends StatefulWidget { final void Function(String text) onText; final void Function(String emoji) onEmoji; final VoidCallback? onWalletChanged; // پس از خرید، موجودی را تازه کن const ChatPanel({ super.key, required this.onText, required this.onEmoji, this.onWalletChanged, }); static Future show( BuildContext context, { required void Function(String text) onText, required void Function(String emoji) onEmoji, VoidCallback? onWalletChanged, }) { return showDialog( context: context, barrierColor: Colors.black.withValues(alpha: 0.6), builder: (_) => ChatPanel( onText: onText, onEmoji: onEmoji, onWalletChanged: onWalletChanged, ), ); } @override State createState() => _ChatPanelState(); } class _ChatPanelState extends State { final _api = locator(); late Future> _future; String? _busyPack; // بسته‌ای که در حالِ خرید است @override void initState() { super.initState(); _future = _api.getChatPacks(); } void _reload() => setState(() { _future = _api.getChatPacks(); }); Future _buy(ChatPack p) async { setState(() => _busyPack = p.id); final err = await _api.buyChatPack(p.id); if (!mounted) return; setState(() => _busyPack = null); if (err == null) { widget.onWalletChanged?.call(); _reload(); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(err), duration: const Duration(seconds: 2)), ); } } void _pick(ChatPack p, String body) { if (p.isEmoji) { widget.onEmoji(body); } else { widget.onText(body); } Navigator.pop(context); } @override Widget build(BuildContext context) { return Dialog( backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 40), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 440), child: FutureBuilder>( future: _future, builder: (context, snap) { if (snap.connectionState != ConnectionState.done) { return _shell(const SizedBox( height: 260, child: Center( child: CircularProgressIndicator(color: AppColors.gold)), )); } final packs = snap.data ?? const []; if (packs.isEmpty) { return _shell(const SizedBox( height: 200, child: Center( child: Text('بسته‌ای موجود نیست', style: TextStyle(color: Colors.white70))), )); } return DefaultTabController( length: packs.length, child: _shell( Column( mainAxisSize: MainAxisSize.min, children: [ _tabs(packs), const SizedBox(height: 12), SizedBox( height: 320, child: TabBarView( children: [for (final p in packs) _packView(p)], ), ), ], ), ), ); }, ), ), ); } // تبِ حبّه‌ای؛ بسته‌های قفل یک قفلِ کوچک کنارِ عنوان دارند. Widget _tabs(List packs) { return SizedBox( height: 40, child: TabBar( isScrollable: true, tabAlignment: TabAlignment.start, padding: const EdgeInsets.symmetric(horizontal: 2), indicator: BoxDecoration( gradient: const LinearGradient( colors: [AppColors.lapis, AppColors.lapisHi], ), borderRadius: BorderRadius.circular(20), border: Border.all(color: AppColors.gold, width: 1.3), boxShadow: [ BoxShadow( color: AppColors.gold.withValues(alpha: 0.25), blurRadius: 8, ), ], ), indicatorSize: TabBarIndicatorSize.tab, labelColor: AppColors.gold, unselectedLabelColor: Colors.white60, labelStyle: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13), unselectedLabelStyle: const TextStyle(fontSize: 13), dividerColor: Colors.transparent, overlayColor: WidgetStateProperty.all(Colors.transparent), tabs: [ for (final p in packs) Tab( height: 36, child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( p.isEmoji ? Icons.emoji_emotions_outlined : Icons.chat_bubble_outline, size: 15, ), const SizedBox(width: 5), Text(p.title), if (!p.owned) ...[ const SizedBox(width: 5), Icon( p.vip ? Icons.workspace_premium : Icons.lock, size: 13, color: AppColors.gold, ), ], ], ), ), ], ), ); } // قابِ لاجوردیِ پنل با ربانِ طلاییِ «بسته چت» و دکمه‌ی بستن. Widget _shell(Widget child) { return Stack( clipBehavior: Clip.none, children: [ Container( margin: const EdgeInsets.only(top: 20), padding: const EdgeInsets.fromLTRB(14, 34, 14, 16), decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [AppColors.lapisMid, AppColors.lapisLo], ), borderRadius: BorderRadius.circular(22), border: Border.all(color: AppColors.gold, width: 1.6), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.5), blurRadius: 24, offset: const Offset(0, 12), ), BoxShadow( color: AppColors.gold.withValues(alpha: 0.12), blurRadius: 20, ), ], ), child: child, ), // ربانِ عنوان Positioned( top: 0, left: 0, right: 0, child: Center( child: Container( padding: const EdgeInsets.symmetric(horizontal: 26, vertical: 8), decoration: BoxDecoration( gradient: const LinearGradient( colors: [AppColors.gold, AppColors.goldDark], ), borderRadius: BorderRadius.circular(14), border: Border.all(color: Colors.white.withValues(alpha: 0.35)), boxShadow: [ BoxShadow( color: AppColors.goldDark.withValues(alpha: 0.5), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: const Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.forum_rounded, color: AppColors.lapisInk, size: 18), SizedBox(width: 7), Text('بسته چت', style: TextStyle( color: AppColors.lapisInk, fontSize: 16, fontWeight: FontWeight.bold)), ], ), ), ), ), Positioned( top: 12, right: 4, child: GestureDetector( onTap: () => Navigator.pop(context), child: Container( width: 30, height: 30, decoration: BoxDecoration( color: AppColors.accent, shape: BoxShape.circle, border: Border.all(color: Colors.white.withValues(alpha: 0.4)), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.4), blurRadius: 6, ), ], ), child: const Icon(Icons.close, color: Colors.white, size: 18), ), ), ), ], ); } Widget _packView(ChatPack p) { final grid = p.isEmoji ? _emojiGrid(p) : _textGrid(p); if (p.owned) return grid; // بسته‌ی قفل: گریدِ محو (blur) + کارتِ خرید روی آن. return ClipRRect( borderRadius: BorderRadius.circular(14), child: Stack( fit: StackFit.expand, children: [ IgnorePointer(child: Opacity(opacity: 0.5, child: grid)), BackdropFilter( filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3), child: Container(color: AppColors.lapisLo.withValues(alpha: 0.25)), ), Center(child: _buyCard(p)), ], ), ); } Widget _buyCard(ChatPack p) { final busy = _busyPack == p.id; return Container( padding: const EdgeInsets.fromLTRB(20, 22, 20, 20), margin: const EdgeInsets.symmetric(horizontal: 28), decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [AppColors.lapisHi, AppColors.lapisLo], ), borderRadius: BorderRadius.circular(18), border: Border.all(color: AppColors.gold, width: 1.5), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.45), blurRadius: 16, offset: const Offset(0, 8), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppColors.gold.withValues(alpha: 0.12), shape: BoxShape.circle, border: Border.all(color: AppColors.goldFaint), ), child: Icon( p.vip ? Icons.workspace_premium : Icons.lock, color: AppColors.gold, size: 28, ), ), const SizedBox(height: 12), Text( p.vip ? 'ویژه‌ی اعضای VIP' : 'این بسته قفل است', textAlign: TextAlign.center, style: const TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold, ), ), if (p.vip) const Padding( padding: EdgeInsets.only(top: 4), child: Text('برای اعضای VIP رایگان است', textAlign: TextAlign.center, style: TextStyle(color: AppColors.gold, fontSize: 12)), ), const SizedBox(height: 16), SizedBox( width: double.infinity, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: AppColors.success, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), side: const BorderSide(color: AppColors.gold, width: 1.3), ), ), onPressed: busy ? null : () => _buy(p), child: busy ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2.2, color: Colors.white)) : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('خرید', style: TextStyle( color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold)), const SizedBox(width: 8), const Icon(Icons.monetization_on, color: AppColors.gold, size: 20), const SizedBox(width: 3), Text('${p.priceCoins}', style: const TextStyle( color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold)), ], ), ), ), ], ), ); } Widget _textGrid(ChatPack p) => GridView.count( crossAxisCount: 2, padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 4), childAspectRatio: 2.7, mainAxisSpacing: 10, crossAxisSpacing: 10, physics: const BouncingScrollPhysics(), children: [ for (final m in p.messages) Material( color: Colors.transparent, borderRadius: BorderRadius.circular(12), child: InkWell( borderRadius: BorderRadius.circular(12), onTap: p.owned ? () => _pick(p, m) : null, child: Ink( decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [AppColors.lapisHi, AppColors.lapisLo], ), borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColors.goldDark, width: 1.1), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.3), blurRadius: 5, offset: const Offset(0, 3), ), ], ), child: Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Text(m, maxLines: 2, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13.5)), ), ), ), ), ), ], ); Widget _emojiGrid(ChatPack p) => GridView.count( crossAxisCount: 5, padding: const EdgeInsets.all(6), mainAxisSpacing: 8, crossAxisSpacing: 8, physics: const BouncingScrollPhysics(), children: [ for (final e in p.messages) Material( color: Colors.transparent, borderRadius: BorderRadius.circular(14), child: InkWell( borderRadius: BorderRadius.circular(14), onTap: p.owned ? () => _pick(p, e) : null, child: Ink( decoration: BoxDecoration( color: AppColors.lapisHi.withValues(alpha: 0.55), borderRadius: BorderRadius.circular(14), border: Border.all(color: AppColors.goldFaint), ), child: Center( child: Text(e, style: const TextStyle(fontSize: 30))), ), ), ), ], ); }