diff --git a/ShoppingAssistantWebClient.Web/Models/Messages.cs b/ShoppingAssistantWebClient.Web/Models/Messages.cs index 9cbed36..0c7ecf9 100644 --- a/ShoppingAssistantWebClient.Web/Models/Messages.cs +++ b/ShoppingAssistantWebClient.Web/Models/Messages.cs @@ -3,12 +3,16 @@ public class Messages { - public required string Id { get; set; } + public string Id { get; set; } - public required string Text { get; set; } + public string Text { get; set; } - public required string Role { get; set; } + public string Role { get; set; } - public required string CreatedById { get; set; } + public string CreatedById { get; set; } + + } + + } diff --git a/ShoppingAssistantWebClient.Web/Network/ApiClient.cs b/ShoppingAssistantWebClient.Web/Network/ApiClient.cs index d87bb3a..a4a6f24 100644 --- a/ShoppingAssistantWebClient.Web/Network/ApiClient.cs +++ b/ShoppingAssistantWebClient.Web/Network/ApiClient.cs @@ -99,7 +99,7 @@ public class ApiClient public async IAsyncEnumerable GetServerSentEventStreamed(string url, Object obj, CancellationToken cancellationToken) { await SetAuthenticationAsync(); - + var count = 0; // var requestUrl = $"{_httpClient.BaseAddress}{url}"; var response = await _httpClient.PostAsJsonAsync(requestUrl, obj); using var responseStream = await response.Content.ReadAsStreamAsync(); @@ -109,9 +109,14 @@ public class ApiClient while (!cancellationToken.IsCancellationRequested) { var jsonChunk = await reader.ReadLineAsync(cancellationToken); + count += 1; // + if (count >=5 ){ // + break; // + }; // if (jsonChunk == null) continue; if (jsonChunk.StartsWith("event: ")) { + count = 0; // var type = jsonChunk.Substring("event: ".Length); switch(type) { diff --git a/ShoppingAssistantWebClient.Web/Pages/Chat.razor b/ShoppingAssistantWebClient.Web/Pages/Chat.razor index 939c8e8..88b9c82 100644 --- a/ShoppingAssistantWebClient.Web/Pages/Chat.razor +++ b/ShoppingAssistantWebClient.Web/Pages/Chat.razor @@ -1,8 +1,9 @@ @page "/chat/{chatId}" @inject IHttpClientFactory ClientFactory +@inject IJSRuntime JSRuntime -Gift for Jessica +@name
@@ -16,30 +17,35 @@
-
+
@name
-
Gift for Jessica
+
    - @if(!isLoading && Messages!=null){ + @if (!isLoading && Messages != null) + { - @foreach (var item in Messages){ + @foreach (var item in Messages) + { - if(item.Role!="User"){ + if (item.Role != "User") + { -
  • -

    @item.Text

    -
  • +
  • +

    @item.Text

    +
  • - }else{ + } + else + { -
  • -

    @item.Text

    -
  • +
  • +

    @item.Text

    +
  • - } + } } } @@ -52,21 +58,27 @@
    -
    Several possible options
    + @if (Suggestion.Count != 0) + { -
    +
    Several possible options
    -
    - HDMI -
    -
    - VGA -
    -
    - DisplayPort -
    +
    -
    + + + @foreach (var item in Suggestion) + { + +
    + @item +
    + } + + + +
    + }
    @@ -84,8 +96,15 @@ @code { @@ -100,8 +119,21 @@ { if (e.Code == "Enter" || e.Code == "NumpadEnter") { - AddNewMessage(); + AddNewMessage(); } } - + + private ElementReference chatMessageRef; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await JSRuntime.InvokeVoidAsync("scrollToBottom", chatMessageRef); + } + + + private void ClickOption(string item) + { + inputValue = item; + } + } \ No newline at end of file diff --git a/ShoppingAssistantWebClient.Web/Pages/Chat.razor.cs b/ShoppingAssistantWebClient.Web/Pages/Chat.razor.cs index 6fbee2d..d361622 100644 --- a/ShoppingAssistantWebClient.Web/Pages/Chat.razor.cs +++ b/ShoppingAssistantWebClient.Web/Pages/Chat.razor.cs @@ -3,7 +3,9 @@ using ShoppingAssistantWebClient.Web.Models; using GraphQL; using Newtonsoft.Json; using ShoppingAssistantWebClient.Web.Network; - +using ShoppingAssistantWebClient.Web.Models.Input; +using ShoppingAssistantWebClient.Web.Models.Enums; +using System.Text.RegularExpressions; namespace ShoppingAssistantWebClient.Web.Pages; public partial class Chat : ComponentBase @@ -11,10 +13,20 @@ public partial class Chat : ComponentBase [Inject] private ApiClient _apiClient { get; set; } + [Inject] + private NavigationManager Navigation { get; set; } public List Messages { get; set; } + public List Suggestion { get; set; } = new List(); + + public Messages Message { get; set; } + + private CancellationTokenSource cancelTokenSource; + + private MessageCreateDto messageCreateDto; public bool isLoading = true; private string inputValue = ""; + private string name = ""; protected override async Task OnInitializedAsync() { await LoadMessages(); @@ -23,79 +35,135 @@ public partial class Chat : ComponentBase private async Task LoadMessages() { - - - isLoading = true; - int pageNumber = 1; - string wishlistId = chatId; - var request = new GraphQLRequest - { - Query = @"query MessagesPageFromPersonalWishlist($wishlistId: String!, $pageNumber: Int!, $pageSize: Int!) { - messagesPageFromPersonalWishlist( wishlistId: $wishlistId, pageNumber: $pageNumber, pageSize: $pageSize) - { - items { - id - text - role - createdById - } + try{ + string wishlistId = chatId; + + var request = new GraphQLRequest + { + Query = @"query PersonalWishlist( $wishlistId: String!) { + personalWishlist(wishlistId: $wishlistId) { + name } }", - Variables = new - { - wishlistId, - pageNumber, - pageSize = 20 - } - }; - try{ + Variables = new + { + wishlistId, + } + }; + var response = await _apiClient.QueryAsync(request); - var responseData = response.Data; + var responseData = response.Data; + name = responseData.personalWishlist.name; + + + isLoading = true; + int pageNumber = 1; + request = new GraphQLRequest + { + Query = @"query MessagesPageFromPersonalWishlist($wishlistId: String!, $pageNumber: Int!, $pageSize: Int!) { + messagesPageFromPersonalWishlist( wishlistId: $wishlistId, pageNumber: $pageNumber, pageSize: $pageSize) + { + items { + id + text + role + createdById + } + } + }", + + Variables = new + { + wishlistId, + pageNumber, + pageSize = 200 + } + }; + + + + response = await _apiClient.QueryAsync(request); + responseData = response.Data; var jsonCategoriesResponse = JsonConvert.SerializeObject(responseData.messagesPageFromPersonalWishlist.items); this.Messages = JsonConvert.DeserializeObject>(jsonCategoriesResponse); Messages.Reverse(); isLoading = false; - }catch{ - + }catch(Exception ex){ + Console.WriteLine($"Error : {ex.Message}"); } - - } - private async Task AddNewMessage() + private async Task AddNewMessage() + { + try{ + messageCreateDto = new MessageCreateDto { Text = inputValue };; + Message = new Messages(); + Message.Text = inputValue; + Message.Role = "User"; + Message.Id = ""; + Message.CreatedById = ""; + inputValue = ""; + Suggestion = new List(); + Messages.Add(Message); + StateHasChanged(); + + cancelTokenSource = new CancellationTokenSource(); + var cancellationToken = cancelTokenSource.Token; + + var serverSentEvent = _apiClient.GetServerSentEventStreamed($"ProductsSearch/search/{chatId}", messageCreateDto, cancellationToken); + bool first = true; + + await foreach (var sseEvent in serverSentEvent.WithCancellation(cancellationToken)) { + Console.WriteLine($"Received SSE Event: {sseEvent.Event}, Data: {sseEvent.Data}"); - isLoading = true; - var pageNumber = 1; - var wishlistId = chatId; - var text = inputValue; - inputValue=""; - var request = new GraphQLRequest + + if(sseEvent.Event == SearchEventType.Message){ + + string input = sseEvent.Data; + Regex regex = new Regex("\"(.*?)\""); + Match match = regex.Match(input); + string result = match.Groups[1].Value; + + + + Message = new Messages(); + Message.Text = result; + Message.Role = "bot"; + Message.Id = ""; + Message.CreatedById = ""; + + if (first) { - Query = @"mutation AddMessageToPersonalWishlist($wishlistId: String!, $text: String!) { - addMessageToPersonalWishlist(wishlistId: $wishlistId, dto: { text: $text }) { - id - text - role - createdById - } - } - ", + Messages.Add(Message); + first = false; + } + else + { + var lengt = Messages.Count(); + Messages[lengt-1].Text += Message.Text; + } - Variables = new - { - wishlistId, - text - } - }; + StateHasChanged(); + + }else if(sseEvent.Event == SearchEventType.Product){ + + var url = $"/chat/{chatId}/product"; + Navigation.NavigateTo(url); + + }else if(sseEvent.Event == SearchEventType.Suggestion){ + + Suggestion.Add(sseEvent.Data); + } - var response = await _apiClient.QueryAsync(request); - await LoadMessages(); } + }catch(Exception ex){ + Console.WriteLine($"Error : {ex.Message}"); + } - + } } diff --git a/ShoppingAssistantWebClient.Web/Pages/Chat.razor.css b/ShoppingAssistantWebClient.Web/Pages/Chat.razor.css index 495038c..5596580 100644 --- a/ShoppingAssistantWebClient.Web/Pages/Chat.razor.css +++ b/ShoppingAssistantWebClient.Web/Pages/Chat.razor.css @@ -56,7 +56,6 @@ } .possible_options { - visibility: hidden; position: absolute; bottom: 5.5em; margin-left: 25%; @@ -84,6 +83,7 @@ margin: 0em 0.6em; flex: 1; text-align: center; + cursor: pointer; } .input_messages { @@ -117,7 +117,7 @@ .chat_message { position: relative; overflow-y: auto; - height: calc(100% - 5.5em); + height: calc(100% - 8em); width: 100%; } .chat_message::-webkit-scrollbar { diff --git a/ShoppingAssistantWebClient.Web/Pages/Index.razor b/ShoppingAssistantWebClient.Web/Pages/Index.razor index 59320ca..a3db7bb 100644 --- a/ShoppingAssistantWebClient.Web/Pages/Index.razor +++ b/ShoppingAssistantWebClient.Web/Pages/Index.razor @@ -1,5 +1,4 @@ @page "/" - New Chat
    @@ -12,8 +11,9 @@
    +@if(isLoading == false){ -
    +
    New chat
    What you're looking for
    @@ -59,6 +59,17 @@
    +}else{ + +
    + + Loading chat + +
    + +} + +
    + + + @code { + [Parameter] public string chatId { get; set; } + + + private string selectedWishlistId; + + + private static NavMenu _app; + + public NavMenu () + { + _app = this; + + } + + [JSInvokable] + public static void Update(string wishlistId) + { + _app.LoadMenus(1,200); + _app.selectedWishlistId=wishlistId; + } + private void RedirectToPage(string itemId) { - var url = $"/chat/{itemId}"; - Navigation.NavigateTo(url); - } + _app.selectedWishlistId = itemId; + var url = $"/chat/{itemId}"; + Navigation.NavigateTo(url); + } private void RedirectToNewChat() { - var url = $"/"; - Navigation.NavigateTo(url); - } + var url = $"/"; + Navigation.NavigateTo(url); + } private void RedirectToCard(string itemId) { - var url = $"/chat/{itemId}/cart"; - Navigation.NavigateTo(url); - } + var url = $"/chat/{itemId}/cart"; + Navigation.NavigateTo(url); + } private async void DeleteWishlist(string itemId) { await DeleteWish(itemId); - - } - public void UpdateSideMenu() - { - StateHasChanged(); } +/* + private int savedScrollTop = 0; + + private ElementReference wishlishtRef; + + private async Task OnScroll() + { + var scrollTop = await JSRuntime.InvokeAsync("getScrollTop", wishlishtRef); + var offsetHeight = await JSRuntime.InvokeAsync("getOffsetHeight", wishlishtRef); + var scrollHeight = await JSRuntime.InvokeAsync("getScrollHeight", wishlishtRef); + + if (scrollTop + offsetHeight > scrollHeight - 100) + { + savedScrollTop = scrollTop; + currentPage++; + await LoadMenus(currentPage, pageSize); + await InvokeAsync(() => JSRuntime.InvokeVoidAsync("setScrollTop", wishlishtRef, savedScrollTop)); + } + } +*/ + + } diff --git a/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor.cs b/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor.cs index b4ca1e9..dc36e61 100644 --- a/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor.cs +++ b/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor.cs @@ -13,58 +13,81 @@ namespace ShoppingAssistantWebClient.Web.Shared private ApiClient _apiClient { get; set; } public List Wishlists { get; set; } public bool isLoading = true; + + public int pageSize { get; set; } + public int currentPage { get; set; } + protected override async Task OnInitializedAsync() { - await LoadMenus(); + pageSize = 200; + currentPage = 1; + Wishlists = new List(); + await LoadMenus(currentPage, pageSize); + } - private async Task LoadMenus() + public async Task LoadMenus(int pageNumber, int pageSize ) { - isLoading = true; - var pageNumber = 1; - var request = new GraphQLRequest - { - Query = @"query PersonalWishlistsPage( $pageNumber: Int!, $pageSize: Int!) { - personalWishlistsPage(pageNumber: $pageNumber, pageSize: $pageSize) { - items { - id - name - } - } - }", - - Variables = new + try{ + isLoading = true; + var request = new GraphQLRequest { - pageNumber, - pageSize = 10, - } - }; + Query = @"query PersonalWishlistsPage( $pageNumber: Int!, $pageSize: Int!) { + personalWishlistsPage(pageNumber: $pageNumber, pageSize: $pageSize) { + items { + id + name + } + } + }", - var response = await _apiClient.QueryAsync(request); - var responseData = response.Data; - var jsonCategoriesResponse = JsonConvert.SerializeObject(responseData.personalWishlistsPage.items); - this.Wishlists = JsonConvert.DeserializeObject>(jsonCategoriesResponse); - isLoading = false; + Variables = new + { + pageNumber, + pageSize, + } + }; + + var response = await _apiClient.QueryAsync(request); + var responseData = response.Data; + var jsonCategoriesResponse = JsonConvert.SerializeObject(responseData.personalWishlistsPage.items); + this.Wishlists = JsonConvert.DeserializeObject>(jsonCategoriesResponse); + Wishlists.Reverse(); + isLoading = false; + StateHasChanged(); + + }catch(Exception ex){ + Console.WriteLine($"Error : {ex.Message}"); + } + } - + protected async Task DeleteWish(string wishlistId) { - var request = new GraphQLRequest - { - Query = @"mutation DeletePersonalWishlist($wishlistId: String!) { - deletePersonalWishlist(wishlistId: $wishlistId) { - id - } - } - ", - - Variables = new + try{ + var request = new GraphQLRequest { - wishlistId - } - }; + Query = @"mutation DeletePersonalWishlist($wishlistId: String!) { + deletePersonalWishlist(wishlistId: $wishlistId) { + id + } + } + ", - var response = await _apiClient.QueryAsync(request); - await LoadMenus(); + Variables = new + { + wishlistId + } + }; + + var response = await _apiClient.QueryAsync(request); + await LoadMenus(currentPage, pageSize); + var url = $"/"; + Navigation.NavigateTo(url); + + }catch(Exception ex){ + Console.WriteLine($"Error : {ex.Message}"); + } + } } diff --git a/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor.css b/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor.css index 091f52e..bc5c52c 100644 --- a/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor.css +++ b/ShoppingAssistantWebClient.Web/Shared/NavMenu.razor.css @@ -1,10 +1,10 @@ - .left_frame { position: relative; height: 100%; width: 100%; transition: 1s; } + .logo { height: 5em; position: relative; @@ -12,26 +12,35 @@ align-items: center; padding-bottom: 1.5%; } + .menu { position: absolute; width: 100%; - top: 5em; + top: 5em; bottom: 0; border: 1.5px solid; border-color: #0052CC; border-radius: 0.6em; padding-top: 1.5%; } + .elements_wishlisht { - position: absolute; - overflow-y: auto; top: 2.43em; width: 100%; - bottom: 4em; + bottom: 3.8em; padding-left: 1.25em; padding-right: 1.25em; + padding-bottom: 3.8em; } + +.wishlist_names { + overflow-y: auto; + position: relative; + width: 100%; + height:100%; +} + .info_user { position: absolute; display: flex; @@ -46,6 +55,7 @@ padding-right: 1.25em; border-top: 1px solid #0165FF; } + .logo_name { padding-top: 0.5em; padding-left: 0.3em; @@ -56,11 +66,13 @@ font-weight: 600; overflow: hidden; } + .logo img { float: left; height: 3.25em; width: 3.25em; } + .wishlist_name { padding-left: 0.7em; padding-right: 5em; @@ -68,10 +80,11 @@ white-space: nowrap; overflow: hidden; position: absolute; - height: 2.5em; + height: 2.5em; line-height: 2.5em; cursor: pointer; } + .cont_wishlist { margin-top: 0.4375em; margin-bottom: 0.4375em; @@ -79,23 +92,64 @@ border-radius: 0.6em; font-size: 1.1em; width: 100%; - height:2.5em; + height: 2.5em; } + .cont_wishlist:hover { + background-color: #e6e6e6; transition: 0.2s; } -.cont_wishlist:hover .button_delete_chat{ + +.cont_wishlist:hover .button_delete_chat { visibility: visible; } -.cont_wishlist:hover .button_open_card{ + +.cont_wishlist:hover .button_open_card { visibility: visible; - + } + +.selected_wishlist { + margin-top: 0.4375em; + margin-bottom: 0.4375em; + color: black; + border-radius: 0.6em; + font-size: 1.1em; + width: 100%; + height: 2.5em; + background-color: #e6e6e6; + transition: 0.2s; + +} + +.sel_del { + visibility: visible; + cursor: pointer; + margin-top: 0.55em; + margin-right: 1em; + float: right; + position: relative; + + +} + +.selected_card{ + cursor: pointer; + position: relative; + z-index: 999; + margin-top: 0.55em; + margin-right: 1em; + float: right; + visibility: visible; + height: 1.4em; + width: 1.5em; +} + .button_delete_chat { cursor: pointer; - margin-top: 0.55em; + margin-top: 0.55em; margin-right: 1em; float: right; position: relative; @@ -103,6 +157,7 @@ height: 1.4em; width: 1.4em; } + .button_open_card { cursor: pointer; position: relative; @@ -114,14 +169,17 @@ height: 1.4em; width: 1.5em; } -.elements_wishlisht::-webkit-scrollbar { + +.wishlist_names::-webkit-scrollbar { border-radius: 20px; width: 0.2em; } -.elements_wishlisht::-webkit-scrollbar-thumb { - background-color: #0052CC; /* Колір позиції покажчика */ - border-radius: 10px; /* Закруглення країв позиції покажчика */ +.wishlist_names::-webkit-scrollbar-thumb { + background-color: #0052CC; + /* Колір позиції покажчика */ + border-radius: 10px; + /* Закруглення країв позиції покажчика */ width: 0.2em; } @@ -131,10 +189,11 @@ border-radius: 0.6em; font-size: 1.2em; width: 100%; - height:2.5em; + height: 2.5em; cursor: pointer; line-height: 2.5em; } + .add_chat div { justify-content: center; align-items: center; @@ -143,11 +202,12 @@ text-decoration: none; color: #FFFFFF; } + .plus { position: absolute; right: 1em; top: -0.1em; - font-size: 1.9em; + font-size: 1.9em; } .user_name { @@ -156,12 +216,14 @@ justify-content: center; align-items: center; } + .info_user img { float: left; border-radius: 50%; width: 2.3em; height: 2.3em; } + .button_close_menu { position: relative; width: 1.43em; @@ -171,6 +233,7 @@ margin-top: 0.5em; margin-right: 1.30em; } + .button_close_menu span { width: 20px; height: 1.5px; @@ -180,9 +243,11 @@ transform: translate(-50%, -50%); background-color: #4E4E4E; } + .button_close_menu span:nth-of-type(2) { top: calc(50% - 5px); } + .button_close_menu span:nth-of-type(3) { top: calc(50% + 5px); } \ No newline at end of file diff --git a/ShoppingAssistantWebClient.Web/wwwroot/images/loading.svg b/ShoppingAssistantWebClient.Web/wwwroot/images/loading.svg new file mode 100644 index 0000000..4a32def --- /dev/null +++ b/ShoppingAssistantWebClient.Web/wwwroot/images/loading.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + +