+8
src/api/status.rs
+8
src/api/status.rs
···
130
130
vec![]
131
131
});
132
132
133
+
let is_admin_flag = is_admin(did.as_str());
133
134
let html = StatusTemplate {
134
135
title: "your status",
135
136
handle,
136
137
current_status,
137
138
history,
138
139
is_owner: true, // They're viewing their own status
140
+
is_admin: is_admin_flag,
139
141
}
140
142
.render()
141
143
.expect("template should be valid");
···
192
194
current_status,
193
195
history,
194
196
is_owner: false, // Visitor viewing owner's status
197
+
is_admin: false,
195
198
}
196
199
.render()
197
200
.expect("template should be valid");
···
275
278
vec![]
276
279
});
277
280
281
+
let is_admin_flag = match session.get::<String>("did").unwrap_or(None) {
282
+
Some(d) => is_admin(&d),
283
+
None => false,
284
+
};
278
285
let html = StatusTemplate {
279
286
title: &format!("@{} status", handle),
280
287
handle: handle.clone(),
281
288
current_status,
282
289
history,
283
290
is_owner,
291
+
is_admin: is_admin_flag,
284
292
}
285
293
.render()
286
294
.expect("template should be valid");
+1
src/templates.rs
+1
src/templates.rs
+109
-1
templates/feed.html
+109
-1
templates/feed.html
···
45
45
</button>
46
46
</div>
47
47
</header>
48
+
49
+
{% if is_admin %}
50
+
<!-- Admin Upload (fixed top-left) -->
51
+
<div class="admin-panel" id="admin-panel">
52
+
<button class="admin-toggle" id="admin-toggle" title="admin tools" aria-label="admin tools">⚙️</button>
53
+
<div class="admin-content" id="admin-content" style="display:none;">
54
+
<div class="admin-section">
55
+
<div class="admin-title">upload emoji</div>
56
+
<form id="emoji-upload-form">
57
+
<input type="text" id="emoji-name" placeholder="name (optional)" maxlength="40" />
58
+
<input type="file" id="emoji-file" accept="image/png,image/gif" required />
59
+
<button type="submit">upload</button>
60
+
</form>
61
+
<div class="admin-msg" id="admin-msg" aria-live="polite"></div>
62
+
</div>
63
+
</div>
64
+
</div>
65
+
{% endif %}
48
66
49
67
<!-- Simple Settings (logged in users only) -->
50
68
{% if let Some(p) = &profile %}
···
738
756
font-size: 1.5rem;
739
757
}
740
758
}
759
+
760
+
/* Admin panel (top-left) */
761
+
.admin-panel {
762
+
position: fixed;
763
+
top: 12px;
764
+
left: 12px;
765
+
z-index: 9999;
766
+
}
767
+
.admin-toggle {
768
+
background: var(--bg-tertiary);
769
+
border: 1px solid var(--border-color);
770
+
border-radius: 10px;
771
+
padding: 6px 8px;
772
+
cursor: pointer;
773
+
color: var(--text-secondary);
774
+
}
775
+
.admin-content {
776
+
margin-top: 8px;
777
+
background: var(--bg-tertiary);
778
+
border: 1px solid var(--border-color);
779
+
border-radius: 12px;
780
+
padding: 10px;
781
+
width: 240px;
782
+
box-shadow: var(--shadow-md);
783
+
}
784
+
.admin-title {
785
+
font-size: 12px;
786
+
color: var(--text-secondary);
787
+
margin-bottom: 6px;
788
+
}
789
+
.admin-content input[type="text"],
790
+
.admin-content input[type="file"] {
791
+
width: 100%;
792
+
margin-bottom: 8px;
793
+
}
794
+
.admin-content button[type="submit"] {
795
+
width: 100%;
796
+
background: var(--accent);
797
+
color: #fff;
798
+
border: none;
799
+
border-radius: 8px;
800
+
padding: 6px 8px;
801
+
cursor: pointer;
802
+
}
803
+
.admin-msg { font-size: 12px; color: var(--text-secondary); margin-top: 6px; }
741
804
</style>
742
805
743
806
<script>
···
1206
1269
});
1207
1270
});
1208
1271
});
1209
-
</script>
1272
+
</script>
1273
+
<script>
1274
+
// Admin upload toggles and submit
1275
+
document.addEventListener('DOMContentLoaded', function () {
1276
+
const toggle = document.getElementById('admin-toggle');
1277
+
const content = document.getElementById('admin-content');
1278
+
const form = document.getElementById('emoji-upload-form');
1279
+
const file = document.getElementById('emoji-file');
1280
+
const name = document.getElementById('emoji-name');
1281
+
const msg = document.getElementById('admin-msg');
1282
+
if (!toggle || !content || !form) return;
1283
+
1284
+
toggle.addEventListener('click', () => {
1285
+
content.style.display = content.style.display === 'none' ? 'block' : 'none';
1286
+
});
1287
+
1288
+
form.addEventListener('submit', async (e) => {
1289
+
e.preventDefault();
1290
+
msg.textContent = '';
1291
+
if (!file.files || file.files.length === 0) {
1292
+
msg.textContent = 'choose a PNG or GIF';
1293
+
return;
1294
+
}
1295
+
const f = file.files[0];
1296
+
if (!['image/png','image/gif'].includes(f.type)) {
1297
+
msg.textContent = 'only PNG or GIF';
1298
+
return;
1299
+
}
1300
+
const fd = new FormData();
1301
+
fd.append('file', f);
1302
+
if (name.value.trim().length) fd.append('name', name.value.trim());
1303
+
try {
1304
+
const res = await fetch('/admin/upload-emoji', { method: 'POST', body: fd });
1305
+
const json = await res.json();
1306
+
if (!res.ok || !json.success) {
1307
+
msg.textContent = json.error || 'upload failed';
1308
+
return;
1309
+
}
1310
+
msg.textContent = `uploaded: ${json.filename}`;
1311
+
// Optionally refresh emoji list used by picker
1312
+
} catch (err) {
1313
+
msg.textContent = 'network error';
1314
+
}
1315
+
});
1316
+
});
1317
+
</script>
1210
1318
{%endblock content%}
+108
templates/status.html
+108
templates/status.html
···
46
46
{% endif %}
47
47
</div>
48
48
</header>
49
+
50
+
{% if is_admin %}
51
+
<!-- Admin Upload (fixed top-left) -->
52
+
<div class="admin-panel" id="admin-panel">
53
+
<button class="admin-toggle" id="admin-toggle" title="admin tools" aria-label="admin tools">⚙️</button>
54
+
<div class="admin-content" id="admin-content" style="display:none;">
55
+
<div class="admin-section">
56
+
<div class="admin-title">upload emoji</div>
57
+
<form id="emoji-upload-form">
58
+
<input type="text" id="emoji-name" placeholder="name (optional)" maxlength="40" />
59
+
<input type="file" id="emoji-file" accept="image/png,image/gif" required />
60
+
<button type="submit">upload</button>
61
+
</form>
62
+
<div class="admin-msg" id="admin-msg" aria-live="polite"></div>
63
+
</div>
64
+
</div>
65
+
</div>
66
+
{% endif %}
49
67
50
68
<!-- Simple Settings (owner only) -->
51
69
{% if is_owner %}
···
350
368
align-items: center;
351
369
margin-bottom: 2rem;
352
370
}
371
+
372
+
/* Admin panel (top-left) */
373
+
.admin-panel {
374
+
position: fixed;
375
+
top: 12px;
376
+
left: 12px;
377
+
z-index: 9999;
378
+
}
379
+
.admin-toggle {
380
+
background: var(--bg-tertiary);
381
+
border: 1px solid var(--border-color);
382
+
border-radius: 10px;
383
+
padding: 6px 8px;
384
+
cursor: pointer;
385
+
color: var(--text-secondary);
386
+
}
387
+
.admin-content {
388
+
margin-top: 8px;
389
+
background: var(--bg-tertiary);
390
+
border: 1px solid var(--border-color);
391
+
border-radius: 12px;
392
+
padding: 10px;
393
+
width: 240px;
394
+
box-shadow: var(--shadow-md);
395
+
}
396
+
.admin-title {
397
+
font-size: 12px;
398
+
color: var(--text-secondary);
399
+
margin-bottom: 6px;
400
+
}
401
+
.admin-content input[type="text"],
402
+
.admin-content input[type="file"] {
403
+
width: 100%;
404
+
margin-bottom: 8px;
405
+
}
406
+
.admin-content button[type="submit"] {
407
+
width: 100%;
408
+
background: var(--accent);
409
+
color: #fff;
410
+
border: none;
411
+
border-radius: 8px;
412
+
padding: 6px 8px;
413
+
cursor: pointer;
414
+
}
415
+
.admin-msg { font-size: 12px; color: var(--text-secondary); margin-top: 6px; }
353
416
354
417
.header-actions {
355
418
display: flex;
···
1963
2026
});
1964
2027
});
1965
2028
});
2029
+
</script>
2030
+
<script>
2031
+
// Admin upload toggles and submit
2032
+
document.addEventListener('DOMContentLoaded', function () {
2033
+
const toggle = document.getElementById('admin-toggle');
2034
+
const content = document.getElementById('admin-content');
2035
+
const form = document.getElementById('emoji-upload-form');
2036
+
const file = document.getElementById('emoji-file');
2037
+
const name = document.getElementById('emoji-name');
2038
+
const msg = document.getElementById('admin-msg');
2039
+
if (!toggle || !content || !form) return;
2040
+
2041
+
toggle.addEventListener('click', () => {
2042
+
content.style.display = content.style.display === 'none' ? 'block' : 'none';
2043
+
});
2044
+
2045
+
form.addEventListener('submit', async (e) => {
2046
+
e.preventDefault();
2047
+
msg.textContent = '';
2048
+
if (!file.files || file.files.length === 0) {
2049
+
msg.textContent = 'choose a PNG or GIF';
2050
+
return;
2051
+
}
2052
+
const f = file.files[0];
2053
+
if (!['image/png','image/gif'].includes(f.type)) {
2054
+
msg.textContent = 'only PNG or GIF';
2055
+
return;
2056
+
}
2057
+
const fd = new FormData();
2058
+
fd.append('file', f);
2059
+
if (name.value.trim().length) fd.append('name', name.value.trim());
2060
+
try {
2061
+
const res = await fetch('/admin/upload-emoji', { method: 'POST', body: fd });
2062
+
const json = await res.json();
2063
+
if (!res.ok || !json.success) {
2064
+
msg.textContent = json.error || 'upload failed';
2065
+
return;
2066
+
}
2067
+
msg.textContent = `uploaded: ${json.filename}`;
2068
+
// Optionally refresh emoji list used by picker
2069
+
} catch (err) {
2070
+
msg.textContent = 'network error';
2071
+
}
2072
+
});
2073
+
});
1966
2074
</script>
1967
2075
{%endblock content%}