mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-07-30 05:23:21 +02:00
77c0a33da6
Changes: - Convert all Polish UI text to English (buttons, labels, messages) - Menu: "Powiadomienia" → "Notifications" - Status badges: "Włączone/Wyłączone/Zablokowane" → "Enabled/Disabled/Blocked" - Toast messages: all notification messages translated to English - Notification bodies: "Nowe: X kanały" → "New: X channels" - Fix notification toggle UI bug - Badge now correctly shows "Disabled" when user turns off notifications - Previously showed "Enabled" whenever permission was granted (ignoring localStorage) - Now checks both permission AND localStorage state This ensures the UI respects the international nature of the project and fixes the toggle state display issue found during Android testing. Files modified: - app/templates/base.html - app/static/js/app.js - app/static/js/dm.js 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
312 lines
16 KiB
HTML
312 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
|
<title>{% block title %}mc-webui{% endblock %}</title>
|
|
|
|
<!-- Favicon -->
|
|
<link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', filename='images/apple-touch-icon.png') }}">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='images/favicon-32x32.png') }}">
|
|
<link rel="icon" type="image/png" sizes="16x16" href="{{ url_for('static', filename='images/favicon-16x16.png') }}">
|
|
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
|
|
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
|
|
|
<!-- Bootstrap 5 CSS -->
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<!-- Bootstrap Icons -->
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.css">
|
|
|
|
<!-- Custom CSS -->
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
|
|
|
{% block extra_head %}{% endblock %}
|
|
</head>
|
|
<body>
|
|
<!-- Navbar -->
|
|
<nav class="navbar navbar-dark bg-primary">
|
|
<div class="container-fluid">
|
|
<span class="navbar-brand mb-0 h1">
|
|
<i class="bi bi-broadcast"></i> mc-webui
|
|
{% if device_name %}
|
|
<small class="text-white-50 d-none d-sm-inline">- {{ device_name }}</small>
|
|
{% endif %}
|
|
</span>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<div id="notificationBell" class="btn btn-outline-light btn-sm position-relative" style="cursor: default;" title="Unread messages">
|
|
<i class="bi bi-bell"></i>
|
|
</div>
|
|
<select id="channelSelector" class="form-select form-select-sm" style="width: auto; min-width: 100px;" title="Select channel">
|
|
<option value="0">Public</option>
|
|
<!-- Channels loaded dynamically via JavaScript -->
|
|
</select>
|
|
<button class="btn btn-outline-light btn-sm" data-bs-toggle="offcanvas" data-bs-target="#mainMenu" title="Menu">
|
|
<i class="bi bi-list"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Offcanvas Menu -->
|
|
<div class="offcanvas offcanvas-end" tabindex="-1" id="mainMenu">
|
|
<div class="offcanvas-header">
|
|
<h5 class="offcanvas-title"><i class="bi bi-menu-button-wide"></i> Menu</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
|
|
</div>
|
|
<div class="offcanvas-body">
|
|
<div class="list-group list-group-flush">
|
|
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3" id="refreshBtn">
|
|
<i class="bi bi-arrow-clockwise" style="font-size: 1.5rem;"></i>
|
|
<span>Refresh Messages</span>
|
|
</button>
|
|
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3" data-bs-toggle="modal" data-bs-target="#channelsModal" data-bs-dismiss="offcanvas">
|
|
<i class="bi bi-broadcast-pin" style="font-size: 1.5rem;"></i>
|
|
<span>Manage Channels</span>
|
|
</button>
|
|
<!-- Notifications Toggle -->
|
|
<button id="notificationsToggle" class="list-group-item list-group-item-action d-flex align-items-center gap-3" type="button">
|
|
<i class="bi bi-bell" style="font-size: 1.5rem;"></i>
|
|
<div class="flex-grow-1">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span>Notifications</span>
|
|
<span id="notificationStatus" class="badge bg-secondary">Disabled</span>
|
|
</div>
|
|
<small class="text-muted d-block mt-1" style="font-size: 0.75rem;">
|
|
Works when app is hidden
|
|
</small>
|
|
</div>
|
|
</button>
|
|
<div class="list-group-item">
|
|
<div class="d-flex align-items-center gap-3 mb-2">
|
|
<i class="bi bi-calendar3" style="font-size: 1.5rem;"></i>
|
|
<label for="dateSelector" class="form-label mb-0">Message History</label>
|
|
</div>
|
|
<select id="dateSelector" class="form-select" title="Select date">
|
|
<option value="">Today (Live)</option>
|
|
<!-- Archive dates loaded dynamically via JavaScript -->
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Network Commands Section -->
|
|
<div class="list-group-item py-2 mt-2">
|
|
<small class="text-muted fw-bold text-uppercase">Network Commands</small>
|
|
</div>
|
|
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3" id="advertBtn" title="Send single advertisement (recommended for normal operation)">
|
|
<i class="bi bi-megaphone" style="font-size: 1.5rem;"></i>
|
|
<div>
|
|
<span>Send Advert</span>
|
|
<small class="d-block text-muted">Announce presence (normal)</small>
|
|
</div>
|
|
</button>
|
|
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3 text-warning" id="floodadvBtn" title="Flood advertisement - use sparingly! High airtime usage.">
|
|
<i class="bi bi-broadcast" style="font-size: 1.5rem;"></i>
|
|
<div>
|
|
<span>Flood Advert</span>
|
|
<small class="d-block text-muted">Network recovery only!</small>
|
|
</div>
|
|
</button>
|
|
|
|
<div class="list-group-item py-2 mt-2">
|
|
<small class="text-muted fw-bold text-uppercase">Configuration</small>
|
|
</div>
|
|
<button class="list-group-item list-group-item-action d-flex align-items-center gap-3" data-bs-toggle="modal" data-bs-target="#settingsModal" data-bs-dismiss="offcanvas">
|
|
<i class="bi bi-gear" style="font-size: 1.5rem;"></i>
|
|
<span>Settings</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content -->
|
|
<main>
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
|
|
<!-- Channels Management Modal -->
|
|
<div class="modal fade" id="channelsModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title"><i class="bi bi-broadcast-pin"></i> Manage Channels</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<!-- Channel List -->
|
|
<h6>Your Channels</h6>
|
|
<div id="channelsList" class="list-group mb-3">
|
|
<div class="text-center text-muted py-3">
|
|
<div class="spinner-border spinner-border-sm"></div> Loading...
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Actions -->
|
|
<div class="btn-group w-100 mb-3" role="group">
|
|
<button type="button" class="btn btn-primary" data-bs-toggle="collapse" data-bs-target="#addChannelForm">
|
|
<i class="bi bi-plus-circle"></i> Add New Channel
|
|
</button>
|
|
<button type="button" class="btn btn-success" data-bs-toggle="collapse" data-bs-target="#joinChannelForm">
|
|
<i class="bi bi-box-arrow-in-right"></i> Join Existing
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Add Channel Form (collapsed) -->
|
|
<div class="collapse" id="addChannelForm">
|
|
<div class="card card-body mb-3">
|
|
<h6>Create New Channel</h6>
|
|
<form id="createChannelForm">
|
|
<div class="mb-2">
|
|
<label for="newChannelName" class="form-label">Channel Name</label>
|
|
<input type="text" class="form-control" id="newChannelName"
|
|
placeholder="e.g., Malopolska"
|
|
pattern="[a-zA-Z0-9_\-]+"
|
|
required>
|
|
<small class="text-muted">Only letters, numbers, _ and -</small>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="bi bi-plus-circle"></i> Create & Auto-generate Key
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Join Channel Form (collapsed) -->
|
|
<div class="collapse" id="joinChannelForm">
|
|
<div class="card card-body mb-3">
|
|
<h6>Join Existing Channel</h6>
|
|
<form id="joinChannelFormSubmit">
|
|
<div class="mb-2">
|
|
<label for="joinChannelName" class="form-label">Channel Name</label>
|
|
<input type="text" class="form-control" id="joinChannelName" required>
|
|
</div>
|
|
<div class="mb-2">
|
|
<label for="joinChannelKey" class="form-label">Channel Key (32 hex chars) <small class="text-muted">- optional for channels starting with #</small></label>
|
|
<input type="text" class="form-control" id="joinChannelKey"
|
|
placeholder="485af7e164459d280d8818d9c99fb30d (leave empty for # channels)"
|
|
pattern="[a-fA-F0-9]{32}"
|
|
maxlength="32">
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<button type="submit" class="btn btn-success">
|
|
<i class="bi bi-box-arrow-in-right"></i> Join Channel
|
|
</button>
|
|
<button type="button" class="btn btn-secondary" id="scanQRBtn">
|
|
<i class="bi bi-qr-code-scan"></i> Scan QR Code
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Share Channel Modal -->
|
|
<div class="modal fade" id="shareChannelModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title"><i class="bi bi-share"></i> Share Channel</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<h6 id="shareChannelName"></h6>
|
|
|
|
<!-- QR Code -->
|
|
<div class="text-center mb-3">
|
|
<img id="shareChannelQR" src="" alt="QR Code" class="img-fluid" style="max-width: 300px;">
|
|
</div>
|
|
|
|
<!-- Channel Details -->
|
|
<div class="mb-3">
|
|
<label class="form-label fw-bold">Channel Key:</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control font-monospace" id="shareChannelKey" readonly>
|
|
<button class="btn btn-outline-secondary" type="button" onclick="copyChannelKey()">
|
|
<i class="bi bi-clipboard"></i> Copy
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alert alert-info small">
|
|
<i class="bi bi-info-circle"></i>
|
|
Share this QR code or key with others to let them join this channel.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings Modal -->
|
|
<div class="modal fade" id="settingsModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title"><i class="bi bi-gear"></i> Settings</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<h6>Device Information</h6>
|
|
<div id="deviceInfo" class="small text-muted">
|
|
Loading...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toast container for notifications -->
|
|
<div class="toast-container position-fixed top-0 start-0 p-3">
|
|
<div id="notificationToast" class="toast" role="alert">
|
|
<div class="toast-header">
|
|
<strong class="me-auto">mc-webui</strong>
|
|
<button type="button" class="btn-close" data-bs-dismiss="toast"></button>
|
|
</div>
|
|
<div class="toast-body"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bootstrap 5 JS Bundle -->
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
|
|
<!-- Message Content Processing Utilities (must load before app.js and dm.js) -->
|
|
<script src="{{ url_for('static', filename='js/message-utils.js') }}"></script>
|
|
|
|
<!-- Custom JS -->
|
|
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
|
|
|
|
<!-- PWA Viewport Fix for Android -->
|
|
<script>
|
|
// Fix viewport corruption on Android PWA after page reload
|
|
window.addEventListener('load', function() {
|
|
// Force viewport recalculation without modifying body height
|
|
setTimeout(() => {
|
|
window.scrollTo(0, 0);
|
|
// Force reflow using a less invasive method
|
|
void(document.documentElement.offsetHeight);
|
|
// Trigger resize event to recalculate layout
|
|
window.dispatchEvent(new Event('resize'));
|
|
}, 150);
|
|
});
|
|
</script>
|
|
|
|
<!-- Service Worker Registration for PWA -->
|
|
<script>
|
|
// Register Service Worker for PWA functionality
|
|
if ('serviceWorker' in navigator) {
|
|
window.addEventListener('load', () => {
|
|
navigator.serviceWorker.register('/static/js/sw.js')
|
|
.then((registration) => {
|
|
console.log('Service Worker registered:', registration.scope);
|
|
})
|
|
.catch((error) => {
|
|
console.error('Service Worker registration failed:', error);
|
|
});
|
|
});
|
|
}
|
|
</script>
|
|
|
|
{% block extra_scripts %}{% endblock %}
|
|
</body>
|
|
</html>
|