Snapshot of main at 9f39a6e1e (2026-11-02)

This commit is contained in:
Ronald Huynen committed 2026-11-02 10:41:47 +01:00
commit 6ccf1792d7
2580 files changed
+1040202

No files matched your search

@@ -0,0 +1,57 @@
@use('Namu\WireChat\Facades\WireChat')
<div x-data="{ selectedConversationId: '{{ request()->conversation ?? $selectedConversationId }}' }"
x-on:open-chat.window="selectedConversationId= $event.detail.conversation; $wire.selectedConversationId= $event.detail.conversation;"
x-init=" setTimeout(() => {
conversationElement = document.getElementById('conversation-' + selectedConversationId);
// Scroll to the conversation element
if (conversationElement) {
conversationElement.scrollIntoView({ behavior: 'smooth' });
}
}, 200);"
class="flex flex-col bg-[var(--wc-light-primary)] dark:bg-[var(--wc-dark-primary)] transition-all h-full overflow-hidden w-full sm:p-3">
@php
/* Show header if any of these conditions are true */
$showHeader = $showNewChatModalButton || $allowChatsSearch || $showHomeRouteButton || !empty($title);
@endphp
{{-- include header --}}
@includeWhen($showHeader, 'wirechat::livewire.chats.partials.header')
<main x-data
@scroll.self.debounce="
{{-- Detect when scrolled to the bottom --}}
// Calculate scroll values
scrollTop = $el.scrollTop;
scrollHeight = $el.scrollHeight;
clientHeight = $el.clientHeight;
// Check if the user is at the bottom of the scrollable element
if ((scrollTop + clientHeight) >= (scrollHeight - 1) && $wire.canLoadMore) {
// Trigger load more if we're at the bottom
await $nextTick();
$wire.loadMore();
}
"
class=" overflow-y-auto py-2 grow h-full relative " style="contain:content">
{{-- loading indicator --}}
@if (count($conversations) > 0)
{{-- include list item --}}
@include('wirechat::livewire.chats.partials.list')
{{-- include load more if true --}}
@includeWhen($canLoadMore, 'wirechat::livewire.chats.partials.load-more-button')
@else
<div class="w-full flex items-center h-full justify-center">
<h6 class=" font-bold text-gray-700 dark:text-white">{{ __('wirechat::chats.labels.no_conversations_yet') }}</h6>
</div>
@endif
</main>
</div>
@@ -0,0 +1,61 @@
@use('Namu\WireChat\Facades\WireChat')
<header class="px-3 z-10 sticky top-0 w-full py-2 " dusk="header">
{{-- Title/name and Icon --}}
<section class="flex flex-col pb-2">
@if (isset($title))
<div class="flex items-center gap-2 truncate" wire:ignore>
<h2 class="text-2xl font-bold dark:text-white" dusk="title">{{$title}}</h2>
</div>
@endif
@if ($showNewChatModalButton)
<div class="mt-2">
<x-wirechat::actions.new-chat widget="{{$this->isWidget()}}">
@if ($title === '')
<x-jetstream.button id="open-new-chat-modal-button" type="button">
{{ __('wirechat::new.chat.labels.heading') }}
</x-jetstream.button>
@else
<x-jetstream.secondary-button id="open-new-chat-modal-button" type="button">
{{ __('wirechat::new.chat.labels.heading') }}
</x-jetstream.secondary-button>
@endif
</x-wirechat::actions.new-chat>
</div>
@endif
</section>
{{-- Search input --}}
@if ($allowChatsSearch)
<section class="mt-4">
<div class="px-2 rounded-lg dark:bg-[var(--wc-dark-secondary)] bg-[var(--wc-light-secondary)] grid grid-cols-12 items-center">
<label for="chats-search-field" class="col-span-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="size-5 w-5 h-5 dark:text-gray-300">
<path stroke-linecap="round" stroke-linejoin="round"
d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
</svg>
</label>
<input id="chats-search-field"
name="chats_search"
maxlength="100"
type="search"
wire:model.live.debounce='search'
placeholder="{{ __('wirechat::chats.inputs.search.placeholder') }}"
autocomplete="off"
@click.stop
class="col-span-11 border-0 bg-inherit dark:text-white outline-hidden w-full focus:outline-hidden focus:ring-0 hover:ring-0">
</div>
</section>
@endif
</header>
@@ -0,0 +1,107 @@
@use('Namu\WireChat\Facades\WireChat')
<ul wire:loading.delay.long.remove wire:target="search" class="p-2 grid w-full spacey-y-2">
@foreach ($conversations as $key=> $conversation)
@php
//$receiver =$conversation->getReceiver();
$group = $conversation->isGroup() ? $conversation->group : null;
$receiver = $conversation->isGroup() ? null : ($conversation->isPrivate() ? $conversation->peer_participant?->participantable : $this->auth);
//$receiver = $conversation->isGroup() ? null : ($conversation->isPrivate() ? $conversation->peerParticipant()?->participantable : $this->auth);
$lastMessage = $conversation->lastMessage;
//mark isReadByAuth true if user has chat opened
$isReadByAuth = $conversation?->readBy($conversation->auth_participant??$this->auth) || $selectedConversationId == $conversation->id;
$belongsToAuth = $lastMessage?->belongsToAuth();
@endphp
<li x-data="{
conversationID: @js($conversation->id),
showUnreadStatus: @js(!$isReadByAuth),
handleChatOpened(event) {
// Hide unread dot
if (event.detail.conversation== this.conversationID) {
this.showUnreadStatus= false;
}
//update this so that the the selected conversation highlighter can be updated
$wire.selectedConversationId= event.detail.conversation;
},
handleChatClosed(event) {
// Clear the globally selected conversation.
$wire.selectedConversationId = null;
selectedConversationId = null;
},
handleOpenChat(event) {
// Clear the globally selected conversation.
if (this.showUnreadStatus== event.detail.conversation== this.conversationID) {
this.showUnreadStatus= false;
}
}
}"
id="conversation-{{ $conversation->id }}"
wire:key="conversation-em-{{ $conversation->id }}-{{ $conversation->updated_at->timestamp }}"
x-on:chat-opened.window="handleChatOpened($event)"
x-on:chat-closed.window="handleChatClosed($event)">
<a @if ($widget) tabindex="0"
role="button"
dusk="openChatWidgetButton"
@click="$dispatch('open-chat',{conversation:@js($conversation->id)})"
@keydown.enter="$dispatch('open-chat',{conversation:@js($conversation->id)})"
@else
wire:navigate href="{{ route(WireChat::viewRouteName(), $conversation->id) }}" @endif
@style(['border-color:var(--wc-brand-primary)' => $selectedConversationId == $conversation?->id])
class="py-3 flex gap-4 dark:hover:bg-[var(--wc-dark-secondary)] hover:bg-[var(--wc-light-secondary)] rounded-xs transition-colors duration-150 relative w-full cursor-pointer px-2"
:class="$wire.selectedConversationId == conversationID &&
'rounded-md dark:bg-[var(--wc-dark-secondary)] bg-[var(--wc-light-secondary)] border-[var(--wc-brand-primary)]'">
<div class="shrink-0">
<x-wirechat::avatar group="{{ $conversation->isGroup() }}"
:src="$group ? $group?->cover_url : $receiver?->cover_url ?? null" class="w-12 h-12" />
</div>
<aside class="grid grid-cols-12 w-full">
<div
class="col-span-10 pb-2 relative overflow-hidden truncate leading-5 w-full flex-nowrap p-1">
{{-- name --}}
<div class="flex gap-1 mb-1 w-full items-center">
<h6 class="truncate font-medium text-gray-900 dark:text-white">
{{ $group ? $group?->name : $receiver?->display_name }}
</h6>
@if ($conversation->isSelfConversation())
<span class="font-medium dark:text-white">({{__('wirechat::chats.labels.you') }})</span>
@endif
</div>
{{-- Message body --}}
@if ($lastMessage != null)
@include('wirechat::livewire.chats.partials.message-body')
@endif
</div>
{{-- Read status --}}
{{-- Only show if AUTH is NOT onwer of message --}}
@if ($lastMessage != null && !$lastMessage?->ownedBy($this->auth) && !$isReadByAuth)
<div x-show="showUnreadStatus" dusk="unreadMessagesDot" class=" col-span-2 flex flex-col text-center my-auto">
{{-- Dots icon --}}
<span dusk="unreadDotItem" class="sr-only">unread dot</span>
{{-- 1. Remove the @style directive and change the class --}}
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
fill="#b91c1c" class="bi bi-dot w-10 h-10 text-gray-700" viewBox="0 0 16 16">
<path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z" />
</svg>
</div>
@endif
</aside>
</a>
</li>
@endforeach
</ul>
@@ -0,0 +1,11 @@
<section wire:loading.remove wire:target="search" class="w-full justify-center flex my-3 ">
<button wire:loading.remove wire:target="loadMore" wire:loading.attr="disabled"
dusk="loadMoreButton" @click="$wire.loadMore()"
class=" text-sm dark:text-white disabled:hover:cursor-not-allowed hover:text-gray-700 transition-colors dark:hover:text-gray-500 dark:gray-200">
@lang('wirechat::chats.labels.load_more')
</button>
<div wire:loading wire:target="loadMore">
<x-wirechat::loading-spin />
</div>
</section>
@@ -0,0 +1,5 @@
<div x-cloak wire:loading.delay.class.remove="hidden"
wire:target="search"class="hidden transition-all duration-300 ">
<x-wirechat::loading-spin />
</div>
@@ -0,0 +1,35 @@
<div class="flex gap-x-2 items-center">
{{-- Only show if AUTH is onwer of message --}}
@if ($lastMessage?->ownedBy($this->auth))
<span class="font-bold text-xs dark:text-white/90 dark:font-normal">
@lang('wirechat::chats.labels.you'):
</span>
@elseif(!$lastMessage?->ownedBy($this->auth) && $group !== null)
<span class="font-bold text-xs dark:text-white/80 dark:font-normal">
{{ $lastMessage->sendable?->display_name }}:
</span>
@endif
<p @class([
'truncate text-sm dark:text-white gap-2 items-center',
'font-semibold text-black' =>
!$isReadByAuth && !$lastMessage?->ownedBy($this->auth),
'font-normal text-gray-600' =>
$isReadByAuth && !$lastMessage?->ownedBy($this->auth),
'font-normal text-gray-600' =>
$isReadByAuth && $lastMessage?->ownedBy($this->auth),
])>
{{ $lastMessage->body != '' ? $lastMessage->body : ($lastMessage->isAttachment() ? '📎 '.__('wirechat::chats.labels.attachment') : '') }}
</p>
<span class="font-medium px-1 text-xs shrink-0 text-gray-800 dark:text-gray-50">
@if ((int) ($lastMessage->created_at->diffInMinutes(now(), true)) < 1)
@lang('wirechat::chats.labels.now')
@else
{{ $lastMessage->created_at->shortAbsoluteDiffForHumans() }}
@endif
</span>
</div>