feat: shop is change

This commit is contained in:
2026-07-05 19:49:59 +03:30
parent e94f03c499
commit df5cc699b6
3 changed files with 109 additions and 63 deletions
+17
View File
@@ -19,6 +19,23 @@ class AppConfig {
defaultValue: '',
);
/// App ID تپسل (از پنلِ تپسل). تا زمانی که تنظیم نشود، تبلیغِ ویدیویی-جایزه‌ای
/// («سکه رایگان») نمایش داده نمی‌شود. با `--dart-define=TAPSELL_APP_ID=...`
/// یا مقداردهیِ پیش‌فرض این‌جا فعال می‌شود.
static const String tapsellAppId = String.fromEnvironment(
'TAPSELL_APP_ID',
defaultValue: '',
);
/// شناسه‌ی جایگاهِ (zone) تبلیغِ ویدیویی-جایزه‌ای تپسل.
static const String tapsellRewardedZoneId = String.fromEnvironment(
'TAPSELL_REWARDED_ZONE',
defaultValue: '',
);
/// آیا تبلیغِ جایزه‌ای فعال است؟ فقط وقتی App ID تپسل موجود باشد.
static bool get adsEnabled => tapsellAppId.isNotEmpty;
/// آدرس پایه‌ی سرور.
/// - موبایل واقعی روی همان Wi-Fi: IP محلی مک (پیش‌فرض فعلی).
/// - امولاتور اندروید: `--dart-define=BASE_URL=http://10.0.2.2:8080`
@@ -54,7 +54,7 @@ class ShopScreen extends StatelessWidget {
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF4A0C16), Color(0xFF2A0710)],
colors: [AppColors.lapisMid, AppColors.lapisLo],
),
borderRadius: BorderRadius.circular(16),
border: Border.all(
@@ -155,9 +155,9 @@ class _ShopTabs extends StatelessWidget {
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [AppColors.panel, AppColors.lapis],
colors: [AppColors.lapis, AppColors.suitDark],
),
borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)),
borderRadius: BorderRadius.all(Radius.circular(12)),
),
indicatorSize: TabBarIndicatorSize.tab,
labelColor: AppColors.gold,
@@ -188,6 +188,8 @@ class _CoinsTab extends StatelessWidget {
return _grid(
note: 'با داشتن اشتراک VIP در هر خرید ۱۰٪ سکه اضافه هدیه می‌گیرید.',
children: [
// «سکه رایگان» (تبلیغِ ویدیویی-جایزه‌ای) فقط وقتی App ID تپسل تنظیم شده باشد.
if (AppConfig.adsEnabled)
_ItemCard(
title: 'سکه رایگان',
glowColor: AppColors.successDark,
@@ -210,8 +212,9 @@ class _CoinsTab extends StatelessWidget {
action: _PriceButton(
label: '${p.priceToman} تومان',
onTap:
() =>
context.read<ShopBloc>().add(PurchaseEvent('coin', p.id, sku: p.sku)),
() => context.read<ShopBloc>().add(
PurchaseEvent('coin', p.id, sku: p.sku),
),
),
),
],
@@ -291,8 +294,9 @@ class _VipTab extends StatelessWidget {
action: _PriceButton(
label: '${p.priceToman} تومان',
onTap:
() =>
context.read<ShopBloc>().add(PurchaseEvent('vip', p.id, sku: p.sku)),
() => context.read<ShopBloc>().add(
PurchaseEvent('vip', p.id, sku: p.sku),
),
),
),
],
@@ -366,15 +370,15 @@ class _CarpetsTabState extends State<_CarpetsTab> {
Future<void> _act(Carpet c, {required bool buy}) async {
setState(() => _busy = c.id);
final err = buy ? await _api.buyCarpet(c.id) : await _api.selectCarpet(c.id);
final err =
buy ? await _api.buyCarpet(c.id) : await _api.selectCarpet(c.id);
if (!mounted) return;
setState(() => _busy = null);
if (err == null) {
context.read<WalletBloc>().add(LoadWalletEvent());
_reload();
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(err)));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(err)));
}
}
@@ -385,12 +389,15 @@ class _CarpetsTabState extends State<_CarpetsTab> {
builder: (context, snap) {
if (snap.connectionState != ConnectionState.done) {
return const Center(
child: CircularProgressIndicator(color: AppColors.gold));
child: CircularProgressIndicator(color: AppColors.gold),
);
}
if (!snap.hasData) {
return Center(
child: TextButton(
onPressed: _reload, child: const Text('تلاش مجدد')),
onPressed: _reload,
child: const Text('تلاش مجدد'),
),
);
}
final cat = snap.data!;
@@ -435,20 +442,25 @@ class _CarpetCard extends StatelessWidget {
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF6E1322), Color(0xFF3A0A12)],
colors: [AppColors.lapisHi, AppColors.lapisLo],
),
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: selected ? AppColors.gold : AppColors.goldDark,
width: selected ? 2 : 1.4),
width: selected ? 2 : 1.4,
),
),
child: Column(
children: [
Text(c.title,
Text(
c.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: AppColors.gold, fontWeight: FontWeight.bold)),
color: AppColors.gold,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 6),
Expanded(child: _preview(c)),
const SizedBox(height: 6),
@@ -461,37 +473,51 @@ class _CarpetCard extends StatelessWidget {
Widget _preview(Carpet c) {
final frame = ClipRRect(
borderRadius: BorderRadius.circular(8),
child: c.isClassic
child:
c.isClassic
? Container(
color: const Color(0xFF0E3614),
child: const Center(
child: Icon(Icons.casino, color: AppColors.gold, size: 36)),
child: Icon(Icons.casino, color: AppColors.gold, size: 36),
),
)
: Image.network(
c.imageUrl,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => Container(
color: const Color(0xFF2A0710),
child: const Icon(Icons.image_not_supported,
color: AppColors.goldDark)),
errorBuilder:
(_, __, ___) => Container(
color: AppColors.lapisLo,
child: const Icon(
Icons.image_not_supported,
color: AppColors.goldDark,
),
),
),
);
return Stack(fit: StackFit.expand, children: [
return Stack(
fit: StackFit.expand,
children: [
frame,
if (c.vip && !c.owned)
const Positioned(
top: 4,
left: 4,
child: Chip(
label: Text('VIP',
label: Text(
'VIP',
style: TextStyle(
color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)),
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
backgroundColor: AppColors.goldDark,
padding: EdgeInsets.zero,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
),
]);
],
);
}
Widget _action(Carpet c) {
@@ -567,7 +593,7 @@ class _ItemCard extends StatelessWidget {
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF6E1322), AppColors.bg],
colors: [AppColors.lapisHi, AppColors.lapisLo],
),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColors.gold, width: 1.6),
@@ -740,7 +766,10 @@ class _PriceButton extends StatelessWidget {
? const [Color(0xFF555555), Color(0xFF333333)]
: green
? const [AppColors.success, AppColors.successDark]
: const [Color(0xFFD83A4A), Color(0xFF8E0E1B)];
: const [
AppColors.accent,
Color(0xFF7A1019),
]; // شنگرفِ ایرانی (هم‌رنگِ تم)
return Opacity(
opacity: disabled ? 0.85 : 1,
child: GestureDetector(
@@ -155,7 +155,7 @@ class _VipPackageTile extends StatelessWidget {
gradient: const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF6E1322), AppColors.bg],
colors: [AppColors.lapisHi, AppColors.lapisLo],
),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColors.gold, width: 1.4),