/* 07 Chat — cada match tiene su propia conversación única */ // Hilo inicial por match: textos distintos según su personalidad (MATCHES en screen-wall.jsx) const INITIAL_THREADS = { lucia: { day: 'Hoy · 8 sept', unread: 2, online: true, time: '10:24', msgs: [ { from:'lucia', t:'¡Hola! Te confieso que llevaba media hora dudando si escribir.', at:'10:12' }, { from:'you', t:'Ja, yo también. Bien por dar el paso.', at:'10:14' }, { from:'lucia', t:'Me flipó que tuvieras 93% de afinidad. Nunca me había pasado.', at:'10:15' }, { from:'you', t:'A mí tampoco. Y no soy fácil.', at:'10:16' }, { from:'lucia', t:'Vi que te gusta el cine indie. ¿Has visto lo último de Almodóvar?', at:'10:22' }, { from:'lucia', t:'¿Vamos al Reina Sofía este sábado?', at:'10:24' }, ], }, mateo: { day: 'Hoy · 8 sept', unread: 0, online: false, time: '09:52', msgs: [ { from:'mateo', t:'Ojo, lo de los vinilos también salió en mi test. Esto pinta bien.', at:'09:38' }, { from:'you', t:'Jajaja ¿cuántos discos tienes? Yo empiezo a no tener paredes.', at:'09:41' }, { from:'mateo', t:'Unos 400. Mi salón es básicamente una tienda de segunda mano con sofá.', at:'09:44' }, { from:'you', t:'Me encanta. Arquitecto además: diseñas la casa y la banda sonora.', at:'09:46' }, { from:'mateo', t:'Te iba a pasar mi móvil para hablar por ahí: 612 34 56 78', at:'09:47' }, { from:'mateo', t:'Ah. La app te lo acaba de tapar. Buena señal, la verdad.', at:'09:48' }, { from:'you', t:'Seguro que sí. Mejor aquí dentro, al principio al menos.', at:'09:50' }, { from:'mateo', t:'Trato hecho. Hay una feria de vinilos el sábado en Malasaña. ¿Te va?', at:'09:52' }, ], }, carmen: { day: 'Ayer · 7 sept', unread: 1, online: false, time: 'ayer', msgs: [ { from:'carmen', t:'Ya te digo que tu foto de perfil tiene buena luz. Es broma. Bueno, no del todo 😏', at:'18:20' }, { from:'you', t:'Jajaja ¿qué nota le pones del 1 al 10?', at:'18:22' }, { from:'carmen', t:'Un 7. El resto lo defiende tu cara de persona que sabe escuchar.', at:'18:24' }, { from:'carmen', t:'Hoy he revelado un carrete de la feria. Te enseñaría una foto pero aquí aún no dejan adjuntos 😅', at:'18:31' }, { from:'you', t:'Promesas, promesas.', at:'18:33' }, { from:'carmen', t:'El barrio está en feria y mi calle huele a rebujito. Un vermut en Feria puede curar cualquier cosa 🍷', at:'18:36' }, ], }, nerea: { day: 'Hoy · 8 sept', unread: 0, online: true, time: '12:15', msgs: [ { from:'nerea', t:'Vale, has sobrevivido al test. Ya es más de lo que hace la mayoría.', at:'12:03' }, { from:'you', t:'¿Y la mayoría qué hace?', at:'12:04' }, { from:'nerea', t:'Escribe "hola guapa". Automático. Cero esfuerzo.', at:'12:04' }, { from:'you', t:'Anotado. Yo prefiero el esfuerzo.', at:'12:05' }, { from:'nerea', t:'Bien. Primera prueba: ¿skate sí o skate no?', at:'12:06' }, { from:'you', t:'No skate. Pero corro y no me quejo.', at:'12:08' }, { from:'nerea', t:'Aceptable. Segunda: ¿has escuchado ambient alguna vez en tu vida?', at:'12:10' }, { from:'you', t:'Brian Eno mientras trabajo. Desaparecen tres horas.', at:'12:12' }, { from:'nerea', t:'Escuchando tu recomendación ahora mismo', at:'12:15' }, ], }, diego: { day: 'Anoche · 7 sept', unread: 0, online: false, time: '22:25', msgs: [ { from:'diego', t:'Acabo de salir del turno y lo primero que hago es escribirte. Analiza eso 😂', at:'22:14' }, { from:'you', t:'¿Buena señal o me ves cara de sueño?', at:'22:15' }, { from:'diego', t:'Buena señal. En mi trabajo aprendes a leer a la gente rápido.', at:'22:16' }, { from:'diego', t:'Te lanzo un reto: cena de tu vida, pero eliges 3 ingredientes imposibles.', at:'22:18' }, { from:'you', t:'Vale: café, chocolate y... ¿brócoli?', at:'22:20' }, { from:'diego', t:'JAJAJA brócoli. Aceptado. El chocolate con café me lo saco de postre fácil. El brócoli lo trabajo.', at:'22:21' }, { from:'diego', t:'Hablando en serio: cuando tengas un hueco, primera cita y todo eso 😄', at:'22:23' }, { from:'you', t:'Perfecto, entonces el jueves.', at:'22:25' }, ], }, }; const THREAD_ORDER = ['lucia','mateo','carmen','nerea','diego']; function threadPreview(thread) { const last = thread.msgs[thread.msgs.length - 1]; if (!last) return ''; return (last.from === 'you' ? 'Tú: ' : '') + last.t; } function Chat({ go }) { const [active, setActive] = useState('lucia'); const [threads, setThreads] = useState(INITIAL_THREADS); const [text, setText] = useState(''); const [showVideo, setShowVideo] = useState(false); const listRef = useRef(null); const activeThread = threads[active]; useEffect(() => { if (listRef.current) listRef.current.scrollTop = listRef.current.scrollHeight; }, [active, threads]); const openThread = (id) => { setActive(id); setThreads(prev => ({ ...prev, [id]: { ...prev[id], unread: 0 } })); }; const send = () => { if (!text.trim()) return; setThreads(prev => ({ ...prev, [active]: { ...prev[active], msgs: [...prev[active].msgs, { from:'you', t: text, at: 'ahora' }] }, })); setText(''); }; const activeMatch = MATCHES.find(m => m.id === active); return (
{activeMatch?.name}, {activeMatch?.age} Verificada
{activeMatch?.city} · Cruce-Q {activeMatch?.score}%
Chat cifrado extremo a extremo dentro de Cruce. Nadie ve estos mensajes — ni siquiera nosotros.
{activeThread.day}
{activeThread.msgs.map((m, i) => (
{m.from !== 'you' && (
)}
{m.at}
))}
Recuerda: Cruce nunca te pedirá tu número. Si alguien lo hace, repórtalo.
setText(e.target.value)} onKeyDown={e=>e.key === 'Enter' && send()} />
{showVideo && setShowVideo(false)}/>}
); } // Ocultar números de teléfono y emails automáticamente function ChatText({ text }) { const phone = /(\+?\d[\d\s\-]{7,}\d)/g; const email = /([\w\.\-]+@[\w\.\-]+\.\w+)/g; const parts = []; let last = 0; const marks = []; let m; while ((m = phone.exec(text)) !== null) marks.push({ i: m.index, len: m[0].length, kind: 'phone' }); while ((m = email.exec(text)) !== null) marks.push({ i: m.index, len: m[0].length, kind: 'email' }); marks.sort((a,b) => a.i - b.i); marks.forEach((mk, k) => { if (last < mk.i) parts.push({text.slice(last, mk.i)}); parts.push( {text.slice(mk.i, mk.i + mk.len)} OCULTO POR CRUCE ); last = mk.i + mk.len; }); if (last < text.length) parts.push({text.slice(last)}); return <>{parts}; } function VideoCallModal({ matchId, onClose }) { const match = MATCHES.find(m => m.id === matchId); return (
{match?.name}, {match?.age}
Llamada cifrada · Cruce Video
00:42
Cifrado E2E · Sin grabación · Sin conexiones externas
); } window.Chat = Chat;