fixed message bugs and added the ability to view history

This commit is contained in:
stasex 2023-11-24 22:19:01 +02:00
parent 4754a2b07c
commit 747909fe42
4 changed files with 151 additions and 35 deletions

View File

@ -12,11 +12,8 @@ class AuthenticationService {
late SharedPreferences prefs; late SharedPreferences prefs;
AuthenticationService() {
SharedPreferences.getInstance().then((result) => {prefs = result});
}
Future<String> getAccessToken() async { Future<String> getAccessToken() async {
prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString('accessToken'); var accessToken = prefs.getString('accessToken');
var refreshToken = prefs.getString('refreshToken'); var refreshToken = prefs.getString('refreshToken');

View File

@ -1,13 +1,12 @@
// search_service.dart // search_service.dart
import 'dart:async'; import 'dart:async';
import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:graphql_flutter/graphql_flutter.dart';
import '../models/enums/search_event_type.dart'; import '../models/enums/search_event_type.dart';
import '../models/server_sent_event.dart'; import '../models/server_sent_event.dart';
import '../network/api_client.dart'; import '../network/api_client.dart';
import '../network/authentication_service.dart';
import '../screens/chat.dart'; import '../screens/chat.dart';
import 'authentication_service.dart';
const String startPersonalWishlistMutations = r''' const String startPersonalWishlistMutations = r'''
mutation startPersonalWishlist($dto: WishlistCreateDtoInput!) { mutation startPersonalWishlist($dto: WishlistCreateDtoInput!) {
@ -20,18 +19,17 @@ const String startPersonalWishlistMutations = r'''
SearchEventType type = SearchEventType.message; SearchEventType type = SearchEventType.message;
class SearchService { class SearchService {
final AuthenticationService _authenticationService = AuthenticationService();
final ApiClient client = ApiClient(); final ApiClient client = ApiClient();
final _sseController = StreamController<ServerSentEvent>(); late final _sseController = StreamController<ServerSentEvent>();
Stream<ServerSentEvent> get sseStream => _sseController.stream; Stream<ServerSentEvent> get sseStream => _sseController.stream;
Future<void> initializeAuthenticationService() async { bool checkerForProduct() {
await _authenticationService.initialize(); return type == SearchEventType.product;
} }
bool checkerForProduct() { bool checkerForSuggestion() {
return type == SearchEventType.product; return type == SearchEventType.product;
} }
@ -60,8 +58,7 @@ class SearchService {
return null; return null;
} }
Future<void> startPersonalWishlist(String message) async { Future<String> startPersonalWishlist(String message) async {
await _authenticationService.initialize();
// Перевіряємо, чи вже створений wishlist // Перевіряємо, чи вже створений wishlist
if (wishlistId == null) { if (wishlistId == null) {
@ -88,18 +85,76 @@ class SearchService {
await for (final chunk in sseStream) { await for (final chunk in sseStream) {
print("Original chunk.data: ${chunk.event}"); print("Original chunk.data: ${chunk.event}");
final cleanedMessage = chunk.data.replaceAll(RegExp(r'(^"|"$)'), ''); final cleanedMessage = chunk.data.replaceAll(RegExp(r'(^"|"$)'), '');
if(chunk.event == SearchEventType.message)
{
type = SearchEventType.message;
}
if(chunk.event == SearchEventType.product)
{
type = SearchEventType.product;
}
final event = ServerSentEvent(type, cleanedMessage); final event = ServerSentEvent(type, cleanedMessage);
_sseController.add(event); _sseController.add(event);
} }
} }
return wishlistId.toString();
}
Future<void> sendMessages(String message) async {
if (wishlistId != null) {
final sseStream = client.getServerSentEventStream(
'api/productssearch/search/$wishlistId',
{'text': message},
);
await for (final chunk in sseStream) {
print("Original chunk.data: ${chunk.event}");
final cleanedMessage = chunk.data.replaceAll(RegExp(r'(^"|"$)'), '');
final event = ServerSentEvent(chunk.event, cleanedMessage);
type = chunk.event;
_sseController.add(event);
}
}
}
Future<List<Message>> getMessagesFromPersonalWishlist(String wishlistIdPar, int pageNumber, int pageSize) async {
final options = QueryOptions(
document: gql('''
query MessagesPageFromPersonalWishlist(\$wishlistId: String!, \$pageNumber: Int!, \$pageSize: Int!) {
messagesPageFromPersonalWishlist(wishlistId: \$wishlistId, pageNumber: \$pageNumber, pageSize: \$pageSize) {
items {
id
text
role
createdById
}
}
}
'''),
variables: {
'wishlistId': wishlistIdPar,
'pageNumber': pageNumber,
'pageSize': pageSize,
},
);
print("DOCUMENT: ${options.document}");
final result = await client.query(options);
print("RESULT: ${result}");
print(result);
if (result != null &&
result.containsKey('messagesPageFromPersonalWishlist') &&
result['messagesPageFromPersonalWishlist'] != null &&
result['messagesPageFromPersonalWishlist']['items'] != null) {
final List<dynamic> items = result['messagesPageFromPersonalWishlist']['items'];
final List<Message> messages = items.map((item) {
return Message(
text: item['text'],
role: item['role'],
isProduct: false,
);
}).toList();
return messages;
}
return [];
} }
} }

View File

@ -1,14 +1,15 @@
// search_screen.dart // search_screen.dart
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:shopping_assistant_mobile_client/network/search_service.dart'; import 'package:shopping_assistant_mobile_client/network/search_service.dart';
class Message { class Message {
final String text; final String text;
final bool isUser; final String role;
bool isProduct; bool isProduct;
bool isSuggestion;
Message({required this.text, this.isUser = false, this.isProduct = false}); Message({required this.text, this.role = "", this.isProduct = false, this.isSuggestion = false});
} }
class MessageBubble extends StatelessWidget { class MessageBubble extends StatelessWidget {
@ -71,6 +72,7 @@ class ChatScreenState extends State<ChatScreen> {
bool buttonsVisible = true; bool buttonsVisible = true;
bool isSendButtonEnabled = false; bool isSendButtonEnabled = false;
bool showButtonsContainer = true; bool showButtonsContainer = true;
bool isWaitingForResponse = false;
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
late Widget appBarTitle; late Widget appBarTitle;
@ -82,16 +84,48 @@ class ChatScreenState extends State<ChatScreen> {
_searchService.sseStream.listen((event) { _searchService.sseStream.listen((event) {
_handleSSEMessage(Message(text: '${event.data}')); _handleSSEMessage(Message(text: '${event.data}'));
}); });
Future.delayed(Duration(milliseconds: 2000));
if(!wishlistId.isEmpty)
{
_loadPreviousMessages();
showButtonsContainer = false;
buttonsVisible = false;
}
}
Future<void> _loadPreviousMessages() async {
final pageNumber = 1;
final pageSize = 200;
print('Previous Messages:');
try {
final previousMessages = await _searchService.getMessagesFromPersonalWishlist("6560b4c210686c50ed4b9fec", pageNumber, pageSize);
final reversedMessages = previousMessages.reversed.toList();
setState(() {
messages.addAll(reversedMessages);
});
print('Previous Messages: $previousMessages');
for(final message in messages)
{
print("MESSAGES TEXT: ${message.text}");
print("MESSAGES ROLE: ${message.role}");
}
} catch (error) {
print('Error loading previous messages: $error');
}
} }
void _handleSSEMessage(Message message) { void _handleSSEMessage(Message message) {
setState(() { setState(() {
isWaitingForResponse = true;
final lastMessage = messages.isNotEmpty ? messages.last : null; final lastMessage = messages.isNotEmpty ? messages.last : null;
message.isProduct = _searchService.checkerForProduct(); message.isProduct = _searchService.checkerForProduct();
message.isSuggestion = _searchService.checkerForSuggestion();
print("Product status: ${message.isProduct}"); print("Product status: ${message.isProduct}");
if (lastMessage != null && !lastMessage.isUser && !message.isUser) { if (lastMessage != null && lastMessage.role != "User" && message.role != "User") {
final updatedMessage = Message( final updatedMessage = Message(
text: "${lastMessage.text}${message.text}", text: "${lastMessage.text}${message.text}",
role: "Application",
isProduct: message.isProduct); isProduct: message.isProduct);
messages.removeLast(); messages.removeLast();
messages.add(updatedMessage); messages.add(updatedMessage);
@ -99,6 +133,9 @@ class ChatScreenState extends State<ChatScreen> {
messages.add(message); messages.add(message);
} }
}); });
setState(() {
isWaitingForResponse = false;
});
_scrollToBottom(); _scrollToBottom();
} }
@ -106,7 +143,6 @@ class ChatScreenState extends State<ChatScreen> {
final wishlistName = await _searchService.generateNameForPersonalWishlist(wishlistId); final wishlistName = await _searchService.generateNameForPersonalWishlist(wishlistId);
if (wishlistName != null) { if (wishlistName != null) {
setState(() { setState(() {
// Оновіть назву чату з результатом методу generateNameForPersonalWishlist
appBarTitle = Text(wishlistName); appBarTitle = Text(wishlistName);
}); });
} }
@ -116,30 +152,35 @@ class ChatScreenState extends State<ChatScreen> {
setState(() { setState(() {
buttonsVisible = false; buttonsVisible = false;
showButtonsContainer = false; showButtonsContainer = false;
isWaitingForResponse = true;
}); });
await _searchService.initializeAuthenticationService(); wishlistId = await _searchService.startPersonalWishlist(message);
await _searchService.startPersonalWishlist(message);
updateChatTitle(_searchService.wishlistId.toString()); updateChatTitle(_searchService.wishlistId.toString());
_scrollToBottom(); _scrollToBottom();
setState(() {
isWaitingForResponse = false;
});
} }
Future<void> _sendMessageToAPI(String message)async { Future<void> _sendMessageToAPI(String message)async {
setState(() { setState(() {
buttonsVisible = false; buttonsVisible = false;
showButtonsContainer = false; showButtonsContainer = false;
isWaitingForResponse = true;
}); });
await _searchService.startPersonalWishlist(message); await _searchService.sendMessages(message);
_scrollToBottom(); _scrollToBottom();
setState(() { setState(() {
messages.add(Message(text: message, isUser: true)); isWaitingForResponse = false;
}); });
} }
void _sendMessage() { void _sendMessage() {
final message = _messageController.text; final message = _messageController.text;
setState(() { setState(() {
messages.add(Message(text: message, isUser: true)); messages.add(Message(text: message, role: "User"));
}); });
if (wishlistId.isEmpty) { if (wishlistId.isEmpty) {
@ -302,12 +343,36 @@ class ChatScreenState extends State<ChatScreen> {
final message = messages[index]; final message = messages[index];
return MessageBubble( return MessageBubble(
message: message.text, message: message.text,
isOutgoing: message.isUser, isOutgoing: message.role == "User",
isProduct: message.isProduct, isProduct: message.isProduct,
); );
}, },
), ),
), ),
if (isWaitingForResponse)
SpinKitFadingCircle(
color: Colors.blue,
size: 25.0,
),
if (messages.any((message) => message.isSuggestion))
Container(
padding: EdgeInsets.all(8.0),
color: Colors.grey[300],
child: Row(
children: [
Icon(Icons.lightbulb),
SizedBox(width: 8.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: messages
.where((message) => message.isSuggestion)
.map((message) => Text(message.text))
.toList(),
),
],
),
),
// Поле введення повідомлень
Container( Container(
margin: const EdgeInsets.all(8.0), margin: const EdgeInsets.all(8.0),
child: Row( child: Row(
@ -316,7 +381,6 @@ class ChatScreenState extends State<ChatScreen> {
child: TextField( child: TextField(
controller: _messageController, controller: _messageController,
onChanged: (text) { onChanged: (text) {
// Коли текст змінюється, оновлюємо стан кнопки
setState(() { setState(() {
isSendButtonEnabled = text.isNotEmpty; isSendButtonEnabled = text.isNotEmpty;
}); });

View File

@ -30,7 +30,7 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_spinkit: ^5.0.0
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.