Skip to content

Commit 24a484b

Browse files
committed
Add server message banner and textarea in dashboard settings.
1 parent 7abacea commit 24a484b

4 files changed

Lines changed: 127 additions & 24 deletions

File tree

Public/app/css/main.css

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,79 @@ input[type="text"]:disabled, textarea:disabled {
13741374
border-color: hsl(140 60% 50% / 0.2);
13751375
}
13761376

1377+
/* Server message textarea */
1378+
.settings-message-textarea,
1379+
.settings-input {
1380+
width: 100%;
1381+
padding: 0.5rem 0.75rem;
1382+
font-size: 0.875rem;
1383+
color: hsl(var(--foreground));
1384+
background: hsl(var(--muted));
1385+
border: 1px solid hsl(var(--border));
1386+
border-radius: 0.5rem;
1387+
font-family: inherit;
1388+
transition: border-color 0.15s ease;
1389+
box-sizing: border-box;
1390+
}
1391+
1392+
.settings-message-textarea {
1393+
resize: vertical;
1394+
min-height: 4.5rem;
1395+
line-height: 1.5;
1396+
}
1397+
1398+
.settings-message-textarea:focus,
1399+
.settings-input:focus {
1400+
outline: none;
1401+
border-color: hsl(var(--ring));
1402+
}
1403+
1404+
/* Server message banner */
1405+
.server-message-banner {
1406+
position: fixed;
1407+
top: 0;
1408+
left: 0;
1409+
right: 0;
1410+
z-index: 9999;
1411+
background: hsl(var(--muted));
1412+
border-bottom: 1px solid hsl(var(--border));
1413+
padding: 0.625rem 1rem;
1414+
display: flex;
1415+
align-items: center;
1416+
gap: 0.75rem;
1417+
opacity: 0;
1418+
transform: translateY(-100%);
1419+
transition: opacity 0.3s ease, transform 0.3s ease;
1420+
}
1421+
1422+
.server-message-banner.show {
1423+
opacity: 1;
1424+
transform: translateY(0);
1425+
}
1426+
1427+
.server-message-text {
1428+
flex: 1;
1429+
font-size: 0.875rem;
1430+
color: hsl(var(--foreground));
1431+
}
1432+
1433+
.server-message-dismiss {
1434+
background: none;
1435+
border: none;
1436+
color: hsl(var(--muted-foreground));
1437+
cursor: pointer;
1438+
padding: 0.25rem;
1439+
display: flex;
1440+
align-items: center;
1441+
justify-content: center;
1442+
opacity: 0.7;
1443+
transition: opacity 0.15s ease;
1444+
}
1445+
1446+
.server-message-dismiss:hover {
1447+
opacity: 1;
1448+
}
1449+
13771450
/* Group Chat Modal */
13781451
.group-chat-modal {
13791452
position: fixed;

Public/app/html/main.leaf

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,11 @@
360360
<script src="/app/js/media-viewer.js?v=23"></script>
361361
<script src="/app/js/user-profile.js?v=23"></script>
362362
#if(serverMessage):
363-
<script>alert("#(serverMessage)");</script>
363+
<div id="serverMessageData" style="display:none" data-message="#(serverMessage)"></div>
364+
<script>
365+
const _msgElement = document.getElementById('serverMessageData');
366+
if (_msgElement) initServerMessage(_msgElement.dataset.message);
367+
</script>
364368
#endif
365369
</body>
366370
</html>

Public/app/js/dashboard.js

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,37 +82,33 @@ async function loadDashboardSettings() {
8282

8383
list.innerHTML = '';
8484
settings.forEach(setting => {
85-
let meta;
86-
try { meta = JSON.parse(setting.meta); } catch (_) { return; }
85+
let meta = {};
86+
try { meta = JSON.parse(setting.meta); } catch (_) {}
8787

8888
const variants = meta.variants ? meta.variants.split(',').map(v => v.trim()) : [];
89+
const title = meta.title || (setting.name.charAt(0).toUpperCase() + setting.name.slice(1));
8990

9091
const section = document.createElement('div');
9192
section.className = 'settings-section';
9293

9394
const titleRow = document.createElement('div');
9495
titleRow.className = 'settings-label';
95-
9696
const titleText = document.createElement('span');
97-
titleText.textContent = meta.title || setting.name;
98-
if (meta.description) {
99-
titleRow.title = meta.description;
100-
}
97+
titleText.textContent = title;
98+
if (meta.description) titleRow.title = meta.description;
10199
titleRow.appendChild(titleText);
102100
section.appendChild(titleRow);
103101

104102
if (variants.length > 0) {
105103
const select = document.createElement('select');
106104
select.className = 'settings-dropdown';
107-
108105
variants.forEach(variant => {
109106
const option = document.createElement('option');
110107
option.value = variant;
111108
option.textContent = variant.charAt(0).toUpperCase() + variant.slice(1);
112109
option.selected = variant === setting.value;
113110
select.appendChild(option);
114111
});
115-
116112
select.addEventListener('change', async () => {
117113
const previousValue = setting.value;
118114
try {
@@ -123,15 +119,13 @@ async function loadDashboardSettings() {
123119
select.value = previousValue;
124120
}
125121
});
126-
127122
section.appendChild(select);
128123
} else {
129-
const input = document.createElement('input');
130-
input.type = 'text';
131-
input.className = 'settings-dropdown';
124+
const input = document.createElement('textarea');
125+
input.className = 'settings-message-textarea';
132126
input.value = setting.value;
133-
input.placeholder = meta.description || '';
134-
127+
input.placeholder = 'Enter system message';
128+
input.rows = 3;
135129
let saveTimeout;
136130
input.addEventListener('input', () => {
137131
clearTimeout(saveTimeout);
@@ -144,14 +138,48 @@ async function loadDashboardSettings() {
144138
}
145139
}, 500);
146140
});
147-
148141
section.appendChild(input);
149142
}
150143

151144
list.appendChild(section);
152145
});
153146
}
154147

148+
// ── Server Message Banner ────────────────────────────────────────────────────
149+
150+
function initServerMessage(message) {
151+
if (!message) return;
152+
const storageKey = 'chatserver_dismissed_server_message';
153+
if (localStorage.getItem(storageKey) === message) return;
154+
155+
const banner = document.createElement('div');
156+
banner.id = 'serverMessageBanner';
157+
banner.className = 'server-message-banner';
158+
159+
const textSpan = document.createElement('span');
160+
textSpan.className = 'server-message-text';
161+
textSpan.textContent = message;
162+
163+
const dismissButton = document.createElement('button');
164+
dismissButton.className = 'server-message-dismiss';
165+
dismissButton.innerHTML = closeIcon;
166+
dismissButton.addEventListener('click', () => dismissServerMessage(message));
167+
168+
banner.appendChild(textSpan);
169+
banner.appendChild(dismissButton);
170+
document.body.appendChild(banner);
171+
172+
requestAnimationFrame(() => requestAnimationFrame(() => banner.classList.add('show')));
173+
}
174+
175+
function dismissServerMessage(message) {
176+
const banner = document.getElementById('serverMessageBanner');
177+
if (!banner) return;
178+
if (message) localStorage.setItem('chatserver_dismissed_server_message', message);
179+
banner.classList.remove('show');
180+
setTimeout(() => banner.remove(), 300);
181+
}
182+
155183
async function dashboardRefresh() {
156184
const button = document.getElementById('dashboardRefreshButton');
157185
const updateButton = document.getElementById('dashboardUpdateButton');

Sources/App/Services/SettingsService.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,12 @@ actor SettingsService: SettingsServiceProtocol {
4444

4545
func allSettings() async throws -> [ServerSettingInfo] {
4646
let stored = try await repo.all()
47-
let allNames = Set(Self.defaults.keys).union(Set(stored.map { $0.name }))
48-
return allNames.compactMap { name in
49-
let def = Self.defaults[name]
50-
if let setting = stored.first(where: { $0.name == name }), !setting.value.trim().isEmpty {
51-
return setting.info(meta: def?.meta ?? "")
47+
return Self.defaults.keys.compactMap { name in
48+
guard let def = Self.defaults[name] else { return nil }
49+
if let setting = stored.first(where: { $0.name == name }) {
50+
return setting.info(meta: def.meta)
5251
}
53-
guard let value = def?.value, !value.isEmpty else { return nil }
54-
return ServerSettingInfo(name: name, value: value, meta: def?.meta ?? "", updatedAt: nil)
52+
return ServerSettingInfo(name: name, value: def.value, meta: def.meta, updatedAt: nil)
5553
}
5654
}
5755

0 commit comments

Comments
 (0)