mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-03-28 17:42:45 +01:00
Two critical fixes: 1. DM Badge Sync Issue: - After closing DM modal, badge showed correct count for 10 seconds - Then auto-refresh (running every 10s) used stale dmLastSeenTimestamps - Solution: Call loadDmLastSeenTimestampsFromServer() before updating badge - This ensures app.js auto-refresh uses current server state 2. Android PWA Viewport Corruption: - Viewport corrupted on F5 refresh in Android PWA mode - Status bar content hidden under system UI - Solution: Force viewport recalculation on page load - Scrolls to top and forces reflow after 100ms delay Testing needed on Android PWA to verify both fixes work correctly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
251 lines
9.4 KiB
HTML
251 lines
9.4 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 {
|
|
// Reload DM read status from server to sync with app.js automatic updates
|
|
// This ensures dmLastSeenTimestamps in app.js is current
|
|
if (typeof loadDmLastSeenTimestampsFromServer === 'function') {
|
|
await loadDmLastSeenTimestampsFromServer();
|
|
}
|
|
|
|
// Fetch latest DM conversations with unread counts
|
|
const response = await fetch('/api/dm/conversations');
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
|
|
// Calculate total unread by summing unread counts from all conversations
|
|
let totalUnread = 0;
|
|
if (data.conversations && Array.isArray(data.conversations)) {
|
|
totalUnread = data.conversations.reduce((sum, conv) => sum + (conv.unread || 0), 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 %}
|