@page "/UserCP/{CompanyID:int}" @using Common.Dtos.Company @using Common.Dtos.Conversation @using Common.Dtos.Group @using HushianWebApp.Service @using HushianWebApp.Services @using Microsoft.AspNetCore.SignalR.Client; @using System.Threading; به هوشیان خوش آمدید @if (CompanyInfo != null && !string.IsNullOrEmpty(CompanyInfo?.FullName)) { گفتگو با @CompanyInfo?.FullName } @implements IAsyncDisposable @inject NavigationManager NavigationManager @inject ChatService ChatService @inject ILocalStorageService localStorageService; @inject AuthService authService; @inject BaseController baseController; @inject CompanyService companyService @inject UserService userService @inject GroupService groupService @inject ChatService chatService @inject IJSRuntime JS @inject ToastService toastService @inject HttpClient _Http; @layout UserPanelLayout
@if (IsEndFirstProcess) { if (string.IsNullOrEmpty(Error)) {
@if (Chat != null) {

@ExperYou

}
@if (Chat != null) { }
@if (Chat != null && Chat.Responses != null) {
@{ bool target = false; } @foreach (var msg in Chat?.Responses) { @if (!target && ((!msg.IsRead && msg.Type != Common.Enums.ConversationType.UE) || Chat.Responses.Last() == msg)) { target = true;
@if (!msg.IsRead && msg.Type != Common.Enums.ConversationType.UE) {
پیام جدید
}
}
@if (msg.FileContent != null && msg.FileContent.Length > 0 && !string.IsNullOrWhiteSpace(msg.FileType)) { @if (msg.FileType.StartsWith("image/")) { image } else if (msg.FileType.StartsWith("audio/")) {
} @if (!string.IsNullOrWhiteSpace(msg.text)) {
@msg.text
} } else { @msg.text }
@if (msg.Type == Common.Enums.ConversationType.UE) { if (msg.IsRead) { } else { } }
} @{ target = false; }
} else {

هوشیان

@if (CompanyGroups != null && CompanyGroups.Any(w => w.Available)) {
انتخاب گروه:
@foreach (var group in CompanyGroups.Where(w => w.Available)) {
@if (group.img == null || group.img.Length == 0) { } else { Uploaded Image } @group.Name
}
}
}
@if (CompanyInfo != null && (Chat == null || (Chat != null && Chat.status != Common.Enums.ConversationStatus.Finished && Chat.Responses != null))) {
@if (SelectedImagePreview != null) {
preview
} @if (RecordedAudioBytes != null) {
@RecordedAudioDuration
} @if (IsRecording) {
در حال ضبط... @RecordingTime
}
@if (Chat != null && Chat.status == Common.Enums.ConversationStatus.InProgress) { } } @if (Chat?.status == Common.Enums.ConversationStatus.Finished) {

این گفتگو به پایان رسیده

}
} else {
@Error
} } else {

در حال بررسی وضعیت ...

}
@code { #region fild private bool IsEndFirstProcess { get; set; } = false; private bool _shouldObserveVisibility = false; string MsgInput = string.Empty; bool chatloading = false; string ExperYou { get { if (CompanyInfo == null) return string.Empty; string value = $"{CompanyInfo.FullName}"; if (GroupID.HasValue) { value += "/" + CompanyGroups.FirstOrDefault(f => f.ID == GroupID.GetValueOrDefault()).Name; } if (Chat != null && Chat.Responses != null) { var model = Chat.Responses.OrderBy(o => o.ID).LastOrDefault(l => l.Type != Common.Enums.ConversationType.UE); if (model != null && model.Type == Common.Enums.ConversationType.CU && !string.IsNullOrEmpty(CompanyInfo.FullNameManager)) { value += "/" + CompanyInfo.FullNameManager; } else if (!string.IsNullOrEmpty(Chat.ExperFullName)) value += "/" + Chat.ExperFullName; } return value; } } string Error = ""; #endregion #region Image IBrowserFile? SelectedImageFile = null; byte[]? SelectedImageBytes = null; string? SelectedImagePreview = null; #endregion #region Audio recording properties bool IsRecording = false; string RecordingTime = "00:00"; byte[]? RecordedAudioBytes = null; string? RecordedAudioUrl = null; string RecordedAudioDuration = "00:00"; private System.Threading.Timer? recordingTimer; private DateTime recordingStartTime; #endregion #region Parameter [Parameter] public int CompanyID { get; set; } [SupplyParameterFromQuery(Name = "ChatID")] private int? ChatID { get; set; } [SupplyParameterFromQuery(Name = "GroupID")] private int? GroupID { get; set; } #endregion ConfirmDialog dialog = default!; HubConnection? hubConnection; ReadANDUpdate_CompanyDto? CompanyInfo = null; Common.Dtos.CurrentUserInfo CurrentUser = new(); List CompanyGroups = new(); ChatItemDto? Chat = null; } @functions{ #region Audio recording methods private async Task ToggleAudioRecording() { if (IsRecording) { await StopAudioRecording(); } else { await StartAudioRecording(); } } private async Task StartAudioRecording() { try { var result = await JS.InvokeAsync("startAudioRecording"); if (result) { IsRecording = true; recordingStartTime = DateTime.Now; recordingTimer = new System.Threading.Timer(UpdateRecordingTime, null, 0, 1000); StateHasChanged(); } else { toastService.Notify(new ToastMessage(ToastType.Warning, "خطا در شروع ضبط صدا")); } } catch (Exception ex) { toastService.Notify(new ToastMessage(ToastType.Danger, $"خطا در ضبط صدا: {ex.Message}")); } } private async Task StopAudioRecording() { try { var audioData = await JS.InvokeAsync("stopAudioRecording"); if (!string.IsNullOrEmpty(audioData)) { // Convert base64 to bytes var base64Data = audioData.Split(',')[1]; RecordedAudioBytes = Convert.FromBase64String(base64Data); RecordedAudioUrl = audioData; RecordedAudioDuration = RecordingTime; IsRecording = false; recordingTimer?.Dispose(); recordingTimer = null; await ClearSelectedImage(); StateHasChanged(); } } catch (Exception ex) { toastService.Notify(new ToastMessage(ToastType.Danger, $"خطا در توقف ضبط صدا: {ex.Message}")); } finally { IsRecording = false; recordingTimer?.Dispose(); recordingTimer = null; StateHasChanged(); } } private void UpdateRecordingTime(object? state) { var elapsed = DateTime.Now - recordingStartTime; RecordingTime = $"{elapsed.Minutes:D2}:{elapsed.Seconds:D2}"; InvokeAsync(StateHasChanged); } private Task ClearRecordedAudio() { RecordedAudioBytes = null; RecordedAudioUrl = null; RecordedAudioDuration = "00:00"; return Task.CompletedTask; } private string GetAudioDataUrl(string? fileType, byte[]? content) => (string.IsNullOrWhiteSpace(fileType) || content == null || content.Length == 0) ? string.Empty : $"data:{fileType};base64,{Convert.ToBase64String(content)}"; #endregion } @functions { #region Image private async Task OpenFileDialog() { await JS.InvokeVoidAsync("triggerClick", "chatImageInput"); } private async Task OnImageSelected(InputFileChangeEventArgs e) { var file = e.File; if (file is null) { SelectedImageFile = null; SelectedImageBytes = null; SelectedImagePreview = null; return; } SelectedImageFile = file; using var memoryStream = new MemoryStream(); await file.OpenReadStream().CopyToAsync(memoryStream); SelectedImageBytes = memoryStream.ToArray(); SelectedImagePreview = $"data:{file.ContentType};base64,{Convert.ToBase64String(SelectedImageBytes)}"; await ClearRecordedAudio(); } private Task ClearSelectedImage() { SelectedImageFile = null; SelectedImageBytes = null; SelectedImagePreview = null; return Task.CompletedTask; } private static string GetImageDataUrl(string? fileType, byte[]? content) => (string.IsNullOrWhiteSpace(fileType) || content == null || content.Length == 0) ? string.Empty : $"data:{fileType};base64,{Convert.ToBase64String(content)}"; private static string GetDownloadFileName(string? fileName, string? fileType) { if (!string.IsNullOrWhiteSpace(fileName)) return fileName; var ext = ""; if (!string.IsNullOrWhiteSpace(fileType) && fileType.StartsWith("image/")) { ext = "." + fileType.Split('/').Last(); } return $"image_{DateTimeOffset.Now.ToUnixTimeSeconds()}{ext}"; } private string GetImageSource(byte[] bytes) => $"data:image/jpeg;base64,{Convert.ToBase64String(bytes)}"; #endregion } @functions { protected override async Task OnAfterRenderAsync(bool firstRender) { if (_shouldObserveVisibility) { _shouldObserveVisibility = false; await JS.InvokeVoidAsync("observeVisibility", DotNetObjectReference.Create(this)); } } protected override async Task OnInitializedAsync() { Error = string.Empty; IsEndFirstProcess = false; if (await CheckLogin()) { await GetCurrentUser(); if (await ExsistCompany()) { await GetGroups(); if (ChatID.HasValue) await GetChatByID(); else if (GroupID.HasValue && !await ExsistGroup()) Error = $"گروه با شناسه {GroupID.Value} یافت برای شرکت {CompanyInfo.FullName} یافت نشد"; else await GetLastChat(); } else Error = $"شرکت با شناسه {CompanyID} یافت نشد"; } else await Login(); if (Chat != null) { GroupID = Chat.GroupID; _shouldObserveVisibility = true; StateHasChanged(); await Task.Delay(200); await JS.InvokeVoidAsync("scrollToTargetOrBottom"); } if (string.IsNullOrEmpty(Error)) { await ConectedToHub(); } IsEndFirstProcess = true; await base.OnInitializedAsync(); } public async ValueTask DisposeAsync() { if (hubConnection is not null) { await hubConnection.StopAsync(); await hubConnection.DisposeAsync(); } } } @functions { //Login async Task CheckLogin() { var token = await localStorageService.GetItem("U/key"); if (string.IsNullOrEmpty(token)) return false; else { await baseController.RemoveToken(); await baseController.SetToken(token); if (!await authService.IsOnline()) { await baseController.RemoveToken(); return false; } else return true; } } async Task Login() { await localStorageService.RemoveItem("CompanyID"); await localStorageService.RemoveItem("ChatID"); await localStorageService.RemoveItem("GroupID"); await localStorageService.SetItem("CompanyID", CompanyID); if (ChatID.HasValue) await localStorageService.SetItem("ChatID", ChatID.Value); if (GroupID.HasValue) await localStorageService.SetItem("GroupID",GroupID.Value); NavigationManager.NavigateTo("UserPanelLogin"); } async Task GetCurrentUser() { CurrentUser = await userService.GetCurrentUserInfo(); } async Task ExsistCompany() { CompanyInfo = await companyService.GetCompany(CompanyID); return CompanyInfo != null; } async Task GetGroups() { CompanyGroups = await groupService.GetGroupsCompany(CompanyID); } async Task ExsistGroup() { if (GroupID.HasValue) return (CompanyGroups.Count > 0 && CompanyGroups.Any(a => a.ID == GroupID.Value)); else return true; } async Task GetChatByID() { if (ChatID.HasValue) Chat = await ChatService.Getchat(ChatID.Value); if (Chat == null) { Error = $"گفتگو با شناسه {ChatID.Value} یافت با شرکت {CompanyInfo.FullName} یافت نشد"; } } async Task GetLastChat() { Chat = await ChatService.GetLastOpenChatInCompany(CompanyID); } async Task OnClickSendMsg() { if (!string.IsNullOrEmpty(MsgInput) || SelectedImageFile != null || RecordedAudioBytes != null) { if (Chat != null) { Common.Enums.ConversationType type = Common.Enums.ConversationType.UE; ChatItemResponseDto? model; if (SelectedImageFile != null) { var bytes = SelectedImageBytes ?? Array.Empty(); model = await chatService.ADDChatResponse( Chat.ID, MsgInput, type, SelectedImageFile.Name, SelectedImageFile.ContentType, bytes); } else if (RecordedAudioBytes != null) { // Send audio message var fileName = $"audio_{DateTimeOffset.Now.ToUnixTimeSeconds()}.wav"; model = await chatService.ADDChatResponse( Chat.ID, MsgInput, type, fileName, "audio/wav", RecordedAudioBytes); } else { model = await chatService.ADDChatResponse(Chat.ID, MsgInput, type); } Chat?.Responses.Add(model); Chat.LastText = MsgInput; } else { //TODO New Chat var model = await chatService.NewChatFromCurrentUser(new ADD_ConversationDto() { CompanyID = CompanyID, GroupID = GroupID, Question = MsgInput, UserID = 0 }); if (model != null) { Chat = model; } else toastService.Notify(new ToastMessage(ToastType.Danger, "خطا در گفتگو جدید")); } await Task.Yield(); // Scroll to bottom for user's own messages await JS.InvokeVoidAsync("scrollToBottom", "B1"); MsgInput = string.Empty; SelectedImageFile = null; SelectedImageBytes = null; SelectedImagePreview = null; // Clear recorded audio after sending RecordedAudioBytes = null; RecordedAudioUrl = null; RecordedAudioDuration = "00:00"; } } async Task ConectedToHub() { //-------------hub var token = await localStorageService.GetItem("U/key"); string AddressHub = _Http.BaseAddress.AbsoluteUri.Replace("api/", ""); hubConnection = new HubConnectionBuilder() .WithUrl($"{AddressHub}chatNotificationHub", options => { options.AccessTokenProvider = () => Task.FromResult(token); }) .WithAutomaticReconnect() .Build(); hubConnection.On("ReceiveNewChatItemFromCompany", async (chatitem) => { if (Chat.ID == chatitem.ChatItemID) { Chat.Responses.Add(chatitem); StateHasChanged(); await MarkAsRead(chatitem.ID); // Scroll to target if exists, otherwise scroll to bottom await JS.InvokeVoidAsync("scrollToTargetOrBottom"); } }); //------------------------------------- hubConnection.On("CheckMarkAsRead", async (chatresponseid) => { if (Chat.Responses.Any(a => a.ID == chatresponseid && !a.IsRead && a.Type == Common.Enums.ConversationType.UE)) { Chat.Responses.First(a => a.ID == chatresponseid).IsRead = true; StateHasChanged(); } }); await hubConnection.StartAsync(); //---------end hub } async Task SelectGroup(int groupId) { GroupID = groupId; StateHasChanged(); } [JSInvokable] public async Task MarkAsRead(int id) { if (Chat == null) return; var msg = Chat.Responses.FirstOrDefault(m => m.ID == id); if (msg != null && !msg.IsRead && msg.Type != Common.Enums.ConversationType.UE) { msg.IsRead = true; await chatService.MarkAsReadChatItemAsync(id); // StateHasChanged(); } await Task.CompletedTask; } // Method to handle new messages from other users public async Task HandleNewMessage() { if (Chat?.Responses != null) { var hasUnreadMessages =Chat.Responses.Any(m => !m.IsRead && m.Type != Common.Enums.ConversationType.UE); if (hasUnreadMessages) { await JS.InvokeVoidAsync("autoScrollToNewMessage"); } } } async Task NewChat() { Chat = null; GroupID = null; } async Task CloseChat() { var options = new ConfirmDialogOptions { YesButtonText = "بله", YesButtonColor = ButtonColor.Success, NoButtonText = "انصراف", NoButtonColor = ButtonColor.Danger }; var confirmation = await dialog.ShowAsync( title: "پایان دادن به گفتگو", message1: "اطمینان دارید ؟", confirmDialogOptions: options); if (confirmation) { if (await chatService.ChatIsFinishFromUser(Chat.ID)) { Chat.status = Common.Enums.ConversationStatus.Finished; StateHasChanged(); } } } async Task Logout() { await baseController.RemoveToken(); await localStorageService.RemoveItem("U/key"); NavigationManager.NavigateTo("UserPanelLogin"); } private async Task HandleKeyDown(KeyboardEventArgs e) { if (e.Key == "Enter") await OnClickSendMsg(); } }