fix(chat-room): drop redundant room header bar, topic moves to tab tooltip
- commit
- a2d83fb4c7e07ecc39f57c8f9e50955c4491d835
- author
- Kit · · 2026-09-27 13:49 UTC
- parent
- 6750577
- signature
- SSH Unverified
SHA256:H91DFuEG4jpk5edzLA4aUKkpv4zv9Q339ipRmkph6TkThe key is unknown
2 files · +25 -45
M src/lib/components/chat-room.svelte +15 -44
| @@ -35,12 +35,10 @@ | |||
| 35 | 35 | // Props | |
| 36 | 36 | let { | |
| 37 | 37 | roomId: requestedRoomId = null, | |
| 38 | - | roomName = 'General', | |
| 39 | 38 | initialTextStyle = DEFAULT_TEXT_STYLE, | |
| 40 | 39 | showUserList = false | |
| 41 | 40 | } = $props<{ | |
| 42 | 41 | roomId?: Id<'chatRooms'> | null; | |
| 43 | - | roomName?: string; | |
| 44 | 42 | initialTextStyle?: TextStyle; | |
| 45 | 43 | /** Mobile only: whether the people list is expanded */ | |
| 46 | 44 | showUserList?: boolean; | |
| @@ -64,10 +62,6 @@ | |||
| 64 | 62 | (requestedRoomId ?? defaultRoomQuery?.data?.id) as Id<'chatRooms'> | undefined | |
| 65 | 63 | ); | |
| 66 | 64 | ||
| 67 | - | // Room topic (from public group rooms list) | |
| 68 | - | const groupRoomsQuery = isClient ? useQuery(convexApi.aim.getGroupRooms, {}) : null; | |
| 69 | - | const roomTopic = $derived(groupRoomsQuery?.data?.find((r) => r.id === roomId)?.topic ?? null); | |
| 70 | - | ||
| 71 | 65 | // Subscribe to messages - skipped until the room ID is available | |
| 72 | 66 | const messagesQuery = isClient | |
| 73 | 67 | ? useQuery(convexApi.queries.getMessagesPublic, () => (roomId ? { roomId } : 'skip')) | |
| @@ -1051,21 +1045,18 @@ | |||
| 1051 | 1045 | </script> | |
| 1052 | 1046 | ||
| 1053 | 1047 | <div class="chat-room"> | |
| 1054 | - | <div class="room-header"> | |
| 1055 | - | <img src="/aim/chat-room-16.png" alt="" width="16" height="16" /> | |
| 1056 | - | <span class="room-name">{roomName}</span> | |
| 1057 | - | {#if roomTopic} | |
| 1058 | - | <span class="room-topic">— {roomTopic}</span> | |
| 1059 | - | {/if} | |
| 1060 | - | {#if mentionUnreadCount > 0} | |
| 1061 | - | <span class="room-mentions" | |
| 1062 | - | >({mentionUnreadCount} mention{mentionUnreadCount > 1 ? 's' : ''})</span | |
| 1063 | - | > | |
| 1064 | - | {/if} | |
| 1065 | - | {#if connectionError} | |
| 1066 | - | <span class="connection-error">⚠️ {connectionError}</span> | |
| 1067 | - | {/if} | |
| 1068 | - | </div> | |
| 1048 | + | {#if mentionUnreadCount > 0 || connectionError} | |
| 1049 | + | <div class="room-status"> | |
| 1050 | + | {#if mentionUnreadCount > 0} | |
| 1051 | + | <span class="room-mentions" | |
| 1052 | + | >({mentionUnreadCount} mention{mentionUnreadCount > 1 ? 's' : ''})</span | |
| 1053 | + | > | |
| 1054 | + | {/if} | |
| 1055 | + | {#if connectionError} | |
| 1056 | + | <span class="connection-error">⚠️ {connectionError}</span> | |
| 1057 | + | {/if} | |
| 1058 | + | </div> | |
| 1059 | + | {/if} | |
| 1069 | 1060 | ||
| 1070 | 1061 | <div class="room-panel" bind:this={roomPanel}> | |
| 1071 | 1062 | <div class="chat-container"> | |
| @@ -1449,32 +1440,12 @@ | |||
| 1449 | 1440 | } | |
| 1450 | 1441 | ||
| 1451 | 1442 | /* ===== Layout inside the XP window ===== */ | |
| 1452 | - | .room-header { | |
| 1443 | + | .room-status { | |
| 1453 | 1444 | display: flex; | |
| 1454 | - | align-items: center; | |
| 1445 | + | justify-content: flex-end; | |
| 1455 | 1446 | gap: 6px; | |
| 1456 | - | padding: 3px 6px; | |
| 1457 | - | margin-bottom: 4px; | |
| 1458 | - | background: #ece9d8; | |
| 1459 | - | border-bottom: 1px solid #aca899; | |
| 1460 | - | color: #333; | |
| 1461 | - | font-family: Tahoma, 'Pixelated MS Sans Serif', sans-serif; | |
| 1462 | 1447 | font-size: 11px; | |
| 1463 | - | flex: 0 0 auto; | |
| 1464 | - | min-width: 0; | |
| 1465 | - | } | |
| 1466 | - | ||
| 1467 | - | .room-name { | |
| 1468 | - | font-weight: bold; | |
| 1469 | - | } | |
| 1470 | - | ||
| 1471 | - | .room-topic { | |
| 1472 | - | color: #666; | |
| 1473 | - | overflow: hidden; | |
| 1474 | - | text-overflow: ellipsis; | |
| 1475 | - | white-space: nowrap; | |
| 1476 | - | min-width: 0; | |
| 1477 | - | flex: 1; | |
| 1448 | + | margin-bottom: 3px; | |
| 1478 | 1449 | } | |
| 1479 | 1450 | ||
| 1480 | 1451 | .room-mentions { | |
| @@ -35,12 +35,10 @@ | |||||
| 35 | // Props | 35 | // Props | ||
| 36 | let { | 36 | let { | ||
| 37 | roomId: requestedRoomId = null, | 37 | roomId: requestedRoomId = null, | ||
| 38 | - | roomName = 'General', | |||
| 39 | initialTextStyle = DEFAULT_TEXT_STYLE, | 38 | initialTextStyle = DEFAULT_TEXT_STYLE, | ||
| 40 | showUserList = false | 39 | showUserList = false | ||
| 41 | } = $props<{ | 40 | } = $props<{ | ||
| 42 | roomId?: Id<'chatRooms'> | null; | 41 | roomId?: Id<'chatRooms'> | null; | ||
| 43 | - | roomName?: string; | |||
| 44 | initialTextStyle?: TextStyle; | 42 | initialTextStyle?: TextStyle; | ||
| 45 | /** Mobile only: whether the people list is expanded */ | 43 | /** Mobile only: whether the people list is expanded */ | ||
| 46 | showUserList?: boolean; | 44 | showUserList?: boolean; | ||
| @@ -64,10 +62,6 @@ | |||||
| 64 | (requestedRoomId ?? defaultRoomQuery?.data?.id) as Id<'chatRooms'> | undefined | 62 | (requestedRoomId ?? defaultRoomQuery?.data?.id) as Id<'chatRooms'> | undefined | ||
| 65 | ); | 63 | ); | ||
| 66 | 64 | ||||
| 67 | - | // Room topic (from public group rooms list) | |||
| 68 | - | const groupRoomsQuery = isClient ? useQuery(convexApi.aim.getGroupRooms, {}) : null; | |||
| 69 | - | const roomTopic = $derived(groupRoomsQuery?.data?.find((r) => r.id === roomId)?.topic ?? null); | |||
| 70 | - | ||||
| 71 | // Subscribe to messages - skipped until the room ID is available | 65 | // Subscribe to messages - skipped until the room ID is available | ||
| 72 | const messagesQuery = isClient | 66 | const messagesQuery = isClient | ||
| 73 | ? useQuery(convexApi.queries.getMessagesPublic, () => (roomId ? { roomId } : 'skip')) | 67 | ? useQuery(convexApi.queries.getMessagesPublic, () => (roomId ? { roomId } : 'skip')) | ||
| @@ -1051,21 +1045,18 @@ | |||||
| 1051 | </script> | 1045 | </script> | ||
| 1052 | 1046 | ||||
| 1053 | <div class="chat-room"> | 1047 | <div class="chat-room"> | ||
| 1054 | - | <div class="room-header"> | 1048 | + | {#if mentionUnreadCount > 0 || connectionError} |
| 1055 | - | <img src="/aim/chat-room-16.png" alt="" width="16" height="16" /> | 1049 | + | <div class="room-status"> |
| 1056 | - | <span class="room-name">{roomName}</span> | 1050 | + | {#if mentionUnreadCount > 0} |
| 1057 | - | {#if roomTopic} | 1051 | + | <span class="room-mentions" |
| 1058 | - | <span class="room-topic">— {roomTopic}</span> | 1052 | + | >({mentionUnreadCount} mention{mentionUnreadCount > 1 ? 's' : ''})</span |
| 1059 | - | {/if} | 1053 | + | > |
| 1060 | - | {#if mentionUnreadCount > 0} | 1054 | + | {/if} |
| 1061 | - | <span class="room-mentions" | 1055 | + | {#if connectionError} |
| 1062 | - | >({mentionUnreadCount} mention{mentionUnreadCount > 1 ? 's' : ''})</span | 1056 | + | <span class="connection-error">⚠️ {connectionError}</span> |
| 1063 | - | > | 1057 | + | {/if} |
| 1064 | - | {/if} | 1058 | + | </div> |
| 1065 | - | {#if connectionError} | 1059 | + | {/if} |
| 1066 | - | <span class="connection-error">⚠️ {connectionError}</span> | |||
| 1067 | - | {/if} | |||
| 1068 | - | </div> | |||
| 1069 | 1060 | ||||
| 1070 | <div class="room-panel" bind:this={roomPanel}> | 1061 | <div class="room-panel" bind:this={roomPanel}> | ||
| 1071 | <div class="chat-container"> | 1062 | <div class="chat-container"> | ||
| @@ -1449,32 +1440,12 @@ | |||||
| 1449 | } | 1440 | } | ||
| 1450 | 1441 | ||||
| 1451 | /* ===== Layout inside the XP window ===== */ | 1442 | /* ===== Layout inside the XP window ===== */ | ||
| 1452 | - | .room-header { | 1443 | + | .room-status { |
| 1453 | display: flex; | 1444 | display: flex; | ||
| 1454 | - | align-items: center; | 1445 | + | justify-content: flex-end; |
| 1455 | gap: 6px; | 1446 | gap: 6px; | ||
| 1456 | - | padding: 3px 6px; | |||
| 1457 | - | margin-bottom: 4px; | |||
| 1458 | - | background: #ece9d8; | |||
| 1459 | - | border-bottom: 1px solid #aca899; | |||
| 1460 | - | color: #333; | |||
| 1461 | - | font-family: Tahoma, 'Pixelated MS Sans Serif', sans-serif; | |||
| 1462 | font-size: 11px; | 1447 | font-size: 11px; | ||
| 1463 | - | flex: 0 0 auto; | 1448 | + | margin-bottom: 3px; |
| 1464 | - | min-width: 0; | |||
| 1465 | - | } | |||
| 1466 | - | ||||
| 1467 | - | .room-name { | |||
| 1468 | - | font-weight: bold; | |||
| 1469 | - | } | |||
| 1470 | - | ||||
| 1471 | - | .room-topic { | |||
| 1472 | - | color: #666; | |||
| 1473 | - | overflow: hidden; | |||
| 1474 | - | text-overflow: ellipsis; | |||
| 1475 | - | white-space: nowrap; | |||
| 1476 | - | min-width: 0; | |||
| 1477 | - | flex: 1; | |||
| 1478 | } | 1449 | } | ||
| 1479 | 1450 | ||||
| 1480 | .room-mentions { | 1451 | .room-mentions { | ||
M src/lib/components/desktop/chat-rooms-window.svelte +10 -1
| @@ -4,12 +4,20 @@ | |||
| 4 | 4 | import { desktop, CHAT_WINDOW_ID } from '$lib/states/desktop.svelte'; | |
| 5 | 5 | import { chatState } from '$lib/states/chat.svelte'; | |
| 6 | 6 | import type { TextStyle } from '$lib/types/text-formatting'; | |
| 7 | + | import { useQuery } from 'convex-svelte'; | |
| 8 | + | import { api } from '../../../convex/_generated/api'; | |
| 9 | + | import { convexAvailable } from '$lib/aim/client'; | |
| 7 | 10 | ||
| 8 | 11 | let { initialTextStyle }: { initialTextStyle?: TextStyle } = $props(); | |
| 9 | 12 | ||
| 10 | 13 | const currentUser = $derived(chatState.getCurrentUser()); | |
| 11 | 14 | let showUserList = $state(false); | |
| 12 | 15 | ||
| 16 | + | // Room topics show as tab tooltips | |
| 17 | + | const roomsQuery = convexAvailable ? useQuery(api.aim.getGroupRooms, {}) : null; | |
| 18 | + | const topicOf = (roomId: string | null) => | |
| 19 | + | roomsQuery?.data?.find((r) => (roomId ? r.id === roomId : r.isDefault))?.topic; | |
| 20 | + | ||
| 13 | 21 | const tabKey = (roomId: string | null) => roomId ?? 'default'; | |
| 14 | 22 | </script> | |
| 15 | 23 | ||
| @@ -39,6 +47,7 @@ | |||
| 39 | 47 | role="tab" | |
| 40 | 48 | aria-selected={active} | |
| 41 | 49 | aria-controls="room-panel-{tabKey(tab.roomId)}" | |
| 50 | + | title={topicOf(tab.roomId) ?? tab.roomName} | |
| 42 | 51 | onclick={() => desktop.selectRoomTab(tab.roomId)} | |
| 43 | 52 | onauxclick={(e) => { | |
| 44 | 53 | if (e.button === 1 && desktop.roomTabs.length > 1) desktop.closeRoomTab(tab.roomId); | |
| @@ -72,7 +81,7 @@ | |||
| 72 | 81 | class="tab-panel" | |
| 73 | 82 | hidden={tab.roomId !== desktop.activeRoomId} | |
| 74 | 83 | > | |
| 75 | - | <ChatRoom roomId={tab.roomId} roomName={tab.roomName} {initialTextStyle} {showUserList} /> | |
| 84 | + | <ChatRoom roomId={tab.roomId} {initialTextStyle} {showUserList} /> | |
| 76 | 85 | </div> | |
| 77 | 86 | {/each} | |
| 78 | 87 | </div> | |
| @@ -4,12 +4,20 @@ | |||||
| 4 | import { desktop, CHAT_WINDOW_ID } from '$lib/states/desktop.svelte'; | 4 | import { desktop, CHAT_WINDOW_ID } from '$lib/states/desktop.svelte'; | ||
| 5 | import { chatState } from '$lib/states/chat.svelte'; | 5 | import { chatState } from '$lib/states/chat.svelte'; | ||
| 6 | import type { TextStyle } from '$lib/types/text-formatting'; | 6 | import type { TextStyle } from '$lib/types/text-formatting'; | ||
| 7 | + | import { useQuery } from 'convex-svelte'; | |||
| 8 | + | import { api } from '../../../convex/_generated/api'; | |||
| 9 | + | import { convexAvailable } from '$lib/aim/client'; | |||
| 7 | 10 | ||||
| 8 | let { initialTextStyle }: { initialTextStyle?: TextStyle } = $props(); | 11 | let { initialTextStyle }: { initialTextStyle?: TextStyle } = $props(); | ||
| 9 | 12 | ||||
| 10 | const currentUser = $derived(chatState.getCurrentUser()); | 13 | const currentUser = $derived(chatState.getCurrentUser()); | ||
| 11 | let showUserList = $state(false); | 14 | let showUserList = $state(false); | ||
| 12 | 15 | ||||
| 16 | + | // Room topics show as tab tooltips | |||
| 17 | + | const roomsQuery = convexAvailable ? useQuery(api.aim.getGroupRooms, {}) : null; | |||
| 18 | + | const topicOf = (roomId: string | null) => | |||
| 19 | + | roomsQuery?.data?.find((r) => (roomId ? r.id === roomId : r.isDefault))?.topic; | |||
| 20 | + | ||||
| 13 | const tabKey = (roomId: string | null) => roomId ?? 'default'; | 21 | const tabKey = (roomId: string | null) => roomId ?? 'default'; | ||
| 14 | </script> | 22 | </script> | ||
| 15 | 23 | ||||
| @@ -39,6 +47,7 @@ | |||||
| 39 | role="tab" | 47 | role="tab" | ||
| 40 | aria-selected={active} | 48 | aria-selected={active} | ||
| 41 | aria-controls="room-panel-{tabKey(tab.roomId)}" | 49 | aria-controls="room-panel-{tabKey(tab.roomId)}" | ||
| 50 | + | title={topicOf(tab.roomId) ?? tab.roomName} | |||
| 42 | onclick={() => desktop.selectRoomTab(tab.roomId)} | 51 | onclick={() => desktop.selectRoomTab(tab.roomId)} | ||
| 43 | onauxclick={(e) => { | 52 | onauxclick={(e) => { | ||
| 44 | if (e.button === 1 && desktop.roomTabs.length > 1) desktop.closeRoomTab(tab.roomId); | 53 | if (e.button === 1 && desktop.roomTabs.length > 1) desktop.closeRoomTab(tab.roomId); | ||
| @@ -72,7 +81,7 @@ | |||||
| 72 | class="tab-panel" | 81 | class="tab-panel" | ||
| 73 | hidden={tab.roomId !== desktop.activeRoomId} | 82 | hidden={tab.roomId !== desktop.activeRoomId} | ||
| 74 | > | 83 | > | ||
| 75 | - | <ChatRoom roomId={tab.roomId} roomName={tab.roomName} {initialTextStyle} {showUserList} /> | 84 | + | <ChatRoom roomId={tab.roomId} {initialTextStyle} {showUserList} /> |
| 76 | </div> | 85 | </div> | ||
| 77 | {/each} | 86 | {/each} | ||
| 78 | </div> | 87 | </div> | ||