Files
animexe/resources/views/frontend/messages/show.blade.php
T
2026-07-14 00:01:48 +03:00

656 lines
26 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('frontend.layouts.app')
@section('title', ($other?->name ?? 'Mesaj') . ' — Animexe')
@push('styles')
<style>
footer{display:none!important}
#page-content{padding:0!important}
#ai-fab,#ai-panel{display:none!important}
#topnav,#mob-bottom-nav{display:none!important}
.w-chat-support{display:none!important}
.chat-root{
display:flex;flex-direction:column;
position:fixed;
top:0;left:0;right:0;bottom:0;
background:#05050e;
}
/* ── Header ─────────────────────────────── */
.chat-hdr{
display:flex;align-items:center;gap:10px;
padding:10px 14px;
background:rgba(5,5,14,.95);backdrop-filter:blur(12px);
border-bottom:1px solid rgba(255,255,255,.07);
flex-shrink:0;z-index:10;
}
.chat-hdr-back{
width:36px;height:36px;border-radius:10px;
display:flex;align-items:center;justify-content:center;
background:rgba(255,255,255,.06);border:none;
color:rgba(255,255,255,.6);font-size:1rem;cursor:pointer;
text-decoration:none;flex-shrink:0;transition:all .15s;
}
.chat-hdr-back:hover{background:rgba(255,255,255,.12);color:#fff}
.chat-hdr-ava{
width:38px;height:38px;border-radius:50%;overflow:hidden;flex-shrink:0;
background:linear-gradient(135deg,var(--accent2),var(--accent));
display:flex;align-items:center;justify-content:center;
font-size:.9rem;font-weight:800;color:#fff;text-decoration:none;
}
.chat-hdr-ava img{width:100%;height:100%;object-fit:cover}
.chat-hdr-info{flex:1;min-width:0}
.chat-hdr-name{font-size:.9rem;font-weight:800;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chat-hdr-sub{font-size:.68rem;color:rgba(255,255,255,.3)}
.chat-hdr-btn{
width:36px;height:36px;border-radius:10px;flex-shrink:0;
display:flex;align-items:center;justify-content:center;
background:rgba(255,255,255,.06);border:none;color:rgba(255,255,255,.5);
font-size:.9rem;cursor:pointer;text-decoration:none;transition:all .15s;
}
.chat-hdr-btn:hover{background:rgba(255,255,255,.12);color:#fff}
.chat-hdr-btn.call{color:#22c55e}
.chat-hdr-btn.call:hover{background:rgba(34,197,94,.15);color:#22c55e}
/* ── Messages area ──────────────────────── */
.chat-body{
flex:1;overflow-y:auto;
padding:14px 14px 8px;
display:flex;flex-direction:column;
gap:4px;
-webkit-overflow-scrolling:touch;
min-height:0;
}
.chat-body::-webkit-scrollbar{width:3px}
.chat-body::-webkit-scrollbar-thumb{background:rgba(255,255,255,.1);border-radius:2px}
/* ── Bubbles ────────────────────────────── */
.chat-msg{display:flex;gap:8px;max-width:80%;align-items:flex-end}
.chat-msg.mine{align-self:flex-end;flex-direction:row-reverse}
.chat-msg.theirs{align-self:flex-start}
.chat-msg-ava{
width:28px;height:28px;border-radius:50%;overflow:hidden;flex-shrink:0;
background:linear-gradient(135deg,var(--accent2),var(--accent));
display:flex;align-items:center;justify-content:center;
font-size:.65rem;font-weight:800;color:#fff;
}
.chat-msg-ava img{width:100%;height:100%;object-fit:cover}
.chat-msg.mine .chat-msg-ava{display:none}
.chat-bubble{
padding:9px 14px;border-radius:18px;
font-size:.88rem;line-height:1.5;word-break:break-word;max-width:100%;
}
.chat-msg.mine .chat-bubble{
background:var(--accent2);color:#05050e;font-weight:500;border-bottom-right-radius:5px;
}
.chat-msg.theirs .chat-bubble{
background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.08);
color:rgba(255,255,255,.9);border-bottom-left-radius:5px;
}
.chat-time{font-size:.6rem;color:rgba(255,255,255,.25);align-self:flex-end;margin-bottom:3px;flex-shrink:0}
.chat-date-sep{
text-align:center;font-size:.65rem;color:rgba(255,255,255,.2);
padding:12px 0 4px;font-weight:600;letter-spacing:.08em;
}
/* ── Media messages ─────────────────────── */
.chat-img{
max-width:220px;max-height:220px;border-radius:14px;
cursor:pointer;display:block;object-fit:cover;
transition:opacity .15s;border:1px solid rgba(255,255,255,.08);
}
.chat-img:hover{opacity:.88}
.chat-gif{max-width:220px;border-radius:14px;display:block}
/* ── Share card ─────────────────────────── */
.share-card{
display:flex;gap:10px;align-items:flex-start;
background:rgba(0,245,255,.06);border:1px solid rgba(0,245,255,.18);
border-radius:14px;padding:10px 12px;text-decoration:none;
max-width:260px;transition:all .15s;
}
.share-card:hover{background:rgba(0,245,255,.12);border-color:rgba(0,245,255,.35)}
.share-card-img{width:54px;height:78px;border-radius:8px;overflow:hidden;flex-shrink:0;background:rgba(255,255,255,.05)}
.share-card-img img{width:100%;height:100%;object-fit:cover;display:block}
.share-card-label{font-size:.58rem;color:var(--accent2);font-weight:700;letter-spacing:.08em;text-transform:uppercase;margin-bottom:4px}
.share-card-title{font-size:.8rem;font-weight:800;color:#fff;line-height:1.3;margin-bottom:3px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.share-card-ep{font-size:.7rem;color:rgba(255,255,255,.5);margin-bottom:3px}
.share-card-play{font-size:.68rem;color:var(--accent2);display:flex;align-items:center;gap:3px;margin-top:4px;font-weight:700}
/* ── Footer input ───────────────────────── */
.chat-footer{
padding:8px 10px;
background:rgba(5,5,14,.95);backdrop-filter:blur(12px);
border-top:1px solid rgba(255,255,255,.07);
flex-shrink:0;
padding-bottom:max(8px,env(safe-area-inset-bottom));
}
.chat-toolbar{
display:flex;align-items:center;gap:6px;
padding:4px 0 8px;
border-bottom:1px solid rgba(255,255,255,.06);
margin-bottom:6px;
}
.chat-tool-btn{
width:38px;height:38px;border-radius:10px;border:none;
background:rgba(255,255,255,.06);color:rgba(255,255,255,.45);
font-size:.88rem;cursor:pointer;
display:flex;align-items:center;justify-content:center;
transition:all .15s;flex-shrink:0;
-webkit-tap-highlight-color:transparent;
}
.chat-tool-btn:hover{background:rgba(255,255,255,.13);color:#fff}
.chat-tool-btn.active{background:rgba(0,245,255,.12);color:var(--accent2)}
.chat-form{display:flex;align-items:flex-end;gap:8px}
.chat-input{
flex:1;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.1);
border-radius:20px;padding:10px 16px;color:#fff;font-size:.9rem;font-family:inherit;
resize:none;max-height:120px;overflow-y:auto;min-height:42px;
line-height:1.4;outline:none;-webkit-appearance:none;transition:border-color .15s;
}
.chat-input:focus{border-color:rgba(0,245,255,.35)}
.chat-input::placeholder{color:rgba(255,255,255,.25)}
.chat-send{
width:42px;height:42px;border-radius:50%;flex-shrink:0;
background:var(--accent2);border:none;color:#05050e;
font-size:1rem;cursor:pointer;
display:flex;align-items:center;justify-content:center;
transition:all .15s;-webkit-tap-highlight-color:transparent;
}
.chat-send:hover{background:#00d4de;transform:scale(1.05)}
.chat-send:disabled{opacity:.4;transform:none;cursor:default}
/* ── Emoji picker ───────────────────────── */
#chat-emoji-picker{
display:none;flex-wrap:wrap;gap:2px;
background:rgba(10,10,22,.98);border:1px solid rgba(255,255,255,.1);
border-radius:14px;padding:10px;
max-height:180px;overflow-y:auto;margin-bottom:6px;
scrollbar-width:thin;
}
#chat-emoji-picker.show{display:flex}
.chat-emoji-btn{
font-size:1.3rem;width:36px;height:36px;
display:flex;align-items:center;justify-content:center;
border:none;background:none;cursor:pointer;border-radius:8px;
transition:background .12s;-webkit-tap-highlight-color:transparent;
}
.chat-emoji-btn:hover{background:rgba(255,255,255,.1)}
/* ── GIF picker ─────────────────────────── */
#chat-gif-picker{
display:none;
background:rgba(10,10,22,.98);border:1px solid rgba(255,255,255,.1);
border-radius:14px;padding:8px;margin-bottom:6px;
}
#chat-gif-picker.show{display:block}
.chat-gif-search{
width:100%;padding:8px 12px;border-radius:10px;
background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);
color:#fff;font-size:.82rem;font-family:inherit;outline:none;
margin-bottom:8px;-webkit-appearance:none;
}
.chat-gif-search::placeholder{color:rgba(255,255,255,.3)}
.chat-gif-grid{
display:grid;grid-template-columns:repeat(3,1fr);gap:4px;
max-height:160px;overflow-y:auto;
}
.chat-gif-item{
aspect-ratio:1;background:rgba(255,255,255,.05);border-radius:8px;
overflow:hidden;cursor:pointer;transition:opacity .15s;
}
.chat-gif-item:hover{opacity:.8}
.chat-gif-item img{width:100%;height:100%;object-fit:cover;display:block}
.chat-gif-ph{padding:20px;text-align:center;color:rgba(255,255,255,.25);font-size:.78rem}
.chat-empty{
flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
color:rgba(255,255,255,.2);gap:10px;
}
.chat-empty i{font-size:2.4rem;opacity:.2}
.chat-empty p{font-size:.85rem}
@media(min-width:640px){
.chat-root{max-width:760px;left:50%;transform:translateX(-50%)}
.chat-msg{max-width:72%}
}
@media(max-width:640px){
.chat-msg{max-width:88%}
}
</style>
@endpush
@section('content')
@php $me = auth()->user(); @endphp
<div class="chat-root">
{{-- Header --}}
<div class="chat-hdr">
<a href="{{ route('messages.index') }}" class="chat-hdr-back">
<i class="bi bi-chevron-left"></i>
</a>
<a href="{{ $other ? route('user.profile', $other) : '#' }}" class="chat-hdr-ava">
@if($other?->avatar)
<img src="{{ \App\Support\MediaUrl::fromStoragePath($other->avatar) }}" alt="{{ $other->name }}">
@else
{{ mb_strtoupper(mb_substr($other?->name ?? '?', 0, 1)) }}
@endif
</a>
<div class="chat-hdr-info">
<div class="chat-hdr-name">{{ $other?->name ?? 'Silinmiş Kullanıcı' }}</div>
@if($other?->username)
<div class="chat-hdr-sub">@php echo '@' . e($other->username); @endphp</div>
@endif
</div>
@if($other)
{{-- Sesli arama --}}
<button class="chat-hdr-btn call" id="call-btn" data-user-id="{{ $other->id }}" title="Sesli ara">
<i class="bi bi-telephone-fill"></i>
</button>
{{-- Profil --}}
<a href="{{ route('user.profile', $other) }}" class="chat-hdr-btn" title="Profil">
<i class="bi bi-person"></i>
</a>
@endif
</div>
{{-- Messages --}}
<div class="chat-body" id="chat-body">
@if($messages->isEmpty())
<div class="chat-empty">
<i class="bi bi-chat-dots"></i>
<p>Henüz mesaj yok. İlk mesajı sen gönder!</p>
</div>
@else
@php $lastDate = null; @endphp
@foreach($messages as $msg)
@php
$msgDate = $msg->created_at->toDateString();
$isMine = $msg->user_id === $me->id;
@endphp
@if($msgDate !== $lastDate)
<div class="chat-date-sep">{{ $msg->created_at->translatedFormat('d F Y') }}</div>
@php $lastDate = $msgDate; @endphp
@endif
<div class="chat-msg {{ $isMine ? 'mine' : 'theirs' }}" data-id="{{ $msg->id }}">
@if(!$isMine)
<div class="chat-msg-ava">
@if($msg->user?->avatar)
<img src="{{ \App\Support\MediaUrl::fromStoragePath($msg->user->avatar) }}" alt="">
@else{{ mb_strtoupper(mb_substr($msg->user?->name ?? '?', 0, 1)) }}@endif
</div>
@endif
<div>
@if(str_starts_with($msg->body, 'ANIMESHARE::'))
@php try { $sd = json_decode(substr($msg->body,12),true); } catch(\Throwable $e){ $sd=null; } @endphp
@if($sd)
<a href="{{ url('/watch/'.($sd['slug']??'').'/'.($sd['s']??1).'/'.($sd['e']??1).(($sd['ts']??0)>0?'?t='.$sd['ts']:'')) }}" class="share-card">
<div class="share-card-img">
@if(!empty($sd['cover']))<img src="{{ $sd['cover'] }}" loading="lazy" alt="">@else<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:rgba(255,255,255,.2);font-size:1.4rem"><i class="bi bi-play-circle"></i></div>@endif
</div>
<div>
<div class="share-card-label"><i class="bi bi-play-fill"></i> Anime Bölümü</div>
<div class="share-card-title">{{ $sd['title'] ?? '' }}</div>
<div class="share-card-ep">{{ $sd['s'] ?? '' }}. Sezon {{ $sd['e'] ?? '' }}. Bölüm</div>
<div class="share-card-play"><i class="bi bi-play-circle-fill"></i> İzle</div>
</div>
</a>
@else
<div class="chat-bubble">{{ $msg->body }}</div>
@endif
@elseif(str_starts_with($msg->body, 'IMAGE::'))
<img src="{{ substr($msg->body, 7) }}" class="chat-img" loading="lazy" alt="Resim"
onclick="window.open('{{ substr($msg->body, 7) }}','_blank')">
@elseif(str_starts_with($msg->body, 'GIF::'))
<img src="{{ substr($msg->body, 5) }}" class="chat-gif" loading="lazy" alt="GIF">
@else
<div class="chat-bubble">{{ $msg->body }}</div>
@endif
</div>
<span class="chat-time">{{ $msg->created_at->format('H:i') }}</span>
</div>
@endforeach
@endif
</div>
{{-- Footer --}}
<div class="chat-footer">
<div id="chat-emoji-picker"></div>
<div id="chat-gif-picker">
<input type="text" class="chat-gif-search" id="chat-gif-input" placeholder="GIF ara…">
<div class="chat-gif-grid" id="chat-gif-grid">
<div class="chat-gif-ph">GIF aramak için yaz</div>
</div>
</div>
<div class="chat-toolbar">
<button class="chat-tool-btn" id="chat-emoji-btn" title="Emoji" style="display:none">
<i class="bi bi-emoji-smile"></i>
</button>
<button class="chat-tool-btn" id="chat-gif-btn" title="GIF">
<span style="font-size:.65rem;font-weight:800;letter-spacing:.03em">GIF</span>
</button>
<label class="chat-tool-btn" title="Resim gönder" style="cursor:pointer">
<i class="bi bi-image"></i>
<input type="file" id="chat-img-input" accept="image/*" style="display:none">
</label>
<div style="flex:1"></div>
</div>
<div class="chat-form">
<textarea class="chat-input" id="chat-input"
placeholder="Mesaj yaz..." rows="1" maxlength="2000"
autocomplete="off" autocorrect="off"></textarea>
<button type="button" class="chat-send" id="chat-send">
<i class="bi bi-send-fill"></i>
</button>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
// Crisp ve diğer overlay widgetları kapat
try { $crisp.push(['do','chat:hide']); } catch(_){}
document.addEventListener('DOMContentLoaded', function(){
try { $crisp.push(['do','chat:hide']); } catch(_){}
// topnav/bottom-nav kesinlikle gizle (layout JS override için)
const tn = document.getElementById('topnav');
const bn = document.getElementById('mob-bottom-nav');
if(tn) tn.style.setProperty('display','none','important');
if(bn) bn.style.setProperty('display','none','important');
});
</script>
<script>
(function(){
'use strict';
const SEND_URL = '{{ route("messages.send", $conversation) }}';
const POLL_URL = '{{ route("messages.poll", $conversation) }}';
const UPLOAD_URL = '{{ route("messages.upload-image") }}';
const TENOR_KEY = '{{ env("TENOR_API_KEY","") }}';
const CSRF = document.querySelector('meta[name=csrf-token]')?.content || '';
const ME_ID = {{ $me->id }};
const MY_AVA = {!! json_encode($me->avatar ? \App\Support\MediaUrl::fromStoragePath($me->avatar) : null) !!};
const MY_NAME = {!! json_encode($me->name) !!};
const IS_DESKTOP = window.innerWidth >= 640;
let lastId = {{ $messages->last()?->id ?? 0 }};
let polling = null;
let emojiOpen = false;
let gifOpen = false;
let gifTimer = null;
const chatBody = document.getElementById('chat-body');
const inputEl = document.getElementById('chat-input');
const sendBtn = document.getElementById('chat-send');
/* ── Scroll ──────────────────────────────── */
function scrollBottom(force = false) {
const near = chatBody.scrollHeight - chatBody.scrollTop - chatBody.clientHeight < 160;
if (force || near) chatBody.scrollTop = chatBody.scrollHeight;
}
scrollBottom(true);
/* ── Input auto-grow ─────────────────────── */
inputEl.addEventListener('input', () => {
inputEl.style.height = 'auto';
inputEl.style.height = Math.min(inputEl.scrollHeight, 120) + 'px';
if (inputEl.value) { hideEmoji(); hideGif(); }
});
inputEl.addEventListener('keydown', e => {
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); doSend(); }
});
sendBtn.addEventListener('click', doSend);
/* ── Escape chars ────────────────────────── */
function escH(s) {
return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
/* ── Render message body ─────────────────── */
function renderBody(body) {
if (!body) return '<div class="chat-bubble"></div>';
if (body.startsWith('ANIMESHARE::')) {
try {
const d = JSON.parse(body.slice(12));
const url = `/watch/${d.slug}/${d.s}/${d.e}${d.ts>0?'?t='+d.ts:''}`;
const img = d.cover
? `<img src="${escH(d.cover)}" loading="lazy" alt="">`
: `<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:rgba(255,255,255,.2)"><i class="bi bi-play-circle" style="font-size:1.4rem"></i></div>`;
return `<a href="${escH(url)}" class="share-card">
<div class="share-card-img">${img}</div>
<div>
<div class="share-card-label"><i class="bi bi-play-fill"></i> Anime Bölümü</div>
<div class="share-card-title">${escH(d.title||'')}</div>
<div class="share-card-ep">${escH(String(d.s))}. Sezon ${escH(String(d.e))}. Bölüm</div>
<div class="share-card-play"><i class="bi bi-play-circle-fill"></i> İzle</div>
</div>
</a>`;
} catch(_) {}
}
if (body.startsWith('IMAGE::')) {
const url = escH(body.slice(7));
return `<img src="${url}" class="chat-img" loading="lazy" alt="Resim" onclick="window.open('${url}','_blank')">`;
}
if (body.startsWith('GIF::')) {
return `<img src="${escH(body.slice(5))}" class="chat-gif" loading="lazy" alt="GIF">`;
}
return `<div class="chat-bubble">${escH(body)}</div>`;
}
/* ── Append message to DOM ───────────────── */
function appendMsg(id, body, isMine, time, avatar, name) {
if (document.querySelector(`.chat-msg[data-id="${id}"]`)) return;
document.querySelector('.chat-empty')?.remove();
const wrap = document.createElement('div');
wrap.className = 'chat-msg ' + (isMine ? 'mine' : 'theirs');
wrap.dataset.id = id;
let ava = '';
if (!isMine) {
ava = avatar
? `<div class="chat-msg-ava"><img src="${escH(avatar)}" alt=""></div>`
: `<div class="chat-msg-ava">${escH((name||'?').charAt(0).toUpperCase())}</div>`;
}
wrap.innerHTML = ava + `<div>${renderBody(body)}</div><span class="chat-time">${escH(time)}</span>`;
chatBody.appendChild(wrap);
scrollBottom();
}
/* ── Send text ───────────────────────────── */
async function doSend() {
const text = inputEl.value.trim();
if (!text) return;
sendBtn.disabled = true;
inputEl.value = '';
inputEl.style.height = 'auto';
try {
const res = await fetch(SEND_URL, {
method: 'POST',
headers: {'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json', 'Content-Type': 'application/json'},
body: JSON.stringify({ body: text }),
});
if (!res.ok) throw new Error();
const d = await res.json();
appendMsg(d.id, d.body, true, d.created_at, MY_AVA, MY_NAME);
if (d.id > lastId) lastId = d.id;
scrollBottom(true);
} catch(e) {
inputEl.value = text;
}
sendBtn.disabled = false;
inputEl.focus();
}
/* ── Send image ──────────────────────────── */
document.getElementById('chat-img-input')?.addEventListener('change', async function() {
const file = this.files[0];
if (!file) return;
this.value = '';
try {
const fd = new FormData();
fd.append('image', file);
const r = await fetch(UPLOAD_URL, {
method: 'POST',
headers: {'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json'},
body: fd,
});
const d = await r.json();
if (!d.url) throw new Error();
const r2 = await fetch(SEND_URL, {
method: 'POST',
headers: {'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json', 'Content-Type': 'application/json'},
body: JSON.stringify({ body: 'IMAGE::' + d.url }),
});
const d2 = await r2.json();
appendMsg(d2.id, d2.body, true, d2.created_at, MY_AVA, MY_NAME);
if (d2.id > lastId) lastId = d2.id;
scrollBottom(true);
} catch(e) {
if (window.showToast) showToast('Resim gönderilemedi.', 'error');
}
});
/* ── Send GIF ────────────────────────────── */
window.chatSendGif = async function(gifUrl) {
hideGif();
try {
const r = await fetch(SEND_URL, {
method: 'POST',
headers: {'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json', 'Content-Type': 'application/json'},
body: JSON.stringify({ body: 'GIF::' + gifUrl }),
});
const d = await r.json();
appendMsg(d.id, d.body, true, d.created_at, MY_AVA, MY_NAME);
if (d.id > lastId) lastId = d.id;
scrollBottom(true);
} catch(_) {}
};
/* ── Polling ─────────────────────────────── */
async function poll() {
try {
const res = await fetch(`${POLL_URL}?after=${lastId}`, {
headers: {'Accept': 'application/json', 'X-CSRF-TOKEN': CSRF}
});
const d = await res.json();
d.messages.forEach(m => {
if (m.user_id !== ME_ID) {
appendMsg(m.id, m.body, false, m.created_at, m.avatar, m.name);
}
if (m.id > lastId) lastId = m.id;
});
} catch(_) {}
}
function startPoll() { polling = setInterval(poll, 3000); }
function stopPoll() { clearInterval(polling); polling = null; }
document.addEventListener('visibilitychange', () => document.hidden ? stopPoll() : startPoll());
startPoll();
/* ── Emoji picker ────────────────────────── */
const EMOJIS = '😀😂😅😊😍🥰😘😎😭😤😡🥺🤔😴🤩🥳😱🙈🙄😏🤗😬😵👍👎❤️🔥💯✨🎉🎊🎮🎬🎵🍕🍜🍣🎃👻🦊🐱🐶🐼🌸🌙⭐🌈💎🚀🏆🎯🔮';
function initEmoji() {
const p = document.getElementById('chat-emoji-picker');
if (!p || p.dataset.built) return;
p.dataset.built = '1';
[...EMOJIS].forEach(em => {
const btn = document.createElement('button');
btn.className = 'chat-emoji-btn';
btn.textContent = em;
btn.addEventListener('click', () => {
const pos = inputEl.selectionStart;
inputEl.value = inputEl.value.slice(0, pos) + em + inputEl.value.slice(inputEl.selectionEnd);
inputEl.selectionStart = inputEl.selectionEnd = pos + em.length;
inputEl.dispatchEvent(new Event('input'));
inputEl.focus();
});
p.appendChild(btn);
});
}
function toggleEmoji() {
emojiOpen = !emojiOpen;
if (emojiOpen) { hideGif(); initEmoji(); }
document.getElementById('chat-emoji-picker').classList.toggle('show', emojiOpen);
document.getElementById('chat-emoji-btn').classList.toggle('active', emojiOpen);
}
function hideEmoji() {
emojiOpen = false;
document.getElementById('chat-emoji-picker')?.classList.remove('show');
document.getElementById('chat-emoji-btn')?.classList.remove('active');
}
document.getElementById('chat-emoji-btn')?.addEventListener('click', toggleEmoji);
// Desktop'ta emoji butonu göster
if (IS_DESKTOP) {
const eb = document.getElementById('chat-emoji-btn');
if (eb) eb.style.display = '';
}
/* ── GIF picker ──────────────────────────── */
async function searchGifs(q) {
const grid = document.getElementById('chat-gif-grid');
if (!grid) return;
if (!TENOR_KEY) {
grid.innerHTML = '<div class="chat-gif-ph">Tenor API key ayarlanmamış.</div>';
return;
}
grid.innerHTML = '<div class="chat-gif-ph">Yükleniyor…</div>';
try {
const ep = q
? `https://tenor.googleapis.com/v2/search?q=${encodeURIComponent(q)}&key=${TENOR_KEY}&limit=12&media_filter=gif`
: `https://tenor.googleapis.com/v2/featured?key=${TENOR_KEY}&limit=12&media_filter=gif`;
const r = await fetch(ep);
const d = await r.json();
const results = d.results || [];
if (!results.length) { grid.innerHTML = '<div class="chat-gif-ph">Sonuç yok.</div>'; return; }
grid.innerHTML = '';
results.forEach(g => {
const url = g.media_formats?.tinygif?.url || g.media_formats?.gif?.url || '';
if (!url) return;
const item = document.createElement('div');
item.className = 'chat-gif-item';
item.innerHTML = `<img src="${escH(url)}" alt="" loading="lazy">`;
item.addEventListener('click', () => window.chatSendGif(url));
grid.appendChild(item);
});
} catch(_) { grid.innerHTML = '<div class="chat-gif-ph">Yüklenemedi.</div>'; }
}
function toggleGif() {
gifOpen = !gifOpen;
if (gifOpen) { hideEmoji(); if (TENOR_KEY) searchGifs(''); setTimeout(() => document.getElementById('chat-gif-input')?.focus(), 80); }
document.getElementById('chat-gif-picker').classList.toggle('show', gifOpen);
document.getElementById('chat-gif-btn').classList.toggle('active', gifOpen);
}
function hideGif() {
gifOpen = false;
document.getElementById('chat-gif-picker')?.classList.remove('show');
document.getElementById('chat-gif-btn')?.classList.remove('active');
}
document.getElementById('chat-gif-btn')?.addEventListener('click', toggleGif);
document.getElementById('chat-gif-input')?.addEventListener('input', function() {
clearTimeout(gifTimer);
gifTimer = setTimeout(() => searchGifs(this.value.trim()), 380);
});
/* ── Sesli arama ─────────────────────────── */
document.getElementById('call-btn')?.addEventListener('click', async function() {
const userId = this.dataset.userId;
if (!userId || !window.cpStartCall) { if (window.showToast) showToast('Arama özelliği yüklenemedi.','error'); return; }
window.cpConvUserId = userId;
window.cpStartCall();
});
/* ── Auto focus ──────────────────────────── */
if (IS_DESKTOP) inputEl.focus();
})();
</script>
@endpush