From 478ac139ddc458c4cc26cee6c97a02e8b89a9467 Mon Sep 17 00:00:00 2001 From: mso7nueh Date: Fri, 4 Jul 2025 16:27:18 +0300 Subject: [PATCH] feat(chat): WIP: Issue-1594 --- lib/core/app_router.dart | 4 ++-- lib/core/const.dart | 2 +- lib/core/token_storage.dart | 22 +++++++++++++++++++ lib/core/websockets/ws_manager.dart | 21 +++++++++++++++++- lib/features/chat/models/chat_room_model.dart | 2 +- .../models/chat_room_pagination_model.dart | 14 +----------- .../chat/models/chat_room_settings_model.dart | 5 +---- .../bloc/chat_room_cubit/chat_room_cubit.dart | 16 +++++++------- .../bloc/chats_cubit/chats_cubit.dart | 10 ++++----- .../chat/presentation/pages/chat_screen.dart | 10 ++++----- .../presentation/pages/empty_chat_screen.dart | 8 +++---- .../widgets/chat_entry_widget.dart | 2 +- .../chat/repositories/chat_repository.dart | 18 ++++++++------- lib/features/chat/services/chat_service.dart | 19 +++++----------- .../presentation/pages/about_plan_screen.dart | 2 +- .../bloc/profile_cubit/profile_cubit.dart | 2 +- .../widgets/sub_action_button_widget.dart | 2 +- 17 files changed, 90 insertions(+), 69 deletions(-) diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index 223a8913..9dc8612f 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -373,10 +373,10 @@ class AppRouter { name: 'chatScreen', builder: (context, state) { final data = state.extra as Map; - final chatId = data['chatId'] as String; + final uuid = data['uuid'] as String; final sendInitialMessage = data['sendInitialMessage'] as bool?; return ChatScreen( - chatId: chatId, + uuid: uuid, sendInitialMessage: sendInitialMessage, ); }, diff --git a/lib/core/const.dart b/lib/core/const.dart index 618a9866..8f5ddb59 100644 --- a/lib/core/const.dart +++ b/lib/core/const.dart @@ -1,4 +1,4 @@ -String baseUrl = 'https://eatvio.ru'; +String baseUrl = 'https://mobile-stage.eatvio.ru'; const List durationValues = [7, 14, 21]; diff --git a/lib/core/token_storage.dart b/lib/core/token_storage.dart index cbfb8abb..30906264 100644 --- a/lib/core/token_storage.dart +++ b/lib/core/token_storage.dart @@ -21,3 +21,25 @@ abstract class TokenStorage { static Future removeTokenInStorage() async => (await const FlutterSecureStorage().delete(key: 'token')); } + +abstract class SocketIdStorage { + static Future haveSocketIdInStorage() async { + try { + if (await const FlutterSecureStorage().read(key: 'socketId') == null) { + return false; + } else { + return true; + } + } catch (e) { + return false; + } + } + + static Future getSocketIdFromStorage() async => await const FlutterSecureStorage().read(key: 'socketId'); + + static Future saveSocketIdInStorage(String socketId) async { + await const FlutterSecureStorage().write(key: 'socketId', value: socketId); + } + + static Future removeSocketIdInStorage() async => (await const FlutterSecureStorage().delete(key: 'socketId')); +} diff --git a/lib/core/websockets/ws_manager.dart b/lib/core/websockets/ws_manager.dart index 73431542..0a5f7241 100644 --- a/lib/core/websockets/ws_manager.dart +++ b/lib/core/websockets/ws_manager.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:developer'; import 'package:dart_pusher_channels/dart_pusher_channels.dart'; @@ -59,6 +60,24 @@ class WsManager { ), ); + String? extractSocketId(String? data) { + if (data == null) return null; + try { + final decoded = jsonDecode(data); + return decoded['socket_id'] as String?; + } catch (_) { + return null; + } + } + + client?.eventStream.listen((event) async { + if (event.name == 'pusher:connection_established') { + final data = event.data; + final socketId = extractSocketId(data); + await SocketIdStorage.saveSocketIdInStorage(socketId!); + } + }); + await client?.connect(); client = client; @@ -77,7 +96,7 @@ class WsManager { EasyDebounce.debounce( 'searchChatRoom', const Duration(milliseconds: 300), - () => sl().getChatRooms(), + () => sl().getChatRooms(), ); log(event.rootObject.toString()); }); diff --git a/lib/features/chat/models/chat_room_model.dart b/lib/features/chat/models/chat_room_model.dart index d7bf450a..dc091ea6 100644 --- a/lib/features/chat/models/chat_room_model.dart +++ b/lib/features/chat/models/chat_room_model.dart @@ -9,7 +9,7 @@ part 'chat_room_model.g.dart'; @freezed abstract class ChatRoomModel with _$ChatRoomModel { const factory ChatRoomModel({ - required String id, + required String id, @JsonKey(name: 'chat_room_setting') required ChatRoomSettingsModel chatRoomSettings, required Users users, @JsonKey(name: 'chosen_plan_id') required int? chosenPlanId, diff --git a/lib/features/chat/models/chat_room_pagination_model.dart b/lib/features/chat/models/chat_room_pagination_model.dart index 7bb8ceb5..699df5e8 100644 --- a/lib/features/chat/models/chat_room_pagination_model.dart +++ b/lib/features/chat/models/chat_room_pagination_model.dart @@ -8,29 +8,17 @@ part 'chat_room_pagination_model.g.dart'; @freezed abstract class ChatRoomPaginationModel with _$ChatRoomPaginationModel { const factory ChatRoomPaginationModel({ - @JsonKey(name: "chatRooms") required ChatRooms chatRooms, + @JsonKey(name: "data") required List data, @JsonKey(name: "number_of_unread_chat_rooms") required int unreadChatsCount, }) = _ChatRoomPreviewModel; factory ChatRoomPaginationModel.fromJson(Map json) => _$ChatRoomPaginationModelFromJson(json); } -@freezed -abstract class ChatRooms with _$ChatRooms { - const factory ChatRooms({ - @JsonKey(name: "data") required List data, - @JsonKey(name: "last_page") required int lastPage, - @JsonKey(name: "current_page") required int currentPage, - }) = _ChatRooms; - - factory ChatRooms.fromJson(Map json) => _$ChatRoomsFromJson(json); -} - @freezed abstract class ChatRoom with _$ChatRoom { const factory ChatRoom({ @JsonKey(name: "id") required String id, - @JsonKey(name: "room_name") required String roomName, @JsonKey(name: "last_message", fromJson: parseLastMessage) required ChatMessageModel? lastMessage, @JsonKey(name: "users") required Users users, @JsonKey(name: "unread_messages_count") required int unreadMessagesCount, diff --git a/lib/features/chat/models/chat_room_settings_model.dart b/lib/features/chat/models/chat_room_settings_model.dart index 5298d3d9..fa332843 100644 --- a/lib/features/chat/models/chat_room_settings_model.dart +++ b/lib/features/chat/models/chat_room_settings_model.dart @@ -6,11 +6,8 @@ part 'chat_room_settings_model.g.dart'; @freezed abstract class ChatRoomSettingsModel with _$ChatRoomSettingsModel { const factory ChatRoomSettingsModel({ - required int id, - @JsonKey(name: 'room_name') required String roomName, + required String name, required String state, - @JsonKey(name: 'access_level') required String accessLevel, - @JsonKey(name: 'updated_at') required String updatedAt, }) = _ChatRoomSettingsModel; factory ChatRoomSettingsModel.fromJson(Map json) => _$ChatRoomSettingsModelFromJson(json); diff --git a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart index 63bf9e19..40ee862d 100644 --- a/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart +++ b/lib/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart @@ -47,14 +47,14 @@ class ChatRoomCubit extends Cubit { _timer?.cancel(); } - Future loadNextPage(String chatId) async { + Future loadNextPage(String uuid) async { if (currentPage < lastPage && !isLoading) { isLoading = true; emit((state as ChatRoomStateLoaded).copyWith(isLoading: isLoading)); try { - final chatMessages = await chatRepository.getChatMessages(chatId, currentPage + 1); + final chatMessages = await chatRepository.getChatMessages(uuid, currentPage + 1); isLoading = false; emit( @@ -89,17 +89,17 @@ class ChatRoomCubit extends Cubit { } } - Future initChat(String chatId, {bool sendInitialMessage = false}) async { + Future initChat(String uuid, {bool sendInitialMessage = false}) async { try { - final chatRoom = await chatRepository.getChatRoom(chatId); + final chatRoom = await chatRepository.getChatRoom(uuid); ChosenPlanModel? chosenPlan; if (chatRoom.chosenPlanId != null) { chosenPlan = await chatRepository.getChosenPlan(id: chatRoom.chosenPlanId!); } - final chatMessages = await chatRepository.getChatMessages(chatId, currentPage); + final chatMessages = await chatRepository.getChatMessages(uuid, currentPage); lastPage = chatMessages.meta.lastPage; - await _subscribeToChatChannel(chatId: chatId); + await _subscribeToChatChannel(uuid: uuid); emit( ChatRoomState.loaded( chatRoom: chatRoom.copyWith(chosenPlan: chosenPlan), @@ -158,10 +158,10 @@ class ChatRoomCubit extends Cubit { } } - Future _subscribeToChatChannel({required String chatId}) async { + Future _subscribeToChatChannel({required String uuid}) async { final client = wsManager.client; if (client == null) return; - await wsManager.initPrivateDirectChatChannel(chatId); + await wsManager.initPrivateDirectChatChannel(uuid); wsManager.subscribeDirectChatChannel(); messagesSubscription = wsManager.directChatStream?.listen((event) { AudioPlayer().play((AssetSource('audio/recive_message.wav'))); diff --git a/lib/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart b/lib/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart index 3b50c995..24028797 100644 --- a/lib/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart +++ b/lib/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart @@ -43,10 +43,10 @@ class ChatsCubit extends Cubit { try { final chatRoomsPaginationModel = await chatRepository.getChatRooms(query: query); final unreadChats = chatRoomsPaginationModel.unreadChatsCount; - lastPage = chatRoomsPaginationModel.chatRooms.lastPage + 1; + // lastPage = chatRoomsPaginationModel.chatRooms.lastPage + 1; emit( state.copyWith( - chatRooms: chatRoomsPaginationModel.chatRooms.data, + chatRooms: chatRoomsPaginationModel.data, chatsWithUnreadMessagesCount: unreadChats, state: BaseState.success, ), @@ -65,10 +65,10 @@ class ChatsCubit extends Cubit { try { final chatRoomsPaginationModel = await chatRepository.getChatRooms(query: query); final unreadChats = chatRoomsPaginationModel.unreadChatsCount; - lastPage = chatRoomsPaginationModel.chatRooms.lastPage + 1; + // lastPage = chatRoomsPaginationModel.chatRooms.lastPage + 1; emit( state.copyWith( - chatRooms: chatRoomsPaginationModel.chatRooms.data, + chatRooms: chatRoomsPaginationModel.data, chatsWithUnreadMessagesCount: unreadChats, state: BaseState.success, ), @@ -90,7 +90,7 @@ class ChatsCubit extends Cubit { final unreadChats = chatRoomsPaginationModel.unreadChatsCount; emit( state.copyWith( - chatRooms: [...state.chatRooms, ...chatRoomsPaginationModel.chatRooms.data], + chatRooms: [...state.chatRooms, ...chatRoomsPaginationModel.data], chatsWithUnreadMessagesCount: unreadChats, state: BaseState.success, isLoadingMoreChats: false, diff --git a/lib/features/chat/presentation/pages/chat_screen.dart b/lib/features/chat/presentation/pages/chat_screen.dart index 55f74d70..c017f047 100644 --- a/lib/features/chat/presentation/pages/chat_screen.dart +++ b/lib/features/chat/presentation/pages/chat_screen.dart @@ -32,10 +32,10 @@ import 'package:intl/intl.dart'; import 'package:visibility_detector/visibility_detector.dart'; class ChatScreen extends StatefulWidget { - final String chatId; + final String uuid; final bool? sendInitialMessage; - const ChatScreen({required this.chatId, super.key, required this.sendInitialMessage}); + const ChatScreen({required this.uuid, super.key, required this.sendInitialMessage}); @override State createState() => _ChatScreenState(); @@ -64,14 +64,14 @@ class _ChatScreenState extends State { void initState() { super.initState(); scrollController.addListener(_onScroll); - chatRoomCubit.initChat(widget.chatId, sendInitialMessage: widget.sendInitialMessage ?? false); + chatRoomCubit.initChat(widget.uuid, sendInitialMessage: widget.sendInitialMessage ?? false); chatRoomCubit.setTimer(); _listener = AppLifecycleListener(onHide: _onHide, onResume: _onResume); } void _onScroll() { if (scrollController.offset >= scrollController.position.maxScrollExtent && !scrollController.position.outOfRange) { - chatRoomCubit.loadNextPage(widget.chatId); + chatRoomCubit.loadNextPage(widget.uuid); } } @@ -93,7 +93,7 @@ class _ChatScreenState extends State { if (state is ChatRoomStateLoaded) { return _buildChat(context, state); } else if (state is ChatRoomStateError) { - return Scaffold(appBar: AppBar(), body: DataErrorWidget(onReload: () => chatRoomCubit.initChat(widget.chatId))); + return Scaffold(appBar: AppBar(), body: DataErrorWidget(onReload: () => chatRoomCubit.initChat(widget.uuid))); } else { return const Scaffold(body: Loader()); } diff --git a/lib/features/chat/presentation/pages/empty_chat_screen.dart b/lib/features/chat/presentation/pages/empty_chat_screen.dart index 3195888c..7b543f86 100644 --- a/lib/features/chat/presentation/pages/empty_chat_screen.dart +++ b/lib/features/chat/presentation/pages/empty_chat_screen.dart @@ -145,7 +145,7 @@ class _EmptyChatScreenState extends State { AppRouter.navigatorKey.currentContext?.pushReplacementNamed( 'chatScreen', extra: { - 'chatId': chatIdOrError, + 'uuid': chatIdOrError, }, ); } @@ -186,7 +186,7 @@ class _EmptyChatScreenState extends State { AppRouter.navigatorKey.currentContext?.pushReplacementNamed( 'chatScreen', extra: { - 'chatId': chatIdOrError, + 'uuid': chatIdOrError, }, ); } @@ -214,7 +214,7 @@ class _EmptyChatScreenState extends State { AppRouter.navigatorKey.currentContext?.pushReplacementNamed( 'chatScreen', extra: { - 'chatId': chatIdOrError, + 'uuid': chatIdOrError, }, ); } @@ -264,7 +264,7 @@ class _EmptyChatScreenState extends State { AppRouter.navigatorKey.currentContext?.pushReplacementNamed( 'chatScreen', extra: { - 'chatId': chatIdOrError, + 'uuid': chatIdOrError, }, ); } diff --git a/lib/features/chat/presentation/widgets/chat_entry_widget.dart b/lib/features/chat/presentation/widgets/chat_entry_widget.dart index 44cc67f9..dacff9cb 100644 --- a/lib/features/chat/presentation/widgets/chat_entry_widget.dart +++ b/lib/features/chat/presentation/widgets/chat_entry_widget.dart @@ -24,7 +24,7 @@ class _ChatEntryWidgetState extends State { onTap: () => GoRouter.of(context).pushNamed( 'chatScreen', extra: { - 'chatId': widget.chatRoom.id.toString(), + 'uuid': widget.chatRoom.id.toString(), }, ), child: Container( diff --git a/lib/features/chat/repositories/chat_repository.dart b/lib/features/chat/repositories/chat_repository.dart index 54924fcd..d682c9a0 100644 --- a/lib/features/chat/repositories/chat_repository.dart +++ b/lib/features/chat/repositories/chat_repository.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; import 'package:dio/dio.dart'; +import 'package:eatvio_app/core/token_storage.dart'; import 'package:eatvio_app/features/chat/models/chat_message_model.dart'; import 'package:eatvio_app/features/chat/models/chat_messages_model.dart'; import 'package:eatvio_app/features/chat/models/chat_room_model.dart'; @@ -47,10 +48,11 @@ class ChatRepository { } } - await chatService.sendMessageByUserSlug( - userSlug: slug, + await chatService.sendMessage( + slug: slug, data: data, mediaData: multipartFiles.isEmpty ? null : multipartFiles, + socketId: (await SocketIdStorage.getSocketIdFromStorage())!, ); } catch (e, st) { log(e.toString(), stackTrace: st); @@ -58,9 +60,9 @@ class ChatRepository { } } - Future getChatRoom(String chatId) async { + Future getChatRoom(String uuid) async { try { - return (await chatService.getChatRoom(id: chatId))['chatRoom']!; + return (await chatService.getChatRoom(uuid: uuid)); } catch (e) { rethrow; } @@ -74,9 +76,9 @@ class ChatRepository { } } - Future getChatMessages(String chatId, int page) async { + Future getChatMessages(String id, int page) async { try { - return (await chatService.getChatMessages(id: chatId, page: page)); + return (await chatService.getChatMessages(id: id, page: page)); } catch (e) { rethrow; } @@ -90,9 +92,9 @@ class ChatRepository { } } - Future markMessagesAsRead(String chatId, List messageIds) async { + Future markMessagesAsRead(String id, List messageIds) async { try { - await chatService.markMessagesAsRead(id: chatId, body: {'messages_ids': messageIds}); + await chatService.markMessagesAsRead(id: id, body: {'messages_ids': messageIds}); } catch (e) { rethrow; } diff --git a/lib/features/chat/services/chat_service.dart b/lib/features/chat/services/chat_service.dart index dcdab395..dee63eab 100644 --- a/lib/features/chat/services/chat_service.dart +++ b/lib/features/chat/services/chat_service.dart @@ -19,15 +19,16 @@ abstract class ChatService { @Query('find') String? query, }); - @GET('/api/chat/show/{id}') - Future> getChatRoom({ - @Path() required String id, + @GET('/api/chat/show/{uuid}') + Future getChatRoom({ + @Path() required String uuid, }); - @POST('/api/v2/chat/message/send/{id}') + @POST('/api/chat/message/send/{slug}') @MultiPart() Future sendMessage({ - @Path() required String id, + @Header('X-Socket-ID') required String socketId, + @Path() required String slug, @Part() String? data, @Part(name: 'media_data[]', contentType: 'multipart/form-data') List? mediaData, }); @@ -85,12 +86,4 @@ abstract class ChatService { Future getChatByUserSlug({ @Path() required String slug, }); - - @POST('/api/v2/chat/message/send/{userSlug}') - @MultiPart() - Future sendMessageByUserSlug({ - @Path() required String userSlug, - @Part() String? data, - @Part(name: 'media_data[]', contentType: 'multipart/form-data') List? mediaData, - }); } diff --git a/lib/features/plans/presentation/pages/about_plan_screen.dart b/lib/features/plans/presentation/pages/about_plan_screen.dart index d2984b68..15c12ecb 100644 --- a/lib/features/plans/presentation/pages/about_plan_screen.dart +++ b/lib/features/plans/presentation/pages/about_plan_screen.dart @@ -208,7 +208,7 @@ class _AboutPlanScreenState extends State { GoRouter.of(context).pushReplacementNamed( 'chatScreen', extra: { - 'chatId': chosenPlan.chatId, + 'uuid': chosenPlan.chatId, 'sendInitialMessage': true, }, ); diff --git a/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart b/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart index b088a073..36a98658 100644 --- a/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart +++ b/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart @@ -123,7 +123,7 @@ class ProfileCubit extends Cubit { AppRouter.navigatorKey.currentContext?.pushNamed( 'chatScreen', extra: { - 'chatId': chatIdOrError, + 'uuid': chatIdOrError, }, ); return chatIdOrError; diff --git a/lib/features/profile/presentation/widgets/sub_action_button_widget.dart b/lib/features/profile/presentation/widgets/sub_action_button_widget.dart index bca5e3da..55b4a095 100644 --- a/lib/features/profile/presentation/widgets/sub_action_button_widget.dart +++ b/lib/features/profile/presentation/widgets/sub_action_button_widget.dart @@ -69,7 +69,7 @@ class _SubActionButtonWidgetState extends State { GoRouter.of(context).pushNamed( 'chatScreen', extra: { - 'chatId': await sl().getChatRoomIdBySlug( + 'uuid': await sl().getChatRoomIdBySlug( author: Author( authorSlug: widget.slug, authorName: widget.name,