...
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
@page "/UserCP/{CompanyID:int}"
|
||||
@page "/UserCP/{CompanyID:int}/{ChatID:int?}"
|
||||
@using Common.Dtos.Company
|
||||
@using Common.Dtos.Conversation
|
||||
@using Common.Dtos.Group
|
||||
@@ -13,6 +12,7 @@
|
||||
{
|
||||
<PageTitle>گفتگو با @CompanyInfo?.FullName</PageTitle>
|
||||
}
|
||||
|
||||
@implements IAsyncDisposable
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ChatService ChatService
|
||||
@@ -27,22 +27,23 @@
|
||||
@inject ToastService toastService
|
||||
@inject HttpClient _Http;
|
||||
@layout UserPanelLayout
|
||||
|
||||
<ConfirmDialog @ref="dialog" />
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row" style="height:85vh">
|
||||
@if (IsEndFirstProcess)
|
||||
{
|
||||
@if (IsLogin)
|
||||
if (string.IsNullOrEmpty(Error))
|
||||
{
|
||||
<div class="col-md-12 d-flex flex-column" id="B">
|
||||
<div class="input-group">
|
||||
@if (LastOpenChat != null)
|
||||
@if (Chat != null)
|
||||
{
|
||||
<p type="text" class="form-control fw-bold text-primary" style="border:none;align-self: center;" aria-describedby="basic-addon1">@ExperYou</p>
|
||||
}
|
||||
<div class="d-flex gap-2 ms-auto">
|
||||
@if (LastOpenChat != null)
|
||||
@if (Chat != null)
|
||||
{
|
||||
|
||||
<Button Color="ButtonColor.Success" Size=ButtonSize.ExtraSmall Outline="true" @onclick="NewChat" Class="new-conversation-btn">
|
||||
@@ -56,15 +57,15 @@
|
||||
</div>
|
||||
<!-- B1: Chat area -->
|
||||
<div class="flex-fill chat-area-container" id="B1">
|
||||
@if (LastOpenChat != null && LastOpenChat.Responses != null)
|
||||
@if (Chat != null && Chat.Responses != null)
|
||||
{
|
||||
<div class="chat-container p-3">
|
||||
@{
|
||||
bool target = false;
|
||||
}
|
||||
@foreach (var msg in LastOpenChat?.Responses)
|
||||
@foreach (var msg in Chat?.Responses)
|
||||
{
|
||||
@if (!target && ((!msg.IsRead && msg.Type != Common.Enums.ConversationType.UE) || LastOpenChat.Responses.Last() == msg))
|
||||
@if (!target && ((!msg.IsRead && msg.Type != Common.Enums.ConversationType.UE) || Chat.Responses.Last() == msg))
|
||||
{
|
||||
target = true;
|
||||
<div id="target" class="chat-separator">
|
||||
@@ -77,8 +78,8 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="d-flex mb-2 @(msg.Type!=Common.Enums.ConversationType.UE ? "justify-content-end" : "justify-content-start")">
|
||||
<div class="chat-bubble @(msg.Type!=Common.Enums.ConversationType.UE ? "chat-mine": "chat-other")" data-id="@msg.ID">
|
||||
<div class="d-flex mb-2 @(msg.Type != Common.Enums.ConversationType.UE ? "justify-content-end" : "justify-content-start")">
|
||||
<div class="chat-bubble @(msg.Type != Common.Enums.ConversationType.UE ? "chat-mine" : "chat-other")" data-id="@msg.ID">
|
||||
@if (msg.FileContent != null && msg.FileContent.Length > 0 && !string.IsNullOrWhiteSpace(msg.FileType))
|
||||
{
|
||||
@if (msg.FileType.StartsWith("image/"))
|
||||
@@ -137,10 +138,10 @@
|
||||
<div class="groups-container mt-4">
|
||||
<h6 class="text-center mb-3 text-muted">انتخاب گروه:</h6>
|
||||
<div class="groups-grid">
|
||||
@foreach (var group in CompanyGroups.Where(w=>w.Available))
|
||||
@foreach (var group in CompanyGroups.Where(w => w.Available))
|
||||
{
|
||||
<div class="group-card @(GroupID == group.ID ? "selected" : "")"
|
||||
@onclick="() => SelectGroup(group.ID)">
|
||||
@onclick="() => SelectGroup(group.ID)">
|
||||
<div class="group-card-content">
|
||||
@if (group.img == null || group.img.Length == 0)
|
||||
{
|
||||
@@ -163,7 +164,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
@if (CompanyInfo != null && (LastOpenChat == null || (LastOpenChat != null && LastOpenChat.status != Common.Enums.ConversationStatus.Finished && LastOpenChat.Responses != null)))
|
||||
@if (CompanyInfo != null && (Chat == null || (Chat != null && Chat.status != Common.Enums.ConversationStatus.Finished && Chat.Responses != null)))
|
||||
{
|
||||
<!-- B2: Message input -->
|
||||
<div class="message-input-container" id="B2">
|
||||
@@ -175,13 +176,12 @@
|
||||
</Button>
|
||||
|
||||
<!-- Audio Recording Button -->
|
||||
<Button Color="@(IsRecording ? ButtonColor.Danger : ButtonColor.Secondary)"
|
||||
Size=ButtonSize.Small
|
||||
Outline="true"
|
||||
@onclick="ToggleAudioRecording"
|
||||
class=@($"audio-btn {(IsRecording ? "recording" : "")}")
|
||||
title="@(IsRecording ? "توقف ضبط" : "ضبط صدا")"
|
||||
>
|
||||
<Button Color="@(IsRecording? ButtonColor.Danger: ButtonColor.Secondary)"
|
||||
Size=ButtonSize.Small
|
||||
Outline="true"
|
||||
@onclick="ToggleAudioRecording"
|
||||
class=@($"audio-btn {(IsRecording ? "recording" : "")}")
|
||||
title="@(IsRecording ? "توقف ضبط" : "ضبط صدا")">
|
||||
@if (IsRecording)
|
||||
{
|
||||
<Icon Name="IconName.StopCircle" Class="recording-pulse" />
|
||||
@@ -235,7 +235,7 @@
|
||||
|
||||
|
||||
</div>
|
||||
@if (LastOpenChat!=null && LastOpenChat.status == Common.Enums.ConversationStatus.InProgress)
|
||||
@if (Chat != null && Chat.status == Common.Enums.ConversationStatus.InProgress)
|
||||
{
|
||||
<Button Color="ButtonColor.Danger" Size=ButtonSize.ExtraSmall Outline="true" @onclick="CloseChat" Class="finish-conversation-btn">
|
||||
<Icon Name="IconName.Escape" Class="me-1" /> اتمام گفتگو
|
||||
@@ -243,23 +243,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
@if (LastOpenChat.status == Common.Enums.ConversationStatus.Finished)
|
||||
@if (Chat?.status == Common.Enums.ConversationStatus.Finished)
|
||||
{
|
||||
<p style="color: red;text-align: center;font-weight: bold;">این گفتگو به پایان رسیده</p>
|
||||
}
|
||||
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="d-flex justify-content-center align-items-center" style="height: 100%;">
|
||||
<div class="login-container p-4 bg-white rounded shadow-sm" style="max-width: 400px; width: 100%;">
|
||||
<div class="text-center mb-4">
|
||||
<h4 class="text-primary mb-2">ورود به سیستم</h4>
|
||||
</div>
|
||||
<LoginComponent OnMultipleOfThree="EventCallback.Factory.Create(this, Afterlogin)" />
|
||||
<div class="text-center">
|
||||
<Alert Color="AlertColor.Danger" Dismissable="false">
|
||||
<Icon Name="IconName.ExclamationTriangleFill" class="me-2"></Icon>@Error </Alert>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -270,36 +272,17 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@code {
|
||||
[Parameter] public int CompanyID { get; set; }
|
||||
[Parameter] public int? ChatID { get; set; }
|
||||
private ConfirmDialog dialog = default!;
|
||||
private HubConnection? hubConnection;
|
||||
|
||||
#region fild
|
||||
private bool IsEndFirstProcess { get; set; } = false;
|
||||
private bool _shouldObserveVisibility = false;
|
||||
int? GroupID = null;
|
||||
ReadANDUpdate_CompanyDto? CompanyInfo = new();
|
||||
Common.Dtos.CurrentUserInfo CurrentUser = new();
|
||||
List<Read_GroupDto> CompanyGroups = new();
|
||||
ChatItemDto? LastOpenChat = null;
|
||||
string MsgInput = string.Empty;
|
||||
IBrowserFile? SelectedImageFile = null;
|
||||
byte[]? SelectedImageBytes = null;
|
||||
string? SelectedImagePreview = null;
|
||||
|
||||
// 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;
|
||||
|
||||
bool chatloading = false;
|
||||
public bool IsLogin { get; set; } = false;
|
||||
public bool IsEndFirstProcess { get; set; } = false;
|
||||
|
||||
string ExperYou
|
||||
{
|
||||
get
|
||||
@@ -311,23 +294,57 @@
|
||||
value += "/" + CompanyGroups.FirstOrDefault(f => f.ID == GroupID.GetValueOrDefault()).Name;
|
||||
|
||||
}
|
||||
if (LastOpenChat != null && LastOpenChat.Responses!=null)
|
||||
if (Chat != null && Chat.Responses != null)
|
||||
{
|
||||
var model = LastOpenChat.Responses.OrderBy(o => o.ID).LastOrDefault(l => l.Type != Common.Enums.ConversationType.UE);
|
||||
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))
|
||||
if (model != null && model.Type == Common.Enums.ConversationType.CU && !string.IsNullOrEmpty(CompanyInfo.FullNameManager))
|
||||
{
|
||||
value += "/" + CompanyInfo.FullNameManager;
|
||||
}
|
||||
else
|
||||
if (!string.IsNullOrEmpty(LastOpenChat.ExperFullName))
|
||||
value += "/" + LastOpenChat.ExperFullName;
|
||||
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; }
|
||||
|
||||
// Audio recording methods
|
||||
[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<Read_GroupDto> CompanyGroups = new();
|
||||
ChatItemDto? Chat = null;
|
||||
|
||||
}
|
||||
@functions{
|
||||
#region Audio recording methods
|
||||
private async Task ToggleAudioRecording()
|
||||
{
|
||||
if (IsRecording)
|
||||
@@ -416,271 +433,10 @@
|
||||
? string.Empty
|
||||
: $"data:{fileType};base64,{Convert.ToBase64String(content)}";
|
||||
|
||||
#endregion
|
||||
}
|
||||
@functions {
|
||||
async Task OnClickSendMsg()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(MsgInput) || SelectedImageFile != null || RecordedAudioBytes != null)
|
||||
{
|
||||
if (LastOpenChat != null)
|
||||
{
|
||||
Common.Enums.ConversationType type = Common.Enums.ConversationType.UE;
|
||||
ChatItemResponseDto? model;
|
||||
|
||||
if (SelectedImageFile != null)
|
||||
{
|
||||
var bytes = SelectedImageBytes ?? Array.Empty<byte>();
|
||||
model = await chatService.ADDChatResponse(
|
||||
LastOpenChat.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(
|
||||
LastOpenChat.ID,
|
||||
MsgInput,
|
||||
type,
|
||||
fileName,
|
||||
"audio/wav",
|
||||
RecordedAudioBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
model = await chatService.ADDChatResponse(LastOpenChat.ID, MsgInput, type);
|
||||
}
|
||||
|
||||
LastOpenChat?.Responses.Add(model);
|
||||
LastOpenChat.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)
|
||||
{
|
||||
LastOpenChat = 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";
|
||||
|
||||
}
|
||||
}
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (_shouldObserveVisibility)
|
||||
{
|
||||
_shouldObserveVisibility = false;
|
||||
await JS.InvokeVoidAsync("observeVisibility", DotNetObjectReference.Create(this));
|
||||
}
|
||||
}
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
|
||||
await IsOnline();
|
||||
//-------------hub
|
||||
var token = await localStorageService.GetItem<string>("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<ChatItemResponseDto>("ReceiveNewChatItemFromCompany",async (chatitem) =>
|
||||
{
|
||||
if (LastOpenChat.ID == chatitem.ChatItemID)
|
||||
{
|
||||
LastOpenChat.Responses.Add(chatitem);
|
||||
StateHasChanged();
|
||||
await MarkAsRead(chatitem.ID);
|
||||
// Scroll to target if exists, otherwise scroll to bottom
|
||||
await JS.InvokeVoidAsync("scrollToTargetOrBottom");
|
||||
}
|
||||
});
|
||||
//-------------------------------------
|
||||
hubConnection.On<int>("CheckMarkAsRead", async (chatresponseid) =>
|
||||
{
|
||||
if (LastOpenChat.Responses.Any(a=>a.ID==chatresponseid && !a.IsRead && a.Type==Common.Enums.ConversationType.UE))
|
||||
{
|
||||
LastOpenChat.Responses.First(a => a.ID == chatresponseid).IsRead = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
});
|
||||
|
||||
await hubConnection.StartAsync();
|
||||
//---------end hub
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
async Task IsOnline()
|
||||
{
|
||||
var token = await localStorageService.GetItem<string>("U/key");
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
IsLogin = false;
|
||||
IsEndFirstProcess = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
await baseController.RemoveToken();
|
||||
await baseController.SetToken(token);
|
||||
if (!await authService.IsOnline())
|
||||
{
|
||||
await baseController.RemoveToken();
|
||||
IsLogin = false;
|
||||
IsEndFirstProcess = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsEndFirstProcess = true;
|
||||
await Afterlogin();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
async Task Afterlogin()
|
||||
{
|
||||
IsLogin = true;
|
||||
CurrentUser = await userService.GetCurrentUserInfo();
|
||||
await IsCompany();
|
||||
}
|
||||
async Task IsCompany()
|
||||
{
|
||||
CompanyInfo = await companyService.GetCompany(CompanyID);
|
||||
if (CompanyInfo != null)
|
||||
{ CompanyGroups = await groupService.GetGroupsCompany(CompanyID);
|
||||
await IsLastChat();}
|
||||
}
|
||||
async Task IsLastChat()
|
||||
{
|
||||
if (CompanyInfo != null)
|
||||
{
|
||||
if (ChatID.HasValue) LastOpenChat = await ChatService.Getchat(ChatID.GetValueOrDefault());
|
||||
else LastOpenChat = LastOpenChat = await ChatService.GetLastOpenChatInCompany(CompanyID);
|
||||
|
||||
if (LastOpenChat != null)
|
||||
{
|
||||
GroupID = LastOpenChat.GroupID;
|
||||
// Always set up visibility observation for chat bubbles
|
||||
_shouldObserveVisibility = true;
|
||||
StateHasChanged();
|
||||
|
||||
// Wait for render to complete
|
||||
await Task.Delay(200);
|
||||
|
||||
// Scroll to target if exists, otherwise scroll to bottom
|
||||
await JS.InvokeVoidAsync("scrollToTargetOrBottom");
|
||||
}
|
||||
}
|
||||
}
|
||||
[JSInvokable]
|
||||
public async Task MarkAsRead(int id)
|
||||
{
|
||||
if (LastOpenChat == null) return;
|
||||
var msg = LastOpenChat.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 (LastOpenChat?.Responses != null)
|
||||
{
|
||||
var hasUnreadMessages = LastOpenChat.Responses.Any(m => !m.IsRead && m.Type != Common.Enums.ConversationType.UE);
|
||||
if (hasUnreadMessages)
|
||||
{
|
||||
await JS.InvokeVoidAsync("autoScrollToNewMessage");
|
||||
}
|
||||
}
|
||||
}
|
||||
async Task NewChat()
|
||||
{
|
||||
LastOpenChat = 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(LastOpenChat.ID))
|
||||
{
|
||||
LastOpenChat.status = Common.Enums.ConversationStatus.Finished;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
async Task Logout()
|
||||
{
|
||||
await baseController.RemoveToken();
|
||||
await localStorageService.RemoveItem("U/key");
|
||||
IsLogin = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
async Task SelectGroup(int groupId)
|
||||
{
|
||||
GroupID = groupId;
|
||||
StateHasChanged();
|
||||
}
|
||||
private string GetImageSource(byte[] bytes)
|
||||
=> $"data:image/jpeg;base64,{Convert.ToBase64String(bytes)}";
|
||||
private async Task HandleKeyDown(KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Key == "Enter") await OnClickSendMsg();
|
||||
}
|
||||
|
||||
}
|
||||
@functions {
|
||||
#region Image
|
||||
private async Task OpenFileDialog()
|
||||
{
|
||||
await JS.InvokeVoidAsync("triggerClick", "chatImageInput");
|
||||
@@ -701,7 +457,7 @@
|
||||
await file.OpenReadStream().CopyToAsync(memoryStream);
|
||||
SelectedImageBytes = memoryStream.ToArray();
|
||||
SelectedImagePreview = $"data:{file.ContentType};base64,{Convert.ToBase64String(SelectedImageBytes)}";
|
||||
await ClearRecordedAudio();
|
||||
await ClearRecordedAudio();
|
||||
}
|
||||
|
||||
private Task ClearSelectedImage()
|
||||
@@ -727,6 +483,58 @@
|
||||
}
|
||||
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)
|
||||
@@ -736,6 +544,254 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@functions {
|
||||
//Login
|
||||
async Task<bool> CheckLogin()
|
||||
{
|
||||
var token = await localStorageService.GetItem<string>("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<bool> ExsistCompany()
|
||||
{
|
||||
CompanyInfo = await companyService.GetCompany(CompanyID);
|
||||
return CompanyInfo != null;
|
||||
}
|
||||
async Task GetGroups()
|
||||
{
|
||||
CompanyGroups = await groupService.GetGroupsCompany(CompanyID);
|
||||
}
|
||||
async Task<bool> 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<byte>();
|
||||
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<string>("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<ChatItemResponseDto>("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<int>("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();
|
||||
}
|
||||
}
|
||||
|
||||
<style>
|
||||
.chat-bubble {
|
||||
padding: 0.5rem 0.75rem;
|
||||
|
||||
Reference in New Issue
Block a user