Initial commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
<div class="space-y-4">
|
||||
@if($platformEnabled && $isEnabled)
|
||||
{{-- Informational Display --}}
|
||||
<div class="rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-4 space-y-3">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="flex-shrink-0 mt-0.5">
|
||||
<svg class="h-5 w-5 text-theme-primary" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('Automatic deletion enabled') }}
|
||||
</p>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ __('Messages will be deleted after') }}: <span class="font-semibold">{{ $this->formattedDuration }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($allowUsersToKeep)
|
||||
<div class="pt-3 border-t border-gray-200 dark:border-gray-700">
|
||||
<div class="flex items-start gap-2">
|
||||
<svg class="h-5 w-5 text-gray-700 dark:text-gray-300 mt-0.5 flex-shrink-0" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<p class="text-xs text-gray-600 dark:text-gray-400">
|
||||
@if ($this->formattedKeptDuration)
|
||||
{{ __('messages.wirechat.keep_messages_info') }} <span class="font-semibold">{{ $this->formattedKeptDuration }}</span>.
|
||||
@else
|
||||
{{ __('messages.wirechat.keep_messages_permanently') }}
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@elseif(!$platformEnabled)
|
||||
{{-- Feature Disabled --}}
|
||||
<div class="rounded-md bg-gray-50 dark:bg-gray-800 p-4">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ __('Automatic message deletion is currently disabled') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@elseif(!$isEnabled)
|
||||
{{-- Enabling... --}}
|
||||
<div class="rounded-md bg-yellow-50 dark:bg-yellow-900/20 p-4">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-5 w-5 text-yellow-400 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm text-yellow-700 dark:text-yellow-300">
|
||||
{{ __('Enabling automatic message deletion...') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
234
resources/views/livewire/wire-chat/typing-indicator.blade.php
Normal file
234
resources/views/livewire/wire-chat/typing-indicator.blade.php
Normal 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
|
||||
Reference in New Issue
Block a user