Nachrichten: Design-Label, server-seitige Präferenz, mobiler Leerraum fix
This commit is contained in:
@@ -246,6 +246,11 @@ if (msgCols2.length && !msgCols2.includes('read_by_recipient')) {
|
|||||||
db.exec("ALTER TABLE messages ADD COLUMN read_by_recipient INTEGER NOT NULL DEFAULT 0");
|
db.exec("ALTER TABLE messages ADD COLUMN read_by_recipient INTEGER NOT NULL DEFAULT 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User-Preferences (JSON-Blob)
|
||||||
|
const userCols2 = db.prepare("PRAGMA table_info(users)").all().map(r => r.name);
|
||||||
|
if (!userCols2.includes('preferences'))
|
||||||
|
db.exec("ALTER TABLE users ADD COLUMN preferences TEXT DEFAULT NULL");
|
||||||
|
|
||||||
// ── Login-Sicherheit ──────────────────────────────────────────────────────────
|
// ── Login-Sicherheit ──────────────────────────────────────────────────────────
|
||||||
// Spalten für Account-Lockout in users-Tabelle
|
// Spalten für Account-Lockout in users-Tabelle
|
||||||
const userCols = db.prepare("PRAGMA table_info(users)").all().map(r => r.name);
|
const userCols = db.prepare("PRAGMA table_info(users)").all().map(r => r.name);
|
||||||
|
|||||||
@@ -150,4 +150,20 @@ router.delete('/account', authenticate, (req, res) => {
|
|||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── GET/PUT /api/auth/preferences ─────────────────────────────────────────────
|
||||||
|
router.get('/preferences', authenticate, (req, res) => {
|
||||||
|
const user = db.prepare('SELECT preferences FROM users WHERE id=?').get(req.user.id);
|
||||||
|
try { res.json(JSON.parse(user?.preferences || '{}')); }
|
||||||
|
catch { res.json({}); }
|
||||||
|
});
|
||||||
|
|
||||||
|
router.put('/preferences', authenticate, (req, res) => {
|
||||||
|
const user = db.prepare('SELECT preferences FROM users WHERE id=?').get(req.user.id);
|
||||||
|
let current = {};
|
||||||
|
try { current = JSON.parse(user?.preferences || '{}'); } catch {}
|
||||||
|
const merged = { ...current, ...req.body };
|
||||||
|
db.prepare('UPDATE users SET preferences=? WHERE id=?').run(JSON.stringify(merged), req.user.id);
|
||||||
|
res.json(merged);
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -1660,7 +1660,7 @@ export default function App() {
|
|||||||
const activeTool = TOOLS.find(t=>t.id===active);
|
const activeTool = TOOLS.find(t=>t.id===active);
|
||||||
|
|
||||||
// Mobile padding bottom für fixed nav
|
// Mobile padding bottom für fixed nav
|
||||||
const mainStyle = { flex:1, overflowY:'auto', ...(mobile ? { paddingBottom:'calc(56px + env(safe-area-inset-bottom, 0px))' } : {}) };
|
const mainStyle = { flex:1, overflowY:'auto', ...(mobile ? { paddingBottom: active==='nachrichten' ? 0 : 'calc(56px + env(safe-area-inset-bottom, 0px))' } : {}) };
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -82,8 +82,16 @@ export default function Nachrichten({ toast, mobile }) {
|
|||||||
const [text, setText] = useState('');
|
const [text, setText] = useState('');
|
||||||
const [sending, setSending] = useState(false);
|
const [sending, setSending] = useState(false);
|
||||||
const [showPicker, setShowPicker] = useState(false);
|
const [showPicker, setShowPicker] = useState(false);
|
||||||
const [mirc, setMirc] = useState(() => localStorage.getItem('dd_chat_design') === 'mirc');
|
const [mirc, setMirc] = useState(false);
|
||||||
const toggleDesign = () => setMirc(v => { const n = !v; localStorage.setItem('dd_chat_design', n ? 'mirc' : 'modern'); return n; });
|
// Preference vom Server laden
|
||||||
|
useEffect(() => {
|
||||||
|
api('/auth/preferences').then(p => { if (p.chat_design === 'mirc') setMirc(true); }).catch(()=>{});
|
||||||
|
}, []);
|
||||||
|
const toggleDesign = () => setMirc(v => {
|
||||||
|
const next = !v;
|
||||||
|
api('/auth/preferences', { method:'PUT', body:{ chat_design: next ? 'mirc' : 'modern' } }).catch(()=>{});
|
||||||
|
return next;
|
||||||
|
});
|
||||||
const bottomRef = useRef(null);
|
const bottomRef = useRef(null);
|
||||||
const pollRef = useRef(null);
|
const pollRef = useRef(null);
|
||||||
const pickerRef = useRef(null);
|
const pickerRef = useRef(null);
|
||||||
@@ -247,12 +255,12 @@ export default function Nachrichten({ toast, mobile }) {
|
|||||||
display:'flex', alignItems:'center', justifyContent:'space-between' }}>
|
display:'flex', alignItems:'center', justifyContent:'space-between' }}>
|
||||||
<div style={{ ...S.head, marginBottom:0 }}>NACHRICHTEN</div>
|
<div style={{ ...S.head, marginBottom:0 }}>NACHRICHTEN</div>
|
||||||
<div style={{ display:'flex', gap:5 }}>
|
<div style={{ display:'flex', gap:5 }}>
|
||||||
<button onClick={toggleDesign} title={mirc ? 'Modern' : 'mIRC-Stil'}
|
<button onClick={toggleDesign} title={mirc ? 'Zu Modern wechseln' : 'Zu mIRC wechseln'}
|
||||||
style={{ background: mirc ? 'rgba(78,205,196,0.12)' : 'transparent',
|
style={{ background: 'rgba(255,255,255,0.05)',
|
||||||
border:`1px solid ${mirc ? 'rgba(78,205,196,0.3)' : 'rgba(255,255,255,0.1)'}`,
|
border:'1px solid rgba(255,255,255,0.1)',
|
||||||
borderRadius:7, color: mirc ? '#4ecdc4' : 'rgba(255,255,255,0.35)',
|
borderRadius:7, color:'rgba(255,255,255,0.55)',
|
||||||
cursor:'pointer', padding:'3px 7px', fontSize:10, fontFamily:'monospace', lineHeight:1 }}>
|
cursor:'pointer', padding:'3px 8px', fontSize:10, fontFamily:'monospace', lineHeight:1 }}>
|
||||||
{mirc ? '◈ mIRC' : '◈'}
|
{mirc ? '◈ mIRC' : '◈ Modern'}
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => { loadUsers(); if (activeUser) loadMessages(activeUser.id); }} title="Neu laden"
|
<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,
|
style={{ background:'transparent', border:'1px solid rgba(255,255,255,0.1)', borderRadius:7,
|
||||||
|
|||||||
Reference in New Issue
Block a user