358 lines
12 KiB
Dart

import 'dart:convert';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:socialbuddy_mobile/core/constants/colors.dart';
import 'package:socialbuddy_mobile/core/constants/api_constants.dart';
import '../../connect/screens/channels_screen.dart';
import '../../connect/screens/social_connect_details_screen.dart';
import '../../automation/screens/automation_screen.dart';
import '../../../core/constants/colors.dart';
class PostsScreen extends StatefulWidget {
const PostsScreen({super.key});
@override
State<PostsScreen> createState() => _PostsScreenState();
}
class _PostsScreenState extends State<PostsScreen> {
List<dynamic> _media = [];
bool _isLoading = true;
String? _error;
String? _connectedChannelName;
Map<String, dynamic>? _accountData;
@override
void initState() {
super.initState();
_fetchMedia();
}
Future<void> _fetchMedia() async {
if (!mounted) return;
setState(() {
_isLoading = true;
_error = null;
});
try {
final prefs = await SharedPreferences.getInstance();
final userEmail = prefs.getString('user_email');
final connectedChannelId = prefs.getString('connectedChannel');
if (userEmail == null) {
setState(() {
_error = "User email not found. Please log in again.";
_isLoading = false;
});
return;
}
// Check if we have account details saved or fetch them
if (connectedChannelId != null) {
await _fetchAccountDetails(userEmail);
}
final url = Uri.parse('${ApiConstants.liveBaseUrl}/social/media?userId=$userEmail&limit=20');
final response = await http.get(url);
if (!mounted) return;
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
setState(() {
_media = data['data'] ?? [];
_isLoading = false;
});
} else {
final data = jsonDecode(response.body);
setState(() {
// Backend might return 'error' or 'message'
_error = data['error'] ?? data['message'] ?? "Failed to load media";
_isLoading = false;
});
}
} catch (e) {
if (mounted) {
setState(() {
_error = "An error occurred: $e";
_isLoading = false;
});
}
}
}
Future<void> _fetchAccountDetails(String email) async {
try {
final response = await http.get(
Uri.parse('${ApiConstants.liveBaseUrl}/social/account?userId=$email'),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
setState(() {
_accountData = data;
_connectedChannelName = data['username'];
});
}
} catch (e) {
debugPrint("Error fetching account for posts header: $e");
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.darkBg,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Media Library", style: GoogleFonts.nunito(fontWeight: FontWeight.bold)),
if (_connectedChannelName != null)
Text("Account: @$_connectedChannelName",
style: GoogleFonts.nunito(fontSize: 12, color: Colors.white54)),
],
),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _fetchMedia,
),
],
),
body: RefreshIndicator(
onRefresh: _fetchMedia,
color: Colors.pinkAccent,
child: _buildBody(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Create Post feature coming soon!")),
);
},
backgroundColor: Colors.blueAccent,
child: const Icon(Icons.add, color: Colors.white),
),
);
}
Widget _buildBody() {
if (_isLoading) {
return const Center(child: CircularProgressIndicator(color: Colors.pinkAccent));
}
if (_error != null) {
return SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Container(
height: MediaQuery.of(context).size.height * 0.7,
alignment: Alignment.center,
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 64, color: Colors.orangeAccent),
const SizedBox(height: 16),
Text(
_error!,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white70, fontSize: 16),
),
const SizedBox(height: 24),
if (_error!.contains("Connect a channel"))
ElevatedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => const ChannelsScreen()),
).then((_) => _fetchMedia()),
style: ElevatedButton.styleFrom(backgroundColor: Colors.pinkAccent),
child: const Text("Go to Channels"),
)
else
ElevatedButton(
onPressed: _fetchMedia,
style: ElevatedButton.styleFrom(backgroundColor: Colors.blueAccent),
child: const Text("Retry"),
),
],
),
),
);
}
if (_media.isEmpty) {
return SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Container(
height: MediaQuery.of(context).size.height * 0.7,
alignment: Alignment.center,
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.photo_library_outlined, size: 80, color: Colors.white10),
const SizedBox(height: 24),
Text(
"No Instagram media found.",
textAlign: TextAlign.center,
style: GoogleFonts.nunito(fontSize: 20, color: Colors.white, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text(
"Make sure you have an active Instagram business account connected.",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white38),
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => const ChannelsScreen()),
).then((_) => _fetchMedia()),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
child: const Text("Connect Channel"),
),
],
),
),
);
}
return GridView.builder(
padding: const EdgeInsets.all(16),
physics: const AlwaysScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.65,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
),
itemCount: _media.length,
itemBuilder: (context, index) {
final item = _media[index];
final mediaType = item['media_type'];
final mediaUrl = item['media_url'];
final caption = item['caption'] ?? "";
final likes = item['like_count']?.toString() ?? "0";
final comments = item['comments_count']?.toString() ?? "0";
return Container(
decoration: BoxDecoration(
color: AppColors.cardBg,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.05)),
boxShadow: [
BoxShadow(color: Colors.black26, blurRadius: 10, offset: const Offset(0, 5)),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
child: Image.network(
mediaUrl,
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
color: Colors.white.withOpacity(0.02),
child: const Center(child: CircularProgressIndicator(strokeWidth: 2)),
);
},
errorBuilder: (c, e, s) => Container(
color: Colors.white10,
child: const Center(child: Icon(Icons.broken_image, color: Colors.white24, size: 32)),
),
),
),
Positioned(
top: 8,
right: 8,
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.black54.withOpacity(0.6),
borderRadius: BorderRadius.circular(8),
),
child: Text(
_getMediaTypeName(mediaType),
style: const TextStyle(color: Colors.white, fontSize: 8, fontWeight: FontWeight.bold),
),
),
),
),
),
],
),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
caption,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: GoogleFonts.nunito(fontSize: 12, color: Colors.white, height: 1.3),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildStatChip(Icons.favorite, likes, Colors.pinkAccent),
_buildStatChip(Icons.comment, comments, Colors.blueAccent),
],
),
],
),
),
],
),
);
},
);
}
String _getMediaTypeName(String type) {
switch (type) {
case 'IMAGE': return 'PHOTO';
case 'VIDEO': return 'VIDEO';
case 'CAROUSEL_ALBUM': return 'CAROUSEL';
default: return type;
}
}
Widget _buildStatChip(IconData icon, String value, Color color) {
return Row(
children: [
Icon(icon, size: 14, color: color),
const SizedBox(width: 4),
Text(value, style: const TextStyle(fontSize: 10, color: Colors.white70, fontWeight: FontWeight.bold)),
],
);
}
}