2025-07-11 20:37:28 +03:30
|
|
|
@page "/Conversation"
|
|
|
|
@page "/"
|
|
|
|
@inject IJSRuntime JS
|
|
|
|
|
2025-07-12 21:33:44 +03:30
|
|
|
@using Common.Dtos.Conversation
|
|
|
|
@using HushianWebApp.Models
|
2025-07-11 20:37:28 +03:30
|
|
|
@using HushianWebApp.Service
|
|
|
|
@using HushianWebApp.Services
|
|
|
|
@inject ILocalStorageService localStorageService;
|
|
|
|
@inject NavigationManager navigationManager;
|
|
|
|
@inject ConversationService conversationService;
|
|
|
|
<Modal @ref="modal" Title="@SelectedChatUserName" UseStaticBackdrop="true" CloseOnEscape="false">
|
|
|
|
<BodyTemplate>
|
|
|
|
@Content
|
|
|
|
</BodyTemplate>
|
|
|
|
</Modal>
|
|
|
|
|
|
|
|
<div class="container-fluid">
|
|
|
|
<div class="row" style="height:85vh">
|
|
|
|
<!-- Sidebar (A) -->
|
|
|
|
<div class="col-md-3 bg-light d-flex flex-column p-2 rounded-end" id="A">
|
|
|
|
<!-- A1: Header -->
|
|
|
|
<div class="border mb-2 p-2" id="A1">
|
|
|
|
گفتگو های اخیر
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- A2: Buttons -->
|
|
|
|
<div class="d-flex justify-content-between mb-2" id="A2">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Inbox1 -->
|
|
|
|
<Button Outline="@isSelectedInbox1" Type="ButtonType.Link" @onclick="async()=>{await OnclickInbox(1);}" Size=ButtonSize.ExtraSmall Color="ButtonColor.Secondary">
|
|
|
|
پیام های آمده <Badge Color="BadgeColor.Warning">@countInbox1</Badge>
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
<!-- Inbox2 -->
|
|
|
|
<Button Outline="@isSelectedInbox2" Type="ButtonType.Link" @onclick="async()=>{await OnclickInbox(2);}" Size=ButtonSize.ExtraSmall Color="ButtonColor.Secondary">
|
|
|
|
پیام های من <Badge Color="BadgeColor.Warning">@countInbox2</Badge>
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
<!-- Inbox3 -->
|
|
|
|
<Button Outline="@isSelectedInbox3" Type="ButtonType.Link" @onclick="async()=>{await OnclickInbox(3);}" Size=ButtonSize.ExtraSmall Color="ButtonColor.Secondary">
|
|
|
|
پیام های بسته
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- A3: Chat list -->
|
|
|
|
<div class="flex-fill border p-2 overflow-auto" id="A3" style="height: 300px; overflow-y: auto;">
|
|
|
|
<Spinner Class="me-3" Type="SpinnerType.Dots" Color="SpinnerColor.Primary" Visible="@convloading" Size="SpinnerSize.Small" />
|
|
|
|
@if (isSelectedInbox1)
|
|
|
|
{
|
|
|
|
@foreach (var item in Inbox1Items)
|
|
|
|
{
|
|
|
|
<div class="d-flex align-items-center p-3 border-bottom message-item hover-bg"
|
|
|
|
style="cursor: pointer; margin-top: -10px;margin-bottom: -10px;" @onclick="async()=>await onClickSelectedCon(1,item)">
|
|
|
|
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
<div class="d-flex justify-content-between">
|
|
|
|
<strong>@item.UserFullName</strong>
|
2025-07-12 21:33:44 +03:30
|
|
|
<small class="text-muted">@item.LastMsgdate</small>
|
|
|
|
<small class="text-muted">@item.LastMsgtime</small>
|
2025-07-11 20:37:28 +03:30
|
|
|
</div>
|
2025-07-12 21:33:44 +03:30
|
|
|
<div class="text-muted small text-truncate">@item.LastText</div>
|
2025-07-11 20:37:28 +03:30
|
|
|
</div>
|
|
|
|
@if (item.NoReadCount > 0)
|
|
|
|
{
|
|
|
|
<Badge Style="margin-top:25px" Color="BadgeColor.Danger">@item.NoReadCount</Badge>
|
|
|
|
}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@if (isSelectedInbox2)
|
|
|
|
{
|
|
|
|
@foreach (var item in Inbox2Items)
|
|
|
|
{
|
|
|
|
<div class="d-flex align-items-center p-3 border-bottom message-item hover-bg"
|
|
|
|
style="cursor: pointer; margin-top: -10px;margin-bottom: -10px;" @onclick="async()=>await onClickSelectedCon(2,item)">
|
|
|
|
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
<div class="d-flex justify-content-between">
|
|
|
|
<strong>@item.UserFullName</strong>
|
2025-07-12 21:33:44 +03:30
|
|
|
<small class="text-muted">@item.LastMsgdate</small>
|
|
|
|
<small class="text-muted">@item.LastMsgtime</small>
|
2025-07-11 20:37:28 +03:30
|
|
|
</div>
|
2025-07-12 21:33:44 +03:30
|
|
|
<div class="text-muted small text-truncate">@item.LastText</div>
|
2025-07-11 20:37:28 +03:30
|
|
|
</div>
|
|
|
|
@if (item.NoReadCount>0)
|
|
|
|
{
|
|
|
|
<Badge Style="margin-top:25px" Color="BadgeColor.Danger">@item.NoReadCount</Badge>
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@if (isSelectedInbox3)
|
|
|
|
{
|
|
|
|
@foreach (var item in Inbox3Items)
|
|
|
|
{
|
|
|
|
<div class="d-flex align-items-center p-3 border-bottom message-item hover-bg"
|
|
|
|
style="cursor: pointer; margin-top: -10px;margin-bottom: -10px;" @onclick="async()=>await onClickSelectedCon(3,item)">
|
|
|
|
|
|
|
|
<div class="flex-grow-1">
|
|
|
|
<div class="d-flex justify-content-between">
|
|
|
|
<strong>@item.UserFullName</strong>
|
2025-07-12 21:33:44 +03:30
|
|
|
<small class="text-muted">@item.LastMsgdate</small>
|
|
|
|
<small class="text-muted">@item.LastMsgtime</small>
|
2025-07-11 20:37:28 +03:30
|
|
|
</div>
|
2025-07-12 21:33:44 +03:30
|
|
|
<div class="text-muted small text-truncate">@item.LastText</div>
|
2025-07-11 20:37:28 +03:30
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Main Chat Section (B) -->
|
|
|
|
@if (maximomeallowsize < width)
|
|
|
|
{
|
|
|
|
@Content
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
@code {
|
|
|
|
private Modal modal = default!;
|
|
|
|
int maximomeallowsize = 700;
|
|
|
|
private int width;
|
|
|
|
private int height;
|
|
|
|
public RenderFragment Content { get; set; }
|
|
|
|
private DotNetObjectReference<Conversation>? objRef;
|
|
|
|
int colmdB = 9;
|
|
|
|
bool chatloading = false;
|
|
|
|
bool convloading = false;
|
|
|
|
public string MsgInput { get; set; }
|
|
|
|
bool isSelectedInbox1 = false;
|
|
|
|
bool isSelectedInbox2 = true;
|
|
|
|
bool isSelectedInbox3 = false;
|
|
|
|
|
|
|
|
public int countInbox1 { get { return Inbox1Items.Count(); } }
|
|
|
|
public int countInbox2 { get { return Inbox2Items.Count(); } }
|
|
|
|
|
2025-07-12 21:33:44 +03:30
|
|
|
public List<Read_ConversationDto> Inbox1Items { get; set; }
|
2025-07-11 20:37:28 +03:30
|
|
|
= new() ;
|
2025-07-12 21:33:44 +03:30
|
|
|
public List<Read_ConversationDto> Inbox2Items { get; set; }
|
2025-07-11 20:37:28 +03:30
|
|
|
= new();
|
2025-07-12 21:33:44 +03:30
|
|
|
public List<Read_ConversationDto> Inbox3Items { get; set; }
|
2025-07-11 20:37:28 +03:30
|
|
|
= new();
|
|
|
|
|
2025-07-12 21:33:44 +03:30
|
|
|
public Read_ConversationDto? SelectedConversation { get; set; } = null;
|
|
|
|
public List<Read_ConversationResponseDto>? SelectedConversationItems { get; set; }
|
2025-07-11 20:37:28 +03:30
|
|
|
= null;
|
|
|
|
public string SelectedChatUserName { get; set; } = "مهدی ربیع نژاد";
|
2025-07-12 21:33:44 +03:30
|
|
|
public string Role { get; set; }
|
|
|
|
public int UserID = 0;
|
2025-07-11 20:37:28 +03:30
|
|
|
async Task OnclickInbox(int ID)
|
|
|
|
{
|
|
|
|
switch (ID)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
isSelectedInbox1 = true;
|
|
|
|
isSelectedInbox2 = false;
|
|
|
|
isSelectedInbox3 = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
isSelectedInbox2 = true;
|
|
|
|
isSelectedInbox1 = false;
|
|
|
|
isSelectedInbox3 = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
isSelectedInbox3 = true;
|
|
|
|
isSelectedInbox2 = false;
|
|
|
|
isSelectedInbox1 = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
SelectedConversation = null;
|
|
|
|
SelectedConversationItems = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
async Task SendMsg()
|
|
|
|
{
|
|
|
|
if (!string.IsNullOrEmpty(MsgInput) && SelectedConversationItems!=null)
|
|
|
|
{
|
|
|
|
await conversationService.ADDConversationItemFromCompanySide(SelectedConversationItems[0].ConversationID, MsgInput);
|
2025-07-12 21:33:44 +03:30
|
|
|
SelectedConversationItems?.Add(new() { text = MsgInput, Type = Common.Enums.ConversationType.EU });
|
|
|
|
SelectedConversation.LastText = MsgInput;
|
2025-07-11 20:37:28 +03:30
|
|
|
await Task.Yield();
|
|
|
|
await JS.InvokeVoidAsync("scrollToBottom", "B1");
|
|
|
|
MsgInput = string.Empty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private async Task HandleKeyDown(KeyboardEventArgs e)
|
|
|
|
{
|
|
|
|
if (e.Key == "Enter")
|
|
|
|
{
|
|
|
|
await SendMsg();
|
|
|
|
}
|
|
|
|
}
|
2025-07-12 21:33:44 +03:30
|
|
|
async Task onClickSelectedCon(int InboxID,Read_ConversationDto conversationDto)
|
2025-07-11 20:37:28 +03:30
|
|
|
{
|
|
|
|
// پر کردن SelectedCon
|
|
|
|
// مقدار دادن به SelectedChatUserName
|
|
|
|
chatloading = true;
|
|
|
|
SelectedChatUserName = "در حال گفتگو با "+ conversationDto.UserFullName;
|
|
|
|
SelectedConversation = conversationDto;
|
|
|
|
SelectedConversationItems = await conversationService.GetConversationItems(conversationDto.ID);
|
|
|
|
chatloading = false;
|
|
|
|
|
|
|
|
if (maximomeallowsize > width)
|
|
|
|
{
|
|
|
|
//await LoadSessionB(12);
|
|
|
|
await modal.ShowAsync();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@functions{
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
|
|
{
|
2025-07-12 21:33:44 +03:30
|
|
|
Role = await localStorageService.GetItem<string>("Role");
|
2025-07-11 20:37:28 +03:30
|
|
|
|
|
|
|
|
|
|
|
|
2025-07-12 21:33:44 +03:30
|
|
|
UserID= await localStorageService.GetItem<int>("UserID");
|
2025-07-11 20:37:28 +03:30
|
|
|
convloading = true;
|
|
|
|
await LoadSessionB();
|
|
|
|
Inbox1Items =await conversationService.ConversationAwaitingOurResponse();
|
|
|
|
Inbox2Items =await conversationService.MyConversationIsInProgress();
|
|
|
|
Inbox3Items =await conversationService.MyConversationIsFinished();
|
|
|
|
convloading = false;
|
|
|
|
await base.OnInitializedAsync();
|
|
|
|
}
|
|
|
|
async Task IsrEADaCTION(int id)
|
|
|
|
{
|
|
|
|
if( await conversationService.MarkAsReadConversationItemAsync(id))
|
|
|
|
{
|
|
|
|
if (isSelectedInbox1) Inbox1Items = await conversationService.ConversationAwaitingOurResponse();
|
|
|
|
|
|
|
|
else if (isSelectedInbox2) Inbox2Items = await conversationService.MyConversationIsInProgress();
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Inbox1Items = await conversationService.ConversationAwaitingOurResponse();
|
|
|
|
Inbox2Items = await conversationService.MyConversationIsInProgress();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
|
|
{
|
|
|
|
if (firstRender)
|
|
|
|
{
|
|
|
|
objRef = DotNetObjectReference.Create(this);
|
|
|
|
await JS.InvokeVoidAsync("registerResizeCallback", objRef);
|
|
|
|
|
|
|
|
await GetWindowSize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
[JSInvokable]
|
|
|
|
public async Task OnResize()
|
|
|
|
{
|
|
|
|
await GetWindowSize();
|
|
|
|
|
|
|
|
if (maximomeallowsize < width)
|
|
|
|
{
|
|
|
|
await modal.HideAsync();
|
|
|
|
await LoadSessionB(9);
|
|
|
|
}
|
|
|
|
|
|
|
|
else await LoadSessionB(12);
|
|
|
|
|
|
|
|
StateHasChanged();
|
|
|
|
}
|
|
|
|
private async Task GetWindowSize()
|
|
|
|
{
|
|
|
|
var size = await JS.InvokeAsync<WindowSize>("getWindowSize");
|
|
|
|
width = size.Width;
|
|
|
|
height = size.Height;
|
|
|
|
}
|
|
|
|
async Task LoadSessionB(int md=9)
|
|
|
|
{
|
|
|
|
Content = @<div class="col-md-@md d-flex flex-column" id="B">
|
|
|
|
<div class="input-group">
|
|
|
|
@if (SelectedConversation!=null)
|
|
|
|
{
|
|
|
|
<p type="text" class="form-control fw-bold text-primary" style="border:none;align-self: center;" aria-describedby="basic-addon1">@SelectedChatUserName</p>
|
|
|
|
<span class="input-group-text-chat" id="basic-addon1">
|
2025-07-12 21:33:44 +03:30
|
|
|
@if ( SelectedConversation.status==Common.Enums.ConversationStatus.InProgress)
|
2025-07-11 20:37:28 +03:30
|
|
|
{
|
|
|
|
<Button Color="ButtonColor.Danger" Size=ButtonSize.ExtraSmall Outline="true"
|
|
|
|
|
|
|
|
@onclick="async()=>
|
|
|
|
{
|
|
|
|
if(await conversationService.ConversationIsFinish(SelectedConversation.ID))
|
2025-07-12 21:33:44 +03:30
|
|
|
SelectedConversation.status=Common.Enums.ConversationStatus.Finished;
|
2025-07-11 20:37:28 +03:30
|
|
|
}">
|
|
|
|
<Icon Name="IconName.Escape" /> اتمام گفتگو
|
|
|
|
</Button>
|
|
|
|
|
|
|
|
<Button Color="ButtonColor.Secondary" Size=ButtonSize.ExtraSmall Outline="true"
|
|
|
|
Class="m-3" >
|
|
|
|
<Icon Name="IconName.EnvelopeArrowUp" /> ارجاع به...
|
|
|
|
</Button>
|
|
|
|
}
|
2025-07-12 21:33:44 +03:30
|
|
|
else if (SelectedConversation.status == Common.Enums.ConversationStatus.Finished
|
|
|
|
&& (Role=="Company" || SelectedConversation.ExperID == UserID))
|
2025-07-11 20:37:28 +03:30
|
|
|
{
|
|
|
|
<Button Color="ButtonColor.Success" Size=ButtonSize.ExtraSmall Outline="true"
|
|
|
|
|
|
|
|
@onclick="async()=>{
|
|
|
|
if(await conversationService.ConversationIsStart(SelectedConversation.ID))
|
2025-07-12 21:33:44 +03:30
|
|
|
SelectedConversation.status=Common.Enums.ConversationStatus.InProgress;
|
2025-07-11 20:37:28 +03:30
|
|
|
}"
|
|
|
|
|
|
|
|
}">
|
|
|
|
|
|
|
|
<Icon Name="IconName.Escape" /> باز کردن گفتگو
|
|
|
|
</Button>
|
|
|
|
}
|
|
|
|
</span>
|
|
|
|
}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<!-- B1: Chat area -->
|
|
|
|
<div class="flex-fill border p-2 overflow-auto" id="B1" style="height: 300px; overflow-y: auto;">
|
|
|
|
@if (SelectedConversationItems != null)
|
|
|
|
{
|
|
|
|
<HushianWebApp.Components.Base.ChatBubble Messages="SelectedConversationItems"
|
|
|
|
EventCallIsRead="EventCallback.Factory.Create<int>(this, IsrEADaCTION)" />
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
<div class="d-flex justify-content-center align-items-center flex-column" style="height: 80%;">
|
|
|
|
|
|
|
|
<Spinner Type="SpinnerType.Dots" Color="SpinnerColor.Primary" Visible="@chatloading" />
|
|
|
|
<p style="margin-top: 15px; font-size: 1.5rem; color: #0d6efd; font-weight: bold; text-shadow: 1px 1px 2px rgba(0,0,0,0.2);">
|
|
|
|
هوشیان
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-07-12 21:33:44 +03:30
|
|
|
@if (SelectedConversation != null && SelectedConversation.status!=Common.Enums.ConversationStatus.Finished && SelectedConversationItems != null)
|
2025-07-11 20:37:28 +03:30
|
|
|
{
|
|
|
|
<!-- B2: Message input -->
|
|
|
|
<div class="border m-2 p-2 rounded d-flex align-items-center" id="B2">
|
|
|
|
<input @onkeydown="HandleKeyDown" type="text" @bind-value="MsgInput" class="form-control" style="margin-left:10px" placeholder="پیام خود را بنویسید..." />
|
|
|
|
|
|
|
|
<Button Color="ButtonColor.Dark" Outline="true" @onclick="SendMsg" ><Icon Name="IconName.AppIndicator" /> </Button>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
<script>
|
|
|
|
window.getWindowSize = () => {
|
|
|
|
return {
|
|
|
|
width: window.innerWidth,
|
|
|
|
height: window.innerHeight
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
window.registerResizeCallback = (dotNetHelper) => {
|
|
|
|
window.onresize = () => {
|
|
|
|
dotNetHelper.invokeMethodAsync("OnResize");
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
window.scrollToBottom = (elementId) => {
|
|
|
|
const el = document.getElementById(elementId);
|
|
|
|
if (el) {
|
|
|
|
el.scrollTop = el.scrollHeight;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
<style>
|
|
|
|
|
|
|
|
.input-group-text-chat {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
padding: .375rem .75rem;
|
|
|
|
font-size: 1rem;
|
|
|
|
font-weight: 400;
|
|
|
|
line-height: 1.5;
|
|
|
|
color: var(--bs-body-color);
|
|
|
|
text-align: center;
|
|
|
|
white-space: nowrap;
|
|
|
|
border-radius: var(--bs-border-radius);
|
|
|
|
}
|
|
|
|
|
|
|
|
</style>
|