333 lines
11 KiB
Dart
333 lines
11 KiB
Dart
import 'dart:convert';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
import '../../../core/constants/colors.dart';
|
|
import '../services/social_auth_service.dart';
|
|
import 'channels_screen.dart';
|
|
import '../../posts/screens/posts_screen.dart';
|
|
import '../../automation/screens/automation_screen.dart';
|
|
|
|
class SocialMediaConnectScreen extends StatefulWidget {
|
|
final bool isTab;
|
|
const SocialMediaConnectScreen({super.key, this.isTab = true});
|
|
|
|
@override
|
|
State<SocialMediaConnectScreen> createState() => _SocialMediaConnectScreenState();
|
|
}
|
|
|
|
class _SocialMediaConnectScreenState extends State<SocialMediaConnectScreen> with WidgetsBindingObserver {
|
|
final SocialAuthService _socialAuthService = SocialAuthService();
|
|
String _status = "checking"; // checking, not_connected, finalizing, connected, error
|
|
String? _error;
|
|
bool _isLoading = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
WidgetsBinding.instance.addObserver(this);
|
|
_validateSessionAndInit();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
if (state == AppLifecycleState.resumed) {
|
|
_checkStatus();
|
|
}
|
|
}
|
|
|
|
Future<void> _validateSessionAndInit() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final userJson = prefs.getString('user_details');
|
|
|
|
if (userJson != null) {
|
|
final user = jsonDecode(userJson);
|
|
final role = user['role'];
|
|
|
|
// customer → pricing check required
|
|
if (role == 'customer') {
|
|
final session = prefs.getString('payment_session');
|
|
if (session == null) {
|
|
// If in a real app, navigate to Pricing
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text("Subscription required to access this feature.")),
|
|
);
|
|
// For now, we continue to check status but could redirect
|
|
}
|
|
}
|
|
}
|
|
|
|
_checkStatus();
|
|
}
|
|
|
|
Future<void> _checkStatus() async {
|
|
if (!mounted) return;
|
|
setState(() {
|
|
_status = "checking";
|
|
_error = null;
|
|
});
|
|
|
|
try {
|
|
final status = await _socialAuthService.getStatus();
|
|
if (status['connected'] == true) {
|
|
setState(() => _status = "connected");
|
|
} else {
|
|
setState(() => _status = "not_connected");
|
|
}
|
|
} catch (e) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_status = "error";
|
|
_error = e.toString().replaceAll("Exception: ", "");
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _handleDisconnect() async {
|
|
setState(() => _isLoading = true);
|
|
try {
|
|
final success = await _socialAuthService.disconnect();
|
|
if (success) {
|
|
setState(() => _status = "not_connected");
|
|
} else {
|
|
setState(() {
|
|
_status = "error";
|
|
_error = "Failed to remove Facebook account";
|
|
});
|
|
}
|
|
} catch (e) {
|
|
setState(() {
|
|
_status = "error";
|
|
_error = "Error during disconnection";
|
|
});
|
|
} finally {
|
|
setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
Future<void> _handleConnect() async {
|
|
const url = 'https://api.socialbuddy.co/api/social/auth/login';
|
|
if (await canLaunchUrl(Uri.parse(url))) {
|
|
await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
|
|
} else {
|
|
setState(() {
|
|
_status = "error";
|
|
_error = "Could not launch connection page";
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: AppColors.darkBg,
|
|
appBar: widget.isTab ? null : AppBar(
|
|
backgroundColor: Colors.transparent,
|
|
elevation: 0,
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
|
onPressed: () => Navigator.pop(context),
|
|
),
|
|
),
|
|
body: Stack(
|
|
children: [
|
|
// Background Bubbles (Replicating React bubbles)
|
|
Positioned(bottom: -20, left: 20, child: _renderBubble(70, Colors.pink.withOpacity(0.3))),
|
|
Positioned(bottom: -30, left: 100, child: _renderBubble(90, Colors.blue.withOpacity(0.3))),
|
|
Positioned(bottom: -15, right: 100, child: _renderBubble(60, Colors.green.withOpacity(0.3))),
|
|
Positioned(bottom: -40, right: 30, child: _renderBubble(85, Colors.purple.withOpacity(0.3))),
|
|
|
|
Center(
|
|
child: Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 24),
|
|
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 50),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF242424),
|
|
borderRadius: BorderRadius.circular(30),
|
|
border: Border.all(color: Colors.white.withOpacity(0.15)),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.4),
|
|
blurRadius: 32,
|
|
offset: const Offset(0, 10),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
ShaderMask(
|
|
shaderCallback: (bounds) => const LinearGradient(
|
|
colors: [Color(0xFF0073C6), Color(0xFFE44DB3), Color(0xFF5BBE5B)],
|
|
).createShader(bounds),
|
|
child: Text(
|
|
"Social Buddy",
|
|
style: GoogleFonts.nunito(
|
|
fontSize: 38,
|
|
fontWeight: FontWeight.w900,
|
|
color: Colors.white,
|
|
letterSpacing: 1.1,
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
"Connect & manage your social accounts securely.",
|
|
textAlign: TextAlign.center,
|
|
style: GoogleFonts.nunito(
|
|
fontSize: 17,
|
|
color: Colors.white.withOpacity(0.9),
|
|
height: 1.4,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
const SizedBox(height: 32),
|
|
|
|
// Status
|
|
if (_status == "checking")
|
|
const Text("Checking connection...", style: TextStyle(color: Colors.grey, fontSize: 16))
|
|
else if (_status == "finalizing")
|
|
const Text("Finalizing connection...", style: TextStyle(color: Colors.white, fontSize: 16))
|
|
else if (_status == "connected")
|
|
const Text(
|
|
"✔ Facebook connected!",
|
|
style: TextStyle(color: Color(0xFF00AB55), fontWeight: FontWeight.bold, fontSize: 16),
|
|
)
|
|
else if (_status == "not_connected")
|
|
const Text(
|
|
"No Facebook account connected.",
|
|
style: TextStyle(color: Color(0xFFD4791E), fontWeight: FontWeight.w600, fontSize: 16),
|
|
)
|
|
else if (_status == "error")
|
|
Text(
|
|
"❌ $_error",
|
|
textAlign: TextAlign.center,
|
|
style: const TextStyle(color: Colors.redAccent, fontSize: 15),
|
|
),
|
|
|
|
const SizedBox(height: 40),
|
|
|
|
// Connect Button
|
|
if (_status == "not_connected")
|
|
_buildGradientButton(
|
|
text: "Connect with Facebook",
|
|
onPressed: _isLoading ? null : _handleConnect,
|
|
colors: [const Color(0xFF2563EB), const Color(0xFFD946EF)],
|
|
),
|
|
|
|
// Disconnect Button
|
|
if (_status == "connected")
|
|
_buildFullWidthButton(
|
|
text: "Disconnect Facebook",
|
|
onPressed: _isLoading ? null : _handleDisconnect,
|
|
color: const Color(0xFFE11D48),
|
|
),
|
|
|
|
if (_status == "connected") ...[
|
|
const SizedBox(height: 20),
|
|
Text(
|
|
"You can re-connect anytime.",
|
|
style: GoogleFonts.nunito(
|
|
color: Colors.white.withOpacity(0.5),
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _renderBubble(double size, Color color) {
|
|
return Container(
|
|
width: size,
|
|
height: size,
|
|
decoration: BoxDecoration(
|
|
color: color,
|
|
shape: BoxShape.circle,
|
|
boxShadow: [BoxShadow(color: color.withOpacity(0.2), blurRadius: 20)],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildGradientButton({required String text, required VoidCallback? onPressed, required List<Color> colors}) {
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
height: 56,
|
|
child: DecoratedBox(
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(colors: colors),
|
|
borderRadius: BorderRadius.circular(20),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: colors.first.withOpacity(0.3),
|
|
blurRadius: 12,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.transparent,
|
|
shadowColor: Colors.transparent,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
),
|
|
onPressed: onPressed,
|
|
child: FittedBox(
|
|
fit: BoxFit.scaleDown,
|
|
child: Text(
|
|
text,
|
|
style: GoogleFonts.nunito(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildFullWidthButton({required String text, required VoidCallback? onPressed, required Color color}) {
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
height: 56,
|
|
child: ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: color,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
|
elevation: 0,
|
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
shadowColor: Colors.transparent,
|
|
).copyWith(
|
|
overlayColor: MaterialStateProperty.all(Colors.white.withOpacity(0.1)),
|
|
),
|
|
onPressed: onPressed,
|
|
child: _isLoading
|
|
? const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2))
|
|
: FittedBox(
|
|
fit: BoxFit.scaleDown,
|
|
child: Text(
|
|
text,
|
|
style: GoogleFonts.nunito(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
|