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;
AuthenticationService() {
SharedPreferences.getInstance().then((result) => {prefs = result});
}
Future<String> getAccessToken() async {
prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString('accessToken');
var refreshToken = prefs.getString('refreshToken');

View File

@ -1,13 +1,12 @@
// search_service.dart
import 'dart:async';
import 'package:graphql_flutter/graphql_flutter.dart';
import '../models/enums/search_event_type.dart';
import '../models/server_sent_event.dart';
import '../network/api_client.dart';
import '../network/authentication_service.dart';
import '../screens/chat.dart';
import 'authentication_service.dart';
const String startPersonalWishlistMutations = r'''
mutation startPersonalWishlist($dto: WishlistCreateDtoInput!) {
@ -20,18 +19,17 @@ const String startPersonalWishlistMutations = r'''
SearchEventType type = SearchEventType.message;
class SearchService {
final AuthenticationService _authenticationService = AuthenticationService();
final ApiClient client = ApiClient();
final _sseController = StreamController<ServerSentEvent>();
late final _sseController = StreamController<ServerSentEvent>();
Stream<ServerSentEvent> get sseStream => _sseController.stream;
Future<void> initializeAuthenticationService() async {
await _authenticationService.initialize();
bool checkerForProduct() {
return type == SearchEventType.product;
}
bool checkerForProduct() {
bool checkerForSuggestion() {
return type == SearchEventType.product;
}
@ -60,8 +58,7 @@ class SearchService {
return null;
}
Future<void> startPersonalWishlist(String message) async {
await _authenticationService.initialize();
Future<String> startPersonalWishlist(String message) async {
// Перевіряємо, чи вже створений wishlist
if (wishlistId == null) {
@ -88,18 +85,76 @@ class SearchService {
await for (final chunk in sseStream) {
print("Original chunk.data: ${chunk.event}");
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);
_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
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:shopping_assistant_mobile_client/network/search_service.dart';
class Message {
final String text;
final bool isUser;
final String role;
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 {
@ -71,6 +72,7 @@ class ChatScreenState extends State<ChatScreen> {
bool buttonsVisible = true;
bool isSendButtonEnabled = false;
bool showButtonsContainer = true;
bool isWaitingForResponse = false;
final ScrollController _scrollController = ScrollController();
late Widget appBarTitle;
@ -82,16 +84,48 @@ class ChatScreenState extends State<ChatScreen> {
_searchService.sseStream.listen((event) {
_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) {
setState(() {
isWaitingForResponse = true;
final lastMessage = messages.isNotEmpty ? messages.last : null;
message.isProduct = _searchService.checkerForProduct();
message.isSuggestion = _searchService.checkerForSuggestion();
print("Product status: ${message.isProduct}");
if (lastMessage != null && !lastMessage.isUser && !message.isUser) {
if (lastMessage != null && lastMessage.role != "User" && message.role != "User") {
final updatedMessage = Message(
text: "${lastMessage.text}${message.text}",
role: "Application",
isProduct: message.isProduct);
messages.removeLast();
messages.add(updatedMessage);
@ -99,6 +133,9 @@ class ChatScreenState extends State<ChatScreen> {
messages.add(message);
}
});
setState(() {
isWaitingForResponse = false;
});
_scrollToBottom();
}
@ -106,7 +143,6 @@ class ChatScreenState extends State<ChatScreen> {
final wishlistName = await _searchService.generateNameForPersonalWishlist(wishlistId);
if (wishlistName != null) {
setState(() {
// Оновіть назву чату з результатом методу generateNameForPersonalWishlist
appBarTitle = Text(wishlistName);
});
}
@ -116,30 +152,35 @@ class ChatScreenState extends State<ChatScreen> {
setState(() {
buttonsVisible = false;
showButtonsContainer = false;
isWaitingForResponse = true;
});
await _searchService.initializeAuthenticationService();
await _searchService.startPersonalWishlist(message);
wishlistId = await _searchService.startPersonalWishlist(message);
updateChatTitle(_searchService.wishlistId.toString());
_scrollToBottom();
setState(() {
isWaitingForResponse = false;
});
}
Future<void> _sendMessageToAPI(String message) async {
Future<void> _sendMessageToAPI(String message)async {
setState(() {
buttonsVisible = false;
showButtonsContainer = false;
isWaitingForResponse = true;
});
await _searchService.startPersonalWishlist(message);
await _searchService.sendMessages(message);
_scrollToBottom();
setState(() {
messages.add(Message(text: message, isUser: true));
isWaitingForResponse = false;
});
}
void _sendMessage() {
final message = _messageController.text;
setState(() {
messages.add(Message(text: message, isUser: true));
messages.add(Message(text: message, role: "User"));
});
if (wishlistId.isEmpty) {
@ -302,12 +343,36 @@ class ChatScreenState extends State<ChatScreen> {
final message = messages[index];
return MessageBubble(
message: message.text,
isOutgoing: message.isUser,
isOutgoing: message.role == "User",
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(
margin: const EdgeInsets.all(8.0),
child: Row(
@ -316,7 +381,6 @@ class ChatScreenState extends State<ChatScreen> {
child: TextField(
controller: _messageController,
onChanged: (text) {
// Коли текст змінюється, оновлюємо стан кнопки
setState(() {
isSendButtonEnabled = text.isNotEmpty;
});

View File

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