MIF_E31222656/lib/screens/community/enhanced_community_screen.dart

1046 lines
32 KiB
Dart

// ignore_for_file: avoid_print
import 'package:flutter/material.dart';
import 'package:tugas_akhir_supabase/screens/community/components/group_chat_screen.dart';
import 'package:tugas_akhir_supabase/screens/community/components/simple_news_tab.dart';
import 'package:tugas_akhir_supabase/screens/community/components/farming_guide_tab.dart';
import 'package:tugas_akhir_supabase/screens/community/components/group_selector.dart';
import 'package:tugas_akhir_supabase/screens/community/models/group.dart';
import 'package:tugas_akhir_supabase/screens/community/services/group_service.dart';
import 'package:tugas_akhir_supabase/core/theme/app_colors.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:tugas_akhir_supabase/screens/community/components/group_card.dart';
import 'package:uuid/uuid.dart';
import 'package:flutter/rendering.dart';
class EnhancedCommunityScreen extends StatefulWidget {
const EnhancedCommunityScreen({super.key});
@override
_EnhancedCommunityScreenState createState() =>
_EnhancedCommunityScreenState();
}
class _EnhancedCommunityScreenState extends State<EnhancedCommunityScreen>
with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
// View state
late TabController _tabController;
int _currentIndex = 0;
bool _isLoadingGroups = true;
String? _selectedGroupId;
bool _showGroupSelector = false;
// Group data
List<Group> _userGroups = [];
Set<String> _userGroupIds = {};
String? _loadingGroupId; // Track grup yang sedang di-join/leave
bool _isJoiningGroup = false;
// Tambahkan deklarasi channel realtime
RealtimeChannel? _groupRealtimeChannel;
// Add a key for the GroupChatScreen
final GlobalKey<GroupChatScreenState> _groupChatKey =
GlobalKey<GroupChatScreenState>();
@override
void initState() {
super.initState();
_tabController = TabController(
length: 3,
vsync: this,
initialIndex: 0, // Default to first tab
);
_tabController.addListener(_handleTabChange);
// Panggil langsung tanpa Future.delayed
_safeLoadGroups();
_preloadDefaultGroupMessages();
Future.delayed(Duration(seconds: 2), () {
if (mounted) {
_forceRefreshAllMessages();
}
});
_setupGroupRealtimeSubscription();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// Jangan reload groups di sini, karena bisa menyebabkan multiple loads
// _loadUserGroups();
}
Future<void> _safeLoadGroups() async {
try {
// Set safety timer lebih awal
Future.delayed(Duration(seconds: 3), () {
if (mounted && _isLoadingGroups) {
print('[WARNING] Safety timer triggered - forcing load completion');
setState(() {
_isLoadingGroups = false;
if (_userGroups.isEmpty) {
_createFallbackGroup();
}
});
}
});
// Coba muat grup dengan timeout
await _loadUserGroups().timeout(
Duration(seconds: 3),
onTimeout: () {
print('[WARNING] _loadUserGroups timed out');
if (mounted && _isLoadingGroups) {
setState(() {
_isLoadingGroups = false;
if (_userGroups.isEmpty) {
_createFallbackGroup();
}
});
}
},
);
} catch (e) {
print('[ERROR] Error in _safeLoadGroups: $e');
if (mounted && _isLoadingGroups) {
setState(() {
_isLoadingGroups = false;
if (_userGroups.isEmpty) {
_createFallbackGroup();
}
});
}
}
}
// Metode untuk membuat grup fallback
void _createFallbackGroup() {
// Gunakan UUID yang valid, bukan string timestamp
final fallbackGroup = Group(
id: '00000000-0000-0000-0000-000000000001', // UUID valid
name: 'Grup Tani Umum',
description: 'Grup diskusi umum untuk petani',
createdBy: 'system',
isDefault: true,
isPublic: true,
);
_userGroups = [fallbackGroup];
_userGroupIds = {fallbackGroup.id};
_selectedGroupId = fallbackGroup.id;
}
Future<void> _loadUserGroups() async {
// Perbaiki: hanya return jika sedang loading dan data sudah ada
if (_isLoadingGroups && _userGroups.isNotEmpty) {
print('[DEBUG] Preventing multiple loads - load already in progress');
return;
}
print('[DEBUG] Starting load process - setting loading state');
setState(() => _isLoadingGroups = true);
// Store the current group ID before refresh to maintain selection
final previouslySelectedGroupId = _selectedGroupId;
print('[DEBUG] Saving current selected group: $previouslySelectedGroupId');
try {
// Gunakan fungsi yang sudah dibuat di Supabase
final supabase = Supabase.instance.client;
// Verify user authentication dengan timeout
User? currentUser;
try {
currentUser = supabase.auth.currentUser;
if (currentUser == null) {
print('[ERROR] User not authenticated');
throw Exception('User not authenticated');
}
} catch (e) {
print('[ERROR] Error getting current user: $e');
throw Exception('Error getting current user');
}
print('[DEBUG] Using SQL functions from Supabase');
List<Group> allGroups = [];
Set<String> userGroupIds = {};
Group? defaultGroup;
// Get all groups with member count dengan timeout
try {
print('[DEBUG] Calling get_all_groups_with_member_count() function');
final result = await supabase
.rpc('get_all_groups_with_member_count')
.timeout(
Duration(seconds: 2),
onTimeout: () {
print('[WARNING] get_all_groups_with_member_count timed out');
return null;
},
);
if (result != null) {
for (final item in result) {
try {
final group = Group.fromMap(item);
allGroups.add(group);
// Mencatat grup default
if (group.isDefault) {
defaultGroup = group;
print('[DEBUG] Found default group: ${group.name}');
}
} catch (e) {
print('[ERROR] Failed to parse group: $e');
}
}
}
} catch (e) {
print('[ERROR] Failed to load groups with count: $e');
}
// Get user memberships dengan timeout
try {
print('[DEBUG] Getting user memberships with get_user_groups_simple()');
final userGroupsResult = await supabase
.rpc('get_user_groups_simple')
.timeout(
Duration(seconds: 2),
onTimeout: () {
print('[WARNING] get_user_groups_simple timed out');
return null;
},
);
if (userGroupsResult != null) {
for (final item in userGroupsResult) {
userGroupIds.add(item['id'] as String);
}
print('[DEBUG] User is member of ${userGroupIds.length} groups');
}
} catch (e) {
print('[ERROR] Failed to load user memberships: $e');
}
// Fallback - always ensure at least one group
if (allGroups.isEmpty) {
print('[DEBUG] No groups loaded, creating fallback group');
final fallbackGroup = Group(
id: '00000000-0000-0000-0000-000000000001', // UUID valid
name: 'Grup Tani Umum',
description: 'Grup diskusi umum untuk petani',
createdBy: 'system',
isDefault: true,
isPublic: true,
);
allGroups = [fallbackGroup];
defaultGroup = fallbackGroup;
}
// Update UI state
if (mounted) {
setState(() {
_userGroups = allGroups;
_userGroupIds = userGroupIds;
_isLoadingGroups = false;
// Check if the previously selected group still exists in the updated groups
bool previousGroupStillExists = false;
if (previouslySelectedGroupId != null) {
previousGroupStillExists = _userGroups.any(
(group) => group.id == previouslySelectedGroupId,
);
}
// Maintain the previously selected group if it still exists
if (previousGroupStillExists) {
_selectedGroupId = previouslySelectedGroupId;
print(
'[DEBUG] Maintained previously selected group: $_selectedGroupId',
);
} else {
// Otherwise select a default group
if (defaultGroup != null) {
_selectedGroupId = defaultGroup.id;
print(
'[DEBUG] Default group auto-selected: ${defaultGroup.name}',
);
} else if (allGroups.isNotEmpty) {
// Fallback: pilih grup yang user ikuti atau grup pertama
final memberGroup = allGroups.firstWhere(
(g) => userGroupIds.contains(g.id),
orElse: () => allGroups.first,
);
_selectedGroupId = memberGroup.id;
print('[DEBUG] Fallback group selected: ${memberGroup.name}');
}
}
});
print('[DEBUG] State updated with ${allGroups.length} groups');
// Immediately force message load if a group is selected
if (_selectedGroupId != null && _groupChatKey.currentState != null) {
_triggerImmediateMessageLoad();
}
}
} catch (e) {
print('[ERROR] Critical error in load process: $e');
// Ensure we complete loading with at least one fallback group
if (mounted) {
setState(() {
_isLoadingGroups = false;
if (_userGroups.isEmpty) {
_createFallbackGroup();
}
// Try to restore the previously selected group if possible
if (previouslySelectedGroupId != null &&
_userGroups.any(
(group) => group.id == previouslySelectedGroupId,
)) {
_selectedGroupId = previouslySelectedGroupId;
print(
'[DEBUG] Restored previously selected group after error: $_selectedGroupId',
);
}
});
}
}
}
void _handleGroupSelected(Group group) {
setState(() {
_selectedGroupId = group.id;
_showGroupSelector = false;
});
}
void _toggleGroupSelector() {
setState(() {
_showGroupSelector = !_showGroupSelector;
});
}
@override
void dispose() {
// Unsubscribe channel realtime
_groupRealtimeChannel?.unsubscribe();
_tabController.removeListener(_handleTabChange);
_tabController.dispose();
// Clean up any active subscriptions
if (_selectedGroupId != null && _groupChatKey.currentState != null) {
_groupChatKey.currentState!.disposeResources();
}
super.dispose();
}
void _handleTabChange() {
if (_tabController.indexIsChanging) {
// You can add analytics or other logic here when tabs change
setState(() {
_currentIndex = _tabController.index;
// Hide group selector when switching tabs
_showGroupSelector = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
toolbarHeight: 56,
elevation: 0,
title: Text(_getTabTitle(_currentIndex)),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(36),
child: TabBar(
controller: _tabController,
indicatorColor: Colors.white,
indicatorWeight: 3,
labelColor: Colors.white,
unselectedLabelColor: Colors.white70,
labelStyle: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
labelPadding: const EdgeInsets.symmetric(vertical: 8),
tabs: const [
Tab(
height: 28,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.chat_bubble, size: 18),
SizedBox(width: 4),
Text('Diskusi'),
],
),
),
Tab(
height: 28,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.article, size: 18),
SizedBox(width: 4),
Text('Berita'),
],
),
),
Tab(
height: 28,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.eco, size: 18),
SizedBox(width: 4),
Text('Panduan'),
],
),
),
],
),
),
actions:
_currentIndex == 0
? [
IconButton(
icon: const Icon(Icons.refresh),
tooltip: 'Refresh',
onPressed: _refreshGroupsPreservingSelection,
),
]
: null,
),
body: Column(
children: [
// Main content
Expanded(
child: TabBarView(
physics: const NeverScrollableScrollPhysics(), // Mencegah swipe
controller: _tabController,
children: [
// Chat tab - Tampilkan daftar grup terlebih dahulu
_buildGroupsListTab(),
// News tab
const SimpleNewsTab(),
// Panduan Budidaya tab
const FarmingGuideTab(),
],
),
),
],
),
);
}
// Widget untuk menampilkan daftar grup
Widget _buildGroupsListTab() {
if (_isLoadingGroups) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text(
'Memuat grup...',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
],
),
);
}
if (_userGroups.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.groups, size: 80, color: Colors.grey.shade300),
const SizedBox(height: 16),
Text(
'Tidak ada grup tersedia',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey.shade700,
),
),
const SizedBox(height: 8),
Text(
'Periksa koneksi internet atau coba refresh',
style: TextStyle(color: Colors.grey.shade600),
),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: _refreshGroupsPreservingSelection,
icon: const Icon(Icons.refresh),
label: const Text('Refresh'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
],
),
);
}
return Column(
children: [
// Pilihan grup
if (_selectedGroupId != null && _userGroups.isNotEmpty)
_buildGroupSelector(),
// Daftar grup atau chat grup
Expanded(
child:
_selectedGroupId == null
? _buildGroupsList()
: GroupChatScreen(
key: _groupChatKey,
screenKey: _groupChatKey,
isInTabView: true,
groupId: _selectedGroupId!,
),
),
],
);
}
Widget _buildGroupsList() {
return RefreshIndicator(
onRefresh: _refreshGroupsPreservingSelection,
child: ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: _userGroups.length,
itemBuilder: (context, index) {
final group = _userGroups[index];
final isUserMember = _userGroupIds.contains(group.id);
// Gunakan GroupCard yang baru dibuat
return GroupCard(
group: group,
isUserMember: isUserMember,
onTap: () => _selectGroup(group.id),
onJoinLeave:
() =>
isUserMember ? _leaveGroup(group.id) : _joinGroup(group.id),
);
},
),
);
}
// Navigasi ke halaman chat grup
void _navigateToGroupChat(Group group) {
if (_selectedGroupId != null) {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => GroupChatScreen(
isInTabView: false,
groupId: _selectedGroupId!,
),
),
);
}
}
// Selector untuk grup yang dipilih
Widget _buildGroupSelector() {
if (_selectedGroupId == null) return const SizedBox.shrink();
// Cari grup yang dipilih
final selectedGroup = _userGroups.firstWhere(
(g) => g.id == _selectedGroupId,
orElse: () => _userGroups.first,
);
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
InkWell(
onTap: () => setState(() => _selectedGroupId = null),
borderRadius: BorderRadius.circular(20),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.arrow_back, color: AppColors.primary),
),
),
const SizedBox(width: 12),
CircleAvatar(
backgroundColor: AppColors.primary.withOpacity(0.2),
radius: 20,
child: Text(
selectedGroup.name.isNotEmpty
? selectedGroup.name[0].toUpperCase()
: 'G',
style: const TextStyle(
color: AppColors.primary,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
selectedGroup.name,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
Text(
'${selectedGroup.memberCount} anggota',
style: TextStyle(fontSize: 12, color: Colors.grey.shade600),
),
],
),
),
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _refreshGroupsPreservingSelection,
color: AppColors.primary,
),
],
),
);
}
// Pilih grup untuk chat
void _selectGroup(String groupId) {
setState(() {
_selectedGroupId = groupId;
_showGroupSelector = true;
});
// Immediate refresh without delay
_triggerImmediateMessageLoad();
// Also set multiple delayed refreshes for redundancy
for (var delay in [100, 500, 1000, 2000]) {
Future.delayed(Duration(milliseconds: delay), () {
if (mounted) {
_triggerImmediateMessageLoad();
}
});
}
}
// Trigger immediate message loading in the chat screen
void _triggerImmediateMessageLoad() {
try {
if (_groupChatKey.currentState != null) {
print('[DEBUG] Triggering immediate message load');
_groupChatKey.currentState!.forceRefresh();
}
} catch (e) {
print('[ERROR] Error triggering message load: $e');
}
}
// Gabung ke grup
Future<void> _joinGroup(String groupId) async {
if (_isJoiningGroup) return;
setState(() {
_isJoiningGroup = true;
_loadingGroupId = groupId;
});
try {
final supabase = Supabase.instance.client;
final userId = supabase.auth.currentUser?.id;
if (userId == null) {
throw Exception('User not authenticated');
}
// Coba bergabung dengan grup
await supabase.from('group_members').insert({
'group_id': groupId,
'user_id': userId,
'role': 'member',
'is_active': true,
});
// Refresh data
await _loadUserGroups();
// Set grup yang baru diikuti sebagai grup yang dipilih
setState(() {
_selectedGroupId = groupId;
_userGroupIds.add(groupId);
});
_showSuccessSnackBar('Berhasil bergabung dengan grup');
} catch (e) {
print('[ERROR] Failed to join group: $e');
_showErrorSnackBar('Gagal bergabung dengan grup: ${e.toString()}');
} finally {
if (mounted) {
setState(() {
_isJoiningGroup = false;
_loadingGroupId = null;
});
}
}
}
// Keluar dari grup
Future<void> _leaveGroup(String groupId) async {
// Tampilkan konfirmasi
final shouldLeave =
await showDialog<bool>(
context: context,
builder:
(context) => AlertDialog(
title: const Text('Keluar dari Grup'),
content: const Text(
'Apakah Anda yakin ingin keluar dari grup ini?',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('Batal'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('Keluar'),
),
],
),
) ??
false;
if (!shouldLeave) return;
setState(() {
_isJoiningGroup = true;
_loadingGroupId = groupId;
});
try {
final supabase = Supabase.instance.client;
final userId = supabase.auth.currentUser?.id;
if (userId == null) {
throw Exception('User not authenticated');
}
// Nonaktifkan keanggotaan
await supabase
.from('group_members')
.update({'is_active': false})
.eq('group_id', groupId)
.eq('user_id', userId);
// Refresh data
await _loadUserGroups();
// Jika grup saat ini yang dikeluar, kembalikan ke daftar grup
if (_selectedGroupId == groupId) {
setState(() {
_selectedGroupId = null;
});
}
_showSuccessSnackBar('Berhasil keluar dari grup');
} catch (e) {
print('[ERROR] Failed to leave group: $e');
_showErrorSnackBar('Gagal keluar dari grup: ${e.toString()}');
} finally {
if (mounted) {
setState(() {
_isJoiningGroup = false;
_loadingGroupId = null;
});
}
}
}
String _getTabTitle(int index) {
switch (index) {
case 0:
return 'Diskusi';
case 1:
return 'Berita';
case 2:
return 'Panduan Pertanian';
default:
return 'Komunitas';
}
}
void _showLoadingDialog(String message) {
showDialog(
context: context,
barrierDismissible: false,
builder:
(context) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 16),
Text(message),
],
),
),
);
}
void _showErrorSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message), backgroundColor: Colors.red),
);
}
void _showSuccessSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message), backgroundColor: Colors.green),
);
}
Future<void> _emergencyDisableRLS() async {
try {
final supabase = Supabase.instance.client;
_showLoadingDialog('Mencoba perbaiki database...');
// Try to disable RLS on problematic tables
final sql = '''
ALTER TABLE group_members DISABLE ROW LEVEL SECURITY;
ALTER TABLE groups DISABLE ROW LEVEL SECURITY;
''';
await supabase.rpc('exec_sql', params: {'sql': sql});
print('[DEBUG] Emergency disabled RLS on tables');
Navigator.of(context).pop(); // Close loading dialog
_showSuccessSnackBar('Database diperbaiki, mencoba ulang...');
// Reload groups
_loadUserGroups();
} catch (e) {
Navigator.of(context).pop(); // Close loading dialog
print('[ERROR] Failed to disable RLS: $e');
_showErrorSnackBar('Gagal memperbaiki database: ${e.toString()}');
}
}
// Make a direct database query to preload messages as early as possible
Future<void> _preloadDefaultGroupMessages() async {
try {
print('[PRELOAD] Attempting to preload default group messages');
final supabase = Supabase.instance.client;
// First check if there are any messages in the default group
final response = await supabase
.rpc(
'count_messages_in_group',
params: {'group_id_param': '00000000-0000-0000-0000-000000000001'},
)
.timeout(Duration(seconds: 2));
final count = response as int? ?? 0;
// If no messages, create a welcome message
if (count == 0) {
print('[PRELOAD] No messages found, creating welcome message');
final userId = supabase.auth.currentUser?.id;
if (userId != null) {
// Get username from profiles
String username = 'User';
String email = '';
try {
final profileData = await supabase
.from('profiles')
.select('username, email')
.eq('user_id', userId)
.single()
.timeout(Duration(seconds: 2));
username = profileData['username'] ?? 'User';
email = profileData['email'] ?? '';
} catch (e) {
print('[ERROR] Failed to get profile: $e');
}
// Create welcome message using an RPC function that bypasses RLS
try {
await supabase.rpc(
'create_welcome_message',
params: {
'message_content':
'Selamat datang di Grup TaniSM4RT! Ini adalah pesan otomatis untuk memulai percakapan.',
'user_id_param': userId,
'username_param': username,
'email_param': email,
'group_id_param': '00000000-0000-0000-0000-000000000001',
},
);
print('[PRELOAD] Created welcome message successfully via RPC');
} catch (e) {
print('[ERROR] Failed to create welcome message via RPC: $e');
// Fallback to direct query with service role if available
try {
final adminClient = Supabase.instance.client;
await adminClient.from('messages').insert({
'id': Uuid().v4(),
'content':
'Selamat datang di Grup TaniSM4RT! Ini adalah pesan otomatis untuk memulai percakapan.',
'sender_user_id': userId,
'sender_username': username,
'sender_email': email,
'group_id': '00000000-0000-0000-0000-000000000001',
'created_at': DateTime.now().toIso8601String(),
});
print('[PRELOAD] Created welcome message via admin client');
} catch (e) {
print(
'[ERROR] Failed to create welcome message via admin client: $e',
);
}
}
}
} else {
print(
'[PRELOAD] Found $count existing messages, no need to create welcome message',
);
}
} catch (e) {
print('[ERROR] Error in preloading messages: $e');
}
}
// Force refresh to fix issues with message loading
Future<void> _forceRefreshAllMessages() async {
try {
if (!mounted) return;
print('[FORCE] Attempting force refresh on GroupChatScreen');
// Wait a second to make sure GroupChatScreen is mounted
Future.delayed(Duration(seconds: 2), () {
if (_groupChatKey.currentState != null) {
print('[FORCE] Executing force refresh on GroupChatScreen');
_groupChatKey.currentState?.setupRealTimeSubscription();
// This is a public method that exists in GroupChatScreenState
_groupChatKey.currentState?.forceRefresh();
} else {
print('[FORCE] GroupChatScreen not yet mounted, delaying refresh');
// Try once more after a delay
Future.delayed(Duration(seconds: 1), () {
if (_groupChatKey.currentState != null) {
_groupChatKey.currentState?.setupRealTimeSubscription();
_groupChatKey.currentState?.forceRefresh();
}
});
}
});
} catch (e) {
print('[ERROR] Error in force refresh: $e');
}
}
// This method refreshes groups but preserves the current selection
Future<void> _refreshGroupsPreservingSelection() async {
// Store the current group ID before refresh to maintain selection
final previouslySelectedGroupId = _selectedGroupId;
print(
'[DEBUG] Refreshing while preserving selection: $previouslySelectedGroupId',
);
// Call the regular load method
await _loadUserGroups();
// If the previously selected group exists but wasn't selected, select it now
if (previouslySelectedGroupId != null && mounted) {
final stillExists = _userGroups.any(
(g) => g.id == previouslySelectedGroupId,
);
if (stillExists && _selectedGroupId != previouslySelectedGroupId) {
setState(() {
_selectedGroupId = previouslySelectedGroupId;
print('[DEBUG] Restored selection to: $_selectedGroupId');
});
// Force refresh the messages in the restored group
if (_groupChatKey.currentState != null) {
_triggerImmediateMessageLoad();
}
}
}
}
// Tambahkan fungsi setup subscription realtime
void _setupGroupRealtimeSubscription() {
final supabase = Supabase.instance.client;
_groupRealtimeChannel = supabase.channel('public:groups_and_members');
_groupRealtimeChannel!
.onPostgresChanges(
event: PostgresChangeEvent.all,
schema: 'public',
table: 'groups',
callback: (payload) {
print('[REALTIME] Groups table changed: $payload');
_loadUserGroups();
},
)
.onPostgresChanges(
event: PostgresChangeEvent.all,
schema: 'public',
table: 'group_members',
callback: (payload) {
print('[REALTIME] Group members table changed: $payload');
_loadUserGroups();
},
);
_groupRealtimeChannel!.subscribe();
}
@override
bool get wantKeepAlive => true;
}