diff --git a/config.php b/config.php index 768a6fc..77c3626 100644 --- a/config.php +++ b/config.php @@ -1,7 +1,7 @@
- chat - + + diff --git a/public/plugins/chat/chat.js b/public/plugins/chat/chat.js index 5a778c3..0e264aa 100644 --- a/public/plugins/chat/chat.js +++ b/public/plugins/chat/chat.js @@ -179,11 +179,18 @@ function hideAllViews() { .forEach(id => $(id)?.classList.add('hidden')); } +// Toggle the caret + make the "chat" trigger look/feel clickable only +// when there's actually somewhere to go back to. +function setNavState(canBack) { + $('chat-header-back-caret')?.classList.toggle('hidden', !canBack); + $('chat-header-nav')?.classList.toggle('navigable', canBack); +} + function showListView() { hideAllViews(); $('chat-list-view').classList.remove('hidden'); $('chat-flyout-title').textContent = 'chat'; - $('chat-header-back')?.classList.add('hidden'); + setNavState(false); $('chat-thread-disposable')?.classList.add('hidden'); $('chat-thread-info')?.classList.add('hidden'); currentThread = null; @@ -193,7 +200,7 @@ function showThreadView(thread) { hideAllViews(); $('chat-thread-view').classList.remove('hidden'); $('chat-flyout-title').textContent = 'chat'; - $('chat-header-back')?.classList.remove('hidden'); + setNavState(true); currentThread = thread; // Room-only: info icon. 1:1-only: disposable icon. $('chat-thread-info')?.classList.toggle('hidden', thread.kind !== 'room'); @@ -205,7 +212,7 @@ function showThreadView(thread) { function showPickView() { hideAllViews(); $('chat-pick-view').classList.remove('hidden'); - $('chat-header-back')?.classList.add('hidden'); + setNavState(false); $('chat-thread-disposable')?.classList.add('hidden'); $('chat-thread-info')?.classList.add('hidden'); } @@ -213,7 +220,7 @@ function showPickView() { function showRoomInfoView() { hideAllViews(); $('chat-room-info-view').classList.remove('hidden'); - $('chat-header-back')?.classList.add('hidden'); + setNavState(false); $('chat-thread-disposable')?.classList.add('hidden'); $('chat-thread-info')?.classList.add('hidden'); } @@ -749,12 +756,14 @@ function init() { $('chat-refresh-btn').addEventListener('click', () => pollOnce().then(loadConversations)); $('chat-new-btn').addEventListener('click', openPickView); $('chat-empty-start')?.addEventListener('click', openPickView); - const gotoList = () => { showListView(); loadConversations(); }; - $('chat-header-back').addEventListener('click', gotoList); - // Clicking the "chat" title is the same as hitting the back caret — - // a second affordance so users don't have to aim at a small icon. - // No-op when already on the list (showListView is idempotent). - $('chat-flyout-title')?.addEventListener('click', gotoList); + // Caret + "chat" label are one button with one tooltip. Click is a + // no-op when the caret is hidden (already on the list) — the CSS + // .navigable class tracks whether the button looks clickable. + $('chat-header-nav')?.addEventListener('click', () => { + if (!$('chat-header-nav')?.classList.contains('navigable')) return; + showListView(); + loadConversations(); + }); $('chat-pick-back').addEventListener('click', showListView); $('chat-thread-form').addEventListener('submit', sendMessage); $('chat-thread-info')?.addEventListener('click', openRoomInfo); diff --git a/public/plugins/tests/chat.test.js b/public/plugins/tests/chat.test.js index fa44953..7e9498a 100644 --- a/public/plugins/tests/chat.test.js +++ b/public/plugins/tests/chat.test.js @@ -30,20 +30,20 @@ describe('Chat: flyout', () => { expect(f.classList.contains('hidden')).toBe(true); }); - it('header-back visibility matches active view', async () => { + it('header back-caret visibility matches active view', async () => { await ensureFlyoutClosed(); click('#chat-footer-btn'); await waitFor(() => !$('#chat-flyout').classList.contains('hidden')); // Wait for loadConversations + initial view pick to settle. await sleep(150); - const back = $('#chat-header-back'); + const caret = $('#chat-header-back-caret'); const threadShown = !$('#chat-thread-view').classList.contains('hidden'); const listShown = !$('#chat-list-view').classList.contains('hidden'); // Exactly one of thread / list should be active here (pick view // is only reached via the + button). expect(threadShown || listShown).toBe(true); - if (threadShown) expect(back.classList.contains('hidden')).toBe(false); - if (listShown) expect(back.classList.contains('hidden')).toBe(true); + if (threadShown) expect(caret.classList.contains('hidden')).toBe(false); + if (listShown) expect(caret.classList.contains('hidden')).toBe(true); await ensureFlyoutClosed(); }); @@ -57,9 +57,9 @@ describe('Chat: flyout', () => { expect($('#chat-pick-view').classList.contains('hidden')).toBe(false); expect($('#chat-list-view').classList.contains('hidden')).toBe(true); expect($('#chat-thread-view').classList.contains('hidden')).toBe(true); - // Header back is only for thread view — not needed when pick has + // Back caret is only for thread view — not needed when pick has // its own dedicated back button. - expect($('#chat-header-back').classList.contains('hidden')).toBe(true); + expect($('#chat-header-back-caret').classList.contains('hidden')).toBe(true); click('#chat-pick-back'); await sleep(50); expect($('#chat-pick-view').classList.contains('hidden')).toBe(true);