// screens-chat.jsx — chat list + thread (branchés sur l'API)
// === Helpers ===
function formatRelativeTime(iso, lang) {
if (!iso) return '';
const d = new Date(iso.replace(' ', 'T') + 'Z');
const now = new Date();
const diff = Math.floor((now - d) / 1000);
if (diff < 60) return lang === 'ka' ? 'ახლა' : 'maint.';
if (diff < 3600) return Math.floor(diff / 60) + (lang === 'ka' ? ' წ' : ' min');
if (diff < 86400) return Math.floor(diff / 3600) + 'h';
if (diff < 86400 * 7) return Math.floor(diff / 86400) + 'j';
return d.toLocaleDateString(lang === 'ka' ? 'ka-GE' : 'fr-FR');
}
function formatTimeShort(iso) {
if (!iso) return '';
const d = new Date(iso.replace(' ', 'T') + 'Z');
return d.getHours() + ':' + String(d.getMinutes()).padStart(2, '0');
}
// === ChatListScreen ===
function ChatListScreen({ lang, t, onOpenThread }) {
const [conversations, setConversations] = React.useState(null);
const [error, setError] = React.useState(null);
const [search, setSearch] = React.useState('');
const load = React.useCallback(async () => {
if (!window.API_AVAILABLE) { setConversations([]); return; }
try {
const list = await window.TrustaAPI.listConversations();
setConversations(list);
} catch (e) {
console.error(e);
setError(window.errorMessage(e, lang));
}
}, [lang]);
React.useEffect(() => { load(); }, [load]);
// Refresh quand on revient sur l'écran (toutes les 15s)
React.useEffect(() => {
const id = setInterval(load, 15000);
return () => clearInterval(id);
}, [load]);
const filtered = (conversations || []).filter(c =>
!search.trim() || (c.peer_name || '').toLowerCase().includes(search.toLowerCase())
);
return (
{conversations === null ? (
{lang === 'ka' ? 'იტვირთება...' : 'Chargement...'}
) : error ? (
{error}
) : filtered.length === 0 ? (
) : filtered.map(c => (
onOpenThread(c)} />
))}
);
}
function EmptyChats({ lang }) {
return (
💬
{lang === 'ka' ? 'ჯერ ჩატები არ გაქვს' : 'Pas encore de conversation'}
{lang === 'ka'
? 'დაიწყე საუბარი განცხადებიდან "შეტყობინება" ღილაკით'
: 'Démarre une conversation depuis une annonce avec "Contacter"'}
);
}
function ConvItem({ conv, lang, onOpen }) {
const me = window.TrustaAuth.getUser();
const myId = me ? me.id : 0;
const lastFromMe = conv.last_sender_id === myId;
const preview = conv.last_message_body || (lang === 'ka' ? 'საუბარი დაიწყო' : 'Conversation démarrée');
const photoSrc = conv.peer_photo || null;
return (
);
}
// === ChatThreadScreen ===
function ChatThreadScreen({ peer, lang, t, onBack, onOpenProfile }) {
// peer = conversation object (from list) OR { peer_id, peer_name, peer_photo, peer_verified, listing_id }
const [conv, setConv] = React.useState(peer && peer.id ? peer : null);
const [messages, setMessages] = React.useState([]);
const [draft, setDraft] = React.useState('');
const [sending, setSending] = React.useState(false);
const [error, setError] = React.useState(null);
const scrollRef = React.useRef(null);
const fileRef = React.useRef(null);
// Ouverture / création de la conversation si nécessaire
React.useEffect(() => {
if (conv) return;
if (!peer || !peer.peer_id || !window.API_AVAILABLE) return;
(async () => {
try {
const c = await window.TrustaAPI.openConversation(peer.peer_id, peer.listing_id || null);
setConv(c);
} catch (e) {
setError(window.errorMessage(e, lang));
}
})();
}, [conv, peer, lang]);
// Polling des messages toutes les 5s
const lastIdRef = React.useRef(0);
const fetchMessages = React.useCallback(async () => {
if (!conv || !window.API_AVAILABLE) return;
try {
const list = await window.TrustaAPI.listMessages(conv.id, lastIdRef.current);
if (list.length > 0) {
setMessages(prev => {
const existing = new Set(prev.map(m => m.id));
const merged = [...prev, ...list.filter(m => !existing.has(m.id))];
if (merged.length) lastIdRef.current = Math.max(...merged.map(m => m.id));
return merged;
});
}
} catch (e) {
console.error(e);
}
}, [conv]);
React.useEffect(() => {
if (!conv) return;
lastIdRef.current = 0;
setMessages([]);
fetchMessages();
const id = setInterval(fetchMessages, 5000);
return () => clearInterval(id);
}, [conv, fetchMessages]);
React.useEffect(() => {
if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
}, [messages]);
const send = async () => {
const text = draft.trim();
if (!text || sending || !conv) return;
setSending(true); setError(null);
try {
const msg = await window.TrustaAPI.sendMessage(conv.id, text);
setMessages(prev => [...prev, msg]);
lastIdRef.current = Math.max(lastIdRef.current, msg.id);
setDraft('');
} catch (e) {
setError(window.errorMessage(e, lang));
} finally {
setSending(false);
}
};
const sendPhoto = async (file) => {
if (!conv || !file || sending) return;
setSending(true); setError(null);
try {
const up = await window.TrustaAPI.uploadListingPhoto(file);
const msg = await window.TrustaAPI.sendMessage(conv.id, '', up.url);
setMessages(prev => [...prev, msg]);
lastIdRef.current = Math.max(lastIdRef.current, msg.id);
} catch (e) {
setError(window.errorMessage(e, lang));
} finally {
setSending(false);
if (fileRef.current) fileRef.current.value = '';
}
};
// Infos peer pour le header
const me = window.TrustaAuth.getUser();
const myId = me ? me.id : 0;
const peerName = (conv && conv.peer_name) || peer?.peer_name || (lang === 'ka' ? 'მომხმარებელი' : 'Utilisateur');
const peerPhoto = (conv && conv.peer_photo) || peer?.peer_photo || null;
const peerVerified = (conv && conv.peer_verified) || peer?.peer_verified || false;
return (
onOpenProfile && onOpenProfile({ id: conv?.peer_id || peer?.peer_id, name: { fr: peerName, ka: peerName }, avatar: peerPhoto, verified: peerVerified })}
style={{ display: 'flex', gap: 10, alignItems: 'center', background: 'transparent', border: 'none', cursor: 'pointer', padding: 0 }}>
}
/>
{/* Trust banner */}
{messages.map((m) => {
const me = m.sender_id === myId;
return (
{m.photo_url && (

)}
{m.body && (
{m.body}
)}
{formatTimeShort(m.created_at)}
);
})}
{error && (
{error}
)}
{/* Composer */}
e.target.files && e.target.files[0] && sendPhoto(e.target.files[0])}
/>
setDraft(e.target.value)}
onKeyDown={e => e.key === 'Enter' && send()}
placeholder={t('type_message')}
disabled={!conv || sending}
style={{
flex: 1, background: 'transparent', border: 'none', outline: 'none',
color: '#fff', fontFamily: 'Inter', fontSize: 14,
}}
/>
);
}
window.ChatListScreen = ChatListScreen;
window.ChatThreadScreen = ChatThreadScreen;