Skip to content

Commit

Permalink
Merge pull request #4 from ArslanYousaf12/admin-role
Browse files Browse the repository at this point in the history
group chat module ADDED
  • Loading branch information
ArslanYousaf12 authored Sep 2, 2024
2 parents e43e3bd + 606f53d commit 7f799a5
Show file tree
Hide file tree
Showing 16 changed files with 367 additions and 14 deletions.
Binary file not shown.
2 changes: 1 addition & 1 deletion lib/src/common_widgets/home_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class HomeAppBar extends ConsumerWidget {
GestureDetector(
onTap: notificationCallBack,
child: const Icon(
Icons.notifications_none_outlined,
Icons.message,
size: 28,
),
),
Expand Down
40 changes: 40 additions & 0 deletions lib/src/features/chat_section/data/chat_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:home_front_pk/src/features/chat_section/domain/chat_model.dart';

class ChatRepository {
final FirebaseFirestore _firestore;

ChatRepository({required FirebaseFirestore firestore})
: _firestore = firestore;

Future<void> sendMessage(ChatMessage message) async {
await _firestore.collection('chats').add(message.toMap());
}

Stream<List<ChatMessage>> getChatMessages() {
return _firestore
.collection('chats')
.orderBy('timestamp', descending: true)
.snapshots()
.map((snapshot) =>
snapshot.docs.map((doc) => ChatMessage.fromDoc(doc)).toList());
}
}

// Provider for ChatRepository
final chatRepositoryProvider = Provider<ChatRepository>((ref) {
return ChatRepository(firestore: FirebaseFirestore.instance);
});
// Provider for sending a chat message
final sendMessageProvider =
FutureProvider.family<void, ChatMessage>((ref, message) async {
final repo = ref.watch(chatRepositoryProvider);
await repo.sendMessage(message);
});

// Provider for streaming chat messages
final getMessagesProvider = StreamProvider<List<ChatMessage>>((ref) {
final repo = ref.watch(chatRepositoryProvider);
return repo.getChatMessages();
});
33 changes: 33 additions & 0 deletions lib/src/features/chat_section/domain/chat_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:cloud_firestore/cloud_firestore.dart';

class ChatMessage {
final String id;
final String senderId; // The ID of the user sending the message
final String content; // The message content
final DateTime timestamp; // The timestamp of the message

ChatMessage({
required this.id,
required this.senderId,
required this.content,
required this.timestamp,
});

Map<String, dynamic> toMap() {
return {
'id': id,
'senderId': senderId,
'content': content,
'timestamp': timestamp,
};
}

factory ChatMessage.fromDoc(DocumentSnapshot doc) {
return ChatMessage(
id: doc.id,
senderId: doc['senderId'] as String,
content: doc['content'] as String,
timestamp: (doc['timestamp'] as Timestamp).toDate(),
);
}
}
37 changes: 37 additions & 0 deletions lib/src/features/chat_section/presentation/chat_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:home_front_pk/src/features/chat_section/data/chat_repository.dart';
import 'package:home_front_pk/src/features/chat_section/domain/chat_model.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import 'package:home_front_pk/src/common_widgets/notifier_mounted.dart';

part 'chat_controller.g.dart';

@riverpod
class ChatController extends _$ChatController with NotifierMounted {
@override
FutureOr<List<ChatMessage>?> build() {
ref.onDispose(setUnmounted);
return null;
}

Future<void> sendMessage(ChatMessage message) async {
state = const AsyncLoading();
final result = await AsyncValue.guard(
() => ref.read(chatRepositoryProvider).sendMessage(message),
);

if (result.hasError) {
state = AsyncError(result.error!, result.stackTrace!);
return;
}

if (mounted) {
// Add the new message to the current state
state = AsyncData([...state.value ?? [], message]);
}
}

Stream<List<ChatMessage>> getChatMessages() {
return ref.read(chatRepositoryProvider).getChatMessages();
}
}
26 changes: 26 additions & 0 deletions lib/src/features/chat_section/presentation/chat_controller.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

182 changes: 182 additions & 0 deletions lib/src/features/chat_section/presentation/chat_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:firebase_auth/firebase_auth.dart'; // Add this import to handle Firebase Authentication
import 'package:home_front_pk/src/features/chat_section/data/chat_repository.dart';
import 'package:home_front_pk/src/features/chat_section/domain/chat_model.dart';

class ChatScreen extends ConsumerStatefulWidget {
@override
_ChatScreenState createState() => _ChatScreenState();
}

class _ChatScreenState extends ConsumerState<ChatScreen> {
final TextEditingController _messageController = TextEditingController();
late final String currentUserId; // Correctly handle currentUserId
late final String currentUserName;

@override
void initState() {
super.initState();
final user = FirebaseAuth
.instance.currentUser; // Get the current user from FirebaseAuth
if (user != null) {
currentUserId = user.uid; // Set the currentUserId from Firebase Auth
currentUserName = user.email ??
'Unknown'; // You can use email as the name or fetch a displayName
} else {
// Handle the case where the user is not logged in
currentUserId = 'unknown';
currentUserName = 'unknown';
}
}

@override
Widget build(BuildContext context) {
final chatMessagesAsync = ref.watch(getMessagesProvider);

return Scaffold(
appBar: AppBar(
title: Text('Group Chat'),
),
body: Column(
children: [
Expanded(
child: chatMessagesAsync.when(
data: (messages) {
return ListView.builder(
reverse: true,
itemCount: messages.length,
itemBuilder: (context, index) {
final message = messages[index];
final isCurrentUser = message.senderId == currentUserId;

return Align(
alignment: isCurrentUser
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
margin:
EdgeInsets.symmetric(vertical: 8, horizontal: 16),
padding:
EdgeInsets.symmetric(vertical: 12, horizontal: 16),
decoration: BoxDecoration(
color: isCurrentUser
? Color.fromARGB(255, 96, 191,
143) // Use blue for the current user
: Colors
.grey.shade300, // Use grey for other users
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
bottomLeft: isCurrentUser
? Radius.circular(16)
: Radius.zero,
bottomRight: isCurrentUser
? Radius.zero
: Radius.circular(16),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isCurrentUser
? 'You'
: 'Anonymous', // Use 'You' or the sender ID/name
style: TextStyle(
fontWeight: FontWeight.bold,
color:
isCurrentUser ? Colors.white : Colors.black,
),
),
SizedBox(height: 5),
Text(
message.content,
style: TextStyle(
color:
isCurrentUser ? Colors.white : Colors.black,
),
),
SizedBox(height: 5),
Align(
alignment: Alignment.bottomRight,
child: Text(
message.timestamp
.toLocal()
.toString()
.split(' ')[1],
style: TextStyle(
color: isCurrentUser
? Colors.white70
: Colors.black54,
fontSize: 12,
),
),
),
],
),
),
);
},
);
},
loading: () => Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _messageController,
decoration: InputDecoration(
hintText: 'Enter message',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(24),
),
filled: true,
fillColor: Colors.white,
contentPadding:
EdgeInsets.symmetric(vertical: 12, horizontal: 16),
),
),
),
SizedBox(width: 16),
ElevatedButton.icon(
icon: Icon(Icons.send),
label: Text('Send'),
onPressed: () {
if (_messageController.text.trim().isNotEmpty) {
final message = ChatMessage(
id: '',
senderId: currentUserId, // Send correct current user ID
content: _messageController.text.trim(),
timestamp: DateTime.now(),
);
ref.read(sendMessageProvider(message).future);
_messageController.clear();
}
},
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16),
),
),
],
),
),
],
),
);
}

@override
void dispose() {
_messageController.dispose();
super.dispose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:home_front_pk/src/common_widgets/persons_card.dart';
import 'package:home_front_pk/src/common_widgets/responsive_center.dart';
import 'package:home_front_pk/src/constants/app_sizes.dart';
import 'package:home_front_pk/src/features/authentication/presentation/account/account_screen_controller.dart';
import 'package:home_front_pk/src/features/chat_section/presentation/chat_screen.dart';
import 'package:home_front_pk/src/features/dashboard/data/constructor_repo/constructor_repository.dart';

import 'package:home_front_pk/src/features/user-management/presentation/widgets/sliver_products_grid.dart';
Expand Down Expand Up @@ -68,7 +69,7 @@ class _ClientDashboardState extends ConsumerState<ClientDashboard> {
titles: 'Dashboard ',
userRole: 'client',
notificationCallBack: () =>
showNotImplementedAlertDialog(context: context),
context.goNamed(AppRoute.chatScreen.name),
logOut: () async {
final goRouter = GoRouter.of(context);
final logout = await showAlertDialog(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:home_front_pk/src/common_widgets/grid_card.dart';
import 'package:home_front_pk/src/common_widgets/home_app_bar.dart';
import 'package:home_front_pk/src/constants/app_sizes.dart';
import 'package:home_front_pk/src/features/authentication/presentation/account/account_screen_controller.dart';
import 'package:home_front_pk/src/features/chat_section/presentation/chat_screen.dart';
import 'package:home_front_pk/src/localization/string_hardcoded.dart';
import 'package:home_front_pk/src/routing/app_router.dart';

Expand Down Expand Up @@ -124,13 +125,16 @@ class _ConstructorDashboardState extends ConsumerState<ConstructorDashboard> {
showNotImplementedAlertDialog(context: context),
),
GridCard(
iconName: FontAwesomeIcons.comments,
title: 'Messages',
gradients: [const Color(0xFFF6F7C4), Colors.amber.shade100],
color: Colors.black54,
onPressed: () =>
showNotImplementedAlertDialog(context: context),
),
iconName: FontAwesomeIcons.comments,
title: 'Messages',
gradients: [
const Color(0xFFF6F7C4),
Colors.amber.shade100
],
color: Colors.black54,
onPressed: () => context.goNamed(
AppRoute.chatScreen.name,
)),
GridCard(
iconName: FontAwesomeIcons.paperPlane,
title: 'Offers Sent',
Expand Down
Loading

0 comments on commit 7f799a5

Please sign in to comment.