���Root Mod By V1rus_Alfa
User / IP
:
216.73.217.78
Host / Server
:
195.214.235.115 / npln.ir
System
:
Linux damavand.pws-dns.net 5.14.0-503.35.1.el9_5.x86_64 #1 SMP PREEMPT_DYNAMIC Fri Apr 4 05:23:43 EDT 2025 x86_64
Command
|
Upload
|
Mass Deface
|
Create
:
/
home2
/
cutunixc
/
repositories
/
portal-cutunix
/
resources
/
views
/
livewire
/
chat
/
Viewing: chat-component.blade.php
@section('styles') {{-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">--}} <style> /* Custom scrollbar */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: #f1f1f1; } ::-webkit-scrollbar-thumb { background: #888; border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { background: #555; } /* Animation for message bubbles */ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .message-animation { animation: fadeIn 0.3s ease-out; } /* Sidebar transition */ .sidebar { transition: all 0.3s ease; } /* Hamburger animation */ .hamburger-line { transition: all 0.3s ease; } .hamburger-active .hamburger-line:nth-child(1) { transform: translateY(8px) rotate(45deg); } .hamburger-active .hamburger-line:nth-child(2) { opacity: 0; } .hamburger-active .hamburger-line:nth-child(3) { transform: translateY(-8px) rotate(-45deg); } /* Voice recording animation */ @keyframes pulse { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.1); opacity: 0.8; } 100% { transform: scale(1); opacity: 1; } } .recording-pulse { animation: pulse 1.5s ease infinite; } .recording-time { font-size: 12px; color: #666; margin-right: 8px; } </style> @endsection <div wire:poll> <div class="bg-gray-100 font-sans text-gray-800 flex h-[92vh] overflow-hidden w-full"> <!-- Mobile header with hamburger --> <div class="lg:hidden fixed top-0 right-0 left-0 bg-white p-3 border-b border-gray-200 flex items-center justify-between z-10"> <button id="hamburger" class="w-8 h-8 flex flex-col justify-between"> <span class="hamburger-line block w-full h-1 bg-gray-600 rounded"></span> <span class="hamburger-line block w-full h-1 bg-gray-600 rounded"></span> <span class="hamburger-line block w-full h-1 bg-gray-600 rounded"></span> </button> <h1 class="text-lg font-medium">تلگرام</h1> <div class="w-8"></div> <!-- Spacer for alignment --> </div> <!-- Right sidebar - Contacts --> <div id="sidebar" class="sidebar w-4/12 bg-white border-l border-gray-200 flex flex-col h-full z-20 transform translate-x-0"> <!-- Search bar --> <div class="p-3 bg-gray-50 mt-12 lg:mt-0"> <div class="relative"> <input type="text" placeholder="جستجو" class="w-full bg-gray-100 rounded-lg py-2 px-4 pl-10 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500" wire:model.live="searchQuery" > <i class="fas fa-search absolute right-3 top-2.5 text-gray-400 text-sm"></i> </div> </div> @if(!is_null($conversations)) <!-- Contact list --> <div class="flex-1 overflow-y-auto"> @foreach($conversations as $conversation) <!-- Contact item --> <div class="flex items-center p-3 border-b border-gray-100 hover:bg-gray-50 cursor-pointer @if($conversation->subject_id == $subject_id) bg-gray-50 @endif" @click="@this.setActiveConversation({{ $conversation->id }})" > {{--<div class="relative"> <img src="https://via.placeholder.com/50" alt="Profile" class="w-12 h-12 rounded-full"> <span class="absolute bottom-0 left-0 w-3 h-3 bg-green-500 rounded-full border-2 border-white"></span> </div>--}} <div class="mr-3 flex-1"> <div class="flex justify-between items-center"> <h3 class="font-medium">{{ $conversation?->title }}</h3> <span class="text-xs text-gray-500">{{ $conversation?->created_at->format('H:i') }}</span> </div> </div> </div> @endforeach </div> @endif </div> <!-- Main chat area --> <div class="flex flex-col h-full pt-12 lg:pt-0 w-8/12"> <!-- Chat header --> <div class="bg-white p-3 border-b border-gray-200 flex items-center justify-between"> <div class="flex items-center"> <button id="desktop-hamburger" class="lg:hidden w-10 h-10 rounded-full hover:bg-gray-100 flex items-center justify-center ml-3"> <i class="fas fa-bars text-gray-500"></i> </button> <img src="https://via.placeholder.com/50" alt="Profile" class="w-10 h-10 rounded-full"> <div class="mr-3"> <h3 class="font-medium"> {{ $participant_name }} </h3> <p class="text-xs text-gray-500">آنلاین</p> </div> </div> <div class="flex items-center space-x-2 space-x-reverse"> <button class="w-8 h-8 rounded-full hover:bg-gray-100 flex items-center justify-center"> <i class="bx bx-search text-gray-500"></i> </button> <button class="w-8 h-8 rounded-full hover:bg-gray-100 flex items-center justify-center"> <i class="bx bx-phone text-gray-500"></i> </button> </div> </div> <!-- Messages area --> <div class="flex-1 overflow-y-auto p-4 bg-gray-50" id="messages"> <!-- Date divider --> <div class="flex justify-center my-4"> <span class="bg-white px-3 py-1 rounded-full text-xs text-gray-500 shadow-sm"> {{-- امروز--}} </span> </div> @if(!is_null($selectedConversation)) @foreach($selectedConversation->messages as $key => $message) @if((auth('web')->check() && $message->didCustomerSentIt()) || (auth('tenant')->check() && $message->didTenantSentIt()) || (auth('admin')->check() && $message->didAdminSentIt())) <!-- Incoming message --> <div class="flex mb-4 message-animation"> <div class="text-right"> <div class="bg-white rounded-lg rounded-tr-none p-3 shadow-sm max-w-xs lg:max-w-md"> @if($message->type == 'audio') <div class="flex items-center gap-2"> <i class="bx bx-microphone text-gray-500"></i> <audio controls class="w-[50em] h-8" > <source src="{{ $message->file?->host . $message->file?->address . $message->file?->name }}" type="audio/webm"> دستگاه شما از این فایل پشتیبانی نمی کند </audio> </div> @endif @if ($message->type =='image') <a href="{{ $message->file?->host . $message->file?->address . $message->file?->name }}" target="_blank"> <img src="{{ $message->file?->host . $message->file?->address . $message->file?->name }}" alt="تصویر یافت نشد" width="100"> </a> @endif @if($message->type == 'text') <p> {{ $message?->content }} </p> @endif </div> <div class="flex gap-1"> @if($message?->is_read) <i class="bx bx-check-double text-blue-500 mr-1"></i> @else <i class="bx bx-check text-gray-500 mr-1"></i> @endif <span class="text-xs text-gray-500 mt-1 block"> {{ $message?->created_at->format('H:i') }} </span> </div> </div> </div> @endif @if((auth('web')->check() && !$message->didCustomerSentIt()) || (auth('tenant')->check() && !$message->didTenantSentIt()) || (auth('admin')->check() && !$message->didAdminSentIt())) <!-- Outgoing message --> <div class="flex justify-end mb-4 message-animation"> <div class="text-left"> <div class="bg-blue-500 text-white rounded-lg rounded-tl-none p-3 shadow-sm max-w-xs lg:max-w-md"> @if($message->type == 'audio') <div class="flex items-center gap-2"> <i class="bx bx-microphone text-gray-500"></i> <audio controls class="w-[50em] h-8" > <source src="{{ $message->file?->host . $message->file?->address . $message->file?->name }}" type="audio/webm"> دستگاه شما از این فایل پشتیبانی نمی کند </audio> </div> @endif @if ($message->type =='image') <a href="{{ $message->file?->host . $message->file?->address . $message->file?->name }}" target="_blank"> <img src="{{ $message->file?->host . $message->file?->address . $message->file?->name }}" alt="تصویر یافت نشد" width="100"> </a> @endif @if($message->type == 'text') <p> {{ $message?->content }} </p> @endif </div> <span class="text-xs text-gray-500 mt-1 block flex items-center justify-end"> <span> {{ $message?->created_at->format('H:i') }} </span> </span> </div> </div> @endif @endforeach @endif </div> <div class="audio-player" style="display: none;" id="audioPlayer"> <audio id="audioElement" preload="metadata"> <source src="" type="audio/webm"> <source src="" type="audio/ogg"> <source src="" type="audio/wav"> Your browser does not support the audio element. </audio> <div class="player-controls"> <button id="playPauseBtn" class="control-btn"> <span class="play-icon">▶️</span> <span class="pause-icon" style="display: none;">⏸️</span> </button> <button id="stopBtn" class="control-btn">⏹️</button> <div class="progress-container"> <div class="progress-bar"> <div class="progress-fill" id="progressFill"></div> </div> <div class="time-display"> <span id="currentTime">0:00</span> / <span id="duration">0:00</span> </div> </div> <div class="volume-container"> <button id="muteBtn" class="control-btn">�</button> <input type="range" id="volumeSlider" min="0" max="100" value="100" class="volume-slider"> </div> </div> </div> <!-- Message input --> <div class="bg-white border-t p-4"> <div class="flex items-center gap-2 relative"> <!-- Voice Recording Button --> <button wire:click="toggleRecording" class="p-2 rounded-full {{ $isRecording ? 'bg-red-500 text-white' : 'bg-gray-200 hover:bg-gray-300' }}" title="{{ $isRecording ? 'Stop Recording' : 'Start Recording' }}"> <i class="bx {{ $isRecording ? 'bx-stop' : 'bx-microphone' }}"></i> </button> <!-- Attachment Button --> <label for="attachment" class="p-2 rounded-full bg-gray-200 hover:bg-gray-300 cursor-pointer"> <i class="bx bx-paperclip"></i> </label> <input type="file" id="attachment" class="hidden" wire:model="attachment" > <!-- Message Input --> <div class="flex-1"> <input type="text" wire:model="message" wire:keydown.enter="sendTextMessage" class="w-full border rounded-full px-4 py-2 focus:outline-none focus:border-blue-500" placeholder="پیام خود را بنویسید..."> </div> <!-- Send Button --> <button wire:click="sendTextMessage" class="p-2 rounded-full bg-blue-500 text-white hover:bg-blue-600"> <i class="bx bx-send"></i> </button> </div> </div> </div> </div> </div> @section('scripts') <script> function scrollToBottom() { const messagesContainer = document.getElementById('messages'); setTimeout(() => { messagesContainer.scrollTop = messagesContainer.scrollHeight; }, 50); Livewire.dispatch('set-messages-as-read'); } document.addEventListener('DOMContentLoaded', function () { scrollToBottom(); Livewire.on('messageAdded', () => { scrollToBottom(); }); const messagesContainer = document.getElementById('messages'); messagesContainer.addEventListener('scroll', function () { if (isScrolledToBottom(messagesContainer)) { Livewire.dispatch('set-messages-as-read'); } }); }); const sidebar = document.getElementById('sidebar'); const hamburger = document.getElementById('hamburger'); const desktopHamburger = document.getElementById('desktop-hamburger'); function toggleSidebar() { sidebar.classList.toggle('translate-x-0'); sidebar.classList.toggle('-translate-x-full'); hamburger.classList.toggle('hamburger-active'); } hamburger.addEventListener('click', toggleSidebar); desktopHamburger.addEventListener('click', toggleSidebar); document.addEventListener('click', function (event) { if (window.innerWidth < 1024) { const isClickInsideSidebar = sidebar.contains(event.target); const isClickOnHamburger = hamburger.contains(event.target) || desktopHamburger.contains(event.target); if (!isClickInsideSidebar && !isClickOnHamburger && sidebar.classList.contains('translate-x-0')) { toggleSidebar(); } } }); const messagesContainer = document.getElementById('messages'); messagesContainer.scrollTop = messagesContainer.scrollHeight; function isScrolledToBottom(element) { return element.scrollHeight - element.scrollTop <= element.clientHeight + 5; } </script> <script> let mediaRecorder; let audioChunks = []; // Function to get the best supported audio format function getSupportedMimeType() { const types = [ 'audio/webm;codecs=opus', 'audio/webm', 'audio/ogg;codecs=opus', 'audio/ogg', 'audio/wav' ]; for (let type of types) { if (MediaRecorder.isTypeSupported(type)) { return type; } } return ''; // Fallback to default } document.addEventListener('livewire:initialized', () => { @this.on('toggleRecording', async ({isRecording}) => { if (isRecording) { try { // Add this check before creating MediaRecorder if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { alert('Your browser does not support audio recording.'); return; } if (!window.MediaRecorder) { alert('MediaRecorder is not supported in your browser.'); return; } const stream = await navigator.mediaDevices.getUserMedia({audio: true}); const mimeType = getSupportedMimeType(); mediaRecorder = new MediaRecorder(stream, { mimeType: mimeType }); mediaRecorder.ondataavailable = (event) => { audioChunks.push(event.data); }; mediaRecorder.onstop = () => { const audioBlob = new Blob(audioChunks, {type: mimeType}); const reader = new FileReader(); reader.readAsDataURL(audioBlob); reader.onloadend = () => { const base64Audio = reader.result; @this. call('handleAudioRecording', base64Audio); }; audioChunks = []; }; mediaRecorder.start(); } catch (error) { console.error('Error accessing microphone:', error); alert('Could not access microphone. Please check your permissions.'); @this. call('toggleRecording'); } } else if (mediaRecorder && mediaRecorder.state == 'recording') { mediaRecorder.stop(); mediaRecorder.stream.getTracks().forEach(track => track.stop()); } }); }); </script> @endsection
Coded With 💗 by
0x6ick Mod By V1rus_Alfa