Nachrichten: Emoji-Button volle Höhe, mIRC-Design-Toggle
This commit is contained in:
@@ -82,6 +82,8 @@ export default function Nachrichten({ toast, mobile }) {
|
||||
const [text, setText] = useState('');
|
||||
const [sending, setSending] = useState(false);
|
||||
const [showPicker, setShowPicker] = useState(false);
|
||||
const [mirc, setMirc] = useState(() => localStorage.getItem('dd_chat_design') === 'mirc');
|
||||
const toggleDesign = () => setMirc(v => { const n = !v; localStorage.setItem('dd_chat_design', n ? 'mirc' : 'modern'); return n; });
|
||||
const bottomRef = useRef(null);
|
||||
const pollRef = useRef(null);
|
||||
const pickerRef = useRef(null);
|
||||
@@ -244,11 +246,20 @@ export default function Nachrichten({ toast, mobile }) {
|
||||
<div style={{ padding:'16px 14px 12px', borderBottom:'1px solid rgba(255,255,255,0.06)',
|
||||
display:'flex', alignItems:'center', justifyContent:'space-between' }}>
|
||||
<div style={{ ...S.head, marginBottom:0 }}>NACHRICHTEN</div>
|
||||
<div style={{ display:'flex', gap:5 }}>
|
||||
<button onClick={toggleDesign} title={mirc ? 'Modern' : 'mIRC-Stil'}
|
||||
style={{ background: mirc ? 'rgba(78,205,196,0.12)' : 'transparent',
|
||||
border:`1px solid ${mirc ? 'rgba(78,205,196,0.3)' : 'rgba(255,255,255,0.1)'}`,
|
||||
borderRadius:7, color: mirc ? '#4ecdc4' : 'rgba(255,255,255,0.35)',
|
||||
cursor:'pointer', padding:'3px 7px', fontSize:10, fontFamily:'monospace', lineHeight:1 }}>
|
||||
{mirc ? '◈ mIRC' : '◈'}
|
||||
</button>
|
||||
<button onClick={() => { loadUsers(); if (activeUser) loadMessages(activeUser.id); }} title="Neu laden"
|
||||
style={{ background:'transparent', border:'1px solid rgba(255,255,255,0.1)', borderRadius:7,
|
||||
color:'rgba(255,255,255,0.35)', cursor:'pointer', padding:'3px 8px',
|
||||
fontSize:13, fontFamily:'monospace', lineHeight:1 }}>↺</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ flex:1, overflowY:'auto' }}>
|
||||
{users.length === 0 ? (
|
||||
<div style={{ color:'rgba(255,255,255,0.28)', fontSize:11, fontFamily:'monospace',
|
||||
@@ -349,7 +360,10 @@ export default function Nachrichten({ toast, mobile }) {
|
||||
</div>
|
||||
|
||||
{/* Nachrichten */}
|
||||
<div style={{ flex:1, overflowY:'auto', padding:'14px', display:'flex', flexDirection:'column', gap:10 }}>
|
||||
<div style={{ flex:1, overflowY:'auto', padding: mirc ? '0' : '14px',
|
||||
display:'flex', flexDirection:'column', gap: mirc ? 0 : 10,
|
||||
background: mirc ? '#000' : undefined,
|
||||
fontFamily: mirc ? "'Courier New', Courier, monospace" : undefined }}>
|
||||
{!activeUser.public_key && (
|
||||
<InfoBox color="#ffe66d">
|
||||
⚠ Dieser Benutzer hat die Nachrichten noch nicht geöffnet und besitzt noch kein Schlüsselpaar.
|
||||
@@ -357,25 +371,56 @@ export default function Nachrichten({ toast, mobile }) {
|
||||
)}
|
||||
{sharedKey === 'error' && (
|
||||
<InfoBox color="#ff6b9d">
|
||||
⚠ Schlüssel inkompatibel – der Empfänger verwendet ein anderes Schlüsselformat (z. B. altes P-256).
|
||||
⚠ Schlüssel inkompatibel – der Empfänger verwendet ein anderes Schlüsselformat.
|
||||
Er muss in Einstellungen → Sicherheit einen neuen Schlüssel generieren.
|
||||
</InfoBox>
|
||||
)}
|
||||
{messages.length === 0 && activeUser.public_key && sharedKey !== 'error' && (
|
||||
<div style={{ flex:1, display:'flex', alignItems:'center', justifyContent:'center',
|
||||
color:'rgba(255,255,255,0.18)', fontFamily:'monospace', fontSize:11 }}>
|
||||
Noch keine Nachrichten
|
||||
color: mirc ? '#808080' : 'rgba(255,255,255,0.18)',
|
||||
fontFamily: mirc ? "'Courier New', monospace" : 'monospace', fontSize:11 }}>
|
||||
{mirc ? '*** Noch keine Nachrichten ***' : 'Noch keine Nachrichten'}
|
||||
</div>
|
||||
)}
|
||||
{/* Hinweis wenn Nachrichten nicht entschlüsselt werden können */}
|
||||
{Object.values(decrypted).some(v => v === null) && (
|
||||
<InfoBox color="#ffe66d">
|
||||
🔑 Einige Nachrichten können nicht entschlüsselt werden – dein privater Schlüssel passt nicht mehr.{' '}
|
||||
Importiere deinen alten Schlüssel unter{' '}
|
||||
<strong>Einstellungen → Sicherheit → Schlüssel importieren</strong>,
|
||||
oder erzeuge einen neuen Schlüssel (löscht alle bisherigen Nachrichten).
|
||||
🔑 Einige Nachrichten können nicht entschlüsselt werden.
|
||||
Importiere deinen alten Schlüssel unter Einstellungen → Sicherheit.
|
||||
</InfoBox>
|
||||
)}
|
||||
|
||||
{mirc ? (
|
||||
/* ── mIRC Design ─────────────────────────────────────── */
|
||||
<div style={{ padding:'4px 0' }}>
|
||||
{messages.map(m => {
|
||||
const isMine = !!m.is_mine;
|
||||
const txt = decrypted[m.id];
|
||||
const time = m.created_at ? (() => { const d = new Date(m.created_at.replace(' ','T')); return isNaN(d)?'':d.toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'}); })() : '';
|
||||
const nick = isMine ? 'du' : activeUser.username;
|
||||
const nickColor = isMine ? '#00ff00' : '#00ffff';
|
||||
return (
|
||||
<div key={m.id} style={{ display:'flex', alignItems:'baseline', gap:0,
|
||||
padding:'1px 8px', lineHeight:1.5,
|
||||
background: isMine ? 'rgba(0,255,0,0.03)' : 'transparent' }}
|
||||
onMouseEnter={e=>e.currentTarget.style.background='rgba(255,255,255,0.04)'}
|
||||
onMouseLeave={e=>e.currentTarget.style.background=isMine?'rgba(0,255,0,0.03)':'transparent'}>
|
||||
<span style={{ color:'#808080', fontSize:11, flexShrink:0, marginRight:2 }}>[{time}]</span>
|
||||
<span style={{ color:'#808080', fontSize:11, flexShrink:0, marginRight:2 }}><</span>
|
||||
<span style={{ color:nickColor, fontSize:11, fontWeight:700, flexShrink:0 }}>{nick}</span>
|
||||
<span style={{ color:'#808080', fontSize:11, flexShrink:0, marginRight:6 }}>></span>
|
||||
<span style={{ color: txt===null?'#404040':'#d0d0d0', fontSize:12, wordBreak:'break-word', flex:1 }}>
|
||||
{txt===undefined ? '…' : txt===null ? '[verschlüsselt]' : txt}
|
||||
</span>
|
||||
{isMine && m.read_by_recipient && <span style={{color:'#004400',fontSize:9,marginLeft:4,flexShrink:0}}>✓✓</span>}
|
||||
<button onClick={() => deleteMsg(m.id)} style={{ background:'transparent', border:'none',
|
||||
cursor:'pointer', color:'#400000', fontSize:9, padding:'0 0 0 6px', flexShrink:0, opacity:0.5 }}>✕</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
/* ── Modernes Design ─────────────────────────────────── */
|
||||
<>
|
||||
{messages.map(m => {
|
||||
const isMine = !!m.is_mine;
|
||||
const txt = decrypted[m.id];
|
||||
@@ -385,9 +430,7 @@ export default function Nachrichten({ toast, mobile }) {
|
||||
{!isMine && (
|
||||
<div style={{ width:22, height:22, borderRadius:'50%', flexShrink:0,
|
||||
background:'rgba(255,255,255,0.04)', border:'1px solid rgba(255,255,255,0.07)',
|
||||
display:'flex', alignItems:'center', justifyContent:'center' }}>
|
||||
<UserIcon size={10} color="rgba(255,255,255,0.4)"/>
|
||||
</div>
|
||||
display:'flex', alignItems:'center', justifyContent:'center', fontSize:10 }}>👤</div>
|
||||
)}
|
||||
<div style={{ maxWidth:'75%', display:'flex', flexDirection:'column',
|
||||
alignItems: isMine ? 'flex-end' : 'flex-start', gap:4 }}>
|
||||
@@ -400,9 +443,7 @@ export default function Nachrichten({ toast, mobile }) {
|
||||
{txt === undefined ? (
|
||||
<span style={{ color:'rgba(255,255,255,0.18)', fontSize:12 }}>⋯</span>
|
||||
) : txt === null ? (
|
||||
<span style={{ color:'rgba(255,107,157,0.5)', fontSize:11, fontFamily:'monospace' }}>
|
||||
🔒 Nicht entschlüsselbar
|
||||
</span>
|
||||
<span style={{ color:'rgba(255,107,157,0.5)', fontSize:11, fontFamily:'monospace' }}>🔒 Nicht entschlüsselbar</span>
|
||||
) : (
|
||||
<span style={{ color:'#e6e6e6', fontSize:13, fontFamily:'monospace',
|
||||
lineHeight:1.55, whiteSpace:'pre-wrap', wordBreak:'break-word' }}>{txt}</span>
|
||||
@@ -428,19 +469,22 @@ export default function Nachrichten({ toast, mobile }) {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
<div ref={bottomRef}/>
|
||||
</div>
|
||||
|
||||
{/* Eingabe */}
|
||||
<div style={{ padding:'10px 14px', borderTop:'1px solid rgba(255,255,255,0.06)',
|
||||
background:'#0d0d0f', flexShrink:0 }}>
|
||||
<div style={{ padding:'10px 14px', flexShrink:0,
|
||||
borderTop:`1px solid ${mirc?'#333':'rgba(255,255,255,0.06)'}`,
|
||||
background: mirc ? '#000' : '#0d0d0f' }}>
|
||||
{!activeUser.public_key || sharedKey === 'error' ? (
|
||||
<div style={{ color:'rgba(255,255,255,0.2)', fontSize:11, fontFamily:'monospace',
|
||||
textAlign:'center', padding:'8px 0' }}>
|
||||
Senden nicht möglich
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ display:'flex', gap:8, alignItems:'flex-end', position:'relative' }}>
|
||||
<div style={{ display:'flex', gap:8, alignItems:'stretch', position:'relative' }}>
|
||||
<div ref={pickerRef} style={{ position:'absolute', bottom:'100%', right:0 }}>
|
||||
{showPicker && (
|
||||
<EmojiPicker
|
||||
@@ -449,16 +493,17 @@ export default function Nachrichten({ toast, mobile }) {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{mirc && (
|
||||
<span style={{ color:'#00ff00', fontFamily:"'Courier New',monospace", fontSize:12,
|
||||
alignSelf:'center', flexShrink:0 }}>[du]</span>
|
||||
)}
|
||||
<textarea
|
||||
value={text}
|
||||
onChange={e => {
|
||||
const val = e.target.value;
|
||||
const last = val[val.length - 1];
|
||||
if (last === ' ' || last === '\n') {
|
||||
setText(convertEmojis(val));
|
||||
} else {
|
||||
setText(val);
|
||||
}
|
||||
if (last === ' ' || last === '\n') setText(convertEmojis(val));
|
||||
else setText(val);
|
||||
}}
|
||||
onFocus={() => setShowPicker(false)}
|
||||
onKeyDown={e => {
|
||||
@@ -469,18 +514,18 @@ export default function Nachrichten({ toast, mobile }) {
|
||||
send(converted);
|
||||
}
|
||||
}}
|
||||
placeholder="Nachricht… (Enter senden · Shift+Enter neue Zeile)"
|
||||
placeholder={mirc ? 'Nachricht…' : 'Nachricht… (Enter senden · Shift+Enter neue Zeile)'}
|
||||
rows={2}
|
||||
style={{ ...S.inp, resize:'none', flex:1, fontSize:13, padding:'8px 10px',
|
||||
lineHeight:1.45, minHeight:42 }}
|
||||
style={{ ...S.inp, resize:'none', flex:1, fontSize: mirc ? 12 : 13,
|
||||
padding:'8px 10px', lineHeight:1.45, minHeight:42,
|
||||
...(mirc ? { background:'#111', border:'1px solid #444', color:'#d0d0d0',
|
||||
fontFamily:"'Courier New',monospace", borderRadius:2 } : {}) }}
|
||||
/>
|
||||
<div style={{ display:'flex', flexDirection:'column', gap:4, flexShrink:0, alignSelf:'stretch' }}>
|
||||
<button onClick={() => setShowPicker(v => !v)} style={{
|
||||
background: showPicker ? 'rgba(78,205,196,0.12)' : 'rgba(255,255,255,0.05)',
|
||||
border:`1px solid ${showPicker ? 'rgba(78,205,196,0.3)' : 'rgba(255,255,255,0.1)'}`,
|
||||
borderRadius:8, cursor:'pointer', fontSize:17, lineHeight:1,
|
||||
padding:'5px 8px', flexShrink:0, alignSelf:'flex-end' }}>😊</button>
|
||||
</div>
|
||||
background: showPicker ? 'rgba(78,205,196,0.12)' : (mirc ? '#111' : 'rgba(255,255,255,0.05)'),
|
||||
border:`1px solid ${showPicker ? 'rgba(78,205,196,0.3)' : (mirc ? '#444' : 'rgba(255,255,255,0.1)')}`,
|
||||
borderRadius: mirc ? 2 : 8, cursor:'pointer', fontSize:17,
|
||||
padding:'0 10px', flexShrink:0, alignSelf:'stretch' }}>😊</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user