import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:onsolgo/core/constants.dart'; /// Public feed of artist-scheduled releases (`upcoming` collection). class ComingSoon extends StatelessWidget { const ComingSoon({super.key}); String _fmtDate(DateTime d) => '${d.month.toString().padLeft(2, '0')}/${d.day.toString().padLeft(2, '0')}/${d.year}'; String _countdown(Timestamp? ts, {required bool dateTbd}) { if (dateTbd) return 'TBD'; if (ts == null) return ''; final t = ts.toDate(); final now = DateTime.now(); final endOfToday = DateTime(now.year, now.month, now.day, 23, 59, 59); if (t.isBefore(endOfToday) && t.year == now.year && t.month == now.month && t.day == now.day) { return 'Today'; } if (t.isBefore(now)) return 'Out now'; final diff = t.difference(now); if (diff.inDays >= 1) return 'in ${diff.inDays}d'; if (diff.inHours >= 1) return 'in ${diff.inHours}h'; return 'in ${diff.inMinutes}m'; } int _sortUpcoming(DocumentSnapshot a, DocumentSnapshot b) { final ad = a.data() as Map; final bd = b.data() as Map; final af = ad['featured'] == true; final bf = bd['featured'] == true; if (af != bf) return af ? -1 : 1; final atbd = ad['dateTbd'] == true; final btbd = bd['dateTbd'] == true; if (atbd != btbd) return atbd ? 1 : -1; final ta = (ad['targetDate'] as Timestamp?)?.millisecondsSinceEpoch ?? 0; final tb = (bd['targetDate'] as Timestamp?)?.millisecondsSinceEpoch ?? 0; return ta.compareTo(tb); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, body: CustomScrollView( slivers: [ SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.fromLTRB(20, 56, 20, 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Icon(Icons.auto_awesome, size: 40, color: kOnsolGold), const SizedBox(height: 12), const Text( 'THE SUN IS RISING', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, letterSpacing: 3), ), const SizedBox(height: 8), Text( 'New series and chapter drops from ManaA artists.', style: TextStyle(color: Colors.grey[400], letterSpacing: 0.5, height: 1.3), ), ], ), ), ), StreamBuilder( stream: FirebaseFirestore.instance.collection('upcoming').limit(80).snapshots(), builder: (context, snapshot) { if (snapshot.hasError) { return SliverFillRemaining( child: Center(child: Text('Could not load schedule.\n${snapshot.error}', textAlign: TextAlign.center, style: const TextStyle(color: Colors.white54))), ); } if (!snapshot.hasData) { return const SliverFillRemaining(child: Center(child: CircularProgressIndicator(color: kOnsolGold))); } final docs = snapshot.data!.docs.toList()..sort(_sortUpcoming); if (docs.isEmpty) { return SliverFillRemaining( child: Center( child: Text('Check back soon for announcements.', style: TextStyle(color: Colors.grey[500])), ), ); } return SliverPadding( padding: const EdgeInsets.fromLTRB(16, 0, 16, 32), sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, i) { final doc = docs[i]; final d = doc.data() as Map; final kind = d['kind'] as String? ?? ''; final seriesTitle = d['seriesTitle'] ?? 'Untitled'; final artist = d['authorName'] ?? 'Artist'; final dateTbd = d['dateTbd'] as bool? ?? false; final ts = d['targetDate'] as Timestamp?; final dateStr = dateTbd ? 'Date TBD' : (ts != null ? _fmtDate(ts.toDate()) : '—'); final cd = _countdown(ts, dateTbd: dateTbd); final ch = d['chapterNumber']; final featured = d['featured'] == true; final description = (d['description'] as String?)?.trim() ?? ''; final coverUrl = (d['teaserCoverUrl'] as String?)?.trim() ?? ''; final headline = kind == 'new_series' ? '$seriesTitle · $artist' : 'New chapter · $seriesTitle${ch != null ? ' (Ch $ch)' : ''}'; final borderColor = featured ? kOnsolGold : kOnsolGold.withValues(alpha: 0.25); final borderWidth = featured ? 2.5 : 1.0; return Container( margin: const EdgeInsets.only(bottom: 14), decoration: BoxDecoration( borderRadius: BorderRadius.circular(14), border: Border.all(color: borderColor, width: borderWidth), boxShadow: featured ? [ BoxShadow( color: kOnsolGold.withValues(alpha: 0.18), blurRadius: 16, spreadRadius: 0, ), ] : null, ), child: Card( color: featured ? const Color(0xFF1A1708) : Colors.grey[900], margin: EdgeInsets.zero, elevation: featured ? 4 : 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(13)), child: Padding( padding: const EdgeInsets.all(14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (featured) Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( children: [ Icon(Icons.star_rounded, color: kOnsolGold, size: 18), const SizedBox(width: 6), Text( 'FEATURED DROP', style: TextStyle( color: kOnsolGold, fontSize: 11, fontWeight: FontWeight.w800, letterSpacing: 1.4, ), ), ], ), ), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (coverUrl.isNotEmpty) ...[ ClipRRect( borderRadius: BorderRadius.circular(10), child: CachedNetworkImage( imageUrl: coverUrl, width: 92, height: 130, fit: BoxFit.cover, alignment: Alignment.topCenter, ), ), const SizedBox(width: 14), ], Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( headline, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, height: 1.3, fontSize: 15), ), if (kind != 'new_series') ...[ const SizedBox(height: 4), Text(artist, style: TextStyle(color: Colors.grey[500], fontSize: 12)), ], if (description.isNotEmpty) ...[ const SizedBox(height: 8), Text( description, maxLines: 4, overflow: TextOverflow.ellipsis, style: TextStyle(color: Colors.grey[300], fontSize: 13, height: 1.35), ), ], ], ), ), ], ), const SizedBox(height: 12), Row( children: [ Icon(Icons.calendar_today, size: 14, color: Colors.grey[500]), const SizedBox(width: 6), Text( dateTbd ? 'Target: to be announced' : 'Target: $dateStr', style: TextStyle(color: Colors.grey[400], fontSize: 12), ), const Spacer(), if (!dateTbd && cd.isNotEmpty) Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: kOnsolGold.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(8), ), child: Text(cd, style: const TextStyle(color: kOnsolGold, fontSize: 11, fontWeight: FontWeight.bold)), ) else if (dateTbd) Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(8), ), child: const Text('SOON', style: TextStyle(color: Colors.white70, fontSize: 11, fontWeight: FontWeight.bold)), ), ], ), ], ), ), ), ); }, childCount: docs.length, ), ), ); }, ), ], ), ); } }