Replace bottom nav with hamburger menu, move friends to Friends panel
This commit is contained in:
+173
-65
@@ -191,11 +191,8 @@
|
|||||||
.modal-actions .btn { flex: 1; }
|
.modal-actions .btn { flex: 1; }
|
||||||
|
|
||||||
/* ── SAFE AREA / MOBILE ── */
|
/* ── SAFE AREA / MOBILE ── */
|
||||||
.bottom-nav {
|
|
||||||
padding-bottom: env(safe-area-inset-bottom);
|
|
||||||
}
|
|
||||||
.dashboard-content {
|
.dashboard-content {
|
||||||
padding-bottom: calc(80px + env(safe-area-inset-bottom));
|
padding-bottom: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── TOUCH TARGETS ── */
|
/* ── TOUCH TARGETS ── */
|
||||||
@@ -755,51 +752,124 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── BOTTOM NAV ── */
|
/* ── HAMBURGER MENU ── */
|
||||||
.bottom-nav {
|
.hamburger-btn {
|
||||||
position: fixed;
|
width: 38px;
|
||||||
bottom: 0;
|
height: 38px;
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: var(--bg);
|
|
||||||
border-top: 1px solid var(--border);
|
|
||||||
display: flex;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
.bn-item {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10px 0 14px;
|
justify-content: center;
|
||||||
|
gap: 5px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: none;
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--muted);
|
border: none;
|
||||||
font-family: var(--font-body);
|
padding: 4px;
|
||||||
font-size: 11px;
|
border-radius: 8px;
|
||||||
gap: 4px;
|
transition: background 0.15s;
|
||||||
transition: color 0.15s;
|
|
||||||
}
|
}
|
||||||
.bn-item.active { color: var(--teal); }
|
.hamburger-btn:hover { background: var(--surface2); }
|
||||||
.bn-item .bn-icon { font-size: 20px; line-height: 1; }
|
.hamburger-btn span {
|
||||||
.bn-badge {
|
display: block;
|
||||||
|
width: 20px;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.hamburger-btn.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
|
||||||
|
.hamburger-btn.open span:nth-child(2) { opacity: 0; }
|
||||||
|
.hamburger-btn.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
|
||||||
|
|
||||||
|
.menu-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 150;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.menu-overlay.open { pointer-events: all; }
|
||||||
|
|
||||||
|
.menu-backdrop {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
.menu-overlay.open .menu-backdrop { opacity: 1; }
|
||||||
|
|
||||||
|
.menu-drawer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 240px;
|
||||||
|
background: var(--surface);
|
||||||
|
border-left: 1px solid var(--border);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transform: translateX(100%);
|
||||||
|
transition: transform 0.25s cubic-bezier(0.16,1,0.3,1);
|
||||||
|
padding: 0 0 max(24px, env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
.menu-overlay.open .menu-drawer { transform: translateX(0); }
|
||||||
|
|
||||||
|
.menu-header {
|
||||||
|
padding: 20px 20px 16px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.menu-header .menu-user-name {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.menu-header .menu-user-email {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-top: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 14px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--white);
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: 15px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
transition: background 0.15s;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.bn-badge::after {
|
.menu-item:hover { background: var(--surface2); }
|
||||||
content: attr(data-count);
|
.menu-item.active { color: var(--teal); }
|
||||||
position: absolute;
|
.menu-item .mi-icon { font-size: 20px; width: 24px; text-align: center; }
|
||||||
top: -4px;
|
.menu-item .mi-badge {
|
||||||
right: -8px;
|
margin-left: auto;
|
||||||
background: var(--danger);
|
background: var(--danger);
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 9px;
|
font-size: 10px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 1px 5px;
|
padding: 2px 7px;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.bn-badge[data-count]:not([data-count="0"])::after { display: block; }
|
.menu-item .mi-badge.show { display: block; }
|
||||||
|
|
||||||
|
.menu-divider {
|
||||||
|
height: 1px;
|
||||||
|
background: var(--border);
|
||||||
|
margin: 8px 20px;
|
||||||
|
}
|
||||||
|
.menu-spacer { flex: 1; }
|
||||||
|
|
||||||
/* ── PANEL VIEWS ── */
|
/* ── PANEL VIEWS ── */
|
||||||
.panel { display: none; }
|
.panel { display: none; }
|
||||||
@@ -1028,14 +1098,46 @@
|
|||||||
<div id="screen-dashboard" class="screen">
|
<div id="screen-dashboard" class="screen">
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-logo">Step<span>Mates</span></div>
|
<div class="nav-logo">Step<span>Mates</span></div>
|
||||||
<div class="nav-user">
|
<div style="display:flex;align-items:center;gap:10px">
|
||||||
<span style="font-size:13px;color:var(--muted)" id="nav-name"></span>
|
<div class="avatar" id="nav-avatar" onclick="openProfileModal()" title="Edit profile" style="cursor:pointer">
|
||||||
<div class="avatar" id="nav-avatar" onclick="openProfileModal()" title="Edit profile">
|
|
||||||
<span id="nav-avatar-initial">?</span>
|
<span id="nav-avatar-initial">?</span>
|
||||||
</div>
|
</div>
|
||||||
|
<button class="hamburger-btn" id="hamburger-btn" onclick="toggleMenu()" aria-label="Menu">
|
||||||
|
<span></span><span></span><span></span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<!-- SLIDE-OUT MENU -->
|
||||||
|
<div class="menu-overlay" id="menu-overlay">
|
||||||
|
<div class="menu-backdrop" onclick="closeMenu()"></div>
|
||||||
|
<div class="menu-drawer">
|
||||||
|
<div class="menu-header">
|
||||||
|
<div class="menu-user-name" id="menu-user-name">—</div>
|
||||||
|
<div class="menu-user-email" id="menu-user-email">—</div>
|
||||||
|
</div>
|
||||||
|
<button class="menu-item active" id="mi-home" onclick="switchPanel('home');closeMenu()">
|
||||||
|
<span class="mi-icon">👟</span> My Steps
|
||||||
|
</button>
|
||||||
|
<button class="menu-item" id="mi-friends" onclick="switchPanel('friends');closeMenu()">
|
||||||
|
<span class="mi-icon">👥</span> Friends
|
||||||
|
<span class="mi-badge" id="mi-friends-badge"></span>
|
||||||
|
</button>
|
||||||
|
<button class="menu-item" id="mi-settings" onclick="switchPanel('settings');closeMenu()">
|
||||||
|
<span class="mi-icon">⚙️</span> Setup & Sync
|
||||||
|
</button>
|
||||||
|
<div class="menu-divider"></div>
|
||||||
|
<button class="menu-item" onclick="openProfileModal();closeMenu()">
|
||||||
|
<span class="mi-icon">✏️</span> Edit Profile
|
||||||
|
</button>
|
||||||
|
<div class="menu-spacer"></div>
|
||||||
|
<div class="menu-divider"></div>
|
||||||
|
<button class="menu-item" onclick="signOut()" style="color:var(--muted)">
|
||||||
|
<span class="mi-icon">🚪</span> Sign out
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container dashboard-content">
|
<div class="container dashboard-content">
|
||||||
|
|
||||||
<!-- TIME TABS -->
|
<!-- TIME TABS -->
|
||||||
@@ -1081,22 +1183,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- PANEL: MY STEPS / FRIENDS -->
|
<!-- PANEL: MY STEPS -->
|
||||||
<div id="panel-home" class="panel active">
|
<div id="panel-home" class="panel active">
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Friends' steps -->
|
<!-- PANEL: FRIENDS -->
|
||||||
|
<div id="panel-friends" class="panel">
|
||||||
|
|
||||||
|
<!-- Friends' steps leaderboard -->
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<div class="section-title">Friends</div>
|
<div class="section-title">Friends' Steps</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="friends-steps-list" class="friends-list">
|
<div id="friends-steps-list" class="friends-list" style="margin-bottom:28px">
|
||||||
<div class="loader-full" style="padding:40px 0;flex:none">
|
<div class="loader-full" style="padding:40px 0;flex:none">
|
||||||
<div class="spinner" style="width:24px;height:24px;border-width:2px;color:var(--teal)"></div>
|
<div class="spinner" style="width:24px;height:24px;border-width:2px;color:var(--teal)"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- PANEL: FRIENDS MANAGEMENT -->
|
|
||||||
<div id="panel-friends" class="panel">
|
|
||||||
<!-- Pending received requests -->
|
<!-- Pending received requests -->
|
||||||
<div id="pending-received-section" style="display:none">
|
<div id="pending-received-section" style="display:none">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
@@ -1195,18 +1297,6 @@
|
|||||||
|
|
||||||
</div><!-- /container -->
|
</div><!-- /container -->
|
||||||
|
|
||||||
<!-- BOTTOM NAV -->
|
|
||||||
<div class="bottom-nav">
|
|
||||||
<button class="bn-item active" id="bn-home" onclick="switchPanel('home')">
|
|
||||||
<span class="bn-icon">👟</span>Steps
|
|
||||||
</button>
|
|
||||||
<button class="bn-item" id="bn-friends" onclick="switchPanel('friends')">
|
|
||||||
<span class="bn-icon bn-badge" id="bn-friends-badge" data-count="0">👥</span>Friends
|
|
||||||
</button>
|
|
||||||
<button class="bn-item" id="bn-settings" onclick="switchPanel('settings')">
|
|
||||||
<span class="bn-icon">⚙️</span>Setup
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- PROFILE MODAL -->
|
<!-- PROFILE MODAL -->
|
||||||
@@ -1471,11 +1561,11 @@ function refreshProfileUI() {
|
|||||||
const name = currentUserData.displayName || currentUser.email.split('@')[0];
|
const name = currentUserData.displayName || currentUser.email.split('@')[0];
|
||||||
const initial = name[0].toUpperCase();
|
const initial = name[0].toUpperCase();
|
||||||
const avatarUrl = currentUserData.avatarUrl || '';
|
const avatarUrl = currentUserData.avatarUrl || '';
|
||||||
|
|
||||||
document.getElementById('nav-name').textContent = name;
|
|
||||||
setAvatarEl(document.getElementById('nav-avatar'), 'nav-avatar-initial', initial, avatarUrl, 36);
|
setAvatarEl(document.getElementById('nav-avatar'), 'nav-avatar-initial', initial, avatarUrl, 36);
|
||||||
setAvatarEl(document.getElementById('settings-avatar-display'), 'settings-avatar-initial', initial, avatarUrl, 48);
|
setAvatarEl(document.getElementById('settings-avatar-display'), 'settings-avatar-initial', initial, avatarUrl, 48);
|
||||||
document.getElementById('settings-display-name-show').textContent = name;
|
document.getElementById('settings-display-name-show').textContent = name;
|
||||||
|
document.getElementById('menu-user-name').textContent = name;
|
||||||
|
document.getElementById('menu-user-email').textContent = currentUser.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAvatarEl(wrapEl, initialId, initial, url, size) {
|
function setAvatarEl(wrapEl, initialId, initial, url, size) {
|
||||||
@@ -1742,8 +1832,13 @@ function renderFriendsPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateBadge() {
|
function updateBadge() {
|
||||||
const badge = document.getElementById('bn-friends-badge');
|
const badge = document.getElementById('mi-friends-badge');
|
||||||
badge.setAttribute('data-count', pendingReceived.length);
|
if (pendingReceived.length > 0) {
|
||||||
|
badge.textContent = pendingReceived.length;
|
||||||
|
badge.classList.add('show');
|
||||||
|
} else {
|
||||||
|
badge.classList.remove('show');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.sendFriendRequest = async function() {
|
window.sendFriendRequest = async function() {
|
||||||
@@ -1813,11 +1908,24 @@ window.respondRequest = async function(requestId, status) {
|
|||||||
window.switchPanel = function(name) {
|
window.switchPanel = function(name) {
|
||||||
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
|
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
|
||||||
document.getElementById('panel-' + name).classList.add('active');
|
document.getElementById('panel-' + name).classList.add('active');
|
||||||
document.querySelectorAll('.bn-item').forEach(b => b.classList.remove('active'));
|
document.querySelectorAll('.menu-item').forEach(b => b.classList.remove('active'));
|
||||||
document.getElementById('bn-' + name).classList.add('active');
|
const mi = document.getElementById('mi-' + name);
|
||||||
|
if (mi) mi.classList.add('active');
|
||||||
if (name === 'friends') loadFriends();
|
if (name === 'friends') loadFriends();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.toggleMenu = function() {
|
||||||
|
const overlay = document.getElementById('menu-overlay');
|
||||||
|
const btn = document.getElementById('hamburger-btn');
|
||||||
|
const isOpen = overlay.classList.contains('open');
|
||||||
|
if (isOpen) { closeMenu(); } else { overlay.classList.add('open'); btn.classList.add('open'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
window.closeMenu = function() {
|
||||||
|
document.getElementById('menu-overlay').classList.remove('open');
|
||||||
|
document.getElementById('hamburger-btn').classList.remove('open');
|
||||||
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────
|
// ─────────────────────────────────────────────
|
||||||
// COPY WEBHOOK
|
// COPY WEBHOOK
|
||||||
// ─────────────────────────────────────────────
|
// ─────────────────────────────────────────────
|
||||||
@@ -1948,7 +2056,7 @@ document.getElementById('profile-name-input').addEventListener('input', updatePr
|
|||||||
document.getElementById('profile-avatar-input').addEventListener('input', updateProfilePreview);
|
document.getElementById('profile-avatar-input').addEventListener('input', updateProfilePreview);
|
||||||
|
|
||||||
document.addEventListener('keydown', e => {
|
document.addEventListener('keydown', e => {
|
||||||
if (e.key === 'Escape') closeProfileModal();
|
if (e.key === 'Escape') { closeProfileModal(); closeMenu(); }
|
||||||
});
|
});
|
||||||
|
|
||||||
let ptrStartY = 0;
|
let ptrStartY = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user