feat(chat-room): authentic AIM people list and XP-style registration notice
- commit
- c4d09f0792bc33f6d30caf3aff75ed9b6e048c1e
- author
- Kit · · 2026-09-27 13:25 UTC
- parent
- 8702b01
- signature
- SSH Unverified
SHA256:H91DFuEG4jpk5edzLA4aUKkpv4zv9Q339ipRmkph6TkThe key is unknown
1 file · +94 -137
M src/lib/components/chat-room.svelte +94 -137
| @@ -15,7 +15,6 @@ | |||
| 15 | 15 | import XpWindow from './desktop/xp-window.svelte'; | |
| 16 | 16 | import { desktop } from '$lib/states/desktop.svelte'; | |
| 17 | 17 | import { useAimClient } from '$lib/aim/client'; | |
| 18 | - | import { getBuddyIcon } from '$lib/aim/buddy-icons'; | |
| 19 | 18 | import { STATUS_LABELS_FR, type AimStatus } from '$lib/types/aim'; | |
| 20 | 19 | import FormattedMessage from './formatted-message.svelte'; | |
| 21 | 20 | import TextFormattingToolbar from './text-formatting-toolbar.svelte'; | |
| @@ -214,11 +213,6 @@ | |||
| 214 | 213 | userMenu = null; | |
| 215 | 214 | } | |
| 216 | 215 | ||
| 217 | - | function statusIcon(status: string) { | |
| 218 | - | const known: AimStatus[] = ['online', 'away', 'busy', 'idle', 'offline']; | |
| 219 | - | return `/aim/status-${known.includes(status as AimStatus) ? status : 'offline'}.svg`; | |
| 220 | - | } | |
| 221 | - | ||
| 222 | 216 | function statusLabel(user: SafeUser) { | |
| 223 | 217 | const label = STATUS_LABELS_FR[(user.status as AimStatus) ?? 'offline'] ?? user.status; | |
| 224 | 218 | if (user.status === 'away' && user.awayMessage) return `${label} : ${user.awayMessage}`; | |
| @@ -304,7 +298,7 @@ | |||
| 304 | 298 | ||
| 305 | 299 | // User counts | |
| 306 | 300 | const usersOnline = $derived(onlineUsers.filter((u) => u.status !== 'offline')); | |
| 307 | - | const usersOffline = $derived(onlineUsers.filter((u) => u.status === 'offline')); | |
| 301 | + | const selectedUser = $derived(onlineUsers.find((u) => u.id === selectedUserId) ?? null); | |
| 308 | 302 | ||
| 309 | 303 | const mentionableUsers = $derived( | |
| 310 | 304 | onlineUsers.filter((user) => !currentUser || user.id !== currentUser.id) | |
| @@ -1123,10 +1117,9 @@ | |||
| 1123 | 1117 | {/if} | |
| 1124 | 1118 | {#if showRegistrationPrompt} | |
| 1125 | 1119 | <div class="registration-prompt"> | |
| 1126 | - | <p> | |
| 1127 | - | 👋 <button class="link-button" onclick={openSignup}>Inscris-toi</button>pour lire le | |
| 1128 | - | reste du chat ! | |
| 1129 | - | </p> | |
| 1120 | + | <img src="/aim/xp-info-16.png" alt="" width="16" height="16" /> | |
| 1121 | + | <span>Vous devez être inscrit pour lire l'historique complet de ce salon.</span> | |
| 1122 | + | <button type="button" onclick={openSignup}>S'inscrire</button> | |
| 1130 | 1123 | </div> | |
| 1131 | 1124 | {/if} | |
| 1132 | 1125 | {#each visibleMessages as message (message.id)} | |
| @@ -1355,50 +1348,20 @@ | |||
| 1355 | 1348 | {/if} | |
| 1356 | 1349 | </div> | |
| 1357 | 1350 | ||
| 1358 | - | <!-- Participants (AIM chat room people list) --> | |
| 1359 | - | <div | |
| 1360 | - | class="sunken-panel users-list" | |
| 1361 | - | class:mobile={isMobile} | |
| 1362 | - | class:hidden={isMobile && !showUserList} | |
| 1363 | - | > | |
| 1364 | - | <p class="section-header">Participants ({usersOnline.length})</p> | |
| 1365 | - | {#each usersOnline as user (user.id)} | |
| 1366 | - | <div | |
| 1367 | - | class="user {user.status}" | |
| 1368 | - | class:selected={selectedUserId === user.id} | |
| 1369 | - | class:me={currentUser?.id === user.id} | |
| 1370 | - | role="button" | |
| 1371 | - | tabindex="0" | |
| 1372 | - | title={statusLabel(user)} | |
| 1373 | - | onclick={(e) => { | |
| 1374 | - | e.stopPropagation(); | |
| 1375 | - | selectedUserId = user.id; | |
| 1376 | - | closeUserMenu(); | |
| 1377 | - | }} | |
| 1378 | - | ondblclick={() => openImWith(user)} | |
| 1379 | - | oncontextmenu={(e) => openUserMenu(e, user)} | |
| 1380 | - | onkeydown={(e) => { | |
| 1381 | - | if (e.key === 'Enter') openImWith(user); | |
| 1382 | - | }} | |
| 1383 | - | > | |
| 1384 | - | <span | |
| 1385 | - | class="buddy-tile" | |
| 1386 | - | style="background: {getBuddyIcon(user.buddyIcon).bg}" | |
| 1387 | - | aria-hidden="true">{getBuddyIcon(user.buddyIcon).emoji}</span | |
| 1388 | - | > | |
| 1389 | - | <img class="status-icon" src={statusIcon(user.status)} alt="" width="12" height="12" /> | |
| 1390 | - | <span class="nickname">{user.nickname}</span> | |
| 1391 | - | </div> | |
| 1392 | - | {/each} | |
| 1393 | - | ||
| 1394 | - | {#if usersOffline.length > 0} | |
| 1395 | - | <div class="section-separator"></div> | |
| 1396 | - | <p class="section-header offline-header">Hors ligne ({usersOffline.length})</p> | |
| 1397 | - | {#each usersOffline as user (user.id)} | |
| 1351 | + | <!-- People here (AIM chat room) --> | |
| 1352 | + | <div class="people-panel" class:mobile={isMobile} class:hidden={isMobile && !showUserList}> | |
| 1353 | + | <p class="people-count"> | |
| 1354 | + | {usersOnline.length} | |
| 1355 | + | {usersOnline.length === 1 ? 'personne ici' : 'personnes ici'} | |
| 1356 | + | </p> | |
| 1357 | + | <div class="sunken-panel users-list" role="listbox" aria-label="Personnes dans le salon"> | |
| 1358 | + | {#each usersOnline as user (user.id)} | |
| 1398 | 1359 | <div | |
| 1399 | - | class="user offline" | |
| 1360 | + | class="user {user.status}" | |
| 1400 | 1361 | class:selected={selectedUserId === user.id} | |
| 1401 | - | role="button" | |
| 1362 | + | class:me={currentUser?.id === user.id} | |
| 1363 | + | role="option" | |
| 1364 | + | aria-selected={selectedUserId === user.id} | |
| 1402 | 1365 | tabindex="0" | |
| 1403 | 1366 | title={statusLabel(user)} | |
| 1404 | 1367 | onclick={(e) => { | |
| @@ -1412,11 +1375,34 @@ | |||
| 1412 | 1375 | if (e.key === 'Enter') openImWith(user); | |
| 1413 | 1376 | }} | |
| 1414 | 1377 | > | |
| 1415 | - | <img class="status-icon" src={statusIcon('offline')} alt="" width="12" height="12" /> | |
| 1416 | 1378 | <span class="nickname">{user.nickname}</span> | |
| 1417 | 1379 | </div> | |
| 1418 | 1380 | {/each} | |
| 1419 | - | {/if} | |
| 1381 | + | </div> | |
| 1382 | + | <div class="people-actions"> | |
| 1383 | + | <button | |
| 1384 | + | type="button" | |
| 1385 | + | disabled={!selectedUser || selectedUser.id === currentUser?.id} | |
| 1386 | + | onclick={() => selectedUser && openImWith(selectedUser)}>IM</button | |
| 1387 | + | > | |
| 1388 | + | <button | |
| 1389 | + | type="button" | |
| 1390 | + | disabled={!selectedUser} | |
| 1391 | + | onclick={() => | |
| 1392 | + | selectedUser && | |
| 1393 | + | desktop.openProfile({ | |
| 1394 | + | userId: selectedUser.id as Id<'users'>, | |
| 1395 | + | nickname: selectedUser.nickname | |
| 1396 | + | })}>Infos</button | |
| 1397 | + | > | |
| 1398 | + | <button | |
| 1399 | + | type="button" | |
| 1400 | + | disabled={!selectedUser || selectedUser.id === currentUser?.id} | |
| 1401 | + | onclick={() => | |
| 1402 | + | selectedUser && void aim.addBuddy(selectedUser.id as Id<'users'>).catch(console.error)} | |
| 1403 | + | >Ajouter</button | |
| 1404 | + | > | |
| 1405 | + | </div> | |
| 1420 | 1406 | </div> | |
| 1421 | 1407 | ||
| 1422 | 1408 | {#if userMenu} | |
| @@ -1533,26 +1519,56 @@ | |||
| 1533 | 1519 | flex-direction: column; | |
| 1534 | 1520 | } | |
| 1535 | 1521 | ||
| 1536 | - | .users-list { | |
| 1537 | - | width: 10.5rem; | |
| 1522 | + | .people-panel { | |
| 1523 | + | width: 9.5rem; | |
| 1538 | 1524 | flex: 0 0 auto; | |
| 1539 | - | padding: 0.4rem; | |
| 1525 | + | display: flex; | |
| 1526 | + | flex-direction: column; | |
| 1527 | + | min-height: 0; | |
| 1528 | + | } | |
| 1529 | + | ||
| 1530 | + | .people-count { | |
| 1531 | + | margin: 0 0 3px 0; | |
| 1532 | + | font-weight: bold; | |
| 1533 | + | font-size: 0.8rem; | |
| 1534 | + | white-space: nowrap; | |
| 1535 | + | } | |
| 1536 | + | ||
| 1537 | + | .users-list { | |
| 1538 | + | flex: 1 1 auto; | |
| 1539 | + | min-height: 0; | |
| 1540 | + | padding: 2px; | |
| 1540 | 1541 | overflow-y: auto; | |
| 1541 | 1542 | font-size: 0.85rem; | |
| 1542 | 1543 | } | |
| 1543 | 1544 | ||
| 1545 | + | .people-actions { | |
| 1546 | + | display: flex; | |
| 1547 | + | gap: 3px; | |
| 1548 | + | margin-top: 4px; | |
| 1549 | + | } | |
| 1550 | + | ||
| 1551 | + | .people-actions button { | |
| 1552 | + | flex: 1 1 0; | |
| 1553 | + | min-width: 0; | |
| 1554 | + | padding: 0 4px; | |
| 1555 | + | font-size: 0.75rem; | |
| 1556 | + | } | |
| 1557 | + | ||
| 1544 | 1558 | .user { | |
| 1545 | 1559 | display: flex; | |
| 1546 | 1560 | align-items: center; | |
| 1547 | - | gap: 4px; | |
| 1548 | 1561 | padding: 1px 3px; | |
| 1549 | - | cursor: pointer; | |
| 1562 | + | cursor: default; | |
| 1550 | 1563 | border: 1px dotted transparent; | |
| 1551 | 1564 | user-select: none; | |
| 1565 | + | overflow: hidden; | |
| 1552 | 1566 | } | |
| 1553 | 1567 | ||
| 1554 | - | .user:hover { | |
| 1555 | - | background: #e8eefb; | |
| 1568 | + | .user .nickname { | |
| 1569 | + | overflow: hidden; | |
| 1570 | + | text-overflow: ellipsis; | |
| 1571 | + | white-space: nowrap; | |
| 1556 | 1572 | } | |
| 1557 | 1573 | ||
| 1558 | 1574 | .user.selected { | |
| @@ -1575,22 +1591,6 @@ | |||
| 1575 | 1591 | color: #fff; | |
| 1576 | 1592 | } | |
| 1577 | 1593 | ||
| 1578 | - | .buddy-tile { | |
| 1579 | - | display: inline-flex; | |
| 1580 | - | align-items: center; | |
| 1581 | - | justify-content: center; | |
| 1582 | - | width: 16px; | |
| 1583 | - | height: 16px; | |
| 1584 | - | font-size: 11px; | |
| 1585 | - | border: 1px solid rgba(0, 0, 0, 0.25); | |
| 1586 | - | border-radius: 2px; | |
| 1587 | - | flex: 0 0 16px; | |
| 1588 | - | } | |
| 1589 | - | ||
| 1590 | - | .status-icon { | |
| 1591 | - | flex: 0 0 12px; | |
| 1592 | - | } | |
| 1593 | - | ||
| 1594 | 1594 | .user-menu { | |
| 1595 | 1595 | position: absolute; | |
| 1596 | 1596 | z-index: 50; | |
| @@ -1715,29 +1715,6 @@ | |||
| 1715 | 1715 | -webkit-text-fill-color: transparent !important; | |
| 1716 | 1716 | } | |
| 1717 | 1717 | ||
| 1718 | - | .section-header { | |
| 1719 | - | margin: 0 0 0.25rem 0; | |
| 1720 | - | font-weight: bold; | |
| 1721 | - | font-size: 0.75rem; | |
| 1722 | - | text-transform: uppercase; | |
| 1723 | - | letter-spacing: 0.05em; | |
| 1724 | - | color: var(--color-muted-foreground); | |
| 1725 | - | } | |
| 1726 | - | ||
| 1727 | - | .offline-header { | |
| 1728 | - | opacity: 0.7; | |
| 1729 | - | } | |
| 1730 | - | ||
| 1731 | - | .section-separator { | |
| 1732 | - | border-top: 1px solid var(--color-border); | |
| 1733 | - | margin: 0.5rem 0; | |
| 1734 | - | } | |
| 1735 | - | ||
| 1736 | - | .user.offline { | |
| 1737 | - | color: var(--color-muted-foreground); | |
| 1738 | - | font-style: italic; | |
| 1739 | - | } | |
| 1740 | - | ||
| 1741 | 1718 | .sunken-panel { | |
| 1742 | 1719 | background: white; | |
| 1743 | 1720 | border: 0.125rem inset #dfdfdf; | |
| @@ -1788,7 +1765,7 @@ | |||
| 1788 | 1765 | flex-direction: column; | |
| 1789 | 1766 | } | |
| 1790 | 1767 | ||
| 1791 | - | .users-list.mobile { | |
| 1768 | + | .people-panel.mobile { | |
| 1792 | 1769 | width: 100%; | |
| 1793 | 1770 | max-height: 30vh; | |
| 1794 | 1771 | } | |
| @@ -1926,45 +1903,25 @@ | |||
| 1926 | 1903 | } | |
| 1927 | 1904 | ||
| 1928 | 1905 | .registration-prompt { | |
| 1929 | - | background: #fff3e0; | |
| 1930 | - | color: #e65100; | |
| 1931 | - | padding: 1rem; | |
| 1932 | - | margin-bottom: 1rem; | |
| 1933 | - | border-radius: 4px; | |
| 1934 | - | text-align: center; | |
| 1935 | - | font-size: 0.95rem; | |
| 1936 | - | border: 1px solid #ffe0b2; | |
| 1937 | - | animation: fadeIn 0.3s ease-out; | |
| 1938 | - | } | |
| 1939 | - | ||
| 1940 | - | .registration-prompt p { | |
| 1941 | - | margin: 0; | |
| 1942 | - | } | |
| 1943 | - | ||
| 1944 | - | @keyframes fadeIn { | |
| 1945 | - | from { | |
| 1946 | - | opacity: 0; | |
| 1947 | - | transform: translateY(-10px); | |
| 1948 | - | } | |
| 1949 | - | to { | |
| 1950 | - | opacity: 1; | |
| 1951 | - | transform: translateY(0); | |
| 1952 | - | } | |
| 1906 | + | display: flex; | |
| 1907 | + | align-items: center; | |
| 1908 | + | gap: 8px; | |
| 1909 | + | background: #ece9d8; | |
| 1910 | + | color: #000; | |
| 1911 | + | padding: 6px 8px; | |
| 1912 | + | margin-bottom: 6px; | |
| 1913 | + | border: 1px solid #aca899; | |
| 1914 | + | font-size: 0.85rem; | |
| 1953 | 1915 | } | |
| 1954 | 1916 | ||
| 1955 | - | .registration-prompt .link-button { | |
| 1956 | - | color: #e65100; | |
| 1957 | - | text-decoration: underline; | |
| 1958 | - | cursor: pointer; | |
| 1959 | - | background: none; | |
| 1960 | - | border: none; | |
| 1961 | - | padding: 0; | |
| 1962 | - | font: inherit; | |
| 1917 | + | .registration-prompt span { | |
| 1918 | + | flex: 1; | |
| 1963 | 1919 | } | |
| 1964 | 1920 | ||
| 1965 | - | .registration-prompt .link-button:hover { | |
| 1966 | - | color: #ef6c00; | |
| 1967 | - | text-decoration: none; | |
| 1921 | + | .registration-prompt button { | |
| 1922 | + | min-width: 0; | |
| 1923 | + | padding: 0 10px; | |
| 1924 | + | white-space: nowrap; | |
| 1968 | 1925 | } | |
| 1969 | 1926 | ||
| 1970 | 1927 | .chat-window.minimized { | |
| @@ -15,7 +15,6 @@ | |||||
| 15 | import XpWindow from './desktop/xp-window.svelte'; | 15 | import XpWindow from './desktop/xp-window.svelte'; | ||
| 16 | import { desktop } from '$lib/states/desktop.svelte'; | 16 | import { desktop } from '$lib/states/desktop.svelte'; | ||
| 17 | import { useAimClient } from '$lib/aim/client'; | 17 | import { useAimClient } from '$lib/aim/client'; | ||
| 18 | - | import { getBuddyIcon } from '$lib/aim/buddy-icons'; | |||
| 19 | import { STATUS_LABELS_FR, type AimStatus } from '$lib/types/aim'; | 18 | import { STATUS_LABELS_FR, type AimStatus } from '$lib/types/aim'; | ||
| 20 | import FormattedMessage from './formatted-message.svelte'; | 19 | import FormattedMessage from './formatted-message.svelte'; | ||
| 21 | import TextFormattingToolbar from './text-formatting-toolbar.svelte'; | 20 | import TextFormattingToolbar from './text-formatting-toolbar.svelte'; | ||
| @@ -214,11 +213,6 @@ | |||||
| 214 | userMenu = null; | 213 | userMenu = null; | ||
| 215 | } | 214 | } | ||
| 216 | 215 | ||||
| 217 | - | function statusIcon(status: string) { | |||
| 218 | - | const known: AimStatus[] = ['online', 'away', 'busy', 'idle', 'offline']; | |||
| 219 | - | return `/aim/status-${known.includes(status as AimStatus) ? status : 'offline'}.svg`; | |||
| 220 | - | } | |||
| 221 | - | ||||
| 222 | function statusLabel(user: SafeUser) { | 216 | function statusLabel(user: SafeUser) { | ||
| 223 | const label = STATUS_LABELS_FR[(user.status as AimStatus) ?? 'offline'] ?? user.status; | 217 | const label = STATUS_LABELS_FR[(user.status as AimStatus) ?? 'offline'] ?? user.status; | ||
| 224 | if (user.status === 'away' && user.awayMessage) return `${label} : ${user.awayMessage}`; | 218 | if (user.status === 'away' && user.awayMessage) return `${label} : ${user.awayMessage}`; | ||
| @@ -304,7 +298,7 @@ | |||||
| 304 | 298 | ||||
| 305 | // User counts | 299 | // User counts | ||
| 306 | const usersOnline = $derived(onlineUsers.filter((u) => u.status !== 'offline')); | 300 | const usersOnline = $derived(onlineUsers.filter((u) => u.status !== 'offline')); | ||
| 307 | - | const usersOffline = $derived(onlineUsers.filter((u) => u.status === 'offline')); | 301 | + | const selectedUser = $derived(onlineUsers.find((u) => u.id === selectedUserId) ?? null); |
| 308 | 302 | ||||
| 309 | const mentionableUsers = $derived( | 303 | const mentionableUsers = $derived( | ||
| 310 | onlineUsers.filter((user) => !currentUser || user.id !== currentUser.id) | 304 | onlineUsers.filter((user) => !currentUser || user.id !== currentUser.id) | ||
| @@ -1123,10 +1117,9 @@ | |||||
| 1123 | {/if} | 1117 | {/if} | ||
| 1124 | {#if showRegistrationPrompt} | 1118 | {#if showRegistrationPrompt} | ||
| 1125 | <div class="registration-prompt"> | 1119 | <div class="registration-prompt"> | ||
| 1126 | - | <p> | 1120 | + | <img src="/aim/xp-info-16.png" alt="" width="16" height="16" /> |
| 1127 | - | 👋 <button class="link-button" onclick={openSignup}>Inscris-toi</button>pour lire le | 1121 | + | <span>Vous devez être inscrit pour lire l'historique complet de ce salon.</span> |
| 1128 | - | reste du chat ! | 1122 | + | <button type="button" onclick={openSignup}>S'inscrire</button> |
| 1129 | - | </p> | |||
| 1130 | </div> | 1123 | </div> | ||
| 1131 | {/if} | 1124 | {/if} | ||
| 1132 | {#each visibleMessages as message (message.id)} | 1125 | {#each visibleMessages as message (message.id)} | ||
| @@ -1355,50 +1348,20 @@ | |||||
| 1355 | {/if} | 1348 | {/if} | ||
| 1356 | </div> | 1349 | </div> | ||
| 1357 | 1350 | ||||
| 1358 | - | <!-- Participants (AIM chat room people list) --> | 1351 | + | <!-- People here (AIM chat room) --> |
| 1359 | - | <div | 1352 | + | <div class="people-panel" class:mobile={isMobile} class:hidden={isMobile && !showUserList}> |
| 1360 | - | class="sunken-panel users-list" | 1353 | + | <p class="people-count"> |
| 1361 | - | class:mobile={isMobile} | 1354 | + | {usersOnline.length} |
| 1362 | - | class:hidden={isMobile && !showUserList} | 1355 | + | {usersOnline.length === 1 ? 'personne ici' : 'personnes ici'} |
| 1363 | - | > | 1356 | + | </p> |
| 1364 | - | <p class="section-header">Participants ({usersOnline.length})</p> | 1357 | + | <div class="sunken-panel users-list" role="listbox" aria-label="Personnes dans le salon"> |
| 1365 | - | {#each usersOnline as user (user.id)} | 1358 | + | {#each usersOnline as user (user.id)} |
| 1366 | - | <div | |||
| 1367 | - | class="user {user.status}" | |||
| 1368 | - | class:selected={selectedUserId === user.id} | |||
| 1369 | - | class:me={currentUser?.id === user.id} | |||
| 1370 | - | role="button" | |||
| 1371 | - | tabindex="0" | |||
| 1372 | - | title={statusLabel(user)} | |||
| 1373 | - | onclick={(e) => { | |||
| 1374 | - | e.stopPropagation(); | |||
| 1375 | - | selectedUserId = user.id; | |||
| 1376 | - | closeUserMenu(); | |||
| 1377 | - | }} | |||
| 1378 | - | ondblclick={() => openImWith(user)} | |||
| 1379 | - | oncontextmenu={(e) => openUserMenu(e, user)} | |||
| 1380 | - | onkeydown={(e) => { | |||
| 1381 | - | if (e.key === 'Enter') openImWith(user); | |||
| 1382 | - | }} | |||
| 1383 | - | > | |||
| 1384 | - | <span | |||
| 1385 | - | class="buddy-tile" | |||
| 1386 | - | style="background: {getBuddyIcon(user.buddyIcon).bg}" | |||
| 1387 | - | aria-hidden="true">{getBuddyIcon(user.buddyIcon).emoji}</span | |||
| 1388 | - | > | |||
| 1389 | - | <img class="status-icon" src={statusIcon(user.status)} alt="" width="12" height="12" /> | |||
| 1390 | - | <span class="nickname">{user.nickname}</span> | |||
| 1391 | - | </div> | |||
| 1392 | - | {/each} | |||
| 1393 | - | ||||
| 1394 | - | {#if usersOffline.length > 0} | |||
| 1395 | - | <div class="section-separator"></div> | |||
| 1396 | - | <p class="section-header offline-header">Hors ligne ({usersOffline.length})</p> | |||
| 1397 | - | {#each usersOffline as user (user.id)} | |||
| 1398 | <div | 1359 | <div | ||
| 1399 | - | class="user offline" | 1360 | + | class="user {user.status}" |
| 1400 | class:selected={selectedUserId === user.id} | 1361 | class:selected={selectedUserId === user.id} | ||
| 1401 | - | role="button" | 1362 | + | class:me={currentUser?.id === user.id} |
| 1363 | + | role="option" | |||
| 1364 | + | aria-selected={selectedUserId === user.id} | |||
| 1402 | tabindex="0" | 1365 | tabindex="0" | ||
| 1403 | title={statusLabel(user)} | 1366 | title={statusLabel(user)} | ||
| 1404 | onclick={(e) => { | 1367 | onclick={(e) => { | ||
| @@ -1412,11 +1375,34 @@ | |||||
| 1412 | if (e.key === 'Enter') openImWith(user); | 1375 | if (e.key === 'Enter') openImWith(user); | ||
| 1413 | }} | 1376 | }} | ||
| 1414 | > | 1377 | > | ||
| 1415 | - | <img class="status-icon" src={statusIcon('offline')} alt="" width="12" height="12" /> | |||
| 1416 | <span class="nickname">{user.nickname}</span> | 1378 | <span class="nickname">{user.nickname}</span> | ||
| 1417 | </div> | 1379 | </div> | ||
| 1418 | {/each} | 1380 | {/each} | ||
| 1419 | - | {/if} | 1381 | + | </div> |
| 1382 | + | <div class="people-actions"> | |||
| 1383 | + | <button | |||
| 1384 | + | type="button" | |||
| 1385 | + | disabled={!selectedUser || selectedUser.id === currentUser?.id} | |||
| 1386 | + | onclick={() => selectedUser && openImWith(selectedUser)}>IM</button | |||
| 1387 | + | > | |||
| 1388 | + | <button | |||
| 1389 | + | type="button" | |||
| 1390 | + | disabled={!selectedUser} | |||
| 1391 | + | onclick={() => | |||
| 1392 | + | selectedUser && | |||
| 1393 | + | desktop.openProfile({ | |||
| 1394 | + | userId: selectedUser.id as Id<'users'>, | |||
| 1395 | + | nickname: selectedUser.nickname | |||
| 1396 | + | })}>Infos</button | |||
| 1397 | + | > | |||
| 1398 | + | <button | |||
| 1399 | + | type="button" | |||
| 1400 | + | disabled={!selectedUser || selectedUser.id === currentUser?.id} | |||
| 1401 | + | onclick={() => | |||
| 1402 | + | selectedUser && void aim.addBuddy(selectedUser.id as Id<'users'>).catch(console.error)} | |||
| 1403 | + | >Ajouter</button | |||
| 1404 | + | > | |||
| 1405 | + | </div> | |||
| 1420 | </div> | 1406 | </div> | ||
| 1421 | 1407 | ||||
| 1422 | {#if userMenu} | 1408 | {#if userMenu} | ||
| @@ -1533,26 +1519,56 @@ | |||||
| 1533 | flex-direction: column; | 1519 | flex-direction: column; | ||
| 1534 | } | 1520 | } | ||
| 1535 | 1521 | ||||
| 1536 | - | .users-list { | 1522 | + | .people-panel { |
| 1537 | - | width: 10.5rem; | 1523 | + | width: 9.5rem; |
| 1538 | flex: 0 0 auto; | 1524 | flex: 0 0 auto; | ||
| 1539 | - | padding: 0.4rem; | 1525 | + | display: flex; |
| 1526 | + | flex-direction: column; | |||
| 1527 | + | min-height: 0; | |||
| 1528 | + | } | |||
| 1529 | + | ||||
| 1530 | + | .people-count { | |||
| 1531 | + | margin: 0 0 3px 0; | |||
| 1532 | + | font-weight: bold; | |||
| 1533 | + | font-size: 0.8rem; | |||
| 1534 | + | white-space: nowrap; | |||
| 1535 | + | } | |||
| 1536 | + | ||||
| 1537 | + | .users-list { | |||
| 1538 | + | flex: 1 1 auto; | |||
| 1539 | + | min-height: 0; | |||
| 1540 | + | padding: 2px; | |||
| 1540 | overflow-y: auto; | 1541 | overflow-y: auto; | ||
| 1541 | font-size: 0.85rem; | 1542 | font-size: 0.85rem; | ||
| 1542 | } | 1543 | } | ||
| 1543 | 1544 | ||||
| 1545 | + | .people-actions { | |||
| 1546 | + | display: flex; | |||
| 1547 | + | gap: 3px; | |||
| 1548 | + | margin-top: 4px; | |||
| 1549 | + | } | |||
| 1550 | + | ||||
| 1551 | + | .people-actions button { | |||
| 1552 | + | flex: 1 1 0; | |||
| 1553 | + | min-width: 0; | |||
| 1554 | + | padding: 0 4px; | |||
| 1555 | + | font-size: 0.75rem; | |||
| 1556 | + | } | |||
| 1557 | + | ||||
| 1544 | .user { | 1558 | .user { | ||
| 1545 | display: flex; | 1559 | display: flex; | ||
| 1546 | align-items: center; | 1560 | align-items: center; | ||
| 1547 | - | gap: 4px; | |||
| 1548 | padding: 1px 3px; | 1561 | padding: 1px 3px; | ||
| 1549 | - | cursor: pointer; | 1562 | + | cursor: default; |
| 1550 | border: 1px dotted transparent; | 1563 | border: 1px dotted transparent; | ||
| 1551 | user-select: none; | 1564 | user-select: none; | ||
| 1565 | + | overflow: hidden; | |||
| 1552 | } | 1566 | } | ||
| 1553 | 1567 | ||||
| 1554 | - | .user:hover { | 1568 | + | .user .nickname { |
| 1555 | - | background: #e8eefb; | 1569 | + | overflow: hidden; |
| 1570 | + | text-overflow: ellipsis; | |||
| 1571 | + | white-space: nowrap; | |||
| 1556 | } | 1572 | } | ||
| 1557 | 1573 | ||||
| 1558 | .user.selected { | 1574 | .user.selected { | ||
| @@ -1575,22 +1591,6 @@ | |||||
| 1575 | color: #fff; | 1591 | color: #fff; | ||
| 1576 | } | 1592 | } | ||
| 1577 | 1593 | ||||
| 1578 | - | .buddy-tile { | |||
| 1579 | - | display: inline-flex; | |||
| 1580 | - | align-items: center; | |||
| 1581 | - | justify-content: center; | |||
| 1582 | - | width: 16px; | |||
| 1583 | - | height: 16px; | |||
| 1584 | - | font-size: 11px; | |||
| 1585 | - | border: 1px solid rgba(0, 0, 0, 0.25); | |||
| 1586 | - | border-radius: 2px; | |||
| 1587 | - | flex: 0 0 16px; | |||
| 1588 | - | } | |||
| 1589 | - | ||||
| 1590 | - | .status-icon { | |||
| 1591 | - | flex: 0 0 12px; | |||
| 1592 | - | } | |||
| 1593 | - | ||||
| 1594 | .user-menu { | 1594 | .user-menu { | ||
| 1595 | position: absolute; | 1595 | position: absolute; | ||
| 1596 | z-index: 50; | 1596 | z-index: 50; | ||
| @@ -1715,29 +1715,6 @@ | |||||
| 1715 | -webkit-text-fill-color: transparent !important; | 1715 | -webkit-text-fill-color: transparent !important; | ||
| 1716 | } | 1716 | } | ||
| 1717 | 1717 | ||||
| 1718 | - | .section-header { | |||
| 1719 | - | margin: 0 0 0.25rem 0; | |||
| 1720 | - | font-weight: bold; | |||
| 1721 | - | font-size: 0.75rem; | |||
| 1722 | - | text-transform: uppercase; | |||
| 1723 | - | letter-spacing: 0.05em; | |||
| 1724 | - | color: var(--color-muted-foreground); | |||
| 1725 | - | } | |||
| 1726 | - | ||||
| 1727 | - | .offline-header { | |||
| 1728 | - | opacity: 0.7; | |||
| 1729 | - | } | |||
| 1730 | - | ||||
| 1731 | - | .section-separator { | |||
| 1732 | - | border-top: 1px solid var(--color-border); | |||
| 1733 | - | margin: 0.5rem 0; | |||
| 1734 | - | } | |||
| 1735 | - | ||||
| 1736 | - | .user.offline { | |||
| 1737 | - | color: var(--color-muted-foreground); | |||
| 1738 | - | font-style: italic; | |||
| 1739 | - | } | |||
| 1740 | - | ||||
| 1741 | .sunken-panel { | 1718 | .sunken-panel { | ||
| 1742 | background: white; | 1719 | background: white; | ||
| 1743 | border: 0.125rem inset #dfdfdf; | 1720 | border: 0.125rem inset #dfdfdf; | ||
| @@ -1788,7 +1765,7 @@ | |||||
| 1788 | flex-direction: column; | 1765 | flex-direction: column; | ||
| 1789 | } | 1766 | } | ||
| 1790 | 1767 | ||||
| 1791 | - | .users-list.mobile { | 1768 | + | .people-panel.mobile { |
| 1792 | width: 100%; | 1769 | width: 100%; | ||
| 1793 | max-height: 30vh; | 1770 | max-height: 30vh; | ||
| 1794 | } | 1771 | } | ||
| @@ -1926,45 +1903,25 @@ | |||||
| 1926 | } | 1903 | } | ||
| 1927 | 1904 | ||||
| 1928 | .registration-prompt { | 1905 | .registration-prompt { | ||
| 1929 | - | background: #fff3e0; | 1906 | + | display: flex; |
| 1930 | - | color: #e65100; | 1907 | + | align-items: center; |
| 1931 | - | padding: 1rem; | 1908 | + | gap: 8px; |
| 1932 | - | margin-bottom: 1rem; | 1909 | + | background: #ece9d8; |
| 1933 | - | border-radius: 4px; | 1910 | + | color: #000; |
| 1934 | - | text-align: center; | 1911 | + | padding: 6px 8px; |
| 1935 | - | font-size: 0.95rem; | 1912 | + | margin-bottom: 6px; |
| 1936 | - | border: 1px solid #ffe0b2; | 1913 | + | border: 1px solid #aca899; |
| 1937 | - | animation: fadeIn 0.3s ease-out; | 1914 | + | font-size: 0.85rem; |
| 1938 | - | } | |||
| 1939 | - | ||||
| 1940 | - | .registration-prompt p { | |||
| 1941 | - | margin: 0; | |||
| 1942 | - | } | |||
| 1943 | - | ||||
| 1944 | - | @keyframes fadeIn { | |||
| 1945 | - | from { | |||
| 1946 | - | opacity: 0; | |||
| 1947 | - | transform: translateY(-10px); | |||
| 1948 | - | } | |||
| 1949 | - | to { | |||
| 1950 | - | opacity: 1; | |||
| 1951 | - | transform: translateY(0); | |||
| 1952 | - | } | |||
| 1953 | } | 1915 | } | ||
| 1954 | 1916 | ||||
| 1955 | - | .registration-prompt .link-button { | 1917 | + | .registration-prompt span { |
| 1956 | - | color: #e65100; | 1918 | + | flex: 1; |
| 1957 | - | text-decoration: underline; | |||
| 1958 | - | cursor: pointer; | |||
| 1959 | - | background: none; | |||
| 1960 | - | border: none; | |||
| 1961 | - | padding: 0; | |||
| 1962 | - | font: inherit; | |||
| 1963 | } | 1919 | } | ||
| 1964 | 1920 | ||||
| 1965 | - | .registration-prompt .link-button:hover { | 1921 | + | .registration-prompt button { |
| 1966 | - | color: #ef6c00; | 1922 | + | min-width: 0; |
| 1967 | - | text-decoration: none; | 1923 | + | padding: 0 10px; |
| 1924 | + | white-space: nowrap; | |||
| 1968 | } | 1925 | } | ||
| 1969 | 1926 | ||||
| 1970 | .chat-window.minimized { | 1927 | .chat-window.minimized { | ||