diff --git a/lib/api/firebase_message.dart b/lib/api/firebase_message.dart index 2cc3eee..2f61bfc 100644 --- a/lib/api/firebase_message.dart +++ b/lib/api/firebase_message.dart @@ -42,21 +42,34 @@ class FirebaseMessage extends FirebaseAPI implements IMessage { @override Future?> readConversation( - String firstId, String secondId) async { - List userIds = [firstId, secondId]; - final querySnapshot = await collectionReference - .where('fromId', whereIn: userIds) - .where('toId', whereIn: userIds) + String userId1, String userId2) async { + final query = collectionReference + .where('fromId', isEqualTo: userId1) + .where('toId', isEqualTo: userId2) .withConverter( fromFirestore: Message.fromFirestore, toFirestore: (Message msg, _) => msg.toFirestore()) .get(); - final messages = querySnapshot.docs.map((doc) => doc.data()).toList(); + + final query2 = collectionReference + .where('fromId', isEqualTo: userId2) + .where('toId', isEqualTo: userId1) + .withConverter( + fromFirestore: Message.fromFirestore, + toFirestore: (Message msg, _) => msg.toFirestore()) + .get(); + + final querySnapshots = await Future.wait([query, query2]); + + final messages = [ + ...querySnapshots[0].docs.map((doc) => doc.data()).toList(), + ...querySnapshots[1].docs.map((doc) => doc.data()).toList(), + ]; + return messages; } - Stream followConversation( - String firstId, String secondId) { + Stream followConversation(String firstId, String secondId) { List userIds = [firstId, secondId]; final Stream msgStream = collectionReference .where('fromId', whereIn: userIds) @@ -67,8 +80,7 @@ class FirebaseMessage extends FirebaseAPI implements IMessage { .snapshots(); return msgStream - .map((querySnapshot) => - querySnapshot.docs.map((doc) => doc.data())) + .map((querySnapshot) => querySnapshot.docs.map((doc) => doc.data())) .cast(); } diff --git a/lib/api/imessage.dart b/lib/api/imessage.dart index ae071b6..1ce7c0e 100644 --- a/lib/api/imessage.dart +++ b/lib/api/imessage.dart @@ -2,8 +2,8 @@ import 'package:pdg_app/model/message.dart'; abstract class IMessage { void createMessage(Message message); - Future readMessage(String messageId); - Future?> readConversation(String firstId, String secondId); + Future readMessage(String messageId); + Future?> readConversation(String firstId, String secondId); void updateMessage(Message message); void deleteMessage(String messageId); -} \ No newline at end of file +} diff --git a/lib/model/custom_list_tile_data.dart b/lib/model/custom_list_tile_data.dart index 3cc84b6..84b8621 100644 --- a/lib/model/custom_list_tile_data.dart +++ b/lib/model/custom_list_tile_data.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; class CustomListTileData { final Widget? avatar; final String? title; + final String? subtitle; final DateTime? date; final int? badgeCount; final void Function()? onTap; @@ -10,6 +11,7 @@ class CustomListTileData { CustomListTileData({ this.avatar, this.title, + this.subtitle, this.date, this.badgeCount, this.onTap, diff --git a/lib/model/user.dart b/lib/model/user.dart index b062ebd..bd9ccdc 100644 --- a/lib/model/user.dart +++ b/lib/model/user.dart @@ -67,6 +67,15 @@ class User implements IModel { firstName = name; } + @override + bool operator ==(Object other) { + if (other is User && other.uid == uid) return true; + return false; + } + + @override + int get hashCode => Object.hash(uid, null); + void addUser(String userId) { if (clientList == null) { throw Exception("Client list is null"); diff --git a/lib/provider/chat_provider.dart b/lib/provider/chat_provider.dart new file mode 100644 index 0000000..ebbe4de --- /dev/null +++ b/lib/provider/chat_provider.dart @@ -0,0 +1,81 @@ +import 'dart:collection'; +import 'dart:developer'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/material.dart'; +import 'package:pdg_app/api/firebase_message.dart'; +import 'package:sorted_list/sorted_list.dart'; + +import '../api/firebase_user.dart'; +import '../api/imessage.dart'; +import '../api/iuser.dart'; +import '../model/message.dart'; +import '../model/user.dart'; +import 'auth_provider.dart'; + +typedef Uid = String; + +class ChatProvider extends ChangeNotifier { + /// Getting the instance of the AuthProvider class. + final AuthProvider _auth; + final IMessage _messageApi = FirebaseMessage(FirebaseFirestore.instance); + final IUser _userApi = FirebaseUser(FirebaseFirestore.instance); + + /// The list of messages using a [LinkedHashMap] to keep the order of the messages. + final Map> _messages = {}; + + ChatProvider(AuthProvider authProvider) : _auth = authProvider; + + Map> get messages => UnmodifiableMapView(_messages); + + /// Returning an unmodifiable list view of the messages. + UnmodifiableListView getMessagesWithUid(String uid) { + final messages = _messages[uid]; + if (messages != null) { + return UnmodifiableListView(messages); + } + return UnmodifiableListView([]); + } + + UnmodifiableListView> getLastMessageOfEachUser() { + final result = + _messages.entries.where((element) => element.value.isNotEmpty).map((e) { + MapEntry entry = MapEntry(e.key, e.value.last); + return entry; + }).toList(); + return UnmodifiableListView(result); + } + + /// Fetching all the messages from the database. + Future fetchAllMessages() async { + List users = []; + + if (_auth.isAdmin) { + final clients = await _userApi.getDietitianClient(_auth.userUid); + users.addAll(clients); + } else { + final diet = await _userApi.readDietitianOfClient(_auth.userUid); + if (diet != null) { + users.add(diet); + } + } + final List?>> functions = []; + for (final user in users) { + functions.add(_messageApi.readConversation(_auth.userUid, user.uid)); + } + + final futureResult = await Future.wait(functions); + + int index = 0; + for (final conv in futureResult) { + final sortedList = + SortedList((a, b) => a.time.compareTo(b.time)); + + sortedList.addAll(conv!); + + _messages[users[index]] = sortedList; + index++; + } + log(_messages.toString()); + } +} diff --git a/lib/router/chat_guard.dart b/lib/router/chat_guard.dart index d7a32af..b446da9 100644 --- a/lib/router/chat_guard.dart +++ b/lib/router/chat_guard.dart @@ -13,7 +13,7 @@ class ChatGuard extends AutoRouteGuard { if (isAdmin) { resolver.next(true); } else { - router.push(const ChatScreenRoute()); + router.push(ChatScreenRoute()); } } } diff --git a/lib/router/chat_router_page.dart b/lib/router/chat_router_page.dart new file mode 100644 index 0000000..7b35743 --- /dev/null +++ b/lib/router/chat_router_page.dart @@ -0,0 +1,31 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; +import 'package:pdg_app/provider/auth_provider.dart'; +import 'package:pdg_app/provider/chat_provider.dart'; +import 'package:provider/provider.dart'; + +class ChatRouterPage extends StatelessWidget { + const ChatRouterPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (context) => ChatProvider(GetIt.I.get()), + builder: (context, child) { + return FutureBuilder( + future: context.read().fetchAllMessages(), + builder: (context, snapshot) => + snapshot.connectionState == ConnectionState.done + ? const AutoRouter() + : Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: const [CircularProgressIndicator()]), + ), + ); + }, + ); + } +} diff --git a/lib/router/router.dart b/lib/router/router.dart index fad5f73..2d48003 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -20,6 +20,7 @@ import '../screens/home.dart'; import '../widgets/register/register_third_page.dart'; import '../screens/update_client_record.dart'; import './auth_gard.dart'; +import 'chat_router_page.dart'; import 'home_guard.dart'; @MaterialAutoRouter( @@ -32,8 +33,7 @@ import 'home_guard.dart'; initial: true, children: [ AutoRoute( - page: EmptyRouterPage, - name: 'ChatRouterPage', + page: ChatRouterPage, path: 'chat', children: [ AutoRoute( diff --git a/lib/router/router.gr.dart b/lib/router/router.gr.dart index 55f8821..295d64f 100644 --- a/lib/router/router.gr.dart +++ b/lib/router/router.gr.dart @@ -11,222 +11,226 @@ // ignore_for_file: type=lint // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i17; -import 'package:auto_route/empty_router_widgets.dart' as _i4; -import 'package:flutter/material.dart' as _i18; - -import '../model/aftercare.dart' as _i22; -import '../model/meal.dart' as _i23; -import '../model/user.dart' as _i24; -import '../screens/add_meal.dart' as _i13; -import '../screens/chat.dart' as _i6; -import '../screens/client_list.dart' as _i9; -import '../screens/client_record.dart' as _i10; -import '../screens/diary.dart' as _i12; -import '../screens/discussion_list.dart' as _i7; -import '../screens/document_list.dart' as _i8; +import 'package:auto_route/auto_route.dart' as _i18; +import 'package:auto_route/empty_router_widgets.dart' as _i5; +import 'package:flutter/material.dart' as _i19; + +import '../model/aftercare.dart' as _i23; +import '../model/meal.dart' as _i24; +import '../model/user.dart' as _i25; +import '../screens/add_meal.dart' as _i14; +import '../screens/chat.dart' as _i7; +import '../screens/client_list.dart' as _i10; +import '../screens/client_record.dart' as _i11; +import '../screens/diary.dart' as _i13; +import '../screens/discussion_list.dart' as _i8; +import '../screens/document_list.dart' as _i9; import '../screens/home.dart' as _i1; import '../screens/login.dart' as _i2; -import '../screens/profile.dart' as _i5; +import '../screens/profile.dart' as _i6; import '../screens/register.dart' as _i3; -import '../screens/update_client_record.dart' as _i11; -import '../widgets/register/register_first_page.dart' as _i14; -import '../widgets/register/register_second_page.dart' as _i15; -import '../widgets/register/register_third_page.dart' as _i16; -import 'auth_gard.dart' as _i19; -import 'chat_guard.dart' as _i20; -import 'home_guard.dart' as _i21; - -class AppRouter extends _i17.RootStackRouter { +import '../screens/update_client_record.dart' as _i12; +import '../widgets/register/register_first_page.dart' as _i15; +import '../widgets/register/register_second_page.dart' as _i16; +import '../widgets/register/register_third_page.dart' as _i17; +import 'auth_gard.dart' as _i20; +import 'chat_guard.dart' as _i21; +import 'chat_router_page.dart' as _i4; +import 'home_guard.dart' as _i22; + +class AppRouter extends _i18.RootStackRouter { AppRouter( - {_i18.GlobalKey<_i18.NavigatorState>? navigatorKey, + {_i19.GlobalKey<_i19.NavigatorState>? navigatorKey, required this.authGuard, required this.chatGuard, required this.homeGuard}) : super(navigatorKey); - final _i19.AuthGuard authGuard; + final _i20.AuthGuard authGuard; - final _i20.ChatGuard chatGuard; + final _i21.ChatGuard chatGuard; - final _i21.HomeGuard homeGuard; + final _i22.HomeGuard homeGuard; @override - final Map pagesMap = { + final Map pagesMap = { HomeScreenRoute.name: (routeData) { - return _i17.MaterialPageX( + return _i18.MaterialPageX( routeData: routeData, child: const _i1.HomeScreen()); }, LoginScreenRoute.name: (routeData) { - return _i17.MaterialPageX( + return _i18.MaterialPageX( routeData: routeData, child: const _i2.LoginScreen()); }, RegisterScreenRoute.name: (routeData) { - return _i17.MaterialPageX( + return _i18.MaterialPageX( routeData: routeData, child: const _i3.RegisterScreen()); }, - ChatRouterPage.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i4.EmptyRouterPage()); + ChatRouterPageRoute.name: (routeData) { + return _i18.MaterialPageX( + routeData: routeData, child: const _i4.ChatRouterPage()); }, MainRouterPage.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i4.EmptyRouterPage()); + return _i18.MaterialPageX( + routeData: routeData, child: const _i5.EmptyRouterPage()); }, ProfileScreenRoute.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i5.ProfileScreen()); + return _i18.MaterialPageX( + routeData: routeData, child: const _i6.ProfileScreen()); }, ChatScreenRoute.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i6.ChatScreen()); + final args = routeData.argsAs( + orElse: () => const ChatScreenRouteArgs()); + return _i18.MaterialPageX( + routeData: routeData, + child: _i7.ChatScreen(key: args.key, otherUser: args.otherUser)); }, DiscussionListScreenRoute.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i7.DiscussionListScreen()); + return _i18.MaterialPageX( + routeData: routeData, child: const _i8.DiscussionListScreen()); }, DocumentListScreenRoute.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i8.DocumentListScreen()); + return _i18.MaterialPageX( + routeData: routeData, child: const _i9.DocumentListScreen()); }, ClientListRouter.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i4.EmptyRouterPage()); + return _i18.MaterialPageX( + routeData: routeData, child: const _i5.EmptyRouterPage()); }, DiaryRouterPage.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i4.EmptyRouterPage()); + return _i18.MaterialPageX( + routeData: routeData, child: const _i5.EmptyRouterPage()); }, ClientListScreenRoute.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i9.ClientListScreen()); + return _i18.MaterialPageX( + routeData: routeData, child: const _i10.ClientListScreen()); }, ClientRecordScreenRoute.name: (routeData) { final args = routeData.argsAs(); - return _i17.MaterialPageX( + return _i18.MaterialPageX( routeData: routeData, - child: _i10.ClientRecordScreen(user: args.user, key: args.key)); + child: _i11.ClientRecordScreen(user: args.user, key: args.key)); }, UpdateClientRecordScreenRoute.name: (routeData) { final args = routeData.argsAs(); - return _i17.MaterialPageX<_i22.Aftercare?>( + return _i18.MaterialPageX<_i23.Aftercare?>( routeData: routeData, - child: _i11.UpdateClientRecordScreen( + child: _i12.UpdateClientRecordScreen( user: args.user, aftercare: args.aftercare, key: args.key)); }, DiaryScreenRoute.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i12.DiaryScreen()); + return _i18.MaterialPageX( + routeData: routeData, child: const _i13.DiaryScreen()); }, AddMealScreenRoute.name: (routeData) { final args = routeData.argsAs(); - return _i17.MaterialPageX<_i23.Meal?>( + return _i18.MaterialPageX<_i24.Meal?>( routeData: routeData, - child: _i13.AddMealScreen( + child: _i14.AddMealScreen( day: args.day, meal: args.meal, key: args.key)); }, RegisterFirstPageRoute.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i14.RegisterFirstPage()); + return _i18.MaterialPageX( + routeData: routeData, child: const _i15.RegisterFirstPage()); }, RegisterSecondPageRoute.name: (routeData) { final args = routeData.argsAs( orElse: () => const RegisterSecondPageRouteArgs()); - return _i17.MaterialPageX( - routeData: routeData, child: _i15.RegisterSecondPage(key: args.key)); + return _i18.MaterialPageX( + routeData: routeData, child: _i16.RegisterSecondPage(key: args.key)); }, RegisterThirdPageRoute.name: (routeData) { - return _i17.MaterialPageX( - routeData: routeData, child: const _i16.RegisterThirdPage()); + return _i18.MaterialPageX( + routeData: routeData, child: const _i17.RegisterThirdPage()); } }; @override - List<_i17.RouteConfig> get routes => [ - _i17.RouteConfig('/#redirect', + List<_i18.RouteConfig> get routes => [ + _i18.RouteConfig('/#redirect', path: '/', redirectTo: '/home', fullMatch: true), - _i17.RouteConfig(HomeScreenRoute.name, path: '/home', guards: [ + _i18.RouteConfig(HomeScreenRoute.name, path: '/home', guards: [ authGuard ], children: [ - _i17.RouteConfig('#redirect', + _i18.RouteConfig('#redirect', path: '', parent: HomeScreenRoute.name, redirectTo: 'main', fullMatch: true), - _i17.RouteConfig(ChatRouterPage.name, + _i18.RouteConfig(ChatRouterPageRoute.name, path: 'chat', parent: HomeScreenRoute.name, children: [ - _i17.RouteConfig('#redirect', + _i18.RouteConfig('#redirect', path: '', - parent: ChatRouterPage.name, + parent: ChatRouterPageRoute.name, redirectTo: 'chats', fullMatch: true), - _i17.RouteConfig(ChatScreenRoute.name, - path: 'onechat', parent: ChatRouterPage.name), - _i17.RouteConfig(DiscussionListScreenRoute.name, + _i18.RouteConfig(ChatScreenRoute.name, + path: 'onechat', parent: ChatRouterPageRoute.name), + _i18.RouteConfig(DiscussionListScreenRoute.name, path: 'chats', - parent: ChatRouterPage.name, + parent: ChatRouterPageRoute.name, guards: [chatGuard]), - _i17.RouteConfig(DocumentListScreenRoute.name, - path: 'documents', parent: ChatRouterPage.name) + _i18.RouteConfig(DocumentListScreenRoute.name, + path: 'documents', parent: ChatRouterPageRoute.name) ]), - _i17.RouteConfig(MainRouterPage.name, + _i18.RouteConfig(MainRouterPage.name, path: 'main', parent: HomeScreenRoute.name, children: [ - _i17.RouteConfig('#redirect', + _i18.RouteConfig('#redirect', path: '', parent: MainRouterPage.name, redirectTo: 'clients', fullMatch: true), - _i17.RouteConfig(ClientListRouter.name, + _i18.RouteConfig(ClientListRouter.name, path: 'clients', parent: MainRouterPage.name, guards: [ homeGuard ], children: [ - _i17.RouteConfig(ClientListScreenRoute.name, + _i18.RouteConfig(ClientListScreenRoute.name, path: '', parent: ClientListRouter.name), - _i17.RouteConfig(ClientRecordScreenRoute.name, + _i18.RouteConfig(ClientRecordScreenRoute.name, path: 'record', parent: ClientListRouter.name), - _i17.RouteConfig(UpdateClientRecordScreenRoute.name, + _i18.RouteConfig(UpdateClientRecordScreenRoute.name, path: 'update', parent: ClientListRouter.name) ]), - _i17.RouteConfig(DiaryRouterPage.name, + _i18.RouteConfig(DiaryRouterPage.name, path: 'diary', parent: MainRouterPage.name, children: [ - _i17.RouteConfig(DiaryScreenRoute.name, + _i18.RouteConfig(DiaryScreenRoute.name, path: '', parent: DiaryRouterPage.name), - _i17.RouteConfig(AddMealScreenRoute.name, + _i18.RouteConfig(AddMealScreenRoute.name, path: 'add', parent: DiaryRouterPage.name) ]) ]), - _i17.RouteConfig(ProfileScreenRoute.name, + _i18.RouteConfig(ProfileScreenRoute.name, path: 'my', parent: HomeScreenRoute.name) ]), - _i17.RouteConfig(LoginScreenRoute.name, path: '/login'), - _i17.RouteConfig(RegisterScreenRoute.name, + _i18.RouteConfig(LoginScreenRoute.name, path: '/login'), + _i18.RouteConfig(RegisterScreenRoute.name, path: '/register', children: [ - _i17.RouteConfig(RegisterFirstPageRoute.name, + _i18.RouteConfig(RegisterFirstPageRoute.name, path: '', parent: RegisterScreenRoute.name), - _i17.RouteConfig(RegisterSecondPageRoute.name, + _i18.RouteConfig(RegisterSecondPageRoute.name, path: '1', parent: RegisterScreenRoute.name), - _i17.RouteConfig(RegisterThirdPageRoute.name, + _i18.RouteConfig(RegisterThirdPageRoute.name, path: '2', parent: RegisterScreenRoute.name) ]), - _i17.RouteConfig('*#redirect', + _i18.RouteConfig('*#redirect', path: '*', redirectTo: '/home/diary', fullMatch: true) ]; } /// generated route for /// [_i1.HomeScreen] -class HomeScreenRoute extends _i17.PageRouteInfo { - const HomeScreenRoute({List<_i17.PageRouteInfo>? children}) +class HomeScreenRoute extends _i18.PageRouteInfo { + const HomeScreenRoute({List<_i18.PageRouteInfo>? children}) : super(HomeScreenRoute.name, path: '/home', initialChildren: children); static const String name = 'HomeScreenRoute'; @@ -234,7 +238,7 @@ class HomeScreenRoute extends _i17.PageRouteInfo { /// generated route for /// [_i2.LoginScreen] -class LoginScreenRoute extends _i17.PageRouteInfo { +class LoginScreenRoute extends _i18.PageRouteInfo { const LoginScreenRoute() : super(LoginScreenRoute.name, path: '/login'); static const String name = 'LoginScreenRoute'; @@ -242,8 +246,8 @@ class LoginScreenRoute extends _i17.PageRouteInfo { /// generated route for /// [_i3.RegisterScreen] -class RegisterScreenRoute extends _i17.PageRouteInfo { - const RegisterScreenRoute({List<_i17.PageRouteInfo>? children}) +class RegisterScreenRoute extends _i18.PageRouteInfo { + const RegisterScreenRoute({List<_i18.PageRouteInfo>? children}) : super(RegisterScreenRoute.name, path: '/register', initialChildren: children); @@ -251,42 +255,59 @@ class RegisterScreenRoute extends _i17.PageRouteInfo { } /// generated route for -/// [_i4.EmptyRouterPage] -class ChatRouterPage extends _i17.PageRouteInfo { - const ChatRouterPage({List<_i17.PageRouteInfo>? children}) - : super(ChatRouterPage.name, path: 'chat', initialChildren: children); +/// [_i4.ChatRouterPage] +class ChatRouterPageRoute extends _i18.PageRouteInfo { + const ChatRouterPageRoute({List<_i18.PageRouteInfo>? children}) + : super(ChatRouterPageRoute.name, + path: 'chat', initialChildren: children); - static const String name = 'ChatRouterPage'; + static const String name = 'ChatRouterPageRoute'; } /// generated route for -/// [_i4.EmptyRouterPage] -class MainRouterPage extends _i17.PageRouteInfo { - const MainRouterPage({List<_i17.PageRouteInfo>? children}) +/// [_i5.EmptyRouterPage] +class MainRouterPage extends _i18.PageRouteInfo { + const MainRouterPage({List<_i18.PageRouteInfo>? children}) : super(MainRouterPage.name, path: 'main', initialChildren: children); static const String name = 'MainRouterPage'; } /// generated route for -/// [_i5.ProfileScreen] -class ProfileScreenRoute extends _i17.PageRouteInfo { +/// [_i6.ProfileScreen] +class ProfileScreenRoute extends _i18.PageRouteInfo { const ProfileScreenRoute() : super(ProfileScreenRoute.name, path: 'my'); static const String name = 'ProfileScreenRoute'; } /// generated route for -/// [_i6.ChatScreen] -class ChatScreenRoute extends _i17.PageRouteInfo { - const ChatScreenRoute() : super(ChatScreenRoute.name, path: 'onechat'); +/// [_i7.ChatScreen] +class ChatScreenRoute extends _i18.PageRouteInfo { + ChatScreenRoute({_i19.Key? key, _i25.User? otherUser}) + : super(ChatScreenRoute.name, + path: 'onechat', + args: ChatScreenRouteArgs(key: key, otherUser: otherUser)); static const String name = 'ChatScreenRoute'; } +class ChatScreenRouteArgs { + const ChatScreenRouteArgs({this.key, this.otherUser}); + + final _i19.Key? key; + + final _i25.User? otherUser; + + @override + String toString() { + return 'ChatScreenRouteArgs{key: $key, otherUser: $otherUser}'; + } +} + /// generated route for -/// [_i7.DiscussionListScreen] -class DiscussionListScreenRoute extends _i17.PageRouteInfo { +/// [_i8.DiscussionListScreen] +class DiscussionListScreenRoute extends _i18.PageRouteInfo { const DiscussionListScreenRoute() : super(DiscussionListScreenRoute.name, path: 'chats'); @@ -294,8 +315,8 @@ class DiscussionListScreenRoute extends _i17.PageRouteInfo { } /// generated route for -/// [_i8.DocumentListScreen] -class DocumentListScreenRoute extends _i17.PageRouteInfo { +/// [_i9.DocumentListScreen] +class DocumentListScreenRoute extends _i18.PageRouteInfo { const DocumentListScreenRoute() : super(DocumentListScreenRoute.name, path: 'documents'); @@ -303,9 +324,9 @@ class DocumentListScreenRoute extends _i17.PageRouteInfo { } /// generated route for -/// [_i4.EmptyRouterPage] -class ClientListRouter extends _i17.PageRouteInfo { - const ClientListRouter({List<_i17.PageRouteInfo>? children}) +/// [_i5.EmptyRouterPage] +class ClientListRouter extends _i18.PageRouteInfo { + const ClientListRouter({List<_i18.PageRouteInfo>? children}) : super(ClientListRouter.name, path: 'clients', initialChildren: children); @@ -313,27 +334,27 @@ class ClientListRouter extends _i17.PageRouteInfo { } /// generated route for -/// [_i4.EmptyRouterPage] -class DiaryRouterPage extends _i17.PageRouteInfo { - const DiaryRouterPage({List<_i17.PageRouteInfo>? children}) +/// [_i5.EmptyRouterPage] +class DiaryRouterPage extends _i18.PageRouteInfo { + const DiaryRouterPage({List<_i18.PageRouteInfo>? children}) : super(DiaryRouterPage.name, path: 'diary', initialChildren: children); static const String name = 'DiaryRouterPage'; } /// generated route for -/// [_i9.ClientListScreen] -class ClientListScreenRoute extends _i17.PageRouteInfo { +/// [_i10.ClientListScreen] +class ClientListScreenRoute extends _i18.PageRouteInfo { const ClientListScreenRoute() : super(ClientListScreenRoute.name, path: ''); static const String name = 'ClientListScreenRoute'; } /// generated route for -/// [_i10.ClientRecordScreen] +/// [_i11.ClientRecordScreen] class ClientRecordScreenRoute - extends _i17.PageRouteInfo { - ClientRecordScreenRoute({required _i24.User user, _i18.Key? key}) + extends _i18.PageRouteInfo { + ClientRecordScreenRoute({required _i25.User user, _i19.Key? key}) : super(ClientRecordScreenRoute.name, path: 'record', args: ClientRecordScreenRouteArgs(user: user, key: key)); @@ -344,9 +365,9 @@ class ClientRecordScreenRoute class ClientRecordScreenRouteArgs { const ClientRecordScreenRouteArgs({required this.user, this.key}); - final _i24.User user; + final _i25.User user; - final _i18.Key? key; + final _i19.Key? key; @override String toString() { @@ -355,11 +376,11 @@ class ClientRecordScreenRouteArgs { } /// generated route for -/// [_i11.UpdateClientRecordScreen] +/// [_i12.UpdateClientRecordScreen] class UpdateClientRecordScreenRoute - extends _i17.PageRouteInfo { + extends _i18.PageRouteInfo { UpdateClientRecordScreenRoute( - {required dynamic user, _i22.Aftercare? aftercare, _i18.Key? key}) + {required dynamic user, _i23.Aftercare? aftercare, _i19.Key? key}) : super(UpdateClientRecordScreenRoute.name, path: 'update', args: UpdateClientRecordScreenRouteArgs( @@ -374,9 +395,9 @@ class UpdateClientRecordScreenRouteArgs { final dynamic user; - final _i22.Aftercare? aftercare; + final _i23.Aftercare? aftercare; - final _i18.Key? key; + final _i19.Key? key; @override String toString() { @@ -385,17 +406,17 @@ class UpdateClientRecordScreenRouteArgs { } /// generated route for -/// [_i12.DiaryScreen] -class DiaryScreenRoute extends _i17.PageRouteInfo { +/// [_i13.DiaryScreen] +class DiaryScreenRoute extends _i18.PageRouteInfo { const DiaryScreenRoute() : super(DiaryScreenRoute.name, path: ''); static const String name = 'DiaryScreenRoute'; } /// generated route for -/// [_i13.AddMealScreen] -class AddMealScreenRoute extends _i17.PageRouteInfo { - AddMealScreenRoute({required DateTime day, _i23.Meal? meal, _i18.Key? key}) +/// [_i14.AddMealScreen] +class AddMealScreenRoute extends _i18.PageRouteInfo { + AddMealScreenRoute({required DateTime day, _i24.Meal? meal, _i19.Key? key}) : super(AddMealScreenRoute.name, path: 'add', args: AddMealScreenRouteArgs(day: day, meal: meal, key: key)); @@ -408,9 +429,9 @@ class AddMealScreenRouteArgs { final DateTime day; - final _i23.Meal? meal; + final _i24.Meal? meal; - final _i18.Key? key; + final _i19.Key? key; @override String toString() { @@ -419,18 +440,18 @@ class AddMealScreenRouteArgs { } /// generated route for -/// [_i14.RegisterFirstPage] -class RegisterFirstPageRoute extends _i17.PageRouteInfo { +/// [_i15.RegisterFirstPage] +class RegisterFirstPageRoute extends _i18.PageRouteInfo { const RegisterFirstPageRoute() : super(RegisterFirstPageRoute.name, path: ''); static const String name = 'RegisterFirstPageRoute'; } /// generated route for -/// [_i15.RegisterSecondPage] +/// [_i16.RegisterSecondPage] class RegisterSecondPageRoute - extends _i17.PageRouteInfo { - RegisterSecondPageRoute({_i18.Key? key}) + extends _i18.PageRouteInfo { + RegisterSecondPageRoute({_i19.Key? key}) : super(RegisterSecondPageRoute.name, path: '1', args: RegisterSecondPageRouteArgs(key: key)); @@ -440,7 +461,7 @@ class RegisterSecondPageRoute class RegisterSecondPageRouteArgs { const RegisterSecondPageRouteArgs({this.key}); - final _i18.Key? key; + final _i19.Key? key; @override String toString() { @@ -449,8 +470,8 @@ class RegisterSecondPageRouteArgs { } /// generated route for -/// [_i16.RegisterThirdPage] -class RegisterThirdPageRoute extends _i17.PageRouteInfo { +/// [_i17.RegisterThirdPage] +class RegisterThirdPageRoute extends _i18.PageRouteInfo { const RegisterThirdPageRoute() : super(RegisterThirdPageRoute.name, path: '2'); diff --git a/lib/screens/chat.dart b/lib/screens/chat.dart index e5c3fc0..4b3dec1 100644 --- a/lib/screens/chat.dart +++ b/lib/screens/chat.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:math' as math; import 'package:auto_route/auto_route.dart'; @@ -6,7 +7,13 @@ import 'package:flutter/material.dart'; // ignore: depend_on_referenced_packages import 'package:flutter_chat_types/flutter_chat_types.dart' as types; import 'package:flutter_chat_ui/flutter_chat_ui.dart'; +import 'package:get_it/get_it.dart'; import 'package:pdg_app/router/router.gr.dart'; +import 'package:provider/provider.dart'; + +import '../model/user.dart'; +import '../provider/auth_provider.dart'; +import '../provider/chat_provider.dart'; String randomString() { final random = math.Random.secure(); @@ -15,26 +22,35 @@ String randomString() { } class ChatScreen extends StatefulWidget { - const ChatScreen({Key? key}) : super(key: key); + final User? _otherUser; + const ChatScreen({ + Key? key, + User? otherUser, + }) : _otherUser = otherUser, + super(key: key); @override State createState() => _ChatScreenState(); } class _ChatScreenState extends State { - final List _messages = []; - final _user = const types.User(id: '82091008-a484-4a89-ae75-a22bf8d6f3ac'); - final _user2 = const types.User(id: '82091008-a484-4a89-ae75-a22bf8d6f3af'); + final _mainUser = types.User(id: GetIt.I.get().userUid); + late User _otherUser; + + @override + void initState() { + super.initState(); + } void _addMessage(types.Message message) { setState(() { - _messages.insert(0, message); + //_messages.insert(0, message); }); } void _handleSendPressed(types.PartialText message) { final textMessage = types.TextMessage( - author: _user2, + author: _mainUser, createdAt: DateTime.now().millisecondsSinceEpoch, id: randomString(), text: message.text, @@ -45,10 +61,20 @@ class _ChatScreenState extends State { @override Widget build(BuildContext context) { + final ChatProvider chatProvider = context.watch(); + _otherUser = widget._otherUser ?? chatProvider.messages.keys.first; + log(chatProvider.messages.toString()); return ChatInterface( - name: "Nelson", - currentUser: _user, - messages: _messages, + name: '${_otherUser.firstName} ${_otherUser.lastName}', + currentUser: _mainUser, + messages: chatProvider.messages[_otherUser]! + .map((m) => types.TextMessage( + id: m.uid, + author: types.User(id: m.fromId), + type: types.MessageType.text, + text: m.content, + )) + .toList(), onSendPressed: _handleSendPressed, onDocumentPressed: () { AutoRouter.of(context).push(const DocumentListScreenRoute()); diff --git a/lib/screens/client_record.dart b/lib/screens/client_record.dart index 6fcf936..1a18579 100644 --- a/lib/screens/client_record.dart +++ b/lib/screens/client_record.dart @@ -146,8 +146,7 @@ class ClientRecord extends StatelessWidget { GradientButton( color1: Theme.of(context).colorScheme.tertiary, color2: Theme.of(context).colorScheme.tertiary, - onPress: (() => AutoRouter.of(context).push( - const ChatScreenRoute())), //TODO pour sélectionner bonne discussion + onPress: () {}, // TODO changer pour sélectionner le bon chat. child: const Text( "CHAT", style: TextStyle(color: Colors.white), diff --git a/lib/screens/discussion_list.dart b/lib/screens/discussion_list.dart index d5a030a..84f8e2c 100644 --- a/lib/screens/discussion_list.dart +++ b/lib/screens/discussion_list.dart @@ -1,6 +1,10 @@ +import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:pdg_app/model/custom_list_tile_data.dart'; +import 'package:provider/provider.dart'; +import '../provider/chat_provider.dart'; +import '../router/router.gr.dart'; import '../widgets/custom_list.dart'; class DiscussionListScreen extends StatelessWidget { @@ -10,13 +14,19 @@ class DiscussionListScreen extends StatelessWidget { Widget build(BuildContext context) { return CustomList( title: 'Discussions', - conversationsTileData: [ - CustomListTileData( - title: "test", - date: DateTime.now(), - badgeCount: 2, - ), - ], + conversationsTileData: context + .watch() + .getLastMessageOfEachUser() + .map((e) => CustomListTileData( + title: '${e.key.firstName} ${e.key.lastName}', + subtitle: e.value.content, + date: e.value.time, + onTap: () { + AutoRouter.of(context) + .push(ChatScreenRoute(otherUser: e.key)); + }, + )) + .toList(), ); } } diff --git a/lib/screens/home.dart b/lib/screens/home.dart index e642019..8590097 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -16,7 +16,7 @@ class _HomeScreenState extends State { Widget build(BuildContext context) { return AutoTabsRouter( routes: const [ - ChatRouterPage(), + ChatRouterPageRoute(), MainRouterPage(), ProfileScreenRoute(), ], diff --git a/lib/screens/login.dart b/lib/screens/login.dart index 2aec8f9..25bb4a2 100644 --- a/lib/screens/login.dart +++ b/lib/screens/login.dart @@ -25,8 +25,8 @@ class _LoginScreenState extends State { @override void initState() { super.initState(); - _emailController.text = "chloe.fontaine@heig-vd.ch"; - _passwordController.text = "crepes123"; + _emailController.text = "luca.coduri@heig-vd.ch"; + _passwordController.text = "crepes"; } @override diff --git a/lib/widgets/custom_list.dart b/lib/widgets/custom_list.dart index c3bac47..3a2e0df 100644 --- a/lib/widgets/custom_list.dart +++ b/lib/widgets/custom_list.dart @@ -34,7 +34,8 @@ class CustomList extends StatelessWidget { image: AssetImage('assets/images/default_user_pic.png')), title: conv.title ?? 'Conversation ${index + 1}', - subtitle: conv.date != null + subtitle: conv.subtitle ?? '', + date: conv.date != null ? format.format(conv.date!) : 'No messages yet', badgeCount: _conversationsTileData[index].badgeCount ?? 0, @@ -58,6 +59,7 @@ class CustomList extends StatelessWidget { class _CustomListTile extends StatelessWidget { final String _title; final String _subtitle; + final String? _date; final int _badgeCount; final Widget _avatar; final void Function()? _onTap; @@ -66,6 +68,7 @@ class _CustomListTile extends StatelessWidget { Key? key, required String title, required String subtitle, + String? date, required int badgeCount, required Widget avatar, void Function()? onTap, @@ -74,6 +77,7 @@ class _CustomListTile extends StatelessWidget { _badgeCount = badgeCount, _avatar = avatar, _onTap = onTap, + _date = date, super(key: key); @override @@ -84,7 +88,15 @@ class _CustomListTile extends StatelessWidget { onTap: _onTap, leading: _avatar, trailing: _badgeCount == 0 ? const SizedBox() : Badge(_badgeCount), - title: Text(_title), + title: + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text(_title), + if (_date != null) + Text( + _date!, + style: const TextStyle(fontSize: 14), + ) + ]), subtitle: Text(_subtitle), ), ); diff --git a/pubspec.lock b/pubspec.lock index e7fb5ba..4a4103f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -833,6 +833,13 @@ packages: description: flutter source: sdk version: "0.0.99" + sorted_list: + dependency: "direct main" + description: + name: sorted_list + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" source_gen: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 96d1e49..1011942 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,6 +55,7 @@ dependencies: file_picker: ^3.0.4 file: ^6.1.4 awesome_snackbar_content: ^0.0.8 + sorted_list: ^1.0.0 dev_dependencies: flutter_test: