Files
Hushian/Presentation/HushianWebApp/Pages/Chat.razor

402 lines
16 KiB
Plaintext
Raw Normal View History

2025-07-29 17:07:37 +03:30
@page "/Chat"
2025-07-29 22:21:38 +03:30
@page "/"
2025-07-28 17:41:14 +03:30
@using Common.Dtos.Conversation
@using Common.Dtos.Group
2025-07-29 00:00:16 +03:30
@using Common.Enums
2025-07-28 17:41:14 +03:30
@using HushianWebApp.Service
@inject ChatService chatService
@inject GroupService groupService
@inject UserService userService
@inject IJSRuntime JS
2025-07-29 00:00:16 +03:30
<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">@Inbox1Items.Count()</Badge>
</Button>
<!-- Inbox2 -->
<Button Outline="@isSelectedInbox2" Type="ButtonType.Link" @onclick="async()=>{await OnclickInbox(2);}" Size=ButtonSize.ExtraSmall Color="ButtonColor.Secondary">
پیام های من <Badge Color="BadgeColor.Warning">@Inbox2Items.Count()</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="@chatloading" 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 onClickSelectedChat(1,item)">
<div class="flex-grow-1">
<div class="d-flex justify-content-between">
<strong>@item.UserFullName</strong>
<small class="text-muted">@item.LastMsgdate</small>
<small class="text-muted">@item.LastMsgtime</small>
</div>
<div class="text-muted small text-truncate">@item.LastText</div>
</div>
<Badge Style="margin-top:25px" Color="BadgeColor.Danger">@item.Responses.Count()</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 onClickSelectedChat(2,item)">
<div class="flex-grow-1">
<div class="d-flex justify-content-between">
<strong>@item.UserFullName</strong>
<small class="text-muted">@item.LastMsgdate</small>
<small class="text-muted">@item.LastMsgtime</small>
</div>
<div class="text-muted small text-truncate">@item.LastText</div>
</div>
@if (item.Responses.Count(c => !c.IsRead && c.Type == ConversationType.UE) > 0)
{
<Badge Style="margin-top:25px" Color="BadgeColor.Danger">@item.Responses.Count(c => !c.IsRead && c.Type == ConversationType.UE)</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 onClickSelectedChat(3,item)">
<div class="flex-grow-1">
<div class="d-flex justify-content-between">
<strong>@item.UserFullName</strong>
<small class="text-muted">@item.LastMsgdate</small>
<small class="text-muted">@item.LastMsgtime</small>
</div>
<div class="text-muted small text-truncate">@item.LastText</div>
</div>
</div>
}
}
</div>
</div>
<!-- Main Chat Section (B) -->
<div class="col-md-9 d-flex flex-column" id="B">
<div class="input-group">
@if (ChatCurrent != 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">
@if (ChatCurrent.status == Common.Enums.ConversationStatus.InProgress)
{
<Button Color="ButtonColor.Danger" Size=ButtonSize.ExtraSmall Outline="true"
@onclick="async()=>
{
if(await chatService.ChatIsFinish(ChatCurrent.ID))
ChatCurrent.status=Common.Enums.ConversationStatus.Finished;
}">
<Icon Name="IconName.Escape" /> اتمام گفتگو
</Button>
<Button Color="ButtonColor.Secondary" Size=ButtonSize.ExtraSmall Outline="true"
Class="m-3">
<Icon Name="IconName.EnvelopeArrowUp" /> ارجاع به...
</Button>
}
else if (ChatCurrent.status == Common.Enums.ConversationStatus.Finished
&& (CurrentUser.Role == "Company" || ChatCurrent.ExperID == CurrentUser.ExperID))
{
<Button Color="ButtonColor.Success" Size=ButtonSize.ExtraSmall Outline="true">
<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 (ChatCurrent != null && ChatCurrent.Responses != null)
2025-07-29 17:07:37 +03:30
{
<div class="chat-container p-3">
@{
bool target = false;
}
@foreach (var msg in ChatCurrent?.Responses)
{
@if (!target && ((!msg.IsRead && msg.Type == Common.Enums.ConversationType.UE) || ChatCurrent.Responses.Last() == msg))
{
target = true;
<div id="target" style="text-align: center;">
@if (!msg.IsRead && msg.Type == Common.Enums.ConversationType.UE)
{
<p>ـــــــــــــــــــــــــ</p>
}
</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"> @msg.text </div>
@if (msg.Type == Common.Enums.ConversationType.EU)
{
if (msg.IsRead)
{
<Icon Style="align-self: self-end;" Name="IconName.CheckAll" Size="IconSize.x5" Color="IconColor.Success" />
}
else
{
<Icon Style="align-self: self-end;" Name="IconName.CheckLg" Size="IconSize.x5" />
}
}
</div>
}
@{
target = false;
}
</div>
}
2025-07-29 00:00:16 +03:30
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>
@if (ChatCurrent != null && ChatCurrent.status != Common.Enums.ConversationStatus.Finished && ChatCurrent.Responses != null)
{
<!-- B2: Message input -->
<div class="border m-2 p-2 rounded d-flex align-items-center" id="B2">
<input type="text" @bind-value="MsgInput" class="form-control" style="margin-left:10px" placeholder="پیام خود را بنویسید..." />
<Button Color="ButtonColor.Dark" Outline="true" @onclick="OnClickSendMsg"><Icon Name="IconName.AppIndicator" /> </Button>
</div>
}
</div>
</div>
</div>
2025-07-28 17:41:14 +03:30
@code {
public Common.Dtos.CurrentUserInfo CurrentUser { get; set; }
List<Read_GroupDto> _Group = new List<Read_GroupDto>();
//-------------------------------------
bool isSelectedInbox1 = false;
public List<ChatItemDto> Inbox1Items { get; set; } = new();
bool isSelectedInbox2 = true;
public List<ChatItemDto> Inbox2Items { get; set; } = new();
bool isSelectedInbox3 = false;
public List<ChatItemDto> Inbox3Items { get; set; } = new();
/////////////
public ChatItemDto? ChatCurrent { get; set; } = null;
public string MsgInput { get; set; }
2025-07-29 00:00:16 +03:30
bool chatloading = false;
string SelectedChatUserName = "مهدی ربیع نژاد";
2025-07-29 17:07:37 +03:30
private bool _shouldObserveVisibility = false;
2025-07-29 00:00:16 +03:30
2025-07-28 17:41:14 +03:30
}
@functions {
protected override async Task OnInitializedAsync()
{
2025-07-29 00:00:16 +03:30
CurrentUser = await userService.GetCurrentUserInfo();
2025-07-28 17:41:14 +03:30
_Group = await groupService.GetGroups();
Inbox1Items = await chatService.ChatAwaitingOurResponse();
Inbox2Items = await chatService.MyChatsIsInProgress();
Inbox3Items = await chatService.MyChatsIsFinished();
2025-07-29 17:07:37 +03:30
2025-07-28 17:41:14 +03:30
await base.OnInitializedAsync();
}
2025-07-29 17:07:37 +03:30
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (_shouldObserveVisibility)
{
_shouldObserveVisibility = false;
await JS.InvokeVoidAsync("observeVisibility", DotNetObjectReference.Create(this));
await JS.InvokeVoidAsync("scrollToTarget");
}
}
2025-07-28 17:41:14 +03:30
async Task OnclickInbox(int ID)
{
switch (ID)
{
case 1:
isSelectedInbox1 = true;
isSelectedInbox2 = false;
2025-07-29 00:00:16 +03:30
isSelectedInbox3 = false;
2025-07-28 17:41:14 +03:30
break;
case 2:
isSelectedInbox2 = true;
isSelectedInbox1 = false;
isSelectedInbox3 = false;
break;
case 3:
isSelectedInbox3 = true;
isSelectedInbox2 = false;
isSelectedInbox1 = false;
break;
}
ChatCurrent = null;
}
2025-07-29 00:00:16 +03:30
async Task OnClickSendMsg()
2025-07-28 17:41:14 +03:30
{
if (!string.IsNullOrEmpty(MsgInput) && ChatCurrent != null)
{
Common.Enums.ConversationType type = CurrentUser.Role == "Company" ? Common.Enums.ConversationType.CU : Common.Enums.ConversationType.EU;
await chatService.ADDChatResponse(ChatCurrent.ID, MsgInput, type);
ChatCurrent?.Responses.Add(new() { text = MsgInput, Type = type });
ChatCurrent.LastText = MsgInput;
await Task.Yield();
await JS.InvokeVoidAsync("scrollToBottom", "B1");
MsgInput = string.Empty;
}
}
2025-07-29 00:00:16 +03:30
async Task onClickSelectedChat(int InboxID, ChatItemDto chatItem)
2025-07-28 17:41:14 +03:30
{
2025-07-29 17:07:37 +03:30
2025-07-29 00:00:16 +03:30
chatloading = true;
SelectedChatUserName = "در حال گفتگو با " + chatItem.UserFullName;
ChatCurrent = chatItem;
2025-07-29 17:07:37 +03:30
_shouldObserveVisibility = true; // فعال کن تا در OnAfterRenderAsync صدا زده بشه
2025-07-29 00:00:16 +03:30
2025-07-29 17:07:37 +03:30
StateHasChanged(); // مجبور کن Blazor رندر کنه
2025-07-29 00:00:16 +03:30
chatloading = false;
2025-07-28 17:41:14 +03:30
}
2025-07-29 17:07:37 +03:30
[JSInvokable]
public async Task MarkAsRead(int id)
{
var msg = ChatCurrent.Responses.FirstOrDefault(m => m.ID == id);
if (msg != null && !msg.IsRead && msg.Type == Common.Enums.ConversationType.UE)
{
msg.IsRead = true;
await chatService.MarkAsReadChatItemAsync(id);
}
2025-07-31 02:35:14 +03:30
// StateHasChanged();
2025-07-29 17:07:37 +03:30
await Task.CompletedTask;
}
2025-07-28 17:41:14 +03:30
}
<style>
2025-07-29 00:00:16 +03:30
.chat-bubble {
padding: 0.5rem 0.75rem;
border-radius: 1rem;
max-width: 75%;
word-wrap: break-word;
white-space: pre-wrap;
}
.chat-mine {
background: linear-gradient(to right, #005eff, #267fff);
color: white;
border-top-left-radius: 0;
}
2025-07-28 17:41:14 +03:30
2025-07-29 00:00:16 +03:30
.chat-other {
background-color: #f1f1f1;
color: #333;
border-top-right-radius: 0;
}
.chat-ai {
background-color: #f1f1f1;
color: #353;
border-top-right-radius: 0;
}
2025-07-29 17:07:37 +03:30
2025-07-28 17:41:14 +03:30
.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>
<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>