Initial commit

This commit is contained in:
Ronald Huynen
2026-03-23 21:37:59 +01:00
commit 2547717edb
2193 changed files with 972171 additions and 0 deletions

View File

@@ -0,0 +1,234 @@
{{-- resources/views/livewire/wire-chat/typing-indicator.blade.php --}}
@php
// Defensive programming - ensure variables exist
$typingUsers = $typingUsers ?? [];
$showAvatars = $showAvatars ?? true;
$conversationId = $conversationId ?? 'unknown';
@endphp
{{-- Reduced polling frequency to avoid too many requests --}}
<div class="wirechat-typing-indicator" wire:poll.2000ms="loadTypingUsers">
{{-- Debug information --}}
{{-- @if(config('app.debug'))
<div class="text-xs text-theme-muted mb-2 p-2 bg-gray-100 dark:bg-theme-secondary rounded">
<strong>Debug Info:</strong><br>
Cache Driver: {{ config('cache.default') }}<br>
Conversation ID: {{ $conversationId }}<br>
Current User: {{ $currentUserId ?? 'null' }} ({{ $currentUserName ?? 'null' }})<br>
Current User Type: {{ $currentUserType ?? 'null' }}<br>
Typing Users: {{ is_array($typingUsers) ? count($typingUsers) : 'not-array' }}
@if(is_array($typingUsers) && count($typingUsers) > 0)
@foreach($typingUsers as $user)
<br>- {{ $user['user_name'] ?? 'Unknown' }} (ID: {{ $user['user_id'] ?? 'Unknown' }})
@endforeach
@endif
<br>
<button wire:click="debug" class="text-blue-500 underline text-xs">
Run Debug
</button>
</div>
@endif --}}
@if(is_array($typingUsers) && count($typingUsers) > 0)
<div class="flex items-center space-x-3 px-4 py-2 text-sm text-theme-muted dark:text-theme-muted rounded-lg transition-all duration-500 ease-in-out">
@if($showAvatars && count($typingUsers) <= 3)
<div class="flex -space-x-2">
@foreach($typingUsers as $index => $user)
@php
$userName = $user['user_name'] ?? 'User';
$userAvatar = Storage::url($user['avatar']) ?? null;
@endphp
@if($userAvatar)
<img src="{{ $userAvatar }}" alt="{{ $userName }}"
class="w-6 h-6 rounded-full border-1 border-theme-primary">
@else
<div class="w-6 h-6 bg-theme-primary rounded-full border-2 border-theme-background dark:border-theme-secondary flex items-center justify-center text-white text-xs font-medium">
{{ substr($userName, 0, 1) }}
</div>
@endif
@endforeach
</div>
@endif
<div class="flex items-center space-x-2">
<span class="italic">
@php
$count = count($typingUsers);
if ($count === 1) {
$text = ($typingUsers[0]['user_name'] ?? __('Someone')) . ' ' . __('is typing...');
} elseif ($count === 2) {
$name1 = $typingUsers[0]['user_name'] ?? __('Someone');
$name2 = $typingUsers[1]['user_name'] ?? __('Someone');
$text = $name1 . ' and ' . $name2 . ' ' . __('are typing...');
} else {
$name1 = $typingUsers[0]['user_name'] ?? __('Someone');
$text = $name1 . ' and ' . ($count - 1) . ' ' . __('others are typing...');
}
echo $text;
@endphp
</span>
</div>
</div>
@else
{{-- Show when no one is typing (for debugging) --}}
{{-- @if(config('app.debug'))
<div class="text-xs text-theme-muted p-1">
No one typing... Polling every 2 seconds for updates.
</div>
@endif --}}
@endif
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
function setupTypingDetection() {
const typingIndicators = document.querySelectorAll('.wirechat-typing-indicator');
typingIndicators.forEach((indicator, index) => {
const wireId = indicator.getAttribute('wire:id');
if (!wireId) {
return;
}
// Find WireChat specific inputs
const messageInputs = document.querySelectorAll(
'input[wire\\:model*="message"], ' +
'textarea[wire\\:model*="message"], ' +
'input[placeholder*="message"], ' +
'textarea[placeholder*="message"], ' +
'input[placeholder*="Type"], ' +
'textarea[placeholder*="Type"]'
);
if (messageInputs.length === 0) {
const allInputs = document.querySelectorAll('input[type="text"], textarea');
messageInputs = Array.from(allInputs);
}
let typingTimer;
let isTyping = false;
function startTyping() {
if (!isTyping) {
isTyping = true;
try {
const component = window.Livewire.find(wireId);
if (component && typeof component.call === 'function') {
component.call('startTyping');
} else {
}
} catch (e) {
}
}
// Reset timer
clearTimeout(typingTimer);
typingTimer = setTimeout(() => {
stopTyping();
}, 2000);
}
function stopTyping() {
if (isTyping) {
isTyping = false;
clearTimeout(typingTimer);
try {
const component = window.Livewire.find(wireId);
if (component && typeof component.call === 'function') {
component.call('stopTyping');
}
} catch (e) {
}
}
}
// Add event listeners
messageInputs.forEach((input, inputIndex) => {
const inputDesc = input.placeholder || input.getAttribute('wire:model') || `input-${inputIndex}`;
// Input event - most reliable
input.addEventListener('input', function(e) {
startTyping();
});
// Keydown event
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
stopTyping();
} else if (e.key.length === 1 || e.key === 'Backspace' || e.key === 'Delete') {
startTyping();
}
});
// Blur event
input.addEventListener('blur', function() {
setTimeout(() => {
if (isTyping) {
stopTyping();
}
}, 100);
});
});
});
}
// Setup with delay
setTimeout(setupTypingDetection, 500);
// Re-setup on navigation
document.addEventListener('livewire:navigated', function() {
setTimeout(setupTypingDetection, 500);
});
});
// Global test functions
window.testWireChatTyping = {
start: function() {
document.querySelectorAll('.wirechat-typing-indicator').forEach(indicator => {
const wireId = indicator.getAttribute('wire:id');
if (wireId) {
try {
const component = window.Livewire.find(wireId);
component.call('startTyping');
} catch (e) {
}
}
});
},
stop: function() {
document.querySelectorAll('.wirechat-typing-indicator').forEach(indicator => {
const wireId = indicator.getAttribute('wire:id');
if (wireId) {
try {
const component = window.Livewire.find(wireId);
component.call('stopTyping');
} catch (e) {
}
}
});
},
debug: function() {
document.querySelectorAll('.wirechat-typing-indicator').forEach(indicator => {
const wireId = indicator.getAttribute('wire:id');
if (wireId) {
try {
const component = window.Livewire.find(wireId);
component.call('debug');
} catch (e) {
}
}
});
}
};
</script>
@endpush