diff --git a/lib/main.dart b/lib/main.dart index 1deb065..8add828 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,10 +2,12 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:graphql/client.dart'; import 'package:shopping_assistant_mobile_client/network/api_client.dart'; +import 'package:shopping_assistant_mobile_client/screens/chat.dart'; void main() { runApp(const MyApp()); } +final ApiClient client = ApiClient(); class MyApp extends StatelessWidget { const MyApp({super.key}); @@ -34,7 +36,7 @@ class MyApp extends StatelessWidget { colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), + home: ChatScreen(), ); } } @@ -80,13 +82,13 @@ class _MyHomePageState extends State { '''; MutationOptions mutationOptions = MutationOptions( - document: gql(startPersonalWishlistMutations), - variables: const { - 'dto': { - 'firstMessageText': 'Gaming mechanical keyboard', - 'type': 'Product' - }, - } + document: gql(startPersonalWishlistMutations), + variables: const { + 'dto': { + 'firstMessageText': 'Gaming mechanical keyboard', + 'type': 'Product' + }, + } ); var result = await client.mutate(mutationOptions); @@ -155,4 +157,4 @@ class _MyHomePageState extends State { ), // This trailing comma makes auto-formatting nicer for build methods. ); } -} +} \ No newline at end of file diff --git a/lib/screens/chat.dart b/lib/screens/chat.dart new file mode 100644 index 0000000..595d208 --- /dev/null +++ b/lib/screens/chat.dart @@ -0,0 +1,253 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:shopping_assistant_mobile_client/network/api_client.dart'; + +const String startPersonalWishlistMutations = r''' + mutation startPersonalWishlist($dto: WishlistCreateDtoInput!) { + startPersonalWishlist(dto: $dto) { + createdById, id, name, type + } + } +'''; + +const String sendMessageMutation = r''' + mutation sendMessage($wishlistId: ID!, $message: String!) { + sendMessage(wishlistId: $wishlistId, message: $message) { + // Опис того, що ви очікуєте від відповіді + } + } +'''; + +final ApiClient client = ApiClient(); + +class ChatScreen extends StatefulWidget { + @override + State createState() => ChatScreenState(); +} + +class MessageBubble extends StatelessWidget { + final String message; + + MessageBubble({required this.message}); + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.centerRight, + child: Container( + margin: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(10.0), + ), + child: Text( + message, + style: TextStyle(color: Colors.white), + ), + ), + ); + } +} + +class ChatScreenState extends State { + + final TextEditingController _messageController = TextEditingController(); + List messages = []; + bool buttonsVisible = true; + final ScrollController _scrollController = ScrollController(); + + + String wishlistId = ''; + + // Функція для старту першої вішлісту при відправці першого повідомлення + Future _startPersonalWishlist() async { + final options = MutationOptions( + document: gql(startPersonalWishlistMutations), + variables: { + 'dto': {'firstMessageText': messages.first, 'type': 'Product'}, + }, + ); + + final result = await client.mutate(options); + + if (result != null && result.containsKey('startPersonalWishlist')) { + setState(() { + wishlistId = result['startPersonalWishlist']['id']; + }); + } + } + + // Функція для відправки повідомлення до API + Future _sendMessageToAPI(String message) async { + final options = MutationOptions( + document: gql(sendMessageMutation), + variables: {'wishlistId': wishlistId, 'message': message}, + ); + + final result = await client.mutate(options); + + // Обробка результатів відправки повідомлення + if (result != null && result.containsKey('sendMessage')) { + // Отримання та обробка відповідей з GPT-4 + var sseStream = client.getServerSentEventStream( + 'api/productssearch/search/$wishlistId', + {'text': message}, + ); + + await for (var chunk in sseStream) { + print('${chunk.event}: ${chunk.data}'); + // Оновлення UI або збереження результатів, якщо необхідно + } + } + } + + // Функція для відправки повідомлення + void _sendMessage() { + String message = _messageController.text; + setState(() { + messages.insert(0, message); + }); + + if (wishlistId.isEmpty) { + // Якщо вішліст не створено, стартуємо його + _startPersonalWishlist().then((_) { + // Після створення вішлісту, відправляємо перше повідомлення до API + _sendMessageToAPI(message); + }); + } else { + // Якщо вішліст вже існує, відправляємо повідомлення до API + _sendMessageToAPI(message); + } + + _messageController.clear(); + _scrollController.animateTo( + 0.0, + duration: Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + } + + + void _showGiftNotAvailable() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Gift Functionality'), + content: Text('This function is currently unavailable.'), + actions: [ + TextButton( + child: Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('New Chat'), + centerTitle: true, // Відцентрувати заголовок + leading: IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () { + // Обробник для кнопки "Назад" + print('Back button pressed'); + }, + ), + ), + body: Column( + children: [ + Visibility( + visible: buttonsVisible, + child: Align( + alignment: Alignment.topCenter, + child: Column( + children: [ + Text( + 'Choose an Option', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () { + // Обробник для кнопки "Product" + print('Product button pressed'); + }, + style: ElevatedButton.styleFrom( + padding: EdgeInsets.symmetric(horizontal: 30, vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), // Закруглення країв + ), + primary: Colors.blue, // Колір кнопки + onPrimary: Colors.white, // Колір тексту на активній кнопці + ), + child: Text('Product'), + ), + SizedBox(width: 16.0), // Простір між кнопками + ElevatedButton( + onPressed: _showGiftNotAvailable, + style: ElevatedButton.styleFrom( + padding: EdgeInsets.symmetric(horizontal: 30, vertical: 16), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), // Закруглення країв + ), + primary: Colors.white, // Колір кнопки "Gift" + onPrimary: Colors.black, // Колір тексту на активній кнопці + ), + child: Text('Gift'), + ), + ], + ), + ], + ), + ), + ), + Expanded( + child: ListView( + controller: _scrollController, + reverse: true, // Щоб список був у зворотньому порядку + children: [ + // Повідомлення користувача + for (var message in messages) + MessageBubble( + message: message, + ), + ], + ), + ), + Container( + margin: const EdgeInsets.all(8.0), + child: Row( + children: [ + Expanded( + child: TextField( + controller: _messageController, + decoration: InputDecoration( + hintText: 'Enter your message...', + ), + ), + ), + IconButton( + icon: Icon(Icons.send), + onPressed: _sendMessage, + ), + ], + ), + ), + ], + ), + ); + } + } \ No newline at end of file