Files
mc-webui/app/templates/index.html
T
MarekWo abb84b6712 fix: Update DM badge without full page reload to avoid viewport corruption
Replace full page reload with targeted badge update when DM modal closes.
This prevents the viewport corruption issue that was reintroduced by
window.location.reload() in the previous commit.

Problem:
- Previous solution used window.location.reload() to update badges
- This caused viewport corruption (status bar hidden under system UI)
- Same issue we solved by using modals instead of navigation

New solution:
- Fetch latest unread counts from /api/dm/updates when modal closes
- Update only the green DM badge (notification-badge-dm) on notification bell
- No page reload = no viewport corruption
- Keeps modal solution benefits intact

Changes:
- Remove window.location.reload() from hidden.bs.modal event listener
- Add async fetch to /api/dm/updates endpoint
- Update notification-badge-dm element directly using DOM manipulation
- Handle badge creation/update/hiding based on unread count

Result:
- DM badges update correctly after closing modal
- No viewport corruption
- Fast, smooth user experience

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-01 22:09:20 +01:00

240 lines
8.8 KiB
HTML

{% extends "base.html" %}
{% block title %}Chat - mc-webui{% endblock %}
{% block extra_head %}
<!-- Emoji Picker -->
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>
<style>
emoji-picker {
--emoji-size: 1.5rem;
--num-columns: 8;
}
.emoji-picker-container {
position: relative;
}
.emoji-picker-popup {
position: absolute;
bottom: 100%;
right: 0;
z-index: 1000;
margin-bottom: 0.5rem;
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
border-radius: 0.5rem;
overflow: hidden;
}
.emoji-picker-popup.hidden {
display: none;
}
/* Mobile responsive adjustments */
@media (max-width: 576px) {
emoji-picker {
--emoji-size: 1.25rem;
--num-columns: 6;
}
.emoji-picker-popup {
right: auto;
left: 0;
width: 100%;
max-width: 100%;
}
}
/* Modal fullscreen - remove all margins and padding */
#dmModal .modal-dialog.modal-fullscreen,
#contactsModal .modal-dialog.modal-fullscreen {
margin: 0 !important;
width: 100vw !important;
max-width: 100vw !important;
height: 100vh !important;
max-height: 100vh !important;
}
#dmModal .modal-content,
#contactsModal .modal-content {
border: none !important;
border-radius: 0 !important;
height: 100vh !important;
}
#dmModal .modal-body,
#contactsModal .modal-body {
overflow: hidden !important;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid d-flex flex-column" style="height: 100%;">
<!-- Messages Container -->
<div class="row flex-grow-1 overflow-hidden" style="min-height: 0;">
<div class="col-12" style="height: 100%;">
<div id="messagesContainer" class="messages-container h-100 overflow-auto p-3">
<div id="messagesList">
<!-- Messages will be loaded here via JavaScript -->
<div class="text-center text-muted py-5">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3">Loading messages...</p>
</div>
</div>
</div>
</div>
</div>
<!-- Send Message Form -->
<div class="row border-top bg-light">
<div class="col-12">
<form id="sendMessageForm" class="p-3">
<div class="emoji-picker-container">
<div class="input-group">
<textarea
id="messageInput"
class="form-control"
placeholder="Type a message..."
rows="2"
maxlength="500"
required
></textarea>
<button type="button" class="btn btn-outline-secondary" id="emojiBtn" title="Insert emoji">
<i class="bi bi-emoji-smile"></i>
</button>
<button type="submit" class="btn btn-primary px-4" id="sendBtn">
<i class="bi bi-send"></i>
</button>
</div>
<!-- Emoji picker popup (hidden by default) -->
<div id="emojiPickerPopup" class="emoji-picker-popup hidden"></div>
</div>
<div class="d-flex justify-content-end">
<small id="charCounter" class="text-muted">0 / 140</small>
</div>
</form>
</div>
</div>
<!-- Status Bar -->
<div class="row border-top">
<div class="col-12">
<div class="p-2 small text-muted d-flex justify-content-between align-items-center">
<span id="statusText">
<i class="bi bi-circle-fill text-secondary"></i> Connecting...
</span>
<span id="lastRefresh">Updated: Never</span>
</div>
</div>
</div>
</div>
<!-- Floating Action Buttons -->
<div class="fab-container">
<button class="fab fab-dm" data-bs-toggle="modal" data-bs-target="#dmModal" title="Direct Messages">
<i class="bi bi-envelope-fill"></i>
</button>
<button class="fab fab-contacts" data-bs-toggle="modal" data-bs-target="#contactsModal" title="Contact Management">
<i class="bi bi-person-fill"></i>
</button>
</div>
<!-- DM Modal (Full Screen) -->
<div class="modal fade" id="dmModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-fullscreen">
<div class="modal-content">
<div class="modal-header bg-success text-white">
<h5 class="modal-title"><i class="bi bi-envelope"></i> Direct Messages</h5>
<button type="button" class="btn btn-outline-light" data-bs-dismiss="modal">
<i class="bi bi-x-lg"></i> Close
</button>
</div>
<div class="modal-body p-0">
<iframe id="dmFrame" src="/dm" style="width: 100%; height: 100%; border: none;"></iframe>
</div>
</div>
</div>
</div>
<!-- Contact Management Modal (Full Screen) -->
<div class="modal fade" id="contactsModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-fullscreen">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title"><i class="bi bi-person-check"></i> Contact Management</h5>
<button type="button" class="btn btn-outline-light" data-bs-dismiss="modal">
<i class="bi bi-x-lg"></i> Close
</button>
</div>
<div class="modal-body p-0">
<iframe id="contactsFrame" src="/contacts/manage" style="width: 100%; height: 100%; border: none;"></iframe>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script>
// Pass configuration from Flask to JavaScript
window.MC_CONFIG = {
deviceName: "{{ device_name }}"
};
// Reload iframe content when modals are opened to ensure fresh data
document.addEventListener('DOMContentLoaded', function() {
const dmModal = document.getElementById('dmModal');
const contactsModal = document.getElementById('contactsModal');
if (dmModal) {
dmModal.addEventListener('show.bs.modal', function () {
const dmFrame = document.getElementById('dmFrame');
if (dmFrame) {
dmFrame.src = dmFrame.src;
}
});
// Update DM badges when modal is closed (without full page reload)
dmModal.addEventListener('hidden.bs.modal', async function () {
try {
// Fetch latest DM unread counts
const response = await fetch('/api/dm/updates?last_seen={}');
if (response.ok) {
const data = await response.json();
const totalUnread = data.total_unread || 0;
// Update the green DM badge on notification bell
const bellContainer = document.getElementById('notificationBell');
if (bellContainer) {
let dmBadge = bellContainer.querySelector('.notification-badge-dm');
if (totalUnread > 0) {
if (!dmBadge) {
dmBadge = document.createElement('span');
dmBadge.className = 'notification-badge-dm';
bellContainer.appendChild(dmBadge);
}
dmBadge.textContent = totalUnread > 99 ? '99+' : totalUnread;
dmBadge.style.display = 'inline-block';
} else if (dmBadge) {
dmBadge.style.display = 'none';
}
}
}
} catch (error) {
console.error('Error updating DM badges:', error);
}
});
}
if (contactsModal) {
contactsModal.addEventListener('show.bs.modal', function () {
const contactsFrame = document.getElementById('contactsFrame');
if (contactsFrame) {
contactsFrame.src = contactsFrame.src;
}
});
}
});
</script>
{% endblock %}